]> git.donarmstrong.com Git - qmk_firmware.git/commitdiff
Improve backlight PWM pin support (#6202)
authorfauxpark <fauxpark@gmail.com>
Thu, 8 Aug 2019 20:12:12 +0000 (06:12 +1000)
committerDrashna Jaelre <drashna@live.com>
Thu, 8 Aug 2019 20:12:12 +0000 (13:12 -0700)
* Improve backlight PWM pin support

* I accidentally an equals sign

* Another typo

* Order by pin number

* Throw an error if backlight pin is C4 or C5 on 16/32U4

* Use else for clarity

* Minor alignment adjustments

docs/config_options.md
docs/feature_backlight.md
docs/getting_started_make_guide.md
docs/hardware_avr.md
docs/porting_your_keyboard_to_qmk_(arm_and_other_chibios_cpus).md
quantum/quantum.c

index 3be294db89498db659d7aba8762077911c5a37cf..d2ae56179877c2d04821156170514b4abb89610b 100644 (file)
@@ -76,7 +76,7 @@ This is a C header file that is one of the first things included, and will persi
 * `#define B7_AUDIO`
   * enables audio on pin B7 (duophony is enables if one of B[5-7]\_AUDIO is enabled along with one of C[4-6]\_AUDIO)
 * `#define BACKLIGHT_PIN B7`
-  * pin of the backlight - `B5`, `B6`, `B7` and `C6` (and `D4` on ATmega32A) use hardware PWM, others use software implementation
+  * pin of the backlight
 * `#define BACKLIGHT_LEVELS 3`
   * number of levels your backlight will have (maximum 15 excluding off)
 * `#define BACKLIGHT_BREATHING`
index 64c663076ba1f61af53929dd28c15c68caf5d315..2d9eea67bfa79cd547f750133ef16d668c4b17a5 100644 (file)
@@ -30,32 +30,31 @@ You should then be able to use the keycodes below to change the backlight level.
 
 This feature is distinct from both the [RGB underglow](feature_rgblight.md) and [RGB matrix](feature_rgb_matrix.md) features as it usually allows for only a single colour per switch, though you can obviously use multiple different coloured LEDs on a keyboard.
 
-Hardware PWM is only supported on certain pins of the MCU, so if the backlighting is not connected to one of them, a software PWM implementation triggered by hardware timer interrupts will be used.
-
 Hardware PWM is supported according to the following table:
 
-| Backlight Pin | Hardware timer          |
-|---------------|-------------------------|
-|`B5`           | Timer 1                 |
-|`B6`           | Timer 1                 |
-|`B7`           | Timer 1                 |
-|`C6`           | Timer 3                 |
-|`D4`           | Timer 1 (ATmega32A only)|
-| other         | Software PWM            |
-
-The [audio feature](feature_audio.md) also uses hardware timers. Please refer to the following table to know what hardware timer the software PWM will use depending on the audio configuration:
-
-| Audio Pin(s) | Audio Timer | Software PWM Timer |
-|--------------|-------------|--------------------|
-| `C4`         | Timer 3     | Timer 1            |
-| `C5`         | Timer 3     | Timer 1            |
-| `C6`         | Timer 3     | Timer 1            |
-| `B5`         | Timer 1     | Timer 3            |
-| `B6`         | Timer 1     | Timer 3            |
-| `B7`         | Timer 1     | Timer 3            |
-| `Bx` & `Cx`  | Timer 1 & 3 | None               |
-
-When all timers are in use for [audio](feature_audio.md), the backlight software PWM will not use a hardware timer, but instead will be triggered during the matrix scan. In this case the backlight doesn't support breathing and might show lighting artifacts (for instance flickering), because the PWM computation might not be called with enough timing precision.
+|Backlight Pin|AT90USB64/128|ATmega16/32U4|ATmega16/32U2|ATmega32A|
+|-------------|-------------|-------------|-------------|---------|
+|`B5`         |Timer 1      |Timer 1      |             |         |
+|`B6`         |Timer 1      |Timer 1      |             |         |
+|`B7`         |Timer 1      |Timer 1      |Timer 1      |         |
+|`C4`         |Timer 3      |             |             |         |
+|`C5`         |Timer 3      |             |Timer 1      |         |
+|`C6`         |Timer 3      |Timer 3      |Timer 1      |         |
+|`D4`         |             |             |             |Timer 1  |
+|`D5`         |             |             |             |Timer 1  |
+
+All other pins will use software PWM. If the [Audio](feature_audio.md) feature is disabled or only using one timer, the backlight PWM can be triggered by a hardware timer:
+
+|Audio Pin|Audio Timer|Software PWM Timer|
+|---------|-----------|------------------|
+|`C4`     |Timer 3    |Timer 1           |
+|`C5`     |Timer 3    |Timer 1           |
+|`C6`     |Timer 3    |Timer 1           |
+|`B5`     |Timer 1    |Timer 3           |
+|`B6`     |Timer 1    |Timer 3           |
+|`B7`     |Timer 1    |Timer 3           |
+
+When both timers are in use for Audio, the backlight PWM will not use a hardware timer, but will instead be triggered during the matrix scan. In this case, breathing is not supported, and the backlight might flicker, because the PWM computation may not be called with enough timing precision.
 
 ## Configuration
 
index 75eafd42ccca52cdb1f2c1c7b017f54b9cfa973f..4fe3f184df45a9d272c148ed82503c16540dea12 100644 (file)
@@ -83,7 +83,7 @@ This allows the keyboard to tell the host OS that up to 248 keys are held down a
 
 `BACKLIGHT_ENABLE`
 
-This enables your backlight on Timer1 and ports B5, B6, or B7 (for now). You can specify your port by putting this in your `config.h`:
+This enables the in-switch LED backlighting. You can specify the backlight pin by putting this in your `config.h`:
 
     #define BACKLIGHT_PIN B7
 
index 7c28ab6dbcc5d20dfdfb239d057e5868df5bc310..c6987d1bdf91ecd3acbe03d153098df57245a2f1 100644 (file)
@@ -125,7 +125,7 @@ To configure a keyboard where each switch is connected to a separate pin and gro
 
 ### Backlight Configuration
 
-By default QMK supports backlighting on pins `B5`, `B6`, and `B7`. If you are using one of those you can simply enable it here. For more details see the [Backlight Documentation](feature_backlight.md).
+QMK supports backlighting on most GPIO pins. A select few of these can be driven by the MCU in hardware. For more details see the [Backlight Documentation](feature_backlight.md).
 
 ```c
 #define BACKLIGHT_PIN B7
@@ -134,8 +134,6 @@ By default QMK supports backlighting on pins `B5`, `B6`, and `B7`. If you are us
 #define BREATHING_PERIOD 6
 ```
 
-?> You can use backlighting on any pin you like, but you will have to do more work to support that. See the [Backlight Documentation](feature_backlight.md) for more details.
-
 ### Other Configuration Options
 
 There are a lot of features that can be configured or tuned in `config.h`. You should see the [Config Options](config_options.md) page for more details.
index 979eafbc80f7c09526e3e4f5c95e3f80eb4c1d02..bce9f9dc9bcad113025317ff7d4dacbf687fc247 100644 (file)
@@ -34,7 +34,7 @@ For the `DIODE_DIRECTION`, most hand-wiring guides will instruct you to wire the
 
 To configure a keyboard where each switch is connected to a separate pin and ground instead of sharing row and column pins, use `DIRECT_PINS`. The mapping defines the pins of each switch in rows and columns, from left to right. Must conform to the sizes within `MATRIX_ROWS` and `MATRIX_COLS`, use `NO_PIN` to fill in blank spaces. Overrides the behaviour of `DIODE_DIRECTION`, `MATRIX_ROW_PINS` and `MATRIX_COL_PINS`.
 
-`BACKLIGHT_PIN` is the pin that your PWM-controlled backlight (if one exists) is hooked-up to. Currently only B5, B6, and B7 are supported.
+`BACKLIGHT_PIN` is the pin that your PWM-controlled backlight (if one exists) is hooked-up to.
 
 `BACKLIGHT_BREATHING` is a fancier backlight feature that adds breathing/pulsing/fading effects to the backlight. It uses the same timer as the normal backlight. These breathing effects must be called by code in your keymap.
 
index d98c601d991ab381e1375dedbe1dcad32d6a9d43..77cbbb2e77d779f247cede7a63fd0948e3892a27 100644 (file)
@@ -1034,104 +1034,147 @@ void matrix_scan_quantum() {
 }
 #if defined(BACKLIGHT_ENABLE) && (defined(BACKLIGHT_PIN) || defined(BACKLIGHT_PINS))
 
-// 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
-
-#if BACKLIGHT_PIN == B7
-#  define HARDWARE_PWM
-#  define TCCRxA TCCR1A
-#  define TCCRxB TCCR1B
-#  define COMxx1 COM1C1
-#  define OCRxx  OCR1C
-#  define TIMERx_OVF_vect TIMER1_OVF_vect
-#  define TOIEx  TOIE1
-#  define ICRx   ICR1
-#  define TIMSKx TIMSK1
-#elif BACKLIGHT_PIN == B6
-#  define HARDWARE_PWM
-#  define TCCRxA TCCR1A
-#  define TCCRxB TCCR1B
-#  define COMxx1 COM1B1
-#  define OCRxx  OCR1B
-#  define TIMERx_OVF_vect TIMER1_OVF_vect
-#  define TOIEx  TOIE1
-#  define ICRx   ICR1
-#  define TIMSKx TIMSK1
-#elif BACKLIGHT_PIN == B5
-#  define HARDWARE_PWM
-#  define TCCRxA TCCR1A
-#  define TCCRxB TCCR1B
-#  define COMxx1 COM1A1
-#  define OCRxx  OCR1A
-#  define TIMERx_OVF_vect TIMER1_OVF_vect
-#  define TOIEx  TOIE1
-#  define ICRx   ICR1
-#  define TIMSKx TIMSK1
-#elif BACKLIGHT_PIN == C6
-#  define HARDWARE_PWM
-#  define TCCRxA TCCR3A
-#  define TCCRxB TCCR3B
-#  define COMxx1 COM3A1
-#  define OCRxx  OCR3A
-#  define TIMERx_OVF_vect TIMER3_OVF_vect
-#  define TOIEx  TOIE3
-#  define ICRx   ICR3
-#  define TIMSKx TIMSK3
-#elif defined(__AVR_ATmega32A__) && BACKLIGHT_PIN == D4
-#  define TCCRxA TCCR1A
-#  define TCCRxB TCCR1B
-#  define COMxx1 COM1B1
-#  define OCRxx  OCR1B
-#  define TIMERx_OVF_vect TIMER1_OVF_vect
-#  define TOIEx  TOIE1
-#  define ICRx   ICR1
-#  define TIMSKx TIMSK1
+// This 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 over Backlight).
+//   3. Full software PWM, driven by the matrix scan, if both timers are used by Audio.
+
+#if (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) \
+  || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) \
+  || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__)) \
+  && (BACKLIGHT_PIN == B5 || BACKLIGHT_PIN == B6 || BACKLIGHT_PIN == B7)
+  #define HARDWARE_PWM
+  #define ICRx            ICR1
+  #define TCCRxA          TCCR1A
+  #define TCCRxB          TCCR1B
+  #define TIMERx_OVF_vect TIMER1_OVF_vect
+  #define TIMSKx          TIMSK1
+  #define TOIEx           TOIE1
+
+  #if BACKLIGHT_PIN == B5
+    #define COMxx1        COM1A1
+    #define OCRxx         OCR1A
+  #elif BACKLIGHT_PIN == B6
+    #define COMxx1        COM1B1
+    #define OCRxx         OCR1B
+  #elif BACKLIGHT_PIN == B7
+    #define COMxx1        COM1C1
+    #define OCRxx         OCR1C
+  #endif
+#elif (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) \
+  || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) \
+  || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__)) \
+  && (BACKLIGHT_PIN == C4 || BACKLIGHT_PIN == C5 || BACKLIGHT_PIN == C6)
+  #define HARDWARE_PWM
+  #define ICRx            ICR3
+  #define TCCRxA          TCCR3A
+  #define TCCRxB          TCCR3B
+  #define TIMERx_OVF_vect TIMER3_OVF_vect
+  #define TIMSKx          TIMSK3
+  #define TOIEx           TOIE3
+
+  #if BACKLIGHT_PIN == C4
+    #if (defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__))
+      #error This MCU has no C4 pin!
+    #else
+      #define COMxx1      COM3C1
+      #define OCRxx       OCR3C
+    #endif
+  #elif BACKLIGHT_PIN == C5
+    #if (defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__))
+      #error This MCU has no C5 pin!
+    #else
+      #define COMxx1      COM3B1
+      #define OCRxx       OCR3B
+    #endif
+  #elif BACKLIGHT_PIN == C6
+    #define COMxx1        COM3A1
+    #define OCRxx         OCR3A
+  #endif
+#elif (defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__)) \
+  && (BACKLIGHT_PIN == B7 || BACKLIGHT_PIN == C5 || BACKLIGHT_PIN == C6)
+  #define HARDWARE_PWM
+  #define ICRx            ICR1
+  #define TCCRxA          TCCR1A
+  #define TCCRxB          TCCR1B
+  #define TIMERx_OVF_vect TIMER1_OVF_vect
+  #define TIMSKx          TIMSK1
+  #define TOIEx           TOIE1
+
+  #if BACKLIGHT_PIN == B7
+    #define COMxx1        COM1C1
+    #define OCRxx         OCR1C
+  #elif BACKLIGHT_PIN == C5
+    #define COMxx1        COM1B1
+    #define OCRxx         OCR1B
+  #elif BACKLIGHT_PIN == C6
+    #define COMxx1        COM1A1
+    #define OCRxx         OCR1A
+  #endif
+#elif defined(__AVR_ATmega32A__) \
+  && (BACKLIGHT_PIN == D4 || BACKLIGHT_PIN == D5)
+  #define HARDWARE_PWM
+  #define ICRx            ICR1
+  #define TCCRxA          TCCR1A
+  #define TCCRxB          TCCR1B
+  #define TIMERx_OVF_vect TIMER1_OVF_vect
+  #define TIMSKx          TIMSK
+  #define TOIEx           TOIE1
+
+  #if BACKLIGHT_PIN == D4
+    #define COMxx1        COM1B1
+    #define OCRxx         OCR1B
+  #elif BACKLIGHT_PIN == D5
+    #define COMxx1        COM1A1
+    #define OCRxx         OCR1A
+  #endif
 #else
