diff options
Diffstat (limited to 'tmk_core/common/sleep_led.c')
-rw-r--r-- | tmk_core/common/sleep_led.c | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/tmk_core/common/sleep_led.c b/tmk_core/common/sleep_led.c new file mode 100644 index 000000000..dab3eb0f3 --- /dev/null +++ b/tmk_core/common/sleep_led.c | |||
@@ -0,0 +1,95 @@ | |||
1 | #include <stdint.h> | ||
2 | #include <avr/io.h> | ||
3 | #include <avr/interrupt.h> | ||
4 | #include <avr/pgmspace.h> | ||
5 | #include "led.h" | ||
6 | #include "sleep_led.h" | ||
7 | |||
8 | /* Software PWM | ||
9 | * ______ ______ __ | ||
10 | * | ON |___OFF___| ON |___OFF___| .... | ||
11 | * |<-------------->|<-------------->|<- .... | ||
12 | * PWM period PWM period | ||
13 | * | ||
14 | * 256 interrupts/period[resolution] | ||
15 | * 64 periods/second[frequency] | ||
16 | * 256*64 interrupts/second | ||
17 | * F_CPU/(256*64) clocks/interrupt | ||
18 | */ | ||
19 | #define SLEEP_LED_TIMER_TOP F_CPU/(256*64) | ||
20 | |||
21 | void sleep_led_init(void) | ||
22 | { | ||
23 | /* Timer1 setup */ | ||
24 | /* CTC mode */ | ||
25 | TCCR1B |= _BV(WGM12); | ||
26 | /* Clock selelct: clk/1 */ | ||
27 | TCCR1B |= _BV(CS10); | ||
28 | /* Set TOP value */ | ||
29 | uint8_t sreg = SREG; | ||
30 | cli(); | ||
31 | OCR1AH = (SLEEP_LED_TIMER_TOP>>8)&0xff; | ||
32 | OCR1AL = SLEEP_LED_TIMER_TOP&0xff; | ||
33 | SREG = sreg; | ||
34 | } | ||
35 | |||
36 | void sleep_led_enable(void) | ||
37 | { | ||
38 | /* Enable Compare Match Interrupt */ | ||
39 | TIMSK1 |= _BV(OCIE1A); | ||
40 | } | ||
41 | |||
42 | void sleep_led_disable(void) | ||
43 | { | ||
44 | /* Disable Compare Match Interrupt */ | ||
45 | TIMSK1 &= ~_BV(OCIE1A); | ||
46 | } | ||
47 | |||
48 | void sleep_led_toggle(void) | ||
49 | { | ||
50 | /* Disable Compare Match Interrupt */ | ||
51 | TIMSK1 ^= _BV(OCIE1A); | ||
52 | } | ||
53 | |||
54 | |||
55 | /* Breathing Sleep LED brighness(PWM On period) table | ||
56 | * (64[steps] * 4[duration]) / 64[PWM periods/s] = 4 second breath cycle | ||
57 | * | ||
58 | * http://www.wolframalpha.com/input/?i=%28sin%28+x%2F64*pi%29**8+*+255%2C+x%3D0+to+63 | ||
59 | * (0..63).each {|x| p ((sin(x/64.0*PI)**8)*255).to_i } | ||
60 | */ | ||
61 | static const uint8_t breathing_table[64] PROGMEM = { | ||
62 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 6, 10, | ||
63 | 15, 23, 32, 44, 58, 74, 93, 113, 135, 157, 179, 199, 218, 233, 245, 252, | ||
64 | 255, 252, 245, 233, 218, 199, 179, 157, 135, 113, 93, 74, 58, 44, 32, 23, | ||
65 | 15, 10, 6, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 | ||
66 | }; | ||
67 | |||
68 | ISR(TIMER1_COMPA_vect) | ||
69 | { | ||
70 | /* Software PWM | ||
71 | * timer:1111 1111 1111 1111 | ||
72 | * \_____/\/ \_______/____ count(0-255) | ||
73 | * \ \______________ duration of step(4) | ||
74 | * \__________________ index of step table(0-63) | ||
75 | */ | ||
76 | static union { | ||
77 | uint16_t row; | ||
78 | struct { | ||
79 | uint8_t count:8; | ||
80 | uint8_t duration:2; | ||
81 | uint8_t index:6; | ||
82 | } pwm; | ||
83 | } timer = { .row = 0 }; | ||
84 | |||
85 | timer.row++; | ||
86 | |||
87 | // LED on | ||
88 | if (timer.pwm.count == 0) { | ||
89 | led_set(1<<USB_LED_CAPS_LOCK); | ||
90 | } | ||
91 | // LED off | ||
92 | if (timer.pwm.count == pgm_read_byte(&breathing_table[timer.pwm.index])) { | ||
93 | led_set(0); | ||
94 | } | ||
95 | } | ||