qmk_firmware

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

is31fl3731.c (9033B)


      1 /* Copyright 2017 Jason Williams
      2  * Copyright 2018 Jack Humbert
      3  * Copyright 2021 Doni Crosby
      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 "is31fl3731.h"
     20 #include "i2c_master.h"
     21 #include "gpio.h"
     22 #include "wait.h"
     23 
     24 #define IS31FL3731_PWM_REGISTER_COUNT 144
     25 #define IS31FL3731_LED_CONTROL_REGISTER_COUNT 18
     26 
     27 #ifndef IS31FL3731_I2C_TIMEOUT
     28 #    define IS31FL3731_I2C_TIMEOUT 100
     29 #endif
     30 
     31 #ifndef IS31FL3731_I2C_PERSISTENCE
     32 #    define IS31FL3731_I2C_PERSISTENCE 0
     33 #endif
     34 
     35 const uint8_t i2c_addresses[IS31FL3731_DRIVER_COUNT] = {
     36     IS31FL3731_I2C_ADDRESS_1,
     37 #ifdef IS31FL3731_I2C_ADDRESS_2
     38     IS31FL3731_I2C_ADDRESS_2,
     39 #    ifdef IS31FL3731_I2C_ADDRESS_3
     40     IS31FL3731_I2C_ADDRESS_3,
     41 #        ifdef IS31FL3731_I2C_ADDRESS_4
     42     IS31FL3731_I2C_ADDRESS_4,
     43 #        endif
     44 #    endif
     45 #endif
     46 };
     47 
     48 // These buffers match the IS31FL3731 PWM registers 0x24-0xB3.
     49 // Storing them like this is optimal for I2C transfers to the registers.
     50 // We could optimize this and take out the unused registers from these
     51 // buffers and the transfers in is31fl3731_write_pwm_buffer() but it's
     52 // probably not worth the extra complexity.
     53 typedef struct is31fl3731_driver_t {
     54     uint8_t pwm_buffer[IS31FL3731_PWM_REGISTER_COUNT];
     55     bool    pwm_buffer_dirty;
     56     uint8_t led_control_buffer[IS31FL3731_LED_CONTROL_REGISTER_COUNT];
     57     bool    led_control_buffer_dirty;
     58 } PACKED is31fl3731_driver_t;
     59 
     60 is31fl3731_driver_t driver_buffers[IS31FL3731_DRIVER_COUNT] = {{
     61     .pwm_buffer               = {0},
     62     .pwm_buffer_dirty         = false,
     63     .led_control_buffer       = {0},
     64     .led_control_buffer_dirty = false,
     65 }};
     66 
     67 void is31fl3731_write_register(uint8_t index, uint8_t reg, uint8_t data) {
     68 #if IS31FL3731_I2C_PERSISTENCE > 0
     69     for (uint8_t i = 0; i < IS31FL3731_I2C_PERSISTENCE; i++) {
     70         if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3731_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
     71     }
     72 #else
     73     i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3731_I2C_TIMEOUT);
     74 #endif
     75 }
     76 
     77 void is31fl3731_select_page(uint8_t index, uint8_t page) {
     78     is31fl3731_write_register(index, IS31FL3731_REG_COMMAND, page);
     79 }
     80 
     81 void is31fl3731_write_pwm_buffer(uint8_t index) {
     82     // Assumes page 0 is already selected.
     83     // Transmit PWM registers in 9 transfers of 16 bytes.
     84 
     85     // Iterate over the pwm_buffer contents at 16 byte intervals.
     86     for (uint8_t i = 0; i < IS31FL3731_PWM_REGISTER_COUNT; i += 16) {
     87 #if IS31FL3731_I2C_PERSISTENCE > 0
     88         for (uint8_t j = 0; j < IS31FL3731_I2C_PERSISTENCE; j++) {
     89             if (i2c_write_register(i2c_addresses[index] << 1, IS31FL3731_FRAME_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3731_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
     90         }
     91 #else
     92         i2c_write_register(i2c_addresses[index] << 1, IS31FL3731_FRAME_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3731_I2C_TIMEOUT);
     93 #endif
     94     }
     95 }
     96 
     97 void is31fl3731_init_drivers(void) {
     98     i2c_init();
     99 
    100 #if defined(IS31FL3731_SDB_PIN)
    101     gpio_set_pin_output(IS31FL3731_SDB_PIN);
    102     gpio_write_pin_high(IS31FL3731_SDB_PIN);
    103 #endif
    104 
    105     for (uint8_t i = 0; i < IS31FL3731_DRIVER_COUNT; i++) {
    106         is31fl3731_init(i);
    107     }
    108 
    109     for (int i = 0; i < IS31FL3731_LED_COUNT; i++) {
    110         is31fl3731_set_led_control_register(i, true, true, true);
    111     }
    112 
    113     for (uint8_t i = 0; i < IS31FL3731_DRIVER_COUNT; i++) {
    114         is31fl3731_update_led_control_registers(i);
    115     }
    116 }
    117 
    118 void is31fl3731_init(uint8_t index) {
    119     // In order to avoid the LEDs being driven with garbage data
    120     // in the LED driver's PWM registers, first enable software shutdown,
    121     // then set up the mode and other settings, clear the PWM registers,
    122     // then disable software shutdown.
    123 
    124     is31fl3731_select_page(index, IS31FL3731_COMMAND_FUNCTION);
    125 
    126     // enable software shutdown
    127     is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_SHUTDOWN, 0x00);
    128 #ifdef IS31FL3731_DEGHOST // set to enable de-ghosting of the array
    129     is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_GHOST_IMAGE_PREVENTION, IS31FL3731_GHOST_IMAGE_PREVENTION_GEN);
    130 #endif
    131 
    132     // this delay was copied from other drivers, might not be needed
    133     wait_ms(10);
    134 
    135     // picture mode
    136     is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_CONFIG, IS31FL3731_CONFIG_MODE_PICTURE);
    137     // display frame 0
    138     is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_PICTURE_DISPLAY, 0x00);
    139     // audio sync off
    140     is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_AUDIO_SYNC, 0x00);
    141 
    142     is31fl3731_select_page(index, IS31FL3731_COMMAND_FRAME_1);
    143 
    144     // turn off all LEDs in the LED control register
    145     for (uint8_t i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) {
    146         is31fl3731_write_register(index, IS31FL3731_FRAME_REG_LED_CONTROL + i, 0x00);
    147     }
    148 
    149     // turn off all LEDs in the blink control register (not really needed)
    150     for (uint8_t i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) {
    151         is31fl3731_write_register(index, IS31FL3731_FRAME_REG_BLINK_CONTROL + i, 0x00);
    152     }
    153 
    154     // set PWM on all LEDs to 0
    155     for (uint8_t i = 0; i < IS31FL3731_PWM_REGISTER_COUNT; i++) {
    156         is31fl3731_write_register(index, IS31FL3731_FRAME_REG_PWM + i, 0x00);
    157     }
    158 
    159     is31fl3731_select_page(index, IS31FL3731_COMMAND_FUNCTION);
    160 
    161     // disable software shutdown
    162     is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_SHUTDOWN, 0x01);
    163 
    164     // select page 0 and leave it selected.
    165     // most usage after initialization is just writing PWM buffers in page 0
    166     // as there's not much point in double-buffering
    167     is31fl3731_select_page(index, IS31FL3731_COMMAND_FRAME_1);
    168 }
    169 
    170 void is31fl3731_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
    171     is31fl3731_led_t led;
    172 
    173     if (index >= 0 && index < IS31FL3731_LED_COUNT) {
    174         memcpy_P(&led, (&g_is31fl3731_leds[index]), sizeof(led));
    175 
    176         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) {
    177             return;
    178         }
    179 
    180         driver_buffers[led.driver].pwm_buffer[led.r] = red;
    181         driver_buffers[led.driver].pwm_buffer[led.g] = green;
    182         driver_buffers[led.driver].pwm_buffer[led.b] = blue;
    183         driver_buffers[led.driver].pwm_buffer_dirty  = true;
    184     }
    185 }
    186 
    187 void is31fl3731_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
    188     for (int i = 0; i < IS31FL3731_LED_COUNT; i++) {
    189         is31fl3731_set_color(i, red, green, blue);
    190     }
    191 }
    192 
    193 void is31fl3731_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
    194     is31fl3731_led_t led;
    195     memcpy_P(&led, (&g_is31fl3731_leds[index]), sizeof(led));
    196 
    197     uint8_t control_register_r = led.r / 8;
    198     uint8_t control_register_g = led.g / 8;
    199     uint8_t control_register_b = led.b / 8;
    200     uint8_t bit_r              = led.r % 8;
    201     uint8_t bit_g              = led.g % 8;
    202     uint8_t bit_b              = led.b % 8;
    203 
    204     if (red) {
    205         driver_buffers[led.driver].led_control_buffer[control_register_r] |= (1 << bit_r);
    206     } else {
    207         driver_buffers[led.driver].led_control_buffer[control_register_r] &= ~(1 << bit_r);
    208     }
    209     if (green) {
    210         driver_buffers[led.driver].led_control_buffer[control_register_g] |= (1 << bit_g);
    211     } else {
    212         driver_buffers[led.driver].led_control_buffer[control_register_g] &= ~(1 << bit_g);
    213     }
    214     if (blue) {
    215         driver_buffers[led.driver].led_control_buffer[control_register_b] |= (1 << bit_b);
    216     } else {
    217         driver_buffers[led.driver].led_control_buffer[control_register_b] &= ~(1 << bit_b);
    218     }
    219 
    220     driver_buffers[led.driver].led_control_buffer_dirty = true;
    221 }
    222 
    223 void is31fl3731_update_pwm_buffers(uint8_t index) {
    224     if (driver_buffers[index].pwm_buffer_dirty) {
    225         is31fl3731_write_pwm_buffer(index);
    226 
    227         driver_buffers[index].pwm_buffer_dirty = false;
    228     }
    229 }
    230 
    231 void is31fl3731_update_led_control_registers(uint8_t index) {
    232     if (driver_buffers[index].led_control_buffer_dirty) {
    233         for (uint8_t i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) {
    234             is31fl3731_write_register(index, i, driver_buffers[index].led_control_buffer[i]);
    235         }
    236 
    237         driver_buffers[index].led_control_buffer_dirty = false;
    238     }
    239 }
    240 
    241 void is31fl3731_flush(void) {
    242     for (uint8_t i = 0; i < IS31FL3731_DRIVER_COUNT; i++) {
    243         is31fl3731_update_pwm_buffers(i);
    244     }
    245 }