* 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 |