diff options
Diffstat (limited to 'quantum/quantum.c')
| -rw-r--r-- | quantum/quantum.c | 392 | 
1 files changed, 216 insertions, 176 deletions
| diff --git a/quantum/quantum.c b/quantum/quantum.c index 9aa498dadb..d4fa7f2efc 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -24,10 +24,6 @@  #include "outputselect.h"  #endif -#ifndef TAPPING_TERM -#define TAPPING_TERM 200 -#endif -  #ifndef BREATHING_PERIOD  #define BREATHING_PERIOD 6  #endif @@ -196,30 +192,6 @@ void reset_keyboard(void) {    bootloader_jump();  } -// Shift / paren setup - -#ifndef LSPO_KEY -  #define LSPO_KEY KC_9 -#endif -#ifndef RSPC_KEY -  #define RSPC_KEY KC_0 -#endif - -#ifndef LSPO_MOD -  #define LSPO_MOD KC_LSFT -#endif -#ifndef RSPC_MOD -  #define RSPC_MOD KC_RSFT -#endif - -// Shift / Enter setup -#ifndef SFTENT_KEY -  #define SFTENT_KEY KC_ENT -#endif - -static bool shift_interrupted[2] = {0, 0}; -static uint16_t scs_timer[2] = {0, 0}; -  /* true if the last press of GRAVE_ESC was shifted (i.e. GUI or SHIFT were pressed), false otherwise.   * Used to ensure that the correct keycode is released if the key is released.   */ @@ -275,12 +247,6 @@ 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. @@ -329,6 +295,9 @@ bool process_record_quantum(keyrecord_t *record) {    #ifdef TERMINAL_ENABLE      process_terminal(keycode, record) &&    #endif +  #ifdef SPACE_CADET_ENABLE +    process_space_cadet(keycode, record) && +  #endif        true)) {      return false;    } @@ -685,92 +654,6 @@ bool process_record_quantum(keyrecord_t *record) {          return false;        }        break; -    case KC_LSPO: { -      if (record->event.pressed) { -        shift_interrupted[0] = false; -        scs_timer[0] = timer_read (); -        register_mods(MOD_BIT(KC_LSFT)); -      } -      else { -        #ifdef DISABLE_SPACE_CADET_ROLLOVER -          if (get_mods() & MOD_BIT(RSPC_MOD)) { -            shift_interrupted[0] = true; -            shift_interrupted[1] = true; -          } -        #endif -        if (!shift_interrupted[0] && timer_elapsed(scs_timer[0]) < TAPPING_TERM) { -          #ifdef DISABLE_SPACE_CADET_MODIFIER -            unregister_mods(MOD_BIT(KC_LSFT)); -          #else -            if( LSPO_MOD != KC_LSFT ){ -              unregister_mods(MOD_BIT(KC_LSFT)); -              register_mods(MOD_BIT(LSPO_MOD)); -            } -          #endif -          register_code(LSPO_KEY); -          unregister_code(LSPO_KEY); -          #ifndef DISABLE_SPACE_CADET_MODIFIER -            if( LSPO_MOD != KC_LSFT ){ -              unregister_mods(MOD_BIT(LSPO_MOD)); -            } -          #endif -        } -        unregister_mods(MOD_BIT(KC_LSFT)); -      } -      return false; -    } - -    case KC_RSPC: { -      if (record->event.pressed) { -        shift_interrupted[1] = false; -        scs_timer[1] = timer_read (); -        register_mods(MOD_BIT(KC_RSFT)); -      } -      else { -        #ifdef DISABLE_SPACE_CADET_ROLLOVER -          if (get_mods() & MOD_BIT(LSPO_MOD)) { -            shift_interrupted[0] = true; -            shift_interrupted[1] = true; -          } -        #endif -        if (!shift_interrupted[1] && timer_elapsed(scs_timer[1]) < TAPPING_TERM) { -          #ifdef DISABLE_SPACE_CADET_MODIFIER -            unregister_mods(MOD_BIT(KC_RSFT)); -          #else -            if( RSPC_MOD != KC_RSFT ){ -              unregister_mods(MOD_BIT(KC_RSFT)); -              register_mods(MOD_BIT(RSPC_MOD)); -            } -          #endif -          register_code(RSPC_KEY); -          unregister_code(RSPC_KEY); -          #ifndef DISABLE_SPACE_CADET_MODIFIER -            if ( RSPC_MOD != KC_RSFT ){ -              unregister_mods(MOD_BIT(RSPC_MOD)); -            } -          #endif -        } -        unregister_mods(MOD_BIT(KC_RSFT)); -      } -      return false; -    } - -    case KC_SFTENT: { -      if (record->event.pressed) { -        shift_interrupted[1] = false; -        scs_timer[1] = timer_read (); -        register_mods(MOD_BIT(KC_RSFT)); -      } -      else if (!shift_interrupted[1] && timer_elapsed(scs_timer[1]) < TAPPING_TERM) { -        unregister_mods(MOD_BIT(KC_RSFT)); -        register_code(SFTENT_KEY); -        unregister_code(SFTENT_KEY); -      } -      else { -        unregister_mods(MOD_BIT(KC_RSFT)); -      } -      return false; -    }      case GRAVE_ESC: {        uint8_t shifted = get_mods() & ((MOD_BIT(KC_LSHIFT)|MOD_BIT(KC_RSHIFT) @@ -825,12 +708,6 @@ bool process_record_quantum(keyrecord_t *record) {        return false;      }  #endif - -    default: { -      shift_interrupted[0] = true; -      shift_interrupted[1] = true; -      break; -    }    }    return process_action_kb(record); @@ -1093,9 +970,6 @@ void matrix_init_quantum() {    #ifdef OUTPUT_AUTO_ENABLE      set_output(OUTPUT_AUTO);    #endif -  #ifdef OLED_DRIVER_ENABLE -    oled_init(OLED_ROTATION_0); -  #endif    matrix_init_kb();  } @@ -1132,36 +1006,40 @@ 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 @@ -1175,28 +1053,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)) @@ -1207,21 +1172,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;  } @@ -1233,7 +1191,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 @@ -1265,11 +1268,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)); @@ -1289,12 +1309,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) @@ -1368,10 +1401,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. @@ -1393,19 +1430,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.) @@ -1417,8 +1456,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; @@ -1428,9 +1468,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) {} | 
