aboutsummaryrefslogtreecommitdiff
path: root/drivers/led
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/led')
-rw-r--r--drivers/led/apa102.c151
-rw-r--r--drivers/led/apa102.h41
-rw-r--r--drivers/led/aw20216.c141
-rw-r--r--drivers/led/aw20216.h253
-rw-r--r--drivers/led/issi/is31fl3218.c96
-rw-r--r--drivers/led/issi/is31fl3218.h25
-rw-r--r--drivers/led/issi/is31fl3731-simple.c233
-rw-r--r--drivers/led/issi/is31fl3731-simple.h208
-rw-r--r--drivers/led/issi/is31fl3731.c237
-rw-r--r--drivers/led/issi/is31fl3731.h209
-rw-r--r--drivers/led/issi/is31fl3733.c237
-rw-r--r--drivers/led/issi/is31fl3733.h252
-rw-r--r--drivers/led/issi/is31fl3736.c269
-rw-r--r--drivers/led/issi/is31fl3736.h169
-rw-r--r--drivers/led/issi/is31fl3737.c222
-rw-r--r--drivers/led/issi/is31fl3737.h204
-rw-r--r--drivers/led/issi/is31fl3741.c255
-rw-r--r--drivers/led/issi/is31fl3741.h421
18 files changed, 3623 insertions, 0 deletions
diff --git a/drivers/led/apa102.c b/drivers/led/apa102.c
new file mode 100644
index 000000000..7396dc3c5
--- /dev/null
+++ b/drivers/led/apa102.c
@@ -0,0 +1,151 @@
1/* Copyright 2020 Aldehir Rojas
2 * Copyright 2017 Mikkel (Duckle29)
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 "apa102.h"
19#include "quantum.h"
20
21#ifndef APA102_NOPS
22# if defined(__AVR__)
23# define APA102_NOPS 0 // AVR at 16 MHz already spends 62.5 ns per clock, so no extra delay is needed
24# elif defined(PROTOCOL_CHIBIOS)
25
26# include "hal.h"
27# if defined(STM32F0XX) || defined(STM32F1XX) || defined(STM32F3XX) || defined(STM32F4XX) || defined(STM32L0XX)
28# define APA102_NOPS (100 / (1000000000L / (STM32_SYSCLK / 4))) // This calculates how many loops of 4 nops to run to delay 100 ns
29# else
30# error("APA102_NOPS configuration required")
31# define APA102_NOPS 0 // this just pleases the compile so the above error is easier to spot
32# endif
33# endif
34#endif
35
36#define io_wait \
37 do { \
38 for (int i = 0; i < APA102_NOPS; i++) { \
39 __asm__ volatile("nop\n\t" \
40 "nop\n\t" \
41 "nop\n\t" \
42 "nop\n\t"); \
43 } \
44 } while (0)
45
46#define APA102_SEND_BIT(byte, bit) \
47 do { \
48 writePin(RGB_DI_PIN, (byte >> bit) & 1); \
49 io_wait; \
50 writePinHigh(RGB_CI_PIN); \
51 io_wait; \
52 writePinLow(RGB_CI_PIN); \
53 io_wait; \
54 } while (0)
55
56uint8_t apa102_led_brightness = APA102_DEFAULT_BRIGHTNESS;
57
58void static apa102_start_frame(void);
59void static apa102_end_frame(uint16_t num_leds);
60
61void static apa102_send_frame(uint8_t red, uint8_t green, uint8_t blue, uint8_t brightness);
62void static apa102_send_byte(uint8_t byte);
63
64void apa102_setleds(LED_TYPE *start_led, uint16_t num_leds) {
65 LED_TYPE *end = start_led + num_leds;
66
67 apa102_start_frame();
68 for (LED_TYPE *led = start_led; led < end; led++) {
69 apa102_send_frame(led->r, led->g, led->b, apa102_led_brightness);
70 }
71 apa102_end_frame(num_leds);
72}
73
74// Overwrite the default rgblight_call_driver to use apa102 driver
75void rgblight_call_driver(LED_TYPE *start_led, uint8_t num_leds) { apa102_setleds(start_led, num_leds); }
76
77void static apa102_init(void) {
78 setPinOutput(RGB_DI_PIN);
79 setPinOutput(RGB_CI_PIN);
80
81 writePinLow(RGB_DI_PIN);
82 writePinLow(RGB_CI_PIN);
83}
84
85void apa102_set_brightness(uint8_t brightness) {
86 if (brightness > APA102_MAX_BRIGHTNESS) {
87 apa102_led_brightness = APA102_MAX_BRIGHTNESS;
88 } else if (brightness < 0) {
89 apa102_led_brightness = 0;
90 } else {
91 apa102_led_brightness = brightness;
92 }
93}
94
95void static apa102_send_frame(uint8_t red, uint8_t green, uint8_t blue, uint8_t brightness) {
96 apa102_send_byte(0b11100000 | brightness);
97 apa102_send_byte(blue);
98 apa102_send_byte(green);
99 apa102_send_byte(red);
100}
101
102void static apa102_start_frame(void) {
103 apa102_init();
104 for (uint16_t i = 0; i < 4; i++) {
105 apa102_send_byte(0);
106 }
107}
108
109void static apa102_end_frame(uint16_t num_leds) {
110 // This function has been taken from: https://github.com/pololu/apa102-arduino/blob/master/APA102.h
111 // and adapted. The code is MIT licensed. I think thats compatible?
112 //
113 // The data stream seen by the last LED in the chain will be delayed by
114 // (count - 1) clock edges, because each LED before it inverts the clock
115 // line and delays the data by one clock edge. Therefore, to make sure
116 // the last LED actually receives the data we wrote, the number of extra
117 // edges we send at the end of the frame must be at least (count - 1).
118 //
119 // Assuming we only want to send these edges in groups of size K, the
120 // C/C++ expression for the minimum number of groups to send is:
121 //
122 // ((count - 1) + (K - 1)) / K
123 //
124 // The C/C++ expression above is just (count - 1) divided by K,
125 // rounded up to the nearest whole number if there is a remainder.
126 //
127 // We set K to 16 and use the formula above as the number of frame-end
128 // bytes to transfer. Each byte has 16 clock edges.
129 //
130 // We are ignoring the specification for the end frame in the APA102
131 // datasheet, which says to send 0xFF four times, because it does not work
132 // when you have 66 LEDs or more, and also it results in unwanted white
133 // pixels if you try to update fewer LEDs than are on your LED strip.
134 uint16_t iterations = (num_leds + 14) / 16;
135 for (uint16_t i = 0; i < iterations; i++) {
136 apa102_send_byte(0);
137 }
138
139 apa102_init();
140}
141
142void static apa102_send_byte(uint8_t byte) {
143 APA102_SEND_BIT(byte, 7);
144 APA102_SEND_BIT(byte, 6);
145 APA102_SEND_BIT(byte, 5);
146 APA102_SEND_BIT(byte, 4);
147 APA102_SEND_BIT(byte, 3);
148 APA102_SEND_BIT(byte, 2);
149 APA102_SEND_BIT(byte, 1);
150 APA102_SEND_BIT(byte, 0);
151}
diff --git a/drivers/led/apa102.h b/drivers/led/apa102.h
new file mode 100644
index 000000000..58cf020c1
--- /dev/null
+++ b/drivers/led/apa102.h
@@ -0,0 +1,41 @@
1/* Copyright 2020 Aldehir Rojas
2 * Copyright 2017 Mikkel (Duckle29)
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#pragma once
19
20#include "color.h"
21
22#ifndef APA102_DEFAULT_BRIGHTNESS
23# define APA102_DEFAULT_BRIGHTNESS 31
24#endif
25
26#define APA102_MAX_BRIGHTNESS 31
27
28extern uint8_t apa102_led_brightness;
29
30/* User Interface
31 *
32 * Input:
33 * start_led: An array of GRB data describing the LED colors
34 * num_leds: The number of LEDs to write
35 *
36 * The functions will perform the following actions:
37 * - Set the data-out pin as output
38 * - Send out the LED data
39 */
40void apa102_setleds(LED_TYPE *start_led, uint16_t num_leds);
41void apa102_set_brightness(uint8_t brightness);
diff --git a/drivers/led/aw20216.c b/drivers/led/aw20216.c
new file mode 100644
index 000000000..c608c0ab4
--- /dev/null
+++ b/drivers/led/aw20216.c
@@ -0,0 +1,141 @@
1/* Copyright 2021 Jasper Chan
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "aw20216.h"
18#include "spi_master.h"
19
20/* The AW20216 appears to be somewhat similar to the IS31FL743, although quite
21 * a few things are different, such as the command byte format and page ordering.
22 * The LED addresses start from 0x00 instead of 0x01.
23 */
24#define AWINIC_ID 0b1010 << 4
25
26#define AW_PAGE_FUNCTION 0x00 << 1 // PG0, Function registers
27#define AW_PAGE_PWM 0x01 << 1 // PG1, LED PWM control
28#define AW_PAGE_SCALING 0x02 << 1 // PG2, LED current scaling control
29#define AW_PAGE_PATCHOICE 0x03 << 1 // PG3, Pattern choice?
30#define AW_PAGE_PWMSCALING 0x04 << 1 // PG4, LED PWM + Scaling control?
31
32#define AW_WRITE 0
33#define AW_READ 1
34
35#define AW_REG_CONFIGURATION 0x00 // PG0
36#define AW_REG_GLOBALCURRENT 0x01 // PG0
37
38// Default value of AW_REG_CONFIGURATION
39// D7:D4 = 1011, SWSEL (SW1~SW12 active)
40// D3 = 0?, reserved (apparently this should be 1 but it doesn't seem to matter)
41// D2:D1 = 00, OSDE (open/short detection enable)
42// D0 = 0, CHIPEN (write 1 to enable LEDs when hardware enable pulled high)
43#define AW_CONFIG_DEFAULT 0b10110000
44#define AW_CHIPEN 1
45
46#define AW_PWM_REGISTER_COUNT 216
47
48#ifndef AW_SCALING_MAX
49# define AW_SCALING_MAX 150
50#endif
51
52#ifndef AW_GLOBAL_CURRENT_MAX
53# define AW_GLOBAL_CURRENT_MAX 150
54#endif
55
56#ifndef AW_SPI_DIVISOR
57# define AW_SPI_DIVISOR 4
58#endif
59
60uint8_t g_pwm_buffer[DRIVER_COUNT][AW_PWM_REGISTER_COUNT];
61bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};
62
63bool AW20216_write(pin_t cs_pin, uint8_t page, uint8_t reg, uint8_t* data, uint8_t len) {
64 static uint8_t s_spi_transfer_buffer[2] = {0};
65
66 if (!spi_start(cs_pin, false, 0, AW_SPI_DIVISOR)) {
67 spi_stop();
68 return false;
69 }
70
71 s_spi_transfer_buffer[0] = (AWINIC_ID | page | AW_WRITE);
72 s_spi_transfer_buffer[1] = reg;
73
74 if (spi_transmit(s_spi_transfer_buffer, 2) != SPI_STATUS_SUCCESS) {
75 spi_stop();
76 return false;
77 }
78
79 if (spi_transmit(data, len) != SPI_STATUS_SUCCESS) {
80 spi_stop();
81 return false;
82 }
83
84 spi_stop();
85 return true;
86}
87
88static inline bool AW20216_write_register(pin_t cs_pin, uint8_t page, uint8_t reg, uint8_t value) {
89 // Little wrapper so callers need not care about sending a buffer
90 return AW20216_write(cs_pin, page, reg, &value, 1);
91}
92
93static void AW20216_init_scaling(pin_t cs_pin) {
94 // Set constant current to the max, control brightness with PWM
95 for (uint8_t i = 0; i < AW_PWM_REGISTER_COUNT; i++) {
96 AW20216_write_register(cs_pin, AW_PAGE_SCALING, i, AW_SCALING_MAX);
97 }
98}
99
100static inline void AW20216_init_current_limit(pin_t cs_pin) {
101 // Push config
102 AW20216_write_register(cs_pin, AW_PAGE_FUNCTION, AW_REG_GLOBALCURRENT, AW_GLOBAL_CURRENT_MAX);
103}
104
105static inline void AW20216_soft_enable(pin_t cs_pin) {
106 // Push config
107 AW20216_write_register(cs_pin, AW_PAGE_FUNCTION, AW_REG_CONFIGURATION, AW_CONFIG_DEFAULT | AW_CHIPEN);
108}
109
110void AW20216_init(pin_t cs_pin, pin_t en_pin) {
111 setPinOutput(en_pin);
112 writePinHigh(en_pin);
113
114 // Drivers should start with all scaling and PWM registers as off
115 AW20216_init_current_limit(cs_pin);
116 AW20216_init_scaling(cs_pin);
117
118 AW20216_soft_enable(cs_pin);
119}
120
121void AW20216_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
122 aw_led led = g_aw_leds[index];
123
124 g_pwm_buffer[led.driver][led.r] = red;
125 g_pwm_buffer[led.driver][led.g] = green;
126 g_pwm_buffer[led.driver][led.b] = blue;
127 g_pwm_buffer_update_required[led.driver] = true;
128}
129
130void AW20216_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
131 for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
132 AW20216_set_color(i, red, green, blue);
133 }
134}
135
136void AW20216_update_pwm_buffers(pin_t cs_pin, uint8_t index) {
137 if (g_pwm_buffer_update_required[index]) {
138 AW20216_write(cs_pin, AW_PAGE_PWM, 0, g_pwm_buffer[index], AW_PWM_REGISTER_COUNT);
139 }
140 g_pwm_buffer_update_required[index] = false;
141}
diff --git a/drivers/led/aw20216.h b/drivers/led/aw20216.h
new file mode 100644
index 000000000..97ac6dc5b
--- /dev/null
+++ b/drivers/led/aw20216.h
@@ -0,0 +1,253 @@
1/* Copyright 2021 Jasper Chan (Gigahawk)
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include <stdint.h>
20#include <stdbool.h>
21#include "progmem.h"
22#include "gpio.h"
23
24typedef struct aw_led {
25 uint8_t driver : 2;
26 uint8_t r;
27 uint8_t g;
28 uint8_t b;
29} aw_led;
30
31extern const aw_led __flash g_aw_leds[DRIVER_LED_TOTAL];
32
33void AW20216_init(pin_t cs_pin, pin_t en_pin);
34void AW20216_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
35void AW20216_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
36void AW20216_update_pwm_buffers(pin_t cs_pin, uint8_t index);
37
38#define CS1_SW1 0x00
39#define CS2_SW1 0x01
40#define CS3_SW1 0x02
41#define CS4_SW1 0x03
42#define CS5_SW1 0x04
43#define CS6_SW1 0x05
44#define CS7_SW1 0x06
45#define CS8_SW1 0x07
46#define CS9_SW1 0x08
47#define CS10_SW1 0x09
48#define CS11_SW1 0x0A
49#define CS12_SW1 0x0B
50#define CS13_SW1 0x0C
51#define CS14_SW1 0x0D
52#define CS15_SW1 0x0E
53#define CS16_SW1 0x0F
54#define CS17_SW1 0x10
55#define CS18_SW1 0x11
56#define CS1_SW2 0x12
57#define CS2_SW2 0x13
58#define CS3_SW2 0x14
59#define CS4_SW2 0x15
60#define CS5_SW2 0x16
61#define CS6_SW2 0x17
62#define CS7_SW2 0x18
63#define CS8_SW2 0x19
64#define CS9_SW2 0x1A
65#define CS10_SW2 0x1B
66#define CS11_SW2 0x1C
67#define CS12_SW2 0x1D
68#define CS13_SW2 0x1E
69#define CS14_SW2 0x1F
70#define CS15_SW2 0x20
71#define CS16_SW2 0x21
72#define CS17_SW2 0x22
73#define CS18_SW2 0x23
74#define CS1_SW3 0x24
75#define CS2_SW3 0x25
76#define CS3_SW3 0x26
77#define CS4_SW3 0x27
78#define CS5_SW3 0x28
79#define CS6_SW3 0x29
80#define CS7_SW3 0x2A
81#define CS8_SW3 0x2B
82#define CS9_SW3 0x2C
83#define CS10_SW3 0x2D
84#define CS11_SW3 0x2E
85#define CS12_SW3 0x2F
86#define CS13_SW3 0x30
87#define CS14_SW3 0x31
88#define CS15_SW3 0x32
89#define CS16_SW3 0x33
90#define CS17_SW3 0x34
91#define CS18_SW3 0x35
92#define CS1_SW4 0x36
93#define CS2_SW4 0x37
94#define CS3_SW4 0x38
95#define CS4_SW4 0x39
96#define CS5_SW4 0x3A
97#define CS6_SW4 0x3B
98#define CS7_SW4 0x3C
99#define CS8_SW4 0x3D
100#define CS9_SW4 0x3E
101#define CS10_SW4 0x3F
102#define CS11_SW4 0x40
103#define CS12_SW4 0x41
104#define CS13_SW4 0x42
105#define CS14_SW4 0x43
106#define CS15_SW4 0x44
107#define CS16_SW4 0x45
108#define CS17_SW4 0x46
109#define CS18_SW4 0x47
110#define CS1_SW5 0x48
111#define CS2_SW5 0x49
112#define CS3_SW5 0x4A
113#define CS4_SW5 0x4B
114#define CS5_SW5 0x4C
115#define CS6_SW5 0x4D
116#define CS7_SW5 0x4E
117#define CS8_SW5 0x4F
118#define CS9_SW5 0x50
119#define CS10_SW5 0x51
120#define CS11_SW5 0x52
121#define CS12_SW5 0x53
122#define CS13_SW5 0x54
123#define CS14_SW5 0x55
124#define CS15_SW5 0x56
125#define CS16_SW5 0x57
126#define CS17_SW5 0x58
127#define CS18_SW5 0x59
128#define CS1_SW6 0x5A
129#define CS2_SW6 0x5B
130#define CS3_SW6 0x5C
131#define CS4_SW6 0x5D
132#define CS5_SW6 0x5E
133#define CS6_SW6 0x5F
134#define CS7_SW6 0x60
135#define CS8_SW6 0x61
136#define CS9_SW6 0x62
137#define CS10_SW6 0x63
138#define CS11_SW6 0x64
139#define CS12_SW6 0x65
140#define CS13_SW6 0x66
141#define CS14_SW6 0x67
142#define CS15_SW6 0x68
143#define CS16_SW6 0x69
144#define CS17_SW6 0x6A
145#define CS18_SW6 0x6B
146#define CS1_SW7 0x6C
147#define CS2_SW7 0x6D
148#define CS3_SW7 0x6E
149#define CS4_SW7 0x6F
150#define CS5_SW7 0x70
151#define CS6_SW7 0x71
152#define CS7_SW7 0x72
153#define CS8_SW7 0x73
154#define CS9_SW7 0x74
155#define CS10_SW7 0x75
156#define CS11_SW7 0x76
157#define CS12_SW7 0x77
158#define CS13_SW7 0x78
159#define CS14_SW7 0x79
160#define CS15_SW7 0x7A
161#define CS16_SW7 0x7B
162#define CS17_SW7 0x7C
163#define CS18_SW7 0x7D
164#define CS1_SW8 0x7E
165#define CS2_SW8 0x7F
166#define CS3_SW8 0x80
167#define CS4_SW8 0x81
168#define CS5_SW8 0x82
169#define CS6_SW8 0x83
170#define CS7_SW8 0x84
171#define CS8_SW8 0x85
172#define CS9_SW8 0x86
173#define CS10_SW8 0x87
174#define CS11_SW8 0x88
175#define CS12_SW8 0x89
176#define CS13_SW8 0x8A
177#define CS14_SW8 0x8B
178#define CS15_SW8 0x8C
179#define CS16_SW8 0x8D
180#define CS17_SW8 0x8E
181#define CS18_SW8 0x8F
182#define CS1_SW9 0x90
183#define CS2_SW9 0x91
184#define CS3_SW9 0x92
185#define CS4_SW9 0x93
186#define CS5_SW9 0x94
187#define CS6_SW9 0x95
188#define CS7_SW9 0x96
189#define CS8_SW9 0x97
190#define CS9_SW9 0x98
191#define CS10_SW9 0x99
192#define CS11_SW9 0x9A
193#define CS12_SW9 0x9B
194#define CS13_SW9 0x9C
195#define CS14_SW9 0x9D
196#define CS15_SW9 0x9E
197#define CS16_SW9 0x9F
198#define CS17_SW9 0xA0
199#define CS18_SW9 0xA1
200#define CS1_SW10 0xA2
201#define CS2_SW10 0xA3
202#define CS3_SW10 0xA4
203#define CS4_SW10 0xA5
204#define CS5_SW10 0xA6
205#define CS6_SW10 0xA7
206#define CS7_SW10 0xA8
207#define CS8_SW10 0xA9
208#define CS9_SW10 0xAA
209#define CS10_SW10 0xAB
210#define CS11_SW10 0xAC
211#define CS12_SW10 0xAD
212#define CS13_SW10 0xAE
213#define CS14_SW10 0xAF
214#define CS15_SW10 0xB0
215#define CS16_SW10 0xB1
216#define CS17_SW10 0xB2
217#define CS18_SW10 0xB3
218#define CS1_SW11 0xB4
219#define CS2_SW11 0xB5
220#define CS3_SW11 0xB6
221#define CS4_SW11 0xB7
222#define CS5_SW11 0xB8
223#define CS6_SW11 0xB9
224#define CS7_SW11 0xBA
225#define CS8_SW11 0xBB
226#define CS9_SW11 0xBC
227#define CS10_SW11 0xBD
228#define CS11_SW11 0xBE
229#define CS12_SW11 0xBF
230#define CS13_SW11 0xC0
231#define CS14_SW11 0xC1
232#define CS15_SW11 0xC2
233#define CS16_SW11 0xC3
234#define CS17_SW11 0xC4
235#define CS18_SW11 0xC5
236#define CS1_SW12 0xC6
237#define CS2_SW12 0xC7
238#define CS3_SW12 0xC8
239#define CS4_SW12 0xC9
240#define CS5_SW12 0xCA
241#define CS6_SW12 0xCB
242#define CS7_SW12 0xCC
243#define CS8_SW12 0xCD
244#define CS9_SW12 0xCE
245#define CS10_SW12 0xCF
246#define CS11_SW12 0xD0
247#define CS12_SW12 0xD1
248#define CS13_SW12 0xD2
249#define CS14_SW12 0xD3
250#define CS15_SW12 0xD4
251#define CS16_SW12 0xD5
252#define CS17_SW12 0xD6
253#define CS18_SW12 0xD7
diff --git a/drivers/led/issi/is31fl3218.c b/drivers/led/issi/is31fl3218.c
new file mode 100644
index 000000000..d43863ac4
--- /dev/null
+++ b/drivers/led/issi/is31fl3218.c
@@ -0,0 +1,96 @@
1/* Copyright 2018 Jason Williams (Wilba)
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16#include "is31fl3218.h"
17#include "i2c_master.h"
18
19// This is the full 8-bit address
20#define ISSI_ADDRESS 0b10101000
21
22// These are the register addresses
23#define ISSI_REG_SHUTDOWN 0x00
24#define ISSI_REG_PWM 0x01
25#define ISSI_REG_CONTROL 0x13
26#define ISSI_REG_UPDATE 0x16
27#define ISSI_REG_RESET 0x17
28
29// Default timeout if no I2C response
30#define ISSI_TIMEOUT 100
31
32// Reusable buffer for transfers
33uint8_t g_twi_transfer_buffer[20];
34
35// IS31FL3218 has 18 PWM outputs and a fixed I2C address, so no chaining.
36// If used as RGB LED driver, LEDs are assigned RGB,RGB,RGB,RGB,RGB,RGB
37uint8_t g_pwm_buffer[18];
38bool g_pwm_buffer_update_required = false;
39
40void IS31FL3218_write_register(uint8_t reg, uint8_t data) {
41 g_twi_transfer_buffer[0] = reg;
42 g_twi_transfer_buffer[1] = data;
43 i2c_transmit(ISSI_ADDRESS, g_twi_transfer_buffer, 2, ISSI_TIMEOUT);
44}
45
46void IS31FL3218_write_pwm_buffer(uint8_t *pwm_buffer) {
47 g_twi_transfer_buffer[0] = ISSI_REG_PWM;
48 for (int i = 0; i < 18; i++) {
49 g_twi_transfer_buffer[1 + i] = pwm_buffer[i];
50 }
51
52 i2c_transmit(ISSI_ADDRESS, g_twi_transfer_buffer, 19, ISSI_TIMEOUT);
53}
54
55void IS31FL3218_init(void) {
56 // In case we ever want to reinitialize (?)
57 IS31FL3218_write_register(ISSI_REG_RESET, 0x00);
58
59 // Turn off software shutdown
60 IS31FL3218_write_register(ISSI_REG_SHUTDOWN, 0x01);
61
62 // Set all PWM values to zero
63 for (uint8_t i = 0; i < 18; i++) {
64 IS31FL3218_write_register(ISSI_REG_PWM + i, 0x00);
65 }
66
67 // Enable all channels
68 for (uint8_t i = 0; i < 3; i++) {
69 IS31FL3218_write_register(ISSI_REG_CONTROL + i, 0b00111111);
70 }
71
72 // Load PWM registers and LED Control register data
73 IS31FL3218_write_register(ISSI_REG_UPDATE, 0x01);
74}
75
76void IS31FL3218_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
77 g_pwm_buffer[index * 3 + 0] = red;
78 g_pwm_buffer[index * 3 + 1] = green;
79 g_pwm_buffer[index * 3 + 2] = blue;
80 g_pwm_buffer_update_required = true;
81}
82
83void IS31FL3218_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
84 for (int i = 0; i < 6; i++) {
85 IS31FL3218_set_color(i, red, green, blue);
86 }
87}
88
89void IS31FL3218_update_pwm_buffers(void) {
90 if (g_pwm_buffer_update_required) {
91 IS31FL3218_write_pwm_buffer(g_pwm_buffer);
92 // Load PWM registers and LED Control register data
93 IS31FL3218_write_register(ISSI_REG_UPDATE, 0x01);
94 }
95 g_pwm_buffer_update_required = false;
96}
diff --git a/drivers/led/issi/is31fl3218.h b/drivers/led/issi/is31fl3218.h
new file mode 100644
index 000000000..fa760da19
--- /dev/null
+++ b/drivers/led/issi/is31fl3218.h
@@ -0,0 +1,25 @@
1/* Copyright 2018 Jason Williams (Wilba)
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include <stdint.h>
20#include <stdbool.h>
21
22void IS31FL3218_init(void);
23void IS31FL3218_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
24void IS31FL3218_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
25void IS31FL3218_update_pwm_buffers(void);
diff --git a/drivers/led/issi/is31fl3731-simple.c b/drivers/led/issi/is31fl3731-simple.c
new file mode 100644
index 000000000..d295772f5
--- /dev/null
+++ b/drivers/led/issi/is31fl3731-simple.c
@@ -0,0 +1,233 @@
1/* Copyright 2017 Jason Williams
2 * Copyright 2018 Jack Humbert
3 * Copyright 2019 Clueboard
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include "is31fl3731-simple.h"
20#include "i2c_master.h"
21#include "wait.h"
22
23// This is a 7-bit address, that gets left-shifted and bit 0
24// set to 0 for write, 1 for read (as per I2C protocol)
25// The address will vary depending on your wiring:
26// 0b1110100 AD <-> GND
27// 0b1110111 AD <-> VCC
28// 0b1110101 AD <-> SCL
29// 0b1110110 AD <-> SDA
30#define ISSI_ADDR_DEFAULT 0x74
31
32#define ISSI_REG_CONFIG 0x00
33#define ISSI_REG_CONFIG_PICTUREMODE 0x00
34#define ISSI_REG_CONFIG_AUTOPLAYMODE 0x08
35#define ISSI_REG_CONFIG_AUDIOPLAYMODE 0x18
36
37#define ISSI_CONF_PICTUREMODE 0x00
38#define ISSI_CONF_AUTOFRAMEMODE 0x04
39#define ISSI_CONF_AUDIOMODE 0x08
40
41#define ISSI_REG_PICTUREFRAME 0x01
42
43#define ISSI_REG_SHUTDOWN 0x0A
44#define ISSI_REG_AUDIOSYNC 0x06
45
46#define ISSI_COMMANDREGISTER 0xFD
47#define ISSI_BANK_FUNCTIONREG 0x0B // helpfully called 'page nine'
48
49#ifndef ISSI_TIMEOUT
50# define ISSI_TIMEOUT 100
51#endif
52
53#ifndef ISSI_PERSISTENCE
54# define ISSI_PERSISTENCE 0
55#endif
56
57// Transfer buffer for TWITransmitData()
58uint8_t g_twi_transfer_buffer[20];
59
60// These buffers match the IS31FL3731 PWM registers 0x24-0xB3.
61// Storing them like this is optimal for I2C transfers to the registers.
62// We could optimize this and take out the unused registers from these
63// buffers and the transfers in IS31FL3731_write_pwm_buffer() but it's
64// probably not worth the extra complexity.
65uint8_t g_pwm_buffer[LED_DRIVER_COUNT][144];
66bool g_pwm_buffer_update_required[LED_DRIVER_COUNT] = {false};
67
68/* There's probably a better way to init this... */
69#if LED_DRIVER_COUNT == 1
70uint8_t g_led_control_registers[LED_DRIVER_COUNT][18] = {{0}};
71#elif LED_DRIVER_COUNT == 2
72uint8_t g_led_control_registers[LED_DRIVER_COUNT][18] = {{0}, {0}};
73#elif LED_DRIVER_COUNT == 3
74uint8_t g_led_control_registers[LED_DRIVER_COUNT][18] = {{0}, {0}, {0}};
75#elif LED_DRIVER_COUNT == 4
76uint8_t g_led_control_registers[LED_DRIVER_COUNT][18] = {{0}, {0}, {0}, {0}};
77#endif
78bool g_led_control_registers_update_required[LED_DRIVER_COUNT] = {false};
79
80// This is the bit pattern in the LED control registers
81// (for matrix A, add one to register for matrix B)
82//
83// reg - b7 b6 b5 b4 b3 b2 b1 b0
84// 0x00 - R08,R07,R06,R05,R04,R03,R02,R01
85// 0x02 - G08,G07,G06,G05,G04,G03,G02,R00
86// 0x04 - B08,B07,B06,B05,B04,B03,G01,G00
87// 0x06 - - , - , - , - , - ,B02,B01,B00
88// 0x08 - - , - , - , - , - , - , - , -
89// 0x0A - B17,B16,B15, - , - , - , - , -
90// 0x0C - G17,G16,B14,B13,B12,B11,B10,B09
91// 0x0E - R17,G15,G14,G13,G12,G11,G10,G09
92// 0x10 - R16,R15,R14,R13,R12,R11,R10,R09
93
94void IS31FL3731_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
95 g_twi_transfer_buffer[0] = reg;
96 g_twi_transfer_buffer[1] = data;
97
98#if ISSI_PERSISTENCE > 0
99 for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
100 if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) == 0) {
101 break;
102 }
103 }
104#else
105 i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT);
106#endif
107}
108
109void IS31FL3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
110 // assumes bank is already selected
111
112 // transmit PWM registers in 9 transfers of 16 bytes
113 // g_twi_transfer_buffer[] is 20 bytes
114
115 // iterate over the pwm_buffer contents at 16 byte intervals
116 for (int i = 0; i < 144; i += 16) {
117 // set the first register, e.g. 0x24, 0x34, 0x44, etc.
118 g_twi_transfer_buffer[0] = 0x24 + i;
119 // copy the data from i to i+15
120 // device will auto-increment register for data after the first byte
121 // thus this sets registers 0x24-0x33, 0x34-0x43, etc. in one transfer
122 for (int j = 0; j < 16; j++) {
123 g_twi_transfer_buffer[1 + j] = pwm_buffer[i + j];
124 }
125
126#if ISSI_PERSISTENCE > 0
127 for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
128 if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT) == 0) break;
129 }
130#else
131 i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT);
132#endif
133 }
134}
135
136void IS31FL3731_init(uint8_t addr) {
137 // In order to avoid the LEDs being driven with garbage data
138 // in the LED driver's PWM registers, first enable software shutdown,
139 // then set up the mode and other settings, clear the PWM registers,
140 // then disable software shutdown.
141
142 // select "function register" bank
143 IS31FL3731_write_register(addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG);
144
145 // enable software shutdown
146 IS31FL3731_write_register(addr, ISSI_REG_SHUTDOWN, 0x00);
147
148 // this delay was copied from other drivers, might not be needed
149 wait_ms(10);
150
151 // picture mode
152 IS31FL3731_write_register(addr, ISSI_REG_CONFIG, ISSI_REG_CONFIG_PICTUREMODE);
153 // display frame 0
154 IS31FL3731_write_register(addr, ISSI_REG_PICTUREFRAME, 0x00);
155 // audio sync off
156 IS31FL3731_write_register(addr, ISSI_REG_AUDIOSYNC, 0x00);
157
158 // select bank 0
159 IS31FL3731_write_register(addr, ISSI_COMMANDREGISTER, 0);
160
161 // turn off all LEDs in the LED control register
162 for (int i = 0x00; i <= 0x11; i++) {
163 IS31FL3731_write_register(addr, i, 0x00);
164 }
165
166 // turn off all LEDs in the blink control register (not really needed)
167 for (int i = 0x12; i <= 0x23; i++) {
168 IS31FL3731_write_register(addr, i, 0x00);
169 }
170
171 // set PWM on all LEDs to 0
172 for (int i = 0x24; i <= 0xB3; i++) {
173 IS31FL3731_write_register(addr, i, 0x00);
174 }
175
176 // select "function register" bank
177 IS31FL3731_write_register(addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG);
178
179 // disable software shutdown
180 IS31FL3731_write_register(addr, ISSI_REG_SHUTDOWN, 0x01);
181
182 // select bank 0 and leave it selected.
183 // most usage after initialization is just writing PWM buffers in bank 0
184 // as there's not much point in double-buffering
185 IS31FL3731_write_register(addr, ISSI_COMMANDREGISTER, 0);
186}
187
188void IS31FL3731_set_value(int index, uint8_t value) {
189 if (index >= 0 && index < DRIVER_LED_TOTAL) {
190 is31_led led = g_is31_leds[index];
191
192 // Subtract 0x24 to get the second index of g_pwm_buffer
193 g_pwm_buffer[led.driver][led.v - 0x24] = value;
194 g_pwm_buffer_update_required[led.driver] = true;
195 }
196}
197
198void IS31FL3731_set_value_all(uint8_t value) {
199 for (int i = 0; i < DRIVER_LED_TOTAL; i++) {
200 IS31FL3731_set_value(i, value);
201 }
202}
203
204void IS31FL3731_set_led_control_register(uint8_t index, bool value) {
205 is31_led led = g_is31_leds[index];
206
207 uint8_t control_register = (led.v - 0x24) / 8;
208 uint8_t bit_value = (led.v - 0x24) % 8;
209
210 if (value) {
211 g_led_control_registers[led.driver][control_register] |= (1 << bit_value);
212 } else {
213 g_led_control_registers[led.driver][control_register] &= ~(1 << bit_value);
214 }
215
216 g_led_control_registers_update_required[led.driver] = true;
217}
218
219void IS31FL3731_update_pwm_buffers(uint8_t addr, uint8_t index) {
220 if (g_pwm_buffer_update_required[index]) {
221 IS31FL3731_write_pwm_buffer(addr, g_pwm_buffer[index]);
222 g_pwm_buffer_update_required[index] = false;
223 }
224}
225
226void IS31FL3731_update_led_control_registers(uint8_t addr, uint8_t index) {
227 if (g_led_control_registers_update_required[index]) {
228 for (int i = 0; i < 18; i++) {
229 IS31FL3731_write_register(addr, i, g_led_control_registers[index][i]);
230 }
231 g_led_control_registers_update_required[index] = false;
232 }
233}
diff --git a/drivers/led/issi/is31fl3731-simple.h b/drivers/led/issi/is31fl3731-simple.h
new file mode 100644
index 000000000..ecde31eed
--- /dev/null
+++ b/drivers/led/issi/is31fl3731-simple.h
@@ -0,0 +1,208 @@
1/* Copyright 2017 Jason Williams
2 * Copyright 2018 Jack Humbert
3 * Copyright 2019 Clueboard
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#pragma once
20
21#include <stdint.h>
22#include <stdbool.h>
23#include "progmem.h"
24
25typedef struct is31_led {
26 uint8_t driver : 2;
27 uint8_t v;
28} __attribute__((packed)) is31_led;
29
30extern const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL];
31
32void IS31FL3731_init(uint8_t addr);
33void IS31FL3731_write_register(uint8_t addr, uint8_t reg, uint8_t data);
34void IS31FL3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
35
36void IS31FL3731_set_value(int index, uint8_t value);
37void IS31FL3731_set_value_all(uint8_t value);
38
39void IS31FL3731_set_led_control_register(uint8_t index, bool value);
40
41// This should not be called from an interrupt
42// (eg. from a timer interrupt).
43// Call this while idle (in between matrix scans).
44// If the buffer is dirty, it will update the driver with the buffer.
45void IS31FL3731_update_pwm_buffers(uint8_t addr, uint8_t index);
46void IS31FL3731_update_led_control_registers(uint8_t addr, uint8_t index);
47
48#define C1_1 0x24
49#define C1_2 0x25
50#define C1_3 0x26
51#define C1_4 0x27
52#define C1_5 0x28
53#define C1_6 0x29
54#define C1_7 0x2A
55#define C1_8 0x2B
56
57#define C1_9 0x2C
58#define C1_10 0x2D
59#define C1_11 0x2E
60#define C1_12 0x2F
61#define C1_13 0x30
62#define C1_14 0x31
63#define C1_15 0x32
64#define C1_16 0x33
65
66#define C2_1 0x34
67#define C2_2 0x35
68#define C2_3 0x36
69#define C2_4 0x37
70#define C2_5 0x38
71#define C2_6 0x39
72#define C2_7 0x3A
73#define C2_8 0x3B
74
75#define C2_9 0x3C
76#define C2_10 0x3D
77#define C2_11 0x3E
78#define C2_12 0x3F
79#define C2_13 0x40
80#define C2_14 0x41
81#define C2_15 0x42
82#define C2_16 0x43
83
84#define C3_1 0x44
85#define C3_2 0x45
86#define C3_3 0x46
87#define C3_4 0x47
88#define C3_5 0x48
89#define C3_6 0x49
90#define C3_7 0x4A
91#define C3_8 0x4B
92
93#define C3_9 0x4C
94#define C3_10 0x4D
95#define C3_11 0x4E
96#define C3_12 0x4F
97#define C3_13 0x50
98#define C3_14 0x51
99#define C3_15 0x52
100#define C3_16 0x53
101
102#define C4_1 0x54
103#define C4_2 0x55
104#define C4_3 0x56
105#define C4_4 0x57
106#define C4_5 0x58
107#define C4_6 0x59
108#define C4_7 0x5A
109#define C4_8 0x5B
110
111#define C4_9 0x5C
112#define C4_10 0x5D
113#define C4_11 0x5E
114#define C4_12 0x5F
115#define C4_13 0x60
116#define C4_14 0x61
117#define C4_15 0x62
118#define C4_16 0x63
119
120#define C5_1 0x64
121#define C5_2 0x65
122#define C5_3 0x66
123#define C5_4 0x67
124#define C5_5 0x68
125#define C5_6 0x69
126#define C5_7 0x6A
127#define C5_8 0x6B
128
129#define C5_9 0x6C
130#define C5_10 0x6D
131#define C5_11 0x6E
132#define C5_12 0x6F
133#define C5_13 0x70
134#define C5_14 0x71
135#define C5_15 0x72
136#define C5_16 0x73
137
138#define C6_1 0x74
139#define C6_2 0x75
140#define C6_3 0x76
141#define C6_4 0x77
142#define C6_5 0x78
143#define C6_6 0x79
144#define C6_7 0x7A
145#define C6_8 0x7B
146
147#define C6_9 0x7C
148#define C6_10 0x7D
149#define C6_11 0x7E
150#define C6_12 0x7F
151#define C6_13 0x80
152#define C6_14 0x81
153#define C6_15 0x82
154#define C6_16 0x83
155
156#define C7_1 0x84
157#define C7_2 0x85
158#define C7_3 0x86
159#define C7_4 0x87
160#define C7_5 0x88
161#define C7_6 0x89
162#define C7_7 0x8A
163#define C7_8 0x8B
164
165#define C7_9 0x8C
166#define C7_10 0x8D
167#define C7_11 0x8E
168#define C7_12 0x8F
169#define C7_13 0x90
170#define C7_14 0x91
171#define C7_15 0x92
172#define C7_16 0x93
173
174#define C8_1 0x94
175#define C8_2 0x95
176#define C8_3 0x96
177#define C8_4 0x97
178#define C8_5 0x98
179#define C8_6 0x99
180#define C8_7 0x9A
181#define C8_8 0x9B
182
183#define C8_9 0x9C
184#define C8_10 0x9D
185#define C8_11 0x9E
186#define C8_12 0x9F
187#define C8_13 0xA0
188#define C8_14 0xA1
189#define C8_15 0xA2
190#define C8_16 0xA3
191
192#define C9_1 0xA4
193#define C9_2 0xA5
194#define C9_3 0xA6
195#define C9_4 0xA7
196#define C9_5 0xA8
197#define C9_6 0xA9
198#define C9_7 0xAA
199#define C9_8 0xAB
200
201#define C9_9 0xAC
202#define C9_10 0xAD
203#define C9_11 0xAE
204#define C9_12 0xAF
205#define C9_13 0xB0
206#define C9_14 0xB1
207#define C9_15 0xB2
208#define C9_16 0xB3
diff --git a/drivers/led/issi/is31fl3731.c b/drivers/led/issi/is31fl3731.c
new file mode 100644
index 000000000..110bdc1be
--- /dev/null
+++ b/drivers/led/issi/is31fl3731.c
@@ -0,0 +1,237 @@
1/* Copyright 2017 Jason Williams
2 * Copyright 2018 Jack Humbert
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 "is31fl3731.h"
19#include "i2c_master.h"
20#include "wait.h"
21
22// This is a 7-bit address, that gets left-shifted and bit 0
23// set to 0 for write, 1 for read (as per I2C protocol)
24// The address will vary depending on your wiring:
25// 0b1110100 AD <-> GND
26// 0b1110111 AD <-> VCC
27// 0b1110101 AD <-> SCL
28// 0b1110110 AD <-> SDA
29#define ISSI_ADDR_DEFAULT 0x74
30
31#define ISSI_REG_CONFIG 0x00
32#define ISSI_REG_CONFIG_PICTUREMODE 0x00
33#define ISSI_REG_CONFIG_AUTOPLAYMODE 0x08
34#define ISSI_REG_CONFIG_AUDIOPLAYMODE 0x18
35
36#define ISSI_CONF_PICTUREMODE 0x00
37#define ISSI_CONF_AUTOFRAMEMODE 0x04
38#define ISSI_CONF_AUDIOMODE 0x08
39
40#define ISSI_REG_PICTUREFRAME 0x01
41
42#define ISSI_REG_SHUTDOWN 0x0A
43#define ISSI_REG_AUDIOSYNC 0x06
44
45#define ISSI_COMMANDREGISTER 0xFD
46#define ISSI_BANK_FUNCTIONREG 0x0B // helpfully called 'page nine'
47
48#ifndef ISSI_TIMEOUT
49# define ISSI_TIMEOUT 100
50#endif
51
52#ifndef ISSI_PERSISTENCE
53# define ISSI_PERSISTENCE 0
54#endif
55
56// Transfer buffer for TWITransmitData()
57uint8_t g_twi_transfer_buffer[20];
58
59// These buffers match the IS31FL3731 PWM registers 0x24-0xB3.
60// Storing them like this is optimal for I2C transfers to the registers.
61// We could optimize this and take out the unused registers from these
62// buffers and the transfers in IS31FL3731_write_pwm_buffer() but it's
63// probably not worth the extra complexity.
64uint8_t g_pwm_buffer[DRIVER_COUNT][144];
65bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};
66
67uint8_t g_led_control_registers[DRIVER_COUNT][18] = {{0}};
68bool g_led_control_registers_update_required[DRIVER_COUNT] = {false};
69
70// This is the bit pattern in the LED control registers
71// (for matrix A, add one to register for matrix B)
72//
73// reg - b7 b6 b5 b4 b3 b2 b1 b0
74// 0x00 - R08,R07,R06,R05,R04,R03,R02,R01
75// 0x02 - G08,G07,G06,G05,G04,G03,G02,R00
76// 0x04 - B08,B07,B06,B05,B04,B03,G01,G00
77// 0x06 - - , - , - , - , - ,B02,B01,B00
78// 0x08 - - , - , - , - , - , - , - , -
79// 0x0A - B17,B16,B15, - , - , - , - , -
80// 0x0C - G17,G16,B14,B13,B12,B11,B10,B09
81// 0x0E - R17,G15,G14,G13,G12,G11,G10,G09
82// 0x10 - R16,R15,R14,R13,R12,R11,R10,R09
83
84void IS31FL3731_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
85 g_twi_transfer_buffer[0] = reg;
86 g_twi_transfer_buffer[1] = data;
87
88#if ISSI_PERSISTENCE > 0
89 for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
90 if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) == 0) break;
91 }
92#else
93 i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT);
94#endif
95}
96
97void IS31FL3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
98 // assumes bank is already selected
99
100 // transmit PWM registers in 9 transfers of 16 bytes
101 // g_twi_transfer_buffer[] is 20 bytes
102
103 // iterate over the pwm_buffer contents at 16 byte intervals
104 for (int i = 0; i < 144; i += 16) {
105 // set the first register, e.g. 0x24, 0x34, 0x44, etc.
106 g_twi_transfer_buffer[0] = 0x24 + i;
107 // copy the data from i to i+15
108 // device will auto-increment register for data after the first byte
109 // thus this sets registers 0x24-0x33, 0x34-0x43, etc. in one transfer
110 for (int j = 0; j < 16; j++) {
111 g_twi_transfer_buffer[1 + j] = pwm_buffer[i + j];
112 }
113
114#if ISSI_PERSISTENCE > 0
115 for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
116 if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT) == 0) break;
117 }
118#else
119 i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT);
120#endif
121 }
122}
123
124void IS31FL3731_init(uint8_t addr) {
125 // In order to avoid the LEDs being driven with garbage data
126 // in the LED driver's PWM registers, first enable software shutdown,
127 // then set up the mode and other settings, clear the PWM registers,
128 // then disable software shutdown.
129
130 // select "function register" bank
131 IS31FL3731_write_register(addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG);
132
133 // enable software shutdown
134 IS31FL3731_write_register(addr, ISSI_REG_SHUTDOWN, 0x00);
135
136 // this delay was copied from other drivers, might not be needed
137 wait_ms(10);
138
139 // picture mode
140 IS31FL3731_write_register(addr, ISSI_REG_CONFIG, ISSI_REG_CONFIG_PICTUREMODE);
141 // display frame 0
142 IS31FL3731_write_register(addr, ISSI_REG_PICTUREFRAME, 0x00);
143 // audio sync off
144 IS31FL3731_write_register(addr, ISSI_REG_AUDIOSYNC, 0x00);
145
146 // select bank 0
147 IS31FL3731_write_register(addr, ISSI_COMMANDREGISTER, 0);
148
149 // turn off all LEDs in the LED control register
150 for (int i = 0x00; i <= 0x11; i++) {
151 IS31FL3731_write_register(addr, i, 0x00);
152 }
153
154 // turn off all LEDs in the blink control register (not really needed)
155 for (int i = 0x12; i <= 0x23; i++) {
156 IS31FL3731_write_register(addr, i, 0x00);
157 }
158
159 // set PWM on all LEDs to 0
160 for (int i = 0x24; i <= 0xB3; i++) {
161 IS31FL3731_write_register(addr, i, 0x00);
162 }
163
164 // select "function register" bank
165 IS31FL3731_write_register(addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG);
166
167 // disable software shutdown
168 IS31FL3731_write_register(addr, ISSI_REG_SHUTDOWN, 0x01);
169
170 // select bank 0 and leave it selected.
171 // most usage after initialization is just writing PWM buffers in bank 0
172 // as there's not much point in double-buffering
173 IS31FL3731_write_register(addr, ISSI_COMMANDREGISTER, 0);
174}
175
176void IS31FL3731_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
177 if (index >= 0 && index < DRIVER_LED_TOTAL) {
178 is31_led led = g_is31_leds[index];
179
180 // Subtract 0x24 to get the second index of g_pwm_buffer
181 g_pwm_buffer[led.driver][led.r - 0x24] = red;
182 g_pwm_buffer[led.driver][led.g - 0x24] = green;
183 g_pwm_buffer[led.driver][led.b - 0x24] = blue;
184 g_pwm_buffer_update_required[led.driver] = true;
185 }
186}
187
188void IS31FL3731_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
189 for (int i = 0; i < DRIVER_LED_TOTAL; i++) {
190 IS31FL3731_set_color(i, red, green, blue);
191 }
192}
193
194void IS31FL3731_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
195 is31_led led = g_is31_leds[index];
196
197 uint8_t control_register_r = (led.r - 0x24) / 8;
198 uint8_t control_register_g = (led.g - 0x24) / 8;
199 uint8_t control_register_b = (led.b - 0x24) / 8;
200 uint8_t bit_r = (led.r - 0x24) % 8;
201 uint8_t bit_g = (led.g - 0x24) % 8;
202 uint8_t bit_b = (led.b - 0x24) % 8;
203
204 if (red) {
205 g_led_control_registers[led.driver][control_register_r] |= (1 << bit_r);
206 } else {
207 g_led_control_registers[led.driver][control_register_r] &= ~(1 << bit_r);
208 }
209 if (green) {
210 g_led_control_registers[led.driver][control_register_g] |= (1 << bit_g);
211 } else {
212 g_led_control_registers[led.driver][control_register_g] &= ~(1 << bit_g);
213 }
214 if (blue) {
215 g_led_control_registers[led.driver][control_register_b] |= (1 << bit_b);
216 } else {
217 g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b);
218 }
219
220 g_led_control_registers_update_required[led.driver] = true;
221}
222
223void IS31FL3731_update_pwm_buffers(uint8_t addr, uint8_t index) {
224 if (g_pwm_buffer_update_required[index]) {
225 IS31FL3731_write_pwm_buffer(addr, g_pwm_buffer[index]);
226 }
227 g_pwm_buffer_update_required[index] = false;
228}
229
230void IS31FL3731_update_led_control_registers(uint8_t addr, uint8_t index) {
231 if (g_led_control_registers_update_required[index]) {
232 for (int i = 0; i < 18; i++) {
233 IS31FL3731_write_register(addr, i, g_led_control_registers[index][i]);
234 }
235 }
236 g_led_control_registers_update_required[index] = false;
237}
diff --git a/drivers/led/issi/is31fl3731.h b/drivers/led/issi/is31fl3731.h
new file mode 100644
index 000000000..803ea3ea1
--- /dev/null
+++ b/drivers/led/issi/is31fl3731.h
@@ -0,0 +1,209 @@
1/* Copyright 2017 Jason Williams
2 * Copyright 2018 Jack Humbert
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#pragma once
19
20#include <stdint.h>
21#include <stdbool.h>
22#include "progmem.h"
23
24typedef struct is31_led {
25 uint8_t driver : 2;
26 uint8_t r;
27 uint8_t g;
28 uint8_t b;
29} __attribute__((packed)) is31_led;
30
31extern const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL];
32
33void IS31FL3731_init(uint8_t addr);
34void IS31FL3731_write_register(uint8_t addr, uint8_t reg, uint8_t data);
35void IS31FL3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
36
37void IS31FL3731_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
38void IS31FL3731_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
39
40void IS31FL3731_set_led_control_register(uint8_t index, bool red, bool green, bool blue);
41
42// This should not be called from an interrupt
43// (eg. from a timer interrupt).
44// Call this while idle (in between matrix scans).
45// If the buffer is dirty, it will update the driver with the buffer.
46void IS31FL3731_update_pwm_buffers(uint8_t addr, uint8_t index);
47void IS31FL3731_update_led_control_registers(uint8_t addr, uint8_t index);
48
49#define C1_1 0x24
50#define C1_2 0x25
51#define C1_3 0x26
52#define C1_4 0x27
53#define C1_5 0x28
54#define C1_6 0x29
55#define C1_7 0x2A
56#define C1_8 0x2B
57
58#define C1_9 0x2C
59#define C1_10 0x2D
60#define C1_11 0x2E
61#define C1_12 0x2F
62#define C1_13 0x30
63#define C1_14 0x31
64#define C1_15 0x32
65#define C1_16 0x33
66
67#define C2_1 0x34
68#define C2_2 0x35
69#define C2_3 0x36
70#define C2_4 0x37
71#define C2_5 0x38
72#define C2_6 0x39
73#define C2_7 0x3A
74#define C2_8 0x3B
75
76#define C2_9 0x3C
77#define C2_10 0x3D
78#define C2_11 0x3E
79#define C2_12 0x3F
80#define C2_13 0x40
81#define C2_14 0x41
82#define C2_15 0x42
83#define C2_16 0x43
84
85#define C3_1 0x44
86#define C3_2 0x45
87#define C3_3 0x46
88#define C3_4 0x47
89#define C3_5 0x48
90#define C3_6 0x49
91#define C3_7 0x4A
92#define C3_8 0x4B
93
94#define C3_9 0x4C
95#define C3_10 0x4D
96#define C3_11 0x4E
97#define C3_12 0x4F
98#define C3_13 0x50
99#define C3_14 0x51
100#define C3_15 0x52
101#define C3_16 0x53
102
103#define C4_1 0x54
104#define C4_2 0x55
105#define C4_3 0x56
106#define C4_4 0x57
107#define C4_5 0x58
108#define C4_6 0x59
109#define C4_7 0x5A
110#define C4_8 0x5B
111
112#define C4_9 0x5C
113#define C4_10 0x5D
114#define C4_11 0x5E
115#define C4_12 0x5F
116#define C4_13 0x60
117#define C4_14 0x61
118#define C4_15 0x62
119#define C4_16 0x63
120
121#define C5_1 0x64
122#define C5_2 0x65
123#define C5_3 0x66
124#define C5_4 0x67
125#define C5_5 0x68
126#define C5_6 0x69
127#define C5_7 0x6A
128#define C5_8 0x6B
129
130#define C5_9 0x6C
131#define C5_10 0x6D
132#define C5_11 0x6E
133#define C5_12 0x6F
134#define C5_13 0x70
135#define C5_14 0x71
136#define C5_15 0x72
137#define C5_16 0x73
138
139#define C6_1 0x74
140#define C6_2 0x75
141#define C6_3 0x76
142#define C6_4 0x77
143#define C6_5 0x78
144#define C6_6 0x79
145#define C6_7 0x7A
146#define C6_8 0x7B
147
148#define C6_9 0x7C
149#define C6_10 0x7D
150#define C6_11 0x7E
151#define C6_12 0x7F
152#define C6_13 0x80
153#define C6_14 0x81
154#define C6_15 0x82
155#define C6_16 0x83
156
157#define C7_1 0x84
158#define C7_2 0x85
159#define C7_3 0x86
160#define C7_4 0x87
161#define C7_5 0x88
162#define C7_6 0x89
163#define C7_7 0x8A
164#define C7_8 0x8B
165
166#define C7_9 0x8C
167#define C7_10 0x8D
168#define C7_11 0x8E
169#define C7_12 0x8F
170#define C7_13 0x90
171#define C7_14 0x91
172#define C7_15 0x92
173#define C7_16 0x93
174
175#define C8_1 0x94
176#define C8_2 0x95
177#define C8_3 0x96
178#define C8_4 0x97
179#define C8_5 0x98
180#define C8_6 0x99
181#define C8_7 0x9A
182#define C8_8 0x9B
183
184#define C8_9 0x9C
185#define C8_10 0x9D
186#define C8_11 0x9E
187#define C8_12 0x9F
188#define C8_13 0xA0
189#define C8_14 0xA1
190#define C8_15 0xA2
191#define C8_16 0xA3
192
193#define C9_1 0xA4
194#define C9_2 0xA5
195#define C9_3 0xA6
196#define C9_4 0xA7
197#define C9_5 0xA8
198#define C9_6 0xA9
199#define C9_7 0xAA
200#define C9_8 0xAB
201
202#define C9_9 0xAC
203#define C9_10 0xAD
204#define C9_11 0xAE
205#define C9_12 0xAF
206#define C9_13 0xB0
207#define C9_14 0xB1
208#define C9_15 0xB2
209#define C9_16 0xB3
diff --git a/drivers/led/issi/is31fl3733.c b/drivers/led/issi/is31fl3733.c
new file mode 100644
index 000000000..d99e5339c
--- /dev/null
+++ b/drivers/led/issi/is31fl3733.c
@@ -0,0 +1,237 @@
1/* Copyright 2017 Jason Williams
2 * Copyright 2018 Jack Humbert
3 * Copyright 2018 Yiancar
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include "is31fl3733.h"
20#include "i2c_master.h"
21#include "wait.h"
22
23// This is a 7-bit address, that gets left-shifted and bit 0
24// set to 0 for write, 1 for read (as per I2C protocol)
25// The address will vary depending on your wiring:
26// 00 <-> GND
27// 01 <-> SCL
28// 10 <-> SDA
29// 11 <-> VCC
30// ADDR1 represents A1:A0 of the 7-bit address.
31// ADDR2 represents A3:A2 of the 7-bit address.
32// The result is: 0b101(ADDR2)(ADDR1)
33#define ISSI_ADDR_DEFAULT 0x50
34
35#define ISSI_COMMANDREGISTER 0xFD
36#define ISSI_COMMANDREGISTER_WRITELOCK 0xFE
37#define ISSI_INTERRUPTMASKREGISTER 0xF0
38#define ISSI_INTERRUPTSTATUSREGISTER 0xF1
39
40#define ISSI_PAGE_LEDCONTROL 0x00 // PG0
41#define ISSI_PAGE_PWM 0x01 // PG1
42#define ISSI_PAGE_AUTOBREATH 0x02 // PG2
43#define ISSI_PAGE_FUNCTION 0x03 // PG3
44
45#define ISSI_REG_CONFIGURATION 0x00 // PG3
46#define ISSI_REG_GLOBALCURRENT 0x01 // PG3
47#define ISSI_REG_RESET 0x11 // PG3
48#define ISSI_REG_SWPULLUP 0x0F // PG3
49#define ISSI_REG_CSPULLUP 0x10 // PG3
50
51#ifndef ISSI_TIMEOUT
52# define ISSI_TIMEOUT 100
53#endif
54
55#ifndef ISSI_PERSISTENCE
56# define ISSI_PERSISTENCE 0
57#endif
58
59// Transfer buffer for TWITransmitData()
60uint8_t g_twi_transfer_buffer[20];
61
62// These buffers match the IS31FL3733 PWM registers.
63// The control buffers match the PG0 LED On/Off registers.
64// Storing them like this is optimal for I2C transfers to the registers.
65// We could optimize this and take out the unused registers from these
66// buffers and the transfers in IS31FL3733_write_pwm_buffer() but it's
67// probably not worth the extra complexity.
68uint8_t g_pwm_buffer[DRIVER_COUNT][192];
69bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};
70
71uint8_t g_led_control_registers[DRIVER_COUNT][24] = {0};
72bool g_led_control_registers_update_required[DRIVER_COUNT] = {false};
73
74bool IS31FL3733_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
75 // If the transaction fails function returns false.
76 g_twi_transfer_buffer[0] = reg;
77 g_twi_transfer_buffer[1] = data;
78
79#if ISSI_PERSISTENCE > 0
80 for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
81 if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) != 0) {
82 return false;
83 }
84 }
85#else
86 if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) != 0) {
87 return false;
88 }
89#endif
90 return true;
91}
92
93bool IS31FL3733_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
94 // Assumes PG1 is already selected.
95 // If any of the transactions fails function returns false.
96 // Transmit PWM registers in 12 transfers of 16 bytes.
97 // g_twi_transfer_buffer[] is 20 bytes
98
99 // Iterate over the pwm_buffer contents at 16 byte intervals.
100 for (int i = 0; i < 192; i += 16) {
101 g_twi_transfer_buffer[0] = i;
102 // Copy the data from i to i+15.
103 // Device will auto-increment register for data after the first byte
104 // Thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer.
105 for (int j = 0; j < 16; j++) {
106 g_twi_transfer_buffer[1 + j] = pwm_buffer[i + j];
107 }
108
109#if ISSI_PERSISTENCE > 0
110 for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
111 if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT) != 0) {
112 return false;
113 }
114 }
115#else
116 if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT) != 0) {
117 return false;
118 }
119#endif
120 }
121 return true;
122}
123
124void IS31FL3733_init(uint8_t addr, uint8_t sync) {
125 // In order to avoid the LEDs being driven with garbage data
126 // in the LED driver's PWM registers, shutdown is enabled last.
127 // Set up the mode and other settings, clear the PWM registers,
128 // then disable software shutdown.
129 // Sync is passed so set it according to the datasheet.
130
131 // Unlock the command register.
132 IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
133
134 // Select PG0
135 IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
136 // Turn off all LEDs.
137 for (int i = 0x00; i <= 0x17; i++) {
138 IS31FL3733_write_register(addr, i, 0x00);
139 }
140
141 // Unlock the command register.
142 IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
143
144 // Select PG1
145 IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
146 // Set PWM on all LEDs to 0
147 // No need to setup Breath registers to PWM as that is the default.
148 for (int i = 0x00; i <= 0xBF; i++) {
149 IS31FL3733_write_register(addr, i, 0x00);
150 }
151
152 // Unlock the command register.
153 IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
154
155 // Select PG3
156 IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_FUNCTION);
157 // Set global current to maximum.
158 IS31FL3733_write_register(addr, ISSI_REG_GLOBALCURRENT, 0xFF);
159 // Disable software shutdown.
160 IS31FL3733_write_register(addr, ISSI_REG_CONFIGURATION, (sync << 6) | 0x01);
161
162 // Wait 10ms to ensure the device has woken up.
163 wait_ms(10);
164}
165
166void IS31FL3733_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
167 if (index >= 0 && index < DRIVER_LED_TOTAL) {
168 is31_led led = g_is31_leds[index];
169
170 g_pwm_buffer[led.driver][led.r] = red;
171 g_pwm_buffer[led.driver][led.g] = green;
172 g_pwm_buffer[led.driver][led.b] = blue;
173 g_pwm_buffer_update_required[led.driver] = true;
174 }
175}
176
177void IS31FL3733_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
178 for (int i = 0; i < DRIVER_LED_TOTAL; i++) {
179 IS31FL3733_set_color(i, red, green, blue);
180 }
181}
182
183void IS31FL3733_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
184 is31_led led = g_is31_leds[index];
185
186 uint8_t control_register_r = led.r / 8;
187 uint8_t control_register_g = led.g / 8;
188 uint8_t control_register_b = led.b / 8;
189 uint8_t bit_r = led.r % 8;
190 uint8_t bit_g = led.g % 8;
191 uint8_t bit_b = led.b % 8;
192
193 if (red) {
194 g_led_control_registers[led.driver][control_register_r] |= (1 << bit_r);
195 } else {
196 g_led_control_registers[led.driver][control_register_r] &= ~(1 << bit_r);
197 }
198 if (green) {
199 g_led_control_registers[led.driver][control_register_g] |= (1 << bit_g);
200 } else {
201 g_led_control_registers[led.driver][control_register_g] &= ~(1 << bit_g);
202 }
203 if (blue) {
204 g_led_control_registers[led.driver][control_register_b] |= (1 << bit_b);
205 } else {
206 g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b);
207 }
208
209 g_led_control_registers_update_required[led.driver] = true;
210}
211
212void IS31FL3733_update_pwm_buffers(uint8_t addr, uint8_t index) {
213 if (g_pwm_buffer_update_required[index]) {
214 // Firstly we need to unlock the command register and select PG1.
215 IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
216 IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
217
218 // If any of the transactions fail we risk writing dirty PG0,
219 // refresh page 0 just in case.
220 if (!IS31FL3733_write_pwm_buffer(addr, g_pwm_buffer[index])) {
221 g_led_control_registers_update_required[index] = true;
222 }
223 }
224 g_pwm_buffer_update_required[index] = false;
225}
226
227void IS31FL3733_update_led_control_registers(uint8_t addr, uint8_t index) {
228 if (g_led_control_registers_update_required[index]) {
229 // Firstly we need to unlock the command register and select PG0
230 IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
231 IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
232 for (int i = 0; i < 24; i++) {
233 IS31FL3733_write_register(addr, i, g_led_control_registers[index][i]);
234 }
235 }
236 g_led_control_registers_update_required[index] = false;
237}
diff --git a/drivers/led/issi/is31fl3733.h b/drivers/led/issi/is31fl3733.h
new file mode 100644
index 000000000..64fd38eb1
--- /dev/null
+++ b/drivers/led/issi/is31fl3733.h
@@ -0,0 +1,252 @@
1/* Copyright 2017 Jason Williams
2 * Copyright 2018 Jack Humbert
3 * Copyright 2018 Yiancar
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#pragma once
20
21#include <stdint.h>
22#include <stdbool.h>
23#include "progmem.h"
24
25typedef struct is31_led {
26 uint8_t driver : 2;
27 uint8_t r;
28 uint8_t g;
29 uint8_t b;
30} __attribute__((packed)) is31_led;
31
32extern const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL];
33
34void IS31FL3733_init(uint8_t addr, uint8_t sync);
35bool IS31FL3733_write_register(uint8_t addr, uint8_t reg, uint8_t data);
36bool IS31FL3733_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
37
38void IS31FL3733_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
39void IS31FL3733_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
40
41void IS31FL3733_set_led_control_register(uint8_t index, bool red, bool green, bool blue);
42
43// This should not be called from an interrupt
44// (eg. from a timer interrupt).
45// Call this while idle (in between matrix scans).
46// If the buffer is dirty, it will update the driver with the buffer.
47void IS31FL3733_update_pwm_buffers(uint8_t addr, uint8_t index);
48void IS31FL3733_update_led_control_registers(uint8_t addr, uint8_t index);
49
50#define A_1 0x00
51#define A_2 0x01
52#define A_3 0x02
53#define A_4 0x03
54#define A_5 0x04
55#define A_6 0x05
56#define A_7 0x06
57#define A_8 0x07
58#define A_9 0x08
59#define A_10 0x09
60#define A_11 0x0A
61#define A_12 0x0B
62#define A_13 0x0C
63#define A_14 0x0D
64#define A_15 0x0E
65#define A_16 0x0F
66
67#define B_1 0x10
68#define B_2 0x11
69#define B_3 0x12
70#define B_4 0x13
71#define B_5 0x14
72#define B_6 0x15
73#define B_7 0x16
74#define B_8 0x17
75#define B_9 0x18
76#define B_10 0x19
77#define B_11 0x1A
78#define B_12 0x1B
79#define B_13 0x1C
80#define B_14 0x1D
81#define B_15 0x1E
82#define B_16 0x1F
83
84#define C_1 0x20
85#define C_2 0x21
86#define C_3 0x22
87#define C_4 0x23
88#define C_5 0x24
89#define C_6 0x25
90#define C_7 0x26
91#define C_8 0x27
92#define C_9 0x28
93#define C_10 0x29
94#define C_11 0x2A
95#define C_12 0x2B
96#define C_13 0x2C
97#define C_14 0x2D
98#define C_15 0x2E
99#define C_16 0x2F
100
101#define D_1 0x30
102#define D_2 0x31
103#define D_3 0x32
104#define D_4 0x33
105#define D_5 0x34
106#define D_6 0x35
107#define D_7 0x36
108#define D_8 0x37
109#define D_9 0x38
110#define D_10 0x39
111#define D_11 0x3A
112#define D_12 0x3B
113#define D_13 0x3C
114#define D_14 0x3D
115#define D_15 0x3E
116#define D_16 0x3F
117
118#define E_1 0x40
119#define E_2 0x41
120#define E_3 0x42
121#define E_4 0x43
122#define E_5 0x44
123#define E_6 0x45
124#define E_7 0x46
125#define E_8 0x47
126#define E_9 0x48
127#define E_10 0x49
128#define E_11 0x4A
129#define E_12 0x4B
130#define E_13 0x4C
131#define E_14 0x4D
132#define E_15 0x4E
133#define E_16 0x4F
134
135#define F_1 0x50
136#define F_2 0x51
137#define F_3 0x52
138#define F_4 0x53
139#define F_5 0x54
140#define F_6 0x55
141#define F_7 0x56
142#define F_8 0x57
143#define F_9 0x58
144#define F_10 0x59
145#define F_11 0x5A
146#define F_12 0x5B
147#define F_13 0x5C
148#define F_14 0x5D
149#define F_15 0x5E
150#define F_16 0x5F
151
152#define G_1 0x60
153#define G_2 0x61
154#define G_3 0x62
155#define G_4 0x63
156#define G_5 0x64
157#define G_6 0x65
158#define G_7 0x66
159#define G_8 0x67
160#define G_9 0x68
161#define G_10 0x69
162#define G_11 0x6A
163#define G_12 0x6B
164#define G_13 0x6C
165#define G_14 0x6D
166#define G_15 0x6E
167#define G_16 0x6F
168
169#define H_1 0x70
170#define H_2 0x71
171#define H_3 0x72
172#define H_4 0x73
173#define H_5 0x74
174#define H_6 0x75
175#define H_7 0x76
176#define H_8 0x77
177#define H_9 0x78
178#define H_10 0x79
179#define H_11 0x7A
180#define H_12 0x7B
181#define H_13 0x7C
182#define H_14 0x7D
183#define H_15 0x7E
184#define H_16 0x7F
185
186#define I_1 0x80
187#define I_2 0x81
188#define I_3 0x82
189#define I_4 0x83
190#define I_5 0x84
191#define I_6 0x85
192#define I_7 0x86
193#define I_8 0x87
194#define I_9 0x88
195#define I_10 0x89
196#define I_11 0x8A
197#define I_12 0x8B
198#define I_13 0x8C
199#define I_14 0x8D
200#define I_15 0x8E
201#define I_16 0x8F
202
203#define J_1 0x90
204#define J_2 0x91
205#define J_3 0x92
206#define J_4 0x93
207#define J_5 0x94
208#define J_6 0x95
209#define J_7 0x96
210#define J_8 0x97
211#define J_9 0x98
212#define J_10 0x99
213#define J_11 0x9A
214#define J_12 0x9B
215#define J_13 0x9C
216#define J_14 0x9D
217#define J_15 0x9E
218#define J_16 0x9F
219
220#define K_1 0xA0
221#define K_2 0xA1
222#define K_3 0xA2
223#define K_4 0xA3
224#define K_5 0xA4
225#define K_6 0xA5
226#define K_7 0xA6
227#define K_8 0xA7
228#define K_9 0xA8
229#define K_10 0xA9
230#define K_11 0xAA
231#define K_12 0xAB
232#define K_13 0xAC
233#define K_14 0xAD
234#define K_15 0xAE
235#define K_16 0xAF
236
237#define L_1 0xB0
238#define L_2 0xB1
239#define L_3 0xB2
240#define L_4 0xB3
241#define L_5 0xB4
242#define L_6 0xB5
243#define L_7 0xB6
244#define L_8 0xB7
245#define L_9 0xB8
246#define L_10 0xB9
247#define L_11 0xBA
248#define L_12 0xBB
249#define L_13 0xBC
250#define L_14 0xBD
251#define L_15 0xBE
252#define L_16 0xBF
diff --git a/drivers/led/issi/is31fl3736.c b/drivers/led/issi/is31fl3736.c
new file mode 100644
index 000000000..7dece1b1e
--- /dev/null
+++ b/drivers/led/issi/is31fl3736.c
@@ -0,0 +1,269 @@
1/* Copyright 2018 Jason Williams (Wilba)
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "is31fl3736.h"
18#include "i2c_master.h"
19#include "wait.h"
20
21// This is a 7-bit address, that gets left-shifted and bit 0
22// set to 0 for write, 1 for read (as per I2C protocol)
23// The address will vary depending on your wiring:
24// 00 <-> GND
25// 01 <-> SCL
26// 10 <-> SDA
27// 11 <-> VCC
28// ADDR1 represents A1:A0 of the 7-bit address.
29// ADDR2 represents A3:A2 of the 7-bit address.
30// The result is: 0b101(ADDR2)(ADDR1)
31#define ISSI_ADDR_DEFAULT 0x50
32
33#define ISSI_COMMANDREGISTER 0xFD
34#define ISSI_COMMANDREGISTER_WRITELOCK 0xFE
35#define ISSI_INTERRUPTMASKREGISTER 0xF0
36#define ISSI_INTERRUPTSTATUSREGISTER 0xF1
37
38#define ISSI_PAGE_LEDCONTROL 0x00 // PG0
39#define ISSI_PAGE_PWM 0x01 // PG1
40#define ISSI_PAGE_AUTOBREATH 0x02 // PG2
41#define ISSI_PAGE_FUNCTION 0x03 // PG3
42
43#define ISSI_REG_CONFIGURATION 0x00 // PG3
44#define ISSI_REG_GLOBALCURRENT 0x01 // PG3
45#define ISSI_REG_RESET 0x11 // PG3
46#define ISSI_REG_SWPULLUP 0x0F // PG3
47#define ISSI_REG_CSPULLUP 0x10 // PG3
48
49#ifndef ISSI_TIMEOUT
50# define ISSI_TIMEOUT 100
51#endif
52
53#ifndef ISSI_PERSISTENCE
54# define ISSI_PERSISTENCE 0
55#endif
56
57// Transfer buffer for TWITransmitData()
58uint8_t g_twi_transfer_buffer[20];
59
60// These buffers match the IS31FL3736 PWM registers.
61// The control buffers match the PG0 LED On/Off registers.
62// Storing them like this is optimal for I2C transfers to the registers.
63// We could optimize this and take out the unused registers from these
64// buffers and the transfers in IS31FL3736_write_pwm_buffer() but it's
65// probably not worth the extra complexity.
66uint8_t g_pwm_buffer[DRIVER_COUNT][192];
67bool g_pwm_buffer_update_required = false;
68
69uint8_t g_led_control_registers[DRIVER_COUNT][24] = {{0}, {0}};
70bool g_led_control_registers_update_required = false;
71
72void IS31FL3736_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
73 g_twi_transfer_buffer[0] = reg;
74 g_twi_transfer_buffer[1] = data;
75
76#if ISSI_PERSISTENCE > 0
77 for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
78 if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) == 0) break;
79 }
80#else
81 i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT);
82#endif
83}
84
85void IS31FL3736_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
86 // assumes PG1 is already selected
87
88 // transmit PWM registers in 12 transfers of 16 bytes
89 // g_twi_transfer_buffer[] is 20 bytes
90
91 // iterate over the pwm_buffer contents at 16 byte intervals
92 for (int i = 0; i < 192; i += 16) {
93 g_twi_transfer_buffer[0] = i;
94 // copy the data from i to i+15
95 // device will auto-increment register for data after the first byte
96 // thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer
97 for (int j = 0; j < 16; j++) {
98 g_twi_transfer_buffer[1 + j] = pwm_buffer[i + j];
99 }
100
101#if ISSI_PERSISTENCE > 0
102 for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
103 if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT) == 0) break;
104 }
105#else
106 i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT);
107#endif
108 }
109}
110
111void IS31FL3736_init(uint8_t addr) {
112 // In order to avoid the LEDs being driven with garbage data
113 // in the LED driver's PWM registers, shutdown is enabled last.
114 // Set up the mode and other settings, clear the PWM registers,
115 // then disable software shutdown.
116
117 // Unlock the command register.
118 IS31FL3736_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
119
120 // Select PG0
121 IS31FL3736_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
122 // Turn off all LEDs.
123 for (int i = 0x00; i <= 0x17; i++) {
124 IS31FL3736_write_register(addr, i, 0x00);
125 }
126
127 // Unlock the command register.
128 IS31FL3736_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
129
130 // Select PG1
131 IS31FL3736_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
132 // Set PWM on all LEDs to 0
133 // No need to setup Breath registers to PWM as that is the default.
134 for (int i = 0x00; i <= 0xBF; i++) {
135 IS31FL3736_write_register(addr, i, 0x00);
136 }
137
138 // Unlock the command register.
139 IS31FL3736_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
140
141 // Select PG3
142 IS31FL3736_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_FUNCTION);
143 // Set global current to maximum.
144 IS31FL3736_write_register(addr, ISSI_REG_GLOBALCURRENT, 0xFF);
145 // Disable software shutdown.
146 IS31FL3736_write_register(addr, ISSI_REG_CONFIGURATION, 0x01);
147
148 // Wait 10ms to ensure the device has woken up.
149 wait_ms(10);
150}
151
152void IS31FL3736_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
153 if (index >= 0 && index < DRIVER_LED_TOTAL) {
154 is31_led led = g_is31_leds[index];
155
156 g_pwm_buffer[led.driver][led.r] = red;
157 g_pwm_buffer[led.driver][led.g] = green;
158 g_pwm_buffer[led.driver][led.b] = blue;
159 g_pwm_buffer_update_required = true;
160 }
161}
162
163void IS31FL3736_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
164 for (int i = 0; i < DRIVER_LED_TOTAL; i++) {
165 IS31FL3736_set_color(i, red, green, blue);
166 }
167}
168
169void IS31FL3736_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
170 is31_led led = g_is31_leds[index];
171
172 // IS31FL3733
173 // The PWM register for a matrix position (0x00 to 0xBF) can be
174 // divided by 8 to get the LED control register (0x00 to 0x17),
175 // then mod 8 to get the bit position within that register.
176
177 // IS31FL3736
178 // The PWM register for a matrix position (0x00 to 0xBF) is interleaved, so:
179 // A1=0x00 A2=0x02 A3=0x04 A4=0x06 A5=0x08 A6=0x0A A7=0x0C A8=0x0E
180 // B1=0x10 B2=0x12 B3=0x14
181 // But also, the LED control registers (0x00 to 0x17) are also interleaved, so:
182 // A1-A4=0x00 A5-A8=0x01
183 // So, the same math applies.
184
185 uint8_t control_register_r = led.r / 8;
186 uint8_t control_register_g = led.g / 8;
187 uint8_t control_register_b = led.b / 8;
188
189 uint8_t bit_r = led.r % 8;
190 uint8_t bit_g = led.g % 8;
191 uint8_t bit_b = led.b % 8;
192
193 if (red) {
194 g_led_control_registers[led.driver][control_register_r] |= (1 << bit_r);
195 } else {
196 g_led_control_registers[led.driver][control_register_r] &= ~(1 << bit_r);
197 }
198 if (green) {
199 g_led_control_registers[led.driver][control_register_g] |= (1 << bit_g);
200 } else {
201 g_led_control_registers[led.driver][control_register_g] &= ~(1 << bit_g);
202 }
203 if (blue) {
204 g_led_control_registers[led.driver][control_register_b] |= (1 << bit_b);
205 } else {
206 g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b);
207 }
208
209 g_led_control_registers_update_required = true;
210}
211
212void IS31FL3736_mono_set_brightness(int index, uint8_t value) {
213 if (index >= 0 && index < 96) {
214 // Index in range 0..95 -> A1..A8, B1..B8, etc.
215 // Map index 0..95 to registers 0x00..0xBE (interleaved)
216 uint8_t pwm_register = index * 2;
217 g_pwm_buffer[0][pwm_register] = value;
218 g_pwm_buffer_update_required = true;
219 }
220}
221
222void IS31FL3736_mono_set_brightness_all(uint8_t value) {
223 for (int i = 0; i < 96; i++) {
224 IS31FL3736_mono_set_brightness(i, value);
225 }
226}
227
228void IS31FL3736_mono_set_led_control_register(uint8_t index, bool enabled) {
229 // Index in range 0..95 -> A1..A8, B1..B8, etc.
230
231 // Map index 0..95 to registers 0x00..0xBE (interleaved)
232 uint8_t pwm_register = index * 2;
233 // Map register 0x00..0xBE (interleaved) into control register and bit
234 uint8_t control_register = pwm_register / 8;
235 uint8_t bit = pwm_register % 8;
236
237 if (enabled) {
238 g_led_control_registers[0][control_register] |= (1 << bit);
239 } else {
240 g_led_control_registers[0][control_register] &= ~(1 << bit);
241 }
242
243 g_led_control_registers_update_required = true;
244}
245
246void IS31FL3736_update_pwm_buffers(uint8_t addr1, uint8_t addr2) {
247 if (g_pwm_buffer_update_required) {
248 // Firstly we need to unlock the command register and select PG1
249 IS31FL3736_write_register(addr1, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
250 IS31FL3736_write_register(addr1, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
251
252 IS31FL3736_write_pwm_buffer(addr1, g_pwm_buffer[0]);
253 // IS31FL3736_write_pwm_buffer(addr2, g_pwm_buffer[1]);
254 }
255 g_pwm_buffer_update_required = false;
256}
257
258void IS31FL3736_update_led_control_registers(uint8_t addr1, uint8_t addr2) {
259 if (g_led_control_registers_update_required) {
260 // Firstly we need to unlock the command register and select PG0
261 IS31FL3736_write_register(addr1, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
262 IS31FL3736_write_register(addr1, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
263 for (int i = 0; i < 24; i++) {
264 IS31FL3736_write_register(addr1, i, g_led_control_registers[0][i]);
265 // IS31FL3736_write_register(addr2, i, g_led_control_registers[1][i]);
266 }
267 g_led_control_registers_update_required = false;
268 }
269}
diff --git a/drivers/led/issi/is31fl3736.h b/drivers/led/issi/is31fl3736.h
new file mode 100644
index 000000000..c956c87f7
--- /dev/null
+++ b/drivers/led/issi/is31fl3736.h
@@ -0,0 +1,169 @@
1/* Copyright 2018 Jason Williams (Wilba)
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include <stdint.h>
20#include <stdbool.h>
21#include "progmem.h"
22
23// Simple interface option.
24// If these aren't defined, just define them to make it compile
25
26#ifndef DRIVER_COUNT
27# define DRIVER_COUNT 2
28#endif
29
30#ifndef DRIVER_LED_TOTAL
31# define DRIVER_LED_TOTAL 96
32#endif
33
34typedef struct is31_led {
35 uint8_t driver : 2;
36 uint8_t r;
37 uint8_t g;
38 uint8_t b;
39} __attribute__((packed)) is31_led;
40
41extern const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL];
42
43void IS31FL3736_init(uint8_t addr);
44void IS31FL3736_write_register(uint8_t addr, uint8_t reg, uint8_t data);
45void IS31FL3736_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
46
47void IS31FL3736_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
48void IS31FL3736_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
49
50void IS31FL3736_set_led_control_register(uint8_t index, bool red, bool green, bool blue);
51
52void IS31FL3736_mono_set_brightness(int index, uint8_t value);
53void IS31FL3736_mono_set_brightness_all(uint8_t value);
54void IS31FL3736_mono_set_led_control_register(uint8_t index, bool enabled);
55
56// This should not be called from an interrupt
57// (eg. from a timer interrupt).
58// Call this while idle (in between matrix scans).
59// If the buffer is dirty, it will update the driver with the buffer.
60void IS31FL3736_update_pwm_buffers(uint8_t addr1, uint8_t addr2);
61void IS31FL3736_update_led_control_registers(uint8_t addr1, uint8_t addr2);
62
63#define A_1 0x00
64#define A_2 0x02
65#define A_3 0x04
66#define A_4 0x06
67#define A_5 0x08
68#define A_6 0x0A
69#define A_7 0x0C
70#define A_8 0x0E
71
72#define B_1 0x10
73#define B_2 0x12
74#define B_3 0x14
75#define B_4 0x16
76#define B_5 0x18
77#define B_6 0x1A
78#define B_7 0x1C
79#define B_8 0x1E
80
81#define C_1 0x20
82#define C_2 0x22
83#define C_3 0x24
84#define C_4 0x26
85#define C_5 0x28
86#define C_6 0x2A
87#define C_7 0x2C
88#define C_8 0x2E
89
90#define D_1 0x30
91#define D_2 0x32
92#define D_3 0x34
93#define D_4 0x36
94#define D_5 0x38
95#define D_6 0x3A
96#define D_7 0x3C
97#define D_8 0x3E
98
99#define E_1 0x40
100#define E_2 0x42
101#define E_3 0x44
102#define E_4 0x46
103#define E_5 0x48
104#define E_6 0x4A
105#define E_7 0x4C
106#define E_8 0x4E
107
108#define F_1 0x50
109#define F_2 0x52
110#define F_3 0x54
111#define F_4 0x56
112#define F_5 0x58
113#define F_6 0x5A
114#define F_7 0x5C
115#define F_8 0x5E
116
117#define G_1 0x60
118#define G_2 0x62
119#define G_3 0x64
120#define G_4 0x66
121#define G_5 0x68
122#define G_6 0x6A
123#define G_7 0x6C
124#define G_8 0x6E
125
126#define H_1 0x70
127#define H_2 0x72
128#define H_3 0x74
129#define H_4 0x76
130#define H_5 0x78
131#define H_6 0x7A
132#define H_7 0x7C
133#define H_8 0x7E
134
135#define I_1 0x80
136#define I_2 0x82
137#define I_3 0x84
138#define I_4 0x86
139#define I_5 0x88
140#define I_6 0x8A
141#define I_7 0x8C
142#define I_8 0x8E
143
144#define J_1 0x90
145#define J_2 0x92
146#define J_3 0x94
147#define J_4 0x96
148#define J_5 0x98
149#define J_6 0x9A
150#define J_7 0x9C
151#define J_8 0x9E
152
153#define K_1 0xA0
154#define K_2 0xA2
155#define K_3 0xA4
156#define K_4 0xA6
157#define K_5 0xA8
158#define K_6 0xAA
159#define K_7 0xAC
160#define K_8 0xAE
161
162#define L_1 0xB0
163#define L_2 0xB2
164#define L_3 0xB4
165#define L_4 0xB6
166#define L_5 0xB8
167#define L_6 0xBA
168#define L_7 0xBC
169#define L_8 0xBE
diff --git a/drivers/led/issi/is31fl3737.c b/drivers/led/issi/is31fl3737.c
new file mode 100644
index 000000000..0bb4ddd42
--- /dev/null
+++ b/drivers/led/issi/is31fl3737.c
@@ -0,0 +1,222 @@
1/* Copyright 2017 Jason Williams
2 * Copyright 2018 Jack Humbert
3 * Copyright 2018 Yiancar
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include "is31fl3737.h"
20#include "i2c_master.h"
21#include "wait.h"
22
23// This is a 7-bit address, that gets left-shifted and bit 0
24// set to 0 for write, 1 for read (as per I2C protocol)
25// The address will vary depending on your wiring:
26// 00 <-> GND
27// 01 <-> SCL
28// 10 <-> SDA
29// 11 <-> VCC
30// ADDR1 represents A1:A0 of the 7-bit address.
31// ADDR2 represents A3:A2 of the 7-bit address.
32// The result is: 0b101(ADDR2)(ADDR1)
33#define ISSI_ADDR_DEFAULT 0x50
34
35#define ISSI_COMMANDREGISTER 0xFD
36#define ISSI_COMMANDREGISTER_WRITELOCK 0xFE
37#define ISSI_INTERRUPTMASKREGISTER 0xF0
38#define ISSI_INTERRUPTSTATUSREGISTER 0xF1
39
40#define ISSI_PAGE_LEDCONTROL 0x00 // PG0
41#define ISSI_PAGE_PWM 0x01 // PG1
42#define ISSI_PAGE_AUTOBREATH 0x02 // PG2
43#define ISSI_PAGE_FUNCTION 0x03 // PG3
44
45#define ISSI_REG_CONFIGURATION 0x00 // PG3
46#define ISSI_REG_GLOBALCURRENT 0x01 // PG3
47#define ISSI_REG_RESET 0x11 // PG3
48#define ISSI_REG_SWPULLUP 0x0F // PG3
49#define ISSI_REG_CSPULLUP 0x10 // PG3
50
51#ifndef ISSI_TIMEOUT
52# define ISSI_TIMEOUT 100
53#endif
54
55#ifndef ISSI_PERSISTENCE
56# define ISSI_PERSISTENCE 0
57#endif
58
59// Transfer buffer for TWITransmitData()
60uint8_t g_twi_transfer_buffer[20];
61
62// These buffers match the IS31FL3737 PWM registers.
63// The control buffers match the PG0 LED On/Off registers.
64// Storing them like this is optimal for I2C transfers to the registers.
65// We could optimize this and take out the unused registers from these
66// buffers and the transfers in IS31FL3737_write_pwm_buffer() but it's
67// probably not worth the extra complexity.
68
69uint8_t g_pwm_buffer[DRIVER_COUNT][192];
70bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};
71
72uint8_t g_led_control_registers[DRIVER_COUNT][24] = {0};
73bool g_led_control_registers_update_required[DRIVER_COUNT] = {false};
74
75void IS31FL3737_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
76 g_twi_transfer_buffer[0] = reg;
77 g_twi_transfer_buffer[1] = data;
78
79#if ISSI_PERSISTENCE > 0
80 for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
81 if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) == 0) break;
82 }
83#else
84 i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT);
85#endif
86}
87
88void IS31FL3737_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
89 // assumes PG1 is already selected
90
91 // transmit PWM registers in 12 transfers of 16 bytes
92 // g_twi_transfer_buffer[] is 20 bytes
93
94 // iterate over the pwm_buffer contents at 16 byte intervals
95 for (int i = 0; i < 192; i += 16) {
96 g_twi_transfer_buffer[0] = i;
97 // copy the data from i to i+15
98 // device will auto-increment register for data after the first byte
99 // thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer
100 for (int j = 0; j < 16; j++) {
101 g_twi_transfer_buffer[1 + j] = pwm_buffer[i + j];
102 }
103
104#if ISSI_PERSISTENCE > 0
105 for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
106 if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT) == 0) break;
107 }
108#else
109 i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT);
110#endif
111 }
112}
113
114void IS31FL3737_init(uint8_t addr) {
115 // In order to avoid the LEDs being driven with garbage data
116 // in the LED driver's PWM registers, shutdown is enabled last.
117 // Set up the mode and other settings, clear the PWM registers,
118 // then disable software shutdown.
119
120 // Unlock the command register.
121 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
122
123 // Select PG0
124 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
125 // Turn off all LEDs.
126 for (int i = 0x00; i <= 0x17; i++) {
127 IS31FL3737_write_register(addr, i, 0x00);
128 }
129
130 // Unlock the command register.
131 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
132
133 // Select PG1
134 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
135 // Set PWM on all LEDs to 0
136 // No need to setup Breath registers to PWM as that is the default.
137 for (int i = 0x00; i <= 0xBF; i++) {
138 IS31FL3737_write_register(addr, i, 0x00);
139 }
140
141 // Unlock the command register.
142 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
143
144 // Select PG3
145 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_FUNCTION);
146 // Set global current to maximum.
147 IS31FL3737_write_register(addr, ISSI_REG_GLOBALCURRENT, 0xFF);
148 // Disable software shutdown.
149 IS31FL3737_write_register(addr, ISSI_REG_CONFIGURATION, 0x01);
150
151 // Wait 10ms to ensure the device has woken up.
152 wait_ms(10);
153}
154
155void IS31FL3737_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
156 if (index >= 0 && index < DRIVER_LED_TOTAL) {
157 is31_led led = g_is31_leds[index];
158
159 g_pwm_buffer[led.driver][led.r] = red;
160 g_pwm_buffer[led.driver][led.g] = green;
161 g_pwm_buffer[led.driver][led.b] = blue;
162 g_pwm_buffer_update_required[led.driver] = true;
163 }
164}
165
166void IS31FL3737_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
167 for (int i = 0; i < DRIVER_LED_TOTAL; i++) {
168 IS31FL3737_set_color(i, red, green, blue);
169 }
170}
171
172void IS31FL3737_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
173 is31_led led = g_is31_leds[index];
174
175 uint8_t control_register_r = led.r / 8;
176 uint8_t control_register_g = led.g / 8;
177 uint8_t control_register_b = led.b / 8;
178 uint8_t bit_r = led.r % 8;
179 uint8_t bit_g = led.g % 8;
180 uint8_t bit_b = led.b % 8;
181
182 if (red) {
183 g_led_control_registers[led.driver][control_register_r] |= (1 << bit_r);
184 } else {
185 g_led_control_registers[led.driver][control_register_r] &= ~(1 << bit_r);
186 }
187 if (green) {
188 g_led_control_registers[led.driver][control_register_g] |= (1 << bit_g);
189 } else {
190 g_led_control_registers[led.driver][control_register_g] &= ~(1 << bit_g);
191 }
192 if (blue) {
193 g_led_control_registers[led.driver][control_register_b] |= (1 << bit_b);
194 } else {
195 g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b);
196 }
197
198 g_led_control_registers_update_required[led.driver] = true;
199}
200
201void IS31FL3737_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 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
205 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
206
207 IS31FL3737_write_pwm_buffer(addr, g_pwm_buffer[index]);
208 }
209 g_pwm_buffer_update_required[index] = false;
210}
211
212void IS31FL3737_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 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
216 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
217 for (int i = 0; i < 24; i++) {
218 IS31FL3737_write_register(addr, i, g_led_control_registers[index][i]);
219 }
220 }
221 g_led_control_registers_update_required[index] = false;
222}
diff --git a/drivers/led/issi/is31fl3737.h b/drivers/led/issi/is31fl3737.h
new file mode 100644
index 000000000..06886e9c9
--- /dev/null
+++ b/drivers/led/issi/is31fl3737.h
@@ -0,0 +1,204 @@
1/* Copyright 2017 Jason Williams
2 * Copyright 2018 Jack Humbert
3 * Copyright 2018 Yiancar
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#pragma once
20
21#include <stdint.h>
22#include <stdbool.h>
23#include "progmem.h"
24
25typedef struct is31_led {
26 uint8_t driver : 2;
27 uint8_t r;
28 uint8_t g;
29 uint8_t b;
30} __attribute__((packed)) is31_led;
31
32extern const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL];
33
34void IS31FL3737_init(uint8_t addr);
35void IS31FL3737_write_register(uint8_t addr, uint8_t reg, uint8_t data);
36void IS31FL3737_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
37
38void IS31FL3737_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
39void IS31FL3737_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
40
41void IS31FL3737_set_led_control_register(uint8_t index, bool red, bool green, bool blue);
42
43// This should not be called from an interrupt
44// (eg. from a timer interrupt).
45// Call this while idle (in between matrix scans).
46// If the buffer is dirty, it will update the driver with the buffer.
47void IS31FL3737_update_pwm_buffers(uint8_t addr1, uint8_t addr2);
48void IS31FL3737_update_led_control_registers(uint8_t addr1, uint8_t addr2);
49
50#define A_1 0x00
51#define A_2 0x01
52#define A_3 0x02
53#define A_4 0x03
54#define A_5 0x04
55#define A_6 0x05
56#define A_7 0x08
57#define A_8 0x09
58#define A_9 0x0A
59#define A_10 0x0B
60#define A_11 0x0C
61#define A_12 0x0D
62
63#define B_1 0x10
64#define B_2 0x11
65#define B_3 0x12
66#define B_4 0x13
67#define B_5 0x14
68#define B_6 0x15
69#define B_7 0x18
70#define B_8 0x19
71#define B_9 0x1A
72#define B_10 0x1B
73#define B_11 0x1C
74#define B_12 0x1D
75
76#define C_1 0x20
77#define C_2 0x21
78#define C_3 0x22
79#define C_4 0x23
80#define C_5 0x24
81#define C_6 0x25
82#define C_7 0x28
83#define C_8 0x29
84#define C_9 0x2A
85#define C_10 0x2B
86#define C_11 0x2C
87#define C_12 0x2D
88
89#define D_1 0x30
90#define D_2 0x31
91#define D_3 0x32
92#define D_4 0x33
93#define D_5 0x34
94#define D_6 0x35
95#define D_7 0x38
96#define D_8 0x39
97#define D_9 0x3A
98#define D_10 0x3B
99#define D_11 0x3C
100#define D_12 0x3D
101
102#define E_1 0x40
103#define E_2 0x41
104#define E_3 0x42
105#define E_4 0x43
106#define E_5 0x44
107#define E_6 0x45
108#define E_7 0x48
109#define E_8 0x49
110#define E_9 0x4A
111#define E_10 0x4B
112#define E_11 0x4C
113#define E_12 0x4D
114
115#define F_1 0x50
116#define F_2 0x51
117#define F_3 0x52
118#define F_4 0x53
119#define F_5 0x54
120#define F_6 0x55
121#define F_7 0x58
122#define F_8 0x59
123#define F_9 0x5A
124#define F_10 0x5B
125#define F_11 0x5C
126#define F_12 0x5D
127
128#define G_1 0x60
129#define G_2 0x61
130#define G_3 0x62
131#define G_4 0x63
132#define G_5 0x64
133#define G_6 0x65
134#define G_7 0x68
135#define G_8 0x69
136#define G_9 0x6A
137#define G_10 0x6B
138#define G_11 0x6C
139#define G_12 0x6D
140
141#define H_1 0x70
142#define H_2 0x71
143#define H_3 0x72
144#define H_4 0x73
145#define H_5 0x74
146#define H_6 0x75
147#define H_7 0x78
148#define H_8 0x79
149#define H_9 0x7A
150#define H_10 0x7B
151#define H_11 0x7C
152#define H_12 0x7D
153
154#define I_1 0x80
155#define I_2 0x81
156#define I_3 0x82
157#define I_4 0x83
158#define I_5 0x84
159#define I_6 0x85
160#define I_7 0x88
161#define I_8 0x89
162#define I_9 0x8A
163#define I_10 0x8B
164#define I_11 0x8C
165#define I_12 0x8D
166
167#define J_1 0x90
168#define J_2 0x91
169#define J_3 0x92
170#define J_4 0x93
171#define J_5 0x94
172#define J_6 0x95
173#define J_7 0x98
174#define J_8 0x99
175#define J_9 0x9A
176#define J_10 0x9B
177#define J_11 0x9C
178#define J_12 0x9D
179
180#define K_1 0xA0
181#define K_2 0xA1
182#define K_3 0xA2
183#define K_4 0xA3
184#define K_5 0xA4
185#define K_6 0xA5
186#define K_7 0xA8
187#define K_8 0xA9
188#define K_9 0xAA
189#define K_10 0xAB
190#define K_11 0xAC
191#define K_12 0xAD
192
193#define L_1 0xB0
194#define L_2 0xB1
195#define L_3 0xB2
196#define L_4 0xB3
197#define L_5 0xB4
198#define L_6 0xB5
199#define L_7 0xB8
200#define L_8 0xB9
201#define L_9 0xBA
202#define L_10 0xBB
203#define L_11 0xBC
204#define L_12 0xBD
diff --git a/drivers/led/issi/is31fl3741.c b/drivers/led/issi/is31fl3741.c
new file mode 100644
index 000000000..1b533c9b6
--- /dev/null
+++ b/drivers/led/issi/is31fl3741.c
@@ -0,0 +1,255 @@
1/* Copyright 2017 Jason Williams
2 * Copyright 2018 Jack Humbert
3 * Copyright 2018 Yiancar
4 * Copyright 2020 MelGeek
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "wait.h"
21
22#include "is31fl3741.h"
23#include <string.h>
24#include "i2c_master.h"
25#include "progmem.h"
26
27// This is a 7-bit address, that gets left-shifted and bit 0
28// set to 0 for write, 1 for read (as per I2C protocol)
29// The address will vary depending on your wiring:
30// 00 <-> GND
31// 01 <-> SCL
32// 10 <-> SDA
33// 11 <-> VCC
34// ADDR1 represents A1:A0 of the 7-bit address.
35// ADDR2 represents A3:A2 of the 7-bit address.
36// The result is: 0b101(ADDR2)(ADDR1)
37#define ISSI_ADDR_DEFAULT 0x60
38
39#define ISSI_COMMANDREGISTER 0xFD
40#define ISSI_COMMANDREGISTER_WRITELOCK 0xFE
41#define ISSI_INTERRUPTMASKREGISTER 0xF0
42#define ISSI_INTERRUPTSTATUSREGISTER 0xF1
43#define ISSI_IDREGISTER 0xFC
44
45#define ISSI_PAGE_PWM0 0x00 // PG0
46#define ISSI_PAGE_PWM1 0x01 // PG1
47#define ISSI_PAGE_SCALING_0 0x02 // PG2
48#define ISSI_PAGE_SCALING_1 0x03 // PG3
49#define ISSI_PAGE_FUNCTION 0x04 // PG4
50
51#define ISSI_REG_CONFIGURATION 0x00 // PG4
52#define ISSI_REG_GLOBALCURRENT 0x01 // PG4
53#define ISSI_REG_PULLDOWNUP 0x02 // PG4
54#define ISSI_REG_RESET 0x3F // PG4
55
56#ifndef ISSI_TIMEOUT
57# define ISSI_TIMEOUT 100
58#endif
59
60#ifndef ISSI_PERSISTENCE
61# define ISSI_PERSISTENCE 0
62#endif
63
64#define ISSI_MAX_LEDS 351
65
66// Transfer buffer for TWITransmitData()
67uint8_t g_twi_transfer_buffer[20] = {0xFF};
68
69// These buffers match the IS31FL3741 and IS31FL3741A PWM registers.
70// The scaling buffers match the PG2 and PG3 LED On/Off registers.
71// Storing them like this is optimal for I2C transfers to the registers.
72// We could optimize this and take out the unused registers from these
73// buffers and the transfers in IS31FL3741_write_pwm_buffer() but it's
74// probably not worth the extra complexity.
75uint8_t g_pwm_buffer[DRIVER_COUNT][ISSI_MAX_LEDS];
76bool g_pwm_buffer_update_required = false;
77bool g_scaling_registers_update_required[DRIVER_COUNT] = {false};
78
79uint8_t g_scaling_registers[DRIVER_COUNT][ISSI_MAX_LEDS];
80
81void IS31FL3741_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
82 g_twi_transfer_buffer[0] = reg;
83 g_twi_transfer_buffer[1] = data;
84
85#if ISSI_PERSISTENCE > 0
86 for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
87 if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) == 0) break;
88 }
89#else
90 i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT);
91#endif
92}
93
94bool IS31FL3741_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
95 // unlock the command register and select PG2
96 IS31FL3741_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
97 IS31FL3741_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM0);
98
99 for (int i = 0; i < 342; i += 18) {
100 if (i == 180) {
101 // unlock the command register and select PG2
102 IS31FL3741_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
103 IS31FL3741_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM1);
104 }
105
106 g_twi_transfer_buffer[0] = i % 180;
107 memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 18);
108
109#if ISSI_PERSISTENCE > 0
110 for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
111 if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 19, ISSI_TIMEOUT) != 0) {
112 return false;
113 }
114 }
115#else
116 if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 19, ISSI_TIMEOUT) != 0) {
117 return false;
118 }
119#endif
120 }
121
122 // transfer the left cause the total number is 351
123 g_twi_transfer_buffer[0] = 162;
124 memcpy(g_twi_transfer_buffer + 1, pwm_buffer + 342, 9);
125
126#if ISSI_PERSISTENCE > 0
127 for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
128 if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 10, ISSI_TIMEOUT) != 0) {
129 return false;
130 }
131 }
132#else
133 if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 10, ISSI_TIMEOUT) != 0) {
134 return false;
135 }
136#endif
137
138 return true;
139}
140
141void IS31FL3741_init(uint8_t addr) {
142 // In order to avoid the LEDs being driven with garbage data
143 // in the LED driver's PWM registers, shutdown is enabled last.
144 // Set up the mode and other settings, clear the PWM registers,
145 // then disable software shutdown.
146 // Unlock the command register.
147
148 // Unlock the command register.
149 IS31FL3741_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
150
151 // Select PG4
152 IS31FL3741_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_FUNCTION);
153
154 // Set to Normal operation
155 IS31FL3741_write_register(addr, ISSI_REG_CONFIGURATION, 0x01);
156
157 // Set Golbal Current Control Register
158 IS31FL3741_write_register(addr, ISSI_REG_GLOBALCURRENT, 0xFF);
159 // Set Pull up & Down for SWx CSy
160 IS31FL3741_write_register(addr, ISSI_REG_PULLDOWNUP, 0x77);
161
162 // IS31FL3741_update_led_scaling_registers(addr, 0xFF, 0xFF, 0xFF);
163
164 // Wait 10ms to ensure the device has woken up.
165 wait_ms(10);
166}
167
168void IS31FL3741_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
169 if (index >= 0 && index < DRIVER_LED_TOTAL) {
170 is31_led led = g_is31_leds[index];
171
172 g_pwm_buffer[led.driver][led.r] = red;
173 g_pwm_buffer[led.driver][led.g] = green;
174 g_pwm_buffer[led.driver][led.b] = blue;
175 g_pwm_buffer_update_required = true;
176 }
177}
178
179void IS31FL3741_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
180 for (int i = 0; i < DRIVER_LED_TOTAL; i++) {
181 IS31FL3741_set_color(i, red, green, blue);
182 }
183}
184
185void IS31FL3741_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
186 is31_led led = g_is31_leds[index];
187
188 if (red) {
189 g_scaling_registers[led.driver][led.r] = 0xFF;
190 } else {
191 g_scaling_registers[led.driver][led.r] = 0x00;
192 }
193
194 if (green) {
195 g_scaling_registers[led.driver][led.g] = 0xFF;
196 } else {
197 g_scaling_registers[led.driver][led.g] = 0x00;
198 }
199
200 if (blue) {
201 g_scaling_registers[led.driver][led.b] = 0xFF;
202 } else {
203 g_scaling_registers[led.driver][led.b] = 0x00;
204 }
205
206 g_scaling_registers_update_required[led.driver] = true;
207}
208
209void IS31FL3741_update_pwm_buffers(uint8_t addr1, uint8_t addr2) {
210 if (g_pwm_buffer_update_required) {
211 IS31FL3741_write_pwm_buffer(addr1, g_pwm_buffer[0]);
212 }
213
214 g_pwm_buffer_update_required = false;
215}
216
217void IS31FL3741_set_pwm_buffer(const is31_led *pled, uint8_t red, uint8_t green, uint8_t blue) {
218 g_pwm_buffer[pled->driver][pled->r] = red;
219 g_pwm_buffer[pled->driver][pled->g] = green;
220 g_pwm_buffer[pled->driver][pled->b] = blue;
221
222 g_pwm_buffer_update_required = true;
223}
224
225void IS31FL3741_update_led_control_registers(uint8_t addr, uint8_t index) {
226 if (g_scaling_registers_update_required[index]) {
227 // unlock the command register and select PG2
228 IS31FL3741_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
229 IS31FL3741_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_SCALING_0);
230
231 // CS1_SW1 to CS30_SW6 are on PG2
232 for (int i = CS1_SW1; i <= CS30_SW6; ++i) {
233 IS31FL3741_write_register(addr, i, g_scaling_registers[0][i]);
234 }
235
236 // unlock the command register and select PG3
237 IS31FL3741_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
238 IS31FL3741_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_SCALING_1);
239
240 // CS1_SW7 to CS39_SW9 are on PG3
241 for (int i = CS1_SW7; i <= CS39_SW9; ++i) {
242 IS31FL3741_write_register(addr, i - CS1_SW7, g_scaling_registers[0][i]);
243 }
244
245 g_scaling_registers_update_required[index] = false;
246 }
247}
248
249void IS31FL3741_set_scaling_registers(const is31_led *pled, uint8_t red, uint8_t green, uint8_t blue) {
250 g_scaling_registers[pled->driver][pled->r] = red;
251 g_scaling_registers[pled->driver][pled->g] = green;
252 g_scaling_registers[pled->driver][pled->b] = blue;
253
254 g_scaling_registers_update_required[pled->driver] = true;
255}
diff --git a/drivers/led/issi/is31fl3741.h b/drivers/led/issi/is31fl3741.h
new file mode 100644
index 000000000..cea6761ca
--- /dev/null
+++ b/drivers/led/issi/is31fl3741.h
@@ -0,0 +1,421 @@
1/* Copyright 2017 Jason Williams
2 * Copyright 2018 Jack Humbert
3 * Copyright 2018 Yiancar
4 * Copyright 2020 MelGeek
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#pragma once
21
22#include <stdint.h>
23#include <stdbool.h>
24#include "progmem.h"
25
26typedef struct is31_led {
27 uint32_t driver : 2;
28 uint32_t r : 10;
29 uint32_t g : 10;
30 uint32_t b : 10;
31} __attribute__((packed)) is31_led;
32
33extern const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL];
34
35void IS31FL3741_init(uint8_t addr);
36void IS31FL3741_write_register(uint8_t addr, uint8_t reg, uint8_t data);
37bool IS31FL3741_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
38
39void IS31FL3741_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
40void IS31FL3741_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
41
42void IS31FL3741_set_led_control_register(uint8_t index, bool red, bool green, bool blue);
43
44// This should not be called from an interrupt
45// (eg. from a timer interrupt).
46// Call this while idle (in between matrix scans).
47// If the buffer is dirty, it will update the driver with the buffer.
48void IS31FL3741_update_pwm_buffers(uint8_t addr1, uint8_t addr2);
49void IS31FL3741_update_led_control_registers(uint8_t addr1, uint8_t addr2);
50void IS31FL3741_set_scaling_registers(const is31_led *pled, uint8_t red, uint8_t green, uint8_t blue);
51
52void IS31FL3741_set_pwm_buffer(const is31_led *pled, uint8_t red, uint8_t green, uint8_t blue);
53
54#define CS1_SW1 0x00
55#define CS2_SW1 0x01
56#define CS3_SW1 0x02
57#define CS4_SW1 0x03
58#define CS5_SW1 0x04
59#define CS6_SW1 0x05
60#define CS7_SW1 0x06
61#define CS8_SW1 0x07
62#define CS9_SW1 0x08
63#define CS10_SW1 0x09
64#define CS11_SW1 0x0A
65#define CS12_SW1 0x0B
66#define CS13_SW1 0x0C
67#define CS14_SW1 0x0D
68#define CS15_SW1 0x0E
69#define CS16_SW1 0x0F
70#define CS17_SW1 0x10
71#define CS18_SW1 0x11
72#define CS19_SW1 0x12
73#define CS20_SW1 0x13
74#define CS21_SW1 0x14
75#define CS22_SW1 0x15
76#define CS23_SW1 0x16
77#define CS24_SW1 0x17
78#define CS25_SW1 0x18
79#define CS26_SW1 0x19
80#define CS27_SW1 0x1A
81#define CS28_SW1 0x1B
82#define CS29_SW1 0x1C
83#define CS30_SW1 0x1D
84
85#define CS1_SW2 0x1E
86#define CS2_SW2 0x1F
87#define CS3_SW2 0x20
88#define CS4_SW2 0x21
89#define CS5_SW2 0x22
90#define CS6_SW2 0x23
91#define CS7_SW2 0x24
92#define CS8_SW2 0x25
93#define CS9_SW2 0x26
94#define CS10_SW2 0x27
95#define CS11_SW2 0x28
96#define CS12_SW2 0x29
97#define CS13_SW2 0x2A
98#define CS14_SW2 0x2B
99#define CS15_SW2 0x2C
100#define CS16_SW2 0x2D
101#define CS17_SW2 0x2E
102#define CS18_SW2 0x2F
103#define CS19_SW2 0x30
104#define CS20_SW2 0x31
105#define CS21_SW2 0x32
106#define CS22_SW2 0x33
107#define CS23_SW2 0x34
108#define CS24_SW2 0x35
109#define CS25_SW2 0x36
110#define CS26_SW2 0x37
111#define CS27_SW2 0x38
112#define CS28_SW2 0x39
113#define CS29_SW2 0x3A
114#define CS30_SW2 0x3B
115
116#define CS1_SW3 0x3C
117#define CS2_SW3 0x3D
118#define CS3_SW3 0x3E
119#define CS4_SW3 0x3F
120#define CS5_SW3 0x40
121#define CS6_SW3 0x41
122#define CS7_SW3 0x42
123#define CS8_SW3 0x43
124#define CS9_SW3 0x44
125#define CS10_SW3 0x45
126#define CS11_SW3 0x46
127#define CS12_SW3 0x47
128#define CS13_SW3 0x48
129#define CS14_SW3 0x49
130#define CS15_SW3 0x4A
131#define CS16_SW3 0x4B
132#define CS17_SW3 0x4C
133#define CS18_SW3 0x4D
134#define CS19_SW3 0x4E
135#define CS20_SW3 0x4F
136#define CS21_SW3 0x50
137#define CS22_SW3 0x51
138#define CS23_SW3 0x52
139#define CS24_SW3 0x53
140#define CS25_SW3 0x54
141#define CS26_SW3 0x55
142#define CS27_SW3 0x56
143#define CS28_SW3 0x57
144#define CS29_SW3 0x58
145#define CS30_SW3 0x59
146
147#define CS1_SW4 0x5A
148#define CS2_SW4 0x5B
149#define CS3_SW4 0x5C
150#define CS4_SW4 0x5D
151#define CS5_SW4 0x5E
152#define CS6_SW4 0x5F
153#define CS7_SW4 0x60
154#define CS8_SW4 0x61
155#define CS9_SW4 0x62
156#define CS10_SW4 0x63
157#define CS11_SW4 0x64
158#define CS12_SW4 0x65
159#define CS13_SW4 0x66
160#define CS14_SW4 0x67
161#define CS15_SW4 0x68
162#define CS16_SW4 0x69
163#define CS17_SW4 0x6A
164#define CS18_SW4 0x6B
165#define CS19_SW4 0x6C
166#define CS20_SW4 0x6D
167#define CS21_SW4 0x6E
168#define CS22_SW4 0x6F
169#define CS23_SW4 0x70
170#define CS24_SW4 0x71
171#define CS25_SW4 0x72
172#define CS26_SW4 0x73
173#define CS27_SW4 0x74
174#define CS28_SW4 0x75
175#define CS29_SW4 0x76
176#define CS30_SW4 0x77
177
178#define CS1_SW5 0x78
179#define CS2_SW5 0x79
180#define CS3_SW5 0x7A
181#define CS4_SW5 0x7B
182#define CS5_SW5 0x7C
183#define CS6_SW5 0x7D
184#define CS7_SW5 0x7E
185#define CS8_SW5 0x7F
186#define CS9_SW5 0x80
187#define CS10_SW5 0x81
188#define CS11_SW5 0x82
189#define CS12_SW5 0x83
190#define CS13_SW5 0x84
191#define CS14_SW5 0x85
192#define CS15_SW5 0x86
193#define CS16_SW5 0x87
194#define CS17_SW5 0x88
195#define CS18_SW5 0x89
196#define CS19_SW5 0x8A
197#define CS20_SW5 0x8B
198#define CS21_SW5 0x8C
199#define CS22_SW5 0x8D
200#define CS23_SW5 0x8E
201#define CS24_SW5 0x8F
202#define CS25_SW5 0x90
203#define CS26_SW5 0x91
204#define CS27_SW5 0x92
205#define CS28_SW5 0x93
206#define CS29_SW5 0x94
207#define CS30_SW5 0x95
208
209#define CS1_SW6 0x96
210#define CS2_SW6 0x97
211#define CS3_SW6 0x98
212#define CS4_SW6 0x99
213#define CS5_SW6 0x9A
214#define CS6_SW6 0x9B
215#define CS7_SW6 0x9C
216#define CS8_SW6 0x9D
217#define CS9_SW6 0x9E
218#define CS10_SW6 0x9F
219#define CS11_SW6 0xA0
220#define CS12_SW6 0xA1
221#define CS13_SW6 0xA2
222#define CS14_SW6 0xA3
223#define CS15_SW6 0xA4
224#define CS16_SW6 0xA5
225#define CS17_SW6 0xA6
226#define CS18_SW6 0xA7
227#define CS19_SW6 0xA8
228#define CS20_SW6 0xA9
229#define CS21_SW6 0xAA
230#define CS22_SW6 0xAB
231#define CS23_SW6 0xAC
232#define CS24_SW6 0xAD
233#define CS25_SW6 0xAE
234#define CS26_SW6 0xAF
235#define CS27_SW6 0xB0
236#define CS28_SW6 0xB1
237#define CS29_SW6 0xB2
238#define CS30_SW6 0xB3
239
240#define CS1_SW7 0xB4
241#define CS2_SW7 0xB5
242#define CS3_SW7 0xB6
243#define CS4_SW7 0xB7
244#define CS5_SW7 0xB8
245#define CS6_SW7 0xB9
246#define CS7_SW7 0xBA
247#define CS8_SW7 0xBB
248#define CS9_SW7 0xBC
249#define CS10_SW7 0xBD
250#define CS11_SW7 0xBE
251#define CS12_SW7 0xBF
252#define CS13_SW7 0xC0
253#define CS14_SW7 0xC1
254#define CS15_SW7 0xC2
255#define CS16_SW7 0xC3
256#define CS17_SW7 0xC4
257#define CS18_SW7 0xC5
258#define CS19_SW7 0xC6
259#define CS20_SW7 0xC7
260#define CS21_SW7 0xC8
261#define CS22_SW7 0xC9
262#define CS23_SW7 0xCA
263#define CS24_SW7 0xCB
264#define CS25_SW7 0xCC
265#define CS26_SW7 0xCD
266#define CS27_SW7 0xCE
267#define CS28_SW7 0xCF
268#define CS29_SW7 0xD0
269#define CS30_SW7 0xD1
270
271#define CS1_SW8 0xD2
272#define CS2_SW8 0xD3
273#define CS3_SW8 0xD4
274#define CS4_SW8 0xD5
275#define CS5_SW8 0xD6
276#define CS6_SW8 0xD7
277#define CS7_SW8 0xD8
278#define CS8_SW8 0xD9
279#define CS9_SW8 0xDA
280#define CS10_SW8 0xDB
281#define CS11_SW8 0xDC
282#define CS12_SW8 0xDD
283#define CS13_SW8 0xDE
284#define CS14_SW8 0xDF
285#define CS15_SW8 0xE0
286#define CS16_SW8 0xE1
287#define CS17_SW8 0xE2
288#define CS18_SW8 0xE3
289#define CS19_SW8 0xE4
290#define CS20_SW8 0xE5
291#define CS21_SW8 0xE6
292#define CS22_SW8 0xE7
293#define CS23_SW8 0xE8
294#define CS24_SW8 0xE9
295#define CS25_SW8 0xEA
296#define CS26_SW8 0xEB
297#define CS27_SW8 0xEC
298#define CS28_SW8 0xED
299#define CS29_SW8 0xEE
300#define CS30_SW8 0xEF
301
302#define CS1_SW9 0xF0
303#define CS2_SW9 0xF1
304#define CS3_SW9 0xF2
305#define CS4_SW9 0xF3
306#define CS5_SW9 0xF4
307#define CS6_SW9 0xF5
308#define CS7_SW9 0xF6
309#define CS8_SW9 0xF7
310#define CS9_SW9 0xF8
311#define CS10_SW9 0xF9
312#define CS11_SW9 0xFA
313#define CS12_SW9 0xFB
314#define CS13_SW9 0xFC
315#define CS14_SW9 0xFD
316#define CS15_SW9 0xFE
317#define CS16_SW9 0xFF
318#define CS17_SW9 0x100
319#define CS18_SW9 0x101
320#define CS19_SW9 0x102
321#define CS20_SW9 0x103
322#define CS21_SW9 0x104
323#define CS22_SW9 0x105
324#define CS23_SW9 0x106
325#define CS24_SW9 0x107
326#define CS25_SW9 0x108
327#define CS26_SW9 0x109
328#define CS27_SW9 0x10A
329#define CS28_SW9 0x10B
330#define CS29_SW9 0x10C
331#define CS30_SW9 0x10D
332
333#define CS31_SW1 0x10E
334#define CS32_SW1 0x10F
335#define CS33_SW1 0x110
336#define CS34_SW1 0x111
337#define CS35_SW1 0x112
338#define CS36_SW1 0x113
339#define CS37_SW1 0x114
340#define CS38_SW1 0x115
341#define CS39_SW1 0x116
342
343#define CS31_SW2 0x117
344#define CS32_SW2 0x118
345#define CS33_SW2 0x119
346#define CS34_SW2 0x11A
347#define CS35_SW2 0x11B
348#define CS36_SW2 0x11C
349#define CS37_SW2 0x11D
350#define CS38_SW2 0x11E
351#define CS39_SW2 0x11F
352
353#define CS31_SW3 0x120
354#define CS32_SW3 0x121
355#define CS33_SW3 0x122
356#define CS34_SW3 0x123
357#define CS35_SW3 0x124
358#define CS36_SW3 0x125
359#define CS37_SW3 0x126
360#define CS38_SW3 0x127
361#define CS39_SW3 0x128
362
363#define CS31_SW4 0x129
364#define CS32_SW4 0x12A
365#define CS33_SW4 0x12B
366#define CS34_SW4 0x12C
367#define CS35_SW4 0x12D
368#define CS36_SW4 0x12E
369#define CS37_SW4 0x12F
370#define CS38_SW4 0x130
371#define CS39_SW4 0x131
372
373#define CS31_SW5 0x132
374#define CS32_SW5 0x133
375#define CS33_SW5 0x134
376#define CS34_SW5 0x135
377#define CS35_SW5 0x136
378#define CS36_SW5 0x137
379#define CS37_SW5 0x138
380#define CS38_SW5 0x139
381#define CS39_SW5 0x13A
382
383#define CS31_SW6 0x13B
384#define CS32_SW6 0x13C
385#define CS33_SW6 0x13D
386#define CS34_SW6 0x13E
387#define CS35_SW6 0x13F
388#define CS36_SW6 0x140
389#define CS37_SW6 0x141
390#define CS38_SW6 0x142
391#define CS39_SW6 0x143
392
393#define CS31_SW7 0x144
394#define CS32_SW7 0x145
395#define CS33_SW7 0x146
396#define CS34_SW7 0x147
397#define CS35_SW7 0x148
398#define CS36_SW7 0x149
399#define CS37_SW7 0x14A
400#define CS38_SW7 0x14B
401#define CS39_SW7 0x14C
402
403#define CS31_SW8 0x14D
404#define CS32_SW8 0x14E
405#define CS33_SW8 0x14F
406#define CS34_SW8 0x150
407#define CS35_SW8 0x151
408#define CS36_SW8 0x152
409#define CS37_SW8 0x153
410#define CS38_SW8 0x154
411#define CS39_SW8 0x155
412
413#define CS31_SW9 0x156
414#define CS32_SW9 0x157
415#define CS33_SW9 0x158
416#define CS34_SW9 0x159
417#define CS35_SW9 0x15A
418#define CS36_SW9 0x15B
419#define CS37_SW9 0x15C
420#define CS38_SW9 0x15D
421#define CS39_SW9 0x15E