From ad2bb529c795be066b279f52bebec03257992fc2 Mon Sep 17 00:00:00 2001 From: yiancar Date: Wed, 15 Aug 2018 08:19:38 +0300 Subject: [PATCH] Rgb matrix arm (#3648) * Addition of I2C master driver for STM32, Generalization of ISSI3731 driver - Addition of an i2c_master driver for STM32 to replicate expectations of AVR driver. - Moved ISSI3731 driver one level up to make it accesible by both architectures. - Renamed ISSI3731 functions to a more general name for preparation of other ISSI drivers. - Added compiler directives where necessary to differenciate each architecture. * converted tabs to spaces --- drivers/arm/i2c_master.c | 103 +++++++++++++ drivers/arm/i2c_master.h | 39 +++++ drivers/avr/is31fl3731.c | 262 ------------------------------- drivers/is31fl3731.c | 271 +++++++++++++++++++++++++++++++++ drivers/{avr => }/is31fl3731.h | 18 +-- quantum/rgb_matrix.c | 20 +-- 6 files changed, 432 insertions(+), 281 deletions(-) create mode 100644 drivers/arm/i2c_master.c create mode 100644 drivers/arm/i2c_master.h delete mode 100644 drivers/avr/is31fl3731.c create mode 100644 drivers/is31fl3731.c rename drivers/{avr => }/is31fl3731.h (86%) diff --git a/drivers/arm/i2c_master.c b/drivers/arm/i2c_master.c new file mode 100644 index 000000000..2fdd9f65e --- /dev/null +++ b/drivers/arm/i2c_master.c @@ -0,0 +1,103 @@ +/* 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 . + */ + +/* 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. + */ + +#include "i2c_master.h" +#include +#include + +static uint8_t i2c_address; + +// This configures the I2C clock to 400Mhz assuming a 72Mhz clock +// For more info : https://www.st.com/en/embedded-software/stsw-stm32126.html +static const I2CConfig i2cconfig = { + STM32_TIMINGR_PRESC(15U) | + STM32_TIMINGR_SCLDEL(4U) | STM32_TIMINGR_SDADEL(2U) | + STM32_TIMINGR_SCLH(15U) | STM32_TIMINGR_SCLL(21U), + 0, + 0 +}; + +void i2c_init(void) +{ + palSetGroupMode(GPIOB,6,7, PAL_MODE_INPUT); // Try releasing special pins for a short time + chThdSleepMilliseconds(10); + + palSetPadMode(GPIOB, 6, PAL_MODE_ALTERNATE(4) | PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_PUPDR_PULLUP); + palSetPadMode(GPIOB, 7, PAL_MODE_ALTERNATE(4) | PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_PUPDR_PULLUP); + + //i2cInit(); //This is invoked by halInit() so no need to redo it. +} + +// This is usually not needed +uint8_t i2c_start(uint8_t address) +{ + i2c_address = address; + i2cStart(&I2C_DRIVER, &i2cconfig); + return 0; +} + +uint8_t i2c_transmit(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout) +{ + i2c_address = address; + i2cStart(&I2C_DRIVER, &i2cconfig); + return i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), data, length, 0, 0, MS2ST(timeout)); +} + +uint8_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout) +{ + i2c_address = address; + i2cStart(&I2C_DRIVER, &i2cconfig); + return i2cMasterReceiveTimeout(&I2C_DRIVER, (i2c_address >> 1), data, length, MS2ST(timeout)); +} + +uint8_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) +{ + i2c_address = devaddr; + i2cStart(&I2C_DRIVER, &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 + + return i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), complete_packet, length + 1, 0, 0, MS2ST(timeout)); +} + +uint8_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) +{ + i2c_address = devaddr; + i2cStart(&I2C_DRIVER, &i2cconfig); + return i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), regaddr, 1, data, length, MS2ST(timeout)); +} + +// This is usually not needed. It releases the driver to allow pins to become GPIO again. +uint8_t i2c_stop(uint16_t timeout) +{ + i2c_address = address; + i2cStop(&I2C_DRIVER); + return 0; +} diff --git a/drivers/arm/i2c_master.h b/drivers/arm/i2c_master.h new file mode 100644 index 000000000..9d51245be --- /dev/null +++ b/drivers/arm/i2c_master.h @@ -0,0 +1,39 @@ +/* Copyright 2018 Jack Humbert + * Copyright 2018 Yiancar + * + * This program is free sofare: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Sofare 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 . + */ + +/* 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. + */ + +#include "ch.h" +#include + +#ifndef I2C_DRIVER + #define I2C_DRIVER I2CD1 +#endif + +void i2c_init(void); +uint8_t i2c_start(uint8_t address); +uint8_t i2c_transmit(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout); +uint8_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout); +uint8_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout); +uint8_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout); +void i2c_stop(void); diff --git a/drivers/avr/is31fl3731.c b/drivers/avr/is31fl3731.c deleted file mode 100644 index 70813464b..000000000 --- a/drivers/avr/is31fl3731.c +++ /dev/null @@ -1,262 +0,0 @@ -/* Copyright 2017 Jason Williams - * Copyright 2018 Jack Humbert - * - * 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 . - */ - -#include "is31fl3731.h" -#include -#include -#include -#include -#include "i2c_master.h" -#include "progmem.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: -// 0b1110100 AD <-> GND -// 0b1110111 AD <-> VCC -// 0b1110101 AD <-> SCL -// 0b1110110 AD <-> SDA -#define ISSI_ADDR_DEFAULT 0x74 - -#define ISSI_REG_CONFIG 0x00 -#define ISSI_REG_CONFIG_PICTUREMODE 0x00 -#define ISSI_REG_CONFIG_AUTOPLAYMODE 0x08 -#define ISSI_REG_CONFIG_AUDIOPLAYMODE 0x18 - -#define ISSI_CONF_PICTUREMODE 0x00 -#define ISSI_CONF_AUTOFRAMEMODE 0x04 -#define ISSI_CONF_AUDIOMODE 0x08 - -#define ISSI_REG_PICTUREFRAME 0x01 - -#define ISSI_REG_SHUTDOWN 0x0A -#define ISSI_REG_AUDIOSYNC 0x06 - -#define ISSI_COMMANDREGISTER 0xFD -#define ISSI_BANK_FUNCTIONREG 0x0B // helpfully called 'page nine' - -#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 IS31FL3731 PWM registers 0x24-0xB3. -// 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 IS31FL3731_write_pwm_buffer() but it's -// probably not worth the extra complexity. -uint8_t g_pwm_buffer[DRIVER_COUNT][144]; -bool g_pwm_buffer_update_required = false; - -uint8_t g_led_control_registers[DRIVER_COUNT][18] = { { 0 }, { 0 } }; -bool g_led_control_registers_update_required = false; - -// This is the bit pattern in the LED control registers -// (for matrix A, add one to register for matrix B) -// -// reg - b7 b6 b5 b4 b3 b2 b1 b0 -// 0x00 - R08,R07,R06,R05,R04,R03,R02,R01 -// 0x02 - G08,G07,G06,G05,G04,G03,G02,R00 -// 0x04 - B08,B07,B06,B05,B04,B03,G01,G00 -// 0x06 - - , - , - , - , - ,B02,B01,B00 -// 0x08 - - , - , - , - , - , - , - , - -// 0x0A - B17,B16,B15, - , - , - , - , - -// 0x0C - G17,G16,B14,B13,B12,B11,B10,B09 -// 0x0E - R17,G15,G14,G13,G12,G11,G10,G09 -// 0x10 - R16,R15,R14,R13,R12,R11,R10,R09 - - -void IS31FL3731_write_register( uint8_t addr, uint8_t reg, uint8_t data ) -{ - 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(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) == 0) - break; - } - #else - i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT); - #endif -} - -void IS31FL3731_write_pwm_buffer( uint8_t addr, uint8_t *pwm_buffer ) -{ - // assumes bank is already selected - - // transmit PWM registers in 9 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 < 144; i += 16 ) { - // set the first register, e.g. 0x24, 0x34, 0x44, etc. - g_twi_transfer_buffer[0] = 0x24 + i; - // copy the data from i to i+15 - // device will auto-increment register for data after the first byte - // thus this sets registers 0x24-0x33, 0x34-0x43, 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(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT) == 0) - break; - } - #else - i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT); - #endif - } -} - -void IS31FL3731_init( uint8_t addr ) -{ - // In order to avoid the LEDs being driven with garbage data - // in the LED driver's PWM registers, first enable software shutdown, - // then set up the mode and other settings, clear the PWM registers, - // then disable software shutdown. - - // select "function register" bank - IS31FL3731_write_register( addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG ); - - // enable software shutdown - IS31FL3731_write_register( addr, ISSI_REG_SHUTDOWN, 0x00 ); - // this delay was copied from other drivers, might not be needed - _delay_ms( 10 ); - - // picture mode - IS31FL3731_write_register( addr, ISSI_REG_CONFIG, ISSI_REG_CONFIG_PICTUREMODE ); - // display frame 0 - IS31FL3731_write_register( addr, ISSI_REG_PICTUREFRAME, 0x00 ); - // audio sync off - IS31FL3731_write_register( addr, ISSI_REG_AUDIOSYNC, 0x00 ); - - // select bank 0 - IS31FL3731_write_register( addr, ISSI_COMMANDREGISTER, 0 ); - - // turn off all LEDs in the LED control register - for ( int i = 0x00; i <= 0x11; i++ ) - { - IS31FL3731_write_register( addr, i, 0x00 ); - } - - // turn off all LEDs in the blink control register (not really needed) - for ( int i = 0x12; i <= 0x23; i++ ) - { - IS31FL3731_write_register( addr, i, 0x00 ); - } - - // set PWM on all LEDs to 0 - for ( int i = 0x24; i <= 0xB3; i++ ) - { - IS31FL3731_write_register( addr, i, 0x00 ); - } - - // select "function register" bank - IS31FL3731_write_register( addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG ); - - // disable software shutdown - IS31FL3731_write_register( addr, ISSI_REG_SHUTDOWN, 0x01 ); - - // select bank 0 and leave it selected. - // most usage after initialization is just writing PWM buffers in bank 0 - // as there's not much point in double-buffering - IS31FL3731_write_register( addr, ISSI_COMMANDREGISTER, 0 ); - -} - -void IS31FL3731_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]; - - // Subtract 0x24 to get the second index of g_pwm_buffer - g_pwm_buffer[led.driver][led.r - 0x24] = red; - g_pwm_buffer[led.driver][led.g - 0x24] = green; - g_pwm_buffer[led.driver][led.b - 0x24] = blue; - g_pwm_buffer_update_required = true; - } -} - -void IS31FL3731_set_color_all( uint8_t red, uint8_t green, uint8_t blue ) -{ - for ( int i = 0; i < DRIVER_LED_TOTAL; i++ ) - { - IS31FL3731_set_color( i, red, green, blue ); - } -} - -void IS31FL3731_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 - 0x24) / 8; - uint8_t control_register_g = (led.g - 0x24) / 8; - uint8_t control_register_b = (led.b - 0x24) / 8; - uint8_t bit_r = (led.r - 0x24) % 8; - uint8_t bit_g = (led.g - 0x24) % 8; - uint8_t bit_b = (led.b - 0x24) % 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 = true; - -} - -void IS31FL3731_update_pwm_buffers( uint8_t addr1, uint8_t addr2 ) -{ - if ( g_pwm_buffer_update_required ) - { - IS31FL3731_write_pwm_buffer( addr1, g_pwm_buffer[0] ); - IS31FL3731_write_pwm_buffer( addr2, g_pwm_buffer[1] ); - } - g_pwm_buffer_update_required = false; -} - -void IS31FL3731_update_led_control_registers( uint8_t addr1, uint8_t addr2 ) -{ - if ( g_led_control_registers_update_required ) - { - for ( int i=0; i<18; i++ ) - { - IS31FL3731_write_register(addr1, i, g_led_control_registers[0][i] ); - IS31FL3731_write_register(addr2, i, g_led_control_registers[1][i] ); - } - } -} - diff --git a/drivers/is31fl3731.c b/drivers/is31fl3731.c new file mode 100644 index 000000000..0524feb49 --- /dev/null +++ b/drivers/is31fl3731.c @@ -0,0 +1,271 @@ +/* Copyright 2017 Jason Williams + * Copyright 2018 Jack Humbert + * + * 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 . + */ + +#ifdef __AVR__ +#include +#include +#include +#else +#include "wait.h" +#endif + +#include "is31fl3731.h" +#include +#include "i2c_master.h" +#include "progmem.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: +// 0b1110100 AD <-> GND +// 0b1110111 AD <-> VCC +// 0b1110101 AD <-> SCL +// 0b1110110 AD <-> SDA +#define ISSI_ADDR_DEFAULT 0x74 + +#define ISSI_REG_CONFIG 0x00 +#define ISSI_REG_CONFIG_PICTUREMODE 0x00 +#define ISSI_REG_CONFIG_AUTOPLAYMODE 0x08 +#define ISSI_REG_CONFIG_AUDIOPLAYMODE 0x18 + +#define ISSI_CONF_PICTUREMODE 0x00 +#define ISSI_CONF_AUTOFRAMEMODE 0x04 +#define ISSI_CONF_AUDIOMODE 0x08 + +#define ISSI_REG_PICTUREFRAME 0x01 + +#define ISSI_REG_SHUTDOWN 0x0A +#define ISSI_REG_AUDIOSYNC 0x06 + +#define ISSI_COMMANDREGISTER 0xFD +#define ISSI_BANK_FUNCTIONREG 0x0B // helpfully called 'page nine' + +#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 IS31FL3731 PWM registers 0x24-0xB3. +// 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 IS31FL3731_write_pwm_buffer() but it's +// probably not worth the extra complexity. +uint8_t g_pwm_buffer[DRIVER_COUNT][144]; +bool g_pwm_buffer_update_required = false; + +uint8_t g_led_control_registers[DRIVER_COUNT][18] = { { 0 }, { 0 } }; +bool g_led_control_registers_update_required = false; + +// This is the bit pattern in the LED control registers +// (for matrix A, add one to register for matrix B) +// +// reg - b7 b6 b5 b4 b3 b2 b1 b0 +// 0x00 - R08,R07,R06,R05,R04,R03,R02,R01 +// 0x02 - G08,G07,G06,G05,G04,G03,G02,R00 +// 0x04 - B08,B07,B06,B05,B04,B03,G01,G00 +// 0x06 - - , - , - , - , - ,B02,B01,B00 +// 0x08 - - , - , - , - , - , - , - , - +// 0x0A - B17,B16,B15, - , - , - , - , - +// 0x0C - G17,G16,B14,B13,B12,B11,B10,B09 +// 0x0E - R17,G15,G14,G13,G12,G11,G10,G09 +// 0x10 - R16,R15,R14,R13,R12,R11,R10,R09 + + +void IS31_write_register( uint8_t addr, uint8_t reg, uint8_t data ) +{ + 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(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) == 0) + break; + } + #else + i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT); + #endif +} + +void IS31_write_pwm_buffer( uint8_t addr, uint8_t *pwm_buffer ) +{ + // assumes bank is already selected + + // transmit PWM registers in 9 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 < 144; i += 16 ) { + // set the first register, e.g. 0x24, 0x34, 0x44, etc. + g_twi_transfer_buffer[0] = 0x24 + i; + // copy the data from i to i+15 + // device will auto-increment register for data after the first byte + // thus this sets registers 0x24-0x33, 0x34-0x43, 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(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT) == 0) + break; + } + #else + i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT); + #endif + } +} + +void IS31_init( uint8_t addr ) +{ + // In order to avoid the LEDs being driven with garbage data + // in the LED driver's PWM registers, first enable software shutdown, + // then set up the mode and other settings, clear the PWM registers, + // then disable software shutdown. + + // select "function register" bank + IS31_write_register( addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG ); + + // enable software shutdown + IS31_write_register( addr, ISSI_REG_SHUTDOWN, 0x00 ); + // this delay was copied from other drivers, might not be needed + #ifdef __AVR__ + _delay_ms( 10 ); + #else + wait_ms(10); + #endif + + // picture mode + IS31_write_register( addr, ISSI_REG_CONFIG, ISSI_REG_CONFIG_PICTUREMODE ); + // display frame 0 + IS31_write_register( addr, ISSI_REG_PICTUREFRAME, 0x00 ); + // audio sync off + IS31_write_register( addr, ISSI_REG_AUDIOSYNC, 0x00 ); + + // select bank 0 + IS31_write_register( addr, ISSI_COMMANDREGISTER, 0 ); + + // turn off all LEDs in the LED control register + for ( int i = 0x00; i <= 0x11; i++ ) + { + IS31_write_register( addr, i, 0x00 ); + } + + // turn off all LEDs in the blink control register (not really needed) + for ( int i = 0x12; i <= 0x23; i++ ) + { + IS31_write_register( addr, i, 0x00 ); + } + + // set PWM on all LEDs to 0 + for ( int i = 0x24; i <= 0xB3; i++ ) + { + IS31_write_register( addr, i, 0x00 ); + } + + // select "function register" bank + IS31_write_register( addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG ); + + // disable software shutdown + IS31_write_register( addr, ISSI_REG_SHUTDOWN, 0x01 ); + + // select bank 0 and leave it selected. + // most usage after initialization is just writing PWM buffers in bank 0 + // as there's not much point in double-buffering + IS31_write_register( addr, ISSI_COMMANDREGISTER, 0 ); + +} + +void IS31_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]; + + // Subtract 0x24 to get the second index of g_pwm_buffer + g_pwm_buffer[led.driver][led.r - 0x24] = red; + g_pwm_buffer[led.driver][led.g - 0x24] = green; + g_pwm_buffer[led.driver][led.b - 0x24] = blue; + g_pwm_buffer_update_required = true; + } +} + +void IS31_set_color_all( uint8_t red, uint8_t green, uint8_t blue ) +{ + for ( int i = 0; i < DRIVER_LED_TOTAL; i++ ) + { + IS31_set_color( i, red, green, blue ); + } +} + +void IS31_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 - 0x24) / 8; + uint8_t control_register_g = (led.g - 0x24) / 8; + uint8_t control_register_b = (led.b - 0x24) / 8; + uint8_t bit_r = (led.r - 0x24) % 8; + uint8_t bit_g = (led.g - 0x24) % 8; + uint8_t bit_b = (led.b - 0x24) % 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 = true; + +} + +void IS31_update_pwm_buffers( uint8_t addr1, uint8_t addr2 ) +{ + if ( g_pwm_buffer_update_required ) + { + IS31_write_pwm_buffer( addr1, g_pwm_buffer[0] ); + IS31_write_pwm_buffer( addr2, g_pwm_buffer[1] ); + } + g_pwm_buffer_update_required = false; +} + +void IS31_update_led_control_registers( uint8_t addr1, uint8_t addr2 ) +{ + if ( g_led_control_registers_update_required ) + { + for ( int i=0; i<18; i++ ) + { + IS31_write_register(addr1, i, g_led_control_registers[0][i] ); + IS31_write_register(addr2, i, g_led_control_registers[1][i] ); + } + } +} + diff --git a/drivers/avr/is31fl3731.h b/drivers/is31fl3731.h similarity index 86% rename from drivers/avr/is31fl3731.h rename to drivers/is31fl3731.h index 3d30fc67b..9e195c240 100644 --- a/drivers/avr/is31fl3731.h +++ b/drivers/is31fl3731.h @@ -23,7 +23,7 @@ #include typedef struct is31_led { - uint8_t driver:2; + uint8_t driver:2; uint8_t r; uint8_t g; uint8_t b; @@ -31,21 +31,21 @@ typedef struct is31_led { extern const is31_led g_is31_leds[DRIVER_LED_TOTAL]; -void IS31FL3731_init( uint8_t addr ); -void IS31FL3731_write_register( uint8_t addr, uint8_t reg, uint8_t data ); -void IS31FL3731_write_pwm_buffer( uint8_t addr, uint8_t *pwm_buffer ); +void IS31_init( uint8_t addr ); +void IS31_write_register( uint8_t addr, uint8_t reg, uint8_t data ); +void IS31_write_pwm_buffer( uint8_t addr, uint8_t *pwm_buffer ); -void IS31FL3731_set_color( int index, uint8_t red, uint8_t green, uint8_t blue ); -void IS31FL3731_set_color_all( uint8_t red, uint8_t green, uint8_t blue ); +void IS31_set_color( int index, uint8_t red, uint8_t green, uint8_t blue ); +void IS31_set_color_all( uint8_t red, uint8_t green, uint8_t blue ); -void IS31FL3731_set_led_control_register( uint8_t index, bool red, bool green, bool blue ); +void IS31_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 IS31FL3731_update_pwm_buffers( uint8_t addr1, uint8_t addr2 ); -void IS31FL3731_update_led_control_registers( uint8_t addr1, uint8_t addr2 ); +void IS31_update_pwm_buffers( uint8_t addr1, uint8_t addr2 ); +void IS31_update_led_control_registers( uint8_t addr1, uint8_t addr2 ); #define C1_1 0x24 #define C1_2 0x25 diff --git a/quantum/rgb_matrix.c b/quantum/rgb_matrix.c index b4bbc3dc0..70ad1a178 100644 --- a/quantum/rgb_matrix.c +++ b/quantum/rgb_matrix.c @@ -106,16 +106,16 @@ void map_row_column_to_led( uint8_t row, uint8_t column, uint8_t *led_i, uint8_t } void rgb_matrix_update_pwm_buffers(void) { - IS31FL3731_update_pwm_buffers( DRIVER_ADDR_1, DRIVER_ADDR_2 ); - IS31FL3731_update_led_control_registers( DRIVER_ADDR_1, DRIVER_ADDR_2 ); + IS31_update_pwm_buffers( DRIVER_ADDR_1, DRIVER_ADDR_2 ); + IS31_update_led_control_registers( DRIVER_ADDR_1, DRIVER_ADDR_2 ); } void rgb_matrix_set_color( int index, uint8_t red, uint8_t green, uint8_t blue ) { - IS31FL3731_set_color( index, red, green, blue ); + IS31_set_color( index, red, green, blue ); } void rgb_matrix_set_color_all( uint8_t red, uint8_t green, uint8_t blue ) { - IS31FL3731_set_color_all( red, green, blue ); + IS31_set_color_all( red, green, blue ); } bool process_rgb_matrix(uint16_t keycode, keyrecord_t *record) { @@ -752,16 +752,16 @@ void rgb_matrix_init(void) { void rgb_matrix_setup_drivers(void) { // Initialize TWI i2c_init(); - IS31FL3731_init( DRIVER_ADDR_1 ); - IS31FL3731_init( DRIVER_ADDR_2 ); + IS31_init( DRIVER_ADDR_1 ); + IS31_init( DRIVER_ADDR_2 ); for ( int index = 0; index < DRIVER_LED_TOTAL; index++ ) { bool enabled = true; // This only caches it for later - IS31FL3731_set_led_control_register( index, enabled, enabled, enabled ); + IS31_set_led_control_register( index, enabled, enabled, enabled ); } // This actually updates the LED drivers - IS31FL3731_update_led_control_registers( DRIVER_ADDR_1, DRIVER_ADDR_2 ); + IS31_update_led_control_registers( DRIVER_ADDR_1, DRIVER_ADDR_2 ); } // Deals with the messy details of incrementing an integer @@ -811,11 +811,11 @@ void rgb_matrix_test_led( uint8_t index, bool red, bool green, bool blue ) { { if ( i == index ) { - IS31FL3731_set_led_control_register( i, red, green, blue ); + IS31_set_led_control_register( i, red, green, blue ); } else { - IS31FL3731_set_led_control_register( i, false, false, false ); + IS31_set_led_control_register( i, false, false, false ); } } }