/* Copyright 2018 Massdrop Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "arm_atsam_protocol.h" #include "tmk_core/common/led.h" #include "rgb_matrix.h" #include #include #ifdef USE_MASSDROP_CONFIGURATOR __attribute__((weak)) led_instruction_t led_instructions[] = { { .end = 1 } }; static void led_matrix_massdrop_config_override(int i); #endif // USE_MASSDROP_CONFIGURATOR extern rgb_config_t rgb_matrix_config; extern rgb_counters_t g_rgb_counters; void SERCOM1_0_Handler( void ) { if (SERCOM1->I2CM.INTFLAG.bit.ERROR) { SERCOM1->I2CM.INTFLAG.reg = SERCOM_I2CM_INTENCLR_ERROR; } } void DMAC_0_Handler( void ) { if (DMAC->Channel[0].CHINTFLAG.bit.TCMPL) { DMAC->Channel[0].CHINTFLAG.reg = DMAC_CHINTENCLR_TCMPL; i2c1_stop(); i2c_led_q_running = 0; i2c_led_q_run(); return; } if (DMAC->Channel[0].CHINTFLAG.bit.TERR) { DMAC->Channel[0].CHINTFLAG.reg = DMAC_CHINTENCLR_TERR; } } issi3733_driver_t issidrv[ISSI3733_DRIVER_COUNT]; issi3733_led_t led_map[ISSI3733_LED_COUNT] = ISSI3733_LED_MAP; RGB led_buffer[ISSI3733_LED_COUNT]; uint8_t gcr_desired; uint8_t gcr_actual; uint8_t gcr_actual_last; #ifdef USE_MASSDROP_CONFIGURATOR uint8_t gcr_breathe; float breathe_mult; float pomod; #endif #define ACT_GCR_NONE 0 #define ACT_GCR_INC 1 #define ACT_GCR_DEC 2 #define LED_GCR_STEP_AUTO 2 static uint8_t gcr_min_counter; static uint8_t v_5v_cat_hit; //WARNING: Automatic GCR is in place to prevent USB shutdown and LED driver overloading void gcr_compute(void) { uint8_t action = ACT_GCR_NONE; uint8_t gcr_use = gcr_desired; #ifdef USE_MASSDROP_CONFIGURATOR if (led_animation_breathing) { gcr_use = gcr_breathe; } #endif //If the 5v takes a catastrophic hit, disable the LED drivers briefly, assert auto gcr mode, min gcr and let the auto take over if (v_5v < V5_CAT) { I2C3733_Control_Set(0); //CDC_print("USB: WARNING: 5V catastrophic level reached! Disabling LED drivers!\r\n"); //Blocking print is bad here! v_5v_cat_hit = 20; //~100ms recover gcr_actual = 0; //Minimize GCR usb_gcr_auto = 1; //Force auto mode enabled return; } else if (v_5v_cat_hit > 1) { v_5v_cat_hit--; return; } else if (v_5v_cat_hit == 1) { I2C3733_Control_Set(1); CDC_print("USB: WARNING: Re-enabling LED drivers\r\n"); v_5v_cat_hit = 0; return; } if (usb_gcr_auto) { if (v_5v_avg < V5_LOW) action = ACT_GCR_DEC; else if (v_5v_avg > V5_HIGH && gcr_actual < gcr_use) action = ACT_GCR_INC; else if (gcr_actual > gcr_use) action = ACT_GCR_DEC; } else { if (gcr_actual < gcr_use) action = ACT_GCR_INC; else if (gcr_actual > gcr_use) action = ACT_GCR_DEC; } if (action == ACT_GCR_NONE) { gcr_min_counter = 0; } else if (action == ACT_GCR_INC) { if (LED_GCR_STEP_AUTO > LED_GCR_MAX - gcr_actual) gcr_actual = LED_GCR_MAX; //Obey max and prevent wrapping else gcr_actual += LED_GCR_STEP_AUTO; gcr_min_counter = 0; } else if (action == ACT_GCR_DEC) { if (LED_GCR_STEP_AUTO > gcr_actual) //Prevent wrapping { gcr_actual = 0; //At this point, power can no longer be cut from the LED drivers, so focus on cutting out extra port if active if (usb_extra_state != USB_EXTRA_STATE_DISABLED_UNTIL_REPLUG) //If not in a wait for replug state { if (usb_extra_state == USB_EXTRA_STATE_ENABLED) //If extra usb is enabled { gcr_min_counter++; if (gcr_min_counter > 200) //5ms per check = 1s delay { USB_ExtraSetState(USB_EXTRA_STATE_DISABLED_UNTIL_REPLUG); usb_extra_manual = 0; //Force disable manual mode of extra port if (usb_extra_manual) CDC_print("USB: Disabling extra port until replug and manual mode toggle!\r\n"); else CDC_print("USB: Disabling extra port until replug!\r\n"); } } } } else { //Power successfully cut back from LED drivers gcr_actual -= LED_GCR_STEP_AUTO; gcr_min_counter = 0; #ifdef USE_MASSDROP_CONFIGURATOR //If breathe mode is active, the top end can fluctuate if the host can not supply enough current //So set the breathe GCR to where it becomes stable if (led_animation_breathing == 1) { gcr_breathe = gcr_actual; //PS: At this point, setting breathing to exhale makes a noticebly shorter cycle // and the same would happen maybe one or two more times. Therefore I'm favoring // powering through one full breathe and letting gcr settle completely } #endif } } } void issi3733_prepare_arrays(void) { memset(issidrv,0,sizeof(issi3733_driver_t) * ISSI3733_DRIVER_COUNT); int i; uint8_t addrs[ISSI3733_DRIVER_COUNT] = ISSI3773_DRIVER_ADDRESSES; for (i=0;i= BREATHE_MAX_STEP) breathe_dir = -1; else if (led_animation_breathe_cur <= BREATHE_MIN_STEP) breathe_dir = 1; //Brightness curve created for 256 steps, 0 - ~98% breathe_mult = 0.000015 * led_animation_breathe_cur * led_animation_breathe_cur; if (breathe_mult > 1) breathe_mult = 1; else if (breathe_mult < 0) breathe_mult = 0; } //This should only be performed once per frame pomod = (float)((g_rgb_counters.tick / 10) % (uint32_t)(1000.0f / led_animation_speed)) / 10.0f * led_animation_speed; pomod *= 100.0f; pomod = (uint32_t)pomod % 10000; pomod /= 100.0f; #endif // USE_MASSDROP_CONFIGURATOR uint8_t drvid; //NOTE: GCR does not need to be timed with LED processing, but there is really no harm if (gcr_actual != gcr_actual_last) { for (drvid=0;drvidend != 1) { po = pos; //Reset po for new frame //Add in any moving effects if ((!led_animation_direction && f->ef & EF_SCR_R) || (led_animation_direction && (f->ef & EF_SCR_L))) { po -= pomod; if (po > 100) po -= 100; else if (po < 0) po += 100; } else if ((!led_animation_direction && f->ef & EF_SCR_L) || (led_animation_direction && (f->ef & EF_SCR_R))) { po += pomod; if (po > 100) po -= 100; else if (po < 0) po += 100; } //Check if LED's po is in current frame if (po < f->hs) { f++; continue; } if (po > f->he) { f++; continue; } //note: < 0 or > 100 continue //Calculate the po within the start-stop percentage for color blending po = (po - f->hs) / (f->he - f->hs); //Add in any color effects if (f->ef & EF_OVER) { *ro = (po * (f->re - f->rs)) + f->rs;// + 0.5; *go = (po * (f->ge - f->gs)) + f->gs;// + 0.5; *bo = (po * (f->be - f->bs)) + f->bs;// + 0.5; } else if (f->ef & EF_SUBTRACT) { *ro -= (po * (f->re - f->rs)) + f->rs;// + 0.5; *go -= (po * (f->ge - f->gs)) + f->gs;// + 0.5; *bo -= (po * (f->be - f->bs)) + f->bs;// + 0.5; } else { *ro += (po * (f->re - f->rs)) + f->rs;// + 0.5; *go += (po * (f->ge - f->gs)) + f->gs;// + 0.5; *bo += (po * (f->be - f->bs)) + f->bs;// + 0.5; } f++; } } static void led_matrix_massdrop_config_override(int i) { float ro = 0; float go = 0; float bo = 0; float po = (led_animation_orientation) ? (float)g_rgb_leds[i].point.y / 64.f * 100 : (float)g_rgb_leds[i].point.x / 224.f * 100; uint8_t highest_active_layer = biton32(layer_state); if (led_lighting_mode == LED_MODE_KEYS_ONLY && g_rgb_leds[i].matrix_co.raw == 0xff) { //Do not act on this LED } else if (led_lighting_mode == LED_MODE_NON_KEYS_ONLY && g_rgb_leds[i].matrix_co.raw != 0xff) { //Do not act on this LED } else if (led_lighting_mode == LED_MODE_INDICATORS_ONLY) { //Do not act on this LED (Only show indicators) } else { led_instruction_t* led_cur_instruction = led_instructions; while (!led_cur_instruction->end) { // Check if this applies to current layer if ((led_cur_instruction->flags & LED_FLAG_MATCH_LAYER) && (led_cur_instruction->layer != highest_active_layer)) { goto next_iter; } // Check if this applies to current index if (led_cur_instruction->flags & LED_FLAG_MATCH_ID) { uint8_t modid = i / 32; //Calculate which id# contains the led bit uint32_t modidbit = 1 << (i % 32); //Calculate the bit within the id# uint32_t *bitfield = &led_cur_instruction->id0 + modid; //Add modid as offset to id0 address. *bitfield is now idX of the led id if (~(*bitfield) & modidbit) { //Check if led bit is not set in idX goto next_iter; } } if (led_cur_instruction->flags & LED_FLAG_USE_RGB) { ro = led_cur_instruction->r; go = led_cur_instruction->g; bo = led_cur_instruction->b; } else if (led_cur_instruction->flags & LED_FLAG_USE_PATTERN) { led_run_pattern(led_setups[led_cur_instruction->pattern_id], &ro, &go, &bo, po); } else if (led_cur_instruction->flags & LED_FLAG_USE_ROTATE_PATTERN) { led_run_pattern(led_setups[led_animation_id], &ro, &go, &bo, po); } next_iter: led_cur_instruction++; } if (ro > 255) ro = 255; else if (ro < 0) ro = 0; if (go > 255) go = 255; else if (go < 0) go = 0; if (bo > 255) bo = 255; else if (bo < 0) bo = 0; if (led_animation_breathing) { ro *= breathe_mult; go *= breathe_mult; bo *= breathe_mult; } } led_buffer[i].r = (uint8_t)ro; led_buffer[i].g = (uint8_t)go; led_buffer[i].b = (uint8_t)bo; } #endif // USE_MASSDROP_CONFIGURATOR