Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

haptic: Feature to disable haptic feedback when USB is not configured or suspended #12487

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion common_features.mk
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ QUANTUM_SRC += \
$(QUANTUM_DIR)/bitwise.c \
$(QUANTUM_DIR)/led.c \
$(QUANTUM_DIR)/keymap_common.c \
$(QUANTUM_DIR)/keycode_config.c
$(QUANTUM_DIR)/keycode_config.c \
$(QUANTUM_DIR)/power.c

ifeq ($(strip $(DEBUG_MATRIX_SCAN_RATE_ENABLE)), yes)
OPT_DEFS += -DDEBUG_MATRIX_SCAN_RATE
Expand Down
11 changes: 11 additions & 0 deletions docs/feature_haptic_feedback.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ The following options are currently available for haptic feedback in `rules.mk`:

`HAPTIC_ENABLE += SOLENOID`

The following `config.h` settings are available for all types of haptic feedback:

| Settings | Default | Description |
|--------------------------------------|---------------|---------------------------------------------------------------------------------------------------------------|
|`HAPTIC_ENABLE_PIN` | *Not defined* |Configures a pin to enable a boost converter for some haptic solution, often used with solenoid drivers. |
|`HAPTIC_ENABLE_PIN_ACTIVE_LOW` | *Not defined* |If defined then the haptic enable pin is active-low. |
|`HAPTIC_ENABLE_STATUS_LED` | *Not defined* |Configures a pin to reflect the current enabled/disabled status of haptic feedback. |
|`HAPTIC_ENABLE_STATUS_LED_ACTIVE_LOW` | *Not defined* |If defined then the haptic status led will be active-low. |
|`HAPTIC_OFF_IN_LOW_POWER` | `0` |If set to `1`, haptic feedback is disabled before the device is configured, and while the device is suspended. |

## Known Supported Hardware

| Name | Description |
Expand Down Expand Up @@ -45,6 +55,7 @@ First you will need a build a circuit to drive the solenoid through a mosfet as
| 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. |
Expand Down
25 changes: 21 additions & 4 deletions drivers/haptic/haptic.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "eeconfig.h"
#include "progmem.h"
#include "debug.h"
#include "power.h"
#ifdef DRV2605L
# include "DRV2605L.h"
#endif
Expand All @@ -27,6 +28,18 @@

haptic_config_t haptic_config;

static void set_haptic_config_enable(bool enabled)
{
haptic_config.enable = enabled;
#ifdef HAPTIC_ENABLE_PIN
if (enabled && ((!HAPTIC_OFF_IN_LOW_POWER) || high_power_state)) {
HAPTIC_ENABLE_PIN_WRITE_ACTIVE();
} else {
HAPTIC_ENABLE_PIN_WRITE_INACTIVE();
}
#endif
}

