Skip to content

Commit 32de27b

Browse files
authored
is31fl3736: extract single-color API (#22133)
1 parent 5da3604 commit 32de27b

23 files changed

+654
-95
lines changed

builddefs/common_features.mk

+8-1
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ LED_MATRIX_DRIVER := aw20216s
354354
endif
355355

356356
LED_MATRIX_ENABLE ?= no
357-
VALID_LED_MATRIX_TYPES := is31fl3731 is31fl3742a is31fl3743a is31fl3745 is31fl3746a ckled2001 custom
357+
VALID_LED_MATRIX_TYPES := is31fl3731 is31fl3736 is31fl3742a is31fl3743a is31fl3745 is31fl3746a ckled2001 custom
358358
# TODO: is31fl3733 is31fl3737 is31fl3741
359359

360360
ifeq ($(strip $(LED_MATRIX_ENABLE)), yes)
@@ -384,6 +384,13 @@ endif
384384
QUANTUM_LIB_SRC += i2c_master.c
385385
endif
386386

387+
ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3736)
388+
OPT_DEFS += -DHAL_USE_I2C=TRUE
389+
COMMON_VPATH += $(DRIVER_PATH)/led/issi
390+
SRC += is31fl3736-simple.c
391+
QUANTUM_LIB_SRC += i2c_master.c
392+
endif
393+
387394
ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3742a)
388395
OPT_DEFS += -DIS31FLCOMMON -DHAL_USE_I2C=TRUE
389396
COMMON_VPATH += $(DRIVER_PATH)/led/issi

docs/reference_info_json.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ Configures the [LED Matrix](feature_led_matrix.md) feature.
329329
* The centroid (geometric center) of the LEDs. Used for certain effects.
330330
* Default: `[112, 32]`
331331
* `driver` (Required)
332-
* The driver to use. Must be one of `ckled2001`, `custom`, `is31fl3731`, `is31fl3742a`, `is31fl3743a`, `is31fl3745`, `is31fl3746a`.
332+
* The driver to use. Must be one of `ckled2001`, `custom`, `is31fl3731`, `is31fl3736`, `is31fl3742a`, `is31fl3743a`, `is31fl3745`, `is31fl3746a`.
333333
* `layout` (Required)
334334
* List of LED configuration dictionaries. Each dictionary contains:
335335
* `flags` (Required)

drivers/led/issi/is31fl3736-simple.c

