mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-08-29 08:58:18 +00:00
thinknode m9 stuff + temp remove from build.sh
This commit is contained in:
@@ -45,7 +45,6 @@ ESP32_boards=(
|
||||
heltec_wifi_lora32_v43/esp32s3/procpu
|
||||
heltec_wireless_tracker/esp32s3/procpu
|
||||
heltec_wireless_tracker_v2/esp32s3/procpu
|
||||
thinknode_m9/esp32s3/procpu
|
||||
ttgo_tbeam/esp32/procpu
|
||||
)
|
||||
|
||||
|
||||
@@ -163,7 +163,10 @@ BOARDS = [
|
||||
dict(stem="heltec_wifi_lora32_v4-esp32s3-procpu", kind="esp32", device="Heltec v4"),
|
||||
dict(stem="heltec_wireless_tracker-esp32s3-procpu", kind="esp32", device="Heltec Wireless Tracker"),
|
||||
dict(stem="heltec_wireless_tracker_v2-esp32s3-procpu", kind="esp32", device="Heltec Wireless Tracker v2"),
|
||||
dict(stem="thinknode_m9-esp32s3-procpu", kind="esp32", device="Elecrow ThinkNode M9"),
|
||||
# ThinkNode M9 pulled from the build.sh release matrix 2026-07-22 (bring-up
|
||||
# still in progress — keypad/GPS/battery unverified on hardware). Restore
|
||||
# this line together with the build.sh entry once it's release-ready.
|
||||
# dict(stem="thinknode_m9-esp32s3-procpu", kind="esp32", device="Elecrow ThinkNode M9"),
|
||||
# Classic ESP32 T-Beam: ships -merged.bin (full-flash, 0x0) too.
|
||||
dict(stem="ttgo_tbeam-esp32-procpu", kind="esp32", device="LilyGo T-Beam (SX1262)"),
|
||||
|
||||
|
||||
@@ -240,7 +240,10 @@
|
||||
/* LittleFS auto-mount — standard /lfs mount point */
|
||||
#include "../../common/filesystem.dtsi"
|
||||
|
||||
/* USB OTG companion transport. thinknode_m9 consoles over USB Serial/JTAG,
|
||||
* which shares D+/D- with USB OTG, so the console is rerouted to uart0. */
|
||||
/* Console + shell on uart0 — the USB-C port is a CH9102-class UART bridge
|
||||
* into UART0 (GPIO43/44), verified on hardware. No USB OTG transport on this
|
||||
* board: the S3's native D+/D- pads (GPIO19/20) are not bonded to the
|
||||
* connector, and GPIO20/21 carry the STC8H keypad I2C bus — enabling the OTG
|
||||
* or USB Serial/JTAG PHY would claim the keypad's SDA pin. BLE is the
|
||||
* companion transport. */
|
||||
#include "../../common/esp32s3_console_uart0.dtsi"
|
||||
#include "../../common/esp32s3_usb_otg.dtsi"
|
||||
|
||||
@@ -30,10 +30,15 @@
|
||||
model = "Elecrow ThinkNode M9";
|
||||
compatible = "elecrow,thinknode-m9";
|
||||
|
||||
/* No zephyr,console here: the M9's USB-C data lines ride a CH9102-class
|
||||
* USB-UART bridge into UART0 (verified on hardware — the native USB
|
||||
* Serial/JTAG console produced no output). The S3's native USB pads
|
||||
* (GPIO19/20) are not wired to the connector at all; GPIO20/21 carry the
|
||||
* STC8H keypad I2C bus instead. Every build of this board (app overlay
|
||||
* and lr1110_updater overlay alike) routes console + shell to uart0 via
|
||||
* boards/common/esp32s3_console_uart0.dtsi. */
|
||||
chosen {
|
||||
zephyr,sram = &sram1;
|
||||
zephyr,console = &usb_serial;
|
||||
zephyr,shell-uart = &usb_serial;
|
||||
zephyr,flash = &flash0;
|
||||
zephyr,code-partition = &slot0_partition;
|
||||
zephyr,bt-hci = &esp32_bt_hci;
|
||||
@@ -69,9 +74,11 @@
|
||||
clock-frequency = <DT_FREQ_M(240)>;
|
||||
};
|
||||
|
||||
&usb_serial {
|
||||
status = "okay";
|
||||
};
|
||||
/* USB Serial/JTAG stays disabled (SoC default): its PHY pads are GPIO19/20,
|
||||
* and on this board GPIO20 is the keypad's I2C1 SDA. The USJ driver's init
|
||||
* re-attaches the USB pad (usb_serial_jtag_ll_phy_enable_pad) and enables the
|
||||
* D+ pull-up, fighting the keypad bus. Native USB is unbonded from the
|
||||
* connector anyway — see the chosen-node comment. */
|
||||
|
||||
&i2c0 {
|
||||
status = "okay";
|
||||
|
||||
@@ -8,13 +8,15 @@ description: |
|
||||
over I2C. Used by the Elecrow ThinkNode M9, whose keypad is its only input
|
||||
device — there is no user button on that board.
|
||||
|
||||
Protocol:
|
||||
reg 0x01..0x04 battery millivolts, little-endian
|
||||
reg 0x05 code of the key currently pressed (0 = none)
|
||||
Protocol (as recovered from the stock keypad firmware — note the key
|
||||
register is 0x01, not the 0x05 "matrix key" register that the reference
|
||||
header documents; that define is dead code and 0x05 reads as 0xFF):
|
||||
reg 0x01 code of the last key pressed (latched; 0x88 = invalid)
|
||||
reg 0x01..0x04 battery millivolts, little-endian (same base register)
|
||||
reg 0x06 state; writing 0x01 requests sleep
|
||||
|
||||
The MCU raises its interrupt line (idle-low, rising edge) when a key is
|
||||
pressed. The driver reads register 0x05 on that edge and reports the result
|
||||
pressed. The driver reads register 0x01 on that edge and reports the result
|
||||
through Zephyr's input subsystem. If no interrupt GPIO is supplied the
|
||||
driver falls back to polling.
|
||||
|
||||
|
||||
@@ -22,13 +22,12 @@
|
||||
* helpers/ui-joystick/joystick_defs.h). Everything else is a named key and is
|
||||
* translated here.
|
||||
*
|
||||
* The named codes below are the ones confirmed from two independent firmwares
|
||||
* for this keypad. Arrow keys are NOT among them, and no reachable reference
|
||||
* documents them — so any code this driver does not recognise is reported at
|
||||
* INFO level with its hex value. Press the arrow keys once on real hardware
|
||||
* and the log names them; add them to key_map[] and navigation is complete.
|
||||
* That is deliberate: guessing arrow codes would produce a driver that looks
|
||||
* finished and silently does the wrong thing.
|
||||
* The named codes below (arrows included) come from the stock keypad
|
||||
* firmware's protocol, as recovered from a linked reference-firmware ELF by a
|
||||
* second independent firmware — the register bodies there are the shipped
|
||||
* hardware truth, not header guesses. Any code this driver still does not
|
||||
* recognise is reported at INFO level with its hex value so it can be added
|
||||
* to key_map[].
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zephcore_stc8h_keypad
|
||||
@@ -45,13 +44,23 @@
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(stc8h_keypad, CONFIG_ZEPHCORE_BOARD_LOG_LEVEL);
|
||||
|
||||
/* Register map — see dts/bindings/input/zephcore,stc8h-keypad.yaml */
|
||||
/* Register map — see dts/bindings/input/zephcore,stc8h-keypad.yaml.
|
||||
*
|
||||
* The key register is 0x01, NOT the 0x05 "matrix key" register that the
|
||||
* reference header documents: the recovered implementation bodies read the
|
||||
* key from 0x01, and the 0x05 define is dead code nothing consumes. Reading
|
||||
* 0x05 returns constant 0xFF — that was this driver's original bug (keys
|
||||
* fully dead, phantom 0xFF at init). The overlap with the battery registers
|
||||
* (0x01..0x04 little-endian) is odd but is what the shipped firmware does;
|
||||
* it is also why key reads are IRQ-gated below. */
|
||||
#define STC8H_REG_BATTERY 0x01 /* 0x01..0x04, little-endian millivolts */
|
||||
#define STC8H_REG_KEY 0x05
|
||||
#define STC8H_REG_KEY 0x01
|
||||
#define STC8H_REG_STATE 0x06
|
||||
#define STC8H_STATE_SLEEP 0x01
|
||||
|
||||
#define STC8H_KEY_NONE 0x00
|
||||
#define STC8H_KEY_NONE 0x00
|
||||
#define STC8H_KEY_INVALID 0x88 /* helper MCU's "invalid key" marker */
|
||||
#define STC8H_KEY_ALL_ONES 0xFF /* error sentinel / floating-bus read */
|
||||
|
||||
struct stc8h_config {
|
||||
struct i2c_dt_spec i2c;
|
||||
@@ -63,22 +72,27 @@ struct stc8h_data {
|
||||
const struct device *dev;
|
||||
struct gpio_callback irq_cb;
|
||||
struct k_work_delayable work;
|
||||
atomic_t irq_pending;
|
||||
uint8_t last_key;
|
||||
};
|
||||
|
||||
/*
|
||||
* Named keys, as reported by the helper MCU.
|
||||
*
|
||||
* Only codes corroborated by two independent firmwares for this keypad are
|
||||
* listed. The two function keys are mapped to page navigation because on a
|
||||
* board with no other input they are the only way to move between screens;
|
||||
* the UI treats them as prev/next.
|
||||
* Named keys, as reported by the helper MCU. Codes from the recovered stock
|
||||
* keypad protocol (see file header). The two function keys are mapped to page
|
||||
* navigation because on a board with no other input they are the only way to
|
||||
* move between screens; the UI treats them as prev/next.
|
||||
*/
|
||||
static const struct {
|
||||
uint8_t raw;
|
||||
uint16_t code;
|
||||
} key_map[] = {
|
||||
{ 0x0D, INPUT_KEY_ENTER }, /* enter / confirm */
|
||||
{ 0xB5, INPUT_KEY_UP }, /* arrow up */
|
||||
{ 0xB6, INPUT_KEY_DOWN }, /* arrow down */
|
||||
{ 0xB4, INPUT_KEY_LEFT }, /* arrow left */
|
||||
{ 0xB7, INPUT_KEY_RIGHT }, /* arrow right */
|
||||
{ 0x08, INPUT_KEY_BACK }, /* del -> back / cancel */
|
||||
{ 0x89, INPUT_KEY_BACK }, /* del long-press -> back / cancel */
|
||||
{ 0x86, INPUT_KEY_ESC }, /* back */
|
||||
{ 0x82, INPUT_KEY_HOME }, /* home */
|
||||
{ 0x83, INPUT_KEY_MENU }, /* context menu */
|
||||
@@ -148,19 +162,44 @@ static void stc8h_report(const struct device *dev, uint8_t raw)
|
||||
LOG_INF("unmapped keypad code 0x%02X — add it to key_map[] to bind it", raw);
|
||||
}
|
||||
|
||||
static bool stc8h_key_valid(uint8_t raw)
|
||||
{
|
||||
return raw != STC8H_KEY_NONE && raw != STC8H_KEY_INVALID &&
|
||||
raw != STC8H_KEY_ALL_ONES;
|
||||
}
|
||||
|
||||
static void stc8h_work_handler(struct k_work *work)
|
||||
{
|
||||
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
|
||||
struct stc8h_data *data = CONTAINER_OF(dwork, struct stc8h_data, work);
|
||||
const struct device *dev = data->dev;
|
||||
const struct stc8h_config *cfg = dev->config;
|
||||
bool irq_event = atomic_cas(&data->irq_pending, 1, 0);
|
||||
uint8_t key = STC8H_KEY_NONE;
|
||||
|
||||
if (stc8h_read_reg(cfg, STC8H_REG_KEY, &key) == 0) {
|
||||
/* Report on the transition only — the register reads the key
|
||||
* that is *currently down*, so a held key would otherwise
|
||||
* repeat at the poll rate. */
|
||||
if (key != STC8H_KEY_NONE && key != data->last_key) {
|
||||
if (irq_event) {
|
||||
/* KB_INT edge latched: this read is a fresh keypress —
|
||||
* report unconditionally. The register latches the
|
||||
* *last* key (it does not return to 0 on release), so
|
||||
* comparing against last_key here would swallow every
|
||||
* repeated press of the same key. This mirrors the
|
||||
* reference driver, which only ever reads the key
|
||||
* register in response to the interrupt. */
|
||||
if (stc8h_key_valid(key)) {
|
||||
stc8h_report(dev, key);
|
||||
}
|
||||
} else if (cfg->irq.port == NULL) {
|
||||
/* No IRQ line: transition polling is all we have. */
|
||||
if (stc8h_key_valid(key) && key != data->last_key) {
|
||||
stc8h_report(dev, key);
|
||||
}
|
||||
} else if (stc8h_key_valid(key) && key != data->last_key &&
|
||||
gpio_pin_get_dt(&cfg->irq) > 0) {
|
||||
/* Poll with an IRQ line = missed-edge safety net.
|
||||
* Only report while the key is still held (INT level
|
||||
* active) — the latched register would otherwise
|
||||
* re-report the stale last key forever. */
|
||||
stc8h_report(dev, key);
|
||||
}
|
||||
data->last_key = key;
|
||||
@@ -177,7 +216,10 @@ static void stc8h_irq_handler(const struct device *port, struct gpio_callback *c
|
||||
ARG_UNUSED(port);
|
||||
ARG_UNUSED(pins);
|
||||
|
||||
/* I2C cannot be touched from an ISR — hand off to the work queue. */
|
||||
/* I2C cannot be touched from an ISR — hand off to the work queue.
|
||||
* The flag tells the handler this run is a real key edge, so it must
|
||||
* report even when the latched register still holds the same code. */
|
||||
atomic_set(&data->irq_pending, 1);
|
||||
k_work_reschedule(&data->work, K_NO_WAIT);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
# ThinkNode M9 (ESP32-S3) LR1110 updater — overrides for the nRF-flavored prj.conf
|
||||
#
|
||||
# Console is the native USB Serial/JTAG (base DTS chosen) — no USB device
|
||||
# stack, no CDC ACM, no UF2, no DT code partition (ESP Simple Boot links the
|
||||
# image itself and west flash writes it from 0x0).
|
||||
# Console rides uart0 (CH9102-class USB-C bridge; see board.overlay — native
|
||||
# USB Serial/JTAG is unbonded and its pads carry the keypad I2C bus) — no USB
|
||||
# device stack, no CDC ACM, no UF2, no DT code partition (ESP Simple Boot
|
||||
# links the image itself and west flash writes it from 0x0).
|
||||
|
||||
CONFIG_BUILD_OUTPUT_UF2=n
|
||||
CONFIG_USE_DT_CODE_PARTITION=n
|
||||
|
||||
Reference in New Issue
Block a user