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