From 2cee371bf125a6ec541dd7c5a809573facc7c456 Mon Sep 17 00:00:00 2001 From: ishtob Date: Sat, 16 Feb 2019 21:39:30 -0500 Subject: Haptic feedback generalized - DRV2605 and solenoids (#4939) * initial work to add eeprom to haptic feedback and decouple the feedback process from keyboards * Haptic feedback enhancements: on/off toggle working, feedback order working todo: -work on modes switching -get modes switching to save to eeprom * haptic enhancement - eeprom and modes added * Added set and get functions for haptic feedback * initial implementation of solenoids under haptic feedback * changed eeprom to 32 bits to reserve blocks for future features * start documentation of haptic feedback * change keycode per comment from reviewers * typo fixes * added eeprom for solenoid configs * added solenoid and docs * Add/fix default parameters configs, improve docs * more doc cleanup * add in solenoid buzz toggle, clean up doc * some fixes for error in compiling solenoid * fix a chibios specific i2c read function and added one for AVR controllers in DRV2605L.c * fixes for avr side issues * update keymap * fix keymap compile error * fix bugs found during solenoid testing * set pin that is not powered during bootloader * added warning about certain pins on the MCU may trip solenoid during DFU/bootloader --- drivers/haptic/DRV2605L.c | 24 ++--- drivers/haptic/DRV2605L.h | 26 +++-- drivers/haptic/haptic.c | 248 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/haptic/haptic.h | 82 +++++++++++++++ drivers/haptic/solenoid.c | 109 ++++++++++++++++++++ drivers/haptic/solenoid.h | 54 ++++++++++ 6 files changed, 523 insertions(+), 20 deletions(-) create mode 100644 drivers/haptic/haptic.c create mode 100644 drivers/haptic/haptic.h create mode 100644 drivers/haptic/solenoid.c create mode 100644 drivers/haptic/solenoid.h (limited to 'drivers') diff --git a/drivers/haptic/DRV2605L.c b/drivers/haptic/DRV2605L.c index 97ca292b9..215e6be3e 100644 --- a/drivers/haptic/DRV2605L.c +++ b/drivers/haptic/DRV2605L.c @@ -21,7 +21,7 @@ #include -uint8_t DRV2605L_transfer_buffer[20]; +uint8_t DRV2605L_transfer_buffer[2]; uint8_t DRV2605L_tx_register[0]; uint8_t DRV2605L_read_buffer[0]; uint8_t DRV2605L_read_register; @@ -34,6 +34,11 @@ void DRV_write(uint8_t drv_register, uint8_t settings) { } uint8_t DRV_read(uint8_t regaddress) { +#ifdef __AVR__ + i2c_readReg(DRV2605L_BASE_ADDRESS << 1, + regaddress, DRV2605L_read_buffer, 1, 100); + DRV2605L_read_register = (uint8_t)DRV2605L_read_buffer[0]; +#else DRV2605L_tx_register[0] = regaddress; if (MSG_OK != i2c_transmit_receive(DRV2605L_BASE_ADDRESS << 1, DRV2605L_tx_register, 1, @@ -42,14 +47,13 @@ uint8_t DRV_read(uint8_t regaddress) { printf("err reading reg \n"); } DRV2605L_read_register = (uint8_t)DRV2605L_read_buffer[0]; +#endif 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); @@ -104,21 +108,17 @@ void DRV_init(void) 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 */ +//Play greeting sequence + DRV_write(DRV_GO, 0x00); + DRV_write(DRV_WAVEFORM_SEQ_1, DRV_GREETING); + DRV_write(DRV_GO, 0x01); } void DRV_pulse(uint8_t sequence) diff --git a/drivers/haptic/DRV2605L.h b/drivers/haptic/DRV2605L.h index de9d294e9..836e9cbcd 100644 --- a/drivers/haptic/DRV2605L.h +++ b/drivers/haptic/DRV2605L.h @@ -31,13 +31,6 @@ #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 @@ -49,6 +42,16 @@ #ifndef F_LRA #define F_LRA 205 #endif +#ifndef RATED_VOLTAGE +#define RATED_VOLTAGE 2 /* 2v as safe range in case device voltage is not set */ +#endif +#endif + +#ifndef RATED_VOLTAGE +#define RATED_VOLTAGE 2 /* 2v as safe range in case device voltage is not set */ +#endif +#ifndef V_PEAK +#define V_PEAK 2.8 #endif /* Library Selection */ @@ -60,6 +63,13 @@ #endif #endif +#ifndef DRV_GREETING +#define DRV_GREETING alert_750ms +#endif +#ifndef DRV_MODE_DEFAULT +#define DRV_MODE_DEFAULT strong_click1 +#endif + /* Control 1 register settings */ #ifndef DRIVE_TIME #define DRIVE_TIME 25 @@ -162,7 +172,6 @@ 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, @@ -288,6 +297,7 @@ typedef enum DRV_EFFECT{ smooth_hum3_30 = 121, smooth_hum4_20 = 122, smooth_hum5_10 = 123, + drv_effect_max = 124, } DRV_EFFECT; /* Register bit array unions */ diff --git a/drivers/haptic/haptic.c b/drivers/haptic/haptic.c new file mode 100644 index 000000000..a94f05565 --- /dev/null +++ b/drivers/haptic/haptic.c @@ -0,0 +1,248 @@ +/* Copyright 2019 ishtob + * Driver for haptic feedback 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 . + */ +#include "haptic.h" +#include "eeconfig.h" +#include "progmem.h" +#include "debug.h" +#ifdef DRV2605L +#include "DRV2605L.h" +#endif +#ifdef SOLENOID_ENABLE +#include "solenoid.h" +#endif + +haptic_config_t haptic_config; + +void haptic_init(void) { + debug_enable = 1; //Debug is ON! + if (!eeconfig_is_enabled()) { + eeconfig_init(); + } + haptic_config.raw = eeconfig_read_haptic(); + if (haptic_config.mode < 1){ + haptic_config.mode = 1; + } + if (!haptic_config.mode){ + dprintf("No haptic config found in eeprom, setting default configs\n"); + haptic_reset(); + } + #ifdef SOLENOID_ENABLE + solenoid_setup(); + dprintf("Solenoid driver initialized\n"); + #endif + #ifdef DRV2605L + DRV_init(); + dprintf("DRV2605 driver initialized\n"); + #endif + eeconfig_debug_haptic(); +} + +void haptic_task(void) { + #ifdef SOLENOID_ENABLE + solenoid_check(); + #endif +} + +void eeconfig_debug_haptic(void) { + dprintf("haptic_config eprom\n"); + dprintf("haptic_config.enable = %d\n", haptic_config.enable); + dprintf("haptic_config.mode = %d\n", haptic_config.mode); +} + +void haptic_enable(void) { + haptic_config.enable = 1; + xprintf("haptic_config.enable = %u\n", haptic_config.enable); + eeconfig_update_haptic(haptic_config.raw); +} + +void haptic_disable(void) { + haptic_config.enable = 0; + xprintf("haptic_config.enable = %u\n", haptic_config.enable); + eeconfig_update_haptic(haptic_config.raw); +} + +void haptic_toggle(void) { +if (haptic_config.enable) { + haptic_disable(); + } else { + haptic_enable(); + } + eeconfig_update_haptic(haptic_config.raw); +} + +void haptic_feedback_toggle(void){ + haptic_config.feedback++; + if (haptic_config.feedback >= HAPTIC_FEEDBACK_MAX) + haptic_config.feedback = KEY_PRESS; + xprintf("haptic_config.feedback = %u\n", !haptic_config.feedback); + eeconfig_update_haptic(haptic_config.raw); +} + +void haptic_buzz_toggle(void) { + bool buzz_stat = !haptic_config.buzz; + haptic_config.buzz = buzz_stat; + haptic_set_buzz(buzz_stat); +} + +void haptic_mode_increase(void) { + uint8_t mode = haptic_config.mode + 1; + #ifdef DRV2605L + if (haptic_config.mode >= drv_effect_max) { + mode = 1; + } + #endif + haptic_set_mode(mode); +} + +void haptic_mode_decrease(void) { + uint8_t mode = haptic_config.mode -1; + #ifdef DRV2605L + if (haptic_config.mode < 1) { + mode = (drv_effect_max - 1); + } + #endif + haptic_set_mode(mode); +} + +void haptic_dwell_increase(void) { + uint8_t dwell = haptic_config.dwell + 1; + #ifdef SOLENOID_ENABLE + if (haptic_config.dwell >= SOLENOID_MAX_DWELL) { + dwell = 1; + } + solenoid_set_dwell(dwell); + #endif + haptic_set_dwell(dwell); +} + +void haptic_dwell_decrease(void) { + uint8_t dwell = haptic_config.dwell -1; + #ifdef SOLENOID_ENABLE + if (haptic_config.dwell < SOLENOID_MIN_DWELL) { + dwell = SOLENOID_MAX_DWELL; + } + solenoid_set_dwell(dwell); + #endif + haptic_set_dwell(dwell); +} + +void haptic_reset(void){ + haptic_config.enable = true; + uint8_t feedback = HAPTIC_FEEDBACK_DEFAULT; + haptic_config.feedback = feedback; + #ifdef DRV2605L + uint8_t mode = HAPTIC_MODE_DEFAULT; + haptic_config.mode = mode; + #endif + #ifdef SOLENOID_ENABLE + uint8_t dwell = SOLENOID_DEFAULT_DWELL; + haptic_config.dwell = dwell; + #endif + eeconfig_update_haptic(haptic_config.raw); + xprintf("haptic_config.feedback = %u\n", haptic_config.feedback); + xprintf("haptic_config.mode = %u\n", haptic_config.mode); +} + +void haptic_set_feedback(uint8_t feedback) { + haptic_config.feedback = feedback; + eeconfig_update_haptic(haptic_config.raw); + xprintf("haptic_config.feedback = %u\n", haptic_config.feedback); +} + +void haptic_set_mode(uint8_t mode) { + haptic_config.mode = mode; + eeconfig_update_haptic(haptic_config.raw); + xprintf("haptic_config.mode = %u\n", haptic_config.mode); +} + +void haptic_set_buzz(uint8_t buzz) { + haptic_config.buzz = buzz; + eeconfig_update_haptic(haptic_config.raw); + xprintf("haptic_config.buzz = %u\n", haptic_config.buzz); +} + +void haptic_set_dwell(uint8_t dwell) { + haptic_config.dwell = dwell; + eeconfig_update_haptic(haptic_config.raw); + xprintf("haptic_config.dwell = %u\n", haptic_config.dwell); +} + +uint8_t haptic_get_mode(void) { + if (!haptic_config.enable){ + return false; + } + return haptic_config.mode; +} + +uint8_t haptic_get_feedback(void) { + if (!haptic_config.enable){ + return false; + } + return haptic_config.feedback; +} + +uint8_t haptic_get_dwell(void) { + if (!haptic_config.enable){ + return false; + } + return haptic_config.dwell; +} + +void haptic_play(void) { + #ifdef DRV2605L + uint8_t play_eff = 0; + play_eff = haptic_config.mode; + DRV_pulse(play_eff); + #endif + #ifdef SOLENOID_ENABLE + solenoid_fire(); + #endif +} + +bool process_haptic(uint16_t keycode, keyrecord_t *record) { + if (keycode == HPT_ON && record->event.pressed) { haptic_enable(); } + if (keycode == HPT_OFF && record->event.pressed) { haptic_disable(); } + if (keycode == HPT_TOG && record->event.pressed) { haptic_toggle(); } + if (keycode == HPT_RST && record->event.pressed) { haptic_reset(); } + if (keycode == HPT_FBK && record->event.pressed) { haptic_feedback_toggle(); } + if (keycode == HPT_BUZ && record->event.pressed) { haptic_buzz_toggle(); } + if (keycode == HPT_MODI && record->event.pressed) { haptic_mode_increase(); } + if (keycode == HPT_MODD && record->event.pressed) { haptic_mode_decrease(); } + if (keycode == HPT_DWLI && record->event.pressed) { haptic_dwell_increase(); } + if (keycode == HPT_DWLD && record->event.pressed) { haptic_dwell_decrease(); } + if (haptic_config.enable) { + if ( record->event.pressed ) { + // keypress + if (haptic_config.feedback < 2) { + haptic_play(); + } + } else { + //keyrelease + if (haptic_config.feedback > 0) { + haptic_play(); + } + } + } + return true; +} + +void haptic_shutdown(void) { + #ifdef SOLENOID_ENABLE + solenoid_shutdown(); + #endif + +} diff --git a/drivers/haptic/haptic.h b/drivers/haptic/haptic.h new file mode 100644 index 000000000..d39dc5c3b --- /dev/null +++ b/drivers/haptic/haptic.h @@ -0,0 +1,82 @@ +/* Copyright 2019 ishtob + * Driver for haptic feedback 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 . + */ + +#pragma once +#include +#include +#include "quantum.h" +#ifdef DRV2605L +#include "DRV2605L.h" +#endif + + +#ifndef HAPTIC_FEEDBACK_DEFAULT +#define HAPTIC_FEEDBACK_DEFAULT 0 +#endif +#ifndef HAPTIC_MODE_DEFAULT +#define HAPTIC_MODE_DEFAULT DRV_MODE_DEFAULT +#endif + +/* EEPROM config settings */ +typedef union { + uint32_t raw; + struct { + bool enable :1; + uint8_t feedback :2; + uint8_t mode :7; + bool buzz :1; + uint8_t dwell :7; + uint16_t reserved :16; + }; +} haptic_config_t; + +typedef enum HAPTIC_FEEDBACK{ + KEY_PRESS, + KEY_PRESS_RELEASE, + KEY_RELEASE, + HAPTIC_FEEDBACK_MAX, +} HAPTIC_FEEDBACK; + +bool process_haptic(uint16_t keycode, keyrecord_t *record); +void haptic_init(void); +void haptic_task(void); +void eeconfig_debug_haptic(void); +void haptic_enable(void); +void haptic_disable(void); +void haptic_toggle(void); +void haptic_feedback_toggle(void); +void haptic_mode_increase(void); +void haptic_mode_decrease(void); +void haptic_mode(uint8_t mode); +void haptic_reset(void); +void haptic_set_feedback(uint8_t feedback); +void haptic_set_mode(uint8_t mode); +void haptic_set_dwell(uint8_t dwell); +void haptic_set_buzz(uint8_t buzz); +void haptic_buzz_toggle(void); +uint8_t haptic_get_mode(void); +uint8_t haptic_get_feedback(void); +void haptic_dwell_increase(void); +void haptic_dwell_decrease(void); + +void haptic_play(void); +void haptic_shutdown(void); + + + + + diff --git a/drivers/haptic/solenoid.c b/drivers/haptic/solenoid.c new file mode 100644 index 000000000..2d39dbc17 --- /dev/null +++ b/drivers/haptic/solenoid.c @@ -0,0 +1,109 @@ +/* Copyright 2018 mtdjr - modified by ishtob + * Driver for solenoid 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 . + */ + +#include +#include "solenoid.h" +#include "haptic.h" + +bool solenoid_on = false; +bool solenoid_buzzing = false; +uint16_t solenoid_start = 0; +uint8_t solenoid_dwell = SOLENOID_DEFAULT_DWELL; + +extern haptic_config_t haptic_config; + + +void solenoid_buzz_on(void) { + haptic_set_buzz(1); +} + +void solenoid_buzz_off(void) { + haptic_set_buzz(0); +} + +void solenoid_set_buzz(int buzz) { + haptic_set_buzz(buzz); +} + + +void solenoid_dwell_minus(uint8_t solenoid_dwell) { + if (solenoid_dwell > 0) solenoid_dwell--; +} + +void solenoid_dwell_plus(uint8_t solenoid_dwell) { + if (solenoid_dwell < SOLENOID_MAX_DWELL) solenoid_dwell++; +} + +void solenoid_set_dwell(uint8_t dwell) { + solenoid_dwell = dwell; +} + +void solenoid_stop(void) { + writePinLow(SOLENOID_PIN); + solenoid_on = false; + solenoid_buzzing = false; +} + +void solenoid_fire(void) { + if (!haptic_config.buzz && solenoid_on) return; + if (haptic_config.buzz && solenoid_buzzing) return; + + solenoid_on = true; + solenoid_buzzing = true; + solenoid_start = timer_read(); + writePinHigh(SOLENOID_PIN); +} + +void solenoid_check(void) { + uint16_t elapsed = 0; + + if (!solenoid_on) return; + + elapsed = timer_elapsed(solenoid_start); + + //Check if it's time to finish this solenoid click cycle + if (elapsed > solenoid_dwell) { + solenoid_stop(); + return; + } + + //Check whether to buzz the solenoid on and off + if (haptic_config.buzz) { + if (elapsed / SOLENOID_MIN_DWELL % 2 == 0){ + if (!solenoid_buzzing) { + solenoid_buzzing = true; + writePinHigh(SOLENOID_PIN); + } + } + else { + if (solenoid_buzzing) { + solenoid_buzzing = false; + writePinLow(SOLENOID_PIN); + } + } + } +} + +void solenoid_setup(void) { + setPinOutput(SOLENOID_PIN); + solenoid_fire(); +} + +void solenoid_shutdown(void) { + writePinLow(SOLENOID_PIN); + +} diff --git a/drivers/haptic/solenoid.h b/drivers/haptic/solenoid.h new file mode 100644 index 000000000..a08f62a11 --- /dev/null +++ b/drivers/haptic/solenoid.h @@ -0,0 +1,54 @@ +/* Copyright 2018 mtdjr - modified by ishtob + * Driver for solenoid 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 . + */ + +#pragma once + +#ifndef SOLENOID_DEFAULT_DWELL +#define SOLENOID_DEFAULT_DWELL 12 +#endif + +#ifndef SOLENOID_MAX_DWELL +#define SOLENOID_MAX_DWELL 100 +#endif + +#ifndef SOLENOID_MIN_DWELL +#define SOLENOID_MIN_DWELL 4 +#endif + +#ifndef SOLENOID_ACTIVE +#define SOLENOID_ACTIVE false +#endif + +#ifndef SOLENOID_PIN +#define SOLENOID_PIN F6 +#endif + +void solenoid_buzz_on(void); +void solenoid_buzz_off(void); +void solenoid_set_buzz(int buzz); + +void solenoid_dwell_minus(uint8_t solenoid_dwell); +void solenoid_dwell_plus(uint8_t solenoid_dwell); +void solenoid_set_dwell(uint8_t dwell); + +void solenoid_stop(void); +void solenoid_fire(void); + +void solenoid_check(void); + +void solenoid_setup(void); +void solenoid_shutdown(void); -- cgit v1.2.3-24-g4f1b