* initial rgb driver fix * added underglow LEDs and fixed typo in RGB locations * removed test code * added my key maps * updated rgb keymap to work with changes * refactored my code to make it more maintainable and updated keymaps. * added GPL licence * Turned off matrix scan rate debug info * added checks if RGB matrix is enabled to fix errors when building keymaps without RGB matrix enabled * Apply suggestions from code review by fauxpark Co-authored-by: Ryan <fauxpark@gmail.com> * Renamed led driver file to be less ambiguous * Renamed is31fl3733 driver files to is31fl3733-dual Co-authored-by: Ryan <fauxpark@gmail.com>pull/12380/head
@ -0,0 +1,150 @@ | |||
/* Copyright 2018 Jack Humbert | |||
* Copyright 2018 Yiancar | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
/* This library is only valid for STM32 processors. | |||
* This library follows the convention of the AVR i2c_master library. | |||
* As a result addresses are expected to be already shifted (addr << 1). | |||
* I2CD1 is the default driver which corresponds to pins B6 and B7. This | |||
* can be changed. | |||
* Please ensure that HAL_USE_I2C is TRUE in the halconf.h file and that | |||
* STM32_I2C_USE_I2C1 is TRUE in the mcuconf.h file. Pins B6 and B7 are used | |||
* but using any other I2C pins should be trivial. | |||
*/ | |||
#ifdef RGB_MATRIX_ENABLE | |||
#include "quantum.h" | |||
#include "i2c_master.h" | |||
#include <string.h> | |||
#include <hal.h> | |||
static uint8_t i2c_address; | |||
I2CDriver *drivers[I2C_COUNT]; | |||
static const I2CConfig i2cconfig = { | |||
#if defined(USE_I2CV1_CONTRIB) | |||
I2C1_CLOCK_SPEED, | |||
#elif defined(USE_I2CV1) | |||
I2C1_OPMODE, | |||
I2C1_CLOCK_SPEED, | |||
I2C1_DUTY_CYCLE, | |||
#else | |||
// This configures the I2C clock to 400khz assuming a 72Mhz clock | |||
// For more info : https://www.st.com/en/embedded-software/stsw-stm32126.html | |||
STM32_TIMINGR_PRESC(I2C1_TIMINGR_PRESC) | STM32_TIMINGR_SCLDEL(I2C1_TIMINGR_SCLDEL) | STM32_TIMINGR_SDADEL(I2C1_TIMINGR_SDADEL) | STM32_TIMINGR_SCLH(I2C1_TIMINGR_SCLH) | STM32_TIMINGR_SCLL(I2C1_TIMINGR_SCLL), 0, 0 | |||
#endif | |||
}; | |||
static i2c_status_t chibios_to_qmk(const msg_t* status) { | |||
switch (*status) { | |||
case I2C_NO_ERROR: | |||
return I2C_STATUS_SUCCESS; | |||
case I2C_TIMEOUT: | |||
return I2C_STATUS_TIMEOUT; | |||
// I2C_BUS_ERROR, I2C_ARBITRATION_LOST, I2C_ACK_FAILURE, I2C_OVERRUN, I2C_PEC_ERROR, I2C_SMB_ALERT | |||
default: | |||
return I2C_STATUS_ERROR; | |||
} | |||
} | |||
__attribute__((weak)) void i2c_init(I2CDriver *driver, ioportid_t scl_port, ioportid_t sda_port, iopadid_t scl_pad, iopadid_t sda_pad) { | |||
static uint8_t index = 0; | |||
if (index < I2C_COUNT) { | |||
// Try releasing special pins for a short time | |||
palSetPadMode(scl_port, scl_pad, PAL_MODE_INPUT); | |||
palSetPadMode(sda_port, sda_pad, PAL_MODE_INPUT); | |||
chThdSleepMilliseconds(10); | |||
#if defined(USE_GPIOV1) | |||
palSetPadMode(scl_port, scl_pad, I2C1_SCL_PAL_MODE); | |||
palSetPadMode(sda_port, sda_pad, I2C1_SDA_PAL_MODE); | |||
#else | |||
palSetPadMode(scl_port, scl_pad, PAL_MODE_ALTERNATE(I2C1_SCL_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN); | |||
palSetPadMode(sda_port, sda_pad, PAL_MODE_ALTERNATE(I2C1_SDA_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN); | |||
#endif | |||
drivers[index++] = driver; | |||
} | |||
} | |||
i2c_status_t i2c_start(uint8_t index, uint8_t address) { | |||
if(index >= I2C_COUNT) { | |||
return I2C_STATUS_ERROR; | |||
} | |||
i2c_address = address; | |||
i2cStart(drivers[index], &i2cconfig); | |||
return I2C_STATUS_SUCCESS; | |||
} | |||
i2c_status_t i2c_transmit(uint8_t index, uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout) { | |||
if(index >= I2C_COUNT) { | |||
return I2C_STATUS_ERROR; | |||
} | |||
i2c_address = address; | |||
i2cStart(drivers[index], &i2cconfig); | |||
msg_t status = i2cMasterTransmitTimeout(drivers[index], (i2c_address >> 1), data, length, 0, 0, TIME_MS2I(timeout)); | |||
return chibios_to_qmk(&status); | |||
} | |||
i2c_status_t i2c_receive(uint8_t index, uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout) { | |||
if(index >= I2C_COUNT) { | |||
return I2C_STATUS_ERROR; | |||
} | |||
i2c_address = address; | |||
i2cStart(drivers[index], &i2cconfig); | |||
msg_t status = i2cMasterReceiveTimeout(drivers[index], (i2c_address >> 1), data, length, TIME_MS2I(timeout)); | |||
return chibios_to_qmk(&status); | |||
} | |||
i2c_status_t i2c_writeReg(uint8_t index, uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) { | |||
if(index >= I2C_COUNT) { | |||
return I2C_STATUS_ERROR; | |||
} | |||
i2c_address = devaddr; | |||
i2cStart(drivers[index], &i2cconfig); | |||
uint8_t complete_packet[length + 1]; | |||
for (uint8_t i = 0; i < length; i++) { | |||
complete_packet[i + 1] = data[i]; | |||
} | |||
complete_packet[0] = regaddr; | |||
msg_t status = i2cMasterTransmitTimeout(drivers[index], (i2c_address >> 1), complete_packet, length + 1, 0, 0, TIME_MS2I(timeout)); | |||
return chibios_to_qmk(&status); | |||
} | |||
i2c_status_t i2c_readReg(uint8_t index, uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) { | |||
if(index >= I2C_COUNT) { | |||
return I2C_STATUS_ERROR; | |||
} | |||
i2c_address = devaddr; | |||
i2cStart(drivers[index], &i2cconfig); | |||
msg_t status = i2cMasterTransmitTimeout(drivers[index], (i2c_address >> 1), ®addr, 1, data, length, TIME_MS2I(timeout)); | |||
return chibios_to_qmk(&status); | |||
} | |||
void i2c_stop(uint8_t index) { | |||
if(index < I2C_COUNT) { | |||
i2cStop(drivers[index]); | |||
} | |||
} | |||
#endif |
@ -0,0 +1,127 @@ | |||
/* Copyright 2018 Jack Humbert | |||
* Copyright 2018 Yiancar | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
/* This library follows the convention of the AVR i2c_master library. | |||
* As a result addresses are expected to be already shifted (addr << 1). | |||
* I2CD1 is the default driver which corresponds to pins B6 and B7. This | |||
* can be changed. | |||
* Please ensure that HAL_USE_I2C is TRUE in the halconf.h file and that | |||
* STM32_I2C_USE_I2C1 is TRUE in the mcuconf.h file. | |||
*/ | |||
#pragma once | |||
#include <ch.h> | |||
#include <hal.h> | |||
#ifndef I2C_COUNT | |||
# define I2C_COUNT 1 | |||
#endif | |||
#ifdef I2C1_BANK | |||
# define I2C1_SCL_BANK I2C1_BANK | |||
# define I2C1_SDA_BANK I2C1_BANK | |||
#endif | |||
#ifndef I2C1_SCL_BANK | |||
# define I2C1_SCL_BANK GPIOB | |||
#endif | |||
#ifndef I2C1_SDA_BANK | |||
# define I2C1_SDA_BANK GPIOB | |||
#endif | |||
#ifdef USE_I2C2 | |||
# ifdef I2C2_BANK | |||
# define I2C2_SCL_BANK I2C2_BANK | |||
# define I2C2_SDA_BANK I2C2_BANK | |||
# endif | |||
# ifndef I2C2_SCL_BANK | |||
# define I2C2_SCL_BANK GPIOC | |||
# endif | |||
# ifndef I2C2_SDA_BANK | |||
# define I2C2_SDA_BANK GPIOC | |||
# endif | |||
#endif | |||
#ifndef I2C1_SCL | |||
# define I2C1_SCL 6 | |||
#endif | |||
#ifndef I2C1_SDA | |||
# define I2C1_SDA 7 | |||
#endif | |||
#ifdef USE_I2CV1 | |||
# ifndef I2C1_OPMODE | |||
# define I2C1_OPMODE OPMODE_I2C | |||
# endif | |||
# ifndef I2C1_CLOCK_SPEED | |||
# define I2C1_CLOCK_SPEED 100000 /* 400000 */ | |||
# endif | |||
# ifndef I2C1_DUTY_CYCLE | |||
# define I2C1_DUTY_CYCLE STD_DUTY_CYCLE /* FAST_DUTY_CYCLE_2 */ | |||
# endif | |||
#else | |||
// The default timing values below configures the I2C clock to 400khz assuming a 72Mhz clock | |||
// For more info : https://www.st.com/en/embedded-software/stsw-stm32126.html | |||
# ifndef I2C1_TIMINGR_PRESC | |||
# define I2C1_TIMINGR_PRESC 0U | |||
# endif | |||
# ifndef I2C1_TIMINGR_SCLDEL | |||
# define I2C1_TIMINGR_SCLDEL 7U | |||
# endif | |||
# ifndef I2C1_TIMINGR_SDADEL | |||
# define I2C1_TIMINGR_SDADEL 0U | |||
# endif | |||
# ifndef I2C1_TIMINGR_SCLH | |||
# define I2C1_TIMINGR_SCLH 38U | |||
# endif | |||
# ifndef I2C1_TIMINGR_SCLL | |||
# define I2C1_TIMINGR_SCLL 129U | |||
# endif | |||
#endif | |||
#ifdef USE_GPIOV1 | |||
# ifndef I2C1_SCL_PAL_MODE | |||
# define I2C1_SCL_PAL_MODE PAL_MODE_STM32_ALTERNATE_OPENDRAIN | |||
# endif | |||
# ifndef I2C1_SDA_PAL_MODE | |||
# define I2C1_SDA_PAL_MODE PAL_MODE_STM32_ALTERNATE_OPENDRAIN | |||
# endif | |||
#else | |||
// The default PAL alternate modes are used to signal that the pins are used for I2C | |||
# ifndef I2C1_SCL_PAL_MODE | |||
# define I2C1_SCL_PAL_MODE 4 | |||
# endif | |||
# ifndef I2C1_SDA_PAL_MODE | |||
# define I2C1_SDA_PAL_MODE 4 | |||
# endif | |||
#endif | |||
typedef int16_t i2c_status_t; | |||
#define I2C_STATUS_SUCCESS (0) | |||
#define I2C_STATUS_ERROR (-1) | |||
#define I2C_STATUS_TIMEOUT (-2) | |||
void i2c_init(I2CDriver *driver, ioportid_t scl_port, ioportid_t sda_port, iopadid_t scl_pad, iopadid_t sda_pad); | |||
i2c_status_t i2c_start(uint8_t index, uint8_t address); | |||
i2c_status_t i2c_transmit(uint8_t index, uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout); | |||
i2c_status_t i2c_receive(uint8_t index, uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout); | |||
i2c_status_t i2c_writeReg(uint8_t index, uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout); | |||
i2c_status_t i2c_readReg(uint8_t index, uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout); | |||
void i2c_stop(uint8_t index); |
@ -0,0 +1,236 @@ | |||
/* Copyright 2017 Jason Williams | |||
* Copyright 2018 Jack Humbert | |||
* Copyright 2018 Yiancar | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifdef RGB_MATRIX_ENABLE | |||
#include "is31fl3733-dual.h" | |||
#include "i2c_master.h" | |||
#include "wait.h" | |||
// This is a 7-bit address, that gets left-shifted and bit 0 | |||
// set to 0 for write, 1 for read (as per I2C protocol) | |||
// The address will vary depending on your wiring: | |||
// 00 <-> GND | |||
// 01 <-> SCL | |||
// 10 <-> SDA | |||
// 11 <-> VCC | |||
// ADDR1 represents A1:A0 of the 7-bit address. | |||
// ADDR2 represents A3:A2 of the 7-bit address. | |||
// The result is: 0b101(ADDR2)(ADDR1) | |||
#define ISSI_ADDR_DEFAULT 0x50 | |||
#define ISSI_COMMANDREGISTER 0xFD | |||
#define ISSI_COMMANDREGISTER_WRITELOCK 0xFE | |||
#define ISSI_INTERRUPTMASKREGISTER 0xF0 | |||
#define ISSI_INTERRUPTSTATUSREGISTER 0xF1 | |||
#define ISSI_PAGE_LEDCONTROL 0x00 // PG0 | |||
#define ISSI_PAGE_PWM 0x01 // PG1 | |||
#define ISSI_PAGE_AUTOBREATH 0x02 // PG2 | |||
#define ISSI_PAGE_FUNCTION 0x03 // PG3 | |||
#define ISSI_REG_CONFIGURATION 0x00 // PG3 | |||
#define ISSI_REG_GLOBALCURRENT 0x01 // PG3 | |||
#define ISSI_REG_RESET 0x11 // PG3 | |||
#define ISSI_REG_SWPULLUP 0x0F // PG3 | |||
#define ISSI_REG_CSPULLUP 0x10 // PG3 | |||
#ifndef ISSI_TIMEOUT | |||
# define ISSI_TIMEOUT 100 | |||
#endif | |||
#ifndef ISSI_PERSISTENCE | |||
# define ISSI_PERSISTENCE 0 | |||
#endif | |||
// Transfer buffer for TWITransmitData() | |||
uint8_t g_twi_transfer_buffer[20]; | |||
// These buffers match the IS31FL3733 PWM registers. | |||
// The control buffers match the PG0 LED On/Off registers. | |||
// Storing them like this is optimal for I2C transfers to the registers. | |||
// We could optimize this and take out the unused registers from these | |||
// buffers and the transfers in IS31FL3733_write_pwm_buffer() but it's | |||
// probably not worth the extra complexity. | |||
uint8_t g_pwm_buffer[DRIVER_COUNT][192]; | |||
bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false}; | |||
uint8_t g_led_control_registers[DRIVER_COUNT][24] = {{0}, {0}}; | |||
bool g_led_control_registers_update_required[DRIVER_COUNT] = {false}; | |||
bool IS31FL3733_write_register(uint8_t index, uint8_t addr, uint8_t reg, uint8_t data) { | |||
// If the transaction fails function returns false. | |||
g_twi_transfer_buffer[0] = reg; | |||
g_twi_transfer_buffer[1] = data; | |||
#if ISSI_PERSISTENCE > 0 | |||
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) { | |||
if (i2c_transmit(index, addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) != 0) { | |||
return false; | |||
} | |||
} | |||
#else | |||
if (i2c_transmit(index, addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) != 0) { | |||
return false; | |||
} | |||
#endif | |||
return true; | |||
} | |||
bool IS31FL3733_write_pwm_buffer(uint8_t index, uint8_t addr, uint8_t *pwm_buffer) { | |||
// Assumes PG1 is already selected. | |||
// If any of the transactions fails function returns false. | |||
// Transmit PWM registers in 12 transfers of 16 bytes. | |||
// g_twi_transfer_buffer[] is 20 bytes | |||
// Iterate over the pwm_buffer contents at 16 byte intervals. | |||
for (int i = 0; i < 192; i += 16) { | |||
g_twi_transfer_buffer[0] = i; | |||
// Copy the data from i to i+15. | |||
// Device will auto-increment register for data after the first byte | |||
// Thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer. | |||
for (int j = 0; j < 16; j++) { | |||
g_twi_transfer_buffer[1 + j] = pwm_buffer[i + j]; | |||
} | |||
#if ISSI_PERSISTENCE > 0 | |||
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) { | |||
if (i2c_transmit(index, addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT) != 0) { | |||
return false; | |||
} | |||
} | |||
#else | |||
if (i2c_transmit(index, addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT) != 0) { | |||
return false; | |||
} | |||
#endif | |||
} | |||
return true; | |||
} | |||
void IS31FL3733_init(uint8_t bus, uint8_t addr, uint8_t sync) { | |||
// In order to avoid the LEDs being driven with garbage data | |||
// in the LED driver's PWM registers, shutdown is enabled last. | |||
// Set up the mode and other settings, clear the PWM registers, | |||
// then disable software shutdown. | |||
// Sync is passed so set it according to the datasheet. | |||
// Unlock the command register. | |||
IS31FL3733_write_register(bus, addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5); | |||
// Select PG0 | |||
IS31FL3733_write_register(bus, addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL); | |||
// Turn off all LEDs. | |||
for (int i = 0x00; i <= 0x17; i++) { | |||
IS31FL3733_write_register(bus, addr, i, 0x00); | |||
} | |||
// Unlock the command register. | |||
IS31FL3733_write_register(bus, addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5); | |||
// Select PG1 | |||
IS31FL3733_write_register(bus, addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM); | |||
// Set PWM on all LEDs to 0 | |||
// No need to setup Breath registers to PWM as that is the default. | |||
for (int i = 0x00; i <= 0xBF; i++) { | |||
IS31FL3733_write_register(bus, addr, i, 0x00); | |||
} | |||
// Unlock the command register. | |||
IS31FL3733_write_register(bus, addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5); | |||
// Select PG3 | |||
IS31FL3733_write_register(bus, addr, ISSI_COMMANDREGISTER, ISSI_PAGE_FUNCTION); | |||
// Set global current to maximum. | |||
IS31FL3733_write_register(bus, addr, ISSI_REG_GLOBALCURRENT, 0xFF); | |||
// Disable software shutdown. | |||
IS31FL3733_write_register(bus, addr, ISSI_REG_CONFIGURATION, (sync << 6) | 0x01); | |||
// Wait 10ms to ensure the device has woken up. | |||
wait_ms(10); | |||
} | |||
void IS31FL3733_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) { | |||
if (index >= 0 && index < DRIVER_LED_TOTAL) { | |||
is31_led led = g_is31_leds[index]; | |||
g_pwm_buffer[led.driver][led.r] = red; | |||
g_pwm_buffer[led.driver][led.g] = green; | |||
g_pwm_buffer[led.driver][led.b] = blue; | |||
g_pwm_buffer_update_required[led.driver] = true; | |||
} | |||
} | |||
void IS31FL3733_set_color_all(uint8_t red, uint8_t green, uint8_t blue) { | |||
for (int i = 0; i < DRIVER_LED_TOTAL; i++) { | |||
IS31FL3733_set_color(i, red, green, blue); | |||
} | |||
} | |||
void IS31FL3733_set_led_control_register(uint8_t index, bool red, bool green, bool blue) { | |||
is31_led led = g_is31_leds[index]; | |||
uint8_t control_register_r = led.r / 8; | |||
uint8_t control_register_g = led.g / 8; | |||
uint8_t control_register_b = led.b / 8; | |||
uint8_t bit_r = led.r % 8; | |||
uint8_t bit_g = led.g % 8; | |||
uint8_t bit_b = led.b % 8; | |||
if (red) { | |||
g_led_control_registers[led.driver][control_register_r] |= (1 << bit_r); | |||
} else { | |||
g_led_control_registers[led.driver][control_register_r] &= ~(1 << bit_r); | |||
} | |||
if (green) { | |||
g_led_control_registers[led.driver][control_register_g] |= (1 << bit_g); | |||
} else { | |||
g_led_control_registers[led.driver][control_register_g] &= ~(1 << bit_g); | |||
} | |||
if (blue) { | |||
g_led_control_registers[led.driver][control_register_b] |= (1 << bit_b); | |||
} else { | |||
g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b); | |||
} | |||
g_led_control_registers_update_required[led.driver] = true; | |||
} | |||
void IS31FL3733_update_pwm_buffers(uint8_t addr, uint8_t index) { | |||
if (g_pwm_buffer_update_required[index]) { | |||
// Firstly we need to unlock the command register and select PG1. | |||
IS31FL3733_write_register(index, addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5); | |||
IS31FL3733_write_register(index, addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM); | |||
// If any of the transactions fail we risk writing dirty PG0, | |||
// refresh page 0 just in case. | |||
if (!IS31FL3733_write_pwm_buffer(index, addr, g_pwm_buffer[index])) { | |||
g_led_control_registers_update_required[index] = true; | |||
} | |||
} | |||
g_pwm_buffer_update_required[index] = false; | |||
} | |||
void IS31FL3733_update_led_control_registers(uint8_t addr, uint8_t index) { | |||
if (g_led_control_registers_update_required[index]) { | |||
// Firstly we need to unlock the command register and select PG0 | |||
IS31FL3733_write_register(index, addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5); | |||
IS31FL3733_write_register(index, addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL); | |||
for (int i = 0; i < 24; i++) { | |||
IS31FL3733_write_register(index, addr, i, g_led_control_registers[index][i]); | |||
} | |||
} | |||
g_led_control_registers_update_required[index] = false; | |||
} | |||
#endif |
@ -0,0 +1,251 @@ | |||
/* Copyright 2017 Jason Williams | |||
* Copyright 2018 Jack Humbert | |||
* Copyright 2018 Yiancar | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#pragma once | |||
#include <stdint.h> | |||
#include <stdbool.h> | |||
typedef struct is31_led { | |||
uint8_t driver : 2; | |||
uint8_t r; | |||
uint8_t g; | |||
uint8_t b; | |||
} __attribute__((packed)) is31_led; | |||
extern const is31_led g_is31_leds[DRIVER_LED_TOTAL]; | |||
void IS31FL3733_init(uint8_t bus, uint8_t addr, uint8_t sync); | |||
bool IS31FL3733_write_register(uint8_t index, uint8_t addr, uint8_t reg, uint8_t data); | |||
bool IS31FL3733_write_pwm_buffer(uint8_t index, uint8_t addr, uint8_t *pwm_buffer); | |||
void IS31FL3733_set_color(int index, uint8_t red, uint8_t green, uint8_t blue); | |||
void IS31FL3733_set_color_all(uint8_t red, uint8_t green, uint8_t blue); | |||
void IS31FL3733_set_led_control_register(uint8_t index, bool red, bool green, bool blue); | |||
// This should not be called from an interrupt | |||
// (eg. from a timer interrupt). | |||
// Call this while idle (in between matrix scans). | |||
// If the buffer is dirty, it will update the driver with the buffer. | |||
void IS31FL3733_update_pwm_buffers(uint8_t addr, uint8_t index); // index is the driver index | |||
void IS31FL3733_update_led_control_registers(uint8_t addr, uint8_t index); | |||
#define A_1 0x00 | |||
#define A_2 0x01 | |||
#define A_3 0x02 | |||
#define A_4 0x03 | |||
#define A_5 0x04 | |||
#define A_6 0x05 | |||
#define A_7 0x06 | |||
#define A_8 0x07 | |||
#define A_9 0x08 | |||
#define A_10 0x09 | |||
#define A_11 0x0A | |||
#define A_12 0x0B | |||
#define A_13 0x0C | |||
#define A_14 0x0D | |||
#define A_15 0x0E | |||
#define A_16 0x0F | |||
#define B_1 0x10 | |||
#define B_2 0x11 | |||
#define B_3 0x12 | |||
#define B_4 0x13 | |||
#define B_5 0x14 | |||
#define B_6 0x15 | |||
#define B_7 0x16 | |||
#define B_8 0x17 | |||
#define B_9 0x18 | |||
#define B_10 0x19 | |||
#define B_11 0x1A | |||
#define B_12 0x1B | |||
#define B_13 0x1C | |||
#define B_14 0x1D | |||
#define B_15 0x1E | |||
#define B_16 0x1F | |||
#define C_1 0x20 | |||
#define C_2 0x21 | |||
#define C_3 0x22 | |||
#define C_4 0x23 | |||
#define C_5 0x24 | |||
#define C_6 0x25 | |||
#define C_7 0x26 | |||
#define C_8 0x27 | |||
#define C_9 0x28 | |||
#define C_10 0x29 | |||
#define C_11 0x2A | |||
#define C_12 0x2B | |||
#define C_13 0x2C | |||
#define C_14 0x2D | |||
#define C_15 0x2E | |||
#define C_16 0x2F | |||
#define D_1 0x30 | |||
#define D_2 0x31 | |||
#define D_3 0x32 | |||
#define D_4 0x33 | |||
#define D_5 0x34 | |||
#define D_6 0x35 | |||
#define D_7 0x36 | |||
#define D_8 0x37 | |||
#define D_9 0x38 | |||
#define D_10 0x39 | |||
#define D_11 0x3A | |||
#define D_12 0x3B | |||
#define D_13 0x3C | |||
#define D_14 0x3D | |||
#define D_15 0x3E | |||
#define D_16 0x3F | |||
#define E_1 0x40 | |||
#define E_2 0x41 | |||
#define E_3 0x42 | |||
#define E_4 0x43 | |||
#define E_5 0x44 | |||
#define E_6 0x45 | |||
#define E_7 0x46 | |||
#define E_8 0x47 | |||
#define E_9 0x48 | |||
#define E_10 0x49 | |||
#define E_11 0x4A | |||
#define E_12 0x4B | |||
#define E_13 0x4C | |||
#define E_14 0x4D | |||
#define E_15 0x4E | |||
#define E_16 0x4F | |||
#define F_1 0x50 | |||
#define F_2 0x51 | |||
#define F_3 0x52 | |||
#define F_4 0x53 | |||
#define F_5 0x54 | |||
#define F_6 0x55 | |||
#define F_7 0x56 | |||
#define F_8 0x57 | |||
#define F_9 0x58 | |||
#define F_10 0x59 | |||
#define F_11 0x5A | |||
#define F_12 0x5B | |||
#define F_13 0x5C | |||
#define F_14 0x5D | |||
#define F_15 0x5E | |||
#define F_16 0x5F | |||
#define G_1 0x60 | |||
#define G_2 0x61 | |||
#define G_3 0x62 | |||
#define G_4 0x63 | |||
#define G_5 0x64 | |||
#define G_6 0x65 | |||
#define G_7 0x66 | |||
#define G_8 0x67 | |||
#define G_9 0x68 | |||
#define G_10 0x69 | |||
#define G_11 0x6A | |||
#define G_12 0x6B | |||
#define G_13 0x6C | |||
#define G_14 0x6D | |||
#define G_15 0x6E | |||
#define G_16 0x6F | |||
#define H_1 0x70 | |||
#define H_2 0x71 | |||
#define H_3 0x72 | |||
#define H_4 0x73 | |||
#define H_5 0x74 | |||
#define H_6 0x75 | |||
#define H_7 0x76 | |||
#define H_8 0x77 | |||
#define H_9 0x78 | |||
#define H_10 0x79 | |||
#define H_11 0x7A | |||
#define H_12 0x7B | |||
#define H_13 0x7C | |||
#define H_14 0x7D | |||
#define H_15 0x7E | |||
#define H_16 0x7F | |||
#define I_1 0x80 | |||
#define I_2 0x81 | |||
#define I_3 0x82 | |||
#define I_4 0x83 | |||
#define I_5 0x84 | |||
#define I_6 0x85 | |||
#define I_7 0x86 | |||
#define I_8 0x87 | |||
#define I_9 0x88 | |||
#define I_10 0x89 | |||
#define I_11 0x8A | |||
#define I_12 0x8B | |||
#define I_13 0x8C | |||
#define I_14 0x8D | |||
#define I_15 0x8E | |||
#define I_16 0x8F | |||
#define J_1 0x90 | |||
#define J_2 0x91 | |||
#define J_3 0x92 | |||
#define J_4 0x93 | |||
#define J_5 0x94 | |||
#define J_6 0x95 | |||
#define J_7 0x96 | |||
#define J_8 0x97 | |||
#define J_9 0x98 | |||
#define J_10 0x99 | |||
#define J_11 0x9A | |||
#define J_12 0x9B | |||
#define J_13 0x9C | |||
#define J_14 0x9D | |||
#define J_15 0x9E | |||
#define J_16 0x9F | |||
#define K_1 0xA0 | |||
#define K_2 0xA1 | |||
#define K_3 0xA2 | |||
#define K_4 0xA3 | |||
#define K_5 0xA4 | |||
#define K_6 0xA5 | |||
#define K_7 0xA6 | |||
#define K_8 0xA7 | |||
#define K_9 0xA8 | |||
#define K_10 0xA9 | |||
#define K_11 0xAA | |||
#define K_12 0xAB | |||
#define K_13 0xAC | |||
#define K_14 0xAD | |||
#define K_15 0xAE | |||
#define K_16 0xAF | |||
#define L_1 0xB0 | |||
#define L_2 0xB1 | |||
#define L_3 0xB2 | |||
#define L_4 0xB3 | |||
#define L_5 0xB4 | |||
#define L_6 0xB5 | |||
#define L_7 0xB6 | |||
#define L_8 0xB7 | |||
#define L_9 0xB8 | |||
#define L_10 0xB9 | |||
#define L_11 0xBA | |||
#define L_12 0xBB | |||
#define L_13 0xBC | |||
#define L_14 0xBD | |||
#define L_15 0xBE | |||
#define L_16 0xBF |
@ -0,0 +1,57 @@ | |||
/* Copyright 2021 Andrew Fahmy | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifdef RGB_MATRIX_ENABLE | |||
#include "rgb_matrix.h" | |||
#include "i2c_master.h" | |||
#include "is31fl3733-dual.h" | |||
static void init(void) { | |||
i2c_init(&I2CD1, I2C1_SCL_BANK, I2C1_SDA_BANK, I2C1_SCL, I2C1_SDA); | |||
IS31FL3733_init(0, DRIVER_ADDR_1, 0); | |||
# ifdef USE_I2C2 | |||
i2c_init(&I2CD2, I2C2_SCL_BANK, I2C2_SDA_BANK, I2C2_SCL, I2C2_SDA); | |||
IS31FL3733_init(1, DRIVER_ADDR_2, 0); | |||
# endif | |||
for (int index = 0; index < DRIVER_LED_TOTAL; index++) { | |||
bool enabled = true; | |||
// This only caches it for later | |||
IS31FL3733_set_led_control_register(index, enabled, enabled, enabled); | |||
} | |||
IS31FL3733_update_led_control_registers(DRIVER_ADDR_1, 0); | |||
# ifdef USE_I2C2 | |||
IS31FL3733_update_led_control_registers(DRIVER_ADDR_2, 1); | |||
# endif | |||
} | |||
static void flush(void) { | |||
IS31FL3733_update_pwm_buffers(DRIVER_ADDR_1, 0); | |||
# ifdef USE_I2C2 | |||
IS31FL3733_update_pwm_buffers(DRIVER_ADDR_2, 1); | |||
# endif | |||
} | |||
const rgb_matrix_driver_t rgb_matrix_driver = { | |||
.init = init, | |||
.flush = flush, | |||
.set_color = IS31FL3733_set_color, | |||
.set_color_all = IS31FL3733_set_color_all, | |||
}; | |||
#endif |
@ -0,0 +1,52 @@ | |||
/* Copyright 2021 Andrew Fahmy | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#pragma once | |||
#ifdef RGB_MATRIX_ENABLE | |||
// # define RGB_MATRIX_FRAMEBUFFER_EFFECTS | |||
// # define RGB_MATRIX_KEYPRESSES | |||
# define RGB_MATRIX_STARTUP_MODE RGB_MATRIX_RAINDROPS | |||
// # define DEBUG_MATRIX_SCAN_RATE | |||
# define RGB_MATRIX_LED_FLUSH_LIMIT 100 | |||
// # define RGB_MATRIX_LED_PROCESS_LIMIT 2 | |||
// i2c_master defines | |||
# define I2C_COUNT 2 | |||
# define I2C1_BANK GPIOB | |||
# define I2C1_SCL 0 // A2 on pinout = B0 | |||
# define I2C1_SDA 1 // A2 on pinout = B1 | |||
# define I2C1_SCL_PAL_MODE PAL_MODE_ALTERNATIVE_2 | |||
# define I2C1_SDA_PAL_MODE PAL_MODE_ALTERNATIVE_2 | |||
# define USE_I2C2 | |||
# define I2C2_BANK GPIOC | |||
# define I2C2_SCL 10 // A2 on pinout = C10 | |||
# define I2C2_SDA 11 // A2 on pinout = C11 | |||
# define I2C2_SCL_PAL_MODE PAL_MODE_ALTERNATIVE_2 | |||
# define I2C2_SDA_PAL_MODE PAL_MODE_ALTERNATIVE_2 | |||
# define DRIVER_ADDR_1 0b1010000 | |||
# define DRIVER_ADDR_2 0b1010000 | |||
# define DRIVER_COUNT 2 | |||
# define DRIVER_1_LED_TOTAL 64 | |||
# define DRIVER_2_LED_TOTAL 55 | |||
# define DRIVER_LED_TOTAL (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL) | |||
#endif |
@ -0,0 +1,60 @@ | |||
/* Copyright 2021 Andrew Fahmy | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include QMK_KEYBOARD_H | |||
enum layer_names { | |||
_MAIN, | |||
_L1 | |||
}; | |||
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { | |||
[_MAIN] = LAYOUT_tkl_ansi( | |||
KC_ESC, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_PSCR, KC_SLCK, KC_PAUS, | |||
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_INS, KC_HOME, KC_PGUP, | |||
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_DEL, KC_END, KC_PGDN, | |||
MO(_L1), KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, | |||
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, | |||
KC_LCTL, _______, KC_LALT, KC_SPC, KC_RALT, MO(_L1), KC_APP, KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT | |||
), | |||
[_L1] = LAYOUT_tkl_ansi( | |||
RESET, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, RGB_TOG, RGB_MOD, _______, | |||
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, RGB_SPD, RGB_SPI, _______, RGB_HUI, RGB_SAI, RGB_VAI, | |||
_______, KC_MPRV, KC_MPLY, KC_MNXT, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, RGB_HUD, RGB_SAD, RGB_VAD, | |||
_______, KC_MUTE, KC_VOLD, KC_VOLU, _______, _______, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT, _______, _______, _______, | |||
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, | |||
_______, KC_LGUI, _______, _______, _______, _______, _______, _______, _______, _______, _______ | |||
) | |||
}; | |||
void keyboard_post_init_user(void) { | |||
// Customise these values to desired behaviour | |||
// debug_enable=true; | |||
// debug_matrix=true; | |||
// debug_keyboard=true; | |||
// debug_mouse=true; | |||
} | |||
#ifdef RGB_MATRIX_ENABLE | |||
// Turn off SDB | |||
void keyboard_pre_init_user() { | |||
palSetPadMode(GPIOB, 16, PAL_MODE_OUTPUT_PUSHPULL); | |||
palSetPad(GPIOB, 16); | |||
} | |||
#endif |
@ -0,0 +1 @@ | |||
RGB_MATRIX_ENABLE = yes |