From fd698c43d78ebbc42c1eb2bec74078b791616ad1 Mon Sep 17 00:00:00 2001 From: skullY Date: Wed, 23 Jan 2019 15:43:48 -0800 Subject: The beginning of a simple led matrix driver for is31fl3731 --- quantum/led_matrix.c | 404 +++++++++++++++++++++++++++++++++++++++++++ quantum/led_matrix.h | 142 +++++++++++++++ quantum/led_matrix_drivers.c | 147 ++++++++++++++++ quantum/rgb_matrix.h | 12 +- 4 files changed, 695 insertions(+), 10 deletions(-) create mode 100644 quantum/led_matrix.c create mode 100644 quantum/led_matrix.h create mode 100644 quantum/led_matrix_drivers.c (limited to 'quantum') diff --git a/quantum/led_matrix.c b/quantum/led_matrix.c new file mode 100644 index 000000000..9a0aa6acd --- /dev/null +++ b/quantum/led_matrix.c @@ -0,0 +1,404 @@ +/* Copyright 2017 Jason Williams + * Copyright 2017 Jack Humbert + * Copyright 2018 Yiancar + * Copyright 2019 Clueboard + * + * 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 . + */ + +#include +#include +#include "quantum.h" +#include "led_matrix.h" +#include "progmem.h" +#include "config.h" +#include "eeprom.h" +#include +#include + +led_config_t led_matrix_config; + +#ifndef MAX + #define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) +#endif + +#ifndef MIN + #define MIN(a,b) ((a) < (b)? (a): (b)) +#endif + +#ifndef LED_DISABLE_AFTER_TIMEOUT + #define LED_DISABLE_AFTER_TIMEOUT 0 +#endif + +#ifndef LED_DISABLE_WHEN_USB_SUSPENDED + #define LED_DISABLE_WHEN_USB_SUSPENDED false +#endif + +#ifndef EECONFIG_LED_MATRIX + #define EECONFIG_LED_MATRIX EECONFIG_RGBLIGHT +#endif + +#if !defined(LED_MATRIX_MAXIMUM_BRIGHTNESS) || LED_MATRIX_MAXIMUM_BRIGHTNESS > 255 + #define LED_MATRIX_MAXIMUM_BRIGHTNESS 255 +#endif + +bool g_suspend_state = false; + +// Global tick at 20 Hz +uint32_t g_tick = 0; + +// Ticks since this key was last hit. +uint8_t g_key_hit[DRIVER_LED_TOTAL]; + +// Ticks since any key was last hit. +uint32_t g_any_key_hit = 0; + +uint32_t eeconfig_read_led_matrix(void) { + return eeprom_read_dword(EECONFIG_LED_MATRIX); +} +void eeconfig_update_led_matrix(uint32_t config_value) { + eeprom_update_dword(EECONFIG_LED_MATRIX, config_value); +} +void eeconfig_update_led_matrix_default(void) { + dprintf("eeconfig_update_led_matrix_default\n"); + led_matrix_config.enable = 1; + led_matrix_config.mode = LED_MATRIX_UNIFORM_BRIGHTNESS; + led_matrix_config.val = 128; + led_matrix_config.speed = 0; + eeconfig_update_led_matrix(led_matrix_config.raw); +} +void eeconfig_debug_led_matrix(void) { + dprintf("led_matrix_config eprom\n"); + dprintf("led_matrix_config.enable = %d\n", led_matrix_config.enable); + dprintf("led_matrix_config.mode = %d\n", led_matrix_config.mode); + dprintf("led_matrix_config.val = %d\n", led_matrix_config.val); + dprintf("led_matrix_config.speed = %d\n", led_matrix_config.speed); +} + +// Last led hit +#define LED_HITS_TO_REMEMBER 8 +uint8_t g_last_led_hit[LED_HITS_TO_REMEMBER] = {255}; +uint8_t g_last_led_count = 0; + +void map_row_column_to_led(uint8_t row, uint8_t column, uint8_t *led_i, uint8_t *led_count) { + led_matrix led; + *led_count = 0; + + for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) { + // map_index_to_led(i, &led); + led = g_leds[i]; + if (row == led.matrix_co.row && column == led.matrix_co.col) { + led_i[*led_count] = i; + (*led_count)++; + } + } +} + +void led_matrix_update_pwm_buffers(void) { + led_matrix_driver.flush(); +} + +void led_matrix_set_index_value(int index, uint8_t value) { + led_matrix_driver.set_value(index, value); +} + +void led_matrix_set_index_value_all(uint8_t value) { + led_matrix_driver.set_value_all(value); +} + +bool process_led_matrix(uint16_t keycode, keyrecord_t *record) { + if (record->event.pressed) { + uint8_t led[8], led_count; + map_row_column_to_led(record->event.key.row, record->event.key.col, led, &led_count); + if (led_count > 0) { + for (uint8_t i = LED_HITS_TO_REMEMBER; i > 1; i--) { + g_last_led_hit[i - 1] = g_last_led_hit[i - 2]; + } + g_last_led_hit[0] = led[0]; + g_last_led_count = MIN(LED_HITS_TO_REMEMBER, g_last_led_count + 1); + } + for(uint8_t i = 0; i < led_count; i++) + g_key_hit[led[i]] = 0; + g_any_key_hit = 0; + } else { + #ifdef LED_MATRIX_KEYRELEASES + uint8_t led[8], led_count; + map_row_column_to_led(record->event.key.row, record->event.key.col, led, &led_count); + for(uint8_t i = 0; i < led_count; i++) + g_key_hit[led[i]] = 255; + + g_any_key_hit = 255; + #endif + } + return true; +} + +void led_matrix_set_suspend_state(bool state) { + g_suspend_state = state; +} + +// All LEDs off +void led_matrix_all_off(void) { + led_matrix_set_index_value_all(0); +} + +// Uniform brightness +void led_matrix_uniform_brightness(void) { + led_matrix_set_index_value_all(led_matrix_config.val); +} + +void led_matrix_custom(void) {} + +void led_matrix_task(void) { + #ifdef TRACK_PREVIOUS_EFFECT + static uint8_t toggle_enable_last = 255; + #endif + if (!led_matrix_config.enable) { + led_matrix_all_off(); + led_matrix_indicators(); + #ifdef TRACK_PREVIOUS_EFFECT + toggle_enable_last = led_matrix_config.enable; + #endif + return; + } + + // delay 1 second before driving LEDs or doing anything else + // FIXME: Can't we use wait_ms() here? + 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++; + } + + for (int led = 0; led < DRIVER_LED_TOTAL; led++) { + if (g_key_hit[led] < 255) { + if (g_key_hit[led] == 254) + g_last_led_count = MAX(g_last_led_count - 1, 0); + g_key_hit[led]++; + } + } + + // Factory default magic value + if (led_matrix_config.mode == 255) { + led_matrix_uniform_brightness(); + return; + } + + // 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 && LED_DISABLE_WHEN_USB_SUSPENDED) || + (LED_DISABLE_AFTER_TIMEOUT > 0 && g_any_key_hit > LED_DISABLE_AFTER_TIMEOUT * 60 * 20)); + uint8_t effect = suspend_backlight ? 0 : led_matrix_config.mode; + + #ifdef TRACK_PREVIOUS_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) || (led_matrix_config.enable != toggle_enable_last); + effect_last = effect; + toggle_enable_last = led_matrix_config.enable; + #endif + + // this gets ticked at 20 Hz. + // each effect can opt to do calculations + // and/or request PWM buffer updates. + switch (effect) { + case LED_MATRIX_UNIFORM_BRIGHTNESS: + led_matrix_uniform_brightness(); + break; + default: + led_matrix_custom(); + break; + } + + if (! suspend_backlight) { + led_matrix_indicators(); + } + +} + +void led_matrix_indicators(void) { + led_matrix_indicators_kb(); + led_matrix_indicators_user(); +} + +__attribute__((weak)) +void led_matrix_indicators_kb(void) {} + +__attribute__((weak)) +void led_matrix_indicators_user(void) {} + + +// void led_matrix_set_indicator_index(uint8_t *index, uint8_t row, uint8_t column) +// { +// if (row >= MATRIX_ROWS) +// { +// // Special value, 255=none, 254=all +// *index = row; +// } +// else +// { +// // This needs updated to something like +// // uint8_t led[8], led_count; +// // map_row_column_to_led(row,column,led,&led_count); +// // for(uint8_t i = 0; i < led_count; i++) +// map_row_column_to_led(row, column, index); +// } +// } + +void led_matrix_init(void) { + led_matrix_driver.init(); + + // TODO: put the 1 second startup delay here? + + // clear the key hits + for (int led=0; led= LED_MATRIX_EFFECT_MAX) + led_matrix_config.mode = 1; + eeconfig_update_led_matrix(led_matrix_config.raw); +} + +void led_matrix_step_reverse(void) { + led_matrix_config.mode--; + if (led_matrix_config.mode < 1) + led_matrix_config.mode = LED_MATRIX_EFFECT_MAX - 1; + eeconfig_update_led_matrix(led_matrix_config.raw); +} + +void led_matrix_increase_val(void) { + led_matrix_config.val = increment(led_matrix_config.val, 8, 0, LED_MATRIX_MAXIMUM_BRIGHTNESS); + eeconfig_update_led_matrix(led_matrix_config.raw); +} + +void led_matrix_decrease_val(void) { + led_matrix_config.val = decrement(led_matrix_config.val, 8, 0, LED_MATRIX_MAXIMUM_BRIGHTNESS); + eeconfig_update_led_matrix(led_matrix_config.raw); +} + +void led_matrix_increase_speed(void) { + led_matrix_config.speed = increment(led_matrix_config.speed, 1, 0, 3); + eeconfig_update_led_matrix(led_matrix_config.raw);//EECONFIG needs to be increased to support this +} + +void led_matrix_decrease_speed(void) { + led_matrix_config.speed = decrement(led_matrix_config.speed, 1, 0, 3); + eeconfig_update_led_matrix(led_matrix_config.raw);//EECONFIG needs to be increased to support this +} + +void led_matrix_mode(uint8_t mode, bool eeprom_write) { + led_matrix_config.mode = mode; + if (eeprom_write) { + eeconfig_update_led_matrix(led_matrix_config.raw); + } +} + +uint8_t led_matrix_get_mode(void) { + return led_matrix_config.mode; +} + +void led_matrix_set_value(uint8_t val, bool eeprom_write) { + led_matrix_config.val = val; + if (eeprom_write) { + eeconfig_update_led_matrix(led_matrix_config.raw); + } +} diff --git a/quantum/led_matrix.h b/quantum/led_matrix.h new file mode 100644 index 000000000..20f2e73c6 --- /dev/null +++ b/quantum/led_matrix.h @@ -0,0 +1,142 @@ +/* Copyright 2017 Jason Williams + * Copyright 2017 Jack Humbert + * Copyright 2018 Yiancar + * Copyright 2019 Clueboard + * + * 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 . + */ + +#ifndef LED_MATRIX_H +#define LED_MATRIX_H + + +typedef struct Point { + uint8_t x; + uint8_t y; +} __attribute__((packed)) Point; + +typedef struct led_matrix { + union { + uint8_t raw; + struct { + uint8_t row:4; // 16 max + uint8_t col:4; // 16 max + }; + } matrix_co; + Point point; + uint8_t modifier:1; +} __attribute__((packed)) led_matrix; + +extern const led_matrix g_leds[DRIVER_LED_TOTAL]; + +typedef struct { + uint8_t index; + uint8_t value; +} led_indicator; + +typedef union { + uint32_t raw; + struct { + bool enable :1; + uint8_t mode :6; + uint8_t hue :8; // Unused by led_matrix + uint8_t sat :8; // Unused by led_matrix + uint8_t val :8; + uint8_t speed :8;//EECONFIG needs to be increased to support this + }; +} led_config_t; + +enum led_matrix_effects { + LED_MATRIX_UNIFORM_BRIGHTNESS = 1, + // All new effects go above this line + LED_MATRIX_EFFECT_MAX +}; + +void led_matrix_set_index_value(int index, uint8_t value); +void led_matrix_set_index_value_all(uint8_t value); + +// This runs after another backlight effect and replaces +// colors already set +void led_matrix_indicators(void); +void led_matrix_indicators_kb(void); +void led_matrix_indicators_user(void); + +void led_matrix_init(void); +void led_matrix_setup_drivers(void); + +void led_matrix_set_suspend_state(bool state); +void led_matrix_set_indicator_state(uint8_t state); + +void led_matrix_task(void); + +// This should not be called from an interrupt +// (eg. from a timer interrupt). +// Call this while idle (in between matrix scans). +// If the buffer is dirty, it will update the driver with the buffer. +void led_matrix_update_pwm_buffers(void); + +bool process_led_matrix(uint16_t keycode, keyrecord_t *record); + +uint32_t led_matrix_get_tick(void); + +void led_matrix_toggle(void); +void led_matrix_enable(void); +void led_matrix_enable_noeeprom(void); +void led_matrix_disable(void); +void led_matrix_disable_noeeprom(void); +void led_matrix_step(void); +void led_matrix_step_reverse(void); +void led_matrix_increase_val(void); +void led_matrix_decrease_val(void); +void led_matrix_increase_speed(void); +void led_matrix_decrease_speed(void); +void led_matrix_mode(uint8_t mode, bool eeprom_write); +void led_matrix_mode_noeeprom(uint8_t mode); +uint8_t led_matrix_get_mode(void); +void led_matrix_set_value(uint8_t mode, bool eeprom_write); + +#ifndef BACKLIGHT_ENABLE +#define backlight_toggle() backlight_matrix_toggle() +#define backlight_enable() backlight_matrix_enable() +#define backlight_enable_noeeprom() backlight_matrix_enable_noeeprom() +#define backlight_disable() backlight_matrix_disable() +#define backlight_disable_noeeprom() backlight_matrix_disable_noeeprom() +#define backlight_step() backlight_matrix_step() +#define backlight_set_value(val) backlight_matrix_set_value(val) +#define backlight_set_value_noeeprom(val) backlight_matrix_set_value_noeeprom(val) +#define backlight_step_reverse() backlight_matrix_step_reverse() +#define backlight_increase_val() backlight_matrix_increase_val() +#define backlight_decrease_val() backlight_matrix_decrease_val() +#define backlight_increase_speed() backlight_matrix_increase_speed() +#define backlight_decrease_speed() backlight_matrix_decrease_speed() +#define backlight_mode(mode) backlight_matrix_mode(mode) +#define backlight_mode_noeeprom(mode) backlight_matrix_mode_noeeprom(mode) +#define backlight_get_mode() backlight_matrix_get_mode() +#endif + +typedef struct { + /* Perform any initialisation required for the other driver functions to work. */ + void (*init)(void); + + /* Set the brightness of a single LED in the buffer. */ + void (*set_value)(int index, uint8_t value); + /* Set the brightness of all LEDS on the keyboard in the buffer. */ + void (*set_value_all)(uint8_t value); + /* Flush any buffered changes to the hardware. */ + void (*flush)(void); +} led_matrix_driver_t; + +extern const led_matrix_driver_t led_matrix_driver; + +#endif diff --git a/quantum/led_matrix_drivers.c b/quantum/led_matrix_drivers.c new file mode 100644 index 000000000..f00f4f366 --- /dev/null +++ b/quantum/led_matrix_drivers.c @@ -0,0 +1,147 @@ +/* Copyright 2018 Clueboard + * + * 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 . + */ + +#include +#include +#include "quantum.h" +#include "led_matrix.h" + +/* Each driver needs to define a struct: + * + * const led_matrix_driver_t led_matrix_driver; + * + * All members must be provided. Keyboard custom drivers must define this + * in their own files. + */ + +#if defined(IS31FL3731) || defined(IS31FL3733) + +#if defined(IS31FL3731) + #include "is31fl3731-simple.h" +#endif + +#include "i2c_master.h" + +static void init(void) { + i2c_init(); + #ifdef IS31FL3731 + #ifdef LED_DRIVER_ADDR_1 + IS31FL3731_init(DRIVER_ADDR_1); + #endif + #ifdef LED_DRIVER_ADDR_2 + IS31FL3731_init(DRIVER_ADDR_2); + #endif + #ifdef LED_DRIVER_ADDR_3 + IS31FL3731_init(DRIVER_ADDR_3); + #endif + #ifdef LED_DRIVER_ADDR_4 + IS31FL3731_init(DRIVER_ADDR_4); + #endif + #else + #ifdef LED_DRIVER_ADDR_1 + IS31FL3733_init(DRIVER_ADDR_1); + #endif + #ifdef LED_DRIVER_ADDR_2 + IS31FL3733_init(DRIVER_ADDR_2); + #endif + #ifdef LED_DRIVER_ADDR_3 + IS31FL3733_init(DRIVER_ADDR_3); + #endif + #ifdef LED_DRIVER_ADDR_4 + IS31FL3733_init(DRIVER_ADDR_4); + #endif + #endif + for (int index = 0; index < DRIVER_LED_TOTAL; index++) { + #ifdef IS31FL3731 + IS31FL3731_set_led_control_register(index, true); + #else + IS31FL3733_set_led_control_register(index, true); + #endif + } + // This actually updates the LED drivers + #ifdef IS31FL3731 + #ifdef LED_DRIVER_ADDR_1 + IS31FL3731_update_led_control_registers(DRIVER_ADDR_1); + #endif + #ifdef LED_DRIVER_ADDR_2 + IS31FL3731_update_led_control_registers(DRIVER_ADDR_2); + #endif + #ifdef LED_DRIVER_ADDR_3 + IS31FL3731_update_led_control_registers(DRIVER_ADDR_3); + #endif + #ifdef LED_DRIVER_ADDR_4 + IS31FL3731_update_led_control_registers(DRIVER_ADDR_4); + #endif + #else + #ifdef LED_DRIVER_ADDR_1 + IS31FL3733_update_led_control_registers(DRIVER_ADDR_1); + #endif + #ifdef LED_DRIVER_ADDR_2 + IS31FL3733_update_led_control_registers(DRIVER_ADDR_2); + #endif + #ifdef LED_DRIVER_ADDR_3 + IS31FL3733_update_led_control_registers(DRIVER_ADDR_3); + #endif + #ifdef LED_DRIVER_ADDR_4 + IS31FL3733_update_led_control_registers(DRIVER_ADDR_4); + #endif + #endif +} + +static void flush(void) { + #ifdef IS31FL3731 + #ifdef LED_DRIVER_ADDR_1 + IS31FL3731_update_pwm_buffers(DRIVER_ADDR_1); + #endif + #ifdef LED_DRIVER_ADDR_2 + IS31FL3731_update_pwm_buffers(DRIVER_ADDR_2); + #endif + #ifdef LED_DRIVER_ADDR_3 + IS31FL3731_update_pwm_buffers(DRIVER_ADDR_3); + #endif + #ifdef LED_DRIVER_ADDR_4 + IS31FL3731_update_pwm_buffers(DRIVER_ADDR_4); + #endif + #else + #ifdef LED_DRIVER_ADDR_1 + IS31FL3733_update_pwm_buffers(DRIVER_ADDR_1); + #endif + #ifdef LED_DRIVER_ADDR_2 + IS31FL3733_update_pwm_buffers(DRIVER_ADDR_2); + #endif + #ifdef LED_DRIVER_ADDR_3 + IS31FL3733_update_pwm_buffers(DRIVER_ADDR_3); + #endif + #ifdef LED_DRIVER_ADDR_4 + IS31FL3733_update_pwm_buffers(DRIVER_ADDR_4); + #endif + #endif +} + +const led_matrix_driver_t led_matrix_driver = { + .init = init, + .flush = flush, +#ifdef IS31FL3731 + .set_value = IS31FL3731_set_value, + .set_value_all = IS31FL3731_set_value_all, +#else + .set_value = IS31FL3733_set_value, + .set_value_all = IS31FL3733_set_value_all, +#endif +}; + + +#endif diff --git a/quantum/rgb_matrix.h b/quantum/rgb_matrix.h index e43532d11..b64ddec07 100644 --- a/quantum/rgb_matrix.h +++ b/quantum/rgb_matrix.h @@ -50,25 +50,17 @@ typedef struct rgb_led { extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL]; -typedef struct -{ - HSV color; - uint8_t index; -} rgb_indicator; - typedef union { uint32_t raw; struct { bool enable :1; uint8_t mode :6; - uint16_t hue :9; - uint8_t sat :8; uint8_t val :8; uint8_t speed :8;//EECONFIG needs to be increased to support this }; -} rgb_config_t; +} led_config_t; -enum rgb_matrix_effects { +enum _matrix_effects { RGB_MATRIX_SOLID_COLOR = 1, #ifndef DISABLE_RGB_MATRIX_ALPHAS_MODS RGB_MATRIX_ALPHAS_MODS, -- cgit v1.2.3-24-g4f1b