diff options
author | yiancar <yiangosyiangou@cytanet.com.cy> | 2018-08-28 16:03:11 +0300 |
---|---|---|
committer | Jack Humbert <jack.humb@gmail.com> | 2018-08-28 09:03:11 -0400 |
commit | fdd0f915271f79b104aa5d216566bcc3fd134e85 (patch) | |
tree | 42656e9ebd132461b99d7b037563374e575053ae | |
parent | 2410f023598af65a551b42f569a2703a5621bdca (diff) |
ISSI31FL3733 driver (#3679)
* ISSI31FL3733 driver
- Addapted IS31 driver for the above driver
* fix my branch
* ISSI31FL3733 driver
- Inclusion of above ISSI led driver
* IS31fl3733 driver
- Added correct function for control registers
* Finalized support for ISSI31fl3733 led driver
- Finalized and tested driver.
- Modified i2c_master for arm due to declaration mistake.
- Fixed spaces/tabs in quantum.h file.
- Fixed spaces/tabs in common_features.mk file.
- Removed unnecessary includes from rgb_matrix.c file.
- Added local definitions for MIN and MAX macros in rgb_matrix.c file.
- Adjusted chevron effect.
- Added necessary define (RGB_3733_MATRIX_ENABLE) for makefile.
- Added necessary C define (ISSI3733) to aid with inclusion of the correct header file.
- Added documentation for the new driver.
* Driver structure update
- Changed rule includes to be more condensed (RGB_MATRIX_ENABLE = IS31FL3731) and (RGB_MATRIX_ENABLE = IS31FL3733)
- Updated documentation
- Reverted to the use of differently named functions for each driver and selecting the needed ones within rgb_matrix.c
* ISSI Drivers refractoring
- Moved issi drivers in a dedicated folder
- Updated documentation
* I2C library fix
I released the special pins incorrectly before. It is now fixed.
-rw-r--r-- | common_features.mk | 40 | ||||
-rw-r--r-- | docs/feature_rgb_matrix.md | 52 | ||||
-rw-r--r-- | docs/hardware_drivers.md | 6 | ||||
-rw-r--r-- | drivers/arm/i2c_master.c | 13 | ||||
-rw-r--r-- | drivers/arm/i2c_master.h | 4 | ||||
-rw-r--r-- | drivers/issi/is31fl3731.c (renamed from drivers/is31fl3731.c) | 50 | ||||
-rw-r--r-- | drivers/issi/is31fl3731.h (renamed from drivers/is31fl3731.h) | 16 | ||||
-rw-r--r-- | drivers/issi/is31fl3733.c | 253 | ||||
-rw-r--r-- | drivers/issi/is31fl3733.h | 255 | ||||
-rw-r--r-- | quantum/quantum.h | 40 | ||||
-rw-r--r-- | quantum/rgb_matrix.c | 68 | ||||
-rw-r--r-- | quantum/rgb_matrix.h | 8 |
12 files changed, 717 insertions, 88 deletions
diff --git a/common_features.mk b/common_features.mk index b78f04d2a8..e0d4ca297c 100644 --- a/common_features.mk +++ b/common_features.mk @@ -61,8 +61,8 @@ endif ifeq ($(strip $(STENO_ENABLE)), yes) OPT_DEFS += -DSTENO_ENABLE - VIRTSER_ENABLE := yes - SRC += $(QUANTUM_DIR)/process_keycode/process_steno.c + VIRTSER_ENABLE := yes + SRC += $(QUANTUM_DIR)/process_keycode/process_steno.c endif ifeq ($(strip $(VIRTSER_ENABLE)), yes) @@ -75,9 +75,9 @@ ifeq ($(strip $(FAUXCLICKY_ENABLE)), yes) endif ifeq ($(strip $(POINTING_DEVICE_ENABLE)), yes) - OPT_DEFS += -DPOINTING_DEVICE_ENABLE - OPT_DEFS += -DMOUSE_ENABLE - SRC += $(QUANTUM_DIR)/pointing_device.c + OPT_DEFS += -DPOINTING_DEVICE_ENABLE + OPT_DEFS += -DMOUSE_ENABLE + SRC += $(QUANTUM_DIR)/pointing_device.c endif ifeq ($(strip $(UCIS_ENABLE)), yes) @@ -110,12 +110,14 @@ ifeq ($(strip $(RGBLIGHT_ENABLE)), yes) ifeq ($(strip $(RGBLIGHT_CUSTOM_DRIVER)), yes) OPT_DEFS += -DRGBLIGHT_CUSTOM_DRIVER else - SRC += ws2812.c + SRC += ws2812.c endif endif ifeq ($(strip $(RGB_MATRIX_ENABLE)), yes) OPT_DEFS += -DRGB_MATRIX_ENABLE + OPT_DEFS += -DIS31FL3731 + COMMON_VPATH += $(DRIVER_PATH)/issi SRC += is31fl3731.c SRC += i2c_master.c SRC += $(QUANTUM_DIR)/color.c @@ -123,6 +125,28 @@ ifeq ($(strip $(RGB_MATRIX_ENABLE)), yes) CIE1931_CURVE = yes endif +ifeq ($(strip $(RGB_MATRIX_ENABLE)), IS31FL3731) + OPT_DEFS += -DRGB_MATRIX_ENABLE + OPT_DEFS += -DIS31FL3731 + COMMON_VPATH += $(DRIVER_PATH)/issi + SRC += is31fl3731.c + SRC += i2c_master.c + SRC += $(QUANTUM_DIR)/color.c + SRC += $(QUANTUM_DIR)/rgb_matrix.c + CIE1931_CURVE = yes +endif + +ifeq ($(strip $(RGB_MATRIX_ENABLE)), IS31FL3733) + OPT_DEFS += -DRGB_MATRIX_ENABLE + OPT_DEFS += -DIS31FL3733 + COMMON_VPATH += $(DRIVER_PATH)/issi + SRC += is31fl3733.c + SRC += i2c_master.c + SRC += $(QUANTUM_DIR)/color.c + SRC += $(QUANTUM_DIR)/rgb_matrix.c + CIE1931_CURVE = yes +endif + ifeq ($(strip $(TAP_DANCE_ENABLE)), yes) OPT_DEFS += -DTAP_DANCE_ENABLE SRC += $(QUANTUM_DIR)/process_keycode/process_tap_dance.c @@ -169,7 +193,7 @@ ifeq ($(strip $(BACKLIGHT_ENABLE)), yes) ifeq ($(strip $(VISUALIZER_ENABLE)), yes) CIE1931_CURVE = yes endif - ifeq ($(strip $(BACKLIGHT_CUSTOM_DRIVER)), yes) + ifeq ($(strip $(BACKLIGHT_CUSTOM_DRIVER)), yes) OPT_DEFS += -DBACKLIGHT_CUSTOM_DRIVER endif endif @@ -200,7 +224,7 @@ endif ifeq ($(strip $(HD44780_ENABLE)), yes) SRC += drivers/avr/hd44780.c - OPT_DEFS += -DHD44780_ENABLE + OPT_DEFS += -DHD44780_ENABLE endif QUANTUM_SRC:= \ diff --git a/docs/feature_rgb_matrix.md b/docs/feature_rgb_matrix.md index ed33c7ea4e..4f827f8dc9 100644 --- a/docs/feature_rgb_matrix.md +++ b/docs/feature_rgb_matrix.md @@ -1,8 +1,12 @@ # RGB Matrix Lighting +## Driver configuration + +### IS31FL3731 + There is basic support for addressable RGB matrix lighting with the I2C IS31FL3731 RGB controller. To enable it, add this to your `rules.mk`: - RGB_MATRIX_ENABLE = yes + RGB_MATRIX_ENABLE = IS31FL3731 Configure the hardware via your `config.h`: @@ -36,7 +40,51 @@ Define these arrays listing all the LEDs in your `<keyboard>.c`: .... } -Where `Cx_y` is the location of the LED in the matrix defined by [the datasheet](http://www.issi.com/WW/pdf/31FL3731.pdf). The `driver` is the index of the driver you defined in your `config.h` (`0` or `1` right now). +Where `Cx_y` is the location of the LED in the matrix defined by [the datasheet](http://www.issi.com/WW/pdf/31FL3731.pdf) and the header file `drivers/issi/is31fl3731.h`. The `driver` is the index of the driver you defined in your `config.h` (`0` or `1` right now). + +### IS31FL3733 + +There is basic support for addressable RGB matrix lighting with the I2C IS31FL3733 RGB controller. To enable it, add this to your `rules.mk`: + + RGB_MATRIX_ENABLE = IS31FL3733 + +Configure the hardware via your `config.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 DRIVER_ADDR_1 0b1010000 + #define DRIVER_ADDR_2 0b1010000 // this is here for compliancy reasons. + + #define DRIVER_COUNT 1 + #define DRIVER_1_LED_TOTAL 64 + #define DRIVER_LED_TOTAL DRIVER_1_LED_TOTAL + +Currently only a single drivers is supported, but it would be trivial to support all 4 combinations. For now define `DRIVER_ADDR_2` as `DRIVER_ADDR_1` + +Define these arrays listing all the LEDs in your `<keyboard>.c`: + + const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { + /* Refer to IS31 manual for these locations + * driver + * | R location + * | | G location + * | | | B location + * | | | | */ + {0, B_1, A_1, C_1}, + .... + } + +Where `X_Y` is the location of the LED in the matrix defined by [the datasheet](http://www.issi.com/WW/pdf/31FL3733.pdf) and the header file `drivers/issi/is31fl3733.h`. The `driver` is the index of the driver you defined in your `config.h` (Only `0` right now). + +From this point forward the configuration is the same for all the drivers. const rgb_led g_rgb_leds[DRIVER_LED_TOTAL] = { /* {row | col << 4} diff --git a/docs/hardware_drivers.md b/docs/hardware_drivers.md index 336bc908ef..4c1266f224 100644 --- a/docs/hardware_drivers.md +++ b/docs/hardware_drivers.md @@ -26,6 +26,10 @@ You can make use of uGFX within QMK to drive character and graphic LCD's, LED ar Support for WS2811/WS2812{a,b,c} LED's. For more information see the [RGB Light](feature_rgblight.md) page. -## IS31FL3731 (AVR Only) +## IS31FL3731 Support for up to 2 drivers. Each driver impliments 2 charlieplex matrices to individually address LEDs using I2C. This allows up to 144 same color LEDs or 32 RGB LEDs. For more information on how to setup the driver see the [RGB Matrix](feature_rgb_matrix.md) page. + +## IS31FL3733 + +Support for up to a single driver with room for expansion. Each driver can control 192 individual LEDs or 64 RGB LEDs. For more information on how to setup the driver see the [RGB Matrix](feature_rgb_matrix.md) page.
\ No newline at end of file diff --git a/drivers/arm/i2c_master.c b/drivers/arm/i2c_master.c index 2fdd9f65e1..2a7badd351 100644 --- a/drivers/arm/i2c_master.c +++ b/drivers/arm/i2c_master.c @@ -15,12 +15,14 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* This library follows the convention of the AVR i2c_master library. +/* 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. + * 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. */ #include "i2c_master.h" @@ -41,7 +43,7 @@ static const I2CConfig i2cconfig = { void i2c_init(void) { - palSetGroupMode(GPIOB,6,7, PAL_MODE_INPUT); // Try releasing special pins for a short time + palSetGroupMode(GPIOB, GPIOB_PIN6 | GPIOB_PIN7, 0, 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); @@ -82,12 +84,12 @@ uint8_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t l { complete_packet[i+1] = data[i]; } - complete_packet[0] = regaddr + 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) +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); @@ -97,7 +99,6 @@ uint8_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t le // 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 index 9d51245be1..591fa7f77d 100644 --- a/drivers/arm/i2c_master.h +++ b/drivers/arm/i2c_master.h @@ -35,5 +35,5 @@ 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); +uint8_t i2c_readReg(uint8_t devaddr, uint8_t* regaddr, uint8_t* data, uint16_t length, uint16_t timeout); +uint8_t i2c_stop(uint16_t timeout); diff --git a/drivers/is31fl3731.c b/drivers/issi/is31fl3731.c index 0524feb49f..4d0d6b8a5e 100644 --- a/drivers/is31fl3731.c +++ b/drivers/issi/is31fl3731.c @@ -91,7 +91,7 @@ bool g_led_control_registers_update_required = false; // 0x10 - R16,R15,R14,R13,R12,R11,R10,R09 -void IS31_write_register( uint8_t addr, uint8_t reg, uint8_t data ) +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; @@ -106,7 +106,7 @@ void IS31_write_register( uint8_t addr, uint8_t reg, uint8_t data ) #endif } -void IS31_write_pwm_buffer( uint8_t addr, uint8_t *pwm_buffer ) +void IS31FL3731_write_pwm_buffer( uint8_t addr, uint8_t *pwm_buffer ) { // assumes bank is already selected @@ -135,7 +135,7 @@ void IS31_write_pwm_buffer( uint8_t addr, uint8_t *pwm_buffer ) } } -void IS31_init( uint8_t addr ) +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, @@ -143,10 +143,10 @@ void IS31_init( uint8_t addr ) // then disable software shutdown. // select "function register" bank - IS31_write_register( addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG ); + IS31FL3731_write_register( addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG ); // enable software shutdown - IS31_write_register( addr, ISSI_REG_SHUTDOWN, 0x00 ); + IS31FL3731_write_register( addr, ISSI_REG_SHUTDOWN, 0x00 ); // this delay was copied from other drivers, might not be needed #ifdef __AVR__ _delay_ms( 10 ); @@ -155,47 +155,47 @@ void IS31_init( uint8_t addr ) #endif // picture mode - IS31_write_register( addr, ISSI_REG_CONFIG, ISSI_REG_CONFIG_PICTUREMODE ); + IS31FL3731_write_register( addr, ISSI_REG_CONFIG, ISSI_REG_CONFIG_PICTUREMODE ); // display frame 0 - IS31_write_register( addr, ISSI_REG_PICTUREFRAME, 0x00 ); + IS31FL3731_write_register( addr, ISSI_REG_PICTUREFRAME, 0x00 ); // audio sync off - IS31_write_register( addr, ISSI_REG_AUDIOSYNC, 0x00 ); + IS31FL3731_write_register( addr, ISSI_REG_AUDIOSYNC, 0x00 ); // select bank 0 - IS31_write_register( addr, ISSI_COMMANDREGISTER, 0 ); + IS31FL3731_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 ); + 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++ ) { - IS31_write_register( addr, i, 0x00 ); + IS31FL3731_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 ); + IS31FL3731_write_register( addr, i, 0x00 ); } // select "function register" bank - IS31_write_register( addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG ); + IS31FL3731_write_register( addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG ); // disable software shutdown - IS31_write_register( addr, ISSI_REG_SHUTDOWN, 0x01 ); + 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 - IS31_write_register( addr, ISSI_COMMANDREGISTER, 0 ); + IS31FL3731_write_register( addr, ISSI_COMMANDREGISTER, 0 ); } -void IS31_set_color( int index, uint8_t red, uint8_t green, uint8_t blue ) +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]; @@ -208,15 +208,15 @@ 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_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 ); + IS31FL3731_set_color( i, red, green, blue ); } } -void IS31_set_led_control_register( uint8_t index, bool red, bool green, bool blue ) +void IS31FL3731_set_led_control_register( uint8_t index, bool red, bool green, bool blue ) { is31_led led = g_is31_leds[index]; @@ -247,24 +247,24 @@ void IS31_set_led_control_register( uint8_t index, bool red, bool green, bool bl } -void IS31_update_pwm_buffers( uint8_t addr1, uint8_t addr2 ) +void IS31FL3731_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] ); + 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 IS31_update_led_control_registers( uint8_t addr1, uint8_t addr2 ) +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++ ) { - IS31_write_register(addr1, i, g_led_control_registers[0][i] ); - IS31_write_register(addr2, i, g_led_control_registers[1][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.h b/drivers/issi/is31fl3731.h index 9e195c240e..f354a12db2 100644 --- a/drivers/is31fl3731.h +++ b/drivers/issi/is31fl3731.h @@ -31,21 +31,21 @@ typedef struct is31_led { extern const is31_led g_is31_leds[DRIVER_LED_TOTAL]; -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_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_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_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_led_control_register( uint8_t index, bool red, bool green, bool blue ); +void IS31FL3731_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 IS31_update_pwm_buffers( uint8_t addr1, uint8_t addr2 ); -void IS31_update_led_control_registers( uint8_t addr1, uint8_t addr2 ); +void IS31FL3731_update_pwm_buffers( uint8_t addr1, uint8_t addr2 ); +void IS31FL3731_update_led_control_registers( uint8_t addr1, uint8_t addr2 ); #define C1_1 0x24 #define C1_2 0x25 diff --git a/drivers/issi/is31fl3733.c b/drivers/issi/is31fl3733.c new file mode 100644 index 0000000000..4098b54689 --- /dev/null +++ b/drivers/issi/is31fl3733.c @@ -0,0 +1,253 @@ +/* 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 __AVR__ +#include <avr/interrupt.h> +#include <avr/io.h> +#include <util/delay.h> +#else +#include "wait.h" +#endif + +#include "is31fl3733.h" +#include <string.h> +#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: +// 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 = false; + +uint8_t g_led_control_registers[DRIVER_COUNT][24] = { { 0 }, { 0 } }; +bool g_led_control_registers_update_required = false; + +void IS31FL3733_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 IS31FL3733_write_pwm_buffer( uint8_t addr, uint8_t *pwm_buffer ) +{ + // assumes PG1 is already selected + + // 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(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 IS31FL3733_init( uint8_t addr ) +{ + // 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. + + // Unlock the command register. + IS31FL3733_write_register( addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5 ); + + // Select PG0 + IS31FL3733_write_register( addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL ); + // Turn off all LEDs. + for ( int i = 0x00; i <= 0x17; i++ ) + { + IS31FL3733_write_register( addr, i, 0x00 ); + } + + // Unlock the command register. + IS31FL3733_write_register( addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5 ); + + // Select PG1 + IS31FL3733_write_register( 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( addr, i, 0x00 ); + } + + // Unlock the command register. + IS31FL3733_write_register( addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5 ); + + // Select PG3 + IS31FL3733_write_register( addr, ISSI_COMMANDREGISTER, ISSI_PAGE_FUNCTION ); + // Set global current to maximum. + IS31FL3733_write_register( addr, ISSI_REG_GLOBALCURRENT, 0xFF ); + // Disable software shutdown. + IS31FL3733_write_register( addr, ISSI_REG_CONFIGURATION, 0x01 ); + + // Wait 10ms to ensure the device has woken up. + #ifdef __AVR__ + _delay_ms( 10 ); + #else + wait_ms(10); + #endif +} + +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 = 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 = true; + +} + +void IS31FL3733_update_pwm_buffers( uint8_t addr1, uint8_t addr2 ) +{ + if ( g_pwm_buffer_update_required ) + { + // Firstly we need to unlock the command register and select PG1 + IS31FL3733_write_register( addr1, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5 ); + IS31FL3733_write_register( addr1, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM ); + + IS31FL3733_write_pwm_buffer( addr1, g_pwm_buffer[0] ); + //IS31FL3733_write_pwm_buffer( addr2, g_pwm_buffer[1] ); + } + g_pwm_buffer_update_required = false; +} + +void IS31FL3733_update_led_control_registers( uint8_t addr1, uint8_t addr2 ) +{ + if ( g_led_control_registers_update_required ) + { + // Firstly we need to unlock the command register and select PG0 + IS31FL3733_write_register( addr1, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5 ); + IS31FL3733_write_register( addr1, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL ); + for ( int i=0; i<24; i++ ) + { + IS31FL3733_write_register(addr1, i, g_led_control_registers[0][i] ); + //IS31FL3733_write_register(addr2, i, g_led_control_registers[1][i] ); + } + } +} + diff --git a/drivers/issi/is31fl3733.h b/drivers/issi/is31fl3733.h new file mode 100644 index 0000000000..3d23b188aa --- /dev/null +++ b/drivers/issi/is31fl3733.h @@ -0,0 +1,255 @@ +/* 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/>. + */ + + +#ifndef IS31FL3733_DRIVER_H +#define IS31FL3733_DRIVER_H + +#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 addr ); +void IS31FL3733_write_register( uint8_t addr, uint8_t reg, uint8_t data ); +void IS31FL3733_write_pwm_buffer( 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 addr1, uint8_t addr2 ); +void IS31FL3733_update_led_control_registers( uint8_t addr1, uint8_t addr2 ); + +#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 + +#endif // IS31FL3733_DRIVER_H diff --git a/quantum/quantum.h b/quantum/quantum.h index 1db9846f04..b4e4de1743 100644 --- a/quantum/quantum.h +++ b/ |