1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-04-02 16:35:55 +00:00

feat(core): add event polling to button driver

[no changelog]
This commit is contained in:
cepetr 2025-03-20 15:29:14 +01:00
parent 149abfefc5
commit cb2c4d17eb
6 changed files with 147 additions and 99 deletions

View File

@ -68,8 +68,4 @@ void button_deinit(void);
bool button_get_event(button_event_t* event);
// Checks if the specified button is currently pressed
//
// The current implementation returns the state of the button at the time
// `button_get_event()` was called. In the future, we may fix this limitation.
// For now, `button_get_event()` must be called before `button_is_down()`.
bool button_is_down(button_t button);

View File

@ -23,6 +23,8 @@
#include <io/button.h>
#include <sys/irq.h>
#include <sys/mpu.h>
#include <sys/sysevent_source.h>
#include <sys/systick.h>
#ifdef USE_POWERCTL
#include <sys/wakeup_flags.h>
@ -30,19 +32,23 @@
#ifdef KERNEL_MODE
typedef struct {
// Time of last update of pressed/released data
uint64_t time;
// Button presses that were detected since last get_event call
uint32_t pressed;
// Button releases that were detected since last get_event call
uint32_t released;
// State of buttons signalled to the poller
uint32_t state;
} button_tls_t;
// Button driver state
typedef struct {
bool initialized;
#ifdef BTN_LEFT_PIN
bool left_down;
#endif
#ifdef BTN_RIGHT_PIN
bool right_down;
#endif
#ifdef BTN_POWER_PIN
bool power_down;
#endif
// Task local storage for button driver
button_tls_t tls[SYSTASK_MAX_TASKS];
} button_driver_t;
@ -51,7 +57,10 @@ static button_driver_t g_button_driver = {
.initialized = false,
};
static void button_setup_pin(GPIO_TypeDef *port, uint16_t pin) {
// Forward declarations
static const syshandle_vmt_t g_button_handle_vmt;
static void button_setup_pin(GPIO_TypeDef* port, uint16_t pin) {
GPIO_InitTypeDef GPIO_InitStructure = {0};
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
@ -62,7 +71,7 @@ static void button_setup_pin(GPIO_TypeDef *port, uint16_t pin) {
}
bool button_init(void) {
button_driver_t *drv = &g_button_driver;
button_driver_t* drv = &g_button_driver;
if (drv->initialized) {
return true;
@ -99,106 +108,118 @@ bool button_init(void) {
NVIC_EnableIRQ(BTN_EXTI_INTERRUPT_NUM);
#endif // BTN_EXTI_INTERRUPT_HANDLER
drv->initialized = true;
if (!syshandle_register(SYSHANDLE_BUTTON, &g_button_handle_vmt, drv)) {
goto cleanup;
}
drv->initialized = true;
return true;
cleanup:
button_deinit();
return false;
}
void button_deinit(void) {
button_driver_t* drv = &g_button_driver;
syshandle_unregister(SYSHANDLE_BUTTON);
#ifdef BTN_EXIT_INTERRUPT_HANDLER
NVIC_DisableIRQ(BTN_EXTI_INTERRUPT_NUM);
#endif
memset(drv, 0, sizeof(button_driver_t));
}
bool button_get_event(button_event_t *event) {
button_driver_t *drv = &g_button_driver;
static uint32_t button_read_state(button_driver_t* drv) {
UNUSED(drv);
uint32_t state = 0;
#ifdef BTN_LEFT_PIN
if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(BTN_LEFT_PORT, BTN_LEFT_PIN)) {
state |= (1U << BTN_LEFT);
}
#endif
#ifdef BTN_RIGHT_PIN
if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(BTN_RIGHT_PORT, BTN_RIGHT_PIN)) {
state |= (1U << BTN_RIGHT);
}
#endif
#ifdef BTN_POWER_PIN
if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(BTN_POWER_PORT, BTN_POWER_PIN)) {
state |= (1U << BTN_POWER);
}
#endif
return state;
}
bool button_get_event(button_event_t* event) {
button_driver_t* drv = &g_button_driver;
memset(event, 0, sizeof(*event));
if (!drv->initialized) {
return false;
}
#ifdef BTN_LEFT_PIN
bool left_down =
(GPIO_PIN_RESET == HAL_GPIO_ReadPin(BTN_LEFT_PORT, BTN_LEFT_PIN));
button_tls_t* tls = &drv->tls[systask_id(systask_active())];
if (drv->left_down != left_down) {
drv->left_down = left_down;
if (left_down) {
event->button = BTN_LEFT;
uint64_t now = systick_us();
if ((now - tls->time) > 100000) {
// Reset the history if the button was not read for 100ms
tls->pressed = 0;
tls->released = 0;
}
uint32_t new_state = button_read_state(drv);
// Remember state changes and the time of the last read
tls->pressed |= new_state & ~tls->state;
tls->released |= ~new_state & tls->state;
tls->time = now;
// Bring the automaton out of invalid states,
// in case it somehow ends up in one.
tls->released &= tls->pressed | tls->state;
tls->pressed &= tls->released | ~tls->state;
button_t button = 0;
while (tls->pressed | tls->released) {
uint32_t mask = 1 << button;
if ((tls->pressed & mask) != 0 && (tls->state & mask) == 0) {
// Button press was not signalled yet
tls->pressed &= ~mask;
tls->state |= mask;
event->button = button;
event->event_type = BTN_EVENT_DOWN;
return true;
} else {
event->button = BTN_LEFT;
} else if ((tls->released & mask) != 0 && (tls->state & mask) != 0) {
// Button release was not signalled yet
tls->released &= ~mask;
tls->state &= ~mask;
event->button = button;
event->event_type = BTN_EVENT_UP;
return true;
}
++button;
}
#endif
#ifdef BTN_RIGHT_PIN
bool right_down =
(GPIO_PIN_RESET == HAL_GPIO_ReadPin(BTN_RIGHT_PORT, BTN_RIGHT_PIN));
if (drv->right_down != right_down) {
drv->right_down = right_down;
if (right_down) {
event->button = BTN_RIGHT;
event->event_type = BTN_EVENT_DOWN;
return true;
} else {
event->button = BTN_RIGHT;
event->event_type = BTN_EVENT_UP;
return true;
}
}
#endif
#ifdef BTN_POWER_PIN
bool power_down =
(GPIO_PIN_RESET == HAL_GPIO_ReadPin(BTN_POWER_PORT, BTN_POWER_PIN));
if (drv->power_down != power_down) {
drv->power_down = power_down;
if (power_down) {
event->button = BTN_POWER;
event->event_type = BTN_EVENT_DOWN;
return true;
} else {
event->button = BTN_POWER;
event->event_type = BTN_EVENT_UP;
return true;
}
}
#endif
return 0;
return false;
}
bool button_is_down(button_t button) {
button_driver_t *drv = &g_button_driver;
button_driver_t* drv = &g_button_driver;
if (!drv->initialized) {
return false;
}
switch (button) {
#ifdef BTN_LEFT_PIN
case BTN_LEFT:
return drv->left_down;
#endif
#ifdef BTN_RIGHT_PIN
case BTN_RIGHT:
return drv->right_down;
#endif
#ifdef BTN_POWER_PIN
case BTN_POWER:
return drv->power_down;
#endif
default:
return false;
}
return (button_read_state(drv) & (1 << button)) != 0;
}
#ifdef BTN_EXTI_INTERRUPT_HANDLER
@ -221,4 +242,45 @@ void BTN_EXTI_INTERRUPT_HANDLER(void) {
}
#endif
static void on_task_created(void* context, systask_id_t task_id) {
button_driver_t* drv = (button_driver_t*)context;
button_tls_t* tls = &drv->tls[task_id];
memset(tls, 0, sizeof(button_tls_t));
}
static void on_event_poll(void* context, bool read_awaited,
bool write_awaited) {
button_driver_t* drv = (button_driver_t*)context;
UNUSED(write_awaited);
if (read_awaited) {
uint32_t state = button_read_state(drv);
syshandle_signal_read_ready(SYSHANDLE_BUTTON, &state);
}
}
static bool on_check_read_ready(void* context, systask_id_t task_id,
void* param) {
button_driver_t* drv = (button_driver_t*)context;
button_tls_t* tls = &drv->tls[task_id];
uint32_t new_state = *(uint32_t*)param;
// Remember state changes and the time of the last read
tls->pressed |= new_state & ~tls->state;
tls->released |= ~new_state & tls->state;
tls->time = systick_us();
return (tls->pressed | tls->released) != 0;
}
static const syshandle_vmt_t g_button_handle_vmt = {
.task_created = on_task_created,
.task_killed = NULL,
.check_read_ready = on_check_read_ready,
.check_write_ready = NULL,
.poll = on_event_poll,
};
#endif // KERNEL_MODE

View File

@ -112,18 +112,22 @@ bool button_is_down(button_t button) {
return false;
}
SDL_PumpEvents();
const uint8_t *keystate = SDL_GetKeyboardState(NULL);
switch (button) {
#ifdef BTN_LEFT_KEY
case BTN_LEFT:
return drv->left_down;
return keystate[BTN_LEFT_KEY] != 0;
#endif
#ifdef BTN_RIGHT_KEY
case BTN_RIGHT:
return drv->right_down;
return keystate[BTN_RIGHT_KEY] != 0;
#endif
#ifdef BTN_POWER_KEY
case BTN_POWER:
return drv->power_down;
return keystate[BTN_POWER_KEY] != 0;
#endif
default:
return false;

View File

@ -92,15 +92,11 @@ void ui_click(void) {
void ui_click(void) {
for (;;) {
button_event_t event = {0};
button_get_event(&event);
if (button_is_down(BTN_LEFT) && button_is_down(BTN_RIGHT)) {
break;
}
}
for (;;) {
button_event_t event = {0};
button_get_event(&event);
if (!button_is_down(BTN_LEFT) && !button_is_down(BTN_RIGHT)) {
break;
}

View File

@ -535,8 +535,6 @@ int bootloader_main(void) {
}
}
#elif defined USE_BUTTON
button_event_t btn_evt = {0};
button_get_event(&btn_evt);
if (button_is_down(BTN_LEFT)) {
touched = 1;
}

View File

@ -67,10 +67,6 @@ static void test_button_combination(cli_t* cli, uint32_t timeout, button_t btn1,
cli_trace(cli, "Waiting for button combination to be pressed...");
while (true) {
// Event must be read before calling `button_is_down()`
button_event_t e = {0};
button_get_event(&e);
if (button_is_down(btn1) && button_is_down(btn2)) {
break;
} else if (ticks_expired(expire_time)) {
@ -84,10 +80,6 @@ static void test_button_combination(cli_t* cli, uint32_t timeout, button_t btn1,
cli_trace(cli, "Waiting for buttons to be released...");
while (true) {
// Event must be read before calling `button_is_down()`
button_event_t e = {0};
button_get_event(&e);
if (!button_is_down(btn1) && !button_is_down(btn2)) {
break;
} else if (ticks_expired(expire_time)) {