is31fl3737-mono.c (8096B)
1 /* Copyright 2017 Jason Williams 2 * Copyright 2018 Jack Humbert 3 * Copyright 2018 Yiancar 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 "is31fl3737-mono.h" 21 #include "i2c_master.h" 22 #include "gpio.h" 23 #include "wait.h" 24 25 #define IS31FL3737_PWM_REGISTER_COUNT 192 // actually 144 26 #define IS31FL3737_LED_CONTROL_REGISTER_COUNT 24 27 28 #ifndef IS31FL3737_I2C_TIMEOUT 29 # define IS31FL3737_I2C_TIMEOUT 100 30 #endif 31 32 #ifndef IS31FL3737_I2C_PERSISTENCE 33 # define IS31FL3737_I2C_PERSISTENCE 0 34 #endif 35 36 #ifndef IS31FL3737_PWM_FREQUENCY 37 # define IS31FL3737_PWM_FREQUENCY IS31FL3737_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3737B only 38 #endif 39 40 #ifndef IS31FL3737_SW_PULLUP 41 # define IS31FL3737_SW_PULLUP IS31FL3737_PUR_0_OHM 42 #endif 43 44 #ifndef IS31FL3737_CS_PULLDOWN 45 # define IS31FL3737_CS_PULLDOWN IS31FL3737_PDR_0_OHM 46 #endif 47 48 #ifndef IS31FL3737_GLOBAL_CURRENT 49 # define IS31FL3737_GLOBAL_CURRENT 0xFF 50 #endif 51 52 const uint8_t i2c_addresses[IS31FL3737_DRIVER_COUNT] = { 53 IS31FL3737_I2C_ADDRESS_1, 54 #ifdef IS31FL3737_I2C_ADDRESS_2 55 IS31FL3737_I2C_ADDRESS_2, 56 # ifdef IS31FL3737_I2C_ADDRESS_3 57 IS31FL3737_I2C_ADDRESS_3, 58 # ifdef IS31FL3737_I2C_ADDRESS_4 59 IS31FL3737_I2C_ADDRESS_4, 60 # endif 61 # endif 62 #endif 63 }; 64 65 // These buffers match the IS31FL3737 PWM registers. 66 // The control buffers match the page 0 LED On/Off registers. 67 // Storing them like this is optimal for I2C transfers to the registers. 68 // We could optimize this and take out the unused registers from these 69 // buffers and the transfers in is31fl3737_write_pwm_buffer() but it's 70 // probably not worth the extra complexity. 71 typedef struct is31fl3737_driver_t { 72 uint8_t pwm_buffer[IS31FL3737_PWM_REGISTER_COUNT]; 73 bool pwm_buffer_dirty; 74 uint8_t led_control_buffer[IS31FL3737_LED_CONTROL_REGISTER_COUNT]; 75 bool led_control_buffer_dirty; 76 } PACKED is31fl3737_driver_t; 77 78 is31fl3737_driver_t driver_buffers[IS31FL3737_DRIVER_COUNT] = {{ 79 .pwm_buffer = {0}, 80 .pwm_buffer_dirty = false, 81 .led_control_buffer = {0}, 82 .led_control_buffer_dirty = false, 83 }}; 84 85 void is31fl3737_write_register(uint8_t index, uint8_t reg, uint8_t data) { 86 #if IS31FL3737_I2C_PERSISTENCE > 0 87 for (uint8_t i = 0; i < IS31FL3737_I2C_PERSISTENCE; i++) { 88 if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3737_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; 89 } 90 #else 91 i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3737_I2C_TIMEOUT); 92 #endif 93 } 94 95 void is31fl3737_select_page(uint8_t index, uint8_t page) { 96 is31fl3737_write_register(index, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC); 97 is31fl3737_write_register(index, IS31FL3737_REG_COMMAND, page); 98 } 99 100 void is31fl3737_write_pwm_buffer(uint8_t index) { 101 // Assumes page 1 is already selected. 102 // Transmit PWM registers in 12 transfers of 16 bytes. 103 104 // Iterate over the pwm_buffer contents at 16 byte intervals. 105 for (uint8_t i = 0; i < IS31FL3737_PWM_REGISTER_COUNT; i += 16) { 106 #if IS31FL3737_I2C_PERSISTENCE > 0 107 for (uint8_t j = 0; j < IS31FL3737_I2C_PERSISTENCE; j++) { 108 if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3737_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; 109 } 110 #else 111 i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3737_I2C_TIMEOUT); 112 #endif 113 } 114 } 115 116 void is31fl3737_init_drivers(void) { 117 i2c_init(); 118 119 #if defined(IS31FL3737_SDB_PIN) 120 gpio_set_pin_output(IS31FL3737_SDB_PIN); 121 gpio_write_pin_high(IS31FL3737_SDB_PIN); 122 #endif 123 124 for (uint8_t i = 0; i < IS31FL3737_DRIVER_COUNT; i++) { 125 is31fl3737_init(i); 126 } 127 128 for (int i = 0; i < IS31FL3737_LED_COUNT; i++) { 129 is31fl3737_set_led_control_register(i, true); 130 } 131 132 for (uint8_t i = 0; i < IS31FL3737_DRIVER_COUNT; i++) { 133 is31fl3737_update_led_control_registers(i); 134 } 135 } 136 137 void is31fl3737_init(uint8_t index) { 138 // In order to avoid the LEDs being driven with garbage data 139 // in the LED driver's PWM registers, shutdown is enabled last. 140 // Set up the mode and other settings, clear the PWM registers, 141 // then disable software shutdown. 142 143 is31fl3737_select_page(index, IS31FL3737_COMMAND_LED_CONTROL); 144 145 // Turn off all LEDs. 146 for (uint8_t i = 0; i < IS31FL3737_LED_CONTROL_REGISTER_COUNT; i++) { 147 is31fl3737_write_register(index, i, 0x00); 148 } 149 150 is31fl3737_select_page(index, IS31FL3737_COMMAND_PWM); 151 152 // Set PWM on all LEDs to 0 153 // No need to setup Breath registers to PWM as that is the default. 154 for (uint8_t i = 0; i < IS31FL3737_PWM_REGISTER_COUNT; i++) { 155 is31fl3737_write_register(index, i, 0x00); 156 } 157 158 is31fl3737_select_page(index, IS31FL3737_COMMAND_FUNCTION); 159 160 // Set de-ghost pull-up resistors (SWx) 161 is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_SW_PULLUP, IS31FL3737_SW_PULLUP); 162 // Set de-ghost pull-down resistors (CSx) 163 is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_CS_PULLDOWN, IS31FL3737_CS_PULLDOWN); 164 // Set global current to maximum. 165 is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3737_GLOBAL_CURRENT); 166 // Disable software shutdown. 167 is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_CONFIGURATION, ((IS31FL3737_PWM_FREQUENCY & 0b111) << 3) | 0x01); 168 169 // Wait 10ms to ensure the device has woken up. 170 wait_ms(10); 171 } 172 173 void is31fl3737_set_value(int index, uint8_t value) { 174 is31fl3737_led_t led; 175 176 if (index >= 0 && index < IS31FL3737_LED_COUNT) { 177 memcpy_P(&led, (&g_is31fl3737_leds[index]), sizeof(led)); 178 179 if (driver_buffers[led.driver].pwm_buffer[led.v] == value) { 180 return; 181 } 182 183 driver_buffers[led.driver].pwm_buffer[led.v] = value; 184 driver_buffers[led.driver].pwm_buffer_dirty = true; 185 } 186 } 187 188 void is31fl3737_set_value_all(uint8_t value) { 189 for (int i = 0; i < IS31FL3737_LED_COUNT; i++) { 190 is31fl3737_set_value(i, value); 191 } 192 } 193 194 void is31fl3737_set_led_control_register(uint8_t index, bool value) { 195 is31fl3737_led_t led; 196 memcpy_P(&led, (&g_is31fl3737_leds[index]), sizeof(led)); 197 198 uint8_t control_register = led.v / 8; 199 uint8_t bit_value = led.v % 8; 200 201 if (value) { 202 driver_buffers[led.driver].led_control_buffer[control_register] |= (1 << bit_value); 203 } else { 204 driver_buffers[led.driver].led_control_buffer[control_register] &= ~(1 << bit_value); 205 } 206 207 driver_buffers[led.driver].led_control_buffer_dirty = true; 208 } 209 210 void is31fl3737_update_pwm_buffers(uint8_t index) { 211 if (driver_buffers[index].pwm_buffer_dirty) { 212 is31fl3737_select_page(index, IS31FL3737_COMMAND_PWM); 213 214 is31fl3737_write_pwm_buffer(index); 215 216 driver_buffers[index].pwm_buffer_dirty = false; 217 } 218 } 219 220 void is31fl3737_update_led_control_registers(uint8_t index) { 221 if (driver_buffers[index].led_control_buffer_dirty) { 222 is31fl3737_select_page(index, IS31FL3737_COMMAND_LED_CONTROL); 223 224 for (uint8_t i = 0; i < IS31FL3737_LED_CONTROL_REGISTER_COUNT; i++) { 225 is31fl3737_write_register(index, i, driver_buffers[index].led_control_buffer[i]); 226 } 227 228 driver_buffers[index].led_control_buffer_dirty = false; 229 } 230 } 231 232 void is31fl3737_flush(void) { 233 for (uint8_t i = 0; i < IS31FL3737_DRIVER_COUNT; i++) { 234 is31fl3737_update_pwm_buffers(i); 235 } 236 }