Parent directory

wt_mono_backlight.c

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