diff options
author | yiancar <yiangosyiangou@cytanet.com.cy> | 2018-08-15 08:19:38 +0300 |
---|---|---|
committer | Jack Humbert <jack.humb@gmail.com> | 2018-08-15 01:19:38 -0400 |
commit | ad2bb529c795be066b279f52bebec03257992fc2 (patch) | |
tree | addf2d3b665316c985f0b0d1c524a60977d3ed72 | |
parent | feec8ad4694814ed8953c6f72798f51f39724af5 (diff) | |
download | qmk_firmware-ad2bb529c795be066b279f52bebec03257992fc2.tar.gz qmk_firmware-ad2bb529c795be066b279f52bebec03257992fc2.zip |
Rgb matrix arm (#3648)
* Addition of I2C master driver for STM32, Generalization of ISSI3731 driver
- Addition of an i2c_master driver for STM32 to replicate expectations of AVR driver.
- Moved ISSI3731 driver one level up to make it accesible by both architectures.
- Renamed ISSI3731 functions to a more general name for preparation of other ISSI drivers.
- Added compiler directives where necessary to differenciate each architecture.
* converted tabs to spaces
-rw-r--r-- | drivers/arm/i2c_master.c | 103 | ||||
-rw-r--r-- | drivers/arm/i2c_master.h | 39 | ||||
-rw-r--r-- | drivers/avr/is31fl3731.c | 262 | ||||
-rw-r--r-- | drivers/is31fl3731.c | 271 | ||||
-rw-r--r-- | drivers/is31fl3731.h (renamed from drivers/avr/is31fl3731.h) | 18 | ||||
-rw-r--r-- | quantum/rgb_matrix.c | 20 |
6 files changed, 432 insertions, 281 deletions
diff --git a/drivers/arm/i2c_master.c b/drivers/arm/i2c_master.c new file mode 100644 index 000000000..2fdd9f65e --- /dev/null +++ b/drivers/arm/i2c_master.c | |||
@@ -0,0 +1,103 @@ | |||
1 | /* Copyright 2018 Jack Humbert | ||
2 | * Copyright 2018 Yiancar | ||
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 | /* This library follows the convention of the AVR i2c_master library. | ||
19 | * As a result addresses are expected to be already shifted (addr << 1). | ||
20 | * I2CD1 is the default driver which corresponds to pins B6 and B7. This | ||
21 | * can be changed. | ||
22 | * Please ensure that HAL_USE_I2C is TRUE in the halconf.h file and that | ||
23 | * STM32_I2C_USE_I2C1 is TRUE in the mcuconf.h file. | ||
24 | */ | ||
25 | |||
26 | #include "i2c_master.h" | ||
27 | #include <string.h> | ||
28 | #include <hal.h> | ||
29 | |||
30 | static uint8_t i2c_address; | ||
31 | |||
32 | // This configures the I2C clock to 400Mhz assuming a 72Mhz clock | ||
33 | // For more info : https://www.st.com/en/embedded-software/stsw-stm32126.html | ||
34 | static const I2CConfig i2cconfig = { | ||
35 | STM32_TIMINGR_PRESC(15U) | | ||
36 | STM32_TIMINGR_SCLDEL(4U) | STM32_TIMINGR_SDADEL(2U) | | ||
37 | STM32_TIMINGR_SCLH(15U) | STM32_TIMINGR_SCLL(21U), | ||
38 | 0, | ||
39 | 0 | ||
40 | }; | ||
41 | |||
42 | void i2c_init(void) | ||
43 | { | ||
44 | palSetGroupMode(GPIOB,6,7, PAL_MODE_INPUT); // Try releasing special pins for a short time | ||
45 | chThdSleepMilliseconds(10); | ||
46 | |||
47 | palSetPadMode(GPIOB, 6, PAL_MODE_ALTERNATE(4) | PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_PUPDR_PULLUP); | ||
48 | palSetPadMode(GPIOB, 7, PAL_MODE_ALTERNATE(4) | PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_PUPDR_PULLUP); | ||
49 | |||
50 | //i2cInit(); //This is invoked by halInit() so no need to redo it. | ||
51 | } | ||
52 | |||
53 | // This is usually not needed | ||
54 | uint8_t i2c_start(uint8_t address) | ||
55 | { | ||
56 | i2c_address = address; | ||
57 | i2cStart(&I2C_DRIVER, &i2cconfig); | ||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | uint8_t i2c_transmit(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout) | ||
62 | { | ||
63 | i2c_address = address; | ||
64 | i2cStart(&I2C_DRIVER, &i2cconfig); | ||
65 | return i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), data, length, 0, 0, MS2ST(timeout)); | ||
66 | } | ||
67 | |||
68 | uint8_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout) | ||
69 | { | ||
70 | i2c_address = address; | ||
71 | i2cStart(&I2C_DRIVER, &i2cconfig); | ||
72 | return i2cMasterReceiveTimeout(&I2C_DRIVER, (i2c_address >> 1), data, length, MS2ST(timeout)); | ||
73 | } | ||
74 | |||
75 | uint8_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) | ||
76 | { | ||
77 | i2c_address = devaddr; | ||
78 | i2cStart(&I2C_DRIVER, &i2cconfig); | ||
79 | |||
80 | uint8_t complete_packet[length + 1]; | ||
81 | for(uint8_t i = 0; i < length; i++) | ||
82 | { | ||
83 | complete_packet[i+1] = data[i]; | ||
84 | } | ||
85 | complete_packet[0] = regaddr | ||
86 | |||
87 | return i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), complete_packet, length + 1, 0, 0, MS2ST(timeout)); | ||
88 | } | ||
89 | |||
90 | uint8_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) | ||
91 | { | ||
92 | i2c_address = devaddr; | ||
93 | i2cStart(&I2C_DRIVER, &i2cconfig); | ||
94 | return i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), regaddr, 1, data, length, MS2ST(timeout)); | ||
95 | } | ||
96 | |||
97 | // This is usually not needed. It releases the driver to allow pins to become GPIO again. | ||
98 | uint8_t i2c_stop(uint16_t timeout) | ||
99 | { | ||
100 | i2c_address = address; | ||
101 | i2cStop(&I2C_DRIVER); | ||
102 | return 0; | ||
103 | } | ||
diff --git a/drivers/arm/i2c_master.h b/drivers/arm/i2c_master.h new file mode 100644 index 000000000..9d51245be --- /dev/null +++ b/drivers/arm/i2c_master.h | |||
@@ -0,0 +1,39 @@ | |||
1 | /* Copyright 2018 Jack Humbert | ||
2 | * Copyright 2018 Yiancar | ||
3 | * | ||
4 | * This program is free sofare: you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Sofare 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 | /* This library follows the convention of the AVR i2c_master library. | ||
19 | * As a result addresses are expected to be already shifted (addr << 1). | ||
20 | * I2CD1 is the default driver which corresponds to pins B6 and B7. This | ||
21 | * can be changed. | ||
22 | * Please ensure that HAL_USE_I2C is TRUE in the halconf.h file and that | ||
23 | * STM32_I2C_USE_I2C1 is TRUE in the mcuconf.h file. | ||
24 | */ | ||
25 | |||
26 | #include "ch.h" | ||
27 | #include <hal.h> | ||
28 | |||
29 | #ifndef I2C_DRIVER | ||
30 | #define I2C_DRIVER I2CD1 | ||
31 | #endif | ||
32 | |||
33 | void i2c_init(void); | ||
34 | uint8_t i2c_start(uint8_t address); | ||
35 | uint8_t i2c_transmit(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout); | ||
36 | uint8_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout); | ||
37 | uint8_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout); | ||
38 | uint8_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout); | ||
39 | void i2c_stop(void); | ||
diff --git a/drivers/avr/is31fl3731.c b/drivers/avr/is31fl3731.c deleted file mode 100644 index 70813464b..000000000 --- a/drivers/avr/is31fl3731.c +++ /dev/null | |||
@@ -1,262 +0,0 @@ | |||
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 <avr/interrupt.h> | ||
20 | #include <avr/io.h> | ||
21 | #include <util/delay.h> | ||
22 | #include <string.h> | ||
23 | #include "i2c_master.h" | ||
24 | #include "progmem.h" | ||
25 | |||
26 | // This is a 7-bit address, that gets left-shifted and bit 0 | ||
27 | // set to 0 for write, 1 for read (as per I2C protocol) | ||
28 | // The address will vary depending on your wiring: | ||
29 | // 0b1110100 AD <-> GND | ||
30 | // 0b1110111 AD <-> VCC | ||
31 | // 0b1110101 AD <-> SCL | ||
32 | // 0b1110110 AD <-> SDA | ||
33 | #define ISSI_ADDR_DEFAULT 0x74 | ||
34 | |||
35 | #define ISSI_REG_CONFIG 0x00 | ||
36 | #define ISSI_REG_CONFIG_PICTUREMODE 0x00 | ||
37 | #define ISSI_REG_CONFIG_AUTOPLAYMODE 0x08 | ||
38 | #define ISSI_REG_CONFIG_AUDIOPLAYMODE 0x18 | ||
39 | |||
40 | #define ISSI_CONF_PICTUREMODE 0x00 | ||
41 | #define ISSI_CONF_AUTOFRAMEMODE 0x04 | ||
42 | #define ISSI_CONF_AUDIOMODE 0x08 | ||
43 | |||
44 | #define ISSI_REG_PICTUREFRAME 0x01 | ||
45 | |||
46 | #define ISSI_REG_SHUTDOWN 0x0A | ||
47 | #define ISSI_REG_AUDIOSYNC 0x06 | ||
48 | |||
49 | #define ISSI_COMMANDREGISTER 0xFD | ||
50 | #define ISSI_BANK_FUNCTIONREG 0x0B // helpfully called 'page nine' | ||
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() | ||
61 | uint8_t g_twi_transfer_buffer[20]; | ||
62 | |||
63 | // These buffers match the IS31FL3731 PWM registers 0x24-0xB3. | ||
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 IS31FL3731_write_pwm_buffer() but it's | ||
67 | // probably not worth the extra complexity. | ||
68 | uint8_t g_pwm_buffer[DRIVER_COUNT][144]; | ||
69 | bool g_pwm_buffer_update_required = false; | ||
70 | |||
71 | uint8_t g_led_control_registers[DRIVER_COUNT][18] = { { 0 }, { 0 } }; | ||
72 | bool g_led_control_registers_update_required = false; | ||
73 | |||
74 | // This is the bit pattern in the LED control registers | ||
75 | // (for matrix A, add one to register for matrix B) | ||
76 | // | ||
77 | // reg - b7 b6 b5 b4 b3 b2 b1 b0 | ||
78 | // 0x00 - R08,R07,R06,R05,R04,R03,R02,R01 | ||
79 | // 0x02 - G08,G07,G06,G05,G04,G03,G02,R00 | ||
80 | // 0x04 - B08,B07,B06,B05,B04,B03,G01,G00 | ||
81 | // 0x06 - - , - , - , - , - ,B02,B01,B00 | ||
82 | // 0x08 - - , - , - , - , - , - , - , - | ||
83 | // 0x0A - B17,B16,B15, - , - , - , - , - | ||
84 | // 0x0C - G17,G16,B14,B13,B12,B11,B10,B09 | ||
85 | // 0x0E - R17,G15,G14,G13,G12,G11,G10,G09 | ||
86 | // 0x10 - R16,R15,R14,R13,R12,R11,R10,R09 | ||
87 | |||
88 | |||
89 | void IS31FL3731_write_register( uint8_t addr, uint8_t reg, uint8_t data ) | ||
90 | { | ||
91 | g_twi_transfer_buffer[0] = reg; | ||
92 | g_twi_transfer_buffer[1] = data; | ||
93 | |||
94 | #if ISSI_PERSISTENCE > 0 | ||
95 | for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) { | ||
96 | if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) == 0) | ||
97 | break; | ||
98 | } | ||
99 | #else | ||
100 | i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT); | ||
101 | #endif | ||
102 | } | ||
103 | |||
104 | void IS31FL3731_write_pwm_buffer( uint8_t addr, uint8_t *pwm_buffer ) | ||
105 | { | ||
106 | // assumes bank is already selected | ||
107 | |||
108 | // transmit PWM registers in 9 transfers of 16 bytes | ||
109 | // g_twi_transfer_buffer[] is 20 bytes | ||
110 | |||
111 | // iterate over the pwm_buffer contents at 16 byte intervals | ||
112 | for ( int i = 0; i < 144; i += 16 ) { | ||
113 | // set the first register, e.g. 0x24, 0x34, 0x44, etc. | ||
114 | g_twi_transfer_buffer[0] = 0x24 + i; | ||
115 | // copy the data from i to i+15 | ||
116 | // device will auto-increment register for data after the first byte | ||
117 | // thus this sets registers 0x24-0x33, 0x34-0x43, etc. in one transfer | ||
118 | for ( int j = 0; j < 16; j++ ) { | ||
119 | g_twi_transfer_buffer[1 + j] = pwm_buffer[i + j]; | ||
120 | } | ||
121 | |||
122 | #if ISSI_PERSISTENCE > 0 | ||
123 | for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) { | ||
124 | if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT) == 0) | ||
125 | break; | ||
126 | } | ||
127 | #else | ||
128 | i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT); | ||
129 | #endif | ||
130 | } | ||
131 | } | ||
132 | |||
133 | void IS31FL3731_init( uint8_t addr ) | ||
134 | { | ||
135 | // In order to avoid the LEDs being driven with garbage data | ||
136 | // in the LED driver's PWM registers, first enable software shutdown, | ||
137 | // then set up the mode and other settings, clear the PWM registers, | ||
138 | // then disable software shutdown. | ||
139 | |||
140 | // select "function register" bank | ||
141 | IS31FL3731_write_register( addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG ); | ||
142 | |||
143 | // enable software shutdown | ||
144 | IS31FL3731_write_register( addr, ISSI_REG_SHUTDOWN, 0x00 ); | ||
145 | // this delay was copied from other drivers, might not be needed | ||
146 | _delay_ms( 10 ); | ||
147 | |||
148 | // picture mode | ||
149 | IS31FL3731_write_register( addr, ISSI_REG_CONFIG, ISSI_REG_CONFIG_PICTUREMODE ); | ||
150 | // display frame 0 | ||
151 | IS31FL3731_write_register( addr, ISSI_REG_PICTUREFRAME, 0x00 ); | ||
152 | // audio sync off | ||
153 | IS31FL3731_write_register( addr, ISSI_REG_AUDIOSYNC, 0x00 ); | ||
154 | |||
155 | // select bank 0 | ||
156 | IS31FL3731_write_register( addr, ISSI_COMMANDREGISTER, 0 ); | ||
157 | |||
158 | // turn off all LEDs in the LED control register | ||
159 | for ( int i = 0x00; i <= 0x11; i++ ) | ||
160 | { | ||
161 | IS31FL3731_write_register( addr, i, 0x00 ); | ||
162 | } | ||
163 | |||
164 | // turn off all LEDs in the blink control register (not really needed) | ||
165 | for ( int i = 0x12; i <= 0x23; i++ ) | ||
166 | { | ||
167 | IS31FL3731_write_register( addr, i, 0x00 ); | ||
168 | } | ||
169 | |||
170 | // set PWM on all LEDs to 0 | ||
171 | for ( int i = 0x24; i <= 0xB3; i++ ) | ||
172 | { | ||
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 | } | ||
188 | |||
189 | void IS31FL3731_set_color( int index, uint8_t red, uint8_t green, uint8_t blue ) | ||
190 | { | ||
191 | if ( index >= 0 && index < DRIVER_LED_TOTAL ) { | ||
192 | is31_led led = g_is31_leds[index]; | ||
193 | |||
194 | // Subtract 0x24 to get the second index of g_pwm_buffer | ||
195 | g_pwm_buffer[led.driver][led.r - 0x24] = red; | ||
196 | g_pwm_buffer[led.driver][led.g - 0x24] = green; | ||
197 | g_pwm_buffer[led.driver][led.b - 0x24] = blue; | ||
198 | g_pwm_buffer_update_required = true; | ||
199 | } | ||
200 | } | ||
201 | |||
202 | void IS31FL3731_set_color_all( uint8_t red, uint8_t green, uint8_t blue ) | ||
203 | { | ||
204 | for ( int i = 0; i < DRIVER_LED_TOTAL; i++ ) | ||
205 | { | ||
206 | IS31FL3731_set_color( i, red, green, blue ); | ||
207 | } | ||
208 | } | ||
209 | |||
210 | void IS31FL3731_set_led_control_register( uint8_t index, bool red, bool green, bool blue ) | ||
211 | { | ||
212 | is31_led led = g_is31_leds[index]; | ||
213 | |||
214 | uint8_t control_register_r = (led.r - 0x24) / 8; | ||
215 | uint8_t control_register_g = (led.g - 0x24) / 8; | ||
216 | uint8_t control_register_b = (led.b - 0x24) / 8; | ||
217 | uint8_t bit_r = (led.r - 0x24) % 8; | ||
218 | uint8_t bit_g = (led.g - 0x24) % 8; | ||
219 | uint8_t bit_b = (led.b - 0x24) % 8; | ||
220 | |||
221 | if ( red ) { | ||
222 | g_led_control_registers[led.driver][control_register_r] |= (1 << bit_r); | ||
223 | } else { | ||
224 | g_led_control_registers[led.driver][control_register_r] &= ~(1 << bit_r); | ||
225 | } | ||
226 | if ( green ) { | ||
227 | g_led_control_registers[led.driver][control_register_g] |= (1 << bit_g); | ||
228 | } else { | ||
229 | g_led_control_registers[led.driver][control_register_g] &= ~(1 << bit_g); | ||
230 | } | ||
231 | if ( blue ) { | ||
232 | g_led_control_registers[led.driver][control_register_b] |= (1 << bit_b); | ||
233 | } else { | ||
234 | g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b); | ||
235 | } | ||
236 | |||
237 | g_led_control_registers_update_required = true; | ||
238 | |||
239 | } | ||
240 | |||
241 | void IS31FL3731_update_pwm_buffers( uint8_t addr1, uint8_t addr2 ) | ||
242 | { | ||
243 | if ( g_pwm_buffer_update_required ) | ||
244 | { | ||
245 | IS31FL3731_write_pwm_buffer( addr1, g_pwm_buffer[0] ); | ||
246 | IS31FL3731_write_pwm_buffer( addr2, g_pwm_buffer[1] ); | ||
247 | } | ||
248 | g_pwm_buffer_update_required = false; | ||
249 | } | ||
250 | |||
251 | void IS31FL3731_update_led_control_registers( uint8_t addr1, uint8_t addr2 ) | ||
252 | { | ||
253 | if ( g_led_control_registers_update_required ) | ||
254 | { | ||
255 | for ( int i=0; i<18; i++ ) | ||
256 | { | ||
257 | IS31FL3731_write_register(addr1, i, g_led_control_registers[0][i] ); | ||
258 | IS31FL3731_write_register(addr2, i, g_led_control_registers[1][i] ); | ||
259 | } | ||
260 | } | ||
261 | } | ||
262 | |||
diff --git a/drivers/is31fl3731.c b/drivers/is31fl3731.c new file mode 100644 index 000000000..0524feb49 --- /dev/null +++ b/drivers/is31fl3731.c | |||
@@ -0,0 +1,271 @@ | |||
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 | #ifdef __AVR__ | ||
19 | #include <avr/interrupt.h> | ||
20 | #include <avr/io.h> | ||
21 | #include <util/delay.h> | ||
22 | #else | ||
23 | #include "wait.h" | ||
24 | #endif | ||
25 | |||
26 | #include "is31fl3731.h" | ||
27 | #include <string.h> | ||
28 | #include "i2c_master.h" | ||
29 | #include "progmem.h" | ||
30 | |||
31 | // This is a 7-bit address, that gets left-shifted and bit 0 | ||
32 | // set to 0 for write, 1 for read (as per I2C protocol) | ||
33 | // The address will vary depending on your wiring: | ||
34 | // 0b1110100 AD <-> GND | ||
35 | // 0b1110111 AD <-> VCC | ||
36 | // 0b1110101 AD <-> SCL | ||
37 | // 0b1110110 AD <-> SDA | ||
38 | #define ISSI_ADDR_DEFAULT 0x74 | ||
39 | |||
40 | #define ISSI_REG_CONFIG 0x00 | ||
41 | #define ISSI_REG_CONFIG_PICTUREMODE 0x00 | ||
42 | #define ISSI_REG_CONFIG_AUTOPLAYMODE 0x08 | ||
43 | #define ISSI_REG_CONFIG_AUDIOPLAYMODE 0x18 | ||
44 | |||
45 | #define ISSI_CONF_PICTUREMODE 0x00 | ||
46 | #define ISSI_CONF_AUTOFRAMEMODE 0x04 | ||
47 | #define ISSI_CONF_AUDIOMODE 0x08 | ||
48 | |||
49 | #define ISSI_REG_PICTUREFRAME 0x01 | ||
50 | |||
51 | #define ISSI_REG_SHUTDOWN 0x0A | ||
52 | #define ISSI_REG_AUDIOSYNC 0x06 | ||
53 | |||
54 | #define ISSI_COMMANDREGISTER 0xFD | ||
55 | #define ISSI_BANK_FUNCTIONREG 0x0B // helpfully called 'page nine' | ||
56 | |||
57 | #ifndef ISSI_TIMEOUT | ||
58 | #define ISSI_TIMEOUT 100 | ||
59 | #endif | ||
60 | |||
61 | #ifndef ISSI_PERSISTENCE | ||
62 | #define ISSI_PERSISTENCE 0 | ||
63 | #endif | ||
64 | |||
65 | // Transfer buffer for TWITransmitData() | ||
66 | uint8_t g_twi_transfer_buffer[20]; | ||
67 | |||
68 | // These buffers match the IS31FL3731 PWM registers 0x24-0xB3. | ||
69 | // Storing them like this is optimal for I2C transfers to the registers. | ||
70 | // We could optimize this and take out the unused registers from these | ||
71 | // buffers and the transfers in IS31FL3731_write_pwm_buffer() but it's | ||
72 | // probably not worth the extra complexity. | ||
73 | uint8_t g_pwm_buffer[DRIVER_COUNT][144]; | ||
74 | bool g_pwm_buffer_update_required = false; | ||
75 | |||
76 | uint8_t g_led_control_registers[DRIVER_COUNT][18] = { { 0 }, { 0 } }; | ||
77 | bool g_led_control_registers_update_required = false; | ||
78 | |||
79 | // This is the bit pattern in the LED control registers | ||
80 | // (for matrix A, add one to register for matrix B) | ||
81 | // | ||
82 | // reg - b7 b6 b5 b4 b3 b2 b1 b0 | ||
83 | // 0x00 - R08,R07,R06,R05,R04,R03,R02,R01 | ||
84 | // 0x02 - G08,G07,G06,G05,G04,G03,G02,R00 | ||
85 | // 0x04 - B08,B07,B06,B05,B04,B03,G01,G00 | ||
86 | // 0x06 - - , - , - , - , - ,B02,B01,B00 | ||
87 | // 0x08 - - , - , - , - , - , - , - , - | ||
88 | // 0x0A - B17,B16,B15, - , - , - , - , - | ||
89 | // 0x0C - G17,G16,B14,B13,B12,B11,B10,B09 | ||
90 | // 0x0E - R17,G15,G14,G13,G12,G11,G10,G09 | ||
91 | // 0x10 - R16,R15,R14,R13,R12,R11,R10,R09 | ||
92 | |||
93 | |||
94 | void IS31_write_register( uint8_t addr, uint8_t reg, uint8_t data ) | ||
95 | { | ||
96 | g_twi_transfer_buffer[0] = reg; | ||
97 | g_twi_transfer_buffer[1] = data; | ||
98 | |||
99 | #if ISSI_PERSISTENCE > 0 | ||
100 | for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) { | ||
101 | if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) == 0) | ||
102 | break; | ||
103 | } | ||
104 | #else | ||
105 | i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT); | ||
106 | #endif | ||
107 | } | ||
108 | |||
109 | void IS31_write_pwm_buffer( uint8_t addr, uint8_t *pwm_buffer ) | ||
110 | { | ||
111 | // assumes bank is already selected | ||
112 | |||
113 | // transmit PWM registers in 9 transfers of 16 bytes | ||
114 | // g_twi_transfer_buffer[] is 20 bytes | ||
115 | |||
116 | // iterate over the pwm_buffer contents at 16 byte intervals | ||
117 | for ( int i = 0; i < 144; i += 16 ) { | ||
118 | // set the first register, e.g. 0x24, 0x34, 0x44, etc. | ||
119 | g_twi_transfer_buffer[0] = 0x24 + i; | ||
120 | // copy the data from i to i+15 | ||
121 | // device will auto-increment register for data after the first byte | ||
122 | // thus this sets registers 0x24-0x33, 0x34-0x43, etc. in one transfer | ||
123 | for ( int j = 0; j < 16; j++ ) { | ||
124 | g_twi_transfer_buffer[1 + j] = pwm_buffer[i + j]; | ||
125 | } | ||
126 | |||
127 | #if ISSI_PERSISTENCE > 0 | ||
128 | for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) { | ||
129 | if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT) == 0) | ||
130 | break; | ||
131 | } | ||
132 | #else | ||
133 | i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT); | ||
134 | #endif | ||
135 | } | ||
136 | } | ||
137 | |||
138 | void IS31_init( uint8_t addr ) | ||
139 | { | ||
140 | // In order to avoid the LEDs being driven with garbage data | ||
141 | // in the LED driver's PWM registers, first enable software shutdown, | ||
142 | // then set up the mode and other settings, clear the PWM registers, | ||
143 | // then disable software shutdown. | ||
144 | |||
145 | // select "function register" bank | ||
146 | IS31_write_register( addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG ); | ||
147 | |||
148 | // enable software shutdown | ||
149 | IS31_write_register( addr, ISSI_REG_SHUTDOWN, 0x00 ); | ||
150 | // this delay was copied from other drivers, might not be needed | ||
151 | #ifdef __AVR__ | ||
152 | _delay_ms( 10 ); | ||
153 | #else | ||
154 | wait_ms(10); | ||
155 | #endif | ||
156 | |||
157 | // picture mode | ||
158 | IS31_write_register( addr, ISSI_REG_CONFIG, ISSI_REG_CONFIG_PICTUREMODE ); | ||
159 | // display frame 0 | ||
160 | IS31_write_register( addr, ISSI_REG_PICTUREFRAME, 0x00 ); | ||
161 | // audio sync off | ||
162 | IS31_write_register( addr, ISSI_REG_AUDIOSYNC, 0x00 ); | ||
163 | |||
164 | // select bank 0 | ||
165 | IS31_write_register( addr, ISSI_COMMANDREGISTER, 0 ); | ||
166 | |||
167 | // turn off all LEDs in the LED control register | ||
168 | for ( int i = 0x00; i <= 0x11; i++ ) | ||
169 | { | ||
170 | IS31_write_register( addr, i, 0x00 ); | ||
171 | } | ||
172 | |||
173 | // turn off all LEDs in the blink control register (not really needed) | ||
174 | for ( int i = 0x12; i <= 0x23; i++ ) | ||
175 | { | ||
176 | IS31_write_register( addr, i, 0x00 ); | ||
177 | } | ||
178 | |||
179 | // set PWM on all LEDs to 0 | ||
180 | for ( int i = 0x24; i <= 0xB3; i++ ) | ||
181 | { | ||
182 | IS31_write_register( addr, i, 0x00 ); | ||
183 | } | ||
184 | |||
185 | // select "function register" bank | ||
186 | IS31_write_register( addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG ); | ||
187 | |||
188 | // disable software shutdown | ||
189 | IS31_write_register( addr, ISSI_REG_SHUTDOWN, 0x01 ); | ||
190 | |||
191 | // select bank 0 and leave it selected. | ||
192 | // most usage after initialization is just writing PWM buffers in bank 0 | ||
193 | // as there's not much point in double-buffering | ||
194 | IS31_write_register( addr, ISSI_COMMANDREGISTER, 0 ); | ||
195 | |||
196 | } | ||
197 | |||
198 | void IS31_set_color( int index, uint8_t red, uint8_t green, uint8_t blue ) | ||
199 | { | ||
200 | if ( index >= 0 && index < DRIVER_LED_TOTAL ) { | ||
201 | is31_led led = g_is31_leds[index]; | ||
202 | |||
203 | // Subtract 0x24 to get the second index of g_pwm_buffer | ||
204 | g_pwm_buffer[led.driver][led.r - 0x24] = red; | ||
205 | g_pwm_buffer[led.driver][led.g - 0x24] = green; | ||
206 | g_pwm_buffer[led.driver][led.b - 0x24] = blue; | ||
207 | g_pwm_buffer_update_required = true; | ||
208 | } | ||
209 | } | ||
210 | |||
211 | void IS31_set_color_all( uint8_t red, uint8_t green, uint8_t blue ) | ||
212 | { | ||
213 | for ( int i = 0; i < DRIVER_LED_TOTAL; i++ ) | ||
214 | { | ||
215 | IS31_set_color( i, red, green, blue ); | ||
216 | } | ||
217 | } | ||
218 | |||
219 | void IS31_set_led_control_register( uint8_t index, bool red, bool green, bool blue ) | ||
220 | { | ||
221 | is31_led led = g_is31_leds[index]; | ||
222 | |||
223 | uint8_t control_register_r = (led.r - 0x24) / 8; | ||
224 | uint8_t control_register_g = (led.g - 0x24) / 8; | ||
225 | uint8_t control_register_b = (led.b - 0x24) / 8; | ||
226 | uint8_t bit_r = (led.r - 0x24) % 8; | ||
227 | uint8_t bit_g = (led.g - 0x24) % 8; | ||
228 | uint8_t bit_b = (led.b - 0x24) % 8; | ||
229 | |||
230 | if ( red ) { | ||
231 | g_led_control_registers[led.driver][control_register_r] |= (1 << bit_r); | ||
232 | } else { | ||
233 | g_led_control_registers[led.driver][control_register_r] &= ~(1 << bit_r); | ||
234 | } | ||
235 | if ( green ) { | ||
236 | g_led_control_registers[led.driver][control_register_g] |= (1 << bit_g); | ||
237 | } else { | ||
238 | g_led_control_registers[led.driver][control_register_g] &= ~(1 << bit_g); | ||
239 | } | ||
240 | if ( blue ) { | ||
241 | g_led_control_registers[led.driver][control_register_b] |= (1 << bit_b); | ||
242 | } else { | ||
243 | g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b); | ||
244 | } | ||
245 | |||
246 | g_led_control_registers_update_required = true; | ||
247 | |||
248 | } | ||
249 | |||
250 | void IS31_update_pwm_buffers( uint8_t addr1, uint8_t addr2 ) | ||
251 | { | ||
252 | if ( g_pwm_buffer_update_required ) | ||
253 | { | ||
254 | IS31_write_pwm_buffer( addr1, g_pwm_buffer[0] ); | ||
255 | IS31_write_pwm_buffer( addr2, g_pwm_buffer[1] ); | ||
256 | } | ||
257 | g_pwm_buffer_update_required = false; | ||
258 | } | ||
259 | |||
260 | void IS31_update_led_control_registers( uint8_t addr1, uint8_t addr2 ) | ||
261 | { | ||
262 | if ( g_led_control_registers_update_required ) | ||
263 | { | ||
264 | for ( int i=0; i<18; i++ ) | ||
265 | { | ||
266 | IS31_write_register(addr1, i, g_led_control_registers[0][i] ); | ||
267 | IS31_write_register(addr2, i, g_led_control_registers[1][i] ); | ||
268 | } | ||
269 | } | ||
270 | } | ||
271 | |||
diff --git a/drivers/avr/is31fl3731.h b/drivers/is31fl3731.h index 3d30fc67b..9e195c240 100644 --- a/drivers/avr/is31fl3731.h +++ b/drivers/is31fl3731.h | |||
@@ -23,7 +23,7 @@ | |||
23 | #include <stdbool.h> | 23 | #include <stdbool.h> |
24 | 24 | ||
25 | typedef struct is31_led { | 25 | typedef struct is31_led { |
26 | uint8_t driver:2; | 26 | uint8_t driver:2; |
27 | uint8_t r; | 27 | uint8_t r; |
28 | uint8_t g; | 28 | uint8_t g; |
29 | uint8_t b; | 29 | uint8_t b; |
@@ -31,21 +31,21 @@ typedef struct is31_led { | |||
31 | 31 | ||
32 | extern const is31_led g_is31_leds[DRIVER_LED_TOTAL]; | 32 | extern const is31_led g_is31_leds[DRIVER_LED_TOTAL]; |
33 | 33 | ||
34 | void IS31FL3731_init( uint8_t addr ); | 34 | void IS31_init( uint8_t addr ); |
35 | void IS31FL3731_write_register( uint8_t addr, uint8_t reg, uint8_t data ); | 35 | void IS31_write_register( uint8_t addr, uint8_t reg, uint8_t data ); |
36 | void IS31FL3731_write_pwm_buffer( uint8_t addr, uint8_t *pwm_buffer ); | 36 | void IS31_write_pwm_buffer( uint8_t addr, uint8_t *pwm_buffer ); |
37 | 37 | ||
38 | void IS31FL3731_set_color( int index, uint8_t red, uint8_t green, uint8_t blue ); | 38 | void IS31_set_color( int index, uint8_t red, uint8_t green, uint8_t blue ); |
39 | void IS31FL3731_set_color_all( uint8_t red, uint8_t green, uint8_t blue ); | 39 | void IS31_set_color_all( uint8_t red, uint8_t green, uint8_t blue ); |
40 | 40 | ||
41 | void IS31FL3731_set_led_control_register( uint8_t index, bool red, bool green, bool blue ); | 41 | void IS31_set_led_control_register( uint8_t index, bool red, bool green, bool blue ); |
42 | 42 | ||
43 | // This should not be called from an interrupt | 43 | // This should not be called from an interrupt |
44 | // (eg. from a timer interrupt). | 44 | // (eg. from a timer interrupt). |
45 | // Call this while idle (in between matrix scans). | 45 | // Call this while idle (in between matrix scans). |
46 | // If the buffer is dirty, it will update the driver with the buffer. | 46 | // If the buffer is dirty, it will update the driver with the buffer. |
47 | void IS31FL3731_update_pwm_buffers( uint8_t addr1, uint8_t addr2 ); | 47 | void IS31_update_pwm_buffers( uint8_t addr1, uint8_t addr2 ); |
48 | void IS31FL3731_update_led_control_registers( uint8_t addr1, uint8_t addr2 ); | 48 | void IS31_update_led_control_registers( uint8_t addr1, uint8_t addr2 ); |
49 | 49 | ||
50 | #define C1_1 0x24 | 50 | #define C1_1 0x24 |
51 | #define C1_2 0x25 | 51 | #define C1_2 0x25 |
diff --git a/quantum/rgb_matrix.c b/quantum/rgb_matrix.c index b4bbc3dc0..70ad1a178 100644 --- a/quantum/rgb_matrix.c +++ b/quantum/rgb_matrix.c | |||
@@ -106,16 +106,16 @@ void map_row_column_to_led( uint8_t row, uint8_t column, uint8_t *led_i, uint8_t | |||
106 | } | 106 | } |
107 | 107 | ||
108 | void rgb_matrix_update_pwm_buffers(void) { | 108 | void rgb_matrix_update_pwm_buffers(void) { |
109 | IS31FL3731_update_pwm_buffers( DRIVER_ADDR_1, DRIVER_ADDR_2 ); | 109 | IS31_update_pwm_buffers( DRIVER_ADDR_1, DRIVER_ADDR_2 ); |
110 | IS31FL3731_update_led_control_registers( DRIVER_ADDR_1, DRIVER_ADDR_2 ); | 110 | IS31_update_led_control_registers( DRIVER_ADDR_1, DRIVER_ADDR_2 ); |
111 | } | 111 | } |
112 | 112 | ||
113 | void rgb_matrix_set_color( int index, uint8_t red, uint8_t green, uint8_t blue ) { | 113 | void rgb_matrix_set_color( int index, uint8_t red, uint8_t green, uint8_t blue ) { |
114 | IS31FL3731_set_color( index, red, green, blue ); | 114 | IS31_set_color( index, red, green, blue ); |
115 | } | 115 | } |
116 | 116 | ||
117 | void rgb_matrix_set_color_all( uint8_t red, uint8_t green, uint8_t blue ) { | 117 | void rgb_matrix_set_color_all( uint8_t red, uint8_t green, uint8_t blue ) { |
118 | IS31FL3731_set_color_all( red, green, blue ); | 118 | IS31_set_color_all( red, green, blue ); |
119 | } | 119 | } |
120 | 120 | ||
121 | bool process_rgb_matrix(uint16_t keycode, keyrecord_t *record) { | 121 | bool process_rgb_matrix(uint16_t keycode, keyrecord_t *record) { |
@@ -752,16 +752,16 @@ void rgb_matrix_init(void) { | |||
752 | void rgb_matrix_setup_drivers(void) { | 752 | void rgb_matrix_setup_drivers(void) { |
753 | // Initialize TWI | 753 | // Initialize TWI |
754 | i2c_init(); | 754 | i2c_init(); |
755 | IS31FL3731_init( DRIVER_ADDR_1 ); | 755 | IS31_init( DRIVER_ADDR_1 ); |
756 | IS31FL3731_init( DRIVER_ADDR_2 ); | 756 | IS31_init( DRIVER_ADDR_2 ); |
757 | 757 | ||
758 | for ( int index = 0; index < DRIVER_LED_TOTAL; index++ ) { | 758 | for ( int index = 0; index < DRIVER_LED_TOTAL; index++ ) { |
759 | bool enabled = true; | 759 | bool enabled = true; |
760 | // This only caches it for later | 760 | // This only caches it for later |
761 | IS31FL3731_set_led_control_register( index, enabled, enabled, enabled ); | 761 | IS31_set_led_control_register( index, enabled, enabled, enabled ); |
762 | } | 762 | } |
763 | // This actually updates the LED drivers | 763 | // This actually updates the LED drivers |
764 | IS31FL3731_update_led_control_registers( DRIVER_ADDR_1, DRIVER_ADDR_2 ); | 764 | IS31_update_led_control_registers( DRIVER_ADDR_1, DRIVER_ADDR_2 ); |
765 | } | 765 | } |
766 | 766 | ||
767 | // Deals with the messy details of incrementing an integer | 767 | // Deals with the messy details of incrementing an integer |
@@ -811,11 +811,11 @@ void rgb_matrix_test_led( uint8_t index, bool red, bool green, bool blue ) { | |||
811 | { | 811 | { |
812 | if ( i == index ) | 812 | if ( i == index ) |
813 | { | 813 | { |
814 | IS31FL3731_set_led_control_register( i, red, green, blue ); | 814 | IS31_set_led_control_register( i, red, green, blue ); |
815 | } | 815 | } |
816 | else | 816 | else |
817 | { | 817 | { |
818 | IS31FL3731_set_led_control_register( i, false, false, false ); | 818 | IS31_set_led_control_register( i, false, false, false ); |
819 | } | 819 | } |
820 | } | 820 | } |
821 | } | 821 | } |