/* Copyright 2017 Jason Williams * Copyright 2017 Jack Humbert * * 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 "rgb_matrix.h" #include #include "TWIlib.h" #include #include #include "progmem.h" #include "config.h" #include "eeprom.h" #include "lufa.h" #include rgb_config_t rgb_matrix_config; #ifndef RGB_DISABLE_AFTER_TIMEOUT #define RGB_DISABLE_AFTER_TIMEOUT 0 #endif #ifndef RGB_DISABLE_WHEN_USB_SUSPENDED #define RGB_DISABLE_WHEN_USB_SUSPENDED false #endif #ifndef EECONFIG_RGB_MATRIX #define EECONFIG_RGB_MATRIX EECONFIG_RGBLIGHT #endif bool g_suspend_state = false; // Global tick at 20 Hz uint32_t g_tick = 0; // Ticks since this key was last hit. uint8_t g_key_hit[DRIVER_LED_TOTAL]; // Ticks since any key was last hit. uint32_t g_any_key_hit = 0; #ifndef PI #define PI 3.14159265 #endif uint32_t eeconfig_read_rgb_matrix(void) { return eeprom_read_dword(EECONFIG_RGB_MATRIX); } void eeconfig_update_rgb_matrix(uint32_t val) { eeprom_update_dword(EECONFIG_RGB_MATRIX, val); } void eeconfig_update_rgb_matrix_default(void) { dprintf("eeconfig_update_rgb_matrix_default\n"); rgb_matrix_config.enable = 1; rgb_matrix_config.mode = RGB_MATRIX_CYCLE_LEFT_RIGHT; rgb_matrix_config.hue = 0; rgb_matrix_config.sat = 255; rgb_matrix_config.val = 255; eeconfig_update_rgb_matrix(rgb_matrix_config.raw); } void eeconfig_debug_rgb_matrix(void) { dprintf("rgb_matrix_config eprom\n"); dprintf("rgb_matrix_config.enable = %d\n", rgb_matrix_config.enable); dprintf("rgb_matrix_config.mode = %d\n", rgb_matrix_config.mode); dprintf("rgb_matrix_config.hue = %d\n", rgb_matrix_config.hue); dprintf("rgb_matrix_config.sat = %d\n", rgb_matrix_config.sat); dprintf("rgb_matrix_config.val = %d\n", rgb_matrix_config.val); } // Last led hit #define LED_HITS_TO_REMEMBER 8 uint8_t g_last_led_hit[LED_HITS_TO_REMEMBER] = {255}; uint8_t g_last_led_count = 0; void map_row_column_to_led( uint8_t row, uint8_t column, uint8_t *led_i, uint8_t *led_count) { rgb_led led; *led_count = 0; for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) { // map_index_to_led(i, &led); led = g_rgb_leds[i]; if (row == led.matrix_co.row && column == led.matrix_co.col) { led_i[*led_count] = i; (*led_count)++; } } } void rgb_matrix_update_pwm_buffers(void) { IS31FL3731_update_pwm_buffers( DRIVER_ADDR_1, DRIVER_ADDR_2 ); IS31FL3731_update_led_control_registers( DRIVER_ADDR_1, DRIVER_ADDR_2 ); } void rgb_matrix_set_color( int index, uint8_t red, uint8_t green, uint8_t blue ) { IS31FL3731_set_color( index, red, green, blue ); } void rgb_matrix_set_color_all( uint8_t red, uint8_t green, uint8_t blue ) { IS31FL3731_set_color_all( red, green, blue ); } bool process_rgb_matrix(uint16_t keycode, keyrecord_t *record) { if ( record->event.pressed ) { uint8_t led[8], led_count; map_row_column_to_led(record->event.key.row, record->event.key.col, led, &led_count); if (led_count > 0) { for (uint8_t i = LED_HITS_TO_REMEMBER; i > 1; i--) { g_last_led_hit[i - 1] = g_last_led_hit[i - 2]; } g_last_led_hit[0] = led[0]; g_last_led_count = MIN(LED_HITS_TO_REMEMBER, g_last_led_count + 1); } for(uint8_t i = 0; i < led_count; i++) g_key_hit[led[i]] = 0; g_any_key_hit = 0; } else { #ifdef RGB_MATRIX_KEYRELEASES uint8_t led[8], led_count; map_row_column_to_led(record->event.key.row, record->event.key.col, led, &led_count); for(uint8_t i = 0; i < led_count; i++) g_key_hit[led[i]] = 255; g_any_key_hit = 255; #endif } return true; } void rgb_matrix_set_suspend_state(bool state) { g_suspend_state = state; } void rgb_matrix_test(void) { // Mask out bits 4 and 5 // This 2-bit value will stay the same for 16 ticks. switch ( (g_tick & 0x30) >> 4 ) { case 0: { rgb_matrix_set_color_all( 20, 0, 0 ); break; } case 1: { rgb_matrix_set_color_all( 0, 20, 0 ); break; } case 2: { rgb_matrix_set_color_all( 0, 0, 20 ); break; } case 3: { rgb_matrix_set_color_all( 20, 20, 20 ); break; } } } // This tests the LEDs // Note that it will change the LED control registers // in the LED drivers, and leave them in an invalid // state for other backlight effects. // ONLY USE THIS FOR TESTING LEDS! void rgb_matrix_single_LED_test(void) { static uint8_t color = 0; // 0,1,2 for R,G,B static uint8_t row = 0; static uint8_t column = 0; static uint8_t tick = 0; tick++; if ( tick > 2 ) { tick = 0; column++; } if ( column > MATRIX_COLS ) { column = 0; row++; } if ( row > MATRIX_ROWS ) { row = 0; color++; } if ( color > 2 ) { color = 0; } uint8_t led[8], led_count; map_row_column_to_led(row,column,led,&led_count); for(uint8_t i = 0; i < led_count; i++) { rgb_matrix_set_color_all( 40, 40, 40 ); rgb_matrix_test_led( led[i], color==0, color==1, color==2 ); } } // All LEDs off void rgb_matrix_all_off(void) { rgb_matrix_set_color_all( 0, 0, 0 ); } static HSV current_solid_color; // Solid color void rgb_matrix_solid_color(void) { if (current_solid_color.h != rgb_matrix_config.hue && current_solid_color.s != rgb_matrix_config.sat && current_solid_color.v != rgb_matrix_config.val) { HSV hsv = { .h = rgb_matrix_config.hue, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val }; RGB rgb = hsv_to_rgb( hsv ); rgb_matrix_set_color_all( rgb.r, rgb.g, rgb.b ); current_solid_color.h = hsv.h; current_solid_color.s = hsv.s; current_solid_color.v = hsv.v; } } void rgb_matrix_solid_reactive(void) { // Relies on hue being 8-bit and wrapping for ( int i=0; i 127 ) { deltaH -= 256; } else if ( deltaH < -127 ) { deltaH += 256; } // Divide delta by 4, this gives the delta per row deltaH /= 4; int16_t s1 = rgb_matrix_config.sat; int16_t s2 = rgb_matrix_config.hue; int16_t deltaS = ( s2 - s1 ) / 4; HSV hsv = { .h = 0, .s = 255, .v = rgb_matrix_config.val }; RGB rgb; Point point; for ( int i=0; i>4); // Relies on hue being 8-bit and wrapping hsv.h = rgb_matrix_config.hue + ( deltaH * y ); hsv.s = rgb_matrix_config.sat + ( deltaS * y ); rgb = hsv_to_rgb( hsv ); rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b ); } } void rgb_matrix_raindrops(bool initialize) { int16_t h1 = rgb_matrix_config.hue; int16_t h2 = (rgb_matrix_config.hue + 180) % 360; int16_t deltaH = h2 - h1; deltaH /= 4; // Take the shortest path between hues if ( deltaH > 127 ) { deltaH -= 256; } else if ( deltaH < -127 ) { deltaH += 256; } int16_t s1 = rgb_matrix_config.sat; int16_t s2 = rgb_matrix_config.sat; int16_t deltaS = ( s2 - s1 ) / 4; HSV hsv; RGB rgb; // Change one LED every tick uint8_t led_to_change = ( g_tick & 0x000 ) == 0 ? rand() % DRIVER_LED_TOTAL : 255; for ( int i=0; i 0 && g_any_key_hit > RGB_DISABLE_AFTER_TIMEOUT * 60 * 20)); uint8_t effect = suspend_backlight ? 0 : rgb_matrix_config.mode; // Keep track of the effect used last time, // detect change in effect, so each effect can // have an optional initialization. static uint8_t effect_last = 255; bool initialize = effect != effect_last; effect_last = effect; // this gets ticked at 20 Hz. // each effect can opt to do calculations // and/or request PWM buffer updates. switch ( effect ) { case RGB_MATRIX_SOLID_COLOR: rgb_matrix_solid_color(); break; case RGB_MATRIX_SOLID_REACTIVE: rgb_matrix_solid_reactive(); break; case RGB_MATRIX_ALPHAS_MODS: rgb_matrix_alphas_mods(); break; case RGB_MATRIX_DUAL_BEACON: rgb_matrix_dual_beacon(); break; case RGB_MATRIX_GRADIENT_UP_DOWN: rgb_matrix_gradient_up_down(); break; case RGB_MATRIX_RAINDROPS: rgb_matrix_raindrops( initialize ); break; case RGB_MATRIX_CYCLE_ALL: rgb_matrix_cycle_all(); break; case RGB_MATRIX_CYCLE_LEFT_RIGHT: rgb_matrix_cycle_left_right(); break; case RGB_MATRIX_CYCLE_UP_DOWN: rgb_matrix_cycle_up_down(); break; case RGB_MATRIX_RAINBOW_BEACON: rgb_matrix_rainbow_beacon(); break; case RGB_MATRIX_RAINBOW_PINWHEELS: rgb_matrix_rainbow_pinwheels(); break; case RGB_MATRIX_RAINBOW_MOVING_CHEVRON: rgb_matrix_rainbow_moving_chevron(); break; case RGB_MATRIX_JELLYBEAN_RAINDROPS: rgb_matrix_jellybean_raindrops( initialize ); break; #ifdef RGB_MATRIX_KEYPRESSES case RGB_MATRIX_SPLASH: rgb_matrix_splash(); break; case RGB_MATRIX_MULTISPLASH: rgb_matrix_multisplash(); break; case RGB_MATRIX_SOLID_SPLASH: rgb_matrix_solid_splash(); break; case RGB_MATRIX_SOLID_MULTISPLASH: rgb_matrix_solid_multisplash(); break; #endif default: rgb_matrix_custom(); break; } if ( ! suspend_backlight ) { rgb_matrix_indicators(); } rgb_matrix_update_pwm_buffers(); } void rgb_matrix_indicators(void) { rgb_matrix_indicators_kb(); rgb_matrix_indicators_user(); } __attribute__((weak)) void rgb_matrix_indicators_kb(void) {} __attribute__((weak)) void rgb_matrix_indicators_user(void) {} // void rgb_matrix_set_indicator_index( uint8_t *index, uint8_t row, uint8_t column ) // { // if ( row >= MATRIX_ROWS ) // { // // Special value, 255=none, 254=all // *index = row; // } // else // { // // This needs updated to something like // // uint8_t led[8], led_count; // // map_row_column_to_led(row,column,led,&led_count); // // for(uint8_t i = 0; i < led_count; i++) // map_row_column_to_led( row, column, index ); // } // } void rgb_matrix_init_drivers(void) { //sei(); // Initialize TWI TWIInit(); IS31FL3731_init( DRIVER_ADDR_1 ); IS31FL3731_init( DRIVER_ADDR_2 ); for ( int index = 0; index < DRIVER_LED_TOTAL; index++ ) { bool enabled = true; // This only caches it for later IS31FL3731_set_led_control_register( index, enabled, enabled, enabled ); } // This actually updates the LED drivers IS31FL3731_update_led_control_registers( DRIVER_ADDR_1, DRIVER_ADDR_2 ); // TODO: put the 1 second startup delay here? // clear the key hits for ( int led=0; ledh = eeprom_read_byte(address); // hsv->s = eeprom_read_byte(address+1); // hsv->v = eeprom_read_byte(address+2); // } // void backlight_set_key_color( uint8_t row, uint8_t column, HSV hsv ) // { // uint8_t led[8], led_count; // map_row_column_to_led(row,column,led,&led_count); // for(uint8_t i = 0; i < led_count; i++) { // if ( led[i] < DRIVER_LED_TOTAL ) // { // void *address = backlight_get_custom_key_color_eeprom_address(led[i]); // eeprom_update_byte(address, hsv.h); // eeprom_update_byte(address+1, hsv.s); // eeprom_update_byte(address+2, hsv.v); // } // } // } void rgb_matrix_test_led( uint8_t index, bool red, bool green, bool blue ) { for ( int i=0; i= RGB_MATRIX_EFFECT_MAX) rgb_matrix_config.mode = 1; eeconfig_update_rgb_matrix(rgb_matrix_config.raw); } void rgblight_step_reverse(void) { rgb_matrix_config.mode--; if (rgb_matrix_config.mode <= 1) rgb_matrix_config.mode = (RGB_MATRIX_EFFECT_MAX - 1); eeconfig_update_rgb_matrix(rgb_matrix_config.raw); } void rgblight_increase_hue(void) { rgb_matrix_config.hue = increment( rgb_matrix_config.hue, 8, 0, 255 ); eeconfig_update_rgb_matrix(rgb_matrix_config.raw); } void rgblight_decrease_hue(void) { rgb_matrix_config.hue = decrement( rgb_matrix_config.hue, 8, 0, 255 ); eeconfig_update_rgb_matrix(rgb_matrix_config.raw); } void rgblight_increase_sat(void) { rgb_matrix_config.sat = increment( rgb_matrix_config.sat, 8, 0, 255 ); eeconfig_update_rgb_matrix(rgb_matrix_config.raw); } void rgblight_decrease_sat(void) { rgb_matrix_config.sat = decrement( rgb_matrix_config.sat, 8, 0, 255 ); eeconfig_update_rgb_matrix(rgb_matrix_config.raw); } void rgblight_increase_val(void) { rgb_matrix_config.val = increment( rgb_matrix_config.val, 8, 0, 255 ); eeconfig_update_rgb_matrix(rgb_matrix_config.raw); } void rgblight_decrease_val(void) { rgb_matrix_config.val = decrement( rgb_matrix_config.val, 8, 0, 255 ); eeconfig_update_rgb_matrix(rgb_matrix_config.raw); } void rgblight_mode(uint8_t mode) { rgb_matrix_config.mode = 1; eeconfig_update_rgb_matrix(rgb_matrix_config.raw); } uint32_t rgblight_get_mode(void) { return rgb_matrix_config.mode; }