Bladeren bron

adds is31fl3731 rgb matrix implementation

Jack Humbert 8 jaren geleden
bovenliggende
commit
16de11fbc5
12 gewijzigde bestanden met toevoegingen van 2136 en 3 verwijderingen
  1. 9 0
      common_features.mk
  2. 232 0
      drivers/avr/TWIlib.c
  3. 82 0
      drivers/avr/TWIlib.h
  4. 357 0
      drivers/avr/is31fl3731.c
  5. 64 0
      drivers/avr/is31fl3731.h
  6. 87 0
      quantum/color.c
  7. 55 0
      quantum/color.h
  8. 22 3
      quantum/quantum.c
  9. 6 0
      quantum/quantum.h
  10. 44 0
      quantum/rgb.h
  11. 1056 0
      quantum/rgb_matrix.c
  12. 122 0
      quantum/rgb_matrix.h

+ 9 - 0
common_features.mk

@@ -107,6 +107,15 @@ ifeq ($(strip $(RGBLIGHT_ENABLE)), yes)
     endif
 endif
 
+ifeq ($(strip $(RGB_MATRIX_ENABLE)), yes)
+    OPT_DEFS += -DRGB_MATRIX_ENABLE
+    SRC += is31fl3731.c
+    SRC += TWIlib.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

+ 232 - 0
drivers/avr/TWIlib.c

