Commit | Line | Data |
---|---|---|
38aefaf7 JC |
1 | #include "quantum.h" |
2 | #include "backlight.h" | |
3 | #include "debug.h" | |
4 | ||
5 | #if defined(BACKLIGHT_ENABLE) && (defined(BACKLIGHT_PIN) || defined(BACKLIGHT_PINS)) | |
6 | ||
7 | // This logic is a bit complex, we support 3 setups: | |
8 | // | |
9 | // 1. Hardware PWM when backlight is wired to a PWM pin. | |
10 | // Depending on this pin, we use a different output compare unit. | |
11 | // 2. Software PWM with hardware timers, but the used timer | |
12 | // depends on the Audio setup (Audio wins over Backlight). | |
13 | // 3. Full software PWM, driven by the matrix scan, if both timers are used by Audio. | |
14 | ||
15 | # 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) | |
16 | # define HARDWARE_PWM | |
17 | # define ICRx ICR1 | |
18 | # define TCCRxA TCCR1A | |
19 | # define TCCRxB TCCR1B | |
20 | # define TIMERx_OVF_vect TIMER1_OVF_vect | |
21 | # define TIMSKx TIMSK1 | |
22 | # define TOIEx TOIE1 | |
23 | ||
24 | # if BACKLIGHT_PIN == B5 | |
26eef35f | 25 | # define COMxx0 COM1A0 |
38aefaf7 JC |
26 | # define COMxx1 COM1A1 |
27 | # define OCRxx OCR1A | |
28 | # elif BACKLIGHT_PIN == B6 | |
26eef35f | 29 | # define COMxx0 COM1B0 |
38aefaf7 JC |
30 | # define COMxx1 COM1B1 |
31 | # define OCRxx OCR1B | |
32 | # elif BACKLIGHT_PIN == B7 | |
26eef35f | 33 | # define COMxx0 COM1C0 |
38aefaf7 JC |
34 | # define COMxx1 COM1C1 |
35 | # define OCRxx OCR1C | |
36 | # endif | |
37 | # 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) | |
38 | # define HARDWARE_PWM | |
39 | # define ICRx ICR3 | |
40 | # define TCCRxA TCCR3A | |
41 | # define TCCRxB TCCR3B | |
42 | # define TIMERx_OVF_vect TIMER3_OVF_vect | |
43 | # define TIMSKx TIMSK3 | |
44 | # define TOIEx TOIE3 | |
45 | ||
46 | # if BACKLIGHT_PIN == C4 | |
47 | # if (defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__)) | |
48 | # error This MCU has no C4 pin! | |
49 | # else | |
26eef35f | 50 | # define COMxx0 COM3C0 |
38aefaf7 JC |
51 | # define COMxx1 COM3C1 |
52 | # define OCRxx OCR3C | |
53 | # endif | |
54 | # elif BACKLIGHT_PIN == C5 | |
55 | # if (defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__)) | |
56 | # error This MCU has no C5 pin! | |
57 | # else | |
26eef35f | 58 | # define COMxx0 COM3B0 |
38aefaf7 JC |
59 | # define COMxx1 COM3B1 |
60 | # define OCRxx OCR3B | |
61 | # endif | |
62 | # elif BACKLIGHT_PIN == C6 | |
26eef35f | 63 | # define COMxx0 COM3A0 |
38aefaf7 JC |
64 | # define COMxx1 COM3A1 |
65 | # define OCRxx OCR3A | |
66 | # endif | |
67 | # elif (defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__)) && (BACKLIGHT_PIN == B7 || BACKLIGHT_PIN == C5 || BACKLIGHT_PIN == C6) | |
68 | # define HARDWARE_PWM | |
69 | # define ICRx ICR1 | |
70 | # define TCCRxA TCCR1A | |
71 | # define TCCRxB TCCR1B | |
72 | # define TIMERx_OVF_vect TIMER1_OVF_vect | |
73 | # define TIMSKx TIMSK1 | |
74 | # define TOIEx TOIE1 | |
75 | ||
76 | # if BACKLIGHT_PIN == B7 | |
26eef35f | 77 | # define COMxx0 COM1C0 |
38aefaf7 JC |
78 | # define COMxx1 COM1C1 |
79 | # define OCRxx OCR1C | |
80 | # elif BACKLIGHT_PIN == C5 | |
26eef35f | 81 | # define COMxx0 COM1B0 |
38aefaf7 JC |
82 | # define COMxx1 COM1B1 |
83 | # define OCRxx OCR1B | |
84 | # elif BACKLIGHT_PIN == C6 | |
26eef35f | 85 | # define COMxx0 COM1A0 |
38aefaf7 JC |
86 | # define COMxx1 COM1A1 |
87 | # define OCRxx OCR1A | |
88 | # endif | |
89 | # elif defined(__AVR_ATmega32A__) && (BACKLIGHT_PIN == D4 || BACKLIGHT_PIN == D5) | |
90 | # define HARDWARE_PWM | |
91 | # define ICRx ICR1 | |
92 | # define TCCRxA TCCR1A | |
93 | # define TCCRxB TCCR1B | |
94 | # define TIMERx_OVF_vect TIMER1_OVF_vect | |
95 | # define TIMSKx TIMSK | |
96 | # define TOIEx TOIE1 | |
97 | ||
98 | # if BACKLIGHT_PIN == D4 | |
26eef35f | 99 | # define COMxx0 COM1B0 |
38aefaf7 JC |
100 | # define COMxx1 COM1B1 |
101 | # define OCRxx OCR1B | |
102 | # elif BACKLIGHT_PIN == D5 | |
26eef35f | 103 | # define COMxx0 COM1A0 |
38aefaf7 JC |
104 | # define COMxx1 COM1A1 |
105 | # define OCRxx OCR1A | |
106 | # endif | |
107 | # elif defined(__AVR_ATmega328P__) && (BACKLIGHT_PIN == B1 || BACKLIGHT_PIN == B2) | |
108 | # define HARDWARE_PWM | |
109 | # define ICRx ICR1 | |
110 | # define TCCRxA TCCR1A | |
111 | # define TCCRxB TCCR1B | |
112 | # define TIMERx_OVF_vect TIMER1_OVF_vect | |
113 | # define TIMSKx TIMSK1 | |
114 | # define TOIEx TOIE1 | |
115 | ||
116 | # if BACKLIGHT_PIN == B1 | |
26eef35f | 117 | # define COMxx0 COM1A0 |
38aefaf7 JC |
118 | # define COMxx1 COM1A1 |
119 | # define OCRxx OCR1A | |
120 | # elif BACKLIGHT_PIN == B2 | |
26eef35f | 121 | # define COMxx0 COM1B0 |
38aefaf7 JC |
122 | # define COMxx1 COM1B1 |
123 | # define OCRxx OCR1B | |
124 | # endif | |
125 | # else | |
126 | # if !defined(BACKLIGHT_CUSTOM_DRIVER) | |
127 | # if !defined(B5_AUDIO) && !defined(B6_AUDIO) && !defined(B7_AUDIO) | |
128 | // Timer 1 is not in use by Audio feature, Backlight can use it | |
129 | # pragma message "Using hardware timer 1 with software PWM" | |
130 | # define HARDWARE_PWM | |
131 | # define BACKLIGHT_PWM_TIMER | |
132 | # define ICRx ICR1 | |
133 | # define TCCRxA TCCR1A | |
134 | # define TCCRxB TCCR1B | |
135 | # define TIMERx_COMPA_vect TIMER1_COMPA_vect | |
136 | # define TIMERx_OVF_vect TIMER1_OVF_vect | |
137 | # if defined(__AVR_ATmega32A__) // This MCU has only one TIMSK register | |
138 | # define TIMSKx TIMSK | |
139 | # else | |
140 | # define TIMSKx TIMSK1 | |
141 | # endif | |
142 | # define TOIEx TOIE1 | |
143 | ||
144 | # define OCIExA OCIE1A | |
145 | # define OCRxx OCR1A | |
146 | # elif !defined(C6_AUDIO) && !defined(C5_AUDIO) && !defined(C4_AUDIO) | |
147 | # pragma message "Using hardware timer 3 with software PWM" | |
148 | // Timer 3 is not in use by Audio feature, Backlight can use it | |
149 | # define HARDWARE_PWM | |
150 | # define BACKLIGHT_PWM_TIMER | |
151 | # define ICRx ICR1 | |
152 | # define TCCRxA TCCR3A | |
153 | # define TCCRxB TCCR3B | |
154 | # define TIMERx_COMPA_vect TIMER3_COMPA_vect | |
155 | # define TIMERx_OVF_vect TIMER3_OVF_vect | |
156 | # define TIMSKx TIMSK3 | |
157 | # define TOIEx TOIE3 | |
158 | ||
159 | # define OCIExA OCIE3A | |
160 | # define OCRxx OCR3A | |
161 | # else | |
162 | # pragma message "Audio in use - using pure software PWM" | |
163 | # define NO_HARDWARE_PWM | |
164 | # endif | |
165 | # else | |
166 | # pragma message "Custom driver defined - using pure software PWM" | |
167 | # define NO_HARDWARE_PWM | |
168 | # endif | |
169 | # endif | |
170 | ||
171 | # ifndef BACKLIGHT_ON_STATE | |
26eef35f | 172 | # define BACKLIGHT_ON_STATE 1 |
38aefaf7 JC |
173 | # endif |
174 | ||
4531cc87 | 175 | void backlight_on(pin_t backlight_pin) { |
26eef35f | 176 | # if BACKLIGHT_ON_STATE == 1 |
38aefaf7 | 177 | writePinHigh(backlight_pin); |
26eef35f JY |
178 | # else |
179 | writePinLow(backlight_pin); | |
38aefaf7 JC |
180 | # endif |
181 | } | |
182 | ||
4531cc87 | 183 | void backlight_off(pin_t backlight_pin) { |
26eef35f | 184 | # if BACKLIGHT_ON_STATE == 1 |
38aefaf7 | 185 | writePinLow(backlight_pin); |
26eef35f JY |
186 | # else |
187 | writePinHigh(backlight_pin); | |
38aefaf7 JC |
188 | # endif |
189 | } | |
190 | ||
191 | # if defined(NO_HARDWARE_PWM) || defined(BACKLIGHT_PWM_TIMER) // pwm through software | |
192 | ||
193 | // we support multiple backlight pins | |
194 | # ifndef BACKLIGHT_LED_COUNT | |
195 | # define BACKLIGHT_LED_COUNT 1 | |
196 | # endif | |
197 | ||
198 | # if BACKLIGHT_LED_COUNT == 1 | |
199 | # define BACKLIGHT_PIN_INIT \ | |
200 | { BACKLIGHT_PIN } | |
201 | # else | |
202 | # define BACKLIGHT_PIN_INIT BACKLIGHT_PINS | |
203 | # endif | |
204 | ||
205 | # define FOR_EACH_LED(x) \ | |
206 | for (uint8_t i = 0; i < BACKLIGHT_LED_COUNT; i++) { \ | |
a91c0c47 | 207 | pin_t backlight_pin = backlight_pins[i]; \ |
38aefaf7 JC |
208 | { x } \ |
209 | } | |
210 | ||
4531cc87 | 211 | static const pin_t backlight_pins[BACKLIGHT_LED_COUNT] = BACKLIGHT_PIN_INIT; |
38aefaf7 JC |
212 | |
213 | # else // full hardware PWM | |
214 | ||
26eef35f JY |
215 | static inline void enable_pwm(void) { |
216 | # if BACKLIGHT_ON_STATE == 1 | |
217 | TCCRxA |= _BV(COMxx1); | |
218 | # else | |
219 | TCCRxA |= _BV(COMxx1) | _BV(COMxx0); | |
220 | # endif | |
221 | } | |
222 | ||
223 | static inline void disable_pwm(void) { | |
224 | # if BACKLIGHT_ON_STATE == 1 | |
225 | TCCRxA &= ~(_BV(COMxx1)); | |
226 | # else | |
227 | TCCRxA &= ~(_BV(COMxx1) | _BV(COMxx0)); | |
228 | # endif | |
229 | } | |
230 | ||
38aefaf7 | 231 | // we support only one backlight pin |
4531cc87 | 232 | static const pin_t backlight_pin = BACKLIGHT_PIN; |
38aefaf7 JC |
233 | # define FOR_EACH_LED(x) x |
234 | ||
235 | # endif | |
236 | ||
237 | # ifdef NO_HARDWARE_PWM | |
b89e35bd | 238 | void backlight_init_ports(void) { |
38aefaf7 JC |
239 | // Setup backlight pin as output and output to on state. |
240 | FOR_EACH_LED(setPinOutput(backlight_pin); backlight_on(backlight_pin);) | |
241 | ||
242 | # ifdef BACKLIGHT_BREATHING | |
243 | if (is_backlight_breathing()) { | |
244 | breathing_enable(); | |
245 | } | |
246 | # endif | |
247 | } | |
248 | ||
38aefaf7 JC |
249 | uint8_t backlight_tick = 0; |
250 | ||
251 | # ifndef BACKLIGHT_CUSTOM_DRIVER | |
252 | void backlight_task(void) { | |
253 | if ((0xFFFF >> ((BACKLIGHT_LEVELS - get_backlight_level()) * ((BACKLIGHT_LEVELS + 1) / 2))) & (1 << backlight_tick)) { | |
254 | FOR_EACH_LED(backlight_on(backlight_pin);) | |
255 | } else { | |
256 | FOR_EACH_LED(backlight_off(backlight_pin);) | |
257 | } | |
258 | backlight_tick = (backlight_tick + 1) % 16; | |
259 | } | |
260 | # endif | |
261 | ||
262 | # ifdef BACKLIGHT_BREATHING | |
263 | # ifndef BACKLIGHT_CUSTOM_DRIVER | |
264 | # error "Backlight breathing only available with hardware PWM. Please disable." | |
265 | # endif | |
266 | # endif | |
267 | ||
268 | # else // hardware pwm through timer | |
269 | ||
270 | # ifdef BACKLIGHT_PWM_TIMER | |
271 | ||
272 | // The idea of software PWM assisted by hardware timers is the following | |
273 | // we use the hardware timer in fast PWM mode like for hardware PWM, but | |
274 | // instead of letting the Output Match Comparator control the led pin | |
275 | // (which is not possible since the backlight is not wired to PWM pins on the | |
276 | // CPU), we do the LED on/off by oursleves. | |
277 | // The timer is setup to count up to 0xFFFF, and we set the Output Compare | |
278 | // register to the current 16bits backlight level (after CIE correction). | |
279 | // This means the CPU will trigger a compare match interrupt when the counter | |
280 | // reaches the backlight level, where we turn off the LEDs, | |
281 | // but also an overflow interrupt when the counter rolls back to 0, | |
282 | // in which we're going to turn on the LEDs. | |
283 | // The LED will then be on for OCRxx/0xFFFF time, adjusted every 244Hz. | |
284 | ||
285 | // Triggered when the counter reaches the OCRx value | |
286 | ISR(TIMERx_COMPA_vect) { FOR_EACH_LED(backlight_off(backlight_pin);) } | |
287 | ||
288 | // Triggered when the counter reaches the TOP value | |
289 | // this one triggers at F_CPU/65536 =~ 244 Hz | |
290 | ISR(TIMERx_OVF_vect) { | |
291 | # ifdef BACKLIGHT_BREATHING | |
292 | if (is_breathing()) { | |
293 | breathing_task(); | |
294 | } | |
295 | # endif | |
296 | // for very small values of OCRxx (or backlight level) | |
297 | // we can't guarantee this whole code won't execute | |
298 | // at the same time as the compare match interrupt | |
299 | // which means that we might turn on the leds while | |
300 | // trying to turn them off, leading to flickering | |
301 | // artifacts (especially while breathing, because breathing_task | |
302 | // takes many computation cycles). | |
303 | // so better not turn them on while the counter TOP is very low. | |
304 | if (OCRxx > 256) { | |
305 | FOR_EACH_LED(backlight_on(backlight_pin);) | |
306 | } | |
307 | } | |
308 | ||
309 | # endif | |
310 | ||
311 | # define TIMER_TOP 0xFFFFU | |
312 | ||
313 | // See http://jared.geek.nz/2013/feb/linear-led-pwm | |
314 | static uint16_t cie_lightness(uint16_t v) { | |
315 | if (v <= 5243) // if below 8% of max | |
316 | return v / 9; // same as dividing by 900% | |
317 | else { | |
318 | uint32_t y = (((uint32_t)v + 10486) << 8) / (10486 + 0xFFFFUL); // add 16% of max and compare | |
319 | // to get a useful result with integer division, we shift left in the expression above | |
320 | // and revert what we've done again after squaring. | |
321 | y = y * y * y >> 8; | |
322 | if (y > 0xFFFFUL) // prevent overflow | |
323 | return 0xFFFFU; | |
324 | else | |
325 | return (uint16_t)y; | |
326 | } | |
327 | } | |
328 | ||
329 | // range for val is [0..TIMER_TOP]. PWM pin is high while the timer count is below val. | |
330 | static inline void set_pwm(uint16_t val) { OCRxx = val; } | |
331 | ||
332 | # ifndef BACKLIGHT_CUSTOM_DRIVER | |
b89e35bd | 333 | void backlight_set(uint8_t level) { |
38aefaf7 JC |
334 | if (level > BACKLIGHT_LEVELS) level = BACKLIGHT_LEVELS; |
335 | ||
336 | if (level == 0) { | |
337 | # ifdef BACKLIGHT_PWM_TIMER | |
338 | if (OCRxx) { | |
339 | TIMSKx &= ~(_BV(OCIExA)); | |
340 | TIMSKx &= ~(_BV(TOIEx)); | |
38aefaf7 JC |
341 | } |
342 | # else | |
343 | // Turn off PWM control on backlight pin | |
26eef35f | 344 | disable_pwm(); |
38aefaf7 | 345 | # endif |
26eef35f | 346 | FOR_EACH_LED(backlight_off(backlight_pin);) |
38aefaf7 JC |
347 | } else { |
348 | # ifdef BACKLIGHT_PWM_TIMER | |
349 | if (!OCRxx) { | |
350 | TIMSKx |= _BV(OCIExA); | |
351 | TIMSKx |= _BV(TOIEx); | |
352 | } | |
353 | # else | |
354 | // Turn on PWM control of backlight pin | |
26eef35f | 355 | enable_pwm(); |
38aefaf7 JC |
356 | # endif |
357 | } | |
358 | // Set the brightness | |
359 | set_pwm(cie_lightness(TIMER_TOP * (uint32_t)level / BACKLIGHT_LEVELS)); | |
360 | } | |
361 | ||
362 | void backlight_task(void) {} | |
363 | # endif // BACKLIGHT_CUSTOM_DRIVER | |
364 | ||
365 | # ifdef BACKLIGHT_BREATHING | |
366 | ||
367 | # define BREATHING_NO_HALT 0 | |
368 | # define BREATHING_HALT_OFF 1 | |
369 | # define BREATHING_HALT_ON 2 | |
370 | # define BREATHING_STEPS 128 | |
371 | ||
38aefaf7 JC |
372 | static uint8_t breathing_halt = BREATHING_NO_HALT; |
373 | static uint16_t breathing_counter = 0; | |
374 | ||
375 | # ifdef BACKLIGHT_PWM_TIMER | |
376 | static bool breathing = false; | |
377 | ||
378 | bool is_breathing(void) { return breathing; } | |
379 | ||
380 | # define breathing_interrupt_enable() \ | |
381 | do { \ | |
382 | breathing = true; \ | |
383 | } while (0) | |
384 | # define breathing_interrupt_disable() \ | |
385 | do { \ | |
386 | breathing = false; \ | |
387 | } while (0) | |
388 | # else | |
389 | ||
390 | bool is_breathing(void) { return !!(TIMSKx & _BV(TOIEx)); } | |
391 | ||
392 | # define breathing_interrupt_enable() \ | |
393 | do { \ | |
394 | TIMSKx |= _BV(TOIEx); \ | |
395 | } while (0) | |
396 | # define breathing_interrupt_disable() \ | |
397 | do { \ | |
398 | TIMSKx &= ~_BV(TOIEx); \ | |
399 | } while (0) | |
400 | # endif | |
401 | ||
402 | # define breathing_min() \ | |
403 | do { \ | |
404 | breathing_counter = 0; \ | |
405 | } while (0) | |
b89e35bd JC |
406 | # define breathing_max() \ |
407 | do { \ | |
408 | breathing_counter = get_breathing_period() * 244 / 2; \ | |
38aefaf7 JC |
409 | } while (0) |
410 | ||
411 | void breathing_enable(void) { | |
412 | breathing_counter = 0; | |
413 | breathing_halt = BREATHING_NO_HALT; | |
414 | breathing_interrupt_enable(); | |
415 | } | |
416 | ||
417 | void breathing_pulse(void) { | |
418 | if (get_backlight_level() == 0) | |
419 | breathing_min(); | |
420 | else | |
421 | breathing_max(); | |
422 | breathing_halt = BREATHING_HALT_ON; | |
423 | breathing_interrupt_enable(); | |
424 | } | |
425 | ||
426 | void breathing_disable(void) { | |
427 | breathing_interrupt_disable(); | |
428 | // Restore backlight level | |
429 | backlight_set(get_backlight_level()); | |
430 | } | |
431 | ||
432 | void breathing_self_disable(void) { | |
433 | if (get_backlight_level() == 0) | |
434 | breathing_halt = BREATHING_HALT_OFF; | |
435 | else | |
436 | breathing_halt = BREATHING_HALT_ON; | |
437 | } | |
438 | ||
439 | void breathing_toggle(void) { | |
440 | if (is_breathing()) | |
441 | breathing_disable(); | |
442 | else | |
443 | breathing_enable(); | |
444 | } | |
445 | ||
38aefaf7 JC |
446 | /* To generate breathing curve in python: |
447 | * from math import sin, pi; [int(sin(x/128.0*pi)**4*255) for x in range(128)] | |
448 | */ | |
449 | static const uint8_t breathing_table[BREATHING_STEPS] PROGMEM = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 17, 20, 24, 28, 32, 36, 41, 46, 51, 57, 63, 70, 76, 83, 91, 98, 106, 113, 121, 129, 138, 146, 154, 162, 170, 178, 185, 193, 200, 207, 213, 220, 225, 231, 235, 240, 244, 247, 250, 252, 253, 254, 255, 254, 253, 252, 250, 247, 244, 240, 235, 231, 225, 220, 213, 207, 200, 193, 185, 178, 170, 162, 154, 146, 138, 129, 121, 113, 106, 98, 91, 83, 76, 70, 63, 57, 51, 46, 41, 36, 32, 28, 24, 20, 17, 15, 12, 10, 8, 6, 5, 4, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; | |
450 | ||
451 | // Use this before the cie_lightness function. | |
452 | static inline uint16_t scale_backlight(uint16_t v) { return v / BACKLIGHT_LEVELS * get_backlight_level(); } | |
453 | ||
454 | # ifdef BACKLIGHT_PWM_TIMER | |
455 | void breathing_task(void) | |
456 | # else | |
457 | /* Assuming a 16MHz CPU clock and a timer that resets at 64k (ICR1), the following interrupt handler will run | |
458 | * about 244 times per second. | |
459 | */ | |
460 | ISR(TIMERx_OVF_vect) | |
461 | # endif | |
462 | { | |
b89e35bd | 463 | uint8_t breathing_period = get_breathing_period(); |
38aefaf7 JC |
464 | uint16_t interval = (uint16_t)breathing_period * 244 / BREATHING_STEPS; |
465 | // resetting after one period to prevent ugly reset at overflow. | |
466 | breathing_counter = (breathing_counter + 1) % (breathing_period * 244); | |
467 | uint8_t index = breathing_counter / interval % BREATHING_STEPS; | |
468 | ||
469 | if (((breathing_halt == BREATHING_HALT_ON) && (index == BREATHING_STEPS / 2)) || ((breathing_halt == BREATHING_HALT_OFF) && (index == BREATHING_STEPS - 1))) { | |
470 | breathing_interrupt_disable(); | |
471 | } | |
472 | ||
473 | set_pwm(cie_lightness(scale_backlight((uint16_t)pgm_read_byte(&breathing_table[index]) * 0x0101U))); | |
474 | } | |
475 | ||
476 | # endif // BACKLIGHT_BREATHING | |
477 | ||
b89e35bd | 478 | void backlight_init_ports(void) { |
38aefaf7 JC |
479 | // Setup backlight pin as output and output to on state. |
480 | FOR_EACH_LED(setPinOutput(backlight_pin); backlight_on(backlight_pin);) | |
481 | ||
482 | // I could write a wall of text here to explain... but TL;DW | |
483 | // Go read the ATmega32u4 datasheet. | |
484 | // And this: http://blog.saikoled.com/post/43165849837/secret-konami-cheat-code-to-high-resolution-pwm-on | |
485 | ||
486 | # ifdef BACKLIGHT_PWM_TIMER | |
487 | // TimerX setup, Fast PWM mode count to TOP set in ICRx | |
488 | TCCRxA = _BV(WGM11); // = 0b00000010; | |
489 | // clock select clk/1 | |
490 | TCCRxB = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // = 0b00011001; | |
491 | # else // hardware PWM | |
492 | // Pin PB7 = OCR1C (Timer 1, Channel C) | |
493 | // Compare Output Mode = Clear on compare match, Channel C = COM1C1=1 COM1C0=0 | |
494 | // (i.e. start high, go low when counter matches.) | |
495 | // WGM Mode 14 (Fast PWM) = WGM13=1 WGM12=1 WGM11=1 WGM10=0 | |
496 | // Clock Select = clk/1 (no prescaling) = CS12=0 CS11=0 CS10=1 | |
497 | ||
498 | /* | |
499 | 14.8.3: | |
500 | "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 [..]." | |
501 | "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)." | |
502 | */ | |
26eef35f JY |
503 | # if BACKLIGHT_ON_STATE == 1 |
504 | TCCRxA = _BV(COMxx1) | _BV(WGM11); | |
505 | # else | |
506 | TCCRxA = _BV(COMxx1) | _BV(COMxx0) | _BV(WGM11); | |
507 | # endif | |
508 | ||
509 | TCCRxB = _BV(WGM13) | _BV(WGM12) | _BV(CS10); | |
38aefaf7 JC |
510 | # endif |
511 | // Use full 16-bit resolution. Counter counts to ICR1 before reset to 0. | |
512 | ICRx = TIMER_TOP; | |
513 | ||
514 | backlight_init(); | |
515 | # ifdef BACKLIGHT_BREATHING | |
516 | if (is_backlight_breathing()) { | |
517 | breathing_enable(); | |
518 | } | |
519 | # endif | |
520 | } | |
521 | ||
522 | # endif // hardware backlight | |
523 | ||
b89e35bd | 524 | #endif // backlight |