summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
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 x1