2 Copyright 2018 Massdrop Inc.
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.
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.
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/>.
18 #include "arm_atsam_protocol.h"
19 #include "tmk_core/common/led.h"
23 void SERCOM1_0_Handler( void )
25 if (SERCOM1->I2CM.INTFLAG.bit.ERROR)
27 SERCOM1->I2CM.INTFLAG.reg = SERCOM_I2CM_INTENCLR_ERROR;
31 void DMAC_0_Handler( void )
33 if (DMAC->Channel[0].CHINTFLAG.bit.TCMPL)
35 DMAC->Channel[0].CHINTFLAG.reg = DMAC_CHINTENCLR_TCMPL;
39 i2c_led_q_running = 0;
46 if (DMAC->Channel[0].CHINTFLAG.bit.TERR)
48 DMAC->Channel[0].CHINTFLAG.reg = DMAC_CHINTENCLR_TERR;
52 issi3733_driver_t issidrv[ISSI3733_DRIVER_COUNT];
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
61 uint8_t gcr_actual_last;
63 #define ACT_GCR_NONE 0
67 #define LED_GCR_STEP_AUTO 2
69 static uint8_t gcr_min_counter;
70 static uint8_t v_5v_cat_hit;
72 //WARNING: Automatic GCR is in place to prevent USB shutdown and LED driver overloading
73 void gcr_compute(void)
75 uint8_t action = ACT_GCR_NONE;
77 if (led_animation_breathing)
78 gcr_use = gcr_breathe;
80 gcr_use = gcr_desired;
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
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
92 else if (v_5v_cat_hit > 1)
97 else if (v_5v_cat_hit == 1)
99 I2C3733_Control_Set(1);
100 CDC_print("USB: WARNING: Re-enabling LED drivers\r\n");
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;
113 if (gcr_actual < gcr_use) action = ACT_GCR_INC;
114 else if (gcr_actual > gcr_use) action = ACT_GCR_DEC;
117 if (action == ACT_GCR_NONE)
121 else if (action == ACT_GCR_INC)
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;
127 else if (action == ACT_GCR_DEC)
129 if (LED_GCR_STEP_AUTO > gcr_actual) //Prevent wrapping
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
135 if (usb_extra_state == USB_EXTRA_STATE_ENABLED) //If extra usb is enabled
138 if (gcr_min_counter > 200) //5ms per check = 1s delay
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");
150 //Power successfully cut back from LED drivers
151 gcr_actual -= LED_GCR_STEP_AUTO;
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)
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
169 void issi3733_prepare_arrays(void)
171 memset(issidrv,0,sizeof(issi3733_driver_t) * ISSI3733_DRIVER_COUNT);
174 uint8_t addrs[ISSI3733_DRIVER_COUNT] = ISSI3773_DRIVER_ADDRESSES;
176 for (i=0;i<ISSI3733_DRIVER_COUNT;i++)
178 issidrv[i].addr = addrs[i];
181 issi3733_led_t *cur = led_map;
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));
190 //BYTE: 1 + (SW-1)*2 + (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));
200 void disp_calc_extents(void)
202 issi3733_led_t *cur = led_map;
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;
219 disp.width = disp.right - disp.left;
220 disp.height = disp.top - disp.bottom;
223 void disp_pixel_setup(void)
225 issi3733_led_t *cur = led_map;
229 cur->px = (cur->x - disp.left) / disp.width * 100;
230 cur->py = (cur->y - disp.bottom) / disp.height * 100;
239 void led_matrix_prepare(void)
246 float led_animation_speed;
247 uint8_t led_animation_direction;
248 uint8_t led_animation_orientation;
249 uint8_t led_animation_breathing;
250 uint8_t led_animation_breathe_cur;
251 uint8_t breathe_step;
253 uint8_t led_animation_circular;
254 uint64_t led_next_run;
256 uint8_t led_animation_id;
257 uint8_t led_lighting_mode;
259 issi3733_led_t *led_cur;
260 uint8_t led_per_run = 15;
263 __attribute__ ((weak))
264 void led_matrix_run(void)
270 uint8_t led_this_run = 0;
271 led_setup_t *f = (led_setup_t*)led_setups[led_animation_id];
273 if (led_cur == 0) //Denotes start of new processing cycle in the case of chunked processing
281 if (led_animation_breathing)
283 led_animation_breathe_cur += breathe_step * breathe_dir;
285 if (led_animation_breathe_cur >= BREATHE_MAX_STEP)
287 else if (led_animation_breathe_cur <= BREATHE_MIN_STEP)
290 //Brightness curve created for 256 steps, 0 - ~98%
291 breathe_mult = 0.000015 * led_animation_breathe_cur * led_animation_breathe_cur;
292 if (breathe_mult > 1) breathe_mult = 1;
293 else if (breathe_mult < 0) breathe_mult = 0;
301 while (f[fcur].end != 1)
303 fcur++; //Count frames
306 fmax = fcur; //Store total frames count
308 while (led_cur < lede && led_this_run < led_per_run)
314 if (led_lighting_mode == LED_MODE_KEYS_ONLY && led_cur->scan == 255)
316 //Do not act on this LED
318 else if (led_lighting_mode == LED_MODE_NON_KEYS_ONLY && led_cur->scan != 255)
320 //Do not act on this LED
322 else if (led_lighting_mode == LED_MODE_INDICATORS_ONLY)
324 //Do not act on this LED (Only show indicators)
329 for (fcur = 0; fcur < fmax; fcur++)
332 if (led_animation_circular) {
333 po = sqrtf((powf(fabsf(50 - led_cur->py), 2) + powf(fabsf(50 - led_cur->px), 2)));
336 if (led_animation_orientation)
347 pomod = (float)(disp.frame % (uint32_t)(1000.0f / led_animation_speed)) / 10.0f * led_animation_speed;
349 //Add in any moving effects
350 if ((!led_animation_direction && f[fcur].ef & EF_SCR_R) || (led_animation_direction && (f[fcur].ef & EF_SCR_L)))
353 pomod = (uint32_t)pomod % 10000;
358 if (po > 100) po -= 100;
359 else if (po < 0) po += 100;
361 else if ((!led_animation_direction && f[fcur].ef & EF_SCR_L) || (led_animation_direction && (f[fcur].ef & EF_SCR_R)))
364 pomod = (uint32_t)pomod % 10000;
368 if (po > 100) po -= 100;
369 else if (po < 0) po += 100;
372 //Check if LED's po is in current frame
373 if (po < f[fcur].hs) continue;
374 if (po > f[fcur].he) continue;
375 //note: < 0 or > 100 continue
377 //Calculate the po within the start-stop percentage for color blending
378 po = (po - f[fcur].hs) / (f[fcur].he - f[fcur].hs);
380 //Add in any color effects
381 if (f[fcur].ef & EF_OVER)
383 ro = (po * (f[fcur].re - f[fcur].rs)) + f[fcur].rs;// + 0.5;
384 go = (po * (f[fcur].ge - f[fcur].gs)) + f[fcur].gs;// + 0.5;
385 bo = (po * (f[fcur].be - f[fcur].bs)) + f[fcur].bs;// + 0.5;
387 else if (f[fcur].ef & EF_SUBTRACT)
389 ro -= (po * (f[fcur].re - f[fcur].rs)) + f[fcur].rs;// + 0.5;
390 go -= (po * (f[fcur].ge - f[fcur].gs)) + f[fcur].gs;// + 0.5;
391 bo -= (po * (f[fcur].be - f[fcur].bs)) + f[fcur].bs;// + 0.5;
395 ro += (po * (f[fcur].re - f[fcur].rs)) + f[fcur].rs;// + 0.5;
396 go += (po * (f[fcur].ge - f[fcur].gs)) + f[fcur].gs;// + 0.5;
397 bo += (po * (f[fcur].be - f[fcur].bs)) + f[fcur].bs;// + 0.5;
403 if (ro > 255) ro = 255; else if (ro < 0) ro = 0;
404 if (go > 255) go = 255; else if (go < 0) go = 0;
405 if (bo > 255) bo = 255; else if (bo < 0) bo = 0;
407 if (led_animation_breathing)
414 *led_cur->rgb.r = (uint8_t)ro;
415 *led_cur->rgb.g = (uint8_t)go;
416 *led_cur->rgb.b = (uint8_t)bo;
418 #ifdef USB_LED_INDICATOR_ENABLE
421 uint8_t kbled = keyboard_leds();
423 #if USB_LED_NUM_LOCK_SCANCODE != 255
424 (led_cur->scan == USB_LED_NUM_LOCK_SCANCODE && kbled & (1<<USB_LED_NUM_LOCK)) ||
426 #if USB_LED_CAPS_LOCK_SCANCODE != 255
427 (led_cur->scan == USB_LED_CAPS_LOCK_SCANCODE && kbled & (1<<USB_LED_CAPS_LOCK)) ||
429 #if USB_LED_SCROLL_LOCK_SCANCODE != 255
430 (led_cur->scan == USB_LED_SCROLL_LOCK_SCANCODE && kbled & (1<<USB_LED_SCROLL_LOCK)) ||
432 #if USB_LED_COMPOSE_SCANCODE != 255
433 (led_cur->scan == USB_LED_COMPOSE_SCANCODE && kbled & (1<<USB_LED_COMPOSE)) ||
435 #if USB_LED_KANA_SCANCODE != 255
436 (led_cur->scan == USB_LED_KANA_SCANCODE && kbled & (1<<USB_LED_KANA)) ||
440 if (*led_cur->rgb.r > 127) *led_cur->rgb.r = 0;
441 else *led_cur->rgb.r = 255;
442 if (*led_cur->rgb.g > 127) *led_cur->rgb.g = 0;
443 else *led_cur->rgb.g = 255;
444 if (*led_cur->rgb.b > 127) *led_cur->rgb.b = 0;
445 else *led_cur->rgb.b = 255;
448 #endif //USB_LED_INDICATOR_ENABLE
455 uint8_t led_matrix_init(void)
457 DBGC(DC_LED_MATRIX_INIT_BEGIN);
459 issi3733_prepare_arrays();
461 led_matrix_prepare();
467 led_animation_id = 0;
468 led_lighting_mode = LED_MODE_NORMAL;
469 led_animation_speed = 4.0f;
470 led_animation_direction = 0;
471 led_animation_orientation = 0;
472 led_animation_breathing = 0;
473 led_animation_breathe_cur = BREATHE_MIN_STEP;
476 led_animation_circular = 0;
481 //Run led matrix code once for initial LED coloring
483 rgb_matrix_init_user();
486 DBGC(DC_LED_MATRIX_INIT_COMPLETE);
491 __attribute__ ((weak))
492 void rgb_matrix_init_user(void) {
496 #define LED_UPDATE_RATE 10 //ms
498 //led data processing can take time, so process data in chunks to free up the processor
499 //this is done through led_cur and lede
500 void led_matrix_task(void)
504 //If an update may run and frame processing has completed
505 if (CLK_get_ms() >= led_next_run && led_cur == lede)
509 led_next_run = CLK_get_ms() + LED_UPDATE_RATE; //Set next frame update time
511 //NOTE: GCR does not need to be timed with LED processing, but there is really no harm
512 if (gcr_actual != gcr_actual_last)
514 for (drvid=0;drvid<ISSI3733_DRIVER_COUNT;drvid++)
515 I2C_LED_Q_GCR(drvid); //Queue data
516 gcr_actual_last = gcr_actual;
519 for (drvid=0;drvid<ISSI3733_DRIVER_COUNT;drvid++)
520 I2C_LED_Q_PWM(drvid); //Queue data
524 led_cur = 0; //Signal next frame calculations may begin
528 //Process more data if not finished
531 //DBG_1_OFF; //debug profiling
533 //DBG_1_ON; //debug profiling