]> git.donarmstrong.com Git - qmk_firmware.git/blob - tmk_core/protocol/arm_atsam/led_matrix.c
Merge branch 'master' of https://github.com/qmk/qmk_firmware
[qmk_firmware.git] / tmk_core / protocol / arm_atsam / led_matrix.c
1 /*
2 Copyright 2018 Massdrop Inc.
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "arm_atsam_protocol.h"
19 #include "tmk_core/common/led.h"
20 #include <string.h>
21
22 void SERCOM1_0_Handler( void )
23 {
24     if (SERCOM1->I2CM.INTFLAG.bit.ERROR)
25     {
26         SERCOM1->I2CM.INTFLAG.reg = SERCOM_I2CM_INTENCLR_ERROR;
27     }
28 }
29
30 void DMAC_0_Handler( void )
31 {
32     if (DMAC->Channel[0].CHINTFLAG.bit.TCMPL)
33     {
34         DMAC->Channel[0].CHINTFLAG.reg = DMAC_CHINTENCLR_TCMPL;
35
36         i2c1_stop();
37
38         i2c_led_q_running = 0;
39
40         i2c_led_q_run();
41
42         return;
43     }
44
45     if (DMAC->Channel[0].CHINTFLAG.bit.TERR)
46     {
47         DMAC->Channel[0].CHINTFLAG.reg = DMAC_CHINTENCLR_TERR;
48     }
49 }
50
51 issi3733_driver_t issidrv[ISSI3733_DRIVER_COUNT];
52
53 issi3733_led_t led_map[ISSI3733_LED_COUNT+1] = ISSI3733_LED_MAP;
54 issi3733_led_t *lede = led_map + ISSI3733_LED_COUNT; //End pointer of mapping
55
56 uint8_t gcr_desired;
57 uint8_t gcr_breathe;
58 uint8_t gcr_use;
59 uint8_t gcr_actual;
60 uint8_t gcr_actual_last;
61
62 #define ACT_GCR_NONE    0
63 #define ACT_GCR_INC     1
64 #define ACT_GCR_DEC     2
65
66 #define LED_GCR_STEP_AUTO 2
67
68 static uint8_t gcr_min_counter;
69 static uint8_t v_5v_cat_hit;
70
71 //WARNING: Automatic GCR is in place to prevent USB shutdown and LED driver overloading
72 void gcr_compute(void)
73 {
74     uint8_t action = ACT_GCR_NONE;
75
76     if (led_animation_breathing)
77         gcr_use = gcr_breathe;
78     else
79         gcr_use = gcr_desired;
80
81     //If the 5v takes a catastrophic hit, disable the LED drivers briefly, assert auto gcr mode, min gcr and let the auto take over
82     if (v_5v < V5_CAT)
83     {
84         I2C3733_Control_Set(0);
85         //CDC_print("USB: WARNING: 5V catastrophic level reached! Disabling LED drivers!\r\n"); //Blocking print is bad here!
86         v_5v_cat_hit = 20; //~100ms recover
87         gcr_actual = 0; //Minimize GCR
88         usb_gcr_auto = 1; //Force auto mode enabled
89         return;
90     }
91     else if (v_5v_cat_hit > 1)
92     {
93         v_5v_cat_hit--;
94         return;
95     }
96     else if (v_5v_cat_hit == 1)
97     {
98         I2C3733_Control_Set(1);
99         CDC_print("USB: WARNING: Re-enabling LED drivers\r\n");
100         v_5v_cat_hit = 0;
101         return;
102     }
103
104     if (usb_gcr_auto)
105     {
106         if (v_5v_avg < V5_LOW) action = ACT_GCR_DEC;
107         else if (v_5v_avg > V5_HIGH && gcr_actual < gcr_use) action = ACT_GCR_INC;
108         else if (gcr_actual > gcr_use) action = ACT_GCR_DEC;
109     }
110     else
111     {
112         if (gcr_actual < gcr_use) action = ACT_GCR_INC;
113         else if (gcr_actual > gcr_use) action = ACT_GCR_DEC;
114     }
115
116     if (action == ACT_GCR_NONE)
117     {
118         gcr_min_counter = 0;
119     }
120     else if (action == ACT_GCR_INC)
121     {
122         if (LED_GCR_STEP_AUTO > LED_GCR_MAX - gcr_actual) gcr_actual = LED_GCR_MAX; //Obey max and prevent wrapping
123         else gcr_actual += LED_GCR_STEP_AUTO;
124         gcr_min_counter = 0;
125     }
126     else if (action == ACT_GCR_DEC)
127     {
128         if (LED_GCR_STEP_AUTO > gcr_actual) //Prevent wrapping
129         {
130             gcr_actual = 0;
131             //At this point, power can no longer be cut from the LED drivers, so focus on cutting out extra port if active
132             if (usb_extra_state != USB_EXTRA_STATE_DISABLED_UNTIL_REPLUG) //If not in a wait for replug state
133             {
134                 if (usb_extra_state == USB_EXTRA_STATE_ENABLED) //If extra usb is enabled
135                 {
136                     gcr_min_counter++;
137                     if (gcr_min_counter > 200) //5ms per check = 1s delay
138                     {
139                         USB_ExtraSetState(USB_EXTRA_STATE_DISABLED_UNTIL_REPLUG);
140                         usb_extra_manual = 0; //Force disable manual mode of extra port
141                         if (usb_extra_manual) CDC_print("USB: Disabling extra port until replug and manual mode toggle!\r\n");
142                         else CDC_print("USB: Disabling extra port until replug!\r\n");
143                     }
144                 }
145             }
146         }
147         else
148         {
149             //Power successfully cut back from LED drivers
150             gcr_actual -= LED_GCR_STEP_AUTO;
151             gcr_min_counter = 0;
152
153             //If breathe mode is active, the top end can fluctuate if the host can not supply enough current
154             //So set the breathe GCR to where it becomes stable
155             if (led_animation_breathing == 1)
156             {
157                 gcr_breathe = gcr_actual;
158                 //PS: At this point, setting breathing to exhale makes a noticebly shorter cycle
159                 //    and the same would happen maybe one or two more times. Therefore I'm favoring
160                 //    powering through one full breathe and letting gcr settle completely
161             }
162         }
163     }
164 }
165
166 led_disp_t disp;
167
168 void issi3733_prepare_arrays(void)
169 {
170     memset(issidrv,0,sizeof(issi3733_driver_t) * ISSI3733_DRIVER_COUNT);
171
172     int i;
173     uint8_t addrs[ISSI3733_DRIVER_COUNT] = ISSI3773_DRIVER_ADDRESSES;
174
175     for (i=0;i<ISSI3733_DRIVER_COUNT;i++)
176     {
177         issidrv[i].addr = addrs[i];
178     }
179
180     issi3733_led_t *cur = led_map;
181
182     while (cur < lede)
183     {
184         //BYTE: 1 + (SW-1)*16 + (CS-1)
185         cur->rgb.g = issidrv[cur->adr.drv-1].pwm + 1 + ((cur->adr.swg-1)*16 + (cur->adr.cs-1));
186         cur->rgb.r = issidrv[cur->adr.drv-1].pwm + 1 + ((cur->adr.swr-1)*16 + (cur->adr.cs-1));
187         cur->rgb.b = issidrv[cur->adr.drv-1].pwm + 1 + ((cur->adr.swb-1)*16 + (cur->adr.cs-1));
188
189         //BYTE: 1 + (SW-1)*2 + (CS-1)/8
190         //BIT: (CS-1)%8
191         *(issidrv[cur->adr.drv-1].onoff + 1 + (cur->adr.swg-1)*2+(cur->adr.cs-1)/8) |= (1<<((cur->adr.cs-1)%8));
192         *(issidrv[cur->adr.drv-1].onoff + 1 + (cur->adr.swr-1)*2+(cur->adr.cs-1)/8) |= (1<<((cur->adr.cs-1)%8));
193         *(issidrv[cur->adr.drv-1].onoff + 1 + (cur->adr.swb-1)*2+(cur->adr.cs-1)/8) |= (1<<((cur->adr.cs-1)%8));
194
195         cur++;
196     }
197 }
198
199 void disp_calc_extents(void)
200 {
201     issi3733_led_t *cur = led_map;
202
203     disp.left = 1e10;
204     disp.right = -1e10;
205     disp.top = -1e10;
206     disp.bottom = 1e10;
207
208     while (cur < lede)
209     {
210         if (cur->x < disp.left) disp.left = cur->x;
211         if (cur->x > disp.right) disp.right = cur->x;
212         if (cur->y < disp.bottom) disp.bottom = cur->y;
213         if (cur->y > disp.top) disp.top = cur->y;
214
215         cur++;
216     }
217
218     disp.width = disp.right - disp.left;
219     disp.height = disp.top - disp.bottom;
220 }
221
222 void disp_pixel_setup(void)
223 {
224     issi3733_led_t *cur = led_map;
225
226     while (cur < lede)
227     {
228         cur->px = (cur->x - disp.left) / disp.width * 100;
229         cur->py = (cur->y - disp.bottom) / disp.height * 100;
230         *cur->rgb.r = 0;
231         *cur->rgb.g = 0;
232         *cur->rgb.b = 0;
233
234         cur++;
235     }
236 }
237
238 void led_matrix_prepare(void)
239 {
240     disp_calc_extents();
241     disp_pixel_setup();
242 }
243
244 uint8_t led_enabled;
245 float led_animation_speed;
246 uint8_t led_animation_direction;
247 uint8_t led_animation_orientation;
248 uint8_t led_animation_breathing;
249 uint8_t led_animation_breathe_cur;
250 uint8_t breathe_step;
251 uint8_t breathe_dir;
252 uint64_t led_next_run;
253
254 uint8_t led_animation_id;
255 uint8_t led_lighting_mode;
256
257 issi3733_led_t *led_cur;
258 uint8_t led_per_run = 15;
259 float breathe_mult;
260
261 __attribute__ ((weak))
262 void led_matrix_run(void)
263 {
264     float ro;
265     float go;
266     float bo;
267     float po;
268     uint8_t led_this_run = 0;
269     led_setup_t *f = (led_setup_t*)led_setups[led_animation_id];
270
271     if (led_cur == 0) //Denotes start of new processing cycle in the case of chunked processing
272     {
273         led_cur = led_map;
274
275         disp.frame += 1;
276
277         breathe_mult = 1;
278
279         if (led_animation_breathing)
280         {
281             led_animation_breathe_cur += breathe_step * breathe_dir;
282
283             if (led_animation_breathe_cur >= BREATHE_MAX_STEP)
284                 breathe_dir = -1;
285             else if (led_animation_breathe_cur <= BREATHE_MIN_STEP)
286                 breathe_dir = 1;
287
288             //Brightness curve created for 256 steps, 0 - ~98%
289             breathe_mult = 0.000015 * led_animation_breathe_cur * led_animation_breathe_cur;
290             if (breathe_mult > 1) breathe_mult = 1;
291             else if (breathe_mult < 0) breathe_mult = 0;
292         }
293     }
294
295     uint8_t fcur = 0;
296     uint8_t fmax = 0;
297
298     //Frames setup
299     while (f[fcur].end != 1)
300     {
301         fcur++; //Count frames
302     }
303
304     fmax = fcur; //Store total frames count
305
306     while (led_cur < lede && led_this_run < led_per_run)
307     {
308         ro = 0;
309         go = 0;
310         bo = 0;
311
312         if (led_lighting_mode == LED_MODE_KEYS_ONLY && led_cur->scan == 255)
313         {
314             //Do not act on this LED
315         }
316         else if (led_lighting_mode == LED_MODE_NON_KEYS_ONLY && led_cur->scan != 255)
317         {
318             //Do not act on this LED
319         }
320         else if (led_lighting_mode == LED_MODE_INDICATORS_ONLY)
321         {
322             //Do not act on this LED (Only show indicators)
323         }
324         else
325         {
326             //Act on LED
327             for (fcur = 0; fcur < fmax; fcur++)
328             {
329
330                 if (led_animation_orientation)
331                 {
332                   po = led_cur->py;
333                 }
334                 else
335                 {
336                   po = led_cur->px;
337                 }
338
339                 float pomod;
340                 pomod = (float)(disp.frame % (uint32_t)(1000.0f / led_animation_speed)) / 10.0f * led_animation_speed;
341
342                 //Add in any moving effects
343                 if ((!led_animation_direction && f[fcur].ef & EF_SCR_R) || (led_animation_direction && (f[fcur].ef & EF_SCR_L)))
344                 {
345                     pomod *= 100.0f;
346                     pomod = (uint32_t)pomod % 10000;
347                     pomod /= 100.0f;
348
349                     po -= pomod;
350
351                     if (po > 100) po -= 100;
352                     else if (po < 0) po += 100;
353                 }
354                 else if ((!led_animation_direction && f[fcur].ef & EF_SCR_L) || (led_animation_direction && (f[fcur].ef & EF_SCR_R)))
355                 {
356                     pomod *= 100.0f;
357                     pomod = (uint32_t)pomod % 10000;
358                     pomod /= 100.0f;
359                     po += pomod;
360
361                     if (po > 100) po -= 100;
362                     else if (po < 0) po += 100;
363                 }
364
365                 //Check if LED's po is in current frame
366                 if (po < f[fcur].hs) continue;
367                 if (po > f[fcur].he) continue;
368                 //note: < 0 or > 100 continue
369
370                 //Calculate the po within the start-stop percentage for color blending
371                 po = (po - f[fcur].hs) / (f[fcur].he - f[fcur].hs);
372
373                 //Add in any color effects
374                 if (f[fcur].ef & EF_OVER)
375                 {
376                     ro = (po * (f[fcur].re - f[fcur].rs)) + f[fcur].rs;// + 0.5;
377                     go = (po * (f[fcur].ge - f[fcur].gs)) + f[fcur].gs;// + 0.5;
378                     bo = (po * (f[fcur].be - f[fcur].bs)) + f[fcur].bs;// + 0.5;
379                 }
380                 else if (f[fcur].ef & EF_SUBTRACT)
381                 {
382                     ro -= (po * (f[fcur].re - f[fcur].rs)) + f[fcur].rs;// + 0.5;
383                     go -= (po * (f[fcur].ge - f[fcur].gs)) + f[fcur].gs;// + 0.5;
384                     bo -= (po * (f[fcur].be - f[fcur].bs)) + f[fcur].bs;// + 0.5;
385                 }
386                 else
387                 {
388                     ro += (po * (f[fcur].re - f[fcur].rs)) + f[fcur].rs;// + 0.5;
389                     go += (po * (f[fcur].ge - f[fcur].gs)) + f[fcur].gs;// + 0.5;
390                     bo += (po * (f[fcur].be - f[fcur].bs)) + f[fcur].bs;// + 0.5;
391                 }
392             }
393         }
394
395         //Clamp values 0-255
396         if (ro > 255) ro = 255; else if (ro < 0) ro = 0;
397         if (go > 255) go = 255; else if (go < 0) go = 0;
398         if (bo > 255) bo = 255; else if (bo < 0) bo = 0;
399
400         if (led_animation_breathing)
401         {
402             ro *= breathe_mult;
403             go *= breathe_mult;
404             bo *= breathe_mult;
405         }
406
407         *led_cur->rgb.r = (uint8_t)ro;
408         *led_cur->rgb.g = (uint8_t)go;
409         *led_cur->rgb.b = (uint8_t)bo;
410
411 #ifdef USB_LED_INDICATOR_ENABLE
412         if (keyboard_leds())
413         {
414             uint8_t kbled = keyboard_leds();
415             if (
416                 #if USB_LED_NUM_LOCK_SCANCODE != 255
417                 (led_cur->scan == USB_LED_NUM_LOCK_SCANCODE && kbled & (1<<USB_LED_NUM_LOCK)) ||
418                 #endif //NUM LOCK
419                 #if USB_LED_CAPS_LOCK_SCANCODE != 255
420                 (led_cur->scan == USB_LED_CAPS_LOCK_SCANCODE && kbled & (1<<USB_LED_CAPS_LOCK)) ||
421                 #endif //CAPS LOCK
422                 #if USB_LED_SCROLL_LOCK_SCANCODE != 255
423                 (led_cur->scan == USB_LED_SCROLL_LOCK_SCANCODE && kbled & (1<<USB_LED_SCROLL_LOCK)) ||
424                 #endif //SCROLL LOCK
425                 #if USB_LED_COMPOSE_SCANCODE != 255
426                 (led_cur->scan == USB_LED_COMPOSE_SCANCODE && kbled & (1<<USB_LED_COMPOSE)) ||
427                 #endif //COMPOSE
428                 #if USB_LED_KANA_SCANCODE != 255
429                 (led_cur->scan == USB_LED_KANA_SCANCODE && kbled & (1<<USB_LED_KANA)) ||
430                 #endif //KANA
431                 (0))
432             {
433                 if (*led_cur->rgb.r > 127) *led_cur->rgb.r = 0;
434                 else *led_cur->rgb.r = 255;
435                 if (*led_cur->rgb.g > 127) *led_cur->rgb.g = 0;
436                 else *led_cur->rgb.g = 255;
437                 if (*led_cur->rgb.b > 127) *led_cur->rgb.b = 0;
438                 else *led_cur->rgb.b = 255;
439             }
440         }
441 #endif //USB_LED_INDICATOR_ENABLE
442
443         led_cur++;
444         led_this_run++;
445     }
446 }
447
448 uint8_t led_matrix_init(void)
449 {
450     DBGC(DC_LED_MATRIX_INIT_BEGIN);
451
452     issi3733_prepare_arrays();
453
454     led_matrix_prepare();
455
456     disp.frame = 0;
457     led_next_run = 0;
458
459     led_enabled = 1;
460     led_animation_id = 0;
461     led_lighting_mode = LED_MODE_NORMAL;
462     led_animation_speed = 4.0f;
463     led_animation_direction = 0;
464     led_animation_orientation = 0;
465     led_animation_breathing = 0;
466     led_animation_breathe_cur = BREATHE_MIN_STEP;
467     breathe_step = 1;
468     breathe_dir = 1;
469
470     gcr_min_counter = 0;
471     v_5v_cat_hit = 0;
472
473     //Run led matrix code once for initial LED coloring
474     led_cur = 0;
475     rgb_matrix_init_user();
476     led_matrix_run();
477
478     DBGC(DC_LED_MATRIX_INIT_COMPLETE);
479
480     return 0;
481 }
482
483 __attribute__ ((weak))
484 void rgb_matrix_init_user(void) {
485
486 }
487
488 #define LED_UPDATE_RATE 10  //ms
489
490 //led data processing can take time, so process data in chunks to free up the processor
491 //this is done through led_cur and lede
492 void led_matrix_task(void)
493 {
494     if (led_enabled)
495     {
496         //If an update may run and frame processing has completed
497         if (CLK_get_ms() >= led_next_run && led_cur == lede)
498         {
499             uint8_t drvid;
500
501             led_next_run = CLK_get_ms() + LED_UPDATE_RATE;  //Set next frame update time
502
503             //NOTE: GCR does not need to be timed with LED processing, but there is really no harm
504             if (gcr_actual != gcr_actual_last)
505             {
506                 for (drvid=0;drvid<ISSI3733_DRIVER_COUNT;drvid++)
507                     I2C_LED_Q_GCR(drvid); //Queue data
508                 gcr_actual_last = gcr_actual;
509             }
510
511             for (drvid=0;drvid<ISSI3733_DRIVER_COUNT;drvid++)
512                 I2C_LED_Q_PWM(drvid); //Queue data
513
514             i2c_led_q_run();
515
516             led_cur = 0; //Signal next frame calculations may begin
517         }
518     }
519
520     //Process more data if not finished
521     if (led_cur != lede)
522     {
523         //DBG_1_OFF; //debug profiling
524         led_matrix_run();
525         //DBG_1_ON; //debug profiling
526     }
527 }
528