summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDrashna Jaelre <drashna@live.com>2022-05-15 04:24:35 -0700
committerGitHub <noreply@github.com>2022-05-15 21:24:35 +1000
commitf090881aebbd7af74294b53fabdf55a35004f190 (patch)
tree53d786c3be9bf1b2823bdee7bf6a55686d8b1461
parent4d107feca9621ee3f342507136b04f176e8f2320 (diff)
downloadqmk_firmware-f090881aebbd7af74294b53fabdf55a35004f190.tar.gz
qmk_firmware-f090881aebbd7af74294b53fabdf55a35004f190.zip
[Feature] Add support for multiple switchs/solenoids to Haptic Feedback engine (#15657)
-rw-r--r--docs/feature_haptic_feedback.md32
-rw-r--r--drivers/haptic/solenoid.c155
-rw-r--r--drivers/haptic/solenoid.h26
-rw-r--r--keyboards/handwired/onekey/blackpill_f411/config.h4
-rw-r--r--keyboards/handwired/onekey/keymaps/haptic/keymap.c11
-rw-r--r--keyboards/handwired/onekey/keymaps/haptic/rules.mk2
-rw-r--r--quantum/haptic.c2
7 files changed, 163 insertions, 69 deletions
diff --git a/docs/feature_haptic_feedback.md b/docs/feature_haptic_feedback.md
index 63ac4305ff..f4546b6914 100644
--- a/docs/feature_haptic_feedback.md
+++ b/docs/feature_haptic_feedback.md
@@ -50,22 +50,28 @@ Not all keycodes below will work depending on which haptic mechanism you have ch
### 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.
+The solenoid code supports relay switches, and similar hardware, as well as solenoids.
-[Wiring diagram provided by Adafruit](https://cdn-shop.adafruit.com/product-files/412/solenoid_driver.pdf)
+For a regular solenoid, 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://cdn-shop.adafruit.com/product-files/412/solenoid_driver.pdf)
-| Settings | Default | Description |
-|----------------------------|----------------------|-------------------------------------------------------|
-|`SOLENOID_PIN` | *Not defined* |Configures the pin that the Solenoid is connected to. |
-|`SOLENOID_PIN_ACTIVE_LOW` | *Not defined* |If defined then the solenoid trigger pin is active low.|
-|`SOLENOID_DEFAULT_DWELL` | `12` ms |Configures the default dwell time for the solenoid. |
-|`SOLENOID_MIN_DWELL` | `4` ms |Sets the lower limit for the dwell. |
-|`SOLENOID_MAX_DWELL` | `100` ms |Sets the upper limit for the dwell. |
-|`SOLENOID_DWELL_STEP_SIZE` | `1` ms |The step size to use when `HPT_DWL*` keycodes are sent |
-|`SOLENOID_DEFAULT_BUZZ` | `0` (disabled) |On HPT_RST buzz is set "on" if this is "1" |
-|`SOLENOID_BUZZ_ACTUATED` | `SOLENOID_MIN_DWELL` |Actuated-time when the solenoid is in buzz mode |
-|`SOLENOID_BUZZ_NONACTUATED` | `SOLENOID_MIN_DWELL` |Non-Actuated-time when the solenoid is in buzz mode |
+For relay switches, the hardware may already contain all of that ciruitry, and just require VCC, GND and a data pin.
+
+| Settings | Default | Description |
+|----------------------------|----------------------|--------------------------------------------------------------|
+|`SOLENOID_PIN` | *Not defined* |Configures the pin that the switch is connected to. |
+|`SOLENOID_PIN_ACTIVE_LOW` | *Not defined* |If defined then the switch trigger pin is active low. |
+|`SOLENOID_PINS` | *Not defined* |Configures an array of pins to be used for switch activation. |
+|`SOLENOID_PINS_ACTIVE_LOW` | *Not defined* |Allows you to specify how each pin is pulled for activation. |
+|`SOLENOID_RANDOM_FIRE` | *Not defined* |When there are multiple solenoids, will select a random one to fire.|
+|`SOLENOID_DEFAULT_DWELL` | `12` ms |Configures the default dwell time for the switch. |
+|`SOLENOID_MIN_DWELL` | `4` ms |Sets the lower limit for the dwell. |
+|`SOLENOID_MAX_DWELL` | `100` ms |Sets the upper limit for the dwell. |
+|`SOLENOID_DWELL_STEP_SIZE` | `1` ms |The step size to use when `HPT_DWL*` keycodes are sent. |
+|`SOLENOID_DEFAULT_BUZZ` | `0` (disabled) |On HPT_RST buzz is set "on" if this is "1" |
+|`SOLENOID_BUZZ_ACTUATED` | `SOLENOID_MIN_DWELL` |Actuated-time when the switch is in buzz mode. |
+|`SOLENOID_BUZZ_NONACTUATED` | `SOLENOID_MIN_DWELL` |Non-Actuated-time when the switch is in buzz mode. |
* If solenoid buzz is off, then dwell time is how long the "plunger" stays activated. The dwell time changes how the solenoid sounds.
* If solenoid buzz is on, then dwell time sets the length of the buzz, while `SOLENOID_BUZZ_ACTUATED` and `SOLENOID_BUZZ_NONACTUATED` set the (non-)actuation times withing the buzz period.
diff --git a/drivers/haptic/solenoid.c b/drivers/haptic/solenoid.c
index 14d868bffe..637a77da3d 100644
--- a/drivers/haptic/solenoid.c
+++ b/drivers/haptic/solenoid.c
@@ -20,11 +20,22 @@
#include "haptic.h"
#include "gpio.h"
#include "usb_device_state.h"
-
-bool solenoid_on = false;
-bool solenoid_buzzing = false;
-uint16_t solenoid_start = 0;
-uint8_t solenoid_dwell = SOLENOID_DEFAULT_DWELL;
+#include <stdlib.h>
+
+uint8_t solenoid_dwell = SOLENOID_DEFAULT_DWELL;
+static pin_t solenoid_pads[] = SOLENOID_PINS;
+#define NUMBER_OF_SOLENOIDS (sizeof(solenoid_pads) / sizeof(pin_t))
+bool solenoid_on[NUMBER_OF_SOLENOIDS] = {false};
+bool solenoid_buzzing[NUMBER_OF_SOLENOIDS] = {false};
+uint16_t solenoid_start[NUMBER_OF_SOLENOIDS] = {0};
+#ifdef SOLENOID_PIN_ACTIVE_LOW
+# define low true
+# define high false
+#else
+# define low false
+# define high true
+#endif
+static bool solenoid_active_state[NUMBER_OF_SOLENOIDS];
extern haptic_config_t haptic_config;
@@ -36,7 +47,7 @@ void solenoid_buzz_off(void) {
haptic_set_buzz(0);
}
-void solenoid_set_buzz(int buzz) {
+void solenoid_set_buzz(uint8_t buzz) {
haptic_set_buzz(buzz);
}
@@ -44,59 +55,121 @@ void solenoid_set_dwell(uint8_t dwell) {
solenoid_dwell = dwell;
}
-void solenoid_stop(void) {
- SOLENOID_PIN_WRITE_INACTIVE();
- solenoid_on = false;
- solenoid_buzzing = false;
+/**
+ * @brief Stops a specific solenoid
+ *
+ * @param index select which solenoid to check/stop
+ */
+void solenoid_stop(uint8_t index) {
+ writePin(solenoid_pads[index], !solenoid_active_state[index]);
+ solenoid_on[index] = false;
+ solenoid_buzzing[index] = false;
}
-void solenoid_fire(void) {
- if (!haptic_config.buzz && solenoid_on) return;
- if (haptic_config.buzz && solenoid_buzzing) return;
+/**
+ * @brief Fires off a specific solenoid
+ *
+ * @param index Selects which solenoid to fire
+ */
+void solenoid_fire(uint8_t index) {
+ if (!haptic_config.buzz && solenoid_on[index]) return;
+ if (haptic_config.buzz && solenoid_buzzing[index]) return;
+
+ solenoid_on[index] = true;
+ solenoid_buzzing[index] = true;
+ solenoid_start[index] = timer_read();
+ writePin(solenoid_pads[index], solenoid_active_state[index]);
+}
- solenoid_on = true;
- solenoid_buzzing = true;
- solenoid_start = timer_read();
- SOLENOID_PIN_WRITE_ACTIVE();
+/**
+ * @brief Handles selecting a non-active solenoid, and firing it.
+ *
+ */
+void solenoid_fire_handler(void) {
+#ifndef SOLENOID_RANDOM_FIRE
+ if (NUMBER_OF_SOLENOIDS > 1) {
+ uint8_t i = rand() % NUMBER_OF_SOLENOIDS;
+ if (!solenoid_on[i]) {
+ solenoid_fire(i);
+ }
+ } else {
+ solenoid_fire(0);
+ }
+#else
+ for (uint8_t i = 0; i < NUMBER_OF_SOLENOIDS; i++) {
+ if (!solenoid_on[i]) {
+ solenoid_fire(i);
+ break;
+ }
+ }
+#endif
}
+/**
+ * @brief Checks active solenoid to stop them, and to handle buzz mode
+ *
+ */
void solenoid_check(void) {
- uint16_t elapsed = 0;
+ uint16_t elapsed[NUMBER_OF_SOLENOIDS] = {0};
- if (!solenoid_on) return;
+ for (uint8_t i = 0; i < NUMBER_OF_SOLENOIDS; i++) {
+ if (!solenoid_on[i]) continue;
- elapsed = timer_elapsed(solenoid_start);
+ elapsed[i] = timer_elapsed(solenoid_start[i]);
- // Check if it's time to finish this solenoid click cycle
- if (elapsed > solenoid_dwell) {
- solenoid_stop();
- return;
- }
+ // Check if it's time to finish this solenoid click cycle
+ if (elapsed[i] > solenoid_dwell) {
+ solenoid_stop(i);
+ continue;
+ }
- // Check whether to buzz the solenoid on and off
- if (haptic_config.buzz) {
- if ((elapsed % (SOLENOID_BUZZ_ACTUATED + SOLENOID_BUZZ_NONACTUATED)) < SOLENOID_BUZZ_ACTUATED) {
- if (!solenoid_buzzing) {
- solenoid_buzzing = true;
- SOLENOID_PIN_WRITE_ACTIVE();
- }
- } else {
- if (solenoid_buzzing) {
- solenoid_buzzing = false;
- SOLENOID_PIN_WRITE_INACTIVE();
+ // Check whether to buzz the solenoid on and off
+ if (haptic_config.buzz) {
+ if ((elapsed[i] % (SOLENOID_BUZZ_ACTUATED + SOLENOID_BUZZ_NONACTUATED)) < SOLENOID_BUZZ_ACTUATED) {
+ if (!solenoid_buzzing[i]) {
+ solenoid_buzzing[i] = true;
+ writePin(solenoid_pads[i], solenoid_active_state[i]);
+ }
+ } else {
+ if (solenoid_buzzing[i]) {
+ solenoid_buzzing[i] = false;
+ writePin(solenoid_pads[i], !solenoid_active_state[i]);
+ }
}
}
}
}
+/**
+ * @brief Initial configuration for solenoids
+ *
+ */
void solenoid_setup(void) {
- SOLENOID_PIN_WRITE_INACTIVE();
- setPinOutput(SOLENOID_PIN);
- if ((!HAPTIC_OFF_IN_LOW_POWER) || (usb_device_state == USB_DEVICE_STATE_CONFIGURED)) {
- solenoid_fire();
+#ifdef SOLENOID_PINS_ACTIVE_STATE
+ bool state_temp[] = SOLENOID_PINS_ACTIVE_STATE;
+ uint8_t bound_check = (sizeof(state_temp) / sizeof(bool));
+#endif
+
+ for (uint8_t i = 0; i < NUMBER_OF_SOLENOIDS; i++) {
+#ifdef SOLENOID_PINS_ACTIVE_STATE
+ solenoid_active_state[i] = (bound_check - i) ? state_temp[i] : high;
+#else
+ solenoid_active_state[i] = high;
+#endif
+ writePin(solenoid_pads[i], !solenoid_active_state[i]);
+ setPinOutput(solenoid_pads[i]);
+ if ((!HAPTIC_OFF_IN_LOW_POWER) || (usb_device_state == USB_DEVICE_STATE_CONFIGURED)) {
+ solenoid_fire(i);
+ }
}
}
+/**
+ * @brief stops solenoids prior to device reboot, to prevent them from being locked on
+ *
+ */
void solenoid_shutdown(void) {
- SOLENOID_PIN_WRITE_INACTIVE();
+ for (uint8_t i = 0; i < NUMBER_OF_SOLENOIDS; i++) {
+ writePin(solenoid_pads[i], !solenoid_active_state[i]);
+ }
}
diff --git a/drivers/haptic/solenoid.h b/drivers/haptic/solenoid.h
index 653148154f..952f86e922 100644
--- a/drivers/haptic/solenoid.h
+++ b/drivers/haptic/solenoid.h
@@ -45,26 +45,24 @@
# define SOLENOID_BUZZ_NONACTUATED SOLENOID_MIN_DWELL
#endif
-#ifndef SOLENOID_PIN
-# error SOLENOID_PIN not defined
+#ifndef SOLENOID_PINS
+# ifdef SOLENOID_PIN
+# define SOLENOID_PINS \
+ { SOLENOID_PIN }
+# else
+# error SOLENOID_PINS array not defined
+# endif
#endif
-#ifdef SOLENOID_PIN_ACTIVE_LOW
-# define SOLENOID_PIN_WRITE_ACTIVE() writePinLow(SOLENOID_PIN)
-# define SOLENOID_PIN_WRITE_INACTIVE() writePinHigh(SOLENOID_PIN)
-#else
-# define SOLENOID_PIN_WRITE_ACTIVE() writePinHigh(SOLENOID_PIN)
-# define SOLENOID_PIN_WRITE_INACTIVE() writePinLow(SOLENOID_PIN)
-#endif
-
-void solenoid_buzz_on(void);
+void solenoidbuzz_on(void);
void solenoid_buzz_off(void);
-void solenoid_set_buzz(int buzz);
+void solenoid_set_buzz(uint8_t buzz);
void solenoid_set_dwell(uint8_t dwell);
-void solenoid_stop(void);
-void solenoid_fire(void);
+void solenoid_stop(uint8_t index);
+void solenoid_fire(uint8_t index);
+void solenoid_fire_handler(void);
void solenoid_check(void);
diff --git a/keyboards/handwired/onekey/blackpill_f411/config.h b/keyboards/handwired/onekey/blackpill_f411/config.h
index 16e8f27924..3797217153 100644
--- a/keyboards/handwired/onekey/blackpill_f411/config.h
+++ b/keyboards/handwired/onekey/blackpill_f411/config.h
@@ -31,3 +31,7 @@
#define RGB_DI_PIN A1
#define ADC_PIN A0
+
+#define SOLENOID_PIN B12
+#define SOLENOID_PINS { B12, B13, B14, B15 }
+#define SOLENOID_PINS_ACTIVE_STATE { high, high, low }
diff --git a/keyboards/handwired/onekey/keymaps/haptic/keymap.c b/keyboards/handwired/onekey/keymaps/haptic/keymap.c
new file mode 100644
index 0000000000..e3b0093c18
--- /dev/null
+++ b/keyboards/handwired/onekey/keymaps/haptic/keymap.c
@@ -0,0 +1,11 @@
+#include QMK_KEYBOARD_H
+
+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+ LAYOUT_ortho_1x1(KC_A)
+};
+
+void haptic_enable(void);
+
+void keyboard_post_init_user(void) {
+ haptic_enable();
+}
diff --git a/keyboards/handwired/onekey/keymaps/haptic/rules.mk b/keyboards/handwired/onekey/keymaps/haptic/rules.mk
new file mode 100644
index 0000000000..802f7e5dde
--- /dev/null
+++ b/keyboards/handwired/onekey/keymaps/haptic/rules.mk
@@ -0,0 +1,2 @@
+HAPTIC_ENABLE = yes
+HAPTIC_DRIVER = SOLENOID
diff --git a/quantum/haptic.c b/quantum/haptic.c
index 31d1114806..ad64fe2cc7 100644
--- a/quantum/haptic.c
+++ b/quantum/haptic.c
@@ -321,7 +321,7 @@ void haptic_play(void) {
DRV_pulse(play_eff);
#endif
#ifdef SOLENOID_ENABLE
- solenoid_fire();
+ solenoid_fire_handler();
#endif
}