mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-09-01 19:38:20 +00:00
M9 keys P1
This commit is contained in:
@@ -115,7 +115,14 @@ It is ZephCore's first ESP32-S3 + LR1110 board, so a fair amount of it is new gr
|
||||
an existing port. Currently unverified: display init parameters (inherited from another ST7789V board — if
|
||||
the screen is mirrored or upside down, that's a known one-line fix), GPS output from the CC1167Q module,
|
||||
battery calibration, and whether the USB-C port reaches the native USB pads at all (BLE is the companion
|
||||
path meanwhile). Not ported: the I2C keyboard, which is driven by a second MCU.
|
||||
path meanwhile).
|
||||
|
||||
The M9 has no user button in hardware — its keypad is the only input — so it now runs the full companion UI
|
||||
driven by a new driver for the STC8H keypad MCU on I2C. Enter, back, home, menu and page prev/next are wired
|
||||
up, and typed characters pass straight through. **The arrow keys are not mapped yet**: no reference we could
|
||||
find documents their codes, so rather than guess, the driver logs any key it doesn't recognise along with its
|
||||
hex value. Press the arrows once with a serial console attached and they can be bound in a one-line change.
|
||||
Until then you can move between pages but not up and down within a list.
|
||||
|
||||
Also worth knowing on this board specifically: the **SD slot shares the radio's SPI bus** with the LR1110
|
||||
and the display. An inserted card was confirmed to break LR1110 radio-firmware flashing on this board — the
|
||||
|
||||
@@ -530,6 +530,12 @@ else()
|
||||
message(STATUS "ZephCore Battery: generic LiPo curve")
|
||||
endif()
|
||||
|
||||
# ========== I2C keypad (boards whose only input is a keyboard) ==========
|
||||
if(CONFIG_INPUT)
|
||||
target_sources_ifdef(CONFIG_ZEPHCORE_UI_DISPLAY app PRIVATE
|
||||
helpers/input/stc8h_keypad.c)
|
||||
endif()
|
||||
|
||||
# ========== Board-specific startup quirks ==========
|
||||
# Optional per-board sd_quiesce.c — boards whose SD slot shares the radio's SPI
|
||||
# bus put any inserted card into SPI idle before the radio driver initialises,
|
||||
|
||||
+3
-2
@@ -859,8 +859,9 @@ config ZEPHCORE_UI_KEYBOARD
|
||||
separate one. Selecting this makes ZEPHCORE_UI_DESIGN_JOYSTICK
|
||||
available on a board that has no 5-way stick.
|
||||
|
||||
No board selects this yet: the STC8H driver and the keyboard text
|
||||
entry screen are not written, and the M9 is unverified on hardware.
|
||||
Selected by ThinkNode M9. The STC8H driver is helpers/input/
|
||||
stc8h_keypad.c; the keypad's arrow-key codes are still unknown, so
|
||||
the driver logs any code it does not recognise (see its header).
|
||||
The key-space reservation in helpers/ui-joystick/joystick_defs.h is
|
||||
the groundwork that makes them droppable in later.
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
config BOARD_THINKNODE_M9
|
||||
select SOC_ESP32S3_WROOM_N16R8
|
||||
select SOC_ESP32S3_PROCPU if BOARD_THINKNODE_M9_ESP32S3_PROCPU
|
||||
# STC8H keypad on I2C1 @0x6C is this board's ONLY input — there is no
|
||||
# user button in hardware. Selects the full companion UI, whose key
|
||||
# space is built for a keyboard.
|
||||
select ZEPHCORE_UI_KEYBOARD
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Elecrow ThinkNode M9
|
||||
|
||||
ESP32-S3 handheld with LR1110 radio, ST7789 320x240 TFT, CC1167Q GPS, PCF8563
|
||||
RTC, buzzer, STC8H I2C keyboard, QMI8658 IMU + QMC6309 magnetometer, and an SD
|
||||
slot (keyboard/IMU/SD not ported — see below).
|
||||
RTC, buzzer, STC8H I2C keypad, QMI8658 IMU + QMC6309 magnetometer, and an SD
|
||||
slot (IMU/SD not ported — see below).
|
||||
|
||||
Ported 2026-07-18 from Arduino MeshCore `variants/thinknode_m9` (which itself
|
||||
landed upstream only days earlier), cross-checked against a second,
|
||||
@@ -90,10 +90,18 @@ in high flash are untouched by the updater flash.
|
||||
LR1110 updater).
|
||||
- **Charge/power sense** — GPIO1 = external-power detect, **active LOW**;
|
||||
GPIO8 = charger DONE. Not consumed by ZephCore yet.
|
||||
- **Keyboard identified** — STC8H helper MCU at **I2C 0x6C** on the second
|
||||
bus (SDA=20 SCL=21), `KB_INT` GPIO12 idle-low/rising-edge, backlight
|
||||
GPIO46. Registers: 0x01 battery, 0x03 long-press ms (16-bit write), 0x05
|
||||
matrix key, 0x06 state.
|
||||
- **Keypad PORTED** — STC8H helper MCU at **I2C 0x6C** on i2c1 (SDA=20
|
||||
SCL=21), `KB_INT` GPIO12 idle-low/rising-edge, backlight GPIO46.
|
||||
Registers: 0x01-0x04 battery mV little-endian, 0x05 pressed key, 0x06
|
||||
state (write 0x01 = sleep). Driver: `helpers/input/stc8h_keypad.c`,
|
||||
binding `zephcore,stc8h-keypad`. This is the board's ONLY input, so
|
||||
`Kconfig.thinknode_m9` selects `ZEPHCORE_UI_KEYBOARD` -> the full
|
||||
companion UI. **Arrow-key codes are still unknown** — no reference
|
||||
documents them; the driver logs unrecognised codes at INFO so they can be
|
||||
identified on hardware and added to `key_map[]`. Mapped so far: 0x0D
|
||||
enter, 0x86 back, 0x82 home, 0x83 menu, 0x81/0x85 function keys ->
|
||||
page prev/next, 0x87 and 0xA3 long-press variants. Printable ASCII
|
||||
(0x20-0x7E) passes through untranslated.
|
||||
- **Also on the sensor bus** — QMI8658 IMU + QMC6309 magnetometer.
|
||||
Unported; no address collision with the PCF8563 @0x51.
|
||||
|
||||
@@ -142,21 +150,26 @@ firmware-sourced (both firmwares agree on all of them).
|
||||
|
||||
Still unverified on hardware, in rough priority order:
|
||||
|
||||
1. **Display init params** — gamma/porch/vcom inherited from Heltec T114's
|
||||
1. **Keypad arrow codes** — the one blocker for a usable UI. Attach a serial
|
||||
console, press each arrow key, and read the `unmapped keypad code 0x..`
|
||||
lines; add them to `key_map[]` in `helpers/input/stc8h_keypad.c` as
|
||||
`INPUT_KEY_UP/DOWN/LEFT/RIGHT`. Until then the UI changes pages but
|
||||
cannot move within a list. Confirm `keypad at 0x6C ready` appears at boot.
|
||||
2. **Display init params** — gamma/porch/vcom inherited from Heltec T114's
|
||||
ST7789V (no board-specific init values exist in any reference); check
|
||||
contrast/colors. If mirrored or upside down, swap `mdac` 0xA0 ↔ 0x60.
|
||||
2. **GPS identity/output** — confirm NMEA at 115200 and that the CC1167Q
|
||||
3. **GPS identity/output** — confirm NMEA at 115200 and that the CC1167Q
|
||||
talks standard sentences (it should; both upstreams parse plain NMEA).
|
||||
If a fix never arrives, probe `$PDTINFO` manually.
|
||||
3. **Battery calibration** — the 2:1 divider is what both upstreams assume;
|
||||
4. **Battery calibration** — the 2:1 divider is what both upstreams assume;
|
||||
sanity-check `get bat` against a multimeter once, and check whether the
|
||||
4.35 V charger variant actually charges above 4.20 V (OCV curve top).
|
||||
4. **USB-OTG companion transport** — see the schematic section above:
|
||||
5. **USB-OTG companion transport** — see the schematic section above:
|
||||
verify whether the native USB pads reach the USB-C connector at all.
|
||||
5. **rx-boosted** — deliberately ON (both upstreams leave boosted gain off /
|
||||
6. **rx-boosted** — deliberately ON (both upstreams leave boosted gain off /
|
||||
unset; every other ZephCore LR1110 board uses it). Verify RX sensitivity
|
||||
and TX power on air.
|
||||
6. **ADC2 vs WiFi** — battery reads ride ADC unit 2 (GPIO13), which WiFi also
|
||||
7. **ADC2 vs WiFi** — battery reads ride ADC unit 2 (GPIO13), which WiFi also
|
||||
uses; expect intermittent read failures in `wifi_ota.conf` builds.
|
||||
|
||||
## Not ported
|
||||
|
||||
@@ -20,6 +20,10 @@ CONFIG_ZEPHCORE_RADIO_LR1110=y
|
||||
# Flash size — 16MB (override Zephyr default)
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
|
||||
|
||||
# The full companion UI is selected via ZEPHCORE_UI_KEYBOARD in
|
||||
# Kconfig.thinknode_m9 (the STC8H keypad is this board's only input), so no
|
||||
# UI_DESIGN override belongs here.
|
||||
|
||||
# PSRAM mode — WROOM-1-N16R8 is OPI octal.
|
||||
# Enable is auto-detected from DTS by Kconfig.psram; only the mode is set here.
|
||||
CONFIG_SPIRAM_MODE_OCT=y
|
||||
|
||||
@@ -210,6 +210,20 @@
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* STC8H keypad helper MCU — this board's only input device (no user button
|
||||
* exists at hardware level). KB_INT on GPIO12 is idle-low and pulses high on
|
||||
* a keypress; the driver also polls as a safety net.
|
||||
*/
|
||||
&i2c1 {
|
||||
keypad: keypad@6c {
|
||||
compatible = "zephcore,stc8h-keypad";
|
||||
reg = <0x6c>;
|
||||
irq-gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>;
|
||||
poll-interval-ms = <40>;
|
||||
};
|
||||
};
|
||||
|
||||
&i2c0 {
|
||||
#include "../../common/sensors-i2c.dtsi"
|
||||
/* ThinkNode M9 has a battery-backed PCF8563 @ 0x51 — opt in to boot-time
|
||||
|
||||
@@ -39,6 +39,18 @@
|
||||
};
|
||||
};
|
||||
|
||||
/* I2C1 — STC8H keypad helper MCU @0x6C. Separate bus from the RTC and
|
||||
* sensors; the keypad is this board's only input device. */
|
||||
i2c1_default: i2c1_default {
|
||||
group1 {
|
||||
pinmux = <I2C1_SDA_GPIO20>,
|
||||
<I2C1_SCL_GPIO21>;
|
||||
bias-pull-up;
|
||||
drive-open-drain;
|
||||
output-high;
|
||||
};
|
||||
};
|
||||
|
||||
/* UART1 — L76K GPS (GPIO2 = GPS TX → CPU RX, GPIO3 = GPS RX → CPU TX) */
|
||||
uart1_default: uart1_default {
|
||||
group1 {
|
||||
|
||||
@@ -67,6 +67,20 @@
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
|
||||
/*
|
||||
* I2C1 — STC8H keypad helper MCU @0x6C (GPIO20 SDA / GPIO21 SCL).
|
||||
*
|
||||
* This board has NO user button at hardware level, so the keypad is its only
|
||||
* input device: without it the UI renders but cannot be operated. The keypad
|
||||
* node itself lives in board.overlay alongside the rest of the app hookup.
|
||||
*/
|
||||
&i2c1 {
|
||||
status = "okay";
|
||||
clock-frequency = <I2C_BITRATE_STANDARD>;
|
||||
pinctrl-0 = <&i2c1_default>;
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
|
||||
&spi2 {
|
||||
status = "okay";
|
||||
#address-cells = <1>;
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
# Copyright 2026 ZephCore
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: |
|
||||
STC8H companion-MCU keypad (I2C).
|
||||
|
||||
A small helper MCU scans the key matrix and presents one register interface
|
||||
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)
|
||||
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
|
||||
through Zephyr's input subsystem. If no interrupt GPIO is supplied the
|
||||
driver falls back to polling.
|
||||
|
||||
Example:
|
||||
&i2c1 {
|
||||
keypad: keypad@6c {
|
||||
compatible = "zephcore,stc8h-keypad";
|
||||
reg = <0x6c>;
|
||||
irq-gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>;
|
||||
poll-interval-ms = <40>;
|
||||
};
|
||||
};
|
||||
|
||||
compatible: "zephcore,stc8h-keypad"
|
||||
|
||||
include: [i2c-device.yaml]
|
||||
|
||||
properties:
|
||||
irq-gpios:
|
||||
type: phandle-array
|
||||
description: |
|
||||
Key-press interrupt from the helper MCU. Idle-low, rising edge on press.
|
||||
Optional — the driver polls when it is absent.
|
||||
|
||||
poll-interval-ms:
|
||||
type: int
|
||||
default: 40
|
||||
description: |
|
||||
Poll period. Used as the sole scan mechanism when irq-gpios is absent,
|
||||
and as a safety net for a missed edge when it is present.
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* ZephCore — STC8H companion-MCU keypad driver
|
||||
* Copyright (c) 2026 ZephCore
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Reports keypresses from an I2C helper MCU through Zephyr's input subsystem.
|
||||
* Written against the register interface documented in the binding; no vendor
|
||||
* driver code is reused.
|
||||
*
|
||||
* Board context (ThinkNode M9): the keypad is the ONLY input device — that
|
||||
* board has no user button at hardware level, confirmed against both upstream
|
||||
* firmwares and the V1.0 schematic. Without this driver the UI renders but
|
||||
* cannot be operated at all.
|
||||
*
|
||||
* Key codes emitted
|
||||
* -----------------
|
||||
* Printable ASCII (0x20-0x7E) is forwarded verbatim as INPUT_KEY_* would not
|
||||
* express it; the joystick UI reserves exactly that range for typed characters
|
||||
* (see helpers/ui-joystick/joystick_defs.h), so a character can travel to the
|
||||
* UI untranslated. 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.
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zephcore_stc8h_keypad
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/input/input.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(stc8h_keypad, CONFIG_ZEPHCORE_LOG_LEVEL);
|
||||
|
||||
/* Register map — see dts/bindings/input/zephcore,stc8h-keypad.yaml */
|
||||
#define STC8H_REG_BATTERY 0x01 /* 0x01..0x04, little-endian millivolts */
|
||||
#define STC8H_REG_KEY 0x05
|
||||
#define STC8H_REG_STATE 0x06
|
||||
#define STC8H_STATE_SLEEP 0x01
|
||||
|
||||
#define STC8H_KEY_NONE 0x00
|
||||
|
||||
struct stc8h_config {
|
||||
struct i2c_dt_spec i2c;
|
||||
struct gpio_dt_spec irq;
|
||||
uint32_t poll_interval_ms;
|
||||
};
|
||||
|
||||
struct stc8h_data {
|
||||
const struct device *dev;
|
||||
struct gpio_callback irq_cb;
|
||||
struct k_work_delayable work;
|
||||
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.
|
||||
*/
|
||||
static const struct {
|
||||
uint8_t raw;
|
||||
uint16_t code;
|
||||
} key_map[] = {
|
||||
{ 0x0D, INPUT_KEY_ENTER }, /* enter / confirm */
|
||||
{ 0x86, INPUT_KEY_ESC }, /* back */
|
||||
{ 0x82, INPUT_KEY_HOME }, /* home */
|
||||
{ 0x83, INPUT_KEY_MENU }, /* context menu */
|
||||
{ 0x81, INPUT_KEY_PAGEUP }, /* left function key -> previous page */
|
||||
{ 0x85, INPUT_KEY_PAGEDOWN },/* right function key -> next page */
|
||||
{ 0x87, INPUT_KEY_F1 }, /* long-press map pin */
|
||||
{ 0xA3, INPUT_KEY_F2 }, /* long-press enter */
|
||||
};
|
||||
|
||||
static int stc8h_read_reg(const struct stc8h_config *cfg, uint8_t reg, uint8_t *val)
|
||||
{
|
||||
return i2c_write_read_dt(&cfg->i2c, ®, 1, val, 1);
|
||||
}
|
||||
|
||||
/* Battery millivolts as measured by the helper MCU. Not consumed yet — the
|
||||
* board reads its own cell through the host ADC — but the registers exist and
|
||||
* a second opinion is cheap to expose. */
|
||||
int stc8h_keypad_battery_mv(const struct device *dev, uint16_t *mv)
|
||||
{
|
||||
const struct stc8h_config *cfg = dev->config;
|
||||
uint8_t reg = STC8H_REG_BATTERY;
|
||||
uint8_t buf[4];
|
||||
int ret;
|
||||
|
||||
if (!mv) {
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = i2c_write_read_dt(&cfg->i2c, ®, 1, buf, sizeof(buf));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
*mv = (uint16_t)(buf[0] | ((uint16_t)buf[1] << 8));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int stc8h_keypad_sleep(const struct device *dev)
|
||||
{
|
||||
const struct stc8h_config *cfg = dev->config;
|
||||
const uint8_t cmd[2] = { STC8H_REG_STATE, STC8H_STATE_SLEEP };
|
||||
|
||||
return i2c_write_dt(&cfg->i2c, cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static void stc8h_report(const struct device *dev, uint8_t raw)
|
||||
{
|
||||
/* Printable characters travel to the UI as themselves. */
|
||||
if (raw >= 0x20 && raw <= 0x7E) {
|
||||
input_report_key(dev, raw, 1, false, K_NO_WAIT);
|
||||
input_report_key(dev, raw, 0, true, K_NO_WAIT);
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(key_map); i++) {
|
||||
if (key_map[i].raw == raw) {
|
||||
input_report_key(dev, key_map[i].code, 1, false, K_NO_WAIT);
|
||||
input_report_key(dev, key_map[i].code, 0, true, K_NO_WAIT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Deliberately loud: this is how the missing arrow codes get identified.
|
||||
* See the file header. */
|
||||
LOG_INF("unmapped keypad code 0x%02X — add it to key_map[] to bind it", raw);
|
||||
}
|
||||
|
||||
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;
|
||||
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) {
|
||||
stc8h_report(dev, key);
|
||||
}
|
||||
data->last_key = key;
|
||||
}
|
||||
|
||||
k_work_reschedule(&data->work, K_MSEC(cfg->poll_interval_ms));
|
||||
}
|
||||
|
||||
static void stc8h_irq_handler(const struct device *port, struct gpio_callback *cb,
|
||||
uint32_t pins)
|
||||
{
|
||||
struct stc8h_data *data = CONTAINER_OF(cb, struct stc8h_data, irq_cb);
|
||||
|
||||
ARG_UNUSED(port);
|
||||
ARG_UNUSED(pins);
|
||||
|
||||
/* I2C cannot be touched from an ISR — hand off to the work queue. */
|
||||
k_work_reschedule(&data->work, K_NO_WAIT);
|
||||
}
|
||||
|
||||
static int stc8h_init(const struct device *dev)
|
||||
{
|
||||
const struct stc8h_config *cfg = dev->config;
|
||||
struct stc8h_data *data = dev->data;
|
||||
uint8_t probe;
|
||||
|
||||
data->dev = dev;
|
||||
data->last_key = STC8H_KEY_NONE;
|
||||
|
||||
if (!i2c_is_ready_dt(&cfg->i2c)) {
|
||||
LOG_ERR("I2C bus not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* A keypad that does not answer is not fatal — the node still boots,
|
||||
* it just has no input. Say so plainly rather than failing init, which
|
||||
* on this board would look like a dead device for a missing keypad. */
|
||||
if (stc8h_read_reg(cfg, STC8H_REG_KEY, &probe) < 0) {
|
||||
LOG_WRN("keypad not responding at 0x%02X — no input available",
|
||||
cfg->i2c.addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
k_work_init_delayable(&data->work, stc8h_work_handler);
|
||||
|
||||
if (cfg->irq.port != NULL) {
|
||||
if (!gpio_is_ready_dt(&cfg->irq)) {
|
||||
LOG_ERR("IRQ GPIO not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
gpio_pin_configure_dt(&cfg->irq, GPIO_INPUT);
|
||||
gpio_init_callback(&data->irq_cb, stc8h_irq_handler,
|
||||
BIT(cfg->irq.pin));
|
||||
gpio_add_callback(cfg->irq.port, &data->irq_cb);
|
||||
gpio_pin_interrupt_configure_dt(&cfg->irq, GPIO_INT_EDGE_TO_ACTIVE);
|
||||
}
|
||||
|
||||
/* Poll regardless: it is the only mechanism without an IRQ line, and
|
||||
* the safety net for a missed edge with one. */
|
||||
k_work_reschedule(&data->work, K_MSEC(cfg->poll_interval_ms));
|
||||
|
||||
LOG_INF("keypad at 0x%02X ready (%s)", cfg->i2c.addr,
|
||||
cfg->irq.port ? "interrupt + poll" : "poll only");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define STC8H_INIT(n) \
|
||||
static const struct stc8h_config stc8h_cfg_##n = { \
|
||||
.i2c = I2C_DT_SPEC_INST_GET(n), \
|
||||
.irq = GPIO_DT_SPEC_INST_GET_OR(n, irq_gpios, { 0 }), \
|
||||
.poll_interval_ms = DT_INST_PROP(n, poll_interval_ms), \
|
||||
}; \
|
||||
static struct stc8h_data stc8h_data_##n; \
|
||||
DEVICE_DT_INST_DEFINE(n, stc8h_init, NULL, &stc8h_data_##n, \
|
||||
&stc8h_cfg_##n, POST_KERNEL, \
|
||||
CONFIG_INPUT_INIT_PRIORITY, NULL);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(STC8H_INIT)
|
||||
@@ -439,6 +439,17 @@ static void joystick_ui_input_cb(struct input_event *evt, void *user_data)
|
||||
/* All other keys: fire on press, ignore release */
|
||||
if (!evt->value) return;
|
||||
|
||||
/* A keypad reports typed characters as their own ASCII code (see the
|
||||
* key-space contract in joystick_defs.h: 0x20-0x7E is reserved for
|
||||
* exactly this). Pass them through untranslated — the control codes
|
||||
* below all live outside that range, so there is no ambiguity. */
|
||||
if (evt->code >= 0x20 && evt->code <= 0x7E) {
|
||||
if (joystick_queue_initialized) {
|
||||
JoystickUITask::enqueueKey((char)evt->code);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
char key = 0;
|
||||
switch (evt->code) {
|
||||
case INPUT_KEY_LEFT: key = KEY_LEFT; break;
|
||||
@@ -446,6 +457,12 @@ static void joystick_ui_input_cb(struct input_event *evt, void *user_data)
|
||||
case INPUT_KEY_BACK:
|
||||
case INPUT_KEY_ESC: key = KEY_CANCEL; break;
|
||||
case INPUT_KEY_1: key = KEY_CANCEL; break;
|
||||
/* Keypad boards (ThinkNode M9) — the only input those have */
|
||||
case INPUT_KEY_HOME: key = KEY_HOME; break;
|
||||
case INPUT_KEY_MENU: key = KEY_SELECT; break;
|
||||
case INPUT_KEY_PAGEUP: key = KEY_PREV; break;
|
||||
case INPUT_KEY_PAGEDOWN:key = KEY_NEXT; break;
|
||||
case INPUT_KEY_F2: key = KEY_ENTER_LONG; break;
|
||||
/* Multi tap outputs from input_multi_tap filter */
|
||||
case INPUT_KEY_D: key = KEY_BUZZ_TOGGLE; break; /* 3 taps */
|
||||
case INPUT_KEY_C: key = KEY_GPS_TOGGLE; break; /* 4 taps */
|
||||
|
||||
Reference in New Issue
Block a user