is31fl3731.c (9033B)
1 /* Copyright 2017 Jason Williams 2 * Copyright 2018 Jack Humbert 3 * Copyright 2021 Doni Crosby 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.h" 20 #include "i2c_master.h" 21 #include "gpio.h" 22 #include "wait.h" 23 24 #define IS31FL3731_PWM_REGISTER_COUNT 144 25 #define IS31FL3731_LED_CONTROL_REGISTER_COUNT 18 26 27 #ifndef IS31FL3731_I2C_TIMEOUT 28 # define IS31FL3731_I2C_TIMEOUT 100 29 #endif 30 31 #ifndef IS31FL3731_I2C_PERSISTENCE 32 # define IS31FL3731_I2C_PERSISTENCE 0 33 #endif 34 35 const uint8_t i2c_addresses[IS31FL3731_DRIVER_COUNT] = { 36 IS31FL3731_I2C_ADDRESS_1, 37 #ifdef IS31FL3731_I2C_ADDRESS_2 38 IS31FL3731_I2C_ADDRESS_2, 39 # ifdef IS31FL3731_I2C_ADDRESS_3 40 IS31FL3731_I2C_ADDRESS_3, 41 # ifdef IS31FL3731_I2C_ADDRESS_4 42 IS31FL3731_I2C_ADDRESS_4, 43 # endif 44 # endif 45 #endif 46 }; 47 48 // These buffers match the IS31FL3731 PWM registers 0x24-0xB3. 49 // Storing them like this is optimal for I2C transfers to the registers. 50 // We could optimize this and take out the unused registers from these 51 // buffers and the transfers in is31fl3731_write_pwm_buffer() but it's 52 // probably not worth the extra complexity. 53 typedef struct is31fl3731_driver_t { 54 uint8_t pwm_buffer[IS31FL3731_PWM_REGISTER_COUNT]; 55 bool pwm_buffer_dirty; 56 uint8_t led_control_buffer[IS31FL3731_LED_CONTROL_REGISTER_COUNT]; 57 bool led_control_buffer_dirty; 58 } PACKED is31fl3731_driver_t; 59 60 is31fl3731_driver_t driver_buffers[IS31FL3731_DRIVER_COUNT] = {{ 61 .pwm_buffer = {0}, 62 .pwm_buffer_dirty = false, 63 .led_control_buffer = {0}, 64 .led_control_buffer_dirty = false, 65 }}; 66 67 void is31fl3731_write_register(uint8_t index, uint8_t reg, uint8_t data) { 68 #if IS31FL3731_I2C_PERSISTENCE > 0 69 for (uint8_t i = 0; i < IS31FL3731_I2C_PERSISTENCE; i++) { 70 if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3731_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; 71 } 72 #else 73 i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3731_I2C_TIMEOUT); 74 #endif 75 } 76 77 void is31fl3731_select_page(uint8_t index, uint8_t page) { 78 is31fl3731_write_register(index, IS31FL3731_REG_COMMAND, page); 79 } 80 81 void is31fl3731_write_pwm_buffer(uint8_t index) { 82 // Assumes page 0 is already selected. 83 // Transmit PWM registers in 9 transfers of 16 bytes. 84 85 // Iterate over the pwm_buffer contents at 16 byte intervals. 86 for (uint8_t i = 0; i < IS31FL3731_PWM_REGISTER_COUNT; i += 16) { 87 #if IS31FL3731_I2C_PERSISTENCE > 0 88 for (uint8_t j = 0; j < IS31FL3731_I2C_PERSISTENCE; j++) { 89 if (i2c_write_register(i2c_addresses[index] << 1, IS31FL3731_FRAME_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3731_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; 90 } 91 #else 92 i2c_write_register(i2c_addresses[index] << 1, IS31FL3731_FRAME_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3731_I2C_TIMEOUT); 93 #endif 94 } 95 } 96 97 void is31fl3731_init_drivers(void) { 98 i2c_init(); 99 100 #if defined(IS31FL3731_SDB_PIN) 101 gpio_set_pin_output(IS31FL3731_SDB_PIN); 102 gpio_write_pin_high(IS31FL3731_SDB_PIN); 103 #endif 104 105 for (uint8_t i = 0; i < IS31FL3731_DRIVER_COUNT; i++) { 106 is31fl3731_init(i); 107 } 108 109 for (int i = 0; i < IS31FL3731_LED_COUNT; i++) { 110 is31fl3731_set_led_control_register(i, true, true, true); 111 } 112 113 for (uint8_t i = 0; i < IS31FL3731_DRIVER_COUNT; i++) { 114 is31fl3731_update_led_control_registers(i); 115 } 116 } 117 118 void is31fl3731_init(uint8_t index) { 119 // In order to avoid the LEDs being driven with garbage data 120 // in the LED driver's PWM registers, first enable software shutdown, 121 // then set up the mode and other settings, clear the PWM registers, 122 // then disable software shutdown. 123 124 is31fl3731_select_page(index, IS31FL3731_COMMAND_FUNCTION); 125 126 // enable software shutdown 127 is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_SHUTDOWN, 0x00); 128 #ifdef IS31FL3731_DEGHOST // set to enable de-ghosting of the array 129 is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_GHOST_IMAGE_PREVENTION, IS31FL3731_GHOST_IMAGE_PREVENTION_GEN); 130 #endif 131 132 // this delay was copied from other drivers, might not be needed 133 wait_ms(10); 134 135 // picture mode 136 is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_CONFIG, IS31FL3731_CONFIG_MODE_PICTURE); 137 // display frame 0 138 is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_PICTURE_DISPLAY, 0x00); 139 // audio sync off 140 is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_AUDIO_SYNC, 0x00); 141 142 is31fl3731_select_page(index, IS31FL3731_COMMAND_FRAME_1); 143 144 // turn off all LEDs in the LED control register 145 for (uint8_t i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) { 146 is31fl3731_write_register(index, IS31FL3731_FRAME_REG_LED_CONTROL + i, 0x00); 147 } 148 149 // turn off all LEDs in the blink control register (not really needed) 150 for (uint8_t i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) { 151 is31fl3731_write_register(index, IS31FL3731_FRAME_REG_BLINK_CONTROL + i, 0x00); 152 } 153 154 // set PWM on all LEDs to 0 155 for (uint8_t i = 0; i < IS31FL3731_PWM_REGISTER_COUNT; i++) { 156 is31fl3731_write_register(index, IS31FL3731_FRAME_REG_PWM + i, 0x00); 157 } 158 159 is31fl3731_select_page(index, IS31FL3731_COMMAND_FUNCTION); 160 161 // disable software shutdown 162 is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_SHUTDOWN, 0x01); 163 164 // select page 0 and leave it selected. 165 // most usage after initialization is just writing PWM buffers in page 0 166 // as there's not much point in double-buffering 167 is31fl3731_select_page(index, IS31FL3731_COMMAND_FRAME_1); 168 } 169 170 void is31fl3731_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) { 171 is31fl3731_led_t led; 172 173 if (index >= 0 && index < IS31FL3731_LED_COUNT) { 174 memcpy_P(&led, (&g_is31fl3731_leds[index]), sizeof(led)); 175 176 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) { 177 return; 178 } 179 180 driver_buffers[led.driver].pwm_buffer[led.r] = red; 181 driver_buffers[led.driver].pwm_buffer[led.g] = green; 182 driver_buffers[led.driver].pwm_buffer[led.b] = blue; 183 driver_buffers[led.driver].pwm_buffer_dirty = true; 184 } 185 } 186 187 void is31fl3731_set_color_all(uint8_t red, uint8_t green, uint8_t blue) { 188 for (int i = 0; i < IS31FL3731_LED_COUNT; i++) { 189 is31fl3731_set_color(i, red, green, blue); 190 } 191 } 192 193 void is31fl3731_set_led_control_register(uint8_t index, bool red, bool green, bool blue) { 194 is31fl3731_led_t led; 195 memcpy_P(&led, (&g_is31fl3731_leds[index]), sizeof(led)); 196 197 uint8_t control_register_r = led.r / 8; 198 uint8_t control_register_g = led.g / 8; 199 uint8_t control_register_b = led.b / 8; 200 uint8_t bit_r = led.r % 8; 201 uint8_t bit_g = led.g % 8; 202 uint8_t bit_b = led.b % 8; 203 204 if (red) { 205 driver_buffers[led.driver].led_control_buffer[control_register_r] |= (1 << bit_r); 206 } else { 207 driver_buffers[led.driver].led_control_buffer[control_register_r] &= ~(1 << bit_r); 208 } 209 if (green) { 210 driver_buffers[led.driver].led_control_buffer[control_register_g] |= (1 << bit_g); 211 } else { 212 driver_buffers[led.driver].led_control_buffer[control_register_g] &= ~(1 << bit_g); 213 } 214 if (blue) { 215 driver_buffers[led.driver].led_control_buffer[control_register_b] |= (1 << bit_b); 216 } else { 217 driver_buffers[led.driver].led_control_buffer[control_register_b] &= ~(1 << bit_b); 218 } 219 220 driver_buffers[led.driver].led_control_buffer_dirty = true; 221 } 222 223 void is31fl3731_update_pwm_buffers(uint8_t index) { 224 if (driver_buffers[index].pwm_buffer_dirty) { 225 is31fl3731_write_pwm_buffer(index); 226 227 driver_buffers[index].pwm_buffer_dirty = false; 228 } 229 } 230 231 void is31fl3731_update_led_control_registers(uint8_t index) { 232 if (driver_buffers[index].led_control_buffer_dirty) { 233 for (uint8_t i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) { 234 is31fl3731_write_register(index, i, driver_buffers[index].led_control_buffer[i]); 235 } 236 237 driver_buffers[index].led_control_buffer_dirty = false; 238 } 239 } 240 241 void is31fl3731_flush(void) { 242 for (uint8_t i = 0; i < IS31FL3731_DRIVER_COUNT; i++) { 243 is31fl3731_update_pwm_buffers(i); 244 } 245 }