@@ -0,0 +1,232 @@
+/*
+ * TWIlib.c
+ *
+ *  Created: 6/01/2014 10:41:33 PM
+ *  Author: Chris Herring
+ *  http://www.chrisherring.net/all/tutorial-interrupt-driven-twi-interface-for-avr-part1/
+ */ 
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include "TWIlib.h"
+#include "util/delay.h"
+
+void TWIInit()
+{
+	TWIInfo.mode = Ready;
+	TWIInfo.errorCode = 0xFF;
+	TWIInfo.repStart = 0;
+	// Set pre-scalers (no pre-scaling)
+	TWSR = 0;
+	// Set bit rate
+	TWBR = ((F_CPU / TWI_FREQ) - 16) / 2;
+	// Enable TWI and interrupt
+	TWCR = (1 << TWIE) | (1 << TWEN);
+}
+
+uint8_t isTWIReady()
+{
+	if ( (TWIInfo.mode == Ready) | (TWIInfo.mode == RepeatedStartSent) )
+	{
+		return 1;
+	}
+	else
+	{
+		return 0;
+	}
+}
+
+uint8_t TWITransmitData(void *const TXdata, uint8_t dataLen, uint8_t repStart)
+{
+	if (dataLen <= TXMAXBUFLEN)
+	{
+		// Wait until ready
+		while (!isTWIReady()) {_delay_us(1);}
+		// Set repeated start mode
+		TWIInfo.repStart = repStart;
+		// Copy data into the transmit buffer
+		uint8_t *data = (uint8_t *)TXdata;
+		for (int i = 0; i < dataLen; i++)
+		{
+			TWITransmitBuffer[i] = data[i];
+		}
+		// Copy transmit info to global variables
+		TXBuffLen = dataLen;
+		TXBuffIndex = 0;
+		
+		// If a repeated start has been sent, then devices are already listening for an address
+		// and another start does not need to be sent. 
+		if (TWIInfo.mode == RepeatedStartSent)
+		{
+			TWIInfo.mode = Initializing;
+			TWDR = TWITransmitBuffer[TXBuffIndex++]; // Load data to transmit buffer
+			TWISendTransmit(); // Send the data
+		}
+		else // Otherwise, just send the normal start signal to begin transmission.
+		{
+			TWIInfo.mode = Initializing;
+			TWISendStart();
+		}
+		
+	}
+	else
+	{
+		return 1; // return an error if data length is longer than buffer
+	}
+	return 0;
+}
+
+uint8_t TWIReadData(uint8_t TWIaddr, uint8_t bytesToRead, uint8_t repStart)
+{
+	// Check if number of bytes to read can fit in the RXbuffer
+	if (bytesToRead < RXMAXBUFLEN)
+	{
+		// Reset buffer index and set RXBuffLen to the number of bytes to read
+		RXBuffIndex = 0;
+		RXBuffLen = bytesToRead;
+		// Create the one value array for the address to be transmitted
+		uint8_t TXdata[1];
+		// Shift the address and AND a 1 into the read write bit (set to write mode)
+		TXdata[0] = (TWIaddr << 1) | 0x01;
+		// Use the TWITransmitData function to initialize the transfer and address the slave
+		TWITransmitData(TXdata, 1, repStart);
+	}
+	else
+	{
+		return 0;
+	}
+	return 1;
+}
+
+ISR (TWI_vect)
+{
+	switch (TWI_STATUS)
+	{
+		// ----\/ ---- MASTER TRANSMITTER OR WRITING ADDRESS ----\/ ----  //
+		case TWI_MT_SLAW_ACK: // SLA+W transmitted and ACK received
+		// Set mode to Master Transmitter
+		TWIInfo.mode = MasterTransmitter;
+		case TWI_START_SENT: // Start condition has been transmitted
+		case TWI_MT_DATA_ACK: // Data byte has been transmitted, ACK received
+			if (TXBuffIndex < TXBuffLen) // If there is more data to send
+			{
+				TWDR = TWITransmitBuffer[TXBuffIndex++]; // Load data to transmit buffer
+				TWIInfo.errorCode = TWI_NO_RELEVANT_INFO;
+				TWISendTransmit(); // Send the data
+			}
+			// This transmission is complete however do not release bus yet
+			else if (TWIInfo.repStart)
+			{
+				TWIInfo.errorCode = 0xFF;
+				TWISendStart();
+			}
+			// All transmissions are complete, exit
+			else
+			{
+				TWIInfo.mode = Ready;
+				TWIInfo.errorCode = 0xFF;
+				TWISendStop();
+			}
+			break;
+		
+		// ----\/ ---- MASTER RECEIVER ----\/ ----  //
+		
+		case TWI_MR_SLAR_ACK: // SLA+R has been transmitted, ACK has been received
+			// Switch to Master Receiver mode
+			TWIInfo.mode = MasterReceiver;
+			// If there is more than one byte to be read, receive data byte and return an ACK
+			if (RXBuffIndex < RXBuffLen-1)
+			{
+				TWIInfo.errorCode = TWI_NO_RELEVANT_INFO;
+				TWISendACK();
+			}
+			// Otherwise when a data byte (the only data byte) is received, return NACK
+			else
+			{
+				TWIInfo.errorCode = TWI_NO_RELEVANT_INFO;
+				TWISendNACK();
+			}
+			break;
+		
+		case TWI_MR_DATA_ACK: // Data has been received, ACK has been transmitted.
+		
+			/// -- HANDLE DATA BYTE --- ///
+			TWIReceiveBuffer[RXBuffIndex++] = TWDR;
+			// If there is more than one byte to be read, receive data byte and return an ACK
+			if (RXBuffIndex < RXBuffLen-1)
+			{
+				TWIInfo.errorCode = TWI_NO_RELEVANT_INFO;
+				TWISendACK();
+			}
+			// Otherwise when a data byte (the only data byte) is received, return NACK
+			else
+			{
+				TWIInfo.errorCode = TWI_NO_RELEVANT_INFO;
+				TWISendNACK();
+			}
+			break;
+		
+		case TWI_MR_DATA_NACK: // Data byte has been received, NACK has been transmitted. End of transmission.
+		
+			/// -- HANDLE DATA BYTE --- ///
+			TWIReceiveBuffer[RXBuffIndex++] = TWDR;	
+			// This transmission is complete however do not release bus yet
+			if (TWIInfo.repStart)
+			{
+				TWIInfo.errorCode = 0xFF;
+				TWISendStart();
+			}
+			// All transmissions are complete, exit
+			else
+			{
+				TWIInfo.mode = Ready;
+				TWIInfo.errorCode = 0xFF;
+				TWISendStop();
+			}
+			break;
+		
+		// ----\/ ---- MT and MR common ----\/ ---- //
+		
+		case TWI_MR_SLAR_NACK: // SLA+R transmitted, NACK received
+		case TWI_MT_SLAW_NACK: // SLA+W transmitted, NACK received
+		case TWI_MT_DATA_NACK: // Data byte has been transmitted, NACK received
+		case TWI_LOST_ARBIT: // Arbitration has been lost
+			// Return error and send stop and set mode to ready
+			if (TWIInfo.repStart)
+			{				
+				TWIInfo.errorCode = TWI_STATUS;
+				TWISendStart();
+			}
+			// All transmissions are complete, exit
+			else
+			{
+				TWIInfo.mode = Ready;
+				TWIInfo.errorCode = TWI_STATUS;
+				TWISendStop();
+			}
+			break;
+		case TWI_REP_START_SENT: // Repeated start has been transmitted
+			// Set the mode but DO NOT clear TWINT as the next data is not yet ready
+			TWIInfo.mode = RepeatedStartSent;
+			break;
+		
+		// ----\/ ---- SLAVE RECEIVER ----\/ ----  //
+		
+		// TODO  IMPLEMENT SLAVE RECEIVER FUNCTIONALITY
+		
+		// ----\/ ---- SLAVE TRANSMITTER ----\/ ----  //
+		
+		// TODO  IMPLEMENT SLAVE TRANSMITTER FUNCTIONALITY
+		
+		// ----\/ ---- MISCELLANEOUS STATES ----\/ ----  //
+		case TWI_NO_RELEVANT_INFO: // It is not really possible to get into this ISR on this condition
+								   // Rather, it is there to be manually set between operations
+			break;
+		case TWI_ILLEGAL_START_STOP: // Illegal START/STOP, abort and return error
+			TWIInfo.errorCode = TWI_ILLEGAL_START_STOP;
+			TWIInfo.mode = Ready;
+			TWISendStop();
+			break;
+	}
+	
+}

