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.h252
-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.h207
-rw-r--r--drivers/led/issi/is31fl3731.c237
-rw-r--r--drivers/led/issi/is31fl3731.h208
-rw-r--r--drivers/led/issi/is31fl3733.c237
-rw-r--r--drivers/led/issi/is31fl3733.h251
-rw-r--r--drivers/led/issi/is31fl3736.c269
-rw-r--r--drivers/led/issi/is31fl3736.h168
-rw-r--r--drivers/led/issi/is31fl3737.c227
-rw-r--r--drivers/led/issi/is31fl3737.h203
-rw-r--r--drivers/led/issi/is31fl3741.c255
-rw-r--r--drivers/led/issi/is31fl3741.h420
18 files changed, 3621 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..c55d9605f
--- /dev/null
+++ b/drivers/led/aw20216.h
@@ -0,0 +1,252 @@
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 "gpio.h"
22
23typedef struct aw_led {
24 uint8_t driver : 2;
25 uint8_t r;
26 uint8_t g;
27 uint8_t b;
28} aw_led;
29
30extern const aw_led g_aw_leds[DRIVER_LED_TOTAL];
31
32void AW20216_init(pin_t cs_pin, pin_t en_pin);
33void AW20216_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
34void AW20216_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
35void AW20216_update_pwm_buffers(pin_t cs_pin, uint8_t index);
36
37#define CS1_SW1 0x00
38#define CS2_SW1 0x01
39#define CS3_SW1 0x02
40#define CS4_SW1 0x03
41#define CS5_SW1 0x04
42#define CS6_SW1 0x05
43#define CS7_SW1 0x06
44#define CS8_SW1 0x07
45#define CS9_SW1 0x08
46#define CS10_SW1 0x09
47#define CS11_SW1 0x0A
48#define CS12_SW1 0x0B
49#define CS13_SW1 0x0C
50#define CS14_SW1 0x0D
51#define CS15_SW1 0x0E
52#define CS16_SW1 0x0F
53#define CS17_SW1 0x10
54#define CS18_SW1 0x11
55#define CS1_SW2 0x12
56#define CS2_SW2 0x13
57#define CS3_SW2 0x14
58#define CS4_SW2 0x15
59#define CS5_SW2 0x16
60#define CS6_SW2 0x17
61#define CS7_SW2 0x18
62#define CS8_SW2 0x19
63#define CS9_SW2 0x1A
64#define CS10_SW2 0x1B
65#define CS11_SW2 0x1C
66#define CS12_SW2 0x1D
67#define CS13_SW2 0x1E
68#define CS14_SW2 0x1F
69#define CS15_SW2 0x20
70#define CS16_SW2 0x21
71#define CS17_SW2 0x22
72#define CS18_SW2 0x23
73#define CS1_SW3 0x24
74#define CS2_SW3 0x25
75#define CS3_SW3 0x26
76#define CS4_SW3 0x27
77#define CS5_SW3 0x28
78#define CS6_SW3 0x29
79#define CS7_SW3 0x2A
80#define CS8_SW3 0x2B
81#define CS9_SW3 0x2C
82#define CS10_SW3 0x2D
83#define CS11_SW3 0x2E
84#define CS12_SW3 0x2F
85#define CS13_SW3 0x30
86#define CS14_SW3 0x31
87#define CS15_SW3 0x32
88#define CS16_SW3 0x33
89#define CS17_SW3 0x34
90#define CS18_SW3 0x35
91#define CS1_SW4 0x36
92#define CS2_SW4 0x37
93#define CS3_SW4 0x38
94#define CS4_SW4 0x39
95#define CS5_SW4 0x3A
96#define CS6_SW4 0x3B
97#define CS7_SW4 0x3C
98#define CS8_SW4 0x3D
99#define CS9_SW4 0x3E
100#define CS10_SW4 0x3F
101#define CS11_SW4 0x40
102#define CS12_SW4 0x41
103#define CS13_SW4 0x42
104#define CS14_SW4 0x43
105#define CS15_SW4 0x44
106#define CS16_SW4 0x45
107#define CS17_SW4 0x46
108#define CS18_SW4 0x47
109#define CS1_SW5 0x48
110#define CS2_SW5 0x49
111#define CS3_SW5 0x4A
112#define CS4_SW5 0x4B
113#define CS5_SW5 0x4C
114#define CS6_SW5 0x4D
115#define CS7_SW5 0x4E
116#define CS8_SW5 0x4F
117#define CS9_SW5 0x50
118#define CS10_SW5 0x51
119#define CS11_SW5 0x52
120#define CS12_SW5 0x53
121#define CS13_SW5 0x54
122#define CS14_SW5 0x55
123#define CS15_SW5 0x56
124#define CS16_SW5 0x57
125#define CS17_SW5 0x58
126#define CS18_SW5 0x59
127#define CS1_SW6 0x5A
128#define CS2_SW6 0x5B
129#define CS3_SW6 0x5C
130#define CS4_SW6 0x5D
131#define CS5_SW6 0x5E
132#define CS6_SW6 0x5F
133#define CS7_SW6 0x60
134#define CS8_SW6 0x61
135#define CS9_SW6 0x62
136#define CS10_SW6 0x63
137#define CS11_SW6 0x64
138#define CS12_SW6 0x65
139#define CS13_SW6 0x66
140#define CS14_SW6 0x67
141#define CS15_SW6 0x68
142#define CS16_SW6 0x69
143#define CS17_SW6 0x6A
144#define CS18_SW6 0x6B
145#define CS1_SW7 0x6C
146#define CS2_SW7 0x6D
147#define CS3_SW7 0x6E
148#define CS4_SW7 0x6F
149#define CS5_SW7 0x70
150#define CS6_SW7 0x71
151#define CS7_SW7 0x72
152#define CS8_SW7 0x73
153#define CS9_SW7 0x74
154#define CS10_SW7 0x75
155#define CS11_SW7 0x76
156#define CS12_SW7 0x77
157#define CS13_SW7 0x78
158#define CS14_SW7 0x79
159#define CS15_SW7 0x7A
160#define CS16_SW7 0x7B
161#define CS17_SW7 0x7C
162#define CS18_SW7 0x7D
163#define CS1_SW8 0x7E
164#define CS2_SW8 0x7F
165#define CS3_SW8 0x80
166#define CS4_SW8 0x81
167#define CS5_SW8 0x82
168#define CS6_SW8 0x83
169#define CS7_SW8 0x84
170#define CS8_SW8 0x85
171#define CS9_SW8 0x86
172#define CS10_SW8 0x87
173#define CS11_SW8 0x88
174#define CS12_SW8 0x89
175#define CS13_SW8 0x8A
176#define CS14_SW8 0x8B
177#define CS15_SW8 0x8C
178#define CS16_SW8 0x8D
179#define CS17_SW8 0x8E
180#define CS18_SW8 0x8F
181#define CS1_SW9 0x90
182#define CS2_SW9 0x91
183#define CS3_SW9 0x92
184#define CS4_SW9 0x93
185#define CS5_SW9 0x94
186#define CS6_SW9 0x95
187#define CS7_SW9 0x96
188#define CS8_SW9 0x97
189#define CS9_SW9 0x98
190#define CS10_SW9 0x99
191#define CS11_SW9 0x9A
192#define CS12_SW9 0x9B
193#define CS13_SW9 0x9C
194#define CS14_SW9 0x9D
195#define CS15_SW9 0x9E
196#define CS16_SW9 0x9F
197#define CS17_SW9 0xA0
198#define CS18_SW9 0xA1
199#define CS1_SW10 0xA2
200#define CS2_SW10 0xA3
201#define CS3_SW10 0xA4
202#define CS4_SW10 0xA5
203#define CS5_SW10 0xA6
204#define CS6_SW10 0xA7
205#define CS7_SW10 0xA8
206#define CS8_SW10 0xA9
207#define CS9_SW10 0xAA
208#define CS10_SW10 0xAB
209#define CS11_SW10 0xAC
210#define CS12_SW10 0xAD
211#define CS13_SW10 0xAE
212#define CS14_SW10 0xAF
213#define CS15_SW10 0xB0
214#define CS16_SW10 0xB1
215#define CS17_SW10 0xB2
216#define CS18_SW10 0xB3
217#define CS1_SW11 0xB4
218#define CS2_SW11 0xB5
219#define CS3_SW11 0xB6
220#define CS4_SW11 0xB7
221#define CS5_SW11 0xB8
222#define CS6_SW11 0xB9
223#define CS7_SW11 0xBA
224#define CS8_SW11 0xBB
225#define CS9_SW11 0xBC
226#define CS10_SW11 0xBD
227#define CS11_SW11 0xBE
228#define CS12_SW11 0xBF
229#define CS13_SW11 0xC0
230#define CS14_SW11 0xC1
231#define CS15_SW11 0xC2
232#define CS16_SW11 0xC3
233#define CS17_SW11 0xC4
234#define CS18_SW11 0xC5
235#define CS1_SW12 0xC6
236#define CS2_SW12 0xC7
237#define CS3_SW12 0xC8
238#define CS4_SW12 0xC9
239#define CS5_SW12 0xCA
240#define CS6_SW12 0xCB
241#define CS7_SW12 0xCC
242#define CS8_SW12 0xCD
243#define CS9_SW12 0xCE
244#define CS10_SW12 0xCF
245#define CS11_SW12 0xD0
246#define CS12_SW12 0xD1
247#define CS13_SW12 0xD2
248#define CS14_SW12 0xD3
249#define CS15_SW12 0xD4
250#define CS16_SW12 0xD5
251#define CS17_SW12 0xD6
252#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..9665d6ed3
--- /dev/null
+++ b/drivers/led/issi/is31fl3731-simple.h
@@ -0,0 +1,207 @@
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
24typedef struct is31_led {
25 uint8_t driver : 2;
26 uint8_t v;
27} __attribute__((packed)) is31_led;
28
29extern const is31_led g_is31_leds[DRIVER_LED_TOTAL];
30
31void IS31FL3731_init(uint8_t addr);
32void IS31FL3731_write_register(uint8_t addr, uint8_t reg, uint8_t data);
33void IS31FL3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
34
35void IS31FL3731_set_value(int index, uint8_t value);
36void IS31FL3731_set_value_all(uint8_t value);
37
38void IS31FL3731_set_led_control_register(uint8_t index, bool value);
39
40// This should not be called from an interrupt
41// (eg. from a timer interrupt).
42// Call this while idle (in between matrix scans).
43// If the buffer is dirty, it will update the driver with the buffer.
44void IS31FL3731_update_pwm_buffers(uint8_t addr, uint8_t index);
45void IS31FL3731_update_led_control_registers(uint8_t addr, uint8_t index);
46
47#define C1_1 0x24
48#define C1_2 0x25
49#define C1_3 0x26
50#define C1_4 0x27
51#define C1_5 0x28
52#define C1_6 0x29
53#define C1_7 0x2A
54#define C1_8 0x2B
55
56#define C1_9 0x2C
57#define C1_10 0x2D
58#define C1_11 0x2E
59#define C1_12 0x2F
60#define C1_13 0x30
61#define C1_14 0x31
62#define C1_15 0x32
63#define C1_16 0x33
64
65#define C2_1 0x34
66#define C2_2 0x35
67#define C2_3 0x36
68#define C2_4 0x37
69#define C2_5 0x38
70#define C2_6 0x39
71#define C2_7 0x3A
72#define C2_8 0x3B
73
74#define C2_9 0x3C
75#define C2_10 0x3D
76#define C2_11 0x3E
77#define C2_12 0x3F
78#define C2_13 0x40
79#define C2_14 0x41
80#define C2_15 0x42
81#define C2_16 0x43
82
83#define C3_1 0x44
84#define C3_2 0x45
85#define C3_3 0x46
86#define C3_4 0x47
87#define C3_5 0x48
88#define C3_6 0x49
89#define C3_7 0x4A
90#define C3_8 0x4B
91
92#define C3_9 0x4C
93#define C3_10 0x4D
94#define C3_11 0x4E
95#define C3_12 0x4F
96#define C3_13 0x50
97#define C3_14 0x51
98#define C3_15 0x52
99#define C3_16 0x53
100
101#define C4_1 0x54
102#define C4_2 0x55
103#define C4_3 0x56
104#define C4_4 0x57
105#define C4_5 0x58
106#define C4_6 0x59
107#define C4_7 0x5A
108#define C4_8 0x5B
109
110#define C4_9 0x5C
111#define C4_10 0x5D
112#define C4_11 0x5E
113#define C4_12 0x5F
114#define C4_13 0x60
115#define C4_14 0x61
116#define C4_15 0x62
117#define C4_16 0x63
118
119#define C5_1 0x64
120#define C5_2 0x65
121#define C5_3 0x66
122#define C5_4 0x67
123#define C5_5 0x68
124#define C5_6 0x69
125#define C5_7 0x6A
126#define C5_8 0x6B
127
128#define C5_9 0x6C
129#define C5_10 0x6D
130#define C5_11 0x6E
131#define C5_12 0x6F
132#define C5_13 0x70
133#define C5_14 0x71
134#define C5_15 0x72
135#define C5_16 0x73
136
137#define C6_1 0x74
138#define C6_2 0x75
139#define C6_3 0x76
140#define C6_4 0x77
141#define C6_5 0x78
142#define C6_6 0x79
143#define C6_7 0x7A
144#define C6_8 0x7B
145
146#define C6_9 0x7C
147#define C6_10 0x7D
148#define C6_11 0x7E
149#define C6_12 0x7F
150#define C6_13 0x80
151#define C6_14 0x81
152#define C6_15 0x82
153#define C6_16 0x83
154
155#define C7_1 0x84
156#define C7_2 0x85
157#define C7_3 0x86
158#define C7_4 0x87
159#define C7_5 0x88
160#define C7_6 0x89
161#define C7_7 0x8A
162#define C7_8 0x8B
163
164#define C7_9 0x8C
165#define C7_10 0x8D
166#define C7_11 0x8E
167#define C7_12 0x8F
168#define C7_13 0x90
169#define C7_14 0x91
170#define C7_15 0x92
171#define C7_16 0x93
172
173#define C8_1 0x94
174#define C8_2 0x95
175#define C8_3 0x96
176#define C8_4 0x97
177#define C8_5 0x98
178#define C8_6 0x99
179#define C8_7 0x9A
180#define C8_8 0x9B
181
182#define C8_9 0x9C
183#define C8_10 0x9D
184#define C8_11 0x9E
185#define C8_12 0x9F
186#define C8_13 0xA0
187#define C8_14 0xA1
188#define C8_15 0xA2
189#define C8_16 0xA3
190
191#define C9_1 0xA4
192#define C9_2 0xA5
193#define C9_3 0xA6
194#define C9_4 0xA7
195#define C9_5 0xA8
196#define C9_6 0xA9
197#define C9_7 0xAA
198#define C9_8 0xAB
199
200#define C9_9 0xAC
201#define C9_10 0xAD
202#define C9_11 0xAE
203#define C9_12 0xAF
204#define C9_13 0xB0
205#define C9_14 0xB1
206#define C9_15 0xB2
207#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..19e8e6251
--- /dev/null
+++ b/drivers/led/issi/is31fl3731.h
@@ -0,0 +1,208 @@
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
23typedef struct is31_led {
24 uint8_t driver : 2;
25 uint8_t r;
26 uint8_t g;
27 uint8_t b;
28} __attribute__((packed)) is31_led;
29
30extern const is31_led 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_color(int index, uint8_t red, uint8_t green, uint8_t blue);
37void IS31FL3731_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
38
39void IS31FL3731_set_led_control_register(uint8_t index, bool red, bool green, bool blue);
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/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..603d505a1
--- /dev/null
+++ b/drivers/led/issi/is31fl3733.h
@@ -0,0 +1,251 @@
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
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 g_is31_leds[DRIVER_LED_TOTAL];
32
33void IS31FL3733_init(uint8_t addr, uint8_t sync);
34bool IS31FL3733_write_register(uint8_t addr, uint8_t reg, uint8_t data);
35bool IS31FL3733_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
36
37void IS31FL3733_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
38void IS31FL3733_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
39
40void IS31FL3733_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 IS31FL3733_update_pwm_buffers(uint8_t addr, uint8_t index);
47void IS31FL3733_update_led_control_registers(uint8_t addr, uint8_t index);
48
49#define A_1 0x00
50#define A_2 0x01
51#define A_3 0x02
52#define A_4 0x03
53#define A_5 0x04
54#define A_6 0x05
55#define A_7 0x06
56#define A_8 0x07
57#define A_9 0x08
58#define A_10 0x09
59#define A_11 0x0A
60#define A_12 0x0B
61#define A_13 0x0C
62#define A_14 0x0D
63#define A_15 0x0E
64#define A_16 0x0F
65
66#define B_1 0x10
67#define B_2 0x11
68#define B_3 0x12
69#define B_4 0x13
70#define B_5 0x14
71#define B_6 0x15
72#define B_7 0x16
73#define B_8 0x17
74#define B_9 0x18
75#define B_10 0x19
76#define B_11 0x1A
77#define B_12 0x1B
78#define B_13 0x1C
79#define B_14 0x1D
80#define B_15 0x1E
81#define B_16 0x1F
82
83#define C_1 0x20
84#define C_2 0x21
85#define C_3 0x22
86#define C_4 0x23
87#define C_5 0x24
88#define C_6 0x25
89#define C_7 0x26
90#define C_8 0x27
91#define C_9 0x28
92#define C_10 0x29
93#define C_11 0x2A
94#define C_12 0x2B
95#define C_13 0x2C
96#define C_14 0x2D
97#define C_15 0x2E
98#define C_16 0x2F
99
100#define D_1 0x30
101#define D_2 0x31
102#define D_3 0x32
103#define D_4 0x33
104#define D_5 0x34
105#define D_6 0x35
106#define D_7 0x36
107#define D_8 0x37
108#define D_9 0x38
109#define D_10 0x39
110#define D_11 0x3A
111#define D_12 0x3B
112#define D_13 0x3C
113#define D_14 0x3D
114#define D_15 0x3E
115#define D_16 0x3F
116
117#define E_1 0x40
118#define E_2 0x41
119#define E_3 0x42
120#define E_4 0x43
121#define E_5 0x44
122#define E_6 0x45
123#define E_7 0x46
124#define E_8 0x47
125#define E_9 0x48
126#define E_10 0x49
127#define E_11 0x4A
128#define E_12 0x4B
129#define E_13 0x4C
130#define E_14 0x4D
131#define E_15 0x4E
132#define E_16 0x4F
133
134#define F_1 0x50
135#define F_2 0x51
136#define F_3 0x52
137#define F_4 0x53
138#define F_5 0x54
139#define F_6 0x55
140#define F_7 0x56
141#define F_8 0x57
142#define F_9 0x58
143#define F_10 0x59
144#define F_11 0x5A
145#define F_12 0x5B
146#define F_13 0x5C
147#define F_14 0x5D
148#define F_15 0x5E
149#define F_16 0x5F
150
151#define G_1 0x60
152#define G_2 0x61
153#define G_3 0x62
154#define G_4 0x63
155#define G_5 0x64
156#define G_6 0x65
157#define G_7 0x66
158#define G_8 0x67
159#define G_9 0x68
160#define G_10 0x69
161#define G_11 0x6A
162#define G_12 0x6B
163#define G_13 0x6C
164#define G_14 0x6D
165#define G_15 0x6E
166#define G_16 0x6F
167
168#define H_1 0x70
169#define H_2 0x71
170#define H_3 0x72
171#define H_4 0x73
172#define H_5 0x74
173#define H_6 0x75
174#define H_7 0x76
175#define H_8 0x77
176#define H_9 0x78
177#define H_10 0x79
178#define H_11 0x7A
179#define H_12 0x7B
180#define H_13 0x7C
181#define H_14 0x7D
182#define H_15 0x7E
183#define H_16 0x7F
184
185#define I_1 0x80
186#define I_2 0x81
187#define I_3 0x82
188#define I_4 0x83
189#define I_5 0x84
190#define I_6 0x85
191#define I_7 0x86
192#define I_8 0x87
193#define I_9 0x88
194#define I_10 0x89
195#define I_11 0x8A
196#define I_12 0x8B
197#define I_13 0x8C
198#define I_14 0x8D
199#define I_15 0x8E
200#define I_16 0x8F
201
202#define J_1 0x90
203#define J_2 0x91
204#define J_3 0x92
205#define J_4 0x93
206#define J_5 0x94
207#define J_6 0x95
208#define J_7 0x96
209#define J_8 0x97
210#define J_9 0x98
211#define J_10 0x99
212#define J_11 0x9A
213#define J_12 0x9B
214#define J_13 0x9C
215#define J_14 0x9D
216#define J_15 0x9E
217#define J_16 0x9F
218
219#define K_1 0xA0
220#define K_2 0xA1
221#define K_3 0xA2
222#define K_4 0xA3
223#define K_5 0xA4
224#define K_6 0xA5
225#define K_7 0xA6
226#define K_8 0xA7
227#define K_9 0xA8
228#define K_10 0xA9
229#define K_11 0xAA
230#define K_12 0xAB
231#define K_13 0xAC
232#define K_14 0xAD
233#define K_15 0xAE
234#define K_16 0xAF
235
236#define L_1 0xB0
237#define L_2 0xB1
238#define L_3 0xB2
239#define L_4 0xB3
240#define L_5 0xB4
241#define L_6 0xB5
242#define L_7 0xB6
243#define L_8 0xB7
244#define L_9 0xB8
245#define L_10 0xB9
246#define L_11 0xBA
247#define L_12 0xBB
248#define L_13 0xBC
249#define L_14 0xBD
250#define L_15 0xBE
251#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..e48e31c27
--- /dev/null
+++ b/drivers/led/issi/is31fl3736.h
@@ -0,0 +1,168 @@
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
22// Simple interface option.
23// If these aren't defined, just define them to make it compile
24
25#ifndef DRIVER_COUNT
26# define DRIVER_COUNT 2
27#endif
28
29#ifndef DRIVER_LED_TOTAL
30# define DRIVER_LED_TOTAL 96
31#endif
32
33typedef struct is31_led {
34 uint8_t driver : 2;
35 uint8_t r;
36 uint8_t g;
37 uint8_t b;
38} __attribute__((packed)) is31_led;
39
40extern const is31_led g_is31_leds[DRIVER_LED_TOTAL];
41
42void IS31FL3736_init(uint8_t addr);
43void IS31FL3736_write_register(uint8_t addr, uint8_t reg, uint8_t data);
44void IS31FL3736_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
45
46void IS31FL3736_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
47void IS31FL3736_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
48
49void IS31FL3736_set_led_control_register(uint8_t index, bool red, bool green, bool blue);
50
51void IS31FL3736_mono_set_brightness(int index, uint8_t value);
52void IS31FL3736_mono_set_brightness_all(uint8_t value);
53void IS31FL3736_mono_set_led_control_register(uint8_t index, bool enabled);
54
55// This should not be called from an interrupt
56// (eg. from a timer interrupt).
57// Call this while idle (in between matrix scans).
58// If the buffer is dirty, it will update the driver with the buffer.
59void IS31FL3736_update_pwm_buffers(uint8_t addr1, uint8_t addr2);
60void IS31FL3736_update_led_control_registers(uint8_t addr1, uint8_t addr2);
61
62#define A_1 0x00
63#define A_2 0x02
64#define A_3 0x04
65#define A_4 0x06
66#define A_5 0x08
67#define A_6 0x0A
68#define A_7 0x0C
69#define A_8 0x0E
70
71#define B_1 0x10
72#define B_2 0x12
73#define B_3 0x14
74#define B_4 0x16
75#define B_5 0x18
76#define B_6 0x1A
77#define B_7 0x1C
78#define B_8 0x1E
79
80#define C_1 0x20
81#define C_2 0x22
82#define C_3 0x24
83#define C_4 0x26
84#define C_5 0x28
85#define C_6 0x2A
86#define C_7 0x2C
87#define C_8 0x2E
88
89#define D_1 0x30
90#define D_2 0x32
91#define D_3 0x34
92#define D_4 0x36
93#define D_5 0x38
94#define D_6 0x3A
95#define D_7 0x3C
96#define D_8 0x3E
97
98#define E_1 0x40
99#define E_2 0x42
100#define E_3 0x44
101#define E_4 0x46
102#define E_5 0x48
103#define E_6 0x4A
104#define E_7 0x4C
105#define E_8 0x4E
106
107#define F_1 0x50
108#define F_2 0x52
109#define F_3 0x54
110#define F_4 0x56
111#define F_5 0x58
112#define F_6 0x5A
113#define F_7 0x5C
114#define F_8 0x5E
115
116#define G_1 0x60
117#define G_2 0x62
118#define G_3 0x64
119#define G_4 0x66
120#define G_5 0x68
121#define G_6 0x6A
122#define G_7 0x6C
123#define G_8 0x6E
124
125#define H_1 0x70
126#define H_2 0x72
127#define H_3 0x74
128#define H_4 0x76
129#define H_5 0x78
130#define H_6 0x7A
131#define H_7 0x7C
132#define H_8 0x7E
133
134#define I_1 0x80
135#define I_2 0x82
136#define I_3 0x84
137#define I_4 0x86
138#define I_5 0x88
139#define I_6 0x8A
140#define I_7 0x8C
141#define I_8 0x8E
142
143#define J_1 0x90
144#define J_2 0x92
145#define J_3 0x94
146#define J_4 0x96
147#define J_5 0x98
148#define J_6 0x9A
149#define J_7 0x9C
150#define J_8 0x9E
151
152#define K_1 0xA0
153#define K_2 0xA2
154#define K_3 0xA4
155#define K_4 0xA6
156#define K_5 0xA8
157#define K_6 0xAA
158#define K_7 0xAC
159#define K_8 0xAE
160
161#define L_1 0xB0
162#define L_2 0xB2
163#define L_3 0xB4
164#define L_4 0xB6
165#define L_5 0xB8
166#define L_6 0xBA
167#define L_7 0xBC
168#define L_8 0xBE
diff --git a/drivers/led/issi/is31fl3737.c b/drivers/led/issi/is31fl3737.c
new file mode 100644
index 000000000..30906b484
--- /dev/null
+++ b/drivers/led/issi/is31fl3737.c
@@ -0,0 +1,227 @@
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#include "progmem.h"
23
24// This is a 7-bit address, that gets left-shifted and bit 0
25// set to 0 for write, 1 for read (as per I2C protocol)
26// The address will vary depending on your wiring:
27// 00 <-> GND
28// 01 <-> SCL
29// 10 <-> SDA
30// 11 <-> VCC
31// ADDR1 represents A1:A0 of the 7-bit address.
32// ADDR2 represents A3:A2 of the 7-bit address.
33// The result is: 0b101(ADDR2)(ADDR1)
34#define ISSI_ADDR_DEFAULT 0x50
35
36#define ISSI_COMMANDREGISTER 0xFD
37#define ISSI_COMMANDREGISTER_WRITELOCK 0xFE
38#define ISSI_INTERRUPTMASKREGISTER 0xF0
39#define ISSI_INTERRUPTSTATUSREGISTER 0xF1
40
41#define ISSI_PAGE_LEDCONTROL 0x00 // PG0
42#define ISSI_PAGE_PWM 0x01 // PG1
43#define ISSI_PAGE_AUTOBREATH 0x02 // PG2
44#define ISSI_PAGE_FUNCTION 0x03 // PG3
45
46#define ISSI_REG_CONFIGURATION 0x00 // PG3
47#define ISSI_REG_GLOBALCURRENT 0x01 // PG3
48#define ISSI_REG_RESET 0x11 // PG3
49#define ISSI_REG_SWPULLUP 0x0F // PG3
50#define ISSI_REG_CSPULLUP 0x10 // PG3
51
52#ifndef ISSI_TIMEOUT
53# define ISSI_TIMEOUT 100
54#endif
55
56#ifndef ISSI_PERSISTENCE
57# define ISSI_PERSISTENCE 0
58#endif
59
60// Transfer buffer for TWITransmitData()
61uint8_t g_twi_transfer_buffer[20];
62
63// These buffers match the IS31FL3737 PWM registers.
64// The control buffers match the PG0 LED On/Off registers.
65// Storing them like this is optimal for I2C transfers to the registers.
66// We could optimize this and take out the unused registers from these
67// buffers and the transfers in IS31FL3737_write_pwm_buffer() but it's
68// probably not worth the extra complexity.
69
70uint8_t g_pwm_buffer[DRIVER_COUNT][192];
71bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};
72
73uint8_t g_led_control_registers[DRIVER_COUNT][24] = {0};
74bool g_led_control_registers_update_required[DRIVER_COUNT] = {false};
75
76void IS31FL3737_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
77 g_twi_transfer_buffer[0] = reg;
78 g_twi_transfer_buffer[1] = data;
79
80#if ISSI_PERSISTENCE > 0
81 for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
82 if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) == 0) break;
83 }
84#else
85 i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT);
86#endif
87}
88
89void IS31FL3737_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
90 // assumes PG1 is already selected
91
92 // transmit PWM registers in 12 transfers of 16 bytes
93 // g_twi_transfer_buffer[] is 20 bytes
94
95 // iterate over the pwm_buffer contents at 16 byte intervals
96 for (int i = 0; i < 192; i += 16) {
97 g_twi_transfer_buffer[0] = i;
98 // copy the data from i to i+15
99 // device will auto-increment register for data after the first byte
100 // thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer
101 for (int j = 0; j < 16; j++) {
102 g_twi_transfer_buffer[1 + j] = pwm_buffer[i + j];
103 }
104
105#if ISSI_PERSISTENCE > 0
106 for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
107 if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT) == 0) break;
108 }
109#else
110 i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT);
111#endif
112 }
113}
114
115void IS31FL3737_init(uint8_t addr) {
116 // In order to avoid the LEDs being driven with garbage data
117 // in the LED driver's PWM registers, shutdown is enabled last.
118 // Set up the mode and other settings, clear the PWM registers,
119 // then disable software shutdown.
120
121 // Unlock the command register.
122 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
123
124 // Select PG0
125 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
126 // Turn off all LEDs.
127 for (int i = 0x00; i <= 0x17; i++) {
128 IS31FL3737_write_register(addr, i, 0x00);
129 }
130
131 // Unlock the command register.
132 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
133
134 // Select PG1
135 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
136 // Set PWM on all LEDs to 0
137 // No need to setup Breath registers to PWM as that is the default.
138 for (int i = 0x00; i <= 0xBF; i++) {
139 IS31FL3737_write_register(addr, i, 0x00);
140 }
141
142 // Unlock the command register.
143 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
144
145 // Select PG3
146 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_FUNCTION);
147 // Set global current to maximum.
148 IS31FL3737_write_register(addr, ISSI_REG_GLOBALCURRENT, 0xFF);
149 // Disable software shutdown.
150 IS31FL3737_write_register(addr, ISSI_REG_CONFIGURATION, 0x01);
151
152 // Wait 10ms to ensure the device has woken up.
153 wait_ms(10);
154}
155
156void IS31FL3737_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
157 if (index >= 0 && index < DRIVER_LED_TOTAL) {
158 // copy the led config from progmem to SRAM
159 is31_led led;
160 memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
161
162 g_pwm_buffer[led.driver][led.r] = red;
163 g_pwm_buffer[led.driver][led.g] = green;
164 g_pwm_buffer[led.driver][led.b] = blue;
165 g_pwm_buffer_update_required[led.driver] = true;
166 }
167}
168
169void IS31FL3737_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
170 for (int i = 0; i < DRIVER_LED_TOTAL; i++) {
171 IS31FL3737_set_color(i, red, green, blue);
172 }
173}
174
175void IS31FL3737_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
176 // copy the led config from progmem to SRAM
177 is31_led led;
178 memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
179
180 uint8_t control_register_r = led.r / 8;
181 uint8_t control_register_g = led.g / 8;
182 uint8_t control_register_b = led.b / 8;
183 uint8_t bit_r = led.r % 8;
184 uint8_t bit_g = led.g % 8;
185 uint8_t bit_b = led.b % 8;
186
187 if (red) {
188 g_led_control_registers[led.driver][control_register_r] |= (1 << bit_r);
189 } else {
190 g_led_control_registers[led.driver][control_register_r] &= ~(1 << bit_r);
191 }
192 if (green) {
193 g_led_control_registers[led.driver][control_register_g] |= (1 << bit_g);
194 } else {
195 g_led_control_registers[led.driver][control_register_g] &= ~(1 << bit_g);
196 }
197 if (blue) {
198 g_led_control_registers[led.driver][control_register_b] |= (1 << bit_b);
199 } else {
200 g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b);
201 }
202
203 g_led_control_registers_update_required[led.driver] = true;
204}
205
206void IS31FL3737_update_pwm_buffers(uint8_t addr, uint8_t index) {
207 if (g_pwm_buffer_update_required[index]) {
208 // Firstly we need to unlock the command register and select PG1
209 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
210 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
211
212 IS31FL3737_write_pwm_buffer(addr, g_pwm_buffer[index]);
213 }
214 g_pwm_buffer_update_required[index] = false;
215}
216
217void IS31FL3737_update_led_control_registers(uint8_t addr, uint8_t index) {
218 if (g_led_control_registers_update_required[index]) {
219 // Firstly we need to unlock the command register and select PG0
220 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
221 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
222 for (int i = 0; i < 24; i++) {
223 IS31FL3737_write_register(addr, i, g_led_control_registers[index][i]);
224 }
225 }
226 g_led_control_registers_update_required[index] = false;
227}
diff --git a/drivers/led/issi/is31fl3737.h b/drivers/led/issi/is31fl3737.h
new file mode 100644
index 000000000..a1d228177
--- /dev/null
+++ b/drivers/led/issi/is31fl3737.h
@@ -0,0 +1,203 @@
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
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 g_is31_leds[DRIVER_LED_TOTAL];
32
33void IS31FL3737_init(uint8_t addr);
34void IS31FL3737_write_register(uint8_t addr, uint8_t reg, uint8_t data);
35void IS31FL3737_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
36
37void IS31FL3737_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
38void IS31FL3737_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
39
40void IS31FL3737_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 IS31FL3737_update_pwm_buffers(uint8_t addr1, uint8_t addr2);
47void IS31FL3737_update_led_control_registers(uint8_t addr1, uint8_t addr2);
48
49#define A_1 0x00
50#define A_2 0x01
51#define A_3 0x02
52#define A_4 0x03
53#define A_5 0x04
54#define A_6 0x05
55#define A_7 0x08
56#define A_8 0x09
57#define A_9 0x0A
58#define A_10 0x0B
59#define A_11 0x0C
60#define A_12 0x0D
61
62#define B_1 0x10
63#define B_2 0x11
64#define B_3 0x12
65#define B_4 0x13
66#define B_5 0x14
67#define B_6 0x15
68#define B_7 0x18
69#define B_8 0x19
70#define B_9 0x1A
71#define B_10 0x1B
72#define B_11 0x1C
73#define B_12 0x1D
74
75#define C_1 0x20
76#define C_2 0x21
77#define C_3 0x22
78#define C_4 0x23
79#define C_5 0x24
80#define C_6 0x25
81#define C_7 0x28
82#define C_8 0x29
83#define C_9 0x2A
84#define C_10 0x2B
85#define C_11 0x2C
86#define C_12 0x2D
87
88#define D_1 0x30
89#define D_2 0x31
90#define D_3 0x32
91#define D_4 0x33
92#define D_5 0x34
93#define D_6 0x35
94#define D_7 0x38
95#define D_8 0x39
96#define D_9 0x3A
97#define D_10 0x3B
98#define D_11 0x3C
99#define D_12 0x3D
100
101#define E_1 0x40
102#define E_2 0x41
103#define E_3 0x42
104#define E_4 0x43
105#define E_5 0x44
106#define E_6 0x45
107#define E_7 0x48
108#define E_8 0x49
109#define E_9 0x4A
110#define E_10 0x4B
111#define E_11 0x4C
112#define E_12 0x4D
113
114#define F_1 0x50
115#define F_2 0x51
116#define F_3 0x52
117#define F_4 0x53
118#define F_5 0x54
119#define F_6 0x55
120#define F_7 0x58
121#define F_8 0x59
122#define F_9 0x5A
123#define F_10 0x5B
124#define F_11 0x5C
125#define F_12 0x5D
126
127#define G_1 0x60
128#define G_2 0x61
129#define G_3 0x62
130#define G_4 0x63
131#define G_5 0x64
132#define G_6 0x65
133#define G_7 0x68
134#define G_8 0x69
135#define G_9 0x6A
136#define G_10 0x6B
137#define G_11 0x6C
138#define G_12 0x6D
139
140#define H_1 0x70
141#define H_2 0x71
142#define H_3 0x72
143#define H_4 0x73
144#define H_5 0x74
145#define H_6 0x75
146#define H_7 0x78
147#define H_8 0x79
148#define H_9 0x7A
149#define H_10 0x7B
150#define H_11 0x7C
151#define H_12 0x7D
152
153#define I_1 0x80
154#define I_2 0x81
155#define I_3 0x82
156#define I_4 0x83
157#define I_5 0x84
158#define I_6 0x85
159#define I_7 0x88
160#define I_8 0x89
161#define I_9 0x8A
162#define I_10 0x8B
163#define I_11 0x8C
164#define I_12 0x8D
165
166#define J_1 0x90
167#define J_2 0x91
168#define J_3 0x92
169#define J_4 0x93
170#define J_5 0x94
171#define J_6 0x95
172#define J_7 0x98
173#define J_8 0x99
174#define J_9 0x9A
175#define J_10 0x9B
176#define J_11 0x9C
177#define J_12 0x9D
178
179#define K_1 0xA0
180#define K_2 0xA1
181#define K_3 0xA2
182#define K_4 0xA3
183#define K_5 0xA4
184#define K_6 0xA5
185#define K_7 0xA8
186#define K_8 0xA9
187#define K_9 0xAA
188#define K_10 0xAB
189#define K_11 0xAC
190#define K_12 0xAD
191
192#define L_1 0xB0
193#define L_2 0xB1
194#define L_3 0xB2
195#define L_4 0xB3
196#define L_5 0xB4
197#define L_6 0xB5
198#define L_7 0xB8
199#define L_8 0xB9
200#define L_9 0xBA
201#define L_10 0xBB
202#define L_11 0xBC
203#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..2df0c5b1a
--- /dev/null
+++ b/drivers/led/issi/is31fl3741.h
@@ -0,0 +1,420 @@
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
25typedef struct is31_led {
26 uint32_t driver : 2;
27 uint32_t r : 10;
28 uint32_t g : 10;
29 uint32_t b : 10;
30} __attribute__((packed)) is31_led;
31
32extern const is31_led g_is31_leds[DRIVER_LED_TOTAL];
33
34void IS31FL3741_init(uint8_t addr);
35void IS31FL3741_write_register(uint8_t addr, uint8_t reg, uint8_t data);
36bool IS31FL3741_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
37
38void IS31FL3741_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
39void IS31FL3741_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
40
41void IS31FL3741_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 IS31FL3741_update_pwm_buffers(uint8_t addr1, uint8_t addr2);
48void IS31FL3741_update_led_control_registers(uint8_t addr1, uint8_t addr2);
49void IS31FL3741_set_scaling_registers(const is31_led *pled, uint8_t red, uint8_t green, uint8_t blue);
50
51void IS31FL3741_set_pwm_buffer(const is31_led *pled, uint8_t red, uint8_t green, uint8_t blue);
52
53#define CS1_SW1 0x00
54#define CS2_SW1 0x01
55#define CS3_SW1 0x02
56#define CS4_SW1 0x03
57#define CS5_SW1 0x04
58#define CS6_SW1 0x05
59#define CS7_SW1 0x06
60#define CS8_SW1 0x07
61#define CS9_SW1 0x08
62#define CS10_SW1 0x09
63#define CS11_SW1 0x0A
64#define CS12_SW1 0x0B
65#define CS13_SW1 0x0C
66#define CS14_SW1 0x0D
67#define CS15_SW1 0x0E
68#define CS16_SW1 0x0F
69#define CS17_SW1 0x10
70#define CS18_SW1 0x11
71#define CS19_SW1 0x12
72#define CS20_SW1 0x13
73#define CS21_SW1 0x14
74#define CS22_SW1 0x15
75#define CS23_SW1 0x16
76#define CS24_SW1 0x17
77#define CS25_SW1 0x18
78#define CS26_SW1 0x19
79#define CS27_SW1 0x1A
80#define CS28_SW1 0x1B
81#define CS29_SW1 0x1C
82#define CS30_SW1 0x1D
83
84#define CS1_SW2 0x1E
85#define CS2_SW2 0x1F
86#define CS3_SW2 0x20
87#define CS4_SW2 0x21
88#define CS5_SW2 0x22
89#define CS6_SW2 0x23
90#define CS7_SW2 0x24
91#define CS8_SW2 0x25
92#define CS9_SW2 0x26
93#define CS10_SW2 0x27
94#define CS11_SW2 0x28
95#define CS12_SW2 0x29
96#define CS13_SW2 0x2A
97#define CS14_SW2 0x2B
98#define CS15_SW2 0x2C
99#define CS16_SW2 0x2D
100#define CS17_SW2 0x2E
101#define CS18_SW2 0x2F
102#define CS19_SW2 0x30
103#define CS20_SW2 0x31
104#define CS21_SW2 0x32
105#define CS22_SW2 0x33
106#define CS23_SW2 0x34
107#define CS24_SW2 0x35
108#define CS25_SW2 0x36
109#define CS26_SW2 0x37
110#define CS27_SW2 0x38
111#define CS28_SW2 0x39
112#define CS29_SW2 0x3A
113#define CS30_SW2 0x3B
114
115#define CS1_SW3 0x3C
116#define CS2_SW3 0x3D
117#define CS3_SW3 0x3E
118#define CS4_SW3 0x3F
119#define CS5_SW3 0x40
120#define CS6_SW3 0x41
121#define CS7_SW3 0x42
122#define CS8_SW3 0x43
123#define CS9_SW3 0x44
124#define CS10_SW3 0x45
125#define CS11_SW3 0x46
126#define CS12_SW3 0x47
127#define CS13_SW3 0x48
128#define CS14_SW3 0x49
129#define CS15_SW3 0x4A
130#define CS16_SW3 0x4B
131#define CS17_SW3 0x4C
132#define CS18_SW3 0x4D
133#define CS19_SW3 0x4E
134#define CS20_SW3 0x4F
135#define CS21_SW3 0x50
136#define CS22_SW3 0x51
137#define CS23_SW3 0x52
138#define CS24_SW3 0x53
139#define CS25_SW3 0x54
140#define CS26_SW3 0x55
141#define CS27_SW3 0x56
142#define CS28_SW3 0x57
143#define CS29_SW3 0x58
144#define CS30_SW3 0x59
145
146#define CS1_SW4 0x5A
147#define CS2_SW4 0x5B
148#define CS3_SW4 0x5C
149#define CS4_SW4 0x5D
150#define CS5_SW4 0x5E
151#define CS6_SW4 0x5F
152#define CS7_SW4 0x60
153#define CS8_SW4 0x61
154#define CS9_SW4 0x62
155#define CS10_SW4 0x63
156#define CS11_SW4 0x64
157#define CS12_SW4 0x65
158#define CS13_SW4 0x66
159#define CS14_SW4 0x67
160#define CS15_SW4 0x68
161#define CS16_SW4 0x69
162#define CS17_SW4 0x6A
163#define CS18_SW4 0x6B
164#define CS19_SW4 0x6C
165#define CS20_SW4 0x6D
166#define CS21_SW4 0x6E
167#define CS22_SW4 0x6F
168#define CS23_SW4 0x70
169#define CS24_SW4 0x71
170#define CS25_SW4 0x72
171#define CS26_SW4 0x73
172#define CS27_SW4 0x74
173#define CS28_SW4 0x75
174#define CS29_SW4 0x76
175#define CS30_SW4 0x77
176
177#define CS1_SW5 0x78
178#define CS2_SW5 0x79
179#define CS3_SW5 0x7A
180#define CS4_SW5 0x7B
181#define CS5_SW5 0x7C
182#define CS6_SW5 0x7D
183#define CS7_SW5 0x7E
184#define CS8_SW5 0x7F
185#define CS9_SW5 0x80
186#define CS10_SW5 0x81
187#define CS11_SW5 0x82
188#define CS12_SW5 0x83
189#define CS13_SW5 0x84
190#define CS14_SW5 0x85
191#define CS15_SW5 0x86
192#define CS16_SW5 0x87
193#define CS17_SW5 0x88
194#define CS18_SW5 0x89
195#define CS19_SW5 0x8A
196#define CS20_SW5 0x8B
197#define CS21_SW5 0x8C
198#define CS22_SW5 0x8D
199#define CS23_SW5 0x8E
200#define CS24_SW5 0x8F
201#define CS25_SW5 0x90
202#define CS26_SW5 0x91
203#define CS27_SW5 0x92
204#define CS28_SW5 0x93
205#define CS29_SW5 0x94
206#define CS30_SW5 0x95
207
208#define CS1_SW6 0x96
209#define CS2_SW6 0x97
210#define CS3_SW6 0x98
211#define CS4_SW6 0x99
212#define CS5_SW6 0x9A
213#define CS6_SW6 0x9B
214#define CS7_SW6 0x9C
215#define CS8_SW6 0x9D
216#define CS9_SW6 0x9E
217#define CS10_SW6 0x9F
218#define CS11_SW6 0xA0
219#define CS12_SW6 0xA1
220#define CS13_SW6 0xA2
221#define CS14_SW6 0xA3
222#define CS15_SW6 0xA4
223#define CS16_SW6 0xA5
224#define CS17_SW6 0xA6
225#define CS18_SW6 0xA7
226#define CS19_SW6 0xA8
227#define CS20_SW6 0xA9
228#define CS21_SW6 0xAA
229#define CS22_SW6 0xAB
230#define CS23_SW6 0xAC
231#define CS24_SW6 0xAD
232#define CS25_SW6 0xAE
233#define CS26_SW6 0xAF
234#define CS27_SW6 0xB0
235#define CS28_SW6 0xB1
236#define CS29_SW6 0xB2
237#define CS30_SW6 0xB3
238
239#define CS1_SW7 0xB4
240#define CS2_SW7 0xB5
241#define CS3_SW7 0xB6
242#define CS4_SW7 0xB7
243#define CS5_SW7 0xB8
244#define CS6_SW7 0xB9
245#define CS7_SW7 0xBA
246#define CS8_SW7 0xBB
247#define CS9_SW7 0xBC
248#define CS10_SW7 0xBD
249#define CS11_SW7 0xBE
250#define CS12_SW7 0xBF
251#define CS13_SW7 0xC0
252#define CS14_SW7 0xC1
253#define CS15_SW7 0xC2
254#define CS16_SW7 0xC3
255#define CS17_SW7 0xC4
256#define CS18_SW7 0xC5
257#define CS19_SW7 0xC6
258#define CS20_SW7 0xC7
259#define CS21_SW7 0xC8
260#define CS22_SW7 0xC9
261#define CS23_SW7 0xCA
262#define CS24_SW7 0xCB
263#define CS25_SW7 0xCC
264#define CS26_SW7 0xCD
265#define CS27_SW7 0xCE
266#define CS28_SW7 0xCF
267#define CS29_SW7 0xD0
268#define CS30_SW7 0xD1
269
270#define CS1_SW8 0xD2
271#define CS2_SW8 0xD3
272#define CS3_SW8 0xD4
273#define CS4_SW8 0xD5
274#define CS5_SW8 0xD6
275#define CS6_SW8 0xD7
276#define CS7_SW8 0xD8
277#define CS8_SW8 0xD9
278#define CS9_SW8 0xDA
279#define CS10_SW8 0xDB
280#define CS11_SW8 0xDC
281#define CS12_SW8 0xDD
282#define CS13_SW8 0xDE
283#define CS14_SW8 0xDF
284#define CS15_SW8 0xE0
285#define CS16_SW8 0xE1
286#define CS17_SW8 0xE2
287#define CS18_SW8 0xE3
288#define CS19_SW8 0xE4
289#define CS20_SW8 0xE5
290#define CS21_SW8 0xE6
291#define CS22_SW8 0xE7
292#define CS23_SW8 0xE8
293#define CS24_SW8 0xE9
294#define CS25_SW8 0xEA
295#define CS26_SW8 0xEB
296#define CS27_SW8 0xEC
297#define CS28_SW8 0xED
298#define CS29_SW8 0xEE
299#define CS30_SW8 0xEF
300
301#define CS1_SW9 0xF0
302#define CS2_SW9 0xF1
303#define CS3_SW9 0xF2
304#define CS4_SW9 0xF3
305#define CS5_SW9 0xF4
306#define CS6_SW9 0xF5
307#define CS7_SW9 0xF6
308#define CS8_SW9 0xF7
309#define CS9_SW9 0xF8
310#define CS10_SW9 0xF9
311#define CS11_SW9 0xFA
312#define CS12_SW9 0xFB
313#define CS13_SW9 0xFC
314#define CS14_SW9 0xFD
315#define CS15_SW9 0xFE
316#define CS16_SW9 0xFF
317#define CS17_SW9 0x100
318#define CS18_SW9 0x101
319#define CS19_SW9 0x102
320#define CS20_SW9 0x103
321#define CS21_SW9 0x104
322#define CS22_SW9 0x105
323#define CS23_SW9 0x106
324#define CS24_SW9 0x107
325#define CS25_SW9 0x108
326#define CS26_SW9 0x109
327#define CS27_SW9 0x10A
328#define CS28_SW9 0x10B
329#define CS29_SW9 0x10C
330#define CS30_SW9 0x10D
331
332#define CS31_SW1 0x10E
333#define CS32_SW1 0x10F
334#define CS33_SW1 0x110
335#define CS34_SW1 0x111
336#define CS35_SW1 0x112
337#define CS36_SW1 0x113
338#define CS37_SW1 0x114
339#define CS38_SW1 0x115
340#define CS39_SW1 0x116
341
342#define CS31_SW2 0x117
343#define CS32_SW2 0x118
344#define CS33_SW2 0x119
345#define CS34_SW2 0x11A
346#define CS35_SW2 0x11B
347#define CS36_SW2 0x11C
348#define CS37_SW2 0x11D
349#define CS38_SW2 0x11E
350#define CS39_SW2 0x11F
351
352#define CS31_SW3 0x120
353#define CS32_SW3 0x121
354#define CS33_SW3 0x122
355#define CS34_SW3 0x123
356#define CS35_SW3 0x124
357#define CS36_SW3 0x125
358#define CS37_SW3 0x126
359#define CS38_SW3 0x127
360#define CS39_SW3 0x128
361
362#define CS31_SW4 0x129
363#define CS32_SW4 0x12A
364#define CS33_SW4 0x12B
365#define CS34_SW4 0x12C
366#define CS35_SW4 0x12D
367#define CS36_SW4 0x12E
368#define CS37_SW4 0x12F
369#define CS38_SW4 0x130
370#define CS39_SW4 0x131
371
372#define CS31_SW5 0x132
373#define CS32_SW5 0x133
374#define CS33_SW5 0x134
375#define CS34_SW5 0x135
376#define CS35_SW5 0x136
377#define CS36_SW5 0x137
378#define CS37_SW5 0x138
379#define CS38_SW5 0x139
380#define CS39_SW5 0x13A
381
382#define CS31_SW6 0x13B
383#define CS32_SW6 0x13C
384#define CS33_SW6 0x13D
385#define CS34_SW6 0x13E
386#define CS35_SW6 0x13F
387#define CS36_SW6 0x140
388#define CS37_SW6 0x141
389#define CS38_SW6 0x142
390#define CS39_SW6 0x143
391
392#define CS31_SW7 0x144
393#define CS32_SW7 0x145
394#define CS33_SW7 0x146
395#define CS34_SW7 0x147
396#define CS35_SW7 0x148
397#define CS36_SW7 0x149
398#define CS37_SW7 0x14A
399#define CS38_SW7 0x14B
400#define CS39_SW7 0x14C
401
402#define CS31_SW8 0x14D
403#define CS32_SW8 0x14E
404#define CS33_SW8 0x14F
405#define CS34_SW8 0x150
406#define CS35_SW8 0x151
407#define CS36_SW8 0x152
408#define CS37_SW8 0x153
409#define CS38_SW8 0x154
410#define CS39_SW8 0x155
411
412#define CS31_SW9 0x156
413#define CS32_SW9 0x157
414#define CS33_SW9 0x158
415#define CS34_SW9 0x159
416#define CS35_SW9 0x15A
417#define CS36_SW9 0x15B
418#define CS37_SW9 0x15C
419#define CS38_SW9 0x15D
420#define CS39_SW9 0x15E