diff options
Diffstat (limited to 'keyboards/wilba_tech/wt_mono_backlight.c')
-rw-r--r-- | keyboards/wilba_tech/wt_mono_backlight.c | 473 |
1 files changed, 473 insertions, 0 deletions
diff --git a/keyboards/wilba_tech/wt_mono_backlight.c b/keyboards/wilba_tech/wt_mono_backlight.c new file mode 100644 index 0000000000..0fc6e346a1 --- /dev/null +++ b/keyboards/wilba_tech/wt_mono_backlight.c @@ -0,0 +1,473 @@ +/* Copyright 2018 Jason Williams (Wilba) + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "quantum.h" +#include "wt_mono_backlight.h" +#include "wt_rgb_backlight_api.h" // reuse these for now +#include "wt_rgb_backlight_keycodes.h" // reuse these for now + +#include <avr/interrupt.h> +#include "i2c_master.h" + +#include "progmem.h" +#include "quantum/color.h" +#include "eeprom.h" + +#include "via.h" // uses EEPROM address, lighting value IDs +#define MONO_BACKLIGHT_CONFIG_EEPROM_ADDR (VIA_EEPROM_CUSTOM_CONFIG_ADDR) + +#if VIA_EEPROM_CUSTOM_CONFIG_SIZE == 0 +#error VIA_EEPROM_CUSTOM_CONFIG_SIZE was not defined to store backlight_config struct +#endif + +#include "drivers/led/issi/is31fl3736.h" + +#define ISSI_ADDR_DEFAULT 0x50 + +#define BACKLIGHT_EFFECT_MAX 3 + +#ifndef MONO_BACKLIGHT_COLOR_1 +#define MONO_BACKLIGHT_COLOR_1 { .h = 0, .s = 255 } +#endif + +backlight_config g_config = { + .disable_when_usb_suspended = MONO_BACKLIGHT_DISABLE_WHEN_USB_SUSPENDED, + .disable_after_timeout = MONO_BACKLIGHT_DISABLE_AFTER_TIMEOUT, + .brightness = MONO_BACKLIGHT_BRIGHTNESS, + .effect = MONO_BACKLIGHT_EFFECT, + .effect_speed = MONO_BACKLIGHT_EFFECT_SPEED, + .color_1 = MONO_BACKLIGHT_COLOR_1, +}; + +bool g_suspend_state = false; + +// Global tick at 20 Hz +uint32_t g_tick = 0; + +// Ticks since any key was last hit. +uint32_t g_any_key_hit = 0; + +void backlight_init_drivers(void) +{ + // Initialize I2C + i2c_init(); + IS31FL3736_init( ISSI_ADDR_DEFAULT ); + + for ( uint8_t index = 0; index < 96; index++ ) { + IS31FL3736_mono_set_led_control_register( index, true ); + } + IS31FL3736_update_led_control_registers( ISSI_ADDR_DEFAULT, 0x00 ); +} + +void backlight_set_key_hit(uint8_t row, uint8_t column) +{ + g_any_key_hit = 0; +} + +// This is (F_CPU/1024) / 20 Hz +// = 15625 Hz / 20 Hz +// = 781 +#define TIMER3_TOP 781 + +void backlight_timer_init(void) +{ + static uint8_t backlight_timer_is_init = 0; + if ( backlight_timer_is_init ) { + return; + } + backlight_timer_is_init = 1; + + // Timer 3 setup + TCCR3B = _BV(WGM32) | // CTC mode OCR3A as TOP + _BV(CS32) | _BV(CS30); // prescale by /1024 + // Set TOP value + uint8_t sreg = SREG; + cli(); + + OCR3AH = (TIMER3_TOP >> 8) & 0xff; + OCR3AL = TIMER3_TOP & 0xff; + SREG = sreg; +} + +void backlight_timer_enable(void) +{ + TIMSK3 |= _BV(OCIE3A); +} + +void backlight_timer_disable(void) +{ + TIMSK3 &= ~_BV(OCIE3A); +} + +void backlight_set_suspend_state(bool state) +{ + g_suspend_state = state; +} + +void backlight_set_brightness_all( uint8_t value ) +{ + IS31FL3736_mono_set_brightness_all( value ); +} + +void backlight_effect_all_off(void) +{ + IS31FL3736_mono_set_brightness_all( 0 ); +} + +void backlight_effect_all_on(void) +{ + IS31FL3736_mono_set_brightness_all( g_config.brightness ); +} + +void backlight_effect_raindrops(bool initialize) +{ + // Change one LED every tick + uint8_t led_to_change = ( g_tick & 0x000 ) == 0 ? rand() % 96 : 255; + + for ( int i=0; i<96; i++ ) + { + // If initialize, all get set to random brightness + // If not, all but one will stay the same as before. + if ( initialize || i == led_to_change ) + { + IS31FL3736_mono_set_brightness(i, rand() & 0xFF ); + } + } +} + +void backlight_effect_cycle_all(void) +{ + uint8_t offset = ( g_tick << g_config.effect_speed ) & 0xFF; + + backlight_set_brightness_all( offset ); +} + +// This runs after another backlight effect and replaces +// colors already set +void backlight_effect_indicators(void) +{ +#if defined(MONO_BACKLIGHT_WT75_A) + HSV hsv = { .h = g_config.color_1.h, .s = g_config.color_1.s, .v = g_config.brightness }; + RGB rgb = hsv_to_rgb( hsv ); + // SW7,CS8 = (6*8+7) = 55 + // SW8,CS8 = (7*8+7) = 63 + // SW9,CS8 = (8*8+7) = 71 + IS31FL3736_mono_set_brightness(55, rgb.r); + IS31FL3736_mono_set_brightness(63, rgb.g); + IS31FL3736_mono_set_brightness(71, rgb.b); +#endif // MONO_BACKLIGHT_WT75_A + +// This pairs with "All Off" already setting zero brightness, +// and "All On" already setting non-zero brightness. +#if defined(MONO_BACKLIGHT_WT60_A) || \ +defined(MONO_BACKLIGHT_WT65_A) || \ +defined(MONO_BACKLIGHT_WT65_B) || \ +defined(MONO_BACKLIGHT_WT75_A) || \ +defined(MONO_BACKLIGHT_WT75_B) || \ +defined(MONO_BACKLIGHT_WT75_C) || \ +defined(MONO_BACKLIGHT_WT80_A) + if ( host_keyboard_led_state().caps_lock ) { + // SW3,CS1 = (2*8+0) = 16 + IS31FL3736_mono_set_brightness(16, 255); + } +#endif +#if defined(MONO_BACKLIGHT_WT80_A) + if ( host_keyboard_led_state().scroll_lock ) { + // SW7,CS7 = (6*8+6) = 54 + IS31FL3736_mono_set_brightness(54, 255); + } +#endif +#if defined(MONO_BACKLIGHT_WT75_C) + if ( host_keyboard_led_state().scroll_lock ) { + // SW7,CS8 = (6*8+7) = 55 + IS31FL3736_mono_set_brightness(55, 255); + } +#endif +} + +ISR(TIMER3_COMPA_vect) +{ + // delay 1 second before driving LEDs or doing anything else + static uint8_t startup_tick = 0; + if ( startup_tick < 20 ) { + startup_tick++; + return; + } + + g_tick++; + + if ( g_any_key_hit < 0xFFFFFFFF ) + { + g_any_key_hit++; + } + + // Ideally we would also stop sending zeros to the LED driver PWM buffers + // while suspended and just do a software shutdown. This is a cheap hack for now. + bool suspend_backlight = ((g_suspend_state && g_config.disable_when_usb_suspended) || + (g_config.disable_after_timeout > 0 && g_any_key_hit > g_config.disable_after_timeout * 60 * 20)); + uint8_t effect = suspend_backlight ? 0 : g_config.effect; + + // 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 0: + backlight_effect_all_off(); + break; + case 1: + backlight_effect_all_on();; + break; + case 2: + backlight_effect_raindrops(initialize); + break; + default: + backlight_effect_all_off(); + break; + } + + if ( ! suspend_backlight ) + { + backlight_effect_indicators(); + } +} + +// Some helpers for setting/getting HSV +void _set_color( HS *color, uint8_t *data ) +{ + color->h = data[0]; + color->s = data[1]; +} + +void _get_color( HS *color, uint8_t *data ) +{ + data[0] = color->h; + data[1] = color->s; +} + +void backlight_config_set_value( uint8_t *data ) +{ + bool reinitialize = false; + uint8_t *value_id = &(data[0]); + uint8_t *value_data = &(data[1]); + switch ( *value_id ) + { + case id_disable_when_usb_suspended: + { + g_config.disable_when_usb_suspended = (bool)*value_data; + break; + } + case id_disable_after_timeout: + { + g_config.disable_after_timeout = *value_data; + break; + } + case id_brightness: + { + g_config.brightness = *value_data; + break; + } + case id_effect: + { + g_config.effect = *value_data; + break; + } + case id_effect_speed: + { + g_config.effect_speed = *value_data; + break; + } + case id_color_1: + { + _set_color( &(g_config.color_1), value_data ); + break; + } + } + + if ( reinitialize ) + { + backlight_init_drivers(); + } +} + +void backlight_config_get_value( uint8_t *data ) +{ + uint8_t *value_id = &(data[0]); + uint8_t *value_data = &(data[1]); + switch ( *value_id ) + { + case id_disable_when_usb_suspended: + { + *value_data = ( g_config.disable_when_usb_suspended ? 1 : 0 ); + break; + } + case id_disable_after_timeout: + { + *value_data = g_config.disable_after_timeout; + break; + } + case id_brightness: + { + *value_data = g_config.brightness; + break; + } + case id_effect: + { + *value_data = g_config.effect; + break; + } + case id_effect_speed: + { + *value_data = g_config.effect_speed; + break; + } + case id_color_1: + { + _get_color( &(g_config.color_1), value_data ); + break; + } + } +} + +void backlight_config_load(void) +{ + eeprom_read_block( &g_config, ((void*)MONO_BACKLIGHT_CONFIG_EEPROM_ADDR), sizeof(backlight_config) ); +} + +void backlight_config_save(void) +{ + eeprom_update_block( &g_config, ((void*)MONO_BACKLIGHT_CONFIG_EEPROM_ADDR), sizeof(backlight_config) ); +} + +void backlight_update_pwm_buffers(void) +{ + IS31FL3736_update_pwm_buffers(ISSI_ADDR_DEFAULT,0x00); +} + +bool process_record_backlight(uint16_t keycode, keyrecord_t *record) +{ + // Record keypresses for backlight effects + if ( record->event.pressed ) + { + backlight_set_key_hit( record->event.key.row, record->event.key.col ); + } + + switch(keycode) + { + case BR_INC: + if (record->event.pressed) + { + backlight_brightness_increase(); + } + return false; + break; + case BR_DEC: + if (record->event.pressed) + { + backlight_brightness_decrease(); + } + return false; + break; + case EF_INC: + if (record->event.pressed) + { + backlight_effect_increase(); + } + return false; + break; + case EF_DEC: + if (record->event.pressed) + { + backlight_effect_decrease(); + } + return false; + break; + case ES_INC: + if (record->event.pressed) + { + backlight_effect_speed_increase(); + } + return false; + break; + case ES_DEC: + if (record->event.pressed) + { + backlight_effect_speed_decrease(); + } + return false; + break; + } + + return true; +} + +// Deals with the messy details of incrementing an integer +uint8_t increment( uint8_t value, uint8_t step, uint8_t min, uint8_t max ) +{ + int16_t new_value = value; + new_value += step; + return MIN( MAX( new_value, min ), max ); +} + +uint8_t decrement( uint8_t value, uint8_t step, uint8_t min, uint8_t max ) +{ + int16_t new_value = value; + new_value -= step; + return MIN( MAX( new_value, min ), max ); +} + +void backlight_effect_increase(void) +{ + g_config.effect = increment( g_config.effect, 1, 0, BACKLIGHT_EFFECT_MAX ); + backlight_config_save(); +} + +void backlight_effect_decrease(void) +{ + g_config.effect = decrement( g_config.effect, 1, 0, BACKLIGHT_EFFECT_MAX ); + backlight_config_save(); +} + +void backlight_effect_speed_increase(void) +{ + g_config.effect_speed = increment( g_config.effect_speed, 1, 0, 3 ); + backlight_config_save(); +} + +void backlight_effect_speed_decrease(void) +{ + g_config.effect_speed = decrement( g_config.effect_speed, 1, 0, 3 ); + backlight_config_save(); +} + +void backlight_brightness_increase(void) +{ + g_config.brightness = increment( g_config.brightness, 8, 0, 255 ); + backlight_config_save(); +} + +void backlight_brightness_decrease(void) +{ + g_config.brightness = decrement( g_config.brightness, 8, 0, 255 ); + backlight_config_save(); +} |