snled27351-mono.c (9101B)
1 /* Copyright 2021 @ Keychron (https://www.keychron.com) 2 * 3 * This program is free software: you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License as published by 5 * the Free Software Foundation, either version 2 of the License, or 6 * (at your option) any later version. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16 17 #include "snled27351-mono.h" 18 #include "i2c_master.h" 19 #include "gpio.h" 20 21 #define SNLED27351_PWM_REGISTER_COUNT 192 22 #define SNLED27351_LED_CONTROL_REGISTER_COUNT 24 23 24 #ifndef SNLED27351_I2C_TIMEOUT 25 # define SNLED27351_I2C_TIMEOUT 100 26 #endif 27 28 #ifndef SNLED27351_I2C_PERSISTENCE 29 # define SNLED27351_I2C_PERSISTENCE 0 30 #endif 31 32 #ifndef SNLED27351_PHASE_CHANNEL 33 # define SNLED27351_PHASE_CHANNEL SNLED27351_SCAN_PHASE_12_CHANNEL 34 #endif 35 36 #ifndef SNLED27351_CURRENT_TUNE 37 # define SNLED27351_CURRENT_TUNE {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} 38 #endif 39 40 const uint8_t i2c_addresses[SNLED27351_DRIVER_COUNT] = { 41 SNLED27351_I2C_ADDRESS_1, 42 #ifdef SNLED27351_I2C_ADDRESS_2 43 SNLED27351_I2C_ADDRESS_2, 44 # ifdef SNLED27351_I2C_ADDRESS_3 45 SNLED27351_I2C_ADDRESS_3, 46 # ifdef SNLED27351_I2C_ADDRESS_4 47 SNLED27351_I2C_ADDRESS_4, 48 # endif 49 # endif 50 #endif 51 }; 52 53 // These buffers match the SNLED27351 PWM registers. 54 // The control buffers match the PG0 LED On/Off registers. 55 // Storing them like this is optimal for I2C transfers to the registers. 56 // We could optimize this and take out the unused registers from these 57 // buffers and the transfers in snled27351_write_pwm_buffer() but it's 58 // probably not worth the extra complexity. 59 typedef struct snled27351_driver_t { 60 uint8_t pwm_buffer[SNLED27351_PWM_REGISTER_COUNT]; 61 bool pwm_buffer_dirty; 62 uint8_t led_control_buffer[SNLED27351_LED_CONTROL_REGISTER_COUNT]; 63 bool led_control_buffer_dirty; 64 } PACKED snled27351_driver_t; 65 66 snled27351_driver_t driver_buffers[SNLED27351_DRIVER_COUNT] = {{ 67 .pwm_buffer = {0}, 68 .pwm_buffer_dirty = false, 69 .led_control_buffer = {0}, 70 .led_control_buffer_dirty = false, 71 }}; 72 73 void snled27351_write_register(uint8_t index, uint8_t reg, uint8_t data) { 74 #if SNLED27351_I2C_PERSISTENCE > 0 75 for (uint8_t i = 0; i < SNLED27351_I2C_PERSISTENCE; i++) { 76 if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, SNLED27351_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; 77 } 78 #else 79 i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, SNLED27351_I2C_TIMEOUT); 80 #endif 81 } 82 83 void snled27351_select_page(uint8_t index, uint8_t page) { 84 snled27351_write_register(index, SNLED27351_REG_COMMAND, page); 85 } 86 87 void snled27351_write_pwm_buffer(uint8_t index) { 88 // Assumes PG1 is already selected. 89 // Transmit PWM registers in 12 transfers of 16 bytes. 90 91 // Iterate over the pwm_buffer contents at 16 byte intervals. 92 for (uint8_t i = 0; i < SNLED27351_PWM_REGISTER_COUNT; i += 16) { 93 #if SNLED27351_I2C_PERSISTENCE > 0 94 for (uint8_t j = 0; j < SNLED27351_I2C_PERSISTENCE; j++) { 95 if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, SNLED27351_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; 96 } 97 #else 98 i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, SNLED27351_I2C_TIMEOUT); 99 #endif 100 } 101 } 102 103 void snled27351_init_drivers(void) { 104 i2c_init(); 105 106 #if defined(SNLED27351_SDB_PIN) 107 gpio_set_pin_output(SNLED27351_SDB_PIN); 108 gpio_write_pin_high(SNLED27351_SDB_PIN); 109 #endif 110 111 for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) { 112 snled27351_init(i); 113 } 114 115 for (int i = 0; i < SNLED27351_LED_COUNT; i++) { 116 snled27351_set_led_control_register(i, true); 117 } 118 119 for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) { 120 snled27351_update_led_control_registers(i); 121 } 122 } 123 124 void snled27351_init(uint8_t index) { 125 snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION); 126 127 // Setting LED driver to shutdown mode 128 snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN); 129 // Setting internal channel pulldown/pullup 130 snled27351_write_register(index, SNLED27351_FUNCTION_REG_PULLDOWNUP, SNLED27351_PULLDOWNUP_ALL_ENABLED); 131 // Select number of scan phase 132 snled27351_write_register(index, SNLED27351_FUNCTION_REG_SCAN_PHASE, SNLED27351_PHASE_CHANNEL); 133 // Setting PWM Delay Phase 134 snled27351_write_register(index, SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_1, SNLED27351_SLEW_RATE_CONTROL_MODE_1_PDP_ENABLE); 135 // Setting Driving/Sinking Channel Slew Rate 136 snled27351_write_register(index, SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_2, SNLED27351_SLEW_RATE_CONTROL_MODE_2_DSL_ENABLE | SNLED27351_SLEW_RATE_CONTROL_MODE_2_SSL_ENABLE); 137 // Setting Iref 138 snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP, 0); 139 140 snled27351_select_page(index, SNLED27351_COMMAND_LED_CONTROL); 141 142 for (int i = 0; i < SNLED27351_LED_CONTROL_ON_OFF_LENGTH; i++) { 143 snled27351_write_register(index, i, 0x00); 144 } 145 146 snled27351_select_page(index, SNLED27351_COMMAND_PWM); 147 148 for (int i = 0; i < SNLED27351_LED_CURRENT_TUNE_LENGTH; i++) { 149 snled27351_write_register(index, i, 0x00); 150 } 151 152 snled27351_select_page(index, SNLED27351_COMMAND_CURRENT_TUNE); 153 154 uint8_t current_tune_reg_list[SNLED27351_LED_CURRENT_TUNE_LENGTH] = SNLED27351_CURRENT_TUNE; 155 for (int i = 0; i < SNLED27351_LED_CURRENT_TUNE_LENGTH; i++) { 156 snled27351_write_register(index, i, current_tune_reg_list[i]); 157 } 158 159 snled27351_select_page(index, SNLED27351_COMMAND_LED_CONTROL); 160 161 for (int i = 0; i < SNLED27351_LED_CONTROL_ON_OFF_LENGTH; i++) { 162 snled27351_write_register(index, i, 0xFF); 163 } 164 165 snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION); 166 167 // Setting LED driver to normal mode 168 snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL); 169 } 170 171 void snled27351_set_value(int index, uint8_t value) { 172 snled27351_led_t led; 173 if (index >= 0 && index < SNLED27351_LED_COUNT) { 174 memcpy_P(&led, (&g_snled27351_leds[index]), sizeof(led)); 175 176 if (driver_buffers[led.driver].pwm_buffer[led.v] == value) { 177 return; 178 } 179 180 driver_buffers[led.driver].pwm_buffer[led.v] = value; 181 driver_buffers[led.driver].pwm_buffer_dirty = true; 182 } 183 } 184 185 void snled27351_set_value_all(uint8_t value) { 186 for (int i = 0; i < SNLED27351_LED_COUNT; i++) { 187 snled27351_set_value(i, value); 188 } 189 } 190 191 void snled27351_set_led_control_register(uint8_t index, bool value) { 192 snled27351_led_t led; 193 memcpy_P(&led, (&g_snled27351_leds[index]), sizeof(led)); 194 195 uint8_t control_register = led.v / 8; 196 uint8_t bit_value = led.v % 8; 197 198 if (value) { 199 driver_buffers[led.driver].led_control_buffer[control_register] |= (1 << bit_value); 200 } else { 201 driver_buffers[led.driver].led_control_buffer[control_register] &= ~(1 << bit_value); 202 } 203 204 driver_buffers[led.driver].led_control_buffer_dirty = true; 205 } 206 207 void snled27351_update_pwm_buffers(uint8_t index) { 208 if (driver_buffers[index].pwm_buffer_dirty) { 209 snled27351_select_page(index, SNLED27351_COMMAND_PWM); 210 211 snled27351_write_pwm_buffer(index); 212 213 driver_buffers[index].pwm_buffer_dirty = false; 214 } 215 } 216 217 void snled27351_update_led_control_registers(uint8_t index) { 218 if (driver_buffers[index].led_control_buffer_dirty) { 219 snled27351_select_page(index, SNLED27351_COMMAND_LED_CONTROL); 220 221 for (uint8_t i = 0; i < SNLED27351_LED_CONTROL_REGISTER_COUNT; i++) { 222 snled27351_write_register(index, i, driver_buffers[index].led_control_buffer[i]); 223 } 224 225 driver_buffers[index].led_control_buffer_dirty = false; 226 } 227 } 228 229 void snled27351_flush(void) { 230 for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) { 231 snled27351_update_pwm_buffers(i); 232 } 233 } 234 235 void snled27351_sw_return_normal(uint8_t index) { 236 snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION); 237 238 // Setting LED driver to normal mode 239 snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL); 240 } 241 242 void snled27351_sw_shutdown(uint8_t index) { 243 snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION); 244 245 // Setting LED driver to shutdown mode 246 snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN); 247 // Write SW Sleep Register 248 snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP, SNLED27351_SOFTWARE_SLEEP_ENABLE); 249 }