Smart tab5keyboard (#533)
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
#include "devices/Display.h"
|
||||
#include "devices/SdCard.h"
|
||||
#include "devices/Power.h"
|
||||
#include "devices/Tab5Keyboard.h"
|
||||
|
||||
@@ -13,11 +12,11 @@ using namespace tt::hal;
|
||||
static constexpr auto* TAG = "Tab5";
|
||||
|
||||
static DeviceVector createDevices() {
|
||||
::Device* i2c2 = device_find_by_name("i2c2");
|
||||
auto* i2c2 = device_find_by_name("i2c2");
|
||||
check(i2c2, "i2c2 not found");
|
||||
return {
|
||||
createPower(),
|
||||
createDisplay(),
|
||||
createSdCard(),
|
||||
std::make_shared<Tab5Keyboard>(i2c2)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -38,8 +38,10 @@ static std::shared_ptr<tt::hal::touch::TouchDevice> createGt911Touch() {
|
||||
}
|
||||
|
||||
static std::shared_ptr<tt::hal::touch::TouchDevice> createSt7123Touch() {
|
||||
auto* i2c = device_find_by_name("i2c0");
|
||||
check(i2c, "i2c0 not found");
|
||||
auto configuration = std::make_unique<St7123Touch::Configuration>(
|
||||
I2C_NUM_0,
|
||||
i2c,
|
||||
720,
|
||||
1280,
|
||||
false, // swapXY
|
||||
|
||||
@@ -102,7 +102,7 @@ bool Ili9881cDisplay::createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, cons
|
||||
.pixel_format = LCD_COLOR_PIXEL_FORMAT_RGB565,
|
||||
.in_color_format = LCD_COLOR_FMT_RGB565,
|
||||
.out_color_format = LCD_COLOR_FMT_RGB565,
|
||||
.num_fbs = 1, // TODO: 2?
|
||||
.num_fbs = 2,
|
||||
.video_timing =
|
||||
{
|
||||
.h_size = 720,
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
#include "SdCard.h"
|
||||
|
||||
#include <tactility/device.h>
|
||||
#include <Tactility/hal/sdcard/SpiSdCardDevice.h>
|
||||
|
||||
constexpr auto SDCARD_PIN_CS = GPIO_NUM_42;
|
||||
|
||||
using tt::hal::sdcard::SpiSdCardDevice;
|
||||
|
||||
std::shared_ptr<SdCardDevice> createSdCard() {
|
||||
auto configuration = std::make_unique<SpiSdCardDevice::Config>(
|
||||
SDCARD_PIN_CS,
|
||||
GPIO_NUM_NC,
|
||||
GPIO_NUM_NC,
|
||||
GPIO_NUM_NC,
|
||||
SdCardDevice::MountBehaviour::AtBoot
|
||||
);
|
||||
|
||||
auto* spi_controller = device_find_by_name("spi0");
|
||||
check(spi_controller, "spi0 not found");
|
||||
|
||||
return std::make_shared<SpiSdCardDevice>(
|
||||
std::move(configuration),
|
||||
spi_controller
|
||||
);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <Tactility/hal/sdcard/SdCardDevice.h>
|
||||
#include <memory>
|
||||
|
||||
using tt::hal::sdcard::SdCardDevice;
|
||||
|
||||
std::shared_ptr<SdCardDevice> createSdCard();
|
||||
@@ -99,7 +99,7 @@ bool St7123Display::createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, const
|
||||
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT,
|
||||
.dpi_clock_freq_mhz = 70,
|
||||
.pixel_format = LCD_COLOR_PIXEL_FORMAT_RGB565,
|
||||
.num_fbs = 1,
|
||||
.num_fbs = 2,
|
||||
.video_timing = {
|
||||
.h_size = 720,
|
||||
.v_size = 1280,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "St7123Touch.h"
|
||||
|
||||
#include <Tactility/Logger.h>
|
||||
#include <tactility/drivers/esp32_i2c_master.h>
|
||||
#include <esp_lcd_touch_st7123.h>
|
||||
#include <esp_err.h>
|
||||
|
||||
@@ -8,11 +9,9 @@ static const auto LOGGER = tt::Logger("ST7123Touch");
|
||||
|
||||
bool St7123Touch::createIoHandle(esp_lcd_panel_io_handle_t& outHandle) {
|
||||
esp_lcd_panel_io_i2c_config_t io_config = ESP_LCD_TOUCH_IO_I2C_ST7123_CONFIG();
|
||||
return esp_lcd_new_panel_io_i2c(
|
||||
static_cast<esp_lcd_i2c_bus_handle_t>(configuration->port),
|
||||
&io_config,
|
||||
&outHandle
|
||||
) == ESP_OK;
|
||||
io_config.scl_speed_hz = esp32_i2c_master_get_clock_frequency(configuration->controller);
|
||||
i2c_master_bus_handle_t bus = esp32_i2c_master_get_bus_handle(configuration->controller);
|
||||
return esp_lcd_new_panel_io_i2c_v2(bus, &io_config, &outHandle) == ESP_OK;
|
||||
}
|
||||
|
||||
bool St7123Touch::createTouchHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_touch_config_t& config, esp_lcd_touch_handle_t& touchHandle) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <EspLcdTouch.h>
|
||||
#include <Tactility/TactilityCore.h>
|
||||
#include <driver/i2c.h>
|
||||
#include <tactility/device.h>
|
||||
|
||||
class St7123Touch final : public EspLcdTouch {
|
||||
|
||||
@@ -12,14 +12,14 @@ public:
|
||||
public:
|
||||
|
||||
Configuration(
|
||||
i2c_port_t port,
|
||||
::Device* controller,
|
||||
uint16_t xMax,
|
||||
uint16_t yMax,
|
||||
bool swapXy = false,
|
||||
bool mirrorX = false,
|
||||
bool mirrorY = false,
|
||||
gpio_num_t pinInterrupt = GPIO_NUM_NC
|
||||
) : port(port),
|
||||
) : controller(controller),
|
||||
xMax(xMax),
|
||||
yMax(yMax),
|
||||
swapXy(swapXy),
|
||||
@@ -28,7 +28,7 @@ public:
|
||||
pinInterrupt(pinInterrupt)
|
||||
{}
|
||||
|
||||
i2c_port_t port;
|
||||
::Device* controller;
|
||||
uint16_t xMax;
|
||||
uint16_t yMax;
|
||||
bool swapXy;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "Tab5Keyboard.h"
|
||||
#include <Tactility/app/App.h>
|
||||
#include <Tactility/lvgl/Keyboard.h>
|
||||
#include <Tactility/lvgl/LvglSync.h>
|
||||
#include <tactility/drivers/i2c_controller.h>
|
||||
#include <tactility/log.h>
|
||||
#include <esp_timer.h>
|
||||
@@ -329,6 +331,113 @@ void Tab5Keyboard::processKeyboard() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkAttachState();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// applyAutoRotation - on attach, switches to landscape if not already (saving
|
||||
// the prior rotation); on detach, restores the saved rotation if we were the
|
||||
// ones who changed it. Only affects the live LVGL rotation, never persisted
|
||||
// display settings.
|
||||
// ---------------------------------------------------------------------------
|
||||
bool Tab5Keyboard::applyAutoRotation(bool keyboardAttached) {
|
||||
auto* display = lv_indev_get_display(kbHandle);
|
||||
if (display == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tt::lvgl::lock(pdMS_TO_TICKS(100))) {
|
||||
return false; // retry next poll
|
||||
}
|
||||
|
||||
if (keyboardAttached) {
|
||||
if (lv_display_get_rotation(display) != LV_DISPLAY_ROTATION_90) {
|
||||
savedRotation = lv_display_get_rotation(display);
|
||||
rotationOverrideActive = true;
|
||||
lv_display_set_rotation(display, LV_DISPLAY_ROTATION_90);
|
||||
}
|
||||
} else {
|
||||
// Only restore if rotation is still what we set it to - if the user manually
|
||||
// changed it since attaching, respect their choice instead.
|
||||
if (rotationOverrideActive && lv_display_get_rotation(display) == LV_DISPLAY_ROTATION_90) {
|
||||
lv_display_set_rotation(display, savedRotation);
|
||||
}
|
||||
rotationOverrideActive = false;
|
||||
}
|
||||
|
||||
tt::lvgl::unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// checkAttachState - throttled (~1s) hot-plug detection. Reapplies device
|
||||
// register configuration and auto-rotation on detach/attach transitions.
|
||||
// ---------------------------------------------------------------------------
|
||||
void Tab5Keyboard::checkAttachState() {
|
||||
static constexpr uint32_t ATTACH_CHECK_TICKS = 50; // ~1s at 20ms/tick
|
||||
|
||||
if (++attachCheckTickCounter < ATTACH_CHECK_TICKS) {
|
||||
return;
|
||||
}
|
||||
attachCheckTickCounter = 0;
|
||||
|
||||
const bool attached = isAttached();
|
||||
if (attached == wasAttached) {
|
||||
pendingAttachConfirmCount = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Require the new state to be confirmed on a second consecutive check before acting -
|
||||
// a single probe on a floating/half-connected bus (e.g. mid-unplug) can false-positive.
|
||||
if (attached != pendingAttachState || pendingAttachConfirmCount == 0) {
|
||||
pendingAttachState = attached;
|
||||
pendingAttachConfirmCount = 1;
|
||||
return;
|
||||
}
|
||||
pendingAttachConfirmCount = 0;
|
||||
|
||||
if (attached) {
|
||||
reinitDevice();
|
||||
}
|
||||
if (!applyAutoRotation(attached)) {
|
||||
return; // keep prior state so transition is retried
|
||||
}
|
||||
wasAttached = attached;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// lateStart - see header comment. Brings up LVGL input handling for a keyboard
|
||||
// that wasn't attached at boot (startLvgl() wasn't called from attachDevices()).
|
||||
// ---------------------------------------------------------------------------
|
||||
bool Tab5Keyboard::lateStart() {
|
||||
if (kbHandle != nullptr) {
|
||||
return true; // already started
|
||||
}
|
||||
|
||||
auto* display = lv_display_get_default();
|
||||
if (display == nullptr) {
|
||||
return false; // LVGL not ready yet
|
||||
}
|
||||
|
||||
if (!tt::lvgl::lock(pdMS_TO_TICKS(100))) {
|
||||
return false; // try again on the next attach-state check
|
||||
}
|
||||
|
||||
bool started = startLvgl(display);
|
||||
if (started) {
|
||||
tt::lvgl::hardware_keyboard_set_indev(kbHandle);
|
||||
|
||||
// redraw() assigns every indev that exists at the time to the active screen's
|
||||
// input group. This indev didn't exist yet at the last redraw(), so it has no
|
||||
// group and won't deliver key events until the next app switch. Join the
|
||||
// current default group now so input works immediately on the visible screen.
|
||||
lv_indev_set_group(kbHandle, lv_group_get_default());
|
||||
}
|
||||
|
||||
tt::lvgl::unlock();
|
||||
|
||||
return started;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -359,21 +468,31 @@ Tab5Keyboard::~Tab5Keyboard() {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// reinitDevice - (re)applies the device register configuration. Used at
|
||||
// startLvgl() and again on hot-plug reattach, since the device's RGB mode and
|
||||
// interrupt configuration are volatile and reset to power-on defaults when
|
||||
// the keyboard is unplugged and reconnected.
|
||||
// ---------------------------------------------------------------------------
|
||||
void Tab5Keyboard::reinitDevice() {
|
||||
writeReg(REG_KEYBOARD_MODE, 0x00); // Normal mode
|
||||
writeReg(REG_EVENT_NUM, 0x00); // flush event queue
|
||||
writeReg(REG_INT_STAT, 0x00); // clear pending INT
|
||||
writeReg(REG_RGB_MODE, 0x01); // Custom RGB mode (manual LED control)
|
||||
writeReg(REG_BRIGHTNESS, 50); // 50% brightness
|
||||
updateLeds(); // restore current LED state
|
||||
|
||||
if (irqConfigured) {
|
||||
writeReg(REG_INT_CFG, 0x01); // re-enable Normal-mode interrupt (bit 0)
|
||||
}
|
||||
}
|
||||
|
||||
bool Tab5Keyboard::startLvgl(lv_display_t* display) {
|
||||
if (!queue) {
|
||||
LOG_E("Tab5Keyboard", "Input queue allocation failed — cannot start");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set Normal mode explicitly — device may power up in a different mode
|
||||
if (!writeReg(REG_KEYBOARD_MODE, 0x00)) {
|
||||
LOG_E("Tab5Keyboard", "Failed to set keyboard mode");
|
||||
return false;
|
||||
}
|
||||
writeReg(REG_EVENT_NUM, 0x00); // flush event queue
|
||||
writeReg(REG_INT_STAT, 0x00); // clear pending INT
|
||||
writeReg(REG_RGB_MODE, 0x01); // Custom RGB mode (manual LED control)
|
||||
writeReg(REG_BRIGHTNESS, 50); // 50% brightness
|
||||
symActive = false;
|
||||
aaSticky = false;
|
||||
aaHeld = false;
|
||||
@@ -383,13 +502,14 @@ bool Tab5Keyboard::startLvgl(lv_display_t* display) {
|
||||
repeatRow = 0xFF;
|
||||
repeatCol = 0xFF;
|
||||
repeatLastMs = 0;
|
||||
updateLeds(); // both LEDs off initially
|
||||
|
||||
// Enable Normal-mode interrupt (bit 0)
|
||||
if (!writeReg(REG_INT_CFG, 0x01)) {
|
||||
LOG_E("Tab5Keyboard", "Failed to configure interrupt register");
|
||||
return false;
|
||||
}
|
||||
configureIrqPin(); // best-effort; falls back to polling if it fails. Must run before
|
||||
// reinitDevice() so REG_INT_CFG is written if IRQ setup succeeded.
|
||||
|
||||
// Best-effort: if the keyboard isn't attached yet (e.g. started speculatively at
|
||||
// boot so it can be detected later via hot-plug), these I2C writes fail silently
|
||||
// and reinitDevice() runs again once attach is detected.
|
||||
reinitDevice();
|
||||
|
||||
kbHandle = lv_indev_create();
|
||||
lv_indev_set_type(kbHandle, LV_INDEV_TYPE_KEYPAD);
|
||||
@@ -397,7 +517,8 @@ bool Tab5Keyboard::startLvgl(lv_display_t* display) {
|
||||
lv_indev_set_display(kbHandle, display);
|
||||
lv_indev_set_user_data(kbHandle, this);
|
||||
|
||||
configureIrqPin(); // best-effort; falls back to polling if it fails
|
||||
wasAttached = isAttached();
|
||||
rotationOverrideActive = false;
|
||||
|
||||
assert(inputTimer == nullptr);
|
||||
inputTimer = std::make_unique<tt::Timer>(tt::Timer::Type::Periodic, pdMS_TO_TICKS(20), [this] {
|
||||
@@ -405,6 +526,10 @@ bool Tab5Keyboard::startLvgl(lv_display_t* display) {
|
||||
});
|
||||
inputTimer->start();
|
||||
|
||||
if (wasAttached) {
|
||||
applyAutoRotation(true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,16 @@ class Tab5Keyboard final : public tt::hal::keyboard::KeyboardDevice {
|
||||
volatile bool irqPending = false;
|
||||
bool irqConfigured = false;
|
||||
|
||||
// Hot-plug attach-state polling (piggybacks on the 20ms inputTimer)
|
||||
bool wasAttached = false;
|
||||
uint32_t attachCheckTickCounter = 0;
|
||||
// I2C probes can false-positive on a floating/half-connected bus (e.g. mid-unplug), so a
|
||||
// state change is only acted on once it's seen on two consecutive ~1s checks in a row.
|
||||
bool pendingAttachState = false;
|
||||
uint8_t pendingAttachConfirmCount = 0;
|
||||
lv_display_rotation_t savedRotation = LV_DISPLAY_ROTATION_0;
|
||||
bool rotationOverrideActive = false;
|
||||
|
||||
// Software key-repeat state (tracked by position to survive modifier changes)
|
||||
uint32_t repeatKey = 0;
|
||||
uint8_t repeatRow = 0xFF;
|
||||
@@ -44,6 +54,10 @@ class Tab5Keyboard final : public tt::hal::keyboard::KeyboardDevice {
|
||||
void removeIrqPin();
|
||||
static void IRAM_ATTR irqHandler(void* arg);
|
||||
|
||||
void reinitDevice();
|
||||
bool applyAutoRotation(bool keyboardAttached);
|
||||
void checkAttachState();
|
||||
|
||||
void drainEvents();
|
||||
void processKeyboard();
|
||||
static void readCallback(lv_indev_t* indev, lv_indev_data_t* data);
|
||||
@@ -62,4 +76,10 @@ public:
|
||||
bool stopLvgl() override;
|
||||
bool isAttached() const override;
|
||||
lv_indev_t* getLvglIndev() override { return kbHandle; }
|
||||
|
||||
// Starts LVGL input handling and registers the hardware keyboard indev for a device
|
||||
// that wasn't attached at boot (so startLvgl() was never called from Lvgl.cpp's
|
||||
// attachDevices()). Called from the device module's attach-detection timer once the
|
||||
// keyboard is first detected post-boot. No-op if LVGL input is already started.
|
||||
bool lateStart();
|
||||
};
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
#include <tactility/drivers/gpio_controller.h>
|
||||
#include <tactility/log.h>
|
||||
|
||||
#include <Tactility/hal/keyboard/KeyboardDevice.h>
|
||||
|
||||
#include "devices/Tab5Keyboard.h"
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/timers.h>
|
||||
|
||||
@@ -13,14 +17,17 @@
|
||||
constexpr auto GPIO_EXP0_PIN_SPEAKER_ENABLE = 1;
|
||||
constexpr auto GPIO_EXP0_PIN_HEADPHONE_DETECT = 7;
|
||||
constexpr auto HP_DETECT_POLL_MS = 1000;
|
||||
constexpr auto KB_DETECT_POLL_MS = 1000;
|
||||
|
||||
// hp_detect_timer is only touched from start()/stop(), which are called serially
|
||||
// by the module manager — no atomic needed for the handle itself.
|
||||
// hp_detect_timer and kb_detect_timer are only touched from start()/stop(), which are called
|
||||
// serially by the module manager — no atomic needed for the handles themselves.
|
||||
static TimerHandle_t hp_detect_timer = nullptr;
|
||||
static TimerHandle_t kb_detect_timer = nullptr;
|
||||
static std::atomic<Device*> io_expander0_cached { nullptr };
|
||||
// Flags are written by the timer daemon task and read by start()/stop() — use atomics.
|
||||
static std::atomic<bool> hp_detect_last { false };
|
||||
static std::atomic<bool> hp_detect_initialized { false };
|
||||
static std::atomic<bool> kb_late_started { false };
|
||||
|
||||
static void headphoneDetectCallback(TimerHandle_t /*timer*/) {
|
||||
Device* cached = io_expander0_cached.load(std::memory_order_acquire);
|
||||
@@ -68,6 +75,40 @@ static void headphoneDetectCallback(TimerHandle_t /*timer*/) {
|
||||
}
|
||||
}
|
||||
|
||||
// Detects a Tab5 Keyboard add-on that was plugged in after boot (so it wasn't started by
|
||||
// Lvgl.cpp's attachDevices()). Once lateStart() succeeds, this stops polling for good — there's
|
||||
// no support for re-detecting after the indev is torn down again.
|
||||
static void keyboardDetectCallback(TimerHandle_t timer) {
|
||||
if (kb_late_started.load(std::memory_order_acquire)) {
|
||||
xTimerStop(timer, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
using namespace tt::hal;
|
||||
auto keyboard = findFirstDevice<keyboard::KeyboardDevice>(tt::hal::Device::Type::Keyboard);
|
||||
if (!keyboard) {
|
||||
return; // Not registered yet, will retry on next tick
|
||||
}
|
||||
|
||||
if (keyboard->getLvglIndev() != nullptr) {
|
||||
// Already started (boot-time attach) — nothing left to do.
|
||||
kb_late_started.store(true, std::memory_order_release);
|
||||
xTimerStop(timer, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!keyboard->isAttached()) {
|
||||
return; // Not plugged in yet, will retry on next tick
|
||||
}
|
||||
|
||||
auto tab5_keyboard = std::static_pointer_cast<Tab5Keyboard>(keyboard);
|
||||
if (tab5_keyboard->lateStart()) {
|
||||
LOG_I(TAG, "kb_detect: keyboard attached post-boot, LVGL input started");
|
||||
kb_late_started.store(true, std::memory_order_release);
|
||||
xTimerStop(timer, 0);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
static error_t start() {
|
||||
@@ -91,23 +132,48 @@ static error_t start() {
|
||||
hp_detect_timer = nullptr;
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
kb_late_started = false;
|
||||
|
||||
kb_detect_timer = xTimerCreate("kb_detect", pdMS_TO_TICKS(KB_DETECT_POLL_MS), pdTRUE, nullptr, keyboardDetectCallback);
|
||||
if (!kb_detect_timer) {
|
||||
LOG_E(TAG, "Failed to create kb_detect timer");
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
if (xTimerStart(kb_detect_timer, pdMS_TO_TICKS(100)) != pdPASS) {
|
||||
LOG_E(TAG, "Failed to start kb_detect timer");
|
||||
xTimerDelete(kb_detect_timer, pdMS_TO_TICKS(100));
|
||||
kb_detect_timer = nullptr;
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static error_t stop() {
|
||||
if (hp_detect_timer == nullptr) {
|
||||
return ERROR_NONE;
|
||||
if (hp_detect_timer != nullptr) {
|
||||
if (xTimerStop(hp_detect_timer, pdMS_TO_TICKS(100)) != pdPASS) {
|
||||
LOG_W(TAG, "Failed to stop hp_detect timer");
|
||||
}
|
||||
if (xTimerDelete(hp_detect_timer, pdMS_TO_TICKS(100)) != pdPASS) {
|
||||
LOG_E(TAG, "Failed to delete hp_detect timer");
|
||||
}
|
||||
// Always clear the handle — stale non-null handle is worse than a resource leak,
|
||||
// as it would cause start() to silently skip re-creating the timer.
|
||||
hp_detect_timer = nullptr;
|
||||
io_expander0_cached.store(nullptr, std::memory_order_release);
|
||||
}
|
||||
if (xTimerStop(hp_detect_timer, pdMS_TO_TICKS(100)) != pdPASS) {
|
||||
LOG_W(TAG, "Failed to stop hp_detect timer");
|
||||
|
||||
if (kb_detect_timer != nullptr) {
|
||||
if (xTimerStop(kb_detect_timer, pdMS_TO_TICKS(100)) != pdPASS) {
|
||||
LOG_W(TAG, "Failed to stop kb_detect timer");
|
||||
}
|
||||
if (xTimerDelete(kb_detect_timer, pdMS_TO_TICKS(100)) != pdPASS) {
|
||||
LOG_E(TAG, "Failed to delete kb_detect timer");
|
||||
}
|
||||
kb_detect_timer = nullptr;
|
||||
}
|
||||
if (xTimerDelete(hp_detect_timer, pdMS_TO_TICKS(100)) != pdPASS) {
|
||||
LOG_E(TAG, "Failed to delete hp_detect timer");
|
||||
}
|
||||
// Always clear the handle — stale non-null handle is worse than a resource leak,
|
||||
// as it would cause start() to silently skip re-creating the timer.
|
||||
hp_detect_timer = nullptr;
|
||||
io_expander0_cached.store(nullptr, std::memory_order_release);
|
||||
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
#include <tactility/bindings/root.h>
|
||||
#include <tactility/bindings/esp32_ble.h>
|
||||
#include <tactility/bindings/esp32_gpio.h>
|
||||
#include <tactility/bindings/esp32_i2c.h>
|
||||
#include <tactility/bindings/esp32_grove.h>
|
||||
#include <tactility/bindings/esp32_i2c_master.h>
|
||||
#include <tactility/bindings/esp32_i2s.h>
|
||||
#include <tactility/bindings/esp32_spi.h>
|
||||
#include <tactility/bindings/esp32_sdmmc.h>
|
||||
#include <tactility/bindings/esp32_uart.h>
|
||||
#include <tactility/bindings/esp32_usbhost.h>
|
||||
#include <bindings/bmi270.h>
|
||||
@@ -28,11 +28,11 @@
|
||||
};
|
||||
|
||||
i2c_internal: i2c0 {
|
||||
compatible = "espressif,esp32-i2c";
|
||||
compatible = "espressif,esp32-i2c-master";
|
||||
port = <I2C_NUM_0>;
|
||||
clock-frequency = <100000>;
|
||||
pin-sda = <&gpio0 31 GPIO_FLAG_NONE>;
|
||||
pin-scl = <&gpio0 32 GPIO_FLAG_NONE>;
|
||||
pin-sda = <&gpio0 31 GPIO_FLAG_PULL_UP>;
|
||||
pin-scl = <&gpio0 32 GPIO_FLAG_PULL_UP>;
|
||||
|
||||
io_expander0 {
|
||||
compatible = "diodes,pi4ioe5v6408";
|
||||
@@ -61,12 +61,14 @@
|
||||
};
|
||||
};
|
||||
|
||||
i2c_port_a: i2c1 {
|
||||
compatible = "espressif,esp32-i2c";
|
||||
port = <I2C_NUM_1>;
|
||||
clock-frequency = <100000>;
|
||||
pin-sda = <&gpio0 53 GPIO_FLAG_NONE>;
|
||||
pin-scl = <&gpio0 54 GPIO_FLAG_NONE>;
|
||||
port_a: grove0 {
|
||||
compatible = "espressif,esp32-grove";
|
||||
defaultMode = <GROVE_MODE_I2C>;
|
||||
pinSdaTx = <&gpio0 53 GPIO_FLAG_NONE>;
|
||||
pinSclRx = <&gpio0 54 GPIO_FLAG_NONE>;
|
||||
uartPort = <UART_NUM_1>;
|
||||
i2cPort = <I2C_NUM_1>;
|
||||
i2cClockFrequency = <100000>;
|
||||
};
|
||||
|
||||
i2c_keyboard: i2c2 {
|
||||
@@ -78,12 +80,18 @@
|
||||
pin-scl = <&gpio0 1 GPIO_FLAG_PULL_UP>;
|
||||
};
|
||||
|
||||
sdcard_spi: spi0 {
|
||||
compatible = "espressif,esp32-spi";
|
||||
host = <SPI2_HOST>;
|
||||
pin-mosi = <&gpio0 44 GPIO_FLAG_NONE>;
|
||||
pin-miso = <&gpio0 39 GPIO_FLAG_NONE>;
|
||||
pin-sclk = <&gpio0 43 GPIO_FLAG_NONE>;
|
||||
sdmmc0 {
|
||||
compatible = "espressif,esp32-sdmmc";
|
||||
pin-clk = <&gpio0 43 GPIO_FLAG_NONE>;
|
||||
pin-cmd = <&gpio0 44 GPIO_FLAG_NONE>;
|
||||
pin-d0 = <&gpio0 39 GPIO_FLAG_NONE>;
|
||||
pin-d1 = <&gpio0 40 GPIO_FLAG_NONE>;
|
||||
pin-d2 = <&gpio0 41 GPIO_FLAG_NONE>;
|
||||
pin-d3 = <&gpio0 42 GPIO_FLAG_NONE>;
|
||||
bus-width = <4>;
|
||||
slot = <SDMMC_HOST_SLOT_0>;
|
||||
max-freq-khz = <SDMMC_FREQ_HIGHSPEED>;
|
||||
on-chip-ldo-chan = <4>;
|
||||
};
|
||||
|
||||
// ES8388 and ES7210
|
||||
@@ -97,14 +105,6 @@
|
||||
pin-mclk = <&gpio0 30 GPIO_FLAG_NONE>;
|
||||
};
|
||||
|
||||
uart_port_a: uart1 {
|
||||
compatible = "espressif,esp32-uart";
|
||||
status = "disabled";
|
||||
port = <UART_NUM_1>;
|
||||
pin-tx = <&gpio0 53 GPIO_FLAG_NONE>;
|
||||
pin-rx = <&gpio0 54 GPIO_FLAG_NONE>;
|
||||
};
|
||||
|
||||
usbhost0 {
|
||||
compatible = "espressif,esp32-usbhost";
|
||||
peripheral-map = <0>;
|
||||
|
||||
Reference in New Issue
Block a user