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