qmk_firmware

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

is31fl3745-mono.c (7804B)


      1 /* Copyright 2017 Jason Williams
      2  * Copyright 2018 Jack Humbert
      3  * Copyright 2018 Yiancar
      4  * Copyright 2020 MelGeek
      5  * Copyright 2021 MasterSpoon
      6  *
      7  * This program is free software: you can redistribute it and/or modify
      8  * it under the terms of the GNU General Public License as published by
      9  * the Free Software Foundation, either version 2 of the License, or
     10  * (at your option) any later version.
     11  *
     12  * This program is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15  * GNU General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU General Public License
     18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     19  */
     20 
     21 #include "is31fl3745-mono.h"
     22 #include "i2c_master.h"
     23 #include "gpio.h"
     24 #include "wait.h"
     25 
     26 #define IS31FL3745_PWM_REGISTER_COUNT 144
     27 #define IS31FL3745_SCALING_REGISTER_COUNT 144
     28 
     29 #ifndef IS31FL3745_I2C_TIMEOUT
     30 #    define IS31FL3745_I2C_TIMEOUT 100
     31 #endif
     32 
     33 #ifndef IS31FL3745_I2C_PERSISTENCE
     34 #    define IS31FL3745_I2C_PERSISTENCE 0
     35 #endif
     36 
     37 #ifndef IS31FL3745_CONFIGURATION
     38 #    define IS31FL3745_CONFIGURATION 0x31
     39 #endif
     40 
     41 #ifndef IS31FL3745_SW_PULLDOWN
     42 #    define IS31FL3745_SW_PULLDOWN IS31FL3745_PDR_2K_OHM_SW_OFF
     43 #endif
     44 
     45 #ifndef IS31FL3745_CS_PULLUP
     46 #    define IS31FL3745_CS_PULLUP IS31FL3745_PUR_2K_OHM_CS_OFF
     47 #endif
     48 
     49 #ifndef IS31FL3745_GLOBAL_CURRENT
     50 #    define IS31FL3745_GLOBAL_CURRENT 0xFF
     51 #endif
     52 
     53 #ifndef IS31FL3745_SYNC_1
     54 #    define IS31FL3745_SYNC_1 IS31FL3745_SYNC_NONE
     55 #endif
     56 #ifndef IS31FL3745_SYNC_2
     57 #    define IS31FL3745_SYNC_2 IS31FL3745_SYNC_NONE
     58 #endif
     59 #ifndef IS31FL3745_SYNC_3
     60 #    define IS31FL3745_SYNC_3 IS31FL3745_SYNC_NONE
     61 #endif
     62 #ifndef IS31FL3745_SYNC_4
     63 #    define IS31FL3745_SYNC_4 IS31FL3745_SYNC_NONE
     64 #endif
     65 
     66 const uint8_t i2c_addresses[IS31FL3745_DRIVER_COUNT] = {
     67     IS31FL3745_I2C_ADDRESS_1,
     68 #ifdef IS31FL3745_I2C_ADDRESS_2
     69     IS31FL3745_I2C_ADDRESS_2,
     70 #    ifdef IS31FL3745_I2C_ADDRESS_3
     71     IS31FL3745_I2C_ADDRESS_3,
     72 #        ifdef IS31FL3745_I2C_ADDRESS_4
     73     IS31FL3745_I2C_ADDRESS_4,
     74 #        endif
     75 #    endif
     76 #endif
     77 };
     78 
     79 const uint8_t driver_sync[IS31FL3745_DRIVER_COUNT] = {
     80     IS31FL3745_SYNC_1,
     81 #ifdef IS31FL3745_I2C_ADDRESS_2
     82     IS31FL3745_SYNC_2,
     83 #    ifdef IS31FL3745_I2C_ADDRESS_3
     84     IS31FL3745_SYNC_3,
     85 #        ifdef IS31FL3745_I2C_ADDRESS_4
     86     IS31FL3745_SYNC_4,
     87 #        endif
     88 #    endif
     89 #endif
     90 };
     91 
     92 typedef struct is31fl3745_driver_t {
     93     uint8_t pwm_buffer[IS31FL3745_PWM_REGISTER_COUNT];
     94     bool    pwm_buffer_dirty;
     95     uint8_t scaling_buffer[IS31FL3745_SCALING_REGISTER_COUNT];
     96     bool    scaling_buffer_dirty;
     97 } PACKED is31fl3745_driver_t;
     98 
     99 is31fl3745_driver_t driver_buffers[IS31FL3745_DRIVER_COUNT] = {{
    100     .pwm_buffer           = {0},
    101     .pwm_buffer_dirty     = false,
    102     .scaling_buffer       = {0},
    103     .scaling_buffer_dirty = false,
    104 }};
    105 
    106 void is31fl3745_write_register(uint8_t index, uint8_t reg, uint8_t data) {
    107 #if IS31FL3745_I2C_PERSISTENCE > 0
    108     for (uint8_t i = 0; i < IS31FL3745_I2C_PERSISTENCE; i++) {
    109         if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3745_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
    110     }
    111 #else
    112     i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3745_I2C_TIMEOUT);
    113 #endif
    114 }
    115 
    116 void is31fl3745_select_page(uint8_t index, uint8_t page) {
    117     is31fl3745_write_register(index, IS31FL3745_REG_COMMAND_WRITE_LOCK, IS31FL3745_COMMAND_WRITE_LOCK_MAGIC);
    118     is31fl3745_write_register(index, IS31FL3745_REG_COMMAND, page);
    119 }
    120 
    121 void is31fl3745_write_pwm_buffer(uint8_t index) {
    122     // Assumes page 0 is already selected.
    123     // Transmit PWM registers in 8 transfers of 18 bytes.
    124 
    125     // Iterate over the pwm_buffer contents at 18 byte intervals.
    126     for (uint8_t i = 0; i < IS31FL3745_PWM_REGISTER_COUNT; i += 18) {
    127 #if IS31FL3745_I2C_PERSISTENCE > 0
    128         for (uint8_t j = 0; j < IS31FL3745_I2C_PERSISTENCE; j++) {
    129             if (i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3745_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
    130         }
    131 #else
    132         i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3745_I2C_TIMEOUT);
    133 #endif
    134     }
    135 }
    136 
    137 void is31fl3745_init_drivers(void) {
    138     i2c_init();
    139 
    140 #if defined(IS31FL3745_SDB_PIN)
    141     gpio_set_pin_output(IS31FL3745_SDB_PIN);
    142     gpio_write_pin_high(IS31FL3745_SDB_PIN);
    143 #endif
    144 
    145     for (uint8_t i = 0; i < IS31FL3745_DRIVER_COUNT; i++) {
    146         is31fl3745_init(i);
    147     }
    148 
    149     for (int i = 0; i < IS31FL3745_LED_COUNT; i++) {
    150         is31fl3745_set_scaling_register(i, 0xFF);
    151     }
    152 
    153     for (uint8_t i = 0; i < IS31FL3745_DRIVER_COUNT; i++) {
    154         is31fl3745_update_scaling_registers(i);
    155     }
    156 }
    157 
    158 void is31fl3745_init(uint8_t index) {
    159     // In order to avoid the LEDs being driven with garbage data
    160     // in the LED driver's PWM registers, shutdown is enabled last.
    161     // Set up the mode and other settings, clear the PWM registers,
    162     // then disable software shutdown.
    163 
    164     is31fl3745_select_page(index, IS31FL3745_COMMAND_SCALING);
    165 
    166     // Turn off all LEDs.
    167     for (uint8_t i = 0; i < IS31FL3745_SCALING_REGISTER_COUNT; i++) {
    168         is31fl3745_write_register(index, i + 1, 0x00);
    169     }
    170 
    171     is31fl3745_select_page(index, IS31FL3745_COMMAND_PWM);
    172 
    173     for (uint8_t i = 0; i < IS31FL3745_PWM_REGISTER_COUNT; i++) {
    174         is31fl3745_write_register(index, i + 1, 0x00);
    175     }
    176 
    177     is31fl3745_select_page(index, IS31FL3745_COMMAND_FUNCTION);
    178 
    179     uint8_t sync = driver_sync[index];
    180 
    181     is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_PULLDOWNUP, (IS31FL3745_SW_PULLDOWN << 4) | IS31FL3745_CS_PULLUP);
    182     is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3745_GLOBAL_CURRENT);
    183     is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_SPREAD_SPECTRUM, (sync & 0b11) << 6);
    184     is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_CONFIGURATION, IS31FL3745_CONFIGURATION);
    185 
    186     // Wait 10ms to ensure the device has woken up.
    187     wait_ms(10);
    188 }
    189 
    190 void is31fl3745_set_value(int index, uint8_t value) {
    191     is31fl3745_led_t led;
    192 
    193     if (index >= 0 && index < IS31FL3745_LED_COUNT) {
    194         memcpy_P(&led, (&g_is31fl3745_leds[index]), sizeof(led));
    195 
    196         if (driver_buffers[led.driver].pwm_buffer[led.v] == value) {
    197             return;
    198         }
    199 
    200         driver_buffers[led.driver].pwm_buffer[led.v] = value;
    201         driver_buffers[led.driver].pwm_buffer_dirty  = true;
    202     }
    203 }
    204 
    205 void is31fl3745_set_value_all(uint8_t value) {
    206     for (int i = 0; i < IS31FL3745_LED_COUNT; i++) {
    207         is31fl3745_set_value(i, value);
    208     }
    209 }
    210 
    211 void is31fl3745_set_scaling_register(uint8_t index, uint8_t value) {
    212     is31fl3745_led_t led;
    213     memcpy_P(&led, (&g_is31fl3745_leds[index]), sizeof(led));
    214 
    215     driver_buffers[led.driver].scaling_buffer[led.v] = value;
    216     driver_buffers[led.driver].scaling_buffer_dirty  = true;
    217 }
    218 
    219 void is31fl3745_update_pwm_buffers(uint8_t index) {
    220     if (driver_buffers[index].pwm_buffer_dirty) {
    221         is31fl3745_select_page(index, IS31FL3745_COMMAND_PWM);
    222 
    223         is31fl3745_write_pwm_buffer(index);
    224 
    225         driver_buffers[index].pwm_buffer_dirty = false;
    226     }
    227 }
    228 
    229 void is31fl3745_update_scaling_registers(uint8_t index) {
    230     if (driver_buffers[index].scaling_buffer_dirty) {
    231         is31fl3745_select_page(index, IS31FL3745_COMMAND_SCALING);
    232 
    233         for (uint8_t i = 0; i < IS31FL3745_SCALING_REGISTER_COUNT; i++) {
    234             is31fl3745_write_register(index, i + 1, driver_buffers[index].scaling_buffer[i]);
    235         }
    236 
    237         driver_buffers[index].scaling_buffer_dirty = false;
    238     }
    239 }
    240 
    241 void is31fl3745_flush(void) {
    242     for (uint8_t i = 0; i < IS31FL3745_DRIVER_COUNT; i++) {
    243         is31fl3745_update_pwm_buffers(i);
    244     }
    245 }