-#  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 TIMERx_COMPA_vect TIMER1_COMPA_vect
-#      define TIMERx_OVF_vect TIMER1_OVF_vect
-#      define OCIExA OCIE1A
-#      define TOIEx  TOIE1
-#      define ICRx   ICR1
-#      if defined(__AVR_ATmega32A__) // This MCU has only one TIMSK register
-#        define TIMSKx TIMSK
-#      else
-#        define TIMSKx 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 TIMERx_COMPA_vect TIMER3_COMPA_vect
-#      define TIMERx_OVF_vect TIMER3_OVF_vect
-#      define OCIExA OCIE3A
-#      define TOIEx  TOIE3
-#      define ICRx   ICR1
-#      define TIMSKx TIMSK3
-#    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
+  #if !defined(BACKLIGHT_CUSTOM_DRIVER)
+    #if !defined(B5_AUDIO) && !defined(B6_AUDIO) && !defined(B7_AUDIO)
+      // Timer 1 is not in use by Audio feature, Backlight can use it
+      #pragma message "Using hardware timer 1 with software PWM"
+      #define HARDWARE_PWM
+      #define BACKLIGHT_PWM_TIMER
+      #define ICRx              ICR1
+      #define TCCRxA            TCCR1A
+      #define TCCRxB            TCCR1B
+      #define TIMERx_COMPA_vect TIMER1_COMPA_vect
+      #define TIMERx_OVF_vect   TIMER1_OVF_vect
+      #if defined(__AVR_ATmega32A__) // This MCU has only one TIMSK register
+        #define TIMSKx          TIMSK
+      #else
+        #define TIMSKx          TIMSK1
+      #endif
+      #define TOIEx             TOIE1
+
+      #define OCIExA            OCIE1A
+      #define OCRxx             OCR1A
+    #elif !defined(C6_AUDIO) && !defined(C5_AUDIO) && !defined(C4_AUDIO)
+      #pragma message "Using hardware timer 3 with software PWM"
+      // Timer 3 is not in use by Audio feature, Backlight can use it
+      #define HARDWARE_PWM
+      #define BACKLIGHT_PWM_TIMER
+      #define ICRx              ICR1
+      #define TCCRxA            TCCR3A
+      #define TCCRxB            TCCR3B
+      #define TIMERx_COMPA_vect TIMER3_COMPA_vect
+      #define TIMERx_OVF_vect   TIMER3_OVF_vect
+      #define TIMSKx            TIMSK3
+      #define TOIEx             TOIE3
+
+      #define OCIExA            OCIE3A
+      #define OCRxx             OCR3A
+    #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
@@ -1300,7 +1343,7 @@ static uint16_t cie_lightness(uint16_t v) {
 
 // range for val is [0..TIMER_TOP]. PWM pin is high while the timer count is below val.
 static inline void set_pwm(uint16_t val) {
-       OCRxx = val;
+  OCRxx = val;
 }
 
 #ifndef BACKLIGHT_CUSTOM_DRIVER