]> git.donarmstrong.com Git - qmk_firmware.git/blob - keyboard/atomic/atomic.c
Backlight Breathing for Planck and Atomic
[qmk_firmware.git] / keyboard / atomic / atomic.c
1 #include "atomic.h"
2
3 __attribute__ ((weak))
4 void matrix_init_user(void) {
5     // leave this function blank - it can be defined in a keymap file
6 };
7
8 __attribute__ ((weak))
9 void matrix_scan_user(void) {
10     // leave this function blank - it can be defined in a keymap file
11 }
12
13 __attribute__ ((weak))
14 void process_action_user(keyrecord_t *record) {
15     // leave this function blank - it can be defined in a keymap file
16 }
17
18 __attribute__ ((weak))
19 void led_set_user(uint8_t usb_led) {
20     // leave this function blank - it can be defined in a keymap file
21 }
22
23 void matrix_init_kb(void) {
24     // put your keyboard start-up code here
25     // runs once when the firmware starts up
26
27     MCUCR |= (1<<JTD);
28     MCUCR |= (1<<JTD);
29
30 #ifdef BACKLIGHT_ENABLE
31     backlight_init_ports();
32 #endif
33
34     // Turn status LED on
35     DDRE |= (1<<6);
36     PORTE |= (1<<6);
37
38     matrix_init_user();
39 }
40
41 void matrix_scan_kb(void) {
42     // put your looping keyboard code here
43     // runs every cycle (a lot)
44
45     matrix_scan_user();
46 }
47
48 void process_action_kb(keyrecord_t *record) {
49     // put your per-action keyboard code here
50     // runs for every action, just before processing by the firmware
51
52     process_action_user(record);
53 }
54
55 void led_set_kb(uint8_t usb_led) {
56     // put your keyboard LED indicator (ex: Caps Lock LED) toggling code here
57
58     led_set_user(usb_led);
59 }
60
61 #ifdef BACKLIGHT_ENABLE
62 #define CHANNEL OCR1C
63 #define BREATHING_NO_HALT  0
64 #define BREATHING_HALT_OFF 1
65 #define BREATHING_HALT_ON  2
66
67 static uint8_t breath_intensity;
68 static uint8_t breath_speed;
69 static uint16_t breathing_index;
70 static uint8_t breathing_halt;
71
72 void backlight_init_ports()
73 {
74
75     // Setup PB7 as output and output low.
76     DDRB |= (1<<7);
77     PORTB &= ~(1<<7);
78
79     // Use full 16-bit resolution.
80     ICR1 = 0xFFFF;
81
82     // I could write a wall of text here to explain... but TL;DW
83     // Go read the ATmega32u4 datasheet.
84     // And this: http://blog.saikoled.com/post/43165849837/secret-konami-cheat-code-to-high-resolution-pwm-on
85
86     // Pin PB7 = OCR1C (Timer 1, Channel C)
87     // Compare Output Mode = Clear on compare match, Channel C = COM1C1=1 COM1C0=0
88     // (i.e. start high, go low when counter matches.)
89     // WGM Mode 14 (Fast PWM) = WGM13=1 WGM12=1 WGM11=1 WGM10=0
90     // Clock Select = clk/1 (no prescaling) = CS12=0 CS11=0 CS10=1
91
92     TCCR1A = _BV(COM1C1) | _BV(WGM11); // = 0b00001010;
93     TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // = 0b00011001;
94
95     backlight_init();
96     breathing_defaults();
97 }
98
99 void backlight_set(uint8_t level)
100 {
101     // Prevent backlight blink on lowest level
102     PORTB &= ~(_BV(PORTB7));
103
104     if ( level == 0 )
105     {
106         // Turn off PWM control on PB7, revert to output low.
107         TCCR1A &= ~(_BV(COM1C1));
108
109         // Set the brightness to 0
110         CHANNEL = 0x0;
111     }
112     else if ( level >= BACKLIGHT_LEVELS )
113     {
114         // Turn on PWM control of PB7
115         TCCR1A |= _BV(COM1C1);
116
117         // Set the brightness to max
118         CHANNEL = 0xFFFF;
119     }
120     else
121     {
122         // Turn on PWM control of PB7
123         TCCR1A |= _BV(COM1C1);
124
125         // Set the brightness
126         CHANNEL = 0xFFFF >> ((BACKLIGHT_LEVELS - level) * ((BACKLIGHT_LEVELS + 1) / 2));
127     }
128     breathing_intensity_default();
129 }
130
131
132 void breathing_enable(void)
133 {
134     if (get_backlight_level() == 0)
135     {
136         breathing_index = 0;
137     }
138     else
139     {
140         // Set breathing_index to be at the midpoint (brightest point)
141         breathing_index = 0x20 << breath_speed;
142     }
143
144     breathing_halt = BREATHING_NO_HALT;
145
146     // Enable breathing interrupt
147     TIMSK1 |= _BV(OCIE1A);
148 }
149
150 void breathing_pulse(void)
151 {
152     if (get_backlight_level() == 0)
153     {
154         breathing_index = 0;
155     }
156     else
157     {
158         // Set breathing_index to be at the midpoint + 1 (brightest point)
159         breathing_index = 0x21 << breath_speed;
160     }
161
162     breathing_halt = BREATHING_HALT_ON;
163
164     // Enable breathing interrupt
165     TIMSK1 |= _BV(OCIE1A);
166 }
167
168 void breathing_disable(void)
169 {
170     // Disable breathing interrupt
171     TIMSK1 &= ~_BV(OCIE1A);
172     backlight_set(get_backlight_level());
173 }
174
175 void breathing_self_disable(void)
176 {
177     if (get_backlight_level() == 0)
178     {
179         breathing_halt = BREATHING_HALT_OFF;
180     }
181     else
182     {
183         breathing_halt = BREATHING_HALT_ON;
184     }
185
186     //backlight_set(get_backlight_level());
187 }
188
189 void breathing_toggle(void)
190 {
191     if (!is_breathing())
192     {
193         if (get_backlight_level() == 0)
194         {
195             breathing_index = 0;
196         }
197         else
198         {
199             // Set breathing_index to be at the midpoint + 1 (brightest point)
200             breathing_index = 0x21 << breath_speed;
201         }
202
203         breathing_halt = BREATHING_NO_HALT;
204     }
205
206     // Toggle breathing interrupt
207     TIMSK1 ^= _BV(OCIE1A);
208
209     // Restore backlight level
210     if (!is_breathing())
211     {
212         backlight_set(get_backlight_level());
213     }
214 }
215
216 bool is_breathing(void)
217 {
218     return (TIMSK1 && _BV(OCIE1A));
219 }
220
221 void breathing_intensity_default(void)
222 {
223     //breath_intensity = (uint8_t)((uint16_t)100 * (uint16_t)get_backlight_level() / (uint16_t)BACKLIGHT_LEVELS);
224     breath_intensity = ((BACKLIGHT_LEVELS - get_backlight_level()) * ((BACKLIGHT_LEVELS + 1) / 2));
225 }
226
227 void breathing_intensity_set(uint8_t value)
228 {
229     breath_intensity = value;
230 }
231
232 void breathing_speed_default(void)
233 {
234     breath_speed = 4;
235 }
236
237 void breathing_speed_set(uint8_t value)
238 {
239     bool is_breathing_now = is_breathing();
240     uint8_t old_breath_speed = breath_speed;
241
242     if (is_breathing_now)
243     {
244         // Disable breathing interrupt
245         TIMSK1 &= ~_BV(OCIE1A);
246     }
247
248     breath_speed = value;
249
250     if (is_breathing_now)
251     {
252         // Adjust index to account for new speed
253         breathing_index = (( (uint8_t)( (breathing_index) >> old_breath_speed ) ) & 0x3F) << breath_speed;
254
255         // Enable breathing interrupt
256         TIMSK1 |= _BV(OCIE1A);
257     }
258
259 }
260
261 void breathing_speed_inc(uint8_t value)
262 {
263     if ((uint16_t)(breath_speed - value) > 10 )
264     {
265         breathing_speed_set(0);
266     }
267     else
268     {
269         breathing_speed_set(breath_speed - value);
270     }
271 }
272
273 void breathing_speed_dec(uint8_t value)
274 {
275     if ((uint16_t)(breath_speed + value) > 10 )
276     {
277         breathing_speed_set(10);
278     }
279     else
280     {
281         breathing_speed_set(breath_speed + value);
282     }
283 }
284
285 void breathing_defaults(void)
286 {
287     breathing_intensity_default();
288     breathing_speed_default();
289     breathing_halt = BREATHING_NO_HALT;
290 }
291
292 /* Breathing Sleep LED brighness(PWM On period) table
293  * (64[steps] * 4[duration]) / 64[PWM periods/s] = 4 second breath cycle
294  *
295  * http://www.wolframalpha.com/input/?i=%28sin%28+x%2F64*pi%29**8+*+255%2C+x%3D0+to+63
296  * (0..63).each {|x| p ((sin(x/64.0*PI)**8)*255).to_i }
297  */
298 static const uint8_t breathing_table[64] PROGMEM = {
299   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   1,   2,   4,   6,  10,
300  15,  23,  32,  44,  58,  74,  93, 113, 135, 157, 179, 199, 218, 233, 245, 252,
301 255, 252, 245, 233, 218, 199, 179, 157, 135, 113,  93,  74,  58,  44,  32,  23,
302  15,  10,   6,   4,   2,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
303 };
304
305 ISR(TIMER1_COMPA_vect)
306 {
307     // CHANNEL = (pgm_read_byte(&breathing_table[ ( (uint8_t)( (breathing_index++) >> breath_speed ) ) & 0x3F ] )) * breath_intensity;
308
309
310     uint8_t local_index = ( (uint8_t)( (breathing_index++) >> breath_speed ) ) & 0x3F;
311
312     if (((breathing_halt == BREATHING_HALT_ON) && (local_index == 0x20)) || ((breathing_halt == BREATHING_HALT_OFF) && (local_index == 0x3F)))
313     {
314         // Disable breathing interrupt
315         TIMSK1 &= ~_BV(OCIE1A);
316     }
317
318     CHANNEL = (uint16_t)(((uint16_t)pgm_read_byte(&breathing_table[local_index]) * 257)) >> breath_intensity;
319
320 }
321
322
323
324 #endif