diff options
Diffstat (limited to 'drivers/issi')
-rw-r--r-- | drivers/issi/is31fl3218.c | 102 | ||||
-rw-r--r-- | drivers/issi/is31fl3218.h | 24 | ||||
-rw-r--r-- | drivers/issi/is31fl3731.c | 270 | ||||
-rw-r--r-- | drivers/issi/is31fl3731.h | 214 | ||||
-rw-r--r-- | drivers/issi/is31fl3733.c | 252 | ||||
-rw-r--r-- | drivers/issi/is31fl3733.h | 255 | ||||
-rw-r--r-- | drivers/issi/is31fl3736.c | 306 | ||||
-rw-r--r-- | drivers/issi/is31fl3736.h | 172 |
8 files changed, 1595 insertions, 0 deletions
diff --git a/drivers/issi/is31fl3218.c b/drivers/issi/is31fl3218.c new file mode 100644 index 0000000000..db44f72564 --- /dev/null +++ b/drivers/issi/is31fl3218.c @@ -0,0 +1,102 @@ +/* Copyright 2018 Jason Williams (Wilba) + * + * 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 "is31fl3218.h" +#include "i2c_master.h" + +// This is the full 8-bit address +#define ISSI_ADDRESS 0b10101000 + +// These are the register addresses +#define ISSI_REG_SHUTDOWN 0x00 +#define ISSI_REG_PWM 0x01 +#define ISSI_REG_CONTROL 0x13 +#define ISSI_REG_UPDATE 0x16 +#define ISSI_REG_RESET 0x17 + +// Default timeout if no I2C response +#define ISSI_TIMEOUT 100 + +// Reusable buffer for transfers +uint8_t g_twi_transfer_buffer[20]; + +// IS31FL3218 has 18 PWM outputs and a fixed I2C address, so no chaining. +// If used as RGB LED driver, LEDs are assigned RGB,RGB,RGB,RGB,RGB,RGB +uint8_t g_pwm_buffer[18]; +bool g_pwm_buffer_update_required = false; + +void IS31FL3218_write_register( uint8_t reg, uint8_t data ) +{ + g_twi_transfer_buffer[0] = reg; + g_twi_transfer_buffer[1] = data; + i2c_transmit( ISSI_ADDRESS, g_twi_transfer_buffer, 2, ISSI_TIMEOUT); +} + +void IS31FL3218_write_pwm_buffer( uint8_t *pwm_buffer ) +{ + g_twi_transfer_buffer[0] = ISSI_REG_PWM; + for ( int i=0; i<18; i++ ) { + g_twi_transfer_buffer[1+i] = pwm_buffer[i]; + } + + i2c_transmit( ISSI_ADDRESS, g_twi_transfer_buffer, 19, ISSI_TIMEOUT); +} + +void IS31FL3218_init(void) +{ + // In case we ever want to reinitialize (?) + IS31FL3218_write_register( ISSI_REG_RESET, 0x00 ); + + // Turn off software shutdown + IS31FL3218_write_register( ISSI_REG_SHUTDOWN, 0x01 ); + + // Set all PWM values to zero + for ( uint8_t i = 0; i < 18; i++ ) { + IS31FL3218_write_register( ISSI_REG_PWM+i, 0x00 ); + } + + // Enable all channels + for ( uint8_t i = 0; i < 3; i++ ) { + IS31FL3218_write_register( ISSI_REG_CONTROL+i, 0b00111111 ); + } + + // Load PWM registers and LED Control register data + IS31FL3218_write_register( ISSI_REG_UPDATE, 0x01 ); +} + +void IS31FL3218_set_color( int index, uint8_t red, uint8_t green, uint8_t blue ) +{ + g_pwm_buffer[index * 3 + 0] = red; + g_pwm_buffer[index * 3 + 1] = green; + g_pwm_buffer[index * 3 + 2] = blue; + g_pwm_buffer_update_required = true; +} + +void IS31FL3218_set_color_all( uint8_t red, uint8_t green, uint8_t blue ) +{ + for ( int i = 0; i < 6; i++ ) { + IS31FL3218_set_color( i, red, green, blue ); + } +} + +void IS31FL3218_update_pwm_buffers(void) +{ + if ( g_pwm_buffer_update_required ) { + IS31FL3218_write_pwm_buffer( g_pwm_buffer ); + // Load PWM registers and LED Control register data + IS31FL3218_write_register( ISSI_REG_UPDATE, 0x01 ); + } + g_pwm_buffer_update_required = false; +} diff --git a/drivers/issi/is31fl3218.h b/drivers/issi/is31fl3218.h new file mode 100644 index 0000000000..2d24e51463 --- /dev/null +++ b/drivers/issi/is31fl3218.h @@ -0,0 +1,24 @@ +/* Copyright 2018 Jason Williams (Wilba) + * + * 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> + +void IS31FL3218_init(void); +void IS31FL3218_set_color( int index, uint8_t red, uint8_t green, uint8_t blue ); +void IS31FL3218_set_color_all( uint8_t red, uint8_t green, uint8_t blue ); +void IS31FL3218_update_pwm_buffers(void); diff --git a/drivers/issi/is31fl3731.c b/drivers/issi/is31fl3731.c new file mode 100644 index 0000000000..c9155f5a37 --- /dev/null +++ b/drivers/issi/is31fl3731.c @@ -0,0 +1,270 @@ +/* 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 <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 "is31fl3731.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: +// 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 + #ifdef __AVR__ + _delay_ms( 10 ); + #else + wait_ms(10); + #endif + + // 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/issi/is31fl3731.h b/drivers/issi/is31fl3731.h new file mode 100644 index 0000000000..f354a12db2 --- /dev/null +++ b/drivers/issi/is31fl3731.h @@ -0,0 +1,214 @@ +/* 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef IS31FL3731_DRIVER_H +#define IS31FL3731_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 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 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 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 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 +#define C1_3 0x26 +#define C1_4 0x27 +#define C1_5 0x28 +#define C1_6 0x29 +#define C1_7 0x2A +#define C1_8 0x2B + +#define C1_9 0x2C +#define C1_10 0x2D +#define C1_11 0x2E +#define C1_12 0x2F +#define C1_13 0x30 +#define C1_14 0x31 +#define C1_15 0x32 +#define C1_16 0x33 + +#define C2_1 0x34 +#define C2_2 0x35 +#define C2_3 0x36 +#define C2_4 0x37 +#define C2_5 0x38 +#define C2_6 0x39 +#define C2_7 0x3A +#define C2_8 0x3B + +#define C2_9 0x3C +#define C2_10 0x3D +#define C2_11 0x3E +#define C2_12 0x3F +#define C2_13 0x40 +#define C2_14 0x41 +#define C2_15 0x42 +#define C2_16 0x43 + +#define C3_1 0x44 +#define C3_2 0x45 +#define C3_3 0x46 +#define C3_4 0x47 +#define C3_5 0x48 +#define C3_6 0x49 +#define C3_7 0x4A +#define C3_8 0x4B + +#define C3_9 0x4C +#define C3_10 0x4D +#define C3_11 0x4E +#define C3_12 0x4F +#define C3_13 0x50 +#define C3_14 0x51 +#define C3_15 0x52 +#define C3_16 0x53 + +#define C4_1 0x54 +#define C4_2 0x55 +#define C4_3 0x56 +#define C4_4 0x57 +#define C4_5 0x58 +#define C4_6 0x59 +#define C4_7 0x5A +#define C4_8 0x5B + +#define C4_9 0x5C +#define C4_10 0x5D +#define C4_11 0x5E +#define C4_12 0x5F +#define C4_13 0x60 +#define C4_14 0x61 +#define C4_15 0x62 +#define C4_16 0x63 + +#define C5_1 0x64 +#define C5_2 0x65 +#define C5_3 0x66 +#define C5_4 0x67 +#define C5_5 0x68 +#define C5_6 0x69 +#define C5_7 0x6A +#define C5_8 0x6B + +#define C5_9 0x6C +#define C5_10 0x6D +#define C5_11 0x6E +#define C5_12 0x6F +#define C5_13 0x70 +#define C5_14 0x71 +#define C5_15 0x72 +#define C5_16 0x73 + +#define C6_1 0x74 +#define C6_2 0x75 +#define C6_3 0x76 +#define C6_4 0x77 +#define C6_5 0x78 +#define C6_6 0x79 +#define C6_7 0x7A +#define C6_8 0x7B + +#define C6_9 0x7C +#define C6_10 0x7D +#define C6_11 0x7E +#define C6_12 0x7F +#define C6_13 0x80 +#define C6_14 0x81 +#define C6_15 0x82 +#define C6_16 0x83 + +#define C7_1 0x84 +#define C7_2 0x85 +#define C7_3 0x86 +#define C7_4 0x87 +#define C7_5 0x88 +#define C7_6 0x89 +#define C7_7 0x8A +#define C7_8 0x8B + +#define C7_9 0x8C +#define C7_10 0x8D +#define C7_11 0x8E +#define C7_12 0x8F +#define C7_13 0x90 +#define C7_14 0x91 +#define C7_15 0x92 +#define C7_16 0x93 + +#define C8_1 0x94 +#define C8_2 0x95 +#define C8_3 0x96 +#define C8_4 0x97 +#define C8_5 0x98 +#define C8_6 0x99 +#define C8_7 0x9A +#define C8_8 0x9B + +#define C8_9 0x9C +#define C8_10 0x9D +#define C8_11 0x9E +#define C8_12 0x9F +#define C8_13 0xA0 +#define C8_14 0xA1 +#define C8_15 0xA2 +#define C8_16 0xA3 + +#define C9_1 0xA4 +#define C9_2 0xA5 +#define C9_3 0xA6 +#define C9_4 0xA7 +#define C9_5 0xA8 +#define C9_6 0xA9 +#define C9_7 0xAA +#define C9_8 0xAB + +#define C9_9 0xAC +#define C9_10 0xAD +#define C9_11 0xAE +#define C9_12 0xAF +#define C9_13 0xB0 +#define C9_14 0xB1 +#define C9_15 0xB2 +#define C9_16 0xB3 + + + +#endif // IS31FL3731_DRIVER_H diff --git a/drivers/issi/is31fl3733.c b/drivers/issi/is31fl3733.c new file mode 100644 index 0000000000..c18ed7ca30 --- /dev/null +++ b/drivers/issi/is31fl3733.c @@ -0,0 +1,252 @@ +/* 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/drivers/issi/is31fl3736.c b/drivers/issi/is31fl3736.c new file mode 100644 index 0000000000..c5d4310971 --- /dev/null +++ b/drivers/issi/is31fl3736.c @@ -0,0 +1,306 @@ +/* Copyright 2018 Jason Williams (Wilba) + * + * 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 "is31fl3736.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 IS31FL3736 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 IS31FL3736_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 IS31FL3736_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 IS31FL3736_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 IS31FL3736_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. + IS31FL3736_write_register( addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5 ); + + // Select PG0 + IS31FL3736_write_register( addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL ); + // Turn off all LEDs. + for ( int i = 0x00; i <= 0x17; i++ ) + { + IS31FL3736_write_register( addr, i, 0x00 ); + } + + // Unlock the command register. + IS31FL3736_write_register( addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5 ); + + // Select PG1 + IS31FL3736_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++ ) + { + IS31FL3736_write_register( addr, i, 0x00 ); + } + + // Unlock the command register. + IS31FL3736_write_register( addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5 ); + + // Select PG3 + IS31FL3736_write_register( addr, ISSI_COMMANDREGISTER, ISSI_PAGE_FUNCTION ); + // Set global current to maximum. + IS31FL3736_write_register( addr, ISSI_REG_GLOBALCURRENT, 0xFF ); + // Disable software shutdown. + IS31FL3736_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 IS31FL3736_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 IS31FL3736_set_color_all( uint8_t red, uint8_t green, uint8_t blue ) +{ + for ( int i = 0; i < DRIVER_LED_TOTAL; i++ ) + { + IS31FL3736_set_color( i, red, green, blue ); + } +} + +void IS31FL3736_set_led_control_register( uint8_t index, bool red, bool green, bool blue ) +{ + is31_led led = g_is31_leds[index]; + + // IS31FL3733 + // The PWM register for a matrix position (0x00 to 0xBF) can be + // divided by 8 to get the LED control register (0x00 to 0x17), + // then mod 8 to get the bit position within that register. + + // IS31FL3736 + // The PWM register for a matrix position (0x00 to 0xBF) is interleaved, so: + // A1=0x00 A2=0x02 A3=0x04 A4=0x06 A5=0x08 A6=0x0A A7=0x0C A8=0x0E + // B1=0x10 B2=0x12 B3=0x14 + // But also, the LED control registers (0x00 to 0x17) are also interleaved, so: + // A1-A4=0x00 A5-A8=0x01 + // So, the same math applies. + + 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 IS31FL3736_mono_set_brightness( int index, uint8_t value ) +{ + if ( index >= 0 && index < 96 ) { + // Index in range 0..95 -> A1..A8, B1..B8, etc. + // Map index 0..95 to registers 0x00..0xBE (interleaved) + uint8_t pwm_register = index * 2; + g_pwm_buffer[0][pwm_register] = value; + g_pwm_buffer_update_required = true; + } +} + +void IS31FL3736_mono_set_brightness_all( uint8_t value ) +{ + for ( int i = 0; i < 96; i++ ) + { + IS31FL3736_mono_set_brightness( i, value ); + } +} + +void IS31FL3736_mono_set_led_control_register( uint8_t index, bool enabled ) +{ + // Index in range 0..95 -> A1..A8, B1..B8, etc. + + // Map index 0..95 to registers 0x00..0xBE (interleaved) + uint8_t pwm_register = index * 2; + // Map register 0x00..0xBE (interleaved) into control register and bit + uint8_t control_register = pwm_register / 8; + uint8_t bit = pwm_register % 8; + + if ( enabled ) { + g_led_control_registers[0][control_register] |= (1 << bit); + } else { + g_led_control_registers[0][control_register] &= ~(1 << bit); + } + + g_led_control_registers_update_required = true; +} + +void IS31FL3736_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 + IS31FL3736_write_register( addr1, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5 ); + IS31FL3736_write_register( addr1, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM ); + + IS31FL3736_write_pwm_buffer( addr1, g_pwm_buffer[0] ); + //IS31FL3736_write_pwm_buffer( addr2, g_pwm_buffer[1] ); + } + g_pwm_buffer_update_required = false; +} + +void IS31FL3736_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 + IS31FL3736_write_register( addr1, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5 ); + IS31FL3736_write_register( addr1, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL ); + for ( int i=0; i<24; i++ ) + { + IS31FL3736_write_register(addr1, i, g_led_control_registers[0][i] ); + //IS31FL3736_write_register(addr2, i, g_led_control_registers[1][i] ); + } + } +} + diff --git a/drivers/issi/is31fl3736.h b/drivers/issi/is31fl3736.h new file mode 100644 index 0000000000..cff50fd0d8 --- /dev/null +++ b/drivers/issi/is31fl3736.h @@ -0,0 +1,172 @@ +/* Copyright 2018 Jason Williams (Wilba) + * + * 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> + + +// Simple interface option. +// If these aren't defined, just define them to make it compile + + +#ifndef DRIVER_COUNT +#define DRIVER_COUNT 2 +#endif + +#ifndef DRIVER_LED_TOTAL +#define DRIVER_LED_TOTAL 96 +#endif + + +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 IS31FL3736_init( uint8_t addr ); +void IS31FL3736_write_register( uint8_t addr, uint8_t reg, uint8_t data ); +void IS31FL3736_write_pwm_buffer( uint8_t addr, uint8_t *pwm_buffer ); + +void IS31FL3736_set_color( int index, uint8_t red, uint8_t green, uint8_t blue ); +void IS31FL3736_set_color_all( uint8_t red, uint8_t green, uint8_t blue ); + +void IS31FL3736_set_led_control_register( uint8_t index, bool red, bool green, bool blue ); + +void IS31FL3736_mono_set_brightness( int index, uint8_t value ); +void IS31FL3736_mono_set_brightness_all( uint8_t value ); +void IS31FL3736_mono_set_led_control_register( uint8_t index, bool enabled ); + +// 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 IS31FL3736_update_pwm_buffers( uint8_t addr1, uint8_t addr2 ); +void IS31FL3736_update_led_control_registers( uint8_t addr1, uint8_t addr2 ); + +#define A_1 0x00 +#define A_2 0x02 +#define A_3 0x04 +#define A_4 0x06 +#define A_5 0x08 +#define A_6 0x0A +#define A_7 0x0C +#define A_8 0x0E + +#define B_1 0x10 +#define B_2 0x12 +#define B_3 0x14 +#define B_4 0x16 +#define B_5 0x18 +#define B_6 0x1A +#define B_7 0x1C +#define B_8 0x1E + +#define C_1 0x20 +#define C_2 0x22 +#define C_3 0x24 +#define C_4 0x26 +#define C_5 0x28 +#define C_6 0x2A +#define C_7 0x2C +#define C_8 0x2E + +#define D_1 0x30 +#define D_2 0x32 +#define D_3 0x34 +#define D_4 0x36 +#define D_5 0x38 +#define D_6 0x3A +#define D_7 0x3C +#define D_8 0x3E + +#define E_1 0x40 +#define E_2 0x42 +#define E_3 0x44 +#define E_4 0x46 +#define E_5 0x48 +#define E_6 0x4A +#define E_7 0x4C +#define E_8 0x4E + +#define F_1 0x50 +#define F_2 0x52 +#define F_3 0x54 +#define F_4 0x56 +#define F_5 0x58 +#define F_6 0x5A +#define F_7 0x5C +#define F_8 0x5E + +#define G_1 0x60 +#define G_2 0x62 +#define G_3 0x64 +#define G_4 0x66 +#define G_5 0x68 +#define G_6 0x6A +#define G_7 0x6C +#define G_8 0x6E + +#define H_1 0x70 +#define H_2 0x72 +#define H_3 0x74 +#define H_4 0x76 +#define H_5 0x78 +#define H_6 0x7A +#define H_7 0x7C +#define H_8 0x7E + +#define I_1 0x80 +#define I_2 0x82 +#define I_3 0x84 +#define I_4 0x86 +#define I_5 0x88 +#define I_6 0x8A +#define I_7 0x8C +#define I_8 0x8E + +#define J_1 0x90 +#define J_2 0x92 +#define J_3 0x94 +#define J_4 0x96 +#define J_5 0x98 +#define J_6 0x9A +#define J_7 0x9C +#define J_8 0x9E + +#define K_1 0xA0 +#define K_2 0xA2 +#define K_3 0xA4 +#define K_4 0xA6 +#define K_5 0xA8 +#define K_6 0xAA +#define K_7 0xAC +#define K_8 0xAE + +#define L_1 0xB0 +#define L_2 0xB2 +#define L_3 0xB4 +#define L_4 0xB6 +#define L_5 0xB8 +#define L_6 0xBA +#define L_7 0xBC +#define L_8 0xBE + |