+222
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
/* Copyright 2018 Jason Williams (Wilba)
2+
* Copyright 2021 Doni Crosby
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
#include "is31fl3736-simple.h"
19+
#include <string.h>
20+
#include "i2c_master.h"
21+
#include "wait.h"
22+
23+
#define IS31FL3736_COMMANDREGISTER 0xFD
24+
#define IS31FL3736_COMMANDREGISTER_WRITELOCK 0xFE
25+
#define IS31FL3736_INTERRUPTMASKREGISTER 0xF0
26+
#define IS31FL3736_INTERRUPTSTATUSREGISTER 0xF1
27+
28+
#define IS31FL3736_PAGE_LEDCONTROL 0x00 // PG0
29+
#define IS31FL3736_PAGE_PWM 0x01 // PG1
30+
#define IS31FL3736_PAGE_AUTOBREATH 0x02 // PG2
31+
#define IS31FL3736_PAGE_FUNCTION 0x03 // PG3
32+
33+
#define IS31FL3736_REG_CONFIGURATION 0x00 // PG3
34+
#define IS31FL3736_REG_GLOBALCURRENT 0x01 // PG3
35+
#define IS31FL3736_REG_RESET 0x11 // PG3
36+
#define IS31FL3736_REG_SWPULLUP 0x0F // PG3
37+
#define IS31FL3736_REG_CSPULLUP 0x10 // PG3
38+
39+
#ifndef IS31FL3736_I2C_TIMEOUT
40+
# define IS31FL3736_I2C_TIMEOUT 100
41+
#endif
42+
43+
#ifndef IS31FL3736_I2C_PERSISTENCE
44+
# define IS31FL3736_I2C_PERSISTENCE 0
45+
#endif
46+
47+
#ifndef IS31FL3736_PWM_FREQUENCY
48+
# define IS31FL3736_PWM_FREQUENCY IS31FL3736_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3736B only
49+
#endif
50+
51+
#ifndef IS31FL3736_SWPULLUP
52+
# define IS31FL3736_SWPULLUP IS31FL3736_PUR_0R
53+
#endif
54+
55+
#ifndef IS31FL3736_CSPULLUP
56+
# define IS31FL3736_CSPULLUP IS31FL3736_PUR_0R
57+
#endif
58+
59+
#ifndef IS31FL3736_GLOBALCURRENT
60+
# define IS31FL3736_GLOBALCURRENT 0xFF
61+
#endif
62+
63+
// Transfer buffer for TWITransmitData()
64+
uint8_t g_twi_transfer_buffer[20];
65+
66+
// These buffers match the IS31FL3736 PWM registers.
67+
// The control buffers match the PG0 LED On/Off registers.
68+
// Storing them like this is optimal for I2C transfers to the registers.
69+
// We could optimize this and take out the unused registers from these
70+
// buffers and the transfers in is31fl3736_write_pwm_buffer() but it's
71+
// probably not worth the extra complexity.
72+
uint8_t g_pwm_buffer[IS31FL3736_DRIVER_COUNT][192];
73+
bool g_pwm_buffer_update_required[IS31FL3736_DRIVER_COUNT] = {false};
74+
75+
uint8_t g_led_control_registers[IS31FL3736_DRIVER_COUNT][24] = {0};
76+
bool g_led_control_registers_update_required[IS31FL3736_DRIVER_COUNT] = {false};
77+
78+
void is31fl3736_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
79+
g_twi_transfer_buffer[0] = reg;
80+
g_twi_transfer_buffer[1] = data;
81+
82+
#if IS31FL3736_I2C_PERSISTENCE > 0
83+
for (uint8_t i = 0; i < IS31FL3736_I2C_PERSISTENCE; i++) {
84+
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3736_I2C_TIMEOUT) == 0) break;
85+
}
86+
#else
87+
i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3736_I2C_TIMEOUT);
88+
#endif
89+
}
90+
91+
void is31fl3736_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
92+
// assumes PG1 is already selected
93+
94+
// transmit PWM registers in 12 transfers of 16 bytes
95+
// g_twi_transfer_buffer[] is 20 bytes
96+
97+
// iterate over the pwm_buffer contents at 16 byte intervals
98+
for (int i = 0; i < 192; i += 16) {
99+
g_twi_transfer_buffer[0] = i;
100+
// copy the data from i to i+15
101+
// device will auto-increment register for data after the first byte
102+
// thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer
103+
memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16);
104+
105+
#if IS31FL3736_I2C_PERSISTENCE > 0
106+
for (uint8_t i = 0; i < IS31FL3736_I2C_PERSISTENCE; i++) {
107+
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3736_I2C_TIMEOUT) == 0) break;
108+
}
109+
#else
110+
i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3736_I2C_TIMEOUT);
111+
#endif
112+
}
113+
}
114+
115+
void is31fl3736_init(uint8_t addr) {
116+
// In order to avoid the LEDs being driven with garbage data
117+
// in the LED driver's PWM registers, shutdown is enabled last.
118+
// Set up the mode and other settings, clear the PWM registers,
119+
// then disable software shutdown.
120+
121+
// Unlock the command register.
122+
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER_WRITELOCK, 0xC5);
123+
124+
// Select PG0
125+
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER, IS31FL3736_PAGE_LEDCONTROL);
126+
// Turn off all LEDs.
127+
for (int i = 0x00; i <= 0x17; i++) {
128+
is31fl3736_write_register(addr, i, 0x00);
129+
}
130+
131+
// Unlock the command register.
132+
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER_WRITELOCK, 0xC5);
133+
134+
// Select PG1
135+
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER, IS31FL3736_PAGE_PWM);
136+
// Set PWM on all LEDs to 0
137+
// No need to setup Breath registers to PWM as that is the default.
138+
for (int i = 0x00; i <= 0xBF; i++) {
139+
is31fl3736_write_register(addr, i, 0x00);
140+
}
141+
142+
// Unlock the command register.
143+
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER_WRITELOCK, 0xC5);
144+
145+
// Select PG3
146+
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER, IS31FL3736_PAGE_FUNCTION);
147+
// Set de-ghost pull-up resistors (SWx)
148+
is31fl3736_write_register(addr, IS31FL3736_REG_SWPULLUP, IS31FL3736_SWPULLUP);
149+
// Set de-ghost pull-down resistors (CSx)
150+
is31fl3736_write_register(addr, IS31FL3736_REG_CSPULLUP, IS31FL3736_CSPULLUP);
151+
// Set global current to maximum.
152+
is31fl3736_write_register(addr, IS31FL3736_REG_GLOBALCURRENT, IS31FL3736_GLOBALCURRENT);
153+
// Disable software shutdown.
154+
is31fl3736_write_register(addr, IS31FL3736_REG_CONFIGURATION, ((IS31FL3736_PWM_FREQUENCY & 0b111) << 3) | 0x01);
155+
156+
// Wait 10ms to ensure the device has woken up.
157+
wait_ms(10);
158+
}
159+
160+
void is31fl3736_set_value(int index, uint8_t value) {
161+
is31_led led;
162+
if (index >= 0 && index < LED_MATRIX_LED_COUNT) {
163+
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
164+
165+
if (g_pwm_buffer[led.driver][led.v] == value) {
166+
return;
167+
}
168+
g_pwm_buffer[led.driver][led.v] = value;
169+
g_pwm_buffer_update_required[led.driver] = true;
170+
}
171+
}
172+
173+
void is31fl3736_set_value_all(uint8_t value) {
174+
for (int i = 0; i < LED_MATRIX_LED_COUNT; i++) {
175+
is31fl3736_set_value(i, value);
176+
}
177+
}
178+
179+
void is31fl3736_set_led_control_register(uint8_t index, bool value) {
180+
is31_led led;
181+
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
182+
183+
// The PWM register for a matrix position (0x00 to 0xBF) is interleaved, so:
184+
// A1=0x00 A2=0x02 A3=0x04 A4=0x06 A5=0x08 A6=0x0A A7=0x0C A8=0x0E
185+
// B1=0x10 B2=0x12 B3=0x14
186+
// But also, the LED control registers (0x00 to 0x17) are also interleaved, so:
187+
// A1-A4=0x00 A5-A8=0x01
188+
189+
uint8_t control_register = led.v / 8;
190+
uint8_t bit_value = led.v % 8;
191+
192+
if (value) {
193+
g_led_control_registers[led.driver][control_register] |= (1 << bit_value);
194+
} else {
195+
g_led_control_registers[led.driver][control_register] &= ~(1 << bit_value);
196+
}
197+
198+
g_led_control_registers_update_required[led.driver] = true;
199+
}
200+
201+
void is31fl3736_update_pwm_buffers(uint8_t addr, uint8_t index) {
202+
if (g_pwm_buffer_update_required[index]) {
203+
// Firstly we need to unlock the command register and select PG1
204+
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER_WRITELOCK, 0xC5);
205+
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER, IS31FL3736_PAGE_PWM);
206+
207+
is31fl3736_write_pwm_buffer(addr, g_pwm_buffer[index]);
208+
g_pwm_buffer_update_required[index] = false;
209+
}
210+
}
211+
212+
void is31fl3736_update_led_control_registers(uint8_t addr, uint8_t index) {
213+
if (g_led_control_registers_update_required[index]) {
214+
// Firstly we need to unlock the command register and select PG0
215+
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER_WRITELOCK, 0xC5);
216+
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER, IS31FL3736_PAGE_LEDCONTROL);
217+
for (int i = 0; i < 24; i++) {
218+
is31fl3736_write_register(addr, i, g_led_control_registers[index][i]);
219+
}
220+
g_led_control_registers_update_required[index] = false;
221+
}
222+
}

0 commit comments

Comments
 (0)