]> git.donarmstrong.com Git - qmk_firmware.git/blobdiff - quantum/quantum.c
fix LIB_SRC and QUANTUM_LIB_SRC for ARM (#5623)
[qmk_firmware.git] / quantum / quantum.c
index 0fe918b36511250f17d88dacf32a444e78217a02..0fb798a74880e416a8f3f2bafd13498c13677e8c 100644 (file)
@@ -275,6 +275,12 @@ bool process_record_quantum(keyrecord_t *record) {
     preprocess_tap_dance(keycode, record);
   #endif
 
+  #if defined(OLED_DRIVER_ENABLE) && !defined(OLED_DISABLE_TIMEOUT)
+    // Wake up oled if user is using those fabulous keys!
+    if (record->event.pressed)
+      oled_on();
+  #endif
+
   if (!(
   #if defined(KEY_LOCK_ENABLE)
     // Must run first to be able to mask key_up events.
@@ -851,7 +857,7 @@ const bool ascii_to_shift_lut[0x80] PROGMEM = {
 };
 
 __attribute__ ((weak))
-const bool ascii_to_alt_lut[0x80] PROGMEM = {
+const bool ascii_to_altgr_lut[0x80] PROGMEM = {
     0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0,
@@ -951,22 +957,23 @@ void send_string_with_delay_P(const char *str, uint8_t interval) {
 }
 
 void send_char(char ascii_code) {
-  uint8_t keycode;
-  bool is_shifted;
-  bool is_alted;
-
-  keycode = pgm_read_byte(&ascii_to_keycode_lut[(uint8_t)ascii_code]);
-  if (pgm_read_byte(&ascii_to_shift_lut[(uint8_t)ascii_code])) { is_shifted = true; } else { is_shifted = false; }
- if (pgm_read_byte(&ascii_to_alt_lut[(uint8_t)ascii_code])) { is_alted = true; } else { is_alted = false; }
-
-  if (is_shifted) { register_code(KC_LSFT); }
-  if (is_alted) { register_code(KC_RALT); }
-
-  register_code(keycode);
-  unregister_code(keycode);
+  uint8_t keycode = pgm_read_byte(&ascii_to_keycode_lut[(uint8_t)ascii_code]);
+  bool is_shifted = pgm_read_byte(&ascii_to_shift_lut[(uint8_t)ascii_code]);
+  bool is_altgred = pgm_read_byte(&ascii_to_altgr_lut[(uint8_t)ascii_code]);
 
-  if (is_alted) { unregister_code(KC_RALT); }
-  if (is_shifted) { unregister_code(KC_LSFT); }
+  if (is_shifted) {
+    register_code(KC_LSFT);
+  }
+  if (is_altgred) {
+    register_code(KC_RALT);
+  }
+  tap_code(keycode);
+  if (is_altgred) {
+    unregister_code(KC_RALT);
+  }
+  if (is_shifted) {
+    unregister_code(KC_LSFT);
+  }
 }
 
 void set_single_persistent_default_layer(uint8_t default_layer) {
@@ -1083,6 +1090,12 @@ void matrix_init_quantum() {
   #ifdef HAPTIC_ENABLE
     haptic_init();
   #endif
+  #ifdef OUTPUT_AUTO_ENABLE
+    set_output(OUTPUT_AUTO);
+  #endif
+  #ifdef OLED_DRIVER_ENABLE
+    oled_init(OLED_ROTATION_0);
+  #endif
   matrix_init_kb();
 }
 
@@ -1119,32 +1132,44 @@ void matrix_scan_quantum() {
     haptic_task();
   #endif
 
+  #ifdef OLED_DRIVER_ENABLE
+    oled_task();
+  #endif
+
   matrix_scan_kb();
 }
-#if defined(BACKLIGHT_ENABLE) && defined(BACKLIGHT_PIN)
+#if defined(BACKLIGHT_ENABLE) && (defined(BACKLIGHT_PIN) || defined(BACKLIGHT_PINS))
 
-static const uint8_t backlight_pin = BACKLIGHT_PIN;
+// The logic is a bit complex, we support 3 setups:
+// 1. hardware PWM when backlight is wired to a PWM pin
+// depending on this pin, we use a different output compare unit
+// 2. software PWM with hardware timers, but the used timer depends
+// on the audio setup (audio wins other backlight)
+// 3. full software PWM
 
-// depending on the pin, we use a different output compare unit
 #if BACKLIGHT_PIN == B7
+#  define HARDWARE_PWM
 #  define TCCRxA TCCR1A
 #  define TCCRxB TCCR1B
 #  define COMxx1 COM1C1
 #  define OCRxx  OCR1C
 #  define ICRx   ICR1
 #elif BACKLIGHT_PIN == B6
+#  define HARDWARE_PWM
 #  define TCCRxA TCCR1A
 #  define TCCRxB TCCR1B
 #  define COMxx1 COM1B1
 #  define OCRxx  OCR1B
 #  define ICRx   ICR1
 #elif BACKLIGHT_PIN == B5
+#  define HARDWARE_PWM
 #  define TCCRxA TCCR1A
 #  define TCCRxB TCCR1B
 #  define COMxx1 COM1A1
 #  define OCRxx  OCR1A
 #  define ICRx   ICR1
 #elif BACKLIGHT_PIN == C6
+#  define HARDWARE_PWM
 #  define TCCRxA TCCR3A
 #  define TCCRxB TCCR3B
 #  define COMxx1 COM1A1
@@ -1158,28 +1183,115 @@ static const uint8_t backlight_pin = BACKLIGHT_PIN;
 #  define ICRx   ICR1
 #  define TIMSK1 TIMSK
 #else
-#  define NO_HARDWARE_PWM
+#  if !defined(BACKLIGHT_CUSTOM_DRIVER)
+#    if !defined(B5_AUDIO) && !defined(B6_AUDIO) && !defined(B7_AUDIO)
+     // timer 1 is not used by audio , backlight can use it
+#pragma message "Using hardware timer 1 with software PWM"
+#      define HARDWARE_PWM
+#      define BACKLIGHT_PWM_TIMER
+#      define TCCRxA TCCR1A
+#      define TCCRxB TCCR1B
+#      define OCRxx  OCR1A
+#      define OCRxAH OCR1AH
+#      define OCRxAL OCR1AL
+#      define TIMERx_COMPA_vect TIMER1_COMPA_vect
+#      define TIMERx_OVF_vect TIMER1_OVF_vect
+#      define OCIExA OCIE1A
+#      define TOIEx  TOIE1
+#      define ICRx   ICR1
+#      ifndef TIMSK
+#        define TIMSK TIMSK1
+#      endif
+#    elif !defined(C6_AUDIO) && !defined(C5_AUDIO) && !defined(C4_AUDIO)
+#pragma message "Using hardware timer 3 with software PWM"
+// timer 3 is not used by audio, backlight can use it
+#      define HARDWARE_PWM
+#      define BACKLIGHT_PWM_TIMER
+#      define TCCRxA TCCR3A
+#      define TCCRxB TCCR3B
+#      define OCRxx OCR3A
+#      define OCRxAH OCR3AH
+#      define OCRxAL OCR3AL
+#      define TIMERx_COMPA_vect TIMER3_COMPA_vect
+#      define TIMERx_OVF_vect TIMER3_OVF_vect
+#      define OCIExA OCIE3A
+#      define TOIEx  TOIE3
+#      define ICRx   ICR1
+#      ifndef TIMSK
+#        define TIMSK TIMSK3
+#      endif
+#    else
+#pragma message "Audio in use - using pure software PWM"
+#define NO_HARDWARE_PWM
+#    endif
+#  else
+#pragma message "Custom driver defined - using pure software PWM"
+#define NO_HARDWARE_PWM
+#  endif
 #endif
 
 #ifndef BACKLIGHT_ON_STATE
 #define BACKLIGHT_ON_STATE 0
 #endif
 
-#ifdef NO_HARDWARE_PWM // pwm through software
+void backlight_on(uint8_t backlight_pin) {
+#if BACKLIGHT_ON_STATE == 0
+  writePinLow(backlight_pin);
+#else
+  writePinHigh(backlight_pin);
+#endif
+}
 
-__attribute__ ((weak))
+void backlight_off(uint8_t backlight_pin) {
+#if BACKLIGHT_ON_STATE == 0
+  writePinHigh(backlight_pin);
+#else
+  writePinLow(backlight_pin);
+#endif
+}
+
+
+#if defined(NO_HARDWARE_PWM) || defined(BACKLIGHT_PWM_TIMER)  // pwm through software
+
+// we support multiple backlight pins
+#ifndef BACKLIGHT_LED_COUNT
+#define BACKLIGHT_LED_COUNT 1
+#endif
+
+#if BACKLIGHT_LED_COUNT == 1
+#define BACKLIGHT_PIN_INIT { BACKLIGHT_PIN }
+#else
+#define BACKLIGHT_PIN_INIT BACKLIGHT_PINS
+#endif
+
+#define FOR_EACH_LED(x)                             \
+  for (uint8_t i = 0; i < BACKLIGHT_LED_COUNT; i++) \
+  {                                                 \
+    uint8_t backlight_pin = backlight_pins[i];      \
+    { \
+      x                         \
+    }                                             \
+  }
+
+static const uint8_t backlight_pins[BACKLIGHT_LED_COUNT] = BACKLIGHT_PIN_INIT;
+
+#else // full hardware PWM
+
+// we support only one backlight pin
+static const uint8_t backlight_pin = BACKLIGHT_PIN;
+#define FOR_EACH_LED(x) x
+
+#endif
+
+#ifdef NO_HARDWARE_PWM
+__attribute__((weak))
 void backlight_init_ports(void)
 {
   // Setup backlight pin as output and output to on state.
-  // DDRx |= n
-  _SFR_IO8((backlight_pin >> 4) + 1) |= _BV(backlight_pin & 0xF);
-  #if BACKLIGHT_ON_STATE == 0
-    // PORTx &= ~n
-    _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF);
-  #else
-    // PORTx |= n
-    _SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF);
-  #endif
+  FOR_EACH_LED(
+    setPinOutput(backlight_pin);
+    backlight_on(backlight_pin);
+  )
 }
 
 __attribute__ ((weak))
@@ -1190,21 +1302,14 @@ uint8_t backlight_tick = 0;
 #ifndef BACKLIGHT_CUSTOM_DRIVER
 void backlight_task(void) {
   if ((0xFFFF >> ((BACKLIGHT_LEVELS - get_backlight_level()) * ((BACKLIGHT_LEVELS + 1) / 2))) & (1 << backlight_tick)) {
-    #if BACKLIGHT_ON_STATE == 0
-      // PORTx &= ~n
-      _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF);
-    #else
-      // PORTx |= n
-      _SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF);
-    #endif
-  } else {
-    #if BACKLIGHT_ON_STATE == 0
-      // PORTx |= n
-      _SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF);
-    #else
-      // PORTx &= ~n
-      _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF);
-    #endif
+    FOR_EACH_LED(
+      backlight_on(backlight_pin);
+    )
+  }
+  else {
+    FOR_EACH_LED(
+      backlight_off(backlight_pin);
+    )
   }
   backlight_tick = (backlight_tick + 1) % 16;
 }
@@ -1216,7 +1321,52 @@ void backlight_task(void) {
   #endif
 #endif
 
-#else // pwm through timer
+#else // hardware pwm through timer
+
+#ifdef BACKLIGHT_PWM_TIMER
+
+// The idea of software PWM assisted by hardware timers is the following
+// we use the hardware timer in fast PWM mode like for hardware PWM, but
+// instead of letting the Output Match Comparator control the led pin
+// (which is not possible since the backlight is not wired to PWM pins on the
+// CPU), we do the LED on/off by oursleves.
+// The timer is setup to count up to 0xFFFF, and we set the Output Compare
+// register to the current 16bits backlight level (after CIE correction). 
+// This means the CPU will trigger a compare match interrupt when the counter 
+// reaches the backlight level, where we turn off the LEDs, 
+// but also an overflow interrupt when the counter rolls back to 0, 
+// in which we're going to turn on the LEDs.
+// The LED will then be on for OCRxx/0xFFFF time, adjusted every 244Hz.
+
+// Triggered when the counter reaches the OCRx value
+ISR(TIMERx_COMPA_vect) {
+  FOR_EACH_LED(
+    backlight_off(backlight_pin);
+  )
+}
+
+// Triggered when the counter reaches the TOP value
+// this one triggers at F_CPU/65536 =~ 244 Hz 
+ISR(TIMERx_OVF_vect) {
+#ifdef BACKLIGHT_BREATHING
+  breathing_task();
+#endif
+  // for very small values of OCRxx (or backlight level)
+  // we can't guarantee this whole code won't execute
+  // at the same time as the compare match interrupt
+  // which means that we might turn on the leds while
+  // trying to turn them off, leading to flickering
+  // artifacts (especially while breathing, because breathing_task
+  // takes many computation cycles).
+  // so better not turn them on while the counter TOP is very low.
+  if (OCRxx > 256) {
+    FOR_EACH_LED(
+      backlight_on(backlight_pin);
+    )
+  }
+}
+
+#endif
 
 #define TIMER_TOP 0xFFFFU
 
@@ -1248,11 +1398,28 @@ void backlight_set(uint8_t level) {
     level = BACKLIGHT_LEVELS;
 
   if (level == 0) {
+    #ifdef BACKLIGHT_PWM_TIMER
+      if (OCRxx) {
+        TIMSK &= ~(_BV(OCIExA));
+        TIMSK &= ~(_BV(TOIEx));
+        FOR_EACH_LED(
+          backlight_off(backlight_pin);
+        )
+      }
+    #else
     // Turn off PWM control on backlight pin
     TCCRxA &= ~(_BV(COMxx1));
+    #endif
   } else {
+    #ifdef BACKLIGHT_PWM_TIMER
+      if (!OCRxx) {
+        TIMSK |= _BV(OCIExA);
+        TIMSK |= _BV(TOIEx);
+      }
+    #else
     // Turn on PWM control of backlight pin
     TCCRxA |= _BV(COMxx1);
+    #endif
   }
   // Set the brightness
   set_pwm(cie_lightness(TIMER_TOP * (uint32_t)level / BACKLIGHT_LEVELS));
@@ -1272,12 +1439,25 @@ static uint8_t breathing_period = BREATHING_PERIOD;
 static uint8_t breathing_halt = BREATHING_NO_HALT;
 static uint16_t breathing_counter = 0;
 
+#ifdef BACKLIGHT_PWM_TIMER
+static bool breathing = false;
+
+bool is_breathing(void) {
+  return breathing;
+}
+
+#define breathing_interrupt_enable() do { breathing = true; } while (0)
+#define breathing_interrupt_disable() do { breathing = false; } while (0)
+#else
+
 bool is_breathing(void) {
     return !!(TIMSK1 & _BV(TOIE1));
 }
 
 #define breathing_interrupt_enable() do {TIMSK1 |= _BV(TOIE1);} while (0)
 #define breathing_interrupt_disable() do {TIMSK1 &= ~_BV(TOIE1);} while (0)
+#endif
+
 #define breathing_min() do {breathing_counter = 0;} while (0)
 #define breathing_max() do {breathing_counter = breathing_period * 244 / 2;} while (0)
 
@@ -1351,10 +1531,14 @@ static inline uint16_t scale_backlight(uint16_t v) {
   return v / BACKLIGHT_LEVELS * get_backlight_level();
 }
 
+#ifdef BACKLIGHT_PWM_TIMER
+void breathing_task(void)
+#else
 /* Assuming a 16MHz CPU clock and a timer that resets at 64k (ICR1), the following interrupt handler will run
  * about 244 times per second.
  */
 ISR(TIMER1_OVF_vect)
+#endif
 {
   uint16_t interval = (uint16_t) breathing_period * 244 / BREATHING_STEPS;
   // resetting after one period to prevent ugly reset at overflow.
@@ -1376,19 +1560,21 @@ __attribute__ ((weak))
 void backlight_init_ports(void)
 {
   // Setup backlight pin as output and output to on state.
-  // DDRx |= n
-  _SFR_IO8((backlight_pin >> 4) + 1) |= _BV(backlight_pin & 0xF);
-  #if BACKLIGHT_ON_STATE == 0
-    // PORTx &= ~n
-    _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF);
-  #else
-    // PORTx |= n
-    _SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF);
-  #endif
+  FOR_EACH_LED(
+    setPinOutput(backlight_pin);
+    backlight_on(backlight_pin);
+  )
+
   // I could write a wall of text here to explain... but TL;DW
   // Go read the ATmega32u4 datasheet.
   // And this: http://blog.saikoled.com/post/43165849837/secret-konami-cheat-code-to-high-resolution-pwm-on
 
+#ifdef BACKLIGHT_PWM_TIMER
+  // TimerX setup, Fast PWM mode count to TOP set in ICRx
+  TCCRxA = _BV(WGM11); // = 0b00000010;
+  // clock select clk/1
+  TCCRxB = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // = 0b00011001;
+#else // hardware PWM
   // Pin PB7 = OCR1C (Timer 1, Channel C)
   // Compare Output Mode = Clear on compare match, Channel C = COM1C1=1 COM1C0=0
   // (i.e. start high, go low when counter matches.)
@@ -1400,8 +1586,9 @@ void backlight_init_ports(void)
   "In fast PWM mode, the compare units allow generation of PWM waveforms on the OCnx pins. Setting the COMnx1:0 bits to two will produce a non-inverted PWM [..]."
   "In fast PWM mode the counter is incremented until the counter value matches either one of the fixed values 0x00FF, 0x01FF, or 0x03FF (WGMn3:0 = 5, 6, or 7), the value in ICRn (WGMn3:0 = 14), or the value in OCRnA (WGMn3:0 = 15)."
   */
-  TCCRxA = _BV(COMxx1) | _BV(WGM11); // = 0b00001010;
+  TCCRxA = _BV(COMxx1) | _BV(WGM11);            // = 0b00001010;
   TCCRxB = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // = 0b00011001;
+#endif
   // Use full 16-bit resolution. Counter counts to ICR1 before reset to 0.
   ICRx = TIMER_TOP;
 
@@ -1411,9 +1598,9 @@ void backlight_init_ports(void)
   #endif
 }
 
-#endif // NO_HARDWARE_PWM
+#endif // hardware backlight
 
-#else // backlight
+#else // no backlight
 
 __attribute__ ((weak))
 void backlight_init_ports(void) {}