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