diff --git a/core/embed/trezorhal/haptic.h b/core/embed/trezorhal/haptic.h index f4f1c66bc..ca7223b65 100644 --- a/core/embed/trezorhal/haptic.h +++ b/core/embed/trezorhal/haptic.h @@ -1,3 +1,21 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 3 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 . + */ #ifndef TREZORHAL_HAPTIC_H #define TREZORHAL_HAPTIC_H @@ -6,30 +24,64 @@ #include typedef enum { + // Effect at the start of a button press HAPTIC_BUTTON_PRESS = 0, - HAPTIC_ALERT = 1, - HAPTIC_HOLD_TO_CONFIRM = 2, + // Effect at the and of hold-to-confirm action + HAPTIC_HOLD_TO_CONFIRM = 1, } haptic_effect_t; -// Initialize haptic driver -void haptic_init(void); +// Initializes the haptic driver +// +// The function initializes the GPIO pins and the hardware +// peripherals used by the haptic driver. +// +// Returns `true` if the initialization was successful. +bool haptic_init(void); + +// Deinitializes the haptic driver +// +// The function deinitializes the hardware peripherals used by the +// haptic driver so the device can be eventually put into a low-power mode. +void haptic_deinit(void); + +// Enables or disables the haptic driver +// +// When the driver is disabled, it does not play any haptic effects +// and potentially can put the controller into a low-power mode. +// +// The driver is enabled by default (after initialization). +void haptic_set_enabled(bool enabled); -// Calibrate haptic driver -void haptic_calibrate(void); +// Returns `true` if haptic driver is enabled +bool haptic_get_enabled(void); -// Test haptic driver, plays a maximum amplitude for the given duration +// Tests the haptic driver, playing at maximum amplitude for the given duration +// +// This function is used during production testing to verify that the haptic +// motor is working correctly. +// +// Returns `true` if the test effect was successfully started. bool haptic_test(uint16_t duration_ms); -// Play haptic effect -void haptic_play(haptic_effect_t effect); +// Plays one of haptic effects +// +// The function stops playing any currently running effect and +// starts playing the specified effect. +// +// Returns `true` if the effect was successfully started. +bool haptic_play(haptic_effect_t effect); -// Starts the haptic motor with a specified amplitude and period +// Starts the haptic motor with a specified amplitude (in percent) for a +// specified duration (in milliseconds). +// +// The function stops playing any currently running effect and +// starts playing the specified effect. // // The function can be invoked repeatedly during the specified duration // (`duration_ms`) to modify the amplitude dynamically, allowing // the creation of customized haptic effects. +// +// Returns `true` if the effect was successfully started. bool haptic_play_custom(int8_t amplitude_pct, uint16_t duration_ms); -void haptic_set_enabled(bool enable); - -#endif +#endif // TREZORHAL_HAPTIC_H diff --git a/core/embed/trezorhal/stm32u5/haptic/drv2625/drv2625.c b/core/embed/trezorhal/stm32u5/haptic/drv2625/drv2625.c index a4cf9e6c7..4f81f5ba6 100644 --- a/core/embed/trezorhal/stm32u5/haptic/drv2625/drv2625.c +++ b/core/embed/trezorhal/stm32u5/haptic/drv2625/drv2625.c @@ -1,61 +1,46 @@ -#include "drv2625_lib.h" -#include "haptic.h" +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 3 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 TREZOR_BOARD #include +#include + +#include "common.h" +#include "drv2625.h" +#include "haptic.h" +#include "i2c.h" #include STM32_HAL_H -#include "i2c.h" -#include TREZOR_BOARD -#include HAPTIC_ACTUATOR +// Maximum amplitude of the vibration effect +// (DRV2625 supports 7-bit amplitude) +#define MAX_AMPLITUDE 127 +// Amplitude of the vibration effect used for production test +#define PRODTEST_EFFECT_AMPLITUDE 127 +// Amplitude of the button press effect +#define PRESS_EFFECT_AMPLITUDE 25 +// Duration of the button press effect +#define PRESS_EFFECT_DURATION 10 -#define DRV2625_I2C_ADDRESS (0x5A << 1) - -#define DRV2625_REG_CHIPID 0x00 -#define DRV2625_REG_STATUS 0x01 -#define DRV2625_REG_MODE 0x07 -#define DRV2625_REG_MODE_RTP 0 -#define DRV2625_REG_MODE_WAVEFORM 0x01 -#define DRV2625_REG_MODE_DIAG 0x02 -#define DRV2625_REG_MODE_AUTOCAL 0x03 -#define DRV2625_REG_MODE_TRGFUNC_PULSE 0x00 -#define DRV2625_REG_MODE_TRGFUNC_ENABLE 0x04 -#define DRV2625_REG_MODE_TRGFUNC_INTERRUPT 0x08 - -#define DRV2625_REG_LRAERM 0x08 -#define DRV2625_REG_LRAERM_LRA 0x80 -#define DRV2625_REG_LRAERM_OPENLOOP 0x40 -#define DRV2625_REG_LRAERM_AUTO_BRK_OL 0x10 -#define DRV2625_REG_LRAERM_AUTO_BRK_STBY 0x08 - -#define DRV2625_REG_LIBRARY 0x0D ///< Waveform library selection register -#define DRV2625_REG_LIBRARY_OPENLOOP 0x40 -#define DRV2625_REG_LIBRARY_GAIN_100 0x00 -#define DRV2625_REG_LIBRARY_GAIN_75 0x01 -#define DRV2625_REG_LIBRARY_GAIN_50 0x02 -#define DRV2625_REG_LIBRARY_GAIN_25 0x03 - -#define DRV2625_REG_RTP 0x0E ///< RTP input register - -#define DRV2625_REG_WAVESEQ1 0x0F ///< Waveform sequence register 1 -#define DRV2625_REG_WAVESEQ2 0x10 ///< Waveform sequence register 2 -#define DRV2625_REG_WAVESEQ3 0x11 ///< Waveform sequence register 3 -#define DRV2625_REG_WAVESEQ4 0x12 ///< Waveform sequence register 4 -#define DRV2625_REG_WAVESEQ5 0x13 ///< Waveform sequence register 5 -#define DRV2625_REG_WAVESEQ6 0x14 ///< Waveform sequence register 6 -#define DRV2625_REG_WAVESEQ7 0x15 ///< Waveform sequence register 7 -#define DRV2625_REG_WAVESEQ8 0x16 ///< Waveform sequence register 8 - -#define DRV2625_REG_GO 0x0C ///< Go register -#define DRV2625_REG_GO_GO 0x01 - -#define DRV2625_REG_OD_CLAMP 0x20 - -#define DRV2625_REG_LRA_WAVE_SHAPE 0x2C -#define DRV2625_REG_LRA_WAVE_SHAPE_SINE 0x01 - -#define DRV2625_REG_OL_LRA_PERIOD_LO 0x2F -#define DRV2625_REG_OL_LRA_PERIOD_HI 0x2E +// Actuator configuration +#include HAPTIC_ACTUATOR #if defined ACTUATOR_CLOSED_LOOP #define LIB_SEL 0x00 @@ -75,45 +60,68 @@ #error "Must define either LRA or ERM" #endif -#define PRESS_EFFECT_AMPLITUDE 25 -#define PRESS_EFFECT_DURATION 10 - -#define MAX_AMPLITUDE 127 -#define PRODTEST_EFFECT_AMPLITUDE 127 - -bool haptic_enabled = true; - -static bool set_reg(uint8_t addr, uint8_t value) { +// Driver state +typedef struct { + // Set if driver is initialized + bool initialized; + // Set if driver is enabled + bool enabled; + // Set to if real-time playing is activated. + // This prevents the repeated set of `DRV2625_REG_MODE` register + // which would otherwise stop all playback. + bool playing_rtp; + +} haptic_driver_t; + +// Haptic driver instance +static haptic_driver_t g_haptic_driver = { + .initialized = false, +}; + +static bool drv2625_set_reg(uint8_t addr, uint8_t value) { uint8_t data[] = {addr, value}; return i2c_transmit(DRV2625_I2C_INSTANCE, DRV2625_I2C_ADDRESS, data, sizeof(data), 1) == HAL_OK; } -void haptic_calibrate(void) { - set_reg(DRV2625_REG_MODE, DRV2625_REG_MODE_AUTOCAL); - HAL_Delay(1); - set_reg(DRV2625_REG_GO, DRV2625_REG_GO_GO); +bool haptic_init(void) { + haptic_driver_t *driver = &g_haptic_driver; - HAL_Delay(3000); -} + if (driver->initialized) { + return false; + } -// Set to `true` if real-time playing is activated. to -// This prevents the repeated set of `DRV2625_REG_MODE` register -// which would otherwise stop all playback. -static bool playing_rtp = false; + memset(driver, 0, sizeof(haptic_driver_t)); -void haptic_init(void) { // select library - set_reg(DRV2625_REG_LIBRARY, LIB_SEL | DRV2625_REG_LIBRARY_GAIN_25); - set_reg(DRV2625_REG_LRAERM, - LRA_ERM_SEL | LOOP_SEL | DRV2625_REG_LRAERM_AUTO_BRK_OL); + if (!drv2625_set_reg(DRV2625_REG_LIBRARY, + LIB_SEL | DRV2625_REG_LIBRARY_GAIN_25)) { + return false; + } - set_reg(DRV2625_REG_OD_CLAMP, ACTUATOR_OD_CLAMP); + if (!drv2625_set_reg( + DRV2625_REG_LRAERM, + LRA_ERM_SEL | LOOP_SEL | DRV2625_REG_LRAERM_AUTO_BRK_OL)) { + } - set_reg(DRV2625_REG_LRA_WAVE_SHAPE, DRV2625_REG_LRA_WAVE_SHAPE_SINE); + if (!drv2625_set_reg(DRV2625_REG_OD_CLAMP, ACTUATOR_OD_CLAMP)) { + return false; + } - set_reg(DRV2625_REG_OL_LRA_PERIOD_LO, ACTUATOR_LRA_PERIOD & 0xFF); - set_reg(DRV2625_REG_OL_LRA_PERIOD_HI, ACTUATOR_LRA_PERIOD >> 8); + if (!drv2625_set_reg(DRV2625_REG_LRA_WAVE_SHAPE, + DRV2625_REG_LRA_WAVE_SHAPE_SINE)) { + return false; + } + + if (!drv2625_set_reg(DRV2625_REG_OL_LRA_PERIOD_LO, + ACTUATOR_LRA_PERIOD & 0xFF)) { + return false; + } + + if (!drv2625_set_reg(DRV2625_REG_OL_LRA_PERIOD_HI, + ACTUATOR_LRA_PERIOD >> 8)) { + return false; + } GPIO_InitTypeDef GPIO_InitStructure = {0}; GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; @@ -145,19 +153,59 @@ void haptic_init(void) { HAL_TIM_OC_Start(&TIM_Handle, TIM_CHANNEL_1); TIM16->BDTR |= TIM_BDTR_MOE; + + driver->initialized = true; + driver->enabled = true; + + return true; +} + +void haptic_deinit(void) { + haptic_driver_t *driver = &g_haptic_driver; + + if (!driver->initialized) { + return; + } + + // TODO: deinitialize GPIOs and the TIMER + + memset(driver, 0, sizeof(haptic_driver_t)); +} + +void haptic_set_enabled(bool enabled) { + haptic_driver_t *driver = &g_haptic_driver; + + driver->enabled = enabled; +} + +bool haptic_get_enabled(void) { + haptic_driver_t *driver = &g_haptic_driver; + + if (!driver->initialized) { + return false; + } + + return driver->enabled; } static bool haptic_play_rtp(int8_t amplitude, uint16_t duration_ms) { - if (!playing_rtp) { - if (!set_reg(DRV2625_REG_MODE, - DRV2625_REG_MODE_RTP | DRV2625_REG_MODE_TRGFUNC_ENABLE)) { + haptic_driver_t *driver = &g_haptic_driver; + + if (!driver->initialized) { + return false; + } + + if (!driver->playing_rtp) { + if (!drv2625_set_reg( + DRV2625_REG_MODE, + DRV2625_REG_MODE_RTP | DRV2625_REG_MODE_TRGFUNC_ENABLE)) { return false; } - playing_rtp = true; + driver->playing_rtp = true; } - if (!set_reg(DRV2625_REG_RTP, (uint8_t)amplitude)) { + if (!drv2625_set_reg(DRV2625_REG_RTP, (uint8_t)amplitude)) { return false; } @@ -176,33 +224,57 @@ static bool haptic_play_rtp(int8_t amplitude, uint16_t duration_ms) { return true; } -static void haptic_play_lib(drv2625_lib_effect_t effect) { - playing_rtp = false; +static bool haptic_play_lib(drv2625_lib_effect_t effect) { + haptic_driver_t *driver = &g_haptic_driver; - set_reg(DRV2625_REG_MODE, DRV2625_REG_MODE_WAVEFORM); - set_reg(DRV2625_REG_WAVESEQ1, effect); - set_reg(DRV2625_REG_WAVESEQ2, 0); - set_reg(DRV2625_REG_GO, DRV2625_REG_GO_GO); + if (!driver->initialized) { + return false; + } + + driver->playing_rtp = false; + + if (!drv2625_set_reg(DRV2625_REG_MODE, DRV2625_REG_MODE_WAVEFORM)) { + return false; + } + + if (!drv2625_set_reg(DRV2625_REG_WAVESEQ1, effect)) { + return false; + } + + if (!drv2625_set_reg(DRV2625_REG_WAVESEQ2, 0)) { + return false; + } + + if (!drv2625_set_reg(DRV2625_REG_GO, DRV2625_REG_GO_GO)) { + return false; + } + + return true; } -void haptic_play(haptic_effect_t effect) { - if (!haptic_enabled) { - return; +bool haptic_play(haptic_effect_t effect) { + haptic_driver_t *driver = &g_haptic_driver; + + if (!driver->initialized) { + return false; + } + + if (!driver->enabled) { + return true; } switch (effect) { case HAPTIC_BUTTON_PRESS: - haptic_play_rtp(PRESS_EFFECT_AMPLITUDE, PRESS_EFFECT_DURATION); - break; - case HAPTIC_ALERT: - haptic_play_lib(ALERT_750MS_100); + return haptic_play_rtp(PRESS_EFFECT_AMPLITUDE, PRESS_EFFECT_DURATION); break; case HAPTIC_HOLD_TO_CONFIRM: - haptic_play_lib(DOUBLE_CLICK_60); + return haptic_play_lib(DOUBLE_CLICK_60); break; default: break; } + + return false; } bool haptic_play_custom(int8_t amplitude_pct, uint16_t duration_ms) { @@ -219,5 +291,3 @@ bool haptic_play_custom(int8_t amplitude_pct, uint16_t duration_ms) { bool haptic_test(uint16_t duration_ms) { return haptic_play_rtp(PRODTEST_EFFECT_AMPLITUDE, duration_ms); } - -void haptic_set_enabled(bool enable) { haptic_enabled = enable; } diff --git a/core/embed/trezorhal/stm32u5/haptic/drv2625/drv2625_lib.h b/core/embed/trezorhal/stm32u5/haptic/drv2625/drv2625.h similarity index 65% rename from core/embed/trezorhal/stm32u5/haptic/drv2625/drv2625_lib.h rename to core/embed/trezorhal/stm32u5/haptic/drv2625/drv2625.h index dcf449f44..40a0d5e59 100644 --- a/core/embed/trezorhal/stm32u5/haptic/drv2625/drv2625_lib.h +++ b/core/embed/trezorhal/stm32u5/haptic/drv2625/drv2625.h @@ -1,5 +1,63 @@ -#ifndef __DRV_2625_LIB_H__ -#define __DRV_2625_LIB_H__ +#ifndef TREZOR_HAL_DRV_2625_H +#define TREZOR_HAL_DRV_2625_H + +// I2C address of the DRV2625 on the I2C bus. +// `<< 1` is required because the HAL expects the address to be shifted by 1. +#define DRV2625_I2C_ADDRESS (0x5A << 1) + +// ------------------------------------------------------------ +// DRV2625 registers +// ------------------------------------------------------------ + +#define DRV2625_REG_CHIPID 0x00 +#define DRV2625_REG_STATUS 0x01 +#define DRV2625_REG_MODE 0x07 +#define DRV2625_REG_MODE_RTP 0 +#define DRV2625_REG_MODE_WAVEFORM 0x01 +#define DRV2625_REG_MODE_DIAG 0x02 +#define DRV2625_REG_MODE_AUTOCAL 0x03 +#define DRV2625_REG_MODE_TRGFUNC_PULSE 0x00 +#define DRV2625_REG_MODE_TRGFUNC_ENABLE 0x04 +#define DRV2625_REG_MODE_TRGFUNC_INTERRUPT 0x08 + +#define DRV2625_REG_LRAERM 0x08 +#define DRV2625_REG_LRAERM_LRA 0x80 +#define DRV2625_REG_LRAERM_OPENLOOP 0x40 +#define DRV2625_REG_LRAERM_AUTO_BRK_OL 0x10 +#define DRV2625_REG_LRAERM_AUTO_BRK_STBY 0x08 + +#define DRV2625_REG_LIBRARY 0x0D ///< Waveform library selection register +#define DRV2625_REG_LIBRARY_OPENLOOP 0x40 +#define DRV2625_REG_LIBRARY_GAIN_100 0x00 +#define DRV2625_REG_LIBRARY_GAIN_75 0x01 +#define DRV2625_REG_LIBRARY_GAIN_50 0x02 +#define DRV2625_REG_LIBRARY_GAIN_25 0x03 + +#define DRV2625_REG_RTP 0x0E ///< RTP input register + +#define DRV2625_REG_WAVESEQ1 0x0F ///< Waveform sequence register 1 +#define DRV2625_REG_WAVESEQ2 0x10 ///< Waveform sequence register 2 +#define DRV2625_REG_WAVESEQ3 0x11 ///< Waveform sequence register 3 +#define DRV2625_REG_WAVESEQ4 0x12 ///< Waveform sequence register 4 +#define DRV2625_REG_WAVESEQ5 0x13 ///< Waveform sequence register 5 +#define DRV2625_REG_WAVESEQ6 0x14 ///< Waveform sequence register 6 +#define DRV2625_REG_WAVESEQ7 0x15 ///< Waveform sequence register 7 +#define DRV2625_REG_WAVESEQ8 0x16 ///< Waveform sequence register 8 + +#define DRV2625_REG_GO 0x0C ///< Go register +#define DRV2625_REG_GO_GO 0x01 + +#define DRV2625_REG_OD_CLAMP 0x20 + +#define DRV2625_REG_LRA_WAVE_SHAPE 0x2C +#define DRV2625_REG_LRA_WAVE_SHAPE_SINE 0x01 + +#define DRV2625_REG_OL_LRA_PERIOD_LO 0x2F +#define DRV2625_REG_OL_LRA_PERIOD_HI 0x2E + +// ------------------------------------------------------------ +// DRV2625 effect types +// ------------------------------------------------------------ typedef enum { STRONG_CLICK_100 = 1, @@ -127,4 +185,4 @@ typedef enum { SMOOTH_HUM_5_20 = 123, } drv2625_lib_effect_t; -#endif +#endif // TREZOR_HAL_DRV_2625_H