qmk_firmware

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

is31fl3731-mono.c (8125B)


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