is31fl3733.c (9708B)
1 /* Copyright 2017 Jason Williams 2 * Copyright 2018 Jack Humbert 3 * Copyright 2018 Yiancar 4 * Copyright 2021 Doni Crosby 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 "is31fl3733.h" 21 #include "i2c_master.h" 22 #include "gpio.h" 23 #include "wait.h" 24 25 #define IS31FL3733_PWM_REGISTER_COUNT 192 26 #define IS31FL3733_LED_CONTROL_REGISTER_COUNT 24 27 28 #ifndef IS31FL3733_I2C_TIMEOUT 29 # define IS31FL3733_I2C_TIMEOUT 100 30 #endif 31 32 #ifndef IS31FL3733_I2C_PERSISTENCE 33 # define IS31FL3733_I2C_PERSISTENCE 0 34 #endif 35 36 #ifndef IS31FL3733_PWM_FREQUENCY 37 # define IS31FL3733_PWM_FREQUENCY IS31FL3733_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3733B only 38 #endif 39 40 #ifndef IS31FL3733_SW_PULLUP 41 # define IS31FL3733_SW_PULLUP IS31FL3733_PUR_0_OHM 42 #endif 43 44 #ifndef IS31FL3733_CS_PULLDOWN 45 # define IS31FL3733_CS_PULLDOWN IS31FL3733_PDR_0_OHM 46 #endif 47 48 #ifndef IS31FL3733_GLOBAL_CURRENT 49 # define IS31FL3733_GLOBAL_CURRENT 0xFF 50 #endif 51 52 #ifndef IS31FL3733_SYNC_1 53 # define IS31FL3733_SYNC_1 IS31FL3733_SYNC_NONE 54 #endif 55 #ifndef IS31FL3733_SYNC_2 56 # define IS31FL3733_SYNC_2 IS31FL3733_SYNC_NONE 57 #endif 58 #ifndef IS31FL3733_SYNC_3 59 # define IS31FL3733_SYNC_3 IS31FL3733_SYNC_NONE 60 #endif 61 #ifndef IS31FL3733_SYNC_4 62 # define IS31FL3733_SYNC_4 IS31FL3733_SYNC_NONE 63 #endif 64 65 const uint8_t i2c_addresses[IS31FL3733_DRIVER_COUNT] = { 66 IS31FL3733_I2C_ADDRESS_1, 67 #ifdef IS31FL3733_I2C_ADDRESS_2 68 IS31FL3733_I2C_ADDRESS_2, 69 # ifdef IS31FL3733_I2C_ADDRESS_3 70 IS31FL3733_I2C_ADDRESS_3, 71 # ifdef IS31FL3733_I2C_ADDRESS_4 72 IS31FL3733_I2C_ADDRESS_4, 73 # endif 74 # endif 75 #endif 76 }; 77 78 const uint8_t driver_sync[IS31FL3733_DRIVER_COUNT] = { 79 IS31FL3733_SYNC_1, 80 #ifdef IS31FL3733_I2C_ADDRESS_2 81 IS31FL3733_SYNC_2, 82 # ifdef IS31FL3733_I2C_ADDRESS_3 83 IS31FL3733_SYNC_3, 84 # ifdef IS31FL3733_I2C_ADDRESS_4 85 IS31FL3733_SYNC_4, 86 # endif 87 # endif 88 #endif 89 }; 90 91 // These buffers match the IS31FL3733 PWM registers. 92 // The control buffers match the page 0 LED On/Off registers. 93 // Storing them like this is optimal for I2C transfers to the registers. 94 // We could optimize this and take out the unused registers from these 95 // buffers and the transfers in is31fl3733_write_pwm_buffer() but it's 96 // probably not worth the extra complexity. 97 typedef struct is31fl3733_driver_t { 98 uint8_t pwm_buffer[IS31FL3733_PWM_REGISTER_COUNT]; 99 bool pwm_buffer_dirty; 100 uint8_t led_control_buffer[IS31FL3733_LED_CONTROL_REGISTER_COUNT]; 101 bool led_control_buffer_dirty; 102 } PACKED is31fl3733_driver_t; 103 104 is31fl3733_driver_t driver_buffers[IS31FL3733_DRIVER_COUNT] = {{ 105 .pwm_buffer = {0}, 106 .pwm_buffer_dirty = false, 107 .led_control_buffer = {0}, 108 .led_control_buffer_dirty = false, 109 }}; 110 111 void is31fl3733_write_register(uint8_t index, uint8_t reg, uint8_t data) { 112 #if IS31FL3733_I2C_PERSISTENCE > 0 113 for (uint8_t i = 0; i < IS31FL3733_I2C_PERSISTENCE; i++) { 114 if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3733_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; 115 } 116 #else 117 i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3733_I2C_TIMEOUT); 118 #endif 119 } 120 121 void is31fl3733_select_page(uint8_t index, uint8_t page) { 122 is31fl3733_write_register(index, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC); 123 is31fl3733_write_register(index, IS31FL3733_REG_COMMAND, page); 124 } 125 126 void is31fl3733_write_pwm_buffer(uint8_t index) { 127 // Assumes page 1 is already selected. 128 // Transmit PWM registers in 12 transfers of 16 bytes. 129 130 // Iterate over the pwm_buffer contents at 16 byte intervals. 131 for (uint8_t i = 0; i < IS31FL3733_PWM_REGISTER_COUNT; i += 16) { 132 #if IS31FL3733_I2C_PERSISTENCE > 0 133 for (uint8_t j = 0; j < IS31FL3733_I2C_PERSISTENCE; j++) { 134 if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3733_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; 135 } 136 #else 137 i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3733_I2C_TIMEOUT); 138 #endif 139 } 140 } 141 142 void is31fl3733_init_drivers(void) { 143 i2c_init(); 144 145 #if defined(IS31FL3733_SDB_PIN) 146 gpio_set_pin_output(IS31FL3733_SDB_PIN); 147 gpio_write_pin_high(IS31FL3733_SDB_PIN); 148 #endif 149 150 for (uint8_t i = 0; i < IS31FL3733_DRIVER_COUNT; i++) { 151 is31fl3733_init(i); 152 } 153 154 for (int i = 0; i < IS31FL3733_LED_COUNT; i++) { 155 is31fl3733_set_led_control_register(i, true, true, true); 156 } 157 158 for (uint8_t i = 0; i < IS31FL3733_DRIVER_COUNT; i++) { 159 is31fl3733_update_led_control_registers(i); 160 } 161 } 162 163 void is31fl3733_init(uint8_t index) { 164 // In order to avoid the LEDs being driven with garbage data 165 // in the LED driver's PWM registers, shutdown is enabled last. 166 // Set up the mode and other settings, clear the PWM registers, 167 // then disable software shutdown. 168 169 is31fl3733_select_page(index, IS31FL3733_COMMAND_LED_CONTROL); 170 171 // Turn off all LEDs. 172 for (uint8_t i = 0; i < IS31FL3733_LED_CONTROL_REGISTER_COUNT; i++) { 173 is31fl3733_write_register(index, i, 0x00); 174 } 175 176 is31fl3733_select_page(index, IS31FL3733_COMMAND_PWM); 177 178 // Set PWM on all LEDs to 0 179 // No need to setup Breath registers to PWM as that is the default. 180 for (uint8_t i = 0; i < IS31FL3733_PWM_REGISTER_COUNT; i++) { 181 is31fl3733_write_register(index, i, 0x00); 182 } 183 184 is31fl3733_select_page(index, IS31FL3733_COMMAND_FUNCTION); 185 186 uint8_t sync = driver_sync[index]; 187 188 // Set de-ghost pull-up resistors (SWx) 189 is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_SW_PULLUP, IS31FL3733_SW_PULLUP); 190 // Set de-ghost pull-down resistors (CSx) 191 is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_CS_PULLDOWN, IS31FL3733_CS_PULLDOWN); 192 // Set global current to maximum. 193 is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3733_GLOBAL_CURRENT); 194 // Disable software shutdown. 195 is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_CONFIGURATION, ((sync & 0b11) << 6) | ((IS31FL3733_PWM_FREQUENCY & 0b111) << 3) | 0x01); 196 197 // Wait 10ms to ensure the device has woken up. 198 wait_ms(10); 199 } 200 201 void is31fl3733_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) { 202 is31fl3733_led_t led; 203 204 if (index >= 0 && index < IS31FL3733_LED_COUNT) { 205 memcpy_P(&led, (&g_is31fl3733_leds[index]), sizeof(led)); 206 207 if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) { 208 return; 209 } 210 211 driver_buffers[led.driver].pwm_buffer[led.r] = red; 212 driver_buffers[led.driver].pwm_buffer[led.g] = green; 213 driver_buffers[led.driver].pwm_buffer[led.b] = blue; 214 driver_buffers[led.driver].pwm_buffer_dirty = true; 215 } 216 } 217 218 void is31fl3733_set_color_all(uint8_t red, uint8_t green, uint8_t blue) { 219 for (int i = 0; i < IS31FL3733_LED_COUNT; i++) { 220 is31fl3733_set_color(i, red, green, blue); 221 } 222 } 223 224 void is31fl3733_set_led_control_register(uint8_t index, bool red, bool green, bool blue) { 225 is31fl3733_led_t led; 226 memcpy_P(&led, (&g_is31fl3733_leds[index]), sizeof(led)); 227 228 uint8_t control_register_r = led.r / 8; 229 uint8_t control_register_g = led.g / 8; 230 uint8_t control_register_b = led.b / 8; 231 uint8_t bit_r = led.r % 8; 232 uint8_t bit_g = led.g % 8; 233 uint8_t bit_b = led.b % 8; 234 235 if (red) { 236 driver_buffers[led.driver].led_control_buffer[control_register_r] |= (1 << bit_r); 237 } else { 238 driver_buffers[led.driver].led_control_buffer[control_register_r] &= ~(1 << bit_r); 239 } 240 if (green) { 241 driver_buffers[led.driver].led_control_buffer[control_register_g] |= (1 << bit_g); 242 } else { 243 driver_buffers[led.driver].led_control_buffer[control_register_g] &= ~(1 << bit_g); 244 } 245 if (blue) { 246 driver_buffers[led.driver].led_control_buffer[control_register_b] |= (1 << bit_b); 247 } else { 248 driver_buffers[led.driver].led_control_buffer[control_register_b] &= ~(1 << bit_b); 249 } 250 251 driver_buffers[led.driver].led_control_buffer_dirty = true; 252 } 253 254 void is31fl3733_update_pwm_buffers(uint8_t index) { 255 if (driver_buffers[index].pwm_buffer_dirty) { 256 is31fl3733_select_page(index, IS31FL3733_COMMAND_PWM); 257 258 is31fl3733_write_pwm_buffer(index); 259 260 driver_buffers[index].pwm_buffer_dirty = false; 261 } 262 } 263 264 void is31fl3733_update_led_control_registers(uint8_t index) { 265 if (driver_buffers[index].led_control_buffer_dirty) { 266 is31fl3733_select_page(index, IS31FL3733_COMMAND_LED_CONTROL); 267 268 for (uint8_t i = 0; i < IS31FL3733_LED_CONTROL_REGISTER_COUNT; i++) { 269 is31fl3733_write_register(index, i, driver_buffers[index].led_control_buffer[i]); 270 } 271 272 driver_buffers[index].led_control_buffer_dirty = false; 273 } 274 } 275 276 void is31fl3733_flush(void) { 277 for (uint8_t i = 0; i < IS31FL3733_DRIVER_COUNT; i++) { 278 is31fl3733_update_pwm_buffers(i); 279 } 280 }