From 288c2313fe3b705a07166971210e91a33bb1add4 Mon Sep 17 00:00:00 2001 From: Ryan Date: Wed, 27 Sep 2023 14:37:06 +1000 Subject: [PATCH] is31fl3218: Add LED Matrix support (#22126) Co-authored-by: Joel Challis --- builddefs/common_features.mk | 9 +- docs/reference_info_json.md | 2 +- drivers/led/issi/is31fl3218-simple.c | 143 ++++++++++++++++++++++++ drivers/led/issi/is31fl3218-simple.h | 60 ++++++++++ quantum/led_matrix/led_matrix.h | 4 +- quantum/led_matrix/led_matrix_drivers.c | 36 ++++-- 6 files changed, 243 insertions(+), 11 deletions(-) create mode 100644 drivers/led/issi/is31fl3218-simple.c create mode 100644 drivers/led/issi/is31fl3218-simple.h diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk index edc6f30d64..c20c8be8bd 100644 --- a/builddefs/common_features.mk +++ b/builddefs/common_features.mk @@ -354,7 +354,7 @@ LED_MATRIX_DRIVER := aw20216s endif LED_MATRIX_ENABLE ?= no -VALID_LED_MATRIX_TYPES := is31fl3731 is31fl3736 is31fl3742a is31fl3743a is31fl3745 is31fl3746a ckled2001 custom +VALID_LED_MATRIX_TYPES := is31fl3218 is31fl3731 is31fl3736 is31fl3742a is31fl3743a is31fl3745 is31fl3746a ckled2001 custom # TODO: is31fl3733 is31fl3737 is31fl3741 ifeq ($(strip $(LED_MATRIX_ENABLE)), yes) @@ -377,6 +377,13 @@ endif SRC += $(LIB_PATH)/lib8tion/lib8tion.c CIE1931_CURVE := yes + ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3218) + OPT_DEFS += -DHAL_USE_I2C=TRUE + COMMON_VPATH += $(DRIVER_PATH)/led/issi + SRC += is31fl3218-simple.c + QUANTUM_LIB_SRC += i2c_master.c + endif + ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3731) OPT_DEFS += -DHAL_USE_I2C=TRUE COMMON_VPATH += $(DRIVER_PATH)/led/issi diff --git a/docs/reference_info_json.md b/docs/reference_info_json.md index 3ea85932af..55de09f3c7 100644 --- a/docs/reference_info_json.md +++ b/docs/reference_info_json.md @@ -329,7 +329,7 @@ Configures the [LED Matrix](feature_led_matrix.md) feature. * The centroid (geometric center) of the LEDs. Used for certain effects. * Default: `[112, 32]` * `driver` (Required) - * The driver to use. Must be one of `ckled2001`, `custom`, `is31fl3731`, `is31fl3736`, `is31fl3742a`, `is31fl3743a`, `is31fl3745`, `is31fl3746a`. + * The driver to use. Must be one of `ckled2001`, `custom`, `is31fl3218`, `is31fl3731`, `is31fl3736`, `is31fl3742a`, `is31fl3743a`, `is31fl3745`, `is31fl3746a`. * `layout` (Required) * List of LED configuration dictionaries. Each dictionary contains: * `flags` (Required) diff --git a/drivers/led/issi/is31fl3218-simple.c b/drivers/led/issi/is31fl3218-simple.c new file mode 100644 index 0000000000..a78dedfcf1 --- /dev/null +++ b/drivers/led/issi/is31fl3218-simple.c @@ -0,0 +1,143 @@ +/* Copyright 2018 Jason Williams (Wilba) + * + * 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 "is31fl3218.h" +#include +#include "i2c_master.h" + +// These are the register addresses +#define IS31FL3218_REG_SHUTDOWN 0x00 +#define IS31FL3218_REG_PWM 0x01 +#define IS31FL3218_REG_CONTROL 0x13 +#define IS31FL3218_REG_UPDATE 0x16 +#define IS31FL3218_REG_RESET 0x17 + +#ifndef IS31FL3218_I2C_TIMEOUT +# define IS31FL3218_I2C_TIMEOUT 100 +#endif + +#ifndef IS31FL3218_I2C_PERSISTENCE +# define IS31FL3218_I2C_PERSISTENCE 0 +#endif + +// Reusable buffer for transfers +uint8_t g_twi_transfer_buffer[20]; + +// IS31FL3218 has 18 PWM outputs and a fixed I2C address, so no chaining. +uint8_t g_pwm_buffer[18]; +bool g_pwm_buffer_update_required = false; + +uint8_t g_led_control_registers[3] = {0}; +bool g_led_control_registers_update_required = false; + +void is31fl3218_write_register(uint8_t reg, uint8_t data) { + g_twi_transfer_buffer[0] = reg; + g_twi_transfer_buffer[1] = data; +#if IS31FL3218_I2C_PERSISTENCE > 0 + for (uint8_t i = 0; i < IS31FL3218_I2C_PERSISTENCE; i++) { + if (i2c_transmit(IS31FL3218_I2C_ADDRESS << 1, g_twi_transfer_buffer, 2, IS31FL3218_I2C_TIMEOUT) == 0) break; + } +#else + i2c_transmit(IS31FL3218_I2C_ADDRESS << 1, g_twi_transfer_buffer, 2, IS31FL3218_I2C_TIMEOUT); +#endif +} + +void is31fl3218_write_pwm_buffer(uint8_t *pwm_buffer) { + g_twi_transfer_buffer[0] = IS31FL3218_REG_PWM; + memcpy(g_twi_transfer_buffer + 1, pwm_buffer, 18); + +#if IS31FL3218_I2C_PERSISTENCE > 0 + for (uint8_t i = 0; i < IS31FL3218_I2C_PERSISTENCE; i++) { + i2c_transmit(IS31FL3218_I2C_ADDRESS << 1, g_twi_transfer_buffer, 19, IS31FL3218_I2C_TIMEOUT); + } +#else + i2c_transmit(IS31FL3218_I2C_ADDRESS << 1, g_twi_transfer_buffer, 19, IS31FL3218_I2C_TIMEOUT); +#endif +} + +void is31fl3218_init(void) { + // In case we ever want to reinitialize (?) + is31fl3218_write_register(IS31FL3218_REG_RESET, 0x00); + + // Turn off software shutdown + is31fl3218_write_register(IS31FL3218_REG_SHUTDOWN, 0x01); + + // Set all PWM values to zero + for (uint8_t i = 0; i < 18; i++) { + is31fl3218_write_register(IS31FL3218_REG_PWM + i, 0x00); + } + + // turn off all LEDs in the LED control register + for (uint8_t i = 0; i < 3; i++) { + is31fl3218_write_register(IS31FL3218_REG_CONTROL + i, 0x00); + } + + // Load PWM registers and LED Control register data + is31fl3218_write_register(IS31FL3218_REG_UPDATE, 0x01); +} + +void is31fl3218_set_value(int index, uint8_t value) { + is31fl3218_led_t led; + if (index >= 0 && index < LED_MATRIX_LED_COUNT) { + memcpy_P(&led, (&g_is31fl3218_leds[index]), sizeof(led)); + } + if (g_pwm_buffer[led.v - IS31FL3218_REG_PWM] == value) { + return; + } + g_pwm_buffer[led.v - IS31FL3218_REG_PWM] = value; + g_pwm_buffer_update_required = true; +} + +void is31fl3218_set_value_all(uint8_t value) { + for (int i = 0; i < LED_MATRIX_LED_COUNT; i++) { + is31fl3218_set_value(i, value); + } +} + +void is31fl3218_set_led_control_register(uint8_t index, bool value) { + is31fl3218_led_t led; + memcpy_P(&led, (&g_is31fl3218_leds[index]), sizeof(led)); + + uint8_t control_register = (led.v - IS31FL3218_REG_PWM) / 6; + uint8_t bit_value = (led.v - IS31FL3218_REG_PWM) % 6; + + if (value) { + g_led_control_registers[control_register] |= (1 << bit_value); + } else { + g_led_control_registers[control_register] &= ~(1 << bit_value); + } + + g_led_control_registers_update_required = true; +} + +void is31fl3218_update_pwm_buffers(void) { + if (g_pwm_buffer_update_required) { + is31fl3218_write_pwm_buffer(g_pwm_buffer); + // Load PWM registers and LED Control register data + is31fl3218_write_register(IS31FL3218_REG_UPDATE, 0x01); + + g_pwm_buffer_update_required = false; + } +} + +void is31fl3218_update_led_control_registers(void) { + if (g_led_control_registers_update_required) { + for (int i = 0; i < 3; i++) { + is31fl3218_write_register(IS31FL3218_REG_CONTROL + i, g_led_control_registers[i]); + } + + g_led_control_registers_update_required = false; + } +} diff --git a/drivers/led/issi/is31fl3218-simple.h b/drivers/led/issi/is31fl3218-simple.h new file mode 100644 index 0000000000..2260361ec6 --- /dev/null +++ b/drivers/led/issi/is31fl3218-simple.h @@ -0,0 +1,60 @@ +/* Copyright 2018 Jason Williams (Wilba) + * + * 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 . + */ + +#pragma once + +#include +#include +#include "progmem.h" + +#define IS31FL3218_I2C_ADDRESS 0x54 + +typedef struct is31fl3218_led_t { + uint8_t v; +} __attribute__((packed)) is31fl3218_led_t; + +extern const is31fl3218_led_t PROGMEM g_is31fl3218_leds[LED_MATRIX_LED_COUNT]; + +void is31fl3218_init(void); + +void is31fl3218_set_value(int index, uint8_t value); + +void is31fl3218_set_value_all(uint8_t value); + +void is31fl3218_set_led_control_register(uint8_t index, bool value); + +void is31fl3218_update_pwm_buffers(void); + +void is31fl3218_update_led_control_registers(void); + +#define OUT1 0x01 +#define OUT2 0x02 +#define OUT3 0x03 +#define OUT4 0x04 +#define OUT5 0x05 +#define OUT6 0x06 +#define OUT7 0x07 +#define OUT8 0x08 +#define OUT9 0x09 +#define OUT10 0x0A +#define OUT11 0x0B +#define OUT12 0x0C +#define OUT13 0x0D +#define OUT14 0x0E +#define OUT15 0x0F +#define OUT16 0x10 +#define OUT17 0x11 +#define OUT18 0x12 diff --git a/quantum/led_matrix/led_matrix.h b/quantum/led_matrix/led_matrix.h index 2b41f11bbf..cdc90097bb 100644 --- a/quantum/led_matrix/led_matrix.h +++ b/quantum/led_matrix/led_matrix.h @@ -25,7 +25,9 @@ #include "led_matrix_types.h" #include "keyboard.h" -#ifdef LED_MATRIX_IS31FL3731 +#if defined(LED_MATRIX_IS31FL3218) +# include "is31fl3218-simple.h" +#elif defined(LED_MATRIX_IS31FL3731) # include "is31fl3731-simple.h" #endif #ifdef LED_MATRIX_IS31FL3733 diff --git a/quantum/led_matrix/led_matrix_drivers.c b/quantum/led_matrix/led_matrix_drivers.c index 7ca0c7958f..27c53a223a 100644 --- a/quantum/led_matrix/led_matrix_drivers.c +++ b/quantum/led_matrix/led_matrix_drivers.c @@ -25,13 +25,16 @@ * in their own files. */ -#if defined(LED_MATRIX_IS31FL3731) || defined(LED_MATRIX_IS31FL3733) || defined(LED_MATRIX_IS31FL3736) || defined(IS31FLCOMMON) || defined(LED_MATRIX_CKLED2001) +#if defined(LED_MATRIX_IS31FL3218) || defined(LED_MATRIX_IS31FL3731) || defined(LED_MATRIX_IS31FL3733) || defined(LED_MATRIX_IS31FL3736) || defined(IS31FLCOMMON) || defined(LED_MATRIX_CKLED2001) # include "i2c_master.h" static void init(void) { i2c_init(); -# if defined(LED_MATRIX_IS31FL3731) +# if defined(LED_MATRIX_IS31FL3218) + is31fl3218_init(); + +# elif defined(LED_MATRIX_IS31FL3731) is31fl3731_init(LED_DRIVER_ADDR_1); # if defined(LED_DRIVER_ADDR_2) is31fl3731_init(LED_DRIVER_ADDR_2); @@ -109,7 +112,9 @@ static void init(void) { # endif for (int index = 0; index < LED_MATRIX_LED_COUNT; index++) { -# if defined(LED_MATRIX_IS31FL3731) +# if defined(LED_MATRIX_IS31FL3218) + is31fl3218_set_led_control_register(index, true); +# elif defined(LED_MATRIX_IS31FL3731) is31fl3731_set_led_control_register(index, true); # elif defined(LED_MATRIX_IS31FL3733) is31fl3733_set_led_control_register(index, true); @@ -123,7 +128,10 @@ static void init(void) { } // This actually updates the LED drivers -# if defined(LED_MATRIX_IS31FL3731) +# if defined(LED_MATRIX_IS31FL3218) + is31fl3218_update_led_control_registers(); + +# elif defined(LED_MATRIX_IS31FL3731) is31fl3731_update_led_control_registers(LED_DRIVER_ADDR_1, 0); # if defined(LED_DRIVER_ADDR_2) is31fl3731_update_led_control_registers(LED_DRIVER_ADDR_2, 1); @@ -187,7 +195,19 @@ static void init(void) { # endif } -# if defined(LED_MATRIX_IS31FL3731) +# if defined(LED_MATRIX_IS31FL3218) +static void flush(void) { + is31fl3218_update_pwm_buffers(); +} + +const led_matrix_driver_t led_matrix_driver = { + .init = init, + .flush = flush, + .set_value = is31fl3218_set_value, + .set_value_all = is31fl3218_set_value_all, +}; + +# elif defined(LED_MATRIX_IS31FL3731) static void flush(void) { is31fl3731_update_pwm_buffers(LED_DRIVER_ADDR_1, 0); # if defined(LED_DRIVER_ADDR_2) @@ -202,9 +222,9 @@ static void flush(void) { } const led_matrix_driver_t led_matrix_driver = { - .init = init, - .flush = flush, - .set_value = is31fl3731_set_value, + .init = init, + .flush = flush, + .set_value = is31fl3731_set_value, .set_value_all = is31fl3731_set_value_all, };