qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

wt_mono_backlight.c (12264B)


      1 /* Copyright 2018 Jason Williams (Wilba)
      2  *
      3  * This program is free software: you can redistribute it and/or modify
      4  * it under the terms of the GNU General Public License as published by
      5  * the Free Software Foundation, either version 2 of the License, or
      6  * (at your option) any later version.
      7  *
      8  * This program is distributed in the hope that it will be useful,
      9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     11  * GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License
     14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     15  */
     16 
     17 #include "wt_mono_backlight.h"
     18 #include "wt_rgb_backlight_api.h" // reuse these for now
     19 #include "wt_rgb_backlight_keycodes.h" // reuse these for now
     20 
     21 #include <stdlib.h>
     22 #include <avr/interrupt.h>
     23 #include "i2c_master.h"
     24 #include "host.h"
     25 #include "progmem.h"
     26 #include "eeprom.h"
     27 
     28 #include "nvm_eeprom_eeconfig_internal.h" // expose EEPROM addresses, no appetite to move legacy/deprecated code to nvm
     29 #include "nvm_eeprom_via_internal.h" // expose EEPROM addresses, no appetite to move legacy/deprecated code to nvm
     30 #include "via.h" // uses EEPROM address, lighting value IDs
     31 #define MONO_BACKLIGHT_CONFIG_EEPROM_ADDR (VIA_EEPROM_CUSTOM_CONFIG_ADDR)
     32 
     33 #if VIA_EEPROM_CUSTOM_CONFIG_SIZE == 0
     34 #error VIA_EEPROM_CUSTOM_CONFIG_SIZE was not defined to store backlight_config struct
     35 #endif
     36 
     37 #include "drivers/led/issi/is31fl3736-mono.h"
     38 
     39 #define BACKLIGHT_EFFECT_MAX 3
     40 
     41 #ifndef MONO_BACKLIGHT_COLOR_1
     42 #define MONO_BACKLIGHT_COLOR_1 { .h = 0, .s = 255 }
     43 #endif
     44 
     45 backlight_config g_config = {
     46     .disable_when_usb_suspended = MONO_BACKLIGHT_DISABLE_WHEN_USB_SUSPENDED,
     47     .disable_after_timeout = MONO_BACKLIGHT_DISABLE_AFTER_TIMEOUT,
     48     .brightness = MONO_BACKLIGHT_BRIGHTNESS,
     49     .effect = MONO_BACKLIGHT_EFFECT,
     50     .effect_speed = MONO_BACKLIGHT_EFFECT_SPEED,
     51     .color_1 = MONO_BACKLIGHT_COLOR_1,
     52 };
     53 
     54 bool g_suspend_state = false;
     55 
     56 // Global tick at 20 Hz
     57 uint32_t g_tick = 0;
     58 
     59 // Ticks since any key was last hit.
     60 uint32_t g_any_key_hit = 0;
     61 
     62 void backlight_init_drivers(void)
     63 {
     64     is31fl3736_init_drivers();
     65 }
     66 
     67 void backlight_set_key_hit(uint8_t row, uint8_t column)
     68 {
     69     g_any_key_hit = 0;
     70 }
     71 
     72 // This is (F_CPU/1024) / 20 Hz
     73 // = 15625 Hz / 20 Hz
     74 // = 781
     75 #define TIMER3_TOP 781
     76 
     77 void backlight_timer_init(void)
     78 {
     79 	static uint8_t backlight_timer_is_init = 0;
     80 	if ( backlight_timer_is_init ) {
     81 		return;
     82 	}
     83 	backlight_timer_is_init = 1;
     84 
     85 	// Timer 3 setup
     86 	TCCR3B = _BV(WGM32) | 			// CTC mode OCR3A as TOP
     87 			 _BV(CS32) | _BV(CS30); // prescale by /1024
     88 	// Set TOP value
     89 	uint8_t sreg = SREG;
     90 	cli();
     91 
     92 	OCR3AH = (TIMER3_TOP >> 8) & 0xff;
     93 	OCR3AL = TIMER3_TOP & 0xff;
     94 	SREG = sreg;
     95 }
     96 
     97 void backlight_timer_enable(void)
     98 {
     99 	TIMSK3 |= _BV(OCIE3A);
    100 }
    101 
    102 void backlight_timer_disable(void)
    103 {
    104 	TIMSK3 &= ~_BV(OCIE3A);
    105 }
    106 
    107 void backlight_set_suspend_state(bool state)
    108 {
    109 	g_suspend_state = state;
    110 }
    111 
    112 void backlight_set_brightness_all( uint8_t value )
    113 {
    114 	is31fl3736_set_value_all( value );
    115 }
    116 
    117 void backlight_effect_all_off(void)
    118 {
    119 	is31fl3736_set_value_all( 0 );
    120 }
    121 
    122 void backlight_effect_all_on(void)
    123 {
    124 	is31fl3736_set_value_all( g_config.brightness );
    125 }
    126 
    127 void backlight_effect_raindrops(bool initialize)
    128 {
    129     // Change one LED every tick
    130     uint8_t led_to_change = ( g_tick & 0x000 ) == 0 ? rand() % 96 : 255;
    131 
    132     for ( int i=0; i<96; i++ )
    133     {
    134         // If initialize, all get set to random brightness
    135         // If not, all but one will stay the same as before.
    136         if ( initialize || i == led_to_change )
    137         {
    138             is31fl3736_set_value(i, rand() & 0xFF );
    139         }
    140     }
    141 }
    142 
    143 void backlight_effect_cycle_all(void)
    144 {
    145 	uint8_t offset = ( g_tick << g_config.effect_speed ) & 0xFF;
    146 
    147 	backlight_set_brightness_all( offset );
    148 }
    149 
    150 // This runs after another backlight effect and replaces
    151 // colors already set
    152 void backlight_effect_indicators(void)
    153 {
    154 #if defined(MONO_BACKLIGHT_WT75_A)
    155     hsv_t hsv = { .h = g_config.color_1.h, .s = g_config.color_1.s, .v = g_config.brightness };
    156     rgb_t rgb = hsv_to_rgb( hsv );
    157     // SW7,CS8 = (6*8+7) = 55
    158     // SW8,CS8 = (7*8+7) = 63
    159     // SW9,CS8 = (8*8+7) = 71
    160     is31fl3736_set_value(55, rgb.r);
    161     is31fl3736_set_value(63, rgb.g);
    162     is31fl3736_set_value(71, rgb.b);
    163 #endif // MONO_BACKLIGHT_WT75_A
    164 
    165 // This pairs with "All Off" already setting zero brightness,
    166 // and "All On" already setting non-zero brightness.
    167 #if defined(MONO_BACKLIGHT_WT60_A) || \
    168 defined(MONO_BACKLIGHT_WT65_A) || \
    169 defined(MONO_BACKLIGHT_WT65_B) || \
    170 defined(MONO_BACKLIGHT_WT75_A) || \
    171 defined(MONO_BACKLIGHT_WT75_B) || \
    172 defined(MONO_BACKLIGHT_WT75_C) || \
    173 defined(MONO_BACKLIGHT_WT80_A)
    174     if ( host_keyboard_led_state().caps_lock ) {
    175         // SW3,CS1 = (2*8+0) = 16
    176         is31fl3736_set_value(16, 255);
    177     }
    178 #endif
    179 #if defined(MONO_BACKLIGHT_WT80_A)
    180     if ( host_keyboard_led_state().scroll_lock ) {
    181         // SW7,CS7 = (6*8+6) = 54
    182         is31fl3736_set_value(54, 255);
    183     }
    184 #endif
    185 #if defined(MONO_BACKLIGHT_WT75_C)
    186     if ( host_keyboard_led_state().scroll_lock ) {
    187         // SW7,CS8 = (6*8+7) = 55
    188         is31fl3736_set_value(55, 255);
    189     }
    190 #endif
    191 }
    192 
    193 ISR(TIMER3_COMPA_vect)
    194 {
    195 	// delay 1 second before driving LEDs or doing anything else
    196 	static uint8_t startup_tick = 0;
    197 	if ( startup_tick < 20 ) {
    198 		startup_tick++;
    199 		return;
    200 	}
    201 
    202 	g_tick++;
    203 
    204     if ( g_any_key_hit < 0xFFFFFFFF )
    205     {
    206         g_any_key_hit++;
    207     }
    208 
    209     // Ideally we would also stop sending zeros to the LED driver PWM buffers
    210     // while suspended and just do a software shutdown. This is a cheap hack for now.
    211     bool suspend_backlight = ((g_suspend_state && g_config.disable_when_usb_suspended) ||
    212             (g_config.disable_after_timeout > 0 && g_any_key_hit > g_config.disable_after_timeout * 60 * 20));
    213     uint8_t effect = suspend_backlight ? 0 : g_config.effect;
    214 
    215     // Keep track of the effect used last time,
    216     // detect change in effect, so each effect can
    217     // have an optional initialization.
    218     static uint8_t effect_last = 255;
    219     bool initialize = effect != effect_last;
    220     effect_last = effect;
    221 
    222     // this gets ticked at 20 Hz.
    223     // each effect can opt to do calculations
    224     // and/or request PWM buffer updates.
    225     switch ( effect )
    226     {
    227         case 0:
    228             backlight_effect_all_off();
    229             break;
    230         case 1:
    231             backlight_effect_all_on();;
    232             break;
    233         case 2:
    234             backlight_effect_raindrops(initialize);
    235             break;
    236         default:
    237             backlight_effect_all_off();
    238             break;
    239 	}
    240 
    241     if ( ! suspend_backlight )
    242     {
    243         backlight_effect_indicators();
    244     }
    245 }
    246 
    247 // Some helpers for setting/getting HSV
    248 void _set_color( HS *color, uint8_t *data )
    249 {
    250     color->h = data[0];
    251     color->s = data[1];
    252 }
    253 
    254 void _get_color( HS *color, uint8_t *data )
    255 {
    256     data[0] = color->h;
    257     data[1] = color->s;
    258 }
    259 
    260 void backlight_config_set_value( uint8_t *data )
    261 {
    262     bool reinitialize = false;
    263     uint8_t *value_id = &(data[0]);
    264     uint8_t *value_data = &(data[1]);
    265     switch ( *value_id )
    266     {
    267         case id_disable_when_usb_suspended:
    268         {
    269             g_config.disable_when_usb_suspended = (bool)*value_data;
    270             break;
    271         }
    272         case id_disable_after_timeout:
    273         {
    274             g_config.disable_after_timeout = *value_data;
    275             break;
    276         }
    277         case id_brightness:
    278         {
    279             g_config.brightness = *value_data;
    280             break;
    281         }
    282         case id_effect:
    283         {
    284             g_config.effect = *value_data;
    285             break;
    286         }
    287         case id_effect_speed:
    288         {
    289             g_config.effect_speed = *value_data;
    290             break;
    291         }
    292         case id_color_1:
    293         {
    294             _set_color( &(g_config.color_1), value_data );
    295             break;
    296         }
    297     }
    298 
    299     if ( reinitialize )
    300     {
    301         backlight_init_drivers();
    302     }
    303 }
    304 
    305 void backlight_config_get_value( uint8_t *data )
    306 {
    307     uint8_t *value_id = &(data[0]);
    308     uint8_t *value_data = &(data[1]);
    309     switch ( *value_id )
    310     {
    311         case id_disable_when_usb_suspended:
    312         {
    313             *value_data = ( g_config.disable_when_usb_suspended ? 1 : 0 );
    314             break;
    315         }
    316         case id_disable_after_timeout:
    317         {
    318             *value_data = g_config.disable_after_timeout;
    319             break;
    320         }
    321         case id_brightness:
    322         {
    323             *value_data = g_config.brightness;
    324             break;
    325         }
    326         case id_effect:
    327         {
    328             *value_data = g_config.effect;
    329             break;
    330         }
    331         case id_effect_speed:
    332         {
    333             *value_data = g_config.effect_speed;
    334             break;
    335         }
    336         case id_color_1:
    337         {
    338             _get_color( &(g_config.color_1), value_data );
    339             break;
    340         }
    341     }
    342 }
    343 
    344 void backlight_config_load(void)
    345 {
    346     eeprom_read_block( &g_config, ((void*)MONO_BACKLIGHT_CONFIG_EEPROM_ADDR), sizeof(backlight_config) );
    347 }
    348 
    349 void backlight_config_save(void)
    350 {
    351     eeprom_update_block( &g_config, ((void*)MONO_BACKLIGHT_CONFIG_EEPROM_ADDR), sizeof(backlight_config) );
    352 }
    353 
    354 void backlight_update_pwm_buffers(void)
    355 {
    356     is31fl3736_flush();
    357 }
    358 
    359 bool process_record_backlight(uint16_t keycode, keyrecord_t *record)
    360 {
    361     // Record keypresses for backlight effects
    362     if ( record->event.pressed )
    363     {
    364         backlight_set_key_hit( record->event.key.row, record->event.key.col );
    365     }
    366 
    367     switch(keycode)
    368     {
    369         case BR_INC:
    370             if (record->event.pressed)
    371             {
    372                 backlight_brightness_increase();
    373             }
    374             return false;
    375             break;
    376         case BR_DEC:
    377             if (record->event.pressed)
    378             {
    379                 backlight_brightness_decrease();
    380             }
    381             return false;
    382             break;
    383         case EF_INC:
    384             if (record->event.pressed)
    385             {
    386                 backlight_effect_increase();
    387             }
    388             return false;
    389             break;
    390         case EF_DEC:
    391             if (record->event.pressed)
    392             {
    393                 backlight_effect_decrease();
    394             }
    395             return false;
    396             break;
    397         case ES_INC:
    398             if (record->event.pressed)
    399             {
    400                 backlight_effect_speed_increase();
    401             }
    402             return false;
    403             break;
    404         case ES_DEC:
    405             if (record->event.pressed)
    406             {
    407                 backlight_effect_speed_decrease();
    408             }
    409             return false;
    410             break;
    411     }
    412 
    413     return true;
    414 }
    415 
    416 // Deals with the messy details of incrementing an integer
    417 uint8_t increment( uint8_t value, uint8_t step, uint8_t min, uint8_t max )
    418 {
    419     int16_t new_value = value;
    420     new_value += step;
    421     return MIN( MAX( new_value, min ), max );
    422 }
    423 
    424 uint8_t decrement( uint8_t value, uint8_t step, uint8_t min, uint8_t max )
    425 {
    426     int16_t new_value = value;
    427     new_value -= step;
    428     return MIN( MAX( new_value, min ), max );
    429 }
    430 
    431 void backlight_effect_increase(void)
    432 {
    433     g_config.effect = increment( g_config.effect, 1, 0, BACKLIGHT_EFFECT_MAX );
    434     backlight_config_save();
    435 }
    436 
    437 void backlight_effect_decrease(void)
    438 {
    439     g_config.effect = decrement( g_config.effect, 1, 0, BACKLIGHT_EFFECT_MAX );
    440     backlight_config_save();
    441 }
    442 
    443 void backlight_effect_speed_increase(void)
    444 {
    445     g_config.effect_speed = increment( g_config.effect_speed, 1, 0, 3 );
    446     backlight_config_save();
    447 }
    448 
    449 void backlight_effect_speed_decrease(void)
    450 {
    451     g_config.effect_speed = decrement( g_config.effect_speed, 1, 0, 3 );
    452     backlight_config_save();
    453 }
    454 
    455 void backlight_brightness_increase(void)
    456 {
    457     g_config.brightness = increment( g_config.brightness, 8, 0, 255 );
    458     backlight_config_save();
    459 }
    460 
    461 void backlight_brightness_decrease(void)
    462 {
    463     g_config.brightness = decrement( g_config.brightness, 8, 0, 255 );
    464     backlight_config_save();
    465 }
    466 
    467 void backlight_device_indication(uint8_t value)
    468 {
    469     static uint8_t current_effect = 0;
    470     static uint8_t alternate_effect = 0;
    471     if ( value == 0 ) {
    472         current_effect = g_config.effect;
    473         alternate_effect = g_config.effect > 0 ? 0 : 1;
    474     }
    475     g_config.effect = value % 2 == 0 ? alternate_effect : current_effect;
    476 }