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:
parent
149abfefc5
commit
cb2c4d17eb
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)) {
|
||||
|
Loading…
Reference in New Issue
Block a user