qmk_firmware

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

is31fl3733.c (9708B)


      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 "is31fl3733.h"
     21 #include "i2c_master.h"
     22 #include "gpio.h"
     23 #include "wait.h"
     24 
     25 #define IS31FL3733_PWM_REGISTER_COUNT 192
     26 #define IS31FL3733_LED_CONTROL_REGISTER_COUNT 24
     27 
     28 #ifndef IS31FL3733_I2C_TIMEOUT
     29 #    define IS31FL3733_I2C_TIMEOUT 100
     30 #endif
     31 
     32 #ifndef IS31FL3733_I2C_PERSISTENCE
     33 #    define IS31FL3733_I2C_PERSISTENCE 0
     34 #endif
     35 
     36 #ifndef IS31FL3733_PWM_FREQUENCY
     37 #    define IS31FL3733_PWM_FREQUENCY IS31FL3733_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3733B only
     38 #endif
     39 
     40 #ifndef IS31FL3733_SW_PULLUP
     41 #    define IS31FL3733_SW_PULLUP IS31FL3733_PUR_0_OHM
     42 #endif
     43 
     44 #ifndef IS31FL3733_CS_PULLDOWN
     45 #    define IS31FL3733_CS_PULLDOWN IS31FL3733_PDR_0_OHM
     46 #endif
     47 
     48 #ifndef IS31FL3733_GLOBAL_CURRENT
     49 #    define IS31FL3733_GLOBAL_CURRENT 0xFF
     50 #endif
     51 
     52 #ifndef IS31FL3733_SYNC_1
     53 #    define IS31FL3733_SYNC_1 IS31FL3733_SYNC_NONE
     54 #endif
     55 #ifndef IS31FL3733_SYNC_2
     56 #    define IS31FL3733_SYNC_2 IS31FL3733_SYNC_NONE
     57 #endif
     58 #ifndef IS31FL3733_SYNC_3
     59 #    define IS31FL3733_SYNC_3 IS31FL3733_SYNC_NONE
     60 #endif
     61 #ifndef IS31FL3733_SYNC_4
     62 #    define IS31FL3733_SYNC_4 IS31FL3733_SYNC_NONE
     63 #endif
     64 
     65 const uint8_t i2c_addresses[IS31FL3733_DRIVER_COUNT] = {
     66     IS31FL3733_I2C_ADDRESS_1,
     67 #ifdef IS31FL3733_I2C_ADDRESS_2
     68     IS31FL3733_I2C_ADDRESS_2,
     69 #    ifdef IS31FL3733_I2C_ADDRESS_3
     70     IS31FL3733_I2C_ADDRESS_3,
     71 #        ifdef IS31FL3733_I2C_ADDRESS_4
     72     IS31FL3733_I2C_ADDRESS_4,
     73 #        endif
     74 #    endif
     75 #endif
     76 };
     77 
     78 const uint8_t driver_sync[IS31FL3733_DRIVER_COUNT] = {
     79     IS31FL3733_SYNC_1,
     80 #ifdef IS31FL3733_I2C_ADDRESS_2
     81     IS31FL3733_SYNC_2,
     82 #    ifdef IS31FL3733_I2C_ADDRESS_3
     83     IS31FL3733_SYNC_3,
     84 #        ifdef IS31FL3733_I2C_ADDRESS_4
     85     IS31FL3733_SYNC_4,
     86 #        endif
     87 #    endif
     88 #endif
     89 };
     90 
     91 // These buffers match the IS31FL3733 PWM registers.
     92 // The control buffers match the page 0 LED On/Off registers.
     93 // Storing them like this is optimal for I2C transfers to the registers.
     94 // We could optimize this and take out the unused registers from these
     95 // buffers and the transfers in is31fl3733_write_pwm_buffer() but it's
     96 // probably not worth the extra complexity.
     97 typedef struct is31fl3733_driver_t {
     98     uint8_t pwm_buffer[IS31FL3733_PWM_REGISTER_COUNT];
     99     bool    pwm_buffer_dirty;
    100     uint8_t led_control_buffer[IS31FL3733_LED_CONTROL_REGISTER_COUNT];
    101     bool    led_control_buffer_dirty;
    102 } PACKED is31fl3733_driver_t;
    103 
    104 is31fl3733_driver_t driver_buffers[IS31FL3733_DRIVER_COUNT] = {{
    105     .pwm_buffer               = {0},
    106     .pwm_buffer_dirty         = false,
    107     .led_control_buffer       = {0},
    108     .led_control_buffer_dirty = false,
    109 }};
    110 
    111 void is31fl3733_write_register(uint8_t index, uint8_t reg, uint8_t data) {
    112 #if IS31FL3733_I2C_PERSISTENCE > 0
    113     for (uint8_t i = 0; i < IS31FL3733_I2C_PERSISTENCE; i++) {
    114         if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3733_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
    115     }
    116 #else
    117     i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3733_I2C_TIMEOUT);
    118 #endif
    119 }
    120 
    121 void is31fl3733_select_page(uint8_t index, uint8_t page) {
    122     is31fl3733_write_register(index, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC);
    123     is31fl3733_write_register(index, IS31FL3733_REG_COMMAND, page);
    124 }
    125 
    126 void is31fl3733_write_pwm_buffer(uint8_t index) {
    127     // Assumes page 1 is already selected.
    128     // Transmit PWM registers in 12 transfers of 16 bytes.
    129 
    130     // Iterate over the pwm_buffer contents at 16 byte intervals.
    131     for (uint8_t i = 0; i < IS31FL3733_PWM_REGISTER_COUNT; i += 16) {
    132 #if IS31FL3733_I2C_PERSISTENCE > 0
    133         for (uint8_t j = 0; j < IS31FL3733_I2C_PERSISTENCE; j++) {
    134             if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3733_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
    135         }
    136 #else
    137         i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3733_I2C_TIMEOUT);
    138 #endif
    139     }
    140 }
    141 
    142 void is31fl3733_init_drivers(void) {
    143     i2c_init();
    144 
    145 #if defined(IS31FL3733_SDB_PIN)
    146     gpio_set_pin_output(IS31FL3733_SDB_PIN);
    147     gpio_write_pin_high(IS31FL3733_SDB_PIN);
    148 #endif
    149 
    150     for (uint8_t i = 0; i < IS31FL3733_DRIVER_COUNT; i++) {
    151         is31fl3733_init(i);
    152     }
    153 
    154     for (int i = 0; i < IS31FL3733_LED_COUNT; i++) {
    155         is31fl3733_set_led_control_register(i, true, true, true);
    156     }
    157 
    158     for (uint8_t i = 0; i < IS31FL3733_DRIVER_COUNT; i++) {
    159         is31fl3733_update_led_control_registers(i);
    160     }
    161 }
    162 
    163 void is31fl3733_init(uint8_t index) {
    164     // In order to avoid the LEDs being driven with garbage data
    165     // in the LED driver's PWM registers, shutdown is enabled last.
    166     // Set up the mode and other settings, clear the PWM registers,
    167     // then disable software shutdown.
    168 
    169     is31fl3733_select_page(index, IS31FL3733_COMMAND_LED_CONTROL);
    170 
    171     // Turn off all LEDs.
    172     for (uint8_t i = 0; i < IS31FL3733_LED_CONTROL_REGISTER_COUNT; i++) {
    173         is31fl3733_write_register(index, i, 0x00);
    174     }
    175 
    176     is31fl3733_select_page(index, IS31FL3733_COMMAND_PWM);
    177 
    178     // Set PWM on all LEDs to 0
    179     // No need to setup Breath registers to PWM as that is the default.
    180     for (uint8_t i = 0; i < IS31FL3733_PWM_REGISTER_COUNT; i++) {
    181         is31fl3733_write_register(index, i, 0x00);
    182     }
    183 
    184     is31fl3733_select_page(index, IS31FL3733_COMMAND_FUNCTION);
    185 
    186     uint8_t sync = driver_sync[index];
    187 
    188     // Set de-ghost pull-up resistors (SWx)
    189     is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_SW_PULLUP, IS31FL3733_SW_PULLUP);
    190     // Set de-ghost pull-down resistors (CSx)
    191     is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_CS_PULLDOWN, IS31FL3733_CS_PULLDOWN);
    192     // Set global current to maximum.
    193     is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3733_GLOBAL_CURRENT);
    194     // Disable software shutdown.
    195     is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_CONFIGURATION, ((sync & 0b11) << 6) | ((IS31FL3733_PWM_FREQUENCY & 0b111) << 3) | 0x01);
    196 
    197     // Wait 10ms to ensure the device has woken up.
    198     wait_ms(10);
    199 }
    200 
    201 void is31fl3733_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
    202     is31fl3733_led_t led;
    203 
    204     if (index >= 0 && index < IS31FL3733_LED_COUNT) {
    205         memcpy_P(&led, (&g_is31fl3733_leds[index]), sizeof(led));
    206 
    207         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) {
    208             return;
    209         }
    210 
    211         driver_buffers[led.driver].pwm_buffer[led.r] = red;
    212         driver_buffers[led.driver].pwm_buffer[led.g] = green;
    213         driver_buffers[led.driver].pwm_buffer[led.b] = blue;
    214         driver_buffers[led.driver].pwm_buffer_dirty  = true;
    215     }
    216 }
    217 
    218 void is31fl3733_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
    219     for (int i = 0; i < IS31FL3733_LED_COUNT; i++) {
    220         is31fl3733_set_color(i, red, green, blue);
    221     }
    222 }
    223 
    224 void is31fl3733_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
    225     is31fl3733_led_t led;
    226     memcpy_P(&led, (&g_is31fl3733_leds[index]), sizeof(led));
    227 
    228     uint8_t control_register_r = led.r / 8;
    229     uint8_t control_register_g = led.g / 8;
    230     uint8_t control_register_b = led.b / 8;
    231     uint8_t bit_r              = led.r % 8;
    232     uint8_t bit_g              = led.g % 8;
    233     uint8_t bit_b              = led.b % 8;
    234 
    235     if (red) {
    236         driver_buffers[led.driver].led_control_buffer[control_register_r] |= (1 << bit_r);
    237     } else {
    238         driver_buffers[led.driver].led_control_buffer[control_register_r] &= ~(1 << bit_r);
    239     }
    240     if (green) {
    241         driver_buffers[led.driver].led_control_buffer[control_register_g] |= (1 << bit_g);
    242     } else {
    243         driver_buffers[led.driver].led_control_buffer[control_register_g] &= ~(1 << bit_g);
    244     }
    245     if (blue) {
    246         driver_buffers[led.driver].led_control_buffer[control_register_b] |= (1 << bit_b);
    247     } else {
    248         driver_buffers[led.driver].led_control_buffer[control_register_b] &= ~(1 << bit_b);
    249     }
    250 
    251     driver_buffers[led.driver].led_control_buffer_dirty = true;
    252 }
    253 
    254 void is31fl3733_update_pwm_buffers(uint8_t index) {
    255     if (driver_buffers[index].pwm_buffer_dirty) {
    256         is31fl3733_select_page(index, IS31FL3733_COMMAND_PWM);
    257 
    258         is31fl3733_write_pwm_buffer(index);
    259 
    260         driver_buffers[index].pwm_buffer_dirty = false;
    261     }
    262 }
    263 
    264 void is31fl3733_update_led_control_registers(uint8_t index) {
    265     if (driver_buffers[index].led_control_buffer_dirty) {
    266         is31fl3733_select_page(index, IS31FL3733_COMMAND_LED_CONTROL);
    267 
    268         for (uint8_t i = 0; i < IS31FL3733_LED_CONTROL_REGISTER_COUNT; i++) {
    269             is31fl3733_write_register(index, i, driver_buffers[index].led_control_buffer[i]);
    270         }
    271 
    272         driver_buffers[index].led_control_buffer_dirty = false;
    273     }
    274 }
    275 
    276 void is31fl3733_flush(void) {
    277     for (uint8_t i = 0; i < IS31FL3733_DRIVER_COUNT; i++) {
    278         is31fl3733_update_pwm_buffers(i);
    279     }
    280 }