wt_mono_backlight.c 12 KB

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