void haptic_init(void) {
debug_enable = 1; // Debug is ON!
if (!eeconfig_is_enabled()) {
Expand All @@ -46,6 +59,10 @@ void haptic_init(void) {
// or the previous firmware didn't have solenoid enabled,
// and the current one has solenoid enabled.
haptic_reset();
} else {
// Haptic configuration has been loaded through the "raw" union item.
// This is to execute any side effects of the configuration.
set_haptic_config_enable(haptic_config.enable);
}
#ifdef SOLENOID_ENABLE
solenoid_setup();
Expand All @@ -71,13 +88,13 @@ void eeconfig_debug_haptic(void) {
}

void haptic_enable(void) {
haptic_config.enable = 1;
set_haptic_config_enable(true);
xprintf("haptic_config.enable = %u\n", haptic_config.enable);
eeconfig_update_haptic(haptic_config.raw);
}

void haptic_disable(void) {
haptic_config.enable = 0;
set_haptic_config_enable(false);
xprintf("haptic_config.enable = %u\n", haptic_config.enable);
eeconfig_update_haptic(haptic_config.raw);
}
Expand Down Expand Up @@ -159,7 +176,7 @@ void haptic_dwell_decrease(void) {
}

void haptic_reset(void) {
haptic_config.enable = true;
set_haptic_config_enable(true);
uint8_t feedback = HAPTIC_FEEDBACK_DEFAULT;
haptic_config.feedback = feedback;
#ifdef DRV2605L
Expand Down Expand Up @@ -332,7 +349,7 @@ bool process_haptic(uint16_t keycode, keyrecord_t *record) {
haptic_cont_decrease();
}

if (haptic_config.enable) {
if (haptic_config.enable && ((!HAPTIC_OFF_IN_LOW_POWER) || high_power_state)) {
if (record->event.pressed) {
// keypress
if (haptic_config.feedback < 2) {
Expand Down
16 changes: 10 additions & 6 deletions drivers/haptic/solenoid.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "timer.h"
#include "solenoid.h"
#include "haptic.h"
#include "power.h"

bool solenoid_on = false;
bool solenoid_buzzing = false;
Expand All @@ -35,7 +36,7 @@ void solenoid_set_buzz(int buzz) { haptic_set_buzz(buzz); }
void solenoid_set_dwell(uint8_t dwell) { solenoid_dwell = dwell; }

void solenoid_stop(void) {
writePinLow(SOLENOID_PIN);
SOLENOID_PIN_WRITE_INACTIVE();
solenoid_on = false;
solenoid_buzzing = false;
}
Expand All @@ -47,7 +48,7 @@ void solenoid_fire(void) {
solenoid_on = true;
solenoid_buzzing = true;
solenoid_start = timer_read();
writePinHigh(SOLENOID_PIN);
SOLENOID_PIN_WRITE_ACTIVE();
}

void solenoid_check(void) {
Expand All @@ -68,20 +69,23 @@ void solenoid_check(void) {
if ((elapsed % (SOLENOID_BUZZ_ACTUATED + SOLENOID_BUZZ_NONACTUATED)) < SOLENOID_BUZZ_ACTUATED) {
if (!solenoid_buzzing) {
solenoid_buzzing = true;
writePinHigh(SOLENOID_PIN);
SOLENOID_PIN_WRITE_ACTIVE();
}
} else {
if (solenoid_buzzing) {
solenoid_buzzing = false;
writePinLow(SOLENOID_PIN);
SOLENOID_PIN_WRITE_INACTIVE();
}
}
}
}

void solenoid_setup(void) {
SOLENOID_PIN_WRITE_INACTIVE();
setPinOutput(SOLENOID_PIN);
solenoid_fire();
if ((!HAPTIC_OFF_IN_LOW_POWER) || high_power_state) {
solenoid_fire();
}
}

void solenoid_shutdown(void) { writePinLow(SOLENOID_PIN); }
void solenoid_shutdown(void) { SOLENOID_PIN_WRITE_INACTIVE(); }
8 changes: 8 additions & 0 deletions drivers/haptic/solenoid.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@
# error SOLENOID_PIN not defined
#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 solenoid_buzz_off(void);
void solenoid_set_buzz(int buzz);
Expand Down
81 changes: 81 additions & 0 deletions quantum/power.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright 2021 Andrei Purdea <[email protected]>
*
* 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 "power.h"
#if defined(HAPTIC_ENABLE)
#include "haptic.h"
extern haptic_config_t haptic_config;
#endif

bool high_power_state;

static void update_haptic_config_enable_and_status_pins(void)
{
#if defined(HAPTIC_ENABLE)
if (haptic_config.enable && ((HAPTIC_OFF_IN_LOW_POWER != 0) || high_power_state)) {
# if defined(HAPTIC_ENABLE_PIN)
HAPTIC_ENABLE_PIN_WRITE_ACTIVE();
# endif
# if defined(HAPTIC_ENABLE_STATUS_LED)
HAPTIC_ENABLE_STATUS_LED_WRITE_ACTIVE();
# endif
} else {
# if defined(HAPTIC_ENABLE_PIN)
HAPTIC_ENABLE_PIN_WRITE_INACTIVE();
# endif
# if defined(HAPTIC_ENABLE_STATUS_LED)
HAPTIC_ENABLE_STATUS_LED_WRITE_INACTIVE();
# endif
}
#endif
}

void power_set_configuration(bool isConfigured, uint8_t configurationNumber)
{
high_power_state = isConfigured;
update_haptic_config_enable_and_status_pins();
}

void power_set_suspend(bool isConfigured, uint8_t configurationNumber)
{
high_power_state = false;
update_haptic_config_enable_and_status_pins();
}

void power_set_resume(bool isConfigured, uint8_t configurationNumber)
{
high_power_state = isConfigured;
update_haptic_config_enable_and_status_pins();
}

void power_set_reset(void)
{
high_power_state = false;
update_haptic_config_enable_and_status_pins();
}

void power_init(void)
{
high_power_state = false;
update_haptic_config_enable_and_status_pins();
#if defined(HAPTIC_ENABLE_PIN)
setPinOutput(HAPTIC_ENABLE_PIN);
#endif
#if defined(HAPTIC_ENABLE_STATUS_LED)
setPinOutput(HAPTIC_ENABLE_STATUS_LED);
#endif
}
54 changes: 54 additions & 0 deletions quantum/power.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright 2021 Andrei Purdea <[email protected]>
*
* 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 "quantum.h"

void power_set_configuration(bool isConfigured, uint8_t configurationNumber);
void power_set_suspend(bool isConfigured, uint8_t configurationNumber);
void power_set_resume(bool isConfigured, uint8_t configurationNumber);
void power_set_reset(void);
void power_init(void);

extern bool high_power_state;

#ifdef HAPTIC_ENABLE_PIN_ACTIVE_LOW
# ifndef HAPTIC_ENABLE_PIN
# error HAPTIC_ENABLE_PIN not defined
# endif
# define HAPTIC_ENABLE_PIN_WRITE_ACTIVE() writePinLow(HAPTIC_ENABLE_PIN)
# define HAPTIC_ENABLE_PIN_WRITE_INACTIVE() writePinHigh(HAPTIC_ENABLE_PIN)
#else
# define HAPTIC_ENABLE_PIN_WRITE_ACTIVE() writePinHigh(HAPTIC_ENABLE_PIN)
# define HAPTIC_ENABLE_PIN_WRITE_INACTIVE() writePinLow(HAPTIC_ENABLE_PIN)
#endif

#ifdef HAPTIC_ENABLE_STATUS_LED_ACTIVE_LOW
# ifndef HAPTIC_ENABLE_STATUS_LED
# error HAPTIC_ENABLE_STATUS_LED not defined
# endif
# define HAPTIC_ENABLE_STATUS_LED_WRITE_ACTIVE() writePinLow(HAPTIC_ENABLE_STATUS_LED)
# define HAPTIC_ENABLE_STATUS_LED_WRITE_INACTIVE() writePinHigh(HAPTIC_ENABLE_STATUS_LED)
#else
# define HAPTIC_ENABLE_STATUS_LED_WRITE_ACTIVE() writePinHigh(HAPTIC_ENABLE_STATUS_LED)
# define HAPTIC_ENABLE_STATUS_LED_WRITE_INACTIVE() writePinLow(HAPTIC_ENABLE_STATUS_LED)
#endif

#ifndef HAPTIC_OFF_IN_LOW_POWER
# define HAPTIC_OFF_IN_LOW_POWER 0
#endif
3 changes: 3 additions & 0 deletions tmk_core/protocol/chibios/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "keyboard.h"
#include "action.h"
#include "action_util.h"
#include "power.h"
#include "mousekey.h"
#include "led.h"
#include "sendchar.h"
Expand Down Expand Up @@ -157,6 +158,8 @@ int main(void) {
eeprom_driver_init();
#endif

power_init();

// TESTING
// chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, NULL);

Expand Down
15 changes: 14 additions & 1 deletion tmk_core/protocol/chibios/usb_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
# include "led.h"
#endif
#include "wait.h"
#include "power.h"
#include "usb_descriptor.h"
#include "usb_driver.h"

Expand Down Expand Up @@ -401,13 +402,15 @@ static inline bool usb_event_queue_dequeue(usbevent_t *event) {
}

static inline void usb_event_suspend_handler(void) {
power_set_suspend(USB_DRIVER.configuration!=0, USB_DRIVER.configuration);
#ifdef SLEEP_LED_ENABLE
sleep_led_enable();
#endif /* SLEEP_LED_ENABLE */
}

static inline void usb_event_wakeup_handler(void) {
suspend_wakeup_init();
power_set_resume(USB_DRIVER.configuration!=0, USB_DRIVER.configuration);
#ifdef SLEEP_LED_ENABLE
sleep_led_disable();
// NOTE: converters may not accept this
Expand All @@ -425,6 +428,15 @@ void usb_event_queue_task(void) {
case USB_EVENT_WAKEUP:
usb_event_wakeup_handler();
break;
case USB_EVENT_CONFIGURED:
power_set_configuration(USB_DRIVER.configuration!=0, USB_DRIVER.configuration);
break;
case USB_EVENT_UNCONFIGURED:
power_set_configuration(false, 0);
break;
case USB_EVENT_RESET:
power_set_reset();
break;
default:
// Nothing to do, we don't handle it.
break;
Expand Down Expand Up @@ -464,9 +476,9 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) {
qmkusbConfigureHookI(&drivers.array[i].driver);
}
osalSysUnlockFromISR();
usb_event_queue_enqueue(USB_EVENT_CONFIGURED);
return;
case USB_EVENT_SUSPEND:
usb_event_queue_enqueue(USB_EVENT_SUSPEND);
/* Falls into.*/
case USB_EVENT_UNCONFIGURED:
/* Falls into.*/
Expand All @@ -477,6 +489,7 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) {
qmkusbSuspendHookI(&drivers.array[i].driver);
chSysUnlockFromISR();
}
usb_event_queue_enqueue(event);
return;

case USB_EVENT_WAKEUP:
Expand Down
Loading