is31fl3742a-mono.c (7333B)
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 "is31fl3742a-mono.h" 22 #include "i2c_master.h" 23 #include "gpio.h" 24 #include "wait.h" 25 26 #define IS31FL3742A_PWM_REGISTER_COUNT 180 27 #define IS31FL3742A_SCALING_REGISTER_COUNT 180 28 29 #ifndef IS31FL3742A_I2C_TIMEOUT 30 # define IS31FL3742A_I2C_TIMEOUT 100 31 #endif 32 33 #ifndef IS31FL3742A_I2C_PERSISTENCE 34 # define IS31FL3742A_I2C_PERSISTENCE 0 35 #endif 36 37 #ifndef IS31FL3742A_CONFIGURATION 38 # define IS31FL3742A_CONFIGURATION 0x31 39 #endif 40 41 #ifndef IS31FL3742A_PWM_FREQUENCY 42 # define IS31FL3742A_PWM_FREQUENCY IS31FL3742A_PWM_FREQUENCY_29K_HZ 43 #endif 44 45 #ifndef IS31FL3742A_SW_PULLDOWN 46 # define IS31FL3742A_SW_PULLDOWN IS31FL3742A_PDR_8K_OHM 47 #endif 48 49 #ifndef IS31FL3742A_CS_PULLUP 50 # define IS31FL3742A_CS_PULLUP IS31FL3742A_PUR_8K_OHM 51 #endif 52 53 #ifndef IS31FL3742A_GLOBAL_CURRENT 54 # define IS31FL3742A_GLOBAL_CURRENT 0xFF 55 #endif 56 57 const uint8_t i2c_addresses[IS31FL3742A_DRIVER_COUNT] = { 58 IS31FL3742A_I2C_ADDRESS_1, 59 #ifdef IS31FL3742A_I2C_ADDRESS_2 60 IS31FL3742A_I2C_ADDRESS_2, 61 # ifdef IS31FL3742A_I2C_ADDRESS_3 62 IS31FL3742A_I2C_ADDRESS_3, 63 # ifdef IS31FL3742A_I2C_ADDRESS_4 64 IS31FL3742A_I2C_ADDRESS_4, 65 # endif 66 # endif 67 #endif 68 }; 69 70 typedef struct is31fl3742a_driver_t { 71 uint8_t pwm_buffer[IS31FL3742A_PWM_REGISTER_COUNT]; 72 bool pwm_buffer_dirty; 73 uint8_t scaling_buffer[IS31FL3742A_SCALING_REGISTER_COUNT]; 74 bool scaling_buffer_dirty; 75 } PACKED is31fl3742a_driver_t; 76 77 is31fl3742a_driver_t driver_buffers[IS31FL3742A_DRIVER_COUNT] = {{ 78 .pwm_buffer = {0}, 79 .pwm_buffer_dirty = false, 80 .scaling_buffer = {0}, 81 .scaling_buffer_dirty = false, 82 }}; 83 84 void is31fl3742a_write_register(uint8_t index, uint8_t reg, uint8_t data) { 85 #if IS31FL3742A_I2C_PERSISTENCE > 0 86 for (uint8_t i = 0; i < IS31FL3742A_I2C_PERSISTENCE; i++) { 87 if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3742A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; 88 } 89 #else 90 i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3742A_I2C_TIMEOUT); 91 #endif 92 } 93 94 void is31fl3742a_select_page(uint8_t index, uint8_t page) { 95 is31fl3742a_write_register(index, IS31FL3742A_REG_COMMAND_WRITE_LOCK, IS31FL3742A_COMMAND_WRITE_LOCK_MAGIC); 96 is31fl3742a_write_register(index, IS31FL3742A_REG_COMMAND, page); 97 } 98 99 void is31fl3742a_write_pwm_buffer(uint8_t index) { 100 // Assumes page 0 is already selected. 101 // Transmit PWM registers in 6 transfers of 30 bytes. 102 103 // Iterate over the pwm_buffer contents at 30 byte intervals. 104 for (uint8_t i = 0; i < IS31FL3742A_PWM_REGISTER_COUNT; i += 30) { 105 #if IS31FL3742A_I2C_PERSISTENCE > 0 106 for (uint8_t j = 0; j < IS31FL3742A_I2C_PERSISTENCE; j++) { 107 if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 30, IS31FL3742A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; 108 } 109 #else 110 i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 30, IS31FL3742A_I2C_TIMEOUT); 111 #endif 112 } 113 } 114 115 void is31fl3742a_init_drivers(void) { 116 i2c_init(); 117 118 #if defined(IS31FL3742A_SDB_PIN) 119 gpio_set_pin_output(IS31FL3742A_SDB_PIN); 120 gpio_write_pin_high(IS31FL3742A_SDB_PIN); 121 #endif 122 123 for (uint8_t i = 0; i < IS31FL3742A_DRIVER_COUNT; i++) { 124 is31fl3742a_init(i); 125 } 126 127 for (int i = 0; i < IS31FL3742A_LED_COUNT; i++) { 128 is31fl3742a_set_scaling_register(i, 0xFF); 129 } 130 131 for (uint8_t i = 0; i < IS31FL3742A_DRIVER_COUNT; i++) { 132 is31fl3742a_update_scaling_registers(i); 133 } 134 } 135 136 void is31fl3742a_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 is31fl3742a_select_page(index, IS31FL3742A_COMMAND_SCALING); 143 144 // Turn off all LEDs. 145 for (uint8_t i = 0; i < IS31FL3742A_SCALING_REGISTER_COUNT; i++) { 146 is31fl3742a_write_register(index, i, 0x00); 147 } 148 149 is31fl3742a_select_page(index, IS31FL3742A_COMMAND_PWM); 150 151 for (uint8_t i = 0; i < IS31FL3742A_PWM_REGISTER_COUNT; i++) { 152 is31fl3742a_write_register(index, i, 0x00); 153 } 154 155 is31fl3742a_select_page(index, IS31FL3742A_COMMAND_FUNCTION); 156 157 is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_PULLDOWNUP, (IS31FL3742A_SW_PULLDOWN << 4) | IS31FL3742A_CS_PULLUP); 158 is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3742A_GLOBAL_CURRENT); 159 is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_PWM_FREQUENCY, (IS31FL3742A_PWM_FREQUENCY & 0b0111)); 160 is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_CONFIGURATION, IS31FL3742A_CONFIGURATION); 161 162 // Wait 10ms to ensure the device has woken up. 163 wait_ms(10); 164 } 165 166 void is31fl3742a_set_value(int index, uint8_t value) { 167 is31fl3742a_led_t led; 168 169 if (index >= 0 && index < IS31FL3742A_LED_COUNT) { 170 memcpy_P(&led, (&g_is31fl3742a_leds[index]), sizeof(led)); 171 172 if (driver_buffers[led.driver].pwm_buffer[led.v] == value) { 173 return; 174 } 175 176 driver_buffers[led.driver].pwm_buffer[led.v] = value; 177 driver_buffers[led.driver].pwm_buffer_dirty = true; 178 } 179 } 180 181 void is31fl3742a_set_value_all(uint8_t value) { 182 for (int i = 0; i < IS31FL3742A_LED_COUNT; i++) { 183 is31fl3742a_set_value(i, value); 184 } 185 } 186 187 void is31fl3742a_set_scaling_register(uint8_t index, uint8_t value) { 188 is31fl3742a_led_t led; 189 memcpy_P(&led, (&g_is31fl3742a_leds[index]), sizeof(led)); 190 191 driver_buffers[led.driver].scaling_buffer[led.v] = value; 192 driver_buffers[led.driver].scaling_buffer_dirty = true; 193 } 194 195 void is31fl3742a_update_pwm_buffers(uint8_t index) { 196 if (driver_buffers[index].pwm_buffer_dirty) { 197 is31fl3742a_select_page(index, IS31FL3742A_COMMAND_PWM); 198 199 is31fl3742a_write_pwm_buffer(index); 200 201 driver_buffers[index].pwm_buffer_dirty = false; 202 } 203 } 204 205 void is31fl3742a_update_scaling_registers(uint8_t index) { 206 if (driver_buffers[index].scaling_buffer_dirty) { 207 is31fl3742a_select_page(index, IS31FL3742A_COMMAND_SCALING); 208 209 for (uint8_t i = 0; i < IS31FL3742A_SCALING_REGISTER_COUNT; i++) { 210 is31fl3742a_write_register(index, i, driver_buffers[index].scaling_buffer[i]); 211 } 212 213 driver_buffers[index].scaling_buffer_dirty = false; 214 } 215 } 216 217 void is31fl3742a_flush(void) { 218 for (uint8_t i = 0; i < IS31FL3742A_DRIVER_COUNT; i++) { 219 is31fl3742a_update_pwm_buffers(i); 220 } 221 }