+ 82 - 0
drivers/avr/TWIlib.h

@@ -0,0 +1,82 @@
+/*
+ * TWIlib.h
+ *
+ * Created: 6/01/2014 10:38:42 PM
+ *  Author: Chris Herring
+ *  http://www.chrisherring.net/all/tutorial-interrupt-driven-twi-interface-for-avr-part1/
+ */ 
+
+
+#ifndef TWILIB_H_
+#define TWILIB_H_
+// TWI bit rate (was 100000)
+#define TWI_FREQ 400000
+// Get TWI status
+#define TWI_STATUS	(TWSR & 0xF8) 
+// Transmit buffer length
+#define TXMAXBUFLEN 20
+// Receive buffer length
+#define RXMAXBUFLEN 20
+// Global transmit buffer
+uint8_t TWITransmitBuffer[TXMAXBUFLEN];
+// Global receive buffer
+volatile uint8_t TWIReceiveBuffer[RXMAXBUFLEN];
+// Buffer indexes
+volatile int TXBuffIndex; // Index of the transmit buffer. Is volatile, can change at any time.
+int RXBuffIndex; // Current index in the receive buffer
+// Buffer lengths
+int TXBuffLen; // The total length of the transmit buffer
+int RXBuffLen; // The total number of bytes to read (should be less than RXMAXBUFFLEN)
+
+typedef enum {
+	Ready,
+	Initializing,
+	RepeatedStartSent,
+	MasterTransmitter,
+	MasterReceiver,
+	SlaceTransmitter,
+	SlaveReciever
+	} TWIMode;
+
+ typedef struct TWIInfoStruct{
+	TWIMode mode;
+	uint8_t errorCode;
+	uint8_t repStart;	
+	}TWIInfoStruct;
+TWIInfoStruct TWIInfo;
+
+
+// TWI Status Codes
+#define TWI_START_SENT			0x08 // Start sent
+#define TWI_REP_START_SENT		0x10 // Repeated Start sent
+// Master Transmitter Mode
+#define TWI_MT_SLAW_ACK			0x18 // SLA+W sent and ACK received
+#define TWI_MT_SLAW_NACK		0x20 // SLA+W sent and NACK received
+#define TWI_MT_DATA_ACK			0x28 // DATA sent and ACK received
+#define TWI_MT_DATA_NACK		0x30 // DATA sent and NACK received
+// Master Receiver Mode
+#define TWI_MR_SLAR_ACK			0x40 // SLA+R sent, ACK received
+#define TWI_MR_SLAR_NACK		0x48 // SLA+R sent, NACK received
+#define TWI_MR_DATA_ACK			0x50 // Data received, ACK returned
+#define TWI_MR_DATA_NACK		0x58 // Data received, NACK returned
+
+// Miscellaneous States
+#define TWI_LOST_ARBIT			0x38 // Arbitration has been lost
+#define TWI_NO_RELEVANT_INFO	0xF8 // No relevant information available
+#define TWI_ILLEGAL_START_STOP	0x00 // Illegal START or STOP condition has been detected
+#define TWI_SUCCESS				0xFF // Successful transfer, this state is impossible from TWSR as bit2 is 0 and read only
+
+
+#define TWISendStart()		(TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN)|(1<<TWIE)) // Send the START signal, enable interrupts and TWI, clear TWINT flag to resume transfer.
+#define TWISendStop()		(TWCR = (1<<TWINT)|(1<<TWSTO)|(1<<TWEN)|(1<<TWIE)) // Send the STOP signal, enable interrupts and TWI, clear TWINT flag.
+#define TWISendTransmit()	(TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWIE)) // Used to resume a transfer, clear TWINT and ensure that TWI and interrupts are enabled.
+#define TWISendACK()		(TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWIE)|(1<<TWEA)) // FOR MR mode. Resume a transfer, ensure that TWI and interrupts are enabled and respond with an ACK if the device is addressed as a slave or after it receives a byte.
+#define TWISendNACK()		(TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWIE)) // FOR MR mode. Resume a transfer, ensure that TWI and interrupts are enabled but DO NOT respond with an ACK if the device is addressed as a slave or after it receives a byte.
+
+// Function declarations
+uint8_t TWITransmitData(void *const TXdata, uint8_t dataLen, uint8_t repStart);
+void TWIInit(void);
+uint8_t TWIReadData(uint8_t TWIaddr, uint8_t bytesToRead, uint8_t repStart);
+uint8_t isTWIReady(void);
+
+#endif // TWICOMMS_H_ 

