summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorNick Brassel <nick@tzarc.org>2024-02-28 21:47:37 +1100
committerNick Brassel <nick@tzarc.org>2024-02-28 21:47:37 +1100
commit4e369d405af6bba1adce6337b2e1b1ea1788566c (patch)
treeb0f020feff1809e37c9e7795d344929ff0bb290a /drivers
parent4e04da397ef643f8fcf4afbe1d19f63aee1fc561 (diff)
parentdd1706e468bb18dd7f7ae143de735a5d3be1bfb8 (diff)
Merge branch 'develop'
Diffstat (limited to 'drivers')
-rw-r--r--drivers/bluetooth/bluefruit_le.cpp18
-rw-r--r--drivers/eeprom/eeprom_i2c.c12
-rw-r--r--drivers/encoder/encoder_quadrature.c213
-rw-r--r--drivers/gpio/mcp23018.c12
-rw-r--r--drivers/gpio/pca9505.c10
-rw-r--r--drivers/gpio/pca9555.c12
-rw-r--r--drivers/gpio/sn74x138.c24
-rw-r--r--drivers/gpio/sn74x154.c18
-rw-r--r--drivers/haptic/drv2605l.c2
-rw-r--r--drivers/haptic/solenoid.c14
-rw-r--r--drivers/lcd/hd44780.c48
-rw-r--r--drivers/lcd/st7565.c16
-rw-r--r--drivers/led/apa102.c115
-rw-r--r--drivers/led/apa102.h9
-rw-r--r--drivers/led/aw20216s.c42
-rw-r--r--drivers/led/aw20216s.h681
-rw-r--r--drivers/led/issi/is31fl3218-mono.c (renamed from drivers/led/issi/is31fl3218-simple.c)81
-rw-r--r--drivers/led/issi/is31fl3218-mono.h (renamed from drivers/led/issi/is31fl3218-simple.h)38
-rw-r--r--drivers/led/issi/is31fl3218.c99
-rw-r--r--drivers/led/issi/is31fl3218.h38
-rw-r--r--drivers/led/issi/is31fl3729-mono.c213
-rw-r--r--drivers/led/issi/is31fl3729-mono.h278
-rw-r--r--drivers/led/issi/is31fl3729.c219
-rw-r--r--drivers/led/issi/is31fl3729.h280
-rw-r--r--drivers/led/issi/is31fl3731-mono.c230
-rw-r--r--drivers/led/issi/is31fl3731-mono.h (renamed from drivers/led/issi/is31fl3731-simple.h)336
-rw-r--r--drivers/led/issi/is31fl3731-simple.c239
-rw-r--r--drivers/led/issi/is31fl3731.c214
-rw-r--r--drivers/led/issi/is31fl3731.h336
-rw-r--r--drivers/led/issi/is31fl3733-mono.c265
-rw-r--r--drivers/led/issi/is31fl3733-mono.h (renamed from drivers/led/issi/is31fl3733-simple.h)416
-rw-r--r--drivers/led/issi/is31fl3733-simple.c278
-rw-r--r--drivers/led/issi/is31fl3733.c233
-rw-r--r--drivers/led/issi/is31fl3733.h628
-rw-r--r--drivers/led/issi/is31fl3736-mono.c240
-rw-r--r--drivers/led/issi/is31fl3736-mono.h (renamed from drivers/led/issi/is31fl3736-simple.h)334
-rw-r--r--drivers/led/issi/is31fl3736-simple.c252
-rw-r--r--drivers/led/issi/is31fl3736.c204
-rw-r--r--drivers/led/issi/is31fl3736.h224
-rw-r--r--drivers/led/issi/is31fl3737-mono.c236
-rw-r--r--drivers/led/issi/is31fl3737-mono.h (renamed from drivers/led/issi/is31fl3737-simple.h)320
-rw-r--r--drivers/led/issi/is31fl3737-simple.c249
-rw-r--r--drivers/led/issi/is31fl3737.c207
-rw-r--r--drivers/led/issi/is31fl3737.h478
-rw-r--r--drivers/led/issi/is31fl3741-mono.c282
-rw-r--r--drivers/led/issi/is31fl3741-mono.h887
-rw-r--r--drivers/led/issi/is31fl3741-simple.c278
-rw-r--r--drivers/led/issi/is31fl3741-simple.h516
-rw-r--r--drivers/led/issi/is31fl3741.c278
-rw-r--r--drivers/led/issi/is31fl3741.h1117
-rw-r--r--drivers/led/issi/is31fl3742.h299
-rw-r--r--drivers/led/issi/is31fl3742a-mono.c221
-rw-r--r--drivers/led/issi/is31fl3742a-mono.h296
-rw-r--r--drivers/led/issi/is31fl3742a.c225
-rw-r--r--drivers/led/issi/is31fl3742a.h298
-rw-r--r--drivers/led/issi/is31fl3743.h327
-rw-r--r--drivers/led/issi/is31fl3743a-mono.c245
-rw-r--r--drivers/led/issi/is31fl3743a-mono.h328
-rw-r--r--drivers/led/issi/is31fl3743a.c249
-rw-r--r--drivers/led/issi/is31fl3743a.h541
-rw-r--r--drivers/led/issi/is31fl3745-mono.c245
-rw-r--r--drivers/led/issi/is31fl3745-mono.h271
-rw-r--r--drivers/led/issi/is31fl3745.c249
-rw-r--r--drivers/led/issi/is31fl3745.h461
-rw-r--r--drivers/led/issi/is31fl3746.h198
-rw-r--r--drivers/led/issi/is31fl3746a-mono.c222
-rw-r--r--drivers/led/issi/is31fl3746a-mono.h201
-rw-r--r--drivers/led/issi/is31fl3746a.c226
-rw-r--r--drivers/led/issi/is31fl3746a.h203
-rw-r--r--drivers/led/issi/is31flcommon.c330
-rw-r--r--drivers/led/issi/is31flcommon.h95
-rw-r--r--drivers/led/snled27351-mono.c250
-rw-r--r--drivers/led/snled27351-mono.h586
-rw-r--r--drivers/led/snled27351-simple.c266
-rw-r--r--drivers/led/snled27351-simple.h380
-rw-r--r--drivers/led/snled27351.c245
-rw-r--r--drivers/led/snled27351.h628
-rw-r--r--drivers/oled/oled_driver.c29
-rw-r--r--drivers/painter/comms/qp_comms_i2c.c8
-rw-r--r--drivers/painter/comms/qp_comms_spi.c20
-rw-r--r--drivers/painter/comms/qp_comms_spi.h1
-rw-r--r--drivers/painter/ili9xxx/qp_ili9486.c298
-rw-r--r--drivers/painter/ili9xxx/qp_ili9486.h52
-rw-r--r--drivers/painter/ili9xxx/qp_ili9xxx_opcodes.h1
-rw-r--r--drivers/painter/sh1106/qp_sh1106.c14
-rw-r--r--drivers/painter/sh1106/qp_sh1106_opcodes.h2
-rw-r--r--drivers/sensors/adns5050.c32
-rw-r--r--drivers/sensors/adns9800.c2
-rw-r--r--drivers/sensors/analog_joystick.c75
-rw-r--r--drivers/sensors/azoteq_iqs5xx.c37
-rw-r--r--drivers/sensors/cirque_pinnacle.c27
-rw-r--r--drivers/sensors/cirque_pinnacle_i2c.c12
-rw-r--r--drivers/sensors/paw3204.c24
-rw-r--r--drivers/sensors/pimoroni_trackball.c4
-rw-r--r--drivers/sensors/pmw3320.c32
-rw-r--r--drivers/sensors/pmw3360.c3
-rw-r--r--drivers/usb2422.c10
-rw-r--r--drivers/ws2812.h6
98 files changed, 13364 insertions, 7179 deletions
diff --git a/drivers/bluetooth/bluefruit_le.cpp b/drivers/bluetooth/bluefruit_le.cpp
index 39c14ddd13..218eca2195 100644
--- a/drivers/bluetooth/bluefruit_le.cpp
+++ b/drivers/bluetooth/bluefruit_le.cpp
@@ -188,7 +188,7 @@ static bool sdep_recv_pkt(struct sdep_msg *msg, uint16_t timeout) {
bool ready = false;
do {
- ready = readPin(BLUEFRUIT_LE_IRQ_PIN);
+ ready = gpio_read_pin(BLUEFRUIT_LE_IRQ_PIN);
if (ready) {
break;
}
@@ -231,7 +231,7 @@ static void resp_buf_read_one(bool greedy) {
return;
}
- if (readPin(BLUEFRUIT_LE_IRQ_PIN)) {
+ if (gpio_read_pin(BLUEFRUIT_LE_IRQ_PIN)) {
struct sdep_msg msg;
again:
@@ -242,7 +242,7 @@ static void resp_buf_read_one(bool greedy) {
dprintf("recv latency %dms\n", TIMER_DIFF_16(timer_read(), last_send));
}
- if (greedy && resp_buf.peek(last_send) && readPin(BLUEFRUIT_LE_IRQ_PIN)) {
+ if (greedy && resp_buf.peek(last_send) && gpio_read_pin(BLUEFRUIT_LE_IRQ_PIN)) {
goto again;
}
}
@@ -293,16 +293,16 @@ void bluefruit_le_init(void) {
state.configured = false;
state.is_connected = false;
- setPinInput(BLUEFRUIT_LE_IRQ_PIN);
+ gpio_set_pin_input(BLUEFRUIT_LE_IRQ_PIN);
spi_init();
// Perform a hardware reset
- setPinOutput(BLUEFRUIT_LE_RST_PIN);
- writePinHigh(BLUEFRUIT_LE_RST_PIN);
- writePinLow(BLUEFRUIT_LE_RST_PIN);
+ gpio_set_pin_output(BLUEFRUIT_LE_RST_PIN);
+ gpio_write_pin_high(BLUEFRUIT_LE_RST_PIN);
+ gpio_write_pin_low(BLUEFRUIT_LE_RST_PIN);
wait_ms(10);
- writePinHigh(BLUEFRUIT_LE_RST_PIN);
+ gpio_write_pin_high(BLUEFRUIT_LE_RST_PIN);
wait_ms(1000); // Give it a second to initialize
@@ -508,7 +508,7 @@ void bluefruit_le_task(void) {
resp_buf_read_one(true);
send_buf_send_one(SdepShortTimeout);
- if (resp_buf.empty() && (state.event_flags & UsingEvents) && readPin(BLUEFRUIT_LE_IRQ_PIN)) {
+ if (resp_buf.empty() && (state.event_flags & UsingEvents) && gpio_read_pin(BLUEFRUIT_LE_IRQ_PIN)) {
// Must be an event update
if (at_command_P(PSTR("AT+EVENTSTATUS"), resbuf, sizeof(resbuf))) {
uint32_t mask = strtoul(resbuf, NULL, 16);
diff --git a/drivers/eeprom/eeprom_i2c.c b/drivers/eeprom/eeprom_i2c.c
index a74a010415..0d3d5ccbe5 100644
--- a/drivers/eeprom/eeprom_i2c.c
+++ b/drivers/eeprom/eeprom_i2c.c
@@ -57,8 +57,8 @@ void eeprom_driver_init(void) {
i2c_init();
#if defined(EXTERNAL_EEPROM_WP_PIN)
/* We are setting the WP pin to high in a way that requires at least two bit-flips to change back to 0 */
- writePin(EXTERNAL_EEPROM_WP_PIN, 1);
- setPinInputHigh(EXTERNAL_EEPROM_WP_PIN);
+ gpio_write_pin(EXTERNAL_EEPROM_WP_PIN, 1);
+ gpio_set_pin_input_high(EXTERNAL_EEPROM_WP_PIN);
#endif
}
@@ -100,8 +100,8 @@ void eeprom_write_block(const void *buf, void *addr, size_t len) {
uintptr_t target_addr = (uintptr_t)addr;
#if defined(EXTERNAL_EEPROM_WP_PIN)
- setPinOutput(EXTERNAL_EEPROM_WP_PIN);
- writePin(EXTERNAL_EEPROM_WP_PIN, 0);
+ gpio_set_pin_output(EXTERNAL_EEPROM_WP_PIN);
+ gpio_write_pin(EXTERNAL_EEPROM_WP_PIN, 0);
#endif
while (len > 0) {
@@ -134,7 +134,7 @@ void eeprom_write_block(const void *buf, void *addr, size_t len) {
#if defined(EXTERNAL_EEPROM_WP_PIN)
/* We are setting the WP pin to high in a way that requires at least two bit-flips to change back to 0 */
- writePin(EXTERNAL_EEPROM_WP_PIN, 1);
- setPinInputHigh(EXTERNAL_EEPROM_WP_PIN);
+ gpio_write_pin(EXTERNAL_EEPROM_WP_PIN, 1);
+ gpio_set_pin_input_high(EXTERNAL_EEPROM_WP_PIN);
#endif
}
diff --git a/drivers/encoder/encoder_quadrature.c b/drivers/encoder/encoder_quadrature.c
new file mode 100644
index 0000000000..cd589bf1e2
--- /dev/null
+++ b/drivers/encoder/encoder_quadrature.c
@@ -0,0 +1,213 @@
+// Copyright 2018 Jack Humbert <jack.humb@gmail.com>
+// Copyright 2018-2023 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <stdint.h>
+#include "encoder.h"
+#include "gpio.h"
+#include "keyboard.h"
+#include "action.h"
+#include "keycodes.h"
+#include "wait.h"
+
+#ifdef SPLIT_KEYBOARD
+# include "split_util.h"
+#endif
+
+// for memcpy
+#include <string.h>
+
+#if !defined(ENCODER_RESOLUTIONS) && !defined(ENCODER_RESOLUTION)
+# define ENCODER_RESOLUTION 4
+#endif
+
+#undef ENCODER_DEFAULT_PIN_API_IMPL
+#if defined(ENCODERS_PAD_A) && defined(ENCODERS_PAD_B)
+// Inform the quadrature driver that it needs to implement pin init/read functions
+# define ENCODER_DEFAULT_PIN_API_IMPL
+#endif
+
+extern volatile bool isLeftHand;
+
+__attribute__((weak)) void encoder_quadrature_init_pin(uint8_t index, bool pad_b);
+__attribute__((weak)) uint8_t encoder_quadrature_read_pin(uint8_t index, bool pad_b);
+
+#ifdef ENCODER_DEFAULT_PIN_API_IMPL
+
+static pin_t encoders_pad_a[NUM_ENCODERS_MAX_PER_SIDE] = ENCODERS_PAD_A;
+static pin_t encoders_pad_b[NUM_ENCODERS_MAX_PER_SIDE] = ENCODERS_PAD_B;
+
+__attribute__((weak)) void encoder_wait_pullup_charge(void) {
+ wait_us(100);
+}
+
+__attribute__((weak)) void encoder_quadrature_init_pin(uint8_t index, bool pad_b) {
+ pin_t pin = pad_b ? encoders_pad_b[index] : encoders_pad_a[index];
+ if (pin != NO_PIN) {
+ gpio_set_pin_input_high(pin);
+ }
+}
+
+__attribute__((weak)) uint8_t encoder_quadrature_read_pin(uint8_t index, bool pad_b) {
+ pin_t pin = pad_b ? encoders_pad_b[index] : encoders_pad_a[index];
+ if (pin != NO_PIN) {
+ return gpio_read_pin(pin) ? 1 : 0;
+ }
+ return 0;
+}
+
+#endif // ENCODER_DEFAULT_PIN_API_IMPL
+
+#ifdef ENCODER_RESOLUTIONS
+static uint8_t encoder_resolutions[NUM_ENCODERS] = ENCODER_RESOLUTIONS;
+#endif
+
+#ifndef ENCODER_DIRECTION_FLIP
+# define ENCODER_CLOCKWISE true
+# define ENCODER_COUNTER_CLOCKWISE false
+#else
+# define ENCODER_CLOCKWISE false
+# define ENCODER_COUNTER_CLOCKWISE true
+#endif
+static int8_t encoder_LUT[] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};
+
+static uint8_t encoder_state[NUM_ENCODERS] = {0};
+static int8_t encoder_pulses[NUM_ENCODERS] = {0};
+
+// encoder counts
+static uint8_t thisCount;
+#ifdef SPLIT_KEYBOARD
+// encoder offsets for each hand
+static uint8_t thisHand, thatHand;
+// encoder counts for each hand
+static uint8_t thatCount;
+#endif
+
+__attribute__((weak)) void encoder_quadrature_post_init_kb(void) {
+ extern void encoder_quadrature_handle_read(uint8_t index, uint8_t pin_a_state, uint8_t pin_b_state);
+ // Unused normally, but can be used for things like setting up pin-change interrupts in keyboard code.
+ // During the interrupt, read the pins then call `encoder_handle_read()` with the pin states and it'll queue up an encoder event if needed.
+}
+
+void encoder_quadrature_post_init(void) {
+#ifdef ENCODER_DEFAULT_PIN_API_IMPL
+ for (uint8_t i = 0; i < thisCount; i++) {
+ encoder_quadrature_init_pin(i, false);
+ encoder_quadrature_init_pin(i, true);
+ }
+ encoder_wait_pullup_charge();
+ for (uint8_t i = 0; i < thisCount; i++) {
+ encoder_state[i] = (encoder_quadrature_read_pin(i, false) << 0) | (encoder_quadrature_read_pin(i, true) << 1);
+ }
+#else
+ memset(encoder_state, 0, sizeof(encoder_state));
+#endif
+
+ encoder_quadrature_post_init_kb();
+}
+
+void encoder_driver_init(void) {
+#ifdef SPLIT_KEYBOARD
+ thisHand = isLeftHand ? 0 : NUM_ENCODERS_LEFT;
+ thatHand = NUM_ENCODERS_LEFT - thisHand;
+ thisCount = isLeftHand ? NUM_ENCODERS_LEFT : NUM_ENCODERS_RIGHT;
+ thatCount = isLeftHand ? NUM_ENCODERS_RIGHT : NUM_ENCODERS_LEFT;
+#else // SPLIT_KEYBOARD
+ thisCount = NUM_ENCODERS;
+#endif
+
+#ifdef ENCODER_TESTS
+ // Annoying that we have to clear out values during initialisation here, but
+ // because all the arrays are static locals, rerunning tests in the same
+ // executable doesn't reset any of these. Kinda crappy having test-only code
+ // here, but it's the simplest solution.
+ memset(encoder_state, 0, sizeof(encoder_state));
+ memset(encoder_pulses, 0, sizeof(encoder_pulses));
+ const pin_t encoders_pad_a_left[] = ENCODERS_PAD_A;
+ const pin_t encoders_pad_b_left[] = ENCODERS_PAD_B;
+ for (uint8_t i = 0; i < thisCount; i++) {
+ encoders_pad_a[i] = encoders_pad_a_left[i];
+ encoders_pad_b[i] = encoders_pad_b_left[i];
+ }
+#endif
+
+#if defined(SPLIT_KEYBOARD) && defined(ENCODERS_PAD_A_RIGHT) && defined(ENCODERS_PAD_B_RIGHT)
+ // Re-initialise the pads if it's the right-hand side
+ if (!isLeftHand) {
+ const pin_t encoders_pad_a_right[] = ENCODERS_PAD_A_RIGHT;
+ const pin_t encoders_pad_b_right[] = ENCODERS_PAD_B_RIGHT;
+ for (uint8_t i = 0; i < thisCount; i++) {
+ encoders_pad_a[i] = encoders_pad_a_right[i];
+ encoders_pad_b[i] = encoders_pad_b_right[i];
+ }
+ }
+#endif // defined(SPLIT_KEYBOARD) && defined(ENCODERS_PAD_A_RIGHT) && defined(ENCODERS_PAD_B_RIGHT)
+
+ // Encoder resolutions is defined differently in config.h, so concatenate
+#if defined(SPLIT_KEYBOARD) && defined(ENCODER_RESOLUTIONS)
+# if defined(ENCODER_RESOLUTIONS_RIGHT)
+ static const uint8_t encoder_resolutions_right[NUM_ENCODERS_RIGHT] = ENCODER_RESOLUTIONS_RIGHT;
+# else // defined(ENCODER_RESOLUTIONS_RIGHT)
+ static const uint8_t encoder_resolutions_right[NUM_ENCODERS_RIGHT] = ENCODER_RESOLUTIONS;
+# endif // defined(ENCODER_RESOLUTIONS_RIGHT)
+ for (uint8_t i = 0; i < NUM_ENCODERS_RIGHT; i++) {
+ encoder_resolutions[NUM_ENCODERS_LEFT + i] = encoder_resolutions_right[i];
+ }
+#endif // defined(SPLIT_KEYBOARD) && defined(ENCODER_RESOLUTIONS)
+
+ encoder_quadrature_post_init();
+}
+
+static void encoder_handle_state_change(uint8_t index, uint8_t state) {
+ uint8_t i = index;
+
+#ifdef SPLIT_KEYBOARD
+ index += thisHand;
+#endif
+
+#ifdef ENCODER_RESOLUTIONS
+ const uint8_t resolution = encoder_resolutions[index];
+#else
+ const uint8_t resolution = ENCODER_RESOLUTION;
+#endif
+
+ encoder_pulses[i] += encoder_LUT[state & 0xF];
+
+#ifdef ENCODER_DEFAULT_POS
+ if ((encoder_pulses[i] >= resolution) || (encoder_pulses[i] <= -resolution) || ((state & 0x3) == ENCODER_DEFAULT_POS)) {
+ if (encoder_pulses[i] >= 1) {
+#else
+ if (encoder_pulses[i] >= resolution) {
+#endif
+
+ encoder_queue_event(index, ENCODER_COUNTER_CLOCKWISE);
+ }
+
+#ifdef ENCODER_DEFAULT_POS
+ if (encoder_pulses[i] <= -1) {
+#else
+ if (encoder_pulses[i] <= -resolution) { // direction is arbitrary here, but this clockwise
+#endif
+ encoder_queue_event(index, ENCODER_CLOCKWISE);
+ }
+ encoder_pulses[i] %= resolution;
+#ifdef ENCODER_DEFAULT_POS
+ encoder_pulses[i] = 0;
+ }
+#endif
+}
+
+void encoder_quadrature_handle_read(uint8_t index, uint8_t pin_a_state, uint8_t pin_b_state) {
+ uint8_t state = pin_a_state | (pin_b_state << 1);
+ if ((encoder_state[index] & 0x3) != state) {
+ encoder_state[index] <<= 2;
+ encoder_state[index] |= state;
+ encoder_handle_state_change(index, encoder_state[index]);
+ }
+}
+
+__attribute__((weak)) void encoder_driver_task(void) {
+ for (uint8_t i = 0; i < thisCount; i++) {
+ encoder_quadrature_handle_read(i, encoder_quadrature_read_pin(i, false), encoder_quadrature_read_pin(i, true));
+ }
+}
diff --git a/drivers/gpio/mcp23018.c b/drivers/gpio/mcp23018.c
index 41cbfe087e..3eca4f9d34 100644
--- a/drivers/gpio/mcp23018.c
+++ b/drivers/gpio/mcp23018.c
@@ -33,13 +33,13 @@ bool mcp23018_set_config(uint8_t slave_addr, mcp23018_port_t port, uint8_t conf)
uint8_t cmdDirection = port ? CMD_IODIRB : CMD_IODIRA;
uint8_t cmdPullup = port ? CMD_GPPUB : CMD_GPPUA;
- i2c_status_t ret = i2c_writeReg(addr, cmdDirection, &conf, sizeof(conf), TIMEOUT);
+ i2c_status_t ret = i2c_write_register(addr, cmdDirection, &conf, sizeof(conf), TIMEOUT);
if (ret != I2C_STATUS_SUCCESS) {
dprintf("mcp23018_set_config::directionFAILED::%u\n", ret);
return false;
}
- ret = i2c_writeReg(addr, cmdPullup, &conf, sizeof(conf), TIMEOUT);
+ ret = i2c_write_register(addr, cmdPullup, &conf, sizeof(conf), TIMEOUT);
if (ret != I2C_STATUS_SUCCESS) {
dprintf("mcp23018_set_config::pullupFAILED::%u\n", ret);
return false;
@@ -52,7 +52,7 @@ bool mcp23018_set_output(uint8_t slave_addr, mcp23018_port_t port, uint8_t conf)
uint8_t addr = SLAVE_TO_ADDR(slave_addr);
uint8_t cmd = port ? CMD_GPIOB : CMD_GPIOA;
- i2c_status_t ret = i2c_writeReg(addr, cmd, &conf, sizeof(conf), TIMEOUT);
+ i2c_status_t ret = i2c_write_register(addr, cmd, &conf, sizeof(conf), TIMEOUT);
if (ret != I2C_STATUS_SUCCESS) {
dprintf("mcp23018_set_output::FAILED::%u\n", ret);
return false;
@@ -65,7 +65,7 @@ bool mcp23018_set_output_all(uint8_t slave_addr, uint8_t confA, uint8_t confB) {
uint8_t addr = SLAVE_TO_ADDR(slave_addr);
uint8_t conf[2] = {confA, confB};
- i2c_status_t ret = i2c_writeReg(addr, CMD_GPIOA, &conf[0], sizeof(conf), TIMEOUT);
+ i2c_status_t ret = i2c_write_register(addr, CMD_GPIOA, &conf[0], sizeof(conf), TIMEOUT);
if (ret != I2C_STATUS_SUCCESS) {
dprintf("mcp23018_set_output::FAILED::%u\n", ret);
return false;
@@ -78,7 +78,7 @@ bool mcp23018_readPins(uint8_t slave_addr, mcp23018_port_t port, uint8_t* out) {
uint8_t addr = SLAVE_TO_ADDR(slave_addr);
uint8_t cmd = port ? CMD_GPIOB : CMD_GPIOA;
- i2c_status_t ret = i2c_readReg(addr, cmd, out, sizeof(uint8_t), TIMEOUT);
+ i2c_status_t ret = i2c_read_register(addr, cmd, out, sizeof(uint8_t), TIMEOUT);
if (ret != I2C_STATUS_SUCCESS) {
dprintf("mcp23018_readPins::FAILED::%u\n", ret);
return false;
@@ -97,7 +97,7 @@ bool mcp23018_readPins_all(uint8_t slave_addr, uint16_t* out) {
data16 data = {.u16 = 0};
- i2c_status_t ret = i2c_readReg(addr, CMD_GPIOA, &data.u8[0], sizeof(data), TIMEOUT);
+ i2c_status_t ret = i2c_read_register(addr, CMD_GPIOA, &data.u8[0], sizeof(data), TIMEOUT);
if (ret != I2C_STATUS_SUCCESS) {
dprintf("mcp23018_readPins::FAILED::%u\n", ret);
return false;
diff --git a/drivers/gpio/pca9505.c b/drivers/gpio/pca9505.c
index 5803746c96..5617a14a8b 100644
--- a/drivers/gpio/pca9505.c
+++ b/drivers/gpio/pca9505.c
@@ -41,8 +41,6 @@ void pca9505_init(uint8_t slave_addr) {
}
// TODO: could check device connected
- // i2c_start(SLAVE_TO_ADDR(slave) | I2C_WRITE);
- // i2c_stop();
}
bool pca9505_set_config(uint8_t slave_addr, pca9505_port_t port, uint8_t conf) {
@@ -66,7 +64,7 @@ bool pca9505_set_config(uint8_t slave_addr, pca9505_port_t port, uint8_t conf) {
break;
}
- i2c_status_t ret = i2c_writeReg(addr, cmd, &conf, sizeof(conf), TIMEOUT);
+ i2c_status_t ret = i2c_write_register(addr, cmd, &conf, sizeof(conf), TIMEOUT);
if (ret != I2C_STATUS_SUCCESS) {
print("pca9505_set_config::FAILED\n");
return false;
@@ -96,7 +94,7 @@ bool pca9505_set_polarity(uint8_t slave_addr, pca9505_port_t port, uint8_t conf)
break;
}
- i2c_status_t ret = i2c_writeReg(addr, cmd, &conf, sizeof(conf), TIMEOUT);
+ i2c_status_t ret = i2c_write_register(addr, cmd, &conf, sizeof(conf), TIMEOUT);
if (ret != I2C_STATUS_SUCCESS) {
print("pca9505_set_polarity::FAILED\n");
return false;
@@ -126,7 +124,7 @@ bool pca9505_set_output(uint8_t slave_addr, pca9505_port_t port, uint8_t conf) {
break;
}
- i2c_status_t ret = i2c_writeReg(addr, cmd, &conf, sizeof(conf), TIMEOUT);
+ i2c_status_t ret = i2c_write_register(addr, cmd, &conf, sizeof(conf), TIMEOUT);
if (ret != I2C_STATUS_SUCCESS) {
print("pca9505_set_output::FAILED\n");
return false;
@@ -156,7 +154,7 @@ bool pca9505_readPins(uint8_t slave_addr, pca9505_port_t port, uint8_t* out) {
break;
}
- i2c_status_t ret = i2c_readReg(addr, cmd, out, sizeof(uint8_t), TIMEOUT);
+ i2c_status_t ret = i2c_read_register(addr, cmd, out, sizeof(uint8_t), TIMEOUT);
if (ret != I2C_STATUS_SUCCESS) {
print("pca9505_readPins::FAILED\n");
return false;
diff --git a/drivers/gpio/pca9555.c b/drivers/gpio/pca9555.c
index adcd040083..0fc30099ac 100644
--- a/drivers/gpio/pca9555.c
+++ b/drivers/gpio/pca9555.c
@@ -29,15 +29,13 @@ void pca9555_init(uint8_t slave_addr) {
}
// TODO: could check device connected
- // i2c_start(SLAVE_TO_ADDR(slave) | I2C_WRITE);
- // i2c_stop();
}
bool pca9555_set_config(uint8_t slave_addr, pca9555_port_t port, uint8_t conf) {
uint8_t addr = SLAVE_TO_ADDR(slave_addr);
uint8_t cmd = port ? CMD_CONFIG_1 : CMD_CONFIG_0;
- i2c_status_t ret = i2c_writeReg(addr, cmd, &conf, sizeof(conf), TIMEOUT);
+ i2c_status_t ret = i2c_write_register(addr, cmd, &conf, sizeof(conf), TIMEOUT);
if (ret != I2C_STATUS_SUCCESS) {
print("pca9555_set_config::FAILED\n");
return false;
@@ -50,7 +48,7 @@ bool pca9555_set_output(uint8_t slave_addr, pca9555_port_t port, uint8_t conf) {
uint8_t addr = SLAVE_TO_ADDR(slave_addr);
uint8_t cmd = port ? CMD_OUTPUT_1 : CMD_OUTPUT_0;
- i2c_status_t ret = i2c_writeReg(addr, cmd, &conf, sizeof(conf), TIMEOUT);
+ i2c_status_t ret = i2c_write_register(addr, cmd, &conf, sizeof(conf), TIMEOUT);
if (ret != I2C_STATUS_SUCCESS) {
print("pca9555_set_output::FAILED\n");
return false;
@@ -63,7 +61,7 @@ bool pca9555_set_output_all(uint8_t slave_addr, uint8_t confA, uint8_t confB) {
uint8_t addr = SLAVE_TO_ADDR(slave_addr);
uint8_t conf[2] = {confA, confB};
- i2c_status_t ret = i2c_writeReg(addr, CMD_OUTPUT_0, &conf[0], sizeof(conf), TIMEOUT);
+ i2c_status_t ret = i2c_write_register(addr, CMD_OUTPUT_0, &conf[0], sizeof(conf), TIMEOUT);
if (ret != I2C_STATUS_SUCCESS) {
dprintf("pca9555_set_output::FAILED::%u\n", ret);
return false;
@@ -76,7 +74,7 @@ bool pca9555_readPins(uint8_t slave_addr, pca9555_port_t port, uint8_t* out) {
uint8_t addr = SLAVE_TO_ADDR(slave_addr);
uint8_t cmd = port ? CMD_INPUT_1 : CMD_INPUT_0;
- i2c_status_t ret = i2c_readReg(addr, cmd, out, sizeof(uint8_t), TIMEOUT);
+ i2c_status_t ret = i2c_read_register(addr, cmd, out, sizeof(uint8_t), TIMEOUT);
if (ret != I2C_STATUS_SUCCESS) {
print("pca9555_readPins::FAILED\n");
return false;
@@ -95,7 +93,7 @@ bool pca9555_readPins_all(uint8_t slave_addr, uint16_t* out) {
data16 data = {.u16 = 0};
- i2c_status_t ret = i2c_readReg(addr, CMD_INPUT_0, &data.u8[0], sizeof(data), TIMEOUT);
+ i2c_status_t ret = i2c_read_register(addr, CMD_INPUT_0, &data.u8[0], sizeof(data), TIMEOUT);
if (ret != I2C_STATUS_SUCCESS) {
print("pca9555_readPins_all::FAILED\n");
return false;
diff --git a/drivers/gpio/sn74x138.c b/drivers/gpio/sn74x138.c
index 222e5db56c..1cf8e54f56 100644
--- a/drivers/gpio/sn74x138.c
+++ b/drivers/gpio/sn74x138.c
@@ -27,39 +27,39 @@ static const pin_t address_pins[ADDRESS_PIN_COUNT] = SN74X138_ADDRESS_PINS;
void sn74x138_init(void) {
for (int i = 0; i < ADDRESS_PIN_COUNT; i++) {
- setPinOutput(address_pins[i]);
- writePinLow(address_pins[i]);
+ gpio_set_pin_output(address_pins[i]);
+ gpio_write_pin_low(address_pins[i]);
}
#if defined(SN74X138_E1_PIN)
- setPinOutput(SN74X138_E1_PIN);
- writePinHigh(SN74X138_E1_PIN);
+ gpio_set_pin_output(SN74X138_E1_PIN);
+ gpio_write_pin_high(SN74X138_E1_PIN);
#endif
#if defined(SN74X138_E2_PIN)
- setPinOutput(SN74X138_E2_PIN);
- writePinHigh(SN74X138_E2_PIN);
+ gpio_set_pin_output(SN74X138_E2_PIN);
+ gpio_write_pin_high(SN74X138_E2_PIN);
#endif
#if defined(SN74X138_E3_PIN)
- setPinOutput(SN74X138_E3_PIN);
- writePinLow(SN74X138_E3_PIN);
+ gpio_set_pin_output(SN74X138_E3_PIN);
+ gpio_write_pin_low(SN74X138_E3_PIN);
#endif
}
void sn74x138_set_enabled(bool enabled) {
#if defined(SN74X138_E1_PIN)
- writePin(SN74X138_E1_PIN, !enabled);
+ gpio_write_pin(SN74X138_E1_PIN, !enabled);
#endif
#if defined(SN74X138_E2_PIN)
- writePin(SN74X138_E2_PIN, !enabled);
+ gpio_write_pin(SN74X138_E2_PIN, !enabled);
#endif
#if defined(SN74X138_E3_PIN)
- writePin(SN74X138_E3_PIN, enabled);
+ gpio_write_pin(SN74X138_E3_PIN, enabled);
#endif
}
void sn74x138_set_addr(uint8_t address) {
for (int i = 0; i < ADDRESS_PIN_COUNT; i++) {
- writePin(address_pins[i], address & (1 << i));
+ gpio_write_pin(address_pins[i], address & (1 << i));
}
}
diff --git a/drivers/gpio/sn74x154.c b/drivers/gpio/sn74x154.c
index 5f21f12b55..6226adf27e 100644
--- a/drivers/gpio/sn74x154.c
+++ b/drivers/gpio/sn74x154.c
@@ -27,32 +27,32 @@ static const pin_t address_pins[ADDRESS_PIN_COUNT] = SN74X154_ADDRESS_PINS;
void sn74x154_init(void) {
for (int i = 0; i < ADDRESS_PIN_COUNT; i++) {
- setPinOutput(address_pins[i]);
- writePinLow(address_pins[i]);
+ gpio_set_pin_output(address_pins[i]);
+ gpio_write_pin_low(address_pins[i]);
}
#if defined(SN74X154_E0_PIN)
- setPinOutput(SN74X154_E0_PIN);
- writePinHigh(SN74X154_E0_PIN);
+ gpio_set_pin_output(SN74X154_E0_PIN);
+ gpio_write_pin_high(SN74X154_E0_PIN);
#endif
#if defined(SN74X154_E1_PIN)
- setPinOutput(SN74X154_E1_PIN);
- writePinHigh(SN74X154_E1_PIN);
+ gpio_set_pin_output(SN74X154_E1_PIN);
+ gpio_write_pin_high(SN74X154_E1_PIN);
#endif
}
void sn74x154_set_enabled(bool enabled) {
#if defined(SN74X154_E0_PIN)
- writePin(SN74X154_E0_PIN, !enabled);
+ gpio_write_pin(SN74X154_E0_PIN, !enabled);
#endif
#if defined(SN74X154_E1_PIN)
- writePin(SN74X154_E1_PIN, !enabled);
+ gpio_write_pin(SN74X154_E1_PIN, !enabled);
#endif
}
void sn74x154_set_addr(uint8_t address) {
for (int i = 0; i < ADDRESS_PIN_COUNT; i++) {
- writePin(address_pins[i], address & (1 << i));
+ gpio_write_pin(address_pins[i], address & (1 << i));
}
}
diff --git a/drivers/haptic/drv2605l.c b/drivers/haptic/drv2605l.c
index 1ad2ad385f..a5adde5366 100644
--- a/drivers/haptic/drv2605l.c
+++ b/drivers/haptic/drv2605l.c
@@ -29,7 +29,7 @@ void drv2605l_write(uint8_t reg_addr, uint8_t data) {
}
uint8_t drv2605l_read(uint8_t reg_addr) {
- i2c_readReg(DRV2605L_I2C_ADDRESS << 1, reg_addr, &drv2605l_read_buffer, 1, 100);
+ i2c_read_register(DRV2605L_I2C_ADDRESS << 1, reg_addr, &drv2605l_read_buffer, 1, 100);
return drv2605l_read_buffer;
}
diff --git a/drivers/haptic/solenoid.c b/drivers/haptic/solenoid.c
index da4095cda4..346b88bbc4 100644
--- a/drivers/haptic/solenoid.c
+++ b/drivers/haptic/solenoid.c
@@ -61,7 +61,7 @@ void solenoid_set_dwell(uint8_t dwell) {
* @param index select which solenoid to check/stop
*/
void solenoid_stop(uint8_t index) {
- writePin(solenoid_pads[index], !solenoid_active_state[index]);
+ gpio_write_pin(solenoid_pads[index], !solenoid_active_state[index]);
solenoid_on[index] = false;
solenoid_buzzing[index] = false;
}
@@ -78,7 +78,7 @@ void solenoid_fire(uint8_t index) {
solenoid_on[index] = true;
solenoid_buzzing[index] = true;
solenoid_start[index] = timer_read();
- writePin(solenoid_pads[index], solenoid_active_state[index]);
+ gpio_write_pin(solenoid_pads[index], solenoid_active_state[index]);
}
/**
@@ -128,12 +128,12 @@ void solenoid_check(void) {
if ((elapsed[i] % (SOLENOID_BUZZ_ACTUATED + SOLENOID_BUZZ_NONACTUATED)) < SOLENOID_BUZZ_ACTUATED) {
if (!solenoid_buzzing[i]) {
solenoid_buzzing[i] = true;
- writePin(solenoid_pads[i], solenoid_active_state[i]);
+ gpio_write_pin(solenoid_pads[i], solenoid_active_state[i]);
}
} else {
if (solenoid_buzzing[i]) {
solenoid_buzzing[i] = false;
- writePin(solenoid_pads[i], !solenoid_active_state[i]);
+ gpio_write_pin(solenoid_pads[i], !solenoid_active_state[i]);
}
}
}
@@ -156,8 +156,8 @@ void solenoid_setup(void) {
#else
solenoid_active_state[i] = high;
#endif
- writePin(solenoid_pads[i], !solenoid_active_state[i]);
- setPinOutput(solenoid_pads[i]);
+ gpio_write_pin(solenoid_pads[i], !solenoid_active_state[i]);
+ gpio_set_pin_output(solenoid_pads[i]);
if ((!HAPTIC_OFF_IN_LOW_POWER) || (usb_device_state == USB_DEVICE_STATE_CONFIGURED)) {
solenoid_fire(i);
}
@@ -170,6 +170,6 @@ void solenoid_setup(void) {
*/
void solenoid_shutdown(void) {
for (uint8_t i = 0; i < NUMBER_OF_SOLENOIDS; i++) {
- writePin(solenoid_pads[i], !solenoid_active_state[i]);
+ gpio_write_pin(solenoid_pads[i], !solenoid_active_state[i]);
}
}
diff --git a/drivers/lcd/hd44780.c b/drivers/lcd/hd44780.c
index ccc50117ab..9b4e2f0226 100644
--- a/drivers/lcd/hd44780.c
+++ b/drivers/lcd/hd44780.c
@@ -57,67 +57,67 @@ static const pin_t data_pins[4] = HD44780_DATA_PINS;
#define HD44780_ENABLE_DELAY_US 1
static void hd44780_latch(void) {
- writePinHigh(HD44780_E_PIN);
+ gpio_write_pin_high(HD44780_E_PIN);
wait_us(HD44780_ENABLE_DELAY_US);
- writePinLow(HD44780_E_PIN);
+ gpio_write_pin_low(HD44780_E_PIN);
}
void hd44780_write(uint8_t data, bool isData) {
- writePin(HD44780_RS_PIN, isData);
- writePinLow(HD44780_RW_PIN);
+ gpio_write_pin(HD44780_RS_PIN, isData);
+ gpio_write_pin_low(HD44780_RW_PIN);
for (int i = 0; i < 4; i++) {
- setPinOutput(data_pins[i]);
+ gpio_set_pin_output(data_pins[i]);
}
// Write high nibble
for (int i = 0; i < 4; i++) {
- writePin(data_pins[i], (data >> 4) & (1 << i));
+ gpio_write_pin(data_pins[i], (data >> 4) & (1 << i));
}
hd44780_latch();
// Write low nibble
for (int i = 0; i < 4; i++) {
- writePin(data_pins[i], data & (1 << i));
+ gpio_write_pin(data_pins[i], data & (1 << i));
}
hd44780_latch();
for (int i = 0; i < 4; i++) {
- writePinHigh(data_pins[i]);
+ gpio_write_pin_high(data_pins[i]);
}
}
uint8_t hd44780_read(bool isData) {
uint8_t data = 0;
- writePin(HD44780_RS_PIN, isData);
- writePinHigh(HD44780_RW_PIN);
+ gpio_write_pin(HD44780_RS_PIN, isData);
+ gpio_write_pin_high(HD44780_RW_PIN);
for (int i = 0; i < 4; i++) {
- setPinInput(data_pins[i]);
+ gpio_set_pin_input(data_pins[i]);
}
- writePinHigh(HD44780_E_PIN);
+ gpio_write_pin_high(HD44780_E_PIN);
wait_us(HD44780_ENABLE_DELAY_US);
// Read high nibble
for (int i = 0; i < 4; i++) {
- data |= (readPin(data_pins[i]) << i);
+ data |= (gpio_read_pin(data_pins[i]) << i);
}
data <<= 4;
- writePinLow(HD44780_E_PIN);
+ gpio_write_pin_low(HD44780_E_PIN);
wait_us(HD44780_ENABLE_DELAY_US);
- writePinHigh(HD44780_E_PIN);
+ gpio_write_pin_high(HD44780_E_PIN);
wait_us(HD44780_ENABLE_DELAY_US);
// Read low nibble
for (int i = 0; i < 4; i++) {
- data |= (readPin(data_pins[i]) << i);
+ data |= (gpio_read_pin(data_pins[i]) << i);
}
- writePinLow(HD44780_E_PIN);
+ gpio_write_pin_low(HD44780_E_PIN);
return data;
}
@@ -171,20 +171,20 @@ void hd44780_set_ddram_address(uint8_t address) {
}
void hd44780_init(bool cursor, bool blink) {
- setPinOutput(HD44780_RS_PIN);
- setPinOutput(HD44780_RW_PIN);
- setPinOutput(HD44780_E_PIN);
+ gpio_set_pin_output(HD44780_RS_PIN);
+ gpio_set_pin_output(HD44780_RW_PIN);
+ gpio_set_pin_output(HD44780_E_PIN);
for (int i = 0; i < 4; i++) {
- setPinOutput(data_pins[i]);
+ gpio_set_pin_output(data_pins[i]);
}
wait_ms(HD44780_INIT_DELAY_MS);
// Manually configure for 4-bit mode - can't use hd44780_command() yet
// HD44780U datasheet, Fig. 24 (p46)
- writePinHigh(data_pins[0]); // Function set
- writePinHigh(data_pins[1]); // DL = 1
+ gpio_write_pin_high(data_pins[0]); // Function set
+ gpio_write_pin_high(data_pins[1]); // DL = 1
hd44780_latch();
wait_ms(5);
// Send again
@@ -194,7 +194,7 @@ void hd44780_init(bool cursor, bool blink) {
hd44780_latch();
wait_us(64);
- writePinLow(data_pins[0]); // DL = 0
+ gpio_write_pin_low(data_pins[0]); // DL = 0
hd44780_latch();
wait_us(64);
diff --git a/drivers/lcd/st7565.c b/drivers/lcd/st7565.c
index 47ee02804b..4fce40edbe 100644
--- a/drivers/lcd/st7565.c
+++ b/drivers/lcd/st7565.c
@@ -92,10 +92,10 @@ static void InvertCharacter(uint8_t *cursor) {
}
bool st7565_init(display_rotation_t rotation) {
- setPinOutput(ST7565_A0_PIN);
- writePinHigh(ST7565_A0_PIN);
- setPinOutput(ST7565_RST_PIN);
- writePinHigh(ST7565_RST_PIN);
+ gpio_set_pin_output(ST7565_A0_PIN);
+ gpio_write_pin_high(ST7565_A0_PIN);
+ gpio_set_pin_output(ST7565_RST_PIN);
+ gpio_write_pin_high(ST7565_RST_PIN);
st7565_rotation = st7565_init_user(rotation);
@@ -488,18 +488,18 @@ void st7565_task(void) {
__attribute__((weak)) void st7565_task_user(void) {}
void st7565_reset(void) {
- writePinLow(ST7565_RST_PIN);
+ gpio_write_pin_low(ST7565_RST_PIN);
wait_ms(20);
- writePinHigh(ST7565_RST_PIN);
+ gpio_write_pin_high(ST7565_RST_PIN);
wait_ms(20);
}
spi_status_t st7565_send_cmd(uint8_t cmd) {
- writePinLow(ST7565_A0_PIN);
+ gpio_write_pin_low(ST7565_A0_PIN);
return spi_write(cmd);
}
spi_status_t st7565_send_data(uint8_t *data, uint16_t length) {
- writePinHigh(ST7565_A0_PIN);
+ gpio_write_pin_high(ST7565_A0_PIN);
return spi_transmit(data, length);
}
diff --git a/drivers/led/apa102.c b/drivers/led/apa102.c
index 527519eb8a..d6d4327495 100644
--- a/drivers/led/apa102.c
+++ b/drivers/led/apa102.c
@@ -43,72 +43,37 @@
} \
} while (0)
-#define APA102_SEND_BIT(byte, bit) \
- do { \
- writePin(APA102_DI_PIN, (byte >> bit) & 1); \
- io_wait; \
- writePinHigh(APA102_CI_PIN); \
- io_wait; \
- writePinLow(APA102_CI_PIN); \
- io_wait; \
+#define APA102_SEND_BIT(byte, bit) \
+ do { \
+ gpio_write_pin(APA102_DI_PIN, (byte >> bit) & 1); \
+ io_wait; \
+ gpio_write_pin_high(APA102_CI_PIN); \
+ io_wait; \
+ gpio_write_pin_low(APA102_CI_PIN); \
+ io_wait; \
} while (0)
uint8_t apa102_led_brightness = APA102_DEFAULT_BRIGHTNESS;
-void static apa102_start_frame(void);
-void static apa102_end_frame(uint16_t num_leds);
-
-void static apa102_send_frame(uint8_t red, uint8_t green, uint8_t blue, uint8_t brightness);
-void static apa102_send_byte(uint8_t byte);
-
-void apa102_setleds(rgb_led_t *start_led, uint16_t num_leds) {
- rgb_led_t *end = start_led + num_leds;
-
- apa102_start_frame();
- for (rgb_led_t *led = start_led; led < end; led++) {
- apa102_send_frame(led->r, led->g, led->b, apa102_led_brightness);
- }
- apa102_end_frame(num_leds);
-}
-
-// Overwrite the default rgblight_call_driver to use apa102 driver
-void rgblight_call_driver(rgb_led_t *start_led, uint8_t num_leds) {
- apa102_setleds(start_led, num_leds);
-}
-
-void static apa102_init(void) {
- setPinOutput(APA102_DI_PIN);
- setPinOutput(APA102_CI_PIN);
-
- writePinLow(APA102_DI_PIN);
- writePinLow(APA102_CI_PIN);
-}
-
-void apa102_set_brightness(uint8_t brightness) {
- if (brightness > APA102_MAX_BRIGHTNESS) {
- apa102_led_brightness = APA102_MAX_BRIGHTNESS;
- } else if (brightness < 0) {
- apa102_led_brightness = 0;
- } else {
- apa102_led_brightness = brightness;
- }
-}
-
-void static apa102_send_frame(uint8_t red, uint8_t green, uint8_t blue, uint8_t brightness) {
- apa102_send_byte(0b11100000 | brightness);
- apa102_send_byte(blue);
- apa102_send_byte(green);
- apa102_send_byte(red);
+static void apa102_send_byte(uint8_t byte) {
+ APA102_SEND_BIT(byte, 7);
+ APA102_SEND_BIT(byte, 6);
+ APA102_SEND_BIT(byte, 5);
+ APA102_SEND_BIT(byte, 4);
+ APA102_SEND_BIT(byte, 3);
+ APA102_SEND_BIT(byte, 2);
+ APA102_SEND_BIT(byte, 1);
+ APA102_SEND_BIT(byte, 0);
}
-void static apa102_start_frame(void) {
+static void apa102_start_frame(void) {
apa102_init();
for (uint16_t i = 0; i < 4; i++) {
apa102_send_byte(0);
}
}
-void static apa102_end_frame(uint16_t num_leds) {
+static void apa102_end_frame(uint16_t num_leds) {
// This function has been taken from: https://github.com/pololu/apa102-arduino/blob/master/APA102.h
// and adapted. The code is MIT licensed. I think thats compatible?
//
@@ -141,13 +106,37 @@ void static apa102_end_frame(uint16_t num_leds) {
apa102_init();
}
-void static apa102_send_byte(uint8_t byte) {
- APA102_SEND_BIT(byte, 7);
- APA102_SEND_BIT(byte, 6);
- APA102_SEND_BIT(byte, 5);
- APA102_SEND_BIT(byte, 4);
- APA102_SEND_BIT(byte, 3);
- APA102_SEND_BIT(byte, 2);
- APA102_SEND_BIT(byte, 1);
- APA102_SEND_BIT(byte, 0);
+static void apa102_send_frame(uint8_t red, uint8_t green, uint8_t blue, uint8_t brightness) {
+ apa102_send_byte(0b11100000 | brightness);
+ apa102_send_byte(blue);
+ apa102_send_byte(green);
+ apa102_send_byte(red);
+}
+
+void apa102_init(void) {
+ gpio_set_pin_output(APA102_DI_PIN);
+ gpio_set_pin_output(APA102_CI_PIN);
+
+ gpio_write_pin_low(APA102_DI_PIN);
+ gpio_write_pin_low(APA102_CI_PIN);
+}
+
+void apa102_setleds(rgb_led_t *start_led, uint16_t num_leds) {
+ rgb_led_t *end = start_led + num_leds;
+
+ apa102_start_frame();
+ for (rgb_led_t *led = start_led; led < end; led++) {
+ apa102_send_frame(led->r, led->g, led->b, apa102_led_brightness);
+ }
+ apa102_end_frame(num_leds);
+}
+
+void apa102_set_brightness(uint8_t brightness) {
+ if (brightness > APA102_MAX_BRIGHTNESS) {
+ apa102_led_brightness = APA102_MAX_BRIGHTNESS;
+ } else if (brightness < 0) {
+ apa102_led_brightness = 0;
+ } else {
+ apa102_led_brightness = brightness;
+ }
}
diff --git a/drivers/led/apa102.h b/drivers/led/apa102.h
index cd0a19d445..5e2f78658b 100644
--- a/drivers/led/apa102.h
+++ b/drivers/led/apa102.h
@@ -19,13 +19,19 @@
#include "color.h"
+#if defined(RGBLIGHT_APA102)
+# define APA102_LED_COUNT RGBLIGHT_LED_COUNT
+#elif defined(RGB_MATRIX_APA102)
+# define APA102_LED_COUNT RGB_MATRIX_LED_COUNT
+#endif
+
#ifndef APA102_DEFAULT_BRIGHTNESS
# define APA102_DEFAULT_BRIGHTNESS 31
#endif
#define APA102_MAX_BRIGHTNESS 31
-extern uint8_t apa102_led_brightness;
+void apa102_init(void);
/* User Interface
*
@@ -38,4 +44,5 @@ extern uint8_t apa102_led_brightness;
* - Send out the LED data
*/
void apa102_setleds(rgb_led_t *start_led, uint16_t num_leds);
+
void apa102_set_brightness(uint8_t brightness);
diff --git a/drivers/led/aw20216s.c b/drivers/led/aw20216s.c
index ab7f3ccb42..704794f5b5 100644
--- a/drivers/led/aw20216s.c
+++ b/drivers/led/aw20216s.c
@@ -45,8 +45,15 @@
# define AW20216S_SPI_DIVISOR 4
#endif
-uint8_t g_pwm_buffer[AW20216S_DRIVER_COUNT][AW20216S_PWM_REGISTER_COUNT];
-bool g_pwm_buffer_update_required[AW20216S_DRIVER_COUNT] = {false};
+typedef struct aw20216s_driver_t {
+ uint8_t pwm_buffer[AW20216S_PWM_REGISTER_COUNT];
+ bool pwm_buffer_dirty;
+} PACKED aw20216s_driver_t;
+
+aw20216s_driver_t driver_buffers[AW20216S_DRIVER_COUNT] = {{
+ .pwm_buffer = {0},
+ .pwm_buffer_dirty = false,
+}};
bool aw20216s_write(pin_t cs_pin, uint8_t page, uint8_t reg, uint8_t* data, uint8_t len) {
static uint8_t s_spi_transfer_buffer[2] = {0};
@@ -106,16 +113,18 @@ static inline void aw20216s_auto_lowpower(pin_t cs_pin) {
void aw20216s_init_drivers(void) {
spi_init();
- aw20216s_init(AW20216S_CS_PIN_1, AW20216S_EN_PIN_1);
+#if defined(AW20216S_EN_PIN)
+ gpio_set_pin_output(AW20216S_EN_PIN);
+ gpio_write_pin_high(AW20216S_EN_PIN);
+#endif
+
+ aw20216s_init(AW20216S_CS_PIN_1);
#if defined(AW20216S_CS_PIN_2)
- aw20216s_init(AW20216S_CS_PIN_2, AW20216S_EN_PIN_2);
+ aw20216s_init(AW20216S_CS_PIN_2);
#endif
}
-void aw20216s_init(pin_t cs_pin, pin_t en_pin) {
- setPinOutput(en_pin);
- writePinHigh(en_pin);
-
+void aw20216s_init(pin_t cs_pin) {
aw20216s_soft_reset(cs_pin);
wait_ms(2);
@@ -131,13 +140,14 @@ void aw20216s_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
aw20216s_led_t led;
memcpy_P(&led, (&g_aw20216s_leds[index]), sizeof(led));
- if (g_pwm_buffer[led.driver][led.r] == red && g_pwm_buffer[led.driver][led.g] == green && g_pwm_buffer[led.driver][led.b] == blue) {
+ if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) {
return;
}
- g_pwm_buffer[led.driver][led.r] = red;
- g_pwm_buffer[led.driver][led.g] = green;
- g_pwm_buffer[led.driver][led.b] = blue;
- g_pwm_buffer_update_required[led.driver] = true;
+
+ driver_buffers[led.driver].pwm_buffer[led.r] = red;
+ driver_buffers[led.driver].pwm_buffer[led.g] = green;
+ driver_buffers[led.driver].pwm_buffer[led.b] = blue;
+ driver_buffers[led.driver].pwm_buffer_dirty = true;
}
void aw20216s_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
@@ -147,10 +157,10 @@ void aw20216s_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
}
void aw20216s_update_pwm_buffers(pin_t cs_pin, uint8_t index) {
- if (g_pwm_buffer_update_required[index]) {
- aw20216s_write(cs_pin, AW20216S_PAGE_PWM, 0, g_pwm_buffer[index], AW20216S_PWM_REGISTER_COUNT);
+ if (driver_buffers[index].pwm_buffer_dirty) {
+ aw20216s_write(cs_pin, AW20216S_PAGE_PWM, 0, driver_buffers[index].pwm_buffer, AW20216S_PWM_REGISTER_COUNT);
+ driver_buffers[index].pwm_buffer_dirty = false;
}
- g_pwm_buffer_update_required[index] = false;
}
void aw20216s_flush(void) {
diff --git a/drivers/led/aw20216s.h b/drivers/led/aw20216s.h
index 38a0c92b2f..b2c097125f 100644
--- a/drivers/led/aw20216s.h
+++ b/drivers/led/aw20216s.h
@@ -42,10 +42,10 @@
# define AW20216S_CS_PIN_2 DRIVER_2_CS
#endif
#ifdef DRIVER_1_EN
-# define AW20216S_EN_PIN_1 DRIVER_1_EN
+# define AW20216S_EN_PIN DRIVER_1_EN
#endif
-#ifdef DRIVER_2_EN
-# define AW20216S_EN_PIN_2 DRIVER_2_EN
+#ifdef AW20216S_EN_PIN_1
+# define AW20216S_EN_PIN AW20216S_EN_PIN_1
#endif
#define aw_led aw20216s_led_t
@@ -94,226 +94,467 @@ typedef struct aw20216s_led_t {
extern const aw20216s_led_t PROGMEM g_aw20216s_leds[AW20216S_LED_COUNT];
void aw20216s_init_drivers(void);
-void aw20216s_init(pin_t cs_pin, pin_t en_pin);
+void aw20216s_init(pin_t cs_pin);
void aw20216s_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
void aw20216s_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
void aw20216s_update_pwm_buffers(pin_t cs_pin, uint8_t index);
void aw20216s_flush(void);
-#define CS1_SW1 0x00
-#define CS2_SW1 0x01
-#define CS3_SW1 0x02
-#define CS4_SW1 0x03
-#define CS5_SW1 0x04
-#define CS6_SW1 0x05
-#define CS7_SW1 0x06
-#define CS8_SW1 0x07
-#define CS9_SW1 0x08
-#define CS10_SW1 0x09
-#define CS11_SW1 0x0A
-#define CS12_SW1 0x0B
-#define CS13_SW1 0x0C
-#define CS14_SW1 0x0D
-#define CS15_SW1 0x0E
-#define CS16_SW1 0x0F
-#define CS17_SW1 0x10
-#define CS18_SW1 0x11
-#define CS1_SW2 0x12
-#define CS2_SW2 0x13
-#define CS3_SW2 0x14
-#define CS4_SW2 0x15
-#define CS5_SW2 0x16
-#define CS6_SW2 0x17
-#define CS7_SW2 0x18
-#define CS8_SW2 0x19
-#define CS9_SW2 0x1A
-#define CS10_SW2 0x1B
-#define CS11_SW2 0x1C
-#define CS12_SW2 0x1D
-#define CS13_SW2 0x1E
-#define CS14_SW2 0x1F
-#define CS15_SW2 0x20
-#define CS16_SW2 0x21
-#define CS17_SW2 0x22
-#define CS18_SW2 0x23
-#define CS1_SW3 0x24
-#define CS2_SW3 0x25
-#define CS3_SW3 0x26
-#define CS4_SW3 0x27
-#define CS5_SW3 0x28
-#define CS6_SW3 0x29
-#define CS7_SW3 0x2A
-#define CS8_SW3 0x2B
-#define CS9_SW3 0x2C
-#define CS10_SW3 0x2D
-#define CS11_SW3 0x2E
-#define CS12_SW3 0x2F
-#define CS13_SW3 0x30
-#define CS14_SW3 0x31
-#define CS15_SW3 0x32
-#define CS16_SW3 0x33
-#define CS17_SW3 0x34
-#define CS18_SW3 0x35
-#define CS1_SW4 0x36
-#define CS2_SW4 0x37
-#define CS3_SW4 0x38
-#define CS4_SW4 0x39
-#define CS5_SW4 0x3A
-#define CS6_SW4 0x3B
-#define CS7_SW4 0x3C
-#define CS8_SW4 0x3D
-#define CS9_SW4 0x3E
-#define CS10_SW4 0x3F
-#define CS11_SW4 0x40
-#define CS12_SW4 0x41
-#define CS13_SW4 0x42
-#define CS14_SW4 0x43
-#define CS15_SW4 0x44
-#define CS16_SW4 0x45
-#define CS17_SW4 0x46
-#define CS18_SW4 0x47
-#define CS1_SW5 0x48
-#define CS2_SW5 0x49
-#define CS3_SW5 0x4A
-#define CS4_SW5 0x4B
-#define CS5_SW5 0x4C
-#define CS6_SW5 0x4D
-#define CS7_SW5 0x4E
-#define CS8_SW5 0x4F
-#define CS9_SW5 0x50
-#define CS10_SW5 0x51
-#define CS11_SW5 0x52
-#define CS12_SW5 0x53
-#define CS13_SW5 0x54
-#define CS14_SW5 0x55
-#define CS15_SW5 0x56
-#define CS16_SW5 0x57
-#define CS17_SW5 0x58
-#define CS18_SW5 0x59
-#define CS1_SW6 0x5A
-#define CS2_SW6 0x5B
-#define CS3_SW6 0x5C
-#define CS4_SW6 0x5D
-#define CS5_SW6 0x5E
-#define CS6_SW6 0x5F
-#define CS7_SW6 0x60
-#define CS8_SW6 0x61
-#define CS9_SW6 0x62
-#define CS10_SW6 0x63
-#define CS11_SW6 0x64
-#define CS12_SW6 0x65
-#define CS13_SW6 0x66
-#define CS14_SW6 0x67
-#define CS15_SW6 0x68
-#define CS16_SW6 0x69
-#define CS17_SW6 0x6A
-#define CS18_SW6 0x6B
-#define CS1_SW7 0x6C
-#define CS2_SW7 0x6D
-#define CS3_SW7 0x6E
-#define CS4_SW7 0x6F
-#define CS5_SW7 0x70
-#define CS6_SW7 0x71
-#define CS7_SW7 0x72
-#define CS8_SW7 0x73
-#define CS9_SW7 0x74
-#define CS10_SW7 0x75
-#define CS11_SW7 0x76
-#define CS12_SW7 0x77
-#define CS13_SW7 0x78
-#define CS14_SW7 0x79
-#define CS15_SW7 0x7A
-#define CS16_SW7 0x7B
-#define CS17_SW7 0x7C
-#define CS18_SW7 0x7D
-#define CS1_SW8 0x7E
-#define CS2_SW8 0x7F
-#define CS3_SW8 0x80
-#define CS4_SW8 0x81
-#define CS5_SW8 0x82
-#define CS6_SW8 0x83
-#define CS7_SW8 0x84
-#define CS8_SW8 0x85
-#define CS9_SW8 0x86
-#define CS10_SW8 0x87
-#define CS11_SW8 0x88
-#define CS12_SW8 0x89
-#define CS13_SW8 0x8A
-#define CS14_SW8 0x8B
-#define CS15_SW8 0x8C
-#define CS16_SW8 0x8D
-#define CS17_SW8 0x8E
-#define CS18_SW8 0x8F
-#define CS1_SW9 0x90
-#define CS2_SW9 0x91
-#define CS3_SW9 0x92
-#define CS4_SW9 0x93
-#define CS5_SW9 0x94
-#define CS6_SW9 0x95
-#define CS7_SW9 0x96
-#define CS8_SW9 0x97
-#define CS9_SW9 0x98
-#define CS10_SW9 0x99
-#define CS11_SW9 0x9A
-#define CS12_SW9 0x9B
-#define CS13_SW9 0x9C
-#define CS14_SW9 0x9D
-#define CS15_SW9 0x9E
-#define CS16_SW9 0x9F
-#define CS17_SW9 0xA0
-#define CS18_SW9 0xA1
-#define CS1_SW10 0xA2
-#define CS2_SW10 0xA3
-#define CS3_SW10 0xA4
-#define CS4_SW10 0xA5
-#define CS5_SW10 0xA6
-#define CS6_SW10 0xA7
-#define CS7_SW10 0xA8
-#define CS8_SW10 0xA9
-#define CS9_SW10 0xAA
-#define CS10_SW10 0xAB
-#define CS11_SW10 0xAC
-#define CS12_SW10 0xAD
-#define CS13_SW10 0xAE
-#define CS14_SW10 0xAF
-#define CS15_SW10 0xB0
-#define CS16_SW10 0xB1
-#define CS17_SW10 0xB2
-#define CS18_SW10 0xB3
-#define CS1_SW11 0xB4
-#define CS2_SW11 0xB5
-#define CS3_SW11 0xB6
-#define CS4_SW11 0xB7
-#define CS5_SW11 0xB8
-#define CS6_SW11 0xB9
-#define CS7_SW11 0xBA
-#define CS8_SW11 0xBB
-#define CS9_SW11 0xBC
-#define CS10_SW11 0xBD
-#define CS11_SW11 0xBE
-#define CS12_SW11 0xBF
-#define CS13_SW11 0xC0
-#define CS14_SW11 0xC1
-#define CS15_SW11 0xC2
-#define CS16_SW11 0xC3
-#define CS17_SW11 0xC4
-#define CS18_SW11 0xC5
-#define CS1_SW12 0xC6
-#define CS2_SW12 0xC7
-#define CS3_SW12 0xC8
-#define CS4_SW12 0xC9
-#define CS5_SW12 0xCA
-#define CS6_SW12 0xCB
-#define CS7_SW12 0xCC
-#define CS8_SW12 0xCD
-#define CS9_SW12 0xCE
-#define CS10_SW12 0xCF
-#define CS11_SW12 0xD0
-#define CS12_SW12 0xD1
-#define CS13_SW12 0xD2
-#define CS14_SW12 0xD3
-#define CS15_SW12 0xD4
-#define CS16_SW12 0xD5
-#define CS17_SW12 0xD6
-#define CS18_SW12 0xD7
+#define SW1_CS1 0x00
+#define SW1_CS2 0x01
+#define SW1_CS3 0x02
+#define SW1_CS4 0x03
+#define SW1_CS5 0x04
+#define SW1_CS6 0x05
+#define SW1_CS7 0x06
+#define SW1_CS8 0x07
+#define SW1_CS9 0x08
+#define SW1_CS10 0x09
+#define SW1_CS11 0x0A
+#define SW1_CS12 0x0B
+#define SW1_CS13 0x0C
+#define SW1_CS14 0x0D
+#define SW1_CS15 0x0E
+#define SW1_CS16 0x0F
+#define SW1_CS17 0x10
+#define SW1_CS18 0x11
+
+#define SW2_CS1 0x12
+#define SW2_CS2 0x13
+#define SW2_CS3 0x14
+#define SW2_CS4 0x15
+#define SW2_CS5 0x16
+#define SW2_CS6 0x17
+#define SW2_CS7 0x18
+#define SW2_CS8 0x19
+#define SW2_CS9 0x1A
+#define SW2_CS10 0x1B
+#define SW2_CS11 0x1C
+#define SW2_CS12 0x1D
+#define SW2_CS13 0x1E
+#define SW2_CS14 0x1F
+#define SW2_CS15 0x20
+#define SW2_CS16 0x21
+#define SW2_CS17 0x22
+#define SW2_CS18 0x23
+
+#define SW3_CS1 0x24
+#define SW3_CS2 0x25
+#define SW3_CS3 0x26
+#define SW3_CS4 0x27
+#define SW3_CS5 0x28
+#define SW3_CS6 0x29
+#define SW3_CS7 0x2A
+#define SW3_CS8 0x2B
+#define SW3_CS9 0x2C
+#define SW3_CS10 0x2D
+#define SW3_CS11 0x2E
+#define SW3_CS12 0x2F
+#define SW3_CS13 0x30
+#define SW3_CS14 0x31
+#define SW3_CS15 0x32
+#define SW3_CS16 0x33
+#define SW3_CS17 0x34
+#define SW3_CS18 0x35
+
+#define SW4_CS1 0x36
+#define SW4_CS2 0x37
+#define SW4_CS3 0x38
+#define SW4_CS4 0x39
+#define SW4_CS5 0x3A
+#define SW4_CS6 0x3B
+#define SW4_CS7 0x3C
+#define SW4_CS8 0x3D
+#define SW4_CS9 0x3E
+#define SW4_CS10 0x3F
+#define SW4_CS11 0x40
+#define SW4_CS12 0x41
+#define SW4_CS13 0x42
+#define SW4_CS14 0x43
+#define SW4_CS15 0x44
+#define SW4_CS16 0x45
+#define SW4_CS17 0x46
+#define SW4_CS18 0x47
+
+#define SW5_CS1 0x48
+#define SW5_CS2 0x49
+#define SW5_CS3 0x4A
+#define SW5_CS4 0x4B
+#define SW5_CS5 0x4C
+#define SW5_CS6 0x4D
+#define SW5_CS7 0x4E
+#define SW5_CS8 0x4F
+#define SW5_CS9 0x50
+#define SW5_CS10 0x51
+#define SW5_CS11 0x52
+#define SW5_CS12 0x53
+#define SW5_CS13 0x54
+#define SW5_CS14 0x55
+#define SW5_CS15 0x56
+#define SW5_CS16 0x57
+#define SW5_CS17 0x58
+#define SW5_CS18 0x59
+
+#define SW6_CS1 0x5A
+#define SW6_CS2 0x5B
+#define SW6_CS3 0x5C
+#define SW6_CS4 0x5D
+#define SW6_CS5 0x5E
+#define SW6_CS6 0x5F
+#define SW6_CS7 0x60
+#define SW6_CS8 0x61
+#define SW6_CS9 0x62
+#define SW6_CS10 0x63
+#define SW6_CS11 0x64
+#define SW6_CS12 0x65
+#define SW6_CS13 0x66
+#define SW6_CS14 0x67
+#define SW6_CS15 0x68
+#define SW6_CS16 0x69
+#define SW6_CS17 0x6A
+#define SW6_CS18 0x6B
+
+#define SW7_CS1 0x6C
+#define SW7_CS2 0x6D
+#define SW7_CS3 0x6E
+#define SW7_CS4 0x6F
+#define SW7_CS5 0x70
+#define SW7_CS6 0x71
+#define SW7_CS7 0x72
+#define SW7_CS8 0x73
+#define SW7_CS9 0x74
+#define SW7_CS10 0x75
+#define SW7_CS11 0x76
+#define SW7_CS12 0x77
+#define SW7_CS13 0x78
+#define SW7_CS14 0x79
+#define SW7_CS15 0x7A
+#define SW7_CS16 0x7B
+#define SW7_CS17 0x7C
+#define SW7_CS18 0x7D
+
+#define SW8_CS1 0x7E
+#define SW8_CS2 0x7F
+#define SW8_CS3 0x80
+#define SW8_CS4 0x81
+#define SW8_CS5 0x82
+#define SW8_CS6 0x83
+#define SW8_CS7 0x84
+#define SW8_CS8 0x85
+#define SW8_CS9 0x86
+#define SW8_CS10 0x87
+#define SW8_CS11 0x88
+#define SW8_CS12 0x89
+#define SW8_CS13 0x8A
+#define SW8_CS14 0x8B
+#define SW8_CS15 0x8C
+#define SW8_CS16 0x8D
+#define SW8_CS17 0x8E
+#define SW8_CS18 0x8F
+
+#define SW9_CS1 0x90
+#define SW9_CS2 0x91
+#define SW9_CS3 0x92
+#define SW9_CS4 0x93
+#define SW9_CS5 0x94
+#define SW9_CS6 0x95
+#define SW9_CS7 0x96
+#define SW9_CS8 0x97
+#define SW9_CS9 0x98
+#define SW9_CS10 0x99
+#define SW9_CS11 0x9A
+#define SW9_CS12 0x9B
+#define SW9_CS13 0x9C
+#define SW9_CS14 0x9D
+#define SW9_CS15 0x9E
+#define SW9_CS16 0x9F
+#define SW9_CS17 0xA0
+#define SW9_CS18 0xA1
+
+#define SW10_CS1 0xA2
+#define SW10_CS2 0xA3
+#define SW10_CS3 0xA4
+#define SW10_CS4 0xA5
+#define SW10_CS5 0xA6
+#define SW10_CS6 0xA7
+#define SW10_CS7 0xA8
+#define SW10_CS8 0xA9
+#define SW10_CS9 0xAA
+#define SW10_CS10 0xAB
+#define SW10_CS11 0xAC
+#define SW10_CS12 0xAD
+#define SW10_CS13 0xAE
+#define SW10_CS14 0xAF
+#define SW10_CS15 0xB0
+#define SW10_CS16 0xB1
+#define SW10_CS17 0xB2
+#define SW10_CS18 0xB3
+
+#define SW11_CS1 0xB4
+#define SW11_CS2 0xB5
+#define SW11_CS3 0xB6
+#define SW11_CS4 0xB7
+#define SW11_CS5 0xB8
+#define SW11_CS6 0xB9
+#define SW11_CS7 0xBA
+#define SW11_CS8 0xBB
+#define SW11_CS9 0xBC
+#define SW11_CS10 0xBD
+#define SW11_CS11 0xBE
+#define SW11_CS12 0xBF
+#define SW11_CS13 0xC0
+#define SW11_CS14 0xC1
+#define SW11_CS15 0xC2
+#define SW11_CS16 0xC3
+#define SW11_CS17 0xC4
+#define SW11_CS18 0xC5
+
+#define SW12_CS1 0xC6
+#define SW12_CS2 0xC7
+#define SW12_CS3 0xC8
+#define SW12_CS4 0xC9
+#define SW12_CS5 0xCA
+#define SW12_CS6 0xCB
+#define SW12_CS7 0xCC
+#define SW12_CS8 0xCD
+#define SW12_CS9 0xCE
+#define SW12_CS10 0xCF
+#define SW12_CS11 0xD0
+#define SW12_CS12 0xD1
+#define SW12_CS13 0xD2
+#define SW12_CS14 0xD3
+#define SW12_CS15 0xD4
+#define SW12_CS16 0xD5
+#define SW12_CS17 0xD6
+#define SW12_CS18 0xD7
+
+// DEPRECATED - DO NOT USE
+
+#define CS1_SW1 SW1_CS1
+#define CS2_SW1 SW1_CS2
+#define CS3_SW1 SW1_CS3
+#define CS4_SW1 SW1_CS4
+#define CS5_SW1 SW1_CS5
+#define CS6_SW1 SW1_CS6
+#define CS7_SW1 SW1_CS7
+#define CS8_SW1 SW1_CS8
+#define CS9_SW1 SW1_CS9
+#define CS10_SW1 SW1_CS10
+#define CS11_SW1 SW1_CS11
+#define CS12_SW1 SW1_CS12
+#define CS13_SW1 SW1_CS13
+#define CS14_SW1 SW1_CS14
+#define CS15_SW1 SW1_CS15
+#define CS16_SW1 SW1_CS16
+#define CS17_SW1 SW1_CS17
+#define CS18_SW1 SW1_CS18
+
+#define CS1_SW2 SW2_CS1
+#define CS2_SW2 SW2_CS2
+#define CS3_SW2 SW2_CS3
+#define CS4_SW2 SW2_CS4
+#define CS5_SW2 SW2_CS5
+#define CS6_SW2 SW2_CS6
+#define CS7_SW2 SW2_CS7
+#define CS8_SW2 SW2_CS8
+#define CS9_SW2 SW2_CS9
+#define CS10_SW2 SW2_CS10
+#define CS11_SW2 SW2_CS11
+#define CS12_SW2 SW2_CS12
+#define CS13_SW2 SW2_CS13
+#define CS14_SW2 SW2_CS14
+#define CS15_SW2 SW2_CS15
+#define CS16_SW2 SW2_CS16
+#define CS17_SW2 SW2_CS17
+#define CS18_SW2 SW2_CS18
+
+#define CS1_SW3 SW3_CS1
+#define CS2_SW3 SW3_CS2
+#define CS3_SW3 SW3_CS3
+#define CS4_SW3 SW3_CS4
+#define CS5_SW3 SW3_CS5
+#define CS6_SW3 SW3_CS6
+#define CS7_SW3 SW3_CS7
+#define CS8_SW3 SW3_CS8
+#define CS9_SW3 SW3_CS9
+#define CS10_SW3 SW3_CS10
+#define CS11_SW3 SW3_CS11
+#define CS12_SW3 SW3_CS12
+#define CS13_SW3 SW3_CS13
+#define CS14_SW3 SW3_CS14
+#define CS15_SW3 SW3_CS15
+#define CS16_SW3 SW3_CS16
+#define CS17_SW3 SW3_CS17
+#define CS18_SW3 SW3_CS18
+
+#define CS1_SW4 SW4_CS1
+#define CS2_SW4 SW4_CS2
+#define CS3_SW4 SW4_CS3
+#define CS4_SW4 SW4_CS4
+#define CS5_SW4 SW4_CS5
+#define CS6_SW4 SW4_CS6
+#define CS7_SW4 SW4_CS7
+#define CS8_SW4 SW4_CS8
+#define CS9_SW4 SW4_CS9
+#define CS10_SW4 SW4_CS10
+#define CS11_SW4 SW4_CS11
+#define CS12_SW4 SW4_CS12
+#define CS13_SW4 SW4_CS13
+#define CS14_SW4 SW4_CS14
+#define CS15_SW4 SW4_CS15
+#define CS16_SW4 SW4_CS16
+#define CS17_SW4 SW4_CS17
+#define CS18_SW4 SW4_CS18
+
+#define CS1_SW5 SW5_CS1
+#define CS2_SW5 SW5_CS2
+#define CS3_SW5 SW5_CS3
+#define CS4_SW5 SW5_CS4
+#define CS5_SW5 SW5_CS5
+#define CS6_SW5 SW5_CS6
+#define CS7_SW5 SW5_CS7
+#define CS8_SW5 SW5_CS8
+#define CS9_SW5 SW5_CS9
+#define CS10_SW5 SW5_CS10
+#define CS11_SW5 SW5_CS11
+#define CS12_SW5 SW5_CS12
+#define CS13_SW5 SW5_CS13
+#define CS14_SW5 SW5_CS14
+#define CS15_SW5 SW5_CS15
+#define CS16_SW5 SW5_CS16
+#define CS17_SW5 SW5_CS17
+#define CS18_SW5 SW5_CS18
+
+#define CS1_SW6 SW6_CS1
+#define CS2_SW6 SW6_CS2
+#define CS3_SW6 SW6_CS3
+#define CS4_SW6 SW6_CS4
+#define CS5_SW6 SW6_CS5
+#define CS6_SW6 SW6_CS6
+#define CS7_SW6 SW6_CS7
+#define CS8_SW6 SW6_CS8
+#define CS9_SW6 SW6_CS9
+#define CS10_SW6 SW6_CS10
+#define CS11_SW6 SW6_CS11
+#define CS12_SW6 SW6_CS12
+#define CS13_SW6 SW6_CS13
+#define CS14_SW6 SW6_CS14
+#define CS15_SW6 SW6_CS15
+#define CS16_SW6 SW6_CS16
+#define CS17_SW6 SW6_CS17
+#define CS18_SW6 SW6_CS18
+
+#define CS1_SW7 SW7_CS1
+#define CS2_SW7 SW7_CS2
+#define CS3_SW7 SW7_CS3
+#define CS4_SW7 SW7_CS4
+#define CS5_SW7 SW7_CS5
+#define CS6_SW7 SW7_CS6
+#define CS7_SW7 SW7_CS7
+#define CS8_SW7 SW7_CS8
+#define CS9_SW7 SW7_CS9
+#define CS10_SW7 SW7_CS10
+#define CS11_SW7 SW7_CS11
+#define CS12_SW7 SW7_CS12
+#define CS13_SW7 SW7_CS13
+#define CS14_SW7 SW7_CS14
+#define CS15_SW7 SW7_CS15
+#define CS16_SW7 SW7_CS16
+#define CS17_SW7 SW7_CS17
+#define CS18_SW7 SW7_CS18
+
+#define CS1_SW8 SW8_CS1
+#define CS2_SW8 SW8_CS2
+#define CS3_SW8 SW8_CS3
+#define CS4_SW8 SW8_CS4
+#define CS5_SW8 SW8_CS5
+#define CS6_SW8 SW8_CS6
+#define CS7_SW8 SW8_CS7
+#define CS8_SW8 SW8_CS8
+#define CS9_SW8 SW8_CS9
+#define CS10_SW8 SW8_CS10
+#define CS11_SW8 SW8_CS11
+#define CS12_SW8 SW8_CS12
+#define CS13_SW8 SW8_CS13
+#define CS14_SW8 SW8_CS14
+#define CS15_SW8 SW8_CS15
+#define CS16_SW8 SW8_CS16
+#define CS17_SW8 SW8_CS17
+#define CS18_SW8 SW8_CS18
+
+#define CS1_SW9 SW9_CS1
+#define CS2_SW9 SW9_CS2
+#define CS3_SW9 SW9_CS3
+#define CS4_SW9 SW9_CS4
+#define CS5_SW9 SW9_CS5
+#define CS6_SW9 SW9_CS6
+#define CS7_SW9 SW9_CS7
+#define CS8_SW9 SW9_CS8
+#define CS9_SW9 SW9_CS9
+#define CS10_SW9 SW9_CS10
+#define CS11_SW9 SW9_CS11
+#define CS12_SW9 SW9_CS12
+#define CS13_SW9 SW9_CS13
+#define CS14_SW9 SW9_CS14
+#define CS15_SW9 SW9_CS15
+#define CS16_SW9 SW9_CS16
+#define CS17_SW9 SW9_CS17
+#define CS18_SW9 SW9_CS18
+
+#define CS1_SW10 SW10_CS1
+#define CS2_SW10 SW10_CS2
+#define CS3_SW10 SW10_CS3
+#define CS4_SW10 SW10_CS4
+#define CS5_SW10 SW10_CS5
+#define CS6_SW10 SW10_CS6
+#define CS7_SW10 SW10_CS7
+#define CS8_SW10 SW10_CS8
+#define CS9_SW10 SW10_CS9
+#define CS10_SW10 SW10_CS10
+#define CS11_SW10 SW10_CS11
+#define CS12_SW10 SW10_CS12
+#define CS13_SW10 SW10_CS13
+#define CS14_SW10 SW10_CS14
+#define CS15_SW10 SW10_CS15
+#define CS16_SW10 SW10_CS16
+#define CS17_SW10 SW10_CS17
+#define CS18_SW10 SW10_CS18
+
+#define CS1_SW11 SW11_CS1
+#define CS2_SW11 SW11_CS2
+#define CS3_SW11 SW11_CS3
+#define CS4_SW11 SW11_CS4
+#define CS5_SW11 SW11_CS5
+#define CS6_SW11 SW11_CS6
+#define CS7_SW11 SW11_CS7
+#define CS8_SW11 SW11_CS8
+#define CS9_SW11 SW11_CS9
+#define CS10_SW11 SW11_CS10
+#define CS11_SW11 SW11_CS11
+#define CS12_SW11 SW11_CS12
+#define CS13_SW11 SW11_CS13
+#define CS14_SW11 SW11_CS14
+#define CS15_SW11 SW11_CS15
+#define CS16_SW11 SW11_CS16
+#define CS17_SW11 SW11_CS17
+#define CS18_SW11 SW11_CS18
+
+#define CS1_SW12 SW12_CS1
+#define CS2_SW12 SW12_CS2
+#define CS3_SW12 SW12_CS3
+#define CS4_SW12 SW12_CS4
+#define CS5_SW12 SW12_CS5
+#define CS6_SW12 SW12_CS6
+#define CS7_SW12 SW12_CS7
+#define CS8_SW12 SW12_CS8
+#define CS9_SW12 SW12_CS9
+#define CS10_SW12 SW12_CS10
+#define CS11_SW12 SW12_CS11
+#define CS12_SW12 SW12_CS12
+#define CS13_SW12 SW12_CS13
+#define CS14_SW12 SW12_CS14
+#define CS15_SW12 SW12_CS15
+#define CS16_SW12 SW12_CS16
+#define CS17_SW12 SW12_CS17
+#define CS18_SW12 SW12_CS18
diff --git a/drivers/led/issi/is31fl3218-simple.c b/drivers/led/issi/is31fl3218-mono.c
index ce28c51d18..0174da7ab3 100644
--- a/drivers/led/issi/is31fl3218-simple.c
+++ b/drivers/led/issi/is31fl3218-mono.c
@@ -13,9 +13,10 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "is31fl3218.h"
-#include <string.h>
+
+#include "is31fl3218-mono.h"
#include "i2c_master.h"
+#include "gpio.h"
#define IS31FL3218_PWM_REGISTER_COUNT 18
#define IS31FL3218_LED_CONTROL_REGISTER_COUNT 3
@@ -28,44 +29,49 @@
# define IS31FL3218_I2C_PERSISTENCE 0
#endif
-// Reusable buffer for transfers
-uint8_t g_twi_transfer_buffer[20];
+typedef struct is31fl3218_driver_t {
+ uint8_t pwm_buffer[IS31FL3218_PWM_REGISTER_COUNT];
+ bool pwm_buffer_dirty;
+ uint8_t led_control_buffer[IS31FL3218_LED_CONTROL_REGISTER_COUNT];
+ bool led_control_buffer_dirty;
+} PACKED is31fl3218_driver_t;
// IS31FL3218 has 18 PWM outputs and a fixed I2C address, so no chaining.
-uint8_t g_pwm_buffer[IS31FL3218_PWM_REGISTER_COUNT];
-bool g_pwm_buffer_update_required = false;
-
-uint8_t g_led_control_registers[IS31FL3218_LED_CONTROL_REGISTER_COUNT] = {0};
-bool g_led_control_registers_update_required = false;
+is31fl3218_driver_t driver_buffers = {
+ .pwm_buffer = {0},
+ .pwm_buffer_dirty = false,
+ .led_control_buffer = {0},
+ .led_control_buffer_dirty = 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;
+ if (i2c_write_register(IS31FL3218_I2C_ADDRESS << 1, reg, &data, 1, IS31FL3218_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
}
#else
- i2c_transmit(IS31FL3218_I2C_ADDRESS << 1, g_twi_transfer_buffer, 2, IS31FL3218_I2C_TIMEOUT);
+ i2c_write_register(IS31FL3218_I2C_ADDRESS << 1, reg, &data, 1, 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);
-
+void is31fl3218_write_pwm_buffer(void) {
#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);
+ if (i2c_write_register(IS31FL3218_I2C_ADDRESS << 1, IS31FL3218_REG_PWM, driver_buffers.pwm_buffer, 18, IS31FL3218_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
}
#else
- i2c_transmit(IS31FL3218_I2C_ADDRESS << 1, g_twi_transfer_buffer, 19, IS31FL3218_I2C_TIMEOUT);
+ i2c_write_register(IS31FL3218_I2C_ADDRESS << 1, IS31FL3218_REG_PWM, driver_buffers.pwm_buffer, 18, IS31FL3218_I2C_TIMEOUT);
#endif
}
void is31fl3218_init(void) {
i2c_init();
+#if defined(IS31FL3218_SDB_PIN)
+ gpio_set_pin_output(IS31FL3218_SDB_PIN);
+ gpio_write_pin_high(IS31FL3218_SDB_PIN);
+#endif
+
// In case we ever want to reinitialize (?)
is31fl3218_write_register(IS31FL3218_REG_RESET, 0x00);
@@ -94,14 +100,17 @@ void is31fl3218_init(void) {
void is31fl3218_set_value(int index, uint8_t value) {
is31fl3218_led_t led;
+
if (index >= 0 && index < IS31FL3218_LED_COUNT) {
memcpy_P(&led, (&g_is31fl3218_leds[index]), sizeof(led));
+
+ if (driver_buffers.pwm_buffer[led.v] == value) {
+ return;
+ }
+
+ driver_buffers.pwm_buffer[led.v] = value;
+ driver_buffers.pwm_buffer_dirty = true;
}
- 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) {
@@ -114,34 +123,34 @@ 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;
+ uint8_t control_register = led.v / 6;
+ uint8_t bit_value = led.v % 6;
if (value) {
- g_led_control_registers[control_register] |= (1 << bit_value);
+ driver_buffers.led_control_buffer[control_register] |= (1 << bit_value);
} else {
- g_led_control_registers[control_register] &= ~(1 << bit_value);
+ driver_buffers.led_control_buffer[control_register] &= ~(1 << bit_value);
}
- g_led_control_registers_update_required = true;
+ driver_buffers.led_control_buffer_dirty = true;
}
void is31fl3218_update_pwm_buffers(void) {
- if (g_pwm_buffer_update_required) {
- is31fl3218_write_pwm_buffer(g_pwm_buffer);
+ if (driver_buffers.pwm_buffer_dirty) {
+ is31fl3218_write_pwm_buffer();
// Load PWM registers and LED Control register data
is31fl3218_write_register(IS31FL3218_REG_UPDATE, 0x01);
- g_pwm_buffer_update_required = false;
+ driver_buffers.pwm_buffer_dirty = false;
}
}
void is31fl3218_update_led_control_registers(void) {
- if (g_led_control_registers_update_required) {
- for (int i = 0; i < IS31FL3218_LED_CONTROL_REGISTER_COUNT; i++) {
- is31fl3218_write_register(IS31FL3218_REG_LED_CONTROL_1 + i, g_led_control_registers[i]);
+ if (driver_buffers.led_control_buffer_dirty) {
+ for (uint8_t i = 0; i < IS31FL3218_LED_CONTROL_REGISTER_COUNT; i++) {
+ is31fl3218_write_register(IS31FL3218_REG_LED_CONTROL_1 + i, driver_buffers.led_control_buffer[i]);
}
- g_led_control_registers_update_required = false;
+ driver_buffers.led_control_buffer_dirty = false;
}
}
diff --git a/drivers/led/issi/is31fl3218-simple.h b/drivers/led/issi/is31fl3218-mono.h
index 9492817809..38beed0569 100644
--- a/drivers/led/issi/is31fl3218-simple.h
+++ b/drivers/led/issi/is31fl3218-mono.h
@@ -43,6 +43,8 @@ extern const is31fl3218_led_t PROGMEM g_is31fl3218_leds[IS31FL3218_LED_COUNT];
void is31fl3218_init(void);
+void is31fl3218_write_register(uint8_t reg, uint8_t data);
+
void is31fl3218_set_value(int index, uint8_t value);
void is31fl3218_set_value_all(uint8_t value);
@@ -53,21 +55,21 @@ 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
+#define OUT1 0x00
+#define OUT2 0x01
+#define OUT3 0x02
+#define OUT4 0x03
+#define OUT5 0x04
+#define OUT6 0x05
+#define OUT7 0x06
+#define OUT8 0x07
+#define OUT9 0x08
+#define OUT10 0x09
+#define OUT11 0x0A
+#define OUT12 0x0B
+#define OUT13 0x0C
+#define OUT14 0x0D
+#define OUT15 0x0E
+#define OUT16 0x0F
+#define OUT17 0x10
+#define OUT18 0x11
diff --git a/drivers/led/issi/is31fl3218.c b/drivers/led/issi/is31fl3218.c
index 39db09d518..dd97d236f7 100644
--- a/drivers/led/issi/is31fl3218.c
+++ b/drivers/led/issi/is31fl3218.c
@@ -13,9 +13,10 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+
#include "is31fl3218.h"
-#include <string.h>
#include "i2c_master.h"
+#include "gpio.h"
#define IS31FL3218_PWM_REGISTER_COUNT 18
#define IS31FL3218_LED_CONTROL_REGISTER_COUNT 3
@@ -28,44 +29,49 @@
# define IS31FL3218_I2C_PERSISTENCE 0
#endif
-// Reusable buffer for transfers
-uint8_t g_twi_transfer_buffer[20];
+typedef struct is31fl3218_driver_t {
+ uint8_t pwm_buffer[IS31FL3218_PWM_REGISTER_COUNT];
+ bool pwm_buffer_dirty;
+ uint8_t led_control_buffer[IS31FL3218_LED_CONTROL_REGISTER_COUNT];
+ bool led_control_buffer_dirty;
+} PACKED is31fl3218_driver_t;
// IS31FL3218 has 18 PWM outputs and a fixed I2C address, so no chaining.
-uint8_t g_pwm_buffer[IS31FL3218_PWM_REGISTER_COUNT];
-bool g_pwm_buffer_update_required = false;
-
-uint8_t g_led_control_registers[IS31FL3218_LED_CONTROL_REGISTER_COUNT] = {0};
-bool g_led_control_registers_update_required = false;
+is31fl3218_driver_t driver_buffers = {
+ .pwm_buffer = {0},
+ .pwm_buffer_dirty = false,
+ .led_control_buffer = {0},
+ .led_control_buffer_dirty = 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;
+ if (i2c_write_register(IS31FL3218_I2C_ADDRESS << 1, reg, &data, 1, IS31FL3218_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
}
#else
- i2c_transmit(IS31FL3218_I2C_ADDRESS << 1, g_twi_transfer_buffer, 2, IS31FL3218_I2C_TIMEOUT);
+ i2c_write_register(IS31FL3218_I2C_ADDRESS << 1, reg, &data, 1, 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);
-
+void is31fl3218_write_pwm_buffer(void) {
#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);
+ if (i2c_write_register(IS31FL3218_I2C_ADDRESS << 1, IS31FL3218_REG_PWM, driver_buffers.pwm_buffer, 18, IS31FL3218_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
}
#else
- i2c_transmit(IS31FL3218_I2C_ADDRESS << 1, g_twi_transfer_buffer, 19, IS31FL3218_I2C_TIMEOUT);
+ i2c_write_register(IS31FL3218_I2C_ADDRESS << 1, IS31FL3218_REG_PWM, driver_buffers.pwm_buffer, 18, IS31FL3218_I2C_TIMEOUT);
#endif
}
void is31fl3218_init(void) {
i2c_init();
+#if defined(IS31FL3218_SDB_PIN)
+ gpio_set_pin_output(IS31FL3218_SDB_PIN);
+ gpio_write_pin_high(IS31FL3218_SDB_PIN);
+#endif
+
// In case we ever want to reinitialize (?)
is31fl3218_write_register(IS31FL3218_REG_RESET, 0x00);
@@ -94,16 +100,19 @@ void is31fl3218_init(void) {
void is31fl3218_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
is31fl3218_led_t led;
+
if (index >= 0 && index < IS31FL3218_LED_COUNT) {
memcpy_P(&led, (&g_is31fl3218_leds[index]), sizeof(led));
+
+ if (driver_buffers.pwm_buffer[led.r] == red && driver_buffers.pwm_buffer[led.g] == green && driver_buffers.pwm_buffer[led.b] == blue) {
+ return;
+ }
+
+ driver_buffers.pwm_buffer[led.r] = red;
+ driver_buffers.pwm_buffer[led.g] = green;
+ driver_buffers.pwm_buffer[led.b] = blue;
+ driver_buffers.pwm_buffer_dirty = true;
}
- if (g_pwm_buffer[led.r - IS31FL3218_REG_PWM] == red && g_pwm_buffer[led.g - IS31FL3218_REG_PWM] == green && g_pwm_buffer[led.b - IS31FL3218_REG_PWM] == blue) {
- return;
- }
- g_pwm_buffer[led.r - IS31FL3218_REG_PWM] = red;
- g_pwm_buffer[led.g - IS31FL3218_REG_PWM] = green;
- g_pwm_buffer[led.b - IS31FL3218_REG_PWM] = blue;
- g_pwm_buffer_update_required = true;
}
void is31fl3218_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
@@ -116,48 +125,48 @@ void is31fl3218_set_led_control_register(uint8_t index, bool red, bool green, bo
is31fl3218_led_t led;
memcpy_P(&led, (&g_is31fl3218_leds[index]), sizeof(led));
- uint8_t control_register_r = (led.r - IS31FL3218_REG_PWM) / 6;
- uint8_t control_register_g = (led.g - IS31FL3218_REG_PWM) / 6;
- uint8_t control_register_b = (led.b - IS31FL3218_REG_PWM) / 6;
- uint8_t bit_r = (led.r - IS31FL3218_REG_PWM) % 6;
- uint8_t bit_g = (led.g - IS31FL3218_REG_PWM) % 6;
- uint8_t bit_b = (led.b - IS31FL3218_REG_PWM) % 6;
+ uint8_t control_register_r = led.r / 6;
+ uint8_t control_register_g = led.g / 6;
+ uint8_t control_register_b = led.b / 6;
+ uint8_t bit_r = led.r % 6;
+ uint8_t bit_g = led.g % 6;
+ uint8_t bit_b = led.b % 6;
if (red) {
- g_led_control_registers[control_register_r] |= (1 << bit_r);
+ driver_buffers.led_control_buffer[control_register_r] |= (1 << bit_r);
} else {
- g_led_control_registers[control_register_r] &= ~(1 << bit_r);
+ driver_buffers.led_control_buffer[control_register_r] &= ~(1 << bit_r);
}
if (green) {
- g_led_control_registers[control_register_g] |= (1 << bit_g);
+ driver_buffers.led_control_buffer[control_register_g] |= (1 << bit_g);
} else {
- g_led_control_registers[control_register_g] &= ~(1 << bit_g);
+ driver_buffers.led_control_buffer[control_register_g] &= ~(1 << bit_g);
}
if (blue) {
- g_led_control_registers[control_register_b] |= (1 << bit_b);
+ driver_buffers.led_control_buffer[control_register_b] |= (1 << bit_b);
} else {
- g_led_control_registers[control_register_b] &= ~(1 << bit_b);
+ driver_buffers.led_control_buffer[control_register_b] &= ~(1 << bit_b);
}
- g_led_control_registers_update_required = true;
+ driver_buffers.led_control_buffer_dirty = true;
}
void is31fl3218_update_pwm_buffers(void) {
- if (g_pwm_buffer_update_required) {
- is31fl3218_write_pwm_buffer(g_pwm_buffer);
+ if (driver_buffers.pwm_buffer_dirty) {
+ is31fl3218_write_pwm_buffer();
// Load PWM registers and LED Control register data
is31fl3218_write_register(IS31FL3218_REG_UPDATE, 0x01);
- g_pwm_buffer_update_required = false;
+ driver_buffers.pwm_buffer_dirty = false;
}
}
void is31fl3218_update_led_control_registers(void) {
- if (g_led_control_registers_update_required) {
- for (int i = 0; i < IS31FL3218_LED_CONTROL_REGISTER_COUNT; i++) {
- is31fl3218_write_register(IS31FL3218_REG_LED_CONTROL_1 + i, g_led_control_registers[i]);
+ if (driver_buffers.led_control_buffer_dirty) {
+ for (uint8_t i = 0; i < IS31FL3218_LED_CONTROL_REGISTER_COUNT; i++) {
+ is31fl3218_write_register(IS31FL3218_REG_LED_CONTROL_1 + i, driver_buffers.led_control_buffer[i]);
}
- g_led_control_registers_update_required = false;
+ driver_buffers.led_control_buffer_dirty = false;
}
}
diff --git a/drivers/led/issi/is31fl3218.h b/drivers/led/issi/is31fl3218.h
index ffa7f36d61..2228343329 100644
--- a/drivers/led/issi/is31fl3218.h
+++ b/drivers/led/issi/is31fl3218.h
@@ -45,6 +45,8 @@ extern const is31fl3218_led_t PROGMEM g_is31fl3218_leds[IS31FL3218_LED_COUNT];
void is31fl3218_init(void);
+void is31fl3218_write_register(uint8_t reg, uint8_t data);
+
void is31fl3218_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
void is31fl3218_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
@@ -55,21 +57,21 @@ 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
+#define OUT1 0x00
+#define OUT2 0x01
+#define OUT3 0x02
+#define OUT4 0x03
+#define OUT5 0x04
+#define OUT6 0x05
+#define OUT7 0x06
+#define OUT8 0x07
+#define OUT9 0x08
+#define OUT10 0x09
+#define OUT11 0x0A
+#define OUT12 0x0B
+#define OUT13 0x0C
+#define OUT14 0x0D
+#define OUT15 0x0E
+#define OUT16 0x0F
+#define OUT17 0x10
+#define OUT18 0x11
diff --git a/drivers/led/issi/is31fl3729-mono.c b/drivers/led/issi/is31fl3729-mono.c
new file mode 100644
index 0000000000..13d5146877
--- /dev/null
+++ b/drivers/led/issi/is31fl3729-mono.c
@@ -0,0 +1,213 @@
+/* Copyright 2024 HorrorTroll <https://github.com/HorrorTroll>
+ * Copyright 2024 Harrison Chan (Xelus)
+ * Copyright 2024 Dimitris Mantzouranis <d3xter93@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "is31fl3729-mono.h"
+#include "i2c_master.h"
+#include "gpio.h"
+#include "wait.h"
+
+#define IS31FL3729_PWM_REGISTER_COUNT 143
+#define IS31FL3729_SCALING_REGISTER_COUNT 16
+
+#ifndef IS31FL3729_I2C_TIMEOUT
+# define IS31FL3729_I2C_TIMEOUT 100
+#endif
+
+#ifndef IS31FL3729_I2C_PERSISTENCE
+# define IS31FL3729_I2C_PERSISTENCE 0
+#endif
+
+#ifndef IS31FL3729_CONFIGURATION
+# define IS31FL3729_CONFIGURATION IS31FL3729_CONFIG_SWS_15_9
+#endif
+
+#ifndef IS31FL3729_GLOBAL_CURRENT
+# define IS31FL3729_GLOBAL_CURRENT 0x40
+#endif
+
+#ifndef IS31FL3729_SW_PULLDOWN
+# define IS31FL3729_SW_PULLDOWN IS31FL3729_SW_PULLDOWN_2K_OHM_SW_OFF
+#endif
+
+#ifndef IS31FL3729_CS_PULLUP
+# define IS31FL3729_CS_PULLUP IS31FL3729_CS_PULLUP_2K_OHM_CS_OFF
+#endif
+
+#ifndef IS31FL3729_SPREAD_SPECTRUM
+# define IS31FL3729_SPREAD_SPECTRUM IS31FL3729_SPREAD_SPECTRUM_DISABLE
+#endif
+
+#ifndef IS31FL3729_SPREAD_SPECTRUM_RANGE
+# define IS31FL3729_SPREAD_SPECTRUM_RANGE IS31FL3729_SPREAD_SPECTRUM_RANGE_5_PERCENT
+#endif
+
+#ifndef IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME
+# define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_1980_US
+#endif
+
+#ifndef IS31FL3729_PWM_FREQUENCY
+# define IS31FL3729_PWM_FREQUENCY IS31FL3729_PWM_FREQUENCY_32K_HZ
+#endif
+
+const uint8_t i2c_addresses[IS31FL3729_DRIVER_COUNT] = {
+ IS31FL3729_I2C_ADDRESS_1,
+#ifdef IS31FL3729_I2C_ADDRESS_2
+ IS31FL3729_I2C_ADDRESS_2,
+# ifdef IS31FL3729_I2C_ADDRESS_3
+ IS31FL3729_I2C_ADDRESS_3,
+# ifdef IS31FL3729_I2C_ADDRESS_4
+ IS31FL3729_I2C_ADDRESS_4,
+# endif
+# endif
+#endif
+};
+
+// These buffers match the PWM & scaling registers.
+// Storing them like this is optimal for I2C transfers to the registers.
+typedef struct is31fl3729_driver_t {
+ uint8_t pwm_buffer[IS31FL3729_PWM_REGISTER_COUNT];
+ bool pwm_buffer_dirty;
+ uint8_t scaling_buffer[IS31FL3729_SCALING_REGISTER_COUNT];
+ bool scaling_buffer_dirty;
+} PACKED is31fl3729_driver_t;
+
+is31fl3729_driver_t driver_buffers[IS31FL3729_DRIVER_COUNT] = {{
+ .pwm_buffer = {0},
+ .pwm_buffer_dirty = false,
+ .scaling_buffer = {0},
+ .scaling_buffer_dirty = false,
+}};
+
+void is31fl3729_write_register(uint8_t index, uint8_t reg, uint8_t data) {
+#if IS31FL3729_I2C_PERSISTENCE > 0
+ for (uint8_t i = 0; i < IS31FL3729_I2C_PERSISTENCE; i++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3729_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3729_I2C_TIMEOUT);
+#endif
+}
+
+void is31fl3729_write_pwm_buffer(uint8_t index) {
+ // Transmit PWM registers in 11 transfers of 13 bytes.
+
+ // Iterate over the pwm_buffer contents at 13 byte intervals.
+ for (uint8_t i = 0; i <= IS31FL3729_PWM_REGISTER_COUNT; i += 13) {
+#if IS31FL3729_I2C_PERSISTENCE > 0
+ for (uint8_t j = 0; j < IS31FL3729_I2C_PERSISTENCE; j++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, IS31FL3729_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 13, IS31FL3729_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, IS31FL3729_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 13, IS31FL3729_I2C_TIMEOUT);
+#endif
+ }
+}
+
+void is31fl3729_init_drivers(void) {
+ i2c_init();
+
+#if defined(IS31FL3729_SDB_PIN)
+ gpio_set_pin_output(IS31FL3729_SDB_PIN);
+ gpio_write_pin_high(IS31FL3729_SDB_PIN);
+#endif
+
+ for (uint8_t i = 0; i < IS31FL3729_DRIVER_COUNT; i++) {
+ is31fl3729_init(i);
+ }
+
+ for (int i = 0; i < IS31FL3729_LED_COUNT; i++) {
+ is31fl3729_set_scaling_register(i, 0xFF);
+ }
+
+ for (uint8_t i = 0; i < IS31FL3729_DRIVER_COUNT; i++) {
+ is31fl3729_update_scaling_registers(i);
+ }
+}
+
+void is31fl3729_init(uint8_t index) {
+ // In order to avoid the LEDs being driven with garbage data
+ // in the LED driver's PWM registers, shutdown is enabled last.
+ // Set up the mode and other settings, clear the PWM registers,
+ // then disable software shutdown.
+
+ is31fl3729_write_register(index, IS31FL3729_REG_PULLDOWNUP, ((IS31FL3729_SW_PULLDOWN & 0b111) << 4) | (IS31FL3729_CS_PULLUP & 0b111));
+ is31fl3729_write_register(index, IS31FL3729_REG_SPREAD_SPECTRUM, ((IS31FL3729_SPREAD_SPECTRUM & 0b1) << 4) | ((IS31FL3729_SPREAD_SPECTRUM_RANGE & 0b11) << 2) | (IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME & 0b11));
+ is31fl3729_write_register(index, IS31FL3729_REG_PWM_FREQUENCY, IS31FL3729_PWM_FREQUENCY);
+ is31fl3729_write_register(index, IS31FL3729_REG_GLOBAL_CURRENT, IS31FL3729_GLOBAL_CURRENT);
+ is31fl3729_write_register(index, IS31FL3729_REG_CONFIGURATION, IS31FL3729_CONFIGURATION);
+
+ // Wait 10ms to ensure the device has woken up.
+ wait_ms(10);
+}
+
+void is31fl3729_set_value(int index, uint8_t value) {
+ is31fl3729_led_t led;
+ if (index >= 0 && index < IS31FL3729_LED_COUNT) {
+ memcpy_P(&led, (&g_is31fl3729_leds[index]), sizeof(led));
+
+ if (driver_buffers[led.driver].pwm_buffer[led.v] == value) {
+ return;
+ }
+
+ driver_buffers[led.driver].pwm_buffer[led.v] = value;
+ driver_buffers[led.driver].pwm_buffer_dirty = true;
+ }
+}
+
+void is31fl3729_set_value_all(uint8_t value) {
+ for (int i = 0; i < IS31FL3729_LED_COUNT; i++) {
+ is31fl3729_set_value(i, value);
+ }
+}
+
+void is31fl3729_set_scaling_register(uint8_t index, uint8_t value) {
+ is31fl3729_led_t led;
+ memcpy_P(&led, (&g_is31fl3729_leds[index]), sizeof(led));
+
+ // need to do a bit of checking here since 3729 scaling is per CS pin.
+ // not the usual per single LED key as per other ISSI drivers
+ // only enable them, since they should be default disabled
+ int cs_value = (led.v & 0x0F) - 1;
+
+ driver_buffers[led.driver].scaling_buffer[cs_value] = value;
+ driver_buffers[led.driver].scaling_buffer_dirty = true;
+}
+
+void is31fl3729_update_pwm_buffers(uint8_t index) {
+ if (driver_buffers[index].pwm_buffer_dirty) {
+ is31fl3729_write_pwm_buffer(index);
+
+ driver_buffers[index].pwm_buffer_dirty = false;
+ }
+}
+
+void is31fl3729_update_scaling_registers(uint8_t index) {
+ if (driver_buffers[index].scaling_buffer_dirty) {
+ for (uint8_t i = 0; i < IS31FL3729_SCALING_REGISTER_COUNT; i++) {
+ is31fl3729_write_register(index, IS31FL3729_REG_SCALING + i, driver_buffers[index].scaling_buffer[i]);
+ }
+
+ driver_buffers[index].scaling_buffer_dirty = false;
+ }
+}
+
+void is31fl3729_flush(void) {
+ for (uint8_t i = 0; i < IS31FL3729_DRIVER_COUNT; i++) {
+ is31fl3729_update_pwm_buffers(i);
+ }
+}
diff --git a/drivers/led/issi/is31fl3729-mono.h b/drivers/led/issi/is31fl3729-mono.h
new file mode 100644
index 0000000000..9afcde84b3
--- /dev/null
+++ b/drivers/led/issi/is31fl3729-mono.h
@@ -0,0 +1,278 @@
+/* Copyright 2024 HorrorTroll <https://github.com/HorrorTroll>
+ * Copyright 2024 Harrison Chan (Xelus)
+ * Copyright 2024 Dimitris Mantzouranis <d3xter93@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "progmem.h"
+#include "util.h"
+
+#define IS31FL3729_REG_PWM 0x01
+#define IS31FL3729_REG_SCALING 0x90
+#define IS31FL3729_REG_CONFIGURATION 0xA0
+#define IS31FL3729_REG_GLOBAL_CURRENT 0xA1
+#define IS31FL3729_REG_PULLDOWNUP 0xB0
+#define IS31FL3729_REG_SPREAD_SPECTRUM 0xB1
+#define IS31FL3729_REG_PWM_FREQUENCY 0xB2
+#define IS31FL3729_REG_RESET 0xCF
+
+#define IS31FL3729_I2C_ADDRESS_GND 0x34
+#define IS31FL3729_I2C_ADDRESS_SCL 0x35
+#define IS31FL3729_I2C_ADDRESS_SDA 0x36
+#define IS31FL3729_I2C_ADDRESS_VCC 0x37
+
+#if defined(LED_MATRIX_IS31FL3729)
+# define IS31FL3729_LED_COUNT LED_MATRIX_LED_COUNT
+#endif
+
+#if defined(IS31FL3729_I2C_ADDRESS_4)
+# define IS31FL3729_DRIVER_COUNT 4
+#elif defined(IS31FL3729_I2C_ADDRESS_3)
+# define IS31FL3729_DRIVER_COUNT 3
+#elif defined(IS31FL3729_I2C_ADDRESS_2)
+# define IS31FL3729_DRIVER_COUNT 2
+#elif defined(IS31FL3729_I2C_ADDRESS_1)
+# define IS31FL3729_DRIVER_COUNT 1
+#endif
+
+typedef struct is31fl3729_led_t {
+ uint8_t driver : 2;
+ uint8_t v;
+} PACKED is31fl3729_led_t;
+
+extern const is31fl3729_led_t PROGMEM g_is31fl3729_leds[IS31FL3729_LED_COUNT];
+
+void is31fl3729_init_drivers(void);
+void is31fl3729_init(uint8_t index);
+void is31fl3729_write_register(uint8_t index, uint8_t reg, uint8_t data);
+
+void is31fl3729_set_value(int index, uint8_t value);
+void is31fl3729_set_value_all(uint8_t value);
+
+void is31fl3729_set_scaling_register(uint8_t index, uint8_t value);
+
+// 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 is31fl3729_update_pwm_buffers(uint8_t index);
+void is31fl3729_update_scaling_registers(uint8_t index);
+
+void is31fl3729_flush(void);
+
+#define IS31FL3729_SW_PULLDOWN_0_OHM 0b000
+#define IS31FL3729_SW_PULLDOWN_0K5_OHM_SW_OFF 0b001
+#define IS31FL3729_SW_PULLDOWN_1K_OHM_SW_OFF 0b010
+#define IS31FL3729_SW_PULLDOWN_2K_OHM_SW_OFF 0b011
+#define IS31FL3729_SW_PULLDOWN_1K_OHM 0b100
+#define IS31FL3729_SW_PULLDOWN_2K_OHM 0b101
+#define IS31FL3729_SW_PULLDOWN_4K_OHM 0b110
+#define IS31FL3729_SW_PULLDOWN_8K_OHM 0b111
+
+#define IS31FL3729_CS_PULLUP_0_OHM 0b000
+#define IS31FL3729_CS_PULLUP_0K5_OHM_CS_OFF 0b001
+#define IS31FL3729_CS_PULLUP_1K_OHM_CS_OFF 0b010
+#define IS31FL3729_CS_PULLUP_2K_OHM_CS_OFF 0b011
+#define IS31FL3729_CS_PULLUP_1K_OHM 0b100
+#define IS31FL3729_CS_PULLUP_2K_OHM 0b101
+#define IS31FL3729_CS_PULLUP_4K_OHM 0b110
+#define IS31FL3729_CS_PULLUP_8K_OHM 0b111
+
+#define IS31FL3729_SPREAD_SPECTRUM_DISABLE 0b0
+#define IS31FL3729_SPREAD_SPECTRUM_ENABLE 0b1
+
+#define IS31FL3729_SPREAD_SPECTRUM_RANGE_5_PERCENT 0b00
+#define IS31FL3729_SPREAD_SPECTRUM_RANGE_15_PERCENT 0b01
+#define IS31FL3729_SPREAD_SPECTRUM_RANGE_24_PERCENT 0b10
+#define IS31FL3729_SPREAD_SPECTRUM_RANGE_34_PERCENT 0b11
+
+#define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_1980_US 0b00
+#define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_1200_US 0b01
+#define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_820_US 0b10
+#define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_660_US 0b11
+
+#define IS31FL3729_PWM_FREQUENCY_55K_HZ 0b000
+#define IS31FL3729_PWM_FREQUENCY_32K_HZ 0b001
+#define IS31FL3729_PWM_FREQUENCY_4K_HZ 0b010
+#define IS31FL3729_PWM_FREQUENCY_2K_HZ 0b011
+#define IS31FL3729_PWM_FREQUENCY_1K_HZ 0b100
+#define IS31FL3729_PWM_FREQUENCY_500_HZ 0b101
+#define IS31FL3729_PWM_FREQUENCY_250_HZ 0b110
+#define IS31FL3729_PWM_FREQUENCY_80K_HZ 0b111
+
+#define IS31FL3729_CONFIG_SWS_15_9 0x01 // 15 CS x 9 SW matrix
+#define IS31FL3729_CONFIG_SWS_16_8 0x11 // 16 CS x 8 SW matrix
+#define IS31FL3729_CONFIG_SWS_16_7 0x21 // 16 CS x 7 SW matrix
+#define IS31FL3729_CONFIG_SWS_16_6 0x31 // 16 CS x 6 SW matrix
+#define IS31FL3729_CONFIG_SWS_16_5 0x41 // 16 CS x 5 SW matrix
+#define IS31FL3729_CONFIG_SWS_16_4 0x51 // 16 CS x 4 SW matrix
+#define IS31FL3729_CONFIG_SWS_16_3 0x61 // 16 CS x 3 SW matrix
+#define IS31FL3729_CONFIG_SWS_16_2 0x71 // 16 CS x 2 SW matrix
+
+#define SW1_CS1 0x00
+#define SW1_CS2 0x01
+#define SW1_CS3 0x02
+#define SW1_CS4 0x03
+#define SW1_CS5 0x04
+#define SW1_CS6 0x05
+#define SW1_CS7 0x06
+#define SW1_CS8 0x07
+#define SW1_CS9 0x08
+#define SW1_CS10 0x09
+#define SW1_CS11 0x0A
+#define SW1_CS12 0x0B
+#define SW1_CS13 0x0C
+#define SW1_CS14 0x0D
+#define SW1_CS15 0x0E
+#define SW1_CS16 0x0F
+
+#define SW2_CS1 0x10
+#define SW2_CS2 0x11
+#define SW2_CS3 0x12
+#define SW2_CS4 0x13
+#define SW2_CS5 0x14
+#define SW2_CS6 0x15
+#define SW2_CS7 0x16
+#define SW2_CS8 0x17
+#define SW2_CS9 0x18
+#define SW2_CS10 0x19
+#define SW2_CS11 0x1A
+#define SW2_CS12 0x1B
+#define SW2_CS13 0x1C
+#define SW2_CS14 0x1D
+#define SW2_CS15 0x1E
+#define SW2_CS16 0x1F
+
+#define SW3_CS1 0x20
+#define SW3_CS2 0x21
+#define SW3_CS3 0x22
+#define SW3_CS4 0x23
+#define SW3_CS5 0x24
+#define SW3_CS6 0x25
+#define SW3_CS7 0x26
+#define SW3_CS8 0x27
+#define SW3_CS9 0x28
+#define SW3_CS10 0x29
+#define SW3_CS11 0x2A
+#define SW3_CS12 0x2B
+#define SW3_CS13 0x2C
+#define SW3_CS14 0x2D
+#define SW3_CS15 0x2E
+#define SW3_CS16 0x2F
+
+#define SW4_CS1 0x30
+#define SW4_CS2 0x31
+#define SW4_CS3 0x32
+#define SW4_CS4 0x33
+#define SW4_CS5 0x34
+#define SW4_CS6 0x35
+#define SW4_CS7 0x36
+#define SW4_CS8 0x37
+#define SW4_CS9 0x38
+#define SW4_CS10 0x39
+#define SW4_CS11 0x3A
+#define SW4_CS12 0x3B
+#define SW4_CS13 0x3C
+#define SW4_CS14 0x3D
+#define SW4_CS15 0x3E
+#define SW4_CS16 0x3F
+
+#define SW5_CS1 0x40
+#define SW5_CS2 0x41
+#define SW5_CS3 0x42
+#define SW5_CS4 0x43
+#define SW5_CS5 0x44
+#define SW5_CS6 0x45
+#define SW5_CS7 0x46
+#define SW5_CS8 0x47
+#define SW5_CS9 0x48
+#define SW5_CS10 0x49
+#define SW5_CS11 0x4A
+#define SW5_CS12 0x4B
+#define SW5_CS13 0x4C
+#define SW5_CS14 0x4D
+#define SW5_CS15 0x4E
+#define SW5_CS16 0x4F
+
+#define SW6_CS1 0x50
+#define SW6_CS2 0x51
+#define SW6_CS3 0x52
+#define SW6_CS4 0x53
+#define SW6_CS5 0x54
+#define SW6_CS6 0x55
+#define SW6_CS7 0x56
+#define SW6_CS8 0x57
+#define SW6_CS9 0x58
+#define SW6_CS10 0x59
+#define SW6_CS11 0x5A
+#define SW6_CS12 0x5B
+#define SW6_CS13 0x5C
+#define SW6_CS14 0x5D
+#define SW6_CS15 0x5E
+#define SW6_CS16 0x5F
+
+#define SW7_CS1 0x60
+#define SW7_CS2 0x61
+#define SW7_CS3 0x62
+#define SW7_CS4 0x63
+#define SW7_CS5 0x64
+#define SW7_CS6 0x65
+#define SW7_CS7 0x66
+#define SW7_CS8 0x67
+#define SW7_CS9 0x68
+#define SW7_CS10 0x69
+#define SW7_CS11 0x6A
+#define SW7_CS12 0x6B
+#define SW7_CS13 0x6C
+#define SW7_CS14 0x6D
+#define SW7_CS15 0x6E
+#define SW7_CS16 0x6F
+
+#define SW8_CS1 0x70
+#define SW8_CS2 0x71
+#define SW8_CS3 0x72
+#define SW8_CS4 0x73
+#define SW8_CS5 0x74
+#define SW8_CS6 0x75
+#define SW8_CS7 0x76
+#define SW8_CS8 0x77
+#define SW8_CS9 0x78
+#define SW8_CS10 0x79
+#define SW8_CS11 0x7A
+#define SW8_CS12 0x7B
+#define SW8_CS13 0x7C
+#define SW8_CS14 0x7D
+#define SW8_CS15 0x7E
+#define SW8_CS16 0x7F
+
+#define SW9_CS1 0x80
+#define SW9_CS2 0x81
+#define SW9_CS3 0x82
+#define SW9_CS4 0x83
+#define SW9_CS5 0x84
+#define SW9_CS6 0x85
+#define SW9_CS7 0x86
+#define SW9_CS8 0x87
+#define SW9_CS9 0x88
+#define SW9_CS10 0x89
+#define SW9_CS11 0x8A
+#define SW9_CS12 0x8B
+#define SW9_CS13 0x8C
+#define SW9_CS14 0x8D
+#define SW9_CS15 0x8E
diff --git a/drivers/led/issi/is31fl3729.c b/drivers/led/issi/is31fl3729.c
new file mode 100644
index 0000000000..80acb22fa2
--- /dev/null
+++ b/drivers/led/issi/is31fl3729.c
@@ -0,0 +1,219 @@
+/* Copyright 2024 HorrorTroll <https://github.com/HorrorTroll>
+ * Copyright 2024 Harrison Chan (Xelus)
+ * Copyright 2024 Dimitris Mantzouranis <d3xter93@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "is31fl3729.h"
+#include "i2c_master.h"
+#include "gpio.h"
+#include "wait.h"
+
+#define IS31FL3729_PWM_REGISTER_COUNT 143
+#define IS31FL3729_SCALING_REGISTER_COUNT 16
+
+#ifndef IS31FL3729_I2C_TIMEOUT
+# define IS31FL3729_I2C_TIMEOUT 100
+#endif
+
+#ifndef IS31FL3729_I2C_PERSISTENCE
+# define IS31FL3729_I2C_PERSISTENCE 0
+#endif
+
+#ifndef IS31FL3729_CONFIGURATION
+# define IS31FL3729_CONFIGURATION IS31FL3729_CONFIG_SWS_15_9
+#endif
+
+#ifndef IS31FL3729_GLOBAL_CURRENT
+# define IS31FL3729_GLOBAL_CURRENT 0x40
+#endif
+
+#ifndef IS31FL3729_SW_PULLDOWN
+# define IS31FL3729_SW_PULLDOWN IS31FL3729_SW_PULLDOWN_2K_OHM_SW_OFF
+#endif
+
+#ifndef IS31FL3729_CS_PULLUP
+# define IS31FL3729_CS_PULLUP IS31FL3729_CS_PULLUP_2K_OHM_CS_OFF
+#endif
+
+#ifndef IS31FL3729_SPREAD_SPECTRUM
+# define IS31FL3729_SPREAD_SPECTRUM IS31FL3729_SPREAD_SPECTRUM_DISABLE
+#endif
+
+#ifndef IS31FL3729_SPREAD_SPECTRUM_RANGE
+# define IS31FL3729_SPREAD_SPECTRUM_RANGE IS31FL3729_SPREAD_SPECTRUM_RANGE_5_PERCENT
+#endif
+
+#ifndef IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME
+# define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_1980_US
+#endif
+
+#ifndef IS31FL3729_PWM_FREQUENCY
+# define IS31FL3729_PWM_FREQUENCY IS31FL3729_PWM_FREQUENCY_32K_HZ
+#endif
+
+const uint8_t i2c_addresses[IS31FL3729_DRIVER_COUNT] = {
+ IS31FL3729_I2C_ADDRESS_1,
+#ifdef IS31FL3729_I2C_ADDRESS_2
+ IS31FL3729_I2C_ADDRESS_2,
+# ifdef IS31FL3729_I2C_ADDRESS_3
+ IS31FL3729_I2C_ADDRESS_3,
+# ifdef IS31FL3729_I2C_ADDRESS_4
+ IS31FL3729_I2C_ADDRESS_4,
+# endif
+# endif
+#endif
+};
+
+// These buffers match the PWM & scaling registers.
+// Storing them like this is optimal for I2C transfers to the registers.
+typedef struct is31fl3729_driver_t {
+ uint8_t pwm_buffer[IS31FL3729_PWM_REGISTER_COUNT];
+ bool pwm_buffer_dirty;
+ uint8_t scaling_buffer[IS31FL3729_SCALING_REGISTER_COUNT];
+ bool scaling_buffer_dirty;
+} PACKED is31fl3729_driver_t;
+
+is31fl3729_driver_t driver_buffers[IS31FL3729_DRIVER_COUNT] = {{
+ .pwm_buffer = {0},
+ .pwm_buffer_dirty = false,
+ .scaling_buffer = {0},
+ .scaling_buffer_dirty = false,
+}};
+
+void is31fl3729_write_register(uint8_t index, uint8_t reg, uint8_t data) {
+#if IS31FL3729_I2C_PERSISTENCE > 0
+ for (uint8_t i = 0; i < IS31FL3729_I2C_PERSISTENCE; i++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3729_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3729_I2C_TIMEOUT);
+#endif
+}
+
+void is31fl3729_write_pwm_buffer(uint8_t index) {
+ // Transmit PWM registers in 11 transfers of 13 bytes.
+
+ // Iterate over the pwm_buffer contents at 13 byte intervals.
+ for (uint8_t i = 0; i <= IS31FL3729_PWM_REGISTER_COUNT; i += 13) {
+#if IS31FL3729_I2C_PERSISTENCE > 0
+ for (uint8_t j = 0; j < IS31FL3729_I2C_PERSISTENCE; j++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, IS31FL3729_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 13, IS31FL3729_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, IS31FL3729_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 13, IS31FL3729_I2C_TIMEOUT);
+#endif
+ }
+}
+
+void is31fl3729_init_drivers(void) {
+ i2c_init();
+
+#if defined(IS31FL3729_SDB_PIN)
+ gpio_set_pin_output(IS31FL3729_SDB_PIN);
+ gpio_write_pin_high(IS31FL3729_SDB_PIN);
+#endif
+
+ for (uint8_t i = 0; i < IS31FL3729_DRIVER_COUNT; i++) {
+ is31fl3729_init(i);
+ }
+
+ for (int i = 0; i < IS31FL3729_LED_COUNT; i++) {
+ is31fl3729_set_scaling_register(i, 0xFF, 0xFF, 0xFF);
+ }
+
+ for (uint8_t i = 0; i < IS31FL3729_DRIVER_COUNT; i++) {
+ is31fl3729_update_scaling_registers(i);
+ }
+}
+
+void is31fl3729_init(uint8_t index) {
+ // In order to avoid the LEDs being driven with garbage data
+ // in the LED driver's PWM registers, shutdown is enabled last.
+ // Set up the mode and other settings, clear the PWM registers,
+ // then disable software shutdown.
+
+ is31fl3729_write_register(index, IS31FL3729_REG_PULLDOWNUP, ((IS31FL3729_SW_PULLDOWN & 0b111) << 4) | (IS31FL3729_CS_PULLUP & 0b111));
+ is31fl3729_write_register(index, IS31FL3729_REG_SPREAD_SPECTRUM, ((IS31FL3729_SPREAD_SPECTRUM & 0b1) << 4) | ((IS31FL3729_SPREAD_SPECTRUM_RANGE & 0b11) << 2) | (IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME & 0b11));
+ is31fl3729_write_register(index, IS31FL3729_REG_PWM_FREQUENCY, IS31FL3729_PWM_FREQUENCY);
+ is31fl3729_write_register(index, IS31FL3729_REG_GLOBAL_CURRENT, IS31FL3729_GLOBAL_CURRENT);
+ is31fl3729_write_register(index, IS31FL3729_REG_CONFIGURATION, IS31FL3729_CONFIGURATION);
+
+ // Wait 10ms to ensure the device has woken up.
+ wait_ms(10);
+}
+
+void is31fl3729_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
+ is31fl3729_led_t led;
+ if (index >= 0 && index < IS31FL3729_LED_COUNT) {
+ memcpy_P(&led, (&g_is31fl3729_leds[index]), sizeof(led));
+
+ if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) {
+ return;
+ }
+
+ driver_buffers[led.driver].pwm_buffer[led.r] = red;
+ driver_buffers[led.driver].pwm_buffer[led.g] = green;
+ driver_buffers[led.driver].pwm_buffer[led.b] = blue;
+ driver_buffers[led.driver].pwm_buffer_dirty = true;
+ }
+}
+
+void is31fl3729_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
+ for (int i = 0; i < IS31FL3729_LED_COUNT; i++) {
+ is31fl3729_set_color(i, red, green, blue);
+ }
+}
+
+void is31fl3729_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue) {
+ is31fl3729_led_t led;
+ memcpy_P(&led, (&g_is31fl3729_leds[index]), sizeof(led));
+
+ // need to do a bit of checking here since 3729 scaling is per CS pin.
+ // not the usual per RGB key as per other ISSI drivers
+ // only enable them, since they should be default disabled
+ int cs_red = (led.r & 0x0F) - 1;
+ int cs_green = (led.g & 0x0F) - 1;
+ int cs_blue = (led.b & 0x0F) - 1;
+
+ driver_buffers[led.driver].scaling_buffer[cs_red] = red;
+ driver_buffers[led.driver].scaling_buffer[cs_green] = green;
+ driver_buffers[led.driver].scaling_buffer[cs_blue] = blue;
+ driver_buffers[led.driver].scaling_buffer_dirty = true;
+}
+
+void is31fl3729_update_pwm_buffers(uint8_t index) {
+ if (driver_buffers[index].pwm_buffer_dirty) {
+ is31fl3729_write_pwm_buffer(index);
+
+ driver_buffers[index].pwm_buffer_dirty = false;
+ }
+}
+
+void is31fl3729_update_scaling_registers(uint8_t index) {
+ if (driver_buffers[index].scaling_buffer_dirty) {
+ for (uint8_t i = 0; i < IS31FL3729_SCALING_REGISTER_COUNT; i++) {
+ is31fl3729_write_register(index, IS31FL3729_REG_SCALING + i, driver_buffers[index].scaling_buffer[i]);
+ }
+
+ driver_buffers[index].scaling_buffer_dirty = false;
+ }
+}
+
+void is31fl3729_flush(void) {
+ for (uint8_t i = 0; i < IS31FL3729_DRIVER_COUNT; i++) {
+ is31fl3729_update_pwm_buffers(i);
+ }
+}
diff --git a/drivers/led/issi/is31fl3729.h b/drivers/led/issi/is31fl3729.h
new file mode 100644
index 0000000000..865c462f05
--- /dev/null
+++ b/drivers/led/issi/is31fl3729.h
@@ -0,0 +1,280 @@
+/* Copyright 2024 HorrorTroll <https://github.com/HorrorTroll>
+ * Copyright 2024 Harrison Chan (Xelus)
+ * Copyright 2024 Dimitris Mantzouranis <d3xter93@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "progmem.h"
+#include "util.h"
+
+#define IS31FL3729_REG_PWM 0x01
+#define IS31FL3729_REG_SCALING 0x90
+#define IS31FL3729_REG_CONFIGURATION 0xA0
+#define IS31FL3729_REG_GLOBAL_CURRENT 0xA1
+#define IS31FL3729_REG_PULLDOWNUP 0xB0
+#define IS31FL3729_REG_SPREAD_SPECTRUM 0xB1
+#define IS31FL3729_REG_PWM_FREQUENCY 0xB2
+#define IS31FL3729_REG_RESET 0xCF
+
+#define IS31FL3729_I2C_ADDRESS_GND 0x34
+#define IS31FL3729_I2C_ADDRESS_SCL 0x35
+#define IS31FL3729_I2C_ADDRESS_SDA 0x36
+#define IS31FL3729_I2C_ADDRESS_VCC 0x37
+
+#if defined(RGB_MATRIX_IS31FL3729)
+# define IS31FL3729_LED_COUNT RGB_MATRIX_LED_COUNT
+#endif
+
+#if defined(IS31FL3729_I2C_ADDRESS_4)
+# define IS31FL3729_DRIVER_COUNT 4
+#elif defined(IS31FL3729_I2C_ADDRESS_3)
+# define IS31FL3729_DRIVER_COUNT 3
+#elif defined(IS31FL3729_I2C_ADDRESS_2)
+# define IS31FL3729_DRIVER_COUNT 2
+#elif defined(IS31FL3729_I2C_ADDRESS_1)
+# define IS31FL3729_DRIVER_COUNT 1
+#endif
+
+typedef struct is31fl3729_led_t {
+ uint8_t driver : 2;
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+} PACKED is31fl3729_led_t;
+
+extern const is31fl3729_led_t PROGMEM g_is31fl3729_leds[IS31FL3729_LED_COUNT];
+
+void is31fl3729_init_drivers(void);
+void is31fl3729_init(uint8_t index);
+void is31fl3729_write_register(uint8_t index, uint8_t reg, uint8_t data);
+
+void is31fl3729_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
+void is31fl3729_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
+
+void is31fl3729_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue);
+
+// 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 is31fl3729_update_pwm_buffers(uint8_t index);
+void is31fl3729_update_scaling_registers(uint8_t index);
+
+void is31fl3729_flush(void);
+
+#define IS31FL3729_SW_PULLDOWN_0_OHM 0b000
+#define IS31FL3729_SW_PULLDOWN_0K5_OHM_SW_OFF 0b001
+#define IS31FL3729_SW_PULLDOWN_1K_OHM_SW_OFF 0b010
+#define IS31FL3729_SW_PULLDOWN_2K_OHM_SW_OFF 0b011
+#define IS31FL3729_SW_PULLDOWN_1K_OHM 0b100
+#define IS31FL3729_SW_PULLDOWN_2K_OHM 0b101
+#define IS31FL3729_SW_PULLDOWN_4K_OHM 0b110
+#define IS31FL3729_SW_PULLDOWN_8K_OHM 0b111
+
+#define IS31FL3729_CS_PULLUP_0_OHM 0b000
+#define IS31FL3729_CS_PULLUP_0K5_OHM_CS_OFF 0b001
+#define IS31FL3729_CS_PULLUP_1K_OHM_CS_OFF 0b010
+#define IS31FL3729_CS_PULLUP_2K_OHM_CS_OFF 0b011
+#define IS31FL3729_CS_PULLUP_1K_OHM 0b100
+#define IS31FL3729_CS_PULLUP_2K_OHM 0b101
+#define IS31FL3729_CS_PULLUP_4K_OHM 0b110
+#define IS31FL3729_CS_PULLUP_8K_OHM 0b111
+
+#define IS31FL3729_SPREAD_SPECTRUM_DISABLE 0b0
+#define IS31FL3729_SPREAD_SPECTRUM_ENABLE 0b1
+
+#define IS31FL3729_SPREAD_SPECTRUM_RANGE_5_PERCENT 0b00
+#define IS31FL3729_SPREAD_SPECTRUM_RANGE_15_PERCENT 0b01
+#define IS31FL3729_SPREAD_SPECTRUM_RANGE_24_PERCENT 0b10
+#define IS31FL3729_SPREAD_SPECTRUM_RANGE_34_PERCENT 0b11
+
+#define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_1980_US 0b00
+#define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_1200_US 0b01
+#define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_820_US 0b10
+#define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_660_US 0b11
+
+#define IS31FL3729_PWM_FREQUENCY_55K_HZ 0b000
+#define IS31FL3729_PWM_FREQUENCY_32K_HZ 0b001
+#define IS31FL3729_PWM_FREQUENCY_4K_HZ 0b010
+#define IS31FL3729_PWM_FREQUENCY_2K_HZ 0b011
+#define IS31FL3729_PWM_FREQUENCY_1K_HZ 0b100
+#define IS31FL3729_PWM_FREQUENCY_500_HZ 0b101
+#define IS31FL3729_PWM_FREQUENCY_250_HZ 0b110
+#define IS31FL3729_PWM_FREQUENCY_80K_HZ 0b111
+
+#define IS31FL3729_CONFIG_SWS_15_9 0x01 // 15 CS x 9 SW matrix
+#define IS31FL3729_CONFIG_SWS_16_8 0x11 // 16 CS x 8 SW matrix
+#define IS31FL3729_CONFIG_SWS_16_7 0x21 // 16 CS x 7 SW matrix
+#define IS31FL3729_CONFIG_SWS_16_6 0x31 // 16 CS x 6 SW matrix
+#define IS31FL3729_CONFIG_SWS_16_5 0x41 // 16 CS x 5 SW matrix
+#define IS31FL3729_CONFIG_SWS_16_4 0x51 // 16 CS x 4 SW matrix
+#define IS31FL3729_CONFIG_SWS_16_3 0x61 // 16 CS x 3 SW matrix
+#define IS31FL3729_CONFIG_SWS_16_2 0x71 // 16 CS x 2 SW matrix
+
+#define SW1_CS1 0x00
+#define SW1_CS2 0x01
+#define SW1_CS3 0x02
+#define SW1_CS4 0x03
+#define SW1_CS5 0x04
+#define SW1_CS6 0x05
+#define SW1_CS7 0x06
+#define SW1_CS8 0x07
+#define SW1_CS9 0x08
+#define SW1_CS10 0x09
+#define SW1_CS11 0x0A
+#define SW1_CS12 0x0B
+#define SW1_CS13 0x0C
+#define SW1_CS14 0x0D
+#define SW1_CS15 0x0E
+#define SW1_CS16 0x0F
+
+#define SW2_CS1 0x10
+#define SW2_CS2 0x11
+#define SW2_CS3 0x12
+#define SW2_CS4 0x13
+#define SW2_CS5 0x14
+#define SW2_CS6 0x15
+#define SW2_CS7 0x16
+#define SW2_CS8 0x17
+#define SW2_CS9 0x18
+#define SW2_CS10 0x19
+#define SW2_CS11 0x1A
+#define SW2_CS12 0x1B
+#define SW2_CS13 0x1C
+#define SW2_CS14 0x1D
+#define SW2_CS15 0x1E
+#define SW2_CS16 0x1F
+
+#define SW3_CS1 0x20
+#define SW3_CS2 0x21
+#define SW3_CS3 0x22
+#define SW3_CS4 0x23
+#define SW3_CS5 0x24
+#define SW3_CS6 0x25
+#define SW3_CS7 0x26
+#define SW3_CS8 0x27
+#define SW3_CS9 0x28
+#define SW3_CS10 0x29
+#define SW3_CS11 0x2A
+#define SW3_CS12 0x2B
+#define SW3_CS13 0x2C
+#define SW3_CS14 0x2D
+#define SW3_CS15 0x2E
+#define SW3_CS16 0x2F
+
+#define SW4_CS1 0x30
+#define SW4_CS2 0x31
+#define SW4_CS3 0x32
+#define SW4_CS4 0x33
+#define SW4_CS5 0x34
+#define SW4_CS6 0x35
+#define SW4_CS7 0x36
+#define SW4_CS8 0x37
+#define SW4_CS9 0x38
+#define SW4_CS10 0x39
+#define SW4_CS11 0x3A
+#define SW4_CS12 0x3B
+#define SW4_CS13 0x3C
+#define SW4_CS14 0x3D
+#define SW4_CS15 0x3E
+#define SW4_CS16 0x3F
+
+#define SW5_CS1 0x40
+#define SW5_CS2 0x41
+#define SW5_CS3 0x42
+#define SW5_CS4 0x43
+#define SW5_CS5 0x44
+#define SW5_CS6 0x45
+#define SW5_CS7 0x46
+#define SW5_CS8 0x47
+#define SW5_CS9 0x48
+#define SW5_CS10 0x49
+#define SW5_CS11 0x4A
+#define SW5_CS12 0x4B
+#define SW5_CS13 0x4C
+#define SW5_CS14 0x4D
+#define SW5_CS15 0x4E
+#define SW5_CS16 0x4F
+
+#define SW6_CS1 0x50
+#define SW6_CS2 0x51
+#define SW6_CS3 0x52
+#define SW6_CS4 0x53
+#define SW6_CS5 0x54
+#define SW6_CS6 0x55
+#define SW6_CS7 0x56
+#define SW6_CS8 0x57
+#define SW6_CS9 0x58
+#define SW6_CS10 0x59
+#define SW6_CS11 0x5A
+#define SW6_CS12 0x5B
+#define SW6_CS13 0x5C
+#define SW6_CS14 0x5D
+#define SW6_CS15 0x5E
+#define SW6_CS16 0x5F
+
+#define SW7_CS1 0x60
+#define SW7_CS2 0x61
+#define SW7_CS3 0x62
+#define SW7_CS4 0x63
+#define SW7_CS5 0x64
+#define SW7_CS6 0x65
+#define SW7_CS7 0x66
+#define SW7_CS8 0x67
+#define SW7_CS9 0x68
+#define SW7_CS10 0x69
+#define SW7_CS11 0x6A
+#define SW7_CS12 0x6B
+#define SW7_CS13 0x6C
+#define SW7_CS14 0x6D
+#define SW7_CS15 0x6E
+#define SW7_CS16 0x6F
+
+#define SW8_CS1 0x70
+#define SW8_CS2 0x71
+#define SW8_CS3 0x72
+#define SW8_CS4 0x73
+#define SW8_CS5 0x74
+#define SW8_CS6 0x75
+#define SW8_CS7 0x76
+#define SW8_CS8 0x77
+#define SW8_CS9 0x78
+#define SW8_CS10 0x79
+#define SW8_CS11 0x7A
+#define SW8_CS12 0x7B
+#define SW8_CS13 0x7C
+#define SW8_CS14 0x7D
+#define SW8_CS15 0x7E
+#define SW8_CS16 0x7F
+
+#define SW9_CS1 0x80
+#define SW9_CS2 0x81
+#define SW9_CS3 0x82
+#define SW9_CS4 0x83
+#define SW9_CS5 0x84
+#define SW9_CS6 0x85
+#define SW9_CS7 0x86
+#define SW9_CS8 0x87
+#define SW9_CS9 0x88
+#define SW9_CS10 0x89
+#define SW9_CS11 0x8A
+#define SW9_CS12 0x8B
+#define SW9_CS13 0x8C
+#define SW9_CS14 0x8D
+#define SW9_CS15 0x8E
diff --git a/drivers/led/issi/is31fl3731-mono.c b/drivers/led/issi/is31fl3731-mono.c
new file mode 100644
index 0000000000..33a863b982
--- /dev/null
+++ b/drivers/led/issi/is31fl3731-mono.c
@@ -0,0 +1,230 @@
+/* Copyright 2017 Jason Williams
+ * Copyright 2018 Jack Humbert
+ * Copyright 2019 Clueboard
+ * Copyright 2021 Doni Crosby
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "is31fl3731-mono.h"
+#include "i2c_master.h"
+#include "gpio.h"
+#include "wait.h"
+
+#define IS31FL3731_PWM_REGISTER_COUNT 144
+#define IS31FL3731_LED_CONTROL_REGISTER_COUNT 18
+
+#ifndef IS31FL3731_I2C_TIMEOUT
+# define IS31FL3731_I2C_TIMEOUT 100
+#endif
+
+#ifndef IS31FL3731_I2C_PERSISTENCE
+# define IS31FL3731_I2C_PERSISTENCE 0
+#endif
+
+const uint8_t i2c_addresses[IS31FL3731_DRIVER_COUNT] = {
+ IS31FL3731_I2C_ADDRESS_1,
+#ifdef IS31FL3731_I2C_ADDRESS_2
+ IS31FL3731_I2C_ADDRESS_2,
+# ifdef IS31FL3731_I2C_ADDRESS_3
+ IS31FL3731_I2C_ADDRESS_3,
+# ifdef IS31FL3731_I2C_ADDRESS_4
+ IS31FL3731_I2C_ADDRESS_4,
+# endif
+# endif
+#endif
+};
+
+// These buffers match the IS31FL3731 PWM registers 0x24-0xB3.
+// Storing them like this is optimal for I2C transfers to the registers.
+// We could optimize this and take out the unused registers from these
+// buffers and the transfers in is31fl3731_write_pwm_buffer() but it's
+// probably not worth the extra complexity.
+typedef struct is31fl3731_driver_t {
+ uint8_t pwm_buffer[IS31FL3731_PWM_REGISTER_COUNT];
+ bool pwm_buffer_dirty;
+ uint8_t led_control_buffer[IS31FL3731_LED_CONTROL_REGISTER_COUNT];
+ bool led_control_buffer_dirty;
+} PACKED is31fl3731_driver_t;
+
+is31fl3731_driver_t driver_buffers[IS31FL3731_DRIVER_COUNT] = {{
+ .pwm_buffer = {0},
+ .pwm_buffer_dirty = false,
+ .led_control_buffer = {0},
+ .led_control_buffer_dirty = false,
+}};
+
+void is31fl3731_write_register(uint8_t index, uint8_t reg, uint8_t data) {
+#if IS31FL3731_I2C_PERSISTENCE > 0
+ for (uint8_t i = 0; i < IS31FL3731_I2C_PERSISTENCE; i++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3731_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3731_I2C_TIMEOUT);
+#endif
+}
+
+void is31fl3731_select_page(uint8_t index, uint8_t page) {
+ is31fl3731_write_register(index, IS31FL3731_REG_COMMAND, page);
+}
+
+void is31fl3731_write_pwm_buffer(uint8_t index) {
+ // Assumes page 0 is already selected.
+ // Transmit PWM registers in 9 transfers of 16 bytes.
+
+ // Iterate over the pwm_buffer contents at 16 byte intervals.
+ for (uint8_t i = 0; i < IS31FL3731_PWM_REGISTER_COUNT; i += 16) {
+#if IS31FL3731_I2C_PERSISTENCE > 0
+ for (uint8_t j = 0; j < IS31FL3731_I2C_PERSISTENCE; j++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, IS31FL3731_FRAME_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3731_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, IS31FL3731_FRAME_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3731_I2C_TIMEOUT);
+#endif
+ }
+}
+
+void is31fl3731_init_drivers(void) {
+ i2c_init();
+
+#if defined(IS31FL3731_SDB_PIN)
+ gpio_set_pin_output(IS31FL3731_SDB_PIN);
+ gpio_write_pin_high(IS31FL3731_SDB_PIN);
+#endif
+
+ for (uint8_t i = 0; i < IS31FL3731_DRIVER_COUNT; i++) {
+ is31fl3731_init(i);
+ }
+
+ for (int i = 0; i < IS31FL3731_LED_COUNT; i++) {
+ is31fl3731_set_led_control_register(i, true);
+ }
+
+ for (uint8_t i = 0; i < IS31FL3731_DRIVER_COUNT; i++) {
+ is31fl3731_update_led_control_registers(i);
+ }
+}
+
+void is31fl3731_init(uint8_t index) {
+ // In order to avoid the LEDs being driven with garbage data
+ // in the LED driver's PWM registers, first enable software shutdown,
+ // then set up the mode and other settings, clear the PWM registers,
+ // then disable software shutdown.
+
+ is31fl3731_select_page(index, IS31FL3731_COMMAND_FUNCTION);
+
+ // enable software shutdown
+ is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_SHUTDOWN, 0x00);
+#ifdef IS31FL3731_DEGHOST // set to enable de-ghosting of the array
+ is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_GHOST_IMAGE_PREVENTION, IS31FL3731_GHOST_IMAGE_PREVENTION_GEN);
+#endif
+
+ // this delay was copied from other drivers, might not be needed
+ wait_ms(10);
+
+ // picture mode
+ is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_CONFIG, IS31FL3731_CONFIG_MODE_PICTURE);
+ // display frame 0
+ is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_PICTURE_DISPLAY, 0x00);
+ // audio sync off
+ is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_AUDIO_SYNC, 0x00);
+
+ is31fl3731_select_page(index, IS31FL3731_COMMAND_FRAME_1);
+
+ // turn off all LEDs in the LED control register
+ for (uint8_t i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) {
+ is31fl3731_write_register(index, IS31FL3731_FRAME_REG_LED_CONTROL + i, 0x00);
+ }
+
+ // turn off all LEDs in the blink control register (not really needed)
+ for (uint8_t i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) {
+ is31fl3731_write_register(index, IS31FL3731_FRAME_REG_BLINK_CONTROL + i, 0x00);
+ }
+
+ // set PWM on all LEDs to 0
+ for (uint8_t i = 0; i < IS31FL3731_PWM_REGISTER_COUNT; i++) {
+ is31fl3731_write_register(index, IS31FL3731_FRAME_REG_PWM + i, 0x00);
+ }
+
+ is31fl3731_select_page(index, IS31FL3731_COMMAND_FUNCTION);
+
+ // disable software shutdown
+ is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_SHUTDOWN, 0x01);
+
+ // select page 0 and leave it selected.
+ // most usage after initialization is just writing PWM buffers in page 0
+ // as there's not much point in double-buffering
+ is31fl3731_select_page(index, IS31FL3731_COMMAND_FRAME_1);
+}
+
+void is31fl3731_set_value(int index, uint8_t value) {
+ is31fl3731_led_t led;
+
+ if (index >= 0 && index < IS31FL3731_LED_COUNT) {
+ memcpy_P(&led, (&g_is31fl3731_leds[index]), sizeof(led));
+
+ if (driver_buffers[led.driver].pwm_buffer[led.v] == value) {
+ return;
+ }
+
+ driver_buffers[led.driver].pwm_buffer[led.v] = value;
+ driver_buffers[led.driver].pwm_buffer_dirty = true;
+ }
+}
+
+void is31fl3731_set_value_all(uint8_t value) {
+ for (int i = 0; i < IS31FL3731_LED_COUNT; i++) {
+ is31fl3731_set_value(i, value);
+ }
+}
+
+void is31fl3731_set_led_control_register(uint8_t index, bool value) {
+ is31fl3731_led_t led;
+ memcpy_P(&led, (&g_is31fl3731_leds[index]), sizeof(led));
+
+ uint8_t control_register = led.v / 8;
+ uint8_t bit_value = led.v % 8;
+
+ if (value) {
+ driver_buffers[led.driver].led_control_buffer[control_register] |= (1 << bit_value);
+ } else {
+ driver_buffers[led.driver].led_control_buffer[control_register] &= ~(1 << bit_value);
+ }
+
+ driver_buffers[led.driver].led_control_buffer_dirty = true;
+}
+
+void is31fl3731_update_pwm_buffers(uint8_t index) {
+ if (driver_buffers[index].pwm_buffer_dirty) {
+ is31fl3731_write_pwm_buffer(index);
+
+ driver_buffers[index].pwm_buffer_dirty = false;
+ }
+}
+
+void is31fl3731_update_led_control_registers(uint8_t index) {
+ if (driver_buffers[index].led_control_buffer_dirty) {
+ for (uint8_t i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) {
+ is31fl3731_write_register(index, i, driver_buffers[index].led_control_buffer[i]);
+ }
+
+ driver_buffers[index].led_control_buffer_dirty = false;
+ }
+}
+
+void is31fl3731_flush(void) {
+ for (uint8_t i = 0; i < IS31FL3731_DRIVER_COUNT; i++) {
+ is31fl3731_update_pwm_buffers(i);
+ }
+}
diff --git a/drivers/led/issi/is31fl3731-simple.h b/drivers/led/issi/is31fl3731-mono.h
index 4d173847dd..e6e107d309 100644
--- a/drivers/led/issi/is31fl3731-simple.h
+++ b/drivers/led/issi/is31fl3731-mono.h
@@ -61,6 +61,10 @@
#define IS31FL3731_COMMAND_FRAME_8 0x07
#define IS31FL3731_COMMAND_FUNCTION 0x0B
+#define IS31FL3731_FRAME_REG_LED_CONTROL 0x00
+#define IS31FL3731_FRAME_REG_BLINK_CONTROL 0x12
+#define IS31FL3731_FRAME_REG_PWM 0x24
+
#define IS31FL3731_FUNCTION_REG_CONFIG 0x00
#define IS31FL3731_CONFIG_MODE_PICTURE 0x00
#define IS31FL3731_CONFIG_MODE_AUTO_PLAY 0x08
@@ -101,9 +105,9 @@ typedef struct is31fl3731_led_t {
extern const is31fl3731_led_t PROGMEM g_is31fl3731_leds[IS31FL3731_LED_COUNT];
void is31fl3731_init_drivers(void);
-void is31fl3731_init(uint8_t addr);
-void is31fl3731_write_register(uint8_t addr, uint8_t reg, uint8_t data);
-void is31fl3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
+void is31fl3731_init(uint8_t index);
+void is31fl3731_write_register(uint8_t index, uint8_t reg, uint8_t data);
+void is31fl3731_select_page(uint8_t index, uint8_t page);
void is31fl3731_set_value(int index, uint8_t value);
void is31fl3731_set_value_all(uint8_t value);
@@ -114,169 +118,169 @@ void is31fl3731_set_led_control_register(uint8_t index, bool value);
// (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 is31fl3731_update_pwm_buffers(uint8_t addr, uint8_t index);
-void is31fl3731_update_led_control_registers(uint8_t addr, uint8_t index);
+void is31fl3731_update_pwm_buffers(uint8_t index);
+void is31fl3731_update_led_control_registers(uint8_t index);
void is31fl3731_flush(void);
-#define C1_1 0x24
-#define C1_2 0x25
-#define C1_3 0x26
-#define C1_4 0x27
-#define C1_5 0x28
-#define C1_6 0x29
-#define C1_7 0x2A
-#define C1_8 0x2B
-
-#define C1_9 0x2C
-#define C1_10 0x2D
-#define C1_11 0x2E
-#define C1_12 0x2F
-#define C1_13 0x30
-#define C1_14 0x31
-#define C1_15 0x32
-#define C1_16 0x33
-
-#define C2_1 0x34
-#define C2_2 0x35
-#define C2_3 0x36
-#define C2_4 0x37
-#define C2_5 0x38
-#define C2_6 0x39
-#define C2_7 0x3A
-#define C2_8 0x3B
-
-#define C2_9 0x3C
-#define C2_10 0x3D
-#define C2_11 0x3E
-#define C2_12 0x3F
-#define C2_13 0x40
-#define C2_14 0x41
-#define C2_15 0x42
-#define C2_16 0x43
-
-#define C3_1 0x44
-#define C3_2 0x45
-#define C3_3 0x46
-#define C3_4 0x47
-#define C3_5 0x48
-#define C3_6 0x49
-#define C3_7 0x4A
-#define C3_8 0x4B
-
-#define C3_9 0x4C
-#define C3_10 0x4D
-#define C3_11 0x4E
-#define C3_12 0x4F
-#define C3_13 0x50
-#define C3_14 0x51
-#define C3_15 0x52
-#define C3_16 0x53
-
-#define C4_1 0x54
-#define C4_2 0x55
-#define C4_3 0x56
-#define C4_4 0x57
-#define C4_5 0x58
-#define C4_6 0x59
-#define C4_7 0x5A
-#define C4_8 0x5B
-
-#define C4_9 0x5C
-#define C4_10 0x5D
-#define C4_11 0x5E
-#define C4_12 0x5F
-#define C4_13 0x60
-#define C4_14 0x61
-#define C4_15 0x62
-#define C4_16 0x63
-
-#define C5_1 0x64
-#define C5_2 0x65
-#define C5_3 0x66
-#define C5_4 0x67
-#define C5_5 0x68
-#define C5_6 0x69
-#define C5_7 0x6A
-#define C5_8 0x6B
-
-#define C5_9 0x6C
-#define C5_10 0x6D
-#define C5_11 0x6E
-#define C5_12 0x6F
-#define C5_13 0x70
-#define C5_14 0x71
-#define C5_15 0x72
-#define C5_16 0x73
-
-#define C6_1 0x74
-#define C6_2 0x75
-#define C6_3 0x76
-#define C6_4 0x77
-#define C6_5 0x78
-#define C6_6 0x79
-#define C6_7 0x7A
-#define C6_8 0x7B
-
-#define C6_9 0x7C
-#define C6_10 0x7D
-#define C6_11 0x7E
-#define C6_12 0x7F
-#define C6_13 0x80
-#define C6_14 0x81
-#define C6_15 0x82
-#define C6_16 0x83
-
-#define C7_1 0x84
-#define C7_2 0x85
-#define C7_3 0x86
-#define C7_4 0x87
-#define C7_5 0x88
-#define C7_6 0x89
-#define C7_7 0x8A
-#define C7_8 0x8B
-
-#define C7_9 0x8C
-#define C7_10 0x8D
-#define C7_11 0x8E
-#define C7_12 0x8F
-#define C7_13 0x90
-#define C7_14 0x91
-#define C7_15 0x92
-#define C7_16 0x93
-
-#define C8_1 0x94
-#define C8_2 0x95
-#define C8_3 0x96
-#define C8_4 0x97
-#define C8_5 0x98
-#define C8_6 0x99
-#define C8_7 0x9A
-#define C8_8 0x9B
-
-#define C8_9 0x9C
-#define C8_10 0x9D
-#define C8_11 0x9E
-#define C8_12 0x9F
-#define C8_13 0xA0
-#define C8_14 0xA1
-#define C8_15 0xA2
-#define C8_16 0xA3
-
-#define C9_1 0xA4
-#define C9_2 0xA5
-#define C9_3 0xA6
-#define C9_4 0xA7
-#define C9_5 0xA8
-#define C9_6 0xA9
-#define C9_7 0xAA
-#define C9_8 0xAB
-
-#define C9_9 0xAC
-#define C9_10 0xAD
-#define C9_11 0xAE
-#define C9_12 0xAF
-#define C9_13 0xB0
-#define C9_14 0xB1
-#define C9_15 0xB2
-#define C9_16 0xB3
+#define C1_1 0x00
+#define C1_2 0x01
+#define C1_3 0x02
+#define C1_4 0x03
+#define C1_5 0x04
+#define C1_6 0x05
+#define C1_7 0x06
+#define C1_8 0x07
+
+#define C1_9 0x08
+#define C1_10 0x09
+#define C1_11 0x0A
+#define C1_12 0x0B
+#define C1_13 0x0C
+#define C1_14 0x0D
+#define C1_15 0x0E
+#define C1_16 0x0F
+
+#define C2_1 0x10
+#define C2_2 0x11
+#define C2_3 0x12
+#define C2_4 0x13
+#define C2_5 0x14
+#define C2_6 0x15
+#define C2_7 0x16
+#define C2_8 0x17
+
+#define C2_9 0x18
+#define C2_10 0x19
+#define C2_11 0x1A
+#define C2_12 0x1B
+#define C2_13 0x1C
+#define C2_14 0x1D
+#define C2_15 0x1E
+#define C2_16 0x1F
+
+#define C3_1 0x20
+#define C3_2 0x21
+#define C3_3 0x22
+#define C3_4 0x23
+#define C3_5 0x24
+#define C3_6 0x25
+#define C3_7 0x26
+#define C3_8 0x27
+
+#define C3_9 0x28
+#define C3_10 0x29
+#define C3_11 0x2A
+#define C3_12 0x2B
+#define C3_13 0x2C
+#define C3_14 0x2D
+#define C3_15 0x2E
+#define C3_16 0x2F
+
+#define C4_1 0x30
+#define C4_2 0x31
+#define C4_3 0x32
+#define C4_4 0x33
+#define C4_5 0x34
+#define C4_6 0x35
+#define C4_7 0x36
+#define C4_8 0x37
+
+#define C4_9 0x38
+#define C4_10 0x39
+#define C4_11 0x3A
+#define C4_12 0x3B
+#define C4_13 0x3C
+#define C4_14 0x3D
+#define C4_15 0x3E
+#define C4_16 0x3F
+
+#define C5_1 0x40
+#define C5_2 0x41
+#define C5_3 0x42
+#define C5_4 0x43
+#define C5_5 0x44
+#define C5_6 0x45
+#define C5_7 0x46
+#define C5_8 0x47
+
+#define C5_9 0x48
+#define C5_10 0x49
+#define C5_11 0x4A
+#define C5_12 0x4B
+#define C5_13 0x4C
+#define C5_14 0x4D
+#define C5_15 0x4E
+#define C5_16 0x4F
+
+#define C6_1 0x50
+#define C6_2 0x51
+#define C6_3 0x52
+#define C6_4 0x53
+#define C6_5 0x54
+#define C6_6 0x55
+#define C6_7 0x56
+#define C6_8 0x57
+
+#define C6_9 0x58
+#define C6_10 0x59
+#define C6_11 0x5A
+#define C6_12 0x5B
+#define C6_13 0x5C
+#define C6_14 0x5D
+#define C6_15 0x5E
+#define C6_16 0x5F
+
+#define C7_1 0x60
+#define C7_2 0x61
+#define C7_3 0x62
+#define C7_4 0x63
+#define C7_5 0x64
+#define C7_6 0x65
+#define C7_7 0x66
+#define C7_8 0x67
+
+#define C7_9 0x68
+#define C7_10 0x69
+#define C7_11 0x6A
+#define C7_12 0x6B
+#define C7_13 0x6C
+#define C7_14 0x6D
+#define C7_15 0x6E
+#define C7_16 0x6F
+
+#define C8_1 0x70
+#define C8_2 0x71
+#define C8_3 0x72
+#define C8_4 0x73
+#define C8_5 0x74
+#define C8_6 0x75
+#define C8_7 0x76
+#define C8_8 0x77
+
+#define C8_9 0x78
+#define C8_10 0x79
+#define C8_11 0x7A
+#define C8_12 0x7B
+#define C8_13 0x7C
+#define C8_14 0x7D
+#define C8_15 0x7E
+#define C8_16 0x7F
+
+#define C9_1 0x80
+#define C9_2 0x81
+#define C9_3 0x82
+#define C9_4 0x83
+#define C9_5 0x84
+#define C9_6 0x85
+#define C9_7 0x86
+#define C9_8 0x87
+
+#define C9_9 0x88
+#define C9_10 0x89
+#define C9_11 0x8A
+#define C9_12 0x8B
+#define C9_13 0x8C
+#define C9_14 0x8D
+#define C9_15 0x8E
+#define C9_16 0x8F
diff --git a/drivers/led/issi/is31fl3731-simple.c b/drivers/led/issi/is31fl3731-simple.c
deleted file mode 100644
index 8dbfc3cd31..0000000000
--- a/drivers/led/issi/is31fl3731-simple.c
+++ /dev/null
@@ -1,239 +0,0 @@
-/* Copyright 2017 Jason Williams
- * Copyright 2018 Jack Humbert
- * Copyright 2019 Clueboard
- * Copyright 2021 Doni Crosby
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#include "is31fl3731-simple.h"
-#include <string.h>
-#include "i2c_master.h"
-#include "wait.h"
-
-#define IS31FL3731_PWM_REGISTER_COUNT 144
-#define IS31FL3731_LED_CONTROL_REGISTER_COUNT 18
-
-#ifndef IS31FL3731_I2C_TIMEOUT
-# define IS31FL3731_I2C_TIMEOUT 100
-#endif
-
-#ifndef IS31FL3731_I2C_PERSISTENCE
-# define IS31FL3731_I2C_PERSISTENCE 0
-#endif
-
-// Transfer buffer for TWITransmitData()
-uint8_t g_twi_transfer_buffer[20];
-
-// These buffers match the IS31FL3731 PWM registers 0x24-0xB3.
-// Storing them like this is optimal for I2C transfers to the registers.
-// We could optimize this and take out the unused registers from these
-// buffers and the transfers in is31fl3731_write_pwm_buffer() but it's
-// probably not worth the extra complexity.
-uint8_t g_pwm_buffer[IS31FL3731_DRIVER_COUNT][IS31FL3731_PWM_REGISTER_COUNT];
-bool g_pwm_buffer_update_required[IS31FL3731_DRIVER_COUNT] = {false};
-
-uint8_t g_led_control_registers[IS31FL3731_DRIVER_COUNT][IS31FL3731_LED_CONTROL_REGISTER_COUNT] = {0};
-bool g_led_control_registers_update_required[IS31FL3731_DRIVER_COUNT] = {false};
-
-void is31fl3731_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
- g_twi_transfer_buffer[0] = reg;
- g_twi_transfer_buffer[1] = data;
-
-#if IS31FL3731_I2C_PERSISTENCE > 0
- for (uint8_t i = 0; i < IS31FL3731_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3731_I2C_TIMEOUT) == 0) {
- break;
- }
- }
-#else
- i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3731_I2C_TIMEOUT);
-#endif
-}
-
-void is31fl3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
- // assumes bank is already selected
-
- // transmit PWM registers in 9 transfers of 16 bytes
- // g_twi_transfer_buffer[] is 20 bytes
-
- // iterate over the pwm_buffer contents at 16 byte intervals
- for (int i = 0; i < IS31FL3731_PWM_REGISTER_COUNT; i += 16) {
- // set the first register, e.g. 0x24, 0x34, 0x44, etc.
- g_twi_transfer_buffer[0] = 0x24 + i;
- // copy the data from i to i+15
- // device will auto-increment register for data after the first byte
- // thus this sets registers 0x24-0x33, 0x34-0x43, etc. in one transfer
- memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16);
-
-#if IS31FL3731_I2C_PERSISTENCE > 0
- for (uint8_t i = 0; i < IS31FL3731_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3731_I2C_TIMEOUT) == 0) break;
- }
-#else
- i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3731_I2C_TIMEOUT);
-#endif
- }
-}
-
-void is31fl3731_init_drivers(void) {
- i2c_init();
-
- is31fl3731_init(IS31FL3731_I2C_ADDRESS_1);
-#if defined(IS31FL3731_I2C_ADDRESS_2)
- is31fl3731_init(IS31FL3731_I2C_ADDRESS_2);
-# if defined(IS31FL3731_I2C_ADDRESS_3)
- is31fl3731_init(IS31FL3731_I2C_ADDRESS_3);
-# if defined(IS31FL3731_I2C_ADDRESS_4)
- is31fl3731_init(IS31FL3731_I2C_ADDRESS_4);
-# endif
-# endif
-#endif
-
- for (int i = 0; i < IS31FL3731_LED_COUNT; i++) {
- is31fl3731_set_led_control_register(i, true);
- }
-
- is31fl3731_update_led_control_registers(IS31FL3731_I2C_ADDRESS_1, 0);
-#if defined(IS31FL3731_I2C_ADDRESS_2)
- is31fl3731_update_led_control_registers(IS31FL3731_I2C_ADDRESS_2, 1);
-# if defined(IS31FL3731_I2C_ADDRESS_3)
- is31fl3731_update_led_control_registers(IS31FL3731_I2C_ADDRESS_3, 2);
-# if defined(IS31FL3731_I2C_ADDRESS_4)
- is31fl3731_update_led_control_registers(IS31FL3731_I2C_ADDRESS_4, 3);
-# endif
-# endif
-#endif
-}
-
-void is31fl3731_init(uint8_t addr) {
- // In order to avoid the LEDs being driven with garbage data
- // in the LED driver's PWM registers, first enable software shutdown,
- // then set up the mode and other settings, clear the PWM registers,
- // then disable software shutdown.
-
- // select "function register" bank
- is31fl3731_write_register(addr, IS31FL3731_REG_COMMAND, IS31FL3731_COMMAND_FUNCTION);
-
- // enable software shutdown
- is31fl3731_write_register(addr, IS31FL3731_FUNCTION_REG_SHUTDOWN, 0x00);
-#ifdef IS31FL3731_DEGHOST // set to enable de-ghosting of the array
- is31fl3731_write_register(addr, IS31FL3731_FUNCTION_REG_GHOST_IMAGE_PREVENTION, IS31FL3731_GHOST_IMAGE_PREVENTION_GEN);
-#endif
-
- // this delay was copied from other drivers, might not be needed
- wait_ms(10);
-
- // picture mode
- is31fl3731_write_register(addr, IS31FL3731_FUNCTION_REG_CONFIG, IS31FL3731_CONFIG_MODE_PICTURE);
- // display frame 0
- is31fl3731_write_register(addr, IS31FL3731_FUNCTION_REG_PICTURE_DISPLAY, 0x00);
- // audio sync off
- is31fl3731_write_register(addr, IS31FL3731_FUNCTION_REG_AUDIO_SYNC, 0x00);
-
- // select bank 0
- is31fl3731_write_register(addr, IS31FL3731_REG_COMMAND, IS31FL3731_COMMAND_FRAME_1);
-
- // turn off all LEDs in the LED control register
- for (int i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) {
- is31fl3731_write_register(addr, i, 0x00);
- }
-
- // turn off all LEDs in the blink control register (not really needed)
- for (int i = 0x12; i <= 0x23; i++) {
- is31fl3731_write_register(addr, i, 0x00);
- }
-
- // set PWM on all LEDs to 0
- for (int i = 0x24; i <= 0xB3; i++) {
- is31fl3731_write_register(addr, i, 0x00);
- }
-
- // select "function register" bank
- is31fl3731_write_register(addr, IS31FL3731_REG_COMMAND, IS31FL3731_COMMAND_FUNCTION);
-
- // disable software shutdown
- is31fl3731_write_register(addr, IS31FL3731_FUNCTION_REG_SHUTDOWN, 0x01);
-
- // select bank 0 and leave it selected.
- // most usage after initialization is just writing PWM buffers in bank 0
- // as there's not much point in double-buffering
- is31fl3731_write_register(addr, IS31FL3731_REG_COMMAND, IS31FL3731_COMMAND_FRAME_1);
-}
-
-void is31fl3731_set_value(int index, uint8_t value) {
- is31fl3731_led_t led;
- if (index >= 0 && index < IS31FL3731_LED_COUNT) {
- memcpy_P(&led, (&g_is31fl3731_leds[index]), sizeof(led));
-
- // Subtract 0x24 to get the second index of g_pwm_buffer
-
- if (g_pwm_buffer[led.driver][led.v - 0x24] == value) {
- return;
- }
- g_pwm_buffer[led.driver][led.v - 0x24] = value;
- g_pwm_buffer_update_required[led.driver] = true;
- }
-}
-
-void is31fl3731_set_value_all(uint8_t value) {
- for (int i = 0; i < IS31FL3731_LED_COUNT; i++) {
- is31fl3731_set_value(i, value);
- }
-}
-
-void is31fl3731_set_led_control_register(uint8_t index, bool value) {
- is31fl3731_led_t led;
- memcpy_P(&led, (&g_is31fl3731_leds[index]), sizeof(led));
-
- uint8_t control_register = (led.v - 0x24) / 8;
- uint8_t bit_value = (led.v - 0x24) % 8;
-
- if (value) {
- g_led_control_registers[led.driver][control_register] |= (1 << bit_value);
- } else {
- g_led_control_registers[led.driver][control_register] &= ~(1 << bit_value);
- }
-
- g_led_control_registers_update_required[led.driver] = true;
-}
-
-void is31fl3731_update_pwm_buffers(uint8_t addr, uint8_t index) {
- if (g_pwm_buffer_update_required[index]) {
- is31fl3731_write_pwm_buffer(addr, g_pwm_buffer[index]);
- g_pwm_buffer_update_required[index] = false;
- }
-}
-
-void is31fl3731_update_led_control_registers(uint8_t addr, uint8_t index) {
- if (g_led_control_registers_update_required[index]) {
- for (int i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) {
- is31fl3731_write_register(addr, i, g_led_control_registers[index][i]);
- }
- g_led_control_registers_update_required[index] = false;
- }
-}
-
-void is31fl3731_flush(void) {
- is31fl3731_update_pwm_buffers(IS31FL3731_I2C_ADDRESS_1, 0);
-#if defined(IS31FL3731_I2C_ADDRESS_2)
- is31fl3731_update_pwm_buffers(IS31FL3731_I2C_ADDRESS_2, 1);
-# if defined(IS31FL3731_I2C_ADDRESS_3)
- is31fl3731_update_pwm_buffers(IS31FL3731_I2C_ADDRESS_3, 2);
-# if defined(IS31FL3731_I2C_ADDRESS_4)
- is31fl3731_update_pwm_buffers(IS31FL3731_I2C_ADDRESS_4, 3);
-# endif
-# endif
-#endif
-}
diff --git a/drivers/led/issi/is31fl3731.c b/drivers/led/issi/is31fl3731.c
index 1ab8997731..86d953ef25 100644
--- a/drivers/led/issi/is31fl3731.c
+++ b/drivers/led/issi/is31fl3731.c
@@ -17,8 +17,8 @@
*/
#include "is31fl3731.h"
-#include <string.h>
#include "i2c_master.h"
+#include "gpio.h"
#include "wait.h"
#define IS31FL3731_PWM_REGISTER_COUNT 144
@@ -32,54 +32,64 @@
# define IS31FL3731_I2C_PERSISTENCE 0
#endif
-// Transfer buffer for TWITransmitData()
-uint8_t g_twi_transfer_buffer[20];
+const uint8_t i2c_addresses[IS31FL3731_DRIVER_COUNT] = {
+ IS31FL3731_I2C_ADDRESS_1,
+#ifdef IS31FL3731_I2C_ADDRESS_2
+ IS31FL3731_I2C_ADDRESS_2,
+# ifdef IS31FL3731_I2C_ADDRESS_3
+ IS31FL3731_I2C_ADDRESS_3,
+# ifdef IS31FL3731_I2C_ADDRESS_4
+ IS31FL3731_I2C_ADDRESS_4,
+# endif
+# endif
+#endif
+};
// These buffers match the IS31FL3731 PWM registers 0x24-0xB3.
// Storing them like this is optimal for I2C transfers to the registers.
// We could optimize this and take out the unused registers from these
// buffers and the transfers in is31fl3731_write_pwm_buffer() but it's
// probably not worth the extra complexity.
-uint8_t g_pwm_buffer[IS31FL3731_DRIVER_COUNT][IS31FL3731_PWM_REGISTER_COUNT];
-bool g_pwm_buffer_update_required[IS31FL3731_DRIVER_COUNT] = {false};
-
-uint8_t g_led_control_registers[IS31FL3731_DRIVER_COUNT][IS31FL3731_LED_CONTROL_REGISTER_COUNT] = {0};
-bool g_led_control_registers_update_required[IS31FL3731_DRIVER_COUNT] = {false};
-
-void is31fl3731_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
- g_twi_transfer_buffer[0] = reg;
- g_twi_transfer_buffer[1] = data;
-
+typedef struct is31fl3731_driver_t {
+ uint8_t pwm_buffer[IS31FL3731_PWM_REGISTER_COUNT];
+ bool pwm_buffer_dirty;
+ uint8_t led_control_buffer[IS31FL3731_LED_CONTROL_REGISTER_COUNT];
+ bool led_control_buffer_dirty;
+} PACKED is31fl3731_driver_t;
+
+is31fl3731_driver_t driver_buffers[IS31FL3731_DRIVER_COUNT] = {{
+ .pwm_buffer = {0},
+ .pwm_buffer_dirty = false,
+ .led_control_buffer = {0},
+ .led_control_buffer_dirty = false,
+}};
+
+void is31fl3731_write_register(uint8_t index, uint8_t reg, uint8_t data) {
#if IS31FL3731_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3731_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3731_I2C_TIMEOUT) == 0) break;
+ if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3731_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
}
#else
- i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3731_I2C_TIMEOUT);
+ i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3731_I2C_TIMEOUT);
#endif
}
-void is31fl3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
- // assumes bank is already selected
-
- // transmit PWM registers in 9 transfers of 16 bytes
- // g_twi_transfer_buffer[] is 20 bytes
+void is31fl3731_select_page(uint8_t index, uint8_t page) {
+ is31fl3731_write_register(index, IS31FL3731_REG_COMMAND, page);
+}
- // iterate over the pwm_buffer contents at 16 byte intervals
- for (int i = 0; i < IS31FL3731_PWM_REGISTER_COUNT; i += 16) {
- // set the first register, e.g. 0x24, 0x34, 0x44, etc.
- g_twi_transfer_buffer[0] = 0x24 + i;
- // copy the data from i to i+15
- // device will auto-increment register for data after the first byte
- // thus this sets registers 0x24-0x33, 0x34-0x43, etc. in one transfer
- memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16);
+void is31fl3731_write_pwm_buffer(uint8_t index) {
+ // Assumes page 0 is already selected.
+ // Transmit PWM registers in 9 transfers of 16 bytes.
+ // Iterate over the pwm_buffer contents at 16 byte intervals.
+ for (uint8_t i = 0; i < IS31FL3731_PWM_REGISTER_COUNT; i += 16) {
#if IS31FL3731_I2C_PERSISTENCE > 0
- for (uint8_t i = 0; i < IS31FL3731_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3731_I2C_TIMEOUT) == 0) break;
+ for (uint8_t j = 0; j < IS31FL3731_I2C_PERSISTENCE; j++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, IS31FL3731_FRAME_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3731_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
}
#else
- i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3731_I2C_TIMEOUT);
+ i2c_write_register(i2c_addresses[index] << 1, IS31FL3731_FRAME_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3731_I2C_TIMEOUT);
#endif
}
}
@@ -87,101 +97,90 @@ void is31fl3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
void is31fl3731_init_drivers(void) {
i2c_init();
- is31fl3731_init(IS31FL3731_I2C_ADDRESS_1);
-#if defined(IS31FL3731_I2C_ADDRESS_2)
- is31fl3731_init(IS31FL3731_I2C_ADDRESS_2);
-# if defined(IS31FL3731_I2C_ADDRESS_3)
- is31fl3731_init(IS31FL3731_I2C_ADDRESS_3);
-# if defined(IS31FL3731_I2C_ADDRESS_4)
- is31fl3731_init(IS31FL3731_I2C_ADDRESS_4);
-# endif
-# endif
+#if defined(IS31FL3731_SDB_PIN)
+ gpio_set_pin_output(IS31FL3731_SDB_PIN);
+ gpio_write_pin_high(IS31FL3731_SDB_PIN);
#endif
+ for (uint8_t i = 0; i < IS31FL3731_DRIVER_COUNT; i++) {
+ is31fl3731_init(i);
+ }
+
for (int i = 0; i < IS31FL3731_LED_COUNT; i++) {
is31fl3731_set_led_control_register(i, true, true, true);
}
- is31fl3731_update_led_control_registers(IS31FL3731_I2C_ADDRESS_1, 0);
-#if defined(IS31FL3731_I2C_ADDRESS_2)
- is31fl3731_update_led_control_registers(IS31FL3731_I2C_ADDRESS_2, 1);
-# if defined(IS31FL3731_I2C_ADDRESS_3)
- is31fl3731_update_led_control_registers(IS31FL3731_I2C_ADDRESS_3, 2);
-# if defined(IS31FL3731_I2C_ADDRESS_4)
- is31fl3731_update_led_control_registers(IS31FL3731_I2C_ADDRESS_4, 3);
-# endif
-# endif
-#endif
+ for (uint8_t i = 0; i < IS31FL3731_DRIVER_COUNT; i++) {
+ is31fl3731_update_led_control_registers(i);
+ }
}
-void is31fl3731_init(uint8_t addr) {
+void is31fl3731_init(uint8_t index) {
// In order to avoid the LEDs being driven with garbage data
// in the LED driver's PWM registers, first enable software shutdown,
// then set up the mode and other settings, clear the PWM registers,
// then disable software shutdown.
- // select "function register" bank
- is31fl3731_write_register(addr, IS31FL3731_REG_COMMAND, IS31FL3731_COMMAND_FUNCTION);
+ is31fl3731_select_page(index, IS31FL3731_COMMAND_FUNCTION);
// enable software shutdown
- is31fl3731_write_register(addr, IS31FL3731_FUNCTION_REG_SHUTDOWN, 0x00);
+ is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_SHUTDOWN, 0x00);
#ifdef IS31FL3731_DEGHOST // set to enable de-ghosting of the array
- is31fl3731_write_register(addr, IS31FL3731_FUNCTION_REG_GHOST_IMAGE_PREVENTION, IS31FL3731_GHOST_IMAGE_PREVENTION_GEN);
+ is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_GHOST_IMAGE_PREVENTION, IS31FL3731_GHOST_IMAGE_PREVENTION_GEN);
#endif
// this delay was copied from other drivers, might not be needed
wait_ms(10);
// picture mode
- is31fl3731_write_register(addr, IS31FL3731_FUNCTION_REG_CONFIG, IS31FL3731_CONFIG_MODE_PICTURE);
+ is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_CONFIG, IS31FL3731_CONFIG_MODE_PICTURE);
// display frame 0
- is31fl3731_write_register(addr, IS31FL3731_FUNCTION_REG_PICTURE_DISPLAY, 0x00);
+ is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_PICTURE_DISPLAY, 0x00);
// audio sync off
- is31fl3731_write_register(addr, IS31FL3731_FUNCTION_REG_AUDIO_SYNC, 0x00);
+ is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_AUDIO_SYNC, 0x00);
- // select bank 0
- is31fl3731_write_register(addr, IS31FL3731_REG_COMMAND, IS31FL3731_COMMAND_FRAME_1);
+ is31fl3731_select_page(index, IS31FL3731_COMMAND_FRAME_1);
// turn off all LEDs in the LED control register
- for (int i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) {
- is31fl3731_write_register(addr, i, 0x00);
+ for (uint8_t i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) {
+ is31fl3731_write_register(index, IS31FL3731_FRAME_REG_LED_CONTROL + i, 0x00);
}
// turn off all LEDs in the blink control register (not really needed)
- for (int i = 0x12; i <= 0x23; i++) {
- is31fl3731_write_register(addr, i, 0x00);
+ for (uint8_t i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) {
+ is31fl3731_write_register(index, IS31FL3731_FRAME_REG_BLINK_CONTROL + i, 0x00);
}
// set PWM on all LEDs to 0
- for (int i = 0x24; i <= 0xB3; i++) {
- is31fl3731_write_register(addr, i, 0x00);
+ for (uint8_t i = 0; i < IS31FL3731_PWM_REGISTER_COUNT; i++) {
+ is31fl3731_write_register(index, IS31FL3731_FRAME_REG_PWM + i, 0x00);
}
- // select "function register" bank
- is31fl3731_write_register(addr, IS31FL3731_REG_COMMAND, IS31FL3731_COMMAND_FUNCTION);
+ is31fl3731_select_page(index, IS31FL3731_COMMAND_FUNCTION);
// disable software shutdown
- is31fl3731_write_register(addr, IS31FL3731_FUNCTION_REG_SHUTDOWN, 0x01);
+ is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_SHUTDOWN, 0x01);
- // select bank 0 and leave it selected.
- // most usage after initialization is just writing PWM buffers in bank 0
+ // select page 0 and leave it selected.
+ // most usage after initialization is just writing PWM buffers in page 0
// as there's not much point in double-buffering
- is31fl3731_write_register(addr, IS31FL3731_REG_COMMAND, IS31FL3731_COMMAND_FRAME_1);
+ is31fl3731_select_page(index, IS31FL3731_COMMAND_FRAME_1);
}
void is31fl3731_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
is31fl3731_led_t led;
+
if (index >= 0 && index < IS31FL3731_LED_COUNT) {
memcpy_P(&led, (&g_is31fl3731_leds[index]), sizeof(led));
- // Subtract 0x24 to get the second index of g_pwm_buffer
- if (g_pwm_buffer[led.driver][led.r - 0x24] == red && g_pwm_buffer[led.driver][led.g - 0x24] == green && g_pwm_buffer[led.driver][led.b - 0x24] == blue) {
+ if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) {
return;
}
- g_pwm_buffer[led.driver][led.r - 0x24] = red;
- g_pwm_buffer[led.driver][led.g - 0x24] = green;
- g_pwm_buffer[led.driver][led.b - 0x24] = blue;
- g_pwm_buffer_update_required[led.driver] = true;
+
+ driver_buffers[led.driver].pwm_buffer[led.r] = red;
+ driver_buffers[led.driver].pwm_buffer[led.g] = green;
+ driver_buffers[led.driver].pwm_buffer[led.b] = blue;
+ driver_buffers[led.driver].pwm_buffer_dirty = true;
}
}
@@ -195,57 +194,52 @@ void is31fl3731_set_led_control_register(uint8_t index, bool red, bool green, bo
is31fl3731_led_t led;
memcpy_P(&led, (&g_is31fl3731_leds[index]), sizeof(led));
- uint8_t control_register_r = (led.r - 0x24) / 8;
- uint8_t control_register_g = (led.g - 0x24) / 8;
- uint8_t control_register_b = (led.b - 0x24) / 8;
- uint8_t bit_r = (led.r - 0x24) % 8;
- uint8_t bit_g = (led.g - 0x24) % 8;
- uint8_t bit_b = (led.b - 0x24) % 8;
+ uint8_t control_register_r = led.r / 8;
+ uint8_t control_register_g = led.g / 8;
+ uint8_t control_register_b = led.b / 8;
+ uint8_t bit_r = led.r % 8;
+ uint8_t bit_g = led.g % 8;
+ uint8_t bit_b = led.b % 8;
if (red) {
- g_led_control_registers[led.driver][control_register_r] |= (1 << bit_r);
+ driver_buffers[led.driver].led_control_buffer[control_register_r] |= (1 << bit_r);
} else {
- g_led_control_registers[led.driver][control_register_r] &= ~(1 << bit_r);
+ driver_buffers[led.driver].led_control_buffer[control_register_r] &= ~(1 << bit_r);
}
if (green) {
- g_led_control_registers[led.driver][control_register_g] |= (1 << bit_g);
+ driver_buffers[led.driver].led_control_buffer[control_register_g] |= (1 << bit_g);
} else {
- g_led_control_registers[led.driver][control_register_g] &= ~(1 << bit_g);
+ driver_buffers[led.driver].led_control_buffer[control_register_g] &= ~(1 << bit_g);
}
if (blue) {
- g_led_control_registers[led.driver][control_register_b] |= (1 << bit_b);
+ driver_buffers[led.driver].led_control_buffer[control_register_b] |= (1 << bit_b);
} else {
- g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b);
+ driver_buffers[led.driver].led_control_buffer[control_register_b] &= ~(1 << bit_b);
}
- g_led_control_registers_update_required[led.driver] = true;
+ driver_buffers[led.driver].led_control_buffer_dirty = true;
}
-void is31fl3731_update_pwm_buffers(uint8_t addr, uint8_t index) {
- if (g_pwm_buffer_update_required[index]) {
- is31fl3731_write_pwm_buffer(addr, g_pwm_buffer[index]);
+void is31fl3731_update_pwm_buffers(uint8_t index) {
+ if (driver_buffers[index].pwm_buffer_dirty) {
+ is31fl3731_write_pwm_buffer(index);
+
+ driver_buffers[index].pwm_buffer_dirty = false;
}
- g_pwm_buffer_update_required[index] = false;
}
-void is31fl3731_update_led_control_registers(uint8_t addr, uint8_t index) {
- if (g_led_control_registers_update_required[index]) {
- for (int i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) {
- is31fl3731_write_register(addr, i, g_led_control_registers[index][i]);
+void is31fl3731_update_led_control_registers(uint8_t index) {
+ if (driver_buffers[index].led_control_buffer_dirty) {
+ for (uint8_t i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) {
+ is31fl3731_write_register(index, i, driver_buffers[index].led_control_buffer[i]);
}
+
+ driver_buffers[index].led_control_buffer_dirty = false;
}
- g_led_control_registers_update_required[index] = false;
}
void is31fl3731_flush(void) {
- is31fl3731_update_pwm_buffers(IS31FL3731_I2C_ADDRESS_1, 0);
-#if defined(IS31FL3731_I2C_ADDRESS_2)
- is31fl3731_update_pwm_buffers(IS31FL3731_I2C_ADDRESS_2, 1);
-# if defined(IS31FL3731_I2C_ADDRESS_3)
- is31fl3731_update_pwm_buffers(IS31FL3731_I2C_ADDRESS_3, 2);
-# if defined(IS31FL3731_I2C_ADDRESS_4)
- is31fl3731_update_pwm_buffers(IS31FL3731_I2C_ADDRESS_4, 3);
-# endif
-# endif
-#endif
+ for (uint8_t i = 0; i < IS31FL3731_DRIVER_COUNT; i++) {
+ is31fl3731_update_pwm_buffers(i);
+ }
}
diff --git a/drivers/led/issi/is31fl3731.h b/drivers/led/issi/is31fl3731.h
index b45cb2b07d..dc229c876d 100644
--- a/drivers/led/issi/is31fl3731.h
+++ b/drivers/led/issi/is31fl3731.h
@@ -60,6 +60,10 @@
#define IS31FL3731_COMMAND_FRAME_8 0x07
#define IS31FL3731_COMMAND_FUNCTION 0x0B
+#define IS31FL3731_FRAME_REG_LED_CONTROL 0x00
+#define IS31FL3731_FRAME_REG_BLINK_CONTROL 0x12
+#define IS31FL3731_FRAME_REG_PWM 0x24
+
#define IS31FL3731_FUNCTION_REG_CONFIG 0x00
#define IS31FL3731_CONFIG_MODE_PICTURE 0x00
#define IS31FL3731_CONFIG_MODE_AUTO_PLAY 0x08
@@ -102,9 +106,9 @@ typedef struct is31fl3731_led_t {
extern const is31fl3731_led_t PROGMEM g_is31fl3731_leds[IS31FL3731_LED_COUNT];
void is31fl3731_init_drivers(void);
-void is31fl3731_init(uint8_t addr);
-void is31fl3731_write_register(uint8_t addr, uint8_t reg, uint8_t data);
-void is31fl3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
+void is31fl3731_init(uint8_t index);
+void is31fl3731_write_register(uint8_t index, uint8_t reg, uint8_t data);
+void is31fl3731_select_page(uint8_t index, uint8_t page);
void is31fl3731_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
void is31fl3731_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
@@ -115,169 +119,169 @@ void is31fl3731_set_led_control_register(uint8_t index, bool red, bool green, bo
// (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 is31fl3731_update_pwm_buffers(uint8_t addr, uint8_t index);
-void is31fl3731_update_led_control_registers(uint8_t addr, uint8_t index);
+void is31fl3731_update_pwm_buffers(uint8_t index);
+void is31fl3731_update_led_control_registers(uint8_t index);
void is31fl3731_flush(void);
-#define C1_1 0x24
-#define C1_2 0x25
-#define C1_3 0x26
-#define C1_4 0x27
-#define C1_5 0x28
-#define C1_6 0x29
-#define C1_7 0x2A
-#define C1_8 0x2B
-
-#define C1_9 0x2C
-#define C1_10 0x2D
-#define C1_11 0x2E
-#define C1_12 0x2F
-#define C1_13 0x30
-#define C1_14 0x31
-#define C1_15 0x32
-#define C1_16 0x33
-
-#define C2_1 0x34
-#define C2_2 0x35
-#define C2_3 0x36
-#define C2_4 0x37
-#define C2_5 0x38
-#define C2_6 0x39
-#define C2_7 0x3A
-#define C2_8 0x3B
-
-#define C2_9 0x3C
-#define C2_10 0x3D
-#define C2_11 0x3E
-#define C2_12 0x3F
-#define C2_13 0x40
-#define C2_14 0x41
-#define C2_15 0x42
-#define C2_16 0x43
-
-#define C3_1 0x44
-#define C3_2 0x45
-#define C3_3 0x46
-#define C3_4 0x47
-#define C3_5 0x48
-#define C3_6 0x49
-#define C3_7 0x4A
-#define C3_8 0x4B
-
-#define C3_9 0x4C
-#define C3_10 0x4D
-#define C3_11 0x4E
-#define C3_12 0x4F
-#define C3_13 0x50
-#define C3_14 0x51
-#define C3_15 0x52
-#define C3_16 0x53
-
-#define C4_1 0x54
-#define C4_2 0x55
-#define C4_3 0x56
-#define C4_4 0x57
-#define C4_5 0x58
-#define C4_6 0x59
-#define C4_7 0x5A
-#define C4_8 0x5B
-
-#define C4_9 0x5C
-#define C4_10 0x5D
-#define C4_11 0x5E
-#define C4_12 0x5F
-#define C4_13 0x60
-#define C4_14 0x61
-#define C4_15 0x62
-#define C4_16 0x63
-
-#define C5_1 0x64
-#define C5_2 0x65
-#define C5_3 0x66
-#define C5_4 0x67
-#define C5_5 0x68
-#define C5_6 0x69
-#define C5_7 0x6A
-#define C5_8 0x6B
-
-#define C5_9 0x6C
-#define C5_10 0x6D
-#define C5_11 0x6E
-#define C5_12 0x6F
-#define C5_13 0x70
-#define C5_14 0x71
-#define C5_15 0x72
-#define C5_16 0x73
-
-#define C6_1 0x74
-#define C6_2 0x75
-#define C6_3 0x76
-#define C6_4 0x77
-#define C6_5 0x78
-#define C6_6 0x79
-#define C6_7 0x7A
-#define C6_8 0x7B
-
-#define C6_9 0x7C
-#define C6_10 0x7D
-#define C6_11 0x7E
-#define C6_12 0x7F
-#define C6_13 0x80
-#define C6_14 0x81
-#define C6_15 0x82
-#define C6_16 0x83
-
-#define C7_1 0x84
-#define C7_2 0x85
-#define C7_3 0x86
-#define C7_4 0x87
-#define C7_5 0x88
-#define C7_6 0x89
-#define C7_7 0x8A
-#define C7_8 0x8B
-
-#define C7_9 0x8C
-#define C7_10 0x8D
-#define C7_11 0x8E
-#define C7_12 0x8F
-#define C7_13 0x90
-#define C7_14 0x91
-#define C7_15 0x92
-#define C7_16 0x93
-
-#define C8_1 0x94
-#define C8_2 0x95
-#define C8_3 0x96
-#define C8_4 0x97
-#define C8_5 0x98
-#define C8_6 0x99
-#define C8_7 0x9A
-#define C8_8 0x9B
-
-#define C8_9 0x9C
-#define C8_10 0x9D
-#define C8_11 0x9E
-#define C8_12 0x9F
-#define C8_13 0xA0
-#define C8_14 0xA1
-#define C8_15 0xA2
-#define C8_16 0xA3
-
-#define C9_1 0xA4
-#define C9_2 0xA5
-#define C9_3 0xA6
-#define C9_4 0xA7
-#define C9_5 0xA8
-#define C9_6 0xA9
-#define C9_7 0xAA
-#define C9_8 0xAB
-
-#define C9_9 0xAC
-#define C9_10 0xAD
-#define C9_11 0xAE
-#define C9_12 0xAF
-#define C9_13 0xB0
-#define C9_14 0xB1
-#define C9_15 0xB2
-#define C9_16 0xB3
+#define C1_1 0x00
+#define C1_2 0x01
+#define C1_3 0x02
+#define C1_4 0x03
+#define C1_5 0x04
+#define C1_6 0x05
+#define C1_7 0x06
+#define C1_8 0x07
+
+#define C1_9 0x08
+#define C1_10 0x09
+#define C1_11 0x0A
+#define C1_12 0x0B
+#define C1_13 0x0C
+#define C1_14 0x0D
+#define C1_15 0x0E
+#define C1_16 0x0F
+
+#define C2_1 0x10
+#define C2_2 0x11
+#define C2_3 0x12
+#define C2_4 0x13
+#define C2_5 0x14
+#define C2_6 0x15
+#define C2_7 0x16
+#define C2_8 0x17
+
+#define C2_9 0x18
+#define C2_10 0x19
+#define C2_11 0x1A
+#define C2_12 0x1B
+#define C2_13 0x1C
+#define C2_14 0x1D
+#define C2_15 0x1E
+#define C2_16 0x1F
+
+#define C3_1 0x20
+#define C3_2 0x21
+#define C3_3 0x22
+#define C3_4 0x23
+#define C3_5 0x24
+#define C3_6 0x25
+#define C3_7 0x26
+#define C3_8 0x27
+
+#define C3_9 0x28
+#define C3_10 0x29
+#define C3_11 0x2A
+#define C3_12 0x2B
+#define C3_13 0x2C
+#define C3_14 0x2D
+#define C3_15 0x2E
+#define C3_16 0x2F
+
+#define C4_1 0x30
+#define C4_2 0x31
+#define C4_3 0x32
+#define C4_4 0x33
+#define C4_5 0x34
+#define C4_6 0x35
+#define C4_7 0x36
+#define C4_8 0x37
+
+#define C4_9 0x38
+#define C4_10 0x39
+#define C4_11 0x3A
+#define C4_12 0x3B
+#define C4_13 0x3C
+#define C4_14 0x3D
+#define C4_15 0x3E
+#define C4_16 0x3F
+
+#define C5_1 0x40
+#define C5_2 0x41
+#define C5_3 0x42
+#define C5_4 0x43
+#define C5_5 0x44
+#define C5_6 0x45
+#define C5_7 0x46
+#define C5_8 0x47
+
+#define C5_9 0x48
+#define C5_10 0x49
+#define C5_11 0x4A
+#define C5_12 0x4B
+#define C5_13 0x4C
+#define C5_14 0x4D
+#define C5_15 0x4E
+#define C5_16 0x4F
+
+#define C6_1 0x50
+#define C6_2 0x51
+#define C6_3 0x52
+#define C6_4 0x53
+#define C6_5 0x54
+#define C6_6 0x55
+#define C6_7 0x56
+#define C6_8 0x57
+
+#define C6_9 0x58
+#define C6_10 0x59
+#define C6_11 0x5A
+#define C6_12 0x5B
+#define C6_13 0x5C
+#define C6_14 0x5D
+#define C6_15 0x5E
+#define C6_16 0x5F
+
+#define C7_1 0x60
+#define C7_2 0x61
+#define C7_3 0x62
+#define C7_4 0x63
+#define C7_5 0x64
+#define C7_6 0x65
+#define C7_7 0x66
+#define C7_8 0x67
+
+#define C7_9 0x68
+#define C7_10 0x69
+#define C7_11 0x6A
+#define C7_12 0x6B
+#define C7_13 0x6C
+#define C7_14 0x6D
+#define C7_15 0x6E
+#define C7_16 0x6F
+
+#define C8_1 0x70
+#define C8_2 0x71
+#define C8_3 0x72
+#define C8_4 0x73
+#define C8_5 0x74
+#define C8_6 0x75
+#define C8_7 0x76
+#define C8_8 0x77
+
+#define C8_9 0x78
+#define C8_10 0x79
+#define C8_11 0x7A
+#define C8_12 0x7B
+#define C8_13 0x7C
+#define C8_14 0x7D
+#define C8_15 0x7E
+#define C8_16 0x7F
+
+#define C9_1 0x80
+#define C9_2 0x81
+#define C9_3 0x82
+#define C9_4 0x83
+#define C9_5 0x84
+#define C9_6 0x85
+#define C9_7 0x86
+#define C9_8 0x87
+
+#define C9_9 0x88
+#define C9_10 0x89
+#define C9_11 0x8A
+#define C9_12 0x8B
+#define C9_13 0x8C
+#define C9_14 0x8D
+#define C9_15 0x8E
+#define C9_16 0x8F
diff --git a/drivers/led/issi/is31fl3733-mono.c b/drivers/led/issi/is31fl3733-mono.c
new file mode 100644
index 0000000000..740fe06097
--- /dev/null
+++ b/drivers/led/issi/is31fl3733-mono.c
@@ -0,0 +1,265 @@
+/* Copyright 2017 Jason Williams
+ * Copyright 2018 Jack Humbert
+ * Copyright 2018 Yiancar
+ * Copyright 2021 Doni Crosby
+ * Copyright 2021 Leo Deng
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "is31fl3733-mono.h"
+#include "i2c_master.h"
+#include "gpio.h"
+#include "wait.h"
+
+#define IS31FL3733_PWM_REGISTER_COUNT 192
+#define IS31FL3733_LED_CONTROL_REGISTER_COUNT 24
+
+#ifndef IS31FL3733_I2C_TIMEOUT
+# define IS31FL3733_I2C_TIMEOUT 100
+#endif
+
+#ifndef IS31FL3733_I2C_PERSISTENCE
+# define IS31FL3733_I2C_PERSISTENCE 0
+#endif
+
+#ifndef IS31FL3733_PWM_FREQUENCY
+# define IS31FL3733_PWM_FREQUENCY IS31FL3733_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3733B only
+#endif
+
+#ifndef IS31FL3733_SW_PULLUP
+# define IS31FL3733_SW_PULLUP IS31FL3733_PUR_0_OHM
+#endif
+
+#ifndef IS31FL3733_CS_PULLDOWN
+# define IS31FL3733_CS_PULLDOWN IS31FL3733_PDR_0_OHM
+#endif
+
+#ifndef IS31FL3733_GLOBAL_CURRENT
+# define IS31FL3733_GLOBAL_CURRENT 0xFF
+#endif
+
+#ifndef IS31FL3733_SYNC_1
+# define IS31FL3733_SYNC_1 IS31FL3733_SYNC_NONE
+#endif
+#ifndef IS31FL3733_SYNC_2
+# define IS31FL3733_SYNC_2 IS31FL3733_SYNC_NONE
+#endif
+#ifndef IS31FL3733_SYNC_3
+# define IS31FL3733_SYNC_3 IS31FL3733_SYNC_NONE
+#endif
+#ifndef IS31FL3733_SYNC_4
+# define IS31FL3733_SYNC_4 IS31FL3733_SYNC_NONE
+#endif
+
+const uint8_t i2c_addresses[IS31FL3733_DRIVER_COUNT] = {
+ IS31FL3733_I2C_ADDRESS_1,
+#ifdef IS31FL3733_I2C_ADDRESS_2
+ IS31FL3733_I2C_ADDRESS_2,
+# ifdef IS31FL3733_I2C_ADDRESS_3
+ IS31FL3733_I2C_ADDRESS_3,
+# ifdef IS31FL3733_I2C_ADDRESS_4
+ IS31FL3733_I2C_ADDRESS_4,
+# endif
+# endif
+#endif
+};
+
+const uint8_t driver_sync[IS31FL3733_DRIVER_COUNT] = {
+ IS31FL3733_SYNC_1,
+#ifdef IS31FL3733_I2C_ADDRESS_2
+ IS31FL3733_SYNC_2,
+# ifdef IS31FL3733_I2C_ADDRESS_3
+ IS31FL3733_SYNC_3,
+# ifdef IS31FL3733_I2C_ADDRESS_4
+ IS31FL3733_SYNC_4,
+# endif
+# endif
+#endif
+};
+
+// These buffers match the IS31FL3733 PWM registers.
+// The control buffers match the page 0 LED On/Off registers.
+// Storing them like this is optimal for I2C transfers to the registers.
+// We could optimize this and take out the unused registers from these
+// buffers and the transfers in is31fl3733_write_pwm_buffer() but it's
+// probably not worth the extra complexity.
+typedef struct is31fl3733_driver_t {
+ uint8_t pwm_buffer[IS31FL3733_PWM_REGISTER_COUNT];
+ bool pwm_buffer_dirty;
+ uint8_t led_control_buffer[IS31FL3733_LED_CONTROL_REGISTER_COUNT];
+ bool led_control_buffer_dirty;
+} PACKED is31fl3733_driver_t;
+
+is31fl3733_driver_t driver_buffers[IS31FL3733_DRIVER_COUNT] = {{
+ .pwm_buffer = {0},
+ .pwm_buffer_dirty = false,
+ .led_control_buffer = {0},
+ .led_control_buffer_dirty = false,
+}};
+
+void is31fl3733_write_register(uint8_t index, uint8_t reg, uint8_t data) {
+#if IS31FL3733_I2C_PERSISTENCE > 0
+ for (uint8_t i = 0; i < IS31FL3733_I2C_PERSISTENCE; i++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3733_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3733_I2C_TIMEOUT);
+#endif
+}
+
+void is31fl3733_select_page(uint8_t index, uint8_t page) {
+ is31fl3733_write_register(index, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC);
+ is31fl3733_write_register(index, IS31FL3733_REG_COMMAND, page);
+}
+
+void is31fl3733_write_pwm_buffer(uint8_t index) {
+ // Assumes page 1 is already selected.
+ // Transmit PWM registers in 12 transfers of 16 bytes.
+
+ // Iterate over the pwm_buffer contents at 16 byte intervals.
+ for (uint8_t i = 0; i < IS31FL3733_PWM_REGISTER_COUNT; i += 16) {
+#if IS31FL3733_I2C_PERSISTENCE > 0
+ for (uint8_t j = 0; j < IS31FL3733_I2C_PERSISTENCE; j++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3733_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3733_I2C_TIMEOUT);
+#endif
+ }
+}
+
+void is31fl3733_init_drivers(void) {
+ i2c_init();
+
+#if defined(IS31FL3733_SDB_PIN)
+ gpio_set_pin_output(IS31FL3733_SDB_PIN);
+ gpio_write_pin_high(IS31FL3733_SDB_PIN);
+#endif
+
+ for (uint8_t i = 0; i < IS31FL3733_DRIVER_COUNT; i++) {
+ is31fl3733_init(i);
+ }
+
+ for (int i = 0; i < IS31FL3733_LED_COUNT; i++) {
+ is31fl3733_set_led_control_register(i, true);
+ }
+
+ for (uint8_t i = 0; i < IS31FL3733_DRIVER_COUNT; i++) {
+ is31fl3733_update_led_control_registers(i);
+ }
+}
+
+void is31fl3733_init(uint8_t index) {
+ // In order to avoid the LEDs being driven with garbage data
+ // in the LED driver's PWM registers, shutdown is enabled last.
+ // Set up the mode and other settings, clear the PWM registers,
+ // then disable software shutdown.
+
+ is31fl3733_select_page(index, IS31FL3733_COMMAND_LED_CONTROL);
+
+ // Turn off all LEDs.
+ for (uint8_t i = 0; i < IS31FL3733_LED_CONTROL_REGISTER_COUNT; i++) {
+ is31fl3733_write_register(index, i, 0x00);
+ }
+
+ is31fl3733_select_page(index, IS31FL3733_COMMAND_PWM);
+
+ // Set PWM on all LEDs to 0
+ // No need to setup Breath registers to PWM as that is the default.
+ for (uint8_t i = 0; i < IS31FL3733_PWM_REGISTER_COUNT; i++) {
+ is31fl3733_write_register(index, i, 0x00);
+ }
+
+ is31fl3733_select_page(index, IS31FL3733_COMMAND_FUNCTION);
+
+ uint8_t sync = driver_sync[index];
+
+ // Set de-ghost pull-up resistors (SWx)
+ is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_SW_PULLUP, IS31FL3733_SW_PULLUP);
+ // Set de-ghost pull-down resistors (CSx)
+ is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_CS_PULLDOWN, IS31FL3733_CS_PULLDOWN);
+ // Set global current to maximum.
+ is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3733_GLOBAL_CURRENT);
+ // Disable software shutdown.
+ is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_CONFIGURATION, ((sync & 0b11) << 6) | ((IS31FL3733_PWM_FREQUENCY & 0b111) << 3) | 0x01);
+
+ // Wait 10ms to ensure the device has woken up.
+ wait_ms(10);
+}
+
+void is31fl3733_set_value(int index, uint8_t value) {
+ is31fl3733_led_t led;
+
+ if (index >= 0 && index < IS31FL3733_LED_COUNT) {
+ memcpy_P(&led, (&g_is31fl3733_leds[index]), sizeof(led));
+
+ if (driver_buffers[led.driver].pwm_buffer[led.v] == value) {
+ return;
+ }
+
+ driver_buffers[led.driver].pwm_buffer[led.v] = value;
+ driver_buffers[led.driver].pwm_buffer_dirty = true;
+ }
+}
+
+void is31fl3733_set_value_all(uint8_t value) {
+ for (int i = 0; i < IS31FL3733_LED_COUNT; i++) {
+ is31fl3733_set_value(i, value);
+ }
+}
+
+void is31fl3733_set_led_control_register(uint8_t index, bool value) {
+ is31fl3733_led_t led;
+ memcpy_P(&led, (&g_is31fl3733_leds[index]), sizeof(led));
+
+ uint8_t control_register = led.v / 8;
+ uint8_t bit_value = led.v % 8;
+
+ if (value) {
+ driver_buffers[led.driver].led_control_buffer[control_register] |= (1 << bit_value);
+ } else {
+ driver_buffers[led.driver].led_control_buffer[control_register] &= ~(1 << bit_value);
+ }
+
+ driver_buffers[led.driver].led_control_buffer_dirty = true;
+}
+
+void is31fl3733_update_pwm_buffers(uint8_t index) {
+ if (driver_buffers[index].pwm_buffer_dirty) {
+ is31fl3733_select_page(index, IS31FL3733_COMMAND_PWM);
+
+ is31fl3733_write_pwm_buffer(index);
+
+ driver_buffers[index].pwm_buffer_dirty = false;
+ }
+}
+
+void is31fl3733_update_led_control_registers(uint8_t index) {
+ if (driver_buffers[index].led_control_buffer_dirty) {
+ is31fl3733_select_page(index, IS31FL3733_COMMAND_LED_CONTROL);
+
+ for (uint8_t i = 0; i < IS31FL3733_LED_CONTROL_REGISTER_COUNT; i++) {
+ is31fl3733_write_register(index, i, driver_buffers[index].led_control_buffer[i]);
+ }
+
+ driver_buffers[index].led_control_buffer_dirty = false;
+ }
+}
+
+void is31fl3733_flush(void) {
+ for (uint8_t i = 0; i < IS31FL3733_DRIVER_COUNT; i++) {
+ is31fl3733_update_pwm_buffers(i);
+ }
+}
diff --git a/drivers/led/issi/is31fl3733-simple.h b/drivers/led/issi/is31fl3733-mono.h
index c37b1fe5f2..3786b2ed71 100644
--- a/drivers/led/issi/is31fl3733-simple.h
+++ b/drivers/led/issi/is31fl3733-mono.h
@@ -115,9 +115,9 @@ typedef struct is31fl3733_led_t {
extern const is31fl3733_led_t PROGMEM g_is31fl3733_leds[IS31FL3733_LED_COUNT];
void is31fl3733_init_drivers(void);
-void is31fl3733_init(uint8_t addr, uint8_t sync);
-bool is31fl3733_write_register(uint8_t addr, uint8_t reg, uint8_t data);
-bool is31fl3733_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
+void is31fl3733_init(uint8_t index);
+void is31fl3733_write_register(uint8_t index, uint8_t reg, uint8_t data);
+void is31fl3733_select_page(uint8_t index, uint8_t page);
void is31fl3733_set_value(int index, uint8_t value);
void is31fl3733_set_value_all(uint8_t value);
@@ -128,8 +128,8 @@ void is31fl3733_set_led_control_register(uint8_t index, bool value);
// (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 is31fl3733_update_pwm_buffers(uint8_t addr, uint8_t index);
-void is31fl3733_update_led_control_registers(uint8_t addr, uint8_t index);
+void is31fl3733_update_pwm_buffers(uint8_t index);
+void is31fl3733_update_led_control_registers(uint8_t index);
void is31fl3733_flush(void);
@@ -161,206 +161,206 @@ void is31fl3733_flush(void);
#define IS31FL3733_SYNC_MASTER 0b01
#define IS31FL3733_SYNC_SLAVE 0b10
-#define A_1 0x00
-#define A_2 0x01
-#define A_3 0x02
-#define A_4 0x03
-#define A_5 0x04
-#define A_6 0x05
-#define A_7 0x06
-#define A_8 0x07
-#define A_9 0x08
-#define A_10 0x09
-#define A_11 0x0A
-#define A_12 0x0B
-#define A_13 0x0C
-#define A_14 0x0D
-#define A_15 0x0E
-#define A_16 0x0F
-
-#define B_1 0x10
-#define B_2 0x11
-#define B_3 0x12
-#define B_4 0x13
-#define B_5 0x14
-#define B_6 0x15
-#define B_7 0x16
-#define B_8 0x17
-#define B_9 0x18
-#define B_10 0x19
-#define B_11 0x1A
-#define B_12 0x1B
-#define B_13 0x1C
-#define B_14 0x1D
-#define B_15 0x1E
-#define B_16 0x1F
-
-#define C_1 0x20
-#define C_2 0x21
-#define C_3 0x22
-#define C_4 0x23
-#define C_5 0x24
-#define C_6 0x25
-#define C_7 0x26
-#define C_8 0x27
-#define C_9 0x28
-#define C_10 0x29
-#define C_11 0x2A
-#define C_12 0x2B
-#define C_13 0x2C
-#define C_14 0x2D
-#define C_15 0x2E
-#define C_16 0x2F
-
-#define D_1 0x30
-#define D_2 0x31
-#define D_3 0x32
-#define D_4 0x33
-#define D_5 0x34
-#define D_6 0x35
-#define D_7 0x36
-#define D_8 0x37
-#define D_9 0x38
-#define D_10 0x39
-#define D_11 0x3A
-#define D_12 0x3B
-#define D_13 0x3C
-#define D_14 0x3D
-#define D_15 0x3E
-#define D_16 0x3F
-
-#define E_1 0x40
-#define E_2 0x41
-#define E_3 0x42
-#define E_4 0x43
-#define E_5 0x44
-#define E_6 0x45
-#define E_7 0x46
-#define E_8 0x47
-#define E_9 0x48
-#define E_10 0x49
-#define E_11 0x4A
-#define E_12 0x4B
-#define E_13 0x4C
-#define E_14 0x4D
-#define E_15 0x4E
-#define E_16 0x4F
-
-#define F_1 0x50
-#define F_2 0x51
-#define F_3 0x52
-#define F_4 0x53
-#define F_5 0x54
-#define F_6 0x55
-#define F_7 0x56
-#define F_8 0x57
-#define F_9 0x58
-#define F_10 0x59
-#define F_11 0x5A
-#define F_12 0x5B
-#define F_13 0x5C
-#define F_14 0x5D
-#define F_15 0x5E
-#define F_16 0x5F
-
-#define G_1 0x60
-#define G_2 0x61
-#define G_3 0x62
-#define G_4 0x63
-#define G_5 0x64
-#define G_6 0x65
-#define G_7 0x66
-#define G_8 0x67
-#define G_9 0x68
-#define G_10 0x69
-#define G_11 0x6A
-#define G_12 0x6B
-#define G_13 0x6C
-#define G_14 0x6D
-#define G_15 0x6E
-#define G_16 0x6F
-
-#define H_1 0x70
-#define H_2 0x71
-#define H_3 0x72
-#define H_4 0x73
-#define H_5 0x74
-#define H_6 0x75
-#define H_7 0x76
-#define H_8 0x77
-#define H_9 0x78
-#define H_10 0x79
-#define H_11 0x7A
-#define H_12 0x7B
-#define H_13 0x7C
-#define H_14 0x7D
-#define H_15 0x7E
-#define H_16 0x7F
-
-#define I_1 0x80
-#define I_2 0x81
-#define I_3 0x82
-#define I_4 0x83
-#define I_5 0x84
-#define I_6 0x85
-#define I_7 0x86
-#define I_8 0x87
-#define I_9 0x88
-#define I_10 0x89
-#define I_11 0x8A
-#define I_12 0x8B
-#define I_13 0x8C
-#define I_14 0x8D
-#define I_15 0x8E
-#define I_16 0x8F
-
-#define J_1 0x90
-#define J_2 0x91
-#define J_3 0x92
-#define J_4 0x93
-#define J_5 0x94
-#define J_6 0x95
-#define J_7 0x96
-#define J_8 0x97
-#define J_9 0x98
-#define J_10 0x99
-#define J_11 0x9A
-#define J_12 0x9B
-#define J_13 0x9C
-#define J_14 0x9D
-#define J_15 0x9E
-#define J_16 0x9F
-
-#define K_1 0xA0
-#define K_2 0xA1
-#define K_3 0xA2
-#define K_4 0xA3
-#define K_5 0xA4
-#define K_6 0xA5
-#define K_7 0xA6
-#define K_8 0xA7
-#define K_9 0xA8
-#define K_10 0xA9
-#define K_11 0xAA
-#define K_12 0xAB
-#define K_13 0xAC
-#define K_14 0xAD
-#define K_15 0xAE
-#define K_16 0xAF
-
-#define L_1 0xB0
-#define L_2 0xB1
-#define L_3 0xB2
-#define L_4 0xB3
-#define L_5 0xB4
-#define L_6 0xB5
-#define L_7 0xB6
-#define L_8 0xB7
-#define L_9 0xB8
-#define L_10 0xB9
-#define L_11 0xBA
-#define L_12 0xBB
-#define L_13 0xBC
-#define L_14 0xBD
-#define L_15 0xBE
-#define L_16 0xBF
+#define SW1_CS1 0x00
+#define SW1_CS2 0x01
+#define SW1_CS3 0x02
+#define SW1_CS4 0x03
+#define SW1_CS5 0x04
+#define SW1_CS6 0x05
+#define SW1_CS7 0x06
+#define SW1_CS8 0x07
+#define SW1_CS9 0x08
+#define SW1_CS10 0x09
+#define SW1_CS11 0x0A
+#define SW1_CS12 0x0B
+#define SW1_CS13 0x0C
+#define SW1_CS14 0x0D
+#define SW1_CS15 0x0E
+#define SW1_CS16 0x0F
+
+#define SW2_CS1 0x10
+#define SW2_CS2 0x11
+#define SW2_CS3 0x12
+#define SW2_CS4 0x13
+#define SW2_CS5 0x14
+#define SW2_CS6 0x15
+#define SW2_CS7 0x16
+#define SW2_CS8 0x17
+#define SW2_CS9 0x18
+#define SW2_CS10 0x19
+#define SW2_CS11 0x1A
+#define SW2_CS12 0x1B
+#define SW2_CS13 0x1C
+#define SW2_CS14 0x1D
+#define SW2_CS15 0x1E
+#define SW2_CS16 0x1F
+
+#define SW3_CS1 0x20
+#define SW3_CS2 0x21
+#define SW3_CS3 0x22
+#define SW3_CS4 0x23
+#define SW3_CS5 0x24
+#define SW3_CS6 0x25
+#define SW3_CS7 0x26
+#define SW3_CS8 0x27
+#define SW3_CS9 0x28
+#define SW3_CS10 0x29
+#define SW3_CS11 0x2A
+#define SW3_CS12 0x2B
+#define SW3_CS13 0x2C
+#define SW3_CS14 0x2D
+#define SW3_CS15 0x2E
+#define SW3_CS16 0x2F
+
+#define SW4_CS1 0x30
+#define SW4_CS2 0x31
+#define SW4_CS3 0x32
+#define SW4_CS4 0x33
+#define SW4_CS5 0x34
+#define SW4_CS6 0x35
+#define SW4_CS7 0x36
+#define SW4_CS8 0x37
+#define SW4_CS9 0x38
+#define SW4_CS10 0x39
+#define SW4_CS11 0x3A
+#define SW4_CS12 0x3B
+#define SW4_CS13 0x3C
+#define SW4_CS14 0x3D
+#define SW4_CS15 0x3E
+#define SW4_CS16 0x3F
+
+#define SW5_CS1 0x40
+#define SW5_CS2 0x41
+#define SW5_CS3 0x42
+#define SW5_CS4 0x43
+#define SW5_CS5 0x44
+#define SW5_CS6 0x45
+#define SW5_CS7 0x46
+#define SW5_CS8 0x47
+#define SW5_CS9 0x48
+#define SW5_CS10 0x49
+#define SW5_CS11 0x4A
+#define SW5_CS12 0x4B
+#define SW5_CS13 0x4C
+#define SW5_CS14 0x4D
+#define SW5_CS15 0x4E
+#define SW5_CS16 0x4F
+
+#define SW6_CS1 0x50
+#define SW6_CS2 0x51
+#define SW6_CS3 0x52
+#define SW6_CS4 0x53
+#define SW6_CS5 0x54
+#define SW6_CS6 0x55
+#define SW6_CS7 0x56
+#define SW6_CS8 0x57
+#define SW6_CS9 0x58
+#define SW6_CS10 0x59
+#define SW6_CS11 0x5A
+#define SW6_CS12 0x5B
+#define SW6_CS13 0x5C
+#define SW6_CS14 0x5D
+#define SW6_CS15 0x5E
+#define SW6_CS16 0x5F
+
+#define SW7_CS1 0x60
+#define SW7_CS2 0x61
+#define SW7_CS3 0x62
+#define SW7_CS4 0x63
+#define SW7_CS5 0x64
+#define SW7_CS6 0x65
+#define SW7_CS7 0x66
+#define SW7_CS8 0x67
+#define SW7_CS9 0x68
+#define SW7_CS10 0x69
+#define SW7_CS11 0x6A
+#define SW7_CS12 0x6B
+#define SW7_CS13 0x6C
+#define SW7_CS14 0x6D
+#define SW7_CS15 0x6E
+#define SW7_CS16 0x6F
+
+#define SW8_CS1 0x70
+#define SW8_CS2 0x71
+#define SW8_CS3 0x72
+#define SW8_CS4 0x73
+#define SW8_CS5 0x74
+#define SW8_CS6 0x75
+#define SW8_CS7 0x76
+#define SW8_CS8 0x77
+#define SW8_CS9 0x78
+#define SW8_CS10 0x79
+#define SW8_CS11 0x7A
+#define SW8_CS12 0x7B
+#define SW8_CS13 0x7C
+#define SW8_CS14 0x7D
+#define SW8_CS15 0x7E
+#define SW8_CS16 0x7F
+
+#define SW9_CS1 0x80
+#define SW9_CS2 0x81
+#define SW9_CS3 0x82
+#define SW9_CS4 0x83
+#define SW9_CS5 0x84
+#define SW9_CS6 0x85
+#define SW9_CS7 0x86
+#define SW9_CS8 0x87
+#define SW9_CS9 0x88
+#define SW9_CS10 0x89
+#define SW9_CS11 0x8A
+#define SW9_CS12 0x8B
+#define SW9_CS13 0x8C
+#define SW9_CS14 0x8D
+#define SW9_CS15 0x8E
+#define SW9_CS16 0x8F
+
+#define SW10_CS1 0x90
+#define SW10_CS2 0x91
+#define SW10_CS3 0x92
+#define SW10_CS4 0x93
+#define SW10_CS5 0x94
+#define SW10_CS6 0x95
+#define SW10_CS7 0x96
+#define SW10_CS8 0x97
+#define SW10_CS9 0x98
+#define SW10_CS10 0x99
+#define SW10_CS11 0x9A
+#define SW10_CS12 0x9B
+#define SW10_CS13 0x9C
+#define SW10_CS14 0x9D
+#define SW10_CS15 0x9E
+#define SW10_CS16 0x9F
+
+#define SW11_CS1 0xA0
+#define SW11_CS2 0xA1
+#define SW11_CS3 0xA2
+#define SW11_CS4 0xA3
+#define SW11_CS5 0xA4
+#define SW11_CS6 0xA5
+#define SW11_CS7 0xA6
+#define SW11_CS8 0xA7
+#define SW11_CS9 0xA8
+#define SW11_CS10 0xA9
+#define SW11_CS11 0xAA
+#define SW11_CS12 0xAB
+#define SW11_CS13 0xAC
+#define SW11_CS14 0xAD
+#define SW11_CS15 0xAE
+#define SW11_CS16 0xAF
+
+#define SW12_CS1 0xB0
+#define SW12_CS2 0xB1
+#define SW12_CS3 0xB2
+#define SW12_CS4 0xB3
+#define SW12_CS5 0xB4
+#define SW12_CS6 0xB5
+#define SW12_CS7 0xB6
+#define SW12_CS8 0xB7
+#define SW12_CS9 0xB8
+#define SW12_CS10 0xB9
+#define SW12_CS11 0xBA
+#define SW12_CS12 0xBB
+#define SW12_CS13 0xBC
+#define SW12_CS14 0xBD
+#define SW12_CS15 0xBE
+#define SW12_CS16 0xBF
diff --git a/drivers/led/issi/is31fl3733-simple.c b/drivers/led/issi/is31fl3733-simple.c
deleted file mode 100644
index 9f2444c253..0000000000
--- a/drivers/led/issi/is31fl3733-simple.c
+++ /dev/null
@@ -1,278 +0,0 @@
-/* Copyright 2017 Jason Williams
- * Copyright 2018 Jack Humbert
- * Copyright 2018 Yiancar
- * Copyright 2021 Doni Crosby
- * Copyright 2021 Leo Deng
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#include "is31fl3733-simple.h"
-#include <string.h>
-#include "i2c_master.h"
-#include "wait.h"
-
-#define IS31FL3733_PWM_REGISTER_COUNT 192
-#define IS31FL3733_LED_CONTROL_REGISTER_COUNT 24
-
-#ifndef IS31FL3733_I2C_TIMEOUT
-# define IS31FL3733_I2C_TIMEOUT 100
-#endif
-
-#ifndef IS31FL3733_I2C_PERSISTENCE
-# define IS31FL3733_I2C_PERSISTENCE 0
-#endif
-
-#ifndef IS31FL3733_PWM_FREQUENCY
-# define IS31FL3733_PWM_FREQUENCY IS31FL3733_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3733B only
-#endif
-
-#ifndef IS31FL3733_SW_PULLUP
-# define IS31FL3733_SW_PULLUP IS31FL3733_PUR_0_OHM
-#endif
-
-#ifndef IS31FL3733_CS_PULLDOWN
-# define IS31FL3733_CSPULLDOWN IS31FL3733_PDR_0_OHM
-#endif
-
-#ifndef IS31FL3733_GLOBAL_CURRENT
-# define IS31FL3733_GLOBAL_CURRENT 0xFF
-#endif
-
-#ifndef IS31FL3733_SYNC_1
-# define IS31FL3733_SYNC_1 IS31FL3733_SYNC_NONE
-#endif
-#ifndef IS31FL3733_SYNC_2
-# define IS31FL3733_SYNC_2 IS31FL3733_SYNC_NONE
-#endif
-#ifndef IS31FL3733_SYNC_3
-# define IS31FL3733_SYNC_3 IS31FL3733_SYNC_NONE
-#endif
-#ifndef IS31FL3733_SYNC_4
-# define IS31FL3733_SYNC_4 IS31FL3733_SYNC_NONE
-#endif
-
-// Transfer buffer for TWITransmitData()
-uint8_t g_twi_transfer_buffer[20];
-
-// These buffers match the IS31FL3733 PWM registers.
-// The control buffers match the PG0 LED On/Off registers.
-// Storing them like this is optimal for I2C transfers to the registers.
-// We could optimize this and take out the unused registers from these
-// buffers and the transfers in is31fl3733_write_pwm_buffer() but it's
-// probably not worth the extra complexity.
-uint8_t g_pwm_buffer[IS31FL3733_DRIVER_COUNT][IS31FL3733_PWM_REGISTER_COUNT];
-bool g_pwm_buffer_update_required[IS31FL3733_DRIVER_COUNT] = {false};
-
-uint8_t g_led_control_registers[IS31FL3733_DRIVER_COUNT][IS31FL3733_LED_CONTROL_REGISTER_COUNT] = {0};
-bool g_led_control_registers_update_required[IS31FL3733_DRIVER_COUNT] = {false};
-
-bool is31fl3733_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
- // If the transaction fails function returns false.
- g_twi_transfer_buffer[0] = reg;
- g_twi_transfer_buffer[1] = data;
-
-#if IS31FL3733_I2C_PERSISTENCE > 0
- for (uint8_t i = 0; i < IS31FL3733_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3733_I2C_TIMEOUT) != 0) {
- return false;
- }
- }
-#else
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3733_I2C_TIMEOUT) != 0) {
- return false;
- }
-#endif
- return true;
-}
-
-bool is31fl3733_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
- // Assumes PG1 is already selected.
- // If any of the transactions fails function returns false.
- // Transmit PWM registers in 12 transfers of 16 bytes.
- // g_twi_transfer_buffer[] is 20 bytes
-
- // Iterate over the pwm_buffer contents at 16 byte intervals.
- for (int i = 0; i < IS31FL3733_PWM_REGISTER_COUNT; i += 16) {
- g_twi_transfer_buffer[0] = i;
- // Copy the data from i to i+15.
- // Device will auto-increment register for data after the first byte
- // Thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer.
- memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16);
-
-#if IS31FL3733_I2C_PERSISTENCE > 0
- for (uint8_t i = 0; i < IS31FL3733_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3733_I2C_TIMEOUT) != 0) {
- return false;
- }
- }
-#else
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3733_I2C_TIMEOUT) != 0) {
- return false;
- }
-#endif
- }
- return true;
-}
-
-void is31fl3733_init_drivers(void) {
- i2c_init();
-
- is31fl3733_init(IS31FL3733_I2C_ADDRESS_1, IS31FL3733_SYNC_1);
-#if defined(IS31FL3733_I2C_ADDRESS_2)
- is31fl3733_init(IS31FL3733_I2C_ADDRESS_2, IS31FL3733_SYNC_2);
-# if defined(IS31FL3733_I2C_ADDRESS_3)
- is31fl3733_init(IS31FL3733_I2C_ADDRESS_3, IS31FL3733_SYNC_3);
-# if defined(IS31FL3733_I2C_ADDRESS_4)
- is31fl3733_init(IS31FL3733_I2C_ADDRESS_4, IS31FL3733_SYNC_4);
-# endif
-# endif
-#endif
-
- for (int i = 0; i < IS31FL3733_LED_COUNT; i++) {
- is31fl3733_set_led_control_register(i, true);
- }
-
- is31fl3733_update_led_control_registers(IS31FL3733_I2C_ADDRESS_1, 0);
-#if defined(IS31FL3733_I2C_ADDRESS_2)
- is31fl3733_update_led_control_registers(IS31FL3733_I2C_ADDRESS_2, 1);
-# if defined(IS31FL3733_I2C_ADDRESS_3)
- is31fl3733_update_led_control_registers(IS31FL3733_I2C_ADDRESS_3, 2);
-# if defined(IS31FL3733_I2C_ADDRESS_4)
- is31fl3733_update_led_control_registers(IS31FL3733_I2C_ADDRESS_4, 3);
-# endif
-# endif
-#endif
-}
-
-void is31fl3733_init(uint8_t addr, uint8_t sync) {
- // In order to avoid the LEDs being driven with garbage data
- // in the LED driver's PWM registers, shutdown is enabled last.
- // Set up the mode and other settings, clear the PWM registers,
- // then disable software shutdown.
- // Sync is passed so set it according to the datasheet.
-
- // Unlock the command register.
- is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC);
-
- // Select PG0
- is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND, IS31FL3733_COMMAND_LED_CONTROL);
- // Turn off all LEDs.
- for (int i = 0; i < IS31FL3733_LED_CONTROL_REGISTER_COUNT; i++) {
- is31fl3733_write_register(addr, i, 0x00);
- }
-
- // Unlock the command register.
- is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC);
-
- // Select PG1
- is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND, IS31FL3733_COMMAND_PWM);
- // Set PWM on all LEDs to 0
- // No need to setup Breath registers to PWM as that is the default.
- for (int i = 0; i < IS31FL3733_PWM_REGISTER_COUNT; i++) {
- is31fl3733_write_register(addr, i, 0x00);
- }
-
- // Unlock the command register.
- is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC);
-
- // Select PG3
- is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND, IS31FL3733_COMMAND_FUNCTION);
- // Set de-ghost pull-up resistors (SWx)
- is31fl3733_write_register(addr, IS31FL3733_FUNCTION_REG_SW_PULLUP, IS31FL3733_SW_PULLUP);
- // Set de-ghost pull-down resistors (CSx)
- is31fl3733_write_register(addr, IS31FL3733_FUNCTION_REG_CS_PULLDOWN, IS31FL3733_CS_PULLDOWN);
- // Set global current to maximum.
- is31fl3733_write_register(addr, IS31FL3733_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3733_GLOBAL_CURRENT);
- // Disable software shutdown.
- is31fl3733_write_register(addr, IS31FL3733_FUNCTION_REG_CONFIGURATION, ((sync & 0b11) << 6) | ((IS31FL3733_PWM_FREQUENCY & 0b111) << 3) | 0x01);
-
- // Wait 10ms to ensure the device has woken up.
- wait_ms(10);
-}
-
-void is31fl3733_set_value(int index, uint8_t value) {
- is31fl3733_led_t led;
- if (index >= 0 && index < IS31FL3733_LED_COUNT) {
- memcpy_P(&led, (&g_is31fl3733_leds[index]), sizeof(led));
-
- if (g_pwm_buffer[led.driver][led.v] == value) {
- return;
- }
- g_pwm_buffer[led.driver][led.v] = value;
- g_pwm_buffer_update_required[led.driver] = true;
- }
-}
-
-void is31fl3733_set_value_all(uint8_t value) {
- for (int i = 0; i < IS31FL3733_LED_COUNT; i++) {
- is31fl3733_set_value(i, value);
- }
-}
-
-void is31fl3733_set_led_control_register(uint8_t index, bool value) {
- is31fl3733_led_t led;
- memcpy_P(&led, (&g_is31fl3733_leds[index]), sizeof(led));
-
- uint8_t control_register = led.v / 8;
- uint8_t bit_value = led.v % 8;
-
- if (value) {
- g_led_control_registers[led.driver][control_register] |= (1 << bit_value);
- } else {
- g_led_control_registers[led.driver][control_register] &= ~(1 << bit_value);
- }
-
- g_led_control_registers_update_required[led.driver] = true;
-}
-
-void is31fl3733_update_pwm_buffers(uint8_t addr, uint8_t index) {
- if (g_pwm_buffer_update_required[index]) {
- // Firstly we need to unlock the command register and select PG1.
- is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC);
- is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND, IS31FL3733_COMMAND_PWM);
-
- // If any of the transactions fail we risk writing dirty PG0,
- // refresh page 0 just in case.
- if (!is31fl3733_write_pwm_buffer(addr, g_pwm_buffer[index])) {
- g_led_control_registers_update_required[index] = true;
- }
- g_pwm_buffer_update_required[index] = false;
- }
-}
-
-void is31fl3733_update_led_control_registers(uint8_t addr, uint8_t index) {
- if (g_led_control_registers_update_required[index]) {
- // Firstly we need to unlock the command register and select PG0
- is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC);
- is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND, IS31FL3733_COMMAND_LED_CONTROL);
- for (int i = 0; i < IS31FL3733_LED_CONTROL_REGISTER_COUNT; i++) {
- is31fl3733_write_register(addr, i, g_led_control_registers[index][i]);
- }
- g_led_control_registers_update_required[index] = false;
- }
-}
-
-void is31fl3733_flush(void) {
- is31fl3733_update_pwm_buffers(IS31FL3733_I2C_ADDRESS_1, 0);
-#if defined(IS31FL3733_I2C_ADDRESS_2)
- is31fl3733_update_pwm_buffers(IS31FL3733_I2C_ADDRESS_2, 1);
-# if defined(IS31FL3733_I2C_ADDRESS_3)
- is31fl3733_update_pwm_buffers(IS31FL3733_I2C_ADDRESS_3, 2);
-# if defined(IS31FL3733_I2C_ADDRESS_4)
- is31fl3733_update_pwm_buffers(IS31FL3733_I2C_ADDRESS_4, 3);
-# endif
-# endif
-#endif
-}
diff --git a/drivers/led/issi/is31fl3733.c b/drivers/led/issi/is31fl3733.c
index 5857a800d7..a1d6899114 100644
--- a/drivers/led/issi/is31fl3733.c
+++ b/drivers/led/issi/is31fl3733.c
@@ -18,8 +18,8 @@
*/
#include "is31fl3733.h"
-#include <string.h>
#include "i2c_master.h"
+#include "gpio.h"
#include "wait.h"
#define IS31FL3733_PWM_REGISTER_COUNT 192
@@ -62,140 +62,137 @@
# define IS31FL3733_SYNC_4 IS31FL3733_SYNC_NONE
#endif
-// Transfer buffer for TWITransmitData()
-uint8_t g_twi_transfer_buffer[20];
+const uint8_t i2c_addresses[IS31FL3733_DRIVER_COUNT] = {
+ IS31FL3733_I2C_ADDRESS_1,
+#ifdef IS31FL3733_I2C_ADDRESS_2
+ IS31FL3733_I2C_ADDRESS_2,
+# ifdef IS31FL3733_I2C_ADDRESS_3
+ IS31FL3733_I2C_ADDRESS_3,
+# ifdef IS31FL3733_I2C_ADDRESS_4
+ IS31FL3733_I2C_ADDRESS_4,
+# endif
+# endif
+#endif
+};
+
+const uint8_t driver_sync[IS31FL3733_DRIVER_COUNT] = {
+ IS31FL3733_SYNC_1,
+#ifdef IS31FL3733_I2C_ADDRESS_2
+ IS31FL3733_SYNC_2,
+# ifdef IS31FL3733_I2C_ADDRESS_3
+ IS31FL3733_SYNC_3,
+# ifdef IS31FL3733_I2C_ADDRESS_4
+ IS31FL3733_SYNC_4,
+# endif
+# endif
+#endif
+};
// These buffers match the IS31FL3733 PWM registers.
-// The control buffers match the PG0 LED On/Off registers.
+// The control buffers match the page 0 LED On/Off registers.
// Storing them like this is optimal for I2C transfers to the registers.
// We could optimize this and take out the unused registers from these
// buffers and the transfers in is31fl3733_write_pwm_buffer() but it's
// probably not worth the extra complexity.
-uint8_t g_pwm_buffer[IS31FL3733_DRIVER_COUNT][IS31FL3733_PWM_REGISTER_COUNT];
-bool g_pwm_buffer_update_required[IS31FL3733_DRIVER_COUNT] = {false};
-
-uint8_t g_led_control_registers[IS31FL3733_DRIVER_COUNT][IS31FL3733_LED_CONTROL_REGISTER_COUNT] = {0};
-bool g_led_control_registers_update_required[IS31FL3733_DRIVER_COUNT] = {false};
-
-bool is31fl3733_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
- // If the transaction fails function returns false.
- g_twi_transfer_buffer[0] = reg;
- g_twi_transfer_buffer[1] = data;
-
+typedef struct is31fl3733_driver_t {
+ uint8_t pwm_buffer[IS31FL3733_PWM_REGISTER_COUNT];
+ bool pwm_buffer_dirty;
+ uint8_t led_control_buffer[IS31FL3733_LED_CONTROL_REGISTER_COUNT];
+ bool led_control_buffer_dirty;
+} PACKED is31fl3733_driver_t;
+
+is31fl3733_driver_t driver_buffers[IS31FL3733_DRIVER_COUNT] = {{
+ .pwm_buffer = {0},
+ .pwm_buffer_dirty = false,
+ .led_control_buffer = {0},
+ .led_control_buffer_dirty = false,
+}};
+
+void is31fl3733_write_register(uint8_t index, uint8_t reg, uint8_t data) {
#if IS31FL3733_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3733_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3733_I2C_TIMEOUT) != 0) {
- return false;
- }
+ if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3733_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
}
#else
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3733_I2C_TIMEOUT) != 0) {
- return false;
- }
+ i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3733_I2C_TIMEOUT);
#endif
- return true;
}
-bool is31fl3733_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
- // Assumes PG1 is already selected.
- // If any of the transactions fails function returns false.
+void is31fl3733_select_page(uint8_t index, uint8_t page) {
+ is31fl3733_write_register(index, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC);
+ is31fl3733_write_register(index, IS31FL3733_REG_COMMAND, page);
+}
+
+void is31fl3733_write_pwm_buffer(uint8_t index) {
+ // Assumes page 1 is already selected.
// Transmit PWM registers in 12 transfers of 16 bytes.
- // g_twi_transfer_buffer[] is 20 bytes
// Iterate over the pwm_buffer contents at 16 byte intervals.
- for (int i = 0; i < IS31FL3733_PWM_REGISTER_COUNT; i += 16) {
- g_twi_transfer_buffer[0] = i;
- // Copy the data from i to i+15.
- // Device will auto-increment register for data after the first byte
- // Thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer.
- memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16);
-
+ for (uint8_t i = 0; i < IS31FL3733_PWM_REGISTER_COUNT; i += 16) {
#if IS31FL3733_I2C_PERSISTENCE > 0
- for (uint8_t i = 0; i < IS31FL3733_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3733_I2C_TIMEOUT) != 0) {
- return false;
- }
+ for (uint8_t j = 0; j < IS31FL3733_I2C_PERSISTENCE; j++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3733_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
}
#else
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3733_I2C_TIMEOUT) != 0) {
- return false;
- }
+ i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3733_I2C_TIMEOUT);
#endif
}
- return true;
}
void is31fl3733_init_drivers(void) {
i2c_init();
- is31fl3733_init(IS31FL3733_I2C_ADDRESS_1, IS31FL3733_SYNC_1);
-#if defined(IS31FL3733_I2C_ADDRESS_2)
- is31fl3733_init(IS31FL3733_I2C_ADDRESS_2, IS31FL3733_SYNC_2);
-# if defined(IS31FL3733_I2C_ADDRESS_3)
- is31fl3733_init(IS31FL3733_I2C_ADDRESS_3, IS31FL3733_SYNC_3);
-# if defined(IS31FL3733_I2C_ADDRESS_4)
- is31fl3733_init(IS31FL3733_I2C_ADDRESS_4, IS31FL3733_SYNC_4);
-# endif
-# endif
+#if defined(IS31FL3733_SDB_PIN)
+ gpio_set_pin_output(IS31FL3733_SDB_PIN);
+ gpio_write_pin_high(IS31FL3733_SDB_PIN);
#endif
+ for (uint8_t i = 0; i < IS31FL3733_DRIVER_COUNT; i++) {
+ is31fl3733_init(i);
+ }
+
for (int i = 0; i < IS31FL3733_LED_COUNT; i++) {
is31fl3733_set_led_control_register(i, true, true, true);
}
- is31fl3733_update_led_control_registers(IS31FL3733_I2C_ADDRESS_1, 0);
-#if defined(IS31FL3733_I2C_ADDRESS_2)
- is31fl3733_update_led_control_registers(IS31FL3733_I2C_ADDRESS_2, 1);
-# if defined(IS31FL3733_I2C_ADDRESS_3)
- is31fl3733_update_led_control_registers(IS31FL3733_I2C_ADDRESS_3, 2);
-# if defined(IS31FL3733_I2C_ADDRESS_4)
- is31fl3733_update_led_control_registers(IS31FL3733_I2C_ADDRESS_4, 3);
-# endif
-# endif
-#endif
+ for (uint8_t i = 0; i < IS31FL3733_DRIVER_COUNT; i++) {
+ is31fl3733_update_led_control_registers(i);
+ }
}
-void is31fl3733_init(uint8_t addr, uint8_t sync) {
+void is31fl3733_init(uint8_t index) {
// In order to avoid the LEDs being driven with garbage data
// in the LED driver's PWM registers, shutdown is enabled last.
// Set up the mode and other settings, clear the PWM registers,
// then disable software shutdown.
- // Sync is passed so set it according to the datasheet.
- // Unlock the command register.
- is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC);
+ is31fl3733_select_page(index, IS31FL3733_COMMAND_LED_CONTROL);
- // Select PG0
- is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND, IS31FL3733_COMMAND_LED_CONTROL);
// Turn off all LEDs.
- for (int i = 0; i < IS31FL3733_LED_CONTROL_REGISTER_COUNT; i++) {
- is31fl3733_write_register(addr, i, 0x00);
+ for (uint8_t i = 0; i < IS31FL3733_LED_CONTROL_REGISTER_COUNT; i++) {
+ is31fl3733_write_register(index, i, 0x00);
}
- // Unlock the command register.
- is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC);
+ is31fl3733_select_page(index, IS31FL3733_COMMAND_PWM);
- // Select PG1
- is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND, IS31FL3733_COMMAND_PWM);
// Set PWM on all LEDs to 0
// No need to setup Breath registers to PWM as that is the default.
- for (int i = 0; i < IS31FL3733_PWM_REGISTER_COUNT; i++) {
- is31fl3733_write_register(addr, i, 0x00);
+ for (uint8_t i = 0; i < IS31FL3733_PWM_REGISTER_COUNT; i++) {
+ is31fl3733_write_register(index, i, 0x00);
}
- // Unlock the command register.
- is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC);
+ is31fl3733_select_page(index, IS31FL3733_COMMAND_FUNCTION);
+
+ uint8_t sync = driver_sync[index];
- // Select PG3
- is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND, IS31FL3733_COMMAND_FUNCTION);
// Set de-ghost pull-up resistors (SWx)
- is31fl3733_write_register(addr, IS31FL3733_FUNCTION_REG_SW_PULLUP, IS31FL3733_SW_PULLUP);
+ is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_SW_PULLUP, IS31FL3733_SW_PULLUP);
// Set de-ghost pull-down resistors (CSx)
- is31fl3733_write_register(addr, IS31FL3733_FUNCTION_REG_CS_PULLDOWN, IS31FL3733_CS_PULLDOWN);
+ is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_CS_PULLDOWN, IS31FL3733_CS_PULLDOWN);
// Set global current to maximum.
- is31fl3733_write_register(addr, IS31FL3733_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3733_GLOBAL_CURRENT);
+ is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3733_GLOBAL_CURRENT);
// Disable software shutdown.
- is31fl3733_write_register(addr, IS31FL3733_FUNCTION_REG_CONFIGURATION, ((sync & 0b11) << 6) | ((IS31FL3733_PWM_FREQUENCY & 0b111) << 3) | 0x01);
+ is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_CONFIGURATION, ((sync & 0b11) << 6) | ((IS31FL3733_PWM_FREQUENCY & 0b111) << 3) | 0x01);
// Wait 10ms to ensure the device has woken up.
wait_ms(10);
@@ -203,16 +200,18 @@ void is31fl3733_init(uint8_t addr, uint8_t sync) {
void is31fl3733_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
is31fl3733_led_t led;
+
if (index >= 0 && index < IS31FL3733_LED_COUNT) {
memcpy_P(&led, (&g_is31fl3733_leds[index]), sizeof(led));
- if (g_pwm_buffer[led.driver][led.r] == red && g_pwm_buffer[led.driver][led.g] == green && g_pwm_buffer[led.driver][led.b] == blue) {
+ if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) {
return;
}
- g_pwm_buffer[led.driver][led.r] = red;
- g_pwm_buffer[led.driver][led.g] = green;
- g_pwm_buffer[led.driver][led.b] = blue;
- g_pwm_buffer_update_required[led.driver] = true;
+
+ driver_buffers[led.driver].pwm_buffer[led.r] = red;
+ driver_buffers[led.driver].pwm_buffer[led.g] = green;
+ driver_buffers[led.driver].pwm_buffer[led.b] = blue;
+ driver_buffers[led.driver].pwm_buffer_dirty = true;
}
}
@@ -234,60 +233,48 @@ void is31fl3733_set_led_control_register(uint8_t index, bool red, bool green, bo
uint8_t bit_b = led.b % 8;
if (red) {
- g_led_control_registers[led.driver][control_register_r] |= (1 << bit_r);
+ driver_buffers[led.driver].led_control_buffer[control_register_r] |= (1 << bit_r);
} else {
- g_led_control_registers[led.driver][control_register_r] &= ~(1 << bit_r);
+ driver_buffers[led.driver].led_control_buffer[control_register_r] &= ~(1 << bit_r);
}
if (green) {
- g_led_control_registers[led.driver][control_register_g] |= (1 << bit_g);
+ driver_buffers[led.driver].led_control_buffer[control_register_g] |= (1 << bit_g);
} else {
- g_led_control_registers[led.driver][control_register_g] &= ~(1 << bit_g);
+ driver_buffers[led.driver].led_control_buffer[control_register_g] &= ~(1 << bit_g);
}
if (blue) {
- g_led_control_registers[led.driver][control_register_b] |= (1 << bit_b);
+ driver_buffers[led.driver].led_control_buffer[control_register_b] |= (1 << bit_b);
} else {
- g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b);
+ driver_buffers[led.driver].led_control_buffer[control_register_b] &= ~(1 << bit_b);
}
- g_led_control_registers_update_required[led.driver] = true;
+ driver_buffers[led.driver].led_control_buffer_dirty = true;
}
-void is31fl3733_update_pwm_buffers(uint8_t addr, uint8_t index) {
- if (g_pwm_buffer_update_required[index]) {
- // Firstly we need to unlock the command register and select PG1.
- is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC);
- is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND, IS31FL3733_COMMAND_PWM);
+void is31fl3733_update_pwm_buffers(uint8_t index) {
+ if (driver_buffers[index].pwm_buffer_dirty) {
+ is31fl3733_select_page(index, IS31FL3733_COMMAND_PWM);
- // If any of the transactions fail we risk writing dirty PG0,
- // refresh page 0 just in case.
- if (!is31fl3733_write_pwm_buffer(addr, g_pwm_buffer[index])) {
- g_led_control_registers_update_required[index] = true;
- }
- g_pwm_buffer_update_required[index] = false;
+ is31fl3733_write_pwm_buffer(index);
+
+ driver_buffers[index].pwm_buffer_dirty = false;
}
}
-void is31fl3733_update_led_control_registers(uint8_t addr, uint8_t index) {
- if (g_led_control_registers_update_required[index]) {
- // Firstly we need to unlock the command register and select PG0
- is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC);
- is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND, IS31FL3733_COMMAND_LED_CONTROL);
- for (int i = 0; i < IS31FL3733_LED_CONTROL_REGISTER_COUNT; i++) {
- is31fl3733_write_register(addr, i, g_led_control_registers[index][i]);
+void is31fl3733_update_led_control_registers(uint8_t index) {
+ if (driver_buffers[index].led_control_buffer_dirty) {
+ is31fl3733_select_page(index, IS31FL3733_COMMAND_LED_CONTROL);
+
+ for (uint8_t i = 0; i < IS31FL3733_LED_CONTROL_REGISTER_COUNT; i++) {
+ is31fl3733_write_register(index, i, driver_buffers[index].led_control_buffer[i]);
}
- g_led_control_registers_update_required[index] = false;
+
+ driver_buffers[index].led_control_buffer_dirty = false;
}
}
void is31fl3733_flush(void) {
- is31fl3733_update_pwm_buffers(IS31FL3733_I2C_ADDRESS_1, 0);
-#if defined(IS31FL3733_I2C_ADDRESS_2)
- is31fl3733_update_pwm_buffers(IS31FL3733_I2C_ADDRESS_2, 1);
-# if defined(IS31FL3733_I2C_ADDRESS_3)
- is31fl3733_update_pwm_buffers(IS31FL3733_I2C_ADDRESS_3, 2);
-# if defined(IS31FL3733_I2C_ADDRESS_4)
- is31fl3733_update_pwm_buffers(IS31FL3733_I2C_ADDRESS_4, 3);
-# endif
-# endif
-#endif
+ for (uint8_t i = 0; i < IS31FL3733_DRIVER_COUNT; i++) {
+ is31fl3733_update_pwm_buffers(i);
+ }
}
diff --git a/drivers/led/issi/is31fl3733.h b/drivers/led/issi/is31fl3733.h
index 20804b016b..fb60c76c2d 100644
--- a/drivers/led/issi/is31fl3733.h
+++ b/drivers/led/issi/is31fl3733.h
@@ -40,13 +40,13 @@
#ifdef DRIVER_SYNC_1
# define IS31FL3733_SYNC_1 DRIVER_SYNC_1
#endif
-#ifdef DRIVER_ADDR_2
+#ifdef DRIVER_SYNC_2
# define IS31FL3733_SYNC_2 DRIVER_SYNC_2
#endif
-#ifdef DRIVER_ADDR_3
+#ifdef DRIVER_SYNC_3
# define IS31FL3733_SYNC_3 DRIVER_SYNC_3
#endif
-#ifdef DRIVER_ADDR_4
+#ifdef DRIVER_SYNC_4
# define IS31FL3733_SYNC_4 DRIVER_SYNC_4
#endif
#ifdef ISSI_TIMEOUT
@@ -140,9 +140,9 @@ typedef struct is31fl3733_led_t {
extern const is31fl3733_led_t PROGMEM g_is31fl3733_leds[IS31FL3733_LED_COUNT];
void is31fl3733_init_drivers(void);
-void is31fl3733_init(uint8_t addr, uint8_t sync);
-bool is31fl3733_write_register(uint8_t addr, uint8_t reg, uint8_t data);
-bool is31fl3733_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
+void is31fl3733_init(uint8_t index);
+void is31fl3733_write_register(uint8_t index, uint8_t reg, uint8_t data);
+void is31fl3733_select_page(uint8_t index, uint8_t page);
void is31fl3733_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
void is31fl3733_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
@@ -153,8 +153,8 @@ void is31fl3733_set_led_control_register(uint8_t index, bool red, bool green, bo
// (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 is31fl3733_update_pwm_buffers(uint8_t addr, uint8_t index);
-void is31fl3733_update_led_control_registers(uint8_t addr, uint8_t index);
+void is31fl3733_update_pwm_buffers(uint8_t index);
+void is31fl3733_update_led_control_registers(uint8_t index);
void is31fl3733_flush(void);
@@ -186,206 +186,412 @@ void is31fl3733_flush(void);
#define IS31FL3733_SYNC_MASTER 0b01
#define IS31FL3733_SYNC_SLAVE 0b10
-#define A_1 0x00
-#define A_2 0x01
-#define A_3 0x02
-#define A_4 0x03
-#define A_5 0x04
-#define A_6 0x05
-#define A_7 0x06
-#define A_8 0x07
-#define A_9 0x08
-#define A_10 0x09
-#define A_11 0x0A
-#define A_12 0x0B
-#define A_13 0x0C
-#define A_14 0x0D
-#define A_15 0x0E
-#define A_16 0x0F
-
-#define B_1 0x10
-#define B_2 0x11
-#define B_3 0x12
-#define B_4 0x13
-#define B_5 0x14
-#define B_6 0x15
-#define B_7 0x16
-#define B_8 0x17
-#define B_9 0x18
-#define B_10 0x19
-#define B_11 0x1A
-#define B_12 0x1B
-#define B_13 0x1C
-#define B_14 0x1D
-#define B_15 0x1E
-#define B_16 0x1F
-
-#define C_1 0x20
-#define C_2 0x21
-#define C_3 0x22
-#define C_4 0x23
-#define C_5 0x24
-#define C_6 0x25
-#define C_7 0x26
-#define C_8 0x27
-#define C_9 0x28
-#define C_10 0x29
-#define C_11 0x2A
-#define C_12 0x2B
-#define C_13 0x2C
-#define C_14 0x2D
-#define C_15 0x2E
-#define C_16 0x2F
-
-#define D_1 0x30
-#define D_2 0x31
-#define D_3 0x32
-#define D_4 0x33
-#define D_5 0x34
-#define D_6 0x35
-#define D_7 0x36
-#define D_8 0x37
-#define D_9 0x38
-#define D_10 0x39
-#define D_11 0x3A
-#define D_12 0x3B
-#define D_13 0x3C
-#define D_14 0x3D
-#define D_15 0x3E
-#define D_16 0x3F
-
-#define E_1 0x40
-#define E_2 0x41
-#define E_3 0x42
-#define E_4 0x43
-#define E_5 0x44
-#define E_6 0x45
-#define E_7 0x46
-#define E_8 0x47
-#define E_9 0x48
-#define E_10 0x49
-#define E_11 0x4A
-#define E_12 0x4B
-#define E_13 0x4C
-#define E_14 0x4D
-#define E_15 0x4E
-#define E_16 0x4F
-
-#define F_1 0x50
-#define F_2 0x51
-#define F_3 0x52
-#define F_4 0x53
-#define F_5 0x54
-#define F_6 0x55
-#define F_7 0x56
-#define F_8 0x57
-#define F_9 0x58
-#define F_10 0x59
-#define F_11 0x5A
-#define F_12 0x5B
-#define F_13 0x5C
-#define F_14 0x5D
-#define F_15 0x5E
-#define F_16 0x5F
-
-#define G_1 0x60
-#define G_2 0x61
-#define G_3 0x62
-#define G_4 0x63
-#define G_5 0x64
-#define G_6 0x65
-#define G_7 0x66
-#define G_8 0x67
-#define G_9 0x68
-#define G_10 0x69
-#define G_11 0x6A
-#define G_12 0x6B
-#define G_13 0x6C
-#define G_14 0x6D
-#define G_15 0x6E
-#define G_16 0x6F
-
-#define H_1 0x70
-#define H_2 0x71
-#define H_3 0x72
-#define H_4 0x73
-#define H_5 0x74
-#define H_6 0x75
-#define H_7 0x76
-#define H_8 0x77
-#define H_9 0x78
-#define H_10 0x79
-#define H_11 0x7A
-#define H_12 0x7B
-#define H_13 0x7C
-#define H_14 0x7D
-#define H_15 0x7E
-#define H_16 0x7F
-
-#define I_1 0x80
-#define I_2 0x81
-#define I_3 0x82
-#define I_4 0x83
-#define I_5 0x84
-#define I_6 0x85
-#define I_7 0x86
-#define I_8 0x87
-#define I_9 0x88
-#define I_10 0x89
-#define I_11 0x8A
-#define I_12 0x8B
-#define I_13 0x8C
-#define I_14 0x8D
-#define I_15 0x8E
-#define I_16 0x8F
-
-#define J_1 0x90
-#define J_2 0x91
-#define J_3 0x92
-#define J_4 0x93
-#define J_5 0x94
-#define J_6 0x95
-#define J_7 0x96
-#define J_8 0x97
-#define J_9 0x98
-#define J_10 0x99
-#define J_11 0x9A
-#define J_12 0x9B
-#define J_13 0x9C
-#define J_14 0x9D
-#define J_15 0x9E
-#define J_16 0x9F
-
-#define K_1 0xA0
-#define K_2 0xA1
-#define K_3 0xA2
-#define K_4 0xA3
-#define K_5 0xA4
-#define K_6 0xA5
-#define K_7 0xA6
-#define K_8 0xA7
-#define K_9 0xA8
-#define K_10 0xA9
-#define K_11 0xAA
-#define K_12 0xAB
-#define K_13 0xAC
-#define K_14 0xAD
-#define K_15 0xAE
-#define K_16 0xAF
-
-#define L_1 0xB0
-#define L_2 0xB1
-#define L_3 0xB2
-#define L_4 0xB3
-#define L_5 0xB4
-#define L_6 0xB5
-#define L_7 0xB6
-#define L_8 0xB7
-#define L_9 0xB8
-#define L_10 0xB9
-#define L_11 0xBA
-#define L_12 0xBB
-#define L_13 0xBC
-#define L_14 0xBD
-#define L_15 0xBE
-#define L_16 0xBF
+#define SW1_CS1 0x00
+#define SW1_CS2 0x01
+#define SW1_CS3 0x02
+#define SW1_CS4 0x03
+#define SW1_CS5 0x04
+#define SW1_CS6 0x05
+#define SW1_CS7 0x06
+#define SW1_CS8 0x07
+#define SW1_CS9 0x08
+#define SW1_CS10 0x09
+#define SW1_CS11 0x0A
+#define SW1_CS12 0x0B
+#define SW1_CS13 0x0C
+#define SW1_CS14 0x0D
+#define SW1_CS15 0x0E
+#define SW1_CS16 0x0F
+
+#define SW2_CS1 0x10
+#define SW2_CS2 0x11
+#define SW2_CS3 0x12
+#define SW2_CS4 0x13
+#define SW2_CS5 0x14
+#define SW2_CS6 0x15
+#define SW2_CS7 0x16
+#define SW2_CS8 0x17
+#define SW2_CS9 0x18
+#define SW2_CS10 0x19
+#define SW2_CS11 0x1A
+#define SW2_CS12 0x1B
+#define SW2_CS13 0x1C
+#define SW2_CS14 0x1D
+#define SW2_CS15 0x1E
+#define SW2_CS16 0x1F
+
+#define SW3_CS1 0x20
+#define SW3_CS2 0x21
+#define SW3_CS3 0x22
+#define SW3_CS4 0x23
+#define SW3_CS5 0x24
+#define SW3_CS6 0x25
+#define SW3_CS7 0x26
+#define SW3_CS8 0x27
+#define SW3_CS9 0x28
+#define SW3_CS10 0x29
+#define SW3_CS11 0x2A
+#define SW3_CS12 0x2B
+#define SW3_CS13 0x2C
+#define SW3_CS14 0x2D
+#define SW3_CS15 0x2E
+#define SW3_CS16 0x2F
+
+#define SW4_CS1 0x30
+#define SW4_CS2 0x31
+#define SW4_CS3 0x32
+#define SW4_CS4 0x33
+#define SW4_CS5 0x34
+#define SW4_CS6 0x35
+#define SW4_CS7 0x36
+#define SW4_CS8 0x37
+#define SW4_CS9 0x38
+#define SW4_CS10 0x39
+#define SW4_CS11 0x3A
+#define SW4_CS12 0x3B
+#define SW4_CS13 0x3C
+#define SW4_CS14 0x3D
+#define SW4_CS15 0x3E
+#define SW4_CS16 0x3F
+
+#define SW5_CS1 0x40
+#define SW5_CS2 0x41
+#define SW5_CS3 0x42
+#define SW5_CS4 0x43
+#define SW5_CS5 0x44
+#define SW5_CS6 0x45
+#define SW5_CS7 0x46
+#define SW5_CS8 0x47
+#define SW5_CS9 0x48
+#define SW5_CS10 0x49
+#define SW5_CS11 0x4A
+#define SW5_CS12 0x4B
+#define SW5_CS13 0x4C
+#define SW5_CS14 0x4D
+#define SW5_CS15 0x4E
+#define SW5_CS16 0x4F
+
+#define SW6_CS1 0x50
+#define SW6_CS2 0x51
+#define SW6_CS3 0x52
+#define SW6_CS4 0x53
+#define SW6_CS5 0x54
+#define SW6_CS6 0x55
+#define SW6_CS7 0x56
+#define SW6_CS8 0x57
+#define SW6_CS9 0x58
+#define SW6_CS10 0x59
+#define SW6_CS11 0x5A
+#define SW6_CS12 0x5B
+#define SW6_CS13 0x5C
+#define SW6_CS14 0x5D
+#define SW6_CS15 0x5E
+#define SW6_CS16 0x5F
+
+#define SW7_CS1 0x60
+#define SW7_CS2 0x61
+#define SW7_CS3 0x62
+#define SW7_CS4 0x63
+#define SW7_CS5 0x64
+#define SW7_CS6 0x65
+#define SW7_CS7 0x66
+#define SW7_CS8 0x67
+#define SW7_CS9 0x68
+#define SW7_CS10 0x69
+#define SW7_CS11 0x6A
+#define SW7_CS12 0x6B
+#define SW7_CS13 0x6C
+#define SW7_CS14 0x6D
+#define SW7_CS15 0x6E
+#define SW7_CS16 0x6F
+
+#define SW8_CS1 0x70
+#define SW8_CS2 0x71
+#define SW8_CS3 0x72
+#define SW8_CS4 0x73
+#define SW8_CS5 0x74
+#define SW8_CS6 0x75
+#define SW8_CS7 0x76
+#define SW8_CS8 0x77
+#define SW8_CS9 0x78
+#define SW8_CS10 0x79
+#define SW8_CS11 0x7A
+#define SW8_CS12 0x7B
+#define SW8_CS13 0x7C
+#define SW8_CS14 0x7D
+#define SW8_CS15 0x7E
+#define SW8_CS16 0x7F
+
+#define SW9_CS1 0x80
+#define SW9_CS2 0x81
+#define SW9_CS3 0x82
+#define SW9_CS4 0x83
+#define SW9_CS5 0x84
+#define SW9_CS6 0x85
+#define SW9_CS7 0x86
+#define SW9_CS8 0x87
+#define SW9_CS9 0x88
+#define SW9_CS10 0x89
+#define SW9_CS11 0x8A
+#define SW9_CS12 0x8B
+#define SW9_CS13 0x8C
+#define SW9_CS14 0x8D
+#define SW9_CS15 0x8E
+#define SW9_CS16 0x8F
+
+#define SW10_CS1 0x90
+#define SW10_CS2 0x91
+#define SW10_CS3 0x92
+#define SW10_CS4 0x93
+#define SW10_CS5 0x94
+#define SW10_CS6 0x95
+#define SW10_CS7 0x96
+#define SW10_CS8 0x97
+#define SW10_CS9 0x98
+#define SW10_CS10 0x99
+#define SW10_CS11 0x9A
+#define SW10_CS12 0x9B
+#define SW10_CS13 0x9C
+#define SW10_CS14 0x9D
+#define SW10_CS15 0x9E
+#define SW10_CS16 0x9F
+
+#define SW11_CS1 0xA0
+#define SW11_CS2 0xA1
+#define SW11_CS3 0xA2
+#define SW11_CS4 0xA3
+#define SW11_CS5 0xA4
+#define SW11_CS6 0xA5
+#define SW11_CS7 0xA6
+#define SW11_CS8 0xA7
+#define SW11_CS9 0xA8
+#define SW11_CS10 0xA9
+#define SW11_CS11 0xAA
+#define SW11_CS12 0xAB
+#define SW11_CS13 0xAC
+#define SW11_CS14 0xAD
+#define SW11_CS15 0xAE
+#define SW11_CS16 0xAF
+
+#define SW12_CS1 0xB0
+#define SW12_CS2 0xB1
+#define SW12_CS3 0xB2
+#define SW12_CS4 0xB3
+#define SW12_CS5 0xB4
+#define SW12_CS6 0xB5
+#define SW12_CS7 0xB6
+#define SW12_CS8 0xB7
+#define SW12_CS9 0xB8
+#define SW12_CS10 0xB9
+#define SW12_CS11 0xBA
+#define SW12_CS12 0xBB
+#define SW12_CS13 0xBC
+#define SW12_CS14 0xBD
+#define SW12_CS15 0xBE
+#define SW12_CS16 0xBF
+
+// DEPRECATED - DO NOT USE
+
+#define A_1 SW1_CS1
+#define A_2 SW1_CS2
+#define A_3 SW1_CS3
+#define A_4 SW1_CS4
+#define A_5 SW1_CS5
+#define A_6 SW1_CS6
+#define A_7 SW1_CS7
+#define A_8 SW1_CS8
+#define A_9 SW1_CS9
+#define A_10 SW1_CS10
+#define A_11 SW1_CS11
+#define A_12 SW1_CS12
+#define A_13 SW1_CS13
+#define A_14 SW1_CS14
+#define A_15 SW1_CS15
+#define A_16 SW1_CS16
+
+#define B_1 SW2_CS1
+#define B_2 SW2_CS2
+#define B_3 SW2_CS3
+#define B_4 SW2_CS4
+#define B_5 SW2_CS5
+#define B_6 SW2_CS6
+#define B_7 SW2_CS7
+#define B_8 SW2_CS8
+#define B_9 SW2_CS9
+#define B_10 SW2_CS10
+#define B_11 SW2_CS11
+#define B_12 SW2_CS12
+#define B_13 SW2_CS13
+#define B_14 SW2_CS14
+#define B_15 SW2_CS15
+#define B_16 SW2_CS16
+
+#define C_1 SW3_CS1
+#define C_2 SW3_CS2
+#define C_3 SW3_CS3
+#define C_4 SW3_CS4
+#define C_5 SW3_CS5
+#define C_6 SW3_CS6
+#define C_7 SW3_CS7
+#define C_8 SW3_CS8
+#define C_9 SW3_CS9
+#define C_10 SW3_CS10
+#define C_11 SW3_CS11
+#define C_12 SW3_CS12
+#define C_13 SW3_CS13
+#define C_14 SW3_CS14
+#define C_15 SW3_CS15
+#define C_16 SW3_CS16
+
+#define D_1 SW4_CS1
+#define D_2 SW4_CS2
+#define D_3 SW4_CS3
+#define D_4 SW4_CS4
+#define D_5 SW4_CS5
+#define D_6 SW4_CS6
+#define D_7 SW4_CS7
+#define D_8 SW4_CS8
+#define D_9 SW4_CS9
+#define D_10 SW4_CS10
+#define D_11 SW4_CS11
+#define D_12 SW4_CS12
+#define D_13 SW4_CS13
+#define D_14 SW4_CS14
+#define D_15 SW4_CS15
+#define D_16 SW4_CS16
+
+#define E_1 SW5_CS1
+#define E_2 SW5_CS2
+#define E_3 SW5_CS3
+#define E_4 SW5_CS4
+#define E_5 SW5_CS5
+#define E_6 SW5_CS6
+#define E_7 SW5_CS7
+#define E_8 SW5_CS8
+#define E_9 SW5_CS9
+#define E_10 SW5_CS10
+#define E_11 SW5_CS11
+#define E_12 SW5_CS12
+#define E_13 SW5_CS13
+#define E_14 SW5_CS14
+#define E_15 SW5_CS15
+#define E_16 SW5_CS16
+
+#define F_1 SW6_CS1
+#define F_2 SW6_CS2
+#define F_3 SW6_CS3
+#define F_4 SW6_CS4
+#define F_5 SW6_CS5
+#define F_6 SW6_CS6
+#define F_7 SW6_CS7
+#define F_8 SW6_CS8
+#define F_9 SW6_CS9
+#define F_10 SW6_CS10
+#define F_11 SW6_CS11
+#define F_12 SW6_CS12
+#define F_13 SW6_CS13
+#define F_14 SW6_CS14
+#define F_15 SW6_CS15
+#define F_16 SW6_CS16
+
+#define G_1 SW7_CS1
+#define G_2 SW7_CS2
+#define G_3 SW7_CS3
+#define G_4 SW7_CS4
+#define G_5 SW7_CS5
+#define G_6 SW7_CS6
+#define G_7 SW7_CS7
+#define G_8 SW7_CS8
+#define G_9 SW7_CS9
+#define G_10 SW7_CS10
+#define G_11 SW7_CS11
+#define G_12 SW7_CS12
+#define G_13 SW7_CS13
+#define G_14 SW7_CS14
+#define G_15 SW7_CS15
+#define G_16 SW7_CS16
+
+#define H_1 SW8_CS1
+#define H_2 SW8_CS2
+#define H_3 SW8_CS3
+#define H_4 SW8_CS4
+#define H_5 SW8_CS5
+#define H_6 SW8_CS6
+#define H_7 SW8_CS7
+#define H_8 SW8_CS8
+#define H_9 SW8_CS9
+#define H_10 SW8_CS10
+#define H_11 SW8_CS11
+#define H_12 SW8_CS12
+#define H_13 SW8_CS13
+#define H_14 SW8_CS14
+#define H_15 SW8_CS15
+#define H_16 SW8_CS16
+
+#define I_1 SW9_CS1
+#define I_2 SW9_CS2
+#define I_3 SW9_CS3
+#define I_4 SW9_CS4
+#define I_5 SW9_CS5
+#define I_6 SW9_CS6
+#define I_7 SW9_CS7
+#define I_8 SW9_CS8
+#define I_9 SW9_CS9
+#define I_10 SW9_CS10
+#define I_11 SW9_CS11
+#define I_12 SW9_CS12
+#define I_13 SW9_CS13
+#define I_14 SW9_CS14
+#define I_15 SW9_CS15
+#define I_16 SW9_CS16
+
+#define J_1 SW10_CS1
+#define J_2 SW10_CS2
+#define J_3 SW10_CS3
+#define J_4 SW10_CS4
+#define J_5 SW10_CS5
+#define J_6 SW10_CS6
+#define J_7 SW10_CS7
+#define J_8 SW10_CS8
+#define J_9 SW10_CS9
+#define J_10 SW10_CS10
+#define J_11 SW10_CS11
+#define J_12 SW10_CS12
+#define J_13 SW10_CS13
+#define J_14 SW10_CS14
+#define J_15 SW10_CS15
+#define J_16 SW10_CS16
+
+#define K_1 SW11_CS1
+#define K_2 SW11_CS2
+#define K_3 SW11_CS3
+#define K_4 SW11_CS4
+#define K_5 SW11_CS5
+#define K_6 SW11_CS6
+#define K_7 SW11_CS7
+#define K_8 SW11_CS8
+#define K_9 SW11_CS9
+#define K_10 SW11_CS10
+#define K_11 SW11_CS11
+#define K_12 SW11_CS12
+#define K_13 SW11_CS13
+#define K_14 SW11_CS14
+#define K_15 SW11_CS15
+#define K_16 SW11_CS16
+
+#define L_1 SW12_CS1
+#define L_2 SW12_CS2
+#define L_3 SW12_CS3
+#define L_4 SW12_CS4
+#define L_5 SW12_CS5
+#define L_6 SW12_CS6
+#define L_7 SW12_CS7
+#define L_8 SW12_CS8
+#define L_9 SW12_CS9
+#define L_10 SW12_CS10
+#define L_11 SW12_CS11
+#define L_12 SW12_CS12
+#define L_13 SW12_CS13
+#define L_14 SW12_CS14
+#define L_15 SW12_CS15
+#define L_16 SW12_CS16
diff --git a/drivers/led/issi/is31fl3736-mono.c b/drivers/led/issi/is31fl3736-mono.c
new file mode 100644
index 0000000000..7a5415c725
--- /dev/null
+++ b/drivers/led/issi/is31fl3736-mono.c
@@ -0,0 +1,240 @@
+/* Copyright 2018 Jason Williams (Wilba)
+ * Copyright 2021 Doni Crosby
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "is31fl3736-mono.h"
+#include "i2c_master.h"
+#include "gpio.h"
+#include "wait.h"
+
+#define IS31FL3736_PWM_REGISTER_COUNT 192 // actually 96
+#define IS31FL3736_LED_CONTROL_REGISTER_COUNT 24
+
+#ifndef IS31FL3736_I2C_TIMEOUT
+# define IS31FL3736_I2C_TIMEOUT 100
+#endif
+
+#ifndef IS31FL3736_I2C_PERSISTENCE
+# define IS31FL3736_I2C_PERSISTENCE 0
+#endif
+
+#ifndef IS31FL3736_PWM_FREQUENCY
+# define IS31FL3736_PWM_FREQUENCY IS31FL3736_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3736B only
+#endif
+
+#ifndef IS31FL3736_SW_PULLUP
+# define IS31FL3736_SW_PULLUP IS31FL3736_PUR_0_OHM
+#endif
+
+#ifndef IS31FL3736_CS_PULLDOWN
+# define IS31FL3736_CS_PULLDOWN IS31FL3736_PDR_0_OHM
+#endif
+
+#ifndef IS31FL3736_GLOBAL_CURRENT
+# define IS31FL3736_GLOBAL_CURRENT 0xFF
+#endif
+
+const uint8_t i2c_addresses[IS31FL3736_DRIVER_COUNT] = {
+ IS31FL3736_I2C_ADDRESS_1,
+#ifdef IS31FL3736_I2C_ADDRESS_2
+ IS31FL3736_I2C_ADDRESS_2,
+# ifdef IS31FL3736_I2C_ADDRESS_3
+ IS31FL3736_I2C_ADDRESS_3,
+# ifdef IS31FL3736_I2C_ADDRESS_4
+ IS31FL3736_I2C_ADDRESS_4,
+# endif
+# endif
+#endif
+};
+
+// These buffers match the IS31FL3736 PWM registers.
+// The control buffers match the page 0 LED On/Off registers.
+// Storing them like this is optimal for I2C transfers to the registers.
+// We could optimize this and take out the unused registers from these
+// buffers and the transfers in is31fl3736_write_pwm_buffer() but it's
+// probably not worth the extra complexity.
+typedef struct is31fl3736_driver_t {
+ uint8_t pwm_buffer[IS31FL3736_PWM_REGISTER_COUNT];
+ bool pwm_buffer_dirty;
+ uint8_t led_control_buffer[IS31FL3736_LED_CONTROL_REGISTER_COUNT];
+ bool led_control_buffer_dirty;
+} PACKED is31fl3736_driver_t;
+
+is31fl3736_driver_t driver_buffers[IS31FL3736_DRIVER_COUNT] = {{
+ .pwm_buffer = {0},
+ .pwm_buffer_dirty = false,
+ .led_control_buffer = {0},
+ .led_control_buffer_dirty = false,
+}};
+
+void is31fl3736_write_register(uint8_t index, uint8_t reg, uint8_t data) {
+#if IS31FL3736_I2C_PERSISTENCE > 0
+ for (uint8_t i = 0; i < IS31FL3736_I2C_PERSISTENCE; i++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3736_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3736_I2C_TIMEOUT);
+#endif
+}
+
+void is31fl3736_select_page(uint8_t index, uint8_t page) {
+ is31fl3736_write_register(index, IS31FL3736_REG_COMMAND_WRITE_LOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC);
+ is31fl3736_write_register(index, IS31FL3736_REG_COMMAND, page);
+}
+
+void is31fl3736_write_pwm_buffer(uint8_t index) {
+ // Assumes page 1 is already selected.
+ // Transmit PWM registers in 12 transfers of 16 bytes.
+
+ // Iterate over the pwm_buffer contents at 16 byte intervals.
+ for (uint8_t i = 0; i < IS31FL3736_PWM_REGISTER_COUNT; i += 16) {
+#if IS31FL3736_I2C_PERSISTENCE > 0
+ for (uint8_t j = 0; j < IS31FL3736_I2C_PERSISTENCE; j++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3736_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3736_I2C_TIMEOUT);
+#endif
+ }
+}
+
+void is31fl3736_init_drivers(void) {
+ i2c_init();
+
+#if defined(IS31FL3736_SDB_PIN)
+ gpio_set_pin_output(IS31FL3736_SDB_PIN);
+ gpio_write_pin_high(IS31FL3736_SDB_PIN);
+#endif
+
+ for (uint8_t i = 0; i < IS31FL3736_DRIVER_COUNT; i++) {
+ is31fl3736_init(i);
+ }
+
+ for (int i = 0; i < IS31FL3736_LED_COUNT; i++) {
+ is31fl3736_set_led_control_register(i, true);
+ }
+
+ for (uint8_t i = 0; i < IS31FL3736_DRIVER_COUNT; i++) {
+ is31fl3736_update_led_control_registers(i);
+ }
+}
+
+void is31fl3736_init(uint8_t index) {
+ // In order to avoid the LEDs being driven with garbage data
+ // in the LED driver's PWM registers, shutdown is enabled last.
+ // Set up the mode and other settings, clear the PWM registers,
+ // then disable software shutdown.
+
+ is31fl3736_select_page(index, IS31FL3736_COMMAND_LED_CONTROL);
+
+ // Turn off all LEDs.
+ for (uint8_t i = 0; i < IS31FL3736_LED_CONTROL_REGISTER_COUNT; i++) {
+ is31fl3736_write_register(index, i, 0x00);
+ }
+
+ is31fl3736_select_page(index, IS31FL3736_COMMAND_PWM);
+
+ // Set PWM on all LEDs to 0
+ // No need to setup Breath registers to PWM as that is the default.
+ for (uint8_t i = 0; i < IS31FL3736_PWM_REGISTER_COUNT; i++) {
+ is31fl3736_write_register(index, i, 0x00);
+ }
+
+ is31fl3736_select_page(index, IS31FL3736_COMMAND_FUNCTION);
+
+ // Set de-ghost pull-up resistors (SWx)
+ is31fl3736_write_register(index, IS31FL3736_FUNCTION_REG_SW_PULLUP, IS31FL3736_SW_PULLUP);
+ // Set de-ghost pull-down resistors (CSx)
+ is31fl3736_write_register(index, IS31FL3736_FUNCTION_REG_CS_PULLDOWN, IS31FL3736_CS_PULLDOWN);
+ // Set global current to maximum.
+ is31fl3736_write_register(index, IS31FL3736_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3736_GLOBAL_CURRENT);
+ // Disable software shutdown.
+ is31fl3736_write_register(index, IS31FL3736_FUNCTION_REG_CONFIGURATION, ((IS31FL3736_PWM_FREQUENCY & 0b111) << 3) | 0x01);
+
+ // Wait 10ms to ensure the device has woken up.
+ wait_ms(10);
+}
+
+void is31fl3736_set_value(int index, uint8_t value) {
+ is31fl3736_led_t led;
+
+ if (index >= 0 && index < IS31FL3736_LED_COUNT) {
+ memcpy_P(&led, (&g_is31fl3736_leds[index]), sizeof(led));
+
+ if (driver_buffers[led.driver].pwm_buffer[led.v] == value) {
+ return;
+ }
+
+ driver_buffers[led.driver].pwm_buffer[led.v] = value;
+ driver_buffers[led.driver].pwm_buffer_dirty = true;
+ }
+}
+
+void is31fl3736_set_value_all(uint8_t value) {
+ for (int i = 0; i < IS31FL3736_LED_COUNT; i++) {
+ is31fl3736_set_value(i, value);
+ }
+}
+
+void is31fl3736_set_led_control_register(uint8_t index, bool value) {
+ is31fl3736_led_t led;
+ memcpy_P(&led, (&g_is31fl3736_leds[index]), sizeof(led));
+
+ // The PWM register for a matrix position (0x00 to 0xBF) is interleaved, so:
+ // A1=0x00 A2=0x02 A3=0x04 A4=0x06 A5=0x08 A6=0x0A A7=0x0C A8=0x0E
+ // B1=0x10 B2=0x12 B3=0x14
+ // But also, the LED control registers (0x00 to 0x17) are also interleaved, so:
+ // A1-A4=0x00 A5-A8=0x01
+
+ uint8_t control_register = led.v / 8;
+ uint8_t bit_value = led.v % 8;
+
+ if (value) {
+ driver_buffers[led.driver].led_control_buffer[control_register] |= (1 << bit_value);
+ } else {
+ driver_buffers[led.driver].led_control_buffer[control_register] &= ~(1 << bit_value);
+ }
+
+ driver_buffers[led.driver].led_control_buffer_dirty = true;
+}
+
+void is31fl3736_update_pwm_buffers(uint8_t index) {
+ if (driver_buffers[index].pwm_buffer_dirty) {
+ is31fl3736_select_page(index, IS31FL3736_COMMAND_PWM);
+
+ is31fl3736_write_pwm_buffer(index);
+
+ driver_buffers[index].pwm_buffer_dirty = false;
+ }
+}
+
+void is31fl3736_update_led_control_registers(uint8_t index) {
+ if (driver_buffers[index].led_control_buffer_dirty) {
+ is31fl3736_select_page(index, IS31FL3736_COMMAND_LED_CONTROL);
+
+ for (uint8_t i = 0; i < IS31FL3736_LED_CONTROL_REGISTER_COUNT; i++) {
+ is31fl3736_write_register(index, i, driver_buffers[index].led_control_buffer[i]);
+ }
+
+ driver_buffers[index].led_control_buffer_dirty = false;
+ }
+}
+
+void is31fl3736_flush(void) {
+ for (uint8_t i = 0; i < IS31FL3736_DRIVER_COUNT; i++) {
+ is31fl3736_update_pwm_buffers(i);
+ }
+}
diff --git a/drivers/led/issi/is31fl3736-simple.h b/drivers/led/issi/is31fl3736-mono.h
index a73a872545..91c2e0420b 100644
--- a/drivers/led/issi/is31fl3736-simple.h
+++ b/drivers/led/issi/is31fl3736-mono.h
@@ -110,9 +110,9 @@ typedef struct is31fl3736_led_t {
extern const is31fl3736_led_t PROGMEM g_is31fl3736_leds[IS31FL3736_LED_COUNT];
void is31fl3736_init_drivers(void);
-void is31fl3736_init(uint8_t addr);
-void is31fl3736_write_register(uint8_t addr, uint8_t reg, uint8_t data);
-void is31fl3736_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
+void is31fl3736_init(uint8_t index);
+void is31fl3736_write_register(uint8_t index, uint8_t reg, uint8_t data);
+void is31fl3736_select_page(uint8_t index, uint8_t page);
void is31fl3736_set_value(int index, uint8_t value);
void is31fl3736_set_value_all(uint8_t value);
@@ -123,8 +123,8 @@ void is31fl3736_set_led_control_register(uint8_t index, bool value);
// (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 is31fl3736_update_pwm_buffers(uint8_t addr, uint8_t index);
-void is31fl3736_update_led_control_registers(uint8_t addr, uint8_t index);
+void is31fl3736_update_pwm_buffers(uint8_t index);
+void is31fl3736_update_led_control_registers(uint8_t index);
void is31fl3736_flush(void);
@@ -152,110 +152,220 @@ void is31fl3736_flush(void);
#define IS31FL3736_PWM_FREQUENCY_2K1_HZ 0b011
#define IS31FL3736_PWM_FREQUENCY_1K05_HZ 0b100
-#define A_1 0x00
-#define A_2 0x02
-#define A_3 0x04
-#define A_4 0x06
-#define A_5 0x08
-#define A_6 0x0A
-#define A_7 0x0C
-#define A_8 0x0E
-
-#define B_1 0x10
-#define B_2 0x12
-#define B_3 0x14
-#define B_4 0x16
-#define B_5 0x18
-#define B_6 0x1A
-#define B_7 0x1C
-#define B_8 0x1E
-
-#define C_1 0x20
-#define C_2 0x22
-#define C_3 0x24
-#define C_4 0x26
-#define C_5 0x28
-#define C_6 0x2A
-#define C_7 0x2C
-#define C_8 0x2E
-
-#define D_1 0x30
-#define D_2 0x32
-#define D_3 0x34
-#define D_4 0x36
-#define D_5 0x38
-#define D_6 0x3A
-#define D_7 0x3C
-#define D_8 0x3E
-
-#define E_1 0x40
-#define E_2 0x42
-#define E_3 0x44
-#define E_4 0x46
-#define E_5 0x48
-#define E_6 0x4A
-#define E_7 0x4C
-#define E_8 0x4E
-
-#define F_1 0x50
-#define F_2 0x52
-#define F_3 0x54
-#define F_4 0x56
-#define F_5 0x58
-#define F_6 0x5A
-#define F_7 0x5C
-#define F_8 0x5E
-
-#define G_1 0x60
-#define G_2 0x62
-#define G_3 0x64
-#define G_4 0x66
-#define G_5 0x68
-#define G_6 0x6A
-#define G_7 0x6C
-#define G_8 0x6E
-
-#define H_1 0x70
-#define H_2 0x72
-#define H_3 0x74
-#define H_4 0x76
-#define H_5 0x78
-#define H_6 0x7A
-#define H_7 0x7C
-#define H_8 0x7E
-
-#define I_1 0x80
-#define I_2 0x82
-#define I_3 0x84
-#define I_4 0x86
-#define I_5 0x88
-#define I_6 0x8A
-#define I_7 0x8C
-#define I_8 0x8E
-
-#define J_1 0x90
-#define J_2 0x92
-#define J_3 0x94
-#define J_4 0x96
-#define J_5 0x98
-#define J_6 0x9A
-#define J_7 0x9C
-#define J_8 0x9E
-
-#define K_1 0xA0
-#define K_2 0xA2
-#define K_3 0xA4
-#define K_4 0xA6
-#define K_5 0xA8
-#define K_6 0xAA
-#define K_7 0xAC
-#define K_8 0xAE
-
-#define L_1 0xB0
-#define L_2 0xB2
-#define L_3 0xB4
-#define L_4 0xB6
-#define L_5 0xB8
-#define L_6 0xBA
-#define L_7 0xBC
-#define L_8 0xBE
+#define SW1_CS1 0x00
+#define SW1_CS2 0x02
+#define SW1_CS3 0x04
+#define SW1_CS4 0x06
+#define SW1_CS5 0x08
+#define SW1_CS6 0x0A
+#define SW1_CS7 0x0C
+#define SW1_CS8 0x0E
+
+#define SW2_CS1 0x10
+#define SW2_CS2 0x12
+#define SW2_CS3 0x14
+#define SW2_CS4 0x16
+#define SW2_CS5 0x18
+#define SW2_CS6 0x1A
+#define SW2_CS7 0x1C
+#define SW2_CS8 0x1E
+
+#define SW3_CS1 0x20
+#define SW3_CS2 0x22
+#define SW3_CS3 0x24
+#define SW3_CS4 0x26
+#define SW3_CS5 0x28
+#define SW3_CS6 0x2A
+#define SW3_CS7 0x2C
+#define SW3_CS8 0x2E
+
+#define SW4_CS1 0x30
+#define SW4_CS2 0x32
+#define SW4_CS3 0x34
+#define SW4_CS4 0x36
+#define SW4_CS5 0x38
+#define SW4_CS6 0x3A
+#define SW4_CS7 0x3C
+#define SW4_CS8 0x3E
+
+#define SW5_CS1 0x40
+#define SW5_CS2 0x42
+#define SW5_CS3 0x44
+#define SW5_CS4 0x46
+#define SW5_CS5 0x48
+#define SW5_CS6 0x4A
+#define SW5_CS7 0x4C
+#define SW5_CS8 0x4E
+
+#define SW6_CS1 0x50
+#define SW6_CS2 0x52
+#define SW6_CS3 0x54
+#define SW6_CS4 0x56
+#define SW6_CS5 0x58
+#define SW6_CS6 0x5A
+#define SW6_CS7 0x5C
+#define SW6_CS8 0x5E
+
+#define SW7_CS1 0x60
+#define SW7_CS2 0x62
+#define SW7_CS3 0x64
+#define SW7_CS4 0x66
+#define SW7_CS5 0x68
+#define SW7_CS6 0x6A
+#define SW7_CS7 0x6C
+#define SW7_CS8 0x6E
+
+#define SW8_CS1 0x70
+#define SW8_CS2 0x72
+#define SW8_CS3 0x74
+#define SW8_CS4 0x76
+#define SW8_CS5 0x78
+#define SW8_CS6 0x7A
+#define SW8_CS7 0x7C
+#define SW8_CS8 0x7E
+
+#define SW9_CS1 0x80
+#define SW9_CS2 0x82
+#define SW9_CS3 0x84
+#define SW9_CS4 0x86
+#define SW9_CS5 0x88
+#define SW9_CS6 0x8A
+#define SW9_CS7 0x8C
+#define SW9_CS8 0x8E
+
+#define SW10_CS1 0x90
+#define SW10_CS2 0x92
+#define SW10_CS3 0x94
+#define SW10_CS4 0x96
+#define SW10_CS5 0x98
+#define SW10_CS6 0x9A
+#define SW10_CS7 0x9C
+#define SW10_CS8 0x9E
+
+#define SW11_CS1 0xA0
+#define SW11_CS2 0xA2
+#define SW11_CS3 0xA4
+#define SW11_CS4 0xA6
+#define SW11_CS5 0xA8
+#define SW11_CS6 0xAA
+#define SW11_CS7 0xAC
+#define SW11_CS8 0xAE
+
+#define SW12_CS1 0xB0
+#define SW12_CS2 0xB2
+#define SW12_CS3 0xB4
+#define SW12_CS4 0xB6
+#define SW12_CS5 0xB8
+#define SW12_CS6 0xBA
+#define SW12_CS7 0xBC
+#define SW12_CS8 0xBE
+
+// DEPRECATED - DO NOT USE
+
+#define A_1 SW1_CS1
+#define A_2 SW1_CS2
+#define A_3 SW1_CS3
+#define A_4 SW1_CS4
+#define A_5 SW1_CS5
+#define A_6 SW1_CS6
+#define A_7 SW1_CS7
+#define A_8 SW1_CS8
+
+#define B_1 SW2_CS1
+#define B_2 SW2_CS2
+#define B_3 SW2_CS3
+#define B_4 SW2_CS4
+#define B_5 SW2_CS5
+#define B_6 SW2_CS6
+#define B_7 SW2_CS7
+#define B_8 SW2_CS8
+
+#define C_1 SW3_CS1
+#define C_2 SW3_CS2
+#define C_3 SW3_CS3
+#define C_4 SW3_CS4
+#define C_5 SW3_CS5
+#define C_6 SW3_CS6
+#define C_7 SW3_CS7
+#define C_8 SW3_CS8
+
+#define D_1 SW4_CS1
+#define D_2 SW4_CS2
+#define D_3 SW4_CS3
+#define D_4 SW4_CS4
+#define D_5 SW4_CS5
+#define D_6 SW4_CS6
+#define D_7 SW4_CS7
+#define D_8 SW4_CS8
+
+#define E_1 SW5_CS1
+#define E_2 SW5_CS2
+#define E_3 SW5_CS3
+#define E_4 SW5_CS4
+#define E_5 SW5_CS5
+#define E_6 SW5_CS6
+#define E_7 SW5_CS7
+#define E_8 SW5_CS8
+
+#define F_1 SW6_CS1
+#define F_2 SW6_CS2
+#define F_3 SW6_CS3
+#define F_4 SW6_CS4
+#define F_5 SW6_CS5
+#define F_6 SW6_CS6
+#define F_7 SW6_CS7
+#define F_8 SW6_CS8
+
+#define G_1 SW7_CS1
+#define G_2 SW7_CS2
+#define G_3 SW7_CS3
+#define G_4 SW7_CS4
+#define G_5 SW7_CS5
+#define G_6 SW7_CS6
+#define G_7 SW7_CS7
+#define G_8 SW7_CS8
+
+#define H_1 SW8_CS1
+#define H_2 SW8_CS2
+#define H_3 SW8_CS3
+#define H_4 SW8_CS4
+#define H_5 SW8_CS5
+#define H_6 SW8_CS6
+#define H_7 SW8_CS7
+#define H_8 SW8_CS8
+
+#define I_1 SW9_CS1
+#define I_2 SW9_CS2
+#define I_3 SW9_CS3
+#define I_4 SW9_CS4
+#define I_5 SW9_CS5
+#define I_6 SW9_CS6
+#define I_7 SW9_CS7
+#define I_8 SW9_CS8
+
+#define J_1 SW10_CS1
+#define J_2 SW10_CS2
+#define J_3 SW10_CS3
+#define J_4 SW10_CS4
+#define J_5 SW10_CS5
+#define J_6 SW10_CS6
+#define J_7 SW10_CS7
+#define J_8 SW10_CS8
+
+#define K_1 SW11_CS1
+#define K_2 SW11_CS2
+#define K_3 SW11_CS3
+#define K_4 SW11_CS4
+#define K_5 SW11_CS5
+#define K_6 SW11_CS6
+#define K_7 SW11_CS7
+#define K_8 SW11_CS8
+
+#define L_1 SW12_CS1
+#define L_2 SW12_CS2
+#define L_3 SW12_CS3
+#define L_4 SW12_CS4
+#define L_5 SW12_CS5
+#define L_6 SW12_CS6
+#define L_7 SW12_CS7
+#define L_8 SW12_CS8
diff --git a/drivers/led/issi/is31fl3736-simple.c b/drivers/led/issi/is31fl3736-simple.c
deleted file mode 100644
index e1cce3c48a..0000000000
--- a/drivers/led/issi/is31fl3736-simple.c
+++ /dev/null
@@ -1,252 +0,0 @@
-/* Copyright 2018 Jason Williams (Wilba)
- * Copyright 2021 Doni Crosby
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#include "is31fl3736-simple.h"
-#include <string.h>
-#include "i2c_master.h"
-#include "wait.h"
-
-#define IS31FL3736_PWM_REGISTER_COUNT 192 // actually 96
-#define IS31FL3736_LED_CONTROL_REGISTER_COUNT 24
-
-#ifndef IS31FL3736_I2C_TIMEOUT
-# define IS31FL3736_I2C_TIMEOUT 100
-#endif
-
-#ifndef IS31FL3736_I2C_PERSISTENCE
-# define IS31FL3736_I2C_PERSISTENCE 0
-#endif
-
-#ifndef IS31FL3736_PWM_FREQUENCY
-# define IS31FL3736_PWM_FREQUENCY IS31FL3736_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3736B only
-#endif
-
-#ifndef IS31FL3736_SW_PULLUP
-# define IS31FL3736_SW_PULLUP IS31FL3736_PUR_0_OHM
-#endif
-
-#ifndef IS31FL3736_CS_PULLDOWN
-# define IS31FL3736_CS_PULLDOWN IS31FL3736_PDR_0_OHM
-#endif
-
-#ifndef IS31FL3736_GLOBAL_CURRENT
-# define IS31FL3736_GLOBAL_CURRENT 0xFF
-#endif
-
-// Transfer buffer for TWITransmitData()
-uint8_t g_twi_transfer_buffer[20];
-
-// These buffers match the IS31FL3736 PWM registers.
-// The control buffers match the PG0 LED On/Off registers.
-// Storing them like this is optimal for I2C transfers to the registers.
-// We could optimize this and take out the unused registers from these
-// buffers and the transfers in is31fl3736_write_pwm_buffer() but it's
-// probably not worth the extra complexity.
-uint8_t g_pwm_buffer[IS31FL3736_DRIVER_COUNT][IS31FL3736_PWM_REGISTER_COUNT];
-bool g_pwm_buffer_update_required[IS31FL3736_DRIVER_COUNT] = {false};
-
-uint8_t g_led_control_registers[IS31FL3736_DRIVER_COUNT][IS31FL3736_LED_CONTROL_REGISTER_COUNT] = {0};
-bool g_led_control_registers_update_required[IS31FL3736_DRIVER_COUNT] = {false};
-
-void is31fl3736_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
- g_twi_transfer_buffer[0] = reg;
- g_twi_transfer_buffer[1] = data;
-
-#if IS31FL3736_I2C_PERSISTENCE > 0
- for (uint8_t i = 0; i < IS31FL3736_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3736_I2C_TIMEOUT) == 0) break;
- }
-#else
- i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3736_I2C_TIMEOUT);
-#endif
-}
-
-void is31fl3736_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
- // assumes PG1 is already selected
-
- // transmit PWM registers in 12 transfers of 16 bytes
- // g_twi_transfer_buffer[] is 20 bytes
-
- // iterate over the pwm_buffer contents at 16 byte intervals
- for (int i = 0; i < IS31FL3736_PWM_REGISTER_COUNT; i += 16) {
- g_twi_transfer_buffer[0] = i;
- // copy the data from i to i+15
- // device will auto-increment register for data after the first byte
- // thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer
- memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16);
-
-#if IS31FL3736_I2C_PERSISTENCE > 0
- for (uint8_t i = 0; i < IS31FL3736_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3736_I2C_TIMEOUT) == 0) break;
- }
-#else
- i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3736_I2C_TIMEOUT);
-#endif
- }
-}
-
-void is31fl3736_init_drivers(void) {
- i2c_init();
-
- is31fl3736_init(IS31FL3736_I2C_ADDRESS_1);
-#if defined(IS31FL3736_I2C_ADDRESS_2)
- is31fl3736_init(IS31FL3736_I2C_ADDRESS_2);
-# if defined(IS31FL3736_I2C_ADDRESS_3)
- is31fl3736_init(IS31FL3736_I2C_ADDRESS_3);
-# if defined(IS31FL3736_I2C_ADDRESS_4)
- is31fl3736_init(IS31FL3736_I2C_ADDRESS_4);
-# endif
-# endif
-#endif
-
- for (int i = 0; i < IS31FL3736_LED_COUNT; i++) {
- is31fl3736_set_led_control_register(i, true);
- }
-
- is31fl3736_update_led_control_registers(IS31FL3736_I2C_ADDRESS_1, 0);
-#if defined(IS31FL3736_I2C_ADDRESS_2)
- is31fl3736_update_led_control_registers(IS31FL3736_I2C_ADDRESS_2, 1);
-# if defined(IS31FL3736_I2C_ADDRESS_3)
- is31fl3736_update_led_control_registers(IS31FL3736_I2C_ADDRESS_3, 2);
-# if defined(IS31FL3736_I2C_ADDRESS_4)
- is31fl3736_update_led_control_registers(IS31FL3736_I2C_ADDRESS_4, 3);
-# endif
-# endif
-#endif
-}
-
-void is31fl3736_init(uint8_t addr) {
- // In order to avoid the LEDs being driven with garbage data
- // in the LED driver's PWM registers, shutdown is enabled last.
- // Set up the mode and other settings, clear the PWM registers,
- // then disable software shutdown.
-
- // Unlock the command register.
- is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND_WRITE_LOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC);
-
- // Select PG0
- is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND, IS31FL3736_COMMAND_LED_CONTROL);
- // Turn off all LEDs.
- for (int i = 0; i < IS31FL3736_LED_CONTROL_REGISTER_COUNT; i++) {
- is31fl3736_write_register(addr, i, 0x00);
- }
-
- // Unlock the command register.
- is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND_WRITE_LOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC);
-
- // Select PG1
- is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND, IS31FL3736_COMMAND_PWM);
- // Set PWM on all LEDs to 0
- // No need to setup Breath registers to PWM as that is the default.
- for (int i = 0; i < IS31FL3736_PWM_REGISTER_COUNT; i++) {
- is31fl3736_write_register(addr, i, 0x00);
- }
-
- // Unlock the command register.
- is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND_WRITE_LOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC);
-
- // Select PG3
- is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND, IS31FL3736_COMMAND_FUNCTION);
- // Set de-ghost pull-up resistors (SWx)
- is31fl3736_write_register(addr, IS31FL3736_FUNCTION_REG_SW_PULLUP, IS31FL3736_SW_PULLUP);
- // Set de-ghost pull-down resistors (CSx)
- is31fl3736_write_register(addr, IS31FL3736_FUNCTION_REG_CS_PULLDOWN, IS31FL3736_CS_PULLDOWN);
- // Set global current to maximum.
- is31fl3736_write_register(addr, IS31FL3736_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3736_GLOBAL_CURRENT);
- // Disable software shutdown.
- is31fl3736_write_register(addr, IS31FL3736_FUNCTION_REG_CONFIGURATION, ((IS31FL3736_PWM_FREQUENCY & 0b111) << 3) | 0x01);
-
- // Wait 10ms to ensure the device has woken up.
- wait_ms(10);
-}
-
-void is31fl3736_set_value(int index, uint8_t value) {
- is31fl3736_led_t led;
- if (index >= 0 && index < IS31FL3736_LED_COUNT) {
- memcpy_P(&led, (&g_is31fl3736_leds[index]), sizeof(led));
-
- if (g_pwm_buffer[led.driver][led.v] == value) {
- return;
- }
- g_pwm_buffer[led.driver][led.v] = value;
- g_pwm_buffer_update_required[led.driver] = true;
- }
-}
-
-void is31fl3736_set_value_all(uint8_t value) {
- for (int i = 0; i < IS31FL3736_LED_COUNT; i++) {
- is31fl3736_set_value(i, value);
- }
-}
-
-void is31fl3736_set_led_control_register(uint8_t index, bool value) {
- is31fl3736_led_t led;
- memcpy_P(&led, (&g_is31fl3736_leds[index]), sizeof(led));
-
- // The PWM register for a matrix position (0x00 to 0xBF) is interleaved, so:
- // A1=0x00 A2=0x02 A3=0x04 A4=0x06 A5=0x08 A6=0x0A A7=0x0C A8=0x0E
- // B1=0x10 B2=0x12 B3=0x14
- // But also, the LED control registers (0x00 to 0x17) are also interleaved, so:
- // A1-A4=0x00 A5-A8=0x01
-
- uint8_t control_register = led.v / 8;
- uint8_t bit_value = led.v % 8;
-
- if (value) {
- g_led_control_registers[led.driver][control_register] |= (1 << bit_value);
- } else {
- g_led_control_registers[led.driver][control_register] &= ~(1 << bit_value);
- }
-
- g_led_control_registers_update_required[led.driver] = true;
-}
-
-void is31fl3736_update_pwm_buffers(uint8_t addr, uint8_t index) {
- if (g_pwm_buffer_update_required[index]) {
- // Firstly we need to unlock the command register and select PG1
- is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND_WRITE_LOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC);
- is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND, IS31FL3736_COMMAND_PWM);
-
- is31fl3736_write_pwm_buffer(addr, g_pwm_buffer[index]);
- g_pwm_buffer_update_required[index] = false;
- }
-}
-
-void is31fl3736_update_led_control_registers(uint8_t addr, uint8_t index) {
- if (g_led_control_registers_update_required[index]) {
- // Firstly we need to unlock the command register and select PG0
- is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND_WRITE_LOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC);
- is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND, IS31FL3736_COMMAND_LED_CONTROL);
- for (int i = 0; i < IS31FL3736_LED_CONTROL_REGISTER_COUNT; i++) {
- is31fl3736_write_register(addr, i, g_led_control_registers[index][i]);
- }
- g_led_control_registers_update_required[index] = false;
- }
-}
-
-void is31fl3736_flush(void) {
- is31fl3736_update_pwm_buffers(IS31FL3736_I2C_ADDRESS_1, 0);
-#if defined(IS31FL3736_I2C_ADDRESS_2)
- is31fl3736_update_pwm_buffers(IS31FL3736_I2C_ADDRESS_2, 1);
-# if defined(IS31FL3736_I2C_ADDRESS_3)
- is31fl3736_update_pwm_buffers(IS31FL3736_I2C_ADDRESS_3, 2);
-# if defined(IS31FL3736_I2C_ADDRESS_4)
- is31fl3736_update_pwm_buffers(IS31FL3736_I2C_ADDRESS_4, 3);
-# endif
-# endif
-#endif
-}
diff --git a/drivers/led/issi/is31fl3736.c b/drivers/led/issi/is31fl3736.c
index 30ab796f3e..3ab42e2f7c 100644
--- a/drivers/led/issi/is31fl3736.c
+++ b/drivers/led/issi/is31fl3736.c
@@ -16,8 +16,8 @@
*/
#include "is31fl3736.h"
-#include <string.h>
#include "i2c_master.h"
+#include "gpio.h"
#include "wait.h"
#define IS31FL3736_PWM_REGISTER_COUNT 192 // actually 96
@@ -47,54 +47,66 @@
# define IS31FL3736_GLOBAL_CURRENT 0xFF
#endif
-// Transfer buffer for TWITransmitData()
-uint8_t g_twi_transfer_buffer[20];
+const uint8_t i2c_addresses[IS31FL3736_DRIVER_COUNT] = {
+ IS31FL3736_I2C_ADDRESS_1,
+#ifdef IS31FL3736_I2C_ADDRESS_2
+ IS31FL3736_I2C_ADDRESS_2,
+# ifdef IS31FL3736_I2C_ADDRESS_3
+ IS31FL3736_I2C_ADDRESS_3,
+# ifdef IS31FL3736_I2C_ADDRESS_4
+ IS31FL3736_I2C_ADDRESS_4,
+# endif
+# endif
+#endif
+};
// These buffers match the IS31FL3736 PWM registers.
-// The control buffers match the PG0 LED On/Off registers.
+// The control buffers match the page 0 LED On/Off registers.
// Storing them like this is optimal for I2C transfers to the registers.
// We could optimize this and take out the unused registers from these
// buffers and the transfers in is31fl3736_write_pwm_buffer() but it's
// probably not worth the extra complexity.
-uint8_t g_pwm_buffer[IS31FL3736_DRIVER_COUNT][IS31FL3736_PWM_REGISTER_COUNT];
-bool g_pwm_buffer_update_required[IS31FL3736_DRIVER_COUNT] = {false};
-
-uint8_t g_led_control_registers[IS31FL3736_DRIVER_COUNT][IS31FL3736_LED_CONTROL_REGISTER_COUNT] = {0};
-bool g_led_control_registers_update_required[IS31FL3736_DRIVER_COUNT] = {false};
-
-void is31fl3736_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
- g_twi_transfer_buffer[0] = reg;
- g_twi_transfer_buffer[1] = data;
-
+typedef struct is31fl3736_driver_t {
+ uint8_t pwm_buffer[IS31FL3736_PWM_REGISTER_COUNT];
+ bool pwm_buffer_dirty;
+ uint8_t led_control_buffer[IS31FL3736_LED_CONTROL_REGISTER_COUNT];
+ bool led_control_buffer_dirty;
+} PACKED is31fl3736_driver_t;
+
+is31fl3736_driver_t driver_buffers[IS31FL3736_DRIVER_COUNT] = {{
+ .pwm_buffer = {0},
+ .pwm_buffer_dirty = false,
+ .led_control_buffer = {0},
+ .led_control_buffer_dirty = false,
+}};
+
+void is31fl3736_write_register(uint8_t index, uint8_t reg, uint8_t data) {
#if IS31FL3736_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3736_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3736_I2C_TIMEOUT) == 0) break;
+ if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3736_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
}
#else
- i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3736_I2C_TIMEOUT);
+ i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3736_I2C_TIMEOUT);
#endif
}
-void is31fl3736_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
- // assumes PG1 is already selected
-
- // transmit PWM registers in 12 transfers of 16 bytes
- // g_twi_transfer_buffer[] is 20 bytes
+void is31fl3736_select_page(uint8_t index, uint8_t page) {
+ is31fl3736_write_register(index, IS31FL3736_REG_COMMAND_WRITE_LOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC);
+ is31fl3736_write_register(index, IS31FL3736_REG_COMMAND, page);
+}
- // iterate over the pwm_buffer contents at 16 byte intervals
- for (int i = 0; i < IS31FL3736_PWM_REGISTER_COUNT; i += 16) {
- g_twi_transfer_buffer[0] = i;
- // copy the data from i to i+15
- // device will auto-increment register for data after the first byte
- // thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer
- memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16);
+void is31fl3736_write_pwm_buffer(uint8_t index) {
+ // Assumes page 1 is already selected.
+ // Transmit PWM registers in 12 transfers of 16 bytes.
+ // Iterate over the pwm_buffer contents at 16 byte intervals.
+ for (uint8_t i = 0; i < IS31FL3736_PWM_REGISTER_COUNT; i += 16) {
#if IS31FL3736_I2C_PERSISTENCE > 0
- for (uint8_t i = 0; i < IS31FL3736_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3736_I2C_TIMEOUT) == 0) break;
+ for (uint8_t j = 0; j < IS31FL3736_I2C_PERSISTENCE; j++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3736_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
}
#else
- i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3736_I2C_TIMEOUT);
+ i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3736_I2C_TIMEOUT);
#endif
}
}
@@ -102,73 +114,55 @@ void is31fl3736_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
void is31fl3736_init_drivers(void) {
i2c_init();
- is31fl3736_init(IS31FL3736_I2C_ADDRESS_1);
-#if defined(IS31FL3736_I2C_ADDRESS_2)
- is31fl3736_init(IS31FL3736_I2C_ADDRESS_2);
-# if defined(IS31FL3736_I2C_ADDRESS_3)
- is31fl3736_init(IS31FL3736_I2C_ADDRESS_3);
-# if defined(IS31FL3736_I2C_ADDRESS_4)
- is31fl3736_init(IS31FL3736_I2C_ADDRESS_4);
-# endif
-# endif
+#if defined(IS31FL3736_SDB_PIN)
+ gpio_set_pin_output(IS31FL3736_SDB_PIN);
+ gpio_write_pin_high(IS31FL3736_SDB_PIN);
#endif
+ for (uint8_t i = 0; i < IS31FL3736_DRIVER_COUNT; i++) {
+ is31fl3736_init(i);
+ }
+
for (int i = 0; i < IS31FL3736_LED_COUNT; i++) {
is31fl3736_set_led_control_register(i, true, true, true);
}
- is31fl3736_update_led_control_registers(IS31FL3736_I2C_ADDRESS_1, 0);
-#if defined(IS31FL3736_I2C_ADDRESS_2)
- is31fl3736_update_led_control_registers(IS31FL3736_I2C_ADDRESS_2, 1);
-# if defined(IS31FL3736_I2C_ADDRESS_3)
- is31fl3736_update_led_control_registers(IS31FL3736_I2C_ADDRESS_3, 2);
-# if defined(IS31FL3736_I2C_ADDRESS_4)
- is31fl3736_update_led_control_registers(IS31FL3736_I2C_ADDRESS_4, 3);
-# endif
-# endif
-#endif
+ for (uint8_t i = 0; i < IS31FL3736_DRIVER_COUNT; i++) {
+ is31fl3736_update_led_control_registers(i);
+ }
}
-void is31fl3736_init(uint8_t addr) {
+void is31fl3736_init(uint8_t index) {
// In order to avoid the LEDs being driven with garbage data
// in the LED driver's PWM registers, shutdown is enabled last.
// Set up the mode and other settings, clear the PWM registers,
// then disable software shutdown.
- // Unlock the command register.
- is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND_WRITE_LOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC);
+ is31fl3736_select_page(index, IS31FL3736_COMMAND_LED_CONTROL);
- // Select PG0
- is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND, IS31FL3736_REG_LED_CONTROL);
// Turn off all LEDs.
- for (int i = 0; i < IS31FL3736_LED_CONTROL_REGISTER_COUNT; i++) {
- is31fl3736_write_register(addr, i, 0x00);
+ for (uint8_t i = 0; i < IS31FL3736_LED_CONTROL_REGISTER_COUNT; i++) {
+ is31fl3736_write_register(index, i, 0x00);
}
- // Unlock the command register.
- is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND_WRITELOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC);
+ is31fl3736_select_page(index, IS31FL3736_COMMAND_PWM);
- // Select PG1
- is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND, IS31FL3736_COMMAND_PWM);
// Set PWM on all LEDs to 0
// No need to setup Breath registers to PWM as that is the default.
- for (int i = 0; i < IS31FL3736_PWM_REGISTER_COUNT; i++) {
- is31fl3736_write_register(addr, i, 0x00);
+ for (uint8_t i = 0; i < IS31FL3736_PWM_REGISTER_COUNT; i++) {
+ is31fl3736_write_register(index, i, 0x00);
}
- // Unlock the command register.
- is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND_WRITE_LOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC);
+ is31fl3736_select_page(index, IS31FL3736_COMMAND_FUNCTION);
- // Select PG3
- is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND, IS31FL3736_COMMAND_FUNCTION);
// Set de-ghost pull-up resistors (SWx)
- is31fl3736_write_register(addr, IS31FL3736_FUNCTION_REG_SW_PULLUP, IS31FL3736_SW_PULLUP);
+ is31fl3736_write_register(index, IS31FL3736_FUNCTION_REG_SW_PULLUP, IS31FL3736_SW_PULLUP);
// Set de-ghost pull-down resistors (CSx)
- is31fl3736_write_register(addr, IS31FL3736_FUNCTION_REG_CS_PULLDOWN, IS31FL3736_CS_PULLDOWN);
+ is31fl3736_write_register(index, IS31FL3736_FUNCTION_REG_CS_PULLDOWN, IS31FL3736_CS_PULLDOWN);
// Set global current to maximum.
- is31fl3736_write_register(addr, IS31FL3736_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3736_GLOBAL_CURRENT);
+ is31fl3736_write_register(index, IS31FL3736_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3736_GLOBAL_CURRENT);
// Disable software shutdown.
- is31fl3736_write_register(addr, IS31FL3736_FUNCTION_REG_CONFIGURATION, ((IS31FL3736_PWM_FREQUENCY & 0b111) << 3) | 0x01);
+ is31fl3736_write_register(index, IS31FL3736_FUNCTION_REG_CONFIGURATION, ((IS31FL3736_PWM_FREQUENCY & 0b111) << 3) | 0x01);
// Wait 10ms to ensure the device has woken up.
wait_ms(10);
@@ -176,16 +170,18 @@ void is31fl3736_init(uint8_t addr) {
void is31fl3736_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
is31fl3736_led_t led;
+
if (index >= 0 && index < IS31FL3736_LED_COUNT) {
memcpy_P(&led, (&g_is31fl3736_leds[index]), sizeof(led));
- if (g_pwm_buffer[led.driver][led.r] == red && g_pwm_buffer[led.driver][led.g] == green && g_pwm_buffer[led.driver][led.b] == blue) {
+ if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) {
return;
}
- g_pwm_buffer[led.driver][led.r] = red;
- g_pwm_buffer[led.driver][led.g] = green;
- g_pwm_buffer[led.driver][led.b] = blue;
- g_pwm_buffer_update_required[led.driver] = true;
+
+ driver_buffers[led.driver].pwm_buffer[led.r] = red;
+ driver_buffers[led.driver].pwm_buffer[led.g] = green;
+ driver_buffers[led.driver].pwm_buffer[led.b] = blue;
+ driver_buffers[led.driver].pwm_buffer_dirty = true;
}
}
@@ -214,56 +210,48 @@ void is31fl3736_set_led_control_register(uint8_t index, bool red, bool green, bo
uint8_t bit_b = led.b % 8;
if (red) {
- g_led_control_registers[led.driver][control_register_r] |= (1 << bit_r);
+ driver_buffers[led.driver].led_control_buffer[control_register_r] |= (1 << bit_r);
} else {
- g_led_control_registers[led.driver][control_register_r] &= ~(1 << bit_r);
+ driver_buffers[led.driver].led_control_buffer[control_register_r] &= ~(1 << bit_r);
}
if (green) {
- g_led_control_registers[led.driver][control_register_g] |= (1 << bit_g);
+ driver_buffers[led.driver].led_control_buffer[control_register_g] |= (1 << bit_g);
} else {
- g_led_control_registers[led.driver][control_register_g] &= ~(1 << bit_g);
+ driver_buffers[led.driver].led_control_buffer[control_register_g] &= ~(1 << bit_g);
}
if (blue) {
- g_led_control_registers[led.driver][control_register_b] |= (1 << bit_b);
+ driver_buffers[led.driver].led_control_buffer[control_register_b] |= (1 << bit_b);
} else {
- g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b);
+ driver_buffers[led.driver].led_control_buffer[control_register_b] &= ~(1 << bit_b);
}
- g_led_control_registers_update_required[led.driver] = true;
+ driver_buffers[led.driver].led_control_buffer_dirty = true;
}
-void is31fl3736_update_pwm_buffers(uint8_t addr, uint8_t index) {
- if (g_pwm_buffer_update_required[index]) {
- // Firstly we need to unlock the command register and select PG1
- is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND_WRITE_LOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC);
- is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND, IS31FL3736_COMMAND_PWM);
+void is31fl3736_update_pwm_buffers(uint8_t index) {
+ if (driver_buffers[index].pwm_buffer_dirty) {
+ is31fl3736_select_page(index, IS31FL3736_COMMAND_PWM);
+
+ is31fl3736_write_pwm_buffer(index);
- is31fl3736_write_pwm_buffer(addr, g_pwm_buffer[index]);
- g_pwm_buffer_update_required[index] = false;
+ driver_buffers[index].pwm_buffer_dirty = false;
}
}
-void is31fl3736_update_led_control_registers(uint8_t addr, uint8_t index) {
- if (g_led_control_registers_update_required[index]) {
- // Firstly we need to unlock the command register and select PG0
- is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND_WRITE_LOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC);
- is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND, IS31FL3736_COMMAND_LED_CONTROL);
- for (int i = 0; i < IS31FL3736_LED_CONTROL_REGISTER_COUNT; i++) {
- is31fl3736_write_register(addr, i, g_led_control_registers[index][i]);
+void is31fl3736_update_led_control_registers(uint8_t index) {
+ if (driver_buffers[index].led_control_buffer_dirty) {
+ is31fl3736_select_page(index, IS31FL3736_COMMAND_LED_CONTROL);
+
+ for (uint8_t i = 0; i < IS31FL3736_LED_CONTROL_REGISTER_COUNT; i++) {
+ is31fl3736_write_register(index, i, driver_buffers[index].led_control_buffer[i]);
}
- g_led_control_registers_update_required[index] = false;
+
+ driver_buffers[index].led_control_buffer_dirty = false;
}
}
void is31fl3736_flush(void) {
- is31fl3736_update_pwm_buffers(IS31FL3736_I2C_ADDRESS_1, 0);
-#if defined(IS31FL3736_I2C_ADDRESS_2)
- is31fl3736_update_pwm_buffers(IS31FL3736_I2C_ADDRESS_2, 1);
-# if defined(IS31FL3736_I2C_ADDRESS_3)
- is31fl3736_update_pwm_buffers(IS31FL3736_I2C_ADDRESS_3, 2);
-# if defined(IS31FL3736_I2C_ADDRESS_4)
- is31fl3736_update_pwm_buffers(IS31FL3736_I2C_ADDRESS_4, 3);
-# endif
-# endif
-#endif
+ for (uint8_t i = 0; i < IS31FL3736_DRIVER_COUNT; i++) {
+ is31fl3736_update_pwm_buffers(i);
+ }
}
diff --git a/drivers/led/issi/is31fl3736.h b/drivers/led/issi/is31fl3736.h
index a5710d7ed4..dae7b3c812 100644
--- a/drivers/led/issi/is31fl3736.h
+++ b/drivers/led/issi/is31fl3736.h
@@ -124,9 +124,9 @@ typedef struct is31fl3736_led_t {
extern const is31fl3736_led_t PROGMEM g_is31fl3736_leds[IS31FL3736_LED_COUNT];
void is31fl3736_init_drivers(void);
-void is31fl3736_init(uint8_t addr);
-void is31fl3736_write_register(uint8_t addr, uint8_t reg, uint8_t data);
-void is31fl3736_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
+void is31fl3736_init(uint8_t index);
+void is31fl3736_write_register(uint8_t index, uint8_t reg, uint8_t data);
+void is31fl3736_select_page(uint8_t index, uint8_t page);
void is31fl3736_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
void is31fl3736_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
@@ -137,8 +137,8 @@ void is31fl3736_set_led_control_register(uint8_t index, bool red, bool green, bo
// (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 is31fl3736_update_pwm_buffers(uint8_t addr, uint8_t index);
-void is31fl3736_update_led_control_registers(uint8_t addr, uint8_t index);
+void is31fl3736_update_pwm_buffers(uint8_t index);
+void is31fl3736_update_led_control_registers(uint8_t index);
void is31fl3736_flush(void);
@@ -166,110 +166,110 @@ void is31fl3736_flush(void);
#define IS31FL3736_PWM_FREQUENCY_2K1_HZ 0b011
#define IS31FL3736_PWM_FREQUENCY_1K05_HZ 0b100
-#define A_1 0x00
-#define A_2 0x02
-#define A_3 0x04
-#define A_4 0x06
-#define A_5 0x08
-#define A_6 0x0A
-#define A_7 0x0C
-#define A_8 0x0E
-
-#define B_1 0x10
-#define B_2 0x12
-#define B_3 0x14
-#define B_4 0x16
-#define B_5 0x18
-#define B_6 0x1A
-#define B_7 0x1C
-#define B_8 0x1E
-
-#define C_1 0x20
-#define C_2 0x22
-#define C_3 0x24
-#define C_4 0x26
-#define C_5 0x28
-#define C_6 0x2A
-#define C_7 0x2C
-#define C_8 0x2E
-
-#define D_1 0x30
-#define D_2 0x32
-#define D_3 0x34
-#define D_4 0x36
-#define D_5 0x38
-#define D_6 0x3A
-#define D_7 0x3C
-#define D_8 0x3E
-
-#define E_1 0x40
-#define E_2 0x42
-#define E_3 0x44
-#define E_4 0x46
-#define E_5 0x48
-#define E_6 0x4A
-#define E_7 0x4C
-#define E_8 0x4E
-
-#define F_1 0x50
-#define F_2 0x52
-#define F_3 0x54
-#define F_4 0x56
-#define F_5 0x58
-#define F_6 0x5A
-#define F_7 0x5C
-#define F_8 0x5E
-
-#define G_1 0x60
-#define G_2 0x62
-#define G_3 0x64
-#define G_4 0x66
-#define G_5 0x68
-#define G_6 0x6A
-#define G_7 0x6C
-#define G_8 0x6E
-
-#define H_1 0x70
-#define H_2 0x72
-#define H_3 0x74
-#define H_4 0x76
-#define H_5 0x78
-#define H_6 0x7A
-#define H_7 0x7C
-#define H_8 0x7E
-
-#define I_1 0x80
-#define I_2 0x82
-#define I_3 0x84
-#define I_4 0x86
-#define I_5 0x88
-#define I_6 0x8A
-#define I_7 0x8C
-#define I_8 0x8E
-
-#define J_1 0x90
-#define J_2 0x92
-#define J_3 0x94
-#define J_4 0x96
-#define J_5 0x98
-#define J_6 0x9A
-#define J_7 0x9C
-#define J_8 0x9E
-
-#define K_1 0xA0
-#define K_2 0xA2
-#define K_3 0xA4
-#define K_4 0xA6
-#define K_5 0xA8
-#define K_6 0xAA
-#define K_7 0xAC
-#define K_8 0xAE
-
-#define L_1 0xB0
-#define L_2 0xB2
-#define L_3 0xB4
-#define L_4 0xB6
-#define L_5 0xB8
-#define L_6 0xBA
-#define L_7 0xBC
-#define L_8 0xBE
+#define SW1_CS1 0x00
+#define SW1_CS2 0x02
+#define SW1_CS3 0x04
+#define SW1_CS4 0x06
+#define SW1_CS5 0x08
+#define SW1_CS6 0x0A
+#define SW1_CS7 0x0C
+#define SW1_CS8 0x0E
+
+#define SW2_CS1 0x10
+#define SW2_CS2 0x12
+#define SW2_CS3 0x14
+#define SW2_CS4 0x16
+#define SW2_CS5 0x18
+#define SW2_CS6 0x1A
+#define SW2_CS7 0x1C
+#define SW2_CS8 0x1E
+
+#define SW3_CS1 0x20
+#define SW3_CS2 0x22
+#define SW3_CS3 0x24
+#define SW3_CS4 0x26
+#define SW3_CS5 0x28
+#define SW3_CS6 0x2A
+#define SW3_CS7 0x2C
+#define SW3_CS8 0x2E
+
+#define SW4_CS1 0x30
+#define SW4_CS2 0x32
+#define SW4_CS3 0x34
+#define SW4_CS4 0x36
+#define SW4_CS5 0x38
+#define SW4_CS6 0x3A
+#define SW4_CS7 0x3C
+#define SW4_CS8 0x3E
+
+#define SW5_CS1 0x40
+#define SW5_CS2 0x42
+#define SW5_CS3 0x44
+#define SW5_CS4 0x46
+#define SW5_CS5 0x48
+#define SW5_CS6 0x4A
+#define SW5_CS7 0x4C
+#define SW5_CS8 0x4E
+
+#define SW6_CS1 0x50
+#define SW6_CS2 0x52
+#define SW6_CS3 0x54
+#define SW6_CS4 0x56
+#define SW6_CS5 0x58
+#define SW6_CS6 0x5A
+#define SW6_CS7 0x5C
+#define SW6_CS8 0x5E
+
+#define SW7_CS1 0x60
+#define SW7_CS2 0x62
+#define SW7_CS3 0x64
+#define SW7_CS4 0x66
+#define SW7_CS5 0x68
+#define SW7_CS6 0x6A
+#define SW7_CS7 0x6C
+#define SW7_CS8 0x6E
+
+#define SW8_CS1 0x70
+#define SW8_CS2 0x72
+#define SW8_CS3 0x74
+#define SW8_CS4 0x76
+#define SW8_CS5 0x78
+#define SW8_CS6 0x7A
+#define SW8_CS7 0x7C
+#define SW8_CS8 0x7E
+
+#define SW9_CS1 0x80
+#define SW9_CS2 0x82
+#define SW9_CS3 0x84
+#define SW9_CS4 0x86
+#define SW9_CS5 0x88
+#define SW9_CS6 0x8A
+#define SW9_CS7 0x8C
+#define SW9_CS8 0x8E
+
+#define SW10_CS1 0x90
+#define SW10_CS2 0x92
+#define SW10_CS3 0x94
+#define SW10_CS4 0x96
+#define SW10_CS5 0x98
+#define SW10_CS6 0x9A
+#define SW10_CS7 0x9C
+#define SW10_CS8 0x9E
+
+#define SW11_CS1 0xA0
+#define SW11_CS2 0xA2
+#define SW11_CS3 0xA4
+#define SW11_CS4 0xA6
+#define SW11_CS5 0xA8
+#define SW11_CS6 0xAA
+#define SW11_CS7 0xAC
+#define SW11_CS8 0xAE
+
+#define SW12_CS1 0xB0
+#define SW12_CS2 0xB2
+#define SW12_CS3 0xB4
+#define SW12_CS4 0xB6
+#define SW12_CS5 0xB8
+#define SW12_CS6 0xBA
+#define SW12_CS7 0xBC
+#define SW12_CS8 0xBE
diff --git a/drivers/led/issi/is31fl3737-mono.c b/drivers/led/issi/is31fl3737-mono.c
new file mode 100644
index 0000000000..7b2e5a3576
--- /dev/null
+++ b/drivers/led/issi/is31fl3737-mono.c
@@ -0,0 +1,236 @@
+/* Copyright 2017 Jason Williams
+ * Copyright 2018 Jack Humbert
+ * Copyright 2018 Yiancar
+ * Copyright 2021 Doni Crosby
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "is31fl3737-mono.h"
+#include "i2c_master.h"
+#include "gpio.h"
+#include "wait.h"
+
+#define IS31FL3737_PWM_REGISTER_COUNT 192 // actually 144
+#define IS31FL3737_LED_CONTROL_REGISTER_COUNT 24
+
+#ifndef IS31FL3737_I2C_TIMEOUT
+# define IS31FL3737_I2C_TIMEOUT 100
+#endif
+
+#ifndef IS31FL3737_I2C_PERSISTENCE
+# define IS31FL3737_I2C_PERSISTENCE 0
+#endif
+
+#ifndef IS31FL3737_PWM_FREQUENCY
+# define IS31FL3737_PWM_FREQUENCY IS31FL3737_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3737B only
+#endif
+
+#ifndef IS31FL3737_SW_PULLUP
+# define IS31FL3737_SW_PULLUP IS31FL3737_PUR_0_OHM
+#endif
+
+#ifndef IS31FL3737_CS_PULLDOWN
+# define IS31FL3737_CS_PULLDOWN IS31FL3737_PDR_0_OHM
+#endif
+
+#ifndef IS31FL3737_GLOBAL_CURRENT
+# define IS31FL3737_GLOBAL_CURRENT 0xFF
+#endif
+
+const uint8_t i2c_addresses[IS31FL3737_DRIVER_COUNT] = {
+ IS31FL3737_I2C_ADDRESS_1,
+#ifdef IS31FL3737_I2C_ADDRESS_2
+ IS31FL3737_I2C_ADDRESS_2,
+# ifdef IS31FL3737_I2C_ADDRESS_3
+ IS31FL3737_I2C_ADDRESS_3,
+# ifdef IS31FL3737_I2C_ADDRESS_4
+ IS31FL3737_I2C_ADDRESS_4,
+# endif
+# endif
+#endif
+};
+
+// These buffers match the IS31FL3737 PWM registers.
+// The control buffers match the page 0 LED On/Off registers.
+// Storing them like this is optimal for I2C transfers to the registers.
+// We could optimize this and take out the unused registers from these
+// buffers and the transfers in is31fl3737_write_pwm_buffer() but it's
+// probably not worth the extra complexity.
+typedef struct is31fl3737_driver_t {
+ uint8_t pwm_buffer[IS31FL3737_PWM_REGISTER_COUNT];
+ bool pwm_buffer_dirty;
+ uint8_t led_control_buffer[IS31FL3737_LED_CONTROL_REGISTER_COUNT];
+ bool led_control_buffer_dirty;
+} PACKED is31fl3737_driver_t;
+
+is31fl3737_driver_t driver_buffers[IS31FL3737_DRIVER_COUNT] = {{
+ .pwm_buffer = {0},
+ .pwm_buffer_dirty = false,
+ .led_control_buffer = {0},
+ .led_control_buffer_dirty = false,
+}};
+
+void is31fl3737_write_register(uint8_t index, uint8_t reg, uint8_t data) {
+#if IS31FL3737_I2C_PERSISTENCE > 0
+ for (uint8_t i = 0; i < IS31FL3737_I2C_PERSISTENCE; i++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3737_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3737_I2C_TIMEOUT);
+#endif
+}
+
+void is31fl3737_select_page(uint8_t index, uint8_t page) {
+ is31fl3737_write_register(index, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC);
+ is31fl3737_write_register(index, IS31FL3737_REG_COMMAND, page);
+}
+
+void is31fl3737_write_pwm_buffer(uint8_t index) {
+ // Assumes page 1 is already selected.
+ // Transmit PWM registers in 12 transfers of 16 bytes.
+
+ // Iterate over the pwm_buffer contents at 16 byte intervals.
+ for (uint8_t i = 0; i < IS31FL3737_PWM_REGISTER_COUNT; i += 16) {
+#if IS31FL3737_I2C_PERSISTENCE > 0
+ for (uint8_t j = 0; j < IS31FL3737_I2C_PERSISTENCE; j++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3737_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3737_I2C_TIMEOUT);
+#endif
+ }
+}
+
+void is31fl3737_init_drivers(void) {
+ i2c_init();
+
+#if defined(IS31FL3737_SDB_PIN)
+ gpio_set_pin_output(IS31FL3737_SDB_PIN);
+ gpio_write_pin_high(IS31FL3737_SDB_PIN);
+#endif
+
+ for (uint8_t i = 0; i < IS31FL3737_DRIVER_COUNT; i++) {
+ is31fl3737_init(i);
+ }
+
+ for (int i = 0; i < IS31FL3737_LED_COUNT; i++) {
+ is31fl3737_set_led_control_register(i, true);
+ }
+
+ for (uint8_t i = 0; i < IS31FL3737_DRIVER_COUNT; i++) {
+ is31fl3737_update_led_control_registers(i);
+ }
+}
+
+void is31fl3737_init(uint8_t index) {
+ // In order to avoid the LEDs being driven with garbage data
+ // in the LED driver's PWM registers, shutdown is enabled last.
+ // Set up the mode and other settings, clear the PWM registers,
+ // then disable software shutdown.
+
+ is31fl3737_select_page(index, IS31FL3737_COMMAND_LED_CONTROL);
+
+ // Turn off all LEDs.
+ for (uint8_t i = 0; i < IS31FL3737_LED_CONTROL_REGISTER_COUNT; i++) {
+ is31fl3737_write_register(index, i, 0x00);
+ }
+
+ is31fl3737_select_page(index, IS31FL3737_COMMAND_PWM);
+
+ // Set PWM on all LEDs to 0
+ // No need to setup Breath registers to PWM as that is the default.
+ for (uint8_t i = 0; i < IS31FL3737_PWM_REGISTER_COUNT; i++) {
+ is31fl3737_write_register(index, i, 0x00);
+ }
+
+ is31fl3737_select_page(index, IS31FL3737_COMMAND_FUNCTION);
+
+ // Set de-ghost pull-up resistors (SWx)
+ is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_SW_PULLUP, IS31FL3737_SW_PULLUP);
+ // Set de-ghost pull-down resistors (CSx)
+ is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_CS_PULLDOWN, IS31FL3737_CS_PULLDOWN);
+ // Set global current to maximum.
+ is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3737_GLOBAL_CURRENT);
+ // Disable software shutdown.
+ is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_CONFIGURATION, ((IS31FL3737_PWM_FREQUENCY & 0b111) << 3) | 0x01);
+
+ // Wait 10ms to ensure the device has woken up.
+ wait_ms(10);
+}
+
+void is31fl3737_set_value(int index, uint8_t value) {
+ is31fl3737_led_t led;
+
+ if (index >= 0 && index < IS31FL3737_LED_COUNT) {
+ memcpy_P(&led, (&g_is31fl3737_leds[index]), sizeof(led));
+
+ if (driver_buffers[led.driver].pwm_buffer[led.v] == value) {
+ return;
+ }
+
+ driver_buffers[led.driver].pwm_buffer[led.v] = value;
+ driver_buffers[led.driver].pwm_buffer_dirty = true;
+ }
+}
+
+void is31fl3737_set_value_all(uint8_t value) {
+ for (int i = 0; i < IS31FL3737_LED_COUNT; i++) {
+ is31fl3737_set_value(i, value);
+ }
+}
+
+void is31fl3737_set_led_control_register(uint8_t index, bool value) {
+ is31fl3737_led_t led;
+ memcpy_P(&led, (&g_is31fl3737_leds[index]), sizeof(led));
+
+ uint8_t control_register = led.v / 8;
+ uint8_t bit_value = led.v % 8;
+
+ if (value) {
+ driver_buffers[led.driver].led_control_buffer[control_register] |= (1 << bit_value);
+ } else {
+ driver_buffers[led.driver].led_control_buffer[control_register] &= ~(1 << bit_value);
+ }
+
+ driver_buffers[led.driver].led_control_buffer_dirty = true;
+}
+
+void is31fl3737_update_pwm_buffers(uint8_t index) {
+ if (driver_buffers[index].pwm_buffer_dirty) {
+ is31fl3737_select_page(index, IS31FL3737_COMMAND_PWM);
+
+ is31fl3737_write_pwm_buffer(index);
+
+ driver_buffers[index].pwm_buffer_dirty = false;
+ }
+}
+
+void is31fl3737_update_led_control_registers(uint8_t index) {
+ if (driver_buffers[index].led_control_buffer_dirty) {
+ is31fl3737_select_page(index, IS31FL3737_COMMAND_LED_CONTROL);
+
+ for (uint8_t i = 0; i < IS31FL3737_LED_CONTROL_REGISTER_COUNT; i++) {
+ is31fl3737_write_register(index, i, driver_buffers[index].led_control_buffer[i]);
+ }
+
+ driver_buffers[index].led_control_buffer_dirty = false;
+ }
+}
+
+void is31fl3737_flush(void) {
+ for (uint8_t i = 0; i < IS31FL3737_DRIVER_COUNT; i++) {
+ is31fl3737_update_pwm_buffers(i);
+ }
+}
diff --git a/drivers/led/issi/is31fl3737-simple.h b/drivers/led/issi/is31fl3737-mono.h
index 2658702b1b..99151c1e7a 100644
--- a/drivers/led/issi/is31fl3737-simple.h
+++ b/drivers/led/issi/is31fl3737-mono.h
@@ -100,9 +100,9 @@ typedef struct is31fl3737_led_t {
extern const is31fl3737_led_t PROGMEM g_is31fl3737_leds[IS31FL3737_LED_COUNT];
void is31fl3737_init_drivers(void);
-void is31fl3737_init(uint8_t addr);
-void is31fl3737_write_register(uint8_t addr, uint8_t reg, uint8_t data);
-void is31fl3737_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
+void is31fl3737_init(uint8_t index);
+void is31fl3737_write_register(uint8_t index, uint8_t reg, uint8_t data);
+void is31fl3737_select_page(uint8_t index, uint8_t page);
void is31fl3737_set_value(int index, uint8_t value);
void is31fl3737_set_value_all(uint8_t value);
@@ -113,8 +113,8 @@ void is31fl3737_set_led_control_register(uint8_t index, bool value);
// (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 is31fl3737_update_pwm_buffers(uint8_t addr, uint8_t index);
-void is31fl3737_update_led_control_registers(uint8_t addr, uint8_t index);
+void is31fl3737_update_pwm_buffers(uint8_t index);
+void is31fl3737_update_led_control_registers(uint8_t index);
void is31fl3737_flush(void);
@@ -142,158 +142,158 @@ void is31fl3737_flush(void);
#define IS31FL3737_PWM_FREQUENCY_2K1_HZ 0b011
#define IS31FL3737_PWM_FREQUENCY_1K05_HZ 0b100
-#define A_1 0x00
-#define A_2 0x01
-#define A_3 0x02
-#define A_4 0x03
-#define A_5 0x04
-#define A_6 0x05
-#define A_7 0x08
-#define A_8 0x09
-#define A_9 0x0A
-#define A_10 0x0B
-#define A_11 0x0C
-#define A_12 0x0D
-
-#define B_1 0x10
-#define B_2 0x11
-#define B_3 0x12
-#define B_4 0x13
-#define B_5 0x14
-#define B_6 0x15
-#define B_7 0x18
-#define B_8 0x19
-#define B_9 0x1A
-#define B_10 0x1B
-#define B_11 0x1C
-#define B_12 0x1D
-
-#define C_1 0x20
-#define C_2 0x21
-#define C_3 0x22
-#define C_4 0x23
-#define C_5 0x24
-#define C_6 0x25
-#define C_7 0x28
-#define C_8 0x29
-#define C_9 0x2A
-#define C_10 0x2B
-#define C_11 0x2C
-#define C_12 0x2D
-
-#define D_1 0x30
-#define D_2 0x31
-#define D_3 0x32
-#define D_4 0x33
-#define D_5 0x34
-#define D_6 0x35
-#define D_7 0x38
-#define D_8 0x39
-#define D_9 0x3A
-#define D_10 0x3B
-#define D_11 0x3C
-#define D_12 0x3D
-
-#define E_1 0x40
-#define E_2 0x41
-#define E_3 0x42
-#define E_4 0x43
-#define E_5 0x44
-#define E_6 0x45
-#define E_7 0x48
-#define E_8 0x49
-#define E_9 0x4A
-#define E_10 0x4B
-#define E_11 0x4C
-#define E_12 0x4D
-
-#define F_1 0x50
-#define F_2 0x51
-#define F_3 0x52
-#define F_4 0x53
-#define F_5 0x54
-#define F_6 0x55
-#define F_7 0x58
-#define F_8 0x59
-#define F_9 0x5A
-#define F_10 0x5B
-#define F_11 0x5C
-#define F_12 0x5D
-
-#define G_1 0x60
-#define G_2 0x61
-#define G_3 0x62
-#define G_4 0x63
-#define G_5 0x64
-#define G_6 0x65
-#define G_7 0x68
-#define G_8 0x69
-#define G_9 0x6A
-#define G_10 0x6B
-#define G_11 0x6C
-#define G_12 0x6D
-
-#define H_1 0x70
-#define H_2 0x71
-#define H_3 0x72
-#define H_4 0x73
-#define H_5 0x74
-#define H_6 0x75
-#define H_7 0x78
-#define H_8 0x79
-#define H_9 0x7A
-#define H_10 0x7B
-#define H_11 0x7C
-#define H_12 0x7D
-
-#define I_1 0x80
-#define I_2 0x81
-#define I_3 0x82
-#define I_4 0x83
-#define I_5 0x84
-#define I_6 0x85
-#define I_7 0x88
-#define I_8 0x89
-#define I_9 0x8A
-#define I_10 0x8B
-#define I_11 0x8C
-#define I_12 0x8D
-
-#define J_1 0x90
-#define J_2 0x91
-#define J_3 0x92
-#define J_4 0x93
-#define J_5 0x94
-#define J_6 0x95
-#define J_7 0x98
-#define J_8 0x99
-#define J_9 0x9A
-#define J_10 0x9B
-#define J_11 0x9C
-#define J_12 0x9D
-
-#define K_1 0xA0
-#define K_2 0xA1
-#define K_3 0xA2
-#define K_4 0xA3
-#define K_5 0xA4
-#define K_6 0xA5
-#define K_7 0xA8
-#define K_8 0xA9
-#define K_9 0xAA
-#define K_10 0xAB
-#define K_11 0xAC
-#define K_12 0xAD
-
-#define L_1 0xB0
-#define L_2 0xB1
-#define L_3 0xB2
-#define L_4 0xB3
-#define L_5 0xB4
-#define L_6 0xB5
-#define L_7 0xB8
-#define L_8 0xB9
-#define L_9 0xBA
-#define L_10 0xBB
-#define L_11 0xBC
-#define L_12 0xBD
+#define SW1_CS1 0x00
+#define SW1_CS2 0x01
+#define SW1_CS3 0x02
+#define SW1_CS4 0x03
+#define SW1_CS5 0x04
+#define SW1_CS6 0x05
+#define SW1_CS7 0x08
+#define SW1_CS8 0x09
+#define SW1_CS9 0x0A
+#define SW1_CS10 0x0B
+#define SW1_CS11 0x0C
+#define SW1_CS12 0x0D
+
+#define SW2_CS1 0x10
+#define SW2_CS2 0x11
+#define SW2_CS3 0x12
+#define SW2_CS4 0x13
+#define SW2_CS5 0x14
+#define SW2_CS6 0x15
+#define SW2_CS7 0x18
+#define SW2_CS8 0x19
+#define SW2_CS9 0x1A
+#define SW2_CS10 0x1B
+#define SW2_CS11 0x1C
+#define SW2_CS12 0x1D
+
+#define SW3_CS1 0x20
+#define SW3_CS2 0x21
+#define SW3_CS3 0x22
+#define SW3_CS4 0x23
+#define SW3_CS5 0x24
+#define SW3_CS6 0x25
+#define SW3_CS7 0x28
+#define SW3_CS8 0x29
+#define SW3_CS9 0x2A
+#define SW3_CS10 0x2B
+#define SW3_CS11 0x2C
+#define SW3_CS12 0x2D
+
+#define SW4_CS1 0x30
+#define SW4_CS2 0x31
+#define SW4_CS3 0x32
+#define SW4_CS4 0x33
+#define SW4_CS5 0x34
+#define SW4_CS6 0x35
+#define SW4_CS7 0x38
+#define SW4_CS8 0x39
+#define SW4_CS9 0x3A
+#define SW4_CS10 0x3B
+#define SW4_CS11 0x3C
+#define SW4_CS12 0x3D
+
+#define SW5_CS1 0x40
+#define SW5_CS2 0x41
+#define SW5_CS3 0x42
+#define SW5_CS4 0x43
+#define SW5_CS5 0x44
+#define SW5_CS6 0x45
+#define SW5_CS7 0x48
+#define SW5_CS8 0x49
+#define SW5_CS9 0x4A
+#define SW5_CS10 0x4B
+#define SW5_CS11 0x4C
+#define SW5_CS12 0x4D
+
+#define SW6_CS1 0x50
+#define SW6_CS2 0x51
+#define SW6_CS3 0x52
+#define SW6_CS4 0x53
+#define SW6_CS5 0x54
+#define SW6_CS6 0x55
+#define SW6_CS7 0x58
+#define SW6_CS8 0x59
+#define SW6_CS9 0x5A
+#define SW6_CS10 0x5B
+#define SW6_CS11 0x5C
+#define SW6_CS12 0x5D
+
+#define SW7_CS1 0x60
+#define SW7_CS2 0x61
+#define SW7_CS3 0x62
+#define SW7_CS4 0x63
+#define SW7_CS5 0x64
+#define SW7_CS6 0x65
+#define SW7_CS7 0x68
+#define SW7_CS8 0x69
+#define SW7_CS9 0x6A
+#define SW7_CS10 0x6B
+#define SW7_CS11 0x6C
+#define SW7_CS12 0x6D
+
+#define SW8_CS1 0x70
+#define SW8_CS2 0x71
+#define SW8_CS3 0x72
+#define SW8_CS4 0x73
+#define SW8_CS5 0x74
+#define SW8_CS6 0x75
+#define SW8_CS7 0x78
+#define SW8_CS8 0x79
+#define SW8_CS9 0x7A
+#define SW8_CS10 0x7B
+#define SW8_CS11 0x7C
+#define SW8_CS12 0x7D
+
+#define SW9_CS1 0x80
+#define SW9_CS2 0x81
+#define SW9_CS3 0x82
+#define SW9_CS4 0x83
+#define SW9_CS5 0x84
+#define SW9_CS6 0x85
+#define SW9_CS7 0x88
+#define SW9_CS8 0x89
+#define SW9_CS9 0x8A
+#define SW9_CS10 0x8B
+#define SW9_CS11 0x8C
+#define SW9_CS12 0x8D
+
+#define SW10_CS1 0x90
+#define SW10_CS2 0x91
+#define SW10_CS3 0x92
+#define SW10_CS4 0x93
+#define SW10_CS5 0x94
+#define SW10_CS6 0x95
+#define SW10_CS7 0x98
+#define SW10_CS8 0x99
+#define SW10_CS9 0x9A
+#define SW10_CS10 0x9B
+#define SW10_CS11 0x9C
+#define SW10_CS12 0x9D
+
+#define SW11_CS1 0xA0
+#define SW11_CS2 0xA1
+#define SW11_CS3 0xA2
+#define SW11_CS4 0xA3
+#define SW11_CS5 0xA4
+#define SW11_CS6 0xA5
+#define SW11_CS7 0xA8
+#define SW11_CS8 0xA9
+#define SW11_CS9 0xAA
+#define SW11_CS10 0xAB
+#define SW11_CS11 0xAC
+#define SW11_CS12 0xAD
+
+#define SW12_CS1 0xB0
+#define SW12_CS2 0xB1
+#define SW12_CS3 0xB2
+#define SW12_CS4 0xB3
+#define SW12_CS5 0xB4
+#define SW12_CS6 0xB5
+#define SW12_CS7 0xB8
+#define SW12_CS8 0xB9
+#define SW12_CS9 0xBA
+#define SW12_CS10 0xBB
+#define SW12_CS11 0xBC
+#define SW12_CS12 0xBD
diff --git a/drivers/led/issi/is31fl3737-simple.c b/drivers/led/issi/is31fl3737-simple.c
deleted file mode 100644
index 7f641f4ca5..0000000000
--- a/drivers/led/issi/is31fl3737-simple.c
+++ /dev/null
@@ -1,249 +0,0 @@
-/* Copyright 2017 Jason Williams
- * Copyright 2018 Jack Humbert
- * Copyright 2018 Yiancar
- * Copyright 2021 Doni Crosby
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#include "is31fl3737-simple.h"
-#include <string.h>
-#include "i2c_master.h"
-#include "wait.h"
-
-#define IS31FL3737_PWM_REGISTER_COUNT 192 // actually 144
-#define IS31FL3737_LED_CONTROL_REGISTER_COUNT 24
-
-#ifndef IS31FL3737_I2C_TIMEOUT
-# define IS31FL3737_I2C_TIMEOUT 100
-#endif
-
-#ifndef IS31FL3737_I2C_PERSISTENCE
-# define IS31FL3737_I2C_PERSISTENCE 0
-#endif
-
-#ifndef IS31FL3737_PWM_FREQUENCY
-# define IS31FL3737_PWM_FREQUENCY IS31FL3737_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3737B only
-#endif
-
-#ifndef IS31FL3737_SW_PULLUP
-# define IS31FL3737_SW_PULLUP IS31FL3737_PUR_0_OHM
-#endif
-
-#ifndef IS31FL3737_CS_PULLDOWN
-# define IS31FL3737_CS_PULLDOWN IS31FL3737_PDR_0_OHM
-#endif
-
-#ifndef IS31FL3737_GLOBAL_CURRENT
-# define IS31FL3737_GLOBAL_CURRENT 0xFF
-#endif
-
-// Transfer buffer for TWITransmitData()
-uint8_t g_twi_transfer_buffer[20];
-
-// These buffers match the IS31FL3737 PWM registers.
-// The control buffers match the PG0 LED On/Off registers.
-// Storing them like this is optimal for I2C transfers to the registers.
-// We could optimize this and take out the unused registers from these
-// buffers and the transfers in is31fl3737_write_pwm_buffer() but it's
-// probably not worth the extra complexity.
-
-uint8_t g_pwm_buffer[IS31FL3737_DRIVER_COUNT][IS31FL3737_PWM_REGISTER_COUNT];
-bool g_pwm_buffer_update_required[IS31FL3737_DRIVER_COUNT] = {false};
-
-uint8_t g_led_control_registers[IS31FL3737_DRIVER_COUNT][IS31FL3737_LED_CONTROL_REGISTER_COUNT] = {0};
-bool g_led_control_registers_update_required[IS31FL3737_DRIVER_COUNT] = {false};
-
-void is31fl3737_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
- g_twi_transfer_buffer[0] = reg;
- g_twi_transfer_buffer[1] = data;
-
-#if IS31FL3737_I2C_PERSISTENCE > 0
- for (uint8_t i = 0; i < IS31FL3737_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3737_I2C_TIMEOUT) == 0) break;
- }
-#else
- i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3737_I2C_TIMEOUT);
-#endif
-}
-
-void is31fl3737_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
- // assumes PG1 is already selected
-
- // transmit PWM registers in 12 transfers of 16 bytes
- // g_twi_transfer_buffer[] is 20 bytes
-
- // iterate over the pwm_buffer contents at 16 byte intervals
- for (int i = 0; i < IS31FL3737_PWM_REGISTER_COUNT; i += 16) {
- g_twi_transfer_buffer[0] = i;
- // copy the data from i to i+15
- // device will auto-increment register for data after the first byte
- // thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer
- memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16);
-
-#if IS31FL3737_I2C_PERSISTENCE > 0
- for (uint8_t i = 0; i < IS31FL3737_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3737_I2C_TIMEOUT) == 0) break;
- }
-#else
- i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3737_I2C_TIMEOUT);
-#endif
- }
-}
-
-void is31fl3737_init_drivers(void) {
- i2c_init();
-
- is31fl3737_init(IS31FL3737_I2C_ADDRESS_1);
-#if defined(IS31FL3737_I2C_ADDRESS_2)
- is31fl3737_init(IS31FL3737_I2C_ADDRESS_2);
-# if defined(IS31FL3737_I2C_ADDRESS_3)
- is31fl3737_init(IS31FL3737_I2C_ADDRESS_3);
-# if defined(IS31FL3737_I2C_ADDRESS_4)
- is31fl3737_init(IS31FL3737_I2C_ADDRESS_4);
-# endif
-# endif
-#endif
-
- for (int i = 0; i < IS31FL3737_LED_COUNT; i++) {
- is31fl3737_set_led_control_register(i, true);
- }
-
- is31fl3737_update_led_control_registers(IS31FL3737_I2C_ADDRESS_1, 0);
-#if defined(IS31FL3737_I2C_ADDRESS_2)
- is31fl3737_update_led_control_registers(IS31FL3737_I2C_ADDRESS_2, 1);
-# if defined(IS31FL3737_I2C_ADDRESS_3)
- is31fl3737_update_led_control_registers(IS31FL3737_I2C_ADDRESS_3, 2);
-# if defined(IS31FL3737_I2C_ADDRESS_4)
- is31fl3737_update_led_control_registers(IS31FL3737_I2C_ADDRESS_4, 3);
-# endif
-# endif
-#endif
-}
-
-void is31fl3737_init(uint8_t addr) {
- // In order to avoid the LEDs being driven with garbage data
- // in the LED driver's PWM registers, shutdown is enabled last.
- // Set up the mode and other settings, clear the PWM registers,
- // then disable software shutdown.
-
- // Unlock the command register.
- is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC);
-
- // Select PG0
- is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND, IS31FL3737_COMMAND_LED_CONTROL);
- // Turn off all LEDs.
- for (int i = 0; i < IS31FL3737_LED_CONTROL_REGISTER_COUNT; i++) {
- is31fl3737_write_register(addr, i, 0x00);
- }
-
- // Unlock the command register.
- is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC);
-
- // Select PG1
- is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND, IS31FL3737_COMMAND_PWM);
- // Set PWM on all LEDs to 0
- // No need to setup Breath registers to PWM as that is the default.
- for (int i = 0; i < IS31FL3737_PWM_REGISTER_COUNT; i++) {
- is31fl3737_write_register(addr, i, 0x00);
- }
-
- // Unlock the command register.
- is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC);
-
- // Select PG3
- is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND, IS31FL3737_COMMAND_FUNCTION);
- // Set de-ghost pull-up resistors (SWx)
- is31fl3737_write_register(addr, IS31FL3737_FUNCTION_REG_SW_PULLUP, IS31FL3737_SW_PULLUP);
- // Set de-ghost pull-down resistors (CSx)
- is31fl3737_write_register(addr, IS31FL3737_FUNCTION_REG_CS_PULLDOWN, IS31FL3737_CS_PULLDOWN);
- // Set global current to maximum.
- is31fl3737_write_register(addr, IS31FL3737_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3737_GLOBAL_CURRENT);
- // Disable software shutdown.
- is31fl3737_write_register(addr, IS31FL3737_FUNCTION_REG_CONFIGURATION, ((IS31FL3737_PWM_FREQUENCY & 0b111) << 3) | 0x01);
-
- // Wait 10ms to ensure the device has woken up.
- wait_ms(10);
-}
-
-void is31fl3737_set_value(int index, uint8_t value) {
- is31fl3737_led_t led;
- if (index >= 0 && index < IS31FL3737_LED_COUNT) {
- memcpy_P(&led, (&g_is31fl3737_leds[index]), sizeof(led));
-
- if (g_pwm_buffer[led.driver][led.v] == value) {
- return;
- }
- g_pwm_buffer[led.driver][led.v] = value;
- g_pwm_buffer_update_required[led.driver] = true;
- }
-}
-
-void is31fl3737_set_value_all(uint8_t value) {
- for (int i = 0; i < IS31FL3737_LED_COUNT; i++) {
- is31fl3737_set_value(i, value);
- }
-}
-
-void is31fl3737_set_led_control_register(uint8_t index, bool value) {
- is31fl3737_led_t led;
- memcpy_P(&led, (&g_is31fl3737_leds[index]), sizeof(led));
-
- uint8_t control_register = led.v / 8;
- uint8_t bit_value = led.v % 8;
-
- if (value) {
- g_led_control_registers[led.driver][control_register] |= (1 << bit_value);
- } else {
- g_led_control_registers[led.driver][control_register] &= ~(1 << bit_value);
- }
-
- g_led_control_registers_update_required[led.driver] = true;
-}
-
-void is31fl3737_update_pwm_buffers(uint8_t addr, uint8_t index) {
- if (g_pwm_buffer_update_required[index]) {
- // Firstly we need to unlock the command register and select PG1
- is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC);
- is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND, IS31FL3737_COMMAND_PWM);
-
- is31fl3737_write_pwm_buffer(addr, g_pwm_buffer[index]);
- g_pwm_buffer_update_required[index] = false;
- }
-}
-
-void is31fl3737_update_led_control_registers(uint8_t addr, uint8_t index) {
- if (g_led_control_registers_update_required[index]) {
- // Firstly we need to unlock the command register and select PG0
- is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC);
- is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND, IS31FL3737_COMMAND_LED_CONTROL);
- for (int i = 0; i < IS31FL3737_LED_CONTROL_REGISTER_COUNT; i++) {
- is31fl3737_write_register(addr, i, g_led_control_registers[index][i]);
- }
- g_led_control_registers_update_required[index] = false;
- }
-}
-
-void is31fl3737_flush(void) {
- is31fl3737_update_pwm_buffers(IS31FL3737_I2C_ADDRESS_1, 0);
-#if defined(IS31FL3737_I2C_ADDRESS_2)
- is31fl3737_update_pwm_buffers(IS31FL3737_I2C_ADDRESS_2, 1);
-# if defined(IS31FL3737_I2C_ADDRESS_3)
- is31fl3737_update_pwm_buffers(IS31FL3737_I2C_ADDRESS_3, 2);
-# if defined(IS31FL3737_I2C_ADDRESS_4)
- is31fl3737_update_pwm_buffers(IS31FL3737_I2C_ADDRESS_4, 3);
-# endif
-# endif
-#endif
-}
diff --git a/drivers/led/issi/is31fl3737.c b/drivers/led/issi/is31fl3737.c
index a458431952..b27a4cbb0f 100644
--- a/drivers/led/issi/is31fl3737.c
+++ b/drivers/led/issi/is31fl3737.c
@@ -18,8 +18,8 @@
*/
#include "is31fl3737.h"
-#include <string.h>
#include "i2c_master.h"
+#include "gpio.h"
#include "wait.h"
#define IS31FL3737_PWM_REGISTER_COUNT 192 // actually 144
@@ -41,7 +41,7 @@
# define IS31FL3737_SW_PULLUP IS31FL3737_PUR_0_OHM
#endif
-#ifndef IS31FL3737_CS_PULLDONW
+#ifndef IS31FL3737_CS_PULLDOWN
# define IS31FL3737_CS_PULLDOWN IS31FL3737_PDR_0_OHM
#endif
@@ -49,55 +49,66 @@
# define IS31FL3737_GLOBAL_CURRENT 0xFF
#endif
-// Transfer buffer for TWITransmitData()
-uint8_t g_twi_transfer_buffer[20];
+const uint8_t i2c_addresses[IS31FL3737_DRIVER_COUNT] = {
+ IS31FL3737_I2C_ADDRESS_1,
+#ifdef IS31FL3737_I2C_ADDRESS_2
+ IS31FL3737_I2C_ADDRESS_2,
+# ifdef IS31FL3737_I2C_ADDRESS_3
+ IS31FL3737_I2C_ADDRESS_3,
+# ifdef IS31FL3737_I2C_ADDRESS_4
+ IS31FL3737_I2C_ADDRESS_4,
+# endif
+# endif
+#endif
+};
// These buffers match the IS31FL3737 PWM registers.
-// The control buffers match the PG0 LED On/Off registers.
+// The control buffers match the page 0 LED On/Off registers.
// Storing them like this is optimal for I2C transfers to the registers.
// We could optimize this and take out the unused registers from these
// buffers and the transfers in is31fl3737_write_pwm_buffer() but it's
// probably not worth the extra complexity.
-
-uint8_t g_pwm_buffer[IS31FL3737_DRIVER_COUNT][IS31FL3737_PWM_REGISTER_COUNT];
-bool g_pwm_buffer_update_required[IS31FL3737_DRIVER_COUNT] = {false};
-
-uint8_t g_led_control_registers[IS31FL3737_DRIVER_COUNT][IS31FL3737_LED_CONTROL_REGISTER_COUNT] = {0};
-bool g_led_control_registers_update_required[IS31FL3737_DRIVER_COUNT] = {false};
-
-void is31fl3737_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
- g_twi_transfer_buffer[0] = reg;
- g_twi_transfer_buffer[1] = data;
-
+typedef struct is31fl3737_driver_t {
+ uint8_t pwm_buffer[IS31FL3737_PWM_REGISTER_COUNT];
+ bool pwm_buffer_dirty;
+ uint8_t led_control_buffer[IS31FL3737_LED_CONTROL_REGISTER_COUNT];
+ bool led_control_buffer_dirty;
+} PACKED is31fl3737_driver_t;
+
+is31fl3737_driver_t driver_buffers[IS31FL3737_DRIVER_COUNT] = {{
+ .pwm_buffer = {0},
+ .pwm_buffer_dirty = false,
+ .led_control_buffer = {0},
+ .led_control_buffer_dirty = false,
+}};
+
+void is31fl3737_write_register(uint8_t index, uint8_t reg, uint8_t data) {
#if IS31FL3737_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3737_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3737_I2C_TIMEOUT) == 0) break;
+ if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3737_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
}
#else
- i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3737_I2C_TIMEOUT);
+ i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3737_I2C_TIMEOUT);
#endif
}
-void is31fl3737_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
- // assumes PG1 is already selected
-
- // transmit PWM registers in 12 transfers of 16 bytes
- // g_twi_transfer_buffer[] is 20 bytes
+void is31fl3737_select_page(uint8_t index, uint8_t page) {
+ is31fl3737_write_register(index, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC);
+ is31fl3737_write_register(index, IS31FL3737_REG_COMMAND, page);
+}
- // iterate over the pwm_buffer contents at 16 byte intervals
- for (int i = 0; i < IS31FL3737_PWM_REGISTER_COUNT; i += 16) {
- g_twi_transfer_buffer[0] = i;
- // copy the data from i to i+15
- // device will auto-increment register for data after the first byte
- // thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer
- memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16);
+void is31fl3737_write_pwm_buffer(uint8_t index) {
+ // Assumes page 1 is already selected.
+ // Transmit PWM registers in 12 transfers of 16 bytes.
+ // Iterate over the pwm_buffer contents at 16 byte intervals.
+ for (uint8_t i = 0; i < IS31FL3737_PWM_REGISTER_COUNT; i += 16) {
#if IS31FL3737_I2C_PERSISTENCE > 0
- for (uint8_t i = 0; i < IS31FL3737_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3737_I2C_TIMEOUT) == 0) break;
+ for (uint8_t j = 0; j < IS31FL3737_I2C_PERSISTENCE; j++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3737_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
}
#else
- i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3737_I2C_TIMEOUT);
+ i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3737_I2C_TIMEOUT);
#endif
}
}
@@ -105,73 +116,55 @@ void is31fl3737_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
void is31fl3737_init_drivers(void) {
i2c_init();
- is31fl3737_init(IS31FL3737_I2C_ADDRESS_1);
-#if defined(IS31FL3737_I2C_ADDRESS_2)
- is31fl3737_init(IS31FL3737_I2C_ADDRESS_2);
-# if defined(IS31FL3737_I2C_ADDRESS_3)
- is31fl3737_init(IS31FL3737_I2C_ADDRESS_3);
-# if defined(IS31FL3737_I2C_ADDRESS_4)
- is31fl3737_init(IS31FL3737_I2C_ADDRESS_4);
-# endif
-# endif
+#if defined(IS31FL3737_SDB_PIN)
+ gpio_set_pin_output(IS31FL3737_SDB_PIN);
+ gpio_write_pin_high(IS31FL3737_SDB_PIN);
#endif
+ for (uint8_t i = 0; i < IS31FL3737_DRIVER_COUNT; i++) {
+ is31fl3737_init(i);
+ }
+
for (int i = 0; i < IS31FL3737_LED_COUNT; i++) {
is31fl3737_set_led_control_register(i, true, true, true);
}
- is31fl3737_update_led_control_registers(IS31FL3737_I2C_ADDRESS_1, 0);
-#if defined(IS31FL3737_I2C_ADDRESS_2)
- is31fl3737_update_led_control_registers(IS31FL3737_I2C_ADDRESS_2, 1);
-# if defined(IS31FL3737_I2C_ADDRESS_3)
- is31fl3737_update_led_control_registers(IS31FL3737_I2C_ADDRESS_3, 2);
-# if defined(IS31FL3737_I2C_ADDRESS_4)
- is31fl3737_update_led_control_registers(IS31FL3737_I2C_ADDRESS_4, 3);
-# endif
-# endif
-#endif
+ for (uint8_t i = 0; i < IS31FL3737_DRIVER_COUNT; i++) {
+ is31fl3737_update_led_control_registers(i);
+ }
}
-void is31fl3737_init(uint8_t addr) {
+void is31fl3737_init(uint8_t index) {
// In order to avoid the LEDs being driven with garbage data
// in the LED driver's PWM registers, shutdown is enabled last.
// Set up the mode and other settings, clear the PWM registers,
// then disable software shutdown.
- // Unlock the command register.
- is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC);
+ is31fl3737_select_page(index, IS31FL3737_COMMAND_LED_CONTROL);
- // Select PG0
- is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND, IS31FL3737_COMMAND_LED_CONTROL);
// Turn off all LEDs.
- for (int i = 0; i < IS31FL3737_LED_CONTROL_REGISTER_COUNT; i++) {
- is31fl3737_write_register(addr, i, 0x00);
+ for (uint8_t i = 0; i < IS31FL3737_LED_CONTROL_REGISTER_COUNT; i++) {
+ is31fl3737_write_register(index, i, 0x00);
}
- // Unlock the command register.
- is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC);
+ is31fl3737_select_page(index, IS31FL3737_COMMAND_PWM);
- // Select PG1
- is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND, IS31FL3737_COMMAND_PWM);
// Set PWM on all LEDs to 0
// No need to setup Breath registers to PWM as that is the default.
- for (int i = 0; i < IS31FL3737_PWM_REGISTER_COUNT; i++) {
- is31fl3737_write_register(addr, i, 0x00);
+ for (uint8_t i = 0; i < IS31FL3737_PWM_REGISTER_COUNT; i++) {
+ is31fl3737_write_register(index, i, 0x00);
}
- // Unlock the command register.
- is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC);
+ is31fl3737_select_page(index, IS31FL3737_COMMAND_FUNCTION);
- // Select PG3
- is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND, IS31FL3737_COMMAND_FUNCTION);
// Set de-ghost pull-up resistors (SWx)
- is31fl3737_write_register(addr, IS31FL3737_FUNCTION_REG_SW_PULLUP, IS31FL3737_SW_PULLUP);
+ is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_SW_PULLUP, IS31FL3737_SW_PULLUP);
// Set de-ghost pull-down resistors (CSx)
- is31fl3737_write_register(addr, IS31FL3737_FUNCTION_REG_CS_PULLDOWN, IS31FL3737_CS_PULLDOWN);
+ is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_CS_PULLDOWN, IS31FL3737_CS_PULLDOWN);
// Set global current to maximum.
- is31fl3737_write_register(addr, IS31FL3737_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3737_GLOBAL_CURRENT);
+ is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3737_GLOBAL_CURRENT);
// Disable software shutdown.
- is31fl3737_write_register(addr, IS31FL3737_FUNCTION_REG_CONFIGURATION, ((IS31FL3737_PWM_FREQUENCY & 0b111) << 3) | 0x01);
+ is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_CONFIGURATION, ((IS31FL3737_PWM_FREQUENCY & 0b111) << 3) | 0x01);
// Wait 10ms to ensure the device has woken up.
wait_ms(10);
@@ -179,16 +172,18 @@ void is31fl3737_init(uint8_t addr) {
void is31fl3737_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
is31fl3737_led_t led;
+
if (index >= 0 && index < IS31FL3737_LED_COUNT) {
memcpy_P(&led, (&g_is31fl3737_leds[index]), sizeof(led));
- if (g_pwm_buffer[led.driver][led.r] == red && g_pwm_buffer[led.driver][led.g] == green && g_pwm_buffer[led.driver][led.b] == blue) {
+ if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) {
return;
}
- g_pwm_buffer[led.driver][led.r] = red;
- g_pwm_buffer[led.driver][led.g] = green;
- g_pwm_buffer[led.driver][led.b] = blue;
- g_pwm_buffer_update_required[led.driver] = true;
+
+ driver_buffers[led.driver].pwm_buffer[led.r] = red;
+ driver_buffers[led.driver].pwm_buffer[led.g] = green;
+ driver_buffers[led.driver].pwm_buffer[led.b] = blue;
+ driver_buffers[led.driver].pwm_buffer_dirty = true;
}
}
@@ -210,56 +205,48 @@ void is31fl3737_set_led_control_register(uint8_t index, bool red, bool green, bo
uint8_t bit_b = led.b % 8;
if (red) {
- g_led_control_registers[led.driver][control_register_r] |= (1 << bit_r);
+ driver_buffers[led.driver].led_control_buffer[control_register_r] |= (1 << bit_r);
} else {
- g_led_control_registers[led.driver][control_register_r] &= ~(1 << bit_r);
+ driver_buffers[led.driver].led_control_buffer[control_register_r] &= ~(1 << bit_r);
}
if (green) {
- g_led_control_registers[led.driver][control_register_g] |= (1 << bit_g);
+ driver_buffers[led.driver].led_control_buffer[control_register_g] |= (1 << bit_g);
} else {
- g_led_control_registers[led.driver][control_register_g] &= ~(1 << bit_g);
+ driver_buffers[led.driver].led_control_buffer[control_register_g] &= ~(1 << bit_g);
}
if (blue) {
- g_led_control_registers[led.driver][control_register_b] |= (1 << bit_b);
+ driver_buffers[led.driver].led_control_buffer[control_register_b] |= (1 << bit_b);
} else {
- g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b);
+ driver_buffers[led.driver].led_control_buffer[control_register_b] &= ~(1 << bit_b);
}
- g_led_control_registers_update_required[led.driver] = true;
+ driver_buffers[led.driver].led_control_buffer_dirty = true;
}
-void is31fl3737_update_pwm_buffers(uint8_t addr, uint8_t index) {
- if (g_pwm_buffer_update_required[index]) {
- // Firstly we need to unlock the command register and select PG1
- is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC);
- is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND, IS31FL3737_COMMAND_PWM);
+void is31fl3737_update_pwm_buffers(uint8_t index) {
+ if (driver_buffers[index].pwm_buffer_dirty) {
+ is31fl3737_select_page(index, IS31FL3737_COMMAND_PWM);
+
+ is31fl3737_write_pwm_buffer(index);
- is31fl3737_write_pwm_buffer(addr, g_pwm_buffer[index]);
- g_pwm_buffer_update_required[index] = false;
+ driver_buffers[index].pwm_buffer_dirty = false;
}
}
-void is31fl3737_update_led_control_registers(uint8_t addr, uint8_t index) {
- if (g_led_control_registers_update_required[index]) {
- // Firstly we need to unlock the command register and select PG0
- is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC);
- is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND, IS31FL3737_COMMAND_LED_CONTROL);
- for (int i = 0; i < IS31FL3737_LED_CONTROL_REGISTER_COUNT; i++) {
- is31fl3737_write_register(addr, i, g_led_control_registers[index][i]);
+void is31fl3737_update_led_control_registers(uint8_t index) {
+ if (driver_buffers[index].led_control_buffer_dirty) {
+ is31fl3737_select_page(index, IS31FL3737_COMMAND_LED_CONTROL);
+
+ for (uint8_t i = 0; i < IS31FL3737_LED_CONTROL_REGISTER_COUNT; i++) {
+ is31fl3737_write_register(index, i, driver_buffers[index].led_control_buffer[i]);
}
- g_led_control_registers_update_required[index] = false;
+
+ driver_buffers[index].led_control_buffer_dirty = false;
}
}
void is31fl3737_flush(void) {
- is31fl3737_update_pwm_buffers(IS31FL3737_I2C_ADDRESS_1, 0);
-#if defined(IS31FL3737_I2C_ADDRESS_2)
- is31fl3737_update_pwm_buffers(IS31FL3737_I2C_ADDRESS_2, 1);
-# if defined(IS31FL3737_I2C_ADDRESS_3)
- is31fl3737_update_pwm_buffers(IS31FL3737_I2C_ADDRESS_3, 2);
-# if defined(IS31FL3737_I2C_ADDRESS_4)
- is31fl3737_update_pwm_buffers(IS31FL3737_I2C_ADDRESS_4, 3);
-# endif
-# endif
-#endif
+ for (uint8_t i = 0; i < IS31FL3737_DRIVER_COUNT; i++) {
+ is31fl3737_update_pwm_buffers(i);
+ }
}
diff --git a/drivers/led/issi/is31fl3737.h b/drivers/led/issi/is31fl3737.h
index 8de3bf4ef5..735a3a1e30 100644
--- a/drivers/led/issi/is31fl3737.h
+++ b/drivers/led/issi/is31fl3737.h
@@ -117,9 +117,9 @@ typedef struct is31fl3737_led_t {
extern const is31fl3737_led_t PROGMEM g_is31fl3737_leds[IS31FL3737_LED_COUNT];
void is31fl3737_init_drivers(void);
-void is31fl3737_init(uint8_t addr);
-void is31fl3737_write_register(uint8_t addr, uint8_t reg, uint8_t data);
-void is31fl3737_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
+void is31fl3737_init(uint8_t index);
+void is31fl3737_write_register(uint8_t index, uint8_t reg, uint8_t data);
+void is31fl3737_select_page(uint8_t index, uint8_t page);
void is31fl3737_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
void is31fl3737_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
@@ -130,8 +130,8 @@ void is31fl3737_set_led_control_register(uint8_t index, bool red, bool green, bo
// (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 is31fl3737_update_pwm_buffers(uint8_t addr, uint8_t index);
-void is31fl3737_update_led_control_registers(uint8_t addr, uint8_t index);
+void is31fl3737_update_pwm_buffers(uint8_t index);
+void is31fl3737_update_led_control_registers(uint8_t index);
void is31fl3737_flush(void);
@@ -159,158 +159,316 @@ void is31fl3737_flush(void);
#define IS31FL3737_PWM_FREQUENCY_2K1_HZ 0b011
#define IS31FL3737_PWM_FREQUENCY_1K05_HZ 0b100
-#define A_1 0x00
-#define A_2 0x01
-#define A_3 0x02
-#define A_4 0x03
-#define A_5 0x04
-#define A_6 0x05
-#define A_7 0x08
-#define A_8 0x09
-#define A_9 0x0A
-#define A_10 0x0B
-#define A_11 0x0C
-#define A_12 0x0D
-
-#define B_1 0x10
-#define B_2 0x11
-#define B_3 0x12
-#define B_4 0x13
-#define B_5 0x14
-#define B_6 0x15
-#define B_7 0x18
-#define B_8 0x19
-#define B_9 0x1A
-#define B_10 0x1B
-#define B_11 0x1C
-#define B_12 0x1D
-
-#define C_1 0x20
-#define C_2 0x21
-#define C_3 0x22
-#define C_4 0x23
-#define C_5 0x24
-#define C_6 0x25
-#define C_7 0x28
-#define C_8 0x29
-#define C_9 0x2A
-#define C_10 0x2B
-#define C_11 0x2C
-#define C_12 0x2D
-
-#define D_1 0x30
-#define D_2 0x31
-#define D_3 0x32
-#define D_4 0x33
-#define D_5 0x34
-#define D_6 0x35
-#define D_7 0x38
-#define D_8 0x39
-#define D_9 0x3A
-#define D_10 0x3B
-#define D_11 0x3C
-#define D_12 0x3D
-
-#define E_1 0x40
-#define E_2 0x41
-#define E_3 0x42
-#define E_4 0x43
-#define E_5 0x44
-#define E_6 0x45
-#define E_7 0x48
-#define E_8 0x49
-#define E_9 0x4A
-#define E_10 0x4B
-#define E_11 0x4C
-#define E_12 0x4D
-
-#define F_1 0x50
-#define F_2 0x51
-#define F_3 0x52
-#define F_4 0x53
-#define F_5 0x54
-#define F_6 0x55
-#define F_7 0x58
-#define F_8 0x59
-#define F_9 0x5A
-#define F_10 0x5B
-#define F_11 0x5C
-#define F_12 0x5D
-
-#define G_1 0x60
-#define G_2 0x61
-#define G_3 0x62
-#define G_4 0x63
-#define G_5 0x64
-#define G_6 0x65
-#define G_7 0x68
-#define G_8 0x69
-#define G_9 0x6A
-#define G_10 0x6B
-#define G_11 0x6C
-#define G_12 0x6D
-
-#define H_1 0x70
-#define H_2 0x71
-#define H_3 0x72
-#define H_4 0x73
-#define H_5 0x74
-#define H_6 0x75
-#define H_7 0x78
-#define H_8 0x79
-#define H_9 0x7A
-#define H_10 0x7B
-#define H_11 0x7C
-#define H_12 0x7D
-
-#define I_1 0x80
-#define I_2 0x81
-#define I_3 0x82
-#define I_4 0x83
-#define I_5 0x84
-#define I_6 0x85
-#define I_7 0x88
-#define I_8 0x89
-#define I_9 0x8A
-#define I_10 0x8B
-#define I_11 0x8C
-#define I_12 0x8D
-
-#define J_1 0x90
-#define J_2 0x91
-#define J_3 0x92
-#define J_4 0x93
-#define J_5 0x94
-#define J_6 0x95
-#define J_7 0x98
-#define J_8 0x99
-#define J_9 0x9A
-#define J_10 0x9B
-#define J_11 0x9C
-#define J_12 0x9D
-
-#define K_1 0xA0
-#define K_2 0xA1
-#define K_3 0xA2
-#define K_4 0xA3
-#define K_5 0xA4
-#define K_6 0xA5
-#define K_7 0xA8
-#define K_8 0xA9
-#define K_9 0xAA
-#define K_10 0xAB
-#define K_11 0xAC
-#define K_12 0xAD
-
-#define L_1 0xB0
-#define L_2 0xB1
-#define L_3 0xB2
-#define L_4 0xB3
-#define L_5 0xB4
-#define L_6 0xB5
-#define L_7 0xB8
-#define L_8 0xB9
-#define L_9 0xBA
-#define L_10 0xBB
-#define L_11 0xBC
-#define L_12 0xBD
+#define SW1_CS1 0x00
+#define SW1_CS2 0x01
+#define SW1_CS3 0x02
+#define SW1_CS4 0x03
+#define SW1_CS5 0x04
+#define SW1_CS6 0x05
+#define SW1_CS7 0x08
+#define SW1_CS8 0x09
+#define SW1_CS9 0x0A
+#define SW1_CS10 0x0B
+#define SW1_CS11 0x0C
+#define SW1_CS12 0x0D
+
+#define SW2_CS1 0x10
+#define SW2_CS2 0x11
+#define SW2_CS3 0x12
+#define SW2_CS4 0x13
+#define SW2_CS5 0x14
+#define SW2_CS6 0x15
+#define SW2_CS7 0x18
+#define SW2_CS8 0x19
+#define SW2_CS9 0x1A
+#define SW2_CS10 0x1B
+#define SW2_CS11 0x1C
+#define SW2_CS12 0x1D
+
+#define SW3_CS1 0x20
+#define SW3_CS2 0x21
+#define SW3_CS3 0x22
+#define SW3_CS4 0x23
+#define SW3_CS5 0x24
+#define SW3_CS6 0x25
+#define SW3_CS7 0x28
+#define SW3_CS8 0x29
+#define SW3_CS9 0x2A
+#define SW3_CS10 0x2B
+#define SW3_CS11 0x2C
+#define SW3_CS12 0x2D
+
+#define SW4_CS1 0x30
+#define SW4_CS2 0x31
+#define SW4_CS3 0x32
+#define SW4_CS4 0x33
+#define SW4_CS5 0x34
+#define SW4_CS6 0x35
+#define SW4_CS7 0x38
+#define SW4_CS8 0x39
+#define SW4_CS9 0x3A
+#define SW4_CS10 0x3B
+#define SW4_CS11 0x3C
+#define SW4_CS12 0x3D
+
+#define SW5_CS1 0x40
+#define SW5_CS2 0x41
+#define SW5_CS3 0x42
+#define SW5_CS4 0x43
+#define SW5_CS5 0x44
+#define SW5_CS6 0x45
+#define SW5_CS7 0x48
+#define SW5_CS8 0x49
+#define SW5_CS9 0x4A
+#define SW5_CS10 0x4B
+#define SW5_CS11 0x4C
+#define SW5_CS12 0x4D
+
+#define SW6_CS1 0x50
+#define SW6_CS2 0x51
+#define SW6_CS3 0x52
+#define SW6_CS4 0x53
+#define SW6_CS5 0x54
+#define SW6_CS6 0x55
+#define SW6_CS7 0x58
+#define SW6_CS8 0x59
+#define SW6_CS9 0x5A
+#define SW6_CS10 0x5B
+#define SW6_CS11 0x5C
+#define SW6_CS12 0x5D
+
+#define SW7_CS1 0x60
+#define SW7_CS2 0x61
+#define SW7_CS3 0x62
+#define SW7_CS4 0x63
+#define SW7_CS5 0x64
+#define SW7_CS6 0x65
+#define SW7_CS7 0x68
+#define SW7_CS8 0x69
+#define SW7_CS9 0x6A
+#define SW7_CS10 0x6B
+#define SW7_CS11 0x6C
+#define SW7_CS12 0x6D
+
+#define SW8_CS1 0x70
+#define SW8_CS2 0x71
+#define SW8_CS3 0x72
+#define SW8_CS4 0x73
+#define SW8_CS5 0x74
+#define SW8_CS6 0x75
+#define SW8_CS7 0x78
+#define SW8_CS8 0x79
+#define SW8_CS9 0x7A
+#define SW8_CS10 0x7B
+#define SW8_CS11 0x7C
+#define SW8_CS12 0x7D
+
+#define SW9_CS1 0x80
+#define SW9_CS2 0x81
+#define SW9_CS3 0x82
+#define SW9_CS4 0x83
+#define SW9_CS5 0x84
+#define SW9_CS6 0x85
+#define SW9_CS7 0x88
+#define SW9_CS8 0x89
+#define SW9_CS9 0x8A
+#define SW9_CS10 0x8B
+#define SW9_CS11 0x8C
+#define SW9_CS12 0x8D
+
+#define SW10_CS1 0x90
+#define SW10_CS2 0x91
+#define SW10_CS3 0x92
+#define SW10_CS4 0x93
+#define SW10_CS5 0x94
+#define SW10_CS6 0x95
+#define SW10_CS7 0x98
+#define SW10_CS8 0x99
+#define SW10_CS9 0x9A
+#define SW10_CS10 0x9B
+#define SW10_CS11 0x9C
+#define SW10_CS12 0x9D
+
+#define SW11_CS1 0xA0
+#define SW11_CS2 0xA1
+#define SW11_CS3 0xA2
+#define SW11_CS4 0xA3
+#define SW11_CS5 0xA4
+#define SW11_CS6 0xA5
+#define SW11_CS7 0xA8
+#define SW11_CS8 0xA9
+#define SW11_CS9 0xAA
+#define SW11_CS10 0xAB
+#define SW11_CS11 0xAC
+#define SW11_CS12 0xAD
+
+#define SW12_CS1 0xB0
+#define SW12_CS2 0xB1
+#define SW12_CS3 0xB2
+#define SW12_CS4 0xB3
+#define SW12_CS5 0xB4
+#define SW12_CS6 0xB5
+#define SW12_CS7 0xB8
+#define SW12_CS8 0xB9
+#define SW12_CS9 0xBA
+#define SW12_CS10 0xBB
+#define SW12_CS11 0xBC
+#define SW12_CS12 0xBD
+
+// DEPRECATED - DO NOT USE
+
+#define A_1 SW1_CS1
+#define A_2 SW1_CS2
+#define A_3 SW1_CS3
+#define A_4 SW1_CS4
+#define A_5 SW1_CS5
+#define A_6 SW1_CS6
+#define A_7 SW1_CS7
+#define A_8 SW1_CS8
+#define A_9 SW1_CS9
+#define A_10 SW1_CS10
+#define A_11 SW1_CS11
+#define A_12 SW1_CS12
+
+#define B_1 SW2_CS1
+#define B_2 SW2_CS2
+#define B_3 SW2_CS3
+#define B_4 SW2_CS4
+#define B_5 SW2_CS5
+#define B_6 SW2_CS6
+#define B_7 SW2_CS7
+#define B_8 SW2_CS8
+#define B_9 SW2_CS9
+#define B_10 SW2_CS10
+#define B_11 SW2_CS11
+#define B_12 SW2_CS12
+
+#define C_1 SW3_CS1
+#define C_2 SW3_CS2
+#define C_3 SW3_CS3
+#define C_4 SW3_CS4
+#define C_5 SW3_CS5
+#define C_6 SW3_CS6
+#define C_7 SW3_CS7
+#define C_8 SW3_CS8
+#define C_9 SW3_CS9
+#define C_10 SW3_CS10
+#define C_11 SW3_CS11
+#define C_12 SW3_CS12
+
+#define D_1 SW4_CS1
+#define D_2 SW4_CS2
+#define D_3 SW4_CS3
+#define D_4 SW4_CS4
+#define D_5 SW4_CS5
+#define D_6 SW4_CS6
+#define D_7 SW4_CS7
+#define D_8 SW4_CS8
+#define D_9 SW4_CS9
+#define D_10 SW4_CS10
+#define D_11 SW4_CS11
+#define D_12 SW4_CS12
+
+#define E_1 SW5_CS1
+#define E_2 SW5_CS2
+#define E_3 SW5_CS3
+#define E_4 SW5_CS4
+#define E_5 SW5_CS5
+#define E_6 SW5_CS6
+#define E_7 SW5_CS7
+#define E_8 SW5_CS8
+#define E_9 SW5_CS9
+#define E_10 SW5_CS10
+#define E_11 SW5_CS11
+#define E_12 SW5_CS12
+
+#define F_1 SW6_CS1
+#define F_2 SW6_CS2
+#define F_3 SW6_CS3
+#define F_4 SW6_CS4
+#define F_5 SW6_CS5
+#define F_6 SW6_CS6
+#define F_7 SW6_CS7
+#define F_8 SW6_CS8
+#define F_9 SW6_CS9
+#define F_10 SW6_CS10
+#define F_11 SW6_CS11
+#define F_12 SW6_CS12
+
+#define G_1 SW7_CS1
+#define G_2 SW7_CS2
+#define G_3 SW7_CS3
+#define G_4 SW7_CS4
+#define G_5 SW7_CS5
+#define G_6 SW7_CS6
+#define G_7 SW7_CS7
+#define G_8 SW7_CS8
+#define G_9 SW7_CS9
+#define G_10 SW7_CS10
+#define G_11 SW7_CS11
+#define G_12 SW7_CS12
+
+#define H_1 SW8_CS1
+#define H_2 SW8_CS2
+#define H_3 SW8_CS3
+#define H_4 SW8_CS4
+#define H_5 SW8_CS5
+#define H_6 SW8_CS6
+#define H_7 SW8_CS7
+#define H_8 SW8_CS8
+#define H_9 SW8_CS9
+#define H_10 SW8_CS10
+#define H_11 SW8_CS11
+#define H_12 SW8_CS12
+
+#define I_1 SW9_CS1
+#define I_2 SW9_CS2
+#define I_3 SW9_CS3
+#define I_4 SW9_CS4
+#define I_5 SW9_CS5
+#define I_6 SW9_CS6
+#define I_7 SW9_CS7
+#define I_8 SW9_CS8
+#define I_9 SW9_CS9
+#define I_10 SW9_CS10
+#define I_11 SW9_CS11
+#define I_12 SW9_CS12
+
+#define J_1 SW10_CS1
+#define J_2 SW10_CS2
+#define J_3 SW10_CS3
+#define J_4 SW10_CS4
+#define J_5 SW10_CS5
+#define J_6 SW10_CS6
+#define J_7 SW10_CS7
+#define J_8 SW10_CS8
+#define J_9 SW10_CS9
+#define J_10 SW10_CS10
+#define J_11 SW10_CS11
+#define J_12 SW10_CS12
+
+#define K_1 SW11_CS1
+#define K_2 SW11_CS2
+#define K_3 SW11_CS3
+#define K_4 SW11_CS4
+#define K_5 SW11_CS5
+#define K_6 SW11_CS6
+#define K_7 SW11_CS7
+#define K_8 SW11_CS8
+#define K_9 SW11_CS9
+#define K_10 SW11_CS10
+#define K_11 SW11_CS11
+#define K_12 SW11_CS12
+
+#define L_1 SW12_CS1
+#define L_2 SW12_CS2
+#define L_3 SW12_CS3
+#define L_4 SW12_CS4
+#define L_5 SW12_CS5
+#define L_6 SW12_CS6
+#define L_7 SW12_CS7
+#define L_8 SW12_CS8
+#define L_9 SW12_CS9
+#define L_10 SW12_CS10
+#define L_11 SW12_CS11
+#define L_12 SW12_CS12
diff --git a/drivers/led/issi/is31fl3741-mono.c b/drivers/led/issi/is31fl3741-mono.c
new file mode 100644
index 0000000000..dbccba0fc8
--- /dev/null
+++ b/drivers/led/issi/is31fl3741-mono.c
@@ -0,0 +1,282 @@
+/* Copyright 2017 Jason Williams
+ * Copyright 2018 Jack Humbert
+ * Copyright 2018 Yiancar
+ * Copyright 2020 MelGeek
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "is31fl3741-mono.h"
+#include "i2c_master.h"
+#include "gpio.h"
+#include "wait.h"
+
+#define IS31FL3741_PWM_0_REGISTER_COUNT 180
+#define IS31FL3741_PWM_1_REGISTER_COUNT 171
+#define IS31FL3741_SCALING_0_REGISTER_COUNT 180
+#define IS31FL3741_SCALING_1_REGISTER_COUNT 171
+
+#ifndef IS31FL3741_I2C_TIMEOUT
+# define IS31FL3741_I2C_TIMEOUT 100
+#endif
+
+#ifndef IS31FL3741_I2C_PERSISTENCE
+# define IS31FL3741_I2C_PERSISTENCE 0
+#endif
+
+#ifndef IS31FL3741_CONFIGURATION
+# define IS31FL3741_CONFIGURATION 0x01
+#endif
+
+#ifndef IS31FL3741_PWM_FREQUENCY
+# define IS31FL3741_PWM_FREQUENCY IS31FL3741_PWM_FREQUENCY_29K_HZ
+#endif
+
+#ifndef IS31FL3741_SW_PULLUP
+# define IS31FL3741_SW_PULLUP IS31FL3741_PUR_32K_OHM
+#endif
+
+#ifndef IS31FL3741_CS_PULLDOWN
+# define IS31FL3741_CS_PULLDOWN IS31FL3741_PDR_32K_OHM
+#endif
+
+#ifndef IS31FL3741_GLOBAL_CURRENT
+# define IS31FL3741_GLOBAL_CURRENT 0xFF
+#endif
+
+const uint8_t i2c_addresses[IS31FL3741_DRIVER_COUNT] = {
+ IS31FL3741_I2C_ADDRESS_1,
+#ifdef IS31FL3741_I2C_ADDRESS_2
+ IS31FL3741_I2C_ADDRESS_2,
+# ifdef IS31FL3741_I2C_ADDRESS_3
+ IS31FL3741_I2C_ADDRESS_3,
+# ifdef IS31FL3741_I2C_ADDRESS_4
+ IS31FL3741_I2C_ADDRESS_4,
+# endif
+# endif
+#endif
+};
+
+// These buffers match the IS31FL3741 and IS31FL3741A PWM registers.
+// The scaling buffers match the page 2 and 3 LED On/Off registers.
+// Storing them like this is optimal for I2C transfers to the registers.
+// We could optimize this and take out the unused registers from these
+// buffers and the transfers in is31fl3741_write_pwm_buffer() but it's
+// probably not worth the extra complexity.
+typedef struct is31fl3741_driver_t {
+ uint8_t pwm_buffer_0[IS31FL3741_PWM_0_REGISTER_COUNT];
+ uint8_t pwm_buffer_1[IS31FL3741_PWM_1_REGISTER_COUNT];
+ bool pwm_buffer_dirty;
+ uint8_t scaling_buffer_0[IS31FL3741_SCALING_0_REGISTER_COUNT];
+ uint8_t scaling_buffer_1[IS31FL3741_SCALING_1_REGISTER_COUNT];
+ bool scaling_buffer_dirty;
+} PACKED is31fl3741_driver_t;
+
+is31fl3741_driver_t driver_buffers[IS31FL3741_DRIVER_COUNT] = {{
+ .pwm_buffer_0 = {0},
+ .pwm_buffer_1 = {0},
+ .pwm_buffer_dirty = false,
+ .scaling_buffer_0 = {0},
+ .scaling_buffer_1 = {0},
+ .scaling_buffer_dirty = false,
+}};
+
+void is31fl3741_write_register(uint8_t index, uint8_t reg, uint8_t data) {
+#if IS31FL3741_I2C_PERSISTENCE > 0
+ for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3741_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3741_I2C_TIMEOUT);
+#endif
+}
+
+void is31fl3741_select_page(uint8_t index, uint8_t page) {
+ is31fl3741_write_register(index, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC);
+ is31fl3741_write_register(index, IS31FL3741_REG_COMMAND, page);
+}
+
+void is31fl3741_write_pwm_buffer(uint8_t index) {
+ is31fl3741_select_page(index, IS31FL3741_COMMAND_PWM_0);
+
+ // Transmit PWM0 registers in 6 transfers of 30 bytes.
+
+ // Iterate over the pwm_buffer_0 contents at 30 byte intervals.
+ for (uint8_t i = 0; i < IS31FL3741_PWM_0_REGISTER_COUNT; i += 30) {
+#if IS31FL3741_I2C_PERSISTENCE > 0
+ for (uint8_t j = 0; j < IS31FL3741_I2C_PERSISTENCE; j++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer_0 + i, 30, IS31FL3741_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer_0 + i, 30, IS31FL3741_I2C_TIMEOUT);
+#endif
+ }
+
+ is31fl3741_select_page(index, IS31FL3741_COMMAND_PWM_1);
+
+ // Transmit PWM1 registers in 9 transfers of 19 bytes.
+
+ // Iterate over the pwm_buffer_1 contents at 19 byte intervals.
+ for (uint8_t i = 0; i < IS31FL3741_PWM_1_REGISTER_COUNT; i += 19) {
+#if IS31FL3741_I2C_PERSISTENCE > 0
+ for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer_1 + i, 19, IS31FL3741_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer_1 + i, 19, IS31FL3741_I2C_TIMEOUT);
+#endif
+ }
+}
+
+void is31fl3741_init_drivers(void) {
+ i2c_init();
+
+#if defined(IS31FL3741_SDB_PIN)
+ gpio_set_pin_output(IS31FL3741_SDB_PIN);
+ gpio_write_pin_high(IS31FL3741_SDB_PIN);
+#endif
+
+ for (uint8_t i = 0; i < IS31FL3741_DRIVER_COUNT; i++) {
+ is31fl3741_init(i);
+ }
+
+ for (int i = 0; i < IS31FL3741_LED_COUNT; i++) {
+ is31fl3741_set_led_control_register(i, true);
+ }
+
+ for (uint8_t i = 0; i < IS31FL3741_DRIVER_COUNT; i++) {
+ is31fl3741_update_led_control_registers(i);
+ }
+}
+
+void is31fl3741_init(uint8_t index) {
+ // In order to avoid the LEDs being driven with garbage data
+ // in the LED driver's PWM registers, shutdown is enabled last.
+ // Set up the mode and other settings, clear the PWM registers,
+ // then disable software shutdown.
+ // Unlock the command register.
+
+ is31fl3741_select_page(index, IS31FL3741_COMMAND_FUNCTION);
+
+ // Set to Normal operation
+ is31fl3741_write_register(index, IS31FL3741_FUNCTION_REG_CONFIGURATION, IS31FL3741_CONFIGURATION);
+
+ // Set Golbal Current Control Register
+ is31fl3741_write_register(index, IS31FL3741_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3741_GLOBAL_CURRENT);
+ // Set Pull up & Down for SWx CSy
+ is31fl3741_write_register(index, IS31FL3741_FUNCTION_REG_PULLDOWNUP, ((IS31FL3741_CS_PULLDOWN << 4) | IS31FL3741_SW_PULLUP));
+ // Set PWM frequency
+ is31fl3741_write_register(index, IS31FL3741_FUNCTION_REG_PWM_FREQUENCY, (IS31FL3741_PWM_FREQUENCY & 0b1111));
+
+ // is31fl3741_update_led_scaling_registers(index, 0xFF, 0xFF, 0xFF);
+
+ // Wait 10ms to ensure the device has woken up.
+ wait_ms(10);
+}
+
+uint8_t get_pwm_value(uint8_t driver, uint16_t reg) {
+ if (reg & 0x100) {
+ return driver_buffers[driver].pwm_buffer_1[reg & 0xFF];
+ } else {
+ return driver_buffers[driver].pwm_buffer_0[reg];
+ }
+}
+
+void set_pwm_value(uint8_t driver, uint16_t reg, uint8_t value) {
+ if (reg & 0x100) {
+ driver_buffers[driver].pwm_buffer_1[reg & 0xFF] = value;
+ } else {
+ driver_buffers[driver].pwm_buffer_0[reg] = value;
+ }
+}
+
+void is31fl3741_set_value(int index, uint8_t value) {
+ is31fl3741_led_t led;
+
+ if (index >= 0 && index < IS31FL3741_LED_COUNT) {
+ memcpy_P(&led, (&g_is31fl3741_leds[index]), sizeof(led));
+
+ if (get_pwm_value(led.driver, led.v) == value) {
+ return;
+ }
+
+ set_pwm_value(led.driver, led.v, value);
+ driver_buffers[led.driver].pwm_buffer_dirty = true;
+ }
+}
+
+void is31fl3741_set_value_all(uint8_t value) {
+ for (int i = 0; i < IS31FL3741_LED_COUNT; i++) {
+ is31fl3741_set_value(i, value);
+ }
+}
+
+void set_scaling_value(uint8_t driver, uint16_t reg, uint8_t value) {
+ if (reg & 0x100) {
+ driver_buffers[driver].scaling_buffer_1[reg & 0xFF] = value;
+ } else {
+ driver_buffers[driver].scaling_buffer_0[reg] = value;
+ }
+}
+
+void is31fl3741_set_led_control_register(uint8_t index, bool value) {
+ is31fl3741_led_t led;
+ memcpy_P(&led, (&g_is31fl3741_leds[index]), sizeof(led));
+
+ set_scaling_value(led.driver, led.v, value ? 0xFF : 0x00);
+
+ driver_buffers[led.driver].scaling_buffer_dirty = true;
+}
+
+void is31fl3741_update_pwm_buffers(uint8_t index) {
+ if (driver_buffers[index].pwm_buffer_dirty) {
+ is31fl3741_write_pwm_buffer(index);
+
+ driver_buffers[index].pwm_buffer_dirty = false;
+ }
+}
+
+void is31fl3741_set_pwm_buffer(const is31fl3741_led_t *pled, uint8_t value) {
+ set_pwm_value(pled->driver, pled->v, value);
+ driver_buffers[pled->driver].pwm_buffer_dirty = true;
+}
+
+void is31fl3741_update_led_control_registers(uint8_t index) {
+ if (driver_buffers[index].scaling_buffer_dirty) {
+ is31fl3741_select_page(index, IS31FL3741_COMMAND_SCALING_0);
+
+ for (uint8_t i = 0; i < IS31FL3741_SCALING_0_REGISTER_COUNT; i++) {
+ is31fl3741_write_register(index, i, driver_buffers[index].scaling_buffer_0[i]);
+ }
+
+ is31fl3741_select_page(index, IS31FL3741_COMMAND_SCALING_1);
+
+ for (uint8_t i = 0; i < IS31FL3741_SCALING_1_REGISTER_COUNT; i++) {
+ is31fl3741_write_register(index, i, driver_buffers[index].scaling_buffer_1[i]);
+ }
+
+ driver_buffers[index].scaling_buffer_dirty = false;
+ }
+}
+
+void is31fl3741_set_scaling_registers(const is31fl3741_led_t *pled, uint8_t value) {
+ set_scaling_value(pled->driver, pled->v, value);
+ driver_buffers[pled->driver].scaling_buffer_dirty = true;
+}
+
+void is31fl3741_flush(void) {
+ for (uint8_t i = 0; i < IS31FL3741_DRIVER_COUNT; i++) {
+ is31fl3741_update_pwm_buffers(i);
+ }
+}
diff --git a/drivers/led/issi/is31fl3741-mono.h b/drivers/led/issi/is31fl3741-mono.h
new file mode 100644
index 0000000000..6d4f70b1b3
--- /dev/null
+++ b/drivers/led/issi/is31fl3741-mono.h
@@ -0,0 +1,887 @@
+/* Copyright 2017 Jason Williams
+ * Copyright 2018 Jack Humbert
+ * Copyright 2018 Yiancar
+ * Copyright 2020 MelGeek
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "progmem.h"
+#include "util.h"
+
+// ======== DEPRECATED DEFINES - DO NOT USE ========
+#ifdef ISSI_TIMEOUT
+# define IS31FL3741_I2C_TIMEOUT ISSI_TIMEOUT
+#endif
+#ifdef ISSI_PERSISTENCE
+# define IS31FL3741_I2C_PERSISTENCE ISSI_PERSISTENCE
+#endif
+#ifdef ISSI_CONFIGURATION
+# define IS31FL3741_CONFIGURATION ISSI_CONFIGURATION
+#endif
+#ifdef ISSI_SWPULLUP
+# define IS31FL3741_SW_PULLUP ISSI_SWPULLUP
+#endif
+#ifdef ISSI_CSPULLUP
+# define IS31FL3741_CS_PULLDOWN ISSI_CSPULLUP
+#endif
+#ifdef ISSI_GLOBALCURRENT
+# define IS31FL3741_GLOBAL_CURRENT ISSI_GLOBALCURRENT
+#endif
+
+#define PUR_0R IS31FL3741_PUR_0_OHM
+#define PUR_05KR IS31FL3741_PUR_0K5_OHM
+#define PUR_1KR IS31FL3741_PUR_1K_OHM
+#define PUR_2KR IS31FL3741_PUR_2K_OHM
+#define PUR_4KR IS31FL3741_PUR_4K_OHM
+#define PUR_8KR IS31FL3741_PUR_8K_OHM
+#define PUR_16KR IS31FL3741_PUR_16K_OHM
+#define PUR_32KR IS31FL3741_PUR_32K_OHM
+// ========
+
+#define IS31FL3741_REG_INTERRUPT_MASK 0xF0
+#define IS31FL3741_REG_INTERRUPT_STATUS 0xF1
+#define IS31FL3741_REG_ID 0xFC
+
+#define IS31FL3741_REG_COMMAND 0xFD
+
+#define IS31FL3741_COMMAND_PWM_0 0x00
+#define IS31FL3741_COMMAND_PWM_1 0x01
+#define IS31FL3741_COMMAND_SCALING_0 0x02
+#define IS31FL3741_COMMAND_SCALING_1 0x03
+#define IS31FL3741_COMMAND_FUNCTION 0x04
+
+#define IS31FL3741_FUNCTION_REG_CONFIGURATION 0x00
+#define IS31FL3741_FUNCTION_REG_GLOBAL_CURRENT 0x01
+#define IS31FL3741_FUNCTION_REG_PULLDOWNUP 0x02
+#define IS31FL3741_FUNCTION_REG_PWM_FREQUENCY 0x36
+#define IS31FL3741_FUNCTION_REG_RESET 0x3F
+
+#define IS31FL3741_REG_COMMAND_WRITE_LOCK 0xFE
+#define IS31FL3741_COMMAND_WRITE_LOCK_MAGIC 0xC5
+
+#define IS31FL3741_I2C_ADDRESS_GND 0x30
+#define IS31FL3741_I2C_ADDRESS_SCL 0x31
+#define IS31FL3741_I2C_ADDRESS_SDA 0x32
+#define IS31FL3741_I2C_ADDRESS_VCC 0x33
+
+#if defined(LED_MATRIX_IS31FL3741)
+# define IS31FL3741_LED_COUNT LED_MATRIX_LED_COUNT
+#endif
+
+#if defined(IS31FL3741_I2C_ADDRESS_4)
+# define IS31FL3741_DRIVER_COUNT 4
+#elif defined(IS31FL3741_I2C_ADDRESS_3)
+# define IS31FL3741_DRIVER_COUNT 3
+#elif defined(IS31FL3741_I2C_ADDRESS_2)
+# define IS31FL3741_DRIVER_COUNT 2
+#elif defined(IS31FL3741_I2C_ADDRESS_1)
+# define IS31FL3741_DRIVER_COUNT 1
+#endif
+
+typedef struct is31fl3741_led_t {
+ uint8_t driver : 2;
+ uint16_t v : 9;
+} PACKED is31fl3741_led_t;
+
+extern const is31fl3741_led_t PROGMEM g_is31fl3741_leds[IS31FL3741_LED_COUNT];
+
+void is31fl3741_init_drivers(void);
+void is31fl3741_init(uint8_t index);
+void is31fl3741_write_register(uint8_t index, uint8_t reg, uint8_t data);
+void is31fl3741_select_page(uint8_t index, uint8_t page);
+
+void is31fl3741_set_value(int index, uint8_t value);
+void is31fl3741_set_value_all(uint8_t value);
+
+void is31fl3741_set_led_control_register(uint8_t index, bool value);
+
+// 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 is31fl3741_update_pwm_buffers(uint8_t index);
+void is31fl3741_update_led_control_registers(uint8_t index);
+void is31fl3741_set_scaling_registers(const is31fl3741_led_t *pled, uint8_t value);
+
+void is31fl3741_set_pwm_buffer(const is31fl3741_led_t *pled, uint8_t value);
+
+void is31fl3741_flush(void);
+
+#define IS31FL3741_PDR_0_OHM 0b000 // No pull-down resistor
+#define IS31FL3741_PDR_0K5_OHM 0b001 // 0.5 kOhm resistor
+#define IS31FL3741_PDR_1K_OHM 0b010 // 1 kOhm resistor
+#define IS31FL3741_PDR_2K_OHM 0b011 // 2 kOhm resistor
+#define IS31FL3741_PDR_4K_OHM 0b100 // 4 kOhm resistor
+#define IS31FL3741_PDR_8K_OHM 0b101 // 8 kOhm resistor
+#define IS31FL3741_PDR_16K_OHM 0b110 // 16 kOhm resistor
+#define IS31FL3741_PDR_32K_OHM 0b111 // 32 kOhm resistor
+
+#define IS31FL3741_PUR_0_OHM 0b000 // No pull-up resistor
+#define IS31FL3741_PUR_0K5_OHM 0b001 // 0.5 kOhm resistor
+#define IS31FL3741_PUR_1K_OHM 0b010 // 1 kOhm resistor
+#define IS31FL3741_PUR_2K_OHM 0b011 // 2 kOhm resistor
+#define IS31FL3741_PUR_4K_OHM 0b100 // 4 kOhm resistor
+#define IS31FL3741_PUR_8K_OHM 0b101 // 8 kOhm resistor
+#define IS31FL3741_PUR_16K_OHM 0b110 // 16 kOhm resistor
+#define IS31FL3741_PUR_32K_OHM 0b111 // 32 kOhm resistor
+
+#define IS31FL3741_PWM_FREQUENCY_29K_HZ 0b0000
+#define IS31FL3741_PWM_FREQUENCY_3K6_HZ 0b0011
+#define IS31FL3741_PWM_FREQUENCY_1K8_HZ 0b0111
+#define IS31FL3741_PWM_FREQUENCY_900_HZ 0b1011
+
+#define SW1_CS1 0x00
+#define SW1_CS2 0x01
+#define SW1_CS3 0x02
+#define SW1_CS4 0x03
+#define SW1_CS5 0x04
+#define SW1_CS6 0x05
+#define SW1_CS7 0x06
+#define SW1_CS8 0x07
+#define SW1_CS9 0x08
+#define SW1_CS10 0x09
+#define SW1_CS11 0x0A
+#define SW1_CS12 0x0B
+#define SW1_CS13 0x0C
+#define SW1_CS14 0x0D
+#define SW1_CS15 0x0E
+#define SW1_CS16 0x0F
+#define SW1_CS17 0x10
+#define SW1_CS18 0x11
+#define SW1_CS19 0x12
+#define SW1_CS20 0x13
+#define SW1_CS21 0x14
+#define SW1_CS22 0x15
+#define SW1_CS23 0x16
+#define SW1_CS24 0x17
+#define SW1_CS25 0x18
+#define SW1_CS26 0x19
+#define SW1_CS27 0x1A
+#define SW1_CS28 0x1B
+#define SW1_CS29 0x1C
+#define SW1_CS30 0x1D
+
+#define SW2_CS1 0x1E
+#define SW2_CS2 0x1F
+#define SW2_CS3 0x20
+#define SW2_CS4 0x21
+#define SW2_CS5 0x22
+#define SW2_CS6 0x23
+#define SW2_CS7 0x24
+#define SW2_CS8 0x25
+#define SW2_CS9 0x26
+#define SW2_CS10 0x27
+#define SW2_CS11 0x28
+#define SW2_CS12 0x29
+#define SW2_CS13 0x2A
+#define SW2_CS14 0x2B
+#define SW2_CS15 0x2C
+#define SW2_CS16 0x2D
+#define SW2_CS17 0x2E
+#define SW2_CS18 0x2F
+#define SW2_CS19 0x30
+#define SW2_CS20 0x31
+#define SW2_CS21 0x32
+#define SW2_CS22 0x33
+#define SW2_CS23 0x34
+#define SW2_CS24 0x35
+#define SW2_CS25 0x36
+#define SW2_CS26 0x37
+#define SW2_CS27 0x38
+#define SW2_CS28 0x39
+#define SW2_CS29 0x3A
+#define SW2_CS30 0x3B
+
+#define SW3_CS1 0x3C
+#define SW3_CS2 0x3D
+#define SW3_CS3 0x3E
+#define SW3_CS4 0x3F
+#define SW3_CS5 0x40
+#define SW3_CS6 0x41
+#define SW3_CS7 0x42
+#define SW3_CS8 0x43
+#define SW3_CS9 0x44
+#define SW3_CS10 0x45
+#define SW3_CS11 0x46
+#define SW3_CS12 0x47
+#define SW3_CS13 0x48
+#define SW3_CS14 0x49
+#define SW3_CS15 0x4A
+#define SW3_CS16 0x4B
+#define SW3_CS17 0x4C
+#define SW3_CS18 0x4D
+#define SW3_CS19 0x4E
+#define SW3_CS20 0x4F
+#define SW3_CS21 0x50
+#define SW3_CS22 0x51
+#define SW3_CS23 0x52
+#define SW3_CS24 0x53
+#define SW3_CS25 0x54
+#define SW3_CS26 0x55
+#define SW3_CS27 0x56
+#define SW3_CS28 0x57
+#define SW3_CS29 0x58
+#define SW3_CS30 0x59
+
+#define SW4_CS1 0x5A
+#define SW4_CS2 0x5B
+#define SW4_CS3 0x5C
+#define SW4_CS4 0x5D
+#define SW4_CS5 0x5E
+#define SW4_CS6 0x5F
+#define SW4_CS7 0x60
+#define SW4_CS8 0x61
+#define SW4_CS9 0x62
+#define SW4_CS10 0x63
+#define SW4_CS11 0x64
+#define SW4_CS12 0x65
+#define SW4_CS13 0x66
+#define SW4_CS14 0x67
+#define SW4_CS15 0x68
+#define SW4_CS16 0x69
+#define SW4_CS17 0x6A
+#define SW4_CS18 0x6B
+#define SW4_CS19 0x6C
+#define SW4_CS20 0x6D
+#define SW4_CS21 0x6E
+#define SW4_CS22 0x6F
+#define SW4_CS23 0x70
+#define SW4_CS24 0x71
+#define SW4_CS25 0x72
+#define SW4_CS26 0x73
+#define SW4_CS27 0x74
+#define SW4_CS28 0x75
+#define SW4_CS29 0x76
+#define SW4_CS30 0x77
+
+#define SW5_CS1 0x78
+#define SW5_CS2 0x79
+#define SW5_CS3 0x7A
+#define SW5_CS4 0x7B
+#define SW5_CS5 0x7C
+#define SW5_CS6 0x7D
+#define SW5_CS7 0x7E
+#define SW5_CS8 0x7F
+#define SW5_CS9 0x80
+#define SW5_CS10 0x81
+#define SW5_CS11 0x82
+#define SW5_CS12 0x83
+#define SW5_CS13 0x84
+#define SW5_CS14 0x85
+#define SW5_CS15 0x86
+#define SW5_CS16 0x87
+#define SW5_CS17 0x88
+#define SW5_CS18 0x89
+#define SW5_CS19 0x8A
+#define SW5_CS20 0x8B
+#define SW5_CS21 0x8C
+#define SW5_CS22 0x8D
+#define SW5_CS23 0x8E
+#define SW5_CS24 0x8F
+#define SW5_CS25 0x90
+#define SW5_CS26 0x91
+#define SW5_CS27 0x92
+#define SW5_CS28 0x93
+#define SW5_CS29 0x94
+#define SW5_CS30 0x95
+
+#define SW6_CS1 0x96
+#define SW6_CS2 0x97
+#define SW6_CS3 0x98
+#define SW6_CS4 0x99
+#define SW6_CS5 0x9A
+#define SW6_CS6 0x9B
+#define SW6_CS7 0x9C
+#define SW6_CS8 0x9D
+#define SW6_CS9 0x9E
+#define SW6_CS10 0x9F
+#define SW6_CS11 0xA0
+#define SW6_CS12 0xA1
+#define SW6_CS13 0xA2
+#define SW6_CS14 0xA3
+#define SW6_CS15 0xA4
+#define SW6_CS16 0xA5
+#define SW6_CS17 0xA6
+#define SW6_CS18 0xA7
+#define SW6_CS19 0xA8
+#define SW6_CS20 0xA9
+#define SW6_CS21 0xAA
+#define SW6_CS22 0xAB
+#define SW6_CS23 0xAC
+#define SW6_CS24 0xAD
+#define SW6_CS25 0xAE
+#define SW6_CS26 0xAF
+#define SW6_CS27 0xB0
+#define SW6_CS28 0xB1
+#define SW6_CS29 0xB2
+#define SW6_CS30 0xB3
+
+#define SW7_CS1 0x100
+#define SW7_CS2 0x101
+#define SW7_CS3 0x102
+#define SW7_CS4 0x103
+#define SW7_CS5 0x104
+#define SW7_CS6 0x105
+#define SW7_CS7 0x106
+#define SW7_CS8 0x107
+#define SW7_CS9 0x108
+#define SW7_CS10 0x109
+#define SW7_CS11 0x10A
+#define SW7_CS12 0x10B
+#define SW7_CS13 0x10C
+#define SW7_CS14 0x10D
+#define SW7_CS15 0x10E
+#define SW7_CS16 0x10F
+#define SW7_CS17 0x110
+#define SW7_CS18 0x111
+#define SW7_CS19 0x112
+#define SW7_CS20 0x113
+#define SW7_CS21 0x114
+#define SW7_CS22 0x115
+#define SW7_CS23 0x116
+#define SW7_CS24 0x117
+#define SW7_CS25 0x118
+#define SW7_CS26 0x119
+#define SW7_CS27 0x11A
+#define SW7_CS28 0x11B
+#define SW7_CS29 0x11C
+#define SW7_CS30 0x11D
+
+#define SW8_CS1 0x11E
+#define SW8_CS2 0x11F
+#define SW8_CS3 0x120
+#define SW8_CS4 0x121
+#define SW8_CS5 0x122
+#define SW8_CS6 0x123
+#define SW8_CS7 0x124
+#define SW8_CS8 0x125
+#define SW8_CS9 0x126
+#define SW8_CS10 0x127
+#define SW8_CS11 0x128
+#define SW8_CS12 0x129
+#define SW8_CS13 0x12A
+#define SW8_CS14 0x12B
+#define SW8_CS15 0x12C
+#define SW8_CS16 0x12D
+#define SW8_CS17 0x12E
+#define SW8_CS18 0x12F
+#define SW8_CS19 0x130
+#define SW8_CS20 0x131
+#define SW8_CS21 0x132
+#define SW8_CS22 0x133
+#define SW8_CS23 0x134
+#define SW8_CS24 0x135
+#define SW8_CS25 0x136
+#define SW8_CS26 0x137
+#define SW8_CS27 0x138
+#define SW8_CS28 0x139
+#define SW8_CS29 0x13A
+#define SW8_CS30 0x13B
+
+#define SW9_CS1 0x13C
+#define SW9_CS2 0x13D
+#define SW9_CS3 0x13E
+#define SW9_CS4 0x13F
+#define SW9_CS5 0x140
+#define SW9_CS6 0x141
+#define SW9_CS7 0x142
+#define SW9_CS8 0x143
+#define SW9_CS9 0x144
+#define SW9_CS10 0x145
+#define SW9_CS11 0x146
+#define SW9_CS12 0x147
+#define SW9_CS13 0x148
+#define SW9_CS14 0x149
+#define SW9_CS15 0x14A
+#define SW9_CS16 0x14B
+#define SW9_CS17 0x14C
+#define SW9_CS18 0x14D
+#define SW9_CS19 0x14E
+#define SW9_CS20 0x14F
+#define SW9_CS21 0x150
+#define SW9_CS22 0x151
+#define SW9_CS23 0x152
+#define SW9_CS24 0x153
+#define SW9_CS25 0x154
+#define SW9_CS26 0x155
+#define SW9_CS27 0x156
+#define SW9_CS28 0x157
+#define SW9_CS29 0x158
+#define SW9_CS30 0x159
+
+#define SW1_CS31 0x15A
+#define SW1_CS32 0x15B
+#define SW1_CS33 0x15C
+#define SW1_CS34 0x15D
+#define SW1_CS35 0x15E
+#define SW1_CS36 0x15F
+#define SW1_CS37 0x160
+#define SW1_CS38 0x161
+#define SW1_CS39 0x162
+
+#define SW2_CS31 0x163
+#define SW2_CS32 0x164
+#define SW2_CS33 0x165
+#define SW2_CS34 0x166
+#define SW2_CS35 0x167
+#define SW2_CS36 0x168
+#define SW2_CS37 0x169
+#define SW2_CS38 0x16A
+#define SW2_CS39 0x16B
+
+#define SW3_CS31 0x16C
+#define SW3_CS32 0x16D
+#define SW3_CS33 0x16E
+#define SW3_CS34 0x16F
+#define SW3_CS35 0x170
+#define SW3_CS36 0x171
+#define SW3_CS37 0x172
+#define SW3_CS38 0x173
+#define SW3_CS39 0x174
+
+#define SW4_CS31 0x175
+#define SW4_CS32 0x176
+#define SW4_CS33 0x177
+#define SW4_CS34 0x178
+#define SW4_CS35 0x179
+#define SW4_CS36 0x17A
+#define SW4_CS37 0x17B
+#define SW4_CS38 0x17C
+#define SW4_CS39 0x17D
+
+#define SW5_CS31 0x17E
+#define SW5_CS32 0x17F
+#define SW5_CS33 0x180
+#define SW5_CS34 0x181
+#define SW5_CS35 0x182
+#define SW5_CS36 0x183
+#define SW5_CS37 0x184
+#define SW5_CS38 0x185
+#define SW5_CS39 0x186
+
+#define SW6_CS31 0x187
+#define SW6_CS32 0x188
+#define SW6_CS33 0x189
+#define SW6_CS34 0x18A
+#define SW6_CS35 0x18B
+#define SW6_CS36 0x18C
+#define SW6_CS37 0x18D
+#define SW6_CS38 0x18E
+#define SW6_CS39 0x18F
+
+#define SW7_CS31 0x190
+#define SW7_CS32 0x191
+#define SW7_CS33 0x192
+#define SW7_CS34 0x193
+#define SW7_CS35 0x194
+#define SW7_CS36 0x195
+#define SW7_CS37 0x196
+#define SW7_CS38 0x197
+#define SW7_CS39 0x198
+
+#define SW8_CS31 0x199
+#define SW8_CS32 0x19A
+#define SW8_CS33 0x19B
+#define SW8_CS34 0x19C
+#define SW8_CS35 0x19D
+#define SW8_CS36 0x19E
+#define SW8_CS37 0x19F
+#define SW8_CS38 0x1A0
+#define SW8_CS39 0x1A1
+
+#define SW9_CS31 0x1A2
+#define SW9_CS32 0x1A3
+#define SW9_CS33 0x1A4
+#define SW9_CS34 0x1A5
+#define SW9_CS35 0x1A6
+#define SW9_CS36 0x1A7
+#define SW9_CS37 0x1A8
+#define SW9_CS38 0x1A9
+#define SW9_CS39 0x1AA
+
+// DEPRECATED - DO NOT USE
+
+#define CS1_SW1 SW1_CS1
+#define CS2_SW1 SW1_CS2
+#define CS3_SW1 SW1_CS3
+#define CS4_SW1 SW1_CS4
+#define CS5_SW1 SW1_CS5
+#define CS6_SW1 SW1_CS6
+#define CS7_SW1 SW1_CS7
+#define CS8_SW1 SW1_CS8
+#define CS9_SW1 SW1_CS9
+#define CS10_SW1 SW1_CS10
+#define CS11_SW1 SW1_CS11
+#define CS12_SW1 SW1_CS12
+#define CS13_SW1 SW1_CS13
+#define CS14_SW1 SW1_CS14
+#define CS15_SW1 SW1_CS15
+#define CS16_SW1 SW1_CS16
+#define CS17_SW1 SW1_CS17
+#define CS18_SW1 SW1_CS18
+#define CS19_SW1 SW1_CS19
+#define CS20_SW1 SW1_CS20
+#define CS21_SW1 SW1_CS21
+#define CS22_SW1 SW1_CS22
+#define CS23_SW1 SW1_CS23
+#define CS24_SW1 SW1_CS24
+#define CS25_SW1 SW1_CS25
+#define CS26_SW1 SW1_CS26
+#define CS27_SW1 SW1_CS27
+#define CS28_SW1 SW1_CS28
+#define CS29_SW1 SW1_CS29
+#define CS30_SW1 SW1_CS30
+
+#define CS1_SW2 SW2_CS1
+#define CS2_SW2 SW2_CS2
+#define CS3_SW2 SW2_CS3
+#define CS4_SW2 SW2_CS4
+#define CS5_SW2 SW2_CS5
+#define CS6_SW2 SW2_CS6
+#define CS7_SW2 SW2_CS7
+#define CS8_SW2 SW2_CS8
+#define CS9_SW2 SW2_CS9
+#define CS10_SW2 SW2_CS10
+#define CS11_SW2 SW2_CS11
+#define CS12_SW2 SW2_CS12
+#define CS13_SW2 SW2_CS13
+#define CS14_SW2 SW2_CS14
+#define CS15_SW2 SW2_CS15
+#define CS16_SW2 SW2_CS16
+#define CS17_SW2 SW2_CS17
+#define CS18_SW2 SW2_CS18
+#define CS19_SW2 SW2_CS19
+#define CS20_SW2 SW2_CS20
+#define CS21_SW2 SW2_CS21
+#define CS22_SW2 SW2_CS22
+#define CS23_SW2 SW2_CS23
+#define CS24_SW2 SW2_CS24
+#define CS25_SW2 SW2_CS25
+#define CS26_SW2 SW2_CS26
+#define CS27_SW2 SW2_CS27
+#define CS28_SW2 SW2_CS28
+#define CS29_SW2 SW2_CS29
+#define CS30_SW2 SW2_CS30
+
+#define CS1_SW3 SW3_CS1
+#define CS2_SW3 SW3_CS2
+#define CS3_SW3 SW3_CS3
+#define CS4_SW3 SW3_CS4
+#define CS5_SW3 SW3_CS5
+#define CS6_SW3 SW3_CS6
+#define CS7_SW3 SW3_CS7
+#define CS8_SW3 SW3_CS8
+#define CS9_SW3 SW3_CS9
+#define CS10_SW3 SW3_CS10
+#define CS11_SW3 SW3_CS11
+#define CS12_SW3 SW3_CS12
+#define CS13_SW3 SW3_CS13
+#define CS14_SW3 SW3_CS14
+#define CS15_SW3 SW3_CS15
+#define CS16_SW3 SW3_CS16
+#define CS17_SW3 SW3_CS17
+#define CS18_SW3 SW3_CS18
+#define CS19_SW3 SW3_CS19
+#define CS20_SW3 SW3_CS20
+#define CS21_SW3 SW3_CS21
+#define CS22_SW3 SW3_CS22
+#define CS23_SW3 SW3_CS23
+#define CS24_SW3 SW3_CS24
+#define CS25_SW3 SW3_CS25
+#define CS26_SW3 SW3_CS26
+#define CS27_SW3 SW3_CS27
+#define CS28_SW3 SW3_CS28
+#define CS29_SW3 SW3_CS29
+#define CS30_SW3 SW3_CS30
+
+#define CS1_SW4 SW4_CS1
+#define CS2_SW4 SW4_CS2
+#define CS3_SW4 SW4_CS3
+#define CS4_SW4 SW4_CS4
+#define CS5_SW4 SW4_CS5
+#define CS6_SW4 SW4_CS6
+#define CS7_SW4 SW4_CS7
+#define CS8_SW4 SW4_CS8
+#define CS9_SW4 SW4_CS9
+#define CS10_SW4 SW4_CS10
+#define CS11_SW4 SW4_CS11
+#define CS12_SW4 SW4_CS12
+#define CS13_SW4 SW4_CS13
+#define CS14_SW4 SW4_CS14
+#define CS15_SW4 SW4_CS15
+#define CS16_SW4 SW4_CS16
+#define CS17_SW4 SW4_CS17
+#define CS18_SW4 SW4_CS18
+#define CS19_SW4 SW4_CS19
+#define CS20_SW4 SW4_CS20
+#define CS21_SW4 SW4_CS21
+#define CS22_SW4 SW4_CS22
+#define CS23_SW4 SW4_CS23
+#define CS24_SW4 SW4_CS24
+#define CS25_SW4 SW4_CS25
+#define CS26_SW4 SW4_CS26
+#define CS27_SW4 SW4_CS27
+#define CS28_SW4 SW4_CS28
+#define CS29_SW4 SW4_CS29
+#define CS30_SW4 SW4_CS30
+
+#define CS1_SW5 SW5_CS1
+#define CS2_SW5 SW5_CS2
+#define CS3_SW5 SW5_CS3
+#define CS4_SW5 SW5_CS4
+#define CS5_SW5 SW5_CS5
+#define CS6_SW5 SW5_CS6
+#define CS7_SW5 SW5_CS7
+#define CS8_SW5 SW5_CS8
+#define CS9_SW5 SW5_CS9
+#define CS10_SW5 SW5_CS10
+#define CS11_SW5 SW5_CS11
+#define CS12_SW5 SW5_CS12
+#define CS13_SW5 SW5_CS13
+#define CS14_SW5 SW5_CS14
+#define CS15_SW5 SW5_CS15
+#define CS16_SW5 SW5_CS16
+#define CS17_SW5 SW5_CS17
+#define CS18_SW5 SW5_CS18
+#define CS19_SW5 SW5_CS19
+#define CS20_SW5 SW5_CS20
+#define CS21_SW5 SW5_CS21
+#define CS22_SW5 SW5_CS22
+#define CS23_SW5 SW5_CS23
+#define CS24_SW5 SW5_CS24
+#define CS25_SW5 SW5_CS25
+#define CS26_SW5 SW5_CS26
+#define CS27_SW5 SW5_CS27
+#define CS28_SW5 SW5_CS28
+#define CS29_SW5 SW5_CS29
+#define CS30_SW5 SW5_CS30
+
+#define CS1_SW6 SW6_CS1
+#define CS2_SW6 SW6_CS2
+#define CS3_SW6 SW6_CS3
+#define CS4_SW6 SW6_CS4
+#define CS5_SW6 SW6_CS5
+#define CS6_SW6 SW6_CS6
+#define CS7_SW6 SW6_CS7
+#define CS8_SW6 SW6_CS8
+#define CS9_SW6 SW6_CS9
+#define CS10_SW6 SW6_CS10
+#define CS11_SW6 SW6_CS11
+#define CS12_SW6 SW6_CS12
+#define CS13_SW6 SW6_CS13
+#define CS14_SW6 SW6_CS14
+#define CS15_SW6 SW6_CS15
+#define CS16_SW6 SW6_CS16
+#define CS17_SW6 SW6_CS17
+#define CS18_SW6 SW6_CS18
+#define CS19_SW6 SW6_CS19
+#define CS20_SW6 SW6_CS20
+#define CS21_SW6 SW6_CS21
+#define CS22_SW6 SW6_CS22
+#define CS23_SW6 SW6_CS23
+#define CS24_SW6 SW6_CS24
+#define CS25_SW6 SW6_CS25
+#define CS26_SW6 SW6_CS26
+#define CS27_SW6 SW6_CS27
+#define CS28_SW6 SW6_CS28
+#define CS29_SW6 SW6_CS29
+#define CS30_SW6 SW6_CS30
+
+#define CS1_SW7 SW7_CS1
+#define CS2_SW7 SW7_CS2
+#define CS3_SW7 SW7_CS3
+#define CS4_SW7 SW7_CS4
+#define CS5_SW7 SW7_CS5
+#define CS6_SW7 SW7_CS6
+#define CS7_SW7 SW7_CS7
+#define CS8_SW7 SW7_CS8
+#define CS9_SW7 SW7_CS9
+#define CS10_SW7 SW7_CS10
+#define CS11_SW7 SW7_CS11
+#define CS12_SW7 SW7_CS12
+#define CS13_SW7 SW7_CS13
+#define CS14_SW7 SW7_CS14
+#define CS15_SW7 SW7_CS15
+#define CS16_SW7 SW7_CS16
+#define CS17_SW7 SW7_CS17
+#define CS18_SW7 SW7_CS18
+#define CS19_SW7 SW7_CS19
+#define CS20_SW7 SW7_CS20
+#define CS21_SW7 SW7_CS21
+#define CS22_SW7 SW7_CS22
+#define CS23_SW7 SW7_CS23
+#define CS24_SW7 SW7_CS24
+#define CS25_SW7 SW7_CS25
+#define CS26_SW7 SW7_CS26
+#define CS27_SW7 SW7_CS27
+#define CS28_SW7 SW7_CS28
+#define CS29_SW7 SW7_CS29
+#define CS30_SW7 SW7_CS30
+
+#define CS1_SW8 SW8_CS1
+#define CS2_SW8 SW8_CS2
+#define CS3_SW8 SW8_CS3
+#define CS4_SW8 SW8_CS4
+#define CS5_SW8 SW8_CS5
+#define CS6_SW8 SW8_CS6
+#define CS7_SW8 SW8_CS7
+#define CS8_SW8 SW8_CS8
+#define CS9_SW8 SW8_CS9
+#define CS10_SW8 SW8_CS10
+#define CS11_SW8 SW8_CS11
+#define CS12_SW8 SW8_CS12
+#define CS13_SW8 SW8_CS13
+#define CS14_SW8 SW8_CS14
+#define CS15_SW8 SW8_CS15
+#define CS16_SW8 SW8_CS16
+#define CS17_SW8 SW8_CS17
+#define CS18_SW8 SW8_CS18
+#define CS19_SW8 SW8_CS19
+#define CS20_SW8 SW8_CS20
+#define CS21_SW8 SW8_CS21
+#define CS22_SW8 SW8_CS22
+#define CS23_SW8 SW8_CS23
+#define CS24_SW8 SW8_CS24
+#define CS25_SW8 SW8_CS25
+#define CS26_SW8 SW8_CS26
+#define CS27_SW8 SW8_CS27
+#define CS28_SW8 SW8_CS28
+#define CS29_SW8 SW8_CS29
+#define CS30_SW8 SW8_CS30
+
+#define CS1_SW9 SW9_CS1
+#define CS2_SW9 SW9_CS2
+#define CS3_SW9 SW9_CS3
+#define CS4_SW9 SW9_CS4
+#define CS5_SW9 SW9_CS5
+#define CS6_SW9 SW9_CS6
+#define CS7_SW9 SW9_CS7
+#define CS8_SW9 SW9_CS8
+#define CS9_SW9 SW9_CS9
+#define CS10_SW9 SW9_CS10
+#define CS11_SW9 SW9_CS11
+#define CS12_SW9 SW9_CS12
+#define CS13_SW9 SW9_CS13
+#define CS14_SW9 SW9_CS14
+#define CS15_SW9 SW9_CS15
+#define CS16_SW9 SW9_CS16
+#define CS17_SW9 SW9_CS17
+#define CS18_SW9 SW9_CS18
+#define CS19_SW9 SW9_CS19
+#define CS20_SW9 SW9_CS20
+#define CS21_SW9 SW9_CS21
+#define CS22_SW9 SW9_CS22
+#define CS23_SW9 SW9_CS23
+#define CS24_SW9 SW9_CS24
+#define CS25_SW9 SW9_CS25
+#define CS26_SW9 SW9_CS26
+#define CS27_SW9 SW9_CS27
+#define CS28_SW9 SW9_CS28
+#define CS29_SW9 SW9_CS29
+#define CS30_SW9 SW9_CS30
+
+#define CS31_SW1 SW1_CS31
+#define CS32_SW1 SW1_CS32
+#define CS33_SW1 SW1_CS33
+#define CS34_SW1 SW1_CS34
+#define CS35_SW1 SW1_CS35
+#define CS36_SW1 SW1_CS36
+#define CS37_SW1 SW1_CS37
+#define CS38_SW1 SW1_CS38
+#define CS39_SW1 SW1_CS39
+
+#define CS31_SW2 SW2_CS31
+#define CS32_SW2 SW2_CS32
+#define CS33_SW2 SW2_CS33
+#define CS34_SW2 SW2_CS34
+#define CS35_SW2 SW2_CS35
+#define CS36_SW2 SW2_CS36
+#define CS37_SW2 SW2_CS37
+#define CS38_SW2 SW2_CS38
+#define CS39_SW2 SW2_CS39
+
+#define CS31_SW3 SW3_CS31
+#define CS32_SW3 SW3_CS32
+#define CS33_SW3 SW3_CS33
+#define CS34_SW3 SW3_CS34
+#define CS35_SW3 SW3_CS35
+#define CS36_SW3 SW3_CS36
+#define CS37_SW3 SW3_CS37
+#define CS38_SW3 SW3_CS38
+#define CS39_SW3 SW3_CS39
+
+#define CS31_SW4 SW4_CS31
+#define CS32_SW4 SW4_CS32
+#define CS33_SW4 SW4_CS33
+#define CS34_SW4 SW4_CS34
+#define CS35_SW4 SW4_CS35
+#define CS36_SW4 SW4_CS36
+#define CS37_SW4 SW4_CS37
+#define CS38_SW4 SW4_CS38
+#define CS39_SW4 SW4_CS39
+
+#define CS31_SW5 SW5_CS31
+#define CS32_SW5 SW5_CS32
+#define CS33_SW5 SW5_CS33
+#define CS34_SW5 SW5_CS34
+#define CS35_SW5 SW5_CS35
+#define CS36_SW5 SW5_CS36
+#define CS37_SW5 SW5_CS37
+#define CS38_SW5 SW5_CS38
+#define CS39_SW5 SW5_CS39
+
+#define CS31_SW6 SW6_CS31
+#define CS32_SW6 SW6_CS32
+#define CS33_SW6 SW6_CS33
+#define CS34_SW6 SW6_CS34
+#define CS35_SW6 SW6_CS35
+#define CS36_SW6 SW6_CS36
+#define CS37_SW6 SW6_CS37
+#define CS38_SW6 SW6_CS38
+#define CS39_SW6 SW6_CS39
+
+#define CS31_SW7 SW7_CS31
+#define CS32_SW7 SW7_CS32
+#define CS33_SW7 SW7_CS33
+#define CS34_SW7 SW7_CS34
+#define CS35_SW7 SW7_CS35
+#define CS36_SW7 SW7_CS36
+#define CS37_SW7 SW7_CS37
+#define CS38_SW7 SW7_CS38
+#define CS39_SW7 SW7_CS39
+
+#define CS31_SW8 SW8_CS31
+#define CS32_SW8 SW8_CS32
+#define CS33_SW8 SW8_CS33
+#define CS34_SW8 SW8_CS34
+#define CS35_SW8 SW8_CS35
+#define CS36_SW8 SW8_CS36
+#define CS37_SW8 SW8_CS37
+#define CS38_SW8 SW8_CS38
+#define CS39_SW8 SW8_CS39
+
+#define CS31_SW9 SW9_CS31
+#define CS32_SW9 SW9_CS32
+#define CS33_SW9 SW9_CS33
+#define CS34_SW9 SW9_CS34
+#define CS35_SW9 SW9_CS35
+#define CS36_SW9 SW9_CS36
+#define CS37_SW9 SW9_CS37
+#define CS38_SW9 SW9_CS38
+#define CS39_SW9 SW9_CS39
diff --git a/drivers/led/issi/is31fl3741-simple.c b/drivers/led/issi/is31fl3741-simple.c
deleted file mode 100644
index f7009853ba..0000000000
--- a/drivers/led/issi/is31fl3741-simple.c
+++ /dev/null
@@ -1,278 +0,0 @@
-/* Copyright 2017 Jason Williams
- * Copyright 2018 Jack Humbert
- * Copyright 2018 Yiancar
- * Copyright 2020 MelGeek
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#include "is31fl3741-simple.h"
-#include <string.h>
-#include "i2c_master.h"
-#include "wait.h"
-
-#define IS31FL3741_PWM_REGISTER_COUNT 351
-
-#ifndef IS31FL3741_I2C_TIMEOUT
-# define IS31FL3741_I2C_TIMEOUT 100
-#endif
-
-#ifndef IS31FL3741_I2C_PERSISTENCE
-# define IS31FL3741_I2C_PERSISTENCE 0
-#endif
-
-#ifndef IS31FL3741_CONFIGURATION
-# define IS31FL3741_CONFIGURATION 0x01
-#endif
-
-#ifndef IS31FL3741_PWM_FREQUENCY
-# define IS31FL3741_PWM_FREQUENCY IS31FL3741_PWM_FREQUENCY_29K_HZ
-#endif
-
-#ifndef IS31FL3741_SW_PULLUP
-# define IS31FL3741_SW_PULLUP IS31FL3741_PUR_32K_OHM
-#endif
-
-#ifndef IS31FL3741_CS_PULLDOWN
-# define IS31FL3741_CS_PULLDOWN IS31FL3741_PDR_32K_OHM
-#endif
-
-#ifndef IS31FL3741_GLOBAL_CURRENT
-# define IS31FL3741_GLOBAL_CURRENT 0xFF
-#endif
-
-// Transfer buffer for TWITransmitData()
-uint8_t g_twi_transfer_buffer[20] = {0xFF};
-
-// These buffers match the IS31FL3741 and IS31FL3741A PWM registers.
-// The scaling buffers match the PG2 and PG3 LED On/Off registers.
-// Storing them like this is optimal for I2C transfers to the registers.
-// We could optimize this and take out the unused registers from these
-// buffers and the transfers in is31fl3741_write_pwm_buffer() but it's
-// probably not worth the extra complexity.
-uint8_t g_pwm_buffer[IS31FL3741_DRIVER_COUNT][IS31FL3741_PWM_REGISTER_COUNT];
-bool g_pwm_buffer_update_required[IS31FL3741_DRIVER_COUNT] = {false};
-bool g_scaling_registers_update_required[IS31FL3741_DRIVER_COUNT] = {false};
-
-uint8_t g_scaling_registers[IS31FL3741_DRIVER_COUNT][IS31FL3741_PWM_REGISTER_COUNT];
-
-void is31fl3741_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
- g_twi_transfer_buffer[0] = reg;
- g_twi_transfer_buffer[1] = data;
-
-#if IS31FL3741_I2C_PERSISTENCE > 0
- for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3741_I2C_TIMEOUT) == 0) break;
- }
-#else
- i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3741_I2C_TIMEOUT);
-#endif
-}
-
-bool is31fl3741_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
- // Assume PG0 is already selected
-
- for (int i = 0; i < 342; i += 18) {
- if (i == 180) {
- // unlock the command register and select PG1
- is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC);
- is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND, IS31FL3741_COMMAND_PWM_1);
- }
-
- g_twi_transfer_buffer[0] = i % 180;
- memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 18);
-
-#if IS31FL3741_I2C_PERSISTENCE > 0
- for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 19, IS31FL3741_I2C_TIMEOUT) != 0) {
- return false;
- }
- }
-#else
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 19, IS31FL3741_I2C_TIMEOUT) != 0) {
- return false;
- }
-#endif
- }
-
- // transfer the left cause the total number is 351
- g_twi_transfer_buffer[0] = 162;
- memcpy(g_twi_transfer_buffer + 1, pwm_buffer + 342, 9);
-
-#if IS31FL3741_I2C_PERSISTENCE > 0
- for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 10, IS31FL3741_I2C_TIMEOUT) != 0) {
- return false;
- }
- }
-#else
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 10, IS31FL3741_I2C_TIMEOUT) != 0) {
- return false;
- }
-#endif
-
- return true;
-}
-
-void is31fl3741_init_drivers(void) {
- i2c_init();
-
- is31fl3741_init(IS31FL3741_I2C_ADDRESS_1);
-#if defined(IS31FL3741_I2C_ADDRESS_2)
- is31fl3741_init(IS31FL3741_I2C_ADDRESS_2);
-# if defined(IS31FL3741_I2C_ADDRESS_3)
- is31fl3741_init(IS31FL3741_I2C_ADDRESS_3);
-# if defined(IS31FL3741_I2C_ADDRESS_4)
- is31fl3741_init(IS31FL3741_I2C_ADDRESS_4);
-# endif
-# endif
-#endif
-
- for (int i = 0; i < IS31FL3741_LED_COUNT; i++) {
- is31fl3741_set_led_control_register(i, true);
- }
-
- is31fl3741_update_led_control_registers(IS31FL3741_I2C_ADDRESS_1, 0);
-#if defined(IS31FL3741_I2C_ADDRESS_2)
- is31fl3741_update_led_control_registers(IS31FL3741_I2C_ADDRESS_2, 1);
-# if defined(IS31FL3741_I2C_ADDRESS_3)
- is31fl3741_update_led_control_registers(IS31FL3741_I2C_ADDRESS_3, 2);
-# if defined(IS31FL3741_I2C_ADDRESS_4)
- is31fl3741_update_led_control_registers(IS31FL3741_I2C_ADDRESS_4, 3);
-# endif
-# endif
-#endif
-}
-
-void is31fl3741_init(uint8_t addr) {
- // In order to avoid the LEDs being driven with garbage data
- // in the LED driver's PWM registers, shutdown is enabled last.
- // Set up the mode and other settings, clear the PWM registers,
- // then disable software shutdown.
- // Unlock the command register.
-
- // Unlock the command register.
- is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC);
-
- // Select PG4
- is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND, IS31FL3741_COMMAND_FUNCTION);
-
- // Set to Normal operation
- is31fl3741_write_register(addr, IS31FL3741_FUNCTION_REG_CONFIGURATION, IS31FL3741_CONFIGURATION);
-
- // Set Golbal Current Control Register
- is31fl3741_write_register(addr, IS31FL3741_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3741_GLOBAL_CURRENT);
- // Set Pull up & Down for SWx CSy
- is31fl3741_write_register(addr, IS31FL3741_FUNCTION_REG_PULLDOWNUP, ((IS31FL3741_CS_PULLDOWN << 4) | IS31FL3741_SW_PULLUP));
- // Set PWM frequency
- is31fl3741_write_register(addr, IS31FL3741_FUNCTION_REG_PWM_FREQUENCY, (IS31FL3741_PWM_FREQUENCY & 0b1111));
-
- // is31fl3741_update_led_scaling_registers(addr, 0xFF, 0xFF, 0xFF);
-
- // Wait 10ms to ensure the device has woken up.
- wait_ms(10);
-}
-
-void is31fl3741_set_value(int index, uint8_t value) {
- is31fl3741_led_t led;
- if (index >= 0 && index < IS31FL3741_LED_COUNT) {
- memcpy_P(&led, (&g_is31fl3741_leds[index]), sizeof(led));
-
- if (g_pwm_buffer[led.driver][led.v] == value) {
- return;
- }
- g_pwm_buffer_update_required[led.driver] = true;
- g_pwm_buffer[led.driver][led.v] = value;
- }
-}
-
-void is31fl3741_set_value_all(uint8_t value) {
- for (int i = 0; i < IS31FL3741_LED_COUNT; i++) {
- is31fl3741_set_value(i, value);
- }
-}
-
-void is31fl3741_set_led_control_register(uint8_t index, bool value) {
- is31fl3741_led_t led;
- memcpy_P(&led, (&g_is31fl3741_leds[index]), sizeof(led));
-
- if (value) {
- g_scaling_registers[led.driver][led.v] = 0xFF;
- } else {
- g_scaling_registers[led.driver][led.v] = 0x00;
- }
-
- g_scaling_registers_update_required[led.driver] = true;
-}
-
-void is31fl3741_update_pwm_buffers(uint8_t addr, uint8_t index) {
- if (g_pwm_buffer_update_required[index]) {
- // unlock the command register and select PG2
- is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC);
- is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND, IS31FL3741_COMMAND_PWM_0);
-
- is31fl3741_write_pwm_buffer(addr, g_pwm_buffer[index]);
- }
-
- g_pwm_buffer_update_required[index] = false;
-}
-
-void is31fl3741_set_pwm_buffer(const is31fl3741_led_t *pled, uint8_t value) {
- g_pwm_buffer[pled->driver][pled->v] = value;
-
- g_pwm_buffer_update_required[pled->driver] = true;
-}
-
-void is31fl3741_update_led_control_registers(uint8_t addr, uint8_t index) {
- if (g_scaling_registers_update_required[index]) {
- // unlock the command register and select PG2
- is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC);
- is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND, IS31FL3741_COMMAND_SCALING_0);
-
- // CS1_SW1 to CS30_SW6 are on PG2
- for (int i = CS1_SW1; i <= CS30_SW6; ++i) {
- is31fl3741_write_register(addr, i, g_scaling_registers[index][i]);
- }
-
- // unlock the command register and select PG3
- is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC);
- is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND, IS31FL3741_COMMAND_SCALING_1);
-
- // CS1_SW7 to CS39_SW9 are on PG3
- for (int i = CS1_SW7; i <= CS39_SW9; ++i) {
- is31fl3741_write_register(addr, i - CS1_SW7, g_scaling_registers[index][i]);
- }
-
- g_scaling_registers_update_required[index] = false;
- }
-}
-
-void is31fl3741_set_scaling_registers(const is31fl3741_led_t *pled, uint8_t value) {
- g_scaling_registers[pled->driver][pled->v] = value;
-
- g_scaling_registers_update_required[pled->driver] = true;
-}
-
-void is31fl3741_flush(void) {
- is31fl3741_update_pwm_buffers(IS31FL3741_I2C_ADDRESS_1, 0);
-#if defined(IS31FL3741_I2C_ADDRESS_2)
- is31fl3741_update_pwm_buffers(IS31FL3741_I2C_ADDRESS_2, 1);
-# if defined(IS31FL3741_I2C_ADDRESS_3)
- is31fl3741_update_pwm_buffers(IS31FL3741_I2C_ADDRESS_3, 2);
-# if defined(IS31FL3741_I2C_ADDRESS_4)
- is31fl3741_update_pwm_buffers(IS31FL3741_I2C_ADDRESS_4, 3);
-# endif
-# endif
-#endif
-}
diff --git a/drivers/led/issi/is31fl3741-simple.h b/drivers/led/issi/is31fl3741-simple.h
deleted file mode 100644
index 34608a37e0..0000000000
--- a/drivers/led/issi/is31fl3741-simple.h
+++ /dev/null
@@ -1,516 +0,0 @@
-/* Copyright 2017 Jason Williams
- * Copyright 2018 Jack Humbert
- * Copyright 2018 Yiancar
- * Copyright 2020 MelGeek
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-#include <stdint.h>
-#include <stdbool.h>
-#include "progmem.h"
-#include "util.h"
-
-// ======== DEPRECATED DEFINES - DO NOT USE ========
-#ifdef ISSI_TIMEOUT
-# define IS31FL3741_I2C_TIMEOUT ISSI_TIMEOUT
-#endif
-#ifdef ISSI_PERSISTENCE
-# define IS31FL3741_I2C_PERSISTENCE ISSI_PERSISTENCE
-#endif
-#ifdef ISSI_CONFIGURATION
-# define IS31FL3741_CONFIGURATION ISSI_CONFIGURATION
-#endif
-#ifdef ISSI_SWPULLUP
-# define IS31FL3741_SW_PULLUP ISSI_SWPULLUP
-#endif
-#ifdef ISSI_CSPULLUP
-# define IS31FL3741_CS_PULLDOWN ISSI_CSPULLUP
-#endif
-#ifdef ISSI_GLOBALCURRENT
-# define IS31FL3741_GLOBAL_CURRENT ISSI_GLOBALCURRENT
-#endif
-
-#define PUR_0R IS31FL3741_PUR_0_OHM
-#define PUR_05KR IS31FL3741_PUR_0K5_OHM
-#define PUR_1KR IS31FL3741_PUR_1K_OHM
-#define PUR_2KR IS31FL3741_PUR_2K_OHM
-#define PUR_4KR IS31FL3741_PUR_4K_OHM
-#define PUR_8KR IS31FL3741_PUR_8K_OHM
-#define PUR_16KR IS31FL3741_PUR_16K_OHM
-#define PUR_32KR IS31FL3741_PUR_32K_OHM
-// ========
-
-#define IS31FL3741_REG_INTERRUPT_MASK 0xF0
-#define IS31FL3741_REG_INTERRUPT_STATUS 0xF1
-#define IS31FL3741_REG_ID 0xFC
-
-#define IS31FL3741_REG_COMMAND 0xFD
-
-#define IS31FL3741_COMMAND_PWM_0 0x00
-#define IS31FL3741_COMMAND_PWM_1 0x01
-#define IS31FL3741_COMMAND_SCALING_0 0x02
-#define IS31FL3741_COMMAND_SCALING_1 0x03
-#define IS31FL3741_COMMAND_FUNCTION 0x04
-
-#define IS31FL3741_FUNCTION_REG_CONFIGURATION 0x00
-#define IS31FL3741_FUNCTION_REG_GLOBAL_CURRENT 0x01
-#define IS31FL3741_FUNCTION_REG_PULLDOWNUP 0x02
-#define IS31FL3741_FUNCTION_REG_PWM_FREQUENCY 0x36
-#define IS31FL3741_FUNCTION_REG_RESET 0x3F
-
-#define IS31FL3741_REG_COMMAND_WRITE_LOCK 0xFE
-#define IS31FL3741_COMMAND_WRITE_LOCK_MAGIC 0xC5
-
-#define IS31FL3741_I2C_ADDRESS_GND 0x30
-#define IS31FL3741_I2C_ADDRESS_SCL 0x31
-#define IS31FL3741_I2C_ADDRESS_SDA 0x32
-#define IS31FL3741_I2C_ADDRESS_VCC 0x33
-
-#if defined(LED_MATRIX_IS31FL3741)
-# define IS31FL3741_LED_COUNT LED_MATRIX_LED_COUNT
-#endif
-
-#if defined(IS31FL3741_I2C_ADDRESS_4)
-# define IS31FL3741_DRIVER_COUNT 4
-#elif defined(IS31FL3741_I2C_ADDRESS_3)
-# define IS31FL3741_DRIVER_COUNT 3
-#elif defined(IS31FL3741_I2C_ADDRESS_2)
-# define IS31FL3741_DRIVER_COUNT 2
-#elif defined(IS31FL3741_I2C_ADDRESS_1)
-# define IS31FL3741_DRIVER_COUNT 1
-#endif
-
-typedef struct is31fl3741_led_t {
- uint8_t driver : 2;
- uint16_t v : 9;
-} PACKED is31fl3741_led_t;
-
-extern const is31fl3741_led_t PROGMEM g_is31fl3741_leds[IS31FL3741_LED_COUNT];
-
-void is31fl3741_init_drivers(void);
-void is31fl3741_init(uint8_t addr);
-void is31fl3741_write_register(uint8_t addr, uint8_t reg, uint8_t data);
-bool is31fl3741_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
-
-void is31fl3741_set_value(int index, uint8_t value);
-void is31fl3741_set_value_all(uint8_t value);
-
-void is31fl3741_set_led_control_register(uint8_t index, bool value);
-
-// 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 is31fl3741_update_pwm_buffers(uint8_t addr, uint8_t index);
-void is31fl3741_update_led_control_registers(uint8_t addr, uint8_t index);
-void is31fl3741_set_scaling_registers(const is31fl3741_led_t *pled, uint8_t value);
-
-void is31fl3741_set_pwm_buffer(const is31fl3741_led *pled, uint8_t value);
-
-void is31fl3741_flush(void);
-
-#define IS31FL3741_PDR_0_OHM 0b000 // No pull-down resistor
-#define IS31FL3741_PDR_0K5_OHM 0b001 // 0.5 kOhm resistor
-#define IS31FL3741_PDR_1K_OHM 0b010 // 1 kOhm resistor
-#define IS31FL3741_PDR_2K_OHM 0b011 // 2 kOhm resistor
-#define IS31FL3741_PDR_4K_OHM 0b100 // 4 kOhm resistor
-#define IS31FL3741_PDR_8K_OHM 0b101 // 8 kOhm resistor
-#define IS31FL3741_PDR_16K_OHM 0b110 // 16 kOhm resistor
-#define IS31FL3741_PDR_32K_OHM 0b111 // 32 kOhm resistor
-
-#define IS31FL3741_PUR_0_OHM 0b000 // No pull-up resistor
-#define IS31FL3741_PUR_0K5_OHM 0b001 // 0.5 kOhm resistor
-#define IS31FL3741_PUR_1K_OHM 0b010 // 1 kOhm resistor
-#define IS31FL3741_PUR_2K_OHM 0b011 // 2 kOhm resistor
-#define IS31FL3741_PUR_4K_OHM 0b100 // 4 kOhm resistor
-#define IS31FL3741_PUR_8K_OHM 0b101 // 8 kOhm resistor
-#define IS31FL3741_PUR_16K_OHM 0b110 // 16 kOhm resistor
-#define IS31FL3741_PUR_32K_OHM 0b111 // 32 kOhm resistor
-
-#define IS31FL3741_PWM_FREQUENCY_29K_HZ 0b0000
-#define IS31FL3741_PWM_FREQUENCY_3K6_HZ 0b0011
-#define IS31FL3741_PWM_FREQUENCY_1K8_HZ 0b0111
-#define IS31FL3741_PWM_FREQUENCY_900_HZ 0b1011
-
-#define CS1_SW1 0x00
-#define CS2_SW1 0x01
-#define CS3_SW1 0x02
-#define CS4_SW1 0x03
-#define CS5_SW1 0x04
-#define CS6_SW1 0x05
-#define CS7_SW1 0x06
-#define CS8_SW1 0x07
-#define CS9_SW1 0x08
-#define CS10_SW1 0x09
-#define CS11_SW1 0x0A
-#define CS12_SW1 0x0B
-#define CS13_SW1 0x0C
-#define CS14_SW1 0x0D
-#define CS15_SW1 0x0E
-#define CS16_SW1 0x0F
-#define CS17_SW1 0x10
-#define CS18_SW1 0x11
-#define CS19_SW1 0x12
-#define CS20_SW1 0x13
-#define CS21_SW1 0x14
-#define CS22_SW1 0x15
-#define CS23_SW1 0x16
-#define CS24_SW1 0x17
-#define CS25_SW1 0x18
-#define CS26_SW1 0x19
-#define CS27_SW1 0x1A
-#define CS28_SW1 0x1B
-#define CS29_SW1 0x1C
-#define CS30_SW1 0x1D
-
-#define CS1_SW2 0x1E
-#define CS2_SW2 0x1F
-#define CS3_SW2 0x20
-#define CS4_SW2 0x21
-#define CS5_SW2 0x22
-#define CS6_SW2 0x23
-#define CS7_SW2 0x24
-#define CS8_SW2 0x25
-#define CS9_SW2 0x26
-#define CS10_SW2 0x27
-#define CS11_SW2 0x28
-#define CS12_SW2 0x29
-#define CS13_SW2 0x2A
-#define CS14_SW2 0x2B
-#define CS15_SW2 0x2C
-#define CS16_SW2 0x2D
-#define CS17_SW2 0x2E
-#define CS18_SW2 0x2F
-#define CS19_SW2 0x30
-#define CS20_SW2 0x31
-#define CS21_SW2 0x32
-#define CS22_SW2 0x33
-#define CS23_SW2 0x34
-#define CS24_SW2 0x35
-#define CS25_SW2 0x36
-#define CS26_SW2 0x37
-#define CS27_SW2 0x38
-#define CS28_SW2 0x39
-#define CS29_SW2 0x3A
-#define CS30_SW2 0x3B
-
-#define CS1_SW3 0x3C
-#define CS2_SW3 0x3D
-#define CS3_SW3 0x3E
-#define CS4_SW3 0x3F
-#define CS5_SW3 0x40
-#define CS6_SW3 0x41
-#define CS7_SW3 0x42
-#define CS8_SW3 0x43
-#define CS9_SW3 0x44
-#define CS10_SW3 0x45
-#define CS11_SW3 0x46
-#define CS12_SW3 0x47
-#define CS13_SW3 0x48
-#define CS14_SW3 0x49
-#define CS15_SW3 0x4A
-#define CS16_SW3 0x4B
-#define CS17_SW3 0x4C
-#define CS18_SW3 0x4D
-#define CS19_SW3 0x4E
-#define CS20_SW3 0x4F
-#define CS21_SW3 0x50
-#define CS22_SW3 0x51
-#define CS23_SW3 0x52
-#define CS24_SW3 0x53
-#define CS25_SW3 0x54
-#define CS26_SW3 0x55
-#define CS27_SW3 0x56
-#define CS28_SW3 0x57
-#define CS29_SW3 0x58
-#define CS30_SW3 0x59
-
-#define CS1_SW4 0x5A
-#define CS2_SW4 0x5B
-#define CS3_SW4 0x5C
-#define CS4_SW4 0x5D
-#define CS5_SW4 0x5E
-#define CS6_SW4 0x5F
-#define CS7_SW4 0x60
-#define CS8_SW4 0x61
-#define CS9_SW4 0x62
-#define CS10_SW4 0x63
-#define CS11_SW4 0x64
-#define CS12_SW4 0x65
-#define CS13_SW4 0x66
-#define CS14_SW4 0x67
-#define CS15_SW4 0x68
-#define CS16_SW4 0x69
-#define CS17_SW4 0x6A
-#define CS18_SW4 0x6B
-#define CS19_SW4 0x6C
-#define CS20_SW4 0x6D
-#define CS21_SW4 0x6E
-#define CS22_SW4 0x6F
-#define CS23_SW4 0x70
-#define CS24_SW4 0x71
-#define CS25_SW4 0x72
-#define CS26_SW4 0x73
-#define CS27_SW4 0x74
-#define CS28_SW4 0x75
-#define CS29_SW4 0x76
-#define CS30_SW4 0x77
-
-#define CS1_SW5 0x78
-#define CS2_SW5 0x79
-#define CS3_SW5 0x7A
-#define CS4_SW5 0x7B
-#define CS5_SW5 0x7C
-#define CS6_SW5 0x7D
-#define CS7_SW5 0x7E
-#define CS8_SW5 0x7F
-#define CS9_SW5 0x80
-#define CS10_SW5 0x81
-#define CS11_SW5 0x82
-#define CS12_SW5 0x83
-#define CS13_SW5 0x84
-#define CS14_SW5 0x85
-#define CS15_SW5 0x86
-#define CS16_SW5 0x87
-#define CS17_SW5 0x88
-#define CS18_SW5 0x89
-#define CS19_SW5 0x8A
-#define CS20_SW5 0x8B
-#define CS21_SW5 0x8C
-#define CS22_SW5 0x8D
-#define CS23_SW5 0x8E
-#define CS24_SW5 0x8F
-#define CS25_SW5 0x90
-#define CS26_SW5 0x91
-#define CS27_SW5 0x92
-#define CS28_SW5 0x93
-#define CS29_SW5 0x94
-#define CS30_SW5 0x95
-
-#define CS1_SW6 0x96
-#define CS2_SW6 0x97
-#define CS3_SW6 0x98
-#define CS4_SW6 0x99
-#define CS5_SW6 0x9A
-#define CS6_SW6 0x9B
-#define CS7_SW6 0x9C
-#define CS8_SW6 0x9D
-#define CS9_SW6 0x9E
-#define CS10_SW6 0x9F
-#define CS11_SW6 0xA0
-#define CS12_SW6 0xA1
-#define CS13_SW6 0xA2
-#define CS14_SW6 0xA3
-#define CS15_SW6 0xA4
-#define CS16_SW6 0xA5
-#define CS17_SW6 0xA6
-#define CS18_SW6 0xA7
-#define CS19_SW6 0xA8
-#define CS20_SW6 0xA9
-#define CS21_SW6 0xAA
-#define CS22_SW6 0xAB
-#define CS23_SW6 0xAC
-#define CS24_SW6 0xAD
-#define CS25_SW6 0xAE
-#define CS26_SW6 0xAF
-#define CS27_SW6 0xB0
-#define CS28_SW6 0xB1
-#define CS29_SW6 0xB2
-#define CS30_SW6 0xB3
-
-#define CS1_SW7 0xB4
-#define CS2_SW7 0xB5
-#define CS3_SW7 0xB6
-#define CS4_SW7 0xB7
-#define CS5_SW7 0xB8
-#define CS6_SW7 0xB9
-#define CS7_SW7 0xBA
-#define CS8_SW7 0xBB
-#define CS9_SW7 0xBC
-#define CS10_SW7 0xBD
-#define CS11_SW7 0xBE
-#define CS12_SW7 0xBF
-#define CS13_SW7 0xC0
-#define CS14_SW7 0xC1
-#define CS15_SW7 0xC2
-#define CS16_SW7 0xC3
-#define CS17_SW7 0xC4
-#define CS18_SW7 0xC5
-#define CS19_SW7 0xC6
-#define CS20_SW7 0xC7
-#define CS21_SW7 0xC8
-#define CS22_SW7 0xC9
-#define CS23_SW7 0xCA
-#define CS24_SW7 0xCB
-#define CS25_SW7 0xCC
-#define CS26_SW7 0xCD
-#define CS27_SW7 0xCE
-#define CS28_SW7 0xCF
-#define CS29_SW7 0xD0
-#define CS30_SW7 0xD1
-
-#define CS1_SW8 0xD2
-#define CS2_SW8 0xD3
-#define CS3_SW8 0xD4
-#define CS4_SW8 0xD5
-#define CS5_SW8 0xD6
-#define CS6_SW8 0xD7
-#define CS7_SW8 0xD8
-#define CS8_SW8 0xD9
-#define CS9_SW8 0xDA
-#define CS10_SW8 0xDB
-#define CS11_SW8 0xDC
-#define CS12_SW8 0xDD
-#define CS13_SW8 0xDE
-#define CS14_SW8 0xDF
-#define CS15_SW8 0xE0
-#define CS16_SW8 0xE1
-#define CS17_SW8 0xE2
-#define CS18_SW8 0xE3
-#define CS19_SW8 0xE4
-#define CS20_SW8 0xE5
-#define CS21_SW8 0xE6
-#define CS22_SW8 0xE7
-#define CS23_SW8 0xE8
-#define CS24_SW8 0xE9
-#define CS25_SW8 0xEA
-#define CS26_SW8 0xEB
-#define CS27_SW8 0xEC
-#define CS28_SW8 0xED
-#define CS29_SW8 0xEE
-#define CS30_SW8 0xEF
-
-#define CS1_SW9 0xF0
-#define CS2_SW9 0xF1
-#define CS3_SW9 0xF2
-#define CS4_SW9 0xF3
-#define CS5_SW9 0xF4
-#define CS6_SW9 0xF5
-#define CS7_SW9 0xF6
-#define CS8_SW9 0xF7
-#define CS9_SW9 0xF8
-#define CS10_SW9 0xF9
-#define CS11_SW9 0xFA
-#define CS12_SW9 0xFB
-#define CS13_SW9 0xFC
-#define CS14_SW9 0xFD
-#define CS15_SW9 0xFE
-#define CS16_SW9 0xFF
-#define CS17_SW9 0x100
-#define CS18_SW9 0x101
-#define CS19_SW9 0x102
-#define CS20_SW9 0x103
-#define CS21_SW9 0x104
-#define CS22_SW9 0x105
-#define CS23_SW9 0x106
-#define CS24_SW9 0x107
-#define CS25_SW9 0x108
-#define CS26_SW9 0x109
-#define CS27_SW9 0x10A
-#define CS28_SW9 0x10B
-#define CS29_SW9 0x10C
-#define CS30_SW9 0x10D
-
-#define CS31_SW1 0x10E
-#define CS32_SW1 0x10F
-#define CS33_SW1 0x110
-#define CS34_SW1 0x111
-#define CS35_SW1 0x112
-#define CS36_SW1 0x113
-#define CS37_SW1 0x114
-#define CS38_SW1 0x115
-#define CS39_SW1 0x116
-
-#define CS31_SW2 0x117
-#define CS32_SW2 0x118
-#define CS33_SW2 0x119
-#define CS34_SW2 0x11A
-#define CS35_SW2 0x11B
-#define CS36_SW2 0x11C
-#define CS37_SW2 0x11D
-#define CS38_SW2 0x11E
-#define CS39_SW2 0x11F
-
-#define CS31_SW3 0x120
-#define CS32_SW3 0x121
-#define CS33_SW3 0x122
-#define CS34_SW3 0x123
-#define CS35_SW3 0x124
-#define CS36_SW3 0x125
-#define CS37_SW3 0x126
-#define CS38_SW3 0x127
-#define CS39_SW3 0x128
-
-#define CS31_SW4 0x129
-#define CS32_SW4 0x12A
-#define CS33_SW4 0x12B
-#define CS34_SW4 0x12C
-#define CS35_SW4 0x12D
-#define CS36_SW4 0x12E
-#define CS37_SW4 0x12F
-#define CS38_SW4 0x130
-#define CS39_SW4 0x131
-
-#define CS31_SW5 0x132
-#define CS32_SW5 0x133
-#define CS33_SW5 0x134
-#define CS34_SW5 0x135
-#define CS35_SW5 0x136
-#define CS36_SW5 0x137
-#define CS37_SW5 0x138
-#define CS38_SW5 0x139
-#define CS39_SW5 0x13A
-
-#define CS31_SW6 0x13B
-#define CS32_SW6 0x13C
-#define CS33_SW6 0x13D
-#define CS34_SW6 0x13E
-#define CS35_SW6 0x13F
-#define CS36_SW6 0x140
-#define CS37_SW6 0x141
-#define CS38_SW6 0x142
-#define CS39_SW6 0x143
-
-#define CS31_SW7 0x144
-#define CS32_SW7 0x145
-#define CS33_SW7 0x146
-#define CS34_SW7 0x147
-#define CS35_SW7 0x148
-#define CS36_SW7 0x149
-#define CS37_SW7 0x14A
-#define CS38_SW7 0x14B
-#define CS39_SW7 0x14C
-
-#define CS31_SW8 0x14D
-#define CS32_SW8 0x14E
-#define CS33_SW8 0x14F
-#define CS34_SW8 0x150
-#define CS35_SW8 0x151
-#define CS36_SW8 0x152
-#define CS37_SW8 0x153
-#define CS38_SW8 0x154
-#define CS39_SW8 0x155
-
-#define CS31_SW9 0x156
-#define CS32_SW9 0x157
-#define CS33_SW9 0x158
-#define CS34_SW9 0x159
-#define CS35_SW9 0x15A
-#define CS36_SW9 0x15B
-#define CS37_SW9 0x15C
-#define CS38_SW9 0x15D
-#define CS39_SW9 0x15E
diff --git a/drivers/led/issi/is31fl3741.c b/drivers/led/issi/is31fl3741.c
index efcfa77b46..3614d1c104 100644
--- a/drivers/led/issi/is31fl3741.c
+++ b/drivers/led/issi/is31fl3741.c
@@ -18,11 +18,14 @@
*/
#include "is31fl3741.h"
-#include <string.h>
#include "i2c_master.h"
+#include "gpio.h"
#include "wait.h"
-#define IS31FL3741_PWM_REGISTER_COUNT 351
+#define IS31FL3741_PWM_0_REGISTER_COUNT 180
+#define IS31FL3741_PWM_1_REGISTER_COUNT 171
+#define IS31FL3741_SCALING_0_REGISTER_COUNT 180
+#define IS31FL3741_SCALING_1_REGISTER_COUNT 171
#ifndef IS31FL3741_I2C_TIMEOUT
# define IS31FL3741_I2C_TIMEOUT 100
@@ -52,150 +55,166 @@
# define IS31FL3741_GLOBAL_CURRENT 0xFF
#endif
-// Transfer buffer for TWITransmitData()
-uint8_t g_twi_transfer_buffer[20] = {0xFF};
+const uint8_t i2c_addresses[IS31FL3741_DRIVER_COUNT] = {
+ IS31FL3741_I2C_ADDRESS_1,
+#ifdef IS31FL3741_I2C_ADDRESS_2
+ IS31FL3741_I2C_ADDRESS_2,
+# ifdef IS31FL3741_I2C_ADDRESS_3
+ IS31FL3741_I2C_ADDRESS_3,
+# ifdef IS31FL3741_I2C_ADDRESS_4
+ IS31FL3741_I2C_ADDRESS_4,
+# endif
+# endif
+#endif
+};
// These buffers match the IS31FL3741 and IS31FL3741A PWM registers.
-// The scaling buffers match the PG2 and PG3 LED On/Off registers.
+// The scaling buffers match the page 2 and 3 LED On/Off registers.
// Storing them like this is optimal for I2C transfers to the registers.
// We could optimize this and take out the unused registers from these
// buffers and the transfers in is31fl3741_write_pwm_buffer() but it's
// probably not worth the extra complexity.
-uint8_t g_pwm_buffer[IS31FL3741_DRIVER_COUNT][IS31FL3741_PWM_REGISTER_COUNT];
-bool g_pwm_buffer_update_required[IS31FL3741_DRIVER_COUNT] = {false};
-bool g_scaling_registers_update_required[IS31FL3741_DRIVER_COUNT] = {false};
-
-uint8_t g_scaling_registers[IS31FL3741_DRIVER_COUNT][IS31FL3741_PWM_REGISTER_COUNT];
-
-void is31fl3741_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
- g_twi_transfer_buffer[0] = reg;
- g_twi_transfer_buffer[1] = data;
-
+typedef struct is31fl3741_driver_t {
+ uint8_t pwm_buffer_0[IS31FL3741_PWM_0_REGISTER_COUNT];
+ uint8_t pwm_buffer_1[IS31FL3741_PWM_1_REGISTER_COUNT];
+ bool pwm_buffer_dirty;
+ uint8_t scaling_buffer_0[IS31FL3741_SCALING_0_REGISTER_COUNT];
+ uint8_t scaling_buffer_1[IS31FL3741_SCALING_1_REGISTER_COUNT];
+ bool scaling_buffer_dirty;
+} PACKED is31fl3741_driver_t;
+
+is31fl3741_driver_t driver_buffers[IS31FL3741_DRIVER_COUNT] = {{
+ .pwm_buffer_0 = {0},
+ .pwm_buffer_1 = {0},
+ .pwm_buffer_dirty = false,
+ .scaling_buffer_0 = {0},
+ .scaling_buffer_1 = {0},
+ .scaling_buffer_dirty = false,
+}};
+
+void is31fl3741_write_register(uint8_t index, uint8_t reg, uint8_t data) {
#if IS31FL3741_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3741_I2C_TIMEOUT) == 0) break;
+ if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3741_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
}
#else
- i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3741_I2C_TIMEOUT);
+ i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3741_I2C_TIMEOUT);
#endif
}
-bool is31fl3741_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
- // Assume PG0 is already selected
+void is31fl3741_select_page(uint8_t index, uint8_t page) {
+ is31fl3741_write_register(index, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC);
+ is31fl3741_write_register(index, IS31FL3741_REG_COMMAND, page);
+}
- for (int i = 0; i < 342; i += 18) {
- if (i == 180) {
- // unlock the command register and select PG1
- is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC);
- is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND, IS31FL3741_COMMAND_PWM_1);
- }
+void is31fl3741_write_pwm_buffer(uint8_t index) {
+ is31fl3741_select_page(index, IS31FL3741_COMMAND_PWM_0);
- g_twi_transfer_buffer[0] = i % 180;
- memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 18);
+ // Transmit PWM0 registers in 6 transfers of 30 bytes.
+ // Iterate over the pwm_buffer_0 contents at 30 byte intervals.
+ for (uint8_t i = 0; i < IS31FL3741_PWM_0_REGISTER_COUNT; i += 30) {
#if IS31FL3741_I2C_PERSISTENCE > 0
- for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 19, IS31FL3741_I2C_TIMEOUT) != 0) {
- return false;
- }
+ for (uint8_t j = 0; j < IS31FL3741_I2C_PERSISTENCE; j++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer_0 + i, 30, IS31FL3741_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
}
#else
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 19, IS31FL3741_I2C_TIMEOUT) != 0) {
- return false;
- }
+ i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer_0 + i, 30, IS31FL3741_I2C_TIMEOUT);
#endif
}
- // transfer the left cause the total number is 351
- g_twi_transfer_buffer[0] = 162;
- memcpy(g_twi_transfer_buffer + 1, pwm_buffer + 342, 9);
+ is31fl3741_select_page(index, IS31FL3741_COMMAND_PWM_1);
+
+ // Transmit PWM1 registers in 9 transfers of 19 bytes.
+ // Iterate over the pwm_buffer_1 contents at 19 byte intervals.
+ for (uint8_t i = 0; i < IS31FL3741_PWM_1_REGISTER_COUNT; i += 19) {
#if IS31FL3741_I2C_PERSISTENCE > 0
- for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 10, IS31FL3741_I2C_TIMEOUT) != 0) {
- return false;
+ for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer_1 + i, 19, IS31FL3741_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
}
- }
#else
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 10, IS31FL3741_I2C_TIMEOUT) != 0) {
- return false;
- }
+ i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer_1 + i, 19, IS31FL3741_I2C_TIMEOUT);
#endif
-
- return true;
+ }
}
void is31fl3741_init_drivers(void) {
i2c_init();
- is31fl3741_init(IS31FL3741_I2C_ADDRESS_1);
-#if defined(IS31FL3741_I2C_ADDRESS_2)
- is31fl3741_init(IS31FL3741_I2C_ADDRESS_2);
-# if defined(IS31FL3741_I2C_ADDRESS_3)
- is31fl3741_init(IS31FL3741_I2C_ADDRESS_3);
-# if defined(IS31FL3741_I2C_ADDRESS_4)
- is31fl3741_init(IS31FL3741_I2C_ADDRESS_4);
-# endif
-# endif
+#if defined(IS31FL3741_SDB_PIN)
+ gpio_set_pin_output(IS31FL3741_SDB_PIN);
+ gpio_write_pin_high(IS31FL3741_SDB_PIN);
#endif
+ for (uint8_t i = 0; i < IS31FL3741_DRIVER_COUNT; i++) {
+ is31fl3741_init(i);
+ }
+
for (int i = 0; i < IS31FL3741_LED_COUNT; i++) {
is31fl3741_set_led_control_register(i, true, true, true);
}
- is31fl3741_update_led_control_registers(IS31FL3741_I2C_ADDRESS_1, 0);
-#if defined(IS31FL3741_I2C_ADDRESS_2)
- is31fl3741_update_led_control_registers(IS31FL3741_I2C_ADDRESS_2, 1);
-# if defined(IS31FL3741_I2C_ADDRESS_3)
- is31fl3741_update_led_control_registers(IS31FL3741_I2C_ADDRESS_3, 2);
-# if defined(IS31FL3741_I2C_ADDRESS_4)
- is31fl3741_update_led_control_registers(IS31FL3741_I2C_ADDRESS_4, 3);
-# endif
-# endif
-#endif
+ for (uint8_t i = 0; i < IS31FL3741_DRIVER_COUNT; i++) {
+ is31fl3741_update_led_control_registers(i);
+ }
}
-void is31fl3741_init(uint8_t addr) {
+void is31fl3741_init(uint8_t index) {
// In order to avoid the LEDs being driven with garbage data
// in the LED driver's PWM registers, shutdown is enabled last.
// Set up the mode and other settings, clear the PWM registers,
// then disable software shutdown.
// Unlock the command register.
- // Unlock the command register.
- is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC);
-
- // Select PG4
- is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND, IS31FL3741_COMMAND_FUNCTION);
+ is31fl3741_select_page(index, IS31FL3741_COMMAND_FUNCTION);
// Set to Normal operation
- is31fl3741_write_register(addr, IS31FL3741_FUNCTION_REG_CONFIGURATION, IS31FL3741_CONFIGURATION);
+ is31fl3741_write_register(index, IS31FL3741_FUNCTION_REG_CONFIGURATION, IS31FL3741_CONFIGURATION);
// Set Golbal Current Control Register
- is31fl3741_write_register(addr, IS31FL3741_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3741_GLOBAL_CURRENT);
+ is31fl3741_write_register(index, IS31FL3741_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3741_GLOBAL_CURRENT);
// Set Pull up & Down for SWx CSy
- is31fl3741_write_register(addr, IS31FL3741_FUNCTION_REG_PULLDOWNUP, ((IS31FL3741_CS_PULLDOWN << 4) | IS31FL3741_SW_PULLUP));
+ is31fl3741_write_register(index, IS31FL3741_FUNCTION_REG_PULLDOWNUP, ((IS31FL3741_CS_PULLDOWN << 4) | IS31FL3741_SW_PULLUP));
// Set PWM frequency
- is31fl3741_write_register(addr, IS31FL3741_FUNCTION_REG_PWM_FREQUENCY, (IS31FL3741_PWM_FREQUENCY & 0b1111));
+ is31fl3741_write_register(index, IS31FL3741_FUNCTION_REG_PWM_FREQUENCY, (IS31FL3741_PWM_FREQUENCY & 0b1111));
- // is31fl3741_update_led_scaling_registers(addr, 0xFF, 0xFF, 0xFF);
+ // is31fl3741_update_led_scaling_registers(index, 0xFF, 0xFF, 0xFF);
// Wait 10ms to ensure the device has woken up.
wait_ms(10);
}
+uint8_t get_pwm_value(uint8_t driver, uint16_t reg) {
+ if (reg & 0x100) {
+ return driver_buffers[driver].pwm_buffer_1[reg & 0xFF];
+ } else {
+ return driver_buffers[driver].pwm_buffer_0[reg];
+ }
+}
+
+void set_pwm_value(uint8_t driver, uint16_t reg, uint8_t value) {
+ if (reg & 0x100) {
+ driver_buffers[driver].pwm_buffer_1[reg & 0xFF] = value;
+ } else {
+ driver_buffers[driver].pwm_buffer_0[reg] = value;
+ }
+}
+
void is31fl3741_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
is31fl3741_led_t led;
+
if (index >= 0 && index < IS31FL3741_LED_COUNT) {
memcpy_P(&led, (&g_is31fl3741_leds[index]), sizeof(led));
- if (g_pwm_buffer[led.driver][led.r] == red && g_pwm_buffer[led.driver][led.g] == green && g_pwm_buffer[led.driver][led.b] == blue) {
+ if (get_pwm_value(led.driver, led.r) == red && get_pwm_value(led.driver, led.g) == green && get_pwm_value(led.driver, led.b) == blue) {
return;
}
- g_pwm_buffer_update_required[led.driver] = true;
- g_pwm_buffer[led.driver][led.r] = red;
- g_pwm_buffer[led.driver][led.g] = green;
- g_pwm_buffer[led.driver][led.b] = blue;
+
+ set_pwm_value(led.driver, led.r, red);
+ set_pwm_value(led.driver, led.g, green);
+ set_pwm_value(led.driver, led.b, blue);
+ driver_buffers[led.driver].pwm_buffer_dirty = true;
}
}
@@ -205,92 +224,67 @@ void is31fl3741_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
}
}
-void is31fl3741_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
- is31fl3741_led_t led;
- memcpy_P(&led, (&g_is31fl3741_leds[index]), sizeof(led));
-
- if (red) {
- g_scaling_registers[led.driver][led.r] = 0xFF;
+void set_scaling_value(uint8_t driver, uint16_t reg, uint8_t value) {
+ if (reg & 0x100) {
+ driver_buffers[driver].scaling_buffer_1[reg & 0xFF] = value;
} else {
- g_scaling_registers[led.driver][led.r] = 0x00;
+ driver_buffers[driver].scaling_buffer_0[reg] = value;
}
+}
- if (green) {
- g_scaling_registers[led.driver][led.g] = 0xFF;
- } else {
- g_scaling_registers[led.driver][led.g] = 0x00;
- }
+void is31fl3741_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
+ is31fl3741_led_t led;
+ memcpy_P(&led, (&g_is31fl3741_leds[index]), sizeof(led));
- if (blue) {
- g_scaling_registers[led.driver][led.b] = 0xFF;
- } else {
- g_scaling_registers[led.driver][led.b] = 0x00;
- }
+ set_scaling_value(led.driver, led.r, red ? 0xFF : 0x00);
+ set_scaling_value(led.driver, led.g, green ? 0xFF : 0x00);
+ set_scaling_value(led.driver, led.b, blue ? 0xFF : 0x00);
- g_scaling_registers_update_required[led.driver] = true;
+ driver_buffers[led.driver].scaling_buffer_dirty = true;
}
-void is31fl3741_update_pwm_buffers(uint8_t addr, uint8_t index) {
- if (g_pwm_buffer_update_required[index]) {
- // unlock the command register and select PG2
- is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC);
- is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND, IS31FL3741_COMMAND_PWM_0);
+void is31fl3741_update_pwm_buffers(uint8_t index) {
+ if (driver_buffers[index].pwm_buffer_dirty) {
+ is31fl3741_write_pwm_buffer(index);
- is31fl3741_write_pwm_buffer(addr, g_pwm_buffer[index]);
+ driver_buffers[index].pwm_buffer_dirty = false;
}
-
- g_pwm_buffer_update_required[index] = false;
}
void is31fl3741_set_pwm_buffer(const is31fl3741_led_t *pled, uint8_t red, uint8_t green, uint8_t blue) {
- g_pwm_buffer[pled->driver][pled->r] = red;
- g_pwm_buffer[pled->driver][pled->g] = green;
- g_pwm_buffer[pled->driver][pled->b] = blue;
-
- g_pwm_buffer_update_required[pled->driver] = true;
+ set_pwm_value(pled->driver, pled->r, red);
+ set_pwm_value(pled->driver, pled->g, green);
+ set_pwm_value(pled->driver, pled->b, blue);
+ driver_buffers[pled->driver].pwm_buffer_dirty = true;
}
-void is31fl3741_update_led_control_registers(uint8_t addr, uint8_t index) {
- if (g_scaling_registers_update_required[index]) {
- // unlock the command register and select PG2
- is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC);
- is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND, IS31FL3741_COMMAND_SCALING_0);
+void is31fl3741_update_led_control_registers(uint8_t index) {
+ if (driver_buffers[index].scaling_buffer_dirty) {
+ is31fl3741_select_page(index, IS31FL3741_COMMAND_SCALING_0);
- // CS1_SW1 to CS30_SW6 are on PG2
- for (int i = CS1_SW1; i <= CS30_SW6; ++i) {
- is31fl3741_write_register(addr, i, g_scaling_registers[index][i]);
+ for (uint8_t i = 0; i < IS31FL3741_SCALING_0_REGISTER_COUNT; i++) {
+ is31fl3741_write_register(index, i, driver_buffers[index].scaling_buffer_0[i]);
}
- // unlock the command register and select PG3
- is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC);
- is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND, IS31FL3741_COMMAND_SCALING_1);
+ is31fl3741_select_page(index, IS31FL3741_COMMAND_SCALING_1);
- // CS1_SW7 to CS39_SW9 are on PG3
- for (int i = CS1_SW7; i <= CS39_SW9; ++i) {
- is31fl3741_write_register(addr, i - CS1_SW7, g_scaling_registers[index][i]);
+ for (uint8_t i = 0; i < IS31FL3741_SCALING_1_REGISTER_COUNT; i++) {
+ is31fl3741_write_register(index, i, driver_buffers[index].scaling_buffer_1[i]);
}
- g_scaling_registers_update_required[index] = false;
+ driver_buffers[index].scaling_buffer_dirty = false;
}
}
void is31fl3741_set_scaling_registers(const is31fl3741_led_t *pled, uint8_t red, uint8_t green, uint8_t blue) {
- g_scaling_registers[pled->driver][pled->r] = red;
- g_scaling_registers[pled->driver][pled->g] = green;
- g_scaling_registers[pled->driver][pled->b] = blue;
-
- g_scaling_registers_update_required[pled->driver] = true;
+ set_scaling_value(pled->driver, pled->r, red);
+ set_scaling_value(pled->driver, pled->g, green);
+ set_scaling_value(pled->driver, pled->b, blue);
+ driver_buffers[pled->driver].scaling_buffer_dirty = true;
}
void is31fl3741_flush(void) {
- is31fl3741_update_pwm_buffers(IS31FL3741_I2C_ADDRESS_1, 0);
-#if defined(IS31FL3741_I2C_ADDRESS_2)
- is31fl3741_update_pwm_buffers(IS31FL3741_I2C_ADDRESS_2, 1);
-# if defined(IS31FL3741_I2C_ADDRESS_3)
- is31fl3741_update_pwm_buffers(IS31FL3741_I2C_ADDRESS_3, 2);
-# if defined(IS31FL3741_I2C_ADDRESS_4)
- is31fl3741_update_pwm_buffers(IS31FL3741_I2C_ADDRESS_4, 3);
-# endif
-# endif
-#endif
+ for (uint8_t i = 0; i < IS31FL3741_DRIVER_COUNT; i++) {
+ is31fl3741_update_pwm_buffers(i);
+ }
}
diff --git a/drivers/led/issi/is31fl3741.h b/drivers/led/issi/is31fl3741.h
index 6466696b60..cc9637a4e8 100644
--- a/drivers/led/issi/is31fl3741.h
+++ b/drivers/led/issi/is31fl3741.h
@@ -119,9 +119,9 @@ typedef struct is31fl3741_led_t {
extern const is31fl3741_led_t PROGMEM g_is31fl3741_leds[IS31FL3741_LED_COUNT];
void is31fl3741_init_drivers(void);
-void is31fl3741_init(uint8_t addr);
-void is31fl3741_write_register(uint8_t addr, uint8_t reg, uint8_t data);
-bool is31fl3741_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
+void is31fl3741_init(uint8_t index);
+void is31fl3741_write_register(uint8_t index, uint8_t reg, uint8_t data);
+void is31fl3741_select_page(uint8_t index, uint8_t page);
void is31fl3741_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
void is31fl3741_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
@@ -132,8 +132,8 @@ void is31fl3741_set_led_control_register(uint8_t index, bool red, bool green, bo
// (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 is31fl3741_update_pwm_buffers(uint8_t addr, uint8_t index);
-void is31fl3741_update_led_control_registers(uint8_t addr, uint8_t index);
+void is31fl3741_update_pwm_buffers(uint8_t index);
+void is31fl3741_update_led_control_registers(uint8_t index);
void is31fl3741_set_scaling_registers(const is31fl3741_led_t *pled, uint8_t red, uint8_t green, uint8_t blue);
void is31fl3741_set_pwm_buffer(const is31fl3741_led_t *pled, uint8_t red, uint8_t green, uint8_t blue);
@@ -163,371 +163,742 @@ void is31fl3741_flush(void);
#define IS31FL3741_PWM_FREQUENCY_1K8_HZ 0b0111
#define IS31FL3741_PWM_FREQUENCY_900_HZ 0b1011
-#define CS1_SW1 0x00
-#define CS2_SW1 0x01
-#define CS3_SW1 0x02
-#define CS4_SW1 0x03
-#define CS5_SW1 0x04
-#define CS6_SW1 0x05
-#define CS7_SW1 0x06
-#define CS8_SW1 0x07
-#define CS9_SW1 0x08
-#define CS10_SW1 0x09
-#define CS11_SW1 0x0A
-#define CS12_SW1 0x0B
-#define CS13_SW1 0x0C
-#define CS14_SW1 0x0D
-#define CS15_SW1 0x0E
-#define CS16_SW1 0x0F
-#define CS17_SW1 0x10
-#define CS18_SW1 0x11
-#define CS19_SW1 0x12
-#define CS20_SW1 0x13
-#define CS21_SW1 0x14
-#define CS22_SW1 0x15
-#define CS23_SW1 0x16
-#define CS24_SW1 0x17
-#define CS25_SW1 0x18
-#define CS26_SW1 0x19
-#define CS27_SW1 0x1A
-#define CS28_SW1 0x1B
-#define CS29_SW1 0x1C
-#define CS30_SW1 0x1D
-
-#define CS1_SW2 0x1E
-#define CS2_SW2 0x1F
-#define CS3_SW2 0x20
-#define CS4_SW2 0x21
-#define CS5_SW2 0x22
-#define CS6_SW2 0x23
-#define CS7_SW2 0x24
-#define CS8_SW2 0x25
-#define CS9_SW2 0x26
-#define CS10_SW2 0x27
-#define CS11_SW2 0x28
-#define CS12_SW2 0x29
-#define CS13_SW2 0x2A
-#define CS14_SW2 0x2B
-#define CS15_SW2 0x2C
-#define CS16_SW2 0x2D
-#define CS17_SW2 0x2E
-#define CS18_SW2 0x2F
-#define CS19_SW2 0x30
-#define CS20_SW2 0x31
-#define CS21_SW2 0x32
-#define CS22_SW2 0x33
-#define CS23_SW2 0x34
-#define CS24_SW2 0x35
-#define CS25_SW2 0x36
-#define CS26_SW2 0x37
-#define CS27_SW2 0x38
-#define CS28_SW2 0x39
-#define CS29_SW2 0x3A
-#define CS30_SW2 0x3B
-
-#define CS1_SW3 0x3C
-#define CS2_SW3 0x3D
-#define CS3_SW3 0x3E
-#define CS4_SW3 0x3F
-#define CS5_SW3 0x40
-#define CS6_SW3 0x41
-#define CS7_SW3 0x42
-#define CS8_SW3 0x43
-#define CS9_SW3 0x44
-#define CS10_SW3 0x45
-#define CS11_SW3 0x46
-#define CS12_SW3 0x47
-#define CS13_SW3 0x48
-#define CS14_SW3 0x49
-#define CS15_SW3 0x4A
-#define CS16_SW3 0x4B
-#define CS17_SW3 0x4C
-#define CS18_SW3 0x4D
-#define CS19_SW3 0x4E
-#define CS20_SW3 0x4F
-#define CS21_SW3 0x50
-#define CS22_SW3 0x51
-#define CS23_SW3 0x52
-#define CS24_SW3 0x53
-#define CS25_SW3 0x54
-#define CS26_SW3 0x55
-#define CS27_SW3 0x56
-#define CS28_SW3 0x57
-#define CS29_SW3 0x58
-#define CS30_SW3 0x59
-
-#define CS1_SW4 0x5A
-#define CS2_SW4 0x5B
-#define CS3_SW4 0x5C
-#define CS4_SW4 0x5D
-#define CS5_SW4 0x5E
-#define CS6_SW4 0x5F
-#define CS7_SW4 0x60
-#define CS8_SW4 0x61
-#define CS9_SW4 0x62
-#define CS10_SW4 0x63
-#define CS11_SW4 0x64
-#define CS12_SW4 0x65
-#define CS13_SW4 0x66
-#define CS14_SW4 0x67
-#define CS15_SW4 0x68
-#define CS16_SW4 0x69
-#define CS17_SW4 0x6A
-#define CS18_SW4 0x6B
-#define CS19_SW4 0x6C
-#define CS20_SW4 0x6D
-#define CS21_SW4 0x6E
-#define CS22_SW4 0x6F
-#define CS23_SW4 0x70
-#define CS24_SW4 0x71
-#define CS25_SW4 0x72
-#define CS26_SW4 0x73
-#define CS27_SW4 0x74
-#define CS28_SW4 0x75
-#define CS29_SW4 0x76
-#define CS30_SW4 0x77
-
-#define CS1_SW5 0x78
-#define CS2_SW5 0x79
-#define CS3_SW5 0x7A
-#define CS4_SW5 0x7B
-#define CS5_SW5 0x7C
-#define CS6_SW5 0x7D
-#define CS7_SW5 0x7E
-#define CS8_SW5 0x7F
-#define CS9_SW5 0x80
-#define CS10_SW5 0x81
-#define CS11_SW5 0x82
-#define CS12_SW5 0x83
-#define CS13_SW5 0x84
-#define CS14_SW5 0x85
-#define CS15_SW5 0x86
-#define CS16_SW5 0x87
-#define CS17_SW5 0x88
-#define CS18_SW5 0x89
-#define CS19_SW5 0x8A
-#define CS20_SW5 0x8B
-#define CS21_SW5 0x8C
-#define CS22_SW5 0x8D
-#define CS23_SW5 0x8E
-#define CS24_SW5 0x8F
-#define CS25_SW5 0x90
-#define CS26_SW5 0x91
-#define CS27_SW5 0x92
-#define CS28_SW5 0x93
-#define CS29_SW5 0x94
-#define CS30_SW5 0x95
-
-#define CS1_SW6 0x96
-#define CS2_SW6 0x97
-#define CS3_SW6 0x98
-#define CS4_SW6 0x99
-#define CS5_SW6 0x9A
-#define CS6_SW6 0x9B
-#define CS7_SW6 0x9C
-#define CS8_SW6 0x9D
-#define CS9_SW6 0x9E
-#define CS10_SW6 0x9F
-#define CS11_SW6 0xA0
-#define CS12_SW6 0xA1
-#define CS13_SW6 0xA2
-#define CS14_SW6 0xA3
-#define CS15_SW6 0xA4
-#define CS16_SW6 0xA5
-#define CS17_SW6 0xA6
-#define CS18_SW6 0xA7
-#define CS19_SW6 0xA8
-#define CS20_SW6 0xA9
-#define CS21_SW6 0xAA
-#define CS22_SW6 0xAB
-#define CS23_SW6 0xAC
-#define CS24_SW6 0xAD
-#define CS25_SW6 0xAE
-#define CS26_SW6 0xAF
-#define CS27_SW6 0xB0
-#define CS28_SW6 0xB1
-#define CS29_SW6 0xB2
-#define CS30_SW6 0xB3
-
-#define CS1_SW7 0xB4
-#define CS2_SW7 0xB5
-#define CS3_SW7 0xB6
-#define CS4_SW7 0xB7
-#define CS5_SW7 0xB8
-#define CS6_SW7 0xB9
-#define CS7_SW7 0xBA
-#define CS8_SW7 0xBB
-#define CS9_SW7 0xBC
-#define CS10_SW7 0xBD
-#define CS11_SW7 0xBE
-#define CS12_SW7 0xBF
-#define CS13_SW7 0xC0
-#define CS14_SW7 0xC1
-#define CS15_SW7 0xC2
-#define CS16_SW7 0xC3
-#define CS17_SW7 0xC4
-#define CS18_SW7 0xC5
-#define CS19_SW7 0xC6
-#define CS20_SW7 0xC7
-#define CS21_SW7 0xC8
-#define CS22_SW7 0xC9
-#define CS23_SW7 0xCA
-#define CS24_SW7 0xCB
-#define CS25_SW7 0xCC
-#define CS26_SW7 0xCD
-#define CS27_SW7 0xCE
-#define CS28_SW7 0xCF
-#define CS29_SW7 0xD0
-#define CS30_SW7 0xD1
-
-#define CS1_SW8 0xD2
-#define CS2_SW8 0xD3
-#define CS3_SW8 0xD4
-#define CS4_SW8 0xD5
-#define CS5_SW8 0xD6
-#define CS6_SW8 0xD7
-#define CS7_SW8 0xD8
-#define CS8_SW8 0xD9
-#define CS9_SW8 0xDA
-#define CS10_SW8 0xDB
-#define CS11_SW8 0xDC
-#define CS12_SW8 0xDD
-#define CS13_SW8 0xDE
-#define CS14_SW8 0xDF
-#define CS15_SW8 0xE0
-#define CS16_SW8 0xE1
-#define CS17_SW8 0xE2
-#define CS18_SW8 0xE3
-#define CS19_SW8 0xE4
-#define CS20_SW8 0xE5
-#define CS21_SW8 0xE6
-#define CS22_SW8 0xE7
-#define CS23_SW8 0xE8
-#define CS24_SW8 0xE9
-#define CS25_SW8 0xEA
-#define CS26_SW8 0xEB
-#define CS27_SW8 0xEC
-#define CS28_SW8 0xED
-#define CS29_SW8 0xEE
-#define CS30_SW8 0xEF
-
-#define CS1_SW9 0xF0
-#define CS2_SW9 0xF1
-#define CS3_SW9 0xF2
-#define CS4_SW9 0xF3
-#define CS5_SW9 0xF4
-#define CS6_SW9 0xF5
-#define CS7_SW9 0xF6
-#define CS8_SW9 0xF7
-#define CS9_SW9 0xF8
-#define CS10_SW9 0xF9
-#define CS11_SW9 0xFA
-#define CS12_SW9 0xFB
-#define CS13_SW9 0xFC
-#define CS14_SW9 0xFD
-#define CS15_SW9 0xFE
-#define CS16_SW9 0xFF
-#define CS17_SW9 0x100
-#define CS18_SW9 0x101
-#define CS19_SW9 0x102
-#define CS20_SW9 0x103
-#define CS21_SW9 0x104
-#define CS22_SW9 0x105
-#define CS23_SW9 0x106
-#define CS24_SW9 0x107
-#define CS25_SW9 0x108
-#define CS26_SW9 0x109
-#define CS27_SW9 0x10A
-#define CS28_SW9 0x10B
-#define CS29_SW9 0x10C
-#define CS30_SW9 0x10D
-
-#define CS31_SW1 0x10E
-#define CS32_SW1 0x10F
-#define CS33_SW1 0x110
-#define CS34_SW1 0x111
-#define CS35_SW1 0x112
-#define CS36_SW1 0x113
-#define CS37_SW1 0x114
-#define CS38_SW1 0x115
-#define CS39_SW1 0x116
-
-#define CS31_SW2 0x117
-#define CS32_SW2 0x118
-#define CS33_SW2 0x119
-#define CS34_SW2 0x11A
-#define CS35_SW2 0x11B
-#define CS36_SW2 0x11C
-#define CS37_SW2 0x11D
-#define CS38_SW2 0x11E
-#define CS39_SW2 0x11F
-
-#define CS31_SW3 0x120
-#define CS32_SW3 0x121
-#define CS33_SW3 0x122
-#define CS34_SW3 0x123
-#define CS35_SW3 0x124
-#define CS36_SW3 0x125
-#define CS37_SW3 0x126
-#define CS38_SW3 0x127
-#define CS39_SW3 0x128
-
-#define CS31_SW4 0x129
-#define CS32_SW4 0x12A
-#define CS33_SW4 0x12B
-#define CS34_SW4 0x12C
-#define CS35_SW4 0x12D
-#define CS36_SW4 0x12E
-#define CS37_SW4 0x12F
-#define CS38_SW4 0x130
-#define CS39_SW4 0x131
-
-#define CS31_SW5 0x132
-#define CS32_SW5 0x133
-#define CS33_SW5 0x134
-#define CS34_SW5 0x135
-#define CS35_SW5 0x136
-#define CS36_SW5 0x137
-#define CS37_SW5 0x138
-#define CS38_SW5 0x139
-#define CS39_SW5 0x13A
-
-#define CS31_SW6 0x13B
-#define CS32_SW6 0x13C
-#define CS33_SW6 0x13D
-#define CS34_SW6 0x13E
-#define CS35_SW6 0x13F
-#define CS36_SW6 0x140
-#define CS37_SW6 0x141
-#define CS38_SW6 0x142
-#define CS39_SW6 0x143
-
-#define CS31_SW7 0x144
-#define CS32_SW7 0x145
-#define CS33_SW7 0x146
-#define CS34_SW7 0x147
-#define CS35_SW7 0x148
-#define CS36_SW7 0x149
-#define CS37_SW7 0x14A
-#define CS38_SW7 0x14B
-#define CS39_SW7 0x14C
-
-#define CS31_SW8 0x14D
-#define CS32_SW8 0x14E
-#define CS33_SW8 0x14F
-#define CS34_SW8 0x150
-#define CS35_SW8 0x151
-#define CS36_SW8 0x152
-#define CS37_SW8 0x153
-#define CS38_SW8 0x154
-#define CS39_SW8 0x155
-
-#define CS31_SW9 0x156
-#define CS32_SW9 0x157
-#define CS33_SW9 0x158
-#define CS34_SW9 0x159
-#define CS35_SW9 0x15A
-#define CS36_SW9 0x15B
-#define CS37_SW9 0x15C
-#define CS38_SW9 0x15D
-#define CS39_SW9 0x15E
+#define SW1_CS1 0x00
+#define SW1_CS2 0x01
+#define SW1_CS3 0x02
+#define SW1_CS4 0x03
+#define SW1_CS5 0x04
+#define SW1_CS6 0x05
+#define SW1_CS7 0x06
+#define SW1_CS8 0x07
+#define SW1_CS9 0x08
+#define SW1_CS10 0x09
+#define SW1_CS11 0x0A
+#define SW1_CS12 0x0B
+#define SW1_CS13 0x0C
+#define SW1_CS14 0x0D
+#define SW1_CS15 0x0E
+#define SW1_CS16 0x0F
+#define SW1_CS17 0x10
+#define SW1_CS18 0x11
+#define SW1_CS19 0x12
+#define SW1_CS20 0x13
+#define SW1_CS21 0x14
+#define SW1_CS22 0x15
+#define SW1_CS23 0x16
+#define SW1_CS24 0x17
+#define SW1_CS25 0x18
+#define SW1_CS26 0x19
+#define SW1_CS27 0x1A
+#define SW1_CS28 0x1B
+#define SW1_CS29 0x1C
+#define SW1_CS30 0x1D
+
+#define SW2_CS1 0x1E
+#define SW2_CS2 0x1F
+#define SW2_CS3 0x20
+#define SW2_CS4 0x21
+#define SW2_CS5 0x22
+#define SW2_CS6 0x23
+#define SW2_CS7 0x24
+#define SW2_CS8 0x25
+#define SW2_CS9 0x26
+#define SW2_CS10 0x27
+#define SW2_CS11 0x28
+#define SW2_CS12 0x29
+#define SW2_CS13 0x2A
+#define SW2_CS14 0x2B
+#define SW2_CS15 0x2C
+#define SW2_CS16 0x2D
+#define SW2_CS17 0x2E
+#define SW2_CS18 0x2F
+#define SW2_CS19 0x30
+#define SW2_CS20 0x31
+#define SW2_CS21 0x32
+#define SW2_CS22 0x33
+#define SW2_CS23 0x34
+#define SW2_CS24 0x35
+#define SW2_CS25 0x36
+#define SW2_CS26 0x37
+#define SW2_CS27 0x38
+#define SW2_CS28 0x39
+#define SW2_CS29 0x3A
+#define SW2_CS30 0x3B
+
+#define SW3_CS1 0x3C
+#define SW3_CS2 0x3D
+#define SW3_CS3 0x3E
+#define SW3_CS4 0x3F
+#define SW3_CS5 0x40
+#define SW3_CS6 0x41
+#define SW3_CS7 0x42
+#define SW3_CS8 0x43
+#define SW3_CS9 0x44
+#define SW3_CS10 0x45
+#define SW3_CS11 0x46
+#define SW3_CS12 0x47
+#define SW3_CS13 0x48
+#define SW3_CS14 0x49
+#define SW3_CS15 0x4A
+#define SW3_CS16 0x4B
+#define SW3_CS17 0x4C
+#define SW3_CS18 0x4D
+#define SW3_CS19 0x4E
+#define SW3_CS20 0x4F
+#define SW3_CS21 0x50
+#define SW3_CS22 0x51
+#define SW3_CS23 0x52
+#define SW3_CS24 0x53
+#define SW3_CS25 0x54
+#define SW3_CS26 0x55
+#define SW3_CS27 0x56
+#define SW3_CS28 0x57
+#define SW3_CS29 0x58
+#define SW3_CS30 0x59
+
+#define SW4_CS1 0x5A
+#define SW4_CS2 0x5B
+#define SW4_CS3 0x5C
+#define SW4_CS4 0x5D
+#define SW4_CS5 0x5E
+#define SW4_CS6 0x5F
+#define SW4_CS7 0x60
+#define SW4_CS8 0x61
+#define SW4_CS9 0x62
+#define SW4_CS10 0x63
+#define SW4_CS11 0x64
+#define SW4_CS12 0x65
+#define SW4_CS13 0x66
+#define SW4_CS14 0x67
+#define SW4_CS15 0x68
+#define SW4_CS16 0x69
+#define SW4_CS17 0x6A
+#define SW4_CS18 0x6B
+#define SW4_CS19 0x6C
+#define SW4_CS20 0x6D
+#define SW4_CS21 0x6E
+#define SW4_CS22 0x6F
+#define SW4_CS23 0x70
+#define SW4_CS24 0x71
+#define SW4_CS25 0x72
+#define SW4_CS26 0x73
+#define SW4_CS27 0x74
+#define SW4_CS28 0x75
+#define SW4_CS29 0x76
+#define SW4_CS30 0x77
+
+#define SW5_CS1 0x78
+#define SW5_CS2 0x79
+#define SW5_CS3 0x7A
+#define SW5_CS4 0x7B
+#define SW5_CS5 0x7C
+#define SW5_CS6 0x7D
+#define SW5_CS7 0x7E
+#define SW5_CS8 0x7F
+#define SW5_CS9 0x80
+#define SW5_CS10 0x81
+#define SW5_CS11 0x82
+#define SW5_CS12 0x83
+#define SW5_CS13 0x84
+#define SW5_CS14 0x85
+#define SW5_CS15 0x86
+#define SW5_CS16 0x87
+#define SW5_CS17 0x88
+#define SW5_CS18 0x89
+#define SW5_CS19 0x8A
+#define SW5_CS20 0x8B
+#define SW5_CS21 0x8C
+#define SW5_CS22 0x8D
+#define SW5_CS23 0x8E
+#define SW5_CS24 0x8F
+#define SW5_CS25 0x90
+#define SW5_CS26 0x91
+#define SW5_CS27 0x92
+#define SW5_CS28 0x93
+#define SW5_CS29 0x94
+#define SW5_CS30 0x95
+
+#define SW6_CS1 0x96
+#define SW6_CS2 0x97
+#define SW6_CS3 0x98
+#define SW6_CS4 0x99
+#define SW6_CS5 0x9A
+#define SW6_CS6 0x9B
+#define SW6_CS7 0x9C
+#define SW6_CS8 0x9D
+#define SW6_CS9 0x9E
+#define SW6_CS10 0x9F
+#define SW6_CS11 0xA0
+#define SW6_CS12 0xA1
+#define SW6_CS13 0xA2
+#define SW6_CS14 0xA3
+#define SW6_CS15 0xA4
+#define SW6_CS16 0xA5
+#define SW6_CS17 0xA6
+#define SW6_CS18 0xA7
+#define SW6_CS19 0xA8
+#define SW6_CS20 0xA9
+#define SW6_CS21 0xAA
+#define SW6_CS22 0xAB
+#define SW6_CS23 0xAC
+#define SW6_CS24 0xAD
+#define SW6_CS25 0xAE
+#define SW6_CS26 0xAF
+#define SW6_CS27 0xB0
+#define SW6_CS28 0xB1
+#define SW6_CS29 0xB2
+#define SW6_CS30 0xB3
+
+#define SW7_CS1 0x100
+#define SW7_CS2 0x101
+#define SW7_CS3 0x102
+#define SW7_CS4 0x103
+#define SW7_CS5 0x104
+#define SW7_CS6 0x105
+#define SW7_CS7 0x106
+#define SW7_CS8 0x107
+#define SW7_CS9 0x108
+#define SW7_CS10 0x109
+#define SW7_CS11 0x10A
+#define SW7_CS12 0x10B
+#define SW7_CS13 0x10C
+#define SW7_CS14 0x10D
+#define SW7_CS15 0x10E
+#define SW7_CS16 0x10F
+#define SW7_CS17 0x110
+#define SW7_CS18 0x111
+#define SW7_CS19 0x112
+#define SW7_CS20 0x113
+#define SW7_CS21 0x114
+#define SW7_CS22 0x115
+#define SW7_CS23 0x116
+#define SW7_CS24 0x117
+#define SW7_CS25 0x118
+#define SW7_CS26 0x119
+#define SW7_CS27 0x11A
+#define SW7_CS28 0x11B
+#define SW7_CS29 0x11C
+#define SW7_CS30 0x11D
+
+#define SW8_CS1 0x11E
+#define SW8_CS2 0x11F
+#define SW8_CS3 0x120
+#define SW8_CS4 0x121
+#define SW8_CS5 0x122
+#define SW8_CS6 0x123
+#define SW8_CS7 0x124
+#define SW8_CS8 0x125
+#define SW8_CS9 0x126
+#define SW8_CS10 0x127
+#define SW8_CS11 0x128
+#define SW8_CS12 0x129
+#define SW8_CS13 0x12A
+#define SW8_CS14 0x12B
+#define SW8_CS15 0x12C
+#define SW8_CS16 0x12D
+#define SW8_CS17 0x12E
+#define SW8_CS18 0x12F
+#define SW8_CS19 0x130
+#define SW8_CS20 0x131
+#define SW8_CS21 0x132
+#define SW8_CS22 0x133
+#define SW8_CS23 0x134
+#define SW8_CS24 0x135
+#define SW8_CS25 0x136
+#define SW8_CS26 0x137
+#define SW8_CS27 0x138
+#define SW8_CS28 0x139
+#define SW8_CS29 0x13A
+#define SW8_CS30 0x13B
+
+#define SW9_CS1 0x13C
+#define SW9_CS2 0x13D
+#define SW9_CS3 0x13E
+#define SW9_CS4 0x13F
+#define SW9_CS5 0x140
+#define SW9_CS6 0x141
+#define SW9_CS7 0x142
+#define SW9_CS8 0x143
+#define SW9_CS9 0x144
+#define SW9_CS10 0x145
+#define SW9_CS11 0x146
+#define SW9_CS12 0x147
+#define SW9_CS13 0x148
+#define SW9_CS14 0x149
+#define SW9_CS15 0x14A
+#define SW9_CS16 0x14B
+#define SW9_CS17 0x14C
+#define SW9_CS18 0x14D
+#define SW9_CS19 0x14E
+#define SW9_CS20 0x14F
+#define SW9_CS21 0x150
+#define SW9_CS22 0x151
+#define SW9_CS23 0x152
+#define SW9_CS24 0x153
+#define SW9_CS25 0x154
+#define SW9_CS26 0x155
+#define SW9_CS27 0x156
+#define SW9_CS28 0x157
+#define SW9_CS29 0x158
+#define SW9_CS30 0x159
+
+#define SW1_CS31 0x15A
+#define SW1_CS32 0x15B
+#define SW1_CS33 0x15C
+#define SW1_CS34 0x15D
+#define SW1_CS35 0x15E
+#define SW1_CS36 0x15F
+#define SW1_CS37 0x160
+#define SW1_CS38 0x161
+#define SW1_CS39 0x162
+
+#define SW2_CS31 0x163
+#define SW2_CS32 0x164
+#define SW2_CS33 0x165
+#define SW2_CS34 0x166
+#define SW2_CS35 0x167
+#define SW2_CS36 0x168
+#define SW2_CS37 0x169
+#define SW2_CS38 0x16A
+#define SW2_CS39 0x16B
+
+#define SW3_CS31 0x16C
+#define SW3_CS32 0x16D
+#define SW3_CS33 0x16E
+#define SW3_CS34 0x16F
+#define SW3_CS35 0x170
+#define SW3_CS36 0x171
+#define SW3_CS37 0x172
+#define SW3_CS38 0x173
+#define SW3_CS39 0x174
+
+#define SW4_CS31 0x175
+#define SW4_CS32 0x176
+#define SW4_CS33 0x177
+#define SW4_CS34 0x178
+#define SW4_CS35 0x179
+#define SW4_CS36 0x17A
+#define SW4_CS37 0x17B
+#define SW4_CS38 0x17C
+#define SW4_CS39 0x17D
+
+#define SW5_CS31 0x17E
+#define SW5_CS32 0x17F
+#define SW5_CS33 0x180
+#define SW5_CS34 0x181
+#define SW5_CS35 0x182
+#define SW5_CS36 0x183
+#define SW5_CS37 0x184
+#define SW5_CS38 0x185
+#define SW5_CS39 0x186
+
+#define SW6_CS31 0x187
+#define SW6_CS32 0x188
+#define SW6_CS33 0x189
+#define SW6_CS34 0x18A
+#define SW6_CS35 0x18B
+#define SW6_CS36 0x18C
+#define SW6_CS37 0x18D
+#define SW6_CS38 0x18E
+#define SW6_CS39 0x18F
+
+#define SW7_CS31 0x190
+#define SW7_CS32 0x191
+#define SW7_CS33 0x192
+#define SW7_CS34 0x193
+#define SW7_CS35 0x194
+#define SW7_CS36 0x195
+#define SW7_CS37 0x196
+#define SW7_CS38 0x197
+#define SW7_CS39 0x198
+
+#define SW8_CS31 0x199
+#define SW8_CS32 0x19A
+#define SW8_CS33 0x19B
+#define SW8_CS34 0x19C
+#define SW8_CS35 0x19D
+#define SW8_CS36 0x19E
+#define SW8_CS37 0x19F
+#define SW8_CS38 0x1A0
+#define SW8_CS39 0x1A1
+
+#define SW9_CS31 0x1A2
+#define SW9_CS32 0x1A3
+#define SW9_CS33 0x1A4
+#define SW9_CS34 0x1A5
+#define SW9_CS35 0x1A6
+#define SW9_CS36 0x1A7
+#define SW9_CS37 0x1A8
+#define SW9_CS38 0x1A9
+#define SW9_CS39 0x1AA
+
+// DEPRECATED - DO NOT USE
+
+#define CS1_SW1 SW1_CS1
+#define CS2_SW1 SW1_CS2
+#define CS3_SW1 SW1_CS3
+#define CS4_SW1 SW1_CS4
+#define CS5_SW1 SW1_CS5
+#define CS6_SW1 SW1_CS6
+#define CS7_SW1 SW1_CS7
+#define CS8_SW1 SW1_CS8
+#define CS9_SW1 SW1_CS9
+#define CS10_SW1 SW1_CS10
+#define CS11_SW1 SW1_CS11
+#define CS12_SW1 SW1_CS12
+#define CS13_SW1 SW1_CS13
+#define CS14_SW1 SW1_CS14
+#define CS15_SW1 SW1_CS15
+#define CS16_SW1 SW1_CS16
+#define CS17_SW1 SW1_CS17
+#define CS18_SW1 SW1_CS18
+#define CS19_SW1 SW1_CS19
+#define CS20_SW1 SW1_CS20
+#define CS21_SW1 SW1_CS21
+#define CS22_SW1 SW1_CS22
+#define CS23_SW1 SW1_CS23
+#define CS24_SW1 SW1_CS24
+#define CS25_SW1 SW1_CS25
+#define CS26_SW1 SW1_CS26
+#define CS27_SW1 SW1_CS27
+#define CS28_SW1 SW1_CS28
+#define CS29_SW1 SW1_CS29
+#define CS30_SW1 SW1_CS30
+
+#define CS1_SW2 SW2_CS1
+#define CS2_SW2 SW2_CS2
+#define CS3_SW2 SW2_CS3
+#define CS4_SW2 SW2_CS4
+#define CS5_SW2 SW2_CS5
+#define CS6_SW2 SW2_CS6
+#define CS7_SW2 SW2_CS7
+#define CS8_SW2 SW2_CS8
+#define CS9_SW2 SW2_CS9
+#define CS10_SW2 SW2_CS10
+#define CS11_SW2 SW2_CS11
+#define CS12_SW2 SW2_CS12
+#define CS13_SW2 SW2_CS13
+#define CS14_SW2 SW2_CS14
+#define CS15_SW2 SW2_CS15
+#define CS16_SW2 SW2_CS16
+#define CS17_SW2 SW2_CS17
+#define CS18_SW2 SW2_CS18
+#define CS19_SW2 SW2_CS19
+#define CS20_SW2 SW2_CS20
+#define CS21_SW2 SW2_CS21
+#define CS22_SW2 SW2_CS22
+#define CS23_SW2 SW2_CS23
+#define CS24_SW2 SW2_CS24
+#define CS25_SW2 SW2_CS25
+#define CS26_SW2 SW2_CS26
+#define CS27_SW2 SW2_CS27
+#define CS28_SW2 SW2_CS28
+#define CS29_SW2 SW2_CS29
+#define CS30_SW2 SW2_CS30
+
+#define CS1_SW3 SW3_CS1
+#define CS2_SW3 SW3_CS2
+#define CS3_SW3 SW3_CS3
+#define CS4_SW3 SW3_CS4
+#define CS5_SW3 SW3_CS5
+#define CS6_SW3 SW3_CS6
+#define CS7_SW3 SW3_CS7
+#define CS8_SW3 SW3_CS8
+#define CS9_SW3 SW3_CS9
+#define CS10_SW3 SW3_CS10
+#define CS11_SW3 SW3_CS11
+#define CS12_SW3 SW3_CS12
+#define CS13_SW3 SW3_CS13
+#define CS14_SW3 SW3_CS14
+#define CS15_SW3 SW3_CS15
+#define CS16_SW3 SW3_CS16
+#define CS17_SW3 SW3_CS17
+#define CS18_SW3 SW3_CS18
+#define CS19_SW3 SW3_CS19
+#define CS20_SW3 SW3_CS20
+#define CS21_SW3 SW3_CS21
+#define CS22_SW3 SW3_CS22
+#define CS23_SW3 SW3_CS23
+#define CS24_SW3 SW3_CS24
+#define CS25_SW3 SW3_CS25
+#define CS26_SW3 SW3_CS26
+#define CS27_SW3 SW3_CS27
+#define CS28_SW3 SW3_CS28
+#define CS29_SW3 SW3_CS29
+#define CS30_SW3 SW3_CS30
+
+#define CS1_SW4 SW4_CS1
+#define CS2_SW4 SW4_CS2
+#define CS3_SW4 SW4_CS3
+#define CS4_SW4 SW4_CS4
+#define CS5_SW4 SW4_CS5
+#define CS6_SW4 SW4_CS6
+#define CS7_SW4 SW4_CS7
+#define CS8_SW4 SW4_CS8
+#define CS9_SW4 SW4_CS9
+#define CS10_SW4 SW4_CS10
+#define CS11_SW4 SW4_CS11
+#define CS12_SW4 SW4_CS12
+#define CS13_SW4 SW4_CS13
+#define CS14_SW4 SW4_CS14
+#define CS15_SW4 SW4_CS15
+#define CS16_SW4 SW4_CS16
+#define CS17_SW4 SW4_CS17
+#define CS18_SW4 SW4_CS18
+#define CS19_SW4 SW4_CS19
+#define CS20_SW4 SW4_CS20
+#define CS21_SW4 SW4_CS21
+#define CS22_SW4 SW4_CS22
+#define CS23_SW4 SW4_CS23
+#define CS24_SW4 SW4_CS24
+#define CS25_SW4 SW4_CS25
+#define CS26_SW4 SW4_CS26
+#define CS27_SW4 SW4_CS27
+#define CS28_SW4 SW4_CS28
+#define CS29_SW4 SW4_CS29
+#define CS30_SW4 SW4_CS30
+
+#define CS1_SW5 SW5_CS1
+#define CS2_SW5 SW5_CS2
+#define CS3_SW5 SW5_CS3
+#define CS4_SW5 SW5_CS4
+#define CS5_SW5 SW5_CS5
+#define CS6_SW5 SW5_CS6
+#define CS7_SW5 SW5_CS7
+#define CS8_SW5 SW5_CS8
+#define CS9_SW5 SW5_CS9
+#define CS10_SW5 SW5_CS10
+#define CS11_SW5 SW5_CS11
+#define CS12_SW5 SW5_CS12
+#define CS13_SW5 SW5_CS13
+#define CS14_SW5 SW5_CS14
+#define CS15_SW5 SW5_CS15
+#define CS16_SW5 SW5_CS16
+#define CS17_SW5 SW5_CS17
+#define CS18_SW5 SW5_CS18
+#define CS19_SW5 SW5_CS19
+#define CS20_SW5 SW5_CS20
+#define CS21_SW5 SW5_CS21
+#define CS22_SW5 SW5_CS22
+#define CS23_SW5 SW5_CS23
+#define CS24_SW5 SW5_CS24
+#define CS25_SW5 SW5_CS25
+#define CS26_SW5 SW5_CS26
+#define CS27_SW5 SW5_CS27
+#define CS28_SW5 SW5_CS28
+#define CS29_SW5 SW5_CS29
+#define CS30_SW5 SW5_CS30
+
+#define CS1_SW6 SW6_CS1
+#define CS2_SW6 SW6_CS2
+#define CS3_SW6 SW6_CS3
+#define CS4_SW6 SW6_CS4
+#define CS5_SW6 SW6_CS5
+#define CS6_SW6 SW6_CS6
+#define CS7_SW6 SW6_CS7
+#define CS8_SW6 SW6_CS8
+#define CS9_SW6 SW6_CS9
+#define CS10_SW6 SW6_CS10
+#define CS11_SW6 SW6_CS11
+#define CS12_SW6 SW6_CS12
+#define CS13_SW6 SW6_CS13
+#define CS14_SW6 SW6_CS14
+#define CS15_SW6 SW6_CS15
+#define CS16_SW6 SW6_CS16
+#define CS17_SW6 SW6_CS17
+#define CS18_SW6 SW6_CS18
+#define CS19_SW6 SW6_CS19
+#define CS20_SW6 SW6_CS20
+#define CS21_SW6 SW6_CS21
+#define CS22_SW6 SW6_CS22
+#define CS23_SW6 SW6_CS23
+#define CS24_SW6 SW6_CS24
+#define CS25_SW6 SW6_CS25
+#define CS26_SW6 SW6_CS26
+#define CS27_SW6 SW6_CS27
+#define CS28_SW6 SW6_CS28
+#define CS29_SW6 SW6_CS29
+#define CS30_SW6 SW6_CS30
+
+#define CS1_SW7 SW7_CS1
+#define CS2_SW7 SW7_CS2
+#define CS3_SW7 SW7_CS3
+#define CS4_SW7 SW7_CS4
+#define CS5_SW7 SW7_CS5
+#define CS6_SW7 SW7_CS6
+#define CS7_SW7 SW7_CS7
+#define CS8_SW7 SW7_CS8
+#define CS9_SW7 SW7_CS9
+#define CS10_SW7 SW7_CS10
+#define CS11_SW7 SW7_CS11
+#define CS12_SW7 SW7_CS12
+#define CS13_SW7 SW7_CS13
+#define CS14_SW7 SW7_CS14
+#define CS15_SW7 SW7_CS15
+#define CS16_SW7 SW7_CS16
+#define CS17_SW7 SW7_CS17
+#define CS18_SW7 SW7_CS18
+#define CS19_SW7 SW7_CS19
+#define CS20_SW7 SW7_CS20
+#define CS21_SW7 SW7_CS21
+#define CS22_SW7 SW7_CS22
+#define CS23_SW7 SW7_CS23
+#define CS24_SW7 SW7_CS24
+#define CS25_SW7 SW7_CS25
+#define CS26_SW7 SW7_CS26
+#define CS27_SW7 SW7_CS27
+#define CS28_SW7 SW7_CS28
+#define CS29_SW7 SW7_CS29
+#define CS30_SW7 SW7_CS30
+
+#define CS1_SW8 SW8_CS1
+#define CS2_SW8 SW8_CS2
+#define CS3_SW8 SW8_CS3
+#define CS4_SW8 SW8_CS4
+#define CS5_SW8 SW8_CS5
+#define CS6_SW8 SW8_CS6
+#define CS7_SW8 SW8_CS7
+#define CS8_SW8 SW8_CS8
+#define CS9_SW8 SW8_CS9
+#define CS10_SW8 SW8_CS10
+#define CS11_SW8 SW8_CS11
+#define CS12_SW8 SW8_CS12
+#define CS13_SW8 SW8_CS13
+#define CS14_SW8 SW8_CS14
+#define CS15_SW8 SW8_CS15
+#define CS16_SW8 SW8_CS16
+#define CS17_SW8 SW8_CS17
+#define CS18_SW8 SW8_CS18
+#define CS19_SW8 SW8_CS19
+#define CS20_SW8 SW8_CS20
+#define CS21_SW8 SW8_CS21
+#define CS22_SW8 SW8_CS22
+#define CS23_SW8 SW8_CS23
+#define CS24_SW8 SW8_CS24
+#define CS25_SW8 SW8_CS25
+#define CS26_SW8 SW8_CS26
+#define CS27_SW8 SW8_CS27
+#define CS28_SW8 SW8_CS28
+#define CS29_SW8 SW8_CS29
+#define CS30_SW8 SW8_CS30
+
+#define CS1_SW9 SW9_CS1
+#define CS2_SW9 SW9_CS2
+#define CS3_SW9 SW9_CS3
+#define CS4_SW9 SW9_CS4
+#define CS5_SW9 SW9_CS5
+#define CS6_SW9 SW9_CS6
+#define CS7_SW9 SW9_CS7
+#define CS8_SW9 SW9_CS8
+#define CS9_SW9 SW9_CS9
+#define CS10_SW9 SW9_CS10
+#define CS11_SW9 SW9_CS11
+#define CS12_SW9 SW9_CS12
+#define CS13_SW9 SW9_CS13
+#define CS14_SW9 SW9_CS14
+#define CS15_SW9 SW9_CS15
+#define CS16_SW9 SW9_CS16
+#define CS17_SW9 SW9_CS17
+#define CS18_SW9 SW9_CS18
+#define CS19_SW9 SW9_CS19
+#define CS20_SW9 SW9_CS20
+#define CS21_SW9 SW9_CS21
+#define CS22_SW9 SW9_CS22
+#define CS23_SW9 SW9_CS23
+#define CS24_SW9 SW9_CS24
+#define CS25_SW9 SW9_CS25
+#define CS26_SW9 SW9_CS26
+#define CS27_SW9 SW9_CS27
+#define CS28_SW9 SW9_CS28
+#define CS29_SW9 SW9_CS29
+#define CS30_SW9 SW9_CS30
+
+#define CS31_SW1 SW1_CS31
+#define CS32_SW1 SW1_CS32
+#define CS33_SW1 SW1_CS33
+#define CS34_SW1 SW1_CS34
+#define CS35_SW1 SW1_CS35
+#define CS36_SW1 SW1_CS36
+#define CS37_SW1 SW1_CS37
+#define CS38_SW1 SW1_CS38
+#define CS39_SW1 SW1_CS39
+
+#define CS31_SW2 SW2_CS31
+#define CS32_SW2 SW2_CS32
+#define CS33_SW2 SW2_CS33
+#define CS34_SW2 SW2_CS34
+#define CS35_SW2 SW2_CS35
+#define CS36_SW2 SW2_CS36
+#define CS37_SW2 SW2_CS37
+#define CS38_SW2 SW2_CS38
+#define CS39_SW2 SW2_CS39
+
+#define CS31_SW3 SW3_CS31
+#define CS32_SW3 SW3_CS32
+#define CS33_SW3 SW3_CS33
+#define CS34_SW3 SW3_CS34
+#define CS35_SW3 SW3_CS35
+#define CS36_SW3 SW3_CS36
+#define CS37_SW3 SW3_CS37
+#define CS38_SW3 SW3_CS38
+#define CS39_SW3 SW3_CS39
+
+#define CS31_SW4 SW4_CS31
+#define CS32_SW4 SW4_CS32
+#define CS33_SW4 SW4_CS33
+#define CS34_SW4 SW4_CS34
+#define CS35_SW4 SW4_CS35
+#define CS36_SW4 SW4_CS36
+#define CS37_SW4 SW4_CS37
+#define CS38_SW4 SW4_CS38
+#define CS39_SW4 SW4_CS39
+
+#define CS31_SW5 SW5_CS31
+#define CS32_SW5 SW5_CS32
+#define CS33_SW5 SW5_CS33
+#define CS34_SW5 SW5_CS34
+#define CS35_SW5 SW5_CS35
+#define CS36_SW5 SW5_CS36
+#define CS37_SW5 SW5_CS37
+#define CS38_SW5 SW5_CS38
+#define CS39_SW5 SW5_CS39
+
+#define CS31_SW6 SW6_CS31
+#define CS32_SW6 SW6_CS32
+#define CS33_SW6 SW6_CS33
+#define CS34_SW6 SW6_CS34
+#define CS35_SW6 SW6_CS35
+#define CS36_SW6 SW6_CS36
+#define CS37_SW6 SW6_CS37
+#define CS38_SW6 SW6_CS38
+#define CS39_SW6 SW6_CS39
+
+#define CS31_SW7 SW7_CS31
+#define CS32_SW7 SW7_CS32
+#define CS33_SW7 SW7_CS33
+#define CS34_SW7 SW7_CS34
+#define CS35_SW7 SW7_CS35
+#define CS36_SW7 SW7_CS36
+#define CS37_SW7 SW7_CS37
+#define CS38_SW7 SW7_CS38
+#define CS39_SW7 SW7_CS39
+
+#define CS31_SW8 SW8_CS31
+#define CS32_SW8 SW8_CS32
+#define CS33_SW8 SW8_CS33
+#define CS34_SW8 SW8_CS34
+#define CS35_SW8 SW8_CS35
+#define CS36_SW8 SW8_CS36
+#define CS37_SW8 SW8_CS37
+#define CS38_SW8 SW8_CS38
+#define CS39_SW8 SW8_CS39
+
+#define CS31_SW9 SW9_CS31
+#define CS32_SW9 SW9_CS32
+#define CS33_SW9 SW9_CS33
+#define CS34_SW9 SW9_CS34
+#define CS35_SW9 SW9_CS35
+#define CS36_SW9 SW9_CS36
+#define CS37_SW9 SW9_CS37
+#define CS38_SW9 SW9_CS38
+#define CS39_SW9 SW9_CS39
diff --git a/drivers/led/issi/is31fl3742.h b/drivers/led/issi/is31fl3742.h
deleted file mode 100644
index c96f12d0f1..0000000000
--- a/drivers/led/issi/is31fl3742.h
+++ /dev/null
@@ -1,299 +0,0 @@
-/* Copyright 2017 Jason Williams
- * Copyright 2018 Jack Humbert
- * Copyright 2018 Yiancar
- * Copyright 2020 MelGeek
- * Copyright 2021 MasterSpoon
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-// This is a 7-bit address, that gets left-shifted and bit 0
-// set to 0 for write, 1 for read (as per I2C protocol)
-// The address will vary depending on your wiring:
-// 00 <-> GND
-// 01 <-> SCL
-// 10 <-> SDA
-// 11 <-> VCC
-// ADDR represents A1:A0 of the 7-bit address.
-// The result is: 0b01100(ADDR)
-#ifndef DRIVER_ADDR_1
-# define DRIVER_ADDR_1 0b0110000
-#endif
-
-// Command Registers
-#define ISSI_COMMANDREGISTER_WRITELOCK 0xFE
-#define ISSI_COMMANDREGISTER 0xFD
-#define ISSI_IDREGISTER 0xFC
-#define ISSI_REGISTER_UNLOCK 0xC5
-
-// Response Registers
-#define ISSI_PAGE_PWM 0x00
-#define ISSI_PAGE_SCALING 0x02
-#define ISSI_PAGE_FUNCTION 0x04
-
-// Registers under Function Register
-#define ISSI_REG_CONFIGURATION 0x00
-#define ISSI_REG_GLOBALCURRENT 0x01
-#define ISSI_REG_PULLDOWNUP 0x02
-#define ISSI_REG_SSR 0x41
-#define ISSI_REG_RESET 0x3F
-#define ISSI_REG_PWM_SET 0x36
-
-// Set defaults for Function Registers
-#ifndef ISSI_CONFIGURATION
-# define ISSI_CONFIGURATION 0x31
-#endif
-#ifndef ISSI_GLOBALCURRENT
-# define ISSI_GLOBALCURRENT 0xFF
-#endif
-#ifndef ISSI_PULLDOWNUP
-# define ISSI_PULLDOWNUP 0x55
-#endif
-#ifndef ISSI_PWM_SET
-# define ISSI_PWM_SET 0x00
-#endif
-
-// Set defaults for Spread Spectrum Register
-#ifndef ISSI_SSR_1
-# define ISSI_SSR_1 0x00
-#endif
-#ifndef ISSI_SSR_2
-# define ISSI_SSR_2 0x00
-#endif
-#ifndef ISSI_SSR_3
-# define ISSI_SSR_3 0x00
-#endif
-#ifndef ISSI_SSR_4
-# define ISSI_SSR_4 0x00
-#endif
-
-// Set defaults for Scaling registers
-#ifndef ISSI_SCAL_RED
-# define ISSI_SCAL_RED 0xFF
-#endif
-#ifndef ISSI_SCAL_BLUE
-# define ISSI_SCAL_BLUE 0xFF
-#endif
-#ifndef ISSI_SCAL_GREEN
-# define ISSI_SCAL_GREEN 0xFF
-#endif
-#define ISSI_SCAL_RED_OFF 0x00
-#define ISSI_SCAL_GREEN_OFF 0x00
-#define ISSI_SCAL_BLUE_OFF 0x00
-
-#ifndef ISSI_SCAL_LED
-# define ISSI_SCAL_LED 0xFF
-#endif
-#define ISSI_SCAL_LED_OFF 0x00
-
-// Set buffer sizes
-#define ISSI_MAX_LEDS 180
-#define ISSI_SCALING_SIZE 180
-#define ISSI_PWM_TRF_SIZE 18
-#define ISSI_SCALING_TRF_SIZE 18
-
-// Location of 1st bit for PWM and Scaling registers
-#define ISSI_PWM_REG_1ST 0x00
-#define ISSI_SCL_REG_1ST 0x00
-
-// Map CS SW locations to order in PWM / Scaling buffers
-// This matches the ORDER in the Datasheet Register not the POSITION
-// It will always count from 0x00 to (ISSI_MAX_LEDS - 1)
-#define CS1_SW1 0x00
-#define CS2_SW1 0x01
-#define CS3_SW1 0x02
-#define CS4_SW1 0x03
-#define CS5_SW1 0x04
-#define CS6_SW1 0x05
-#define CS7_SW1 0x06
-#define CS8_SW1 0x07
-#define CS9_SW1 0x08
-#define CS10_SW1 0x09
-#define CS11_SW1 0x0A
-#define CS12_SW1 0x0B
-#define CS13_SW1 0x0C
-#define CS14_SW1 0x0D
-#define CS15_SW1 0x0E
-#define CS16_SW1 0x0F
-#define CS17_SW1 0x10
-#define CS18_SW1 0x11
-#define CS19_SW1 0x12
-#define CS20_SW1 0x13
-#define CS21_SW1 0x14
-#define CS22_SW1 0x15
-#define CS23_SW1 0x16
-#define CS24_SW1 0x17
-#define CS25_SW1 0x18
-#define CS26_SW1 0x19
-#define CS27_SW1 0x1A
-#define CS28_SW1 0x1B
-#define CS29_SW1 0x1C
-#define CS30_SW1 0x1D
-
-#define CS1_SW2 0x1E
-#define CS2_SW2 0x1F
-#define CS3_SW2 0x20
-#define CS4_SW2 0x21
-#define CS5_SW2 0x22
-#define CS6_SW2 0x23
-#define CS7_SW2 0x24
-#define CS8_SW2 0x25
-#define CS9_SW2 0x26
-#define CS10_SW2 0x27
-#define CS11_SW2 0x28
-#define CS12_SW2 0x29
-#define CS13_SW2 0x2A
-#define CS14_SW2 0x2B
-#define CS15_SW2 0x2C
-#define CS16_SW2 0x2D
-#define CS17_SW2 0x2E
-#define CS18_SW2 0x2F
-#define CS19_SW2 0x30
-#define CS20_SW2 0x31
-#define CS21_SW2 0x32
-#define CS22_SW2 0x33
-#define CS23_SW2 0x34
-#define CS24_SW2 0x35
-#define CS25_SW2 0x36
-#define CS26_SW2 0x37
-#define CS27_SW2 0x38
-#define CS28_SW2 0x39
-#define CS29_SW2 0x3A
-#define CS30_SW2 0x3B
-
-#define CS1_SW3 0x3C
-#define CS2_SW3 0x3D
-#define CS3_SW3 0x3E
-#define CS4_SW3 0x3F
-#define CS5_SW3 0x40
-#define CS6_SW3 0x41
-#define CS7_SW3 0x42
-#define CS8_SW3 0x43
-#define CS9_SW3 0x44
-#define CS10_SW3 0x45
-#define CS11_SW3 0x46
-#define CS12_SW3 0x47
-#define CS13_SW3 0x48
-#define CS14_SW3 0x49
-#define CS15_SW3 0x4A
-#define CS16_SW3 0x4B
-#define CS17_SW3 0x4C
-#define CS18_SW3 0x4D
-#define CS19_SW3 0x4E
-#define CS20_SW3 0x4F
-#define CS21_SW3 0x50
-#define CS22_SW3 0x51
-#define CS23_SW3 0x52
-#define CS24_SW3 0x53
-#define CS25_SW3 0x54
-#define CS26_SW3 0x55
-#define CS27_SW3 0x56
-#define CS28_SW3 0x57
-#define CS29_SW3 0x58
-#define CS30_SW3 0x59
-
-#define CS1_SW4 0x5A
-#define CS2_SW4 0x5B
-#define CS3_SW4 0x5C
-#define CS4_SW4 0x5D
-#define CS5_SW4 0x5E
-#define CS6_SW4 0x5F
-#define CS7_SW4 0x60
-#define CS8_SW4 0x61
-#define CS9_SW4 0x62
-#define CS10_SW4 0x63
-#define CS11_SW4 0x64
-#define CS12_SW4 0x65
-#define CS13_SW4 0x66
-#define CS14_SW4 0x67
-#define CS15_SW4 0x68
-#define CS16_SW4 0x69
-#define CS17_SW4 0x6A
-#define CS18_SW4 0x6B
-#define CS19_SW4 0x6C
-#define CS20_SW4 0x6D
-#define CS21_SW4 0x6E
-#define CS22_SW4 0x6F
-#define CS23_SW4 0x70
-#define CS24_SW4 0x71
-#define CS25_SW4 0x72
-#define CS26_SW4 0x73
-#define CS27_SW4 0x74
-#define CS28_SW4 0x75
-#define CS29_SW4 0x76
-#define CS30_SW4 0x77
-
-#define CS1_SW5 0x78
-#define CS2_SW5 0x79
-#define CS3_SW5 0x7A
-#define CS4_SW5 0x7B
-#define CS5_SW5 0x7C
-#define CS6_SW5 0x7D
-#define CS7_SW5 0x7E
-#define CS8_SW5 0x7F
-#define CS9_SW5 0x80
-#define CS10_SW5 0x81
-#define CS11_SW5 0x82
-#define CS12_SW5 0x83
-#define CS13_SW5 0x84
-#define CS14_SW5 0x85
-#define CS15_SW5 0x86
-#define CS16_SW5 0x87
-#define CS17_SW5 0x88
-#define CS18_SW5 0x89
-#define CS19_SW5 0x8A
-#define CS20_SW5 0x8B
-#define CS21_SW5 0x8C
-#define CS22_SW5 0x8D
-#define CS23_SW5 0x8E
-#define CS24_SW5 0x8F
-#define CS25_SW5 0x90
-#define CS26_SW5 0x91
-#define CS27_SW5 0x92
-#define CS28_SW5 0x93
-#define CS29_SW5 0x94
-#define CS30_SW5 0x95
-
-#define CS1_SW6 0x96
-#define CS2_SW6 0x97
-#define CS3_SW6 0x98
-#define CS4_SW6 0x99
-#define CS5_SW6 0x9A
-#define CS6_SW6 0x9B
-#define CS7_SW6 0x9C
-#define CS8_SW6 0x9D
-#define CS9_SW6 0x9E
-#define CS10_SW6 0x9F
-#define CS11_SW6 0xA0
-#define CS12_SW6 0xA1
-#define CS13_SW6 0xA2
-#define CS14_SW6 0xA3
-#define CS15_SW6 0xA4
-#define CS16_SW6 0xA5
-#define CS17_SW6 0xA6
-#define CS18_SW6 0xA7
-#define CS19_SW6 0xA8
-#define CS20_SW6 0xA9
-#define CS21_SW6 0xAA
-#define CS22_SW6 0xAB
-#define CS23_SW6 0xAC
-#define CS24_SW6 0xAD
-#define CS25_SW6 0xAE
-#define CS26_SW6 0xAF
-#define CS27_SW6 0xB0
-#define CS28_SW6 0xB1
-#define CS29_SW6 0xB2
-#define CS30_SW6 0xB3
diff --git a/drivers/led/issi/is31fl3742a-mono.c b/drivers/led/issi/is31fl3742a-mono.c
new file mode 100644
index 0000000000..c63db1a7fc
--- /dev/null
+++ b/drivers/led/issi/is31fl3742a-mono.c
@@ -0,0 +1,221 @@
+/* Copyright 2017 Jason Williams
+ * Copyright 2018 Jack Humbert
+ * Copyright 2018 Yiancar
+ * Copyright 2020 MelGeek
+ * Copyright 2021 MasterSpoon
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "is31fl3742a-mono.h"
+#include "i2c_master.h"
+#include "gpio.h"
+#include "wait.h"
+
+#define IS31FL3742A_PWM_REGISTER_COUNT 180
+#define IS31FL3742A_SCALING_REGISTER_COUNT 180
+
+#ifndef IS31FL3742A_I2C_TIMEOUT
+# define IS31FL3742A_I2C_TIMEOUT 100
+#endif
+
+#ifndef IS31FL3742A_I2C_PERSISTENCE
+# define IS31FL3742A_I2C_PERSISTENCE 0
+#endif
+
+#ifndef IS31FL3742A_CONFIGURATION
+# define IS31FL3742A_CONFIGURATION 0x31
+#endif
+
+#ifndef IS31FL3742A_PWM_FREQUENCY
+# define IS31FL3742A_PWM_FREQUENCY IS31FL3742A_PWM_FREQUENCY_29K_HZ
+#endif
+
+#ifndef IS31FL3742A_SW_PULLDOWN
+# define IS31FL3742A_SW_PULLDOWN IS31FL3742A_PDR_8K_OHM
+#endif
+
+#ifndef IS31FL3742A_CS_PULLUP
+# define IS31FL3742A_CS_PULLUP IS31FL3742A_PUR_8K_OHM
+#endif
+
+#ifndef IS31FL3742A_GLOBAL_CURRENT
+# define IS31FL3742A_GLOBAL_CURRENT 0xFF
+#endif
+
+const uint8_t i2c_addresses[IS31FL3742A_DRIVER_COUNT] = {
+ IS31FL3742A_I2C_ADDRESS_1,
+#ifdef IS31FL3742A_I2C_ADDRESS_2
+ IS31FL3742A_I2C_ADDRESS_2,
+# ifdef IS31FL3742A_I2C_ADDRESS_3
+ IS31FL3742A_I2C_ADDRESS_3,
+# ifdef IS31FL3742A_I2C_ADDRESS_4
+ IS31FL3742A_I2C_ADDRESS_4,
+# endif
+# endif
+#endif
+};
+
+typedef struct is31fl3742a_driver_t {
+ uint8_t pwm_buffer[IS31FL3742A_PWM_REGISTER_COUNT];
+ bool pwm_buffer_dirty;
+ uint8_t scaling_buffer[IS31FL3742A_SCALING_REGISTER_COUNT];
+ bool scaling_buffer_dirty;
+} PACKED is31fl3742a_driver_t;
+
+is31fl3742a_driver_t driver_buffers[IS31FL3742A_DRIVER_COUNT] = {{
+ .pwm_buffer = {0},
+ .pwm_buffer_dirty = false,
+ .scaling_buffer = {0},
+ .scaling_buffer_dirty = false,
+}};
+
+void is31fl3742a_write_register(uint8_t index, uint8_t reg, uint8_t data) {
+#if IS31FL3742A_I2C_PERSISTENCE > 0
+ for (uint8_t i = 0; i < IS31FL3742A_I2C_PERSISTENCE; i++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3742A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3742A_I2C_TIMEOUT);
+#endif
+}
+
+void is31fl3742a_select_page(uint8_t index, uint8_t page) {
+ is31fl3742a_write_register(index, IS31FL3742A_REG_COMMAND_WRITE_LOCK, IS31FL3742A_COMMAND_WRITE_LOCK_MAGIC);
+ is31fl3742a_write_register(index, IS31FL3742A_REG_COMMAND, page);
+}
+
+void is31fl3742a_write_pwm_buffer(uint8_t index) {
+ // Assumes page 0 is already selected.
+ // Transmit PWM registers in 6 transfers of 30 bytes.
+
+ // Iterate over the pwm_buffer contents at 30 byte intervals.
+ for (uint8_t i = 0; i < IS31FL3742A_PWM_REGISTER_COUNT; i += 30) {
+#if IS31FL3742A_I2C_PERSISTENCE > 0
+ for (uint8_t j = 0; j < IS31FL3742A_I2C_PERSISTENCE; j++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 30, IS31FL3742A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 30, IS31FL3742A_I2C_TIMEOUT);
+#endif
+ }
+}
+
+void is31fl3742a_init_drivers(void) {
+ i2c_init();
+
+#if defined(IS31FL3742A_SDB_PIN)
+ gpio_set_pin_output(IS31FL3742A_SDB_PIN);
+ gpio_write_pin_high(IS31FL3742A_SDB_PIN);
+#endif
+
+ for (uint8_t i = 0; i < IS31FL3742A_DRIVER_COUNT; i++) {
+ is31fl3742a_init(i);
+ }
+
+ for (int i = 0; i < IS31FL3742A_LED_COUNT; i++) {
+ is31fl3742a_set_scaling_register(i, 0xFF);
+ }
+
+ for (uint8_t i = 0; i < IS31FL3742A_DRIVER_COUNT; i++) {
+ is31fl3742a_update_scaling_registers(i);
+ }
+}
+
+void is31fl3742a_init(uint8_t index) {
+ // In order to avoid the LEDs being driven with garbage data
+ // in the LED driver's PWM registers, shutdown is enabled last.
+ // Set up the mode and other settings, clear the PWM registers,
+ // then disable software shutdown.
+
+ is31fl3742a_select_page(index, IS31FL3742A_COMMAND_SCALING);
+
+ // Turn off all LEDs.
+ for (uint8_t i = 0; i < IS31FL3742A_SCALING_REGISTER_COUNT; i++) {
+ is31fl3742a_write_register(index, i, 0x00);
+ }
+
+ is31fl3742a_select_page(index, IS31FL3742A_COMMAND_PWM);
+
+ for (uint8_t i = 0; i < IS31FL3742A_PWM_REGISTER_COUNT; i++) {
+ is31fl3742a_write_register(index, i, 0x00);
+ }
+
+ is31fl3742a_select_page(index, IS31FL3742A_COMMAND_FUNCTION);
+
+ is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_PULLDOWNUP, (IS31FL3742A_SW_PULLDOWN << 4) | IS31FL3742A_CS_PULLUP);
+ is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3742A_GLOBAL_CURRENT);
+ is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_PWM_FREQUENCY, (IS31FL3742A_PWM_FREQUENCY & 0b0111));
+ is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_CONFIGURATION, IS31FL3742A_CONFIGURATION);
+
+ // Wait 10ms to ensure the device has woken up.
+ wait_ms(10);
+}
+
+void is31fl3742a_set_value(int index, uint8_t value) {
+ is31fl3742a_led_t led;
+
+ if (index >= 0 && index < IS31FL3742A_LED_COUNT) {
+ memcpy_P(&led, (&g_is31fl3742a_leds[index]), sizeof(led));
+
+ if (driver_buffers[led.driver].pwm_buffer[led.v] == value) {
+ return;
+ }
+
+ driver_buffers[led.driver].pwm_buffer[led.v] = value;
+ driver_buffers[led.driver].pwm_buffer_dirty = true;
+ }
+}
+
+void is31fl3742a_set_value_all(uint8_t value) {
+ for (int i = 0; i < IS31FL3742A_LED_COUNT; i++) {
+ is31fl3742a_set_value(i, value);
+ }
+}
+
+void is31fl3742a_set_scaling_register(uint8_t index, uint8_t value) {
+ is31fl3742a_led_t led;
+ memcpy_P(&led, (&g_is31fl3742a_leds[index]), sizeof(led));
+
+ driver_buffers[led.driver].scaling_buffer[led.v] = value;
+ driver_buffers[led.driver].scaling_buffer_dirty = true;
+}
+
+void is31fl3742a_update_pwm_buffers(uint8_t index) {
+ if (driver_buffers[index].pwm_buffer_dirty) {
+ is31fl3742a_select_page(index, IS31FL3742A_COMMAND_PWM);
+
+ is31fl3742a_write_pwm_buffer(index);
+
+ driver_buffers[index].pwm_buffer_dirty = false;
+ }
+}
+
+void is31fl3742a_update_scaling_registers(uint8_t index) {
+ if (driver_buffers[index].scaling_buffer_dirty) {
+ is31fl3742a_select_page(index, IS31FL3742A_COMMAND_SCALING);
+
+ for (uint8_t i = 0; i < IS31FL3742A_SCALING_REGISTER_COUNT; i++) {
+ is31fl3742a_write_register(index, i, driver_buffers[index].scaling_buffer[i]);
+ }
+
+ driver_buffers[index].scaling_buffer_dirty = false;
+ }
+}
+
+void is31fl3742a_flush(void) {
+ for (uint8_t i = 0; i < IS31FL3742A_DRIVER_COUNT; i++) {
+ is31fl3742a_update_pwm_buffers(i);
+ }
+}
diff --git a/drivers/led/issi/is31fl3742a-mono.h b/drivers/led/issi/is31fl3742a-mono.h
new file mode 100644
index 0000000000..5c8938906c
--- /dev/null
+++ b/drivers/led/issi/is31fl3742a-mono.h
@@ -0,0 +1,296 @@
+/* Copyright 2017 Jason Williams
+ * Copyright 2018 Jack Humbert
+ * Copyright 2018 Yiancar
+ * Copyright 2020 MelGeek
+ * Copyright 2021 MasterSpoon
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "progmem.h"
+#include "util.h"
+
+#define IS31FL3742A_REG_INTERRUPT_MASK 0xF0
+#define IS31FL3742A_REG_INTERRUPT_STATUS 0xF1
+#define IS31FL3742A_REG_ID 0xFC
+
+#define IS31FL3742A_REG_COMMAND 0xFD
+
+#define IS31FL3742A_COMMAND_PWM 0x00
+#define IS31FL3742A_COMMAND_SCALING 0x02
+#define IS31FL3742A_COMMAND_FUNCTION 0x04
+
+#define IS31FL3742A_FUNCTION_REG_CONFIGURATION 0x00
+#define IS31FL3742A_FUNCTION_REG_GLOBAL_CURRENT 0x01
+#define IS31FL3742A_FUNCTION_REG_PULLDOWNUP 0x02
+#define IS31FL3742A_FUNCTION_REG_PWM_FREQUENCY 0x36
+#define IS31FL3742A_FUNCTION_REG_RESET 0x3F
+#define IS31FL3742A_FUNCTION_REG_SPREAD_SPECTRUM 0x41
+
+#define IS31FL3742A_REG_COMMAND_WRITE_LOCK 0xFE
+#define IS31FL3742A_COMMAND_WRITE_LOCK_MAGIC 0xC5
+
+#define IS31FL3742A_I2C_ADDRESS_GND 0x30
+#define IS31FL3742A_I2C_ADDRESS_SCL 0x31
+#define IS31FL3742A_I2C_ADDRESS_SDA 0x32
+#define IS31FL3742A_I2C_ADDRESS_VCC 0x33
+
+#if defined(LED_MATRIX_IS31FL3742A)
+# define IS31FL3742A_LED_COUNT LED_MATRIX_LED_COUNT
+#endif
+
+#if defined(IS31FL3742A_I2C_ADDRESS_4)
+# define IS31FL3742A_DRIVER_COUNT 4
+#elif defined(IS31FL3742A_I2C_ADDRESS_3)
+# define IS31FL3742A_DRIVER_COUNT 3
+#elif defined(IS31FL3742A_I2C_ADDRESS_2)
+# define IS31FL3742A_DRIVER_COUNT 2
+#elif defined(IS31FL3742A_I2C_ADDRESS_1)
+# define IS31FL3742A_DRIVER_COUNT 1
+#endif
+
+typedef struct is31fl3742a_led_t {
+ uint8_t driver : 2;
+ uint8_t v;
+} PACKED is31fl3742a_led_t;
+
+extern const is31fl3742a_led_t PROGMEM g_is31fl3742a_leds[IS31FL3742A_LED_COUNT];
+
+void is31fl3742a_init_drivers(void);
+void is31fl3742a_init(uint8_t index);
+void is31fl3742a_write_register(uint8_t index, uint8_t reg, uint8_t data);
+void is31fl3742a_select_page(uint8_t index, uint8_t page);
+
+void is31fl3742a_set_value(int index, uint8_t value);
+void is31fl3742a_set_value_all(uint8_t value);
+
+void is31fl3742a_set_scaling_register(uint8_t index, uint8_t value);
+
+void is31fl3742a_update_pwm_buffers(uint8_t index);
+void is31fl3742a_update_scaling_registers(uint8_t index);
+
+void is31fl3742a_flush(void);
+
+#define IS31FL3742A_PDR_0_OHM 0b000 // No pull-down resistor
+#define IS31FL3742A_PDR_0K5_OHM 0b001 // 0.5 kOhm resistor
+#define IS31FL3742A_PDR_1K_OHM 0b010 // 1 kOhm resistor
+#define IS31FL3742A_PDR_2K_OHM 0b011 // 2 kOhm resistor
+#define IS31FL3742A_PDR_4K_OHM 0b100 // 4 kOhm resistor
+#define IS31FL3742A_PDR_8K_OHM 0b101 // 8 kOhm resistor
+#define IS31FL3742A_PDR_16K_OHM 0b110 // 16 kOhm resistor
+#define IS31FL3742A_PDR_32K_OHM 0b111 // 32 kOhm resistor
+
+#define IS31FL3742A_PUR_0_OHM 0b000 // No pull-up resistor
+#define IS31FL3742A_PUR_0K5_OHM 0b001 // 0.5 kOhm resistor
+#define IS31FL3742A_PUR_1K_OHM 0b010 // 1 kOhm resistor
+#define IS31FL3742A_PUR_2K_OHM 0b011 // 2 kOhm resistor
+#define IS31FL3742A_PUR_4K_OHM 0b100 // 4 kOhm resistor
+#define IS31FL3742A_PUR_8K_OHM 0b101 // 8 kOhm resistor
+#define IS31FL3742A_PUR_16K_OHM 0b110 // 16 kOhm resistor
+#define IS31FL3742A_PUR_32K_OHM 0b111 // 32 kOhm resistor
+
+#define IS31FL3742A_PWM_FREQUENCY_29K_HZ 0b0000
+#define IS31FL3742A_PWM_FREQUENCY_3K6_HZ 0b0011
+#define IS31FL3742A_PWM_FREQUENCY_1K8_HZ 0b0111
+#define IS31FL3742A_PWM_FREQUENCY_900_HZ 0b1011
+
+#define SW1_CS1 0x00
+#define SW1_CS2 0x01
+#define SW1_CS3 0x02
+#define SW1_CS4 0x03
+#define SW1_CS5 0x04
+#define SW1_CS6 0x05
+#define SW1_CS7 0x06
+#define SW1_CS8 0x07
+#define SW1_CS9 0x08
+#define SW1_CS10 0x09
+#define SW1_CS11 0x0A
+#define SW1_CS12 0x0B
+#define SW1_CS13 0x0C
+#define SW1_CS14 0x0D
+#define SW1_CS15 0x0E
+#define SW1_CS16 0x0F
+#define SW1_CS17 0x10
+#define SW1_CS18 0x11
+#define SW1_CS19 0x12
+#define SW1_CS20 0x13
+#define SW1_CS21 0x14
+#define SW1_CS22 0x15
+#define SW1_CS23 0x16
+#define SW1_CS24 0x17
+#define SW1_CS25 0x18
+#define SW1_CS26 0x19
+#define SW1_CS27 0x1A
+#define SW1_CS28 0x1B
+#define SW1_CS29 0x1C
+#define SW1_CS30 0x1D
+
+#define SW2_CS1 0x1E
+#define SW2_CS2 0x1F
+#define SW2_CS3 0x20
+#define SW2_CS4 0x21
+#define SW2_CS5 0x22
+#define SW2_CS6 0x23
+#define SW2_CS7 0x24
+#define SW2_CS8 0x25
+#define SW2_CS9 0x26
+#define SW2_CS10 0x27
+#define SW2_CS11 0x28
+#define SW2_CS12 0x29
+#define SW2_CS13 0x2A
+#define SW2_CS14 0x2B
+#define SW2_CS15 0x2C
+#define SW2_CS16 0x2D
+#define SW2_CS17 0x2E
+#define SW2_CS18 0x2F
+#define SW2_CS19 0x30
+#define SW2_CS20 0x31
+#define SW2_CS21 0x32
+#define SW2_CS22 0x33
+#define SW2_CS23 0x34
+#define SW2_CS24 0x35
+#define SW2_CS25 0x36
+#define SW2_CS26 0x37
+#define SW2_CS27 0x38
+#define SW2_CS28 0x39
+#define SW2_CS29 0x3A
+#define SW2_CS30 0x3B
+
+#define SW3_CS1 0x3C
+#define SW3_CS2 0x3D
+#define SW3_CS3 0x3E
+#define SW3_CS4 0x3F
+#define SW3_CS5 0x40
+#define SW3_CS6 0x41
+#define SW3_CS7 0x42
+#define SW3_CS8 0x43
+#define SW3_CS9 0x44
+#define SW3_CS10 0x45
+#define SW3_CS11 0x46
+#define SW3_CS12 0x47
+#define SW3_CS13 0x48
+#define SW3_CS14 0x49
+#define SW3_CS15 0x4A
+#define SW3_CS16 0x4B
+#define SW3_CS17 0x4C
+#define SW3_CS18 0x4D
+#define SW3_CS19 0x4E
+#define SW3_CS20 0x4F
+#define SW3_CS21 0x50
+#define SW3_CS22 0x51
+#define SW3_CS23 0x52
+#define SW3_CS24 0x53
+#define SW3_CS25 0x54
+#define SW3_CS26 0x55
+#define SW3_CS27 0x56
+#define SW3_CS28 0x57
+#define SW3_CS29 0x58
+#define SW3_CS30 0x59
+
+#define SW4_CS1 0x5A
+#define SW4_CS2 0x5B
+#define SW4_CS3 0x5C
+#define SW4_CS4 0x5D
+#define SW4_CS5 0x5E
+#define SW4_CS6 0x5F
+#define SW4_CS7 0x60
+#define SW4_CS8 0x61
+#define SW4_CS9 0x62
+#define SW4_CS10 0x63
+#define SW4_CS11 0x64
+#define SW4_CS12 0x65
+#define SW4_CS13 0x66
+#define SW4_CS14 0x67
+#define SW4_CS15 0x68
+#define SW4_CS16 0x69
+#define SW4_CS17 0x6A
+#define SW4_CS18 0x6B
+#define SW4_CS19 0x6C
+#define SW4_CS20 0x6D
+#define SW4_CS21 0x6E
+#define SW4_CS22 0x6F
+#define SW4_CS23 0x70
+#define SW4_CS24 0x71
+#define SW4_CS25 0x72
+#define SW4_CS26 0x73
+#define SW4_CS27 0x74
+#define SW4_CS28 0x75
+#define SW4_CS29 0x76
+#define SW4_CS30 0x77
+
+#define SW5_CS1 0x78
+#define SW5_CS2 0x79
+#define SW5_CS3 0x7A
+#define SW5_CS4 0x7B
+#define SW5_CS5 0x7C
+#define SW5_CS6 0x7D
+#define SW5_CS7 0x7E
+#define SW5_CS8 0x7F
+#define SW5_CS9 0x80
+#define SW5_CS10 0x81
+#define SW5_CS11 0x82
+#define SW5_CS12 0x83
+#define SW5_CS13 0x84
+#define SW5_CS14 0x85
+#define SW5_CS15 0x86
+#define SW5_CS16 0x87
+#define SW5_CS17 0x88
+#define SW5_CS18 0x89
+#define SW5_CS19 0x8A
+#define SW5_CS20 0x8B
+#define SW5_CS21 0x8C
+#define SW5_CS22 0x8D
+#define SW5_CS23 0x8E
+#define SW5_CS24 0x8F
+#define SW5_CS25 0x90
+#define SW5_CS26 0x91
+#define SW5_CS27 0x92
+#define SW5_CS28 0x93
+#define SW5_CS29 0x94
+#define SW5_CS30 0x95
+
+#define SW6_CS1 0x96
+#define SW6_CS2 0x97
+#define SW6_CS3 0x98
+#define SW6_CS4 0x99
+#define SW6_CS5 0x9A
+#define SW6_CS6 0x9B
+#define SW6_CS7 0x9C
+#define SW6_CS8 0x9D
+#define SW6_CS9 0x9E
+#define SW6_CS10 0x9F
+#define SW6_CS11 0xA0
+#define SW6_CS12 0xA1
+#define SW6_CS13 0xA2
+#define SW6_CS14 0xA3
+#define SW6_CS15 0xA4
+#define SW6_CS16 0xA5
+#define SW6_CS17 0xA6
+#define SW6_CS18 0xA7
+#define SW6_CS19 0xA8
+#define SW6_CS20 0xA9
+#define SW6_CS21 0xAA
+#define SW6_CS22 0xAB
+#define SW6_CS23 0xAC
+#define SW6_CS24 0xAD
+#define SW6_CS25 0xAE
+#define SW6_CS26 0xAF
+#define SW6_CS27 0xB0
+#define SW6_CS28 0xB1
+#define SW6_CS29 0xB2
+#define SW6_CS30 0xB3
diff --git a/drivers/led/issi/is31fl3742a.c b/drivers/led/issi/is31fl3742a.c
new file mode 100644
index 0000000000..b8e9a58759
--- /dev/null
+++ b/drivers/led/issi/is31fl3742a.c
@@ -0,0 +1,225 @@
+/* Copyright 2017 Jason Williams
+ * Copyright 2018 Jack Humbert
+ * Copyright 2018 Yiancar
+ * Copyright 2020 MelGeek
+ * Copyright 2021 MasterSpoon
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "is31fl3742a.h"
+#include "i2c_master.h"
+#include "gpio.h"
+#include "wait.h"
+
+#define IS31FL3742A_PWM_REGISTER_COUNT 180
+#define IS31FL3742A_SCALING_REGISTER_COUNT 180
+
+#ifndef IS31FL3742A_I2C_TIMEOUT
+# define IS31FL3742A_I2C_TIMEOUT 100
+#endif
+
+#ifndef IS31FL3742A_I2C_PERSISTENCE
+# define IS31FL3742A_I2C_PERSISTENCE 0
+#endif
+
+#ifndef IS31FL3742A_CONFIGURATION
+# define IS31FL3742A_CONFIGURATION 0x31
+#endif
+
+#ifndef IS31FL3742A_PWM_FREQUENCY
+# define IS31FL3742A_PWM_FREQUENCY IS31FL3742A_PWM_FREQUENCY_29K_HZ
+#endif
+
+#ifndef IS31FL3742A_SW_PULLDOWN
+# define IS31FL3742A_SW_PULLDOWN IS31FL3742A_PDR_8K_OHM
+#endif
+
+#ifndef IS31FL3742A_CS_PULLUP
+# define IS31FL3742A_CS_PULLUP IS31FL3742A_PUR_8K_OHM
+#endif
+
+#ifndef IS31FL3742A_GLOBAL_CURRENT
+# define IS31FL3742A_GLOBAL_CURRENT 0xFF
+#endif
+
+const uint8_t i2c_addresses[IS31FL3742A_DRIVER_COUNT] = {
+ IS31FL3742A_I2C_ADDRESS_1,
+#ifdef IS31FL3742A_I2C_ADDRESS_2
+ IS31FL3742A_I2C_ADDRESS_2,
+# ifdef IS31FL3742A_I2C_ADDRESS_3
+ IS31FL3742A_I2C_ADDRESS_3,
+# ifdef IS31FL3742A_I2C_ADDRESS_4
+ IS31FL3742A_I2C_ADDRESS_4,
+# endif
+# endif
+#endif
+};
+
+typedef struct is31fl3742a_driver_t {
+ uint8_t pwm_buffer[IS31FL3742A_PWM_REGISTER_COUNT];
+ bool pwm_buffer_dirty;
+ uint8_t scaling_buffer[IS31FL3742A_SCALING_REGISTER_COUNT];
+ bool scaling_buffer_dirty;
+} PACKED is31fl3742a_driver_t;
+
+is31fl3742a_driver_t driver_buffers[IS31FL3742A_DRIVER_COUNT] = {{
+ .pwm_buffer = {0},
+ .pwm_buffer_dirty = false,
+ .scaling_buffer = {0},
+ .scaling_buffer_dirty = false,
+}};
+
+void is31fl3742a_write_register(uint8_t index, uint8_t reg, uint8_t data) {
+#if IS31FL3742A_I2C_PERSISTENCE > 0
+ for (uint8_t i = 0; i < IS31FL3742A_I2C_PERSISTENCE; i++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3742A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3742A_I2C_TIMEOUT);
+#endif
+}
+
+void is31fl3742a_select_page(uint8_t index, uint8_t page) {
+ is31fl3742a_write_register(index, IS31FL3742A_REG_COMMAND_WRITE_LOCK, IS31FL3742A_COMMAND_WRITE_LOCK_MAGIC);
+ is31fl3742a_write_register(index, IS31FL3742A_REG_COMMAND, page);
+}
+
+void is31fl3742a_write_pwm_buffer(uint8_t index) {
+ // Assumes page 0 is already selected.
+ // Transmit PWM registers in 6 transfers of 30 bytes.
+
+ // Iterate over the pwm_buffer contents at 30 byte intervals.
+ for (uint8_t i = 0; i < IS31FL3742A_PWM_REGISTER_COUNT; i += 30) {
+#if IS31FL3742A_I2C_PERSISTENCE > 0
+ for (uint8_t j = 0; j < IS31FL3742A_I2C_PERSISTENCE; j++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 30, IS31FL3742A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 30, IS31FL3742A_I2C_TIMEOUT);
+#endif
+ }
+}
+
+void is31fl3742a_init_drivers(void) {
+ i2c_init();
+
+#if defined(IS31FL3742A_SDB_PIN)
+ gpio_set_pin_output(IS31FL3742A_SDB_PIN);
+ gpio_write_pin_high(IS31FL3742A_SDB_PIN);
+#endif
+
+ for (uint8_t i = 0; i < IS31FL3742A_DRIVER_COUNT; i++) {
+ is31fl3742a_init(i);
+ }
+
+ for (int i = 0; i < IS31FL3742A_LED_COUNT; i++) {
+ is31fl3742a_set_scaling_register(i, 0xFF, 0xFF, 0xFF);
+ }
+
+ for (uint8_t i = 0; i < IS31FL3742A_DRIVER_COUNT; i++) {
+ is31fl3742a_update_scaling_registers(i);
+ }
+}
+
+void is31fl3742a_init(uint8_t index) {
+ // In order to avoid the LEDs being driven with garbage data
+ // in the LED driver's PWM registers, shutdown is enabled last.
+ // Set up the mode and other settings, clear the PWM registers,
+ // then disable software shutdown.
+
+ is31fl3742a_select_page(index, IS31FL3742A_COMMAND_SCALING);
+
+ // Turn off all LEDs.
+ for (uint8_t i = 0; i < IS31FL3742A_SCALING_REGISTER_COUNT; i++) {
+ is31fl3742a_write_register(index, i, 0x00);
+ }
+
+ is31fl3742a_select_page(index, IS31FL3742A_COMMAND_PWM);
+
+ for (uint8_t i = 0; i < IS31FL3742A_PWM_REGISTER_COUNT; i++) {
+ is31fl3742a_write_register(index, i, 0x00);
+ }
+
+ is31fl3742a_select_page(index, IS31FL3742A_COMMAND_FUNCTION);
+
+ is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_PULLDOWNUP, (IS31FL3742A_SW_PULLDOWN << 4) | IS31FL3742A_CS_PULLUP);
+ is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3742A_GLOBAL_CURRENT);
+ is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_PWM_FREQUENCY, (IS31FL3742A_PWM_FREQUENCY & 0b0111));
+ is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_CONFIGURATION, IS31FL3742A_CONFIGURATION);
+
+ // Wait 10ms to ensure the device has woken up.
+ wait_ms(10);
+}
+
+void is31fl3742a_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
+ is31fl3742a_led_t led;
+
+ if (index >= 0 && index < IS31FL3742A_LED_COUNT) {
+ memcpy_P(&led, (&g_is31fl3742a_leds[index]), sizeof(led));
+
+ if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) {
+ return;
+ }
+
+ driver_buffers[led.driver].pwm_buffer[led.r] = red;
+ driver_buffers[led.driver].pwm_buffer[led.g] = green;
+ driver_buffers[led.driver].pwm_buffer[led.b] = blue;
+ driver_buffers[led.driver].pwm_buffer_dirty = true;
+ }
+}
+
+void is31fl3742a_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
+ for (int i = 0; i < IS31FL3742A_LED_COUNT; i++) {
+ is31fl3742a_set_color(i, red, green, blue);
+ }
+}
+
+void is31fl3742a_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue) {
+ is31fl3742a_led_t led;
+ memcpy_P(&led, (&g_is31fl3742a_leds[index]), sizeof(led));
+
+ driver_buffers[led.driver].scaling_buffer[led.r] = red;
+ driver_buffers[led.driver].scaling_buffer[led.g] = green;
+ driver_buffers[led.driver].scaling_buffer[led.b] = blue;
+ driver_buffers[led.driver].scaling_buffer_dirty = true;
+}
+
+void is31fl3742a_update_pwm_buffers(uint8_t index) {
+ if (driver_buffers[index].pwm_buffer_dirty) {
+ is31fl3742a_select_page(index, IS31FL3742A_COMMAND_PWM);
+
+ is31fl3742a_write_pwm_buffer(index);
+
+ driver_buffers[index].pwm_buffer_dirty = false;
+ }
+}
+
+void is31fl3742a_update_scaling_registers(uint8_t index) {
+ if (driver_buffers[index].scaling_buffer_dirty) {
+ is31fl3742a_select_page(index, IS31FL3742A_COMMAND_SCALING);
+
+ for (uint8_t i = 0; i < IS31FL3742A_SCALING_REGISTER_COUNT; i++) {
+ is31fl3742a_write_register(index, i, driver_buffers[index].scaling_buffer[i]);
+ }
+
+ driver_buffers[index].scaling_buffer_dirty = false;
+ }
+}
+
+void is31fl3742a_flush(void) {
+ for (uint8_t i = 0; i < IS31FL3742A_DRIVER_COUNT; i++) {
+ is31fl3742a_update_pwm_buffers(i);
+ }
+}
diff --git a/drivers/led/issi/is31fl3742a.h b/drivers/led/issi/is31fl3742a.h
new file mode 100644
index 0000000000..10ecf50026
--- /dev/null
+++ b/drivers/led/issi/is31fl3742a.h
@@ -0,0 +1,298 @@
+/* Copyright 2017 Jason Williams
+ * Copyright 2018 Jack Humbert
+ * Copyright 2018 Yiancar
+ * Copyright 2020 MelGeek
+ * Copyright 2021 MasterSpoon
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "progmem.h"
+#include "util.h"
+
+#define IS31FL3742A_REG_INTERRUPT_MASK 0xF0
+#define IS31FL3742A_REG_INTERRUPT_STATUS 0xF1
+#define IS31FL3742A_REG_ID 0xFC
+
+#define IS31FL3742A_REG_COMMAND 0xFD
+
+#define IS31FL3742A_COMMAND_PWM 0x00
+#define IS31FL3742A_COMMAND_SCALING 0x02
+#define IS31FL3742A_COMMAND_FUNCTION 0x04
+
+#define IS31FL3742A_FUNCTION_REG_CONFIGURATION 0x00
+#define IS31FL3742A_FUNCTION_REG_GLOBAL_CURRENT 0x01
+#define IS31FL3742A_FUNCTION_REG_PULLDOWNUP 0x02
+#define IS31FL3742A_FUNCTION_REG_PWM_FREQUENCY 0x36
+#define IS31FL3742A_FUNCTION_REG_RESET 0x3F
+#define IS31FL3742A_FUNCTION_REG_SPREAD_SPECTRUM 0x41
+
+#define IS31FL3742A_REG_COMMAND_WRITE_LOCK 0xFE
+#define IS31FL3742A_COMMAND_WRITE_LOCK_MAGIC 0xC5
+
+#define IS31FL3742A_I2C_ADDRESS_GND 0x30
+#define IS31FL3742A_I2C_ADDRESS_SCL 0x31
+#define IS31FL3742A_I2C_ADDRESS_SDA 0x32
+#define IS31FL3742A_I2C_ADDRESS_VCC 0x33
+
+#if defined(RGB_MATRIX_IS31FL3742A)
+# define IS31FL3742A_LED_COUNT RGB_MATRIX_LED_COUNT
+#endif
+
+#if defined(IS31FL3742A_I2C_ADDRESS_4)
+# define IS31FL3742A_DRIVER_COUNT 4
+#elif defined(IS31FL3742A_I2C_ADDRESS_3)
+# define IS31FL3742A_DRIVER_COUNT 3
+#elif defined(IS31FL3742A_I2C_ADDRESS_2)
+# define IS31FL3742A_DRIVER_COUNT 2
+#elif defined(IS31FL3742A_I2C_ADDRESS_1)
+# define IS31FL3742A_DRIVER_COUNT 1
+#endif
+
+typedef struct is31fl3742a_led_t {
+ uint8_t driver : 2;
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+} PACKED is31fl3742a_led_t;
+
+extern const is31fl3742a_led_t PROGMEM g_is31fl3742a_leds[IS31FL3742A_LED_COUNT];
+
+void is31fl3742a_init_drivers(void);
+void is31fl3742a_init(uint8_t index);
+void is31fl3742a_write_register(uint8_t index, uint8_t reg, uint8_t data);
+void is31fl3742a_select_page(uint8_t index, uint8_t page);
+
+void is31fl3742a_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
+void is31fl3742a_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
+
+void is31fl3742a_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue);
+
+void is31fl3742a_update_pwm_buffers(uint8_t index);
+void is31fl3742a_update_scaling_registers(uint8_t index);
+
+void is31fl3742a_flush(void);
+
+#define IS31FL3742A_PDR_0_OHM 0b000 // No pull-down resistor
+#define IS31FL3742A_PDR_0K5_OHM 0b001 // 0.5 kOhm resistor
+#define IS31FL3742A_PDR_1K_OHM 0b010 // 1 kOhm resistor
+#define IS31FL3742A_PDR_2K_OHM 0b011 // 2 kOhm resistor
+#define IS31FL3742A_PDR_4K_OHM 0b100 // 4 kOhm resistor
+#define IS31FL3742A_PDR_8K_OHM 0b101 // 8 kOhm resistor
+#define IS31FL3742A_PDR_16K_OHM 0b110 // 16 kOhm resistor
+#define IS31FL3742A_PDR_32K_OHM 0b111 // 32 kOhm resistor
+
+#define IS31FL3742A_PUR_0_OHM 0b000 // No pull-up resistor
+#define IS31FL3742A_PUR_0K5_OHM 0b001 // 0.5 kOhm resistor
+#define IS31FL3742A_PUR_1K_OHM 0b010 // 1 kOhm resistor
+#define IS31FL3742A_PUR_2K_OHM 0b011 // 2 kOhm resistor
+#define IS31FL3742A_PUR_4K_OHM 0b100 // 4 kOhm resistor
+#define IS31FL3742A_PUR_8K_OHM 0b101 // 8 kOhm resistor
+#define IS31FL3742A_PUR_16K_OHM 0b110 // 16 kOhm resistor
+#define IS31FL3742A_PUR_32K_OHM 0b111 // 32 kOhm resistor
+
+#define IS31FL3742A_PWM_FREQUENCY_29K_HZ 0b0000
+#define IS31FL3742A_PWM_FREQUENCY_3K6_HZ 0b0011
+#define IS31FL3742A_PWM_FREQUENCY_1K8_HZ 0b0111
+#define IS31FL3742A_PWM_FREQUENCY_900_HZ 0b1011
+
+#define SW1_CS1 0x00
+#define SW1_CS2 0x01
+#define SW1_CS3 0x02
+#define SW1_CS4 0x03
+#define SW1_CS5 0x04
+#define SW1_CS6 0x05
+#define SW1_CS7 0x06
+#define SW1_CS8 0x07
+#define SW1_CS9 0x08
+#define SW1_CS10 0x09
+#define SW1_CS11 0x0A
+#define SW1_CS12 0x0B
+#define SW1_CS13 0x0C
+#define SW1_CS14 0x0D
+#define SW1_CS15 0x0E
+#define SW1_CS16 0x0F
+#define SW1_CS17 0x10
+#define SW1_CS18 0x11
+#define SW1_CS19 0x12
+#define SW1_CS20 0x13
+#define SW1_CS21 0x14
+#define SW1_CS22 0x15
+#define SW1_CS23 0x16
+#define SW1_CS24 0x17
+#define SW1_CS25 0x18
+#define SW1_CS26 0x19
+#define SW1_CS27 0x1A
+#define SW1_CS28 0x1B
+#define SW1_CS29 0x1C
+#define SW1_CS30 0x1D
+
+#define SW2_CS1 0x1E
+#define SW2_CS2 0x1F
+#define SW2_CS3 0x20
+#define SW2_CS4 0x21
+#define SW2_CS5 0x22
+#define SW2_CS6 0x23
+#define SW2_CS7 0x24
+#define SW2_CS8 0x25
+#define SW2_CS9 0x26
+#define SW2_CS10 0x27
+#define SW2_CS11 0x28
+#define SW2_CS12 0x29
+#define SW2_CS13 0x2A
+#define SW2_CS14 0x2B
+#define SW2_CS15 0x2C
+#define SW2_CS16 0x2D
+#define SW2_CS17 0x2E
+#define SW2_CS18 0x2F
+#define SW2_CS19 0x30
+#define SW2_CS20 0x31
+#define SW2_CS21 0x32
+#define SW2_CS22 0x33
+#define SW2_CS23 0x34
+#define SW2_CS24 0x35
+#define SW2_CS25 0x36
+#define SW2_CS26 0x37
+#define SW2_CS27 0x38
+#define SW2_CS28 0x39
+#define SW2_CS29 0x3A
+#define SW2_CS30 0x3B
+
+#define SW3_CS1 0x3C
+#define SW3_CS2 0x3D
+#define SW3_CS3 0x3E
+#define SW3_CS4 0x3F
+#define SW3_CS5 0x40
+#define SW3_CS6 0x41
+#define SW3_CS7 0x42
+#define SW3_CS8 0x43
+#define SW3_CS9 0x44
+#define SW3_CS10 0x45
+#define SW3_CS11 0x46
+#define SW3_CS12 0x47
+#define SW3_CS13 0x48
+#define SW3_CS14 0x49
+#define SW3_CS15 0x4A
+#define SW3_CS16 0x4B
+#define SW3_CS17 0x4C
+#define SW3_CS18 0x4D
+#define SW3_CS19 0x4E
+#define SW3_CS20 0x4F
+#define SW3_CS21 0x50
+#define SW3_CS22 0x51
+#define SW3_CS23 0x52
+#define SW3_CS24 0x53
+#define SW3_CS25 0x54
+#define SW3_CS26 0x55
+#define SW3_CS27 0x56
+#define SW3_CS28 0x57
+#define SW3_CS29 0x58
+#define SW3_CS30 0x59
+
+#define SW4_CS1 0x5A
+#define SW4_CS2 0x5B
+#define SW4_CS3 0x5C
+#define SW4_CS4 0x5D
+#define SW4_CS5 0x5E
+#define SW4_CS6 0x5F
+#define SW4_CS7 0x60
+#define SW4_CS8 0x61
+#define SW4_CS9 0x62
+#define SW4_CS10 0x63
+#define SW4_CS11 0x64
+#define SW4_CS12 0x65
+#define SW4_CS13 0x66
+#define SW4_CS14 0x67
+#define SW4_CS15 0x68
+#define SW4_CS16 0x69
+#define SW4_CS17 0x6A
+#define SW4_CS18 0x6B
+#define SW4_CS19 0x6C
+#define SW4_CS20 0x6D
+#define SW4_CS21 0x6E
+#define SW4_CS22 0x6F
+#define SW4_CS23 0x70
+#define SW4_CS24 0x71
+#define SW4_CS25 0x72
+#define SW4_CS26 0x73
+#define SW4_CS27 0x74
+#define SW4_CS28 0x75
+#define SW4_CS29 0x76
+#define SW4_CS30 0x77
+
+#define SW5_CS1 0x78
+#define SW5_CS2 0x79
+#define SW5_CS3 0x7A
+#define SW5_CS4 0x7B
+#define SW5_CS5 0x7C
+#define SW5_CS6 0x7D
+#define SW5_CS7 0x7E
+#define SW5_CS8 0x7F
+#define SW5_CS9 0x80
+#define SW5_CS10 0x81
+#define SW5_CS11 0x82
+#define SW5_CS12 0x83
+#define SW5_CS13 0x84
+#define SW5_CS14 0x85
+#define SW5_CS15 0x86
+#define SW5_CS16 0x87
+#define SW5_CS17 0x88
+#define SW5_CS18 0x89
+#define SW5_CS19 0x8A
+#define SW5_CS20 0x8B
+#define SW5_CS21 0x8C
+#define SW5_CS22 0x8D
+#define SW5_CS23 0x8E
+#define SW5_CS24 0x8F
+#define SW5_CS25 0x90
+#define SW5_CS26 0x91
+#define SW5_CS27 0x92
+#define SW5_CS28 0x93
+#define SW5_CS29 0x94
+#define SW5_CS30 0x95
+
+#define SW6_CS1 0x96
+#define SW6_CS2 0x97
+#define SW6_CS3 0x98
+#define SW6_CS4 0x99
+#define SW6_CS5 0x9A
+#define SW6_CS6 0x9B
+#define SW6_CS7 0x9C
+#define SW6_CS8 0x9D
+#define SW6_CS9 0x9E
+#define SW6_CS10 0x9F
+#define SW6_CS11 0xA0
+#define SW6_CS12 0xA1
+#define SW6_CS13 0xA2
+#define SW6_CS14 0xA3
+#define SW6_CS15 0xA4
+#define SW6_CS16 0xA5
+#define SW6_CS17 0xA6
+#define SW6_CS18 0xA7
+#define SW6_CS19 0xA8
+#define SW6_CS20 0xA9
+#define SW6_CS21 0xAA
+#define SW6_CS22 0xAB
+#define SW6_CS23 0xAC
+#define SW6_CS24 0xAD
+#define SW6_CS25 0xAE
+#define SW6_CS26 0xAF
+#define SW6_CS27 0xB0
+#define SW6_CS28 0xB1
+#define SW6_CS29 0xB2
+#define SW6_CS30 0xB3
diff --git a/drivers/led/issi/is31fl3743.h b/drivers/led/issi/is31fl3743.h
deleted file mode 100644
index 706b271254..0000000000
--- a/drivers/led/issi/is31fl3743.h
+++ /dev/null
@@ -1,327 +0,0 @@
-/* Copyright 2017 Jason Williams
- * Copyright 2018 Jack Humbert
- * Copyright 2018 Yiancar
- * Copyright 2020 MelGeek
- * Copyright 2021 MasterSpoon
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-// This is a 7-bit address, that gets left-shifted and bit 0
-// set to 0 for write, 1 for read (as per I2C protocol)
-// The address will vary depending on your wiring:
-// 00 <-> GND
-// 01 <-> SCL
-// 10 <-> SDA
-// 11 <-> VCC
-// ADDR1 represents A1:A0 of the 7-bit address.
-// ADDR2 represents A3:A2 of the 7-bit address.
-// The result is: 0b010(ADDR2)(ADDR1)
-#ifndef DRIVER_ADDR_1
-# define DRIVER_ADDR_1 0b0100000
-#endif
-
-// Set defaults for Spread Spectrum Register
-#ifndef ISSI_SSR_1
-# ifndef DRIVER_ADDR_2
-# define ISSI_SSR_1 0x00
-# else
-# define ISSI_SSR_1 0xC0
-# endif
-#endif
-#ifndef ISSI_SSR_2
-# define ISSI_SSR_2 0x80
-#endif
-#ifndef ISSI_SSR_3
-# define ISSI_SSR_3 0x80
-#endif
-#ifndef ISSI_SSR_4
-# define ISSI_SSR_4 0x80
-#endif
-
-// Command Registers
-#define ISSI_COMMANDREGISTER_WRITELOCK 0xFE
-#define ISSI_COMMANDREGISTER 0xFD
-#define ISSI_IDREGISTER 0xFC
-#define ISSI_REGISTER_UNLOCK 0xC5
-
-// Response Registers
-#define ISSI_PAGE_PWM 0x00
-#define ISSI_PAGE_SCALING 0x01
-#define ISSI_PAGE_FUNCTION 0x02
-
-// Registers under Function Register
-#define ISSI_REG_CONFIGURATION 0x00
-#define ISSI_REG_GLOBALCURRENT 0x01
-#define ISSI_REG_PULLDOWNUP 0x02
-#define ISSI_REG_TEMP 0x24
-#define ISSI_REG_SSR 0x25
-#define ISSI_REG_RESET 0x2F
-
-// Set defaults for Function Registers
-#ifndef ISSI_CONFIGURATION
-# define ISSI_CONFIGURATION 0x01
-#endif
-#ifndef ISSI_GLOBALCURRENT
-# define ISSI_GLOBALCURRENT 0xFF
-#endif
-#ifndef ISSI_PULLDOWNUP
-# define ISSI_PULLDOWNUP 0x33
-#endif
-#ifndef ISSI_TEMP
-# define ISSI_TEMP 0x00
-#endif
-
-// Set defaults for Scaling registers
-#ifndef ISSI_SCAL_RED
-# define ISSI_SCAL_RED 0xFF
-#endif
-#ifndef ISSI_SCAL_BLUE
-# define ISSI_SCAL_BLUE 0xFF
-#endif
-#ifndef ISSI_SCAL_GREEN
-# define ISSI_SCAL_GREEN 0xFF
-#endif
-#define ISSI_SCAL_RED_OFF 0x00
-#define ISSI_SCAL_GREEN_OFF 0x00
-#define ISSI_SCAL_BLUE_OFF 0x00
-
-#ifndef ISSI_SCAL_LED
-# define ISSI_SCAL_LED 0xFF
-#endif
-#define ISSI_SCAL_LED_OFF 0x00
-
-// Set buffer sizes
-#define ISSI_MAX_LEDS 198
-#define ISSI_SCALING_SIZE 198
-#define ISSI_PWM_TRF_SIZE 18
-#define ISSI_SCALING_TRF_SIZE 18
-
-// Location of 1st bit for PWM and Scaling registers
-#define ISSI_PWM_REG_1ST 0x01
-#define ISSI_SCL_REG_1ST 0x01
-
-// Map CS SW locations to order in PWM / Scaling buffers
-// This matches the ORDER in the Datasheet Register not the POSITION
-// It will always count from 0x00 to (ISSI_MAX_LEDS - 1)
-#define CS1_SW1 0x00
-#define CS2_SW1 0x01
-#define CS3_SW1 0x02
-#define CS4_SW1 0x03
-#define CS5_SW1 0x04
-#define CS6_SW1 0x05
-#define CS7_SW1 0x06
-#define CS8_SW1 0x07
-#define CS9_SW1 0x08
-#define CS10_SW1 0x09
-#define CS11_SW1 0x0A
-#define CS12_SW1 0x0B
-#define CS13_SW1 0x0C
-#define CS14_SW1 0x0D
-#define CS15_SW1 0x0E
-#define CS16_SW1 0x0F
-#define CS17_SW1 0x10
-#define CS18_SW1 0x11
-
-#define CS1_SW2 0x12
-#define CS2_SW2 0x13
-#define CS3_SW2 0x14
-#define CS4_SW2 0x15
-#define CS5_SW2 0x16
-#define CS6_SW2 0x17
-#define CS7_SW2 0x18
-#define CS8_SW2 0x19
-#define CS9_SW2 0x1A
-#define CS10_SW2 0x1B
-#define CS11_SW2 0x1C
-#define CS12_SW2 0x1D
-#define CS13_SW2 0x1E
-#define CS14_SW2 0x1F
-#define CS15_SW2 0x20
-#define CS16_SW2 0x21
-#define CS17_SW2 0x22
-#define CS18_SW2 0x23
-
-#define CS1_SW3 0x24
-#define CS2_SW3 0x25
-#define CS3_SW3 0x26
-#define CS4_SW3 0x27
-#define CS5_SW3 0x28
-#define CS6_SW3 0x29
-#define CS7_SW3 0x2A
-#define CS8_SW3 0x2B
-#define CS9_SW3 0x2C
-#define CS10_SW3 0x2D
-#define CS11_SW3 0x2E
-#define CS12_SW3 0x2F
-#define CS13_SW3 0x30
-#define CS14_SW3 0x31
-#define CS15_SW3 0x32
-#define CS16_SW3 0x33
-#define CS17_SW3 0x34
-#define CS18_SW3 0x35
-
-#define CS1_SW4 0x36
-#define CS2_SW4 0x37
-#define CS3_SW4 0x38
-#define CS4_SW4 0x39
-#define CS5_SW4 0x3A
-#define CS6_SW4 0x3B
-#define CS7_SW4 0x3C
-#define CS8_SW4 0x3D
-#define CS9_SW4 0x3E
-#define CS10_SW4 0x3F
-#define CS11_SW4 0x40
-#define CS12_SW4 0x41
-#define CS13_SW4 0x42
-#define CS14_SW4 0x43
-#define CS15_SW4 0x44
-#define CS16_SW4 0x45
-#define CS17_SW4 0x46
-#define CS18_SW4 0x47
-
-#define CS1_SW5 0x48
-#define CS2_SW5 0x49
-#define CS3_SW5 0x4A
-#define CS4_SW5 0x4B
-#define CS5_SW5 0x4C
-#define CS6_SW5 0x4D
-#define CS7_SW5 0x4E
-#define CS8_SW5 0x4F
-#define CS9_SW5 0x50
-#define CS10_SW5 0x51
-#define CS11_SW5 0x52
-#define CS12_SW5 0x53
-#define CS13_SW5 0x54
-#define CS14_SW5 0x55
-#define CS15_SW5 0x56
-#define CS16_SW5 0x57
-#define CS17_SW5 0x58
-#define CS18_SW5 0x59
-
-#define CS1_SW6 0x5A
-#define CS2_SW6 0x5B
-#define CS3_SW6 0x5C
-#define CS4_SW6 0x5D
-#define CS5_SW6 0x5E
-#define CS6_SW6 0x5F
-#define CS7_SW6 0x60
-#define CS8_SW6 0x61
-#define CS9_SW6 0x62
-#define CS10_SW6 0x63
-#define CS11_SW6 0x64
-#define CS12_SW6 0x65
-#define CS13_SW6 0x66
-#define CS14_SW6 0x67
-#define CS15_SW6 0x68
-#define CS16_SW6 0x69
-#define CS17_SW6 0x6A
-#define CS18_SW6 0x6B
-
-#define CS1_SW7 0x6C
-#define CS2_SW7 0x6D
-#define CS3_SW7 0x6E
-#define CS4_SW7 0x6F
-#define CS5_SW7 0x70
-#define CS6_SW7 0x71
-#define CS7_SW7 0x72
-#define CS8_SW7 0x73
-#define CS9_SW7 0x74
-#define CS10_SW7 0x75
-#define CS11_SW7 0x76
-#define CS12_SW7 0x77
-#define CS13_SW7 0x78
-#define CS14_SW7 0x79
-#define CS15_SW7 0x7A
-#define CS16_SW7 0x7B
-#define CS17_SW7 0x7C
-#define CS18_SW7 0x7D
-
-#define CS1_SW8 0x7E
-#define CS2_SW8 0x7F
-#define CS3_SW8 0x80
-#define CS4_SW8 0x81
-#define CS5_SW8 0x82
-#define CS6_SW8 0x83
-#define CS7_SW8 0x84
-#define CS8_SW8 0x85
-#define CS9_SW8 0x86
-#define CS10_SW8 0x87
-#define CS11_SW8 0x88
-#define CS12_SW8 0x89
-#define CS13_SW8 0x8A
-#define CS14_SW8 0x8B
-#define CS15_SW8 0x8C
-#define CS16_SW8 0x8D
-#define CS17_SW8 0x8E
-#define CS18_SW8 0x8F
-
-#define CS1_SW9 0x90
-#define CS2_SW9 0x91
-#define CS3_SW9 0x92
-#define CS4_SW9 0x93
-#define CS5_SW9 0x94
-#define CS6_SW9 0x95
-#define CS7_SW9 0x96
-#define CS8_SW9 0x97
-#define CS9_SW9 0x98
-#define CS10_SW9 0x99
-#define CS11_SW9 0x9A
-#define CS12_SW9 0x9B
-#define CS13_SW9 0x9C
-#define CS14_SW9 0x9D
-#define CS15_SW9 0x9E
-#define CS16_SW9 0x9F
-#define CS17_SW9 0xA0
-#define CS18_SW9 0xA1
-
-#define CS1_SW10 0xA2
-#define CS2_SW10 0xA3
-#define CS3_SW10 0xA4
-#define CS4_SW10 0xA5
-#define CS5_SW10 0xA6
-#define CS6_SW10 0xA7
-#define CS7_SW10 0xA8
-#define CS8_SW10 0xA9
-#define CS9_SW10 0xAA
-#define CS10_SW10 0xAB
-#define CS11_SW10 0xAC
-#define CS12_SW10 0xAD
-#define CS13_SW10 0xAE
-#define CS14_SW10 0xAF
-#define CS15_SW10 0xB0
-#define CS16_SW10 0xB1
-#define CS17_SW10 0xB2
-#define CS18_SW10 0xB3
-
-#define CS1_SW11 0xB4
-#define CS2_SW11 0xB5
-#define CS3_SW11 0xB6
-#define CS4_SW11 0xB7
-#define CS5_SW11 0xB8
-#define CS6_SW11 0xB9
-#define CS7_SW11 0xBA
-#define CS8_SW11 0xBB
-#define CS9_SW11 0xBC
-#define CS10_SW11 0xBD
-#define CS11_SW11 0xBE
-#define CS12_SW11 0xBF
-#define CS13_SW11 0xC0
-#define CS14_SW11 0xC1
-#define CS15_SW11 0xC2
-#define CS16_SW11 0xC3
-#define CS17_SW11 0xC4
-#define CS18_SW11 0xC5
diff --git a/drivers/led/issi/is31fl3743a-mono.c b/drivers/led/issi/is31fl3743a-mono.c
new file mode 100644
index 0000000000..6413dbef04
--- /dev/null
+++ b/drivers/led/issi/is31fl3743a-mono.c
@@ -0,0 +1,245 @@
+/* Copyright 2017 Jason Williams
+ * Copyright 2018 Jack Humbert
+ * Copyright 2018 Yiancar
+ * Copyright 2020 MelGeek
+ * Copyright 2021 MasterSpoon
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "is31fl3743a-mono.h"
+#include "i2c_master.h"
+#include "gpio.h"
+#include "wait.h"
+
+#define IS31FL3743A_PWM_REGISTER_COUNT 198
+#define IS31FL3743A_SCALING_REGISTER_COUNT 198
+
+#ifndef IS31FL3743A_I2C_TIMEOUT
+# define IS31FL3743A_I2C_TIMEOUT 100
+#endif
+
+#ifndef IS31FL3743A_I2C_PERSISTENCE
+# define IS31FL3743A_I2C_PERSISTENCE 0
+#endif
+
+#ifndef IS31FL3743A_CONFIGURATION
+# define IS31FL3743A_CONFIGURATION 0x01
+#endif
+
+#ifndef IS31FL3743A_SW_PULLDOWN
+# define IS31FL3743A_SW_PULLDOWN IS31FL3743A_PDR_2K_OHM_SW_OFF
+#endif
+
+#ifndef IS31FL3743A_CS_PULLUP
+# define IS31FL3743A_CS_PULLUP IS31FL3743A_PUR_2K_OHM_CS_OFF
+#endif
+
+#ifndef IS31FL3743A_GLOBAL_CURRENT
+# define IS31FL3743A_GLOBAL_CURRENT 0xFF
+#endif
+
+#ifndef IS31FL3743A_SYNC_1
+# define IS31FL3743A_SYNC_1 IS31FL3743A_SYNC_NONE
+#endif
+#ifndef IS31FL3743A_SYNC_2
+# define IS31FL3743A_SYNC_2 IS31FL3743A_SYNC_NONE
+#endif
+#ifndef IS31FL3743A_SYNC_3
+# define IS31FL3743A_SYNC_3 IS31FL3743A_SYNC_NONE
+#endif
+#ifndef IS31FL3743A_SYNC_4
+# define IS31FL3743A_SYNC_4 IS31FL3743A_SYNC_NONE
+#endif
+
+const uint8_t i2c_addresses[IS31FL3743A_DRIVER_COUNT] = {
+ IS31FL3743A_I2C_ADDRESS_1,
+#ifdef IS31FL3743A_I2C_ADDRESS_2
+ IS31FL3743A_I2C_ADDRESS_2,
+# ifdef IS31FL3743A_I2C_ADDRESS_3
+ IS31FL3743A_I2C_ADDRESS_3,
+# ifdef IS31FL3743A_I2C_ADDRESS_4
+ IS31FL3743A_I2C_ADDRESS_4,
+# endif
+# endif
+#endif
+};
+
+const uint8_t driver_sync[IS31FL3743A_DRIVER_COUNT] = {
+ IS31FL3743A_SYNC_1,
+#ifdef IS31FL3743A_I2C_ADDRESS_2
+ IS31FL3743A_SYNC_2,
+# ifdef IS31FL3743A_I2C_ADDRESS_3
+ IS31FL3743A_SYNC_3,
+# ifdef IS31FL3743A_I2C_ADDRESS_4
+ IS31FL3743A_SYNC_4,
+# endif
+# endif
+#endif
+};
+
+typedef struct is31fl3743a_driver_t {
+ uint8_t pwm_buffer[IS31FL3743A_PWM_REGISTER_COUNT];
+ bool pwm_buffer_dirty;
+ uint8_t scaling_buffer[IS31FL3743A_SCALING_REGISTER_COUNT];
+ bool scaling_buffer_dirty;
+} PACKED is31fl3743a_driver_t;
+
+is31fl3743a_driver_t driver_buffers[IS31FL3743A_DRIVER_COUNT] = {{
+ .pwm_buffer = {0},
+ .pwm_buffer_dirty = false,
+ .scaling_buffer = {0},
+ .scaling_buffer_dirty = false,
+}};
+
+void is31fl3743a_write_register(uint8_t index, uint8_t reg, uint8_t data) {
+#if IS31FL3743A_I2C_PERSISTENCE > 0
+ for (uint8_t i = 0; i < IS31FL3743A_I2C_PERSISTENCE; i++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3743A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3743A_I2C_TIMEOUT);
+#endif
+}
+
+void is31fl3743a_select_page(uint8_t index, uint8_t page) {
+ is31fl3743a_write_register(index, IS31FL3743A_REG_COMMAND_WRITE_LOCK, IS31FL3743A_COMMAND_WRITE_LOCK_MAGIC);
+ is31fl3743a_write_register(index, IS31FL3743A_REG_COMMAND, page);
+}
+
+void is31fl3743a_write_pwm_buffer(uint8_t index) {
+ // Assumes page 0 is already selected.
+ // Transmit PWM registers in 11 transfers of 18 bytes.
+
+ // Iterate over the pwm_buffer contents at 18 byte intervals.
+ for (uint8_t i = 0; i < IS31FL3743A_PWM_REGISTER_COUNT; i += 18) {
+#if IS31FL3743A_I2C_PERSISTENCE > 0
+ for (uint8_t j = 0; j < IS31FL3743A_I2C_PERSISTENCE; j++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3743A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3743A_I2C_TIMEOUT);
+#endif
+ }
+}
+
+void is31fl3743a_init_drivers(void) {
+ i2c_init();
+
+#if defined(IS31FL3743A_SDB_PIN)
+ gpio_set_pin_output(IS31FL3743A_SDB_PIN);
+ gpio_write_pin_high(IS31FL3743A_SDB_PIN);
+#endif
+
+ for (uint8_t i = 0; i < IS31FL3743A_DRIVER_COUNT; i++) {
+ is31fl3743a_init(i);
+ }
+
+ for (int i = 0; i < IS31FL3743A_LED_COUNT; i++) {
+ is31fl3743a_set_scaling_register(i, 0xFF);
+ }
+
+ for (uint8_t i = 0; i < IS31FL3743A_DRIVER_COUNT; i++) {
+ is31fl3743a_update_scaling_registers(i);
+ }
+}
+
+void is31fl3743a_init(uint8_t index) {
+ // In order to avoid the LEDs being driven with garbage data
+ // in the LED driver's PWM registers, shutdown is enabled last.
+ // Set up the mode and other settings, clear the PWM registers,
+ // then disable software shutdown.
+
+ is31fl3743a_select_page(index, IS31FL3743A_COMMAND_SCALING);
+
+ // Turn off all LEDs.
+ for (uint8_t i = 0; i < IS31FL3743A_SCALING_REGISTER_COUNT; i++) {
+ is31fl3743a_write_register(index, i + 1, 0x00);
+ }
+
+ is31fl3743a_select_page(index, IS31FL3743A_COMMAND_PWM);
+
+ for (uint8_t i = 0; i < IS31FL3743A_PWM_REGISTER_COUNT; i++) {
+ is31fl3743a_write_register(index, i + 1, 0x00);
+ }
+
+ is31fl3743a_select_page(index, IS31FL3743A_COMMAND_FUNCTION);
+
+ uint8_t sync = driver_sync[index];
+
+ is31fl3743a_write_register(index, IS31FL3743A_FUNCTION_REG_PULLDOWNUP, (IS31FL3743A_SW_PULLDOWN << 4) | IS31FL3743A_CS_PULLUP);
+ is31fl3743a_write_register(index, IS31FL3743A_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3743A_GLOBAL_CURRENT);
+ is31fl3743a_write_register(index, IS31FL3743A_FUNCTION_REG_SPREAD_SPECTRUM, (sync & 0b11) << 6);
+ is31fl3743a_write_register(index, IS31FL3743A_FUNCTION_REG_CONFIGURATION, IS31FL3743A_CONFIGURATION);
+
+ // Wait 10ms to ensure the device has woken up.
+ wait_ms(10);
+}
+
+void is31fl3743a_set_value(int index, uint8_t value) {
+ is31fl3743a_led_t led;
+
+ if (index >= 0 && index < IS31FL3743A_LED_COUNT) {
+ memcpy_P(&led, (&g_is31fl3743a_leds[index]), sizeof(led));
+
+ if (driver_buffers[led.driver].pwm_buffer[led.v] == value) {
+ return;
+ }
+
+ driver_buffers[led.driver].pwm_buffer[led.v] = value;
+ driver_buffers[led.driver].pwm_buffer_dirty = true;
+ }
+}
+
+void is31fl3743a_set_value_all(uint8_t value) {
+ for (int i = 0; i < IS31FL3743A_LED_COUNT; i++) {
+ is31fl3743a_set_value(i, value);
+ }
+}
+
+void is31fl3743a_set_scaling_register(uint8_t index, uint8_t value) {
+ is31fl3743a_led_t led;
+ memcpy_P(&led, (&g_is31fl3743a_leds[index]), sizeof(led));
+
+ driver_buffers[led.driver].scaling_buffer[led.v] = value;
+ driver_buffers[led.driver].scaling_buffer_dirty = true;
+}
+
+void is31fl3743a_update_pwm_buffers(uint8_t index) {
+ if (driver_buffers[index].pwm_buffer_dirty) {
+ is31fl3743a_select_page(index, IS31FL3743A_COMMAND_PWM);
+
+ is31fl3743a_write_pwm_buffer(index);
+
+ driver_buffers[index].pwm_buffer_dirty = false;
+ }
+}
+
+void is31fl3743a_update_scaling_registers(uint8_t index) {
+ if (driver_buffers[index].scaling_buffer_dirty) {
+ is31fl3743a_select_page(index, IS31FL3743A_COMMAND_SCALING);
+
+ for (uint8_t i = 0; i < IS31FL3743A_SCALING_REGISTER_COUNT; i++) {
+ is31fl3743a_write_register(index, i + 1, driver_buffers[index].scaling_buffer[i]);
+ }
+
+ driver_buffers[index].scaling_buffer_dirty = false;
+ }
+}
+
+void is31fl3743a_flush(void) {
+ for (uint8_t i = 0; i < IS31FL3743A_DRIVER_COUNT; i++) {
+ is31fl3743a_update_pwm_buffers(i);
+ }
+}
diff --git a/drivers/led/issi/is31fl3743a-mono.h b/drivers/led/issi/is31fl3743a-mono.h
new file mode 100644
index 0000000000..4c582d3a7a
--- /dev/null
+++ b/drivers/led/issi/is31fl3743a-mono.h
@@ -0,0 +1,328 @@
+/* Copyright 2017 Jason Williams
+ * Copyright 2018 Jack Humbert
+ * Copyright 2018 Yiancar
+ * Copyright 2020 MelGeek
+ * Copyright 2021 MasterSpoon
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "progmem.h"
+#include "util.h"
+
+#define IS31FL3743A_REG_ID 0xFC
+
+#define IS31FL3743A_REG_COMMAND 0xFD
+
+#define IS31FL3743A_COMMAND_PWM 0x00
+#define IS31FL3743A_COMMAND_SCALING 0x01
+#define IS31FL3743A_COMMAND_FUNCTION 0x02
+
+#define IS31FL3743A_FUNCTION_REG_CONFIGURATION 0x00
+#define IS31FL3743A_FUNCTION_REG_GLOBAL_CURRENT 0x01
+#define IS31FL3743A_FUNCTION_REG_PULLDOWNUP 0x02
+#define IS31FL3743A_FUNCTION_REG_TEMPERATURE 0x24
+#define IS31FL3743A_FUNCTION_REG_SPREAD_SPECTRUM 0x25
+#define IS31FL3743A_FUNCTION_REG_RESET 0x2F
+
+#define IS31FL3743A_REG_COMMAND_WRITE_LOCK 0xFE
+#define IS31FL3743A_COMMAND_WRITE_LOCK_MAGIC 0xC5
+
+#define IS31FL3743A_I2C_ADDRESS_GND_GND 0x20
+#define IS31FL3743A_I2C_ADDRESS_GND_SCL 0x21
+#define IS31FL3743A_I2C_ADDRESS_GND_SDA 0x22
+#define IS31FL3743A_I2C_ADDRESS_GND_VCC 0x23
+#define IS31FL3743A_I2C_ADDRESS_SCL_GND 0x24
+#define IS31FL3743A_I2C_ADDRESS_SCL_SCL 0x25
+#define IS31FL3743A_I2C_ADDRESS_SCL_SDA 0x26
+#define IS31FL3743A_I2C_ADDRESS_SCL_VCC 0x27
+#define IS31FL3743A_I2C_ADDRESS_SDA_GND 0x28
+#define IS31FL3743A_I2C_ADDRESS_SDA_SCL 0x29
+#define IS31FL3743A_I2C_ADDRESS_SDA_SDA 0x2A
+#define IS31FL3743A_I2C_ADDRESS_SDA_VCC 0x2B
+#define IS31FL3743A_I2C_ADDRESS_VCC_GND 0x2C
+#define IS31FL3743A_I2C_ADDRESS_VCC_SCL 0x2D
+#define IS31FL3743A_I2C_ADDRESS_VCC_SDA 0x2E
+#define IS31FL3743A_I2C_ADDRESS_VCC_VCC 0x2F
+
+#if defined(LED_MATRIX_IS31FL3743A)
+# define IS31FL3743A_LED_COUNT LED_MATRIX_LED_COUNT
+#endif
+
+#if defined(IS31FL3743A_I2C_ADDRESS_4)
+# define IS31FL3743A_DRIVER_COUNT 4
+#elif defined(IS31FL3743A_I2C_ADDRESS_3)
+# define IS31FL3743A_DRIVER_COUNT 3
+#elif defined(IS31FL3743A_I2C_ADDRESS_2)
+# define IS31FL3743A_DRIVER_COUNT 2
+#elif defined(IS31FL3743A_I2C_ADDRESS_1)
+# define IS31FL3743A_DRIVER_COUNT 1
+#endif
+
+typedef struct is31fl3743a_led_t {
+ uint8_t driver : 2;
+ uint8_t v;
+} PACKED is31fl3743a_led_t;
+
+extern const is31fl3743a_led_t PROGMEM g_is31fl3743a_leds[IS31FL3743A_LED_COUNT];
+
+void is31fl3743a_init_drivers(void);
+void is31fl3743a_init(uint8_t index);
+void is31fl3743a_write_register(uint8_t index, uint8_t reg, uint8_t data);
+void is31fl3743a_select_page(uint8_t index, uint8_t page);
+
+void is31fl3743a_set_value(int index, uint8_t value);
+void is31fl3743a_set_value_all(uint8_t value);
+
+void is31fl3743a_set_scaling_register(uint8_t index, uint8_t value);
+
+void is31fl3743a_update_pwm_buffers(uint8_t index);
+void is31fl3743a_update_scaling_registers(uint8_t index);
+
+void is31fl3743a_flush(void);
+
+#define IS31FL3743A_PDR_0_OHM 0b000 // No pull-down resistor
+#define IS31FL3743A_PDR_0K5_OHM_SW_OFF 0b001 // 0.5 kOhm resistor in SWx off time
+#define IS31FL3743A_PDR_1K_OHM_SW_OFF 0b010 // 1 kOhm resistor in SWx off time
+#define IS31FL3743A_PDR_2K_OHM_SW_OFF 0b011 // 2 kOhm resistor in SWx off time
+#define IS31FL3743A_PDR_1K_OHM 0b100 // 1 kOhm resistor
+#define IS31FL3743A_PDR_2K_OHM 0b101 // 2 kOhm resistor
+#define IS31FL3743A_PDR_4K_OHM 0b110 // 4 kOhm resistor
+#define IS31FL3743A_PDR_8K_OHM 0b111 // 8 kOhm resistor
+
+#define IS31FL3743A_PUR_0_OHM 0b000 // No pull-up resistor
+#define IS31FL3743A_PUR_0K5_OHM_CS_OFF 0b001 // 0.5 kOhm resistor in CSy off time
+#define IS31FL3743A_PUR_1K_OHM_CS_OFF 0b010 // 1 kOhm resistor in CSy off time
+#define IS31FL3743A_PUR_2K_OHM_CS_OFF 0b011 // 2 kOhm resistor in CSy off time
+#define IS31FL3743A_PUR_1K_OHM 0b100 // 1 kOhm resistor
+#define IS31FL3743A_PUR_2K_OHM 0b101 // 2 kOhm resistor
+#define IS31FL3743A_PUR_4K_OHM 0b110 // 4 kOhm resistor
+#define IS31FL3743A_PUR_8K_OHM 0b111 // 8 kOhm resistor
+
+#define IS31FL3743A_SYNC_NONE 0b00
+#define IS31FL3743A_SYNC_SLAVE 0b10
+#define IS31FL3743A_SYNC_MASTER 0b11
+
+#define SW1_CS1 0x00
+#define SW1_CS2 0x01
+#define SW1_CS3 0x02
+#define SW1_CS4 0x03
+#define SW1_CS5 0x04
+#define SW1_CS6 0x05
+#define SW1_CS7 0x06
+#define SW1_CS8 0x07
+#define SW1_CS9 0x08
+#define SW1_CS10 0x09
+#define SW1_CS11 0x0A
+#define SW1_CS12 0x0B
+#define SW1_CS13 0x0C
+#define SW1_CS14 0x0D
+#define SW1_CS15 0x0E
+#define SW1_CS16 0x0F
+#define SW1_CS17 0x10
+#define SW1_CS18 0x11
+
+#define SW2_CS1 0x12
+#define SW2_CS2 0x13
+#define SW2_CS3 0x14
+#define SW2_CS4 0x15
+#define SW2_CS5 0x16
+#define SW2_CS6 0x17
+#define SW2_CS7 0x18
+#define SW2_CS8 0x19
+#define SW2_CS9 0x1A
+#define SW2_CS10 0x1B
+#define SW2_CS11 0x1C
+#define SW2_CS12 0x1D
+#define SW2_CS13 0x1E
+#define SW2_CS14 0x1F
+#define SW2_CS15 0x20
+#define SW2_CS16 0x21
+#define SW2_CS17 0x22
+#define SW2_CS18 0x23
+
+#define SW3_CS1 0x24
+#define SW3_CS2 0x25
+#define SW3_CS3 0x26
+#define SW3_CS4 0x27
+#define SW3_CS5 0x28
+#define SW3_CS6 0x29
+#define SW3_CS7 0x2A
+#define SW3_CS8 0x2B
+#define SW3_CS9 0x2C
+#define SW3_CS10 0x2D
+#define SW3_CS11 0x2E
+#define SW3_CS12 0x2F
+#define SW3_CS13 0x30
+#define SW3_CS14 0x31
+#define SW3_CS15 0x32
+#define SW3_CS16 0x33
+#define SW3_CS17 0x34
+#define SW3_CS18 0x35
+
+#define SW4_CS1 0x36
+#define SW4_CS2 0x37
+#define SW4_CS3 0x38
+#define SW4_CS4 0x39
+#define SW4_CS5 0x3A
+#define SW4_CS6 0x3B
+#define SW4_CS7 0x3C
+#define SW4_CS8 0x3D
+#define SW4_CS9 0x3E
+#define SW4_CS10 0x3F
+#define SW4_CS11 0x40
+#define SW4_CS12 0x41
+#define SW4_CS13 0x42
+#define SW4_CS14 0x43
+#define SW4_CS15 0x44
+#define SW4_CS16 0x45
+#define SW4_CS17 0x46
+#define SW4_CS18 0x47
+
+#define SW5_CS1 0x48
+#define SW5_CS2 0x49
+#define SW5_CS3 0x4A
+#define SW5_CS4 0x4B
+#define SW5_CS5 0x4C
+#define SW5_CS6 0x4D
+#define SW5_CS7 0x4E
+#define SW5_CS8 0x4F
+#define SW5_CS9 0x50
+#define SW5_CS10 0x51
+#define SW5_CS11 0x52
+#define SW5_CS12 0x53
+#define SW5_CS13 0x54
+#define SW5_CS14 0x55
+#define SW5_CS15 0x56
+#define SW5_CS16 0x57
+#define SW5_CS17 0x58
+#define SW5_CS18 0x59
+
+#define SW6_CS1 0x5A
+#define SW6_CS2 0x5B
+#define SW6_CS3 0x5C
+#define SW6_CS4 0x5D
+#define SW6_CS5 0x5E
+#define SW6_CS6 0x5F
+#define SW6_CS7 0x60
+#define SW6_CS8 0x61
+#define SW6_CS9 0x62
+#define SW6_CS10 0x63
+#define SW6_CS11 0x64
+#define SW6_CS12 0x65
+#define SW6_CS13 0x66
+#define SW6_CS14 0x67
+#define SW6_CS15 0x68
+#define SW6_CS16 0x69
+#define SW6_CS17 0x6A
+#define SW6_CS18 0x6B
+
+#define SW7_CS1 0x6C
+#define SW7_CS2 0x6D
+#define SW7_CS3 0x6E
+#define SW7_CS4 0x6F
+#define SW7_CS5 0x70
+#define SW7_CS6 0x71
+#define SW7_CS7 0x72
+#define SW7_CS8 0x73
+#define SW7_CS9 0x74
+#define SW7_CS10 0x75
+#define SW7_CS11 0x76
+#define SW7_CS12 0x77
+#define SW7_CS13 0x78
+#define SW7_CS14 0x79
+#define SW7_CS15 0x7A
+#define SW7_CS16 0x7B
+#define SW7_CS17 0x7C
+#define SW7_CS18 0x7D
+
+#define SW8_CS1 0x7E
+#define SW8_CS2 0x7F
+#define SW8_CS3 0x80
+#define SW8_CS4 0x81
+#define SW8_CS5 0x82
+#define SW8_CS6 0x83
+#define SW8_CS7 0x84
+#define SW8_CS8 0x85
+#define SW8_CS9 0x86
+#define SW8_CS10 0x87
+#define SW8_CS11 0x88
+#define SW8_CS12 0x89
+#define SW8_CS13 0x8A
+#define SW8_CS14 0x8B
+#define SW8_CS15 0x8C
+#define SW8_CS16 0x8D
+#define SW8_CS17 0x8E
+#define SW8_CS18 0x8F
+
+#define SW9_CS1 0x90
+#define SW9_CS2 0x91
+#define SW9_CS3 0x92
+#define SW9_CS4 0x93
+#define SW9_CS5 0x94
+#define SW9_CS6 0x95
+#define SW9_CS7 0x96
+#define SW9_CS8 0x97
+#define SW9_CS9 0x98
+#define SW9_CS10 0x99
+#define SW9_CS11 0x9A
+#define SW9_CS12 0x9B
+#define SW9_CS13 0x9C
+#define SW9_CS14 0x9D
+#define SW9_CS15 0x9E
+#define SW9_CS16 0x9F
+#define SW9_CS17 0xA0
+#define SW9_CS18 0xA1
+
+#define SW10_CS1 0xA2
+#define SW10_CS2 0xA3
+#define SW10_CS3 0xA4
+#define SW10_CS4 0xA5
+#define SW10_CS5 0xA6
+#define SW10_CS6 0xA7
+#define SW10_CS7 0xA8
+#define SW10_CS8 0xA9
+#define SW10_CS9 0xAA
+#define SW10_CS10 0xAB
+#define SW10_CS11 0xAC
+#define SW10_CS12 0xAD
+#define SW10_CS13 0xAE
+#define SW10_CS14 0xAF
+#define SW10_CS15 0xB0
+#define SW10_CS16 0xB1
+#define SW10_CS17 0xB2
+#define SW10_CS18 0xB3
+
+#define SW11_CS1 0xB4
+#define SW11_CS2 0xB5
+#define SW11_CS3 0xB6
+#define SW11_CS4 0xB7
+#define SW11_CS5 0xB8
+#define SW11_CS6 0xB9
+#define SW11_CS7 0xBA
+#define SW11_CS8 0xBB
+#define SW11_CS9 0xBC
+#define SW11_CS10 0xBD
+#define SW11_CS11 0xBE
+#define SW11_CS12 0xBF
+#define SW11_CS13 0xC0
+#define SW11_CS14 0xC1
+#define SW11_CS15 0xC2
+#define SW11_CS16 0xC3
+#define SW11_CS17 0xC4
+#define SW11_CS18 0xC5
diff --git a/drivers/led/issi/is31fl3743a.c b/drivers/led/issi/is31fl3743a.c
new file mode 100644
index 0000000000..6f13925f27
--- /dev/null
+++ b/drivers/led/issi/is31fl3743a.c
@@ -0,0 +1,249 @@
+/* Copyright 2017 Jason Williams
+ * Copyright 2018 Jack Humbert
+ * Copyright 2018 Yiancar
+ * Copyright 2020 MelGeek
+ * Copyright 2021 MasterSpoon
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "is31fl3743a.h"
+#include "i2c_master.h"
+#include "gpio.h"
+#include "wait.h"
+
+#define IS31FL3743A_PWM_REGISTER_COUNT 198
+#define IS31FL3743A_SCALING_REGISTER_COUNT 198
+
+#ifndef IS31FL3743A_I2C_TIMEOUT
+# define IS31FL3743A_I2C_TIMEOUT 100
+#endif
+
+#ifndef IS31FL3743A_I2C_PERSISTENCE
+# define IS31FL3743A_I2C_PERSISTENCE 0
+#endif
+
+#ifndef IS31FL3743A_CONFIGURATION
+# define IS31FL3743A_CONFIGURATION 0x01
+#endif
+
+#ifndef IS31FL3743A_SW_PULLDOWN
+# define IS31FL3743A_SW_PULLDOWN IS31FL3743A_PDR_2K_OHM_SW_OFF
+#endif
+
+#ifndef IS31FL3743A_CS_PULLUP
+# define IS31FL3743A_CS_PULLUP IS31FL3743A_PUR_2K_OHM_CS_OFF
+#endif
+
+#ifndef IS31FL3743A_GLOBAL_CURRENT
+# define IS31FL3743A_GLOBAL_CURRENT 0xFF
+#endif
+
+#ifndef IS31FL3743A_SYNC_1
+# define IS31FL3743A_SYNC_1 IS31FL3743A_SYNC_NONE
+#endif
+#ifndef IS31FL3743A_SYNC_2
+# define IS31FL3743A_SYNC_2 IS31FL3743A_SYNC_NONE
+#endif
+#ifndef IS31FL3743A_SYNC_3
+# define IS31FL3743A_SYNC_3 IS31FL3743A_SYNC_NONE
+#endif
+#ifndef IS31FL3743A_SYNC_4
+# define IS31FL3743A_SYNC_4 IS31FL3743A_SYNC_NONE
+#endif
+
+const uint8_t i2c_addresses[IS31FL3743A_DRIVER_COUNT] = {
+ IS31FL3743A_I2C_ADDRESS_1,
+#ifdef IS31FL3743A_I2C_ADDRESS_2
+ IS31FL3743A_I2C_ADDRESS_2,
+# ifdef IS31FL3743A_I2C_ADDRESS_3
+ IS31FL3743A_I2C_ADDRESS_3,
+# ifdef IS31FL3743A_I2C_ADDRESS_4
+ IS31FL3743A_I2C_ADDRESS_4,
+# endif
+# endif
+#endif
+};
+
+const uint8_t driver_sync[IS31FL3743A_DRIVER_COUNT] = {
+ IS31FL3743A_SYNC_1,
+#ifdef IS31FL3743A_I2C_ADDRESS_2
+ IS31FL3743A_SYNC_2,
+# ifdef IS31FL3743A_I2C_ADDRESS_3
+ IS31FL3743A_SYNC_3,
+# ifdef IS31FL3743A_I2C_ADDRESS_4
+ IS31FL3743A_SYNC_4,
+# endif
+# endif
+#endif
+};
+
+typedef struct is31fl3743a_driver_t {
+ uint8_t pwm_buffer[IS31FL3743A_PWM_REGISTER_COUNT];
+ bool pwm_buffer_dirty;
+ uint8_t scaling_buffer[IS31FL3743A_SCALING_REGISTER_COUNT];
+ bool scaling_buffer_dirty;
+} PACKED is31fl3743a_driver_t;
+
+is31fl3743a_driver_t driver_buffers[IS31FL3743A_DRIVER_COUNT] = {{
+ .pwm_buffer = {0},
+ .pwm_buffer_dirty = false,
+ .scaling_buffer = {0},
+ .scaling_buffer_dirty = false,
+}};
+
+void is31fl3743a_write_register(uint8_t index, uint8_t reg, uint8_t data) {
+#if IS31FL3743A_I2C_PERSISTENCE > 0
+ for (uint8_t i = 0; i < IS31FL3743A_I2C_PERSISTENCE; i++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3743A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3743A_I2C_TIMEOUT);
+#endif
+}
+
+void is31fl3743a_select_page(uint8_t index, uint8_t page) {
+ is31fl3743a_write_register(index, IS31FL3743A_REG_COMMAND_WRITE_LOCK, IS31FL3743A_COMMAND_WRITE_LOCK_MAGIC);
+ is31fl3743a_write_register(index, IS31FL3743A_REG_COMMAND, page);
+}
+
+void is31fl3743a_write_pwm_buffer(uint8_t index) {
+ // Assumes page 0 is already selected.
+ // Transmit PWM registers in 11 transfers of 18 bytes.
+
+ // Iterate over the pwm_buffer contents at 18 byte intervals.
+ for (uint8_t i = 0; i < IS31FL3743A_PWM_REGISTER_COUNT; i += 18) {
+#if IS31FL3743A_I2C_PERSISTENCE > 0
+ for (uint8_t j = 0; j < IS31FL3743A_I2C_PERSISTENCE; j++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3743A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3743A_I2C_TIMEOUT);
+#endif
+ }
+}
+
+void is31fl3743a_init_drivers(void) {
+ i2c_init();
+
+#if defined(IS31FL3743A_SDB_PIN)
+ gpio_set_pin_output(IS31FL3743A_SDB_PIN);
+ gpio_write_pin_high(IS31FL3743A_SDB_PIN);
+#endif
+
+ for (uint8_t i = 0; i < IS31FL3743A_DRIVER_COUNT; i++) {
+ is31fl3743a_init(i);
+ }
+
+ for (int i = 0; i < IS31FL3743A_LED_COUNT; i++) {
+ is31fl3743a_set_scaling_register(i, 0xFF, 0xFF, 0xFF);
+ }
+
+ for (uint8_t i = 0; i < IS31FL3743A_DRIVER_COUNT; i++) {
+ is31fl3743a_update_scaling_registers(i);
+ }
+}
+
+void is31fl3743a_init(uint8_t index) {
+ // In order to avoid the LEDs being driven with garbage data
+ // in the LED driver's PWM registers, shutdown is enabled last.
+ // Set up the mode and other settings, clear the PWM registers,
+ // then disable software shutdown.
+
+ is31fl3743a_select_page(index, IS31FL3743A_COMMAND_SCALING);
+
+ // Turn off all LEDs.
+ for (uint8_t i = 0; i < IS31FL3743A_SCALING_REGISTER_COUNT; i++) {
+ is31fl3743a_write_register(index, i + 1, 0x00);
+ }
+
+ is31fl3743a_select_page(index, IS31FL3743A_COMMAND_PWM);
+
+ for (uint8_t i = 0; i < IS31FL3743A_PWM_REGISTER_COUNT; i++) {
+ is31fl3743a_write_register(index, i + 1, 0x00);
+ }
+
+ is31fl3743a_select_page(index, IS31FL3743A_COMMAND_FUNCTION);
+
+ uint8_t sync = driver_sync[index];
+
+ is31fl3743a_write_register(index, IS31FL3743A_FUNCTION_REG_PULLDOWNUP, (IS31FL3743A_SW_PULLDOWN << 4) | IS31FL3743A_CS_PULLUP);
+ is31fl3743a_write_register(index, IS31FL3743A_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3743A_GLOBAL_CURRENT);
+ is31fl3743a_write_register(index, IS31FL3743A_FUNCTION_REG_SPREAD_SPECTRUM, (sync & 0b11) << 6);
+ is31fl3743a_write_register(index, IS31FL3743A_FUNCTION_REG_CONFIGURATION, IS31FL3743A_CONFIGURATION);
+
+ // Wait 10ms to ensure the device has woken up.
+ wait_ms(10);
+}
+
+void is31fl3743a_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
+ is31fl3743a_led_t led;
+
+ if (index >= 0 && index < IS31FL3743A_LED_COUNT) {
+ memcpy_P(&led, (&g_is31fl3743a_leds[index]), sizeof(led));
+
+ if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) {
+ return;
+ }
+
+ driver_buffers[led.driver].pwm_buffer[led.r] = red;
+ driver_buffers[led.driver].pwm_buffer[led.g] = green;
+ driver_buffers[led.driver].pwm_buffer[led.b] = blue;
+ driver_buffers[led.driver].pwm_buffer_dirty = true;
+ }
+}
+
+void is31fl3743a_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
+ for (int i = 0; i < IS31FL3743A_LED_COUNT; i++) {
+ is31fl3743a_set_color(i, red, green, blue);
+ }
+}
+
+void is31fl3743a_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue) {
+ is31fl3743a_led_t led;
+ memcpy_P(&led, (&g_is31fl3743a_leds[index]), sizeof(led));
+
+ driver_buffers[led.driver].scaling_buffer[led.r] = red;
+ driver_buffers[led.driver].scaling_buffer[led.g] = green;
+ driver_buffers[led.driver].scaling_buffer[led.b] = blue;
+ driver_buffers[led.driver].scaling_buffer_dirty = true;
+}
+
+void is31fl3743a_update_pwm_buffers(uint8_t index) {
+ if (driver_buffers[index].pwm_buffer_dirty) {
+ is31fl3743a_select_page(index, IS31FL3743A_COMMAND_PWM);
+
+ is31fl3743a_write_pwm_buffer(index);
+
+ driver_buffers[index].pwm_buffer_dirty = false;
+ }
+}
+
+void is31fl3743a_update_scaling_registers(uint8_t index) {
+ if (driver_buffers[index].scaling_buffer_dirty) {
+ is31fl3743a_select_page(index, IS31FL3743A_COMMAND_SCALING);
+
+ for (uint8_t i = 0; i < IS31FL3743A_SCALING_REGISTER_COUNT; i++) {
+ is31fl3743a_write_register(index, i + 1, driver_buffers[index].scaling_buffer[i]);
+ }
+
+ driver_buffers[index].scaling_buffer_dirty = false;
+ }
+}
+
+void is31fl3743a_flush(void) {
+ for (uint8_t i = 0; i < IS31FL3743A_DRIVER_COUNT; i++) {
+ is31fl3743a_update_pwm_buffers(i);
+ }
+}
diff --git a/drivers/led/issi/is31fl3743a.h b/drivers/led/issi/is31fl3743a.h
new file mode 100644
index 0000000000..48aeab46ab
--- /dev/null
+++ b/drivers/led/issi/is31fl3743a.h
@@ -0,0 +1,541 @@
+/* Copyright 2017 Jason Williams
+ * Copyright 2018 Jack Humbert
+ * Copyright 2018 Yiancar
+ * Copyright 2020 MelGeek
+ * Copyright 2021 MasterSpoon
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "progmem.h"
+#include "util.h"
+
+#define IS31FL3743A_REG_ID 0xFC
+
+#define IS31FL3743A_REG_COMMAND 0xFD
+
+#define IS31FL3743A_COMMAND_PWM 0x00
+#define IS31FL3743A_COMMAND_SCALING 0x01
+#define IS31FL3743A_COMMAND_FUNCTION 0x02
+
+#define IS31FL3743A_FUNCTION_REG_CONFIGURATION 0x00
+#define IS31FL3743A_FUNCTION_REG_GLOBAL_CURRENT 0x01
+#define IS31FL3743A_FUNCTION_REG_PULLDOWNUP 0x02
+#define IS31FL3743A_FUNCTION_REG_TEMPERATURE 0x24
+#define IS31FL3743A_FUNCTION_REG_SPREAD_SPECTRUM 0x25
+#define IS31FL3743A_FUNCTION_REG_RESET 0x2F
+
+#define IS31FL3743A_REG_COMMAND_WRITE_LOCK 0xFE
+#define IS31FL3743A_COMMAND_WRITE_LOCK_MAGIC 0xC5
+
+#define IS31FL3743A_I2C_ADDRESS_GND_GND 0x20
+#define IS31FL3743A_I2C_ADDRESS_GND_SCL 0x21
+#define IS31FL3743A_I2C_ADDRESS_GND_SDA 0x22
+#define IS31FL3743A_I2C_ADDRESS_GND_VCC 0x23
+#define IS31FL3743A_I2C_ADDRESS_SCL_GND 0x24
+#define IS31FL3743A_I2C_ADDRESS_SCL_SCL 0x25
+#define IS31FL3743A_I2C_ADDRESS_SCL_SDA 0x26
+#define IS31FL3743A_I2C_ADDRESS_SCL_VCC 0x27
+#define IS31FL3743A_I2C_ADDRESS_SDA_GND 0x28
+#define IS31FL3743A_I2C_ADDRESS_SDA_SCL 0x29
+#define IS31FL3743A_I2C_ADDRESS_SDA_SDA 0x2A
+#define IS31FL3743A_I2C_ADDRESS_SDA_VCC 0x2B
+#define IS31FL3743A_I2C_ADDRESS_VCC_GND 0x2C
+#define IS31FL3743A_I2C_ADDRESS_VCC_SCL 0x2D
+#define IS31FL3743A_I2C_ADDRESS_VCC_SDA 0x2E
+#define IS31FL3743A_I2C_ADDRESS_VCC_VCC 0x2F
+
+#if defined(RGB_MATRIX_IS31FL3743A)
+# define IS31FL3743A_LED_COUNT RGB_MATRIX_LED_COUNT
+#endif
+
+#if defined(IS31FL3743A_I2C_ADDRESS_4)
+# define IS31FL3743A_DRIVER_COUNT 4
+#elif defined(IS31FL3743A_I2C_ADDRESS_3)
+# define IS31FL3743A_DRIVER_COUNT 3
+#elif defined(IS31FL3743A_I2C_ADDRESS_2)
+# define IS31FL3743A_DRIVER_COUNT 2
+#elif defined(IS31FL3743A_I2C_ADDRESS_1)
+# define IS31FL3743A_DRIVER_COUNT 1
+#endif
+
+typedef struct is31fl3743a_led_t {
+ uint8_t driver : 2;
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+} PACKED is31fl3743a_led_t;
+
+extern const is31fl3743a_led_t PROGMEM g_is31fl3743a_leds[IS31FL3743A_LED_COUNT];
+
+void is31fl3743a_init_drivers(void);
+void is31fl3743a_init(uint8_t index);
+void is31fl3743a_write_register(uint8_t index, uint8_t reg, uint8_t data);
+void is31fl3743a_select_page(uint8_t index, uint8_t page);
+
+void is31fl3743a_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
+void is31fl3743a_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
+
+void is31fl3743a_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue);
+
+void is31fl3743a_update_pwm_buffers(uint8_t index);
+void is31fl3743a_update_scaling_registers(uint8_t index);
+
+void is31fl3743a_flush(void);
+
+#define IS31FL3743A_PDR_0_OHM 0b000 // No pull-down resistor
+#define IS31FL3743A_PDR_0K5_OHM_SW_OFF 0b001 // 0.5 kOhm resistor in SWx off time
+#define IS31FL3743A_PDR_1K_OHM_SW_OFF 0b010 // 1 kOhm resistor in SWx off time
+#define IS31FL3743A_PDR_2K_OHM_SW_OFF 0b011 // 2 kOhm resistor in SWx off time
+#define IS31FL3743A_PDR_1K_OHM 0b100 // 1 kOhm resistor
+#define IS31FL3743A_PDR_2K_OHM 0b101 // 2 kOhm resistor
+#define IS31FL3743A_PDR_4K_OHM 0b110 // 4 kOhm resistor
+#define IS31FL3743A_PDR_8K_OHM 0b111 // 8 kOhm resistor
+
+#define IS31FL3743A_PUR_0_OHM 0b000 // No pull-up resistor
+#define IS31FL3743A_PUR_0K5_OHM_CS_OFF 0b001 // 0.5 kOhm resistor in CSy off time
+#define IS31FL3743A_PUR_1K_OHM_CS_OFF 0b010 // 1 kOhm resistor in CSy off time
+#define IS31FL3743A_PUR_2K_OHM_CS_OFF 0b011 // 2 kOhm resistor in CSy off time
+#define IS31FL3743A_PUR_1K_OHM 0b100 // 1 kOhm resistor
+#define IS31FL3743A_PUR_2K_OHM 0b101 // 2 kOhm resistor
+#define IS31FL3743A_PUR_4K_OHM 0b110 // 4 kOhm resistor
+#define IS31FL3743A_PUR_8K_OHM 0b111 // 8 kOhm resistor
+
+#define IS31FL3743A_SYNC_NONE 0b00
+#define IS31FL3743A_SYNC_SLAVE 0b10
+#define IS31FL3743A_SYNC_MASTER 0b11
+
+#define SW1_CS1 0x00
+#define SW1_CS2 0x01
+#define SW1_CS3 0x02
+#define SW1_CS4 0x03
+#define SW1_CS5 0x04
+#define SW1_CS6 0x05
+#define SW1_CS7 0x06
+#define SW1_CS8 0x07
+#define SW1_CS9 0x08
+#define SW1_CS10 0x09
+#define SW1_CS11 0x0A
+#define SW1_CS12 0x0B
+#define SW1_CS13 0x0C
+#define SW1_CS14 0x0D
+#define SW1_CS15 0x0E
+#define SW1_CS16 0x0F
+#define SW1_CS17 0x10
+#define SW1_CS18 0x11
+
+#define SW2_CS1 0x12
+#define SW2_CS2 0x13
+#define SW2_CS3 0x14
+#define SW2_CS4 0x15
+#define SW2_CS5 0x16
+#define SW2_CS6 0x17
+#define SW2_CS7 0x18
+#define SW2_CS8 0x19
+#define SW2_CS9 0x1A
+#define SW2_CS10 0x1B
+#define SW2_CS11 0x1C
+#define SW2_CS12 0x1D
+#define SW2_CS13 0x1E
+#define SW2_CS14 0x1F
+#define SW2_CS15 0x20
+#define SW2_CS16 0x21
+#define SW2_CS17 0x22
+#define SW2_CS18 0x23
+
+#define SW3_CS1 0x24
+#define SW3_CS2 0x25
+#define SW3_CS3 0x26
+#define SW3_CS4 0x27
+#define SW3_CS5 0x28
+#define SW3_CS6 0x29
+#define SW3_CS7 0x2A
+#define SW3_CS8 0x2B
+#define SW3_CS9 0x2C
+#define SW3_CS10 0x2D
+#define SW3_CS11 0x2E
+#define SW3_CS12 0x2F
+#define SW3_CS13 0x30
+#define SW3_CS14 0x31
+#define SW3_CS15 0x32
+#define SW3_CS16 0x33
+#define SW3_CS17 0x34
+#define SW3_CS18 0x35
+
+#define SW4_CS1 0x36
+#define SW4_CS2 0x37
+#define SW4_CS3 0x38
+#define SW4_CS4 0x39
+#define SW4_CS5 0x3A
+#define SW4_CS6 0x3B
+#define SW4_CS7 0x3C
+#define SW4_CS8 0x3D
+#define SW4_CS9 0x3E
+#define SW4_CS10 0x3F
+#define SW4_CS11 0x40
+#define SW4_CS12 0x41
+#define SW4_CS13 0x42
+#define SW4_CS14 0x43
+#define SW4_CS15 0x44
+#define SW4_CS16 0x45
+#define SW4_CS17 0x46
+#define SW4_CS18 0x47
+
+#define SW5_CS1 0x48
+#define SW5_CS2 0x49
+#define SW5_CS3 0x4A
+#define SW5_CS4 0x4B
+#define SW5_CS5 0x4C
+#define SW5_CS6 0x4D
+#define SW5_CS7 0x4E
+#define SW5_CS8 0x4F
+#define SW5_CS9 0x50
+#define SW5_CS10 0x51
+#define SW5_CS11 0x52
+#define SW5_CS12 0x53
+#define SW5_CS13 0x54
+#define SW5_CS14 0x55
+#define SW5_CS15 0x56
+#define SW5_CS16 0x57
+#define SW5_CS17 0x58
+#define SW5_CS18 0x59
+
+#define SW6_CS1 0x5A
+#define SW6_CS2 0x5B
+#define SW6_CS3 0x5C
+#define SW6_CS4 0x5D
+#define SW6_CS5 0x5E
+#define SW6_CS6 0x5F
+#define SW6_CS7 0x60
+#define SW6_CS8 0x61
+#define SW6_CS9 0x62
+#define SW6_CS10 0x63
+#define SW6_CS11 0x64
+#define SW6_CS12 0x65
+#define SW6_CS13 0x66
+#define SW6_CS14 0x67
+#define SW6_CS15 0x68
+#define SW6_CS16 0x69
+#define SW6_CS17 0x6A
+#define SW6_CS18 0x6B
+
+#define SW7_CS1 0x6C
+#define SW7_CS2 0x6D
+#define SW7_CS3 0x6E
+#define SW7_CS4 0x6F
+#define SW7_CS5 0x70
+#define SW7_CS6 0x71
+#define SW7_CS7 0x72
+#define SW7_CS8 0x73
+#define SW7_CS9 0x74
+#define SW7_CS10 0x75
+#define SW7_CS11 0x76
+#define SW7_CS12 0x77
+#define SW7_CS13 0x78
+#define SW7_CS14 0x79
+#define SW7_CS15 0x7A
+#define SW7_CS16 0x7B
+#define SW7_CS17 0x7C
+#define SW7_CS18 0x7D
+
+#define SW8_CS1 0x7E
+#define SW8_CS2 0x7F
+#define SW8_CS3 0x80
+#define SW8_CS4 0x81
+#define SW8_CS5 0x82
+#define SW8_CS6 0x83
+#define SW8_CS7 0x84
+#define SW8_CS8 0x85
+#define SW8_CS9 0x86
+#define SW8_CS10 0x87
+#define SW8_CS11 0x88
+#define SW8_CS12 0x89
+#define SW8_CS13 0x8A
+#define SW8_CS14 0x8B
+#define SW8_CS15 0x8C
+#define SW8_CS16 0x8D
+#define SW8_CS17 0x8E
+#define SW8_CS18 0x8F
+
+#define SW9_CS1 0x90
+#define SW9_CS2 0x91
+#define SW9_CS3 0x92
+#define SW9_CS4 0x93
+#define SW9_CS5 0x94
+#define SW9_CS6 0x95
+#define SW9_CS7 0x96
+#define SW9_CS8 0x97
+#define SW9_CS9 0x98
+#define SW9_CS10 0x99
+#define SW9_CS11 0x9A
+#define SW9_CS12 0x9B
+#define SW9_CS13 0x9C
+#define SW9_CS14 0x9D
+#define SW9_CS15 0x9E
+#define SW9_CS16 0x9F
+#define SW9_CS17 0xA0
+#define SW9_CS18 0xA1
+
+#define SW10_CS1 0xA2
+#define SW10_CS2 0xA3
+#define SW10_CS3 0xA4
+#define SW10_CS4 0xA5
+#define SW10_CS5 0xA6
+#define SW10_CS6 0xA7
+#define SW10_CS7 0xA8
+#define SW10_CS8 0xA9
+#define SW10_CS9 0xAA
+#define SW10_CS10 0xAB
+#define SW10_CS11 0xAC
+#define SW10_CS12 0xAD
+#define SW10_CS13 0xAE
+#define SW10_CS14 0xAF
+#define SW10_CS15 0xB0
+#define SW10_CS16 0xB1
+#define SW10_CS17 0xB2
+#define SW10_CS18 0xB3
+
+#define SW11_CS1 0xB4
+#define SW11_CS2 0xB5
+#define SW11_CS3 0xB6
+#define SW11_CS4 0xB7
+#define SW11_CS5 0xB8
+#define SW11_CS6 0xB9
+#define SW11_CS7 0xBA
+#define SW11_CS8 0xBB
+#define SW11_CS9 0xBC
+#define SW11_CS10 0xBD
+#define SW11_CS11 0xBE
+#define SW11_CS12 0xBF
+#define SW11_CS13 0xC0
+#define SW11_CS14 0xC1
+#define SW11_CS15 0xC2
+#define SW11_CS16 0xC3
+#define SW11_CS17 0xC4
+#define SW11_CS18 0xC5
+
+// DEPRECATED - DO NOT USE
+
+#define CS1_SW1 SW1_CS1
+#define CS2_SW1 SW1_CS2
+#define CS3_SW1 SW1_CS3
+#define CS4_SW1 SW1_CS4
+#define CS5_SW1 SW1_CS5
+#define CS6_SW1 SW1_CS6
+#define CS7_SW1 SW1_CS7
+#define CS8_SW1 SW1_CS8
+#define CS9_SW1 SW1_CS9
+#define CS10_SW1 SW1_CS10
+#define CS11_SW1 SW1_CS11
+#define CS12_SW1 SW1_CS12
+#define CS13_SW1 SW1_CS13
+#define CS14_SW1 SW1_CS14
+#define CS15_SW1 SW1_CS15
+#define CS16_SW1 SW1_CS16
+#define CS17_SW1 SW1_CS17
+#define CS18_SW1 SW1_CS18
+
+#define CS1_SW2 SW2_CS1
+#define CS2_SW2 SW2_CS2
+#define CS3_SW2 SW2_CS3
+#define CS4_SW2 SW2_CS4
+#define CS5_SW2 SW2_CS5
+#define CS6_SW2 SW2_CS6
+#define CS7_SW2 SW2_CS7
+#define CS8_SW2 SW2_CS8
+#define CS9_SW2 SW2_CS9
+#define CS10_SW2 SW2_CS10
+#define CS11_SW2 SW2_CS11
+#define CS12_SW2 SW2_CS12
+#define CS13_SW2 SW2_CS13
+#define CS14_SW2 SW2_CS14
+#define CS15_SW2 SW2_CS15
+#define CS16_SW2 SW2_CS16
+#define CS17_SW2 SW2_CS17
+#define CS18_SW2 SW2_CS18
+
+#define CS1_SW3 SW3_CS1
+#define CS2_SW3 SW3_CS2
+#define CS3_SW3 SW3_CS3
+#define CS4_SW3 SW3_CS4
+#define CS5_SW3 SW3_CS5
+#define CS6_SW3 SW3_CS6
+#define CS7_SW3 SW3_CS7
+#define CS8_SW3 SW3_CS8
+#define CS9_SW3 SW3_CS9
+#define CS10_SW3 SW3_CS10
+#define CS11_SW3 SW3_CS11
+#define CS12_SW3 SW3_CS12
+#define CS13_SW3 SW3_CS13
+#define CS14_SW3 SW3_CS14
+#define CS15_SW3 SW3_CS15
+#define CS16_SW3 SW3_CS16
+#define CS17_SW3 SW3_CS17
+#define CS18_SW3 SW3_CS18
+
+#define CS1_SW4 SW4_CS1
+#define CS2_SW4 SW4_CS2
+#define CS3_SW4 SW4_CS3
+#define CS4_SW4 SW4_CS4
+#define CS5_SW4 SW4_CS5
+#define CS6_SW4 SW4_CS6
+#define CS7_SW4 SW4_CS7
+#define CS8_SW4 SW4_CS8
+#define CS9_SW4 SW4_CS9
+#define CS10_SW4 SW4_CS10
+#define CS11_SW4 SW4_CS11
+#define CS12_SW4 SW4_CS12
+#define CS13_SW4 SW4_CS13
+#define CS14_SW4 SW4_CS14
+#define CS15_SW4 SW4_CS15
+#define CS16_SW4 SW4_CS16
+#define CS17_SW4 SW4_CS17
+#define CS18_SW4 SW4_CS18
+
+#define CS1_SW5 SW5_CS1
+#define CS2_SW5 SW5_CS2
+#define CS3_SW5 SW5_CS3
+#define CS4_SW5 SW5_CS4
+#define CS5_SW5 SW5_CS5
+#define CS6_SW5 SW5_CS6
+#define CS7_SW5 SW5_CS7
+#define CS8_SW5 SW5_CS8
+#define CS9_SW5 SW5_CS9
+#define CS10_SW5 SW5_CS10
+#define CS11_SW5 SW5_CS11
+#define CS12_SW5 SW5_CS12
+#define CS13_SW5 SW5_CS13
+#define CS14_SW5 SW5_CS14
+#define CS15_SW5 SW5_CS15
+#define CS16_SW5 SW5_CS16
+#define CS17_SW5 SW5_CS17
+#define CS18_SW5 SW5_CS18
+
+#define CS1_SW6 SW6_CS1
+#define CS2_SW6 SW6_CS2
+#define CS3_SW6 SW6_CS3
+#define CS4_SW6 SW6_CS4
+#define CS5_SW6 SW6_CS5
+#define CS6_SW6 SW6_CS6
+#define CS7_SW6 SW6_CS7
+#define CS8_SW6 SW6_CS8
+#define CS9_SW6 SW6_CS9
+#define CS10_SW6 SW6_CS10
+#define CS11_SW6 SW6_CS11
+#define CS12_SW6 SW6_CS12
+#define CS13_SW6 SW6_CS13
+#define CS14_SW6 SW6_CS14
+#define CS15_SW6 SW6_CS15
+#define CS16_SW6 SW6_CS16
+#define CS17_SW6 SW6_CS17
+#define CS18_SW6 SW6_CS18
+
+#define CS1_SW7 SW7_CS1
+#define CS2_SW7 SW7_CS2
+#define CS3_SW7 SW7_CS3
+#define CS4_SW7 SW7_CS4
+#define CS5_SW7 SW7_CS5
+#define CS6_SW7 SW7_CS6
+#define CS7_SW7 SW7_CS7
+#define CS8_SW7 SW7_CS8
+#define CS9_SW7 SW7_CS9
+#define CS10_SW7 SW7_CS10
+#define CS11_SW7 SW7_CS11
+#define CS12_SW7 SW7_CS12
+#define CS13_SW7 SW7_CS13
+#define CS14_SW7 SW7_CS14
+#define CS15_SW7 SW7_CS15
+#define CS16_SW7 SW7_CS16
+#define CS17_SW7 SW7_CS17
+#define CS18_SW7 SW7_CS18
+
+#define CS1_SW8 SW8_CS1
+#define CS2_SW8 SW8_CS2
+#define CS3_SW8 SW8_CS3
+#define CS4_SW8 SW8_CS4
+#define CS5_SW8 SW8_CS5
+#define CS6_SW8 SW8_CS6
+#define CS7_SW8 SW8_CS7
+#define CS8_SW8 SW8_CS8
+#define CS9_SW8 SW8_CS9
+#define CS10_SW8 SW8_CS10
+#define CS11_SW8 SW8_CS11
+#define CS12_SW8 SW8_CS12
+#define CS13_SW8 SW8_CS13
+#define CS14_SW8 SW8_CS14
+#define CS15_SW8 SW8_CS15
+#define CS16_SW8 SW8_CS16
+#define CS17_SW8 SW8_CS17
+#define CS18_SW8 SW8_CS18
+
+#define CS1_SW9 SW9_CS1
+#define CS2_SW9 SW9_CS2
+#define CS3_SW9 SW9_CS3
+#define CS4_SW9 SW9_CS4
+#define CS5_SW9 SW9_CS5
+#define CS6_SW9 SW9_CS6
+#define CS7_SW9 SW9_CS7
+#define CS8_SW9 SW9_CS8
+#define CS9_SW9 SW9_CS9
+#define CS10_SW9 SW9_CS10
+#define CS11_SW9 SW9_CS11
+#define CS12_SW9 SW9_CS12
+#define CS13_SW9 SW9_CS13
+#define CS14_SW9 SW9_CS14
+#define CS15_SW9 SW9_CS15
+#define CS16_SW9 SW9_CS16
+#define CS17_SW9 SW9_CS17
+#define CS18_SW9 SW9_CS18
+
+#define CS1_SW10 SW10_CS1
+#define CS2_SW10 SW10_CS2
+#define CS3_SW10 SW10_CS3
+#define CS4_SW10 SW10_CS4
+#define CS5_SW10 SW10_CS5
+#define CS6_SW10 SW10_CS6
+#define CS7_SW10 SW10_CS7
+#define CS8_SW10 SW10_CS8
+#define CS9_SW10 SW10_CS9
+#define CS10_SW10 SW10_CS10
+#define CS11_SW10 SW10_CS11
+#define CS12_SW10 SW10_CS12
+#define CS13_SW10 SW10_CS13
+#define CS14_SW10 SW10_CS14
+#define CS15_SW10 SW10_CS15
+#define CS16_SW10 SW10_CS16
+#define CS17_SW10 SW10_CS17
+#define CS18_SW10 SW10_CS18
+
+#define CS1_SW11 SW11_CS1
+#define CS2_SW11 SW11_CS2
+#define CS3_SW11 SW11_CS3
+#define CS4_SW11 SW11_CS4
+#define CS5_SW11 SW11_CS5
+#define CS6_SW11 SW11_CS6
+#define CS7_SW11 SW11_CS7
+#define CS8_SW11 SW11_CS8
+#define CS9_SW11 SW11_CS9
+#define CS10_SW11 SW11_CS10
+#define CS11_SW11 SW11_CS11
+#define CS12_SW11 SW11_CS12
+#define CS13_SW11 SW11_CS13
+#define CS14_SW11 SW11_CS14
+#define CS15_SW11 SW11_CS15
+#define CS16_SW11 SW11_CS16
+#define CS17_SW11 SW11_CS17
+#define CS18_SW11 SW11_CS18
diff --git a/drivers/led/issi/is31fl3745-mono.c b/drivers/led/issi/is31fl3745-mono.c
new file mode 100644
index 0000000000..a6ab699245
--- /dev/null
+++ b/drivers/led/issi/is31fl3745-mono.c
@@ -0,0 +1,245 @@
+/* Copyright 2017 Jason Williams
+ * Copyright 2018 Jack Humbert
+ * Copyright 2018 Yiancar
+ * Copyright 2020 MelGeek
+ * Copyright 2021 MasterSpoon
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "is31fl3745-mono.h"
+#include "i2c_master.h"
+#include "gpio.h"
+#include "wait.h"
+
+#define IS31FL3745_PWM_REGISTER_COUNT 144
+#define IS31FL3745_SCALING_REGISTER_COUNT 144
+
+#ifndef IS31FL3745_I2C_TIMEOUT
+# define IS31FL3745_I2C_TIMEOUT 100
+#endif
+
+#ifndef IS31FL3745_I2C_PERSISTENCE
+# define IS31FL3745_I2C_PERSISTENCE 0
+#endif
+
+#ifndef IS31FL3745_CONFIGURATION
+# define IS31FL3745_CONFIGURATION 0x31
+#endif
+
+#ifndef IS31FL3745_SW_PULLDOWN
+# define IS31FL3745_SW_PULLDOWN IS31FL3745_PDR_2K_OHM_SW_OFF
+#endif
+
+#ifndef IS31FL3745_CS_PULLUP
+# define IS31FL3745_CS_PULLUP IS31FL3745_PUR_2K_OHM_CS_OFF
+#endif
+
+#ifndef IS31FL3745_GLOBAL_CURRENT
+# define IS31FL3745_GLOBAL_CURRENT 0xFF
+#endif
+
+#ifndef IS31FL3745_SYNC_1
+# define IS31FL3745_SYNC_1 IS31FL3745_SYNC_NONE
+#endif
+#ifndef IS31FL3745_SYNC_2
+# define IS31FL3745_SYNC_2 IS31FL3745_SYNC_NONE
+#endif
+#ifndef IS31FL3745_SYNC_3
+# define IS31FL3745_SYNC_3 IS31FL3745_SYNC_NONE
+#endif
+#ifndef IS31FL3745_SYNC_4
+# define IS31FL3745_SYNC_4 IS31FL3745_SYNC_NONE
+#endif
+
+const uint8_t i2c_addresses[IS31FL3745_DRIVER_COUNT] = {
+ IS31FL3745_I2C_ADDRESS_1,
+#ifdef IS31FL3745_I2C_ADDRESS_2
+ IS31FL3745_I2C_ADDRESS_2,
+# ifdef IS31FL3745_I2C_ADDRESS_3
+ IS31FL3745_I2C_ADDRESS_3,
+# ifdef IS31FL3745_I2C_ADDRESS_4
+ IS31FL3745_I2C_ADDRESS_4,
+# endif
+# endif
+#endif
+};
+
+const uint8_t driver_sync[IS31FL3745_DRIVER_COUNT] = {
+ IS31FL3745_SYNC_1,
+#ifdef IS31FL3745_I2C_ADDRESS_2
+ IS31FL3745_SYNC_2,
+# ifdef IS31FL3745_I2C_ADDRESS_3
+ IS31FL3745_SYNC_3,
+# ifdef IS31FL3745_I2C_ADDRESS_4
+ IS31FL3745_SYNC_4,
+# endif
+# endif
+#endif
+};
+
+typedef struct is31fl3745_driver_t {
+ uint8_t pwm_buffer[IS31FL3745_PWM_REGISTER_COUNT];
+ bool pwm_buffer_dirty;
+ uint8_t scaling_buffer[IS31FL3745_SCALING_REGISTER_COUNT];
+ bool scaling_buffer_dirty;
+} PACKED is31fl3745_driver_t;
+
+is31fl3745_driver_t driver_buffers[IS31FL3745_DRIVER_COUNT] = {{
+ .pwm_buffer = {0},
+ .pwm_buffer_dirty = false,
+ .scaling_buffer = {0},
+ .scaling_buffer_dirty = false,
+}};
+
+void is31fl3745_write_register(uint8_t index, uint8_t reg, uint8_t data) {
+#if IS31FL3745_I2C_PERSISTENCE > 0
+ for (uint8_t i = 0; i < IS31FL3745_I2C_PERSISTENCE; i++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3745_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3745_I2C_TIMEOUT);
+#endif
+}
+
+void is31fl3745_select_page(uint8_t index, uint8_t page) {
+ is31fl3745_write_register(index, IS31FL3745_REG_COMMAND_WRITE_LOCK, IS31FL3745_COMMAND_WRITE_LOCK_MAGIC);
+ is31fl3745_write_register(index, IS31FL3745_REG_COMMAND, page);
+}
+
+void is31fl3745_write_pwm_buffer(uint8_t index) {
+ // Assumes page 0 is already selected.
+ // Transmit PWM registers in 8 transfers of 18 bytes.
+
+ // Iterate over the pwm_buffer contents at 18 byte intervals.
+ for (uint8_t i = 0; i < IS31FL3745_PWM_REGISTER_COUNT; i += 18) {
+#if IS31FL3745_I2C_PERSISTENCE > 0
+ for (uint8_t j = 0; j < IS31FL3745_I2C_PERSISTENCE; j++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3745_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3745_I2C_TIMEOUT);
+#endif
+ }
+}
+
+void is31fl3745_init_drivers(void) {
+ i2c_init();
+
+#if defined(IS31FL3745_SDB_PIN)
+ gpio_set_pin_output(IS31FL3745_SDB_PIN);
+ gpio_write_pin_high(IS31FL3745_SDB_PIN);
+#endif
+
+ for (uint8_t i = 0; i < IS31FL3745_DRIVER_COUNT; i++) {
+ is31fl3745_init(i);
+ }
+
+ for (int i = 0; i < IS31FL3745_LED_COUNT; i++) {
+ is31fl3745_set_scaling_register(i, 0xFF);
+ }
+
+ for (uint8_t i = 0; i < IS31FL3745_DRIVER_COUNT; i++) {
+ is31fl3745_update_scaling_registers(i);
+ }
+}
+
+void is31fl3745_init(uint8_t index) {
+ // In order to avoid the LEDs being driven with garbage data
+ // in the LED driver's PWM registers, shutdown is enabled last.
+ // Set up the mode and other settings, clear the PWM registers,
+ // then disable software shutdown.
+
+ is31fl3745_select_page(index, IS31FL3745_COMMAND_SCALING);
+
+ // Turn off all LEDs.
+ for (uint8_t i = 0; i < IS31FL3745_SCALING_REGISTER_COUNT; i++) {
+ is31fl3745_write_register(index, i + 1, 0x00);
+ }
+
+ is31fl3745_select_page(index, IS31FL3745_COMMAND_PWM);
+
+ for (uint8_t i = 0; i < IS31FL3745_PWM_REGISTER_COUNT; i++) {
+ is31fl3745_write_register(index, i + 1, 0x00);
+ }
+
+ is31fl3745_select_page(index, IS31FL3745_COMMAND_FUNCTION);
+
+ uint8_t sync = driver_sync[index];
+
+ is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_PULLDOWNUP, (IS31FL3745_SW_PULLDOWN << 4) | IS31FL3745_CS_PULLUP);
+ is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3745_GLOBAL_CURRENT);
+ is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_SPREAD_SPECTRUM, (sync & 0b11) << 6);
+ is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_CONFIGURATION, IS31FL3745_CONFIGURATION);
+
+ // Wait 10ms to ensure the device has woken up.
+ wait_ms(10);
+}
+
+void is31fl3745_set_value(int index, uint8_t value) {
+ is31fl3745_led_t led;
+
+ if (index >= 0 && index < IS31FL3745_LED_COUNT) {
+ memcpy_P(&led, (&g_is31fl3745_leds[index]), sizeof(led));
+
+ if (driver_buffers[led.driver].pwm_buffer[led.v] == value) {
+ return;
+ }
+
+ driver_buffers[led.driver].pwm_buffer[led.v] = value;
+ driver_buffers[led.driver].pwm_buffer_dirty = true;
+ }
+}
+
+void is31fl3745_set_value_all(uint8_t value) {
+ for (int i = 0; i < IS31FL3745_LED_COUNT; i++) {
+ is31fl3745_set_value(i, value);
+ }
+}
+
+void is31fl3745_set_scaling_register(uint8_t index, uint8_t value) {
+ is31fl3745_led_t led;
+ memcpy_P(&led, (&g_is31fl3745_leds[index]), sizeof(led));
+
+ driver_buffers[led.driver].scaling_buffer[led.v] = value;
+ driver_buffers[led.driver].scaling_buffer_dirty = true;
+}
+
+void is31fl3745_update_pwm_buffers(uint8_t index) {
+ if (driver_buffers[index].pwm_buffer_dirty) {
+ is31fl3745_select_page(index, IS31FL3745_COMMAND_PWM);
+
+ is31fl3745_write_pwm_buffer(index);
+
+ driver_buffers[index].pwm_buffer_dirty = false;
+ }
+}
+
+void is31fl3745_update_scaling_registers(uint8_t index) {
+ if (driver_buffers[index].scaling_buffer_dirty) {
+ is31fl3745_select_page(index, IS31FL3745_COMMAND_SCALING);
+
+ for (uint8_t i = 0; i < IS31FL3745_SCALING_REGISTER_COUNT; i++) {
+ is31fl3745_write_register(index, i + 1, driver_buffers[index].scaling_buffer[i]);
+ }
+
+ driver_buffers[index].scaling_buffer_dirty = false;
+ }
+}
+
+void is31fl3745_flush(void) {
+ for (uint8_t i = 0; i < IS31FL3745_DRIVER_COUNT; i++) {
+ is31fl3745_update_pwm_buffers(i);
+ }
+}
diff --git a/drivers/led/issi/is31fl3745-mono.h b/drivers/led/issi/is31fl3745-mono.h
new file mode 100644
index 0000000000..29a13004c8
--- /dev/null
+++ b/drivers/led/issi/is31fl3745-mono.h
@@ -0,0 +1,271 @@
+/* Copyright 2017 Jason Williams
+ * Copyright 2018 Jack Humbert
+ * Copyright 2018 Yiancar
+ * Copyright 2020 MelGeek
+ * Copyright 2021 MasterSpoon
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "progmem.h"
+#include "util.h"
+
+#define IS31FL3745_REG_ID 0xFC
+
+#define IS31FL3745_REG_COMMAND 0xFD
+
+#define IS31FL3745_COMMAND_PWM 0x00
+#define IS31FL3745_COMMAND_SCALING 0x01
+#define IS31FL3745_COMMAND_FUNCTION 0x02
+
+#define IS31FL3745_FUNCTION_REG_CONFIGURATION 0x00
+#define IS31FL3745_FUNCTION_REG_GLOBAL_CURRENT 0x01
+#define IS31FL3745_FUNCTION_REG_PULLDOWNUP 0x02
+#define IS31FL3745_FUNCTION_REG_TEMPERATURE 0x24
+#define IS31FL3745_FUNCTION_REG_SPREAD_SPECTRUM 0x25
+#define IS31FL3745_FUNCTION_REG_RESET 0x2F
+
+#define IS31FL3745_REG_COMMAND_WRITE_LOCK 0xFE
+#define IS31FL3745_COMMAND_WRITE_LOCK_MAGIC 0xC5
+
+#define IS31FL3745_I2C_ADDRESS_GND_GND 0x20
+#define IS31FL3745_I2C_ADDRESS_GND_SCL 0x21
+#define IS31FL3745_I2C_ADDRESS_GND_SDA 0x22
+#define IS31FL3745_I2C_ADDRESS_GND_VCC 0x23
+#define IS31FL3745_I2C_ADDRESS_SCL_GND 0x24
+#define IS31FL3745_I2C_ADDRESS_SCL_SCL 0x25
+#define IS31FL3745_I2C_ADDRESS_SCL_SDA 0x26
+#define IS31FL3745_I2C_ADDRESS_SCL_VCC 0x27
+#define IS31FL3745_I2C_ADDRESS_SDA_GND 0x28
+#define IS31FL3745_I2C_ADDRESS_SDA_SCL 0x29
+#define IS31FL3745_I2C_ADDRESS_SDA_SDA 0x2A
+#define IS31FL3745_I2C_ADDRESS_SDA_VCC 0x2B
+#define IS31FL3745_I2C_ADDRESS_VCC_GND 0x2C
+#define IS31FL3745_I2C_ADDRESS_VCC_SCL 0x2D
+#define IS31FL3745_I2C_ADDRESS_VCC_SDA 0x2E
+#define IS31FL3745_I2C_ADDRESS_VCC_VCC 0x2F
+
+#if defined(LED_MATRIX_IS31FL3745)
+# define IS31FL3745_LED_COUNT LED_MATRIX_LED_COUNT
+#endif
+
+#if defined(IS31FL3745_I2C_ADDRESS_4)
+# define IS31FL3745_DRIVER_COUNT 4
+#elif defined(IS31FL3745_I2C_ADDRESS_3)
+# define IS31FL3745_DRIVER_COUNT 3
+#elif defined(IS31FL3745_I2C_ADDRESS_2)
+# define IS31FL3745_DRIVER_COUNT 2
+#elif defined(IS31FL3745_I2C_ADDRESS_1)
+# define IS31FL3745_DRIVER_COUNT 1
+#endif
+
+typedef struct is31fl3745_led_t {
+ uint8_t driver : 2;
+ uint8_t v;
+} PACKED is31fl3745_led_t;
+
+extern const is31fl3745_led_t PROGMEM g_is31fl3745_leds[IS31FL3745_LED_COUNT];
+
+void is31fl3745_init_drivers(void);
+void is31fl3745_init(uint8_t index);
+void is31fl3745_write_register(uint8_t index, uint8_t reg, uint8_t data);
+void is31fl3745_select_page(uint8_t index, uint8_t page);
+
+void is31fl3745_set_value(int index, uint8_t value);
+void is31fl3745_set_value_all(uint8_t value);
+
+void is31fl3745_set_scaling_register(uint8_t index, uint8_t value);
+
+void is31fl3745_update_pwm_buffers(uint8_t index);
+void is31fl3745_update_scaling_registers(uint8_t index);
+
+void is31fl3745_flush(void);
+
+#define IS31FL3745_PDR_0_OHM 0b000 // No pull-down resistor
+#define IS31FL3745_PDR_0K5_OHM_SW_OFF 0b001 // 0.5 kOhm resistor in SWx off time
+#define IS31FL3745_PDR_1K_OHM_SW_OFF 0b010 // 1 kOhm resistor in SWx off time
+#define IS31FL3745_PDR_2K_OHM_SW_OFF 0b011 // 2 kOhm resistor in SWx off time
+#define IS31FL3745_PDR_1K_OHM 0b100 // 1 kOhm resistor
+#define IS31FL3745_PDR_2K_OHM 0b101 // 2 kOhm resistor
+#define IS31FL3745_PDR_4K_OHM 0b110 // 4 kOhm resistor
+#define IS31FL3745_PDR_8K_OHM 0b111 // 8 kOhm resistor
+
+#define IS31FL3745_PUR_0_OHM 0b000 // No pull-up resistor
+#define IS31FL3745_PUR_0K5_OHM_CS_OFF 0b001 // 0.5 kOhm resistor in CSy off time
+#define IS31FL3745_PUR_1K_OHM_CS_OFF 0b010 // 1 kOhm resistor in CSy off time
+#define IS31FL3745_PUR_2K_OHM_CS_OFF 0b011 // 2 kOhm resistor in CSy off time
+#define IS31FL3745_PUR_1K_OHM 0b100 // 1 kOhm resistor
+#define IS31FL3745_PUR_2K_OHM 0b101 // 2 kOhm resistor
+#define IS31FL3745_PUR_4K_OHM 0b110 // 4 kOhm resistor
+#define IS31FL3745_PUR_8K_OHM 0b111 // 8 kOhm resistor
+
+#define IS31FL3745_SYNC_NONE 0b00
+#define IS31FL3745_SYNC_SLAVE 0b10
+#define IS31FL3745_SYNC_MASTER 0b11
+
+#define SW1_CS1 0x00
+#define SW1_CS2 0x01
+#define SW1_CS3 0x02
+#define SW1_CS4 0x03
+#define SW1_CS5 0x04
+#define SW1_CS6 0x05
+#define SW1_CS7 0x06
+#define SW1_CS8 0x07
+#define SW1_CS9 0x08
+#define SW1_CS10 0x09
+#define SW1_CS11 0x0A
+#define SW1_CS12 0x0B
+#define SW1_CS13 0x0C
+#define SW1_CS14 0x0D
+#define SW1_CS15 0x0E
+#define SW1_CS16 0x0F
+#define SW1_CS17 0x10
+#define SW1_CS18 0x11
+
+#define SW2_CS1 0x12
+#define SW2_CS2 0x13
+#define SW2_CS3 0x14
+#define SW2_CS4 0x15
+#define SW2_CS5 0x16
+#define SW2_CS6 0x17
+#define SW2_CS7 0x18
+#define SW2_CS8 0x19
+#define SW2_CS9 0x1A
+#define SW2_CS10 0x1B
+#define SW2_CS11 0x1C
+#define SW2_CS12 0x1D
+#define SW2_CS13 0x1E
+#define SW2_CS14 0x1F
+#define SW2_CS15 0x20
+#define SW2_CS16 0x21
+#define SW2_CS17 0x22
+#define SW2_CS18 0x23
+
+#define SW3_CS1 0x24
+#define SW3_CS2 0x25
+#define SW3_CS3 0x26
+#define SW3_CS4 0x27
+#define SW3_CS5 0x28
+#define SW3_CS6 0x29
+#define SW3_CS7 0x2A
+#define SW3_CS8 0x2B
+#define SW3_CS9 0x2C
+#define SW3_CS10 0x2D
+#define SW3_CS11 0x2E
+#define SW3_CS12 0x2F
+#define SW3_CS13 0x30
+#define SW3_CS14 0x31
+#define SW3_CS15 0x32
+#define SW3_CS16 0x33
+#define SW3_CS17 0x34
+#define SW3_CS18 0x35
+
+#define SW4_CS1 0x36
+#define SW4_CS2 0x37
+#define SW4_CS3 0x38
+#define SW4_CS4 0x39
+#define SW4_CS5 0x3A
+#define SW4_CS6 0x3B
+#define SW4_CS7 0x3C
+#define SW4_CS8 0x3D
+#define SW4_CS9 0x3E
+#define SW4_CS10 0x3F
+#define SW4_CS11 0x40
+#define SW4_CS12 0x41
+#define SW4_CS13 0x42
+#define SW4_CS14 0x43
+#define SW4_CS15 0x44
+#define SW4_CS16 0x45
+#define SW4_CS17 0x46
+#define SW4_CS18 0x47
+
+#define SW5_CS1 0x48
+#define SW5_CS2 0x49
+#define SW5_CS3 0x4A
+#define SW5_CS4 0x4B
+#define SW5_CS5 0x4C
+#define SW5_CS6 0x4D
+#define SW5_CS7 0x4E
+#define SW5_CS8 0x4F
+#define SW5_CS9 0x50
+#define SW5_CS10 0x51
+#define SW5_CS11 0x52
+#define SW5_CS12 0x53
+#define SW5_CS13 0x54
+#define SW5_CS14 0x55
+#define SW5_CS15 0x56
+#define SW5_CS16 0x57
+#define SW5_CS17 0x58
+#define SW5_CS18 0x59
+
+#define SW6_CS1 0x5A
+#define SW6_CS2 0x5B
+#define SW6_CS3 0x5C
+#define SW6_CS4 0x5D
+#define SW6_CS5 0x5E
+#define SW6_CS6 0x5F
+#define SW6_CS7 0x60
+#define SW6_CS8 0x61
+#define SW6_CS9 0x62
+#define SW6_CS10 0x63
+#define SW6_CS11 0x64
+#define SW6_CS12 0x65
+#define SW6_CS13 0x66
+#define SW6_CS14 0x67
+#define SW6_CS15 0x68
+#define SW6_CS16 0x69
+#define SW6_CS17 0x6A
+#define SW6_CS18 0x6B
+
+#define SW7_CS1 0x6C
+#define SW7_CS2 0x6D
+#define SW7_CS3 0x6E
+#define SW7_CS4 0x6F
+#define SW7_CS5 0x70
+#define SW7_CS6 0x71
+#define SW7_CS7 0x72
+#define SW7_CS8 0x73
+#define SW7_CS9 0x74
+#define SW7_CS10 0x75
+#define SW7_CS11 0x76
+#define SW7_CS12 0x77
+#define SW7_CS13 0x78
+#define SW7_CS14 0x79
+#define SW7_CS15 0x7A
+#define SW7_CS16 0x7B
+#define SW7_CS17 0x7C
+#define SW7_CS18 0x7D
+
+#define SW8_CS1 0x7E
+#define SW8_CS2 0x7F
+#define SW8_CS3 0x80
+#define SW8_CS4 0x81
+#define SW8_CS5 0x82
+#define SW8_CS6 0x83
+#define SW8_CS7 0x84
+#define SW8_CS8 0x85
+#define SW8_CS9 0x86
+#define SW8_CS10 0x87
+#define SW8_CS11 0x88
+#define SW8_CS12 0x89
+#define SW8_CS13 0x8A
+#define SW8_CS14 0x8B
+#define SW8_CS15 0x8C
+#define SW8_CS16 0x8D
+#define SW8_CS17 0x8E
+#define SW8_CS18 0x8F
diff --git a/drivers/led/issi/is31fl3745.c b/drivers/led/issi/is31fl3745.c
new file mode 100644
index 0000000000..1e3b437e02
--- /dev/null
+++ b/drivers/led/issi/is31fl3745.c
@@ -0,0 +1,249 @@
+/* Copyright 2017 Jason Williams
+ * Copyright 2018 Jack Humbert
+ * Copyright 2018 Yiancar
+ * Copyright 2020 MelGeek
+ * Copyright 2021 MasterSpoon
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "is31fl3745.h"
+#include "i2c_master.h"
+#include "gpio.h"
+#include "wait.h"
+
+#define IS31FL3745_PWM_REGISTER_COUNT 144
+#define IS31FL3745_SCALING_REGISTER_COUNT 144
+
+#ifndef IS31FL3745_I2C_TIMEOUT
+# define IS31FL3745_I2C_TIMEOUT 100
+#endif
+
+#ifndef IS31FL3745_I2C_PERSISTENCE
+# define IS31FL3745_I2C_PERSISTENCE 0
+#endif
+
+#ifndef IS31FL3745_CONFIGURATION
+# define IS31FL3745_CONFIGURATION 0x31
+#endif
+
+#ifndef IS31FL3745_SW_PULLDOWN
+# define IS31FL3745_SW_PULLDOWN IS31FL3745_PDR_2K_OHM_SW_OFF
+#endif
+
+#ifndef IS31FL3745_CS_PULLUP
+# define IS31FL3745_CS_PULLUP IS31FL3745_PUR_2K_OHM_CS_OFF
+#endif
+
+#ifndef IS31FL3745_GLOBAL_CURRENT
+# define IS31FL3745_GLOBAL_CURRENT 0xFF
+#endif
+
+#ifndef IS31FL3745_SYNC_1
+# define IS31FL3745_SYNC_1 IS31FL3745_SYNC_NONE
+#endif
+#ifndef IS31FL3745_SYNC_2
+# define IS31FL3745_SYNC_2 IS31FL3745_SYNC_NONE
+#endif
+#ifndef IS31FL3745_SYNC_3
+# define IS31FL3745_SYNC_3 IS31FL3745_SYNC_NONE
+#endif
+#ifndef IS31FL3745_SYNC_4
+# define IS31FL3745_SYNC_4 IS31FL3745_SYNC_NONE
+#endif
+
+const uint8_t i2c_addresses[IS31FL3745_DRIVER_COUNT] = {
+ IS31FL3745_I2C_ADDRESS_1,
+#ifdef IS31FL3745_I2C_ADDRESS_2
+ IS31FL3745_I2C_ADDRESS_2,
+# ifdef IS31FL3745_I2C_ADDRESS_3
+ IS31FL3745_I2C_ADDRESS_3,
+# ifdef IS31FL3745_I2C_ADDRESS_4
+ IS31FL3745_I2C_ADDRESS_4,
+# endif
+# endif
+#endif
+};
+
+const uint8_t driver_sync[IS31FL3745_DRIVER_COUNT] = {
+ IS31FL3745_SYNC_1,
+#ifdef IS31FL3745_I2C_ADDRESS_2
+ IS31FL3745_SYNC_2,
+# ifdef IS31FL3745_I2C_ADDRESS_3
+ IS31FL3745_SYNC_3,
+# ifdef IS31FL3745_I2C_ADDRESS_4
+ IS31FL3745_SYNC_4,
+# endif
+# endif
+#endif
+};
+
+typedef struct is31fl3745_driver_t {
+ uint8_t pwm_buffer[IS31FL3745_PWM_REGISTER_COUNT];
+ bool pwm_buffer_dirty;
+ uint8_t scaling_buffer[IS31FL3745_SCALING_REGISTER_COUNT];
+ bool scaling_buffer_dirty;
+} PACKED is31fl3745_driver_t;
+
+is31fl3745_driver_t driver_buffers[IS31FL3745_DRIVER_COUNT] = {{
+ .pwm_buffer = {0},
+ .pwm_buffer_dirty = false,
+ .scaling_buffer = {0},
+ .scaling_buffer_dirty = false,
+}};
+
+void is31fl3745_write_register(uint8_t index, uint8_t reg, uint8_t data) {
+#if IS31FL3745_I2C_PERSISTENCE > 0
+ for (uint8_t i = 0; i < IS31FL3745_I2C_PERSISTENCE; i++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3745_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3745_I2C_TIMEOUT);
+#endif
+}
+
+void is31fl3745_select_page(uint8_t index, uint8_t page) {
+ is31fl3745_write_register(index, IS31FL3745_REG_COMMAND_WRITE_LOCK, IS31FL3745_COMMAND_WRITE_LOCK_MAGIC);
+ is31fl3745_write_register(index, IS31FL3745_REG_COMMAND, page);
+}
+
+void is31fl3745_write_pwm_buffer(uint8_t index) {
+ // Assumes page 0 is already selected.
+ // Transmit PWM registers in 8 transfers of 18 bytes.
+
+ // Iterate over the pwm_buffer contents at 18 byte intervals.
+ for (uint8_t i = 0; i < IS31FL3745_PWM_REGISTER_COUNT; i += 18) {
+#if IS31FL3745_I2C_PERSISTENCE > 0
+ for (uint8_t j = 0; j < IS31FL3745_I2C_PERSISTENCE; j++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3745_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3745_I2C_TIMEOUT);
+#endif
+ }
+}
+
+void is31fl3745_init_drivers(void) {
+ i2c_init();
+
+#if defined(IS31FL3745_SDB_PIN)
+ gpio_set_pin_output(IS31FL3745_SDB_PIN);
+ gpio_write_pin_high(IS31FL3745_SDB_PIN);
+#endif
+
+ for (uint8_t i = 0; i < IS31FL3745_DRIVER_COUNT; i++) {
+ is31fl3745_init(i);
+ }
+
+ for (int i = 0; i < IS31FL3745_LED_COUNT; i++) {
+ is31fl3745_set_scaling_register(i, 0xFF, 0xFF, 0xFF);
+ }
+
+ for (uint8_t i = 0; i < IS31FL3745_DRIVER_COUNT; i++) {
+ is31fl3745_update_scaling_registers(i);
+ }
+}
+
+void is31fl3745_init(uint8_t index) {
+ // In order to avoid the LEDs being driven with garbage data
+ // in the LED driver's PWM registers, shutdown is enabled last.
+ // Set up the mode and other settings, clear the PWM registers,
+ // then disable software shutdown.
+
+ is31fl3745_select_page(index, IS31FL3745_COMMAND_SCALING);
+
+ // Turn off all LEDs.
+ for (uint8_t i = 0; i < IS31FL3745_SCALING_REGISTER_COUNT; i++) {
+ is31fl3745_write_register(index, i + 1, 0x00);
+ }
+
+ is31fl3745_select_page(index, IS31FL3745_COMMAND_PWM);
+
+ for (uint8_t i = 0; i < IS31FL3745_PWM_REGISTER_COUNT; i++) {
+ is31fl3745_write_register(index, i + 1, 0x00);
+ }
+
+ is31fl3745_select_page(index, IS31FL3745_COMMAND_FUNCTION);
+
+ uint8_t sync = driver_sync[index];
+
+ is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_PULLDOWNUP, (IS31FL3745_SW_PULLDOWN << 4) | IS31FL3745_CS_PULLUP);
+ is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3745_GLOBAL_CURRENT);
+ is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_SPREAD_SPECTRUM, (sync & 0b11) << 6);
+ is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_CONFIGURATION, IS31FL3745_CONFIGURATION);
+
+ // Wait 10ms to ensure the device has woken up.
+ wait_ms(10);
+}
+
+void is31fl3745_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
+ is31fl3745_led_t led;
+
+ if (index >= 0 && index < IS31FL3745_LED_COUNT) {
+ memcpy_P(&led, (&g_is31fl3745_leds[index]), sizeof(led));
+
+ if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) {
+ return;
+ }
+
+ driver_buffers[led.driver].pwm_buffer[led.r] = red;
+ driver_buffers[led.driver].pwm_buffer[led.g] = green;
+ driver_buffers[led.driver].pwm_buffer[led.b] = blue;
+ driver_buffers[led.driver].pwm_buffer_dirty = true;
+ }
+}
+
+void is31fl3745_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
+ for (int i = 0; i < IS31FL3745_LED_COUNT; i++) {
+ is31fl3745_set_color(i, red, green, blue);
+ }
+}
+
+void is31fl3745_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue) {
+ is31fl3745_led_t led;
+ memcpy_P(&led, (&g_is31fl3745_leds[index]), sizeof(led));
+
+ driver_buffers[led.driver].scaling_buffer[led.r] = red;
+ driver_buffers[led.driver].scaling_buffer[led.g] = green;
+ driver_buffers[led.driver].scaling_buffer[led.b] = blue;
+ driver_buffers[led.driver].scaling_buffer_dirty = true;
+}
+
+void is31fl3745_update_pwm_buffers(uint8_t index) {
+ if (driver_buffers[index].pwm_buffer_dirty) {
+ is31fl3745_select_page(index, IS31FL3745_COMMAND_PWM);
+
+ is31fl3745_write_pwm_buffer(index);
+
+ driver_buffers[index].pwm_buffer_dirty = false;
+ }
+}
+
+void is31fl3745_update_scaling_registers(uint8_t index) {
+ if (driver_buffers[index].scaling_buffer_dirty) {
+ is31fl3745_select_page(index, IS31FL3745_COMMAND_SCALING);
+
+ for (uint8_t i = 0; i < IS31FL3745_SCALING_REGISTER_COUNT; i++) {
+ is31fl3745_write_register(index, i + 1, driver_buffers[index].scaling_buffer[i]);
+ }
+
+ driver_buffers[index].scaling_buffer_dirty = false;
+ }
+}
+
+void is31fl3745_flush(void) {
+ for (uint8_t i = 0; i < IS31FL3745_DRIVER_COUNT; i++) {
+ is31fl3745_update_pwm_buffers(i);
+ }
+}
diff --git a/drivers/led/issi/is31fl3745.h b/drivers/led/issi/is31fl3745.h
index 1e88aab4a8..be50dd2cd9 100644
--- a/drivers/led/issi/is31fl3745.h
+++ b/drivers/led/issi/is31fl3745.h
@@ -20,251 +20,254 @@
#pragma once
-// This is a 7-bit address, that gets left-shifted and bit 0
-// set to 0 for write, 1 for read (as per I2C protocol)
-// The address will vary depending on your wiring:
-// 00 <-> GND
-// 01 <-> SCL
-// 10 <-> SDA
-// 11 <-> VCC
-// ADDR1 represents A1:A0 of the 7-bit address.
-// ADDR2 represents A3:A2 of the 7-bit address.
-// The result is: 0b010(ADDR2)(ADDR1)
-#ifndef DRIVER_ADDR_1
-# define DRIVER_ADDR_1 0b0100000
-#endif
+#include <stdint.h>
+#include <stdbool.h>
+#include "progmem.h"
+#include "util.h"
-// Set defaults for Spread Spectrum Register
-#ifndef ISSI_SSR_1
-# ifndef DRIVER_ADDR_2
-# define ISSI_SSR_1 0x00
-# else
-# define ISSI_SSR_1 0xC0
-# endif
-#endif
-#ifndef ISSI_SSR_2
-# define ISSI_SSR_2 0x80
-#endif
-#ifndef ISSI_SSR_3
-# define ISSI_SSR_3 0x80
-#endif
-#ifndef ISSI_SSR_4
-# define ISSI_SSR_4 0x80
-#endif
+#define IS31FL3745_REG_ID 0xFC
-// Command Registers
-#define ISSI_COMMANDREGISTER_WRITELOCK 0xFE
-#define ISSI_COMMANDREGISTER 0xFD
-#define ISSI_IDREGISTER 0xFC
-#define ISSI_REGISTER_UNLOCK 0xC5
+#define IS31FL3745_REG_COMMAND 0xFD
-// Response Registers
-#define ISSI_PAGE_PWM 0x00
-#define ISSI_PAGE_SCALING 0x01
-#define ISSI_PAGE_FUNCTION 0x02
+#define IS31FL3745_COMMAND_PWM 0x00
+#define IS31FL3745_COMMAND_SCALING 0x01
+#define IS31FL3745_COMMAND_FUNCTION 0x02
-// Registers under Function Register
-#define ISSI_REG_CONFIGURATION 0x00
-#define ISSI_REG_GLOBALCURRENT 0x01
-#define ISSI_REG_PULLDOWNUP 0x02
-#define ISSI_REG_TEMP 0x24
-#define ISSI_REG_SSR 0x25
-#define ISSI_REG_RESET 0x2F
+#define IS31FL3745_FUNCTION_REG_CONFIGURATION 0x00
+#define IS31FL3745_FUNCTION_REG_GLOBAL_CURRENT 0x01
+#define IS31FL3745_FUNCTION_REG_PULLDOWNUP 0x02
+#define IS31FL3745_FUNCTION_REG_TEMPERATURE 0x24
+#define IS31FL3745_FUNCTION_REG_SPREAD_SPECTRUM 0x25
+#define IS31FL3745_FUNCTION_REG_RESET 0x2F
-// Set defaults for Function Registers
-#ifndef ISSI_CONFIGURATION
-# define ISSI_CONFIGURATION 0x31
-#endif
-#ifndef ISSI_GLOBALCURRENT
-# define ISSI_GLOBALCURRENT 0xFF
-#endif
-#ifndef ISSI_PULLDOWNUP
-# define ISSI_PULLDOWNUP 0x33
-#endif
-#ifndef ISSI_TEMP
-# define ISSI_TEMP 0x00
-#endif
+#define IS31FL3745_REG_COMMAND_WRITE_LOCK 0xFE
+#define IS31FL3745_COMMAND_WRITE_LOCK_MAGIC 0xC5
-// Set defaults for Scaling registers
-#ifndef ISSI_SCAL_RED
-# define ISSI_SCAL_RED 0xFF
-#endif
-#ifndef ISSI_SCAL_BLUE
-# define ISSI_SCAL_BLUE 0xFF
-#endif
-#ifndef ISSI_SCAL_GREEN
-# define ISSI_SCAL_GREEN 0xFF
+#define IS31FL3745_I2C_ADDRESS_GND_GND 0x20
+#define IS31FL3745_I2C_ADDRESS_GND_SCL 0x21
+#define IS31FL3745_I2C_ADDRESS_GND_SDA 0x22
+#define IS31FL3745_I2C_ADDRESS_GND_VCC 0x23
+#define IS31FL3745_I2C_ADDRESS_SCL_GND 0x24
+#define IS31FL3745_I2C_ADDRESS_SCL_SCL 0x25
+#define IS31FL3745_I2C_ADDRESS_SCL_SDA 0x26
+#define IS31FL3745_I2C_ADDRESS_SCL_VCC 0x27
+#define IS31FL3745_I2C_ADDRESS_SDA_GND 0x28
+#define IS31FL3745_I2C_ADDRESS_SDA_SCL 0x29
+#define IS31FL3745_I2C_ADDRESS_SDA_SDA 0x2A
+#define IS31FL3745_I2C_ADDRESS_SDA_VCC 0x2B
+#define IS31FL3745_I2C_ADDRESS_VCC_GND 0x2C
+#define IS31FL3745_I2C_ADDRESS_VCC_SCL 0x2D
+#define IS31FL3745_I2C_ADDRESS_VCC_SDA 0x2E
+#define IS31FL3745_I2C_ADDRESS_VCC_VCC 0x2F
+
+#if defined(RGB_MATRIX_IS31FL3745)
+# define IS31FL3745_LED_COUNT RGB_MATRIX_LED_COUNT
#endif
-#define ISSI_SCAL_RED_OFF 0x00
-#define ISSI_SCAL_GREEN_OFF 0x00
-#define ISSI_SCAL_BLUE_OFF 0x00
-#ifndef ISSI_SCAL_LED
-# define ISSI_SCAL_LED 0xFF
+#if defined(IS31FL3745_I2C_ADDRESS_4)
+# define IS31FL3745_DRIVER_COUNT 4
+#elif defined(IS31FL3745_I2C_ADDRESS_3)
+# define IS31FL3745_DRIVER_COUNT 3
+#elif defined(IS31FL3745_I2C_ADDRESS_2)
+# define IS31FL3745_DRIVER_COUNT 2
+#elif defined(IS31FL3745_I2C_ADDRESS_1)
+# define IS31FL3745_DRIVER_COUNT 1
#endif
-#define ISSI_SCAL_LED_OFF 0x00
-// Set buffer sizes
-#define ISSI_MAX_LEDS 144
-#define ISSI_SCALING_SIZE 144
-#define ISSI_PWM_TRF_SIZE 18
-#define ISSI_SCALING_TRF_SIZE 18
+typedef struct is31fl3745_led_t {
+ uint8_t driver : 2;
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+} PACKED is31fl3745_led_t;
+
+extern const is31fl3745_led_t PROGMEM g_is31fl3745_leds[IS31FL3745_LED_COUNT];
+
+void is31fl3745_init_drivers(void);
+void is31fl3745_init(uint8_t index);
+void is31fl3745_write_register(uint8_t index, uint8_t reg, uint8_t data);
+void is31fl3745_select_page(uint8_t index, uint8_t page);
+
+void is31fl3745_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
+void is31fl3745_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
+
+void is31fl3745_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue);
+
+void is31fl3745_update_pwm_buffers(uint8_t index);
+void is31fl3745_update_scaling_registers(uint8_t index);
+
+void is31fl3745_flush(void);
+
+#define IS31FL3745_PDR_0_OHM 0b000 // No pull-down resistor
+#define IS31FL3745_PDR_0K5_OHM_SW_OFF 0b001 // 0.5 kOhm resistor in SWx off time
+#define IS31FL3745_PDR_1K_OHM_SW_OFF 0b010 // 1 kOhm resistor in SWx off time
+#define IS31FL3745_PDR_2K_OHM_SW_OFF 0b011 // 2 kOhm resistor in SWx off time
+#define IS31FL3745_PDR_1K_OHM 0b100 // 1 kOhm resistor
+#define IS31FL3745_PDR_2K_OHM 0b101 // 2 kOhm resistor
+#define IS31FL3745_PDR_4K_OHM 0b110 // 4 kOhm resistor
+#define IS31FL3745_PDR_8K_OHM 0b111 // 8 kOhm resistor
+
+#define IS31FL3745_PUR_0_OHM 0b000 // No pull-up resistor
+#define IS31FL3745_PUR_0K5_OHM_CS_OFF 0b001 // 0.5 kOhm resistor in CSy off time
+#define IS31FL3745_PUR_1K_OHM_CS_OFF 0b010 // 1 kOhm resistor in CSy off time
+#define IS31FL3745_PUR_2K_OHM_CS_OFF 0b011 // 2 kOhm resistor in CSy off time
+#define IS31FL3745_PUR_1K_OHM 0b100 // 1 kOhm resistor
+#define IS31FL3745_PUR_2K_OHM 0b101 // 2 kOhm resistor
+#define IS31FL3745_PUR_4K_OHM 0b110 // 4 kOhm resistor
+#define IS31FL3745_PUR_8K_OHM 0b111 // 8 kOhm resistor
-// Location of 1st bit for PWM and Scaling registers
-#define ISSI_PWM_REG_1ST 0x01
-#define ISSI_SCL_REG_1ST 0x01
+#define IS31FL3745_SYNC_NONE 0b00
+#define IS31FL3745_SYNC_SLAVE 0b10
+#define IS31FL3745_SYNC_MASTER 0b11
-// Map CS SW locations to order in PWM / Scaling buffers
-// This matches the ORDER in the Datasheet Register not the POSITION
-// It will always count from 0x00 to (ISSI_MAX_LEDS - 1)
-#define CS1_SW1 0x00
-#define CS2_SW1 0x01
-#define CS3_SW1 0x02
-#define CS4_SW1 0x03
-#define CS5_SW1 0x04
-#define CS6_SW1 0x05
-#define CS7_SW1 0x06
-#define CS8_SW1 0x07
-#define CS9_SW1 0x08
-#define CS10_SW1 0x09
-#define CS11_SW1 0x0A
-#define CS12_SW1 0x0B
-#define CS13_SW1 0x0C
-#define CS14_SW1 0x0D
-#define CS15_SW1 0x0E
-#define CS16_SW1 0x0F
-#define CS17_SW1 0x10
-#define CS18_SW1 0x11
+#define SW1_CS1 0x00
+#define SW1_CS2 0x01
+#define SW1_CS3 0x02
+#define SW1_CS4 0x03
+#define SW1_CS5 0x04
+#define SW1_CS6 0x05
+#define SW1_CS7 0x06
+#define SW1_CS8 0x07
+#define SW1_CS9 0x08
+#define SW1_CS10 0x09
+#define SW1_CS11 0x0A
+#define SW1_CS12 0x0B
+#define SW1_CS13 0x0C
+#define SW1_CS14 0x0D
+#define SW1_CS15 0x0E
+#define SW1_CS16 0x0F
+#define SW1_CS17 0x10
+#define SW1_CS18 0x11
-#define CS1_SW2 0x12
-#define CS2_SW2 0x13
-#define CS3_SW2 0x14
-#define CS4_SW2 0x15
-#define CS5_SW2 0x16
-#define CS6_SW2 0x17
-#define CS7_SW2 0x18
-#define CS8_SW2 0x19
-#define CS9_SW2 0x1A
-#define CS10_SW2 0x1B
-#define CS11_SW2 0x1C
-#define CS12_SW2 0x1D
-#define CS13_SW2 0x1E
-#define CS14_SW2 0x1F
-#define CS15_SW2 0x20
-#define CS16_SW2 0x21
-#define CS17_SW2 0x22
-#define CS18_SW2 0x23
+#define SW2_CS1 0x12
+#define SW2_CS2 0x13
+#define SW2_CS3 0x14
+#define SW2_CS4 0x15
+#define SW2_CS5 0x16
+#define SW2_CS6 0x17
+#define SW2_CS7 0x18
+#define SW2_CS8 0x19
+#define SW2_CS9 0x1A
+#define SW2_CS10 0x1B
+#define SW2_CS11 0x1C
+#define SW2_CS12 0x1D
+#define SW2_CS13 0x1E
+#define SW2_CS14 0x1F
+#define SW2_CS15 0x20
+#define SW2_CS16 0x21
+#define SW2_CS17 0x22
+#define SW2_CS18 0x23
-#define CS1_SW3 0x24
-#define CS2_SW3 0x25
-#define CS3_SW3 0x26
-#define CS4_SW3 0x27
-#define CS5_SW3 0x28
-#define CS6_SW3 0x29
-#define CS7_SW3 0x2A
-#define CS8_SW3 0x2B
-#define CS9_SW3 0x2C
-#define CS10_SW3 0x2D
-#define CS11_SW3 0x2E
-#define CS12_SW3 0x2F
-#define CS13_SW3 0x30
-#define CS14_SW3 0x31
-#define CS15_SW3 0x32
-#define CS16_SW3 0x33
-#define CS17_SW3 0x34
-#define CS18_SW3 0x35
+#define SW3_CS1 0x24
+#define SW3_CS2 0x25
+#define SW3_CS3 0x26
+#define SW3_CS4 0x27
+#define SW3_CS5 0x28
+#define SW3_CS6 0x29
+#define SW3_CS7 0x2A
+#define SW3_CS8 0x2B
+#define SW3_CS9 0x2C
+#define SW3_CS10 0x2D
+#define SW3_CS11 0x2E
+#define SW3_CS12 0x2F
+#define SW3_CS13 0x30
+#define SW3_CS14 0x31
+#define SW3_CS15 0x32
+#define SW3_CS16 0x33
+#define SW3_CS17 0x34
+#define SW3_CS18 0x35
-#define CS1_SW4 0x36
-#define CS2_SW4 0x37
-#define CS3_SW4 0x38
-#define CS4_SW4 0x39
-#define CS5_SW4 0x3A
-#define CS6_SW4 0x3B
-#define CS7_SW4 0x3C
-#define CS8_SW4 0x3D
-#define CS9_SW4 0x3E
-#define CS10_SW4 0x3F
-#define CS11_SW4 0x40
-#define CS12_SW4 0x41
-#define CS13_SW4 0x42
-#define CS14_SW4 0x43
-#define CS15_SW4 0x44
-#define CS16_SW4 0x45
-#define CS17_SW4 0x46
-#define CS18_SW4 0x47
+#define SW4_CS1 0x36
+#define SW4_CS2 0x37
+#define SW4_CS3 0x38
+#define SW4_CS4 0x39
+#define SW4_CS5 0x3A
+#define SW4_CS6 0x3B
+#define SW4_CS7 0x3C
+#define SW4_CS8 0x3D
+#define SW4_CS9 0x3E
+#define SW4_CS10 0x3F
+#define SW4_CS11 0x40
+#define SW4_CS12 0x41
+#define SW4_CS13 0x42
+#define SW4_CS14 0x43
+#define SW4_CS15 0x44
+#define SW4_CS16 0x45
+#define SW4_CS17 0x46
+#define SW4_CS18 0x47
-#define CS1_SW5 0x48
-#define CS2_SW5 0x49
-#define CS3_SW5 0x4A
-#define CS4_SW5 0x4B
-#define CS5_SW5 0x4C
-#define CS6_SW5 0x4D
-#define CS7_SW5 0x4E
-#define CS8_SW5 0x4F
-#define CS9_SW5 0x50
-#define CS10_SW5 0x51
-#define CS11_SW5 0x52
-#define CS12_SW5 0x53
-#define CS13_SW5 0x54
-#define CS14_SW5 0x55
-#define CS15_SW5 0x56
-#define CS16_SW5 0x57
-#define CS17_SW5 0x58
-#define CS18_SW5 0x59
+#define SW5_CS1 0x48
+#define SW5_CS2 0x49
+#define SW5_CS3 0x4A
+#define SW5_CS4 0x4B
+#define SW5_CS5 0x4C
+#define SW5_CS6 0x4D
+#define SW5_CS7 0x4E
+#define SW5_CS8 0x4F
+#define SW5_CS9 0x50
+#define SW5_CS10 0x51
+#define SW5_CS11 0x52
+#define SW5_CS12 0x53
+#define SW5_CS13 0x54
+#define SW5_CS14 0x55
+#define SW5_CS15 0x56
+#define SW5_CS16 0x57
+#define SW5_CS17 0x58
+#define SW5_CS18 0x59
-#define CS1_SW6 0x5A
-#define CS2_SW6 0x5B
-#define CS3_SW6 0x5C
-#define CS4_SW6 0x5D
-#define CS5_SW6 0x5E
-#define CS6_SW6 0x5F
-#define CS7_SW6 0x60
-#define CS8_SW6 0x61
-#define CS9_SW6 0x62
-#define CS10_SW6 0x63
-#define CS11_SW6 0x64
-#define CS12_SW6 0x65
-#define CS13_SW6 0x66
-#define CS14_SW6 0x67
-#define CS15_SW6 0x68
-#define CS16_SW6 0x69
-#define CS17_SW6 0x6A
-#define CS18_SW6 0x6B
+#define SW6_CS1 0x5A
+#define SW6_CS2 0x5B
+#define SW6_CS3 0x5C
+#define SW6_CS4 0x5D
+#define SW6_CS5 0x5E
+#define SW6_CS6 0x5F
+#define SW6_CS7 0x60
+#define SW6_CS8 0x61
+#define SW6_CS9 0x62
+#define SW6_CS10 0x63
+#define SW6_CS11 0x64
+#define SW6_CS12 0x65
+#define SW6_CS13 0x66
+#define SW6_CS14 0x67
+#define SW6_CS15 0x68
+#define SW6_CS16 0x69
+#define SW6_CS17 0x6A
+#define SW6_CS18 0x6B
-#define CS1_SW7 0x6C
-#define CS2_SW7 0x6D
-#define CS3_SW7 0x6E
-#define CS4_SW7 0x6F
-#define CS5_SW7 0x70
-#define CS6_SW7 0x71
-#define CS7_SW7 0x72
-#define CS8_SW7 0x73
-#define CS9_SW7 0x74
-#define CS10_SW7 0x75
-#define CS11_SW7 0x76
-#define CS12_SW7 0x77
-#define CS13_SW7 0x78
-#define CS14_SW7 0x79
-#define CS15_SW7 0x7A
-#define CS16_SW7 0x7B
-#define CS17_SW7 0x7C
-#define CS18_SW7 0x7D
+#define SW7_CS1 0x6C
+#define SW7_CS2 0x6D
+#define SW7_CS3 0x6E
+#define SW7_CS4 0x6F
+#define SW7_CS5 0x70
+#define SW7_CS6 0x71
+#define SW7_CS7 0x72
+#define SW7_CS8 0x73
+#define SW7_CS9 0x74
+#define SW7_CS10 0x75
+#define SW7_CS11 0x76
+#define SW7_CS12 0x77
+#define SW7_CS13 0x78
+#define SW7_CS14 0x79
+#define SW7_CS15 0x7A
+#define SW7_CS16 0x7B
+#define SW7_CS17 0x7C
+#define SW7_CS18 0x7D
-#define CS1_SW8 0x7E
-#define CS2_SW8 0x7F
-#define CS3_SW8 0x80
-#define CS4_SW8 0x81
-#define CS5_SW8 0x82
-#define CS6_SW8 0x83
-#define CS7_SW8 0x84
-#define CS8_SW8 0x85
-#define CS9_SW8 0x86
-#define CS10_SW8 0x87
-#define CS11_SW8 0x88
-#define CS12_SW8 0x89
-#define CS13_SW8 0x8A
-#define CS14_SW8 0x8B
-#define CS15_SW8 0x8C
-#define CS16_SW8 0x8D
-#define CS17_SW8 0x8E
-#define CS18_SW8 0x8F
+#define SW8_CS1 0x7E
+#define SW8_CS2 0x7F
+#define SW8_CS3 0x80
+#define SW8_CS4 0x81
+#define SW8_CS5 0x82
+#define SW8_CS6 0x83
+#define SW8_CS7 0x84
+#define SW8_CS8 0x85
+#define SW8_CS9 0x86
+#define SW8_CS10 0x87
+#define SW8_CS11 0x88
+#define SW8_CS12 0x89
+#define SW8_CS13 0x8A
+#define SW8_CS14 0x8B
+#define SW8_CS15 0x8C
+#define SW8_CS16 0x8D
+#define SW8_CS17 0x8E
+#define SW8_CS18 0x8F
diff --git a/drivers/led/issi/is31fl3746.h b/drivers/led/issi/is31fl3746.h
deleted file mode 100644
index f89f281533..0000000000
--- a/drivers/led/issi/is31fl3746.h
+++ /dev/null
@@ -1,198 +0,0 @@
-/* Copyright 2017 Jason Williams
- * Copyright 2018 Jack Humbert
- * Copyright 2018 Yiancar
- * Copyright 2020 MelGeek
- * Copyright 2021 MasterSpoon
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-// This is a 7-bit address, that gets left-shifted and bit 0
-// set to 0 for write, 1 for read (as per I2C protocol)
-// The address will vary depending on your wiring:
-// 00 <-> GND
-// 01 <-> SCL
-// 10 <-> SDA
-// 11 <-> VCC
-// ADDR1 represents A1:A0 of the 7-bit address.
-// ADDR2 represents A3:A2 of the 7-bit address.
-// The result is: 0b110(ADDR2)(ADDR1)
-#ifndef DRIVER_ADDR_1
-# define DRIVER_ADDR_1 0b1100000
-#endif
-
-// Set defaults for Spread Spectrum Register
-#ifndef ISSI_SSR_1
-# define ISSI_SSR_1 0x00
-#endif
-#ifndef ISSI_SSR_2
-# define ISSI_SSR_2 0x00
-#endif
-#ifndef ISSI_SSR_3
-# define ISSI_SSR_3 0x00
-#endif
-#ifndef ISSI_SSR_4
-# define ISSI_SSR_4 0x00
-#endif
-
-// Command Registers
-#define ISSI_COMMANDREGISTER_WRITELOCK 0xFE
-#define ISSI_COMMANDREGISTER 0xFD
-#define ISSI_IDREGISTER 0xFC
-#define ISSI_REGISTER_UNLOCK 0xC5
-
-// Response Registers
-#define ISSI_PAGE_PWM 0x00
-#define ISSI_PAGE_SCALING 0x01
-#define ISSI_PAGE_FUNCTION 0x01
-
-// Registers under Function Register
-#define ISSI_REG_CONFIGURATION 0x50
-#define ISSI_REG_GLOBALCURRENT 0x51
-#define ISSI_REG_PULLDOWNUP 0x52
-#define ISSI_REG_TEMP 0x5F
-#define ISSI_REG_SSR 0x60
-#define ISSI_REG_RESET 0x8F
-#define ISSI_REG_PWM_ENABLE 0xE0
-#define ISSI_REG_PWM_SET 0xE2
-
-// Set defaults for Function Registers
-#ifndef ISSI_CONFIGURATION
-# define ISSI_CONFIGURATION 0x01
-#endif
-#ifndef ISSI_GLOBALCURRENT
-# define ISSI_GLOBALCURRENT 0xFF
-#endif
-#ifndef ISSI_PULLDOWNUP
-# define ISSI_PULLDOWNUP 0x33
-#endif
-#ifndef ISSI_TEMP
-# define ISSI_TEMP 0x00
-#endif
-#ifndef ISSI_PWM_ENABLE
-# define ISSI_PWM_ENABLE 0x00
-#endif
-#ifndef ISSI_PWM_SET
-# define ISSI_PWM_SET 0x00
-#endif
-
-// Set defaults for Scaling registers
-#ifndef ISSI_SCAL_RED
-# define ISSI_SCAL_RED 0xFF
-#endif
-#ifndef ISSI_SCAL_BLUE
-# define ISSI_SCAL_BLUE 0xFF
-#endif
-#ifndef ISSI_SCAL_GREEN
-# define ISSI_SCAL_GREEN 0xFF
-#endif
-#define ISSI_SCAL_RED_OFF 0x00
-#define ISSI_SCAL_GREEN_OFF 0x00
-#define ISSI_SCAL_BLUE_OFF 0x00
-
-#ifndef ISSI_SCAL_LED
-# define ISSI_SCAL_LED 0xFF
-#endif
-#define ISSI_SCAL_LED_OFF 0x00
-
-// Set buffer sizes
-#define ISSI_MAX_LEDS 72
-#define ISSI_SCALING_SIZE 72
-#define ISSI_PWM_TRF_SIZE 18
-#define ISSI_SCALING_TRF_SIZE 18
-
-// Location of 1st bit for PWM and Scaling registers
-#define ISSI_PWM_REG_1ST 0x01
-#define ISSI_SCL_REG_1ST 0x01
-
-// Map CS SW locations to order in PWM / Scaling buffers
-// This matches the ORDER in the Datasheet Register not the POSITION
-// It will always count from 0x00 to (ISSI_MAX_LEDS - 1)
-#define CS1_SW1 0x00
-#define CS2_SW1 0x01
-#define CS3_SW1 0x02
-#define CS4_SW1 0x03
-#define CS5_SW1 0x04
-#define CS6_SW1 0x05
-#define CS7_SW1 0x06
-#define CS8_SW1 0x07
-#define CS9_SW1 0x08
-#define CS10_SW1 0x09
-#define CS11_SW1 0x0A
-#define CS12_SW1 0x0B
-#define CS13_SW1 0x0C
-#define CS14_SW1 0x0D
-#define CS15_SW1 0x0E
-#define CS16_SW1 0x0F
-#define CS17_SW1 0x10
-#define CS18_SW1 0x11
-
-#define CS1_SW2 0x12
-#define CS2_SW2 0x13
-#define CS3_SW2 0x14
-#define CS4_SW2 0x15
-#define CS5_SW2 0x16
-#define CS6_SW2 0x17
-#define CS7_SW2 0x18
-#define CS8_SW2 0x19
-#define CS9_SW2 0x1A
-#define CS10_SW2 0x1B
-#define CS11_SW2 0x1C
-#define CS12_SW2 0x1D
-#define CS13_SW2 0x1E
-#define CS14_SW2 0x1F
-#define CS15_SW2 0x20
-#define CS16_SW2 0x21
-#define CS17_SW2 0x22
-#define CS18_SW2 0x23
-
-#define CS1_SW3 0x24
-#define CS2_SW3 0x25
-#define CS3_SW3 0x26
-#define CS4_SW3 0x27
-#define CS5_SW3 0x28
-#define CS6_SW3 0x29
-#define CS7_SW3 0x2A
-#define CS8_SW3 0x2B
-#define CS9_SW3 0x2C
-#define CS10_SW3 0x2D
-#define CS11_SW3 0x2E
-#define CS12_SW3 0x2F
-#define CS13_SW3 0x30
-#define CS14_SW3 0x31
-#define CS15_SW3 0x32
-#define CS16_SW3 0x33
-#define CS17_SW3 0x34
-#define CS18_SW3 0x35
-
-#define CS1_SW4 0x36
-#define CS2_SW4 0x37
-#define CS3_SW4 0x38
-#define CS4_SW4 0x39
-#define CS5_SW4 0x3A
-#define CS6_SW4 0x3B
-#define CS7_SW4 0x3C
-#define CS8_SW4 0x3D
-#define CS9_SW4 0x3E
-#define CS10_SW4 0x3F
-#define CS11_SW4 0x40
-#define CS12_SW4 0x41
-#define CS13_SW4 0x42
-#define CS14_SW4 0x43
-#define CS15_SW4 0x44
-#define CS16_SW4 0x45
-#define CS17_SW4 0x46
-#define CS18_SW4 0x47
diff --git a/drivers/led/issi/is31fl3746a-mono.c b/drivers/led/issi/is31fl3746a-mono.c
new file mode 100644
index 0000000000..6bff10723f
--- /dev/null
+++ b/drivers/led/issi/is31fl3746a-mono.c
@@ -0,0 +1,222 @@
+/* Copyright 2017 Jason Williams
+ * Copyright 2018 Jack Humbert
+ * Copyright 2018 Yiancar
+ * Copyright 2020 MelGeek
+ * Copyright 2021 MasterSpoon
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "is31fl3746a-mono.h"
+#include "i2c_master.h"
+#include "gpio.h"
+#include "wait.h"
+
+#define IS31FL3746A_PWM_REGISTER_COUNT 72
+#define IS31FL3746A_SCALING_REGISTER_COUNT 72
+
+#ifndef IS31FL3746A_I2C_TIMEOUT
+# define IS31FL3746A_I2C_TIMEOUT 100
+#endif
+
+#ifndef IS31FL3746A_I2C_PERSISTENCE
+# define IS31FL3746A_I2C_PERSISTENCE 0
+#endif
+
+#ifndef IS31FL3746A_CONFIGURATION
+# define IS31FL3746A_CONFIGURATION 0x01
+#endif
+
+#ifndef IS31FL3746A_PWM_FREQUENCY
+# define IS31FL3746A_PWM_FREQUENCY IS31FL3746A_PWM_FREQUENCY_29K_HZ
+#endif
+
+#ifndef IS31FL3746A_SW_PULLDOWN
+# define IS31FL3746A_SW_PULLDOWN IS31FL3746A_PDR_2K_OHM_SW_OFF
+#endif
+
+#ifndef IS31FL3746A_CS_PULLUP
+# define IS31FL3746A_CS_PULLUP IS31FL3746A_PUR_2K_OHM_CS_OFF
+#endif
+
+#ifndef IS31FL3746A_GLOBAL_CURRENT
+# define IS31FL3746A_GLOBAL_CURRENT 0xFF
+#endif
+
+const uint8_t i2c_addresses[IS31FL3746A_DRIVER_COUNT] = {
+ IS31FL3746A_I2C_ADDRESS_1,
+#ifdef IS31FL3746A_I2C_ADDRESS_2
+ IS31FL3746A_I2C_ADDRESS_2,
+# ifdef IS31FL3746A_I2C_ADDRESS_3
+ IS31FL3746A_I2C_ADDRESS_3,
+# ifdef IS31FL3746A_I2C_ADDRESS_4
+ IS31FL3746A_I2C_ADDRESS_4,
+# endif
+# endif
+#endif
+};
+
+typedef struct is31fl3746a_driver_t {
+ uint8_t pwm_buffer[IS31FL3746A_PWM_REGISTER_COUNT];
+ bool pwm_buffer_dirty;
+ uint8_t scaling_buffer[IS31FL3746A_SCALING_REGISTER_COUNT];
+ bool scaling_buffer_dirty;
+} PACKED is31fl3746a_driver_t;
+
+is31fl3746a_driver_t driver_buffers[IS31FL3746A_DRIVER_COUNT] = {{
+ .pwm_buffer = {0},
+ .pwm_buffer_dirty = false,
+ .scaling_buffer = {0},
+ .scaling_buffer_dirty = false,
+}};
+
+void is31fl3746a_write_register(uint8_t index, uint8_t reg, uint8_t data) {
+#if IS31FL3746A_I2C_PERSISTENCE > 0
+ for (uint8_t i = 0; i < IS31FL3746A_I2C_PERSISTENCE; i++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3746A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3746A_I2C_TIMEOUT);
+#endif
+}
+
+void is31fl3746a_select_page(uint8_t index, uint8_t page) {
+ is31fl3746a_write_register(index, IS31FL3746A_REG_COMMAND_WRITE_LOCK, IS31FL3746A_COMMAND_WRITE_LOCK_MAGIC);
+ is31fl3746a_write_register(index, IS31FL3746A_REG_COMMAND, page);
+}
+
+void is31fl3746a_write_pwm_buffer(uint8_t index) {
+ // Assumes page 0 is already selected.
+ // Transmit PWM registers in 4 transfers of 18 bytes.
+
+ // Iterate over the pwm_buffer contents at 18 byte intervals.
+ for (uint8_t i = 0; i < IS31FL3746A_PWM_REGISTER_COUNT; i += 18) {
+#if IS31FL3746A_I2C_PERSISTENCE > 0
+ for (uint8_t j = 0; j < IS31FL3746A_I2C_PERSISTENCE; j++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3746A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3746A_I2C_TIMEOUT);
+#endif
+ }
+}
+
+void is31fl3746a_init_drivers(void) {
+ i2c_init();
+
+#if defined(IS31FL3746A_SDB_PIN)
+ gpio_set_pin_output(IS31FL3746A_SDB_PIN);
+ gpio_write_pin_high(IS31FL3746A_SDB_PIN);
+#endif
+
+ for (uint8_t i = 0; i < IS31FL3746A_DRIVER_COUNT; i++) {
+ is31fl3746a_init(i);
+ }
+
+ for (int i = 0; i < IS31FL3746A_LED_COUNT; i++) {
+ is31fl3746a_set_scaling_register(i, 0xFF);
+ }
+
+ for (uint8_t i = 0; i < IS31FL3746A_DRIVER_COUNT; i++) {
+ is31fl3746a_update_scaling_registers(i);
+ }
+}
+
+void is31fl3746a_init(uint8_t index) {
+ // In order to avoid the LEDs being driven with garbage data
+ // in the LED driver's PWM registers, shutdown is enabled last.
+ // Set up the mode and other settings, clear the PWM registers,
+ // then disable software shutdown.
+
+ is31fl3746a_select_page(index, IS31FL3746A_COMMAND_SCALING);
+
+ // Turn off all LEDs.
+ for (uint8_t i = 0; i < IS31FL3746A_SCALING_REGISTER_COUNT; i++) {
+ is31fl3746a_write_register(index, i + 1, 0x00);
+ }
+
+ is31fl3746a_select_page(index, IS31FL3746A_COMMAND_PWM);
+
+ for (uint8_t i = 0; i < IS31FL3746A_PWM_REGISTER_COUNT; i++) {
+ is31fl3746a_write_register(index, i + 1, 0x00);
+ }
+
+ is31fl3746a_select_page(index, IS31FL3746A_COMMAND_FUNCTION);
+
+ is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_PULLDOWNUP, (IS31FL3746A_SW_PULLDOWN << 4) | IS31FL3746A_CS_PULLUP);
+ is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3746A_GLOBAL_CURRENT);
+ is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_PWM_ENABLE, 0x01);
+ is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_PWM_FREQUENCY, IS31FL3746A_PWM_FREQUENCY);
+ is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_CONFIGURATION, IS31FL3746A_CONFIGURATION);
+
+ // Wait 10ms to ensure the device has woken up.
+ wait_ms(10);
+}
+
+void is31fl3746a_set_value(int index, uint8_t value) {
+ is31fl3746a_led_t led;
+
+ if (index >= 0 && index < IS31FL3746A_LED_COUNT) {
+ memcpy_P(&led, (&g_is31fl3746a_leds[index]), sizeof(led));
+
+ if (driver_buffers[led.driver].pwm_buffer[led.v] == value) {
+ return;
+ }
+
+ driver_buffers[led.driver].pwm_buffer[led.v] = value;
+ driver_buffers[led.driver].pwm_buffer_dirty = true;
+ }
+}
+
+void is31fl3746a_set_value_all(uint8_t value) {
+ for (int i = 0; i < IS31FL3746A_LED_COUNT; i++) {
+ is31fl3746a_set_value(i, value);
+ }
+}
+
+void is31fl3746a_set_scaling_register(uint8_t index, uint8_t value) {
+ is31fl3746a_led_t led;
+ memcpy_P(&led, (&g_is31fl3746a_leds[index]), sizeof(led));
+
+ driver_buffers[led.driver].scaling_buffer[led.v] = value;
+ driver_buffers[led.driver].scaling_buffer_dirty = true;
+}
+
+void is31fl3746a_update_pwm_buffers(uint8_t index) {
+ if (driver_buffers[index].pwm_buffer_dirty) {
+ is31fl3746a_select_page(index, IS31FL3746A_COMMAND_PWM);
+
+ is31fl3746a_write_pwm_buffer(index);
+
+ driver_buffers[index].pwm_buffer_dirty = false;
+ }
+}
+
+void is31fl3746a_update_scaling_registers(uint8_t index) {
+ if (driver_buffers[index].scaling_buffer_dirty) {
+ is31fl3746a_select_page(index, IS31FL3746A_COMMAND_SCALING);
+
+ for (uint8_t i = 0; i < IS31FL3746A_SCALING_REGISTER_COUNT; i++) {
+ is31fl3746a_write_register(index, i + 1, driver_buffers[index].scaling_buffer[i]);
+ }
+
+ driver_buffers[index].scaling_buffer_dirty = false;
+ }
+}
+
+void is31fl3746a_flush(void) {
+ for (uint8_t i = 0; i < IS31FL3746A_DRIVER_COUNT; i++) {
+ is31fl3746a_update_pwm_buffers(i);
+ }
+}
diff --git a/drivers/led/issi/is31fl3746a-mono.h b/drivers/led/issi/is31fl3746a-mono.h
new file mode 100644
index 0000000000..295599cbcf
--- /dev/null
+++ b/drivers/led/issi/is31fl3746a-mono.h
@@ -0,0 +1,201 @@
+/* Copyright 2017 Jason Williams
+ * Copyright 2018 Jack Humbert
+ * Copyright 2018 Yiancar
+ * Copyright 2020 MelGeek
+ * Copyright 2021 MasterSpoon
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "progmem.h"
+#include "util.h"
+
+#define IS31FL3746A_REG_ID 0xFC
+
+#define IS31FL3746A_REG_COMMAND 0xFD
+
+#define IS31FL3746A_COMMAND_PWM 0x00
+#define IS31FL3746A_COMMAND_SCALING 0x01
+#define IS31FL3746A_COMMAND_FUNCTION 0x01
+
+#define IS31FL3746A_FUNCTION_REG_CONFIGURATION 0x50
+#define IS31FL3746A_FUNCTION_REG_GLOBAL_CURRENT 0x51
+#define IS31FL3746A_FUNCTION_REG_PULLDOWNUP 0x52
+#define IS31FL3746A_FUNCTION_REG_TEMPERATURE 0x5F
+#define IS31FL3746A_FUNCTION_REG_SPREAD_SPECTRUM 0x60
+#define IS31FL3746A_FUNCTION_REG_RESET 0x8F
+#define IS31FL3746A_FUNCTION_REG_PWM_ENABLE 0xE0
+#define IS31FL3746A_FUNCTION_REG_PWM_FREQUENCY 0xE2
+
+#define IS31FL3746A_REG_COMMAND_WRITE_LOCK 0xFE
+#define IS31FL3746A_COMMAND_WRITE_LOCK_MAGIC 0xC5
+
+#define IS31FL3746A_I2C_ADDRESS_GND_GND 0x60
+#define IS31FL3746A_I2C_ADDRESS_GND_SCL 0x61
+#define IS31FL3746A_I2C_ADDRESS_GND_SDA 0x62
+#define IS31FL3746A_I2C_ADDRESS_GND_VCC 0x63
+#define IS31FL3746A_I2C_ADDRESS_SCL_GND 0x64
+#define IS31FL3746A_I2C_ADDRESS_SCL_SCL 0x65
+#define IS31FL3746A_I2C_ADDRESS_SCL_SDA 0x66
+#define IS31FL3746A_I2C_ADDRESS_SCL_VCC 0x67
+#define IS31FL3746A_I2C_ADDRESS_SDA_GND 0x68
+#define IS31FL3746A_I2C_ADDRESS_SDA_SCL 0x69
+#define IS31FL3746A_I2C_ADDRESS_SDA_SDA 0x6A
+#define IS31FL3746A_I2C_ADDRESS_SDA_VCC 0x6B
+#define IS31FL3746A_I2C_ADDRESS_VCC_GND 0x6C
+#define IS31FL3746A_I2C_ADDRESS_VCC_SCL 0x6D
+#define IS31FL3746A_I2C_ADDRESS_VCC_SDA 0x6E
+#define IS31FL3746A_I2C_ADDRESS_VCC_VCC 0x6F
+
+#if defined(LED_MATRIX_IS31FL3746A)
+# define IS31FL3746A_LED_COUNT LED_MATRIX_LED_COUNT
+#endif
+
+#if defined(IS31FL3746A_I2C_ADDRESS_4)
+# define IS31FL3746A_DRIVER_COUNT 4
+#elif defined(IS31FL3746A_I2C_ADDRESS_3)
+# define IS31FL3746A_DRIVER_COUNT 3
+#elif defined(IS31FL3746A_I2C_ADDRESS_2)
+# define IS31FL3746A_DRIVER_COUNT 2
+#elif defined(IS31FL3746A_I2C_ADDRESS_1)
+# define IS31FL3746A_DRIVER_COUNT 1
+#endif
+
+typedef struct is31fl3746a_led_t {
+ uint8_t driver : 2;
+ uint8_t v;
+} PACKED is31fl3746a_led_t;
+
+extern const is31fl3746a_led_t PROGMEM g_is31fl3746a_leds[IS31FL3746A_LED_COUNT];
+
+void is31fl3746a_init_drivers(void);
+void is31fl3746a_init(uint8_t index);
+void is31fl3746a_write_register(uint8_t index, uint8_t reg, uint8_t data);
+void is31fl3746a_select_page(uint8_t index, uint8_t page);
+
+void is31fl3746a_set_value(int index, uint8_t value);
+void is31fl3746a_set_value_all(uint8_t value);
+
+void is31fl3746a_set_scaling_register(uint8_t index, uint8_t value);
+
+void is31fl3746a_update_pwm_buffers(uint8_t index);
+void is31fl3746a_update_scaling_registers(uint8_t index);
+
+void is31fl3746a_flush(void);
+
+#define IS31FL3746A_PDR_0_OHM 0b000 // No pull-down resistor
+#define IS31FL3746A_PDR_0K5_OHM_SW_OFF 0b001 // 0.5 kOhm resistor in SWx off time
+#define IS31FL3746A_PDR_1K_OHM_SW_OFF 0b010 // 1 kOhm resistor in SWx off time
+#define IS31FL3746A_PDR_2K_OHM_SW_OFF 0b011 // 2 kOhm resistor in SWx off time
+#define IS31FL3746A_PDR_1K_OHM 0b100 // 1 kOhm resistor
+#define IS31FL3746A_PDR_2K_OHM 0b101 // 2 kOhm resistor
+#define IS31FL3746A_PDR_4K_OHM 0b110 // 4 kOhm resistor
+#define IS31FL3746A_PDR_8K_OHM 0b111 // 8 kOhm resistor
+
+#define IS31FL3746A_PUR_0_OHM 0b000 // No pull-up resistor
+#define IS31FL3746A_PUR_0K5_OHM_CS_OFF 0b001 // 0.5 kOhm resistor in CSy off time
+#define IS31FL3746A_PUR_1K_OHM_CS_OFF 0b010 // 1 kOhm resistor in CSy off time
+#define IS31FL3746A_PUR_2K_OHM_CS_OFF 0b011 // 2 kOhm resistor in CSy off time
+#define IS31FL3746A_PUR_1K_OHM 0b100 // 1 kOhm resistor
+#define IS31FL3746A_PUR_2K_OHM 0b101 // 2 kOhm resistor
+#define IS31FL3746A_PUR_4K_OHM 0b110 // 4 kOhm resistor
+#define IS31FL3746A_PUR_8K_OHM 0b111 // 8 kOhm resistor
+
+#define IS31FL3746A_PWM_FREQUENCY_29K_HZ 0b000
+#define IS31FL3746A_PWM_FREQUENCY_14K5_HZ 0b001
+#define IS31FL3746A_PWM_FREQUENCY_7K25_HZ 0b010
+#define IS31FL3746A_PWM_FREQUENCY_3K63_HZ 0b011
+#define IS31FL3746A_PWM_FREQUENCY_1K81_HZ 0b100
+#define IS31FL3746A_PWM_FREQUENCY_906_HZ 0b101
+#define IS31FL3746A_PWM_FREQUENCY_453_HZ 0b110
+
+#define SW1_CS1 0x00
+#define SW1_CS2 0x01
+#define SW1_CS3 0x02
+#define SW1_CS4 0x03
+#define SW1_CS5 0x04
+#define SW1_CS6 0x05
+#define SW1_CS7 0x06
+#define SW1_CS8 0x07
+#define SW1_CS9 0x08
+#define SW1_CS10 0x09
+#define SW1_CS11 0x0A
+#define SW1_CS12 0x0B
+#define SW1_CS13 0x0C
+#define SW1_CS14 0x0D
+#define SW1_CS15 0x0E
+#define SW1_CS16 0x0F
+#define SW1_CS17 0x10
+#define SW1_CS18 0x11
+
+#define SW2_CS1 0x12
+#define SW2_CS2 0x13
+#define SW2_CS3 0x14
+#define SW2_CS4 0x15
+#define SW2_CS5 0x16
+#define SW2_CS6 0x17
+#define SW2_CS7 0x18
+#define SW2_CS8 0x19
+#define SW2_CS9 0x1A
+#define SW2_CS10 0x1B
+#define SW2_CS11 0x1C
+#define SW2_CS12 0x1D
+#define SW2_CS13 0x1E
+#define SW2_CS14 0x1F
+#define SW2_CS15 0x20
+#define SW2_CS16 0x21
+#define SW2_CS17 0x22
+#define SW2_CS18 0x23
+
+#define SW3_CS1 0x24
+#define SW3_CS2 0x25
+#define SW3_CS3 0x26
+#define SW3_CS4 0x27
+#define SW3_CS5 0x28
+#define SW3_CS6 0x29
+#define SW3_CS7 0x2A
+#define SW3_CS8 0x2B
+#define SW3_CS9 0x2C
+#define SW3_CS10 0x2D
+#define SW3_CS11 0x2E
+#define SW3_CS12 0x2F
+#define SW3_CS13 0x30
+#define SW3_CS14 0x31
+#define SW3_CS15 0x32
+#define SW3_CS16 0x33
+#define SW3_CS17 0x34
+#define SW3_CS18 0x35
+
+#define SW4_CS1 0x36
+#define SW4_CS2 0x37
+#define SW4_CS3 0x38
+#define SW4_CS4 0x39
+#define SW4_CS5 0x3A
+#define SW4_CS6 0x3B
+#define SW4_CS7 0x3C
+#define SW4_CS8 0x3D
+#define SW4_CS9 0x3E
+#define SW4_CS10 0x3F
+#define SW4_CS11 0x40
+#define SW4_CS12 0x41
+#define SW4_CS13 0x42
+#define SW4_CS14 0x43
+#define SW4_CS15 0x44
+#define SW4_CS16 0x45
+#define SW4_CS17 0x46
+#define SW4_CS18 0x47
diff --git a/drivers/led/issi/is31fl3746a.c b/drivers/led/issi/is31fl3746a.c
new file mode 100644
index 0000000000..1ef0b2d632
--- /dev/null
+++ b/drivers/led/issi/is31fl3746a.c
@@ -0,0 +1,226 @@
+/* Copyright 2017 Jason Williams
+ * Copyright 2018 Jack Humbert
+ * Copyright 2018 Yiancar
+ * Copyright 2020 MelGeek
+ * Copyright 2021 MasterSpoon
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "is31fl3746a.h"
+#include "i2c_master.h"
+#include "gpio.h"
+#include "wait.h"
+
+#define IS31FL3746A_PWM_REGISTER_COUNT 72
+#define IS31FL3746A_SCALING_REGISTER_COUNT 72
+
+#ifndef IS31FL3746A_I2C_TIMEOUT
+# define IS31FL3746A_I2C_TIMEOUT 100
+#endif
+
+#ifndef IS31FL3746A_I2C_PERSISTENCE
+# define IS31FL3746A_I2C_PERSISTENCE 0
+#endif
+
+#ifndef IS31FL3746A_CONFIGURATION
+# define IS31FL3746A_CONFIGURATION 0x01
+#endif
+
+#ifndef IS31FL3746A_PWM_FREQUENCY
+# define IS31FL3746A_PWM_FREQUENCY IS31FL3746A_PWM_FREQUENCY_29K_HZ
+#endif
+
+#ifndef IS31FL3746A_SW_PULLDOWN
+# define IS31FL3746A_SW_PULLDOWN IS31FL3746A_PDR_2K_OHM_SW_OFF
+#endif
+
+#ifndef IS31FL3746A_CS_PULLUP
+# define IS31FL3746A_CS_PULLUP IS31FL3746A_PUR_2K_OHM_CS_OFF
+#endif
+
+#ifndef IS31FL3746A_GLOBAL_CURRENT
+# define IS31FL3746A_GLOBAL_CURRENT 0xFF
+#endif
+
+const uint8_t i2c_addresses[IS31FL3746A_DRIVER_COUNT] = {
+ IS31FL3746A_I2C_ADDRESS_1,
+#ifdef IS31FL3746A_I2C_ADDRESS_2
+ IS31FL3746A_I2C_ADDRESS_2,
+# ifdef IS31FL3746A_I2C_ADDRESS_3
+ IS31FL3746A_I2C_ADDRESS_3,
+# ifdef IS31FL3746A_I2C_ADDRESS_4
+ IS31FL3746A_I2C_ADDRESS_4,
+# endif
+# endif
+#endif
+};
+
+typedef struct is31fl3746a_driver_t {
+ uint8_t pwm_buffer[IS31FL3746A_PWM_REGISTER_COUNT];
+ bool pwm_buffer_dirty;
+ uint8_t scaling_buffer[IS31FL3746A_SCALING_REGISTER_COUNT];
+ bool scaling_buffer_dirty;
+} PACKED is31fl3746a_driver_t;
+
+is31fl3746a_driver_t driver_buffers[IS31FL3746A_DRIVER_COUNT] = {{
+ .pwm_buffer = {0},
+ .pwm_buffer_dirty = false,
+ .scaling_buffer = {0},
+ .scaling_buffer_dirty = false,
+}};
+
+void is31fl3746a_write_register(uint8_t index, uint8_t reg, uint8_t data) {
+#if IS31FL3746A_I2C_PERSISTENCE > 0
+ for (uint8_t i = 0; i < IS31FL3746A_I2C_PERSISTENCE; i++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3746A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3746A_I2C_TIMEOUT);
+#endif
+}
+
+void is31fl3746a_select_page(uint8_t index, uint8_t page) {
+ is31fl3746a_write_register(index, IS31FL3746A_REG_COMMAND_WRITE_LOCK, IS31FL3746A_COMMAND_WRITE_LOCK_MAGIC);
+ is31fl3746a_write_register(index, IS31FL3746A_REG_COMMAND, page);
+}
+
+void is31fl3746a_write_pwm_buffer(uint8_t index) {
+ // Assumes page 0 is already selected.
+ // Transmit PWM registers in 4 transfers of 18 bytes.
+
+ // Iterate over the pwm_buffer contents at 18 byte intervals.
+ for (uint8_t i = 0; i < IS31FL3746A_PWM_REGISTER_COUNT; i += 18) {
+#if IS31FL3746A_I2C_PERSISTENCE > 0
+ for (uint8_t j = 0; j < IS31FL3746A_I2C_PERSISTENCE; j++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3746A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3746A_I2C_TIMEOUT);
+#endif
+ }
+}
+
+void is31fl3746a_init_drivers(void) {
+ i2c_init();
+
+#if defined(IS31FL3746A_SDB_PIN)
+ gpio_set_pin_output(IS31FL3746A_SDB_PIN);
+ gpio_write_pin_high(IS31FL3746A_SDB_PIN);
+#endif
+
+ for (uint8_t i = 0; i < IS31FL3746A_DRIVER_COUNT; i++) {
+ is31fl3746a_init(i);
+ }
+
+ for (int i = 0; i < IS31FL3746A_LED_COUNT; i++) {
+ is31fl3746a_set_scaling_register(i, 0xFF, 0xFF, 0xFF);
+ }
+
+ for (uint8_t i = 0; i < IS31FL3746A_DRIVER_COUNT; i++) {
+ is31fl3746a_update_scaling_registers(i);
+ }
+}
+
+void is31fl3746a_init(uint8_t index) {
+ // In order to avoid the LEDs being driven with garbage data
+ // in the LED driver's PWM registers, shutdown is enabled last.
+ // Set up the mode and other settings, clear the PWM registers,
+ // then disable software shutdown.
+
+ is31fl3746a_select_page(index, IS31FL3746A_COMMAND_SCALING);
+
+ // Turn off all LEDs.
+ for (uint8_t i = 0; i < IS31FL3746A_SCALING_REGISTER_COUNT; i++) {
+ is31fl3746a_write_register(index, i + 1, 0x00);
+ }
+
+ is31fl3746a_select_page(index, IS31FL3746A_COMMAND_PWM);
+
+ for (uint8_t i = 0; i < IS31FL3746A_PWM_REGISTER_COUNT; i++) {
+ is31fl3746a_write_register(index, i + 1, 0x00);
+ }
+
+ is31fl3746a_select_page(index, IS31FL3746A_COMMAND_FUNCTION);
+
+ is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_PULLDOWNUP, (IS31FL3746A_SW_PULLDOWN << 4) | IS31FL3746A_CS_PULLUP);
+ is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3746A_GLOBAL_CURRENT);
+ is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_PWM_ENABLE, 0x01);
+ is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_PWM_FREQUENCY, IS31FL3746A_PWM_FREQUENCY);
+ is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_CONFIGURATION, IS31FL3746A_CONFIGURATION);
+
+ // Wait 10ms to ensure the device has woken up.
+ wait_ms(10);
+}
+
+void is31fl3746a_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
+ is31fl3746a_led_t led;
+
+ if (index >= 0 && index < IS31FL3746A_LED_COUNT) {
+ memcpy_P(&led, (&g_is31fl3746a_leds[index]), sizeof(led));
+
+ if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) {
+ return;
+ }
+
+ driver_buffers[led.driver].pwm_buffer[led.r] = red;
+ driver_buffers[led.driver].pwm_buffer[led.g] = green;
+ driver_buffers[led.driver].pwm_buffer[led.b] = blue;
+ driver_buffers[led.driver].pwm_buffer_dirty = true;
+ }
+}
+
+void is31fl3746a_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
+ for (int i = 0; i < IS31FL3746A_LED_COUNT; i++) {
+ is31fl3746a_set_color(i, red, green, blue);
+ }
+}
+
+void is31fl3746a_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue) {
+ is31fl3746a_led_t led;
+ memcpy_P(&led, (&g_is31fl3746a_leds[index]), sizeof(led));
+
+ driver_buffers[led.driver].scaling_buffer[led.r] = red;
+ driver_buffers[led.driver].scaling_buffer[led.g] = green;
+ driver_buffers[led.driver].scaling_buffer[led.b] = blue;
+ driver_buffers[led.driver].scaling_buffer_dirty = true;
+}
+
+void is31fl3746a_update_pwm_buffers(uint8_t index) {
+ if (driver_buffers[index].pwm_buffer_dirty) {
+ is31fl3746a_select_page(index, IS31FL3746A_COMMAND_PWM);
+
+ is31fl3746a_write_pwm_buffer(index);
+
+ driver_buffers[index].pwm_buffer_dirty = false;
+ }
+}
+
+void is31fl3746a_update_scaling_registers(uint8_t index) {
+ if (driver_buffers[index].scaling_buffer_dirty) {
+ is31fl3746a_select_page(index, IS31FL3746A_COMMAND_SCALING);
+
+ for (uint8_t i = 0; i < IS31FL3746A_SCALING_REGISTER_COUNT; i++) {
+ is31fl3746a_write_register(index, i + 1, driver_buffers[index].scaling_buffer[i]);
+ }
+
+ driver_buffers[index].scaling_buffer_dirty = false;
+ }
+}
+
+void is31fl3746a_flush(void) {
+ for (uint8_t i = 0; i < IS31FL3746A_DRIVER_COUNT; i++) {
+ is31fl3746a_update_pwm_buffers(i);
+ }
+}
diff --git a/drivers/led/issi/is31fl3746a.h b/drivers/led/issi/is31fl3746a.h
new file mode 100644
index 0000000000..ba435b8f19
--- /dev/null
+++ b/drivers/led/issi/is31fl3746a.h
@@ -0,0 +1,203 @@
+/* Copyright 2017 Jason Williams
+ * Copyright 2018 Jack Humbert
+ * Copyright 2018 Yiancar
+ * Copyright 2020 MelGeek
+ * Copyright 2021 MasterSpoon
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "progmem.h"
+#include "util.h"
+
+#define IS31FL3746A_REG_ID 0xFC
+
+#define IS31FL3746A_REG_COMMAND 0xFD
+
+#define IS31FL3746A_COMMAND_PWM 0x00
+#define IS31FL3746A_COMMAND_SCALING 0x01
+#define IS31FL3746A_COMMAND_FUNCTION 0x01
+
+#define IS31FL3746A_FUNCTION_REG_CONFIGURATION 0x50
+#define IS31FL3746A_FUNCTION_REG_GLOBAL_CURRENT 0x51
+#define IS31FL3746A_FUNCTION_REG_PULLDOWNUP 0x52
+#define IS31FL3746A_FUNCTION_REG_TEMPERATURE 0x5F
+#define IS31FL3746A_FUNCTION_REG_SPREAD_SPECTRUM 0x60
+#define IS31FL3746A_FUNCTION_REG_RESET 0x8F
+#define IS31FL3746A_FUNCTION_REG_PWM_ENABLE 0xE0
+#define IS31FL3746A_FUNCTION_REG_PWM_FREQUENCY 0xE2
+
+#define IS31FL3746A_REG_COMMAND_WRITE_LOCK 0xFE
+#define IS31FL3746A_COMMAND_WRITE_LOCK_MAGIC 0xC5
+
+#define IS31FL3746A_I2C_ADDRESS_GND_GND 0x60
+#define IS31FL3746A_I2C_ADDRESS_GND_SCL 0x61
+#define IS31FL3746A_I2C_ADDRESS_GND_SDA 0x62
+#define IS31FL3746A_I2C_ADDRESS_GND_VCC 0x63
+#define IS31FL3746A_I2C_ADDRESS_SCL_GND 0x64
+#define IS31FL3746A_I2C_ADDRESS_SCL_SCL 0x65
+#define IS31FL3746A_I2C_ADDRESS_SCL_SDA 0x66
+#define IS31FL3746A_I2C_ADDRESS_SCL_VCC 0x67
+#define IS31FL3746A_I2C_ADDRESS_SDA_GND 0x68
+#define IS31FL3746A_I2C_ADDRESS_SDA_SCL 0x69
+#define IS31FL3746A_I2C_ADDRESS_SDA_SDA 0x6A
+#define IS31FL3746A_I2C_ADDRESS_SDA_VCC 0x6B
+#define IS31FL3746A_I2C_ADDRESS_VCC_GND 0x6C
+#define IS31FL3746A_I2C_ADDRESS_VCC_SCL 0x6D
+#define IS31FL3746A_I2C_ADDRESS_VCC_SDA 0x6E
+#define IS31FL3746A_I2C_ADDRESS_VCC_VCC 0x6F
+
+#if defined(RGB_MATRIX_IS31FL3746A)
+# define IS31FL3746A_LED_COUNT RGB_MATRIX_LED_COUNT
+#endif
+
+#if defined(IS31FL3746A_I2C_ADDRESS_4)
+# define IS31FL3746A_DRIVER_COUNT 4
+#elif defined(IS31FL3746A_I2C_ADDRESS_3)
+# define IS31FL3746A_DRIVER_COUNT 3
+#elif defined(IS31FL3746A_I2C_ADDRESS_2)
+# define IS31FL3746A_DRIVER_COUNT 2
+#elif defined(IS31FL3746A_I2C_ADDRESS_1)
+# define IS31FL3746A_DRIVER_COUNT 1
+#endif
+
+typedef struct is31fl3746a_led_t {
+ uint8_t driver : 2;
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+} PACKED is31fl3746a_led_t;
+
+extern const is31fl3746a_led_t PROGMEM g_is31fl3746a_leds[IS31FL3746A_LED_COUNT];
+
+void is31fl3746a_init_drivers(void);
+void is31fl3746a_init(uint8_t index);
+void is31fl3746a_write_register(uint8_t index, uint8_t reg, uint8_t data);
+void is31fl3746a_select_page(uint8_t index, uint8_t page);
+
+void is31fl3746a_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
+void is31fl3746a_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
+
+void is31fl3746a_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue);
+
+void is31fl3746a_update_pwm_buffers(uint8_t index);
+void is31fl3746a_update_scaling_registers(uint8_t index);
+
+void is31fl3746a_flush(void);
+
+#define IS31FL3746A_PDR_0_OHM 0b000 // No pull-down resistor
+#define IS31FL3746A_PDR_0K5_OHM_SW_OFF 0b001 // 0.5 kOhm resistor in SWx off time
+#define IS31FL3746A_PDR_1K_OHM_SW_OFF 0b010 // 1 kOhm resistor in SWx off time
+#define IS31FL3746A_PDR_2K_OHM_SW_OFF 0b011 // 2 kOhm resistor in SWx off time
+#define IS31FL3746A_PDR_1K_OHM 0b100 // 1 kOhm resistor
+#define IS31FL3746A_PDR_2K_OHM 0b101 // 2 kOhm resistor
+#define IS31FL3746A_PDR_4K_OHM 0b110 // 4 kOhm resistor
+#define IS31FL3746A_PDR_8K_OHM 0b111 // 8 kOhm resistor
+
+#define IS31FL3746A_PUR_0_OHM 0b000 // No pull-up resistor
+#define IS31FL3746A_PUR_0K5_OHM_CS_OFF 0b001 // 0.5 kOhm resistor in CSy off time
+#define IS31FL3746A_PUR_1K_OHM_CS_OFF 0b010 // 1 kOhm resistor in CSy off time
+#define IS31FL3746A_PUR_2K_OHM_CS_OFF 0b011 // 2 kOhm resistor in CSy off time
+#define IS31FL3746A_PUR_1K_OHM 0b100 // 1 kOhm resistor
+#define IS31FL3746A_PUR_2K_OHM 0b101 // 2 kOhm resistor
+#define IS31FL3746A_PUR_4K_OHM 0b110 // 4 kOhm resistor
+#define IS31FL3746A_PUR_8K_OHM 0b111 // 8 kOhm resistor
+
+#define IS31FL3746A_PWM_FREQUENCY_29K_HZ 0b000
+#define IS31FL3746A_PWM_FREQUENCY_14K5_HZ 0b001
+#define IS31FL3746A_PWM_FREQUENCY_7K25_HZ 0b010
+#define IS31FL3746A_PWM_FREQUENCY_3K63_HZ 0b011
+#define IS31FL3746A_PWM_FREQUENCY_1K81_HZ 0b100
+#define IS31FL3746A_PWM_FREQUENCY_906_HZ 0b101
+#define IS31FL3746A_PWM_FREQUENCY_453_HZ 0b110
+
+#define SW1_CS1 0x00
+#define SW1_CS2 0x01
+#define SW1_CS3 0x02
+#define SW1_CS4 0x03
+#define SW1_CS5 0x04
+#define SW1_CS6 0x05
+#define SW1_CS7 0x06
+#define SW1_CS8 0x07
+#define SW1_CS9 0x08
+#define SW1_CS10 0x09
+#define SW1_CS11 0x0A
+#define SW1_CS12 0x0B
+#define SW1_CS13 0x0C
+#define SW1_CS14 0x0D
+#define SW1_CS15 0x0E
+#define SW1_CS16 0x0F
+#define SW1_CS17 0x10
+#define SW1_CS18 0x11
+
+#define SW2_CS1 0x12
+#define SW2_CS2 0x13
+#define SW2_CS3 0x14
+#define SW2_CS4 0x15
+#define SW2_CS5 0x16
+#define SW2_CS6 0x17
+#define SW2_CS7 0x18
+#define SW2_CS8 0x19
+#define SW2_CS9 0x1A
+#define SW2_CS10 0x1B
+#define SW2_CS11 0x1C
+#define SW2_CS12 0x1D
+#define SW2_CS13 0x1E
+#define SW2_CS14 0x1F
+#define SW2_CS15 0x20
+#define SW2_CS16 0x21
+#define SW2_CS17 0x22
+#define SW2_CS18 0x23
+
+#define SW3_CS1 0x24
+#define SW3_CS2 0x25
+#define SW3_CS3 0x26
+#define SW3_CS4 0x27
+#define SW3_CS5 0x28
+#define SW3_CS6 0x29
+#define SW3_CS7 0x2A
+#define SW3_CS8 0x2B
+#define SW3_CS9 0x2C
+#define SW3_CS10 0x2D
+#define SW3_CS11 0x2E
+#define SW3_CS12 0x2F
+#define SW3_CS13 0x30
+#define SW3_CS14 0x31
+#define SW3_CS15 0x32
+#define SW3_CS16 0x33
+#define SW3_CS17 0x34
+#define SW3_CS18 0x35
+
+#define SW4_CS1 0x36
+#define SW4_CS2 0x37
+#define SW4_CS3 0x38
+#define SW4_CS4 0x39
+#define SW4_CS5 0x3A
+#define SW4_CS6 0x3B
+#define SW4_CS7 0x3C
+#define SW4_CS8 0x3D
+#define SW4_CS9 0x3E
+#define SW4_CS10 0x3F
+#define SW4_CS11 0x40
+#define SW4_CS12 0x41
+#define SW4_CS13 0x42
+#define SW4_CS14 0x43
+#define SW4_CS15 0x44
+#define SW4_CS16 0x45
+#define SW4_CS17 0x46
+#define SW4_CS18 0x47
diff --git a/drivers/led/issi/is31flcommon.c b/drivers/led/issi/is31flcommon.c
deleted file mode 100644
index d6b9bce93d..0000000000
--- a/drivers/led/issi/is31flcommon.c
+++ /dev/null
@@ -1,330 +0,0 @@
-/* Copyright 2017 Jason Williams
- * Copyright 2018 Jack Humbert
- * Copyright 2018 Yiancar
- * Copyright 2020 MelGeek
- * Copyright 2021 MasterSpoon
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#include "is31flcommon.h"
-#include "i2c_master.h"
-#include "wait.h"
-#include <string.h>
-
-// Set defaults for Timeout and Persistence
-#ifndef ISSI_TIMEOUT
-# define ISSI_TIMEOUT 100
-#endif
-#ifndef ISSI_PERSISTENCE
-# define ISSI_PERSISTENCE 0
-#endif
-
-// Transfer buffer for TWITransmitData()
-uint8_t g_twi_transfer_buffer[20];
-
-// These buffers match the PWM & scaling registers.
-// Storing them like this is optimal for I2C transfers to the registers.
-uint8_t g_pwm_buffer[DRIVER_COUNT][ISSI_MAX_LEDS];
-bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};
-
-uint8_t g_scaling_buffer[DRIVER_COUNT][ISSI_SCALING_SIZE];
-bool g_scaling_buffer_update_required[DRIVER_COUNT] = {false};
-
-// For writing of single register entry
-void IS31FL_write_single_register(uint8_t addr, uint8_t reg, uint8_t data) {
- // Set register address and register data ready to write
- g_twi_transfer_buffer[0] = reg;
- g_twi_transfer_buffer[1] = data;
-
-#if ISSI_PERSISTENCE > 0
- for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) == 0) break;
- }
-#else
- i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT);
-#endif
-}
-
-// For writing of mulitple register entries to make use of address auto increment
-// Once the controller has been called and we have written the first bit of data
-// the controller will move to the next register meaning we can write sequential blocks.
-bool IS31FL_write_multi_registers(uint8_t addr, uint8_t *source_buffer, uint8_t buffer_size, uint8_t transfer_size, uint8_t start_reg_addr) {
- // Split the buffer into chunks to transfer
- for (int i = 0; i < buffer_size; i += transfer_size) {
- // Set the first entry of transfer buffer to the first register we want to write
- g_twi_transfer_buffer[0] = i + start_reg_addr;
- // Copy the section of our source buffer into the transfer buffer after first register address
- memcpy(g_twi_transfer_buffer + 1, source_buffer + i, transfer_size);
-
-#if ISSI_PERSISTENCE > 0
- for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, transfer_size + 1, ISSI_TIMEOUT) != 0) {
- return false;
- }
- }
-#else
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, transfer_size + 1, ISSI_TIMEOUT) != 0) {
- return false;
- }
-#endif
- }
- return true;
-}
-
-void IS31FL_unlock_register(uint8_t addr, uint8_t page) {
- // unlock the command register and select Page to write
- IS31FL_write_single_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, ISSI_REGISTER_UNLOCK);
- IS31FL_write_single_register(addr, ISSI_COMMANDREGISTER, page);
-}
-
-void IS31FL_common_init(uint8_t addr, uint8_t ssr) {
- // Setup phase, need to take out of software shutdown and configure
- // ISSI_SSR_x is passed to allow Master / Slave setting where applicable
-
- // Unlock the command register & select Function Register
- IS31FL_unlock_register(addr, ISSI_PAGE_FUNCTION);
- // Set Configuration Register to remove Software shutdown
- IS31FL_write_single_register(addr, ISSI_REG_CONFIGURATION, ISSI_CONFIGURATION);
- // Set Golbal Current Control Register
- IS31FL_write_single_register(addr, ISSI_REG_GLOBALCURRENT, ISSI_GLOBALCURRENT);
- // Set Pull up & Down for SWx CSy
- IS31FL_write_single_register(addr, ISSI_REG_PULLDOWNUP, ISSI_PULLDOWNUP);
-// Set Tempature Status
-#ifdef ISSI_REG_TEMP
- IS31FL_write_single_register(addr, ISSI_REG_TEMP, ISSI_TEMP);
-#endif
- // Set Spread Spectrum Register, passed through as sets SYNC function
- IS31FL_write_single_register(addr, ISSI_REG_SSR, ssr);
-// Set PWM Frequency Enable Register if applicable
-#ifdef ISSI_REG_PWM_ENABLE
- IS31FL_write_single_register(addr, ISSI_REG_PWM_ENABLE, ISSI_PWM_ENABLE);
-#endif
-// Set PWM Frequency Register if applicable
-#ifdef ISSI_REG_PWM_SET
- IS31FL_write_single_register(addr, ISSI_REG_PWM_SET, ISSI_PWM_SET);
-#endif
-
- // Wait 10ms to ensure the device has woken up.
- wait_ms(10);
-}
-
-void IS31FL_common_update_pwm_register(uint8_t addr, uint8_t index) {
- if (g_pwm_buffer_update_required[index]) {
- // Queue up the correct page
- IS31FL_unlock_register(addr, ISSI_PAGE_PWM);
- // Hand off the update to IS31FL_write_multi_registers
- IS31FL_write_multi_registers(addr, g_pwm_buffer[index], ISSI_MAX_LEDS, ISSI_PWM_TRF_SIZE, ISSI_PWM_REG_1ST);
- // Update flags that pwm_buffer has been updated
- g_pwm_buffer_update_required[index] = false;
- }
-}
-
-#ifdef ISSI_MANUAL_SCALING
-void IS31FL_set_manual_scaling_buffer(void) {
- is31_led led;
- is31_led scale;
- for (int i = 0; i < ISSI_MANUAL_SCALING; i++) {
- memcpy_P(&scale, (&g_is31_scaling[i]), sizeof(scale));
-
-# ifdef RGB_MATRIX_ENABLE
- if (scale.driver >= 0 && scale.driver < RGB_MATRIX_LED_COUNT) {
- memcpy_P(&led, (&g_is31_leds[scale.driver]), sizeof(led));
-
- if (g_scaling_buffer[led.driver][led.r] = scale.r && g_scaling_buffer[led.driver][led.g] = scale.g && g_scaling_buffer[led.driver][led.b] = scale.b) {
- return;
- }
- g_scaling_buffer[led.driver][led.r] = scale.r;
- g_scaling_buffer[led.driver][led.g] = scale.g;
- g_scaling_buffer[led.driver][led.b] = scale.b;
-# elif defined(LED_MATRIX_ENABLE)
- if (scale.driver >= 0 && scale.driver < LED_MATRIX_LED_COUNT) {
- memcpy_P(&led, (&g_is31_leds[scale.driver]), sizeof(led));
-
- if (g_scaling_buffer[led.driver][led.v] == scale.v) {
- return;
- }
- g_scaling_buffer[led.driver][led.v] = scale.v;
-# endif
- g_scaling_buffer_update_required[led.driver] = true;
- }
- }
-}
-#endif
-
-void IS31FL_common_update_scaling_register(uint8_t addr, uint8_t index) {
- if (g_scaling_buffer_update_required[index]) {
- // Queue up the correct page
- IS31FL_unlock_register(addr, ISSI_PAGE_SCALING);
- // Hand off the update to IS31FL_write_multi_registers
- IS31FL_write_multi_registers(addr, g_scaling_buffer[index], ISSI_SCALING_SIZE, ISSI_SCALING_TRF_SIZE, ISSI_SCL_REG_1ST);
- // Update flags that scaling_buffer has been updated
- g_scaling_buffer_update_required[index] = false;
- }
-}
-
-void IS31FL_common_flush(void) {
- IS31FL_common_update_pwm_register(DRIVER_ADDR_1, 0);
-#if defined(DRIVER_ADDR_2)
- IS31FL_common_update_pwm_register(DRIVER_ADDR_2, 1);
-# if defined(DRIVER_ADDR_3)
- IS31FL_common_update_pwm_register(DRIVER_ADDR_3, 2);
-# if defined(DRIVER_ADDR_4)
- IS31FL_common_update_pwm_register(DRIVER_ADDR_4, 3);
-# endif
-# endif
-#endif
-}
-
-#ifdef RGB_MATRIX_ENABLE
-void IS31FL_RGB_init_drivers(void) {
- i2c_init();
-
- IS31FL_common_init(DRIVER_ADDR_1, ISSI_SSR_1);
-# if defined(DRIVER_ADDR_2)
- IS31FL_common_init(DRIVER_ADDR_2, ISSI_SSR_2);
-# if defined(DRIVER_ADDR_3)
- IS31FL_common_init(DRIVER_ADDR_3, ISSI_SSR_3);
-# if defined(DRIVER_ADDR_4)
- IS31FL_common_init(DRIVER_ADDR_4, ISSI_SSR_4);
-# endif
-# endif
-# endif
-
- for (int i = 0; i < RGB_MATRIX_LED_COUNT; i++) {
- IS31FL_RGB_set_scaling_buffer(i, true, true, true);
- }
-
- // This actually updates the LED drivers
-# ifdef ISSI_MANUAL_SCALING
- IS31FL_set_manual_scaling_buffer();
-# endif
-
- IS31FL_common_update_scaling_register(DRIVER_ADDR_1, 0);
-# if defined(DRIVER_ADDR_2)
- IS31FL_common_update_scaling_register(DRIVER_ADDR_2, 1);
-# if defined(DRIVER_ADDR_3)
- IS31FL_common_update_scaling_register(DRIVER_ADDR_3, 2);
-# if defined(DRIVER_ADDR_4)
- IS31FL_common_update_scaling_register(DRIVER_ADDR_4, 3);
-# endif
-# endif
-# endif
-}
-
-// Colour is set by adjusting PWM register
-void IS31FL_RGB_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
- if (index >= 0 && index < RGB_MATRIX_LED_COUNT) {
- is31_led led;
- memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
-
- g_pwm_buffer[led.driver][led.r] = red;
- g_pwm_buffer[led.driver][led.g] = green;
- g_pwm_buffer[led.driver][led.b] = blue;
- g_pwm_buffer_update_required[led.driver] = true;
- }
-}
-
-void IS31FL_RGB_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
- for (int i = 0; i < RGB_MATRIX_LED_COUNT; i++) {
- IS31FL_RGB_set_color(i, red, green, blue);
- }
-}
-
-// Setup Scaling register that decides the peak current of each LED
-void IS31FL_RGB_set_scaling_buffer(uint8_t index, bool red, bool green, bool blue) {
- is31_led led;
- memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
- if (red) {
- g_scaling_buffer[led.driver][led.r] = ISSI_SCAL_RED;
- } else {
- g_scaling_buffer[led.driver][led.r] = ISSI_SCAL_RED_OFF;
- }
- if (green) {
- g_scaling_buffer[led.driver][led.g] = ISSI_SCAL_GREEN;
- } else {
- g_scaling_buffer[led.driver][led.g] = ISSI_SCAL_GREEN_OFF;
- }
- if (blue) {
- g_scaling_buffer[led.driver][led.b] = ISSI_SCAL_BLUE;
- } else {
- g_scaling_buffer[led.driver][led.b] = ISSI_SCAL_BLUE_OFF;
- }
- g_scaling_buffer_update_required[led.driver] = true;
-}
-
-#elif defined(LED_MATRIX_ENABLE)
-// LED Matrix Specific scripts
-void IS31FL_simple_init_drivers(void) {
- i2c_init();
-
- IS31FL_common_init(DRIVER_ADDR_1, ISSI_SSR_1);
-# if defined(DRIVER_ADDR_2)
- IS31FL_common_init(DRIVER_ADDR_2, ISSI_SSR_2);
-# if defined(DRIVER_ADDR_3)
- IS31FL_common_init(DRIVER_ADDR_3, ISSI_SSR_3);
-# if defined(DRIVER_ADDR_4)
- IS31FL_common_init(DRIVER_ADDR_4, ISSI_SSR_4);
-# endif
-# endif
-# endif
-
- for (int i = 0; i < LED_MATRIX_LED_COUNT; i++) {
- IS31FL_simple_set_scaling_buffer(i, true);
- }
-
-// This actually updates the LED drivers
-# ifdef ISSI_MANUAL_SCALING
- IS31FL_set_manual_scaling_buffer();
-# endif
-
- IS31FL_common_update_scaling_register(DRIVER_ADDR_1, 0);
-# if defined(DRIVER_ADDR_2)
- IS31FL_common_update_scaling_register(DRIVER_ADDR_2, 1);
-# if defined(DRIVER_ADDR_3)
- IS31FL_common_update_scaling_register(DRIVER_ADDR_3, 2);
-# if defined(DRIVER_ADDR_4)
- IS31FL_common_update_scaling_register(DRIVER_ADDR_4, 3);
-# endif
-# endif
-# endif
-}
-
-void IS31FL_simple_set_scaling_buffer(uint8_t index, bool value) {
- is31_led led;
- memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
- if (value) {
- g_scaling_buffer[led.driver][led.v] = ISSI_SCAL_LED;
- } else {
- g_scaling_buffer[led.driver][led.v] = ISSI_SCAL_LED_OFF;
- }
- g_scaling_buffer_update_required[led.driver] = true;
-}
-
-void IS31FL_simple_set_brightness(int index, uint8_t value) {
- if (index >= 0 && index < LED_MATRIX_LED_COUNT) {
- is31_led led;
- memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
-
- g_pwm_buffer[led.driver][led.v] = value;
- g_pwm_buffer_update_required[led.driver] = true;
- }
-}
-
-void IS31FL_simple_set_brigntness_all(uint8_t value) {
- for (int i = 0; i < LED_MATRIX_LED_COUNT; i++) {
- IS31FL_simple_set_brightness(i, value);
- }
-}
-#endif
diff --git a/drivers/led/issi/is31flcommon.h b/drivers/led/issi/is31flcommon.h
deleted file mode 100644
index 10613a6eed..0000000000
--- a/drivers/led/issi/is31flcommon.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/* Copyright 2017 Jason Williams
- * Copyright 2018 Jack Humbert
- * Copyright 2018 Yiancar
- * Copyright 2020 MelGeek
- * Copyright 2021 MasterSpoon
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-#include <stdint.h>
-#include <stdbool.h>
-#include "progmem.h"
-#include "util.h"
-
-// Which variant header file to use
-#if defined(LED_MATRIX_IS31FL3742A) || defined(RGB_MATRIX_IS31FL3742A)
-# include "is31fl3742.h"
-#elif defined(LED_MATRIX_IS31FL3743A) || defined(RGB_MATRIX_IS31FL3743A)
-# include "is31fl3743.h"
-#elif defined(LED_MATRIX_IS31FL3745) || defined(RGB_MATRIX_IS31FL3745)
-# include "is31fl3745.h"
-#elif defined(LED_MATRIX_IS31FL3746A) || defined(RGB_MATRIX_IS31FL3746A)
-# include "is31fl3746.h"
-#endif
-
-#if defined DRIVER_ADDR_4
-# define DRIVER_COUNT 4
-#elif defined DRIVER_ADDR_3
-# define DRIVER_COUNT 3
-#elif defined DRIVER_ADDR_2
-# define DRIVER_COUNT 2
-#elif defined DRIVER_ADDR_1
-# define DRIVER_COUNT 1
-#endif
-
-#ifdef RGB_MATRIX_ENABLE
-typedef struct is31_led {
- uint8_t driver : 2;
- uint8_t r;
- uint8_t g;
- uint8_t b;
-} PACKED is31_led;
-
-extern const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT];
-
-#elif defined(LED_MATRIX_ENABLE)
-typedef struct is31_led {
- uint8_t driver : 2;
- uint8_t v;
-} PACKED is31_led;
-
-extern const is31_led PROGMEM g_is31_leds[LED_MATRIX_LED_COUNT];
-#endif
-
-#ifdef ISSI_MANUAL_SCALING
-extern const is31_led PROGMEM g_is31_scaling[];
-void IS31FL_set_manual_scaling_buffer(void);
-#endif
-
-void IS31FL_write_single_register(uint8_t addr, uint8_t reg, uint8_t data);
-bool IS31FL_write_multi_registers(uint8_t addr, uint8_t *source_buffer, uint8_t buffer_size, uint8_t transfer_size, uint8_t start_reg_addr);
-void IS31FL_unlock_register(uint8_t addr, uint8_t page);
-void IS31FL_common_init(uint8_t addr, uint8_t ssr);
-
-void IS31FL_common_update_pwm_register(uint8_t addr, uint8_t index);
-void IS31FL_common_update_scaling_register(uint8_t addr, uint8_t index);
-
-void IS31FL_common_flush(void);
-
-#ifdef RGB_MATRIX_ENABLE
-// RGB Matrix Specific scripts
-void IS31FL_RGB_init_drivers(void);
-void IS31FL_RGB_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
-void IS31FL_RGB_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
-void IS31FL_RGB_set_scaling_buffer(uint8_t index, bool red, bool green, bool blue);
-#elif defined(LED_MATRIX_ENABLE)
-// LED Matrix Specific scripts
-void IS31FL_simple_init_drivers(void);
-void IS31FL_simple_set_scaling_buffer(uint8_t index, bool value);
-void IS31FL_simple_set_brightness(int index, uint8_t value);
-void IS31FL_simple_set_brigntness_all(uint8_t value);
-#endif
diff --git a/drivers/led/snled27351-mono.c b/drivers/led/snled27351-mono.c
new file mode 100644
index 0000000000..d87b856db6
--- /dev/null
+++ b/drivers/led/snled27351-mono.c
@@ -0,0 +1,250 @@
+/* Copyright 2021 @ Keychron (https://www.keychron.com)
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "snled27351-mono.h"
+#include "i2c_master.h"
+#include "gpio.h"
+
+#define SNLED27351_PWM_REGISTER_COUNT 192
+#define SNLED27351_LED_CONTROL_REGISTER_COUNT 24
+
+#ifndef SNLED27351_I2C_TIMEOUT
+# define SNLED27351_I2C_TIMEOUT 100
+#endif
+
+#ifndef SNLED27351_I2C_PERSISTENCE
+# define SNLED27351_I2C_PERSISTENCE 0
+#endif
+
+#ifndef SNLED27351_PHASE_CHANNEL
+# define SNLED27351_PHASE_CHANNEL SNLED27351_SCAN_PHASE_12_CHANNEL
+#endif
+
+#ifndef SNLED27351_CURRENT_TUNE
+# define SNLED27351_CURRENT_TUNE \
+ { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
+#endif
+
+const uint8_t i2c_addresses[SNLED27351_DRIVER_COUNT] = {
+ SNLED27351_I2C_ADDRESS_1,
+#ifdef SNLED27351_I2C_ADDRESS_2
+ SNLED27351_I2C_ADDRESS_2,
+# ifdef SNLED27351_I2C_ADDRESS_3
+ SNLED27351_I2C_ADDRESS_3,
+# ifdef SNLED27351_I2C_ADDRESS_4
+ SNLED27351_I2C_ADDRESS_4,
+# endif
+# endif
+#endif
+};
+
+// These buffers match the SNLED27351 PWM registers.
+// The control buffers match the PG0 LED On/Off registers.
+// Storing them like this is optimal for I2C transfers to the registers.
+// We could optimize this and take out the unused registers from these
+// buffers and the transfers in snled27351_write_pwm_buffer() but it's
+// probably not worth the extra complexity.
+typedef struct snled27351_driver_t {
+ uint8_t pwm_buffer[SNLED27351_PWM_REGISTER_COUNT];
+ bool pwm_buffer_dirty;
+ uint8_t led_control_buffer[SNLED27351_LED_CONTROL_REGISTER_COUNT];
+ bool led_control_buffer_dirty;
+} PACKED snled27351_driver_t;
+
+snled27351_driver_t driver_buffers[SNLED27351_DRIVER_COUNT] = {{
+ .pwm_buffer = {0},
+ .pwm_buffer_dirty = false,
+ .led_control_buffer = {0},
+ .led_control_buffer_dirty = false,
+}};
+
+void snled27351_write_register(uint8_t index, uint8_t reg, uint8_t data) {
+#if SNLED27351_I2C_PERSISTENCE > 0
+ for (uint8_t i = 0; i < SNLED27351_I2C_PERSISTENCE; i++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, SNLED27351_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, SNLED27351_I2C_TIMEOUT);
+#endif
+}
+
+void snled27351_select_page(uint8_t index, uint8_t page) {
+ snled27351_write_register(index, SNLED27351_REG_COMMAND, page);
+}
+
+void snled27351_write_pwm_buffer(uint8_t index) {
+ // Assumes PG1 is already selected.
+ // Transmit PWM registers in 12 transfers of 16 bytes.
+
+ // Iterate over the pwm_buffer contents at 16 byte intervals.
+ for (uint8_t i = 0; i < SNLED27351_PWM_REGISTER_COUNT; i += 16) {
+#if SNLED27351_I2C_PERSISTENCE > 0
+ for (uint8_t j = 0; j < SNLED27351_I2C_PERSISTENCE; j++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, SNLED27351_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
+ }
+#else
+ i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, SNLED27351_I2C_TIMEOUT);
+#endif
+ }
+}
+
+void snled27351_init_drivers(void) {
+ i2c_init();
+
+#if defined(SNLED27351_SDB_PIN)
+ gpio_set_pin_output(SNLED27351_SDB_PIN);
+ gpio_write_pin_high(SNLED27351_SDB_PIN);
+#endif
+
+ for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) {
+ snled27351_init(i);
+ }
+
+ for (int i = 0; i < SNLED27351_LED_COUNT; i++) {
+ snled27351_set_led_control_register(i, true);
+ }
+
+ for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) {
+ snled27351_update_led_control_registers(i);
+ }
+}
+
+void snled27351_init(uint8_t index) {
+ snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION);
+
+ // Setting LED driver to shutdown mode
+ snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN);
+ // Setting internal channel pulldown/pullup
+ snled27351_write_register(index, SNLED27351_FUNCTION_REG_PULLDOWNUP, SNLED27351_PULLDOWNUP_ALL_ENABLED);
+ // Select number of scan phase
+ snled27351_write_register(index, SNLED27351_FUNCTION_REG_SCAN_PHASE, SNLED27351_PHASE_CHANNEL);
+ // Setting PWM Delay Phase
+ snled27351_write_register(index, SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_1, SNLED27351_SLEW_RATE_CONTROL_MODE_1_PDP_ENABLE);
+ // Setting Driving/Sinking Channel Slew Rate
+ snled27351_write_register(index, SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_2, SNLED27351_SLEW_RATE_CONTROL_MODE_2_DSL_ENABLE | SNLED27351_SLEW_RATE_CONTROL_MODE_2_SSL_ENABLE);
+ // Setting Iref
+ snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP, 0);
+
+ snled27351_select_page(index, SNLED27351_COMMAND_LED_CONTROL);
+
+ for (int i = 0; i < SNLED27351_LED_CONTROL_ON_OFF_LENGTH; i++) {
+ snled27351_write_register(index, i, 0x00);
+ }
+
+ snled27351_select_page(index, SNLED27351_COMMAND_PWM);
+
+ for (int i = 0; i < SNLED27351_LED_CURRENT_TUNE_LENGTH; i++) {
+ snled27351_write_register(index, i, 0x00);
+ }
+
+ snled27351_select_page(index, SNLED27351_COMMAND_CURRENT_TUNE);
+
+ uint8_t current_tune_reg_list[SNLED27351_LED_CURRENT_TUNE_LENGTH] = SNLED27351_CURRENT_TUNE;
+ for (int i = 0; i < SNLED27351_LED_CURRENT_TUNE_LENGTH; i++) {
+ snled27351_write_register(index, i, current_tune_reg_list[i]);
+ }
+
+ snled27351_select_page(index, SNLED27351_COMMAND_LED_CONTROL);
+
+ for (int i = 0; i < SNLED27351_LED_CONTROL_ON_OFF_LENGTH; i++) {
+ snled27351_write_register(index, i, 0xFF);
+ }
+
+ snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION);
+
+ // Setting LED driver to normal mode
+ snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL);
+}
+
+void snled27351_set_value(int index, uint8_t value) {
+ snled27351_led_t led;
+ if (index >= 0 && index < SNLED27351_LED_COUNT) {
+ memcpy_P(&led, (&g_snled27351_leds[index]), sizeof(led));
+
+ if (driver_buffers[led.driver].pwm_buffer[led.v] == value) {
+ return;
+ }
+
+ driver_buffers[led.driver].pwm_buffer[led.v] = value;
+ driver_buffers[led.driver].pwm_buffer_dirty = true;
+ }
+}
+
+void snled27351_set_value_all(uint8_t value) {
+ for (int i = 0; i < SNLED27351_LED_COUNT; i++) {
+ snled27351_set_value(i, value);
+ }
+}
+
+void snled27351_set_led_control_register(uint8_t index, bool value) {
+ snled27351_led_t led;
+ memcpy_P(&led, (&g_snled27351_leds[index]), sizeof(led));
+
+ uint8_t control_register = led.v / 8;
+ uint8_t bit_value = led.v % 8;
+
+ if (value) {
+ driver_buffers[led.driver].led_control_buffer[control_register] |= (1 << bit_value);
+ } else {
+ driver_buffers[led.driver].led_control_buffer[control_register] &= ~(1 << bit_value);
+ }
+
+ driver_buffers[led.driver].led_control_buffer_dirty = true;
+}
+
+void snled27351_update_pwm_buffers(uint8_t index) {
+ if (driver_buffers[index].pwm_buffer_dirty) {
+ snled27351_select_page(index, SNLED27351_COMMAND_PWM);
+
+ snled27351_write_pwm_buffer(index);
+
+ driver_buffers[index].pwm_buffer_dirty = false;
+ }
+}
+
+void snled27351_update_led_control_registers(uint8_t index) {
+ if (driver_buffers[index].led_control_buffer_dirty) {
+ snled27351_select_page(index, SNLED27351_COMMAND_LED_CONTROL);
+
+ for (uint8_t i = 0; i < SNLED27351_LED_CONTROL_REGISTER_COUNT; i++) {
+ snled27351_write_register(index, i, driver_buffers[index].led_control_buffer[i]);
+ }
+
+ driver_buffers[index].led_control_buffer_dirty = false;
+ }
+}
+
+void snled27351_flush(void) {
+ for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) {
+ snled27351_update_pwm_buffers(i);
+ }
+}
+
+void snled27351_sw_return_normal(uint8_t index) {
+ snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION);
+
+ // Setting LED driver to normal mode
+ snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL);
+}
+
+void snled27351_sw_shutdown(uint8_t index) {
+ snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION);
+
+ // Setting LED driver to shutdown mode
+ snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN);
+ // Write SW Sleep Register
+ snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP, SNLED27351_SOFTWARE_SLEEP_ENABLE);
+}
diff --git a/drivers/led/snled27351-mono.h b/drivers/led/snled27351-mono.h
new file mode 100644
index 0000000000..43d39934cb
--- /dev/null
+++ b/drivers/led/snled27351-mono.h
@@ -0,0 +1,586 @@
+/* Copyright 2021 @ Keychron (https://www.keychron.com)
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "progmem.h"
+#include "util.h"
+
+// ======== DEPRECATED DEFINES - DO NOT USE ========
+#ifdef CKLED2001_TIMEOUT
+# define SNLED27351_I2C_TIMEOUT CKLED2001_TIMEOUT
+#endif
+#ifdef CKLED2001_PERSISTENCE
+# define SNLED27351_I2C_PERSISTENCE CKLED2001_PERSISTENCE
+#endif
+#ifdef PHASE_CHANNEL
+# define SNLED27351_PHASE_CHANNEL PHASE_CHANNEL
+#endif
+#ifdef CKLED2001_CURRENT_TUNE
+# define SNLED27351_CURRENT_TUNE CKLED2001_CURRENT_TUNE
+#endif
+
+#define MSKPHASE_12CHANNEL SNLED27351_SCAN_PHASE_12_CHANNEL
+#define MSKPHASE_11CHANNEL SNLED27351_SCAN_PHASE_11_CHANNEL
+#define MSKPHASE_10CHANNEL SNLED27351_SCAN_PHASE_10_CHANNEL
+#define MSKPHASE_9CHANNEL SNLED27351_SCAN_PHASE_9_CHANNEL
+#define MSKPHASE_8CHANNEL SNLED27351_SCAN_PHASE_8_CHANNEL
+#define MSKPHASE_7CHANNEL SNLED27351_SCAN_PHASE_7_CHANNEL
+#define MSKPHASE_6CHANNEL SNLED27351_SCAN_PHASE_6_CHANNEL
+#define MSKPHASE_5CHANNEL SNLED27351_SCAN_PHASE_5_CHANNEL
+#define MSKPHASE_4CHANNEL SNLED27351_SCAN_PHASE_4_CHANNEL
+#define MSKPHASE_3CHANNEL SNLED27351_SCAN_PHASE_3_CHANNEL
+#define MSKPHASE_2CHANNEL SNLED27351_SCAN_PHASE_2_CHANNEL
+#define MSKPHASE_1CHANNEL SNLED27351_SCAN_PHASE_1_CHANNEL
+
+#define ckled2001_led snled27351_led_t
+#define g_ckled2001_leds g_snled27351_leds
+// ========
+
+#define SNLED27351_REG_COMMAND 0xFD
+#define SNLED27351_COMMAND_LED_CONTROL 0x00
+#define SNLED27351_COMMAND_PWM 0x01
+#define SNLED27351_COMMAND_FUNCTION 0x03
+#define SNLED27351_COMMAND_CURRENT_TUNE 0x04
+
+#define SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN 0x00
+#define SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN (0x0 << 0)
+#define SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL (0x1 << 0)
+
+#define SNLED27351_FUNCTION_REG_ID 0x11
+#define SNLED27351_DRIVER_ID 0x8A
+
+#define SNLED27351_FUNCTION_REG_PULLDOWNUP 0x13
+#define SNLED27351_PULLDOWNUP_ALL_ENABLED 0xAA
+
+#define SNLED27351_FUNCTION_REG_SCAN_PHASE 0x14
+#define SNLED27351_SCAN_PHASE_12_CHANNEL 0x00
+#define SNLED27351_SCAN_PHASE_11_CHANNEL 0x01
+#define SNLED27351_SCAN_PHASE_10_CHANNEL 0x02
+#define SNLED27351_SCAN_PHASE_9_CHANNEL 0x03
+#define SNLED27351_SCAN_PHASE_8_CHANNEL 0x04
+#define SNLED27351_SCAN_PHASE_7_CHANNEL 0x05
+#define SNLED27351_SCAN_PHASE_6_CHANNEL 0x06
+#define SNLED27351_SCAN_PHASE_5_CHANNEL 0x07
+#define SNLED27351_SCAN_PHASE_4_CHANNEL 0x08
+#define SNLED27351_SCAN_PHASE_3_CHANNEL 0x09
+#define SNLED27351_SCAN_PHASE_2_CHANNEL 0x0A
+#define SNLED27351_SCAN_PHASE_1_CHANNEL 0x0B
+
+#define SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_1 0x15
+#define SNLED27351_SLEW_RATE_CONTROL_MODE_1_PDP_ENABLE (0b1 << 2)
+
+#define SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_2 0x16
+#define SNLED27351_SLEW_RATE_CONTROL_MODE_2_SSL_ENABLE (0b1 << 6)
+#define SNLED27351_SLEW_RATE_CONTROL_MODE_2_DSL_ENABLE (0b1 << 7)
+
+#define SNLED27351_FUNCTION_REG_OPEN_SHORT_ENABLE 0x17
+#define SNLED27351_OPEN_SHORT_ENABLE_SDS_ENABLE (0b1 << 6)
+#define SNLED27351_OPEN_SHORT_ENABLE_ODS_ENABLE (0b1 << 7)
+
+#define SNLED27351_FUNCTION_REG_OPEN_SHORT_DUTY 0x18
+
+#define SNLED27351_FUNCTION_REG_OPEN_SHORT_FLAG 0x19
+#define SNLED27351_OPEN_SHORT_FLAG_OSINT_ENABLE (0b1 << 6)
+#define SNLED27351_OPEN_SHORT_FLAG_ODINT_ENABLE (0b1 << 7)
+
+#define SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP 0x1A
+#define SNLED27351_SOFTWARE_SLEEP_ENABLE (0b1 << 1)
+
+// LED Control Registers
+#define SNLED27351_LED_CONTROL_ON_OFF_FIRST_ADDR 0x0
+#define SNLED27351_LED_CONTROL_ON_OFF_LAST_ADDR 0x17
+#define SNLED27351_LED_CONTROL_ON_OFF_LENGTH ((SNLED27351_LED_CONTROL_ON_OFF_LAST_ADDR - SNLED27351_LED_CONTROL_ON_OFF_FIRST_ADDR) + 1)
+
+#define SNLED27351_LED_CONTROL_OPEN_FIRST_ADDR 0x18
+#define SNLED27351_LED_CONTROL_OPEN_LAST_ADDR 0x2F
+#define SNLED27351_LED_CONTROL_OPEN_LENGTH ((SNLED27351_LED_CONTROL_OPEN_LAST_ADDR - SNLED27351_LED_CONTROL_OPEN_FIRST_ADDR) + 1)
+
+#define SNLED27351_LED_CONTROL_SHORT_FIRST_ADDR 0x30
+#define SNLED27351_LED_CONTROL_SHORT_LAST_ADDR 0x47
+#define SNLED27351_LED_CONTROL_SHORT_LENGTH ((SNLED27351_LED_CONTROL_SHORT_LAST_ADDR - SNLED27351_LED_CONTROL_SHORT_FIRST_ADDR) + 1)
+
+#define SNLED27351_LED_CONTROL_PAGE_LENGTH 0x48
+
+// LED Control Registers
+#define SNLED27351_LED_PWM_FIRST_ADDR 0x00
+#define SNLED27351_LED_PWM_LAST_ADDR 0xBF
+#define SNLED27351_LED_PWM_LENGTH 0xC0
+
+// Current Tune Registers
+#define SNLED27351_LED_CURRENT_TUNE_FIRST_ADDR 0x00
+#define SNLED27351_LED_CURRENT_TUNE_LAST_ADDR 0x0B
+#define SNLED27351_LED_CURRENT_TUNE_LENGTH 0x0C
+
+#define SNLED27351_I2C_ADDRESS_GND 0x74
+#define SNLED27351_I2C_ADDRESS_SCL 0x75
+#define SNLED27351_I2C_ADDRESS_SDA 0x76
+#define SNLED27351_I2C_ADDRESS_VDDIO 0x77
+
+#if defined(LED_MATRIX_SNLED27351)
+# define SNLED27351_LED_COUNT LED_MATRIX_LED_COUNT
+#endif
+
+#if defined(SNLED27351_I2C_ADDRESS_4)
+# define SNLED27351_DRIVER_COUNT 4
+#elif defined(SNLED27351_I2C_ADDRESS_3)
+# define SNLED27351_DRIVER_COUNT 3
+#elif defined(SNLED27351_I2C_ADDRESS_2)
+# define SNLED27351_DRIVER_COUNT 2
+#elif defined(SNLED27351_I2C_ADDRESS_1)
+# define SNLED27351_DRIVER_COUNT 1
+#endif
+
+typedef struct snled27351_led_t {
+ uint8_t driver : 2;
+ uint8_t v;
+} PACKED snled27351_led_t;
+
+extern const snled27351_led_t PROGMEM g_snled27351_leds[SNLED27351_LED_COUNT];
+
+void snled27351_init_drivers(void);
+void snled27351_init(uint8_t index);
+void snled27351_select_page(uint8_t index, uint8_t page);
+void snled27351_write_register(uint8_t index, uint8_t reg, uint8_t data);
+
+void snled27351_set_value(int index, uint8_t value);
+void snled27351_set_value_all(uint8_t value);
+
+void snled27351_set_led_control_register(uint8_t index, bool value);
+
+// 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 snled27351_update_pwm_buffers(uint8_t index);
+void snled27351_update_led_control_registers(uint8_t index);
+
+void snled27351_flush(void);
+
+void snled27351_sw_return_normal(uint8_t index);
+void snled27351_sw_shutdown(uint8_t index);
+
+#define CB1_CA1 0x00
+#define CB1_CA2 0x01
+#define CB1_CA3 0x02
+#define CB1_CA4 0x03
+#define CB1_CA5 0x04
+#define CB1_CA6 0x05
+#define CB1_CA7 0x06
+#define CB1_CA8 0x07
+#define CB1_CA9 0x08
+#define CB1_CA10 0x09
+#define CB1_CA11 0x0A
+#define CB1_CA12 0x0B
+#define CB1_CA13 0x0C
+#define CB1_CA14 0x0D
+#define CB1_CA15 0x0E
+#define CB1_CA16 0x0F
+
+#define CB2_CA1 0x10
+#define CB2_CA2 0x11
+#define CB2_CA3 0x12
+#define CB2_CA4 0x13
+#define CB2_CA5 0x14
+#define CB2_CA6 0x15
+#define CB2_CA7 0x16
+#define CB2_CA8 0x17
+#define CB2_CA9 0x18
+#define CB2_CA10 0x19
+#define CB2_CA11 0x1A
+#define CB2_CA12 0x1B
+#define CB2_CA13 0x1C
+#define CB2_CA14 0x1D
+#define CB2_CA15 0x1E
+#define CB2_CA16 0x1F
+
+#define CB3_CA1 0x20
+#define CB3_CA2 0x21
+#define CB3_CA3 0x22
+#define CB3_CA4 0x23
+#define CB3_CA5 0x24
+#define CB3_CA6 0x25
+#define CB3_CA7 0x26
+#define CB3_CA8 0x27
+#define CB3_CA9 0x28
+#define CB3_CA10 0x29
+#define CB3_CA11 0x2A
+#define CB3_CA12 0x2B
+#define CB3_CA13 0x2C
+#define CB3_CA14 0x2D
+#define CB3_CA15 0x2E
+#define CB3_CA16 0x2F
+
+#define CB4_CA1 0x30
+#define CB4_CA2 0x31
+#define CB4_CA3 0x32
+#define CB4_CA4 0x33
+#define CB4_CA5 0x34
+#define CB4_CA6 0x35
+#define CB4_CA7 0x36
+#define CB4_CA8 0x37
+#define CB4_CA9 0x38
+#define CB4_CA10 0x39
+#define CB4_CA11 0x3A
+#define CB4_CA12 0x3B
+#define CB4_CA13 0x3C
+#define CB4_CA14 0x3D
+#define CB4_CA15 0x3E
+#define CB4_CA16 0x3F
+
+#define CB5_CA1 0x40
+#define CB5_CA2 0x41
+#define CB5_CA3 0x42
+#define CB5_CA4 0x43
+#define CB5_CA5 0x44
+#define CB5_CA6 0x45
+#define CB5_CA7 0x46
+#define CB5_CA8 0x47
+#define CB5_CA9 0x48
+#define CB5_CA10 0x49
+#define CB5_CA11 0x4A
+#define CB5_CA12 0x4B
+#define CB5_CA13 0x4C
+#define CB5_CA14 0x4D
+#define CB5_CA15 0x4E
+#define CB5_CA16 0x4F
+
+#define CB6_CA1 0x50
+#define CB6_CA2 0x51
+#define CB6_CA3 0x52
+#define CB6_CA4 0x53
+#define CB6_CA5 0x54
+#define CB6_CA6 0x55
+#define CB6_CA7 0x56
+#define CB6_CA8 0x57
+#define CB6_CA9 0x58
+#define CB6_CA10 0x59
+#define CB6_CA11 0x5A
+#define CB6_CA12 0x5B
+#define CB6_CA13 0x5C
+#define CB6_CA14 0x5D
+#define CB6_CA15 0x5E
+#define CB6_CA16 0x5F
+
+#define CB7_CA1 0x60
+#define CB7_CA2 0x61
+#define CB7_CA3 0x62
+#define CB7_CA4 0x63
+#define CB7_CA5 0x64
+#define CB7_CA6 0x65
+#define CB7_CA7 0x66
+#define CB7_CA8 0x67
+#define CB7_CA9 0x68
+#define CB7_CA10 0x69
+#define CB7_CA11 0x6A
+#define CB7_CA12 0x6B
+#define CB7_CA13 0x6C
+#define CB7_CA14 0x6D
+#define CB7_CA15 0x6E
+#define CB7_CA16 0x6F
+
+#define CB8_CA1 0x70
+#define CB8_CA2 0x71
+#define CB8_CA3 0x72
+#define CB8_CA4 0x73
+#define CB8_CA5 0x74
+#define CB8_CA6 0x75
+#define CB8_CA7 0x76
+#define CB8_CA8 0x77
+#define CB8_CA9 0x78
+#define CB8_CA10 0x79
+#define CB8_CA11 0x7A
+#define CB8_CA12 0x7B
+#define CB8_CA13 0x7C
+#define CB8_CA14 0x7D
+#define CB8_CA15 0x7E
+#define CB8_CA16 0x7F
+
+#define CB9_CA1 0x80
+#define CB9_CA2 0x81
+#define CB9_CA3 0x82
+#define CB9_CA4 0x83
+#define CB9_CA5 0x84
+#define CB9_CA6 0x85
+#define CB9_CA7 0x86
+#define CB9_CA8 0x87
+#define CB9_CA9 0x88
+#define CB9_CA10 0x89
+#define CB9_CA11 0x8A
+#define CB9_CA12 0x8B
+#define CB9_CA13 0x8C
+#define CB9_CA14 0x8D
+#define CB9_CA15 0x8E
+#define CB9_CA16 0x8F
+
+#define CB10_CA1 0x90
+#define CB10_CA2 0x91
+#define CB10_CA3 0x92
+#define CB10_CA4 0x93
+#define CB10_CA5 0x94
+#define CB10_CA6 0x95
+#define CB10_CA7 0x96
+#define CB10_CA8 0x97
+#define CB10_CA9 0x98
+#define CB10_CA10 0x99
+#define CB10_CA11 0x9A
+#define CB10_CA12 0x9B
+#define CB10_CA13 0x9C
+#define CB10_CA14 0x9D
+#define CB10_CA15 0x9E
+#define CB10_CA16 0x9F
+
+#define CB11_CA1 0xA0
+#define CB11_CA2 0xA1
+#define CB11_CA3 0xA2
+#define CB11_CA4 0xA3
+#define CB11_CA5 0xA4
+#define CB11_CA6 0xA5
+#define CB11_CA7 0xA6
+#define CB11_CA8 0xA7
+#define CB11_CA9 0xA8
+#define CB11_CA10 0xA9
+#define CB11_CA11 0xAA
+#define CB11_CA12 0xAB
+#define CB11_CA13 0xAC
+#define CB11_CA14 0xAD
+#define CB11_CA15 0xAE
+#define CB11_CA16 0xAF
+
+#define CB12_CA1 0xB0
+#define CB12_CA2 0xB1
+#define CB12_CA3 0xB2
+#define CB12_CA4 0xB3
+#define CB12_CA5 0xB4
+#define CB12_CA6 0xB5
+#define CB12_CA7 0xB6
+#define CB12_CA8 0xB7
+#define CB12_CA9 0xB8
+#define CB12_CA10 0xB9
+#define CB12_CA11 0xBA
+#define CB12_CA12 0xBB
+#define CB12_CA13 0xBC
+#define CB12_CA14 0xBD
+#define CB12_CA15 0xBE
+#define CB12_CA16 0xBF
+
+// DEPRECATED - DO NOT USE
+
+#define A_1 CB1_CA1
+#define A_2 CB1_CA2
+#define A_3 CB1_CA3
+#define A_4 CB1_CA4
+#define A_5 CB1_CA5
+#define A_6 CB1_CA6
+#define A_7 CB1_CA7
+#define A_8 CB1_CA8
+#define A_9 CB1_CA9
+#define A_10 CB1_CA10
+#define A_11 CB1_CA11
+#define A_12 CB1_CA12
+#define A_13 CB1_CA13
+#define A_14 CB1_CA14
+#define A_15 CB1_CA15
+#define A_16 CB1_CA16
+
+#define B_1 CB2_CA1
+#define B_2 CB2_CA2
+#define B_3 CB2_CA3
+#define B_4 CB2_CA4
+#define B_5 CB2_CA5
+#define B_6 CB2_CA6
+#define B_7 CB2_CA7
+#define B_8 CB2_CA8
+#define B_9 CB2_CA9
+#define B_10 CB2_CA10
+#define B_11 CB2_CA11
+#define B_12 CB2_CA12
+#define B_13 CB2_CA13
+#define B_14 CB2_CA14
+#define B_15 CB2_CA15
+#define B_16 CB2_CA16
+
+#define C_1 CB3_CA1
+#define C_2 CB3_CA2
+#define C_3 CB3_CA3
+#define C_4 CB3_CA4
+#define C_5 CB3_CA5
+#define C_6 CB3_CA6
+#define C_7 CB3_CA7
+#define C_8 CB3_CA8
+#define C_9 CB3_CA9
+#define C_10 CB3_CA10
+#define C_11 CB3_CA11
+#define C_12 CB3_CA12
+#define C_13 CB3_CA13
+#define C_14 CB3_CA14
+#define C_15 CB3_CA15
+#define C_16 CB3_CA16
+
+#define D_1 CB4_CA1
+#define D_2 CB4_CA2
+#define D_3 CB4_CA3
+#define D_4 CB4_CA4
+#define D_5 CB4_CA5
+#define D_6 CB4_CA6
+#define D_7 CB4_CA7
+#define D_8 CB4_CA8
+#define D_9 CB4_CA9
+#define D_10 CB4_CA10
+#define D_11 CB4_CA11
+#define D_12 CB4_CA12
+#define D_13 CB4_CA13
+#define D_14 CB4_CA14
+#define D_15 CB4_CA15
+#define D_16 CB4_CA16
+
+#define E_1 CB5_CA1
+#define E_2 CB5_CA2
+#define E_3 CB5_CA3
+#define E_4 CB5_CA4
+#define E_5 CB5_CA5
+#define E_6 CB5_CA6
+#define E_7 CB5_CA7
+#define E_8 CB5_CA8
+#define E_9 CB5_CA9
+#define E_10 CB5_CA10
+#define E_11 CB5_CA11
+#define E_12 CB5_CA12
+#define E_13 CB5_CA13
+#define E_14 CB5_CA14
+#define E_15 CB5_CA15
+#define E_16 CB5_CA16
+
+#define F_1 CB6_CA1
+#define F_2 CB6_CA2
+#define F_3 CB6_CA3
+#define F_4 CB6_CA4
+#define F_5 CB6_CA5
+#define F_6 CB6_CA6
+#define F_7 CB6_CA7
+#define F_8 CB6_CA8
+#define F_9 CB6_CA9
+#define F_10 CB6_CA10
+#define F_11 CB6_CA11
+#define F_12 CB6_CA12
+#define F_13 CB6_CA13
+#define F_14 CB6_CA14
+#define F_15 CB6_CA15
+#define F_16 CB6_CA16
+
+#define G_1 CB7_CA1
+#define G_2 CB7_CA2
+#define G_3 CB7_CA3
+#define G_4 CB7_CA4
+#define G_5 CB7_CA5
+#define G_6 CB7_CA6
+#define G_7 CB7_CA7
+#define G_8 CB7_CA8
+#define G_9 CB7_CA9
+#define G_10 CB7_CA10
+#define G_11 CB7_CA11
+#define G_12 CB7_CA12
+#define G_13 CB7_CA13
+#define G_14 CB7_CA14
+#define G_15 CB7_CA15
+#define G_16 CB7_CA16
+
+#define H_1 CB8_CA1
+#define H_2 CB8_CA2
+#define H_3 CB8_CA3
+#define H_4 CB8_CA4
+#define H_5 CB8_CA5
+#define H_6 CB8_CA6
+#define H_7 CB8_CA7
+#define H_8 CB8_CA8
+#define H_9 CB8_CA9
+#define H_10 CB8_CA10
+#define H_11 CB8_CA11
+#define H_12 CB8_CA12
+#define H_13 CB8_CA13
+#define H_14 CB8_CA14
+#define H_15 CB8_CA15
+#define H_16 CB8_CA16
+
+#define I_1 CB9_CA1
+#define I_2 CB9_CA2
+#define I_3 CB9_CA3
+#define I_4 CB9_CA4
+#define I_5 CB9_CA5
+#define I_6 CB9_CA6
+#define I_7 CB9_CA7
+#define I_8 CB9_CA8
+#define I_9 CB9_CA9
+#define I_10 CB9_CA10
+#define I_11 CB9_CA11
+#define I_12 CB9_CA12
+#define I_13 CB9_CA13
+#define I_14 CB9_CA14
+#define I_15 CB9_CA15
+#define I_16 CB9_CA16
+
+#define J_1 CB10_CA1
+#define J_2 CB10_CA2
+#define J_3 CB10_CA3
+#define J_4 CB10_CA4
+#define J_5 CB10_CA5
+#define J_6 CB10_CA6
+#define J_7 CB10_CA7
+#define J_8 CB10_CA8
+#define J_9 CB10_CA9
+#define J_10 CB10_CA10
+#define J_11 CB10_CA11
+#define J_12 CB10_CA12
+#define J_13 CB10_CA13
+#define J_14 CB10_CA14
+#define J_15 CB10_CA15
+#define J_16 CB10_CA16
+
+#define K_1 CB11_CA1
+#define K_2 CB11_CA2
+#define K_3 CB11_CA3
+#define K_4 CB11_CA4
+#define K_5 CB11_CA5
+#define K_6 CB11_CA6
+#define K_7 CB11_CA7
+#define K_8 CB11_CA8
+#define K_9 CB11_CA9
+#define K_10 CB11_CA10
+#define K_11 CB11_CA11
+#define K_12 CB11_CA12
+#define K_13 CB11_CA13
+#define K_14 CB11_CA14
+#define K_15 CB11_CA15
+#define K_16 CB11_CA16
+
+#define L_1 CB12_CA1
+#define L_2 CB12_CA2
+#define L_3 CB12_CA3
+#define L_4 CB12_CA4
+#define L_5 CB12_CA5
+#define L_6 CB12_CA6
+#define L_7 CB12_CA7
+#define L_8 CB12_CA8
+#define L_9 CB12_CA9
+#define L_10 CB12_CA10
+#define L_11 CB12_CA11
+#define L_12 CB12_CA12
+#define L_13 CB12_CA13
+#define L_14 CB12_CA14
+#define L_15 CB12_CA15
+#define L_16 CB12_CA16
diff --git a/drivers/led/snled27351-simple.c b/drivers/led/snled27351-simple.c
deleted file mode 100644
index b2054c96d5..0000000000
--- a/drivers/led/snled27351-simple.c
+++ /dev/null
@@ -1,266 +0,0 @@
-/* Copyright 2021 @ Keychron (https://www.keychron.com)
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#include "snled27351-simple.h"
-#include "i2c_master.h"
-
-#define SNLED27351_PWM_REGISTER_COUNT 192
-#define SNLED27351_LED_CONTROL_REGISTER_COUNT 24
-
-#ifndef SNLED27351_I2C_TIMEOUT
-# define SNLED27351_I2C_TIMEOUT 100
-#endif
-
-#ifndef SNLED27351_I2C_PERSISTENCE
-# define SNLED27351_I2C_PERSISTENCE 0
-#endif
-
-#ifndef SNLED27351_PHASE_CHANNEL
-# define SNLED27351_PHASE_CHANNEL SNLED27351_SCAN_PHASE_12_CHANNEL
-#endif
-
-#ifndef SNLED27351_CURRENT_TUNE
-# define SNLED27351_CURRENT_TUNE \
- { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
-#endif
-
-// Transfer buffer for TWITransmitData()
-uint8_t g_twi_transfer_buffer[20];
-
-// These buffers match the SNLED27351 PWM registers.
-// The control buffers match the PG0 LED On/Off registers.
-// Storing them like this is optimal for I2C transfers to the registers.
-// We could optimize this and take out the unused registers from these
-// buffers and the transfers in snled27351_write_pwm_buffer() but it's
-// probably not worth the extra complexity.
-uint8_t g_pwm_buffer[SNLED27351_DRIVER_COUNT][SNLED27351_PWM_REGISTER_COUNT];
-bool g_pwm_buffer_update_required[SNLED27351_DRIVER_COUNT] = {false};
-
-uint8_t g_led_control_registers[SNLED27351_DRIVER_COUNT][SNLED27351_LED_CONTROL_REGISTER_COUNT] = {0};
-bool g_led_control_registers_update_required[SNLED27351_DRIVER_COUNT] = {false};
-
-bool snled27351_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
- // If the transaction fails function returns false.
- g_twi_transfer_buffer[0] = reg;
- g_twi_transfer_buffer[1] = data;
-
-#if SNLED27351_I2C_PERSISTENCE > 0
- for (uint8_t i = 0; i < SNLED27351_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, SNLED27351_I2C_TIMEOUT) != 0) {
- return false;
- }
- }
-#else
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, SNLED27351_I2C_TIMEOUT) != 0) {
- return false;
- }
-#endif
- return true;
-}
-
-bool snled27351_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
- // Assumes PG1 is already selected.
- // If any of the transactions fails function returns false.
- // Transmit PWM registers in 12 transfers of 16 bytes.
- // g_twi_transfer_buffer[] is 20 bytes
-
- // Iterate over the pwm_buffer contents at 16 byte intervals.
- for (int i = 0; i < SNLED27351_PWM_REGISTER_COUNT; i += 16) {
- g_twi_transfer_buffer[0] = i;
- // Copy the data from i to i+15.
- // Device will auto-increment register for data after the first byte
- // Thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer.
- for (int j = 0; j < 16; j++) {
- g_twi_transfer_buffer[1 + j] = pwm_buffer[i + j];
- }
-
-#if SNLED27351_I2C_PERSISTENCE > 0
- for (uint8_t i = 0; i < SNLED27351_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, SNLED27351_I2C_TIMEOUT) != 0) {
- return false;
- }
- }
-#else
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, SNLED27351_I2C_TIMEOUT) != 0) {
- return false;
- }
-#endif
- }
- return true;
-}
-
-void snled27351_init_drivers(void) {
- i2c_init();
-
- snled27351_init(SNLED27351_I2C_ADDRESS_1);
-#if defined(SNLED27351_I2C_ADDRESS_2)
- snled27351_init(SNLED27351_I2C_ADDRESS_2);
-# if defined(SNLED27351_I2C_ADDRESS_3)
- snled27351_init(SNLED27351_I2C_ADDRESS_3);
-# if defined(SNLED27351_I2C_ADDRESS_4)
- snled27351_init(SNLED27351_I2C_ADDRESS_4);
-# endif
-# endif
-#endif
-
- for (int i = 0; i < SNLED27351_LED_COUNT; i++) {
- snled27351_set_led_control_register(i, true);
- }
-
- snled27351_update_led_control_registers(SNLED27351_I2C_ADDRESS_1, 0);
-#if defined(SNLED27351_I2C_ADDRESS_2)
- snled27351_update_led_control_registers(SNLED27351_I2C_ADDRESS_2, 1);
-# if defined(SNLED27351_I2C_ADDRESS_3)
- snled27351_update_led_control_registers(SNLED27351_I2C_ADDRESS_3, 2);
-# if defined(SNLED27351_I2C_ADDRESS_4)
- snled27351_update_led_control_registers(SNLED27351_I2C_ADDRESS_4, 3);
-# endif
-# endif
-#endif
-}
-
-void snled27351_init(uint8_t addr) {
- // Select to function page
- snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_FUNCTION);
- // Setting LED driver to shutdown mode
- snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN);
- // Setting internal channel pulldown/pullup
- snled27351_write_register(addr, SNLED27351_FUNCTION_REG_PULLDOWNUP, SNLED27351_PULLDOWNUP_ALL_ENABLED);
- // Select number of scan phase
- snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SCAN_PHASE, SNLED27351_PHASE_CHANNEL);
- // Setting PWM Delay Phase
- snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_1, SNLED27351_SLEW_RATE_CONTROL_MODE_1_PDP_ENABLE);
- // Setting Driving/Sinking Channel Slew Rate
- snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_2, SNLED27351_SLEW_RATE_CONTROL_MODE_2_DSL_ENABLE | SNLED27351_SLEW_RATE_CONTROL_MODE_2_SSL_ENABLE);
- // Setting Iref
- snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP, 0);
- // Set LED CONTROL PAGE (Page 0)
- snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_LED_CONTROL);
- for (int i = 0; i < SNLED27351_LED_CONTROL_ON_OFF_LENGTH; i++) {
- snled27351_write_register(addr, i, 0x00);
- }
-
- // Set PWM PAGE (Page 1)
- snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_PWM);
- for (int i = 0; i < SNLED27351_LED_CURRENT_TUNE_LENGTH; i++) {
- snled27351_write_register(addr, i, 0x00);
- }
-
- // Set CURRENT PAGE (Page 4)
- uint8_t current_tune_reg_list[SNLED27351_LED_CURRENT_TUNE_LENGTH] = SNLED27351_CURRENT_TUNE;
- snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_CURRENT_TUNE);
- for (int i = 0; i < SNLED27351_LED_CURRENT_TUNE_LENGTH; i++) {
- snled27351_write_register(addr, i, current_tune_reg_list[i]);
- }
-
- // Enable LEDs ON/OFF
- snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_LED_CONTROL);
- for (int i = 0; i < SNLED27351_LED_CONTROL_ON_OFF_LENGTH; i++) {
- snled27351_write_register(addr, i, 0xFF);
- }
-
- // Select to function page
- snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_FUNCTION);
- // Setting LED driver to normal mode
- snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL);
-}
-
-void snled27351_set_value(int index, uint8_t value) {
- snled27351_led_t led;
- if (index >= 0 && index < SNLED27351_LED_COUNT) {
- memcpy_P(&led, (&g_snled27351_leds[index]), sizeof(led));
-
- if (g_pwm_buffer[led.driver][led.v] == value) {
- return;
- }
- g_pwm_buffer[led.driver][led.v] = value;
- g_pwm_buffer_update_required[led.driver] = true;
- }
-}
-
-void snled27351_set_value_all(uint8_t value) {
- for (int i = 0; i < SNLED27351_LED_COUNT; i++) {
- snled27351_set_value(i, value);
- }
-}
-
-void snled27351_set_led_control_register(uint8_t index, bool value) {
- snled27351_led_t led;
- memcpy_P(&led, (&g_snled27351_leds[index]), sizeof(led));
-
- uint8_t control_register = led.v / 8;
- uint8_t bit_value = led.v % 8;
-
- if (value) {
- g_led_control_registers[led.driver][control_register] |= (1 << bit_value);
- } else {
- g_led_control_registers[led.driver][control_register] &= ~(1 << bit_value);
- }
-
- g_led_control_registers_update_required[led.driver] = true;
-}
-
-void snled27351_update_pwm_buffers(uint8_t addr, uint8_t index) {
- if (g_pwm_buffer_update_required[index]) {
- snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_PWM);
-
- // If any of the transactions fail we risk writing dirty PG0,
- // refresh page 0 just in case.
- if (!snled27351_write_pwm_buffer(addr, g_pwm_buffer[index])) {
- g_led_control_registers_update_required[index] = true;
- }
- }
- g_pwm_buffer_update_required[index] = false;
-}
-
-void snled27351_update_led_control_registers(uint8_t addr, uint8_t index) {
- if (g_led_control_registers_update_required[index]) {
- snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_LED_CONTROL);
- for (int i = 0; i < SNLED27351_LED_CONTROL_REGISTER_COUNT; i++) {
- snled27351_write_register(addr, i, g_led_control_registers[index][i]);
- }
- }
- g_led_control_registers_update_required[index] = false;
-}
-
-void snled27351_flush(void) {
- snled27351_update_pwm_buffers(SNLED27351_I2C_ADDRESS_1, 0);
-#if defined(SNLED27351_I2C_ADDRESS_2)
- snled27351_update_pwm_buffers(SNLED27351_I2C_ADDRESS_2, 1);
-# if defined(SNLED27351_I2C_ADDRESS_3)
- snled27351_update_pwm_buffers(SNLED27351_I2C_ADDRESS_3, 2);
-# if defined(SNLED27351_I2C_ADDRESS_4)
- snled27351_update_pwm_buffers(SNLED27351_I2C_ADDRESS_4, 3);
-# endif
-# endif
-#endif
-}
-
-void snled27351_sw_return_normal(uint8_t addr) {
- // Select to function page
- snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_FUNCTION);
- // Setting LED driver to normal mode
- snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL);
-}
-
-void snled27351_sw_shutdown(uint8_t addr) {
- // Select to function page
- snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_FUNCTION);
- // Setting LED driver to shutdown mode
- snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN);
- // Write SW Sleep Register
- snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP, SNLED27351_SOFTWARE_SLEEP_ENABLE);
-}
diff --git a/drivers/led/snled27351-simple.h b/drivers/led/snled27351-simple.h
deleted file mode 100644
index 2fc62a6f0a..0000000000
--- a/drivers/led/snled27351-simple.h
+++ /dev/null
@@ -1,380 +0,0 @@
-/* Copyright 2021 @ Keychron (https://www.keychron.com)
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-#include <stdint.h>
-#include <stdbool.h>
-#include "progmem.h"
-#include "util.h"
-
-// ======== DEPRECATED DEFINES - DO NOT USE ========
-#ifdef CKLED2001_TIMEOUT
-# define SNLED27351_I2C_TIMEOUT CKLED2001_TIMEOUT
-#endif
-#ifdef CKLED2001_PERSISTENCE
-# define SNLED27351_I2C_PERSISTENCE CKLED2001_PERSISTENCE
-#endif
-#ifdef PHASE_CHANNEL
-# define SNLED27351_PHASE_CHANNEL PHASE_CHANNEL
-#endif
-#ifdef CKLED2001_CURRENT_TUNE
-# define SNLED27351_CURRENT_TUNE CKLED2001_CURRENT_TUNE
-#endif
-
-#define MSKPHASE_12CHANNEL SNLED27351_SCAN_PHASE_12_CHANNEL
-#define MSKPHASE_11CHANNEL SNLED27351_SCAN_PHASE_11_CHANNEL
-#define MSKPHASE_10CHANNEL SNLED27351_SCAN_PHASE_10_CHANNEL
-#define MSKPHASE_9CHANNEL SNLED27351_SCAN_PHASE_9_CHANNEL
-#define MSKPHASE_8CHANNEL SNLED27351_SCAN_PHASE_8_CHANNEL
-#define MSKPHASE_7CHANNEL SNLED27351_SCAN_PHASE_7_CHANNEL
-#define MSKPHASE_6CHANNEL SNLED27351_SCAN_PHASE_6_CHANNEL
-#define MSKPHASE_5CHANNEL SNLED27351_SCAN_PHASE_5_CHANNEL
-#define MSKPHASE_4CHANNEL SNLED27351_SCAN_PHASE_4_CHANNEL
-#define MSKPHASE_3CHANNEL SNLED27351_SCAN_PHASE_3_CHANNEL
-#define MSKPHASE_2CHANNEL SNLED27351_SCAN_PHASE_2_CHANNEL
-#define MSKPHASE_1CHANNEL SNLED27351_SCAN_PHASE_1_CHANNEL
-
-#define ckled2001_led snled27351_led_t
-#define g_ckled2001_leds g_snled27351_leds
-// ========
-
-#define SNLED27351_REG_COMMAND 0xFD
-#define SNLED27351_COMMAND_LED_CONTROL 0x00
-#define SNLED27351_COMMAND_PWM 0x01
-#define SNLED27351_COMMAND_FUNCTION 0x03
-#define SNLED27351_COMMAND_CURRENT_TUNE 0x04
-
-#define SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN 0x00
-#define SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN (0x0 << 0)
-#define SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL (0x1 << 0)
-
-#define SNLED27351_FUNCTION_REG_ID 0x11
-#define SNLED27351_DRIVER_ID 0x8A
-
-#define SNLED27351_FUNCTION_REG_PULLDOWNUP 0x13
-#define SNLED27351_PULLDOWNUP_ALL_ENABLED 0xAA
-
-#define SNLED27351_FUNCTION_REG_SCAN_PHASE 0x14
-#define SNLED27351_SCAN_PHASE_12_CHANNEL 0x00
-#define SNLED27351_SCAN_PHASE_11_CHANNEL 0x01
-#define SNLED27351_SCAN_PHASE_10_CHANNEL 0x02
-#define SNLED27351_SCAN_PHASE_9_CHANNEL 0x03
-#define SNLED27351_SCAN_PHASE_8_CHANNEL 0x04
-#define SNLED27351_SCAN_PHASE_7_CHANNEL 0x05
-#define SNLED27351_SCAN_PHASE_6_CHANNEL 0x06
-#define SNLED27351_SCAN_PHASE_5_CHANNEL 0x07
-#define SNLED27351_SCAN_PHASE_4_CHANNEL 0x08
-#define SNLED27351_SCAN_PHASE_3_CHANNEL 0x09
-#define SNLED27351_SCAN_PHASE_2_CHANNEL 0x0A
-#define SNLED27351_SCAN_PHASE_1_CHANNEL 0x0B
-
-#define SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_1 0x15
-#define SNLED27351_SLEW_RATE_CONTROL_MODE_1_PDP_ENABLE (0b1 << 2)
-
-#define SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_2 0x16
-#define SNLED27351_SLEW_RATE_CONTROL_MODE_2_SSL_ENABLE (0b1 << 6)
-#define SNLED27351_SLEW_RATE_CONTROL_MODE_2_DSL_ENABLE (0b1 << 7)
-
-#define SNLED27351_FUNCTION_REG_OPEN_SHORT_ENABLE 0x17
-#define SNLED27351_OPEN_SHORT_ENABLE_SDS_ENABLE (0b1 << 6)
-#define SNLED27351_OPEN_SHORT_ENABLE_ODS_ENABLE (0b1 << 7)
-
-#define SNLED27351_FUNCTION_REG_OPEN_SHORT_DUTY 0x18
-
-#define SNLED27351_FUNCTION_REG_OPEN_SHORT_FLAG 0x19
-#define SNLED27351_OPEN_SHORT_FLAG_OSINT_ENABLE (0b1 << 6)
-#define SNLED27351_OPEN_SHORT_FLAG_ODINT_ENABLE (0b1 << 7)
-
-#define SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP 0x1A
-#define SNLED27351_SOFTWARE_SLEEP_ENABLE (0b1 << 1)
-
-// LED Control Registers
-#define SNLED27351_LED_CONTROL_ON_OFF_FIRST_ADDR 0x0
-#define SNLED27351_LED_CONTROL_ON_OFF_LAST_ADDR 0x17
-#define SNLED27351_LED_CONTROL_ON_OFF_LENGTH ((SNLED27351_LED_CONTROL_ON_OFF_LAST_ADDR - SNLED27351_LED_CONTROL_ON_OFF_FIRST_ADDR) + 1)
-
-#define SNLED27351_LED_CONTROL_OPEN_FIRST_ADDR 0x18
-#define SNLED27351_LED_CONTROL_OPEN_LAST_ADDR 0x2F
-#define SNLED27351_LED_CONTROL_OPEN_LENGTH ((SNLED27351_LED_CONTROL_OPEN_LAST_ADDR - SNLED27351_LED_CONTROL_OPEN_FIRST_ADDR) + 1)
-
-#define SNLED27351_LED_CONTROL_SHORT_FIRST_ADDR 0x30
-#define SNLED27351_LED_CONTROL_SHORT_LAST_ADDR 0x47
-#define SNLED27351_LED_CONTROL_SHORT_LENGTH ((SNLED27351_LED_CONTROL_SHORT_LAST_ADDR - SNLED27351_LED_CONTROL_SHORT_FIRST_ADDR) + 1)
-
-#define SNLED27351_LED_CONTROL_PAGE_LENGTH 0x48
-
-// LED Control Registers
-#define SNLED27351_LED_PWM_FIRST_ADDR 0x00
-#define SNLED27351_LED_PWM_LAST_ADDR 0xBF
-#define SNLED27351_LED_PWM_LENGTH 0xC0
-
-// Current Tune Registers
-#define SNLED27351_LED_CURRENT_TUNE_FIRST_ADDR 0x00
-#define SNLED27351_LED_CURRENT_TUNE_LAST_ADDR 0x0B
-#define SNLED27351_LED_CURRENT_TUNE_LENGTH 0x0C
-
-#define SNLED27351_I2C_ADDRESS_GND 0x74
-#define SNLED27351_I2C_ADDRESS_SCL 0x75
-#define SNLED27351_I2C_ADDRESS_SDA 0x76
-#define SNLED27351_I2C_ADDRESS_VDDIO 0x77
-
-#if defined(LED_MATRIX_SNLED27351)
-# define SNLED27351_LED_COUNT LED_MATRIX_LED_COUNT
-#endif
-
-#if defined(SNLED27351_I2C_ADDRESS_4)
-# define SNLED27351_DRIVER_COUNT 4
-#elif defined(SNLED27351_I2C_ADDRESS_3)
-# define SNLED27351_DRIVER_COUNT 3
-#elif defined(SNLED27351_I2C_ADDRESS_2)
-# define SNLED27351_DRIVER_COUNT 2
-#elif defined(SNLED27351_I2C_ADDRESS_1)
-# define SNLED27351_DRIVER_COUNT 1
-#endif
-
-typedef struct snled27351_led_t {
- uint8_t driver : 2;
- uint8_t v;
-} PACKED snled27351_led_t;
-
-extern const snled27351_led_t PROGMEM g_snled27351_leds[SNLED27351_LED_COUNT];
-
-void snled27351_init_drivers(void);
-void snled27351_init(uint8_t addr);
-bool snled27351_write_register(uint8_t addr, uint8_t reg, uint8_t data);
-bool snled27351_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
-
-void snled27351_set_value(int index, uint8_t value);
-void snled27351_set_value_all(uint8_t value);
-
-void snled27351_set_led_control_register(uint8_t index, bool value);
-
-// 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 snled27351_update_pwm_buffers(uint8_t addr, uint8_t index);
-void snled27351_update_led_control_registers(uint8_t addr, uint8_t index);
-
-void snled27351_flush(void);
-
-void snled27351_sw_return_normal(uint8_t addr);
-void snled27351_sw_shutdown(uint8_t addr);
-
-#define A_1 0x00
-#define A_2 0x01
-#define A_3 0x02
-#define A_4 0x03
-#define A_5 0x04
-#define A_6 0x05
-#define A_7 0x06
-#define A_8 0x07
-#define A_9 0x08
-#define A_10 0x09
-#define A_11 0x0A
-#define A_12 0x0B
-#define A_13 0x0C
-#define A_14 0x0D
-#define A_15 0x0E
-#define A_16 0x0F
-
-#define B_1 0x10
-#define B_2 0x11
-#define B_3 0x12
-#define B_4 0x13
-#define B_5 0x14
-#define B_6 0x15
-#define B_7 0x16
-#define B_8 0x17
-#define B_9 0x18
-#define B_10 0x19
-#define B_11 0x1A
-#define B_12 0x1B
-#define B_13 0x1C
-#define B_14 0x1D
-#define B_15 0x1E
-#define B_16 0x1F
-
-#define C_1 0x20
-#define C_2 0x21
-#define C_3 0x22
-#define C_4 0x23
-#define C_5 0x24
-#define C_6 0x25
-#define C_7 0x26
-#define C_8 0x27
-#define C_9 0x28
-#define C_10 0x29
-#define C_11 0x2A
-#define C_12 0x2B
-#define C_13 0x2C
-#define C_14 0x2D
-#define C_15 0x2E
-#define C_16 0x2F
-
-#define D_1 0x30
-#define D_2 0x31
-#define D_3 0x32
-#define D_4 0x33
-#define D_5 0x34
-#define D_6 0x35
-#define D_7 0x36
-#define D_8 0x37
-#define D_9 0x38
-#define D_10 0x39
-#define D_11 0x3A
-#define D_12 0x3B
-#define D_13 0x3C
-#define D_14 0x3D
-#define D_15 0x3E
-#define D_16 0x3F
-
-#define E_1 0x40
-#define E_2 0x41
-#define E_3 0x42
-#define E_4 0x43
-#define E_5 0x44
-#define E_6 0x45
-#define E_7 0x46
-#define E_8 0x47
-#define E_9 0x48
-#define E_10 0x49
-#define E_11 0x4A
-#define E_12 0x4B
-#define E_13 0x4C
-#define E_14 0x4D
-#define E_15 0x4E
-#define E_16 0x4F
-
-#define F_1 0x50
-#define F_2 0x51
-#define F_3 0x52
-#define F_4 0x53
-#define F_5 0x54
-#define F_6 0x55
-#define F_7 0x56
-#define F_8 0x57
-#define F_9 0x58
-#define F_10 0x59
-#define F_11 0x5A
-#define F_12 0x5B
-#define F_13 0x5C
-#define F_14 0x5D
-#define F_15 0x5E
-#define F_16 0x5F
-
-#define G_1 0x60
-#define G_2 0x61
-#define G_3 0x62
-#define G_4 0x63
-#define G_5 0x64
-#define G_6 0x65
-#define G_7 0x66
-#define G_8 0x67
-#define G_9 0x68
-#define G_10 0x69
-#define G_11 0x6A
-#define G_12 0x6B
-#define G_13 0x6C
-#define G_14 0x6D
-#define G_15 0x6E
-#define G_16 0x6F
-
-#define H_1 0x70
-#define H_2 0x71
-#define H_3 0x72
-#define H_4 0x73
-#define H_5 0x74
-#define H_6 0x75
-#define H_7 0x76
-#define H_8 0x77
-#define H_9 0x78
-#define H_10 0x79
-#define H_11 0x7A
-#define H_12 0x7B
-#define H_13 0x7C
-#define H_14 0x7D
-#define H_15 0x7E
-#define H_16 0x7F
-
-#define I_1 0x80
-#define I_2 0x81
-#define I_3 0x82
-#define I_4 0x83
-#define I_5 0x84
-#define I_6 0x85
-#define I_7 0x86
-#define I_8 0x87
-#define I_9 0x88
-#define I_10 0x89
-#define I_11 0x8A
-#define I_12 0x8B
-#define I_13 0x8C
-#define I_14 0x8D
-#define I_15 0x8E
-#define I_16 0x8F
-
-#define J_1 0x90
-#define J_2 0x91
-#define J_3 0x92
-#define J_4 0x93
-#define J_5 0x94
-#define J_6 0x95
-#define J_7 0x96
-#define J_8 0x97
-#define J_9 0x98
-#define J_10 0x99
-#define J_11 0x9A
-#define J_12 0x9B
-#define J_13 0x9C
-#define J_14 0x9D
-#define J_15 0x9E
-#define J_16 0x9F
-
-#define K_1 0xA0
-#define K_2 0xA1
-#define K_3 0xA2
-#define K_4 0xA3
-#define K_5 0xA4
-#define K_6 0xA5
-#define K_7 0xA6
-#define K_8 0xA7
-#define K_9 0xA8
-#define K_10 0xA9
-#define K_11 0xAA
-#define K_12 0xAB
-#define K_13 0xAC
-#define K_14 0xAD
-#define K_15 0xAE
-#define K_16 0xAF
-
-#define L_1 0xB0
-#define L_2 0xB1
-#define L_3 0xB2
-#define L_4 0xB3
-#define L_5 0xB4
-#define L_6 0xB5
-#define L_7 0xB6
-#define L_8 0xB7
-#define L_9 0xB8
-#define L_10 0xB9
-#define L_11 0xBA
-#define L_12 0xBB
-#define L_13 0xBC
-#define L_14 0xBD
-#define L_15 0xBE
-#define L_16 0xBF
diff --git a/drivers/led/snled27351.c b/drivers/led/snled27351.c
index 71992b7322..8ebf681bdb 100644
--- a/drivers/led/snled27351.c
+++ b/drivers/led/snled27351.c
@@ -16,6 +16,7 @@
#include "snled27351.h"
#include "i2c_master.h"
+#include "gpio.h"
#define SNLED27351_PWM_REGISTER_COUNT 192
#define SNLED27351_LED_CONTROL_REGISTER_COUNT 24
@@ -37,8 +38,18 @@
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
#endif
-// Transfer buffer for TWITransmitData()
-uint8_t g_twi_transfer_buffer[65];
+const uint8_t i2c_addresses[SNLED27351_DRIVER_COUNT] = {
+ SNLED27351_I2C_ADDRESS_1,
+#ifdef SNLED27351_I2C_ADDRESS_2
+ SNLED27351_I2C_ADDRESS_2,
+# ifdef SNLED27351_I2C_ADDRESS_3
+ SNLED27351_I2C_ADDRESS_3,
+# ifdef SNLED27351_I2C_ADDRESS_4
+ SNLED27351_I2C_ADDRESS_4,
+# endif
+# endif
+#endif
+};
// These buffers match the SNLED27351 PWM registers.
// The control buffers match the PG0 LED On/Off registers.
@@ -46,135 +57,116 @@ uint8_t g_twi_transfer_buffer[65];
// We could optimize this and take out the unused registers from these
// buffers and the transfers in snled27351_write_pwm_buffer() but it's
// probably not worth the extra complexity.
-uint8_t g_pwm_buffer[SNLED27351_DRIVER_COUNT][SNLED27351_PWM_REGISTER_COUNT];
-bool g_pwm_buffer_update_required[SNLED27351_DRIVER_COUNT] = {false};
-
-uint8_t g_led_control_registers[SNLED27351_DRIVER_COUNT][SNLED27351_LED_CONTROL_REGISTER_COUNT] = {0};
-bool g_led_control_registers_update_required[SNLED27351_DRIVER_COUNT] = {false};
-
-bool snled27351_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
- // If the transaction fails function returns false.
- g_twi_transfer_buffer[0] = reg;
- g_twi_transfer_buffer[1] = data;
-
+typedef struct snled27351_driver_t {
+ uint8_t pwm_buffer[SNLED27351_PWM_REGISTER_COUNT];
+ bool pwm_buffer_dirty;
+ uint8_t led_control_buffer[SNLED27351_LED_CONTROL_REGISTER_COUNT];
+ bool led_control_buffer_dirty;
+} PACKED snled27351_driver_t;
+
+snled27351_driver_t driver_buffers[SNLED27351_DRIVER_COUNT] = {{
+ .pwm_buffer = {0},
+ .pwm_buffer_dirty = false,
+ .led_control_buffer = {0},
+ .led_control_buffer_dirty = false,
+}};
+
+void snled27351_write_register(uint8_t index, uint8_t reg, uint8_t data) {
#if SNLED27351_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < SNLED27351_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, SNLED27351_I2C_TIMEOUT) != 0) {
- return false;
- }
+ if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, SNLED27351_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
}
#else
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, SNLED27351_I2C_TIMEOUT) != 0) {
- return false;
- }
+ i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, SNLED27351_I2C_TIMEOUT);
#endif
- return true;
}
-bool snled27351_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
+void snled27351_select_page(uint8_t index, uint8_t page) {
+ snled27351_write_register(index, SNLED27351_REG_COMMAND, page);
+}
+
+void snled27351_write_pwm_buffer(uint8_t index) {
// Assumes PG1 is already selected.
- // If any of the transactions fails function returns false.
- // Transmit PWM registers in 3 transfers of 64 bytes.
-
- // Iterate over the pwm_buffer contents at 64 byte intervals.
- for (uint8_t i = 0; i < SNLED27351_PWM_REGISTER_COUNT; i += 64) {
- g_twi_transfer_buffer[0] = i;
- // Copy the data from i to i+63.
- // Device will auto-increment register for data after the first byte
- // Thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer.
- for (uint8_t j = 0; j < 64; j++) {
- g_twi_transfer_buffer[1 + j] = pwm_buffer[i + j];
- }
+ // Transmit PWM registers in 12 transfers of 16 bytes.
+ // Iterate over the pwm_buffer contents at 16 byte intervals.
+ for (uint8_t i = 0; i < SNLED27351_PWM_REGISTER_COUNT; i += 16) {
#if SNLED27351_I2C_PERSISTENCE > 0
- for (uint8_t i = 0; i < SNLED27351_I2C_PERSISTENCE; i++) {
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 65, SNLED27351_I2C_TIMEOUT) != 0) {
- return false;
- }
+ for (uint8_t j = 0; j < SNLED27351_I2C_PERSISTENCE; j++) {
+ if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, SNLED27351_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
}
#else
- if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 65, SNLED27351_I2C_TIMEOUT) != 0) {
- return false;
- }
+ i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, SNLED27351_I2C_TIMEOUT);
#endif
}
- return true;
}
void snled27351_init_drivers(void) {
i2c_init();
- snled27351_init(SNLED27351_I2C_ADDRESS_1);
-#if defined(SNLED27351_I2C_ADDRESS_2)
- snled27351_init(SNLED27351_I2C_ADDRESS_2);
-# if defined(SNLED27351_I2C_ADDRESS_3)
- snled27351_init(SNLED27351_I2C_ADDRESS_3);
-# if defined(SNLED27351_I2C_ADDRESS_4)
- snled27351_init(SNLED27351_I2C_ADDRESS_4);
-# endif
-# endif
+#if defined(SNLED27351_SDB_PIN)
+ gpio_set_pin_output(SNLED27351_SDB_PIN);
+ gpio_write_pin_high(SNLED27351_SDB_PIN);
#endif
+ for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) {
+ snled27351_init(i);
+ }
+
for (int i = 0; i < SNLED27351_LED_COUNT; i++) {
snled27351_set_led_control_register(i, true, true, true);
}
- snled27351_update_led_control_registers(SNLED27351_I2C_ADDRESS_1, 0);
-#if defined(SNLED27351_I2C_ADDRESS_2)
- snled27351_update_led_control_registers(SNLED27351_I2C_ADDRESS_2, 1);
-# if defined(SNLED27351_I2C_ADDRESS_3)
- snled27351_update_led_control_registers(SNLED27351_I2C_ADDRESS_3, 2);
-# if defined(SNLED27351_I2C_ADDRESS_4)
- snled27351_update_led_control_registers(SNLED27351_I2C_ADDRESS_4, 3);
-# endif
-# endif
-#endif
+ for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) {
+ snled27351_update_led_control_registers(i);
+ }
}
-void snled27351_init(uint8_t addr) {
- // Select to function page
- snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_FUNCTION);
+void snled27351_init(uint8_t index) {
+ snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION);
+
// Setting LED driver to shutdown mode
- snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN);
+ snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN);
// Setting internal channel pulldown/pullup
- snled27351_write_register(addr, SNLED27351_FUNCTION_REG_PULLDOWNUP, SNLED27351_PULLDOWNUP_ALL_ENABLED);
+ snled27351_write_register(index, SNLED27351_FUNCTION_REG_PULLDOWNUP, SNLED27351_PULLDOWNUP_ALL_ENABLED);
// Select number of scan phase
- snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SCAN_PHASE, SNLED27351_PHASE_CHANNEL);
+ snled27351_write_register(index, SNLED27351_FUNCTION_REG_SCAN_PHASE, SNLED27351_PHASE_CHANNEL);
// Setting PWM Delay Phase
- snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_1, SNLED27351_SLEW_RATE_CONTROL_MODE_1_PDP_ENABLE);
+ snled27351_write_register(index, SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_1, SNLED27351_SLEW_RATE_CONTROL_MODE_1_PDP_ENABLE);
// Setting Driving/Sinking Channel Slew Rate
- snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_2, SNLED27351_SLEW_RATE_CONTROL_MODE_2_DSL_ENABLE | SNLED27351_SLEW_RATE_CONTROL_MODE_2_SSL_ENABLE);
+ snled27351_write_register(index, SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_2, SNLED27351_SLEW_RATE_CONTROL_MODE_2_DSL_ENABLE | SNLED27351_SLEW_RATE_CONTROL_MODE_2_SSL_ENABLE);
// Setting Iref
- snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP, 0);
- // Set LED CONTROL PAGE (Page 0)
- snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_LED_CONTROL);
+ snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP, 0);
+
+ snled27351_select_page(index, SNLED27351_COMMAND_LED_CONTROL);
+
for (int i = 0; i < SNLED27351_LED_CONTROL_ON_OFF_LENGTH; i++) {
- snled27351_write_register(addr, i, 0x00);
+ snled27351_write_register(index, i, 0x00);
}
- // Set PWM PAGE (Page 1)
- snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_PWM);
+ snled27351_select_page(index, SNLED27351_COMMAND_PWM);
+
for (int i = 0; i < SNLED27351_LED_CURRENT_TUNE_LENGTH; i++) {
- snled27351_write_register(addr, i, 0x00);
+ snled27351_write_register(index, i, 0x00);
}
- // Set CURRENT PAGE (Page 4)
+ snled27351_select_page(index, SNLED27351_COMMAND_CURRENT_TUNE);
+
uint8_t current_tune_reg_list[SNLED27351_LED_CURRENT_TUNE_LENGTH] = SNLED27351_CURRENT_TUNE;
- snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_CURRENT_TUNE);
for (int i = 0; i < SNLED27351_LED_CURRENT_TUNE_LENGTH; i++) {
- snled27351_write_register(addr, i, current_tune_reg_list[i]);
+ snled27351_write_register(index, i, current_tune_reg_list[i]);
}
- // Enable LEDs ON/OFF
- snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_LED_CONTROL);
+ snled27351_select_page(index, SNLED27351_COMMAND_LED_CONTROL);
+
for (int i = 0; i < SNLED27351_LED_CONTROL_ON_OFF_LENGTH; i++) {
- snled27351_write_register(addr, i, 0xFF);
+ snled27351_write_register(index, i, 0xFF);
}
- // Select to function page
- snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_FUNCTION);
+ snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION);
+
// Setting LED driver to normal mode
- snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL);
+ snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL);
}
void snled27351_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
@@ -182,13 +174,14 @@ void snled27351_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
if (index >= 0 && index < SNLED27351_LED_COUNT) {
memcpy_P(&led, (&g_snled27351_leds[index]), sizeof(led));
- if (g_pwm_buffer[led.driver][led.r] == red && g_pwm_buffer[led.driver][led.g] == green && g_pwm_buffer[led.driver][led.b] == blue) {
+ if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) {
return;
}
- g_pwm_buffer[led.driver][led.r] = red;
- g_pwm_buffer[led.driver][led.g] = green;
- g_pwm_buffer[led.driver][led.b] = blue;
- g_pwm_buffer_update_required[led.driver] = true;
+
+ driver_buffers[led.driver].pwm_buffer[led.r] = red;
+ driver_buffers[led.driver].pwm_buffer[led.g] = green;
+ driver_buffers[led.driver].pwm_buffer[led.b] = blue;
+ driver_buffers[led.driver].pwm_buffer_dirty = true;
}
}
@@ -210,72 +203,64 @@ void snled27351_set_led_control_register(uint8_t index, bool red, bool green, bo
uint8_t bit_b = led.b % 8;
if (red) {
- g_led_control_registers[led.driver][control_register_r] |= (1 << bit_r);
+ driver_buffers[led.driver].led_control_buffer[control_register_r] |= (1 << bit_r);
} else {
- g_led_control_registers[led.driver][control_register_r] &= ~(1 << bit_r);
+ driver_buffers[led.driver].led_control_buffer[control_register_r] &= ~(1 << bit_r);
}
if (green) {
- g_led_control_registers[led.driver][control_register_g] |= (1 << bit_g);
+ driver_buffers[led.driver].led_control_buffer[control_register_g] |= (1 << bit_g);
} else {
- g_led_control_registers[led.driver][control_register_g] &= ~(1 << bit_g);
+ driver_buffers[led.driver].led_control_buffer[control_register_g] &= ~(1 << bit_g);
}
if (blue) {
- g_led_control_registers[led.driver][control_register_b] |= (1 << bit_b);
+ driver_buffers[led.driver].led_control_buffer[control_register_b] |= (1 << bit_b);
} else {
- g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b);
+ driver_buffers[led.driver].led_control_buffer[control_register_b] &= ~(1 << bit_b);
}
- g_led_control_registers_update_required[led.driver] = true;
+ driver_buffers[led.driver].led_control_buffer_dirty = true;
}
-void snled27351_update_pwm_buffers(uint8_t addr, uint8_t index) {
- if (g_pwm_buffer_update_required[index]) {
- snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_PWM);
+void snled27351_update_pwm_buffers(uint8_t index) {
+ if (driver_buffers[index].pwm_buffer_dirty) {
+ snled27351_select_page(index, SNLED27351_COMMAND_PWM);
- // If any of the transactions fail we risk writing dirty PG0,
- // refresh page 0 just in case.
- if (!snled27351_write_pwm_buffer(addr, g_pwm_buffer[index])) {
- g_led_control_registers_update_required[index] = true;
- }
+ snled27351_write_pwm_buffer(index);
+
+ driver_buffers[index].pwm_buffer_dirty = false;
}
- g_pwm_buffer_update_required[index] = false;
}
-void snled27351_update_led_control_registers(uint8_t addr, uint8_t index) {
- if (g_led_control_registers_update_required[index]) {
- snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_LED_CONTROL);
- for (int i = 0; i < SNLED27351_LED_CONTROL_REGISTER_COUNT; i++) {
- snled27351_write_register(addr, i, g_led_control_registers[index][i]);
+void snled27351_update_led_control_registers(uint8_t index) {
+ if (driver_buffers[index].led_control_buffer_dirty) {
+ snled27351_select_page(index, SNLED27351_COMMAND_LED_CONTROL);
+
+ for (uint8_t i = 0; i < SNLED27351_LED_CONTROL_REGISTER_COUNT; i++) {
+ snled27351_write_register(index, i, driver_buffers[index].led_control_buffer[i]);
}
+
+ driver_buffers[index].led_control_buffer_dirty = false;
}
- g_led_control_registers_update_required[index] = false;
}
void snled27351_flush(void) {
- snled27351_update_pwm_buffers(SNLED27351_I2C_ADDRESS_1, 0);
-#if defined(SNLED27351_I2C_ADDRESS_2)
- snled27351_update_pwm_buffers(SNLED27351_I2C_ADDRESS_2, 1);
-# if defined(SNLED27351_I2C_ADDRESS_3)
- snled27351_update_pwm_buffers(SNLED27351_I2C_ADDRESS_3, 2);
-# if defined(SNLED27351_I2C_ADDRESS_4)
- snled27351_update_pwm_buffers(SNLED27351_I2C_ADDRESS_4, 3);
-# endif
-# endif
-#endif
+ for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) {
+ snled27351_update_pwm_buffers(i);
+ }
}
-void snled27351_sw_return_normal(uint8_t addr) {
- // Select to function page
- snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_FUNCTION);
+void snled27351_sw_return_normal(uint8_t index) {
+ snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION);
+
// Setting LED driver to normal mode
- snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL);
+ snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL);
}
-void snled27351_sw_shutdown(uint8_t addr) {
- // Select to function page
- snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_FUNCTION);
+void snled27351_sw_shutdown(uint8_t index) {
+ snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION);
+
// Setting LED driver to shutdown mode
- snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN);
+ snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN);
// Write SW Sleep Register
- snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP, SNLED27351_SOFTWARE_SLEEP_ENABLE);
+ snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP, SNLED27351_SOFTWARE_SLEEP_ENABLE);
}
diff --git a/drivers/led/snled27351.h b/drivers/led/snled27351.h
index 77337f177b..d902744d14 100644
--- a/drivers/led/snled27351.h
+++ b/drivers/led/snled27351.h
@@ -168,9 +168,9 @@ typedef struct snled27351_led_t {
extern const snled27351_led_t PROGMEM g_snled27351_leds[SNLED27351_LED_COUNT];
void snled27351_init_drivers(void);
-void snled27351_init(uint8_t addr);
-bool snled27351_write_register(uint8_t addr, uint8_t reg, uint8_t data);
-bool snled27351_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
+void snled27351_init(uint8_t index);
+void snled27351_select_page(uint8_t index, uint8_t page);
+void snled27351_write_register(uint8_t index, uint8_t reg, uint8_t data);
void snled27351_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
void snled27351_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
@@ -181,214 +181,420 @@ void snled27351_set_led_control_register(uint8_t index, bool red, bool green, bo
// (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 snled27351_update_pwm_buffers(uint8_t addr, uint8_t index);
-void snled27351_update_led_control_registers(uint8_t addr, uint8_t index);
+void snled27351_update_pwm_buffers(uint8_t index);
+void snled27351_update_led_control_registers(uint8_t index);
void snled27351_flush(void);
-void snled27351_sw_return_normal(uint8_t addr);
-void snled27351_sw_shutdown(uint8_t addr);
-
-#define A_1 0x00
-#define A_2 0x01
-#define A_3 0x02
-#define A_4 0x03
-#define A_5 0x04
-#define A_6 0x05
-#define A_7 0x06
-#define A_8 0x07
-#define A_9 0x08
-#define A_10 0x09
-#define A_11 0x0A
-#define A_12 0x0B
-#define A_13 0x0C
-#define A_14 0x0D
-#define A_15 0x0E
-#define A_16 0x0F
-
-#define B_1 0x10
-#define B_2 0x11
-#define B_3 0x12
-#define B_4 0x13
-#define B_5 0x14
-#define B_6 0x15
-#define B_7 0x16
-#define B_8 0x17
-#define B_9 0x18
-#define B_10 0x19
-#define B_11 0x1A
-#define B_12 0x1B
-#define B_13 0x1C
-#define B_14 0x1D
-#define B_15 0x1E
-#define B_16 0x1F
-
-#define C_1 0x20
-#define C_2 0x21
-#define C_3 0x22
-#define C_4 0x23
-#define C_5 0x24
-#define C_6 0x25
-#define C_7 0x26
-#define C_8 0x27
-#define C_9 0x28
-#define C_10 0x29
-#define C_11 0x2A
-#define C_12 0x2B
-#define C_13 0x2C
-#define C_14 0x2D
-#define C_15 0x2E
-#define C_16 0x2F
-
-#define D_1 0x30
-#define D_2 0x31
-#define D_3 0x32
-#define D_4 0x33
-#define D_5 0x34
-#define D_6 0x35
-#define D_7 0x36
-#define D_8 0x37
-#define D_9 0x38
-#define D_10 0x39
-#define D_11 0x3A
-#define D_12 0x3B
-#define D_13 0x3C
-#define D_14 0x3D
-#define D_15 0x3E
-#define D_16 0x3F
-
-#define E_1 0x40
-#define E_2 0x41
-#define E_3 0x42
-#define E_4 0x43
-#define E_5 0x44
-#define E_6 0x45
-#define E_7 0x46
-#define E_8 0x47
-#define E_9 0x48
-#define E_10 0x49
-#define E_11 0x4A
-#define E_12 0x4B
-#define E_13 0x4C
-#define E_14 0x4D
-#define E_15 0x4E
-#define E_16 0x4F
-
-#define F_1 0x50
-#define F_2 0x51
-#define F_3 0x52
-#define F_4 0x53
-#define F_5 0x54
-#define F_6 0x55
-#define F_7 0x56
-#define F_8 0x57
-#define F_9 0x58
-#define F_10 0x59
-#define F_11 0x5A
-#define F_12 0x5B
-#define F_13 0x5C
-#define F_14 0x5D
-#define F_15 0x5E
-#define F_16 0x5F
-
-#define G_1 0x60
-#define G_2 0x61
-#define G_3 0x62
-#define G_4 0x63
-#define G_5 0x64
-#define G_6 0x65
-#define G_7 0x66
-#define G_8 0x67
-#define G_9 0x68
-#define G_10 0x69
-#define G_11 0x6A
-#define G_12 0x6B
-#define G_13 0x6C
-#define G_14 0x6D
-#define G_15 0x6E
-#define G_16 0x6F
-
-#define H_1 0x70
-#define H_2 0x71
-#define H_3 0x72
-#define H_4 0x73
-#define H_5 0x74
-#define H_6 0x75
-#define H_7 0x76
-#define H_8 0x77
-#define H_9 0x78
-#define H_10 0x79
-#define H_11 0x7A
-#define H_12 0x7B
-#define H_13 0x7C
-#define H_14 0x7D
-#define H_15 0x7E
-#define H_16 0x7F
-
-#define I_1 0x80
-#define I_2 0x81
-#define I_3 0x82
-#define I_4 0x83
-#define I_5 0x84
-#define I_6 0x85
-#define I_7 0x86
-#define I_8 0x87
-#define I_9 0x88
-#define I_10 0x89
-#define I_11 0x8A
-#define I_12 0x8B
-#define I_13 0x8C
-#define I_14 0x8D
-#define I_15 0x8E
-#define I_16 0x8F
-
-#define J_1 0x90
-#define J_2 0x91
-#define J_3 0x92
-#define J_4 0x93
-#define J_5 0x94
-#define J_6 0x95
-#define J_7 0x96
-#define J_8 0x97
-#define J_9 0x98
-#define J_10 0x99
-#define J_11 0x9A
-#define J_12 0x9B
-#define J_13 0x9C
-#define J_14 0x9D
-#define J_15 0x9E
-#define J_16 0x9F
-
-#define K_1 0xA0
-#define K_2 0xA1
-#define K_3 0xA2
-#define K_4 0xA3
-#define K_5 0xA4
-#define K_6 0xA5
-#define K_7 0xA6
-#define K_8 0xA7
-#define K_9 0xA8
-#define K_10 0xA9
-#define K_11 0xAA
-#define K_12 0xAB
-#define K_13 0xAC
-#define K_14 0xAD
-#define K_15 0xAE
-#define K_16 0xAF
-
-#define L_1 0xB0
-#define L_2 0xB1
-#define L_3 0xB2
-#define L_4 0xB3
-#define L_5 0xB4
-#define L_6 0xB5
-#define L_7 0xB6
-#define L_8 0xB7
-#define L_9 0xB8
-#define L_10 0xB9
-#define L_11 0xBA
-#define L_12 0xBB
-#define L_13 0xBC
-#define L_14 0xBD
-#define L_15 0xBE
-#define L_16 0xBF
+void snled27351_sw_return_normal(uint8_t index);
+void snled27351_sw_shutdown(uint8_t index);
+
+#define CB1_CA1 0x00
+#define CB1_CA2 0x01
+#define CB1_CA3 0x02
+#define CB1_CA4 0x03
+#define CB1_CA5 0x04
+#define CB1_CA6 0x05
+#define CB1_CA7 0x06
+#define CB1_CA8 0x07
+#define CB1_CA9 0x08
+#define CB1_CA10 0x09
+#define CB1_CA11 0x0A
+#define CB1_CA12 0x0B
+#define CB1_CA13 0x0C
+#define CB1_CA14 0x0D
+#define CB1_CA15 0x0E
+#define CB1_CA16 0x0F
+
+#define CB2_CA1 0x10
+#define CB2_CA2 0x11
+#define CB2_CA3 0x12
+#define CB2_CA4 0x13
+#define CB2_CA5 0x14
+#define CB2_CA6 0x15
+#define CB2_CA7 0x16
+#define CB2_CA8 0x17
+#define CB2_CA9 0x18
+#define CB2_CA10 0x19
+#define CB2_CA11 0x1A
+#define CB2_CA12 0x1B
+#define CB2_CA13 0x1C
+#define CB2_CA14 0x1D
+#define CB2_CA15 0x1E
+#define CB2_CA16 0x1F
+
+#define CB3_CA1 0x20
+#define CB3_CA2 0x21
+#define CB3_CA3 0x22
+#define CB3_CA4 0x23
+#define CB3_CA5 0x24
+#define CB3_CA6 0x25
+#define CB3_CA7 0x26
+#define CB3_CA8 0x27
+#define CB3_CA9 0x28
+#define CB3_CA10 0x29
+#define CB3_CA11 0x2A
+#define CB3_CA12 0x2B
+#define CB3_CA13 0x2C
+#define CB3_CA14 0x2D
+#define CB3_CA15 0x2E
+#define CB3_CA16 0x2F
+
+#define CB4_CA1 0x30
+#define CB4_CA2 0x31
+#define CB4_CA3 0x32
+#define CB4_CA4 0x33
+#define CB4_CA5 0x34
+#define CB4_CA6 0x35
+#define CB4_CA7 0x36
+#define CB4_CA8 0x37
+#define CB4_CA9 0x38
+#define CB4_CA10 0x39
+#define CB4_CA11 0x3A
+#define CB4_CA12 0x3B
+#define CB4_CA13 0x3C
+#define CB4_CA14 0x3D
+#define CB4_CA15 0x3E
+#define CB4_CA16 0x3F
+
+#define CB5_CA1 0x40
+#define CB5_CA2 0x41
+#define CB5_CA3 0x42
+#define CB5_CA4 0x43
+#define CB5_CA5 0x44
+#define CB5_CA6 0x45
+#define CB5_CA7 0x46
+#define CB5_CA8 0x47
+#define CB5_CA9 0x48
+#define CB5_CA10 0x49
+#define CB5_CA11 0x4A
+#define CB5_CA12 0x4B
+#define CB5_CA13 0x4C
+#define CB5_CA14 0x4D
+#define CB5_CA15 0x4E
+#define CB5_CA16 0x4F
+
+#define CB6_CA1 0x50
+#define CB6_CA2 0x51
+#define CB6_CA3 0x52
+#define CB6_CA4 0x53
+#define CB6_CA5 0x54
+#define CB6_CA6 0x55
+#define CB6_CA7 0x56
+#define CB6_CA8 0x57
+#define CB6_CA9 0x58
+#define CB6_CA10 0x59
+#define CB6_CA11 0x5A
+#define CB6_CA12 0x5B
+#define CB6_CA13 0x5C
+#define CB6_CA14 0x5D
+#define CB6_CA15 0x5E
+#define CB6_CA16 0x5F
+
+#define CB7_CA1 0x60
+#define CB7_CA2 0x61
+#define CB7_CA3 0x62
+#define CB7_CA4 0x63
+#define CB7_CA5 0x64
+#define CB7_CA6 0x65
+#define CB7_CA7 0x66
+#define CB7_CA8 0x67
+#define CB7_CA9 0x68
+#define CB7_CA10 0x69
+#define CB7_CA11 0x6A
+#define CB7_CA12 0x6B
+#define CB7_CA13 0x6C
+#define CB7_CA14 0x6D
+#define CB7_CA15 0x6E
+#define CB7_CA16 0x6F
+
+#define CB8_CA1 0x70
+#define CB8_CA2 0x71
+#define CB8_CA3 0x72
+#define CB8_CA4 0x73
+#define CB8_CA5 0x74
+#define CB8_CA6 0x75
+#define CB8_CA7 0x76
+#define CB8_CA8 0x77
+#define CB8_CA9 0x78
+#define CB8_CA10 0x79
+#define CB8_CA11 0x7A
+#define CB8_CA12 0x7B
+#define CB8_CA13 0x7C
+#define CB8_CA14 0x7D
+#define CB8_CA15 0x7E
+#define CB8_CA16 0x7F
+
+#define CB9_CA1 0x80
+#define CB9_CA2 0x81
+#define CB9_CA3 0x82
+#define CB9_CA4 0x83
+#define CB9_CA5 0x84
+#define CB9_CA6 0x85
+#define CB9_CA7 0x86
+#define CB9_CA8 0x87
+#define CB9_CA9 0x88
+#define CB9_CA10 0x89
+#define CB9_CA11 0x8A
+#define CB9_CA12 0x8B
+#define CB9_CA13 0x8C
+#define CB9_CA14 0x8D
+#define CB9_CA15 0x8E
+#define CB9_CA16 0x8F
+
+#define CB10_CA1 0x90
+#define CB10_CA2 0x91
+#define CB10_CA3 0x92
+#define CB10_CA4 0x93
+#define CB10_CA5 0x94
+#define CB10_CA6 0x95
+#define CB10_CA7 0x96
+#define CB10_CA8 0x97
+#define CB10_CA9 0x98
+#define CB10_CA10 0x99
+#define CB10_CA11 0x9A
+#define CB10_CA12 0x9B
+#define CB10_CA13 0x9C
+#define CB10_CA14 0x9D
+#define CB10_CA15 0x9E
+#define CB10_CA16 0x9F
+
+#define CB11_CA1 0xA0
+#define CB11_CA2 0xA1
+#define CB11_CA3 0xA2
+#define CB11_CA4 0xA3
+#define CB11_CA5 0xA4
+#define CB11_CA6 0xA5
+#define CB11_CA7 0xA6
+#define CB11_CA8 0xA7
+#define CB11_CA9 0xA8
+#define CB11_CA10 0xA9
+#define CB11_CA11 0xAA
+#define CB11_CA12 0xAB
+#define CB11_CA13 0xAC
+#define CB11_CA14 0xAD
+#define CB11_CA15 0xAE
+#define CB11_CA16 0xAF
+
+#define CB12_CA1 0xB0
+#define CB12_CA2 0xB1
+#define CB12_CA3 0xB2
+#define CB12_CA4 0xB3
+#define CB12_CA5 0xB4
+#define CB12_CA6 0xB5
+#define CB12_CA7 0xB6
+#define CB12_CA8 0xB7
+#define CB12_CA9 0xB8
+#define CB12_CA10 0xB9
+#define CB12_CA11 0xBA
+#define CB12_CA12 0xBB
+#define CB12_CA13 0xBC
+#define CB12_CA14 0xBD
+#define CB12_CA15 0xBE
+#define CB12_CA16 0xBF
+
+// DEPRECATED - DO NOT USE
+
+#define A_1 CB1_CA1
+#define A_2 CB1_CA2
+#define A_3 CB1_CA3
+#define A_4 CB1_CA4
+#define A_5 CB1_CA5
+#define A_6 CB1_CA6
+#define A_7 CB1_CA7
+#define A_8 CB1_CA8
+#define A_9 CB1_CA9
+#define A_10 CB1_CA10
+#define A_11 CB1_CA11
+#define A_12 CB1_CA12
+#define A_13 CB1_CA13
+#define A_14 CB1_CA14
+#define A_15 CB1_CA15
+#define A_16 CB1_CA16
+
+#define B_1 CB2_CA1
+#define B_2 CB2_CA2
+#define B_3 CB2_CA3
+#define B_4 CB2_CA4
+#define B_5 CB2_CA5
+#define B_6 CB2_CA6
+#define B_7 CB2_CA7
+#define B_8 CB2_CA8
+#define B_9 CB2_CA9
+#define B_10 CB2_CA10
+#define B_11 CB2_CA11
+#define B_12 CB2_CA12
+#define B_13 CB2_CA13
+#define B_14 CB2_CA14
+#define B_15 CB2_CA15
+#define B_16 CB2_CA16
+
+#define C_1 CB3_CA1
+#define C_2 CB3_CA2
+#define C_3 CB3_CA3
+#define C_4 CB3_CA4
+#define C_5 CB3_CA5
+#define C_6 CB3_CA6
+#define C_7 CB3_CA7
+#define C_8 CB3_CA8
+#define C_9 CB3_CA9
+#define C_10 CB3_CA10
+#define C_11 CB3_CA11
+#define C_12 CB3_CA12
+#define C_13 CB3_CA13
+#define C_14 CB3_CA14
+#define C_15 CB3_CA15
+#define C_16 CB3_CA16
+
+#define D_1 CB4_CA1
+#define D_2 CB4_CA2
+#define D_3 CB4_CA3
+#define D_4 CB4_CA4
+#define D_5 CB4_CA5
+#define D_6 CB4_CA6
+#define D_7 CB4_CA7
+#define D_8 CB4_CA8
+#define D_9 CB4_CA9
+#define D_10 CB4_CA10
+#define D_11 CB4_CA11
+#define D_12 CB4_CA12
+#define D_13 CB4_CA13
+#define D_14 CB4_CA14
+#define D_15 CB4_CA15
+#define D_16 CB4_CA16
+
+#define E_1 CB5_CA1
+#define E_2 CB5_CA2
+#define E_3 CB5_CA3
+#define E_4 CB5_CA4
+#define E_5 CB5_CA5
+#define E_6 CB5_CA6
+#define E_7 CB5_CA7
+#define E_8 CB5_CA8
+#define E_9 CB5_CA9
+#define E_10 CB5_CA10
+#define E_11 CB5_CA11
+#define E_12 CB5_CA12
+#define E_13 CB5_CA13
+#define E_14 CB5_CA14
+#define E_15 CB5_CA15
+#define E_16 CB5_CA16
+
+#define F_1 CB6_CA1
+#define F_2 CB6_CA2
+#define F_3 CB6_CA3
+#define F_4 CB6_CA4
+#define F_5 CB6_CA5
+#define F_6 CB6_CA6
+#define F_7 CB6_CA7
+#define F_8 CB6_CA8
+#define F_9 CB6_CA9
+#define F_10 CB6_CA10
+#define F_11 CB6_CA11
+#define F_12 CB6_CA12
+#define F_13 CB6_CA13
+#define F_14 CB6_CA14
+#define F_15 CB6_CA15
+#define F_16 CB6_CA16
+
+#define G_1 CB7_CA1
+#define G_2 CB7_CA2
+#define G_3 CB7_CA3
+#define G_4 CB7_CA4
+#define G_5 CB7_CA5
+#define G_6 CB7_CA6
+#define G_7 CB7_CA7
+#define G_8 CB7_CA8
+#define G_9 CB7_CA9
+#define G_10 CB7_CA10
+#define G_11 CB7_CA11
+#define G_12 CB7_CA12
+#define G_13 CB7_CA13
+#define G_14 CB7_CA14
+#define G_15 CB7_CA15
+#define G_16 CB7_CA16
+
+#define H_1 CB8_CA1
+#define H_2 CB8_CA2
+#define H_3 CB8_CA3
+#define H_4 CB8_CA4
+#define H_5 CB8_CA5
+#define H_6 CB8_CA6
+#define H_7 CB8_CA7
+#define H_8 CB8_CA8
+#define H_9 CB8_CA9
+#define H_10 CB8_CA10
+#define H_11 CB8_CA11
+#define H_12 CB8_CA12
+#define H_13 CB8_CA13
+#define H_14 CB8_CA14
+#define H_15 CB8_CA15
+#define H_16 CB8_CA16
+
+#define I_1 CB9_CA1
+#define I_2 CB9_CA2
+#define I_3 CB9_CA3
+#define I_4 CB9_CA4
+#define I_5 CB9_CA5
+#define I_6 CB9_CA6
+#define I_7 CB9_CA7
+#define I_8 CB9_CA8
+#define I_9 CB9_CA9
+#define I_10 CB9_CA10
+#define I_11 CB9_CA11
+#define I_12 CB9_CA12
+#define I_13 CB9_CA13
+#define I_14 CB9_CA14
+#define I_15 CB9_CA15
+#define I_16 CB9_CA16
+
+#define J_1 CB10_CA1
+#define J_2 CB10_CA2
+#define J_3 CB10_CA3
+#define J_4 CB10_CA4
+#define J_5 CB10_CA5
+#define J_6 CB10_CA6
+#define J_7 CB10_CA7
+#define J_8 CB10_CA8
+#define J_9 CB10_CA9
+#define J_10 CB10_CA10
+#define J_11 CB10_CA11
+#define J_12 CB10_CA12
+#define J_13 CB10_CA13
+#define J_14 CB10_CA14
+#define J_15 CB10_CA15
+#define J_16 CB10_CA16
+
+#define K_1 CB11_CA1
+#define K_2 CB11_CA2
+#define K_3 CB11_CA3
+#define K_4 CB11_CA4
+#define K_5 CB11_CA5
+#define K_6 CB11_CA6
+#define K_7 CB11_CA7
+#define K_8 CB11_CA8
+#define K_9 CB11_CA9
+#define K_10 CB11_CA10
+#define K_11 CB11_CA11
+#define K_12 CB11_CA12
+#define K_13 CB11_CA13
+#define K_14 CB11_CA14
+#define K_15 CB11_CA15
+#define K_16 CB11_CA16
+
+#define L_1 CB12_CA1
+#define L_2 CB12_CA2
+#define L_3 CB12_CA3
+#define L_4 CB12_CA4
+#define L_5 CB12_CA5
+#define L_6 CB12_CA6
+#define L_7 CB12_CA7
+#define L_8 CB12_CA8
+#define L_9 CB12_CA9
+#define L_10 CB12_CA10
+#define L_11 CB12_CA11
+#define L_12 CB12_CA12
+#define L_13 CB12_CA13
+#define L_14 CB12_CA14
+#define L_15 CB12_CA15
+#define L_16 CB12_CA16
diff --git a/drivers/oled/oled_driver.c b/drivers/oled/oled_driver.c
index 4a2121cd7c..1d1c2a90c4 100644
--- a/drivers/oled/oled_driver.c
+++ b/drivers/oled/oled_driver.c
@@ -192,7 +192,7 @@ __attribute__((weak)) bool oled_send_cmd(const uint8_t *data, uint16_t size) {
return false;
}
// Command Mode
- writePinLow(OLED_DC_PIN);
+ gpio_write_pin_low(OLED_DC_PIN);
// Send the commands
if (spi_transmit(&data[1], size - 1) != SPI_STATUS_SUCCESS) {
spi_stop();
@@ -215,7 +215,7 @@ __attribute__((weak)) bool oled_send_cmd_P(const uint8_t *data, uint16_t size) {
}
spi_status_t status = SPI_STATUS_SUCCESS;
// Command Mode
- writePinLow(OLED_DC_PIN);
+ gpio_write_pin_low(OLED_DC_PIN);
// Send the commands
for (uint16_t i = 1; i < size && status >= 0; i++) {
status = spi_write(pgm_read_byte((const char *)&data[i]));
@@ -223,13 +223,8 @@ __attribute__((weak)) bool oled_send_cmd_P(const uint8_t *data, uint16_t size) {
spi_stop();
return (status >= 0);
# elif defined(OLED_TRANSPORT_I2C)
- i2c_status_t status = i2c_start((OLED_DISPLAY_ADDRESS << 1) | I2C_WRITE, OLED_I2C_TIMEOUT);
- for (uint16_t i = 0; i < size && status >= 0; i++) {
- status = i2c_write(pgm_read_byte((const char *)data++), OLED_I2C_TIMEOUT);
- }
-
- i2c_stop();
+ i2c_status_t status = i2c_transmit_P((OLED_DISPLAY_ADDRESS << 1), data, size, OLED_I2C_TIMEOUT);
return (status == I2C_STATUS_SUCCESS);
# endif
@@ -244,7 +239,7 @@ __attribute__((weak)) bool oled_send_data(const uint8_t *data, uint16_t size) {
return false;
}
// Data Mode
- writePinHigh(OLED_DC_PIN);
+ gpio_write_pin_high(OLED_DC_PIN);
// Send the commands
if (spi_transmit(data, size) != SPI_STATUS_SUCCESS) {
spi_stop();
@@ -253,7 +248,7 @@ __attribute__((weak)) bool oled_send_data(const uint8_t *data, uint16_t size) {
spi_stop();
return true;
#elif defined(OLED_TRANSPORT_I2C)
- i2c_status_t status = i2c_writeReg((OLED_DISPLAY_ADDRESS << 1), I2C_DATA, data, size, OLED_I2C_TIMEOUT);
+ i2c_status_t status = i2c_write_register((OLED_DISPLAY_ADDRESS << 1), I2C_DATA, data, size, OLED_I2C_TIMEOUT);
return (status == I2C_STATUS_SUCCESS);
#endif
}
@@ -261,17 +256,17 @@ __attribute__((weak)) bool oled_send_data(const uint8_t *data, uint16_t size) {
__attribute__((weak)) void oled_driver_init(void) {
#if defined(OLED_TRANSPORT_SPI)
spi_init();
- setPinOutput(OLED_CS_PIN);
- writePinHigh(OLED_CS_PIN);
+ gpio_set_pin_output(OLED_CS_PIN);
+ gpio_write_pin_high(OLED_CS_PIN);
- setPinOutput(OLED_DC_PIN);
- writePinLow(OLED_DC_PIN);
+ gpio_set_pin_output(OLED_DC_PIN);
+ gpio_write_pin_low(OLED_DC_PIN);
# ifdef OLED_RST_PIN
/* Reset device */
- setPinOutput(OLED_RST_PIN);
- writePinLow(OLED_RST_PIN);
+ gpio_set_pin_output(OLED_RST_PIN);
+ gpio_write_pin_low(OLED_RST_PIN);
wait_ms(20);
- writePinHigh(OLED_RST_PIN);
+ gpio_write_pin_high(OLED_RST_PIN);
wait_ms(20);
# endif
#elif defined(OLED_TRANSPORT_I2C)
diff --git a/drivers/painter/comms/qp_comms_i2c.c b/drivers/painter/comms/qp_comms_i2c.c
index ec45ddfb3b..93f503f3dd 100644
--- a/drivers/painter/comms/qp_comms_i2c.c
+++ b/drivers/painter/comms/qp_comms_i2c.c
@@ -28,18 +28,14 @@ bool qp_comms_i2c_init(painter_device_t device) {
}
bool qp_comms_i2c_start(painter_device_t device) {
- painter_driver_t * driver = (painter_driver_t *)device;
- qp_comms_i2c_config_t *comms_config = (qp_comms_i2c_config_t *)driver->comms_config;
- return i2c_start(comms_config->chip_address << 1) == I2C_STATUS_SUCCESS;
+ return true;
}
uint32_t qp_comms_i2c_send_data(painter_device_t device, const void *data, uint32_t byte_count) {
return qp_comms_i2c_send_raw(device, data, byte_count);
}
-void qp_comms_i2c_stop(painter_device_t device) {
- i2c_stop();
-}
+void qp_comms_i2c_stop(painter_device_t device) {}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Command+Data I2C support
diff --git a/drivers/painter/comms/qp_comms_spi.c b/drivers/painter/comms/qp_comms_spi.c
index 9f52bc7d1f..4e6067394b 100644
--- a/drivers/painter/comms/qp_comms_spi.c
+++ b/drivers/painter/comms/qp_comms_spi.c
@@ -17,8 +17,8 @@ bool qp_comms_spi_init(painter_device_t device) {
spi_init();
// Set up CS as output high
- setPinOutput(comms_config->chip_select_pin);
- writePinHigh(comms_config->chip_select_pin);
+ gpio_set_pin_output(comms_config->chip_select_pin);
+ gpio_write_pin_high(comms_config->chip_select_pin);
return true;
}
@@ -49,7 +49,7 @@ void qp_comms_spi_stop(painter_device_t device) {
painter_driver_t * driver = (painter_driver_t *)device;
qp_comms_spi_config_t *comms_config = (qp_comms_spi_config_t *)driver->comms_config;
spi_stop();
- writePinHigh(comms_config->chip_select_pin);
+ gpio_write_pin_high(comms_config->chip_select_pin);
}
const painter_comms_vtable_t spi_comms_vtable = {
@@ -74,16 +74,16 @@ bool qp_comms_spi_dc_reset_init(painter_device_t device) {
// Set up D/C as output low, if specified
if (comms_config->dc_pin != NO_PIN) {
- setPinOutput(comms_config->dc_pin);
- writePinLow(comms_config->dc_pin);
+ gpio_set_pin_output(comms_config->dc_pin);
+ gpio_write_pin_low(comms_config->dc_pin);
}
// Set up RST as output, if specified, performing a reset in the process
if (comms_config->reset_pin != NO_PIN) {
- setPinOutput(comms_config->reset_pin);
- writePinLow(comms_config->reset_pin);
+ gpio_set_pin_output(comms_config->reset_pin);
+ gpio_write_pin_low(comms_config->reset_pin);
wait_ms(20);
- writePinHigh(comms_config->reset_pin);
+ gpio_write_pin_high(comms_config->reset_pin);
wait_ms(20);
}
@@ -93,14 +93,14 @@ bool qp_comms_spi_dc_reset_init(painter_device_t device) {
uint32_t qp_comms_spi_dc_reset_send_data(painter_device_t device, const void *data, uint32_t byte_count) {
painter_driver_t * driver = (painter_driver_t *)device;
qp_comms_spi_dc_reset_config_t *comms_config = (qp_comms_spi_dc_reset_config_t *)driver->comms_config;
- writePinHigh(comms_config->dc_pin);
+ gpio_write_pin_high(comms_config->dc_pin);
return qp_comms_spi_send_data(device, data, byte_count);
}
void qp_comms_spi_dc_reset_send_command(painter_device_t device, uint8_t cmd) {
painter_driver_t * driver = (painter_driver_t *)device;
qp_comms_spi_dc_reset_config_t *comms_config = (qp_comms_spi_dc_reset_config_t *)driver->comms_config;
- writePinLow(comms_config->dc_pin);
+ gpio_write_pin_low(comms_config->dc_pin);
spi_write(cmd);
}
diff --git a/drivers/painter/comms/qp_comms_spi.h b/drivers/painter/comms/qp_comms_spi.h
index ff323c3c10..c39ea95f72 100644
--- a/drivers/painter/comms/qp_comms_spi.h
+++ b/drivers/painter/comms/qp_comms_spi.h
@@ -38,6 +38,7 @@ typedef struct qp_comms_spi_dc_reset_config_t {
bool command_params_uses_command_pin; // keep D/C held low when sending command sequences for data bytes
} qp_comms_spi_dc_reset_config_t;
+bool qp_comms_spi_dc_reset_init(painter_device_t device);
void qp_comms_spi_dc_reset_send_command(painter_device_t device, uint8_t cmd);
uint32_t qp_comms_spi_dc_reset_send_data(painter_device_t device, const void* data, uint32_t byte_count);
void qp_comms_spi_dc_reset_bulk_command_sequence(painter_device_t device, const uint8_t* sequence, size_t sequence_len);
diff --git a/drivers/painter/ili9xxx/qp_ili9486.c b/drivers/painter/ili9xxx/qp_ili9486.c
new file mode 100644
index 0000000000..c4f3c15cec
--- /dev/null
+++ b/drivers/painter/ili9xxx/qp_ili9486.c
@@ -0,0 +1,298 @@
+// Copyright 2021 Nick Brassel (@tzarc)
+// Copyright 2023 Pablo Martinez (@elpekenin) <elpekenin@elpekenin.dev>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "qp_internal.h"
+#include "qp_comms.h"
+#include "qp_ili9486.h"
+#include "qp_ili9xxx_opcodes.h"
+#include "qp_tft_panel.h"
+
+#ifdef QUANTUM_PAINTER_ILI9486_SPI_ENABLE
+# include "spi_master.h"
+# include <qp_comms_spi.h>
+#endif // QUANTUM_PAINTER_ILI9486_SPI_ENABLE
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Common
+
+// Driver storage
+tft_panel_dc_reset_painter_device_t ili9486_drivers[ILI9486_NUM_DEVICES] = {0};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Initialization
+
+bool qp_ili9486_init(painter_device_t device, painter_rotation_t rotation) {
+ // clang-format off
+ const uint8_t ili9486_init_sequence[] = {
+ // Command, Delay, N, Data[N]
+ ILI9XXX_CMD_RESET, 120, 0,
+ ILI9XXX_SET_PIX_FMT, 0, 1, 0x55,
+ ILI9XXX_SET_PGAMMA, 0, 15, 0x0F, 0x1F, 0x1C, 0x0C, 0x0F, 0x08, 0x48, 0x98, 0x37, 0x0A, 0x13, 0x04, 0x11, 0x0D, 0x00,
+ ILI9XXX_SET_NGAMMA, 0, 15, 0x0F, 0x32, 0x2E, 0x0B, 0x0D, 0x05, 0x47, 0x75, 0x37, 0x06, 0x10, 0x03, 0x24, 0x20, 0x00,
+ ILI9XXX_SET_POWER_CTL_1, 0, 2, 0x0D, 0x0D,
+ ILI9XXX_SET_POWER_CTL_2, 0, 2, 0x43, 0x00,
+ ILI9XXX_SET_POWER_CTL_3, 0, 1, 0x00,
+ ILI9XXX_SET_VCOM_CTL_1, 0, 4, 0x00, 0x48, 0x00, 0x48,
+ ILI9XXX_SET_INVERSION_CTL, 0, 1, 0x02,
+ };
+ // clang-format on
+ qp_comms_bulk_command_sequence(device, ili9486_init_sequence, sizeof(ili9486_init_sequence));
+
+ // Configure the rotation (i.e. the ordering and direction of memory writes in GRAM)
+ const uint8_t madctl[] = {
+ [QP_ROTATION_0] = ILI9XXX_MADCTL_BGR,
+ [QP_ROTATION_90] = ILI9XXX_MADCTL_BGR | ILI9XXX_MADCTL_MV,
+ [QP_ROTATION_180] = ILI9XXX_MADCTL_BGR,
+ [QP_ROTATION_270] = ILI9XXX_MADCTL_BGR | ILI9XXX_MADCTL_MV,
+ };
+ const uint8_t functl[] = {
+ [QP_ROTATION_0] = 0x42,
+ [QP_ROTATION_90] = 0x62,
+ [QP_ROTATION_180] = 0x22,
+ [QP_ROTATION_270] = 0x02,
+ };
+
+ // clang-format off
+ uint8_t rotation_sequence[] = {
+ // Command, Delay, N, Data[N]
+ ILI9XXX_SET_MEM_ACS_CTL, 0, 1, madctl[rotation],
+ ILI9XXX_SET_FUNCTION_CTL, 0, 2, 0x00, functl[rotation],
+ ILI9XXX_CMD_SLEEP_OFF, 5, 0,
+ ILI9XXX_CMD_DISPLAY_ON, 5, 0,
+ };
+ // clang-format on
+ qp_comms_bulk_command_sequence(device, rotation_sequence, sizeof(rotation_sequence));
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Driver vtable
+
+// waveshare variant needs some tweaks due to shift registers
+static void qp_comms_spi_dc_reset_send_command_odd_cs_pulse(painter_device_t device, uint8_t cmd) {
+ painter_driver_t * driver = (painter_driver_t *)device;
+ qp_comms_spi_dc_reset_config_t *comms_config = (qp_comms_spi_dc_reset_config_t *)driver->comms_config;
+
+ writePinLow(comms_config->spi_config.chip_select_pin);
+ qp_comms_spi_dc_reset_send_command(device, cmd);
+ writePinHigh(comms_config->spi_config.chip_select_pin);
+}
+
+static uint32_t qp_comms_spi_send_data_odd_cs_pulse(painter_device_t device, const void *data, uint32_t byte_count) {
+ painter_driver_t * driver = (painter_driver_t *)device;
+ qp_comms_spi_dc_reset_config_t *comms_config = (qp_comms_spi_dc_reset_config_t *)driver->comms_config;
+
+ uint32_t bytes_remaining = byte_count;
+ const uint8_t *p = (const uint8_t *)data;
+ uint32_t max_msg_length = 1024;
+
+ writePinHigh(comms_config->dc_pin);
+ while (bytes_remaining > 0) {
+ uint32_t bytes_this_loop = QP_MIN(bytes_remaining, max_msg_length);
+ bool odd_bytes = bytes_this_loop & 1;
+
+ // send data
+ writePinLow(comms_config->spi_config.chip_select_pin);
+ spi_transmit(p, bytes_this_loop);
+ p += bytes_this_loop;
+
+ // extra CS toggle, for alignment
+ if (odd_bytes) {
+ writePinHigh(comms_config->spi_config.chip_select_pin);
+ writePinLow(comms_config->spi_config.chip_select_pin);
+ }
+
+ bytes_remaining -= bytes_this_loop;
+ }
+
+ return byte_count - bytes_remaining;
+}
+
+static uint32_t qp_ili9486_send_data_toggling(painter_device_t device, const uint8_t *data, uint32_t byte_count) {
+ painter_driver_t * driver = (painter_driver_t *)device;
+ qp_comms_spi_dc_reset_config_t *comms_config = (qp_comms_spi_dc_reset_config_t *)driver->comms_config;
+
+ uint32_t ret;
+ for (uint8_t j = 0; j < byte_count; ++j) {
+ writePinLow(comms_config->spi_config.chip_select_pin);
+ ret = qp_comms_spi_dc_reset_send_data(device, &data[j], 1);
+ writePinHigh(comms_config->spi_config.chip_select_pin);
+ }
+
+ return ret;
+}
+
+static void qp_comms_spi_send_command_sequence_odd_cs_pulse(painter_device_t device, const uint8_t *sequence, size_t sequence_len) {
+ for (size_t i = 0; i < sequence_len;) {
+ uint8_t command = sequence[i];
+ uint8_t delay = sequence[i + 1];
+ uint8_t num_bytes = sequence[i + 2];
+
+ qp_comms_spi_dc_reset_send_command_odd_cs_pulse(device, command);
+ if (num_bytes > 0) {
+ qp_ili9486_send_data_toggling(device, &sequence[i + 3], num_bytes);
+ }
+
+ if (delay > 0) {
+ wait_ms(delay);
+ }
+ i += (3 + num_bytes);
+ }
+}
+
+static bool qp_ili9486_viewport(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom) {
+ painter_driver_t * driver = (painter_driver_t *)device;
+ tft_panel_dc_reset_painter_driver_vtable_t *vtable = (tft_panel_dc_reset_painter_driver_vtable_t *)driver->driver_vtable;
+
+ // Fix up the drawing location if required
+ left += driver->offset_x;
+ right += driver->offset_x;
+ top += driver->offset_y;
+ bottom += driver->offset_y;
+
+ // Check if we need to manually swap the window coordinates based on whether or not we're in a sideways rotation
+ if (vtable->swap_window_coords && (driver->rotation == QP_ROTATION_90 || driver->rotation == QP_ROTATION_270)) {
+ uint16_t temp;
+
+ temp = left;
+ left = top;
+ top = temp;
+
+ temp = right;
+ right = bottom;
+ bottom = temp;
+ }
+
+ // Set up the x-window
+ uint8_t xbuf[4] = {left >> 8, left & 0xFF, right >> 8, right & 0xFF};
+ qp_comms_spi_dc_reset_send_command_odd_cs_pulse(device, vtable->opcodes.set_column_address);
+ qp_ili9486_send_data_toggling(device, xbuf, 4);
+
+ // Set up the y-window
+ uint8_t ybuf[4] = {top >> 8, top & 0xFF, bottom >> 8, bottom & 0xFF};
+ qp_comms_spi_dc_reset_send_command_odd_cs_pulse(device, vtable->opcodes.set_row_address);
+ qp_ili9486_send_data_toggling(device, ybuf, 4);
+
+ // Lock in the window
+ qp_comms_spi_dc_reset_send_command_odd_cs_pulse(device, vtable->opcodes.enable_writes);
+ return true;
+}
+
+// Regular
+const tft_panel_dc_reset_painter_driver_vtable_t ili9486_driver_vtable = {
+ .base =
+ {
+ .init = qp_ili9486_init,
+ .power = qp_tft_panel_power,
+ .clear = qp_tft_panel_clear,
+ .flush = qp_tft_panel_flush,
+ .pixdata = qp_tft_panel_pixdata,
+ .viewport = qp_tft_panel_viewport,
+ .palette_convert = qp_tft_panel_palette_convert_rgb565_swapped,
+ .append_pixels = qp_tft_panel_append_pixels_rgb565,
+ .append_pixdata = qp_tft_panel_append_pixdata,
+ },
+ .num_window_bytes = 2,
+ .swap_window_coords = false,
+ .opcodes =
+ {
+ .display_on = ILI9XXX_CMD_DISPLAY_ON,
+ .display_off = ILI9XXX_CMD_DISPLAY_OFF,
+ .set_column_address = ILI9XXX_SET_COL_ADDR,
+ .set_row_address = ILI9XXX_SET_PAGE_ADDR,
+ .enable_writes = ILI9XXX_SET_MEM,
+ },
+};
+
+// Waveshare tweaks
+const tft_panel_dc_reset_painter_driver_vtable_t ili9486_waveshare_driver_vtable = {
+ .base =
+ {
+ .init = qp_ili9486_init,
+ .power = qp_tft_panel_power,
+ .clear = qp_tft_panel_clear,
+ .flush = qp_tft_panel_flush,
+ .pixdata = qp_tft_panel_pixdata,
+ .viewport = qp_ili9486_viewport,
+ .palette_convert = qp_tft_panel_palette_convert_rgb565_swapped,
+ .append_pixels = qp_tft_panel_append_pixels_rgb565,
+ .append_pixdata = qp_tft_panel_append_pixdata,
+ },
+ .num_window_bytes = 2,
+ .swap_window_coords = false,
+ .opcodes =
+ {
+ .display_on = ILI9XXX_CMD_DISPLAY_ON,
+ .display_off = ILI9XXX_CMD_DISPLAY_OFF,
+ .set_column_address = ILI9XXX_SET_COL_ADDR,
+ .set_row_address = ILI9XXX_SET_PAGE_ADDR,
+ .enable_writes = ILI9XXX_SET_MEM,
+ },
+};
+
+static const painter_comms_with_command_vtable_t spi_comms_odd_cs_pulse_vtable = {
+ .base =
+ {
+ .comms_init = qp_comms_spi_dc_reset_init,
+ .comms_start = qp_comms_spi_start,
+ .comms_send = qp_comms_spi_send_data_odd_cs_pulse,
+ .comms_stop = qp_comms_spi_stop,
+ },
+ .send_command = qp_comms_spi_dc_reset_send_command_odd_cs_pulse,
+ .bulk_command_sequence = qp_comms_spi_send_command_sequence_odd_cs_pulse,
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// SPI
+
+#ifdef QUANTUM_PAINTER_ILI9486_SPI_ENABLE
+
+// Factory function for creating a handle to the ILI9486 device
+painter_device_t qp_ili9486_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode) {
+ for (uint32_t i = 0; i < ILI9486_NUM_DEVICES; ++i) {
+ tft_panel_dc_reset_painter_device_t *driver = &ili9486_drivers[i];
+ if (!driver->base.driver_vtable) {
+ driver->base.driver_vtable = (const painter_driver_vtable_t *)&ili9486_driver_vtable;
+ driver->base.comms_vtable = (const painter_comms_vtable_t *)&spi_comms_with_dc_vtable;
+ driver->base.native_bits_per_pixel = 16; // RGB565
+ driver->base.panel_width = panel_width;
+ driver->base.panel_height = panel_height;
+ driver->base.rotation = QP_ROTATION_0;
+ driver->base.offset_x = 0;
+ driver->base.offset_y = 0;
+
+ // SPI and other pin configuration
+ driver->base.comms_config = &driver->spi_dc_reset_config;
+ driver->spi_dc_reset_config.spi_config.chip_select_pin = chip_select_pin;
+ driver->spi_dc_reset_config.spi_config.divisor = spi_divisor;
+ driver->spi_dc_reset_config.spi_config.lsb_first = false;
+ driver->spi_dc_reset_config.spi_config.mode = spi_mode;
+ driver->spi_dc_reset_config.dc_pin = dc_pin;
+ driver->spi_dc_reset_config.reset_pin = reset_pin;
+
+ if (!qp_internal_register_device((painter_device_t)driver)) {
+ memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));
+ return NULL;
+ }
+
+ return (painter_device_t)driver;
+ }
+ }
+ return NULL;
+}
+
+painter_device_t qp_ili9486_make_spi_waveshare_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode) {
+ painter_device_t device = qp_ili9486_make_spi_device(panel_width, panel_height, chip_select_pin, dc_pin, reset_pin, spi_divisor, spi_mode);
+ if (device) {
+ tft_panel_dc_reset_painter_device_t *driver = (tft_panel_dc_reset_painter_device_t *)device;
+ driver->base.driver_vtable = (const painter_driver_vtable_t *)&ili9486_waveshare_driver_vtable;
+ driver->base.comms_vtable = (const painter_comms_vtable_t *)&spi_comms_odd_cs_pulse_vtable;
+ }
+ return device;
+}
+
+#endif // QUANTUM_PAINTER_ILI9486_SPI_ENABLE
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/drivers/painter/ili9xxx/qp_ili9486.h b/drivers/painter/ili9xxx/qp_ili9486.h
new file mode 100644
index 0000000000..9976a78da4
--- /dev/null
+++ b/drivers/painter/ili9xxx/qp_ili9486.h
@@ -0,0 +1,52 @@
+// Copyright 2021 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "gpio.h"
+#include "qp_internal.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Quantum Painter ILI9486 configurables (add to your keyboard's config.h)
+
+#ifndef ILI9486_NUM_DEVICES
+/**
+ * @def This controls the maximum number of ILI9486 devices that Quantum Painter can communicate with at any one time.
+ * Increasing this number allows for multiple displays to be used.
+ */
+# define ILI9486_NUM_DEVICES 1
+#endif
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Quantum Painter ILI9486 device factories
+
+#ifdef QUANTUM_PAINTER_ILI9486_SPI_ENABLE
+/**
+ * Factory method for an ILI9486 SPI LCD device.
+ *
+ * @param panel_width[in] the width of the display panel
+ * @param panel_height[in] the height of the display panel
+ * @param chip_select_pin[in] the GPIO pin used for SPI chip select
+ * @param dc_pin[in] the GPIO pin used for D/C control
+ * @param reset_pin[in] the GPIO pin used for RST
+ * @param spi_divisor[in] the SPI divisor to use when communicating with the display
+ * @param spi_mode[in] the SPI mode to use when communicating with the display
+ * @return the device handle used with all drawing routines in Quantum Painter
+ */
+painter_device_t qp_ili9486_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode);
+
+/**
+ * Factory method for an ILI9486 SPI LCD device.
+ *
+ * @param panel_width[in] the width of the display panel
+ * @param panel_height[in] the height of the display panel
+ * @param chip_select_pin[in] the GPIO pin used for SPI chip select
+ * @param dc_pin[in] the GPIO pin used for D/C control
+ * @param reset_pin[in] the GPIO pin used for RST
+ * @param spi_divisor[in] the SPI divisor to use when communicating with the display
+ * @param spi_mode[in] the SPI mode to use when communicating with the display
+ * @return the device handle used with all drawing routines in Quantum Painter
+ */
+painter_device_t qp_ili9486_make_spi_waveshare_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode);
+
+#endif // QUANTUM_PAINTER_ILI9486_SPI_ENABLE
diff --git a/drivers/painter/ili9xxx/qp_ili9xxx_opcodes.h b/drivers/painter/ili9xxx/qp_ili9xxx_opcodes.h
index f57e638e03..906f6cd772 100644
--- a/drivers/painter/ili9xxx/qp_ili9xxx_opcodes.h
+++ b/drivers/painter/ili9xxx/qp_ili9xxx_opcodes.h
@@ -70,6 +70,7 @@
#define ILI9XXX_SET_LIGHT_CTL_8 0xBF // Set backlight ctl 8
#define ILI9XXX_SET_POWER_CTL_1 0xC0 // Set power ctl 1
#define ILI9XXX_SET_POWER_CTL_2 0xC1 // Set power ctl 2
+#define ILI9XXX_SET_POWER_CTL_3 0xC2 // Set power ctl 3
#define ILI9XXX_SET_VCOM_CTL_1 0xC5 // Set VCOM ctl 1
#define ILI9XXX_SET_VCOM_CTL_2 0xC7 // Set VCOM ctl 2
#define ILI9XXX_POWER_CTL_A 0xCB // Set power control A
diff --git a/drivers/painter/sh1106/qp_sh1106.c b/drivers/painter/sh1106/qp_sh1106.c
index 7cb6e398fa..4117115aec 100644
--- a/drivers/painter/sh1106/qp_sh1106.c
+++ b/drivers/painter/sh1106/qp_sh1106.c
@@ -44,7 +44,7 @@ __attribute__((weak)) bool qp_sh1106_init(painter_device_t device, painter_rotat
}
// clang-format off
- const uint8_t sh1106_init_sequence[] = {
+ uint8_t sh1106_init_sequence[] = {
// Command, Delay, N, Data[N]
SH1106_SET_MUX_RATIO, 0, 1, 0x3F,
SH1106_DISPLAY_OFFSET, 0, 1, 0x00,
@@ -61,6 +61,16 @@ __attribute__((weak)) bool qp_sh1106_init(painter_device_t device, painter_rotat
};
// clang-format on
+ // If the display height is anything other than the default 64 pixels, change SH1106_SET_MUX_RATIO data byte to the correct value
+ if (driver->oled.base.panel_height != 64) {
+ sh1106_init_sequence[3] = driver->oled.base.panel_height - 1;
+ }
+
+ // For 128x32 or 96x16 displays, change SH1106_COM_PADS_HW_CFG data byte from alternative (0x12) to sequential (0x02) configuration
+ if (driver->oled.base.panel_height <= 32) {
+ sh1106_init_sequence[20] = 0x02;
+ }
+
qp_comms_bulk_command_sequence(device, sh1106_init_sequence, sizeof(sh1106_init_sequence));
return true;
}
@@ -203,4 +213,4 @@ painter_device_t qp_sh1106_make_i2c_device(uint16_t panel_width, uint16_t panel_
return NULL;
}
-#endif // QUANTUM_PAINTER_SH1106_SPI_ENABLE
+#endif // QUANTUM_PAINTER_SH1106_I2C_ENABLE
diff --git a/drivers/painter/sh1106/qp_sh1106_opcodes.h b/drivers/painter/sh1106/qp_sh1106_opcodes.h
index a2e100d770..bf86ba4c2c 100644
--- a/drivers/painter/sh1106/qp_sh1106_opcodes.h
+++ b/drivers/painter/sh1106/qp_sh1106_opcodes.h
@@ -16,7 +16,7 @@
#define SH1106_COM_PADS_HW_CFG 0xDA
#define SH1106_SET_CONTRAST 0x81
#define SH1106_SET_PRECHARGE_PERIOD 0xD9
-#define SH1106_VCOM_DETECT 0xDB
+#define SH1106_VCOM_DESELECT_LEVEL 0xDB
#define SH1106_ALL_ON_RESUME 0xA4
#define SH1106_NON_INVERTING_DISPLAY 0xA6
#define SH1106_DEACTIVATE_SCROLL 0x2E
diff --git a/drivers/sensors/adns5050.c b/drivers/sensors/adns5050.c
index b76268fba2..97daa8db09 100644
--- a/drivers/sensors/adns5050.c
+++ b/drivers/sensors/adns5050.c
@@ -47,9 +47,9 @@
void adns5050_init(void) {
// Initialize the ADNS serial pins.
- setPinOutput(ADNS5050_SCLK_PIN);
- setPinOutput(ADNS5050_SDIO_PIN);
- setPinOutput(ADNS5050_CS_PIN);
+ gpio_set_pin_output(ADNS5050_SCLK_PIN);
+ gpio_set_pin_output(ADNS5050_SDIO_PIN);
+ gpio_set_pin_output(ADNS5050_CS_PIN);
// reboot the adns.
// if the adns hasn't initialized yet, this is harmless.
@@ -69,30 +69,30 @@ void adns5050_init(void) {
// Just as with the serial protocol, this is used by the slave to send a
// synchronization signal to the master.
void adns5050_sync(void) {
- writePinLow(ADNS5050_CS_PIN);
+ gpio_write_pin_low(ADNS5050_CS_PIN);
wait_us(1);
- writePinHigh(ADNS5050_CS_PIN);
+ gpio_write_pin_high(ADNS5050_CS_PIN);
}
void adns5050_cs_select(void) {
- writePinLow(ADNS5050_CS_PIN);
+ gpio_write_pin_low(ADNS5050_CS_PIN);
}
void adns5050_cs_deselect(void) {
- writePinHigh(ADNS5050_CS_PIN);
+ gpio_write_pin_high(ADNS5050_CS_PIN);
}
uint8_t adns5050_serial_read(void) {
- setPinInput(ADNS5050_SDIO_PIN);
+ gpio_set_pin_input(ADNS5050_SDIO_PIN);
uint8_t byte = 0;
for (uint8_t i = 0; i < 8; ++i) {
- writePinLow(ADNS5050_SCLK_PIN);
+ gpio_write_pin_low(ADNS5050_SCLK_PIN);
wait_us(1);
- byte = (byte << 1) | readPin(ADNS5050_SDIO_PIN);
+ byte = (byte << 1) | gpio_read_pin(ADNS5050_SDIO_PIN);
- writePinHigh(ADNS5050_SCLK_PIN);
+ gpio_write_pin_high(ADNS5050_SCLK_PIN);
wait_us(1);
}
@@ -100,19 +100,19 @@ uint8_t adns5050_serial_read(void) {
}
void adns5050_serial_write(uint8_t data) {
- setPinOutput(ADNS5050_SDIO_PIN);
+ gpio_set_pin_output(ADNS5050_SDIO_PIN);
for (int8_t b = 7; b >= 0; b--) {
- writePinLow(ADNS5050_SCLK_PIN);
+ gpio_write_pin_low(ADNS5050_SCLK_PIN);
if (data & (1 << b))
- writePinHigh(ADNS5050_SDIO_PIN);
+ gpio_write_pin_high(ADNS5050_SDIO_PIN);
else
- writePinLow(ADNS5050_SDIO_PIN);
+ gpio_write_pin_low(ADNS5050_SDIO_PIN);
wait_us(2);
- writePinHigh(ADNS5050_SCLK_PIN);
+ gpio_write_pin_high(ADNS5050_SCLK_PIN);
}
// tSWR. See page 15 of the ADNS spec sheet.
diff --git a/drivers/sensors/adns9800.c b/drivers/sensors/adns9800.c
index 083ab34d9f..f34529ee90 100644
--- a/drivers/sensors/adns9800.c
+++ b/drivers/sensors/adns9800.c
@@ -100,7 +100,7 @@ uint8_t adns9800_read(uint8_t reg_addr) {
}
void adns9800_init(void) {
- setPinOutput(ADNS9800_CS_PIN);
+ gpio_set_pin_output(ADNS9800_CS_PIN);
spi_init();
diff --git a/drivers/sensors/analog_joystick.c b/drivers/sensors/analog_joystick.c
index 12256a8e7a..15b35a45f2 100644
--- a/drivers/sensors/analog_joystick.c
+++ b/drivers/sensors/analog_joystick.c
@@ -22,17 +22,28 @@
#include <stdlib.h>
// Set Parameters
+#ifndef ANALOG_JOYSTICK_AUTO_AXIS
uint16_t minAxisValue = ANALOG_JOYSTICK_AXIS_MIN;
uint16_t maxAxisValue = ANALOG_JOYSTICK_AXIS_MAX;
+#else
+int16_t minAxisValues[2];
+int16_t maxAxisValues[2];
+#endif
uint8_t maxCursorSpeed = ANALOG_JOYSTICK_SPEED_MAX;
uint8_t speedRegulator = ANALOG_JOYSTICK_SPEED_REGULATOR; // Lower Values Create Faster Movement
+#ifdef ANALOG_JOYSTICK_WEIGHTS
+int8_t weights[101] = ANALOG_JOYSTICK_WEIGHTS;
+#endif
+
int16_t xOrigin, yOrigin;
uint16_t lastCursor = 0;
-int16_t axisCoordinate(pin_t pin, uint16_t origin) {
+uint8_t prevValues[2] = {0, 0};
+
+int16_t axisCoordinate(pin_t pin, uint16_t origin, uint8_t axis) {
int8_t direction;
int16_t distanceFromOrigin;
int16_t range;
@@ -43,12 +54,27 @@ int16_t axisCoordinate(pin_t pin, uint16_t origin) {
return 0;
} else if (origin > position) {
distanceFromOrigin = origin - position;
- range = origin - minAxisValue;
- direction = -1;
+#ifdef ANALOG_JOYSTICK_AUTO_AXIS
+ if (position < minAxisValues[axis]) {
+ minAxisValues[axis] = position;
+ }
+ range = origin - minAxisValues[axis];
+#else
+ range = origin - minAxisValue;
+#endif
+ direction = -1;
} else {
distanceFromOrigin = position - origin;
- range = maxAxisValue - origin;
- direction = 1;
+
+#ifdef ANALOG_JOYSTICK_AUTO_AXIS
+ if (position > maxAxisValues[axis]) {
+ maxAxisValues[axis] = position;
+ }
+ range = maxAxisValues[axis] - origin;
+#else
+ range = maxAxisValue - origin;
+#endif
+ direction = 1;
}
float percent = (float)distanceFromOrigin / range;
@@ -62,14 +88,29 @@ int16_t axisCoordinate(pin_t pin, uint16_t origin) {
}
}
-int8_t axisToMouseComponent(pin_t pin, int16_t origin, uint8_t maxSpeed) {
- int16_t coordinate = axisCoordinate(pin, origin);
+int8_t axisToMouseComponent(pin_t pin, int16_t origin, uint8_t maxSpeed, uint8_t axis) {
+ int16_t coordinate = axisCoordinate(pin, origin, axis);
+ int8_t result;
+#ifndef ANALOG_JOYSTICK_WEIGHTS
if (coordinate != 0) {
float percent = (float)coordinate / 100;
- return percent * maxCursorSpeed * (abs(coordinate) / speedRegulator);
+ result = percent * maxCursorSpeed * (abs(coordinate) / speedRegulator);
} else {
return 0;
}
+#else
+ result = weights[abs(coordinate)] * (coordinate < 0 ? -1 : 1) * maxCursorSpeed / speedRegulator;
+#endif
+
+#ifdef ANALOG_JOYSTICK_CUTOFF
+ uint8_t pv = prevValues[axis];
+ prevValues[axis] = abs(result);
+ if (pv > abs(result)) {
+ return 0;
+ }
+#endif
+
+ return result;
}
report_analog_joystick_t analog_joystick_read(void) {
@@ -77,20 +118,30 @@ report_analog_joystick_t analog_joystick_read(void) {
if (timer_elapsed(lastCursor) > ANALOG_JOYSTICK_READ_INTERVAL) {
lastCursor = timer_read();
- report.x = axisToMouseComponent(ANALOG_JOYSTICK_X_AXIS_PIN, xOrigin, maxCursorSpeed);
- report.y = axisToMouseComponent(ANALOG_JOYSTICK_Y_AXIS_PIN, yOrigin, maxCursorSpeed);
+ report.x = axisToMouseComponent(ANALOG_JOYSTICK_X_AXIS_PIN, xOrigin, maxCursorSpeed, 0);
+ report.y = axisToMouseComponent(ANALOG_JOYSTICK_Y_AXIS_PIN, yOrigin, maxCursorSpeed, 1);
}
#ifdef ANALOG_JOYSTICK_CLICK_PIN
- report.button = !readPin(ANALOG_JOYSTICK_CLICK_PIN);
+ report.button = !gpio_read_pin(ANALOG_JOYSTICK_CLICK_PIN);
#endif
return report;
}
void analog_joystick_init(void) {
+ gpio_set_pin_input_high(ANALOG_JOYSTICK_X_AXIS_PIN);
+ gpio_set_pin_input_high(ANALOG_JOYSTICK_Y_AXIS_PIN);
+
#ifdef ANALOG_JOYSTICK_CLICK_PIN
- setPinInputHigh(ANALOG_JOYSTICK_CLICK_PIN);
+ gpio_set_pin_input_high(ANALOG_JOYSTICK_CLICK_PIN);
#endif
// Account for drift
xOrigin = analogReadPin(ANALOG_JOYSTICK_X_AXIS_PIN);
yOrigin = analogReadPin(ANALOG_JOYSTICK_Y_AXIS_PIN);
+
+#ifdef ANALOG_JOYSTICK_AUTO_AXIS
+ minAxisValues[0] = xOrigin - 100;
+ minAxisValues[1] = yOrigin - 100;
+ maxAxisValues[0] = xOrigin + 100;
+ maxAxisValues[1] = yOrigin + 100;
+#endif
}
diff --git a/drivers/sensors/azoteq_iqs5xx.c b/drivers/sensors/azoteq_iqs5xx.c
index 521f558b5f..367873eb06 100644
--- a/drivers/sensors/azoteq_iqs5xx.c
+++ b/drivers/sensors/azoteq_iqs5xx.c
@@ -107,18 +107,17 @@ static struct {
i2c_status_t azoteq_iqs5xx_wake(void) {
uint8_t data = 0;
- i2c_status_t status = i2c_readReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_PREVIOUS_CYCLE_TIME, (uint8_t *)&data, sizeof(data), 1);
- i2c_stop();
+ i2c_status_t status = i2c_read_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_PREVIOUS_CYCLE_TIME, (uint8_t *)&data, sizeof(data), 1);
wait_us(150);
return status;
}
i2c_status_t azoteq_iqs5xx_end_session(void) {
const uint8_t END_BYTE = 1; // any data
- return i2c_writeReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_END_COMMS, &END_BYTE, 1, AZOTEQ_IQS5XX_TIMEOUT_MS);
+ return i2c_write_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_END_COMMS, &END_BYTE, 1, AZOTEQ_IQS5XX_TIMEOUT_MS);
}
i2c_status_t azoteq_iqs5xx_get_base_data(azoteq_iqs5xx_base_data_t *base_data) {
- i2c_status_t status = i2c_readReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_PREVIOUS_CYCLE_TIME, (uint8_t *)base_data, 10, AZOTEQ_IQS5XX_TIMEOUT_MS);
+ i2c_status_t status = i2c_read_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_PREVIOUS_CYCLE_TIME, (uint8_t *)base_data, 10, AZOTEQ_IQS5XX_TIMEOUT_MS);
if (status == I2C_STATUS_SUCCESS) {
azoteq_iqs5xx_end_session();
}
@@ -131,7 +130,7 @@ i2c_status_t azoteq_iqs5xx_get_report_rate(azoteq_iqs5xx_report_rate_t *report_r
return I2C_STATUS_ERROR;
}
uint16_t selected_reg = AZOTEQ_IQS5XX_REG_REPORT_RATE_ACTIVE + (2 * mode);
- i2c_status_t status = i2c_readReg16(AZOTEQ_IQS5XX_ADDRESS, selected_reg, (uint8_t *)report_rate, 2, AZOTEQ_IQS5XX_TIMEOUT_MS);
+ i2c_status_t status = i2c_read_register16(AZOTEQ_IQS5XX_ADDRESS, selected_reg, (uint8_t *)report_rate, 2, AZOTEQ_IQS5XX_TIMEOUT_MS);
if (end_session) {
azoteq_iqs5xx_end_session();
}
@@ -147,7 +146,7 @@ i2c_status_t azoteq_iqs5xx_set_report_rate(uint16_t report_rate_ms, azoteq_iqs5x
azoteq_iqs5xx_report_rate_t report_rate = {0};
report_rate.h = (uint8_t)((report_rate_ms >> 8) & 0xFF);
report_rate.l = (uint8_t)(report_rate_ms & 0xFF);
- i2c_status_t status = i2c_writeReg16(AZOTEQ_IQS5XX_ADDRESS, selected_reg, (uint8_t *)&report_rate, 2, AZOTEQ_IQS5XX_TIMEOUT_MS);
+ i2c_status_t status = i2c_write_register16(AZOTEQ_IQS5XX_ADDRESS, selected_reg, (uint8_t *)&report_rate, 2, AZOTEQ_IQS5XX_TIMEOUT_MS);
if (end_session) {
azoteq_iqs5xx_end_session();
}
@@ -156,10 +155,10 @@ i2c_status_t azoteq_iqs5xx_set_report_rate(uint16_t report_rate_ms, azoteq_iqs5x
i2c_status_t azoteq_iqs5xx_set_reati(bool enabled, bool end_session) {
azoteq_iqs5xx_system_config_0_t config = {0};
- i2c_status_t status = i2c_readReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SYSTEM_CONFIG_0, (uint8_t *)&config, sizeof(azoteq_iqs5xx_system_config_0_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ i2c_status_t status = i2c_read_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SYSTEM_CONFIG_0, (uint8_t *)&config, sizeof(azoteq_iqs5xx_system_config_0_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
if (status == I2C_STATUS_SUCCESS) {
config.reati = enabled;
- status = i2c_writeReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SYSTEM_CONFIG_0, (uint8_t *)&config, sizeof(azoteq_iqs5xx_system_config_0_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ status = i2c_write_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SYSTEM_CONFIG_0, (uint8_t *)&config, sizeof(azoteq_iqs5xx_system_config_0_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
}
if (end_session) {
azoteq_iqs5xx_end_session();
@@ -169,7 +168,7 @@ i2c_status_t azoteq_iqs5xx_set_reati(bool enabled, bool end_session) {
i2c_status_t azoteq_iqs5xx_set_event_mode(bool enabled, bool end_session) {
azoteq_iqs5xx_system_config_1_t config = {0};
- i2c_status_t status = i2c_readReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SYSTEM_CONFIG_1, (uint8_t *)&config, sizeof(azoteq_iqs5xx_system_config_1_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ i2c_status_t status = i2c_read_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SYSTEM_CONFIG_1, (uint8_t *)&config, sizeof(azoteq_iqs5xx_system_config_1_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
if (status == I2C_STATUS_SUCCESS) {
config.event_mode = enabled;
config.touch_event = true;
@@ -179,7 +178,7 @@ i2c_status_t azoteq_iqs5xx_set_event_mode(bool enabled, bool end_session) {
config.reati_event = false;
config.alp_prox_event = false;
config.gesture_event = true;
- status = i2c_writeReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SYSTEM_CONFIG_1, (uint8_t *)&config, sizeof(azoteq_iqs5xx_system_config_1_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ status = i2c_write_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SYSTEM_CONFIG_1, (uint8_t *)&config, sizeof(azoteq_iqs5xx_system_config_1_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
}
if (end_session) {
azoteq_iqs5xx_end_session();
@@ -189,7 +188,7 @@ i2c_status_t azoteq_iqs5xx_set_event_mode(bool enabled, bool end_session) {
i2c_status_t azoteq_iqs5xx_set_gesture_config(bool end_session) {
azoteq_iqs5xx_gesture_config_t config = {0};
- i2c_status_t status = i2c_readReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SINGLE_FINGER_GESTURES, (uint8_t *)&config, sizeof(azoteq_iqs5xx_gesture_config_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ i2c_status_t status = i2c_read_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SINGLE_FINGER_GESTURES, (uint8_t *)&config, sizeof(azoteq_iqs5xx_gesture_config_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
pd_dprintf("azo scroll: %d\n", config.multi_finger_gestures.scroll);
if (status == I2C_STATUS_SUCCESS) {
config.single_finger_gestures.single_tap = AZOTEQ_IQS5XX_TAP_ENABLE;
@@ -211,7 +210,7 @@ i2c_status_t azoteq_iqs5xx_set_gesture_config(bool end_session) {
config.scroll_initial_distance = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(AZOTEQ_IQS5XX_SCROLL_INITIAL_DISTANCE);
config.zoom_initial_distance = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(AZOTEQ_IQS5XX_ZOOM_INITIAL_DISTANCE);
config.zoom_consecutive_distance = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(AZOTEQ_IQS5XX_ZOOM_CONSECUTIVE_DISTANCE);
- status = i2c_writeReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SINGLE_FINGER_GESTURES, (uint8_t *)&config, sizeof(azoteq_iqs5xx_gesture_config_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ status = i2c_write_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SINGLE_FINGER_GESTURES, (uint8_t *)&config, sizeof(azoteq_iqs5xx_gesture_config_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
}
if (end_session) {
azoteq_iqs5xx_end_session();
@@ -221,7 +220,7 @@ i2c_status_t azoteq_iqs5xx_set_gesture_config(bool end_session) {
i2c_status_t azoteq_iqs5xx_set_xy_config(bool flip_x, bool flip_y, bool switch_xy, bool palm_reject, bool end_session) {
azoteq_iqs5xx_xy_config_0_t config = {0};
- i2c_status_t status = i2c_readReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_XY_CONFIG_0, (uint8_t *)&config, sizeof(azoteq_iqs5xx_xy_config_0_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ i2c_status_t status = i2c_read_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_XY_CONFIG_0, (uint8_t *)&config, sizeof(azoteq_iqs5xx_xy_config_0_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
if (status == I2C_STATUS_SUCCESS) {
if (flip_x) {
config.flip_x = !config.flip_x;
@@ -233,7 +232,7 @@ i2c_status_t azoteq_iqs5xx_set_xy_config(bool flip_x, bool flip_y, bool switch_x
config.switch_xy_axis = !config.switch_xy_axis;
}
config.palm_reject = palm_reject;
- status = i2c_writeReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_XY_CONFIG_0, (uint8_t *)&config, sizeof(azoteq_iqs5xx_xy_config_0_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ status = i2c_write_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_XY_CONFIG_0, (uint8_t *)&config, sizeof(azoteq_iqs5xx_xy_config_0_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
}
if (end_session) {
azoteq_iqs5xx_end_session();
@@ -243,11 +242,11 @@ i2c_status_t azoteq_iqs5xx_set_xy_config(bool flip_x, bool flip_y, bool switch_x
i2c_status_t azoteq_iqs5xx_reset_suspend(bool reset, bool suspend, bool end_session) {
azoteq_iqs5xx_system_control_1_t config = {0};
- i2c_status_t status = i2c_readReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SYSTEM_CONTROL_1, (uint8_t *)&config, sizeof(azoteq_iqs5xx_system_control_1_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ i2c_status_t status = i2c_read_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SYSTEM_CONTROL_1, (uint8_t *)&config, sizeof(azoteq_iqs5xx_system_control_1_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
if (status == I2C_STATUS_SUCCESS) {
config.reset = reset;
config.suspend = suspend;
- status = i2c_writeReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SYSTEM_CONTROL_1, (uint8_t *)&config, sizeof(azoteq_iqs5xx_system_control_1_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ status = i2c_write_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SYSTEM_CONTROL_1, (uint8_t *)&config, sizeof(azoteq_iqs5xx_system_control_1_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
}
if (end_session) {
azoteq_iqs5xx_end_session();
@@ -260,14 +259,14 @@ void azoteq_iqs5xx_set_cpi(uint16_t cpi) {
azoteq_iqs5xx_resolution_t resolution = {0};
resolution.x_resolution = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(MIN(azoteq_iqs5xx_device_resolution_t.resolution_x, AZOTEQ_IQS5XX_INCH_TO_RESOLUTION_X(cpi)));
resolution.y_resolution = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(MIN(azoteq_iqs5xx_device_resolution_t.resolution_y, AZOTEQ_IQS5XX_INCH_TO_RESOLUTION_Y(cpi)));
- i2c_writeReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_X_RESOLUTION, (uint8_t *)&resolution, sizeof(azoteq_iqs5xx_resolution_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ i2c_write_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_X_RESOLUTION, (uint8_t *)&resolution, sizeof(azoteq_iqs5xx_resolution_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
}
}
uint16_t azoteq_iqs5xx_get_cpi(void) {
if (azoteq_iqs5xx_product_number != AZOTEQ_IQS5XX_UNKNOWN) {
azoteq_iqs5xx_resolution_t resolution = {0};
- i2c_status_t status = i2c_readReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_X_RESOLUTION, (uint8_t *)&resolution, sizeof(azoteq_iqs5xx_resolution_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ i2c_status_t status = i2c_read_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_X_RESOLUTION, (uint8_t *)&resolution, sizeof(azoteq_iqs5xx_resolution_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
if (status == I2C_STATUS_SUCCESS) {
return AZOTEQ_IQS5XX_RESOLUTION_X_TO_INCH(AZOTEQ_IQS5XX_SWAP_H_L_BYTES(resolution.x_resolution));
}
@@ -276,7 +275,7 @@ uint16_t azoteq_iqs5xx_get_cpi(void) {
}
uint16_t azoteq_iqs5xx_get_product(void) {
- i2c_status_t status = i2c_readReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_PRODUCT_NUMBER, (uint8_t *)&azoteq_iqs5xx_product_number, sizeof(uint16_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ i2c_status_t status = i2c_read_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_PRODUCT_NUMBER, (uint8_t *)&azoteq_iqs5xx_product_number, sizeof(uint16_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
if (status == I2C_STATUS_SUCCESS) {
azoteq_iqs5xx_product_number = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(azoteq_iqs5xx_product_number);
}
diff --git a/drivers/sensors/cirque_pinnacle.c b/drivers/sensors/cirque_pinnacle.c
index 3131805c20..9afc9df804 100644
--- a/drivers/sensors/cirque_pinnacle.c
+++ b/drivers/sensors/cirque_pinnacle.c
@@ -216,6 +216,20 @@ void cirque_pinnacle_cursor_smoothing(bool enable) {
RAP_Write(HOSTREG__FEEDCONFIG3, feedconfig3);
}
+// Check sensor is connected
+bool cirque_pinnacle_connected(void) {
+ uint8_t current_zidle = 0;
+ uint8_t temp_zidle = 0;
+ RAP_ReadBytes(HOSTREG__ZIDLE, &current_zidle, 1);
+ RAP_Write(HOSTREG__ZIDLE, HOSTREG__ZIDLE_DEFVAL);
+ RAP_ReadBytes(HOSTREG__ZIDLE, &temp_zidle, 1);
+ if (temp_zidle == HOSTREG__ZIDLE_DEFVAL) {
+ RAP_Write(HOSTREG__ZIDLE, current_zidle);
+ return true;
+ }
+ return false;
+}
+
/* Pinnacle-based TM040040/TM035035/TM023023 Functions */
void cirque_pinnacle_init(void) {
#if defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi)
@@ -274,6 +288,10 @@ void cirque_pinnacle_init(void) {
}
cirque_pinnacle_enable_feed(true);
+
+#ifndef CIRQUE_PINNACLE_SKIP_SENSOR_CHECK
+ touchpad_init = cirque_pinnacle_connected();
+#endif
}
pinnacle_data_t cirque_pinnacle_read_data(void) {
@@ -320,6 +338,15 @@ pinnacle_data_t cirque_pinnacle_read_data(void) {
result.wheelCount = ((int8_t*)data)[3];
#endif
+#ifdef CIRQUE_PINNACLE_REACHABLE_CALIBRATION
+ static uint16_t xMin = UINT16_MAX, yMin = UINT16_MAX, yMax = 0, xMax = 0;
+ if (result.xValue < xMin) xMin = result.xValue;
+ if (result.xValue > xMax) xMax = result.xValue;
+ if (result.yValue < yMin) yMin = result.yValue;
+ if (result.yValue > yMax) yMax = result.yValue;
+ pd_dprintf("%s: xLo=%3d xHi=%3d yLo=%3d yHi=%3d\n", __FUNCTION__, xMin, xMax, yMin, yMax);
+#endif
+
result.valid = true;
return result;
}
diff --git a/drivers/sensors/cirque_pinnacle_i2c.c b/drivers/sensors/cirque_pinnacle_i2c.c
index 3c11e5f079..a3622e9d60 100644
--- a/drivers/sensors/cirque_pinnacle_i2c.c
+++ b/drivers/sensors/cirque_pinnacle_i2c.c
@@ -14,12 +14,11 @@ extern bool touchpad_init;
void RAP_ReadBytes(uint8_t address, uint8_t* data, uint8_t count) {
uint8_t cmdByte = READ_MASK | address; // Form the READ command byte
if (touchpad_init) {
- i2c_writeReg(CIRQUE_PINNACLE_ADDR << 1, cmdByte, NULL, 0, CIRQUE_PINNACLE_TIMEOUT);
- if (i2c_readReg(CIRQUE_PINNACLE_ADDR << 1, cmdByte, data, count, CIRQUE_PINNACLE_TIMEOUT) != I2C_STATUS_SUCCESS) {
- pd_dprintf("error cirque_pinnacle i2c_readReg\n");
+ i2c_write_register(CIRQUE_PINNACLE_ADDR << 1, cmdByte, NULL, 0, CIRQUE_PINNACLE_TIMEOUT);
+ if (i2c_read_register(CIRQUE_PINNACLE_ADDR << 1, cmdByte, data, count, CIRQUE_PINNACLE_TIMEOUT) != I2C_STATUS_SUCCESS) {
+ pd_dprintf("error cirque_pinnacle i2c_read_register\n");
touchpad_init = false;
}
- i2c_stop();
}
}
@@ -28,10 +27,9 @@ void RAP_Write(uint8_t address, uint8_t data) {
uint8_t cmdByte = WRITE_MASK | address; // Form the WRITE command byte
if (touchpad_init) {
- if (i2c_writeReg(CIRQUE_PINNACLE_ADDR << 1, cmdByte, &data, sizeof(data), CIRQUE_PINNACLE_TIMEOUT) != I2C_STATUS_SUCCESS) {
- pd_dprintf("error cirque_pinnacle i2c_writeReg\n");
+ if (i2c_write_register(CIRQUE_PINNACLE_ADDR << 1, cmdByte, &data, sizeof(data), CIRQUE_PINNACLE_TIMEOUT) != I2C_STATUS_SUCCESS) {
+ pd_dprintf("error cirque_pinnacle i2c_write_register\n");
touchpad_init = false;
}
- i2c_stop();
}
}
diff --git a/drivers/sensors/paw3204.c b/drivers/sensors/paw3204.c
index a13753dd6f..28c47522ed 100644
--- a/drivers/sensors/paw3204.c
+++ b/drivers/sensors/paw3204.c
@@ -51,8 +51,8 @@ uint8_t paw3204_read_reg(uint8_t reg_addr);
void paw3204_write_reg(uint8_t reg_addr, uint8_t data);
void paw3204_init(void) {
- setPinOutput(PAW3204_SCLK_PIN); // setclockpin to output
- setPinInputHigh(PAW3204_SDIO_PIN); // set datapin input high
+ gpio_set_pin_output(PAW3204_SCLK_PIN); // setclockpin to output
+ gpio_set_pin_input_high(PAW3204_SDIO_PIN); // set datapin input high
paw3204_write_reg(REG_SETUP, 0x86); // reset sensor and set 1600cpi
wait_us(5);
@@ -64,16 +64,16 @@ void paw3204_init(void) {
}
uint8_t paw3204_serial_read(void) {
- setPinInput(PAW3204_SDIO_PIN);
+ gpio_set_pin_input(PAW3204_SDIO_PIN);
uint8_t byte = 0;
for (uint8_t i = 0; i < 8; ++i) {
- writePinLow(PAW3204_SCLK_PIN);
+ gpio_write_pin_low(PAW3204_SCLK_PIN);
wait_us(1);
- byte = (byte << 1) | readPin(PAW3204_SDIO_PIN);
+ byte = (byte << 1) | gpio_read_pin(PAW3204_SDIO_PIN);
- writePinHigh(PAW3204_SCLK_PIN);
+ gpio_write_pin_high(PAW3204_SCLK_PIN);
wait_us(1);
}
@@ -81,17 +81,17 @@ uint8_t paw3204_serial_read(void) {
}
void paw3204_serial_write(uint8_t data) {
- writePinLow(PAW3204_SDIO_PIN);
- setPinOutput(PAW3204_SDIO_PIN);
+ gpio_write_pin_low(PAW3204_SDIO_PIN);
+ gpio_set_pin_output(PAW3204_SDIO_PIN);
for (int8_t b = 7; b >= 0; b--) {
- writePinLow(PAW3204_SCLK_PIN);
+ gpio_write_pin_low(PAW3204_SCLK_PIN);
if (data & (1 << b)) {
- writePinHigh(PAW3204_SDIO_PIN);
+ gpio_write_pin_high(PAW3204_SDIO_PIN);
} else {
- writePinLow(PAW3204_SDIO_PIN);
+ gpio_write_pin_low(PAW3204_SDIO_PIN);
}
- writePinHigh(PAW3204_SCLK_PIN);
+ gpio_write_pin_high(PAW3204_SCLK_PIN);
}
wait_us(4);
diff --git a/drivers/sensors/pimoroni_trackball.c b/drivers/sensors/pimoroni_trackball.c
index 326e59744f..9c6d26d73d 100644
--- a/drivers/sensors/pimoroni_trackball.c
+++ b/drivers/sensors/pimoroni_trackball.c
@@ -56,13 +56,13 @@ void pimoroni_trackball_set_cpi(uint16_t cpi) {
void pimoroni_trackball_set_rgbw(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
uint8_t data[4] = {r, g, b, w};
- __attribute__((unused)) i2c_status_t status = i2c_writeReg(PIMORONI_TRACKBALL_ADDRESS << 1, PIMORONI_TRACKBALL_REG_LED_RED, data, sizeof(data), PIMORONI_TRACKBALL_TIMEOUT);
+ __attribute__((unused)) i2c_status_t status = i2c_write_register(PIMORONI_TRACKBALL_ADDRESS << 1, PIMORONI_TRACKBALL_REG_LED_RED, data, sizeof(data), PIMORONI_TRACKBALL_TIMEOUT);
pd_dprintf("Trackball RGBW i2c_status_t: %d\n", status);
}
i2c_status_t read_pimoroni_trackball(pimoroni_data_t* data) {
- i2c_status_t status = i2c_readReg(PIMORONI_TRACKBALL_ADDRESS << 1, PIMORONI_TRACKBALL_REG_LEFT, (uint8_t*)data, sizeof(*data), PIMORONI_TRACKBALL_TIMEOUT);
+ i2c_status_t status = i2c_read_register(PIMORONI_TRACKBALL_ADDRESS << 1, PIMORONI_TRACKBALL_REG_LEFT, (uint8_t*)data, sizeof(*data), PIMORONI_TRACKBALL_TIMEOUT);
#ifdef POINTING_DEVICE_DEBUG
static uint16_t d_timer;
diff --git a/drivers/sensors/pmw3320.c b/drivers/sensors/pmw3320.c
index 69a584f4e1..f19fbfd1ab 100644
--- a/drivers/sensors/pmw3320.c
+++ b/drivers/sensors/pmw3320.c
@@ -24,9 +24,9 @@
void pmw3320_init(void) {
// Initialize sensor serial pins.
- setPinOutput(PMW3320_SCLK_PIN);
- setPinOutput(PMW3320_SDIO_PIN);
- setPinOutput(PMW3320_CS_PIN);
+ gpio_set_pin_output(PMW3320_SCLK_PIN);
+ gpio_set_pin_output(PMW3320_SDIO_PIN);
+ gpio_set_pin_output(PMW3320_CS_PIN);
// reboot the sensor.
pmw3320_write_reg(REG_Power_Up_Reset, 0x5a);
@@ -54,30 +54,30 @@ void pmw3320_init(void) {
// Just as with the serial protocol, this is used by the slave to send a
// synchronization signal to the master.
void pmw3320_sync(void) {
- writePinLow(PMW3320_CS_PIN);
+ gpio_write_pin_low(PMW3320_CS_PIN);
wait_us(1);
- writePinHigh(PMW3320_CS_PIN);
+ gpio_write_pin_high(PMW3320_CS_PIN);
}
void pmw3320_cs_select(void) {
- writePinLow(PMW3320_CS_PIN);
+ gpio_write_pin_low(PMW3320_CS_PIN);
}
void pmw3320_cs_deselect(void) {
- writePinHigh(PMW3320_CS_PIN);
+ gpio_write_pin_high(PMW3320_CS_PIN);
}
uint8_t pmw3320_serial_read(void) {
- setPinInput(PMW3320_SDIO_PIN);
+ gpio_set_pin_input(PMW3320_SDIO_PIN);
uint8_t byte = 0;
for (uint8_t i = 0; i < 8; ++i) {
- writePinLow(PMW3320_SCLK_PIN);
+ gpio_write_pin_low(PMW3320_SCLK_PIN);
wait_us(1);
- byte = (byte << 1) | readPin(PMW3320_SDIO_PIN);
+ byte = (byte << 1) | gpio_read_pin(PMW3320_SDIO_PIN);
- writePinHigh(PMW3320_SCLK_PIN);
+ gpio_write_pin_high(PMW3320_SCLK_PIN);
wait_us(1);
}
@@ -85,19 +85,19 @@ uint8_t pmw3320_serial_read(void) {
}
void pmw3320_serial_write(uint8_t data) {
- setPinOutput(PMW3320_SDIO_PIN);
+ gpio_set_pin_output(PMW3320_SDIO_PIN);
for (int8_t b = 7; b >= 0; b--) {
- writePinLow(PMW3320_SCLK_PIN);
+ gpio_write_pin_low(PMW3320_SCLK_PIN);
if (data & (1 << b))
- writePinHigh(PMW3320_SDIO_PIN);
+ gpio_write_pin_high(PMW3320_SDIO_PIN);
else
- writePinLow(PMW3320_SDIO_PIN);
+ gpio_write_pin_low(PMW3320_SDIO_PIN);
wait_us(2);
- writePinHigh(PMW3320_SCLK_PIN);
+ gpio_write_pin_high(PMW3320_SCLK_PIN);
}
// This was taken from ADNS5050 driver.
diff --git a/drivers/sensors/pmw3360.c b/drivers/sensors/pmw3360.c
index a7dc687f50..8408daa945 100644
--- a/drivers/sensors/pmw3360.c
+++ b/drivers/sensors/pmw3360.c
@@ -15,6 +15,9 @@ uint16_t pmw33xx_get_cpi(uint8_t sensor) {
}
uint8_t cpival = pmw33xx_read(sensor, REG_Config1);
+ // In some cases (100, 900, 1700, 2500), reading the CPI corrupts the firmware and the sensor stops responding.
+ // To avoid this, we write the value back to the sensor, which seems to prevent the corruption.
+ pmw33xx_write(sensor, REG_Config1, cpival);
return (uint16_t)((cpival + 1) & 0xFF) * PMW33XX_CPI_STEP;
}
diff --git a/drivers/usb2422.c b/drivers/usb2422.c
index 1d33b5acf8..de0e399f87 100644
--- a/drivers/usb2422.c
+++ b/drivers/usb2422.c
@@ -346,10 +346,10 @@ static void USB2422_write_block(void) {
void USB2422_init(void) {
#ifdef USB2422_RESET_PIN
- setPinOutput(USB2422_RESET_PIN);
+ gpio_set_pin_output(USB2422_RESET_PIN);
#endif
#ifdef USB2422_ACTIVE_PIN
- setPinInput(USB2422_ACTIVE_PIN);
+ gpio_set_pin_input(USB2422_ACTIVE_PIN);
#endif
i2c_init(); // IC2 clk must be high at USB2422 reset release time to signal SMB configuration
@@ -387,15 +387,15 @@ void USB2422_configure(void) {
void USB2422_reset(void) {
#ifdef USB2422_RESET_PIN
- writePinLow(USB2422_RESET_PIN);
+ gpio_write_pin_low(USB2422_RESET_PIN);
wait_us(2);
- writePinHigh(USB2422_RESET_PIN);
+ gpio_write_pin_high(USB2422_RESET_PIN);
#endif
}
bool USB2422_active(void) {
#ifdef USB2422_ACTIVE_PIN
- return readPin(USB2422_ACTIVE_PIN);
+ return gpio_read_pin(USB2422_ACTIVE_PIN);
#else
return 1;
#endif
diff --git a/drivers/ws2812.h b/drivers/ws2812.h
index 1527df23d3..134de51c50 100644
--- a/drivers/ws2812.h
+++ b/drivers/ws2812.h
@@ -56,9 +56,9 @@
# define WS2812_TRST_US 280
#endif
-#if defined(RGBLED_NUM)
-# define WS2812_LED_COUNT RGBLED_NUM
-#elif defined(RGB_MATRIX_LED_COUNT)
+#if defined(RGBLIGHT_WS2812)
+# define WS2812_LED_COUNT RGBLIGHT_LED_COUNT
+#elif defined(RGB_MATRIX_WS2812)
# define WS2812_LED_COUNT RGB_MATRIX_LED_COUNT
#endif