The T-Display P4 keyboard expansion (TCA8418 + its own XL9555, ported from camillia-mt) now works. It is looked for every 1.5 s, so it can be clipped on or off while running, and is routed like the CardKB and Bluetooth keyboards: it types into the focused field and keeps the on-screen keyboard down. The external-keyboard routing was gated on Bluetooth, which the P4 lacks, so it now follows a new CAP_EXT_KEYBOARD; the Bluetooth-only parts stay on CAP_BLE_KEYBOARD. - Keys: Shift/Sym/Alt one-shot, Caps Lock lights the expansion LEDs, arrows and Backspace repeat, Alt+Backspace backs out, F1-F5 open the tabs left to right, and F11 steps the keyboard light through off/low/medium/high (saved across reboots). - Wire1 is shared with the ES8311 codec on other pins; P4I2c1 switches it under a lock, and the codec's lazy bring-up now goes through it. - With the keyboard attached the tab bar labels F1-F5 beside each icon, and Enter on a chat bubble opens its action menu. Also, on any keyboard build, Enter runs the command in the terminal and the repeater admin console (the admin console's Enter hook sat on its Send button, where nothing raised it). And on the P4: the status-bar name shrinks to fit the bar, the Discover buttons are spaced out, contact names clear their icon in landscape, and leaving an app returns the focus to its tile in the app drawer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
60 lines
2.1 KiB
C++
60 lines
2.1 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
#if defined(HAS_TDISPLAY_P4)
|
|
#include "P4I2c1.h"
|
|
#include <Arduino.h>
|
|
#include <Wire.h>
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/semphr.h"
|
|
|
|
// LilyGo t_display_p4_config.h: keyboard expansion on 46/45, ES8311 on 20/21.
|
|
#define P4_I2C1_KB_SDA 46
|
|
#define P4_I2C1_KB_SCL 45
|
|
#define P4_I2C1_KB_HZ 100000UL
|
|
#define P4_I2C1_AUDIO_SDA 20
|
|
#define P4_I2C1_AUDIO_SCL 21
|
|
#define P4_I2C1_AUDIO_HZ 400000UL
|
|
|
|
static SemaphoreHandle_t s_mtx = nullptr;
|
|
static portMUX_TYPE s_create_mux = portMUX_INITIALIZER_UNLOCKED;
|
|
static int s_route = -1; // none: nothing has begun Wire1 yet
|
|
static bool s_fail_logged[2] = {};
|
|
|
|
TwoWire* p4I2c1Acquire(P4I2c1Route route, uint32_t wait_ms) {
|
|
if (!s_mtx) {
|
|
SemaphoreHandle_t m = xSemaphoreCreateMutex();
|
|
portENTER_CRITICAL(&s_create_mux);
|
|
if (!s_mtx) { s_mtx = m; m = nullptr; }
|
|
portEXIT_CRITICAL(&s_create_mux);
|
|
if (m) vSemaphoreDelete(m);
|
|
if (!s_mtx) return nullptr;
|
|
}
|
|
const TickType_t ticks = wait_ms == UINT32_MAX ? portMAX_DELAY : pdMS_TO_TICKS(wait_ms);
|
|
if (xSemaphoreTake(s_mtx, ticks) != pdTRUE) return nullptr;
|
|
|
|
// Re-begun when it is on the other route, and also when it is not running at
|
|
// all: a bus that is only remembered as begun fails every transaction after.
|
|
if (s_route != (int)route || !i2cIsInit(1)) {
|
|
if (i2cIsInit(1)) Wire1.end();
|
|
const bool audio = (route == P4_I2C1_AUDIO);
|
|
const bool ok = audio ? Wire1.begin(P4_I2C1_AUDIO_SDA, P4_I2C1_AUDIO_SCL, P4_I2C1_AUDIO_HZ)
|
|
: Wire1.begin(P4_I2C1_KB_SDA, P4_I2C1_KB_SCL, P4_I2C1_KB_HZ);
|
|
// Not recorded as current when it failed, so the next call tries again.
|
|
s_route = ok ? (int)route : -1;
|
|
if (!ok) {
|
|
if (!s_fail_logged[audio ? 1 : 0]) {
|
|
s_fail_logged[audio ? 1 : 0] = true;
|
|
printf("[P4I2C1] Wire1.begin for the %s route failed\n", audio ? "audio" : "keyboard");
|
|
}
|
|
xSemaphoreGive(s_mtx);
|
|
return nullptr;
|
|
}
|
|
}
|
|
return &Wire1;
|
|
}
|
|
|
|
void p4I2c1Release() {
|
|
if (s_mtx) xSemaphoreGive(s_mtx);
|
|
}
|
|
|
|
#endif
|