+ 357 - 0
drivers/avr/is31fl3731.c

@@ -0,0 +1,357 @@
+/* Copyright 2017 Jason Williams
+ *
+ * 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 "is31fl3731.h"
+#include <avr/interrupt.h>
+#include <avr/io.h>
+#include <util/delay.h>
+#include <string.h>
+#include "TWIlib.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'
+
+// Transfer buffer for TWITransmitData()
+uint8_t g_twi_transfer_buffer[TXMAXBUFLEN];
+
+// 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;
+
+
+typedef struct
+{
+	uint8_t red_register;
+	uint8_t red_bit;
+	uint8_t green_register;
+	uint8_t green_bit;
+	uint8_t blue_register;
+	uint8_t blue_bit;
+} led_control_bitmask;
+
+// 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
+
+const led_control_bitmask g_led_control_bitmask[18] =
+{
+	{ 0x02, 0, 0x04, 0, 0x06, 0 }, // R00,G00,B00
+	{ 0x00, 0, 0x04, 1, 0x06, 1 }, // R01,G01,B01
+	{ 0x00, 1, 0x02, 1, 0x06, 2 }, // R02,G02,B02
+	{ 0x00, 2, 0x02, 2, 0x04, 2 }, // R03,G03,B03
+	{ 0x00, 3, 0x02, 3, 0x04, 3 }, // R04,G04,B04
+	{ 0x00, 4, 0x02, 4, 0x04, 4 }, // R05,G05,B05
+	{ 0x00, 5, 0x02, 5, 0x04, 5 }, // R06,G06,B06
+	{ 0x00, 6, 0x02, 6, 0x04, 6 }, // R07,G07,B07
+	{ 0x00, 7, 0x02, 7, 0x04, 7 }, // R08,G08,B08
+
+	{ 0x10, 0, 0x0E, 0, 0x0C, 0 }, // R09,G09,B09
+	{ 0x10, 1, 0x0E, 1, 0x0C, 1 }, // R10,G10,B10
+	{ 0x10, 2, 0x0E, 2, 0x0C, 2 }, // R11,G11,B11
+	{ 0x10, 3, 0x0E, 3, 0x0C, 3 }, // R12,G12,B12
+	{ 0x10, 4, 0x0E, 4, 0x0C, 4 }, // R13,G13,B13
+	{ 0x10, 5, 0x0E, 5, 0x0C, 5 }, // R14,G14,B14
+	{ 0x10, 6, 0x0E, 6, 0x0A, 5 }, // R15,G15,B15
+	{ 0x10, 7, 0x0C, 6, 0x0A, 6 }, // R16,G16,B16
+	{ 0x0E, 7, 0x0C, 7, 0x0A, 7 }, // R17,G17,B17
+};
+
+const uint8_t g_map_control_index_to_register[2][18][3] PROGMEM = {
+ {
+ 	{0x34, 0x44, 0x54}, // 00
+ 	{0x24, 0x45, 0x55}, // 01
+ 	{0x25, 0x35, 0x56}, // 02
+ 	{0x26, 0x36, 0x46}, // 03
+ 	{0x27, 0x37, 0x47}, // 04
+ 	{0x28, 0x38, 0x48}, // 05
+ 	{0x29, 0x39, 0x49}, // 06
+ 	{0x2a, 0x3a, 0x4a}, // 07
+ 	{0x2b, 0x3b, 0x4b}, // 08
+
+ 	{0xa4, 0x94, 0x84}, // 09
+ 	{0xa5, 0x95, 0x85}, // 10
+ 	{0xa6, 0x96, 0x86}, // 11
+ 	{0xa7, 0x97, 0x87}, // 12
+ 	{0xa8, 0x98, 0x88}, // 13
+ 	{0xa9, 0x99, 0x89}, // 14
+ 	{0xaa, 0x9a, 0x79}, // 15
+ 	{0xab, 0x8a, 0x7a}, // 16
+ 	{0x9b, 0x8b, 0x7b}  // 17
+ }, {
+ 	{0x34 + 8, 0x44 + 8, 0x54 + 8}, // 00
+ 	{0x24 + 8, 0x45 + 8, 0x55 + 8}, // 01
+ 	{0x25 + 8, 0x35 + 8, 0x56 + 8}, // 02
+ 	{0x26 + 8, 0x36 + 8, 0x46 + 8}, // 03
+ 	{0x27 + 8, 0x37 + 8, 0x47 + 8}, // 04
+ 	{0x28 + 8, 0x38 + 8, 0x48 + 8}, // 05
+ 	{0x29 + 8, 0x39 + 8, 0x49 + 8}, // 06
+ 	{0x2a + 8, 0x3a + 8, 0x4a + 8}, // 07
+ 	{0x2b + 8, 0x3b + 8, 0x4b + 8}, // 08
+
+ 	{0xa4 + 8, 0x94 + 8, 0x84 + 8}, // 09
+ 	{0xa5 + 8, 0x95 + 8, 0x85 + 8}, // 10
+ 	{0xa6 + 8, 0x96 + 8, 0x86 + 8}, // 11
+ 	{0xa7 + 8, 0x97 + 8, 0x87 + 8}, // 12
+ 	{0xa8 + 8, 0x98 + 8, 0x88 + 8}, // 13
+ 	{0xa9 + 8, 0x99 + 8, 0x89 + 8}, // 14
+ 	{0xaa + 8, 0x9a + 8, 0x79 + 8}, // 15
+ 	{0xab + 8, 0x8a + 8, 0x7a + 8}, // 16
+ 	{0x9b + 8, 0x8b + 8, 0x7b + 8}  // 17
+}};
+
+void IS31FL3731_write_register( uint8_t addr, uint8_t reg, uint8_t data )
+{
+	g_twi_transfer_buffer[0] = (addr << 1) | 0x00;
+	g_twi_transfer_buffer[1] = reg;
+	g_twi_transfer_buffer[2] = data;
+
+	// Set the error code to have no relevant information
+	TWIInfo.errorCode = TWI_NO_RELEVANT_INFO;
+	// Continuously attempt to transmit data until a successful transmission occurs
+	//while ( TWIInfo.errorCode != 0xFF )
+	//{
+		TWITransmitData( g_twi_transfer_buffer, 3, 0 );
+	//}
+}
+
+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
+
+	// set the I2C address
+	g_twi_transfer_buffer[0] = (addr << 1) | 0x00;
+
+	// 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[1] = 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[2 + j] = pwm_buffer[i + j];
+		}
+
+		// Set the error code to have no relevant information
+		TWIInfo.errorCode = TWI_NO_RELEVANT_INFO;
+		// Continuously attempt to transmit data until a successful transmission occurs
+		while ( TWIInfo.errorCode != 0xFF )
+		{
+			TWITransmitData( g_twi_transfer_buffer, 16 + 2, 0 );
+		}
+	}
+}
+
+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 map_index_to_led( uint8_t index, is31_led *led ) {
+	//led = , sizeof(struct is31_led));
+	// led->driver = addr->driver;
+	// led->matrix = addr->matrix;
+	// led->modifier = addr->modifier;
+	// led->control_index = addr->control_index;
+	// led->matrix_co.raw = addr->matrix_co.raw;
+	// led->driver = (pgm_read_byte(addr) >> 6) && 0b11;
+	// led->matrix = (pgm_read_byte(addr) >> 4) && 0b1;
+	// led->modifier = (pgm_read_byte(addr) >> 3) && 0b1;
+	// led->control_index = pgm_read_byte(addr+1);
+	// led->matrix_co.raw = pgm_read_byte(addr+2);
+}
+
+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];
+		//map_index_to_led(index, &led);
+
+		// Subtract 0x24 to get the second index of g_pwm_buffer
+		g_pwm_buffer[led.driver][ pgm_read_byte(&g_map_control_index_to_register[led.matrix][led.control_index][0]) - 0x24] = red;
+		g_pwm_buffer[led.driver][ pgm_read_byte(&g_map_control_index_to_register[led.matrix][led.control_index][1]) - 0x24] = green;
+		g_pwm_buffer[led.driver][ pgm_read_byte(&g_map_control_index_to_register[led.matrix][led.control_index][2]) - 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];
+	// map_index_to_led(index, &led);
+
+	led_control_bitmask bitmask = g_led_control_bitmask[led.control_index];
+
+	// Matrix A and B registers are interleaved.
+	// Add 1 to Matrix A register to get Matrix B register
+	if ( red )
+	{
+		g_led_control_registers[led.driver][bitmask.red_register+led.matrix] |= (1<<bitmask.red_bit);
+	}
+	else
+	{
+		g_led_control_registers[led.driver][bitmask.red_register+led.matrix] &= ~(1<<bitmask.red_bit);
+	}
+	if ( green )
+	{
+		g_led_control_registers[led.driver][bitmask.green_register+led.matrix] |= (1<<bitmask.green_bit);
+	}
+	else
+	{
+		g_led_control_registers[led.driver][bitmask.green_register+led.matrix] &= ~(1<<bitmask.green_bit);
+	}
+	if ( blue )
+	{
+		g_led_control_registers[led.driver][bitmask.blue_register+led.matrix] |= (1<<bitmask.blue_bit);
+	}
+	else
+	{
+		g_led_control_registers[led.driver][bitmask.blue_register+led.matrix] &= ~(1<<bitmask.blue_bit);
+	}
+
+	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] );
+		}
+	}
+}
+

+ 64 - 0
drivers/avr/is31fl3731.h

@@ -0,0 +1,64 @@
+/* Copyright 2017 Jason Williams
+ *
+ * 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 Point {
+	uint8_t x;
+	uint8_t y;
+} __attribute__((packed)) Point;
+
+typedef struct is31_led {
+	uint8_t driver:2;
+	uint8_t matrix:1;
+	uint8_t modifier:1;
+	uint8_t control_index;
+	union {
+		uint8_t raw;
+		struct {
+			uint8_t row:4; // 16 max
+			uint8_t col:4; // 16 max
+		};
+	} matrix_co;
+	Point point;
+} __attribute__((packed)) is31_led;
+
+extern const is31_led g_is31_leds[DRIVER_LED_TOTAL];
+
+void map_index_to_led( uint8_t index, is31_led *led );
+
+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 );
+
+#endif // IS31FL3731_DRIVER_H

+ 87 - 0
quantum/color.c

@@ -0,0 +1,87 @@
+/* Copyright 2017 Jason Williams
+ *
+ * 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 "color.h"
+#include "led_tables.h"
+#include "progmem.h"
+
+RGB hsv_to_rgb( HSV hsv )
+{
+	RGB rgb;
+	uint8_t region, p, q, t;
+	uint16_t h, s, v, remainder;
+
+	if ( hsv.s == 0 )
+	{
+		rgb.r = hsv.v;
+		rgb.g = hsv.v;
+		rgb.b = hsv.v;
+		return rgb;
+	}
+
+	h = hsv.h;
+	s = hsv.s;
+	v = hsv.v;
+
+	region = h / 43;
+	remainder = (h - (region * 43)) * 6;
+
+	p = (v * (255 - s)) >> 8;
+	q = (v * (255 - ((s * remainder) >> 8))) >> 8;
+	t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8;
+
+	switch ( region )
+	{
+		case 0:
+			rgb.r = v;
+			rgb.g = t;
+			rgb.b = p;
+			break;
+		case 1:
+			rgb.r = q;
+			rgb.g = v;
+			rgb.b = p;
+			break;
+		case 2:
+			rgb.r = p;
+			rgb.g = v;
+			rgb.b = t;
+			break;
+		case 3:
+			rgb.r = p;
+			rgb.g = q;
+			rgb.b = v;
+			break;
+		case 4:
+			rgb.r = t;
+			rgb.g = p;
+			rgb.b = v;
+			break;
+		default:
+			rgb.r = v;
+			rgb.g = p;
+			rgb.b = q;
+			break;
+	}
+
+	rgb.r = pgm_read_byte( &CIE1931_CURVE[rgb.r] );
+	rgb.g = pgm_read_byte( &CIE1931_CURVE[rgb.g] );
+	rgb.b = pgm_read_byte( &CIE1931_CURVE[rgb.b] );
+
+	return rgb;
+}
+

+ 55 - 0
quantum/color.h

@@ -0,0 +1,55 @@
+/* Copyright 2017 Jason Williams
+ *
+ * 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 COLOR_H
+#define COLOR_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+
+#if defined(__GNUC__)
+#define PACKED __attribute__ ((__packed__))
+#else
+#define PACKED
+#endif
+
+#if defined(_MSC_VER)
+#pragma pack( push, 1 )
+#endif
+
+typedef struct PACKED
+{
+	uint8_t r;
+	uint8_t g;
+	uint8_t b;
+} RGB;
+
+typedef struct PACKED
+{
+	uint8_t h;
+	uint8_t s;
+	uint8_t v;
+} HSV;
+
+#if defined(_MSC_VER)
+#pragma pack( pop )
+#endif
+
+RGB hsv_to_rgb( HSV hsv );
+
+#endif // COLOR_H

+ 22 - 3
quantum/quantum.c

@@ -279,7 +279,7 @@ bool process_record_quantum(keyrecord_t *record) {
     }
     return false;
   #endif
-  #ifdef RGBLIGHT_ENABLE
+  #if defined(RGBLIGHT_ENABLE) || defined(RGB_MATRIX_ENABLE)
   case RGB_TOG:
     if (record->event.pressed) {
       rgblight_toggle();
@@ -862,13 +862,14 @@ void backlight_set(uint8_t level)
     }
   #endif
 
-  #ifdef BACKLIGHT_BREATHING
+  #if defined(BACKLIGHT_BREATHING)
     breathing_intensity_default();
   #endif
 }
 
 uint8_t backlight_tick = 0;
 
+__attribute__ ((weak))
 void backlight_task(void) {
   #ifdef NO_BACKLIGHT_CLOCK
   if ((0xFFFF >> ((BACKLIGHT_LEVELS - backlight_config.level) * ((BACKLIGHT_LEVELS + 1) / 2))) & (1 << backlight_tick)) {
@@ -894,6 +895,23 @@ void backlight_task(void) {
 
 #ifdef BACKLIGHT_BREATHING
 
+
+#ifdef NO_BACKLIGHT_CLOCK
+  void breathing_enable(void) {}
+  void breathing_pulse(void) {}
+  void breathing_disable(void) {}
+  void breathing_self_disable(void) {}
+  void breathing_toggle(void) {}
+  bool is_breathing(void) { return false; }
+  void breathing_intensity_default(void) {}
+  void breathing_intensity_set(uint8_t value) {}
+  void breathing_speed_default(void) {}
+  void breathing_speed_set(uint8_t value) {}
+  void breathing_speed_inc(uint8_t value) {}
+  void breathing_speed_dec(uint8_t value) {}
+  void breathing_defaults(void) {}
+#else
+
 #define BREATHING_NO_HALT  0
 #define BREATHING_HALT_OFF 1
 #define BREATHING_HALT_ON  2
@@ -1093,7 +1111,7 @@ ISR(TIMER1_COMPA_vect)
 
 }
 
-
+#endif // no clock
 
 #endif // breathing
 
@@ -1156,6 +1174,7 @@ void send_nibble(uint8_t number) {
 __attribute__((weak))
 uint16_t hex_to_keycode(uint8_t hex)
 {
+  hex = hex & 0xF;
   if (hex == 0x0) {
     return KC_0;
   } else if (hex < 0xA) {

+ 6 - 0
quantum/quantum.h

@@ -27,9 +27,15 @@
 #ifdef BACKLIGHT_ENABLE
     #include "backlight.h"
 #endif
+#if !defined(RGBLIGHT_ENABLE) && !defined(RGB_MATRIX_ENABLE) 
+	#include "rgb.h"
+#endif
 #ifdef RGBLIGHT_ENABLE
   #include "rgblight.h"
 #endif
+#ifdef RGB_MATRIX_ENABLE
+	#include "rgb_matrix.h"
+#endif
 #include "action_layer.h"
 #include "eeconfig.h"
 #include <stddef.h>

+ 44 - 0
quantum/rgb.h

@@ -0,0 +1,44 @@
+/* Copyright 2017 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 RGB_H
+#define RGB_H
+
+__attribute__((weak))
+void rgblight_toggle(void) {};
+
+__attribute__((weak))
+void rgblight_step(void) {};
+
+__attribute__((weak))
+void rgblight_increase_hue(void) {};
+
+__attribute__((weak))
+void rgblight_decrease_hue(void) {};
+
+__attribute__((weak))
+void rgblight_increase_sat(void) {};
+
+__attribute__((weak))
+void rgblight_decrease_sat(void) {};
+
+__attribute__((weak))
+void rgblight_increase_val(void) {};
+
+__attribute__((weak))
+void rgblight_decrease_val(void) {};
+
+#endif

File diff suppressed because it is too large
+ 1056 - 0
quantum/rgb_matrix.c


+ 122 - 0
quantum/rgb_matrix.h

@@ -0,0 +1,122 @@
+/* Copyright 2017 Jason Williams
+ * Copyright 2017 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 RGB_MATRIX_H
+#define RGB_MATRIX_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "color.h"
+#include "is31fl3731.h"
+#include "quantum.h"
+
+typedef struct
+{
+	HSV color;
+	uint8_t index;
+} zeal_indicator;
+
+typedef struct
+{
+	bool enabled:1;                     // |
+	bool use_split_left_shift:1;        // |
+	bool use_split_right_shift:1;       // |
+	bool use_7u_spacebar:1;             // |
+	bool use_iso_enter:1;               // |
+	bool disable_when_usb_suspended:1;  // |
+	bool __pad6:1;                      // |
+	bool __pad7:1;                      // 1 byte
+	uint8_t disable_after_timeout;      // 1 byte
+	uint8_t brightness;                 // 1 byte
+	uint8_t effect;                     // 1 byte
+	HSV color_1;                        // 3 bytes
+	HSV color_2;                        // 3 bytes
+	zeal_indicator caps_lock_indicator;	// 4 bytes
+	zeal_indicator layer_1_indicator;	// 4 bytes
+	zeal_indicator layer_2_indicator;	// 4 bytes
+	zeal_indicator layer_3_indicator;	// 4 bytes
+	uint16_t alphas_mods[5];            // 10 bytes
+} rgb_matrix_config;                // = 36 bytes
+
+#define EEPROM_BACKLIGHT_CONFIG_ADDR ((void*)35)
+// rgb_matrix_config uses 36 bytes
+// 35+36=71
+#define EEPROM_BACKLIGHT_KEY_COLOR_ADDR ((void*)71)
+
+
+void backlight_effect_single_LED_test(void);
+
+void backlight_config_set_alphas_mods( uint16_t *value );
+void backlight_config_load(void);
+void backlight_config_save(void);
+
+void backlight_init_drivers(void);
+
+void backlight_timer_init(void);
+void backlight_timer_enable(void);
+void backlight_timer_disable(void);
+
+void backlight_set_suspend_state(bool state);
+void backlight_set_indicator_state(uint8_t state);
+
+
+void backlight_rgb_task(void);
+
+// 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 backlight_update_pwm_buffers(void);
+
+void backlight_set_key_hit(uint8_t row, uint8_t col);
+void backlight_unset_key_hit(uint8_t row, uint8_t col);
+
+void backlight_effect_increase(void);
+void backlight_effect_decrease(void);
+
+void backlight_brightness_increase(void);
+void backlight_brightness_decrease(void);
+
+void backlight_color_1_hue_increase(void);
+void backlight_color_1_hue_decrease(void);
+void backlight_color_1_sat_increase(void);
+void backlight_color_1_sat_decrease(void);
+void backlight_color_2_hue_increase(void);
+void backlight_color_2_hue_decrease(void);
+void backlight_color_2_sat_increase(void);
+void backlight_color_2_sat_decrease(void);
+
+void *backlight_get_key_color_eeprom_address(uint8_t led);
+void backlight_get_key_color( uint8_t led, HSV *hsv );
+void backlight_set_key_color( uint8_t row, uint8_t column, HSV hsv );
+
+void backlight_test_led( uint8_t index, bool red, bool green, bool blue );
+uint32_t backlight_get_tick(void);
+void backlight_debug_led(bool state);
+
+void rgblight_toggle(void);
+void rgblight_step(void);
+void rgblight_increase_hue(void);
+void rgblight_decrease_hue(void);
+void rgblight_increase_sat(void);
+void rgblight_decrease_sat(void);
+void rgblight_increase_val(void);
+void rgblight_decrease_val(void);
+void rgblight_mode(uint8_t mode);
+uint32_t rgblight_get_mode(void);
+
+#endif