1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-01-18 11:21:11 +00:00

refactor(core/embed): prepare touch drivers for low power mode

[no changelog]
This commit is contained in:
cepetr 2024-06-03 20:01:49 +02:00 committed by cepetr
parent 089dc84f38
commit 3460c4b891
29 changed files with 848 additions and 606 deletions

View File

@ -112,7 +112,6 @@ SOURCE_MOD += [
'embed/lib/image.c',
'embed/lib/mini_printf.c',
'embed/lib/terminal.c',
'embed/lib/touch.c',
'embed/lib/unit_variant.c',
'vendor/micropython/lib/uzlib/adler32.c',
'vendor/micropython/lib/uzlib/crc32.c',
@ -150,11 +149,10 @@ SOURCE_TREZORHAL = [
'embed/trezorhal/unix/fault_handlers.c',
'embed/trezorhal/unix/flash.c',
'embed/trezorhal/unix/flash_otp.c',
'embed/trezorhal/unix/touch/touch.c',
'embed/trezorhal/unix/rng.c',
'embed/trezorhal/unix/usb.c',
'embed/trezorhal/unix/random_delays.c',
'embed/trezorhal/unix/rng.c',
'embed/trezorhal/unix/secret.c',
'embed/trezorhal/unix/usb.c',
]
if NEW_RENDERING:

View File

@ -419,20 +419,19 @@ SOURCE_MICROPYTHON = [
SOURCE_UNIX = [
'embed/trezorhal/unix/boot_args.c',
'embed/trezorhal/unix/common.c',
'embed/trezorhal/unix/flash.c',
'embed/trezorhal/unix/flash_otp.c',
'embed/trezorhal/unix/flash.c',
'embed/trezorhal/unix/random_delays.c',
'embed/trezorhal/unix/rng.c',
'embed/trezorhal/unix/usb.c',
'embed/trezorhal/unix/touch/touch.c',
'embed/unix/main.c',
'embed/unix/main_main.c',
'embed/unix/main.c',
'embed/unix/profile.c',
'vendor/micropython/shared/runtime/gchelper_generic.c',
'vendor/micropython/ports/unix/alloc.c',
'vendor/micropython/ports/unix/gccollect.c',
'vendor/micropython/ports/unix/input.c',
'vendor/micropython/ports/unix/unix_mphal.c',
'vendor/micropython/shared/runtime/gchelper_generic.c',
]
if NEW_RENDERING:

View File

@ -154,16 +154,16 @@ static void ui_screen_boot_wait(int wait_seconds) {
void ui_click(void) {
// flush touch events if any
while (touch_read()) {
while (touch_get_event()) {
}
// wait for TOUCH_START
while ((touch_read() & TOUCH_START) == 0) {
while ((touch_get_event() & TOUCH_START) == 0) {
}
// wait for TOUCH_END
while ((touch_read() & TOUCH_END) == 0) {
while ((touch_get_event() & TOUCH_END) == 0) {
}
// flush touch events if any
while (touch_read()) {
while (touch_get_event()) {
}
}

View File

@ -398,7 +398,6 @@ int bootloader_main(void) {
unit_variant_init();
#ifdef USE_TOUCH
touch_power_on();
#ifdef TREZOR_MODEL_T3T1
// on T3T1, tester needs to run without touch, so making an exception
// until unit variant is written in OTP
@ -514,17 +513,22 @@ int bootloader_main(void) {
uint32_t touched = 0;
#ifdef USE_TOUCH
if (firmware_present == sectrue && stay_in_bootloader != sectrue) {
touch_wait_until_ready();
// Wait until the touch controller is ready
// (on hardware this may take a while)
while (touch_ready() != sectrue) {
hal_delay(1);
}
#ifdef TREZOR_EMULATOR
hal_delay(500);
#endif
// Give the touch controller time to report events
// if someone touches the screen
for (int i = 0; i < 10; i++) {
touched = touch_is_detected() | touch_read();
if (touched) {
if (touch_activity() == sectrue) {
touched = 1;
break;
}
#ifdef TREZOR_EMULATOR
hal_delay(25);
#else
hal_delay_us(1000);
#endif
hal_delay(5);
}
}
#elif defined USE_BUTTON

View File

@ -206,7 +206,6 @@ int main(void) {
random_delays_init();
#ifdef USE_TOUCH
touch_init();
touch_power_on();
#endif
#ifdef USE_HASH_PROCESSOR

View File

@ -85,9 +85,7 @@ STATIC mp_obj_t mod_trezorio_poll(mp_obj_t ifaces, mp_obj_t list_ref,
}
#if defined USE_TOUCH
else if (iface == TOUCH_IFACE) {
const uint32_t evt = touch_read();
const uint32_t evt = touch_get_event();
if (evt) {
// ignore TOUCH_MOVE events if they are too frequent
if ((evt & TOUCH_MOVE) == 0 ||

View File

@ -1,22 +0,0 @@
#ifdef USE_TOUCH
#include "touch.h"
uint32_t touch_click(void) {
uint32_t r = 0;
// flush touch events if any
while (touch_read()) {
}
// wait for TOUCH_START
while ((touch_read() & TOUCH_START) == 0) {
}
// wait for TOUCH_END
while (((r = touch_read()) & TOUCH_END) == 0) {
}
// flush touch events if any
while (touch_read()) {
}
// return last touch coordinate
return r;
}
#endif

View File

@ -0,0 +1 @@
Fix TOUCH VERSION command

View File

@ -312,15 +312,15 @@ static secbool touch_click_timeout(uint32_t *touch, uint32_t timeout_ms) {
uint32_t deadline = HAL_GetTick() + timeout_ms;
uint32_t r = 0;
while (touch_read())
while (touch_get_event())
;
while ((touch_read() & TOUCH_START) == 0) {
while ((touch_get_event() & TOUCH_START) == 0) {
if (HAL_GetTick() > deadline) return secfalse;
}
while (((r = touch_read()) & TOUCH_END) == 0) {
while (((r = touch_get_event()) & TOUCH_END) == 0) {
if (HAL_GetTick() > deadline) return secfalse;
}
while (touch_read())
while (touch_get_event())
;
*touch = r;
@ -348,7 +348,7 @@ static void test_touch(const char *args) {
}
display_refresh();
touch_power_on();
touch_init();
uint32_t evt = 0;
if (touch_click_timeout(&evt, timeout * 1000)) {
@ -361,20 +361,20 @@ static void test_touch(const char *args) {
display_clear();
display_refresh();
touch_power_off();
touch_deinit();
}
static void test_sensitivity(const char *args) {
int v = atoi(args);
touch_power_on();
touch_sensitivity(v & 0xFF);
touch_init();
touch_set_sensitivity(v & 0xFF);
display_clear();
display_refresh();
for (;;) {
uint32_t evt = touch_read();
uint32_t evt = touch_get_event();
if (evt & TOUCH_START || evt & TOUCH_MOVE) {
int x = touch_unpack_x(evt);
int y = touch_unpack_y(evt);
@ -387,14 +387,14 @@ static void test_sensitivity(const char *args) {
}
}
touch_power_off();
touch_deinit();
}
static void touch_version(void) {
touch_power_on();
touch_init();
uint8_t version = touch_get_version();
vcp_println("OK %d", version);
touch_power_off();
touch_deinit();
}
#endif

View File

@ -442,7 +442,7 @@ fn generate_trezorhal_bindings() {
//usb
.allowlist_function("usb_configured")
// touch
.allowlist_function("touch_read")
.allowlist_function("touch_get_event")
// button
.allowlist_function("button_read")
// haptic

View File

@ -1,8 +1,8 @@
use super::ffi;
#[cfg(feature = "touch")]
pub fn io_touch_read() -> u32 {
unsafe { ffi::touch_read() }
pub fn io_touch_get_event() -> u32 {
unsafe { ffi::touch_get_event() }
}
#[cfg(feature = "button")]

View File

@ -1,7 +1,7 @@
#[cfg(feature = "button")]
use crate::trezorhal::io::io_button_read;
#[cfg(feature = "touch")]
use crate::trezorhal::io::io_touch_read;
use crate::trezorhal::io::io_touch_get_event;
#[cfg(feature = "button")]
use crate::ui::event::ButtonEvent;
#[cfg(feature = "touch")]
@ -56,7 +56,7 @@ fn button_eval() -> Option<ButtonEvent> {
#[cfg(feature = "touch")]
fn touch_eval() -> Option<TouchEvent> {
let event = io_touch_read();
let event = io_touch_get_event();
if event == 0 {
return None;
}

View File

@ -20,34 +20,105 @@
#include STM32_HAL_H
#include TREZOR_BOARD
#include <stdbool.h>
#include <string.h>
#include "common.h"
#include "secbool.h"
#include "ft6x36.h"
#include "i2c.h"
#include "touch.h"
#define TOUCH_ADDRESS \
(0x38U << 1) // the HAL requires the 7-bit address to be shifted by one bit
#define TOUCH_PACKET_SIZE 7U
#define EVENT_PRESS_DOWN 0x00U
#define EVENT_CONTACT 0x80U
#define EVENT_LIFT_UP 0x40U
#define EVENT_NO_EVENT 0xC0U
#define GESTURE_NO_GESTURE 0x00U
#define X_POS_MSB (touch_data[3] & 0x0FU)
#define X_POS_LSB (touch_data[4])
#define Y_POS_MSB (touch_data[5] & 0x0FU)
#define Y_POS_LSB (touch_data[6])
typedef struct {
// Set if the driver is initialized
secbool initialized;
// Set if the driver is ready to report touches.
// FT6X36 needs about 300ms after power-up to stabilize.
secbool ready;
// Captured tick counter when `touch_init()` was called
uint32_t init_ticks;
// Time (in ticks) when touch_get_event() was called last time
uint32_t poll_ticks;
// Set if the touch controller is currently touched
// (respectively, the we detected a touch event)
bool pressed;
// Previously reported x-coordinate
uint16_t last_x;
// Previously reported y-coordinate
uint16_t last_y;
#define EVENT_OLD_TIMEOUT_MS 50
#define EVENT_MISSING_TIMEOUT_MS 50
} touch_driver_t;
static uint32_t touch_init_ticks = 0;
// Touch driver instance
static touch_driver_t g_touch_driver = {
.initialized = secfalse,
};
static void touch_default_pin_state(void) {
// Reads a subsequent registers from the FT6X36.
//
// Returns: `sectrue` if the register was read
// successfully, `secfalse` otherwise.
//
// If the I2C bus is busy, the function will cycle the
// bus and retry the operation.
static secbool ft6x36_read_regs(uint8_t reg, uint8_t* value, size_t count) {
uint16_t i2c_bus = TOUCH_I2C_INSTANCE;
uint8_t i2c_addr = FT6X36_I2C_ADDR;
uint8_t txdata[] = {reg};
uint8_t retries = 3;
do {
int result = i2c_transmit(i2c_bus, i2c_addr, txdata, sizeof(txdata), 10);
if (HAL_OK == result) {
result = i2c_receive(i2c_bus, i2c_addr, value, count, 10);
}
if (HAL_OK == result) {
// success
return sectrue;
} else if (HAL_BUSY == result && retries > 0) {
// I2C bus is busy, cycle it and try again
i2c_cycle(i2c_bus);
retries--;
} else {
// Aother error or retries exhausted
return secfalse;
}
} while (1);
}
// Writes a register to the FT6X36.
//
// Returns: `sectrue` if the register was written
// successfully, `secfalse` otherwise.
//
// If the I2C bus is busy, the function will cycle the
// bus and retry the operation.
static secbool ft6x36_write_reg(uint8_t reg, uint8_t value) {
uint16_t i2c_bus = TOUCH_I2C_INSTANCE;
uint8_t i2c_addr = FT6X36_I2C_ADDR;
uint8_t txdata[] = {reg, value};
uint8_t retries = 3;
do {
int result = i2c_transmit(i2c_bus, i2c_addr, txdata, sizeof(txdata), 10);
if (HAL_OK == result) {
// success
return sectrue;
} else if (HAL_BUSY == result && retries > 0) {
// I2C bus is busy, cycle it and try again
i2c_cycle(i2c_bus);
retries--;
} else {
// Another error or retries exhausted
return secfalse;
}
} while (1);
}
// Powers down the touch controller and puts all
// the pins in the proper state to save power.
static void ft6x36_power_down(void) {
GPIO_PinState state = HAL_GPIO_ReadPin(TOUCH_ON_PORT, TOUCH_ON_PIN);
// set power off and other pins as per section 3.5 of FT6236 datasheet
@ -74,227 +145,291 @@ static void touch_default_pin_state(void) {
GPIO_InitStructure.Pin = TOUCH_ON_PIN;
HAL_GPIO_Init(TOUCH_ON_PORT, &GPIO_InitStructure);
// in-case power was on, or CTPM was active make sure to wait long enough
// for these changes to take effect. a reset needs to be low for
// a minimum of 5ms. also wait for power circuitry to stabilize (if it
// changed).
HAL_Delay(10);
if (state == GPIO_PIN_SET) {
HAL_Delay(90); // add 90 ms for circuitry to stabilize (being conservative)
// 90 ms for circuitry to stabilize (being conservative)
hal_delay(90);
}
}
static void touch_active_pin_state(void) {
HAL_GPIO_WritePin(TOUCH_ON_PORT, TOUCH_ON_PIN, GPIO_PIN_RESET); // CTP_ON
HAL_Delay(10); // we need to wait until the circuit fully kicks-in
// Powers up the touch controller and do proper reset sequence
//
// `ft6x36_power_down()` must be called before calling this first time function
// to properly initialize the GPIO pins.
static void ft6x36_power_up(void) {
// Ensure the touch controller is in reset state
HAL_GPIO_WritePin(TOUCH_RST_PORT, TOUCH_RST_PIN, GPIO_PIN_RESET);
// Power up the touch controller
HAL_GPIO_WritePin(TOUCH_ON_PORT, TOUCH_ON_PIN, GPIO_PIN_RESET);
// Wait until the circuit fully kicks-in
// (5ms is the minimum time required for the reset signal to be effective)
hal_delay(10);
// Enable intterrupt input
GPIO_InitTypeDef GPIO_InitStructure = {0};
// capacitive touch panel module (CTPM) interrupt (INT) input
GPIO_InitStructure.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStructure.Pin = TOUCH_INT_PIN;
HAL_GPIO_Init(TOUCH_INT_PORT, &GPIO_InitStructure);
// Release touch controller from reset
HAL_GPIO_WritePin(TOUCH_RST_PORT, TOUCH_RST_PIN, GPIO_PIN_SET);
// Wait for the touch controller to boot up
hal_delay(5);
// Clear the flag indicating rising edge on INT_PIN
__HAL_GPIO_EXTI_CLEAR_FLAG(TOUCH_INT_PIN);
HAL_GPIO_WritePin(TOUCH_RST_PORT, TOUCH_RST_PIN,
GPIO_PIN_SET); // release CTPM reset
touch_init_ticks = hal_ticks_ms();
HAL_Delay(5);
}
secbool touch_set_mode(void) {
// set register 0xA4 G_MODE to interrupt trigger mode (0x01). basically, CTPM
// generates a pulse when new data is available
uint8_t touch_panel_config[] = {0xA4, 0x01};
for (int i = 0; i < 3; i++) {
if (HAL_OK == i2c_transmit(TOUCH_I2C_INSTANCE, TOUCH_ADDRESS,
touch_panel_config, sizeof(touch_panel_config),
10)) {
return sectrue;
}
i2c_cycle(TOUCH_I2C_INSTANCE);
}
return secfalse;
}
void touch_power_on(void) {
touch_default_pin_state();
// turn on CTP circuitry
touch_active_pin_state();
}
void touch_power_off(void) {
// turn off CTP circuitry
HAL_Delay(50);
touch_default_pin_state();
}
secbool touch_init(void) {
GPIO_InitTypeDef GPIO_InitStructure = {0};
// PC4 capacitive touch panel module (CTPM) interrupt (INT) input
GPIO_InitStructure.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStructure.Pin = TOUCH_INT_PIN;
HAL_GPIO_Init(TOUCH_INT_PORT, &GPIO_InitStructure);
__HAL_GPIO_EXTI_CLEAR_FLAG(TOUCH_INT_PIN);
if (sectrue != touch_set_mode()) {
return secfalse;
}
return touch_sensitivity(TOUCH_SENSITIVITY);
}
void touch_wait_until_ready(void) {
// wait for the touch controller to be ready
while (hal_ticks_ms() - touch_init_ticks < 310) {
HAL_Delay(1);
}
}
secbool touch_sensitivity(uint8_t value) {
// set panel threshold (TH_GROUP) - default value is 0x12
uint8_t touch_panel_threshold[] = {0x80, value};
for (int i = 0; i < 3; i++) {
if (HAL_OK == i2c_transmit(TOUCH_I2C_INSTANCE, TOUCH_ADDRESS,
touch_panel_threshold,
sizeof(touch_panel_threshold), 10)) {
return sectrue;
}
i2c_cycle(TOUCH_I2C_INSTANCE);
}
return secfalse;
}
uint32_t touch_is_detected(void) {
// check the interrupt line coming in from the CTPM.
// the line make a short pulse, which sets an interrupt flag when new data is
// available.
// Reference section 1.2 of "Application Note for FT6x06 CTPM". we
// configure the touch controller to use "interrupt trigger mode".
// Checks if the touch controller has an interrupt pending
// which indicates that new data is available.
//
// The function clears the interrupt flag if it was set so the
// next call returns `false` if no new impulses were detected.
static bool ft6x36_test_and_clear_interrupt(void) {
uint32_t event = __HAL_GPIO_EXTI_GET_FLAG(TOUCH_INT_PIN);
if (event != 0) {
__HAL_GPIO_EXTI_CLEAR_FLAG(TOUCH_INT_PIN);
}
return event;
return event != 0;
}
uint32_t check_timeout(uint32_t prev, uint32_t timeout) {
uint32_t current = hal_ticks_ms();
uint32_t diff = current - prev;
// Configures the touch controller to the funtional state.
static secbool ft6x36_configure(void) {
const static uint8_t config[] = {
// Set touch controller to the interrupt trigger mode.
// Basically, CTPM generates a pulse when new data is available.
FT6X36_REG_G_MODE,
0x01,
FT6X36_REG_TH_GROUP,
TOUCH_SENSITIVITY,
};
if (diff >= timeout) {
return 1;
}
_Static_assert(sizeof(config) % 2 == 0);
return 0;
}
for (int i = 0; i < sizeof(config); i += 2) {
uint8_t reg = config[i];
uint8_t value = config[i + 1];
uint32_t touch_read(void) {
static uint8_t touch_data[TOUCH_PACKET_SIZE],
previous_touch_data[TOUCH_PACKET_SIZE];
static uint32_t xy;
static uint32_t last_check_time = 0;
static uint32_t last_event_time = 0;
static int touching = 0;
uint32_t detected = touch_is_detected();
if (detected == 0) {
last_check_time = hal_ticks_ms();
if (touching && check_timeout(last_event_time, EVENT_MISSING_TIMEOUT_MS)) {
// we didn't detect an event for a long time, but there was an active
// touch: send END event, as we probably missed the END event
touching = 0;
return TOUCH_END | xy;
if (sectrue != ft6x36_write_reg(reg, value)) {
return secfalse;
}
return 0;
}
if ((touching == 0) &&
(check_timeout(last_check_time, EVENT_OLD_TIMEOUT_MS))) {
// we have detected an event, but it might be too old, rather drop it
// (only dropping old events if there was no touch active)
last_check_time = hal_ticks_ms();
return 0;
return sectrue;
}
secbool touch_init(void) {
touch_driver_t* driver = &g_touch_driver;
if (sectrue == driver->initialized) {
// The driver is already initialized
return sectrue;
}
last_check_time = hal_ticks_ms();
// Initialize GPIO to the default configuration
// (touch controller is powered down)
ft6x36_power_down();
uint8_t outgoing[] = {0x00}; // start reading from address 0x00
int result = i2c_transmit(TOUCH_I2C_INSTANCE, TOUCH_ADDRESS, outgoing,
sizeof(outgoing), 1);
if (result != HAL_OK) {
if (result == HAL_BUSY) i2c_cycle(TOUCH_I2C_INSTANCE);
return 0;
// Power up the touch controller and perform the reset sequence
ft6x36_power_up();
// Configure the touch controller
if (sectrue != ft6x36_configure()) {
ft6x36_power_down();
return secfalse;
}
if (HAL_OK != i2c_receive(TOUCH_I2C_INSTANCE, TOUCH_ADDRESS, touch_data,
TOUCH_PACKET_SIZE, 1)) {
return 0; // read failure
driver->init_ticks = hal_ticks_ms();
driver->poll_ticks = driver->init_ticks;
driver->initialized = sectrue;
return sectrue;
}
void touch_deinit(void) {
touch_driver_t* driver = &g_touch_driver;
if (sectrue == driver->initialized) {
// Do not need to deinitialized the controller
// just power it off
ft6x36_power_down();
memset(driver, 0, sizeof(touch_driver_t));
}
}
secbool touch_ready(void) {
touch_driver_t* driver = &g_touch_driver;
if (sectrue == driver->initialized && sectrue != driver->ready) {
// FT6X36 does not report events for 300ms
// after it is released from the reset state
if ((int)(hal_ticks_ms() - driver->init_ticks) >= 310) {
driver->ready = sectrue;
}
}
last_event_time = hal_ticks_ms();
return driver->ready;
}
if (0 == memcmp(previous_touch_data, touch_data, TOUCH_PACKET_SIZE)) {
return 0; // same data, filter it out
secbool touch_set_sensitivity(uint8_t value) {
touch_driver_t* driver = &g_touch_driver;
if (sectrue == driver->initialized) {
return ft6x36_write_reg(FT6X36_REG_TH_GROUP, value);
} else {
memcpy(previous_touch_data, touch_data, TOUCH_PACKET_SIZE);
return secfalse;
}
const uint32_t number_of_touch_points =
touch_data[2] & 0x0F; // valid values are 0, 1, 2 (invalid 0xF before
// first touch) (tested with FT6206)
const uint32_t event_flag = touch_data[3] & 0xC0;
if (touch_data[1] == GESTURE_NO_GESTURE) {
xy = touch_pack_xy((X_POS_MSB << 8) | X_POS_LSB,
(Y_POS_MSB << 8) | Y_POS_LSB);
if ((number_of_touch_points == 1) && (event_flag == EVENT_PRESS_DOWN)) {
touching = 1;
return TOUCH_START | xy;
} else if ((number_of_touch_points == 1) && (event_flag == EVENT_CONTACT)) {
if (touching) {
return TOUCH_MOVE | xy;
} else {
touching = 1;
return TOUCH_START | xy;
}
} else if ((number_of_touch_points == 0) && (event_flag == EVENT_LIFT_UP)) {
touching = 0;
return TOUCH_END | xy;
}
}
return 0;
}
uint8_t touch_get_version(void) {
uint8_t version = 0;
uint8_t outgoing[] = {0xA6}; // start reading from address 0xA6
int result = i2c_transmit(TOUCH_I2C_INSTANCE, TOUCH_ADDRESS, outgoing,
sizeof(outgoing), 1);
if (result != HAL_OK) {
if (result == HAL_BUSY) i2c_cycle(TOUCH_I2C_INSTANCE);
touch_driver_t* driver = &g_touch_driver;
if (sectrue != driver->initialized) {
return 0;
}
if (HAL_OK != i2c_receive(TOUCH_I2C_INSTANCE, TOUCH_ADDRESS, &version,
sizeof(version), 1)) {
return 0; // read failure
// After powering up the touch controller, we need to wait
// for an unspecified amount of time (~100ms) before attempting
// to read the firmware version. If we try to read too soon, we get 0x00
// and the chip behaves unpredictably.
while (sectrue != touch_ready()) {
hal_delay(1);
}
return version;
uint8_t fw_version = 0;
if (sectrue != ft6x36_read_regs(FT6X36_REG_FIRMID, &fw_version, 1)) {
ft6x36_power_down();
return secfalse;
}
return fw_version;
}
secbool touch_activity(void) {
touch_driver_t* driver = &g_touch_driver;
if (sectrue == driver->initialized) {
if (ft6x36_test_and_clear_interrupt()) {
return sectrue;
}
}
return secfalse;
}
uint32_t touch_get_event(void) {
touch_driver_t* driver = &g_touch_driver;
if (sectrue != driver->initialized) {
return 0;
}
// Content of registers 0x00 - 0x06 read from the touch controller
uint8_t regs[7];
// Ensure the registers are within the bounds
_Static_assert(sizeof(regs) > FT6X63_REG_GEST_ID);
_Static_assert(sizeof(regs) > FT6X63_REG_TD_STATUS);
_Static_assert(sizeof(regs) > FT6X63_REG_P1_XH);
_Static_assert(sizeof(regs) > FT6X63_REG_P1_XL);
_Static_assert(sizeof(regs) > FT6X63_REG_P1_YH);
_Static_assert(sizeof(regs) > FT6X63_REG_P1_YL);
uint32_t ticks = hal_ticks_ms();
// Test if the touch_get_event() is starving (not called frequently enough)
bool starving = (int32_t)(ticks - driver->poll_ticks) > 300 /* ms */;
driver->poll_ticks = ticks;
// Fast track: if there is no new event and the touch controller
// is not touched, we do not need to read the registers
if (!ft6x36_test_and_clear_interrupt() && !driver->pressed) {
return 0;
}
// Read the set of registers containing touch event and coordinates
if (sectrue != ft6x36_read_regs(0x00, regs, sizeof(regs))) {
// Failed to read the touch registers
return 0;
}
// Extract gesture ID (FT6X63_GESTURE_xxx)
uint8_t gesture = regs[FT6X63_REG_GEST_ID];
if (gesture != FT6X36_GESTURE_NONE) {
// This is here for unknown historical reasons
// It seems we can't get here with FT6X36
return 0;
}
// Extract number of touches (0, 1, 2) or 0x0F before
// the first touch (tested with FT6206)
uint8_t nb_touches = regs[FT6X63_REG_TD_STATUS] & 0x0F;
// Extract event flags (one of press down, contact, lift up)
uint8_t flags = regs[FT6X63_REG_P1_XH] & FT6X63_EVENT_MASK;
// Extract touch coordinates
uint16_t x = ((regs[FT6X63_REG_P1_XH] & 0x0F) << 8) | regs[FT6X63_REG_P1_XL];
uint16_t y = ((regs[FT6X63_REG_P1_YH] & 0x0F) << 8) | regs[FT6X63_REG_P1_YL];
uint32_t event = 0;
uint32_t xy = touch_pack_xy(x, y);
if ((nb_touches == 1) && (flags == FT6X63_EVENT_PRESS_DOWN)) {
if (!driver->pressed) {
// Finger was just pressed down
event = TOUCH_START | xy;
} else {
// It looks like we have missed the lift up event
// We should send the TOUCH_END event here with old coordinates
event = TOUCH_END | touch_pack_xy(driver->last_x, driver->last_y);
}
} else if ((nb_touches == 1) && (flags == FT6X63_EVENT_CONTACT)) {
if (driver->pressed) {
if ((x != driver->last_x) || (y != driver->last_y)) {
// Report the move event only if the coordinates
// have changed
event = TOUCH_MOVE | xy;
}
} else {
// We have missed the press down event, we have to simulate it.
// But ensure we don't simulate TOUCH_START if touch_get_event() is not
// called frequently enough to not produce false events.
if (!starving) {
event = TOUCH_START | xy;
}
}
} else if ((nb_touches == 0) && (flags == FT6X63_EVENT_LIFT_UP)) {
if (driver->pressed) {
// Finger was just lifted up
event = TOUCH_END | xy;
} else {
// 1. Most likely, we have missed the PRESS_DOWN event.
// Touch duration was too short (< 20ms) to be worth reporting.
// 2. Finger is still lifted up. Since we have already sent the
// TOUCH_END event => no event needed. This should not happen
// since two consecutive LIFT_UPs are not possible due to
// testing the interrupt line before reading the registers.
}
}
// remember the last state
if ((event & TOUCH_START) || (event & TOUCH_MOVE)) {
driver->pressed = true;
} else if (event & TOUCH_END) {
driver->pressed = false;
}
driver->last_x = x;
driver->last_y = y;
return event;
}

View File

@ -20,4 +20,57 @@
#ifndef _TOUCH_FT6X36_H
#define _TOUCH_FT6X36_H
// I2C address of the FT6X36 on the I2C bus.
// `<< 1` is required because the HAL expects the address to be shifted by 1.
#define FT6X36_I2C_ADDR (0x38 << 1)
// ------------------------------------------------------------
// FT6X36 registers
// ------------------------------------------------------------
// Gesture ID (see `FT6X36_GESTURE_xxx`)
#define FT6X63_REG_GEST_ID 0x01
// TD_STATUS (number of touch points in lower 4 bits)
#define FT6X63_REG_TD_STATUS 0x02
// Event flags in higher 2 bits (see `FT6X63_EVENT_xxx`)
// MSB of touch x-coordinate in lower 4 bits
#define FT6X63_REG_P1_XH 0x03
// LSB of touch x-coordinate
#define FT6X63_REG_P1_XL 0x04
// MSB of touch y-coordinate in lower 4 bits
#define FT6X63_REG_P1_YH 0x05
// LSB of touch y-coordinate
#define FT6X63_REG_P1_YL 0x06
// Threshold for touch detection
#define FT6X36_REG_TH_GROUP 0x80
// Mode register
// 0x00 - interrupt polling mode
// 0x01 - interrupt trigger mode
#define FT6X36_REG_G_MODE 0xA4
// Firmware version
#define FT6X36_REG_FIRMID 0xA6
// ------------------------------------------------------------
// Event bits (see FT6X63_REG_P1_XH)
// ------------------------------------------------------------
#define FT6X63_EVENT_PRESS_DOWN 0x00
#define FT6X63_EVENT_CONTACT 0x80
#define FT6X63_EVENT_LIFT_UP 0x40
#define FT6X63_EVENT_MASK 0xC0
// ------------------------------------------------------------
// Gesture types (see FT6X63_REG_GEST_ID)
// ------------------------------------------------------------
#define FT6X36_GESTURE_NONE 0x00
#endif

View File

@ -320,26 +320,6 @@ uint16_t IOE_ReadMultiple(uint8_t Addr, uint8_t Reg, uint8_t *pBuffer,
*/
void IOE_Delay(uint32_t Delay) { HAL_Delay(Delay); }
static void touch_active_pin_state(void) {
// HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET); // CTP_ON/PB10
// HAL_Delay(10); // we need to wait until the circuit fully kicks-in
GPIO_InitTypeDef GPIO_InitStructure = {0};
// PC4 capacitive touch panel module (CTPM) interrupt (INT) input
GPIO_InitStructure.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStructure.Pin = GPIO_PIN_15;
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
__HAL_GPIO_EXTI_CLEAR_FLAG(GPIO_PIN_15);
// HAL_GPIO_WritePin(GPIOC, GPIO_PIN_5, GPIO_PIN_SET); // release CTPM reset
// HAL_Delay(310); // "Time of starting to report point after resetting" min
// is
// 300ms, giving an extra 10ms
}
/**
* @brief Enable the AF for the selected IO pin(s).
* @param DeviceAddr: Device address on communication Bus.
@ -439,17 +419,6 @@ void touch_set_mode(void) {
IOE_Delay(2);
}
void touch_power_on(void) {
// turn on CTP circuitry
touch_active_pin_state();
HAL_Delay(50);
}
void touch_power_off(void) {
// turn off CTP circuitry
HAL_Delay(50);
}
/**
* @brief Reset the stmpe811 by Software.
* @param DeviceAddr: Device address on communication Bus.
@ -470,55 +439,7 @@ void stmpe811_Reset() {
IOE_Delay(2);
}
secbool touch_init(void) {
GPIO_InitTypeDef GPIO_InitStructure = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
// PC4 capacitive touch panel module (CTPM) interrupt (INT) input
GPIO_InitStructure.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStructure.Pin = GPIO_PIN_15;
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
__HAL_GPIO_EXTI_CLEAR_FLAG(GPIO_PIN_15);
stmpe811_Reset();
touch_set_mode();
touch_sensitivity(0x06);
return sectrue;
}
secbool touch_sensitivity(uint8_t value) {
// set panel threshold (TH_GROUP) - default value is 0x12
// uint8_t touch_panel_threshold[] = {0x80, value};
// ensure(sectrue *
// (HAL_OK == HAL_I2C_Master_Transmit(
// &I2c_handle, TOUCH_ADDRESS, touch_panel_threshold,
// sizeof(touch_panel_threshold), 10)),
// NULL);
return sectrue;
}
uint32_t touch_is_detected(void) {
uint8_t state = ((IOE_Read(TS_I2C_ADDRESS, STMPE811_REG_TSC_CTRL) &
(uint8_t)STMPE811_TS_CTRL_STATUS) == (uint8_t)0x80);
return state > 0;
}
uint32_t touch_active(void) {
// check the interrupt line coming in from the CTPM.
// the line make a short pulse, which sets an interrupt flag when new data is
// available.
// Reference section 1.2 of "Application Note for FT6x06 CTPM". we
// configure the touch controller to use "interrupt trigger mode".
// uint32_t event = __HAL_GPIO_EXTI_GET_FLAG(GPIO_PIN_15);
// if (event != 0) {
// __HAL_GPIO_EXTI_CLEAR_FLAG(GPIO_PIN_15);
// }
static uint32_t touch_active(void) {
uint8_t state;
uint8_t ret = 0;
@ -539,17 +460,6 @@ uint32_t touch_active(void) {
return ret;
}
uint32_t check_timeout(uint32_t prev, uint32_t timeout) {
uint32_t current = hal_ticks_ms();
uint32_t diff = current - prev;
if (diff >= timeout) {
return 1;
}
return 0;
}
/**
* @brief Get the touch screen X and Y positions values
* @param DeviceAddr: Device address on communication Bus.
@ -647,62 +557,87 @@ void BSP_TS_GetState(TS_StateTypeDef *TsState) {
}
}
uint32_t touch_read(void) {
TS_StateTypeDef state = {0};
static uint32_t xy = 0;
static TS_StateTypeDef state_last = {0};
// static uint16_t first = 1;
static uint16_t touching = 0;
typedef struct {
// Set if driver is initialized
secbool initialized;
// Last lower-level driver state
TS_StateTypeDef prev_state;
if (!touch_is_detected()) {
if (touching) {
// touch end
memcpy(&state_last, &state, sizeof(state));
touching = 0;
return TOUCH_END | xy;
}
return 0;
} touch_driver_t;
// Touch driver instance
static touch_driver_t g_touch_driver = {
.initialized = secfalse,
};
secbool touch_init(void) {
touch_driver_t *driver = &g_touch_driver;
if (driver->initialized != sectrue) {
stmpe811_Reset();
touch_set_mode();
driver->initialized = sectrue;
}
BSP_TS_GetState(&state);
return driver->initialized;
}
if (state.TouchDetected == 0) {
return 0;
void touch_deinit(void) {
touch_driver_t *driver = &g_touch_driver;
if (driver->initialized == sectrue) {
// Not implemented properly
memset(driver, 0, sizeof(touch_driver_t));
}
}
// if (first != 0) {
// memcpy(&state_last, &state, sizeof(state));
// first = 0;
// return 0;
// }
secbool touch_ready(void) {
touch_driver_t *driver = &g_touch_driver;
return driver->initialized;
}
if ((state.TouchDetected == 0 && state_last.TouchDetected == 0) ||
memcmp(&state, &state_last, sizeof(state)) == 0) {
// no change detected
return 0;
}
xy = touch_pack_xy(state.X, state.Y);
if (state.TouchDetected && !state_last.TouchDetected) {
// touch start
memcpy(&state_last, &state, sizeof(state));
touching = 1;
return TOUCH_START | xy;
} else if (!state.TouchDetected && state_last.TouchDetected) {
// touch end
memcpy(&state_last, &state, sizeof(state));
touching = 0;
return TOUCH_END | xy;
} else {
// touch move
memcpy(&state_last, &state, sizeof(state));
return TOUCH_MOVE | xy;
}
secbool touch_set_sensitivity(uint8_t value) {
// Not implemented for the discovery kit
return sectrue;
}
uint8_t touch_get_version(void) {
// Not implemented for the discovery kit
return 0;
}
void touch_wait_until_ready(void) {}
secbool touch_activity(void) {
uint8_t state = ((IOE_Read(TS_I2C_ADDRESS, STMPE811_REG_TSC_CTRL) &
(uint8_t)STMPE811_TS_CTRL_STATUS) == (uint8_t)0x80);
return state > 0 ? sectrue : secfalse;
}
uint8_t touch_get_version(void) { return 0; }
uint32_t touch_get_event(void) {
touch_driver_t *driver = &g_touch_driver;
if (driver->initialized != sectrue) {
return 0;
}
TS_StateTypeDef new_state = {0};
BSP_TS_GetState(&new_state);
uint32_t event = 0;
if (new_state.TouchDetected && !driver->prev_state.TouchDetected) {
uint32_t xy = touch_pack_xy(new_state.X, new_state.Y);
event = TOUCH_START | xy;
} else if (!new_state.TouchDetected && driver->prev_state.TouchDetected) {
uint32_t xy = touch_pack_xy(driver->prev_state.X, driver->prev_state.Y);
event = TOUCH_END | xy;
} else if (new_state.TouchDetected) {
uint32_t xy = touch_pack_xy(new_state.X, new_state.Y);
event = TOUCH_MOVE | xy;
}
driver->prev_state = new_state;
return event;
}

View File

@ -360,7 +360,6 @@ int32_t SITRONIX_GetState(SITRONIX_Object_t *pObj, SITRONIX_State_t *State) {
return ret;
}
/**
* @brief Get the touch screen Xn and Yn positions values in multi-touch mode
* @param pObj Component object pointer
@ -1135,86 +1134,100 @@ static int32_t SITRONIX_Probe(uint32_t Instance) {
#include <string.h>
#include "touch.h"
// Touch driver
typedef struct {
// Set if driver is initialized
secbool initialized;
// Last lower-level driver state
TS_State_t prev_state;
} touch_driver_t;
// Touch driver instance
static touch_driver_t g_touch_driver = {
.initialized = secfalse,
};
secbool touch_init(void) {
TS_Init_t TsInit;
touch_driver_t *driver = &g_touch_driver;
/* Initialize the TouchScreen */
TsInit.Width = 480;
TsInit.Height = 480;
TsInit.Orientation = 0;
TsInit.Accuracy = 10;
if (sectrue != driver->initialized) {
TS_Init_t TsInit;
BSP_TS_Init(0, &TsInit);
/* Initialize the TouchScreen */
TsInit.Width = 480;
TsInit.Height = 480;
TsInit.Orientation = 0;
TsInit.Accuracy = 10;
BSP_TS_Init(0, &TsInit);
driver->initialized = sectrue;
}
return driver->initialized;
}
void touch_deinit(void) {
touch_driver_t *driver = &g_touch_driver;
if (sectrue == driver->initialized) {
BSP_TS_DeInit(0);
memset(driver, 0, sizeof(touch_driver_t));
}
}
secbool touch_ready(void) {
touch_driver_t *driver = &g_touch_driver;
return driver->initialized;
}
secbool touch_set_sensitivity(uint8_t value) {
// Not implemented for the discovery kit
return sectrue;
}
void touch_power_on(void) {}
void touch_power_off(void) {}
secbool touch_sensitivity(uint8_t value) { return sectrue; }
uint32_t touch_is_detected(void) { return sitronix_touching != 0; }
uint32_t touch_read(void) {
TS_State_t state = {0};
static uint32_t xy = 0;
static TS_State_t state_last = {0};
// static uint16_t first = 1;
static uint16_t touching = 0;
BSP_TS_GetState(0, &state);
state.TouchDetected = touch_is_detected();
state.TouchY = state.TouchY > 120 ? state.TouchY - 120 : 0;
state.TouchX = state.TouchX > 120 ? state.TouchX - 120 : 0;
if (!touch_is_detected()) {
// if (state.TouchDetected == 0) {
if (touching) {
// touch end
memcpy(&state_last, &state, sizeof(state));
touching = 0;
return TOUCH_END | xy;
}
return 0;
}
if (state.TouchDetected == 0) {
return 0;
}
// if (first != 0) {
// memcpy(&state_last, &state, sizeof(state));
// first = 0;
// return 0;
// }
if ((state.TouchDetected == 0 && state_last.TouchDetected == 0) ||
memcmp(&state, &state_last, sizeof(state)) == 0) {
// no change detected
return 0;
}
xy = touch_pack_xy(state.TouchX, state.TouchY);
if (state.TouchDetected && !state_last.TouchDetected) {
// touch start
memcpy(&state_last, &state, sizeof(state));
touching = 1;
return TOUCH_START | xy;
} else if (!state.TouchDetected && state_last.TouchDetected) {
// touch end
memcpy(&state_last, &state, sizeof(state));
touching = 0;
return TOUCH_END | xy;
} else {
// touch move
memcpy(&state_last, &state, sizeof(state));
return TOUCH_MOVE | xy;
}
uint8_t touch_get_version(void) {
// Not implemented for the discovery kit
return 0;
}
void touch_wait_until_ready(void) {}
secbool touch_activity(void) {
if (sitronix_touching) {
return sectrue;
} else {
return secfalse;
}
}
uint8_t touch_get_version(void) { return 0; }
uint32_t touch_get_event(void) {
touch_driver_t *driver = &g_touch_driver;
if (sectrue != driver->initialized) {
return 0;
}
TS_State_t new_state = {0};
BSP_TS_GetState(0, &new_state);
new_state.TouchX = new_state.TouchX > 120 ? new_state.TouchX - 120 : 0;
new_state.TouchY = new_state.TouchY > 120 ? new_state.TouchY - 120 : 0;
uint32_t event = 0;
if (new_state.TouchDetected && !driver->prev_state.TouchDetected) {
uint32_t xy = touch_pack_xy(new_state.TouchX, new_state.TouchY);
event = TOUCH_START | xy;
} else if (!new_state.TouchDetected && driver->prev_state.TouchDetected) {
uint32_t xy =
touch_pack_xy(driver->prev_state.TouchX, driver->prev_state.TouchY);
event = TOUCH_END | xy;
} else if (new_state.TouchDetected) {
uint32_t xy = touch_pack_xy(new_state.TouchX, new_state.TouchY);
event = TOUCH_MOVE | xy;
}
driver->prev_state = new_state;
return event;
}

View File

@ -1,32 +1,90 @@
#ifndef _TOUCH_H
#define _TOUCH_H
#ifndef TREZOR_HAL_TOUCH_H
#define TREZOR_HAL_TOUCH_H
#include <stdint.h>
#include "secbool.h"
// Initializes the touch driver
//
// Powers on and initializes touch driver controller.
// The function has no effect if the driver was already initialized.
//
// Returns `sectrue` if the hardware was successfuly initialized.
secbool touch_init(void);
// Deinitializes the touch driver
//
// The function deinitializes touch controller and powers it off.
void touch_deinit();
// Checks if the touch driver is ready to report touches
//
// Some drivers need time after power-up to stabilize. The app
// may use this function to wait until touch controller is
// fully functional.
secbool touch_ready(void);
// Gets the touch controller firmware version
//
// Can be called only if the touch controller was initialized,
// othervise returns 0.
//
// We do not interpret the value of the version, we just print it
// during the production test.
uint8_t touch_get_version(void);
// Sets touch controller sensitivity
//
// (Internally threadhsold for ????)
secbool touch_set_sensitivity(uint8_t value);
// Checks if the touch is currently reporting any events
//
// The purpose of this function is very special. It is used
// in bootloader startup to detect if the user is touching the screen.
// On some hardware it's a bit more sensitive then `touch_get_event()`
// since it does not filter out any events.
//
// The function should not be used together with `touch_get_event()`.
secbool touch_activity(void);
// Returns the last event in packed 32-bit format
//
// Returns `0` if there's no event or the driver is not initialized.
uint32_t touch_get_event(void);
// Touch event is packed 32-bit value
//
// 31 24 23 12 11 0
// |--------|------------|------------|
// | event | x-coord | y-coord |
// |--------|------------|------------|
//
//
// Touch event bits
#define TOUCH_START (1U << 24)
#define TOUCH_MOVE (1U << 25)
#define TOUCH_END (1U << 26)
secbool touch_init(void);
void touch_power_on(void);
void touch_power_off(void);
void touch_wait_until_ready(void);
secbool touch_sensitivity(uint8_t value);
uint32_t touch_read(void);
uint32_t touch_click(void);
uint32_t touch_is_detected(void);
uint8_t touch_get_version(void);
// Returns x-coordinates from a packed touch event
static inline uint16_t touch_unpack_x(uint32_t evt) {
return (evt >> 12) & 0xFFF;
}
// Returns y-coordinates from a packed touch event
static inline uint16_t touch_unpack_y(uint32_t evt) {
return (evt >> 0) & 0xFFF;
}
// Creates packed touch event from x and y coordinates
static inline uint32_t touch_pack_xy(uint16_t x, uint16_t y) {
return ((x & 0xFFF) << 12) | (y & 0xFFF);
}
// -------------------------
// legacy:
uint32_t touch_is_detected(void);
#endif //_TOUCH_H

View File

@ -0,0 +1,70 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include TREZOR_BOARD
#include <SDL.h>
#include "button.h"
#include "common.h"
#include "platform.h"
static char last_left = 0, last_right = 0;
char button_state_left(void) { return last_left; }
char button_state_right(void) { return last_right; }
uint32_t button_read(void) {
SDL_Event event;
SDL_PumpEvents();
if (SDL_PollEvent(&event) > 0) {
switch (event.type) {
case SDL_KEYDOWN:
if (event.key.repeat) {
break;
}
switch (event.key.keysym.sym) {
case SDLK_LEFT:
last_left = 1;
return BTN_EVT_DOWN | BTN_LEFT;
case SDLK_RIGHT:
last_right = 1;
return BTN_EVT_DOWN | BTN_RIGHT;
}
break;
case SDL_KEYUP:
if (event.key.repeat) {
break;
}
switch (event.key.keysym.sym) {
case SDLK_LEFT:
last_left = 0;
return BTN_EVT_UP | BTN_LEFT;
case SDLK_RIGHT:
last_right = 0;
return BTN_EVT_UP | BTN_RIGHT;
}
break;
}
}
return 0;
}
void button_init(void) {}

View File

@ -17,13 +17,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include TREZOR_BOARD
#include <SDL.h>
#include <stdbool.h>
#include <stdint.h>
#include TREZOR_BOARD
#ifdef USE_TOUCH
#include "common.h"
#include "platform.h"
#include "touch.h"
@ -31,9 +30,6 @@
extern int sdl_display_res_x, sdl_display_res_y;
extern int sdl_touch_offset_x, sdl_touch_offset_y;
static int _touch_x = 0;
static int _touch_y = 0;
// distance from the edge where arrow button swipe starts [px]
static const int _btn_swipe_begin = 120;
// length of the arrow button swipe [px]
@ -53,9 +49,23 @@ typedef enum {
BUTTON_SWIPE_UP_INITIATED,
BUTTON_SWIPE_DOWN_INITIATED,
BUTTON_SWIPE_COMPLETED
} touch_input_state_t;
} touch_state_t;
static touch_input_state_t input_state = IDLE;
typedef struct {
// Set if driver is initialized
secbool initialized;
// Current state of the touch driver
touch_state_t state;
// Last valid coordinates
int last_x;
int last_y;
} touch_driver_t;
// Touch driver instance
static touch_driver_t g_touch_driver = {
.initialized = secfalse,
};
static bool is_inside_display(int x, int y) {
return x >= sdl_touch_offset_x && y >= sdl_touch_offset_y &&
@ -63,15 +73,15 @@ static bool is_inside_display(int x, int y) {
y - sdl_touch_offset_y < sdl_display_res_y;
}
static bool is_button_swipe_initiated() {
return input_state == BUTTON_SWIPE_LEFT_INITIATED ||
input_state == BUTTON_SWIPE_RIGHT_INITIATED ||
input_state == BUTTON_SWIPE_UP_INITIATED ||
input_state == BUTTON_SWIPE_DOWN_INITIATED;
static bool is_button_swipe_initiated(const touch_driver_t* driver) {
return driver->state == BUTTON_SWIPE_LEFT_INITIATED ||
driver->state == BUTTON_SWIPE_RIGHT_INITIATED ||
driver->state == BUTTON_SWIPE_UP_INITIATED ||
driver->state == BUTTON_SWIPE_DOWN_INITIATED;
}
static void handle_mouse_events(SDL_Event event, int* ev_type, int* ev_x,
int* ev_y) {
static void handle_mouse_events(touch_driver_t* driver, SDL_Event event,
int* ev_type, int* ev_x, int* ev_y) {
bool inside_display = is_inside_display(event.button.x, event.button.y);
switch (event.type) {
case SDL_MOUSEBUTTONDOWN:
@ -79,116 +89,167 @@ static void handle_mouse_events(SDL_Event event, int* ev_type, int* ev_x,
*ev_x = event.button.x - sdl_touch_offset_x;
*ev_y = event.button.y - sdl_touch_offset_y;
*ev_type = TOUCH_START;
input_state = MOUSE_DOWN_INSIDE;
driver->state = MOUSE_DOWN_INSIDE;
}
break;
case SDL_MOUSEBUTTONUP:
if (input_state != IDLE) {
*ev_x = inside_display ? event.button.x - sdl_touch_offset_x : _touch_x;
*ev_y = inside_display ? event.button.y - sdl_touch_offset_y : _touch_y;
if (driver->state != IDLE) {
*ev_x = inside_display ? event.button.x - sdl_touch_offset_x
: driver->last_x;
*ev_y = inside_display ? event.button.y - sdl_touch_offset_y
: driver->last_y;
*ev_type = TOUCH_END;
input_state = IDLE;
driver->state = IDLE;
}
break;
case SDL_MOUSEMOTION:
if (input_state != IDLE) {
if (driver->state != IDLE) {
if (inside_display) {
*ev_x = event.motion.x - sdl_touch_offset_x;
*ev_y = event.motion.y - sdl_touch_offset_y;
// simulate TOUCH_START if pressed in mouse returned on visible area
*ev_type =
(input_state == MOUSE_DOWN_OUTSIDE) ? TOUCH_START : TOUCH_MOVE;
input_state = MOUSE_DOWN_INSIDE;
(driver->state == MOUSE_DOWN_OUTSIDE) ? TOUCH_START : TOUCH_MOVE;
driver->state = MOUSE_DOWN_INSIDE;
} else {
if (input_state == MOUSE_DOWN_INSIDE) {
if (driver->state == MOUSE_DOWN_INSIDE) {
// use last valid coordinates and simulate TOUCH_END
*ev_x = _touch_x;
*ev_y = _touch_y;
*ev_x = driver->last_x;
*ev_y = driver->last_y;
*ev_type = TOUCH_END;
}
input_state = MOUSE_DOWN_OUTSIDE;
driver->state = MOUSE_DOWN_OUTSIDE;
}
}
break;
}
}
static void handle_button_events(SDL_Event event, int* ev_type, int* ev_x,
int* ev_y) {
static void handle_button_events(touch_driver_t* driver, SDL_Event event,
int* ev_type, int* ev_x, int* ev_y) {
// Handle arrow buttons to trigger a scroll movement by set length in the
// direction of the button
if (event.type == SDL_KEYDOWN && !event.key.repeat &&
!is_button_swipe_initiated()) {
!is_button_swipe_initiated(driver)) {
switch (event.key.keysym.sym) {
case SDLK_LEFT:
*ev_x = _btn_swipe_begin;
*ev_y = sdl_display_res_y / 2;
*ev_type = TOUCH_START;
input_state = BUTTON_SWIPE_LEFT_INITIATED;
driver->state = BUTTON_SWIPE_LEFT_INITIATED;
break;
case SDLK_RIGHT:
*ev_x = sdl_display_res_x - _btn_swipe_begin;
*ev_y = sdl_display_res_y / 2;
*ev_type = TOUCH_START;
input_state = BUTTON_SWIPE_RIGHT_INITIATED;
driver->state = BUTTON_SWIPE_RIGHT_INITIATED;
break;
case SDLK_UP:
*ev_x = sdl_display_res_x / 2;
*ev_y = _btn_swipe_begin;
*ev_type = TOUCH_START;
input_state = BUTTON_SWIPE_UP_INITIATED;
driver->state = BUTTON_SWIPE_UP_INITIATED;
break;
case SDLK_DOWN:
*ev_x = sdl_display_res_x / 2;
*ev_y = sdl_display_res_y - _btn_swipe_begin;
*ev_type = TOUCH_START;
input_state = BUTTON_SWIPE_DOWN_INITIATED;
driver->state = BUTTON_SWIPE_DOWN_INITIATED;
break;
}
} else if (event.type == SDL_KEYUP && input_state != IDLE) {
} else if (event.type == SDL_KEYUP && driver->state != IDLE) {
switch (event.key.keysym.sym) {
case SDLK_LEFT:
if (input_state == BUTTON_SWIPE_LEFT_INITIATED) {
if (driver->state == BUTTON_SWIPE_LEFT_INITIATED) {
*ev_x = _btn_swipe_begin + _btn_swipe_length;
*ev_y = sdl_display_res_y / 2;
*ev_type = TOUCH_MOVE;
input_state = BUTTON_SWIPE_COMPLETED;
driver->state = BUTTON_SWIPE_COMPLETED;
}
break;
case SDLK_RIGHT:
if (input_state == BUTTON_SWIPE_RIGHT_INITIATED) {
if (driver->state == BUTTON_SWIPE_RIGHT_INITIATED) {
*ev_x = sdl_display_res_x - _btn_swipe_begin - _btn_swipe_length;
*ev_y = sdl_display_res_y / 2;
*ev_type = TOUCH_MOVE;
input_state = BUTTON_SWIPE_COMPLETED;
driver->state = BUTTON_SWIPE_COMPLETED;
}
break;
case SDLK_UP:
if (input_state == BUTTON_SWIPE_UP_INITIATED) {
if (driver->state == BUTTON_SWIPE_UP_INITIATED) {
*ev_x = sdl_display_res_x / 2;
*ev_y = _btn_swipe_begin + _btn_swipe_length;
*ev_type = TOUCH_MOVE;
input_state = BUTTON_SWIPE_COMPLETED;
driver->state = BUTTON_SWIPE_COMPLETED;
}
break;
case SDLK_DOWN:
if (input_state == BUTTON_SWIPE_DOWN_INITIATED) {
if (driver->state == BUTTON_SWIPE_DOWN_INITIATED) {
*ev_x = sdl_display_res_x / 2;
*ev_y = sdl_display_res_y - _btn_swipe_begin - _btn_swipe_length;
*ev_type = TOUCH_MOVE;
input_state = BUTTON_SWIPE_COMPLETED;
driver->state = BUTTON_SWIPE_COMPLETED;
}
break;
}
}
}
uint32_t touch_read(void) {
if (input_state == BUTTON_SWIPE_COMPLETED) {
input_state = IDLE;
return TOUCH_END | touch_pack_xy(_touch_x, _touch_y);
secbool touch_init(void) {
touch_driver_t* driver = &g_touch_driver;
if (driver->initialized != sectrue) {
memset(driver, 0, sizeof(touch_driver_t));
driver->state = IDLE;
driver->initialized = sectrue;
}
return driver->initialized;
}
void touch_deinit(void) {
touch_driver_t* driver = &g_touch_driver;
if (driver->initialized == sectrue) {
memset(driver, 0, sizeof(touch_driver_t));
}
}
secbool touch_ready(void) {
touch_driver_t* driver = &g_touch_driver;
return driver->initialized;
}
secbool touch_set_sensitivity(uint8_t value) {
// Not implemented on the emulator
return sectrue;
}
uint8_t touch_get_version(void) {
// Not implemented on the emulator
return 0;
}
secbool touch_activity(void) {
if (touch_get_event() != 0) {
return sectrue;
} else {
return secfalse;
}
}
uint32_t touch_get_event(void) {
touch_driver_t* driver = &g_touch_driver;
if (driver->initialized != sectrue) {
return 0;
}
if (driver->state == BUTTON_SWIPE_COMPLETED) {
driver->state = IDLE;
return TOUCH_END | touch_pack_xy(driver->last_x, driver->last_y);
}
emulator_poll_events();
@ -200,80 +261,18 @@ uint32_t touch_read(void) {
int ev_type = 0;
while (SDL_PollEvent(&event) > 0) {
if (input_state == IDLE || input_state == MOUSE_DOWN_INSIDE ||
input_state == MOUSE_DOWN_OUTSIDE) {
handle_mouse_events(event, &ev_type, &ev_x, &ev_y);
if (driver->state == IDLE || driver->state == MOUSE_DOWN_INSIDE ||
driver->state == MOUSE_DOWN_OUTSIDE) {
handle_mouse_events(driver, event, &ev_type, &ev_x, &ev_y);
}
if (input_state == IDLE || is_button_swipe_initiated()) {
handle_button_events(event, &ev_type, &ev_x, &ev_y);
if (driver->state == IDLE || is_button_swipe_initiated(driver)) {
handle_button_events(driver, event, &ev_type, &ev_x, &ev_y);
}
if (ev_type != 0) {
_touch_x = ev_x;
_touch_y = ev_y;
driver->last_x = ev_x;
driver->last_y = ev_y;
}
}
return ev_type | touch_pack_xy(ev_x, ev_y);
}
secbool touch_init(void) { return sectrue; }
void touch_power_on(void) {}
void touch_wait_until_ready(void) {}
uint32_t touch_is_detected(void) {
return input_state == MOUSE_DOWN_INSIDE || is_button_swipe_initiated();
}
uint8_t touch_get_version(void) { return 0; }
#endif
#ifdef USE_BUTTON
#include "button.h"
static char last_left = 0, last_right = 0;
char button_state_left(void) { return last_left; }
char button_state_right(void) { return last_right; }
uint32_t button_read(void) {
SDL_Event event;
SDL_PumpEvents();
if (SDL_PollEvent(&event) > 0) {
switch (event.type) {
case SDL_KEYDOWN:
if (event.key.repeat) {
break;
}
switch (event.key.keysym.sym) {
case SDLK_LEFT:
last_left = 1;
return BTN_EVT_DOWN | BTN_LEFT;
case SDLK_RIGHT:
last_right = 1;
return BTN_EVT_DOWN | BTN_RIGHT;
}
break;
case SDL_KEYUP:
if (event.key.repeat) {
break;
}
switch (event.key.keysym.sym) {
case SDLK_LEFT:
last_left = 0;
return BTN_EVT_UP | BTN_LEFT;
case SDLK_RIGHT:
last_right = 0;
return BTN_EVT_UP | BTN_RIGHT;
}
break;
}
}
return 0;
}
void button_init(void) {}
#endif

View File

@ -53,6 +53,7 @@
#include "py/repl.h"
#include "py/runtime.h"
#include "py/stackctrl.h"
#include "touch.h"
#include "common.h"
@ -474,6 +475,10 @@ MP_NOINLINE int main_(int argc, char **argv) {
display_init();
#if USE_TOUCH
touch_init();
#endif
// Map trezor.flash to memory.
flash_init();
flash_otp_init();

View File

@ -71,7 +71,6 @@ def configure(
if "input" in features_wanted:
sources += ["embed/trezorhal/stm32f4/i2c.c"]
sources += ["embed/trezorhal/stm32f4/touch/stmpe811.c"]
sources += ["embed/lib/touch.c"]
features_available.append("touch")
if "usb" in features_wanted:

View File

@ -60,9 +60,6 @@ def configure(
sources += [
"embed/trezorhal/stm32u5/i2c.c",
]
sources += [
"embed/lib/touch.c",
]
sources += [
"embed/trezorhal/stm32u5/touch/sitronix.c",
]

View File

@ -31,6 +31,7 @@ def configure(
defines += ["FLASH_BLOCK_WORDS=1"]
if "input" in features_wanted:
sources += ["embed/trezorhal/unix/button.c"]
features_available.append("button")
sources += ["embed/models/T1B1/model_T1B1_layout.c"]

View File

@ -41,6 +41,7 @@ def configure(
features_available.append("optiga")
if "input" in features_wanted:
sources += ["embed/trezorhal/unix/button.c"]
features_available.append("button")
sources += ["embed/models/T2B1/model_T2B1_layout.c"]

View File

@ -51,6 +51,7 @@ def configure(
sources += ["embed/trezorhal/unix/sbu.c"]
if "input" in features_wanted:
sources += ["embed/trezorhal/unix/touch.c"]
features_available.append("touch")
features_available.append("backlight")

View File

@ -82,7 +82,6 @@ def configure(
if "input" in features_wanted:
sources += ["embed/trezorhal/stm32f4/i2c.c"]
sources += ["embed/trezorhal/stm32f4/touch/ft6x36.c"]
sources += ["embed/lib/touch.c"]
features_available.append("touch")
if "sd_card" in features_wanted:

View File

@ -59,6 +59,7 @@ def configure(
sources += ["embed/trezorhal/unix/optiga.c"]
if "input" in features_wanted:
sources += ["embed/trezorhal/unix/touch.c"]
features_available.append("touch")
features_available.append("backlight")

View File

@ -73,7 +73,6 @@ def configure(
if "input" in features_wanted:
sources += ["embed/trezorhal/stm32u5/i2c.c"]
sources += ["embed/trezorhal/stm32u5/touch/ft6x36.c"]
sources += ["embed/lib/touch.c"]
features_available.append("touch")
if "haptic" in features_wanted:

View File

@ -75,7 +75,6 @@ def configure(
if "input" in features_wanted:
sources += ["embed/trezorhal/stm32u5/i2c.c"]
sources += ["embed/trezorhal/stm32u5/touch/ft6x36.c"]
sources += ["embed/lib/touch.c"]
features_available.append("touch")
if "haptic" in features_wanted: