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 }