summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorAlex Ong <the.onga@gmail.com>2019-01-04 19:39:14 +1100
committerAlex Ong <the.onga@gmail.com>2019-01-04 19:39:14 +1100
commit47c91fc7f75ae0a477e55b687aa0fc30da0a283c (patch)
tree65ad39452748ff2e6d4a83ce54ede6ca22c9ada9 /drivers
parentac9b88e8ccbbf38762871504cd827ff0d941c426 (diff)
parent563ce3f225d981ce460c12ca5130dfe47af41df0 (diff)
Merge branch 'master' of https://github.com/qmk/qmk_firmware
Diffstat (limited to 'drivers')
-rw-r--r--drivers/arm/i2c_master.c109
-rw-r--r--drivers/arm/i2c_master.h40
-rwxr-xr-xdrivers/avr/i2c_master.c2
-rw-r--r--drivers/avr/is31fl3731.c262
-rw-r--r--drivers/avr/ws2812.h3
-rw-r--r--drivers/haptic/DRV2605L.c129
-rw-r--r--drivers/haptic/DRV2605L.h394
-rw-r--r--drivers/issi/is31fl3218.c102
-rw-r--r--drivers/issi/is31fl3218.h24
-rw-r--r--drivers/issi/is31fl3731.c270
-rw-r--r--drivers/issi/is31fl3731.h (renamed from drivers/avr/is31fl3731.h)2
-rw-r--r--drivers/issi/is31fl3733.c252
-rw-r--r--drivers/issi/is31fl3733.h255
-rw-r--r--drivers/issi/is31fl3736.c306
-rw-r--r--drivers/issi/is31fl3736.h172
-rw-r--r--drivers/qwiic/micro_oled.c691
-rw-r--r--drivers/qwiic/micro_oled.h134
-rw-r--r--drivers/qwiic/qwiic.c31
-rw-r--r--drivers/qwiic/qwiic.h28
-rw-r--r--drivers/qwiic/qwiic.mk18
-rw-r--r--drivers/qwiic/util/font5x7.h288
-rw-r--r--drivers/qwiic/util/font8x16.h127
22 files changed, 3373 insertions, 266 deletions
diff --git a/drivers/arm/i2c_master.c b/drivers/arm/i2c_master.c
new file mode 100644
index 0000000000..385bd97cb8
--- /dev/null
+++ b/drivers/arm/i2c_master.c
@@ -0,0 +1,109 @@
+/* Copyright 2018 Jack Humbert
+ * Copyright 2018 Yiancar
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* This library is only valid for STM32 processors.
+ * This library follows the convention of the AVR i2c_master library.
+ * As a result addresses are expected to be already shifted (addr << 1).
+ * I2CD1 is the default driver which corresponds to pins B6 and B7. This
+ * can be changed.
+ * Please ensure that HAL_USE_I2C is TRUE in the halconf.h file and that
+ * STM32_I2C_USE_I2C1 is TRUE in the mcuconf.h file. Pins B6 and B7 are used
+ * but using any other I2C pins should be trivial.
+ */
+
+#include "i2c_master.h"
+#include "quantum.h"
+#include <string.h>
+#include <hal.h>
+
+static uint8_t i2c_address;
+
+// This configures the I2C clock to 400khz assuming a 72Mhz clock
+// For more info : https://www.st.com/en/embedded-software/stsw-stm32126.html
+static const I2CConfig i2cconfig = {
+ STM32_TIMINGR_PRESC(15U) |
+ STM32_TIMINGR_SCLDEL(4U) | STM32_TIMINGR_SDADEL(2U) |
+ STM32_TIMINGR_SCLH(15U) | STM32_TIMINGR_SCLL(21U),
+ 0,
+ 0
+};
+
+__attribute__ ((weak))
+void i2c_init(void)
+{
+ // Try releasing special pins for a short time
+ palSetPadMode(GPIOB, 6, PAL_MODE_INPUT);
+ palSetPadMode(GPIOB, 7, PAL_MODE_INPUT);
+
+ chThdSleepMilliseconds(10);
+
+ palSetPadMode(GPIOB, 6, PAL_MODE_ALTERNATE(4) | PAL_STM32_OTYPE_OPENDRAIN);
+ palSetPadMode(GPIOB, 7, PAL_MODE_ALTERNATE(4) | PAL_STM32_OTYPE_OPENDRAIN);
+
+ //i2cInit(); //This is invoked by halInit() so no need to redo it.
+}
+
+// This is usually not needed
+uint8_t i2c_start(uint8_t address)
+{
+ i2c_address = address;
+ i2cStart(&I2C_DRIVER, &i2cconfig);
+ return 0;
+}
+
+uint8_t i2c_transmit(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout)
+{
+ i2c_address = address;
+ i2cStart(&I2C_DRIVER, &i2cconfig);
+ return i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), data, length, 0, 0, MS2ST(timeout));
+}
+
+uint8_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout)
+{
+ i2c_address = address;
+ i2cStart(&I2C_DRIVER, &i2cconfig);
+ return i2cMasterReceiveTimeout(&I2C_DRIVER, (i2c_address >> 1), data, length, MS2ST(timeout));
+}
+
+uint8_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)
+{
+ i2c_address = devaddr;
+ i2cStart(&I2C_DRIVER, &i2cconfig);
+
+ uint8_t complete_packet[length + 1];
+ for(uint8_t i = 0; i < length; i++)
+ {
+ complete_packet[i+1] = data[i];
+ }
+ complete_packet[0] = regaddr;
+
+ return i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), complete_packet, length + 1, 0, 0, MS2ST(timeout));
+}
+
+uint8_t i2c_readReg(uint8_t devaddr, uint8_t* regaddr, uint8_t* data, uint16_t length, uint16_t timeout)
+{
+ i2c_address = devaddr;
+ i2cStart(&I2C_DRIVER, &i2cconfig);
+ return i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), regaddr, 1, data, length, MS2ST(timeout));
+}
+
+// This is usually not needed. It releases the driver to allow pins to become GPIO again.
+uint8_t i2c_stop(uint16_t timeout)
+{
+ i2cStop(&I2C_DRIVER);
+ return 0;
+}
diff --git a/drivers/arm/i2c_master.h b/drivers/arm/i2c_master.h
new file mode 100644
index 0000000000..392760328f
--- /dev/null
+++ b/drivers/arm/i2c_master.h
@@ -0,0 +1,40 @@
+/* Copyright 2018 Jack Humbert
+ * Copyright 2018 Yiancar
+ *
+ * This program is free sofare: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Sofare Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* This library follows the convention of the AVR i2c_master library.
+ * As a result addresses are expected to be already shifted (addr << 1).
+ * I2CD1 is the default driver which corresponds to pins B6 and B7. This
+ * can be changed.
+ * Please ensure that HAL_USE_I2C is TRUE in the halconf.h file and that
+ * STM32_I2C_USE_I2C1 is TRUE in the mcuconf.h file.
+ */
+
+#include "ch.h"
+#include <hal.h>
+
+#ifndef I2C_DRIVER
+ #define I2C_DRIVER I2CD1
+#endif
+
+void i2c_init(void);
+uint8_t i2c_start(uint8_t address);
+uint8_t i2c_transmit(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout);
+uint8_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout);
+uint8_t i2c_transmit_receive(uint8_t address, uint8_t * tx_body, uint16_t tx_length, uint8_t * rx_body, uint16_t rx_length);
+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);
+uint8_t i2c_stop(uint16_t timeout);
diff --git a/drivers/avr/i2c_master.c b/drivers/avr/i2c_master.c
index 47c6f8e6c6..a04e6570d7 100755
--- a/drivers/avr/i2c_master.c
+++ b/drivers/avr/i2c_master.c
@@ -8,7 +8,9 @@
#include "i2c_master.h"
#include "timer.h"
+#ifndef F_SCL
#define F_SCL 400000UL // SCL frequency
+#endif
#define Prescaler 1
#define TWBR_val ((((F_CPU / F_SCL) / Prescaler) - 16 ) / 2)
diff --git a/drivers/avr/is31fl3731.c b/drivers/avr/is31fl3731.c
deleted file mode 100644
index 70813464b2..0000000000
--- a/drivers/avr/is31fl3731.c
+++ /dev/null
@@ -1,262 +0,0 @@
-/* Copyright 2017 Jason Williams
- * Copyright 2018 Jack Humbert
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "is31fl3731.h"
-#include <avr/interrupt.h>
-#include <avr/io.h>
-#include <util/delay.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
- _delay_ms( 10 );
-
- // picture mode
- IS31FL3731_write_register( addr, ISSI_REG_CONFIG, ISSI_REG_CONFIG_PICTUREMODE );
- // display frame 0
- IS31FL3731_write_register( addr, ISSI_REG_PICTUREFRAME, 0x00 );
- // audio sync off
- IS31FL3731_write_register( addr, ISSI_REG_AUDIOSYNC, 0x00 );
-
- // select bank 0
- IS31FL3731_write_register( addr, ISSI_COMMANDREGISTER, 0 );
-
- // turn off all LEDs in the LED control register
- for ( int i = 0x00; i <= 0x11; i++ )
- {
- IS31FL3731_write_register( addr, i, 0x00 );
- }
-
- // turn off all LEDs in the blink control register (not really needed)
- for ( int i = 0x12; i <= 0x23; i++ )
- {
- IS31FL3731_write_register( addr, i, 0x00 );
- }
-
- // set PWM on all LEDs to 0
- for ( int i = 0x24; i <= 0xB3; i++ )
- {
- IS31FL3731_write_register( addr, i, 0x00 );
- }
-
- // select "function register" bank
- IS31FL3731_write_register( addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG );
-
- // disable software shutdown
- IS31FL3731_write_register( addr, ISSI_REG_SHUTDOWN, 0x01 );
-
- // select bank 0 and leave it selected.
- // most usage after initialization is just writing PWM buffers in bank 0
- // as there's not much point in double-buffering
- IS31FL3731_write_register( addr, ISSI_COMMANDREGISTER, 0 );
-
-}
-
-void IS31FL3731_set_color( int index, uint8_t red, uint8_t green, uint8_t blue )
-{
- if ( index >= 0 && index < DRIVER_LED_TOTAL ) {
- is31_led led = g_is31_leds[index];
-
- // Subtract 0x24 to get the second index of g_pwm_buffer
- g_pwm_buffer[led.driver][led.r - 0x24] = red;
- g_pwm_buffer[led.driver][led.g - 0x24] = green;
- g_pwm_buffer[led.driver][led.b - 0x24] = blue;
- g_pwm_buffer_update_required = true;
- }
-}
-
-void IS31FL3731_set_color_all( uint8_t red, uint8_t green, uint8_t blue )
-{
- for ( int i = 0; i < DRIVER_LED_TOTAL; i++ )
- {
- IS31FL3731_set_color( i, red, green, blue );
- }
-}
-
-void IS31FL3731_set_led_control_register( uint8_t index, bool red, bool green, bool blue )
-{
- is31_led led = g_is31_leds[index];
-
- uint8_t control_register_r = (led.r - 0x24) / 8;
- uint8_t control_register_g = (led.g - 0x24) / 8;
- uint8_t control_register_b = (led.b - 0x24) / 8;
- uint8_t bit_r = (led.r - 0x24) % 8;
- uint8_t bit_g = (led.g - 0x24) % 8;
- uint8_t bit_b = (led.b - 0x24) % 8;
-
- if ( red ) {
- g_led_control_registers[led.driver][control_register_r] |= (1 << bit_r);
- } else {
- g_led_control_registers[led.driver][control_register_r] &= ~(1 << bit_r);
- }
- if ( green ) {
- g_led_control_registers[led.driver][control_register_g] |= (1 << bit_g);
- } else {
- g_led_control_registers[led.driver][control_register_g] &= ~(1 << bit_g);
- }
- if ( blue ) {
- g_led_control_registers[led.driver][control_register_b] |= (1 << bit_b);
- } else {
- g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b);
- }
-
- g_led_control_registers_update_required = true;
-
-}
-
-void IS31FL3731_update_pwm_buffers( uint8_t addr1, uint8_t addr2 )
-{
- if ( g_pwm_buffer_update_required )
- {
- IS31FL3731_write_pwm_buffer( addr1, g_pwm_buffer[0] );
- IS31FL3731_write_pwm_buffer( addr2, g_pwm_buffer[1] );
- }
- g_pwm_buffer_update_required = false;
-}
-
-void IS31FL3731_update_led_control_registers( uint8_t addr1, uint8_t addr2 )
-{
- if ( g_led_control_registers_update_required )
- {
- for ( int i=0; i<18; i++ )
- {
- IS31FL3731_write_register(addr1, i, g_led_control_registers[0][i] );
- IS31FL3731_write_register(addr2, i, g_led_control_registers[1][i] );
- }
- }
-}
-
diff --git a/drivers/avr/ws2812.h b/drivers/avr/ws2812.h
index f7e0c31440..1f9299ffb5 100644
--- a/drivers/avr/ws2812.h
+++ b/drivers/avr/ws2812.h
@@ -69,7 +69,4 @@ void ws2812_sendarray_mask(uint8_t *array,uint16_t length, uint8_t pinmask);
#define CONCAT_EXP(a, b) CONCAT(a, b)
#endif
-// #define ws2812_PORTREG CONCAT_EXP(PORT,ws2812_port)
-// #define ws2812_DDRREG CONCAT_EXP(DDR,ws2812_port)
-
#endif /* LIGHT_WS2812_H_ */
diff --git a/drivers/haptic/DRV2605L.c b/drivers/haptic/DRV2605L.c
new file mode 100644
index 0000000000..97ca292b9b
--- /dev/null
+++ b/drivers/haptic/DRV2605L.c
@@ -0,0 +1,129 @@
+/* Copyright 2018 ishtob
+ * Driver for DRV2605L written for QMK
+ *
+ * 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 "DRV2605L.h"
+#include "print.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+
+uint8_t DRV2605L_transfer_buffer[20];
+uint8_t DRV2605L_tx_register[0];
+uint8_t DRV2605L_read_buffer[0];
+uint8_t DRV2605L_read_register;
+
+
+void DRV_write(uint8_t drv_register, uint8_t settings) {
+ DRV2605L_transfer_buffer[0] = drv_register;
+ DRV2605L_transfer_buffer[1] = settings;
+ i2c_transmit(DRV2605L_BASE_ADDRESS << 1, DRV2605L_transfer_buffer, 2, 100);
+}
+
+uint8_t DRV_read(uint8_t regaddress) {
+ DRV2605L_tx_register[0] = regaddress;
+ if (MSG_OK != i2c_transmit_receive(DRV2605L_BASE_ADDRESS << 1,
+ DRV2605L_tx_register, 1,
+ DRV2605L_read_buffer, 1
+)){
+ printf("err reading reg \n");
+ }
+ DRV2605L_read_register = (uint8_t)DRV2605L_read_buffer[0];
+return DRV2605L_read_register;
+}
+
+void DRV_init(void)
+{
+ i2c_init();
+ i2c_start(DRV2605L_BASE_ADDRESS);
+
+ /* 0x07 sets DRV2605 into calibration mode */
+ DRV_write(DRV_MODE,0x07);
+
+// DRV_write(DRV_FEEDBACK_CTRL,0xB6);
+
+ #if FB_ERM_LRA == 0
+ /* ERM settings */
+ DRV_write(DRV_RATED_VOLT, (RATED_VOLTAGE/21.33)*1000);
+ #if ERM_OPEN_LOOP == 0
+ DRV_write(DRV_OVERDRIVE_CLAMP_VOLT, (((V_PEAK*(DRIVE_TIME+BLANKING_TIME+IDISS_TIME))/0.02133)/(DRIVE_TIME-0.0003)));
+ #elif ERM_OPEN_LOOP == 1
+ DRV_write(DRV_OVERDRIVE_CLAMP_VOLT, (V_PEAK/0.02196));
+ #endif
+ #elif FB_ERM_LRA == 1
+ DRV_write(DRV_RATED_VOLT, ((V_RMS * sqrt(1 - ((4 * ((150+(SAMPLE_TIME*50))*0.000001)) + 0.0003)* F_LRA)/0.02071)));
+ #if LRA_OPEN_LOOP == 0
+ DRV_write(DRV_OVERDRIVE_CLAMP_VOLT, ((V_PEAK/sqrt(1-(F_LRA*0.0008))/0.02133)));
+ #elif LRA_OPEN_LOOP == 1
+ DRV_write(DRV_OVERDRIVE_CLAMP_VOLT, (V_PEAK/0.02196));
+ #endif
+ #endif
+
+ DRVREG_FBR FB_SET;
+ FB_SET.Bits.ERM_LRA = FB_ERM_LRA;
+ FB_SET.Bits.BRAKE_FACTOR = FB_BRAKEFACTOR;
+ FB_SET.Bits.LOOP_GAIN =FB_LOOPGAIN;
+ FB_SET.Bits.BEMF_GAIN = 0; /* auto-calibration populates this field*/
+ DRV_write(DRV_FEEDBACK_CTRL, (uint8_t) FB_SET.Byte);
+ DRVREG_CTRL1 C1_SET;
+ C1_SET.Bits.C1_DRIVE_TIME = DRIVE_TIME;
+ C1_SET.Bits.C1_AC_COUPLE = AC_COUPLE;
+ C1_SET.Bits.C1_STARTUP_BOOST = STARTUP_BOOST;
+ DRV_write(DRV_CTRL_1, (uint8_t) C1_SET.Byte);
+ DRVREG_CTRL2 C2_SET;
+ C2_SET.Bits.C2_BIDIR_INPUT = BIDIR_INPUT;
+ C2_SET.Bits.C2_BRAKE_STAB = BRAKE_STAB;
+ C2_SET.Bits.C2_SAMPLE_TIME = SAMPLE_TIME;
+ C2_SET.Bits.C2_BLANKING_TIME = BLANKING_TIME;
+ C2_SET.Bits.C2_IDISS_TIME = IDISS_TIME;
+ DRV_write(DRV_CTRL_2, (uint8_t) C2_SET.Byte);
+ DRVREG_CTRL3 C3_SET;
+ C3_SET.Bits.C3_LRA_OPEN_LOOP = LRA_OPEN_LOOP;
+ C3_SET.Bits.C3_N_PWM_ANALOG = N_PWM_ANALOG;
+ C3_SET.Bits.C3_LRA_DRIVE_MODE = LRA_DRIVE_MODE;
+ C3_SET.Bits.C3_DATA_FORMAT_RTO = DATA_FORMAT_RTO;
+ C3_SET.Bits.C3_SUPPLY_COMP_DIS = SUPPLY_COMP_DIS;
+ C3_SET.Bits.C3_ERM_OPEN_LOOP = ERM_OPEN_LOOP;
+ C3_SET.Bits.C3_NG_THRESH = NG_THRESH;
+ DRV_write(DRV_CTRL_3, (uint8_t) C3_SET.Byte);
+ DRVREG_CTRL4 C4_SET;
+ C4_SET.Bits.C4_ZC_DET_TIME = ZC_DET_TIME;
+ C4_SET.Bits.C4_AUTO_CAL_TIME = AUTO_CAL_TIME;
+ DRV_write(DRV_CTRL_4, (uint8_t) C4_SET.Byte);
+ DRV_write(DRV_LIB_SELECTION,LIB_SELECTION);
+ //start autocalibration
+ DRV_write(DRV_GO, 0x01);
+
+ /* 0x00 sets DRV2605 out of standby and to use internal trigger
+ * 0x01 sets DRV2605 out of standby and to use external trigger */
+ DRV_write(DRV_MODE,0x00);
+
+ /* 0x06: LRA library */
+ DRV_write(DRV_WAVEFORM_SEQ_1, 0x01);
+
+ /* 0xB9: LRA, 4x brake factor, medium gain, 7.5x back EMF
+ * 0x39: ERM, 4x brake factor, medium gain, 1.365x back EMF */
+
+ /* TODO: setup auto-calibration as part of initiation */
+
+}
+
+void DRV_pulse(uint8_t sequence)
+{
+ DRV_write(DRV_GO, 0x00);
+ DRV_write(DRV_WAVEFORM_SEQ_1, sequence);
+ DRV_write(DRV_GO, 0x01);
+} \ No newline at end of file
diff --git a/drivers/haptic/DRV2605L.h b/drivers/haptic/DRV2605L.h
new file mode 100644
index 0000000000..de9d294e9d
--- /dev/null
+++ b/drivers/haptic/DRV2605L.h
@@ -0,0 +1,394 @@
+/* Copyright 2018 ishtob
+ * Driver for DRV2605L written for QMK
+ *
+ * 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 "i2c_master.h"
+
+/* Initialization settings
+
+ * Feedback Control Settings */
+#ifndef FB_ERM_LRA
+#define FB_ERM_LRA 1 /* For ERM:0 or LRA:1*/
+#endif
+#ifndef FB_BRAKEFACTOR
+#define FB_BRAKEFACTOR 3 /* For 1x:0, 2x:1, 3x:2, 4x:3, 6x:4, 8x:5, 16x:6, Disable Braking:7 */
+#endif
+#ifndef FB_LOOPGAIN
+#define FB_LOOPGAIN 1 /* For Low:0, Medium:1, High:2, Very High:3 */
+#endif
+
+#ifndef RATED_VOLTAGE
+#define RATED_VOLTAGE 2 /* 2v as safe range in case device voltage is not set */
+#ifndef V_PEAK
+#define V_PEAK 2.8
+#endif
+#endif
+
+/* LRA specific settings */
+#if FB_ERM_LRA == 1
+#ifndef V_RMS
+#define V_RMS 2.0
+#endif
+#ifndef V_PEAK
+#define V_PEAK 2.1
+#endif
+#ifndef F_LRA
+#define F_LRA 205
+#endif
+#endif
+
+/* Library Selection */
+#ifndef LIB_SELECTION
+#if FB_ERM_LRA == 1
+#define LIB_SELECTION 6 /* For Empty:0' TS2200 library A to D:1-5, LRA Library: 6 */
+#else
+#define LIB_SELECTION 1
+#endif
+#endif
+
+/* Control 1 register settings */
+#ifndef DRIVE_TIME
+#define DRIVE_TIME 25
+#endif
+#ifndef AC_COUPLE
+#define AC_COUPLE 0
+#endif
+#ifndef STARTUP_BOOST
+#define STARTUP_BOOST 1
+#endif
+
+/* Control 2 Settings */
+#ifndef BIDIR_INPUT
+#define BIDIR_INPUT 1
+#endif
+#ifndef BRAKE_STAB
+#define BRAKE_STAB 1 /* Loopgain is reduced when braking is almost complete to improve stability */
+#endif
+#ifndef SAMPLE_TIME
+#define SAMPLE_TIME 3
+#endif
+#ifndef BLANKING_TIME
+#define BLANKING_TIME 1
+#endif
+#ifndef IDISS_TIME
+#define IDISS_TIME 1
+#endif
+
+/* Control 3 settings */
+#ifndef NG_THRESH
+#define NG_THRESH 2
+#endif
+#ifndef ERM_OPEN_LOOP
+#define ERM_OPEN_LOOP 1
+#endif
+#ifndef SUPPLY_COMP_DIS
+#define SUPPLY_COMP_DIS 0
+#endif
+#ifndef DATA_FORMAT_RTO
+#define DATA_FORMAT_RTO 0
+#endif
+#ifndef LRA_DRIVE_MODE
+#define LRA_DRIVE_MODE 0
+#endif
+#ifndef N_PWM_ANALOG
+#define N_PWM_ANALOG 0
+#endif
+#ifndef LRA_OPEN_LOOP
+#define LRA_OPEN_LOOP 0
+#endif
+
+/* Control 4 settings */
+#ifndef ZC_DET_TIME
+#define ZC_DET_TIME 0
+#endif
+#ifndef AUTO_CAL_TIME
+#define AUTO_CAL_TIME 3
+#endif
+
+/* register defines -------------------------------------------------------- */
+#define DRV2605L_BASE_ADDRESS 0x5A /* DRV2605L Base address */
+#define DRV_STATUS 0x00
+#define DRV_MODE 0x01
+#define DRV_RTP_INPUT 0x02
+#define DRV_LIB_SELECTION 0x03
+#define DRV_WAVEFORM_SEQ_1 0x04
+#define DRV_WAVEFORM_SEQ_2 0x05
+#define DRV_WAVEFORM_SEQ_3 0x06
+#define DRV_WAVEFORM_SEQ_4 0x07
+#define DRV_WAVEFORM_SEQ_5 0x08
+#define DRV_WAVEFORM_SEQ_6 0x09
+#define DRV_WAVEFORM_SEQ_7 0x0A
+#define DRV_WAVEFORM_SEQ_8 0x0B
+#define DRV_GO 0x0C
+#define DRV_OVERDRIVE_TIME_OFFSET 0x0D
+#define DRV_SUSTAIN_TIME_OFFSET_P 0x0E
+#define DRV_SUSTAIN_TIME_OFFSET_N 0x0F
+#define DRV_BRAKE_TIME_OFFSET 0x10
+#define DRV_AUDIO_2_VIBE_CTRL 0x11
+#define DRV_AUDIO_2_VIBE_MIN_IN 0x12
+#define DRV_AUDIO_2_VIBE_MAX_IN 0x13
+#define DRV_AUDIO_2_VIBE_MIN_OUTDRV 0x14
+#define DRV_AUDIO_2_VIBE_MAX_OUTDRV 0x15
+#define DRV_RATED_VOLT 0x16
+#define DRV_OVERDRIVE_CLAMP_VOLT 0x17
+#define DRV_AUTO_CALIB_COMP_RESULT 0x18
+#define DRV_AUTO_CALIB_BEMF_RESULT 0x19
+#define DRV_FEEDBACK_CTRL 0x1A
+#define DRV_CTRL_1 0x1B
+#define DRV_CTRL_2 0x1C
+#define DRV_CTRL_3 0x1D
+#define DRV_CTRL_4 0x1E
+#define DRV_CTRL_5 0x1F
+#define DRV_OPEN_LOOP_PERIOD 0x20
+#define DRV_VBAT_VOLT_MONITOR 0x21
+#define DRV_LRA_RESONANCE_PERIOD 0x22
+
+void DRV_init(void);
+void DRV_write(const uint8_t drv_register, const uint8_t settings);
+uint8_t DRV_read(const uint8_t regaddress);
+void DRV_pulse(const uint8_t sequence);
+
+
+typedef enum DRV_EFFECT{
+ clear_sequence = 0,
+ strong_click = 1,
+ strong_click_60 = 2,
+ strong_click_30 = 3,
+ sharp_click = 4,
+ sharp_click_60 = 5,
+ sharp_click_30 = 6,
+ soft_bump = 7,
+ soft_bump_60 = 8,
+ soft_bump_30 = 9,
+ dbl_click = 10,
+ dbl_click_60 = 11,
+ trp_click = 12,
+ soft_fuzz = 13,
+ strong_buzz = 14,
+ alert_750ms = 15,
+ alert_1000ms = 16,
+ strong_click1 = 17,
+ strong_click2_80 = 18,
+ strong_click3_60 = 19,
+ strong_click4_30 = 20,
+ medium_click1 = 21,
+ medium_click2_80 = 22,
+ medium_click3_60 = 23,
+ sharp_tick1 = 24,
+ sharp_tick2_80 = 25,
+ sharp_tick3_60 = 26,
+ sh_dblclick_str = 27,
+ sh_dblclick_str_80 = 28,
+ sh_dblclick_str_60 = 29,
+ sh_dblclick_str_30 = 30,
+ sh_dblclick_med = 31,
+ sh_dblclick_med_80 = 32,
+ sh_dblclick_med_60 = 33,
+ sh_dblsharp_tick = 34,
+ sh_dblsharp_tick_80 = 35,
+ sh_dblsharp_tick_60 = 36,
+ lg_dblclick_str = 37,
+ lg_dblclick_str_80 = 38,
+ lg_dblclick_str_60 = 39,
+ lg_dblclick_str_30 = 40,
+ lg_dblclick_med = 41,
+ lg_dblclick_med_80 = 42,
+ lg_dblclick_med_60 = 43,
+ lg_dblsharp_tick = 44,
+ lg_dblsharp_tick_80 = 45,
+ lg_dblsharp_tick_60 = 46,
+ buzz = 47,
+ buzz_80 = 48,
+ buzz_60 = 49,
+ buzz_40 = 50,
+ buzz_20 = 51,
+ pulsing_strong = 52,
+ pulsing_strong_80 = 53,
+ pulsing_medium = 54,
+ pulsing_medium_80 = 55,
+ pulsing_sharp = 56,
+ pulsing_sharp_80 = 57,
+ transition_click = 58,
+ transition_click_80 = 59,
+ transition_click_60 = 60,
+ transition_click_40 = 61,
+ transition_click_20 = 62,
+ transition_click_10 = 63,
+ transition_hum = 64,
+ transition_hum_80 = 65,
+ transition_hum_60 = 66,
+ transition_hum_40 = 67,
+ transition_hum_20 = 68,
+ transition_hum_10 = 69,
+ transition_rampdown_long_smooth1 = 70,
+ transition_rampdown_long_smooth2 = 71,
+ transition_rampdown_med_smooth1 = 72,
+ transition_rampdown_med_smooth2 = 73,
+ transition_rampdown_short_smooth1 = 74,
+ transition_rampdown_short_smooth2 = 75,
+ transition_rampdown_long_sharp1 = 76,
+ transition_rampdown_long_sharp2 = 77,
+ transition_rampdown_med_sharp1 = 78,
+ transition_rampdown_med_sharp2 = 79,
+ transition_rampdown_short_sharp1 = 80,
+ transition_rampdown_short_sharp2 = 81,
+ transition_rampup_long_smooth1 = 82,
+ transition_rampup_long_smooth2 = 83,
+ transition_rampup_med_smooth1 = 84,
+ transition_rampup_med_smooth2 = 85,
+ transition_rampup_short_smooth1 = 86,
+ transition_rampup_short_smooth2 = 87,
+ transition_rampup_long_sharp1 = 88,
+ transition_rampup_long_sharp2 = 89,
+ transition_rampup_med_sharp1 = 90,
+ transition_rampup_med_sharp2 = 91,
+ transition_rampup_short_sharp1 = 92,
+ transition_rampup_short_sharp2 = 93,
+ transition_rampdown_long_smooth1_50 = 94,
+ transition_rampdown_long_smooth2_50 = 95,
+ transition_rampdown_med_smooth1_50 = 96,
+ transition_rampdown_med_smooth2_50 = 97,
+ transition_rampdown_short_smooth1_50 = 98,
+ transition_rampdown_short_smooth2_50 = 99,
+ transition_rampdown_long_sharp1_50 = 100,
+ transition_rampdown_long_sharp2_50 = 101,
+ transition_rampdown_med_sharp1_50 = 102,
+ transition_rampdown_med_sharp2_50 = 103,
+ transition_rampdown_short_sharp1_50 = 104,
+ transition_rampdown_short_sharp2_50 = 105,
+ transition_rampup_long_smooth1_50 = 106,
+ transition_rampup_long_smooth2_50 = 107,
+ transition_rampup_med_smooth1_50 = 108,
+ transition_rampup_med_smooth2_50 = 109,
+ transition_rampup_short_smooth1_50 = 110,
+ transition_rampup_short_smooth2_50 = 111,
+ transition_rampup_long_sharp1_50 = 112,
+ transition_rampup_long_sharp2_50 = 113,
+ transition_rampup_med_sharp1_50 = 114,
+ transition_rampup_med_sharp2_50 = 115,
+ transition_rampup_short_sharp1_50 = 116,
+ transition_rampup_short_sharp2_50 = 117,
+ long_buzz_for_programmatic_stopping = 118,
+ smooth_hum1_50 = 119,
+ smooth_hum2_40 = 120,
+ smooth_hum3_30 = 121,
+ smooth_hum4_20 = 122,
+ smooth_hum5_10 = 123,
+} DRV_EFFECT;
+
+/* Register bit array unions */
+
+typedef union DRVREG_STATUS { /* register 0x00 */
+ uint8_t Byte;
+ struct {
+ uint8_t OC_DETECT :1; /* set to 1 when overcurrent event is detected */
+ uint8_t OVER_TEMP :1; /* set to 1 when device exceeds temp threshold */
+ uint8_t FB_STS :1; /* set to 1 when feedback controller has timed out */
+ /* auto-calibration routine and diagnostic result
+ * result | auto-calibation | diagnostic |
+ * 0 | passed | actuator func normal |
+ * 1 | failed | actuator func fault* |
+ * * actuator is not present or is shorted, timing out, or giving out–of-range back-EMF */
+ uint8_t DIAG_RESULT :1;
+ uint8_t :1;
+ uint8_t DEVICE_ID :3; /* Device IDs 3: DRV2605 4: DRV2604 5: DRV2604L 6: DRV2605L */
+ } Bits;
+} DRVREG_STATUS;
+
+typedef union DRVREG_MODE { /* register 0x01 */
+ uint8_t Byte;
+ struct {
+ uint8_t MODE :3; /* Mode setting */
+ uint8_t :3;
+ uint8_t STANDBY :1; /* 0:standby 1:ready */
+ } Bits;
+} DRVREG_MODE;
+
+typedef union DRVREG_WAIT {
+ uint8_t Byte;
+ struct {
+ uint8_t WAIT_MODE :1; /* Set to 1 to interpret as wait for next 7 bits x10ms */
+ uint8_t WAIT_TIME :7;
+ } Bits;
+} DRVREG_WAIT;
+
+typedef union DRVREG_FBR{ /* register 0x1A */
+ uint8_t Byte;
+ struct {
+ uint8_t BEMF_GAIN :2;
+ uint8_t LOOP_GAIN :2;
+ uint8_t BRAKE_FACTOR :3;
+ uint8_t ERM_LRA :1;
+ } Bits;
+} DRVREG_FBR;
+
+typedef union DRVREG_CTRL1{ /* register 0x1B */
+ uint8_t Byte;
+ struct {
+ uint8_t C1_DRIVE_TIME :5;
+ uint8_t C1_AC_COUPLE :1;
+ uint8_t :1;
+ uint8_t C1_STARTUP_BOOST :1;
+ } Bits;
+} DRVREG_CTRL1;
+
+typedef union DRVREG_CTRL2{ /* register 0x1C */
+ uint8_t Byte;
+ struct {
+ uint8_t C2_IDISS_TIME :2;
+ uint8_t C2_BLANKING_TIME :2;
+ uint8_t C2_SAMPLE_TIME :2;
+ uint8_t C2_BRAKE_STAB :1;
+ uint8_t C2_BIDIR_INPUT :1;
+ } Bits;
+} DRVREG_CTRL2;
+
+typedef union DRVREG_CTRL3{ /* register 0x1D */
+ uint8_t Byte;
+ struct {
+ uint8_t C3_LRA_OPEN_LOOP :1;
+ uint8_t C3_N_PWM_ANALOG :1;
+ uint8_t C3_LRA_DRIVE_MODE :1;
+ uint8_t C3_DATA_FORMAT_RTO :1;
+ uint8_t C3_SUPPLY_COMP_DIS :1;
+ uint8_t C3_ERM_OPEN_LOOP :1;
+ uint8_t C3_NG_THRESH :2;
+ } Bits;
+} DRVREG_CTRL3;
+
+typedef union DRVREG_CTRL4{ /* register 0x1E */
+ uint8_t Byte;
+ struct {
+ uint8_t C4_OTP_PROGRAM :1;
+ uint8_t :1;
+ uint8_t C4_OTP_STATUS :1;
+ uint8_t :1;
+ uint8_t C4_AUTO_CAL_TIME :2;
+ uint8_t C4_ZC_DET_TIME :2;
+ } Bits;
+} DRVREG_CTRL4;
+
+typedef union DRVREG_CTRL5{ /* register 0x1F */
+ uint8_t Byte;
+ struct {
+ uint8_t C5_IDISS_TIME :2;
+ uint8_t C5_BLANKING_TIME :2;
+ uint8_t C5_PLAYBACK_INTERVAL :1;
+ uint8_t C5_LRA_AUTO_OPEN_LOOP :1;
+ uint8_t C5_AUTO_OL_CNT :2;
+ } Bits;
+} DRVREG_CTRL5; \ No newline at end of file
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/avr/is31fl3731.h b/drivers/issi/is31fl3731.h
index 3d30fc67b7..f354a12db2 100644
--- a/drivers/avr/is31fl3731.h
+++ b/drivers/issi/is31fl3731.h
@@ -23,7 +23,7 @@
#include <stdbool.h>
typedef struct is31_led {
- uint8_t driver:2;
+ uint8_t driver:2;
uint8_t r;
uint8_t g;
uint8_t b;
diff --git a/drivers/issi/is31fl3733.c b/drivers/issi/is31fl3733.c
new file mode 100644
index 0000000000..c198ec5174
--- /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 <string.h>
+#include "i2c_master.h"
+#include "progmem.h"
+#include "rgb_matrix.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
+
diff --git a/drivers/qwiic/micro_oled.c b/drivers/qwiic/micro_oled.c
new file mode 100644
index 0000000000..35c5d6ee1d
--- /dev/null
+++ b/drivers/qwiic/micro_oled.c
@@ -0,0 +1,691 @@
+/* Jim Lindblom @ SparkFun Electronics
+ * October 26, 2014
+ * https://github.com/sparkfun/Micro_OLED_Breakout/tree/master/Firmware/Arduino/libraries/SFE_MicroOLED
+ *
+ * Modified by:
+ * Emil Varughese @ Edwin Robotics Pvt. Ltd.
+ * July 27, 2015
+ * https://github.com/emil01/SparkFun_Micro_OLED_Arduino_Library/
+ *
+ * This code was heavily based around the MicroView library, written by GeekAmmo
+ * (https://github.com/geekammo/MicroView-Arduino-Library).
+ *
+ * Adapted for QMK by:
+ * Jack Humbert <jack.humb@gmail.com>
+ * October 11, 2018
+ *
+ * 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 3 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 "micro_oled.h"
+#include <stdlib.h>
+#include "util/font5x7.h"
+#include "util/font8x16.h"
+#include "string.h"
+
+#define TOTALFONTS 2
+const unsigned char * fonts_pointer[]= { font5x7, font8x16 };
+
+uint8_t foreColor,drawMode,fontWidth, fontHeight, fontType, fontStartChar, fontTotalChar, cursorX, cursorY;
+uint16_t fontMapWidth;
+
+#define _BV(x) (1 << (x))
+#define swap(a, b) { uint8_t t = a; a = b; b = t; }
+
+uint8_t micro_oled_transfer_buffer[20];
+static uint8_t micro_oled_screen_current[LCDWIDTH*LCDWIDTH/8] = { 0 };
+
+/* LCD Memory organised in 64 horizontal pixel and 6 rows of byte
+ B B .............B -----
+ y y .............y \
+ t t .............t \
+ e e .............e \
+ 0 1 .............63 \
+ \
+ D0 D0.............D0 \
+ D1 D1.............D1 / ROW 0
+ D2 D2.............D2 /
+ D3 D3.............D3 /
+ D4 D4.............D4 /
+ D5 D5.............D5 /
+ D6 D6.............D6 /
+ D7 D7.............D7 ----
+ */
+
+#if LCDWIDTH == 64
+ #if LCDWIDTH == 48
+static uint8_t micro_oled_screen_buffer[] = {
+// QMK Logo - generated at http://www.majer.ch/lcd/adf_bitmap.php
+//64x48 image
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0x00, 0x00,
+0x00, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0x00, 0x00,
+0x00, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x60, 0x60,
+0xF8, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0x1F, 0x1F, 0xFF, 0xFF,
+0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0x1F, 0xFF, 0xFF, 0xFF,
+0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0x1F, 0xFF, 0xFF, 0xFF, 0xFE,
+0xFE, 0xF8, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x8C, 0x8C, 0x8C, 0x8C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
+0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
+0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8C, 0x8C, 0x8C, 0x8C,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x31, 0x31, 0x31, 0x31, 0xFF, 0xFF,
+0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xF8, 0xF1, 0xE3, 0xE7, 0xCF,
+0xCF, 0xCF, 0xCF, 0x00, 0x00, 0xCF, 0xCF, 0xCF, 0xC7, 0xE7,
+0xE3, 0xF1, 0xF8, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+0x31, 0x31, 0x31, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06,
+0x06, 0x06, 0x1F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xF8, 0xFF,
+0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+0xFF, 0x7F, 0x7F, 0x1F, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00,
+0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00,
+0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00
+};
+ #endif
+#elif LCDWIDTH == 128
+ #if LCDHEIGHT == 32
+ static uint8_t micro_oled_screen_buffer[LCDWIDTH*LCDWIDTH/8] = {
+//128x32 qmk image
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x80, 0xC0, 0xE0, 0xE0, 0xFC, 0xFC, 0xE0, 0xFC, 0xFC,
+0xE0, 0xF0, 0xFC, 0xE0, 0xE0, 0xFC, 0xE0, 0xE0, 0xFC, 0xFC,
+0xE0, 0xE0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0xF0, 0x10, 0x10, 0x30, 0xE0, 0x00, 0x00,
+0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00,
+0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x80,
+0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00,
+0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80,
+0x80, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0xB2, 0xB2, 0xFF,
+0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x03, 0xFF, 0xFF, 0xFF, 0x03,
+0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x03, 0xFF, 0xFF, 0xFF,
+0xFF, 0xB7, 0xB2, 0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x1F, 0x02, 0x02, 0x03, 0x01, 0x00, 0x06, 0x1F, 0x10,
+0x10, 0x10, 0x1F, 0x06, 0x00, 0x03, 0x1E, 0x18, 0x0F, 0x01,
+0x0F, 0x18, 0x1E, 0x01, 0x00, 0x0F, 0x1F, 0x12, 0x02, 0x12,
+0x13, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x0E, 0x1F, 0x12,
+0x02, 0x12, 0x13, 0x00, 0x00, 0x1F, 0x10, 0x10, 0x10, 0x1F,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x48, 0x4D, 0x4D, 0xFF, 0xFF, 0xFF,
+0xFF, 0xFF, 0xFE, 0xF8, 0xF9, 0xF3, 0xF3, 0xC0, 0x80, 0xF3,
+0xF3, 0xF3, 0xF9, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xED,
+0x4D, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xFE, 0x20, 0x10, 0x10, 0xE0, 0xC0, 0x00, 0x70, 0xC0,
+0x00, 0x80, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x0C,
+0x04, 0x04, 0x04, 0x04, 0x1C, 0xF0, 0x00, 0x00, 0xFC, 0x0C,
+0x38, 0xE0, 0x00, 0x00, 0xC0, 0x38, 0x0C, 0xFC, 0x00, 0x00,
+0xFC, 0xFC, 0x60, 0x90, 0x0C, 0x04, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x07, 0x3F,
+0x3F, 0x07, 0x3F, 0x3F, 0x07, 0x0F, 0x3F, 0x07, 0x07, 0x3F,
+0x07, 0x07, 0x3F, 0x3F, 0x07, 0x07, 0x03, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
+0x06, 0x04, 0x04, 0x07, 0x01, 0x00, 0x00, 0x13, 0x1E, 0x03,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x04, 0x04,
+0x04, 0x04, 0x07, 0x0D, 0x08, 0x00, 0x07, 0x00, 0x00, 0x01,
+0x07, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x07,
+0x00, 0x01, 0x03, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00
+ };
+ #elif LCDHEIGHT == 64
+ static uint8_t micro_oled_screen_buffer[LCDWIDTH*LCDWIDTH/8] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0,
+0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00,
+0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0xC0, 0xC0, 0xC0, 0xC0, 0xF8, 0xFC, 0xFC, 0xFE, 0xFE, 0xFF,
+0x7F, 0x7E, 0xFE, 0xFF, 0xFF, 0xFE, 0xFE, 0x7F, 0x7F, 0xFE,
+0xFE, 0xFF, 0xFF, 0xFE, 0x7E, 0x7F, 0xFF, 0xFE, 0xFE, 0xFC,
+0xFC, 0xF8, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x88, 0x88, 0x88, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
+0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
+0xFF, 0xFF, 0xFF, 0xDD, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x99, 0x99, 0x99, 0x99, 0xFF, 0xFF, 0xFF, 0xFF,
+0xFF, 0xFF, 0xFE, 0xF8, 0xF0, 0xF3, 0xF3, 0xE7, 0xE7, 0x00,
+0x00, 0xE7, 0xE7, 0xF3, 0xF3, 0xF0, 0xF8, 0xFE, 0xFF, 0xFF,
+0xFF, 0xFF, 0xFF, 0xFF, 0x99, 0x99, 0x99, 0x99, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x0F, 0x1F, 0x3F,
+0x3F, 0x3F, 0xFF, 0xFF, 0x3F, 0x3F, 0xFF, 0xFF, 0x3F, 0x3F,
+0xFF, 0xFF, 0x3F, 0x3F, 0xFF, 0xFF, 0x3F, 0x3F, 0xFF, 0xFF,
+0x3F, 0x3F, 0x3F, 0x1F, 0x0F, 0x01, 0x01, 0x01, 0x01, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x03, 0x01, 0x00,
+0x80, 0x03, 0x03, 0x00, 0x00, 0x01, 0x03, 0x00, 0x80, 0x01,
+0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0xFF, 0x11, 0x11, 0x11, 0x0E, 0x00, 0x70,
+0x88, 0x04, 0x04, 0x04, 0xF8, 0x00, 0x00, 0x3C, 0xE0, 0xC0,
+0x38, 0x1C, 0xE0, 0x80, 0x70, 0x0C, 0x00, 0xF8, 0xAC, 0x24,
+0x24, 0x3C, 0x30, 0x00, 0x00, 0xFC, 0x0C, 0x04, 0x00, 0xF8,
+0xAC, 0x24, 0x24, 0x2C, 0x30, 0x00, 0x70, 0xDC, 0x04, 0x04,
+0x88, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+0x8C, 0x04, 0x04, 0xF8, 0x00, 0x04, 0x3C, 0xE0, 0x80, 0xF0,
+0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x83, 0x01, 0x01,
+0x01, 0x81, 0xFE, 0x3C, 0x00, 0x00, 0xFF, 0x03, 0x0E, 0x70,
+0xC0, 0xE0, 0x38, 0x06, 0x03, 0xFF, 0x00, 0x00, 0xFF, 0x18,
+0x38, 0x66, 0xC3, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
+0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
+0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01,
+0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00,
+0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+//TODO: generate bitmap of QMK logo here
+ #endif
+#else
+//catchall for custom screen szies
+ static uint8_t micro_oled_screen_buffer[LCDWIDTH*LCDWIDTH/8] = {0};
+#endif
+
+
+
+void micro_oled_init(void) {
+
+ i2c_init();
+ i2c_start(I2C_ADDRESS_SA0_1);
+
+ // Display Init sequence for 64x48 OLED module
+ send_command(DISPLAYOFF); // 0xAE
+
+ send_command(SETDISPLAYCLOCKDIV); // 0xD5
+ send_command(0x80); // the suggested ratio 0x80
+
+ send_command(SETMULTIPLEX); // 0xA8
+ send_command(LCDHEIGHT - 1);
+
+ send_command(SETDISPLAYOFFSET); // 0xD3
+ send_command(0x00); // no offset
+
+ send_command(SETSTARTLINE | 0x00); // line #0
+
+ send_command(CHARGEPUMP); // enable charge pump
+ send_command(0x14);
+
+ send_command(NORMALDISPLAY); // 0xA6
+ send_command(DISPLAYALLONRESUME); // 0xA4
+
+//display at regular orientation
+ send_command(SEGREMAP | 0x1);
+ send_command(COMSCANDEC);
+
+//rotate display 180
+#ifdef micro_oled_rotate_180
+ send_command(SEGREMAP);
+ send_command(COMSCANINC);
+#endif
+
+ send_command(MEMORYMODE);
+ send_command(0x10);
+
+ send_command(SETCOMPINS); // 0xDA
+if (LCDHEIGHT > 32) {
+ send_command(0x12);
+} else {
+ send_command(0x02);
+}
+ send_command(SETCONTRAST); // 0x81
+ send_command(0x8F);
+
+ send_command(SETPRECHARGE); // 0xd9
+ send_command(0xF1);
+
+ send_command(SETVCOMDESELECT); // 0xDB
+ send_command(0x40);
+
+ send_command(DISPLAYON); //--turn on oled panel
+ clear_screen(); // Erase hardware memory inside the OLED controller to avoid random data in memory.
+ send_buffer();
+}
+
+void send_command(uint8_t command) {
+ micro_oled_transfer_buffer[0] = I2C_COMMAND;
+ micro_oled_transfer_buffer[1] = command;
+ i2c_transmit(I2C_ADDRESS_SA0_1 << 1, micro_oled_transfer_buffer, 2, 100);
+}
+
+void send_data(uint8_t data) {
+ micro_oled_transfer_buffer[0] = I2C_DATA;
+ micro_oled_transfer_buffer[1] = data;
+ i2c_transmit(I2C_ADDRESS_SA0_1 << 1, micro_oled_transfer_buffer, 2, 100);
+}
+
+/** \brief Set SSD1306 page address.
+ Send page address command and address to the SSD1306 OLED controller.
+*/
+void set_page_address(uint8_t address) {
+ address = (0xB0 | address);
+ send_command(address);
+}
+
+/** \brief Set SSD1306 column address.
+ Send column address command and address to the SSD1306 OLED controller.
+*/
+void set_column_address(uint8_t address) {
+ send_command( ( 0x10 | (address >> 4) ) + ((128 - LCDWIDTH) >> 8) );
+ send_command( 0x0F & address );
+}
+
+/** \brief Clear SSD1306's memory.
+ To clear GDRAM inside the LCD controller.
+*/
+void clear_screen(void) {
+ for (int i=0;i<8; i++) {
+ set_page_address(i);
+ set_column_address(0);
+ for (int j=0; j<0x80; j++) {
+ send_data(0);
+ }
+ }
+}
+
+/** \brief Clear SSD1306's memory.
+ To clear GDRAM inside the LCD controller.
+*/
+void clear_buffer(void) {
+//384
+ memset(micro_oled_screen_buffer, 0, LCDWIDTH*LCDWIDTH/8);
+}
+
+/** \brief Invert display.
+ The PIXEL_ON color of the display will turn to PIXEL_OFF and the PIXEL_OFF will turn to PIXEL_ON.
+*/
+void invert_screen(bool invert) {
+ if (invert) {
+ send_command(INVERTDISPLAY);
+ } else {
+ send_command(NORMALDISPLAY);
+ }
+}
+
+/** \brief Set contrast.
+ OLED contract value from 0 to 255. Note: Contrast level is not very obvious.
+*/
+void set_contrast(uint8_t contrast) {
+ send_command(SETCONTRAST); // 0x81
+ send_command(contrast);
+}
+
+/** \brief Transfer display buffer.
+ Sends the updated buffer to the controller - the current status is checked before to save i2c exectution time
+*/
+void send_buffer(void) {
+ uint8_t i, j;
+
+ uint8_t page_addr = 0xFF;
+ for (i = 0; i < LCDHEIGHT/8; i++) {
+ uint8_t col_addr = 0xFF;
+ for (j = 0; j < LCDWIDTH; j++) {
+ if (micro_oled_screen_buffer[i*LCDWIDTH+j] != micro_oled_screen_current[i*LCDWIDTH+j]) {
+ if (page_addr != i) {
+ set_page_address(i);
+ }
+ if (col_addr != j) {
+ set_column_address(j);
+ }
+ send_data(micro_oled_screen_buffer[i*LCDWIDTH+j]);
+ micro_oled_screen_current[i*LCDWIDTH+j] = micro_oled_screen_buffer[i*LCDWIDTH+j];
+ col_addr = j + 1;
+ }
+ }
+ }
+}
+
+/** \brief Draw pixel with color and mode.
+ Draw color pixel in the screen buffer's x,y position with NORM or XOR draw mode.
+*/
+void draw_pixel(uint8_t x, uint8_t y, uint8_t color, uint8_t mode) {
+ if ((x<0) || (x>=LCDWIDTH) || (y<0) || (y>=LCDHEIGHT))
+ return;
+
+ if (mode == XOR) {
+ if (color == PIXEL_ON)
+ micro_oled_screen_buffer[x + (y/8)*LCDWIDTH] ^= _BV((y%8));
+ } else {
+ if (color == PIXEL_ON)
+ micro_oled_screen_buffer[x + (y/8)*LCDWIDTH] |= _BV((y%8));
+ else
+ micro_oled_screen_buffer[x + (y/8)*LCDWIDTH] &= ~_BV((y%8));
+ }
+}
+
+/** \brief Draw line with color and mode.
+Draw line using color and mode from x0,y0 to x1,y1 of the screen buffer.
+*/
+void draw_line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint8_t color, uint8_t mode) {
+ uint8_t steep = abs(y1 - y0) > abs(x1 - x0);
+ if (steep) {
+ swap(x0, y0);
+ swap(x1, y1);
+ }
+
+ if (x0 > x1) {
+ swap(x0, x1);
+ swap(y0, y1);
+ }
+
+ uint8_t dx, dy;
+ dx = x1 - x0;
+ dy = abs(y1 - y0);
+
+ int8_t err = dx / 2;
+ int8_t ystep;
+
+ if (y0 < y1) {
+ ystep = 1;
+ } else {
+ ystep = -1;}
+
+ for (; x0<x1; x0++) {
+ if (steep) {
+ draw_pixel(y0, x0, color, mode);
+ } else {
+ draw_pixel(x0, y0, color, mode);
+ }
+ err -= dy;
+ if (err < 0) {
+ y0 += ystep;
+ err += dx;
+ }
+ }
+}
+
+/** \brief Draw horizontal line with color and mode.
+Draw horizontal line using color and mode from x,y to x+width,y of the screen buffer.
+*/
+void draw_line_hori(uint8_t x, uint8_t y, uint8_t width, uint8_t color, uint8_t mode) {
+ draw_line(x,y,x+width,y,color,mode);
+}
+
+/** \brief Draw vertical line.
+Draw vertical line using current fore color and current draw mode from x,y to x,y+height of the screen buffer.
+*/
+void draw_line_vert(uint8_t x, uint8_t y, uint8_t height, bool color, uint8_t mode) {
+ draw_line(x,y,x,y+height,color,mode);
+}
+
+/** \brief Draw rectangle with color and mode.
+Draw rectangle using color and mode from x,y to x+width,y+height of the screen buffer.
+*/
+void draw_rect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color, uint8_t mode) {
+ uint8_t tempHeight;
+
+ draw_line_hori(x,y, width, color, mode);
+ draw_line_hori(x,y+height-1, width, color, mode);
+
+ tempHeight=height-2;
+
+ // skip drawing vertical lines to avoid overlapping of pixel that will
+ // affect XOR plot if no pixel in between horizontal lines
+ if (tempHeight<1) return;
+
+ draw_line_vert(x,y+1, tempHeight, color, mode);
+ draw_line_vert(x+width-1, y+1, tempHeight, color, mode);
+}
+
+/** \brief Draw rectangle with color and mode.
+Draw rectangle using color and mode from x,y to x+width,y+height of the screen buffer.
+*/
+void draw_rect_soft(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color, uint8_t mode) {
+ uint8_t tempHeight;
+
+ draw_line_hori(x+1,y, width-2, color, mode);
+ draw_line_hori(x+1,y+height-1, width-2, color, mode);
+
+ tempHeight=height-2;
+
+ // skip drawing vertical lines to avoid overlapping of pixel that will
+ // affect XOR plot if no pixel in between horizontal lines
+ if (tempHeight<1) return;
+
+ draw_line_vert(x,y+1, tempHeight, color, mode);
+ draw_line_vert(x+width-1, y+1, tempHeight, color, mode);
+}
+
+/** \brief Draw filled rectangle with color and mode.
+Draw filled rectangle using color and mode from x,y to x+width,y+height of the screen buffer.
+*/
+void draw_rect_filled(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color, uint8_t mode) {
+ // TODO - need to optimise the memory map draw so that this function will not call pixel one by one
+ for (int i=x; i<x+width;i++) {
+ draw_line_vert(i,y, height, color, mode);
+ }
+}
+
+/** \brief Draw filled rectangle with color and mode.
+Draw filled rectangle using color and mode from x,y to x+width,y+height of the screen buffer.
+*/
+void draw_rect_filled_soft(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color, uint8_t mode) {
+ // TODO - need to optimise the memory map draw so that this function will not call pixel one by one
+ for (int i=x; i<x+width;i++) {
+ if (i == x || i == (x + width - 1))
+ draw_line_vert(i, y+1, height-2, color, mode);
+ else
+ draw_line_vert(i, y, height, color, mode);
+ }
+}
+
+/** \brief Draw character with color and mode.
+ Draw character c using color and draw mode at x,y.
+*/
+void draw_char(uint8_t x, uint8_t y, uint8_t c, uint8_t color, uint8_t mode, uint8_t font) {
+ // TODO - New routine to take font of any height, at the moment limited to font height in multiple of 8 pixels
+
+ uint8_t rowsToDraw,row, tempC;
+ uint8_t i,j,temp;
+ uint16_t charPerBitmapRow,charColPositionOnBitmap,charRowPositionOnBitmap,charBitmapStartPosition;
+
+ if ((font>=TOTALFONTS) || (font<0))
+ return;
+
+ uint8_t fontType = font;
+ uint8_t fontWidth = pgm_read_byte(fonts_pointer[fontType]+0);
+ uint8_t fontHeight = pgm_read_byte(fonts_pointer[fontType]+1);
+ uint8_t fontStartChar = pgm_read_byte(fonts_pointer[fontType]+2);
+ uint8_t fontTotalChar = pgm_read_byte(fonts_pointer[fontType]+3);
+ uint16_t fontMapWidth = (pgm_read_byte(fonts_pointer[fontType]+4)*100)+pgm_read_byte(fonts_pointer[fontType]+5); // two bytes values into integer 16
+
+ if ((c<fontStartChar) || (c>(fontStartChar+fontTotalChar-1))) // no bitmap for the required c
+ return;
+
+ tempC=c-fontStartChar;
+
+ // each row (in datasheet is call page) is 8 bits high, 16 bit high character will have 2 rows to be drawn
+ rowsToDraw=fontHeight/8; // 8 is LCD's page size, see SSD1306 datasheet
+ if (rowsToDraw<=1) rowsToDraw=1;
+
+ // the following draw function can draw anywhere on the screen, but SLOW pixel by pixel draw
+ if (rowsToDraw==1) {
+ for (i=0;i<fontWidth+1;i++) {
+ if (i==fontWidth) // this is done in a weird way because for 5x7 font, there is no margin, this code add a margin after col 5
+ temp=0;
+ else
+ temp=pgm_read_byte(fonts_pointer[fontType]+FONTHEADERSIZE+(tempC*fontWidth)+i);
+
+ for (j=0;j<8;j++) { // 8 is the LCD's page height (see datasheet for explanation)
+ if (temp & 0x1) {
+ draw_pixel(x+i, y+j, color,mode);
+ }
+ else {
+ draw_pixel(x+i, y+j, !color,mode);
+ }
+
+ temp >>=1;
+ }
+ }
+ return;
+ }
+
+ // font height over 8 bit
+ // take character "0" ASCII 48 as example
+ charPerBitmapRow = fontMapWidth/fontWidth; // 256/8 =32 char per row
+ charColPositionOnBitmap = tempC % charPerBitmapRow; // =16
+ charRowPositionOnBitmap = (int)(tempC/charPerBitmapRow); // =1
+ charBitmapStartPosition = (charRowPositionOnBitmap * fontMapWidth * (fontHeight/8)) + (charColPositionOnBitmap * fontWidth) ;
+
+ // each row on LCD is 8 bit height (see datasheet for explanation)
+ for(row=0;row<rowsToDraw;row++) {
+ for (i=0; i<fontWidth;i++) {
+ temp=pgm_read_byte(fonts_pointer[fontType]+FONTHEADERSIZE+(charBitmapStartPosition+i+(row*fontMapWidth)));
+ for (j=0;j<8;j++) { // 8 is the LCD's page height (see datasheet for explanation)
+ if (temp & 0x1) {
+ draw_pixel(x+i,y+j+(row*8), color, mode);
+ }
+ else {
+ draw_pixel(x+i,y+j+(row*8), !color, mode);
+ }
+ temp >>=1;
+ }
+ }
+ }
+
+}
+
+void draw_string(uint8_t x, uint8_t y, char * string, uint8_t color, uint8_t mode, uint8_t font) {
+
+ if ((font>=TOTALFONTS) || (font<0))
+ return;
+
+ uint8_t fontType = font;
+ uint8_t fontWidth = pgm_read_byte(fonts_pointer[fontType]+0);
+
+ uint8_t cur_x = x;
+ for (int i = 0; i < strlen(string); i++) {
+ draw_char(cur_x, y, string[i], color, mode, font);
+ cur_x += fontWidth + 1;
+ }
+
+}
diff --git a/drivers/qwiic/micro_oled.h b/drivers/qwiic/micro_oled.h
new file mode 100644
index 0000000000..5d6a1029ed
--- /dev/null
+++ b/drivers/qwiic/micro_oled.h
@@ -0,0 +1,134 @@
+/* Jim Lindblom @ SparkFun Electronics
+ * October 26, 2014
+ * https://github.com/sparkfun/Micro_OLED_Breakout/tree/master/Firmware/Arduino/libraries/SFE_MicroOLED
+ *
+ * Modified by:
+ * Emil Varughese @ Edwin Robotics Pvt. Ltd.
+ * July 27, 2015
+ * https://github.com/emil01/SparkFun_Micro_OLED_Arduino_Library/
+ *
+ * This code was heavily based around the MicroView library, written by GeekAmmo
+ * (https://github.com/geekammo/MicroView-Arduino-Library).
+ *
+ * Adapted for QMK by:
+ * Jack Humbert <jack.humb@gmail.com>
+ * October 11, 2018
+ *
+ * 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 3 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 "qwiic.h"
+
+void micro_oled_init(void);
+
+void send_command(uint8_t command);
+void send_data(uint8_t data);
+void set_page_address(uint8_t address);
+void set_column_address(uint8_t address);
+void clear_screen(void);
+void clear_buffer(void);
+void send_buffer(void);
+void draw_pixel(uint8_t x, uint8_t y, uint8_t color, uint8_t mode);
+void draw_line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint8_t color, uint8_t mode);
+void draw_line_hori(uint8_t x, uint8_t y, uint8_t width, uint8_t color, uint8_t mode);
+void draw_line_vert(uint8_t x, uint8_t y, uint8_t height, bool color, uint8_t mode);
+void draw_rect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color, uint8_t mode);
+void draw_rect_soft(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color, uint8_t mode);
+void draw_rect_filled(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color, uint8_t mode);
+void draw_rect_filled_soft(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color, uint8_t mode);
+void draw_char(uint8_t x, uint8_t y, uint8_t c, uint8_t color, uint8_t mode, uint8_t font);
+void draw_string(uint8_t x, uint8_t y, char * string, uint8_t color, uint8_t mode, uint8_t font);
+
+#define I2C_ADDRESS_SA0_0 0b0111100
+#ifndef I2C_ADDRESS_SA0_1
+#define I2C_ADDRESS_SA0_1 0b0111101
+#endif
+#define I2C_COMMAND 0x00
+#define I2C_DATA 0x40
+#define PIXEL_OFF 0
+#define PIXEL_ON 1
+
+#ifndef LCDWIDTH
+#define LCDWIDTH 64
+#endif
+#ifndef LCDWIDTH
+#define LCDHEIGHT 48
+#endif
+#define FONTHEADERSIZE 6
+
+#define NORM 0
+#define XOR 1
+
+#define PAGE 0
+#define ALL 1
+
+#define WIDGETSTYLE0 0
+#define WIDGETSTYLE1 1
+#define WIDGETSTYLE2 2
+
+#define SETCONTRAST 0x81
+#define DISPLAYALLONRESUME 0xA4
+#define DISPLAYALLON 0xA5
+#define NORMALDISPLAY 0xA6
+#define INVERTDISPLAY 0xA7
+#define DISPLAYOFF 0xAE
+#define DISPLAYON 0xAF
+#define SETDISPLAYOFFSET 0xD3
+#define SETCOMPINS 0xDA
+#define SETVCOMDESELECT 0xDB
+#define SETDISPLAYCLOCKDIV 0xD5
+#define SETPRECHARGE 0xD9
+#define SETMULTIPLEX 0xA8
+#define SETLOWCOLUMN 0x00
+#define SETHIGHCOLUMN 0x10
+#define SETSTARTLINE 0x40
+#define MEMORYMODE 0x20
+#define COMSCANINC 0xC0
+#define COMSCANDEC 0xC8
+#define SEGREMAP 0xA0
+#define CHARGEPUMP 0x8D
+#define EXTERNALVCC 0x01
+#define SWITCHCAPVCC 0x02
+
+// Scroll
+#define ACTIVATESCROLL 0x2F
+#define DEACTIVATESCROLL 0x2E
+#define SETVERTICALSCROLLAREA 0xA3
+#define RIGHTHORIZONTALSCROLL 0x26
+#define LEFT_HORIZONTALSCROLL 0x27
+#define VERTICALRIGHTHORIZONTALSCROLL 0x29
+#define VERTICALLEFTHORIZONTALSCROLL 0x2A
+
+typedef enum CMD {
+ CMD_CLEAR, //0
+ CMD_INVERT, //1
+ CMD_CONTRAST, //2
+ CMD_DISPLAY, //3
+ CMD_SETCURSOR, //4
+ CMD_PIXEL, //5
+ CMD_LINE, //6
+ CMD_LINEH, //7
+ CMD_LINEV, //8
+ CMD_RECT, //9
+ CMD_RECTFILL, //10
+ CMD_CIRCLE, //11
+ CMD_CIRCLEFILL, //12
+ CMD_DRAWCHAR, //13
+ CMD_DRAWBITMAP, //14
+ CMD_GETLCDWIDTH, //15
+ CMD_GETLCDHEIGHT, //16
+ CMD_SETCOLOR, //17
+ CMD_SETDRAWMODE //18
+} commCommand_t; \ No newline at end of file
diff --git a/drivers/qwiic/qwiic.c b/drivers/qwiic/qwiic.c
new file mode 100644
index 0000000000..9047919927
--- /dev/null
+++ b/drivers/qwiic/qwiic.c
@@ -0,0 +1,31 @@
+/* Copyright 2018 Jack Humbert <jack.humb@gmail.com>
+ *
+ * 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 "qwiic.h"
+
+void qwiic_init(void) {
+ #ifdef QWIIC_JOYSTIIC_ENABLE
+ joystiic_init();
+ #endif
+ #ifdef QWIIC_MICRO_OLED_ENABLE
+ micro_oled_init();
+ #endif
+}
+
+void qwiic_task(void) {
+ #ifdef QWIIC_JOYSTIIC_ENABLE
+ joystiic_task();
+ #endif
+}
diff --git a/drivers/qwiic/qwiic.h b/drivers/qwiic/qwiic.h
new file mode 100644
index 0000000000..160fb28dfd
--- /dev/null
+++ b/drivers/qwiic/qwiic.h
@@ -0,0 +1,28 @@
+/* Copyright 2018 Jack Humbert <jack.humb@gmail.com>
+ *
+ * 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 "i2c_master.h"
+
+#ifdef QWIIC_JOYSTIIC_ENABLE
+ #include "joystiic.h"
+#endif
+#ifdef QWIIC_MICRO_OLED_ENABLE
+ #include "micro_oled.h"
+#endif
+
+void qwiic_init(void);
+void qwiic_task(void);
diff --git a/drivers/qwiic/qwiic.mk b/drivers/qwiic/qwiic.mk
new file mode 100644
index 0000000000..4ae2d78e3e
--- /dev/null
+++ b/drivers/qwiic/qwiic.mk
@@ -0,0 +1,18 @@
+ifneq ($(strip $(QWIIC_ENABLE)),)
+ COMMON_VPATH += $(DRIVER_PATH)/qwiic
+ OPT_DEFS += -DQWIIC_ENABLE
+ SRC += qwiic.c
+ ifeq ($(filter "i2c_master.c", $(SRC)),)
+ SRC += i2c_master.c
+ endif
+endif
+
+ifneq ($(filter JOYSTIIC, $(QWIIC_ENABLE)),)
+ OPT_DEFS += -DQWIIC_JOYSTIIC_ENABLE
+ SRC += joystiic.c
+endif
+
+ifneq ($(filter MICRO_OLED, $(QWIIC_ENABLE)),)
+ OPT_DEFS += -DQWIIC_MICRO_OLED_ENABLE
+ SRC += micro_oled.c
+endif
diff --git a/drivers/qwiic/util/font5x7.h b/drivers/qwiic/util/font5x7.h
new file mode 100644
index 0000000000..0bad206b7c
--- /dev/null
+++ b/drivers/qwiic/util/font5x7.h
@@ -0,0 +1,288 @@
+/******************************************************************************
+font5x7.h
+Definition for small font
+
+This file was imported from the MicroView library, written by GeekAmmo
+(https://github.com/geekammo/MicroView-Arduino-Library), and released under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 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/>.
+
+Modified by:
+Emil Varughese @ Edwin Robotics Pvt. Ltd.
+July 27, 2015
+https://github.com/emil01/SparkFun_Micro_OLED_Arduino_Library/
+
+******************************************************************************/
+#pragma once
+
+#include "progmem.h"
+
+// Standard ASCII 5x7 font
+static const unsigned char font5x7[] PROGMEM = {
+ // first row defines - FONTWIDTH, FONTHEIGHT, ASCII START CHAR, TOTAL CHARACTERS, FONT MAP WIDTH HIGH, FONT MAP WIDTH LOW (2,56 meaning 256)
+ 5,8,0,255,12,75,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3E, 0x5B, 0x4F, 0x5B, 0x3E,
+ 0x3E, 0x6B, 0x4F, 0x6B, 0x3E,
+ 0x1C, 0x3E, 0x7C, 0x3E, 0x1C,
+ 0x18, 0x3C, 0x7E, 0x3C, 0x18,
+ 0x1C, 0x57, 0x7D, 0x57, 0x1C,
+ 0x1C, 0x5E, 0x7F, 0x5E, 0x1C,
+ 0x00, 0x18, 0x3C, 0x18, 0x00,
+ 0xFF, 0xE7, 0xC3, 0xE7, 0xFF,
+ 0x00, 0x18, 0x24, 0x18, 0x00,
+ 0xFF, 0xE7, 0xDB, 0xE7, 0xFF,
+ 0x30, 0x48, 0x3A, 0x06, 0x0E,
+ 0x26, 0x29, 0x79, 0x29, 0x26,
+ 0x40, 0x7F, 0x05, 0x05, 0x07,
+ 0x40, 0x7F, 0x05, 0x25, 0x3F,
+ 0x5A, 0x3C, 0xE7, 0x3C, 0x5A,
+ 0x7F, 0x3E, 0x1C, 0x1C, 0x08,
+ 0x08, 0x1C, 0x1C, 0x3E, 0x7F,
+ 0x14, 0x22, 0x7F, 0x22, 0x14,
+ 0x5F, 0x5F, 0x00, 0x5F, 0x5F,
+ 0x06, 0x09, 0x7F, 0x01, 0x7F,
+ 0x00, 0x66, 0x89, 0x95, 0x6A,
+ 0x60, 0x60, 0x60, 0x60, 0x60,
+ 0x94, 0xA2, 0xFF, 0xA2, 0x94,
+ 0x08, 0x04, 0x7E, 0x04, 0x08,
+ 0x10, 0x20, 0x7E, 0x20, 0x10,
+ 0x08, 0x08, 0x2A, 0x1C, 0x08,
+ 0x08, 0x1C, 0x2A, 0x08, 0x08,
+ 0x1E, 0x10, 0x10, 0x10, 0x10,
+ 0x0C, 0x1E, 0x0C, 0x1E, 0x0C,
+ 0x30, 0x38, 0x3E, 0x38, 0x30,
+ 0x06, 0x0E, 0x3E, 0x0E, 0x06,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x5F, 0x00, 0x00,
+ 0x00, 0x07, 0x00, 0x07, 0x00,
+ 0x14, 0x7F, 0x14, 0x7F, 0x14,
+ 0x24, 0x2A, 0x7F, 0x2A, 0x12,
+ 0x23, 0x13, 0x08, 0x64, 0x62,
+ 0x36, 0x49, 0x56, 0x20, 0x50,
+ 0x00, 0x08, 0x07, 0x03, 0x00,
+ 0x00, 0x1C, 0x22, 0x41, 0x00,
+ 0x00, 0x41, 0x22, 0x1C, 0x00,
+ 0x2A, 0x1C, 0x7F, 0x1C, 0x2A,
+ 0x08, 0x08, 0x3E, 0x08, 0x08,
+ 0x00, 0x80, 0x70, 0x30, 0x00,
+ 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x00, 0x00, 0x60, 0x60, 0x00,
+ 0x20, 0x10, 0x08, 0x04, 0x02,
+ 0x3E, 0x51, 0x49, 0x45, 0x3E,
+ 0x00, 0x42, 0x7F, 0x40, 0x00,
+ 0x72, 0x49, 0x49, 0x49, 0x46,
+ 0x21, 0x41, 0x49, 0x4D, 0x33,
+ 0x18, 0x14, 0x12, 0x7F, 0x10,
+ 0x27, 0x45, 0x45, 0x45, 0x39,
+ 0x3C, 0x4A, 0x49, 0x49, 0x31,
+ 0x41, 0x21, 0x11, 0x09, 0x07,
+ 0x36, 0x49, 0x49, 0x49, 0x36,
+ 0x46, 0x49, 0x49, 0x29, 0x1E,
+ 0x00, 0x00, 0x14, 0x00, 0x00,
+ 0x00, 0x40, 0x34, 0x00, 0x00,
+ 0x00, 0x08, 0x14, 0x22, 0x41,
+ 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x00, 0x41, 0x22, 0x14, 0x08,
+ 0x02, 0x01, 0x59, 0x09, 0x06,
+ 0x3E, 0x41, 0x5D, 0x59, 0x4E,
+ 0x7C, 0x12, 0x11, 0x12, 0x7C,
+ 0x7F, 0x49, 0x49, 0x49, 0x36,
+ 0x3E, 0x41, 0x41, 0x41, 0x22,
+ 0x7F, 0x41, 0x41, 0x41, 0x3E,
+ 0x7F, 0x49, 0x49, 0x49, 0x41,
+ 0x7F, 0x09, 0x09, 0x09, 0x01,
+ 0x3E, 0x41, 0x41, 0x51, 0x73,
+ 0x7F, 0x08, 0x08, 0x08, 0x7F,
+ 0x00, 0x41, 0x7F, 0x41, 0x00,
+ 0x20, 0x40, 0x41, 0x3F, 0x01,
+ 0x7F, 0x08, 0x14, 0x22, 0x41,
+ 0x7F, 0x40, 0x40, 0x40, 0x40,
+ 0x7F, 0x02, 0x1C, 0x02, 0x7F,
+ 0x7F, 0x04, 0x08, 0x10, 0x7F,
+ 0x3E, 0x41, 0x41, 0x41, 0x3E,
+ 0x7F, 0x09, 0x09, 0x09, 0x06,
+ 0x3E, 0x41, 0x51, 0x21, 0x5E,
+ 0x7F, 0x09, 0x19, 0x29, 0x46,
+ 0x26, 0x49, 0x49, 0x49, 0x32,
+ 0x03, 0x01, 0x7F, 0x01, 0x03,
+ 0x3F, 0x40, 0x40, 0x40, 0x3F,
+ 0x1F, 0x20, 0x40, 0x20, 0x1F,
+ 0x3F, 0x40, 0x38, 0x40, 0x3F,
+ 0x63, 0x14, 0x08, 0x14, 0x63,
+ 0x03, 0x04, 0x78, 0x04, 0x03,
+ 0x61, 0x59, 0x49, 0x4D, 0x43,
+ 0x00, 0x7F, 0x41, 0x41, 0x41,
+ 0x02, 0x04, 0x08, 0x10, 0x20,
+ 0x00, 0x41, 0x41, 0x41, 0x7F,
+ 0x04, 0x02, 0x01, 0x02, 0x04,
+ 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x00, 0x03, 0x07, 0x08, 0x00,
+ 0x20, 0x54, 0x54, 0x78, 0x40,
+ 0x7F, 0x28, 0x44, 0x44, 0x38,
+ 0x38, 0x44, 0x44, 0x44, 0x28,
+ 0x38, 0x44, 0x44, 0x28, 0x7F,
+ 0x38, 0x54, 0x54, 0x54, 0x18,
+ 0x00, 0x08, 0x7E, 0x09, 0x02,
+ 0x18, 0xA4, 0xA4, 0x9C, 0x78,
+ 0x7F, 0x08, 0x04, 0x04, 0x78,
+ 0x00, 0x44, 0x7D, 0x40, 0x00,
+ 0x20, 0x40, 0x40, 0x3D, 0x00,
+ 0x7F, 0x10, 0x28, 0x44, 0x00,
+ 0x00, 0x41, 0x7F, 0x40, 0x00,
+ 0x7C, 0x04, 0x78, 0x04, 0x78,
+ 0x7C, 0x08, 0x04, 0x04, 0x78,
+ 0x38, 0x44, 0x44, 0x44, 0x38,
+ 0xFC, 0x18, 0x24, 0x24, 0x18,
+ 0x18, 0x24, 0x24, 0x18, 0xFC,
+ 0x7C, 0x08, 0x04, 0x04, 0x08,
+ 0x48, 0x54, 0x54, 0x54, 0x24,
+ 0x04, 0x04, 0x3F, 0x44, 0x24,
+ 0x3C, 0x40, 0x40, 0x20, 0x7C,
+ 0x1C, 0x20, 0x40, 0x20, 0x1C,
+ 0x3C, 0x40, 0x30, 0x40, 0x3C,
+ 0x44, 0x28, 0x10, 0x28, 0x44,
+ 0x4C, 0x90, 0x90, 0x90, 0x7C,
+ 0x44, 0x64, 0x54, 0x4C, 0x44,
+ 0x00, 0x08, 0x36, 0x41, 0x00,
+ 0x00, 0x00, 0x77, 0x00, 0x00,
+ 0x00, 0x41, 0x36, 0x08, 0x00,
+ 0x02, 0x01, 0x02, 0x04, 0x02,
+ 0x3C, 0x26, 0x23, 0x26, 0x3C,
+ 0x1E, 0xA1, 0xA1, 0x61, 0x12,
+ 0x3A, 0x40, 0x40, 0x20, 0x7A,
+ 0x38, 0x54, 0x54, 0x55, 0x59,
+ 0x21, 0x55, 0x55, 0x79, 0x41,
+ 0x21, 0x54, 0x54, 0x78, 0x41,
+ 0x21, 0x55, 0x54, 0x78, 0x40,
+ 0x20, 0x54, 0x55, 0x79, 0x40,
+ 0x0C, 0x1E, 0x52, 0x72, 0x12,
+ 0x39, 0x55, 0x55, 0x55, 0x59,
+ 0x39, 0x54, 0x54, 0x54, 0x59,
+ 0x39, 0x55, 0x54, 0x54, 0x58,
+ 0x00, 0x00, 0x45, 0x7C, 0x41,
+ 0x00, 0x02, 0x45, 0x7D, 0x42,
+ 0x00, 0x01, 0x45, 0x7C, 0x40,
+ 0xF0, 0x29, 0x24, 0x29, 0xF0,
+ 0xF0, 0x28, 0x25, 0x28, 0xF0,
+ 0x7C, 0x54, 0x55, 0x45, 0x00,
+ 0x20, 0x54, 0x54, 0x7C, 0x54,
+ 0x7C, 0x0A, 0x09, 0x7F, 0x49,
+ 0x32, 0x49, 0x49, 0x49, 0x32,
+ 0x32, 0x48, 0x48, 0x48, 0x32,
+ 0x32, 0x4A, 0x48, 0x48, 0x30,
+ 0x3A, 0x41, 0x41, 0x21, 0x7A,
+ 0x3A, 0x42, 0x40, 0x20, 0x78,
+ 0x00, 0x9D, 0xA0, 0xA0, 0x7D,
+ 0x39, 0x44, 0x44, 0x44, 0x39,
+ 0x3D, 0x40, 0x40, 0x40, 0x3D,
+ 0x3C, 0x24, 0xFF, 0x24, 0x24,
+ 0x48, 0x7E, 0x49, 0x43, 0x66,
+ 0x2B, 0x2F, 0xFC, 0x2F, 0x2B,
+ 0xFF, 0x09, 0x29, 0xF6, 0x20,
+ 0xC0, 0x88, 0x7E, 0x09, 0x03,
+ 0x20, 0x54, 0x54, 0x79, 0x41,
+ 0x00, 0x00, 0x44, 0x7D, 0x41,
+ 0x30, 0x48, 0x48, 0x4A, 0x32,
+ 0x38, 0x40, 0x40, 0x22, 0x7A,
+ 0x00, 0x7A, 0x0A, 0x0A, 0x72,
+ 0x7D, 0x0D, 0x19, 0x31, 0x7D,
+ 0x26, 0x29, 0x29, 0x2F, 0x28,
+ 0x26, 0x29, 0x29, 0x29, 0x26,
+ 0x30, 0x48, 0x4D, 0x40, 0x20,
+ 0x38, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x38,
+ 0x2F, 0x10, 0xC8, 0xAC, 0xBA,
+ 0x2F, 0x10, 0x28, 0x34, 0xFA,
+ 0x00, 0x00, 0x7B, 0x00, 0x00,
+ 0x08, 0x14, 0x2A, 0x14, 0x22,
+ 0x22, 0x14, 0x2A, 0x14, 0x08,
+ 0xAA, 0x00, 0x55, 0x00, 0xAA,
+ 0xAA, 0x55, 0xAA, 0x55, 0xAA,
+ 0x00, 0x00, 0x00, 0xFF, 0x00,
+ 0x10, 0x10, 0x10, 0xFF, 0x00,
+ 0x14, 0x14, 0x14, 0xFF, 0x00,
+ 0x10, 0x10, 0xFF, 0x00, 0xFF,
+ 0x10, 0x10, 0xF0, 0x10, 0xF0,
+ 0x14, 0x14, 0x14, 0xFC, 0x00,
+ 0x14, 0x14, 0xF7, 0x00, 0xFF,
+ 0x00, 0x00, 0xFF, 0x00, 0xFF,
+ 0x14, 0x14, 0xF4, 0x04, 0xFC,
+ 0x14, 0x14, 0x17, 0x10, 0x1F,
+ 0x10, 0x10, 0x1F, 0x10, 0x1F,
+ 0x14, 0x14, 0x14, 0x1F, 0x00,
+ 0x10, 0x10, 0x10, 0xF0, 0x00,
+ 0x00, 0x00, 0x00, 0x1F, 0x10,
+ 0x10, 0x10, 0x10, 0x1F, 0x10,
+ 0x10, 0x10, 0x10, 0xF0, 0x10,
+ 0x00, 0x00, 0x00, 0xFF, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0xFF, 0x10,
+ 0x00, 0x00, 0x00, 0xFF, 0x14,
+ 0x00, 0x00, 0xFF, 0x00, 0xFF,
+ 0x00, 0x00, 0x1F, 0x10, 0x17,
+ 0x00, 0x00, 0xFC, 0x04, 0xF4,
+ 0x14, 0x14, 0x17, 0x10, 0x17,
+ 0x14, 0x14, 0xF4, 0x04, 0xF4,
+ 0x00, 0x00, 0xFF, 0x00, 0xF7,
+ 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0xF7, 0x00, 0xF7,
+ 0x14, 0x14, 0x14, 0x17, 0x14,
+ 0x10, 0x10, 0x1F, 0x10, 0x1F,
+ 0x14, 0x14, 0x14, 0xF4, 0x14,
+ 0x10, 0x10, 0xF0, 0x10, 0xF0,
+ 0x00, 0x00, 0x1F, 0x10, 0x1F,
+ 0x00, 0x00, 0x00, 0x1F, 0x14,
+ 0x00, 0x00, 0x00, 0xFC, 0x14,
+ 0x00, 0x00, 0xF0, 0x10, 0xF0,
+ 0x10, 0x10, 0xFF, 0x10, 0xFF,
+ 0x14, 0x14, 0x14, 0xFF, 0x14,
+ 0x10, 0x10, 0x10, 0x1F, 0x00,
+ 0x00, 0x00, 0x00, 0xF0, 0x10,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
+ 0xFF, 0xFF, 0xFF, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xFF, 0xFF,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x38, 0x44, 0x44, 0x38, 0x44,
+ 0x7C, 0x2A, 0x2A, 0x3E, 0x14,
+ 0x7E, 0x02, 0x02, 0x06, 0x06,
+ 0x02, 0x7E, 0x02, 0x7E, 0x02,
+ 0x63, 0x55, 0x49, 0x41, 0x63,
+ 0x38, 0x44, 0x44, 0x3C, 0x04,
+ 0x40, 0x7E, 0x20, 0x1E, 0x20,
+ 0x06, 0x02, 0x7E, 0x02, 0x02,
+ 0x99, 0xA5, 0xE7, 0xA5, 0x99,
+ 0x1C, 0x2A, 0x49, 0x2A, 0x1C,
+ 0x4C, 0x72, 0x01, 0x72, 0x4C,
+ 0x30, 0x4A, 0x4D, 0x4D, 0x30,
+ 0x30, 0x48, 0x78, 0x48, 0x30,
+ 0xBC, 0x62, 0x5A, 0x46, 0x3D,
+ 0x3E, 0x49, 0x49, 0x49, 0x00,
+ 0x7E, 0x01, 0x01, 0x01, 0x7E,
+ 0x2A, 0x2A, 0x2A, 0x2A, 0x2A,
+ 0x44, 0x44, 0x5F, 0x44, 0x44,
+ 0x40, 0x51, 0x4A, 0x44, 0x40,
+ 0x40, 0x44, 0x4A, 0x51, 0x40,
+ 0x00, 0x00, 0xFF, 0x01, 0x03,
+ 0xE0, 0x80, 0xFF, 0x00, 0x00,
+ 0x08, 0x08, 0x6B, 0x6B, 0x08,
+ 0x36, 0x12, 0x36, 0x24, 0x36,
+ 0x06, 0x0F, 0x09, 0x0F, 0x06,
+ 0x00, 0x00, 0x18, 0x18, 0x00,
+ 0x00, 0x00, 0x10, 0x10, 0x00,
+ 0x30, 0x40, 0xFF, 0x01, 0x01,
+ 0x00, 0x1F, 0x01, 0x01, 0x1E,
+ 0x00, 0x19, 0x1D, 0x17, 0x12,
+ 0x00, 0x3C, 0x3C, 0x3C, 0x3C,
+ 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/drivers/qwiic/util/font8x16.h b/drivers/qwiic/util/font8x16.h
new file mode 100644
index 0000000000..c070e4ec8c
--- /dev/null
+++ b/drivers/qwiic/util/font8x16.h
@@ -0,0 +1,127 @@
+/******************************************************************************
+font8x16.h
+Definition for medium font
+
+This file was imported from the MicroView library, written by GeekAmmo
+(https://github.com/geekammo/MicroView-Arduino-Library), and released under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 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/>.
+
+Modified by:
+Emil Varughese @ Edwin Robotics Pvt. Ltd.
+July 27, 2015
+https://github.com/emil01/SparkFun_Micro_OLED_Arduino_Library/
+******************************************************************************/
+#pragma once
+
+#include "progmem.h"
+
+static const unsigned char font8x16[] PROGMEM = {
+ // first row defines - FONTWIDTH, FONTHEIGHT, ASCII START CHAR, TOTAL CHARACTERS, FONT MAP WIDTH HIGH, FONT MAP WIDTH LOW (2,56 meaning 256)
+ 8,16,32,96,2,56,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xBE, 0x90, 0xD0, 0xBE, 0x90, 0x00,
+ 0x00, 0x1C, 0x62, 0xFF, 0xC2, 0x80, 0x00, 0x00, 0x0C, 0x12, 0x92, 0x4C, 0xB0, 0x88, 0x06, 0x00,
+ 0x80, 0x7C, 0x62, 0xB2, 0x1C, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x0E, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xE0, 0x18, 0x04, 0x02, 0x02, 0x00, 0x00, 0x00, 0x02, 0x02, 0x04, 0x18, 0xE0, 0x00, 0x00,
+ 0x00, 0x24, 0x18, 0x7E, 0x18, 0x24, 0x00, 0x00, 0x80, 0x80, 0x80, 0xF0, 0x80, 0x80, 0x80, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x18, 0x06, 0x00, 0x00,
+ 0xF8, 0x04, 0xC2, 0x32, 0x0C, 0xF8, 0x00, 0x00, 0x00, 0x04, 0x04, 0xFE, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x82, 0x42, 0x22, 0x1C, 0x00, 0x00, 0x00, 0x02, 0x22, 0x22, 0x22, 0xDC, 0x00, 0x00,
+ 0xC0, 0xA0, 0x98, 0x84, 0xFE, 0x80, 0x80, 0x00, 0x00, 0x1E, 0x12, 0x12, 0x22, 0xC2, 0x00, 0x00,
+ 0xF8, 0x44, 0x22, 0x22, 0x22, 0xC0, 0x00, 0x00, 0x00, 0x02, 0x02, 0xC2, 0x32, 0x0A, 0x06, 0x00,
+ 0x00, 0x8C, 0x52, 0x22, 0x52, 0x8C, 0x00, 0x00, 0x3C, 0x42, 0x42, 0x42, 0x26, 0xF8, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x80, 0x80, 0x40, 0x40, 0x20, 0x20, 0x10, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00,
+ 0x10, 0x20, 0x20, 0x40, 0x40, 0x80, 0x80, 0x00, 0x00, 0x02, 0x82, 0x42, 0x22, 0x1C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x04, 0x0F, 0x04, 0x03, 0x00, 0x00, 0x04, 0x02, 0x01, 0x03, 0x04, 0x04, 0x03, 0x00,
+ 0x03, 0x04, 0x04, 0x04, 0x05, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x06, 0x08, 0x10, 0x10, 0x00, 0x00, 0x00, 0x10, 0x10, 0x08, 0x06, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x16, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x03, 0x04, 0x04, 0x02, 0x01, 0x00, 0x00, 0x00, 0x04, 0x04, 0x07, 0x04, 0x04, 0x00, 0x00,
+ 0x00, 0x07, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x03, 0x00, 0x00,
+ 0x01, 0x02, 0x04, 0x04, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x04, 0x04, 0x04, 0x03, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x02, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x0E, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x04, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00,
+ 0x04, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xF8, 0x04, 0x72, 0x8A, 0xFA, 0x84, 0x78, 0x00, 0x00, 0xC0, 0x38, 0x06, 0x38, 0xC0, 0x00, 0x00,
+ 0x00, 0xFE, 0x22, 0x22, 0x22, 0xDC, 0x00, 0x00, 0xF8, 0x04, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00,
+ 0xFE, 0x02, 0x02, 0x02, 0x04, 0xF8, 0x00, 0x00, 0x00, 0xFE, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00,
+ 0x00, 0xFE, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0xF8, 0x04, 0x02, 0x02, 0x22, 0xE2, 0x00, 0x00,
+ 0xFE, 0x20, 0x20, 0x20, 0x20, 0xFE, 0x00, 0x00, 0x00, 0x02, 0x02, 0xFE, 0x02, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x02, 0xFE, 0x00, 0x00, 0xFE, 0x40, 0xB0, 0x08, 0x04, 0x02, 0x00, 0x00,
+ 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x0C, 0x70, 0x80, 0x70, 0x0C, 0xFE, 0x00,
+ 0xFE, 0x0C, 0x30, 0xC0, 0x00, 0xFE, 0x00, 0x00, 0xF8, 0x04, 0x02, 0x02, 0x04, 0xF8, 0x00, 0x00,
+ 0xFE, 0x42, 0x42, 0x42, 0x22, 0x1C, 0x00, 0x00, 0xF8, 0x04, 0x02, 0x02, 0x04, 0xF8, 0x00, 0x00,
+ 0x00, 0xFE, 0x42, 0x42, 0xA2, 0x1C, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x42, 0x42, 0x80, 0x00, 0x00,
+ 0x02, 0x02, 0x02, 0xFE, 0x02, 0x02, 0x02, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00,
+ 0x06, 0x38, 0xC0, 0x00, 0xC0, 0x38, 0x06, 0x00, 0x3E, 0xC0, 0xF0, 0x0E, 0xF0, 0xC0, 0x3E, 0x00,
+ 0x00, 0x06, 0x98, 0x60, 0x98, 0x06, 0x00, 0x00, 0x00, 0x06, 0x18, 0xE0, 0x18, 0x06, 0x00, 0x00,
+ 0x02, 0x02, 0xC2, 0x32, 0x0A, 0x06, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x02, 0x02, 0x02, 0x02, 0x00,
+ 0x00, 0x06, 0x18, 0x60, 0x80, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0xFE, 0x00, 0x00, 0x00,
+ 0x40, 0x30, 0x0C, 0x0C, 0x30, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x06, 0x01, 0x01, 0x01, 0x01, 0x01, 0x06, 0x00,
+ 0x00, 0x07, 0x04, 0x04, 0x04, 0x03, 0x00, 0x00, 0x01, 0x02, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00,
+ 0x07, 0x04, 0x04, 0x04, 0x02, 0x01, 0x00, 0x00, 0x00, 0x07, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00,
+ 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x04, 0x04, 0x07, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x04, 0x07, 0x04, 0x04, 0x00, 0x00,
+ 0x00, 0x04, 0x04, 0x04, 0x04, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x01, 0x02, 0x04, 0x00, 0x00,
+ 0x00, 0x07, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, 0x01, 0x02, 0x04, 0x04, 0x02, 0x01, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x0C, 0x12, 0x11, 0x10, 0x00,
+ 0x00, 0x07, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
+ 0x00, 0x06, 0x01, 0x00, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x05, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x10, 0x10, 0x10, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x00, 0x10, 0x10, 0x10, 0x10, 0x1F, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x10, 0x10, 0x10, 0xF0, 0x00, 0x00,
+ 0x00, 0xFE, 0x20, 0x10, 0x10, 0xE0, 0x00, 0x00, 0x00, 0xE0, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00,
+ 0x00, 0xE0, 0x10, 0x10, 0x10, 0xFE, 0x00, 0x00, 0x00, 0xE0, 0x90, 0x90, 0x90, 0xE0, 0x00, 0x00,
+ 0x00, 0x20, 0xFC, 0x22, 0x22, 0x22, 0x02, 0x00, 0x00, 0xE0, 0x10, 0x10, 0x10, 0xF0, 0x00, 0x00,
+ 0x00, 0xFE, 0x20, 0x10, 0x10, 0xE0, 0x00, 0x00, 0x10, 0x10, 0xF2, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x10, 0x10, 0xF2, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x80, 0x40, 0x20, 0x10, 0x00, 0x00,
+ 0x00, 0x02, 0x02, 0xFE, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x20, 0x10, 0xF0, 0x20, 0x10, 0xF0, 0x00,
+ 0x00, 0xF0, 0x20, 0x10, 0x10, 0xE0, 0x00, 0x00, 0x00, 0xE0, 0x10, 0x10, 0x10, 0xE0, 0x00, 0x00,
+ 0x00, 0xF0, 0x20, 0x10, 0x10, 0xE0, 0x00, 0x00, 0x00, 0xE0, 0x10, 0x10, 0x10, 0xF0, 0x00, 0x00,
+ 0x00, 0xF0, 0x20, 0x10, 0x10, 0x70, 0x00, 0x00, 0x00, 0x60, 0x90, 0x90, 0x90, 0x20, 0x00, 0x00,
+ 0x00, 0x20, 0x20, 0xFC, 0x20, 0x20, 0x20, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00,
+ 0x00, 0x70, 0x80, 0x00, 0x80, 0x70, 0x00, 0x00, 0xF0, 0x00, 0xC0, 0x30, 0xC0, 0x00, 0xF0, 0x00,
+ 0x00, 0x30, 0xC0, 0xC0, 0x30, 0x00, 0x00, 0x00, 0x00, 0x30, 0xC0, 0x00, 0x80, 0x70, 0x00, 0x00,
+ 0x00, 0x10, 0x10, 0x90, 0x50, 0x30, 0x00, 0x00, 0x00, 0x80, 0x80, 0x7E, 0x02, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x7E, 0x80, 0x80, 0x00, 0x00,
+ 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x04, 0x04, 0x02, 0x07, 0x00, 0x00,
+ 0x00, 0x07, 0x04, 0x04, 0x04, 0x03, 0x00, 0x00, 0x00, 0x03, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00,
+ 0x00, 0x03, 0x04, 0x04, 0x02, 0x07, 0x00, 0x00, 0x00, 0x03, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00,
+ 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x24, 0x24, 0x22, 0x1F, 0x00, 0x00,
+ 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x07, 0x04, 0x04, 0x00, 0x00, 0x00,
+ 0x20, 0x20, 0x20, 0x20, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x01, 0x02, 0x04, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x07, 0x04, 0x04, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00,
+ 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x04, 0x04, 0x04, 0x03, 0x00, 0x00,
+ 0x00, 0x3F, 0x04, 0x04, 0x04, 0x03, 0x00, 0x00, 0x00, 0x03, 0x04, 0x04, 0x02, 0x3F, 0x00, 0x00,
+ 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x04, 0x04, 0x04, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x04, 0x04, 0x04, 0x00, 0x00, 0x03, 0x04, 0x04, 0x02, 0x07, 0x00, 0x00,
+ 0x00, 0x00, 0x03, 0x04, 0x03, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x00, 0x01, 0x06, 0x01, 0x00,
+ 0x00, 0x06, 0x01, 0x01, 0x06, 0x00, 0x00, 0x00, 0x20, 0x20, 0x31, 0x0E, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x06, 0x05, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x10, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x1F, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};