is31fl3746a-mono.c (7436B)
1 /* Copyright 2017 Jason Williams 2 * Copyright 2018 Jack Humbert 3 * Copyright 2018 Yiancar 4 * Copyright 2020 MelGeek 5 * Copyright 2021 MasterSpoon 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 "is31fl3746a-mono.h" 22 #include "i2c_master.h" 23 #include "gpio.h" 24 #include "wait.h" 25 26 #define IS31FL3746A_PWM_REGISTER_COUNT 72 27 #define IS31FL3746A_SCALING_REGISTER_COUNT 72 28 29 #ifndef IS31FL3746A_I2C_TIMEOUT 30 # define IS31FL3746A_I2C_TIMEOUT 100 31 #endif 32 33 #ifndef IS31FL3746A_I2C_PERSISTENCE 34 # define IS31FL3746A_I2C_PERSISTENCE 0 35 #endif 36 37 #ifndef IS31FL3746A_CONFIGURATION 38 # define IS31FL3746A_CONFIGURATION 0x01 39 #endif 40 41 #ifndef IS31FL3746A_PWM_FREQUENCY 42 # define IS31FL3746A_PWM_FREQUENCY IS31FL3746A_PWM_FREQUENCY_29K_HZ 43 #endif 44 45 #ifndef IS31FL3746A_SW_PULLDOWN 46 # define IS31FL3746A_SW_PULLDOWN IS31FL3746A_PDR_2K_OHM_SW_OFF 47 #endif 48 49 #ifndef IS31FL3746A_CS_PULLUP 50 # define IS31FL3746A_CS_PULLUP IS31FL3746A_PUR_2K_OHM_CS_OFF 51 #endif 52 53 #ifndef IS31FL3746A_GLOBAL_CURRENT 54 # define IS31FL3746A_GLOBAL_CURRENT 0xFF 55 #endif 56 57 const uint8_t i2c_addresses[IS31FL3746A_DRIVER_COUNT] = { 58 IS31FL3746A_I2C_ADDRESS_1, 59 #ifdef IS31FL3746A_I2C_ADDRESS_2 60 IS31FL3746A_I2C_ADDRESS_2, 61 # ifdef IS31FL3746A_I2C_ADDRESS_3 62 IS31FL3746A_I2C_ADDRESS_3, 63 # ifdef IS31FL3746A_I2C_ADDRESS_4 64 IS31FL3746A_I2C_ADDRESS_4, 65 # endif 66 # endif 67 #endif 68 }; 69 70 typedef struct is31fl3746a_driver_t { 71 uint8_t pwm_buffer[IS31FL3746A_PWM_REGISTER_COUNT]; 72 bool pwm_buffer_dirty; 73 uint8_t scaling_buffer[IS31FL3746A_SCALING_REGISTER_COUNT]; 74 bool scaling_buffer_dirty; 75 } PACKED is31fl3746a_driver_t; 76 77 is31fl3746a_driver_t driver_buffers[IS31FL3746A_DRIVER_COUNT] = {{ 78 .pwm_buffer = {0}, 79 .pwm_buffer_dirty = false, 80 .scaling_buffer = {0}, 81 .scaling_buffer_dirty = false, 82 }}; 83 84 void is31fl3746a_write_register(uint8_t index, uint8_t reg, uint8_t data) { 85 #if IS31FL3746A_I2C_PERSISTENCE > 0 86 for (uint8_t i = 0; i < IS31FL3746A_I2C_PERSISTENCE; i++) { 87 if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3746A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; 88 } 89 #else 90 i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3746A_I2C_TIMEOUT); 91 #endif 92 } 93 94 void is31fl3746a_select_page(uint8_t index, uint8_t page) { 95 is31fl3746a_write_register(index, IS31FL3746A_REG_COMMAND_WRITE_LOCK, IS31FL3746A_COMMAND_WRITE_LOCK_MAGIC); 96 is31fl3746a_write_register(index, IS31FL3746A_REG_COMMAND, page); 97 } 98 99 void is31fl3746a_write_pwm_buffer(uint8_t index) { 100 // Assumes page 0 is already selected. 101 // Transmit PWM registers in 4 transfers of 18 bytes. 102 103 // Iterate over the pwm_buffer contents at 18 byte intervals. 104 for (uint8_t i = 0; i < IS31FL3746A_PWM_REGISTER_COUNT; i += 18) { 105 #if IS31FL3746A_I2C_PERSISTENCE > 0 106 for (uint8_t j = 0; j < IS31FL3746A_I2C_PERSISTENCE; j++) { 107 if (i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3746A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; 108 } 109 #else 110 i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3746A_I2C_TIMEOUT); 111 #endif 112 } 113 } 114 115 void is31fl3746a_init_drivers(void) { 116 i2c_init(); 117 118 #if defined(IS31FL3746A_SDB_PIN) 119 gpio_set_pin_output(IS31FL3746A_SDB_PIN); 120 gpio_write_pin_high(IS31FL3746A_SDB_PIN); 121 #endif 122 123 for (uint8_t i = 0; i < IS31FL3746A_DRIVER_COUNT; i++) { 124 is31fl3746a_init(i); 125 } 126 127 for (int i = 0; i < IS31FL3746A_LED_COUNT; i++) { 128 is31fl3746a_set_scaling_register(i, 0xFF); 129 } 130 131 for (uint8_t i = 0; i < IS31FL3746A_DRIVER_COUNT; i++) { 132 is31fl3746a_update_scaling_registers(i); 133 } 134 } 135 136 void is31fl3746a_init(uint8_t index) { 137 // In order to avoid the LEDs being driven with garbage data 138 // in the LED driver's PWM registers, shutdown is enabled last. 139 // Set up the mode and other settings, clear the PWM registers, 140 // then disable software shutdown. 141 142 is31fl3746a_select_page(index, IS31FL3746A_COMMAND_SCALING); 143 144 // Turn off all LEDs. 145 for (uint8_t i = 0; i < IS31FL3746A_SCALING_REGISTER_COUNT; i++) { 146 is31fl3746a_write_register(index, i + 1, 0x00); 147 } 148 149 is31fl3746a_select_page(index, IS31FL3746A_COMMAND_PWM); 150 151 for (uint8_t i = 0; i < IS31FL3746A_PWM_REGISTER_COUNT; i++) { 152 is31fl3746a_write_register(index, i + 1, 0x00); 153 } 154 155 is31fl3746a_select_page(index, IS31FL3746A_COMMAND_FUNCTION); 156 157 is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_PULLDOWNUP, (IS31FL3746A_SW_PULLDOWN << 4) | IS31FL3746A_CS_PULLUP); 158 is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3746A_GLOBAL_CURRENT); 159 is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_PWM_ENABLE, 0x01); 160 is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_PWM_FREQUENCY, IS31FL3746A_PWM_FREQUENCY); 161 is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_CONFIGURATION, IS31FL3746A_CONFIGURATION); 162 163 // Wait 10ms to ensure the device has woken up. 164 wait_ms(10); 165 } 166 167 void is31fl3746a_set_value(int index, uint8_t value) { 168 is31fl3746a_led_t led; 169 170 if (index >= 0 && index < IS31FL3746A_LED_COUNT) { 171 memcpy_P(&led, (&g_is31fl3746a_leds[index]), sizeof(led)); 172 173 if (driver_buffers[led.driver].pwm_buffer[led.v] == value) { 174 return; 175 } 176 177 driver_buffers[led.driver].pwm_buffer[led.v] = value; 178 driver_buffers[led.driver].pwm_buffer_dirty = true; 179 } 180 } 181 182 void is31fl3746a_set_value_all(uint8_t value) { 183 for (int i = 0; i < IS31FL3746A_LED_COUNT; i++) { 184 is31fl3746a_set_value(i, value); 185 } 186 } 187 188 void is31fl3746a_set_scaling_register(uint8_t index, uint8_t value) { 189 is31fl3746a_led_t led; 190 memcpy_P(&led, (&g_is31fl3746a_leds[index]), sizeof(led)); 191 192 driver_buffers[led.driver].scaling_buffer[led.v] = value; 193 driver_buffers[led.driver].scaling_buffer_dirty = true; 194 } 195 196 void is31fl3746a_update_pwm_buffers(uint8_t index) { 197 if (driver_buffers[index].pwm_buffer_dirty) { 198 is31fl3746a_select_page(index, IS31FL3746A_COMMAND_PWM); 199 200 is31fl3746a_write_pwm_buffer(index); 201 202 driver_buffers[index].pwm_buffer_dirty = false; 203 } 204 } 205 206 void is31fl3746a_update_scaling_registers(uint8_t index) { 207 if (driver_buffers[index].scaling_buffer_dirty) { 208 is31fl3746a_select_page(index, IS31FL3746A_COMMAND_SCALING); 209 210 for (uint8_t i = 0; i < IS31FL3746A_SCALING_REGISTER_COUNT; i++) { 211 is31fl3746a_write_register(index, i + 1, driver_buffers[index].scaling_buffer[i]); 212 } 213 214 driver_buffers[index].scaling_buffer_dirty = false; 215 } 216 } 217 218 void is31fl3746a_flush(void) { 219 for (uint8_t i = 0; i < IS31FL3746A_DRIVER_COUNT; i++) { 220 is31fl3746a_update_pwm_buffers(i); 221 } 222 }