qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

is31fl3729.c (7973B)


      1 /* Copyright 2024 HorrorTroll <https://github.com/HorrorTroll>
      2  * Copyright 2024 Harrison Chan (Xelus)
      3  * Copyright 2024 Dimitris Mantzouranis <d3xter93@gmail.com>
      4  *
      5  * This program is free software: you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License as published by
      7  * the Free Software Foundation, either version 2 of the License, or
      8  * (at your option) any later version.
      9  *
     10  * This program is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  * GNU General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU General Public License
     16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     17  */
     18 
     19 #include "is31fl3729.h"
     20 #include "i2c_master.h"
     21 #include "gpio.h"
     22 #include "wait.h"
     23 
     24 #define IS31FL3729_PWM_REGISTER_COUNT 143
     25 #define IS31FL3729_SCALING_REGISTER_COUNT 16
     26 
     27 #ifndef IS31FL3729_I2C_TIMEOUT
     28 #    define IS31FL3729_I2C_TIMEOUT 100
     29 #endif
     30 
     31 #ifndef IS31FL3729_I2C_PERSISTENCE
     32 #    define IS31FL3729_I2C_PERSISTENCE 0
     33 #endif
     34 
     35 #ifndef IS31FL3729_CONFIGURATION
     36 #    define IS31FL3729_CONFIGURATION IS31FL3729_CONFIG_SWS_15_9
     37 #endif
     38 
     39 #ifndef IS31FL3729_GLOBAL_CURRENT
     40 #    define IS31FL3729_GLOBAL_CURRENT 0x40
     41 #endif
     42 
     43 #ifndef IS31FL3729_SW_PULLDOWN
     44 #    define IS31FL3729_SW_PULLDOWN IS31FL3729_SW_PULLDOWN_2K_OHM_SW_OFF
     45 #endif
     46 
     47 #ifndef IS31FL3729_CS_PULLUP
     48 #    define IS31FL3729_CS_PULLUP IS31FL3729_CS_PULLUP_2K_OHM_CS_OFF
     49 #endif
     50 
     51 #ifndef IS31FL3729_SPREAD_SPECTRUM
     52 #    define IS31FL3729_SPREAD_SPECTRUM IS31FL3729_SPREAD_SPECTRUM_DISABLE
     53 #endif
     54 
     55 #ifndef IS31FL3729_SPREAD_SPECTRUM_RANGE
     56 #    define IS31FL3729_SPREAD_SPECTRUM_RANGE IS31FL3729_SPREAD_SPECTRUM_RANGE_5_PERCENT
     57 #endif
     58 
     59 #ifndef IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME
     60 #    define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_1980_US
     61 #endif
     62 
     63 #ifndef IS31FL3729_PWM_FREQUENCY
     64 #    define IS31FL3729_PWM_FREQUENCY IS31FL3729_PWM_FREQUENCY_32K_HZ
     65 #endif
     66 
     67 const uint8_t i2c_addresses[IS31FL3729_DRIVER_COUNT] = {
     68     IS31FL3729_I2C_ADDRESS_1,
     69 #ifdef IS31FL3729_I2C_ADDRESS_2
     70     IS31FL3729_I2C_ADDRESS_2,
     71 #    ifdef IS31FL3729_I2C_ADDRESS_3
     72     IS31FL3729_I2C_ADDRESS_3,
     73 #        ifdef IS31FL3729_I2C_ADDRESS_4
     74     IS31FL3729_I2C_ADDRESS_4,
     75 #        endif
     76 #    endif
     77 #endif
     78 };
     79 
     80 // These buffers match the PWM & scaling registers.
     81 // Storing them like this is optimal for I2C transfers to the registers.
     82 typedef struct is31fl3729_driver_t {
     83     uint8_t pwm_buffer[IS31FL3729_PWM_REGISTER_COUNT];
     84     bool    pwm_buffer_dirty;
     85     uint8_t scaling_buffer[IS31FL3729_SCALING_REGISTER_COUNT];
     86     bool    scaling_buffer_dirty;
     87 } PACKED is31fl3729_driver_t;
     88 
     89 is31fl3729_driver_t driver_buffers[IS31FL3729_DRIVER_COUNT] = {{
     90     .pwm_buffer           = {0},
     91     .pwm_buffer_dirty     = false,
     92     .scaling_buffer       = {0},
     93     .scaling_buffer_dirty = false,
     94 }};
     95 
     96 void is31fl3729_write_register(uint8_t index, uint8_t reg, uint8_t data) {
     97 #if IS31FL3729_I2C_PERSISTENCE > 0
     98     for (uint8_t i = 0; i < IS31FL3729_I2C_PERSISTENCE; i++) {
     99         if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3729_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
    100     }
    101 #else
    102     i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3729_I2C_TIMEOUT);
    103 #endif
    104 }
    105 
    106 void is31fl3729_write_pwm_buffer(uint8_t index) {
    107     // Transmit PWM registers in 11 transfers of 13 bytes.
    108 
    109     // Iterate over the pwm_buffer contents at 13 byte intervals.
    110     for (uint8_t i = 0; i <= IS31FL3729_PWM_REGISTER_COUNT; i += 13) {
    111 #if IS31FL3729_I2C_PERSISTENCE > 0
    112         for (uint8_t j = 0; j < IS31FL3729_I2C_PERSISTENCE; j++) {
    113             if (i2c_write_register(i2c_addresses[index] << 1, IS31FL3729_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 13, IS31FL3729_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
    114         }
    115 #else
    116         i2c_write_register(i2c_addresses[index] << 1, IS31FL3729_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 13, IS31FL3729_I2C_TIMEOUT);
    117 #endif
    118     }
    119 }
    120 
    121 void is31fl3729_init_drivers(void) {
    122     i2c_init();
    123 
    124 #if defined(IS31FL3729_SDB_PIN)
    125     gpio_set_pin_output(IS31FL3729_SDB_PIN);
    126     gpio_write_pin_high(IS31FL3729_SDB_PIN);
    127 #endif
    128 
    129     for (uint8_t i = 0; i < IS31FL3729_DRIVER_COUNT; i++) {
    130         is31fl3729_init(i);
    131     }
    132 
    133     for (int i = 0; i < IS31FL3729_LED_COUNT; i++) {
    134         is31fl3729_set_scaling_register(i, 0xFF, 0xFF, 0xFF);
    135     }
    136 
    137     for (uint8_t i = 0; i < IS31FL3729_DRIVER_COUNT; i++) {
    138         is31fl3729_update_scaling_registers(i);
    139     }
    140 }
    141 
    142 void is31fl3729_init(uint8_t index) {
    143     // In order to avoid the LEDs being driven with garbage data
    144     // in the LED driver's PWM registers, shutdown is enabled last.
    145     // Set up the mode and other settings, clear the PWM registers,
    146     // then disable software shutdown.
    147 
    148     is31fl3729_write_register(index, IS31FL3729_REG_PULLDOWNUP, ((IS31FL3729_SW_PULLDOWN & 0b111) << 4) | (IS31FL3729_CS_PULLUP & 0b111));
    149     is31fl3729_write_register(index, IS31FL3729_REG_SPREAD_SPECTRUM, ((IS31FL3729_SPREAD_SPECTRUM & 0b1) << 4) | ((IS31FL3729_SPREAD_SPECTRUM_RANGE & 0b11) << 2) | (IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME & 0b11));
    150     is31fl3729_write_register(index, IS31FL3729_REG_PWM_FREQUENCY, IS31FL3729_PWM_FREQUENCY);
    151     is31fl3729_write_register(index, IS31FL3729_REG_GLOBAL_CURRENT, IS31FL3729_GLOBAL_CURRENT);
    152     is31fl3729_write_register(index, IS31FL3729_REG_CONFIGURATION, IS31FL3729_CONFIGURATION);
    153 
    154     // Wait 10ms to ensure the device has woken up.
    155     wait_ms(10);
    156 }
    157 
    158 void is31fl3729_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
    159     is31fl3729_led_t led;
    160     if (index >= 0 && index < IS31FL3729_LED_COUNT) {
    161         memcpy_P(&led, (&g_is31fl3729_leds[index]), sizeof(led));
    162 
    163         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) {
    164             return;
    165         }
    166 
    167         driver_buffers[led.driver].pwm_buffer[led.r] = red;
    168         driver_buffers[led.driver].pwm_buffer[led.g] = green;
    169         driver_buffers[led.driver].pwm_buffer[led.b] = blue;
    170         driver_buffers[led.driver].pwm_buffer_dirty  = true;
    171     }
    172 }
    173 
    174 void is31fl3729_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
    175     for (int i = 0; i < IS31FL3729_LED_COUNT; i++) {
    176         is31fl3729_set_color(i, red, green, blue);
    177     }
    178 }
    179 
    180 void is31fl3729_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue) {
    181     is31fl3729_led_t led;
    182     memcpy_P(&led, (&g_is31fl3729_leds[index]), sizeof(led));
    183 
    184     // need to do a bit of checking here since 3729 scaling is per CS pin.
    185     // not the usual per RGB key as per other ISSI drivers
    186     // only enable them, since they should be default disabled
    187     int cs_red   = (led.r & 0x0F) - 1;
    188     int cs_green = (led.g & 0x0F) - 1;
    189     int cs_blue  = (led.b & 0x0F) - 1;
    190 
    191     driver_buffers[led.driver].scaling_buffer[cs_red]   = red;
    192     driver_buffers[led.driver].scaling_buffer[cs_green] = green;
    193     driver_buffers[led.driver].scaling_buffer[cs_blue]  = blue;
    194     driver_buffers[led.driver].scaling_buffer_dirty     = true;
    195 }
    196 
    197 void is31fl3729_update_pwm_buffers(uint8_t index) {
    198     if (driver_buffers[index].pwm_buffer_dirty) {
    199         is31fl3729_write_pwm_buffer(index);
    200 
    201         driver_buffers[index].pwm_buffer_dirty = false;
    202     }
    203 }
    204 
    205 void is31fl3729_update_scaling_registers(uint8_t index) {
    206     if (driver_buffers[index].scaling_buffer_dirty) {
    207         for (uint8_t i = 0; i < IS31FL3729_SCALING_REGISTER_COUNT; i++) {
    208             is31fl3729_write_register(index, IS31FL3729_REG_SCALING + i, driver_buffers[index].scaling_buffer[i]);
    209         }
    210 
    211         driver_buffers[index].scaling_buffer_dirty = false;
    212     }
    213 }
    214 
    215 void is31fl3729_flush(void) {
    216     for (uint8_t i = 0; i < IS31FL3729_DRIVER_COUNT; i++) {
    217         is31fl3729_update_pwm_buffers(i);
    218     }
    219 }