aw20216s.c (5410B)
1 /* Copyright 2021 Jasper Chan 2 * 2023 Huckies <https://github.com/Huckies> 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include "aw20216s.h" 19 #include "wait.h" 20 #include "spi_master.h" 21 22 #define AW20216S_PWM_REGISTER_COUNT 216 23 24 #ifndef AW20216S_CONFIGURATION 25 # define AW20216S_CONFIGURATION (AW20216S_CONFIGURATION_SWSEL_1_12 | AW20216S_CONFIGURATION_CHIPEN) 26 #endif 27 28 #ifndef AW20216S_MIX_FUNCTION 29 # define AW20216S_MIX_FUNCTION (AW20216S_MIX_FUNCTION_LPEN) 30 #endif 31 32 #ifndef AW20216S_SCALING_MAX 33 # define AW20216S_SCALING_MAX 150 34 #endif 35 36 #ifndef AW20216S_GLOBAL_CURRENT_MAX 37 # define AW20216S_GLOBAL_CURRENT_MAX 150 38 #endif 39 40 #ifndef AW20216S_SPI_MODE 41 # define AW20216S_SPI_MODE 0 42 #endif 43 44 #ifndef AW20216S_SPI_DIVISOR 45 # define AW20216S_SPI_DIVISOR 4 46 #endif 47 48 typedef struct aw20216s_driver_t { 49 uint8_t pwm_buffer[AW20216S_PWM_REGISTER_COUNT]; 50 bool pwm_buffer_dirty; 51 } PACKED aw20216s_driver_t; 52 53 aw20216s_driver_t driver_buffers[AW20216S_DRIVER_COUNT] = {{ 54 .pwm_buffer = {0}, 55 .pwm_buffer_dirty = false, 56 }}; 57 58 bool aw20216s_write(pin_t cs_pin, uint8_t page, uint8_t reg, uint8_t* data, uint8_t len) { 59 static uint8_t s_spi_transfer_buffer[2] = {0}; 60 61 if (!spi_start(cs_pin, false, AW20216S_SPI_MODE, AW20216S_SPI_DIVISOR)) { 62 spi_stop(); 63 return false; 64 } 65 66 s_spi_transfer_buffer[0] = (AW20216S_ID | page | AW20216S_WRITE); 67 s_spi_transfer_buffer[1] = reg; 68 69 if (spi_transmit(s_spi_transfer_buffer, 2) != SPI_STATUS_SUCCESS) { 70 spi_stop(); 71 return false; 72 } 73 74 if (spi_transmit(data, len) != SPI_STATUS_SUCCESS) { 75 spi_stop(); 76 return false; 77 } 78 79 spi_stop(); 80 return true; 81 } 82 83 static inline bool aw20216s_write_register(pin_t cs_pin, uint8_t page, uint8_t reg, uint8_t value) { 84 // Little wrapper so callers need not care about sending a buffer 85 return aw20216s_write(cs_pin, page, reg, &value, 1); 86 } 87 88 void aw20216s_soft_reset(pin_t cs_pin) { 89 aw20216s_write_register(cs_pin, AW20216S_PAGE_FUNCTION, AW20216S_FUNCTION_REG_RESET, AW20216S_RESET_MAGIC); 90 } 91 92 static void aw20216s_init_scaling(pin_t cs_pin) { 93 // Set constant current to the max, control brightness with PWM 94 for (uint8_t i = 0; i < AW20216S_PWM_REGISTER_COUNT; i++) { 95 aw20216s_write_register(cs_pin, AW20216S_PAGE_SCALING, i, AW20216S_SCALING_MAX); 96 } 97 } 98 99 static inline void aw20216s_init_current_limit(pin_t cs_pin) { 100 // Push config 101 aw20216s_write_register(cs_pin, AW20216S_PAGE_FUNCTION, AW20216S_FUNCTION_REG_GLOBAL_CURRENT, AW20216S_GLOBAL_CURRENT_MAX); 102 } 103 104 static inline void aw20216s_soft_enable(pin_t cs_pin) { 105 // Push config 106 aw20216s_write_register(cs_pin, AW20216S_PAGE_FUNCTION, AW20216S_FUNCTION_REG_CONFIGURATION, AW20216S_CONFIGURATION); 107 } 108 109 static inline void aw20216s_auto_lowpower(pin_t cs_pin) { 110 aw20216s_write_register(cs_pin, AW20216S_PAGE_FUNCTION, AW20216S_FUNCTION_REG_MIX_FUNCTION, AW20216S_MIX_FUNCTION); 111 } 112 113 void aw20216s_init_drivers(void) { 114 spi_init(); 115 116 #if defined(AW20216S_EN_PIN) 117 gpio_set_pin_output(AW20216S_EN_PIN); 118 gpio_write_pin_high(AW20216S_EN_PIN); 119 #endif 120 121 aw20216s_init(AW20216S_CS_PIN_1); 122 #if defined(AW20216S_CS_PIN_2) 123 aw20216s_init(AW20216S_CS_PIN_2); 124 #endif 125 } 126 127 void aw20216s_init(pin_t cs_pin) { 128 aw20216s_soft_reset(cs_pin); 129 wait_ms(2); 130 131 // Drivers should start with all scaling and PWM registers as off 132 aw20216s_init_current_limit(cs_pin); 133 aw20216s_init_scaling(cs_pin); 134 135 aw20216s_soft_enable(cs_pin); 136 aw20216s_auto_lowpower(cs_pin); 137 } 138 139 void aw20216s_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) { 140 aw20216s_led_t led; 141 memcpy_P(&led, (&g_aw20216s_leds[index]), sizeof(led)); 142 143 if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) { 144 return; 145 } 146 147 driver_buffers[led.driver].pwm_buffer[led.r] = red; 148 driver_buffers[led.driver].pwm_buffer[led.g] = green; 149 driver_buffers[led.driver].pwm_buffer[led.b] = blue; 150 driver_buffers[led.driver].pwm_buffer_dirty = true; 151 } 152 153 void aw20216s_set_color_all(uint8_t red, uint8_t green, uint8_t blue) { 154 for (uint8_t i = 0; i < AW20216S_LED_COUNT; i++) { 155 aw20216s_set_color(i, red, green, blue); 156 } 157 } 158 159 void aw20216s_update_pwm_buffers(pin_t cs_pin, uint8_t index) { 160 if (driver_buffers[index].pwm_buffer_dirty) { 161 aw20216s_write(cs_pin, AW20216S_PAGE_PWM, 0, driver_buffers[index].pwm_buffer, AW20216S_PWM_REGISTER_COUNT); 162 driver_buffers[index].pwm_buffer_dirty = false; 163 } 164 } 165 166 void aw20216s_flush(void) { 167 aw20216s_update_pwm_buffers(AW20216S_CS_PIN_1, 0); 168 #if defined(AW20216S_CS_PIN_2) 169 aw20216s_update_pwm_buffers(AW20216S_CS_PIN_2, 1); 170 #endif 171 }