]> git.donarmstrong.com Git - qmk_firmware.git/commitdiff
Haptic feedback generalized - DRV2605 and solenoids (#4939)
authorishtob <ishtob@gmail.com>
Sun, 17 Feb 2019 02:39:30 +0000 (21:39 -0500)
committerMechMerlin <30334081+mechmerlin@users.noreply.github.com>
Sun, 17 Feb 2019 02:39:30 +0000 (18:39 -0800)
* 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

18 files changed:
common_features.mk
docs/feature_haptic_feedback.md [new file with mode: 0644]
drivers/haptic/DRV2605L.c
drivers/haptic/DRV2605L.h
drivers/haptic/haptic.c [new file with mode: 0644]
drivers/haptic/haptic.h [new file with mode: 0644]
drivers/haptic/solenoid.c [new file with mode: 0644]
drivers/haptic/solenoid.h [new file with mode: 0644]
keyboards/hadron/ver3/config.h
keyboards/hadron/ver3/keymaps/default/keymap.c
keyboards/hadron/ver3/keymaps/ishtob/keymap.c
keyboards/hadron/ver3/rules.mk
keyboards/hadron/ver3/ver3.c
quantum/quantum.c
quantum/quantum.h
quantum/quantum_keycodes.h
tmk_core/common/eeconfig.c
tmk_core/common/eeconfig.h

index 6f02dbac6298427ec96e328e9ae7c6f2ccf2b19b..3d3b8506e40ea2b833ebc4b440cef0883683f3af 100644 (file)
@@ -246,11 +246,21 @@ endif
 
 ifeq ($(strip $(HAPTIC_ENABLE)), DRV2605L)
     COMMON_VPATH += $(DRIVER_PATH)/haptic
+    SRC += haptic.c
     SRC += DRV2605L.c
     SRC += i2c_master.c
+    OPT_DEFS += -DHAPTIC_ENABLE
     OPT_DEFS += -DDRV2605L
 endif
 
+ifeq ($(strip $(HAPTIC_ENABLE)), SOLENOID)
+    COMMON_VPATH += $(DRIVER_PATH)/haptic
+    SRC += haptic.c
+    SRC += solenoid.c
+    OPT_DEFS += -DHAPTIC_ENABLE
+    OPT_DEFS += -DSOLENOID_ENABLE
+endif
+
 ifeq ($(strip $(HD44780_ENABLE)), yes)
     SRC += drivers/avr/hd44780.c
     OPT_DEFS += -DHD44780_ENABLE
diff --git a/docs/feature_haptic_feedback.md b/docs/feature_haptic_feedback.md
new file mode 100644 (file)
index 0000000..85fd439
--- /dev/null
@@ -0,0 +1,147 @@
+# Haptic Feedback
+
+## Haptic feedback rules.mk options
+
+The following options are currently available for haptic feedback in `rule.mk`:
+
+`HAPTIC_ENABLE += DRV2605L`
+
+`HAPTIC_ENABLE += SOLENOID`
+
+## Known Supported Hardware
+
+| Name               | Description                                     |
+|--------------------|-------------------------------------------------|
+| [LV061228B-L65-A](https://www.digikey.com/product-detail/en/jinlong-machinery-electronics-inc/LV061228B-L65-A/1670-1050-ND/7732325) | z-axis 2v LRA |
+| [Mini Motor Disc](https://www.adafruit.com/product/1201)  | small 2-5v ERM |
+
+## Haptic Keycodes
+
+Not all keycodes below will work depending on which haptic mechanism you have chosen.
+
+| Name      | Description                                           |
+|-----------|-------------------------------------------------------|
+|`HPT_ON`   | Turn haptic feedback on                               |
+|`HPT_OFF`  | Turn haptic feedback on                               |
+|`HPT_TOG`  | Toggle haptic feedback on/off                         |
+|`HPT_RST`  | Reset haptic feedback config to default               |
+|`HPT_FBK`  | Toggle feedback to occur on keypress, release or both |
+|`HPT_BUZ`  | Toggle solenoid buzz on/off                           |
+|`HPT_MODI` | Go to next DRV2605L waveform                          |
+|`HPT_MODD` | Go to previous DRV2605L waveform                      |
+|`HPT_DWLI` | Increase Solenoid dwell time                          |
+|`HPT_DWLD` | Decrease Solenoid dwell time                          |
+
+### Solenoids
+
+First you will need a build a circuit to drive the solenoid through a mosfet as most MCU will not be able to provide the current needed to drive the coil in the solenoid.
+
+[Wiring diagram provided by Adafruit](https://playground.arduino.cc/uploads/Learning/solenoid_driver.pdf)
+
+Select a pin that has PWM for the signal pin
+
+```
+#define SOLENOID_PIN *pin*
+```
+
+Beware that some pins may be powered during bootloader (ie. A13 on the STM32F303 chip) and will result in the solenoid kept in the on state through the whole flashing process. This may overheat and damage the solenoid. If you find that the pin the solenoid is connected to is triggering the solenoid during bootloader/DFU, select another pin.
+
+### DRV2605L
+
+DRV2605L is controlled over i2c protocol, and has to be connected to the SDA and SCL pins, these varies depending on the MCU in use.
+
+#### Feedback motor setup
+
+This driver supports 2 different feedback motors. Set the following in your `config.h` based on which motor you have selected.
+
+##### ERM
+
+Eccentric Rotating Mass vibration motors (ERM) is motor with a off-set weight attached so when drive signal is attached, the off-set weight spins and causes a sinusoidal wave that translate into vibrations.
+
+```
+#define FB_ERM_LRA 0
+#define FB_BRAKEFACTOR 3 /* For 1x:0, 2x:1, 3x:2, 4x:3, 6x:4, 8x:5, 16x:6, Disable Braking:7 */
+#define FB_LOOPGAIN 1 /* For  Low:0, Medium:1, High:2, Very High:3 */
+
+/* Please refer to your datasheet for the optimal setting for your specific motor. */
+#define RATED_VOLTAGE 3
+#define V_PEAK 5
+```
+##### LRA
+
+Linear resonant actuators (LRA, also know as a linear vibrator) works different from a ERM. A LRA has a weight and magnet suspended by springs and a voice coil. When the drive signal is applied, the weight would be vibrate on a single axis (side to side or up and down). Since the weight is attached to a spring, there is a resonance effect at a specific frequency. This frequency is where the LRA will operate the most efficiently. Refer to the motor's datasheet for the recommanded range for this frequency.
+
+``` 
+#define FB_ERM_LRA 1
+#define FB_BRAKEFACTOR 3 /* For 1x:0, 2x:1, 3x:2, 4x:3, 6x:4, 8x:5, 16x:6, Disable Braking:7 */
+#define FB_LOOPGAIN 1 /* For  Low:0, Medium:1, High:2, Very High:3 */
+
+/* Please refer to your datasheet for the optimal setting for your specific motor. */
+#define RATED_VOLTAGE 2
+#define V_PEAK 2.8
+#define V_RMS 2.0 
+#define V_PEAK 2.1
+#define F_LRA 205 /* resonance freq */
+```
+
+#### DRV2605L waveform library
+
+DRV2605L comes with preloaded library of various waveform sequences that can be called and played. If writing a macro, these waveforms can be played using `DRV_pulse(*sequence name or number*)`
+
+List of waveform sequences from the datasheet:
+
+|seq# | Sequence name          |seq# | Sequence name                  |seq# |Sequence name                           |
+|-----|---------------------|-----|-----------------------------------|-----|--------------------------------------|
+| 1   | strong_click           | 43  | lg_dblclick_med_60                | 85  | transition_rampup_med_smooth2        |
+| 2   | strong_click_60        | 44  | lg_dblsharp_tick                  | 86  | transition_rampup_short_smooth1      |
+| 3   | strong_click_30        | 45  | lg_dblsharp_tick_80               | 87  | transition_rampup_short_smooth2      |
+| 4   | sharp_click            | 46  | lg_dblsharp_tick_60               | 88  | transition_rampup_long_sharp1        |
+| 5   | sharp_click_60      | 47  | buzz                              | 89  | transition_rampup_long_sharp2        |
+| 6   | sharp_click_30      | 48  | buzz_80                                          | 90  | transition_rampup_med_sharp1         |
+| 7   | soft_bump           | 49  | buzz_60                                  | 91  | transition_rampup_med_sharp2         |
+| 8   | soft_bump_60        | 50  | buzz_40                                          | 92  | transition_rampup_short_sharp1       |
+| 9   | soft_bump_30        | 51  | buzz_20                                          | 93  | transition_rampup_short_sharp2       |
+| 10  | dbl_click           | 52  | pulsing_strong                    | 94  | transition_rampdown_long_smooth1_50  |
+| 11  | dbl_click_60        | 53  | pulsing_strong_80                 | 95  | transition_rampdown_long_smooth2_50  |
+| 12  | trp_click           | 54  | pulsing_medium                    | 96  | transition_rampdown_med_smooth1_50   |
+| 13  | soft_fuzz           | 55  | pulsing_medium_80                 | 97  | transition_rampdown_med_smooth2_50   |
+| 14  | strong_buzz         | 56  | pulsing_sharp                     | 98  | transition_rampdown_short_smooth1_50 |
+| 15  | alert_750ms         | 57  | pulsing_sharp_80                  | 99  | transition_rampdown_short_smooth2_50 |
+| 16  | alert_1000ms        | 58  | transition_click                 | 100 | transition_rampdown_long_sharp1_50   |
+| 17  | strong_click1       | 59  | transition_click_80               | 101 | transition_rampdown_long_sharp2_50   |
+| 18  | strong_click2_80    | 60  | transition_click_60                      | 102 | transition_rampdown_med_sharp1_50    |
+| 19  | strong_click3_60    | 61  | transition_click_40                      | 103 | transition_rampdown_med_sharp2_50    |
+| 20  | strong_click4_30    | 62  | transition_click_20                      | 104 | transition_rampdown_short_sharp1_50  |
+| 21  | medium_click1       | 63  | transition_click_10                      | 105 | transition_rampdown_short_sharp2_50  |
+| 22  | medium_click2_80    | 64  | transition_hum                    | 106 | transition_rampup_long_smooth1_50    |
+| 23  | medium_click3_60    | 65  | transition_hum_80                 | 107 | transition_rampup_long_smooth2_50    |
+| 24  | sharp_tick1         | 66  | transition_hum_60                 | 108 | transition_rampup_med_smooth1_50     |
+| 25  | sharp_tick2_80      | 67  | transition_hum_40                 | 109 | transition_rampup_med_smooth2_50     |
+| 26  | sharp_tick3_60      | 68  | transition_hum_20                 | 110 | transition_rampup_short_smooth1_50   |
+| 27  | sh_dblclick_str     | 69  | transition_hum_10                 | 111 | transition_rampup_short_smooth2_50   |
+| 28  | sh_dblclick_str_80  | 70  | transition_rampdown_long_smooth1  | 112 | transition_rampup_long_sharp1_50     |
+| 29  | sh_dblclick_str_60  | 71  | transition_rampdown_long_smooth2  | 113 | transition_rampup_long_sharp2_50     |
+| 30  | sh_dblclick_str_30  | 72  | transition_rampdown_med_smooth1   | 114 | transition_rampup_med_sharp1_50      |
+| 31  | sh_dblclick_med     | 73  | transition_rampdown_med_smooth2   | 115 | transition_rampup_med_sharp2_50      |
+| 32  | sh_dblclick_med_80  | 74  | transition_rampdown_short_smooth1 | 116 | transition_rampup_short_sharp1_50    |
+| 33  | sh_dblclick_med_60  | 75  | transition_rampdown_short_smooth2 | 117 | transition_rampup_short_sharp2_50    |
+| 34  | sh_dblsharp_tick    | 76  | transition_rampdown_long_sharp1   | 118 | long_buzz_for_programmatic_stopping  |
+| 35  | sh_dblsharp_tick_80 | 77  | transition_rampdown_long_sharp2   | 119 | smooth_hum1_50                       |
+| 36  | sh_dblsharp_tick_60 | 78  | transition_rampdown_med_sharp1    | 120 | smooth_hum2_40                       |
+| 37  | lg_dblclick_str     | 79  | transition_rampdown_med_sharp2    | 121 | smooth_hum3_30                       |
+| 38  | lg_dblclick_str_80  | 80  | transition_rampdown_short_sharp1  | 122 | smooth_hum4_20                       |
+| 39  | lg_dblclick_str_60  | 81  | transition_rampdown_short_sharp2  | 123 | smooth_hum5_10                       |
+| 40  | lg_dblclick_str_30  | 82  | transition_rampup_long_smooth1    |     |                                      |
+| 41  | lg_dblclick_med     | 83  | transition_rampup_long_smooth2    |     |                                      |
+| 42  | lg_dblclick_med_80  | 84  | transition_rampup_med_smooth1     |     |                                      |
+### Optional DRV2605L defines
+
+```
+#define DRV_GREETING *sequence name or number*
+```
+If haptic feedback is enabled, the keyboard will vibrate to a specific sqeuence during startup. That can be selected using the following define:
+
+```
+#define DRV_MODE_DEFAULT *sequence name or number*
+```
+This will set what sequence HPT_RST will set as the active mode. If not defined, mode will be set to 1 when HPT_RST is pressed.
index 97ca292b9b1a73c3168af54a9dd85078e46a344b..215e6be3e7cbd2b779e1e11770e52fe6807ca634 100644 (file)
@@ -21,7 +21,7 @@
 #include <math.h>
 
 
-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)
index de9d294e9d137611174a1028ff44786319c7d03f..836e9cbcd281fda1fd50b8cb1bbcf702be9ca490 100644 (file)
 #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
 #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 */
 #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 (file)
index 0000000..a94f055
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+#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 (file)
index 0000000..d39dc5c
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+#include <stdint.h>
+#include <stdbool.h>
+#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 (file)
index 0000000..2d39dbc
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <timer.h>
+#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 (file)
index 0000000..a08f62a
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+#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);
index d6c3830dfd422fffa7d8120f72e69fabd205ca22..180492182515d2b205c6c269590580014e38e1f5 100644 (file)
 //#define DRIVER_LED_TOTAL RGBLED_NUM
 
 //#define RGB_MATRIX_KEYPRESSES
+
+#define SOLENOID_PIN A14
+
 #endif
index 11761b3210bdb73e46acfd27dd8fc5c09026b5e8..507c8314b6ccd4b101eb6fdc0407e1b01b9ad8fc 100644 (file)
@@ -190,8 +190,8 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
  * `--------------------------------------------------------------------------------------------------------'
  */
 [_ADJUST] = LAYOUT(
-  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12, \
-  RESET,   RGB_TOG, RGB_MOD, RGB_HUI, RGB_HUD, RGB_SAI, RGB_SAD, RGB_VAI, RGB_VAD, _______, _______, _______, _______, _______, KC_DEL, \
+  RESET,   HPT_TOG, HPT_FBK, HPT_MODI, HPT_MODD, HPT_RST , _______, _______, _______, _______, _______, _______, \
+  _______, RGB_TOG, RGB_MOD, RGB_HUI, RGB_HUD, RGB_SAI, RGB_SAD, RGB_VAI, RGB_VAD, _______, _______, _______, _______, _______, KC_DEL, \
   _______, _______, _______, AU_ON,   AU_OFF,  AG_NORM, _______, _______, _______, AG_SWAP, QWERTY,  COLEMAK, _______,  _______,  _______, \
   _______, MUV_DE,  MUV_IN,  MU_ON,   MU_OFF,  MI_ON,   MI_OFF, _______, _______, _______,  _______, BL_DEC,  BL_INC,  BL_STEP, BL_TOGG, \
   _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, CK_RST,  CK_DOWN, CK_UP,   CK_TOGG\
index 641c0162473f7c9ca0d604b38227199a805847ce..140d148b7ee887529d87d30476a7ab1d3f70958a 100644 (file)
@@ -165,7 +165,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
  * `--------------------------------------------------------------------------------------------------------'
  */
 [_ADJUST] = LAYOUT_wrapper(
-  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12, \
+  _______, HPT_TOG, HPT_FBK, HPT_MODI, HPT_MODD, HPT_RST, _______, _______, _______, _______, _______, _______, \
   RESET,   RGB_TOG, RGB_MOD, RGB_HUI, RGB_HUD, RGB_SAI, RGB_SAD, RGB_VAI, RGB_VAD, _______, _______, _______, _______, _______, KC_DEL, \
   _______, MAGIC_TOGGLE_NKRO, _______, AU_ON,   AU_OFF,  AG_NORM, _______, _______, _______, AG_SWAP, QWERTY,  COLEMAK, _______,  _______,  _______, \
   _______, MUV_DE,  MUV_IN,  MU_ON,   MU_OFF,  MI_ON,   MI_OFF, _______, _______, _______,  _______, BL_DEC,  BL_INC,  BL_STEP, BL_TOGG, \
index 8375efdd359f30464891898b131e243648b29174..2a86623157954350df2f08be47e012cc96feee9f 100644 (file)
@@ -52,6 +52,6 @@ NKRO_ENABLE = yes         # USB Nkey Rollover
 CUSTOM_MATRIX = yes # Custom matrix file
 AUDIO_ENABLE = yes
 RGBLIGHT_ENABLE = no
-HAPTIC_ENABLE = DRV2605L
+HAPTIC_ENABLE += DRV2605L
 QWIIC_ENABLE += MICRO_OLED
 # SERIAL_LINK_ENABLE = yes
index 5e5e3e009f83f44072536521d85bb5a840884262..37169fe2f387402f1eb6d674375544aa37d3989b 100644 (file)
@@ -17,7 +17,7 @@
 #include "qwiic.h"
 #include "action_layer.h"
 #include "matrix.h"
-#include "DRV2605L.h"
+#include "haptic.h"
 
 #ifdef QWIIC_MICRO_OLED_ENABLE
 
@@ -167,20 +167,12 @@ void encoder_update_kb(uint8_t index, bool clockwise) {
 #endif
 
 void matrix_init_kb(void) {
-#ifdef DRV2605L
-  DRV_init();
-#endif
   queue_for_send = true;
        matrix_init_user();
 }
             
 void matrix_scan_kb(void) {
-
 if (queue_for_send) {
-  #ifdef DRV2605L
-    DRV_EFFECT play_eff = strong_click; 
-    DRV_pulse(play_eff);
-  #endif
 #ifdef QWIIC_MICRO_OLED_ENABLE
    read_host_led_state();
    draw_ui();
index 376578ade42602b0b61a6b55a3722745b3bdc029..cb4d5ee806fca27cb8713673e6df5c798013ec0c 100644 (file)
@@ -47,6 +47,9 @@ extern backlight_config_t backlight_config;
 #include "process_midi.h"
 #endif
 
+#ifdef HAPTIC_ENABLE
+    #include "haptic.h"
+#endif
 
 #ifdef ENCODER_ENABLE
 #include "encoder.h"
@@ -179,6 +182,9 @@ void reset_keyboard(void) {
   shutdown_user();
   wait_ms(250);
 #endif
+#ifdef HAPTIC_ENABLE
+  haptic_shutdown();
+#endif
 // this is also done later in bootloader.c - not sure if it's neccesary here
 #ifdef BOOTLOADER_CATERINA
   *(uint16_t *)0x0800 = 0x7777; // these two are a-star-specific
@@ -257,6 +263,9 @@ bool process_record_quantum(keyrecord_t *record) {
   #if defined(AUDIO_ENABLE) && defined(AUDIO_CLICKY)
     process_clicky(keycode, record) &&
   #endif //AUDIO_CLICKY
+  #ifdef HAPTIC_ENABLE
+    process_haptic(keycode, record) &&
+  #endif //HAPTIC_ENABLE
     process_record_kb(keycode, record) &&
   #if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_KEYPRESSES)
     process_rgb_matrix(keycode, record) &&
@@ -1049,6 +1058,9 @@ void matrix_init_quantum() {
   #if defined(UNICODE_ENABLE) || defined(UNICODEMAP_ENABLE) || defined(UCIS_ENABLE)
     unicode_input_mode_init();
   #endif
+  #ifdef HAPTIC_ENABLE
+    haptic_init();
+  #endif
   matrix_init_kb();
 }
 
@@ -1091,6 +1103,10 @@ void matrix_scan_quantum() {
     encoder_read();
   #endif
 
+  #ifdef HAPTIC_ENABLE
+    haptic_task();
+  #endif
+
   matrix_scan_kb();
 }
 #if defined(BACKLIGHT_ENABLE) && defined(BACKLIGHT_PIN)
index c6acf83e5af4fc55931c1355b2e33d89cf4de080..d2c5862f881cf2d6f57b23f7f410b6294a0bb6ce 100644 (file)
@@ -139,6 +139,10 @@ extern uint32_t default_layer_state;
     #include "hd44780.h"
 #endif
 
+#ifdef HAPTIC_ENABLE
+    #include "haptic.h"
+#endif
+
 //Function substitutions to ease GPIO manipulation
 #ifdef __AVR__
     #define PIN_ADDRESS(p, offset) _SFR_IO8(ADDRESS_BASE + (p >> PORT_SHIFTER) + offset)
index 0462291c2b17bd3e740c10720bdddce29866559e..00c84cbf537c6d5fb7360114e52cc1ae47787690 100644 (file)
@@ -446,25 +446,36 @@ enum quantum_keycodes {
     KC_LOCK,
 #endif
 
-#ifdef TERMINAL_ENABLE
-    TERM_ON,
-    TERM_OFF,
-#endif
-
-    EEPROM_RESET,
-
-    UNICODE_MODE_FORWARD,
-    UNICODE_MODE_REVERSE,
-
-    UNICODE_MODE_OSX,
-    UNICODE_MODE_LNX,
-    UNICODE_MODE_WIN,
-    UNICODE_MODE_BSD,
-    UNICODE_MODE_WINC,
-
-    // always leave at the end
-    SAFE_RANGE
-};
+    #ifdef TERMINAL_ENABLE
+        TERM_ON,
+        TERM_OFF,
+    #endif
+
+        EEPROM_RESET,
+
+        UNICODE_MODE_FORWARD,
+        UNICODE_MODE_REVERSE,
+
+        UNICODE_MODE_OSX,
+        UNICODE_MODE_LNX,
+        UNICODE_MODE_WIN,
+        UNICODE_MODE_BSD,
+        UNICODE_MODE_WINC,
+
+        HPT_ON,
+        HPT_OFF,
+        HPT_TOG,
+        HPT_RST,
+        HPT_FBK,
+        HPT_BUZ,
+        HPT_MODI,
+        HPT_MODD,
+        HPT_DWLI,
+        HPT_DWLD,
+
+        // always leave at the end
+        SAFE_RANGE
+    };
 
 // Ability to use mods in layouts
 #define LCTL(kc) (QK_LCTL | (kc))
index 59b2bffbc714d779b52740216f5cbb9c8e9ceff6..ded27e599f4a5acdf62dac676c13a07ad8d41fef 100644 (file)
@@ -45,6 +45,7 @@ void eeconfig_init_quantum(void) {
   eeprom_update_byte(EECONFIG_AUDIO,             0xFF); // On by default
   eeprom_update_dword(EECONFIG_RGBLIGHT,      0);
   eeprom_update_byte(EECONFIG_STENOMODE,      0);
+  eeprom_update_dword(EECONFIG_HAPTIC,        0);
 
   eeconfig_init_kb();
 }
@@ -177,3 +178,11 @@ uint32_t eeconfig_read_user(void)      { return eeprom_read_dword(EECONFIG_USER)
 void eeconfig_update_user(uint32_t val) { eeprom_update_dword(EECONFIG_USER, val); }
 
 
+uint32_t eeconfig_read_haptic(void)      { return eeprom_read_dword(EECONFIG_HAPTIC); }
+/** \brief eeconfig update user
+ *
+ * FIXME: needs doc
+ */
+void eeconfig_update_haptic(uint32_t val) { eeprom_update_dword(EECONFIG_HAPTIC, val); }
+
+
index eedd67602c69468529cd16232dab3f785cb7cc0b..d576634f579919e694913c20fddd7715c82f647d 100644 (file)
@@ -41,6 +41,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #define EECONFIG_KEYBOARD                          (uint32_t *)15
 #define EECONFIG_USER                              (uint32_t *)19
 
+#define EECONFIG_HAPTIC                            (uint32_t*)24
+
 /* debug bit */
 #define EECONFIG_DEBUG_ENABLE                       (1<<0)
 #define EECONFIG_DEBUG_MATRIX                       (1<<1)
@@ -94,4 +96,9 @@ void eeconfig_update_kb(uint32_t val);
 uint32_t eeconfig_read_user(void);
 void eeconfig_update_user(uint32_t val);
 
+#ifdef HAPTIC_ENABLE
+uint32_t eeconfig_read_haptic(void);
+void eeconfig_update_haptic(uint32_t val);
+#endif
+
 #endif