diff --git a/common_features.mk b/common_features.mk index 8c9d0a90c70e..74ef8ac29607 100644 --- a/common_features.mk +++ b/common_features.mk @@ -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 diff --git a/docs/feature_haptic_feedback.md b/docs/feature_haptic_feedback.md index a092e784c779..0d3ef4875149 100644 --- a/docs/feature_haptic_feedback.md +++ b/docs/feature_haptic_feedback.md @@ -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 | @@ -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. | diff --git a/drivers/haptic/haptic.c b/drivers/haptic/haptic.c index de3f400527a0..be2d474b694a 100644 --- a/drivers/haptic/haptic.c +++ b/drivers/haptic/haptic.c @@ -18,6 +18,7 @@ #include "eeconfig.h" #include "progmem.h" #include "debug.h" +#include "power.h" #ifdef DRV2605L # include "DRV2605L.h" #endif @@ -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()) { @@ -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(); @@ -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); } @@ -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 @@ -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) { diff --git a/drivers/haptic/solenoid.c b/drivers/haptic/solenoid.c index 3e61d5a171ff..020a71f3405b 100644 --- a/drivers/haptic/solenoid.c +++ b/drivers/haptic/solenoid.c @@ -18,6 +18,7 @@ #include "timer.h" #include "solenoid.h" #include "haptic.h" +#include "power.h" bool solenoid_on = false; bool solenoid_buzzing = false; @@ -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; } @@ -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) { @@ -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(); } diff --git a/drivers/haptic/solenoid.h b/drivers/haptic/solenoid.h index f2a3bc4c3094..653148154f40 100644 --- a/drivers/haptic/solenoid.h +++ b/drivers/haptic/solenoid.h @@ -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); diff --git a/quantum/power.c b/quantum/power.c new file mode 100644 index 000000000000..62929b59bf70 --- /dev/null +++ b/quantum/power.c @@ -0,0 +1,81 @@ +/* + * Copyright 2021 Andrei Purdea + * + * 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 "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 +} diff --git a/quantum/power.h b/quantum/power.h new file mode 100644 index 000000000000..61c573eebc1f --- /dev/null +++ b/quantum/power.h @@ -0,0 +1,54 @@ +/* + * Copyright 2021 Andrei Purdea + * + * 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 "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 diff --git a/tmk_core/protocol/chibios/main.c b/tmk_core/protocol/chibios/main.c index 63e4c99d21ac..10edcc38c0f9 100644 --- a/tmk_core/protocol/chibios/main.c +++ b/tmk_core/protocol/chibios/main.c @@ -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" @@ -157,6 +158,8 @@ int main(void) { eeprom_driver_init(); #endif + power_init(); + // TESTING // chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, NULL); diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index 8adecfa71913..72d63f511cec 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -39,6 +39,7 @@ # include "led.h" #endif #include "wait.h" +#include "power.h" #include "usb_descriptor.h" #include "usb_driver.h" @@ -401,6 +402,7 @@ 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 */ @@ -408,6 +410,7 @@ static inline void usb_event_suspend_handler(void) { 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 @@ -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; @@ -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.*/ @@ -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: diff --git a/tmk_core/protocol/lufa/lufa.c b/tmk_core/protocol/lufa/lufa.c index f1908b3d0755..618585abc6f9 100644 --- a/tmk_core/protocol/lufa/lufa.c +++ b/tmk_core/protocol/lufa/lufa.c @@ -52,6 +52,7 @@ #include "usb_descriptor.h" #include "lufa.h" #include "quantum.h" +#include "power.h" #include #ifdef NKRO_ENABLE @@ -416,7 +417,10 @@ void EVENT_USB_Device_Disconnect(void) { * * FIXME: Needs doc */ -void EVENT_USB_Device_Reset(void) { print("[R]"); } +void EVENT_USB_Device_Reset(void) { + print("[R]"); + power_set_reset(); +} /** \brief Event USB Device Connect * @@ -424,6 +428,8 @@ void EVENT_USB_Device_Reset(void) { print("[R]"); } */ void EVENT_USB_Device_Suspend() { print("[S]"); + power_set_suspend(USB_Device_ConfigurationNumber != 0, USB_Device_ConfigurationNumber); + #ifdef SLEEP_LED_ENABLE sleep_led_enable(); #endif @@ -439,6 +445,8 @@ void EVENT_USB_Device_WakeUp() { suspend_wakeup_init(); #endif + power_set_resume(USB_DeviceState == DEVICE_STATE_Configured, USB_Device_ConfigurationNumber); + #ifdef SLEEP_LED_ENABLE sleep_led_disable(); // NOTE: converters may not accept this @@ -526,6 +534,8 @@ void EVENT_USB_Device_ConfigurationChanged(void) { /* Setup joystick endpoint */ ConfigSuccess &= Endpoint_ConfigureEndpoint((JOYSTICK_IN_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_INTERRUPT, JOYSTICK_EPSIZE, 1); #endif + + power_set_configuration(USB_DeviceState == DEVICE_STATE_Configured, USB_Device_ConfigurationNumber); } /* FIXME: Expose this table in the docs somehow @@ -1023,6 +1033,7 @@ int main(void) { #endif setup_mcu(); + power_init(); keyboard_setup(); setup_usb(); sei();