feat(travelers): TCA9535 充电检测 + 键盘背光 + isVbusIn 修复
- 新增 P1.1 CHARGE_DET 充电检测(高电平=充电中),轮询间隔 2s
- Power.cpp isCharging()/isVbusIn() 均使用 TCA9535_CHARGE_DET_PIN 分支
- 新增 P1.0 键盘背光(高电平点亮),按键时亮,5s 无操作自动熄灭
- 修复开机供电维持:POWER_EN 在 Wire.begin() 后立即锁定
- 修复 P1 config 寄存器值 0x0A(之前 0x8D 导致 P1.2 高阻断电)
- ⚠️ 已知问题:TP4057 电压反串导致未充电时 P1.1 仍读高,需硬件修改
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"version": 2,
|
||||
"sessions": {
|
||||
"e341ab1f344d4f54946c2ba835bc7aa3": [
|
||||
{
|
||||
"expertId": "EmbeddedFirmwareEngineer",
|
||||
"name": "Owen",
|
||||
"profession": "嵌入式固件工程师",
|
||||
"avatarUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/avatars/02-Engineering/EmbeddedFirmwareEngineer/EmbeddedFirmwareEngineer.png",
|
||||
"promptUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/experts/02-Engineering/EmbeddedFirmwareEngineer/EmbeddedFirmwareEngineer_zh.md",
|
||||
"usedAt": 1774720870071,
|
||||
"industryId": "all"
|
||||
}
|
||||
]
|
||||
},
|
||||
"lastUpdated": 1774745350742
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
# 2026-03-29 工作日志
|
||||
|
||||
## 项目初始扫描
|
||||
- 首次读取 firmware-2.7.15.567b8ea 代码结构
|
||||
- 确认项目为 Meshtastic 官方固件的自定义 fork
|
||||
- 发现 4 个自研板卡变体:esp32c3_moonshine / moonshine(带ADC/USB CDC)/ moonshine_mv(全功能)/ moonshine_travelers
|
||||
- 详细记录写入 MEMORY.md
|
||||
|
||||
## TCA9535PWR 矩阵键盘驱动实现
|
||||
- 目标板卡:esp32c3_moonshine_travelers
|
||||
- 新建驱动文件:
|
||||
- `src/input/TCA9535ButtonThread.h` - 驱动类声明,含寄存器宏、extern 指针
|
||||
- `src/input/TCA9535ButtonThread.cpp` - 4×4 矩阵扫描实现
|
||||
- 修改 `variants/esp32c3/diy/esp32c3_moonshine_travelers/variant.h`:
|
||||
- 添加 `HAS_TCA9535_BUTTON` 宏
|
||||
- 添加 `TCA9535_KEY_MAP`(4×4 矩阵行优先映射)
|
||||
- `TCA9535_INT_PIN=5`(GPIO5,低电平有效,下降沿触发)
|
||||
- 修改 `src/main.cpp`:include TCA9535ButtonThread.h,在 setupModules() 后初始化线程
|
||||
- I²C 地址:0x20(A0=A1=A2=0),与 SH1106 屏幕共用 Wire(SDA=0, SCL=1)
|
||||
- 矩阵接法:P0.0~P0.3 行输出,P0.4~P0.7 列输入,逐行拉低扫描,50µs 行间延时
|
||||
|
||||
## LTO 链接错误修复
|
||||
- 编译报 `undefined reference to TCA9535ButtonThread::*`(LTO -flto 导致)
|
||||
- 原因:.h/.cpp 中的 `#if defined(HAS_TCA9535_BUTTON)` 守卫导致在部分编译单元中符号被丢弃
|
||||
- 修复:去掉 .h/.cpp 中的条件守卫,让类定义和实现始终编译
|
||||
- main.cpp 中的实例化和初始化仍由 `#ifdef HAS_TCA9535_BUTTON` 控制
|
||||
- extern 指针声明保留在 `#ifdef` 中,非目标板卡不会引用
|
||||
|
||||
## RadioLib LoRa RST 通过 TCA9535 P1.4 控制
|
||||
- 自定义 HAL 子类 `TCA9535GpioHal` 拦截虚拟引脚 200,转发到 I²C
|
||||
- `tca9535LoraReset(bool high)` 静态函数,read-modify-write P1.4
|
||||
|
||||
## 电源管理(P1.2 + P1.3)
|
||||
- P1.2 = POWER_EN 输出,高电平有效,驱动 MOS 管维持供电
|
||||
- P1.3 = POWER_BOOT 输入,低电平有效(按键按下接地)
|
||||
- 开机流程:物理按键 → MOS 导通 → ESP32 得电 → init() 等 P1.3 持续按住 2 秒 → POWER_EN 拉高
|
||||
- 未按够 2 秒松开 → 不拉高 POWER_EN → 断电
|
||||
- 关机流程:运行中 P1.3 持续按住 2 秒 → POWER_EN 拉低 → 断电
|
||||
- 电源状态机:BOOT_PENDING → RUNNING → SHUTDOWN_PENDING
|
||||
- init() 中 P1 config = 0xEB(P1.2=输出, P1.3=输入, P1.4=输出)
|
||||
- 新增 `tca9535ReadPowerBoot()` 静态函数,读取 P1.3 输入状态
|
||||
- 新增 `tca9535PowerEn(bool on)` 静态函数,read-modify-write P1.2
|
||||
|
||||
## GPS 启用(GP-02 模块)
|
||||
- 问题:`tca9535GpsEn(false)` 在 TCA9535 init 中关闭了 GPS 电源,而 GPS::setup()->probe() 时 powerState=GPS_OFF,writePinEN(true) 只在 GPS_ACTIVE 时才调用 → probe 时模块没电
|
||||
- 修复:在 main.cpp 的 `createGps()` + enablePin 桥接之后,立即 `tca9535GpsEn(true)` + `delay(1000)` 给 GP-02 冷启动上电时间
|
||||
- GP-02 接线:VCC→3.3V, GND→GND, TX→GPIO20(ESP RX), RX→GPIO21(ESP TX),EN/RST/PPS/VRTC 悬空即可
|
||||
- 编译验证通过(esp32c3_moonshine_travelers SUCCESS)
|
||||
|
||||
## GPS 修复:P1.6/P1.7 默认高电平 + RX/TX 交换
|
||||
- 用户要求 GPS_RST(P1.6) 和 GPS_EN(P1.7) 通电后直接设为高电平
|
||||
- TCA9535ButtonThread.cpp init() 中改为 `tca9535GpsEn(true)` 和 `tca9535GpsReset(true)`
|
||||
- 移除 main.cpp 中的 delay workaround
|
||||
- GPS 仍然检测不到 → 交换 GPS_RX_PIN(21→20) 和 GPS_TX_PIN(20→21)
|
||||
- 更新 CHANGELOG.md,提交 311232c,push
|
||||
|
||||
## 九宫格键盘映射
|
||||
- 添加 4×4 矩阵中的数字键:key0-2=1-3, key4-6=4-6, key8-10=7-9, key12=*, key13=0, key14=#
|
||||
- 方向键保留:key3=UP, key7=DOWN, key11=LEFT, key15=RIGHT
|
||||
- 新增 `TCA9535_KEY_CHAR_MAP` 数组,`dispatchEvent()` 传 `kbchar` 参数
|
||||
|
||||
## ⚠️ TCA9535 MATRIXKEY 崩溃修复 + 键盘输入修复
|
||||
- 崩溃现象:按下矩阵键后立即 Load access fault at 0x40058766
|
||||
- 根因:`TCA9535_KEY_CHAR_MAP` 传的是 ASCII 字符(如 '0'=0x30, '9'=0x39),但 `CannedMessageModule` 把 `kbchar` 当 1-based 索引用(`currentMessageIndex = event->kbchar - 1`)。ASCII '0'=48 → index=47 → 数组越界 → 野指针 → 崩溃
|
||||
- 参考 RAK14004 实现:`kbI2cBase.cpp` 中 `PrintDataBuf = aCount*4 + bCount + 1` 传的是 1-16 的索引
|
||||
- 第一版修复(错误方向):把 TCA9535_KEY_CHAR_MAP 改为传索引 1-12
|
||||
- 问题:这样矩阵键只能选 canned message,无法进入文本输入
|
||||
- 正确修复:
|
||||
1. `TCA9535_KEY_CHAR_MAP` 恢复为 ASCII 字符('0'-'9', '*', '#')
|
||||
2. `CannedMessageModule::handleInputEvent()` 修改 MATRIXKEY 拦截逻辑:
|
||||
- 如果 `kbchar` 是可打印 ASCII(32-126):不拦截,让事件 fall through 到正常输入路径
|
||||
→ INACTIVE 状态进入 FREETEXT,FREETEXT 状态追加字符
|
||||
- 如果 `kbchar` 是 1-based 索引(如 RAK14004):继续走 canned message 选择路径
|
||||
- 加边界检查 `idx < 0 || idx >= messagesCount` 防御性忽略
|
||||
|
||||
## P1.1 充电检测 (CHARGE_DET)
|
||||
- P1.1 = CHARGE_DET 输入,高电平=正在充电
|
||||
- P1 config 从 0x8B 改为 0x8D(P1.1 配置为输入)
|
||||
- variant.h 新增 `TCA9535_CHARGE_DET_PIN (1u << 1)`
|
||||
- TCA9535ButtonThread.h 新增 `tca9535ReadChargeDet()` 静态内联函数
|
||||
- TCA9535ButtonThread.cpp 的 runOnce() 每 2 秒轮询 P1.1,更新全局 `tca9535IsCharging`
|
||||
- Power.cpp 的 `AnalogBatteryLevel::isCharging()` 读取 `tca9535IsCharging`(`#ifdef TCA9535_CHARGE_DET_PIN`)
|
||||
- Power.cpp include 条件从 `TCA9535_LORA_RST_VIRTUAL_PIN` 改为 `HAS_TCA9535_BUTTON`
|
||||
@@ -0,0 +1,103 @@
|
||||
# 项目长期记忆
|
||||
|
||||
## 项目概述
|
||||
- **项目**:LoRa Meshtastic 固件开发
|
||||
- **主代码目录**:`code/firmware-2.7.15.567b8ea`(基于 Meshtastic 官方固件 v2.7.15)
|
||||
- **构建系统**:PlatformIO + ESP-IDF (Arduino framework for ESP32)
|
||||
|
||||
## 硬件平台 & 自定义板卡
|
||||
项目有多个自研板卡(均为 ESP32-C3 + LoRa 模块组合):
|
||||
|
||||
### esp32c3_moonshine(基础版)
|
||||
- MCU:ESP32-C3(esp32-c3-devkitm-1)
|
||||
- LoRa:E220-400M30S(LLCC68/SX1262/SX1268 三选一探测)
|
||||
- 无屏幕、无 GPS
|
||||
- SPI: SCK=10, MISO=6, MOSI=7, CS=8, RST=5, DIO1=3, BUSY=4
|
||||
- LED_POWER=12,DIO3 TCXO 1.8V,TCXO_OPTIONAL
|
||||
- Flash: 4MB, dio 模式, 80MHz
|
||||
|
||||
### esp32c3_moonshine(firmware 版本,variants 中)
|
||||
- 同上基础版,增加:
|
||||
- 支持 SX126X_RXEN=2(RX 使能 GPIO)
|
||||
- 支持 E22_400M33S(SX126X_MAX_POWER=22, TX_GAIN=0)
|
||||
- BATTERY_PIN=1, ADC_MULTIPLIER=2.0f
|
||||
- USB CDC 启动:ARDUINO_USB_MODE=1, ARDUINO_USB_CDC_ON_BOOT=1
|
||||
|
||||
### esp32c3_moonshine_mv(带屏幕+GPS+多按键版)
|
||||
- MCU:ESP32-C3
|
||||
- LoRa:RA-01SC-P(SETTING_MAX_POWER=29, TX_GAIN=26, SX126X_MAX_POWER=3)
|
||||
- 屏幕:SSD1306(I2C: SDA=0, SCL=1)
|
||||
- GPS:GPS_RX=21, GPS_TX=20, GPS_POWER_TOGGLE, PIN_GPS_EN=12
|
||||
- 多按键:PCF8574 IO 扩展器(地址 0x27,INT=9),映射 SELECT/UP/DOWN/LEFT/RIGHT/CANCEL
|
||||
- NeoPixel:GPIO13, 1颗 NEO_GRB
|
||||
- BATTERY_PIN=2
|
||||
|
||||
### esp32c3_moonshine_travelers(旅行者版)
|
||||
- MCU:ESP32-C3
|
||||
- LoRa:RA-01SC-P(SETTING_MAX_POWER=3, TX_GAIN=0, SX126X_MAX_POWER=3)
|
||||
- 屏幕:SH1106(I2C: SDA=0, SCL=1)
|
||||
- GPS:GPS_RX=21, GPS_TX=20, GPS_RST=P1.6(TCA9535), GPS_EN=P1.7(TCA9535)
|
||||
- GPS EN 通过 `GpioTca9535GpsEnPin` + `GpioUnaryTransformer` 桥接 `gps->enablePin`
|
||||
- GPS RST 由 `tca9535GpsReset()` 控制,init 中释放(高电平)
|
||||
- BATTERY_PIN=2
|
||||
- **GPIO9**:短按=SELECT,长按=无功能(`BUTTON_DISABLE_LONG_PRESS`)
|
||||
- **TCA9535PWR 4×4 矩阵键盘**:与屏幕共用 I2C,A0=A1=A2=0,地址 0x20
|
||||
- 驱动:`src/input/TCA9535ButtonThread.h/.cpp`
|
||||
- 矩阵接法:P0.0~P0.3 行输出(ROW0~ROW3),P0.4~P0.7 列输入(COL0~COL3)
|
||||
- 扫描方式:逐行拉低输出,读列检测低电平,50µs 行间延时
|
||||
- 中断引脚:TCA9535_INT_PIN=5(GPIO5,低电平有效,下降沿触发)
|
||||
- 映射(variant.h):key0-2=索引1-3, key4-6=索引4-6, key8-10=索引7-9, key12=索引10(*), key13=索引11(0), key14=索引12(#);key3=UP, key7=DOWN, key11=LEFT, key15=RIGHT
|
||||
- **kbchar 字段语义**:
|
||||
- RAK14004 传 1-based 索引(kbchar=1..16),走 canned message 选择路径
|
||||
- TCA9535 传 ASCII 字符('0'-'9','*','#'),走文本输入路径(INACTIVE→FREETEXT)
|
||||
- `CannedMessageModule` 的 MATRIXKEY 拦截:可打印 ASCII 不拦截(fall through),索引才拦截
|
||||
- 不能传 ASCII 给索引路径!ASCII '0'=48 → index=47 → 数组越界 → 崩溃
|
||||
- SELECT 由 GPIO9 短按处理,CANCEL 由 POWER_BOOT 短按处理
|
||||
- **P1.2 = POWER_EN**:高电平有效,驱动 MOS 管维持供电
|
||||
- `tca9535PowerEn(bool on)` 静态函数,read-modify-write P1.2
|
||||
- **P1.3 = POWER_BOOT**:输入,低电平有效(按键按下接地)
|
||||
- 开机:物理按键 → MOS → ESP32 得电 → **main.cpp Wire.begin() 后立即调用 `tca9535PowerEn(true)` 锁住供电**
|
||||
- ⚠️ 曾经的设计错误:在 `tca9535ButtonThread::init()` 里等 P1.3 按住 2s 再拉高 POWER_EN,但 setup() 需要数秒初始化,用户早已松手导致 MOS 断电。已修复为在 Wire.begin() 后立即上锁。
|
||||
- 短按:派发 INPUT_BROKER_CANCEL
|
||||
- 长按 2s:派发 INPUT_BROKER_SHUTDOWN(走系统关机流程 → Power::shutdown() → POWER_EN 拉低)
|
||||
- `tca9535ReadPowerBoot()` 静态函数读取 P1.3 状态
|
||||
- **P1.4 = LoRa RST**:通过 TCA9535GpioHal 自定义 HAL 拦截虚拟引脚 200 转发到 I²C
|
||||
- `tca9535LoraReset(bool high)` 静态函数,read-modify-write P1.4
|
||||
- `LORA_RESET = TCA9535_LORA_RST_VIRTUAL_PIN(200)`,RadioLib 的 `findChip()`/`reset()` 全链路走 I²C
|
||||
- **P1.5 = 状态指示灯**:输出,低电平点亮
|
||||
- `tca9535StatusLed(bool on)` 静态函数,read-modify-write P1.5
|
||||
- init() 中配置为输出,默认熄灭(高电平)
|
||||
- 在 TCA9535ButtonThread::runOnce() 中独立驱动,500ms 亮 + 500ms 灭 = 1 秒闪烁
|
||||
- 不再跟随 LED_PIN(已从 GpioSplitter 中移除)
|
||||
- **P1.0 = 键盘背光**:输出,高电平点亮
|
||||
- `TCA9535_BIT_P10` 宏 = `(1u << 0)`
|
||||
- `tca9535Backlight(bool on)` 静态函数,read-modify-write P1.0
|
||||
- init() 中默认熄灭(低电平)
|
||||
- runOnce() 中驱动:按键按下时点亮,刷新 5 秒计时;5 秒无操作自动熄灭
|
||||
- **P1.1 = CHARGE_DET**:输入,高电平=正在充电
|
||||
- `TCA9535_CHARGE_DET_PIN` 宏 = `(1u << 1)`,在 variant.h 中定义
|
||||
- `tca9535ReadChargeDet()` 静态内联函数,读 P1.1 输入寄存器
|
||||
- `tca9535IsCharging` 全局 volatile bool,由 runOnce() 每 2 秒轮询更新
|
||||
- `Power.cpp` 的 `AnalogBatteryLevel::isCharging()` 和 `isVbusIn()` 均读取此变量
|
||||
- P1 config = 0x0A(P1.1=输入 CHARGE_DET, P1.3=输入 POWER_BOOT, 其余输出)
|
||||
- ⚠️ 踩坑1:加 CHARGE_DET 后曾将 config 从 0x8B 改为 0x8D,导致 P1.2(POWER_EN) 被误配成输入(高阻),MOS 失控断电。正确值 0x0A
|
||||
- ⚠️ 踩坑2:TP4057 充电芯片电压反串导致 P1.1 在未充电时仍读高电平,充电检测误报。需硬件修改解决。
|
||||
- **关机路径**:所有关机触发最终走 Power::shutdown() → tca9535PowerEn(false) → doDeepSleep()
|
||||
|
||||
## 代码架构要点
|
||||
- **踩坑记录**:TCA9535 矩阵扫描 cols 计算中 `~` 运算符对 uint8_t 会整数提升为 int,导致高 4 位被污染。修复:`((~(p0In & 0xF0)) >> 4) & 0x0F`。见 scanMatrix()。
|
||||
- 路由层:FloodingRouter → ReliableRouter → NextHopRouter
|
||||
- 无线接口:RadioLib 抽象层(SX126x/SX128x/LR11x0/RF95)
|
||||
- 模块系统:`src/modules/` 下各功能模块(TextMessage, Position, Telemetry, etc.)
|
||||
- Protobuf 消息定义:`src/mesh/generated/` + `protobufs/`
|
||||
- 关键全局变量:`router`, `service`, `screen`, `gps`, `rIf`
|
||||
|
||||
## 自定义修改记录(readme.md)
|
||||
- 增加 CN 频段定义:`RDEF(CN, 470.0f, 510.0f, 100, 0, SETTING_MAX_POWER, true, false, false)`
|
||||
- SETTING_MAX_POWER 使用宏保护:`#ifndef SETTING_MAX_POWER / #define SETTING_MAX_POWER 3`
|
||||
|
||||
## 构建注意事项
|
||||
- `platformio.ini` 中 lib_deps 均已固定 commit hash,符合生产规范
|
||||
- 排除了大量 RadioLib 不用的协议(AX25/LoRaWAN/APRS 等)以节省 flash
|
||||
- MAX_THREADS=40
|
||||
- 默认 env:tbeam(需要切换到 esp32c3_moonshine 系列时要指定 env)
|
||||
Binary file not shown.
@@ -20,7 +20,7 @@
|
||||
#include "meshUtils.h"
|
||||
#include "sleep.h"
|
||||
|
||||
#ifdef TCA9535_LORA_RST_VIRTUAL_PIN
|
||||
#ifdef HAS_TCA9535_BUTTON
|
||||
#include "input/TCA9535ButtonThread.h"
|
||||
#endif
|
||||
|
||||
@@ -445,7 +445,10 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
/// so we use EXT_PWR_DETECT GPIO pin to detect external power source
|
||||
virtual bool isVbusIn() override
|
||||
{
|
||||
#ifdef EXT_PWR_DETECT
|
||||
#ifdef TCA9535_CHARGE_DET_PIN
|
||||
// 使用 TCA9535 CHARGE_DET 检测外部供电(高电平=充电中=有外部电源)
|
||||
return tca9535IsCharging;
|
||||
#elif defined(EXT_PWR_DETECT)
|
||||
#if defined(HELTEC_CAPSULE_SENSOR_V3) || defined(HELTEC_SENSOR_HUB)
|
||||
// if external powered that pin will be pulled down
|
||||
if (digitalRead(EXT_PWR_DETECT) == LOW) {
|
||||
@@ -472,7 +475,9 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
return (rak9154Sensor.isCharging()) ? OptTrue : OptFalse;
|
||||
}
|
||||
#endif
|
||||
#ifdef EXT_CHRG_DETECT
|
||||
#ifdef TCA9535_CHARGE_DET_PIN
|
||||
return tca9535IsCharging;
|
||||
#elif defined(EXT_CHRG_DETECT)
|
||||
return digitalRead(EXT_CHRG_DETECT) == ext_chrg_detect_value;
|
||||
#else
|
||||
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(DISABLE_INA_CHARGING_DETECTION)
|
||||
|
||||
@@ -5,38 +5,67 @@
|
||||
using namespace concurrency;
|
||||
|
||||
// 默认按键映射(4×4 矩阵,行优先:KEY[0]=ROW0·COL0 ... KEY[15]=ROW3·COL3)
|
||||
// 仅保留方向键,SELECT/CANCEL 由其他按键处理
|
||||
// variant.h 中可用 #define TCA9535_KEY_MAP { ... } 覆盖
|
||||
// -----------------------------------------------------------------------
|
||||
#ifndef TCA9535_KEY_MAP
|
||||
#define TCA9535_KEY_MAP \
|
||||
{ \
|
||||
INPUT_BROKER_NONE, /* key0 = ROW0·COL0 */ \
|
||||
INPUT_BROKER_NONE, /* key1 = ROW0·COL1 */ \
|
||||
INPUT_BROKER_NONE, /* key2 = ROW0·COL2 */ \
|
||||
INPUT_BROKER_UP, /* key3 = ROW0·COL3 */ \
|
||||
INPUT_BROKER_NONE, /* key4 = ROW1·COL0 */ \
|
||||
INPUT_BROKER_NONE, /* key5 = ROW1·COL1 */ \
|
||||
INPUT_BROKER_NONE, /* key6 = ROW1·COL2 */ \
|
||||
INPUT_BROKER_DOWN, /* key7 = ROW1·COL3 */ \
|
||||
INPUT_BROKER_NONE, /* key8 = ROW2·COL0 */ \
|
||||
INPUT_BROKER_NONE, /* key9 = ROW2·COL1 */ \
|
||||
INPUT_BROKER_NONE, /* key10 = ROW2·COL2 */ \
|
||||
INPUT_BROKER_LEFT, /* key11 = ROW2·COL3 */ \
|
||||
INPUT_BROKER_NONE, /* key12 = ROW3·COL0 */ \
|
||||
INPUT_BROKER_NONE, /* key13 = ROW3·COL1 */ \
|
||||
INPUT_BROKER_NONE, /* key14 = ROW3·COL2 */ \
|
||||
INPUT_BROKER_RIGHT, /* key15 = ROW3·COL3 */ \
|
||||
INPUT_BROKER_MATRIXKEY, /* key0 = ROW0·COL0 → '1' */ \
|
||||
INPUT_BROKER_MATRIXKEY, /* key1 = ROW0·COL1 → '2' */ \
|
||||
INPUT_BROKER_MATRIXKEY, /* key2 = ROW0·COL2 → '3' */ \
|
||||
INPUT_BROKER_UP, /* key3 = ROW0·COL3 */ \
|
||||
INPUT_BROKER_MATRIXKEY, /* key4 = ROW1·COL0 → '4' */ \
|
||||
INPUT_BROKER_MATRIXKEY, /* key5 = ROW1·COL1 → '5' */ \
|
||||
INPUT_BROKER_MATRIXKEY, /* key6 = ROW1·COL2 → '6' */ \
|
||||
INPUT_BROKER_DOWN, /* key7 = ROW1·COL3 */ \
|
||||
INPUT_BROKER_MATRIXKEY, /* key8 = ROW2·COL0 → '7' */ \
|
||||
INPUT_BROKER_MATRIXKEY, /* key9 = ROW2·COL1 → '8' */ \
|
||||
INPUT_BROKER_MATRIXKEY, /* key10 = ROW2·COL2 → '9' */ \
|
||||
INPUT_BROKER_LEFT, /* key11 = ROW2·COL3 */ \
|
||||
INPUT_BROKER_MATRIXKEY, /* key12 = ROW3·COL0 → '*' */ \
|
||||
INPUT_BROKER_MATRIXKEY, /* key13 = ROW3·COL1 → '0' */ \
|
||||
INPUT_BROKER_MATRIXKEY, /* key14 = ROW3·COL2 → '#' */ \
|
||||
INPUT_BROKER_RIGHT, /* key15 = ROW3·COL3 */ \
|
||||
}
|
||||
#endif
|
||||
|
||||
// 默认按键字符映射(仅 INPUT_BROKER_MATRIXKEY 类型的按键使用)
|
||||
// 传 ASCII 字符,CannedMessageModule 会根据 kbchar 走文本输入路径
|
||||
// variant.h 中可用 #define TCA9535_KEY_CHAR_MAP { ... } 覆盖
|
||||
#ifndef TCA9535_KEY_CHAR_MAP
|
||||
#define TCA9535_KEY_CHAR_MAP \
|
||||
{ \
|
||||
'1', /* key0 = ROW0·COL0 */ \
|
||||
'2', /* key1 = ROW0·COL1 */ \
|
||||
'3', /* key2 = ROW0·COL2 */ \
|
||||
0, /* key3 = ROW0·COL3 → 方向键,无字符 */ \
|
||||
'4', /* key4 = ROW1·COL0 */ \
|
||||
'5', /* key5 = ROW1·COL1 */ \
|
||||
'6', /* key6 = ROW1·COL2 */ \
|
||||
0, /* key7 = ROW1·COL3 → 方向键,无字符 */ \
|
||||
'7', /* key8 = ROW2·COL0 */ \
|
||||
'8', /* key9 = ROW2·COL1 */ \
|
||||
'9', /* key10 = ROW2·COL2 */ \
|
||||
0, /* key11 = ROW2·COL3 → 方向键,无字符 */ \
|
||||
'*', /* key12 = ROW3·COL0 */ \
|
||||
'0', /* key13 = ROW3·COL1 */ \
|
||||
'#', /* key14 = ROW3·COL2 */ \
|
||||
0, /* key15 = ROW3·COL3 → 方向键,无字符 */ \
|
||||
}
|
||||
#endif
|
||||
|
||||
static const input_broker_event tca9535KeyMap[TCA9535_KEY_COUNT] = TCA9535_KEY_MAP;
|
||||
static const unsigned char tca9535KeyCharMap[TCA9535_KEY_COUNT] = TCA9535_KEY_CHAR_MAP;
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// 中断标志(ISR -> runOnce 通信,volatile,只做 set/clear)
|
||||
// -----------------------------------------------------------------------
|
||||
static volatile bool tca9535IntPending = false;
|
||||
|
||||
#ifdef HAS_TCA9535_BUTTON
|
||||
volatile bool tca9535IsCharging = false;
|
||||
#endif
|
||||
|
||||
#ifdef TCA9535_INT_PIN
|
||||
static void IRAM_ATTR tca9535ISR()
|
||||
{
|
||||
@@ -58,18 +87,23 @@ bool TCA9535ButtonThread::init()
|
||||
{
|
||||
// ===================================================================
|
||||
// 第一步:配置 P1 口方向
|
||||
// P1.2=输出(POWER_EN), P1.3=输入(POWER_BOOT), P1.4=输出(LoRa RST),
|
||||
// P1.5=输出(状态灯), P1.6=输出(GPS RST), P1.7=输出(GPS EN)
|
||||
// P1.0=输出(未用), P1.1=输入(CHARGE_DET), P1.2=输出(POWER_EN),
|
||||
// P1.3=输入(POWER_BOOT), P1.4=输出(LoRa RST), P1.5=输出(状态灯),
|
||||
// P1.6=输出(GPS RST), P1.7=输出(GPS EN)
|
||||
// Configuration 寄存器:1=input, 0=output
|
||||
// P1.2=0, P1.3=1, P1.4=0, P1.5=0, P1.6=0, P1.7=0 → 0x8B (1000 1011)
|
||||
// bit: P1.7 P1.6 P1.5 P1.4 P1.3 P1.2 P1.1 P1.0
|
||||
// 0 0 0 0 1 0 1 0 = 0x0A
|
||||
// ===================================================================
|
||||
if (!writeReg(TCA9535_REG_CONFIG_P1, 0x8B)) {
|
||||
if (!writeReg(TCA9535_REG_CONFIG_P1, 0x0A)) {
|
||||
LOG_WARN("TCA9535: P1 config write failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// P1.0 键盘背光默认熄灭(低电平)
|
||||
tca9535Backlight(false);
|
||||
|
||||
// 确保 P1.4 输出高电平(LoRa RST 高 = 正常工作)
|
||||
// 注意:此时不拉高 POWER_EN,等开机确认后再拉高
|
||||
// 注意:POWER_EN 已由 main.cpp 在 Wire.begin() 后立即拉高,此处无需再操作
|
||||
tca9535LoraReset(true);
|
||||
|
||||
// P1.5 状态灯默认熄灭(高电平)
|
||||
@@ -82,54 +116,13 @@ bool TCA9535ButtonThread::init()
|
||||
tca9535GpsEn(true);
|
||||
|
||||
// ===================================================================
|
||||
// 第二步:开机检测 — 等待用户持续按住 P1.3 达 2 秒
|
||||
// 物理按键已使 MOS 导通(ESP32 得电),但 POWER_EN 尚未拉高
|
||||
// 用户必须持续按住 2 秒,否则 init() 返回 false → 系统不完成启动
|
||||
// 第二步:POWER_EN 已由 main.cpp 在 Wire.begin() 后立即拉高(早期锁定)
|
||||
// 此处只需确认状态机进入 RUNNING,不再需要等待 P1.3 按住 2 秒。
|
||||
// 原因:系统从 Wire.begin() 到 tca9535ButtonThread::init() 之间需要
|
||||
// 数秒的初始化时间(LoRa/WiFi/BLE/GPS 等),用户早已松开按键,
|
||||
// 无法在此处等待。开机供电维持已在 main.cpp 最早期完成。
|
||||
// ===================================================================
|
||||
LOG_INFO("TCA9535: Waiting for power button hold (%d ms)...", TCA9535_POWER_BOOT_HOLD_MS);
|
||||
|
||||
uint32_t holdStart = 0;
|
||||
bool wasPressed = false;
|
||||
|
||||
while (true) {
|
||||
bool pressed = tca9535ReadPowerBoot(_wire);
|
||||
|
||||
if (pressed && !wasPressed) {
|
||||
// 按键刚按下,记录起始时间
|
||||
holdStart = millis();
|
||||
wasPressed = true;
|
||||
} else if (!pressed && wasPressed) {
|
||||
// 按键松开 — 检查是否按够时间
|
||||
uint32_t held = millis() - holdStart;
|
||||
if (held >= TCA9535_POWER_BOOT_HOLD_MS) {
|
||||
// 按够 2 秒,确认开机
|
||||
LOG_INFO("TCA9535: Power button held %lu ms -> boot confirmed", held);
|
||||
break;
|
||||
} else {
|
||||
// 未按够,重新等待
|
||||
LOG_INFO("TCA9535: Power button released after %lu ms (need %d), waiting...", held,
|
||||
TCA9535_POWER_BOOT_HOLD_MS);
|
||||
wasPressed = false;
|
||||
}
|
||||
} else if (pressed && wasPressed) {
|
||||
// 持续按住中,检查是否已达 2 秒(即使没松开也确认)
|
||||
if ((millis() - holdStart) >= TCA9535_POWER_BOOT_HOLD_MS) {
|
||||
LOG_INFO("TCA9535: Power button held >= %d ms -> boot confirmed", TCA9535_POWER_BOOT_HOLD_MS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
delay(TCA9535_POWER_BOOT_CHECK_MS);
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// 第三步:确认开机 → 拉高 POWER_EN 维持供电
|
||||
// ===================================================================
|
||||
if (!tca9535PowerEn(true)) {
|
||||
LOG_WARN("TCA9535: Failed to set POWER_EN high");
|
||||
return false;
|
||||
}
|
||||
LOG_INFO("TCA9535: POWER_EN set HIGH (system powered)");
|
||||
LOG_INFO("TCA9535: POWER_EN already latched in early boot, skipping boot-hold wait");
|
||||
_powerState = TCA9535PowerState::RUNNING;
|
||||
|
||||
// ===================================================================
|
||||
@@ -206,6 +199,28 @@ int32_t TCA9535ButtonThread::runOnce()
|
||||
tca9535StatusLed(_statusLedOn);
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// P1.0 键盘背光:有按键按下时点亮,5 秒无操作自动熄灭
|
||||
// ===================================================================
|
||||
if (_backlightOn && millis() - _backlightLastMs >= 5000) {
|
||||
_backlightOn = false;
|
||||
tca9535Backlight(false);
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// 充电检测:轮询 P1.1 (CHARGE_DET),高电平=正在充电
|
||||
// ===================================================================
|
||||
#ifdef TCA9535_CHARGE_DET_PIN
|
||||
if (millis() - _chargeDetLastMs >= 2000) {
|
||||
_chargeDetLastMs = millis();
|
||||
bool charging = tca9535ReadChargeDet();
|
||||
if (charging != tca9535IsCharging) {
|
||||
tca9535IsCharging = charging;
|
||||
LOG_INFO("TCA9535: Charging %s", charging ? "DETECTED" : "STOPPED");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// ===================================================================
|
||||
// 矩阵键盘扫描(仅 RUNNING 状态)
|
||||
// ===================================================================
|
||||
@@ -231,9 +246,17 @@ int32_t TCA9535ButtonThread::runOnce()
|
||||
// 遍历所有键位,派发按下事件
|
||||
for (uint8_t i = 0; i < TCA9535_KEY_COUNT; i++) {
|
||||
if (pressed & (1u << i)) {
|
||||
// 按键按下 → 点亮键盘背光(重置 5 秒计时)
|
||||
if (!_backlightOn) {
|
||||
_backlightOn = true;
|
||||
_backlightLastMs = millis();
|
||||
tca9535Backlight(true);
|
||||
} else {
|
||||
_backlightLastMs = millis(); // 已亮则刷新计时
|
||||
}
|
||||
input_broker_event evt = tca9535KeyMap[i];
|
||||
if (evt != INPUT_BROKER_NONE) {
|
||||
dispatchEvent(evt);
|
||||
dispatchEvent(evt, tca9535KeyCharMap[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -308,12 +331,12 @@ bool TCA9535ButtonThread::readReg(uint8_t reg, uint8_t &val)
|
||||
return true;
|
||||
}
|
||||
|
||||
void TCA9535ButtonThread::dispatchEvent(input_broker_event evt)
|
||||
void TCA9535ButtonThread::dispatchEvent(input_broker_event evt, unsigned char kbchar)
|
||||
{
|
||||
InputEvent e = {};
|
||||
e.source = _originName;
|
||||
e.inputEvent = evt;
|
||||
e.kbchar = 0;
|
||||
e.kbchar = kbchar;
|
||||
e.touchX = 0;
|
||||
e.touchY = 0;
|
||||
this->notifyObservers(&e);
|
||||
|
||||
@@ -7,10 +7,12 @@
|
||||
* - A0=0, A1=0, A2=0 → I²C 地址 0x20
|
||||
* - P0.0~P0.3:行输出(ROW0~ROW3),逐行拉低扫描
|
||||
* - P0.4~P0.7:列输入(COL0~COL3),读取按键状态
|
||||
* - P1.2:电源使能(POWER_EN),高电平有效,驱动 MOS 管维持供电
|
||||
* - P1.1:充电检测输入(CHARGE_DET),高电平=正在充电
|
||||
* - P1.2:电源使能(POWER_EN),高电平有效,驱动 MOS 管维持供电(输出)
|
||||
* - P1.3:电源开机按钮(POWER_BOOT),输入,低电平有效(按键按下接地)
|
||||
* - P1.4:LoRa RST 输出(通过 I²C 控制 RadioLib 复位序列)
|
||||
* - P1.5:状态指示灯,低电平点亮
|
||||
* P1 Config 寄存器 = 0x0A(P1.1、P1.3 为输入,其余为输出)
|
||||
*
|
||||
* 电源管理逻辑:
|
||||
* 开机:物理按键按下 → MOS 导通 → ESP32/TCA9535 得电
|
||||
@@ -81,6 +83,7 @@
|
||||
#define TCA9535_REG_CONFIG_P1 0x07
|
||||
|
||||
// P1 口引脚位掩码
|
||||
#define TCA9535_BIT_P10 (1u << 0) // 键盘背光输出(高电平点亮)
|
||||
#define TCA9535_BIT_P12 (1u << 2) // POWER_EN 输出
|
||||
#define TCA9535_BIT_P13 (1u << 3) // POWER_BOOT 输入
|
||||
#define TCA9535_BIT_P14 (1u << 4) // LoRa RST 输出
|
||||
@@ -88,6 +91,24 @@
|
||||
#define TCA9535_BIT_P16 (1u << 6) // GPS RST 输出
|
||||
#define TCA9535_BIT_P17 (1u << 7) // GPS EN 输出(高电平有效)
|
||||
|
||||
#ifdef TCA9535_CHARGE_DET_PIN
|
||||
/**
|
||||
* 通过 I²C 读取 TCA9535 CHARGE_DET(P1.1)输入状态。
|
||||
* @return true=正在充电(高电平),false=未充电(低电平)
|
||||
*/
|
||||
static inline bool tca9535ReadChargeDet()
|
||||
{
|
||||
Wire.beginTransmission(TCA9535_I2C_ADDR);
|
||||
Wire.write(TCA9535_REG_INPUT_P1);
|
||||
if (Wire.endTransmission(false) != 0)
|
||||
return false;
|
||||
if (Wire.requestFrom((uint8_t)TCA9535_I2C_ADDR, (uint8_t)1) != 1)
|
||||
return false;
|
||||
uint8_t p1In = Wire.read();
|
||||
return !!(p1In & TCA9535_CHARGE_DET_PIN);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* 通过 I²C 控制 TCA9535 P1.2 上的电源使能(POWER_EN)。
|
||||
* 高电平有效:控制 MOS 管维持系统供电。
|
||||
@@ -191,6 +212,33 @@ static inline bool tca9535StatusLed(bool on)
|
||||
return (Wire.endTransmission() == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 I²C 控制 TCA9535 P1.0 上的键盘背光。
|
||||
* 高电平点亮,低电平熄灭。
|
||||
* @param on true=点亮(高电平),false=熄灭(低电平)
|
||||
*/
|
||||
static inline bool tca9535Backlight(bool on)
|
||||
{
|
||||
Wire.beginTransmission(TCA9535_I2C_ADDR);
|
||||
Wire.write(TCA9535_REG_OUTPUT_P1);
|
||||
if (Wire.endTransmission(false) != 0)
|
||||
return false;
|
||||
if (Wire.requestFrom((uint8_t)TCA9535_I2C_ADDR, (uint8_t)1) != 1)
|
||||
return false;
|
||||
uint8_t p1Out = Wire.read();
|
||||
|
||||
// 修改 P1.0 位:高电平点亮
|
||||
if (on)
|
||||
p1Out |= TCA9535_BIT_P10; // 拉高 = 点亮
|
||||
else
|
||||
p1Out &= ~TCA9535_BIT_P10; // 拉低 = 熄灭
|
||||
|
||||
Wire.beginTransmission(TCA9535_I2C_ADDR);
|
||||
Wire.write(TCA9535_REG_OUTPUT_P1);
|
||||
Wire.write(p1Out);
|
||||
return (Wire.endTransmission() == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 I²C 控制 TCA9535 P1.6 上的 GPS RST。
|
||||
* @param high true=释放复位(高电平),false=触发复位(低电平)
|
||||
@@ -288,6 +336,13 @@ class TCA9535ButtonThread : public Observable<const InputEvent *>, public concur
|
||||
bool _statusLedOn = false;
|
||||
uint32_t _statusLedToggleMs = 0;
|
||||
|
||||
// 充电检测轮询间隔
|
||||
uint32_t _chargeDetLastMs = 0;
|
||||
|
||||
// P1.0 键盘背光控制(按键时点亮,5 秒无操作熄灭)
|
||||
bool _backlightOn = false;
|
||||
uint32_t _backlightLastMs = 0;
|
||||
|
||||
// 写寄存器
|
||||
bool writeReg(uint8_t reg, uint8_t val);
|
||||
|
||||
@@ -298,10 +353,14 @@ class TCA9535ButtonThread : public Observable<const InputEvent *>, public concur
|
||||
bool scanMatrix(uint16_t &keys);
|
||||
|
||||
// 派发事件到 InputBroker
|
||||
void dispatchEvent(input_broker_event evt);
|
||||
void dispatchEvent(input_broker_event evt, unsigned char kbchar = 0);
|
||||
};
|
||||
|
||||
// 仅在 HAS_TCA9535_BUTTON 启用时导出全局指针声明
|
||||
#ifdef HAS_TCA9535_BUTTON
|
||||
extern TCA9535ButtonThread *tca9535ButtonThread;
|
||||
|
||||
// 全局充电检测状态,由 TCA9535ButtonThread::runOnce() 轮询更新
|
||||
// 可被 Power 等外部模块读取(需 #ifdef TCA9535_CHARGE_DET_PIN 守卫)
|
||||
extern volatile bool tca9535IsCharging;
|
||||
#endif
|
||||
|
||||
@@ -584,6 +584,13 @@ void setup()
|
||||
Wire.begin();
|
||||
#elif defined(I2C_SDA) && !defined(ARCH_RP2040)
|
||||
Wire.begin(I2C_SDA, I2C_SCL);
|
||||
#ifdef HAS_TCA9535_BUTTON
|
||||
// TCA9535 POWER_EN 必须在 I²C 初始化完成后立即拉高,否则用户松开按键后
|
||||
// MOS 断电,系统在 setup() 中途就会掉电。此处无条件锁住供电,
|
||||
// 后续 tca9535ButtonThread::init() 只负责键盘和状态机初始化。
|
||||
tca9535PowerEn(true);
|
||||
LOG_INFO("TCA9535: POWER_EN latched HIGH (early boot)");
|
||||
#endif
|
||||
#elif defined(ARCH_PORTDUINO)
|
||||
if (portduino_config.i2cdev != "") {
|
||||
LOG_INFO("Use %s as I2C device", portduino_config.i2cdev.c_str());
|
||||
|
||||
@@ -322,14 +322,27 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
|
||||
if (event->kbchar == INPUT_BROKER_MSG_TAB && handleTabSwitch(event))
|
||||
return 1;
|
||||
|
||||
// Matrix keypad: If matrix key, trigger action select for canned message
|
||||
// Matrix keypad: If matrix key with printable char, let it fall through to
|
||||
// the normal input path (INACTIVE→FREETEXT or FREETEXT→append char).
|
||||
// Only intercept as canned-message selector if kbchar is a valid 1-based index
|
||||
// (e.g., RAK14004 sends kbchar=1..16).
|
||||
if (event->inputEvent == INPUT_BROKER_MATRIXKEY) {
|
||||
runState = CANNED_MESSAGE_RUN_STATE_ACTION_SELECT;
|
||||
payload = INPUT_BROKER_MATRIXKEY;
|
||||
currentMessageIndex = event->kbchar - 1;
|
||||
lastTouchMillis = millis();
|
||||
requestFocus();
|
||||
return 1;
|
||||
// Printable ASCII (32-126): treat as keyboard input, don't intercept
|
||||
if (event->kbchar >= 32 && event->kbchar <= 126) {
|
||||
// Fall through to normal input handling below
|
||||
} else {
|
||||
// 1-based index from hardware like RAK14004
|
||||
int idx = event->kbchar - 1;
|
||||
if (idx < 0 || idx >= messagesCount) {
|
||||
return 0; // kbchar out of range, ignore
|
||||
}
|
||||
runState = CANNED_MESSAGE_RUN_STATE_ACTION_SELECT;
|
||||
payload = INPUT_BROKER_MATRIXKEY;
|
||||
currentMessageIndex = idx;
|
||||
lastTouchMillis = millis();
|
||||
requestFocus();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Always normalize navigation/select buttons for further handlers
|
||||
|
||||
+41
-17
@@ -30,6 +30,7 @@
|
||||
// - 与屏幕共用 I²C 总线 (SDA=0, SCL=1)
|
||||
// - A0=0, A1=0, A2=0 → 地址 0x20 (TCA9535_I2C_ADDR)
|
||||
// - P0.0~P0.3 行输出(ROW0~ROW3),P0.4~P0.7 列输入(COL0~COL3)
|
||||
// - P1.0 键盘背光输出(高电平点亮,按键时亮 5 秒后自动熄灭)
|
||||
// - P1.2 电源使能(POWER_EN),高电平有效,驱动 MOS 管维持供电
|
||||
// - P1.3 电源开机按钮(POWER_BOOT),输入,低电平有效
|
||||
// 开机:持续按住 2 秒 → POWER_EN 拉高维持供电
|
||||
@@ -37,35 +38,58 @@
|
||||
// - P1.4 LoRa RST 输出(通过 I²C 控制 RadioLib 复位序列)
|
||||
// - P1.6 GPS_RST 输出(通过 tca9535GpsReset() 控制,init 中释放)
|
||||
// - P1.7 GPS_EN 输出(高电平有效,通过 enablePin 桥接到 TCA9535)
|
||||
// - P1.1 CHARGE_DET 输入(高电平=正在充电)
|
||||
// - 中断引脚 GPIO5,低电平有效,下降沿触发
|
||||
// -----------------------------------------------------------------------
|
||||
#define HAS_TCA9535_BUTTON
|
||||
#define TCA9535_INT_PIN 5 // TCA9535 INT → GPIO5(低电平有效,下降沿触发)
|
||||
#define TCA9535_POWER_EN_BIT (1u << 2) // P1.2 = 电源使能(高电平=开机)
|
||||
#define TCA9535_CHARGE_DET_PIN (1u << 1) // P1.1 = 充电检测输入(高电平=正在充电)
|
||||
|
||||
// 按键映射:4×4 矩阵,行优先排列
|
||||
// KEY[0]=ROW0·COL0, KEY[1]=ROW0·COL1, ..., KEY[15]=ROW3·COL3
|
||||
// 低电平有效(按下接地,列读取到低电平=按下)
|
||||
// 九宫格:key0~2=1~3, key4~6=4~6, key8~10=7~9, key12=*, key13=0, key14=#
|
||||
// 方向键:key3=UP, key7=DOWN, key11=LEFT, key15=RIGHT
|
||||
// SELECT 由 GPIO9 短按触发,CANCEL 由 POWER_BOOT(P1.3) 短按触发
|
||||
// 矩阵仅保留方向键
|
||||
#define TCA9535_KEY_MAP \
|
||||
{ \
|
||||
INPUT_BROKER_NONE, /* key0 = ROW0·COL0 */ \
|
||||
INPUT_BROKER_NONE, /* key1 = ROW0·COL1 */ \
|
||||
INPUT_BROKER_NONE, /* key2 = ROW0·COL2 */ \
|
||||
INPUT_BROKER_UP, /* key3 = ROW0·COL3 */ \
|
||||
INPUT_BROKER_NONE, /* key4 = ROW1·COL0 */ \
|
||||
INPUT_BROKER_NONE, /* key5 = ROW1·COL1 */ \
|
||||
INPUT_BROKER_NONE, /* key6 = ROW1·COL2 */ \
|
||||
INPUT_BROKER_DOWN, /* key7 = ROW1·COL3 */ \
|
||||
INPUT_BROKER_NONE, /* key8 = ROW2·COL0 */ \
|
||||
INPUT_BROKER_NONE, /* key9 = ROW2·COL1 */ \
|
||||
INPUT_BROKER_NONE, /* key10 = ROW2·COL2 */ \
|
||||
INPUT_BROKER_LEFT, /* key11 = ROW2·COL3 */ \
|
||||
INPUT_BROKER_NONE, /* key12 = ROW3·COL0 */ \
|
||||
INPUT_BROKER_NONE, /* key13 = ROW3·COL1 */ \
|
||||
INPUT_BROKER_NONE, /* key14 = ROW3·COL2 */ \
|
||||
INPUT_BROKER_RIGHT, /* key15 = ROW3·COL3 */ \
|
||||
INPUT_BROKER_MATRIXKEY, /* key0 = ROW0·COL0 → '1' */ \
|
||||
INPUT_BROKER_MATRIXKEY, /* key1 = ROW0·COL1 → '2' */ \
|
||||
INPUT_BROKER_MATRIXKEY, /* key2 = ROW0·COL2 → '3' */ \
|
||||
INPUT_BROKER_UP, /* key3 = ROW0·COL3 */ \
|
||||
INPUT_BROKER_MATRIXKEY, /* key4 = ROW1·COL0 → '4' */ \
|
||||
INPUT_BROKER_MATRIXKEY, /* key5 = ROW1·COL1 → '5' */ \
|
||||
INPUT_BROKER_MATRIXKEY, /* key6 = ROW1·COL2 → '6' */ \
|
||||
INPUT_BROKER_DOWN, /* key7 = ROW1·COL3 */ \
|
||||
INPUT_BROKER_MATRIXKEY, /* key8 = ROW2·COL0 → '7' */ \
|
||||
INPUT_BROKER_MATRIXKEY, /* key9 = ROW2·COL1 → '8' */ \
|
||||
INPUT_BROKER_MATRIXKEY, /* key10 = ROW2·COL2 → '9' */ \
|
||||
INPUT_BROKER_LEFT, /* key11 = ROW2·COL3 */ \
|
||||
INPUT_BROKER_MATRIXKEY, /* key12 = ROW3·COL0 → '*' */ \
|
||||
INPUT_BROKER_MATRIXKEY, /* key13 = ROW3·COL1 → '0' */ \
|
||||
INPUT_BROKER_MATRIXKEY, /* key14 = ROW3·COL2 → '#' */ \
|
||||
INPUT_BROKER_RIGHT, /* key15 = ROW3·COL3 */ \
|
||||
}
|
||||
|
||||
#define TCA9535_KEY_CHAR_MAP \
|
||||
{ \
|
||||
'1', /* key0 */ \
|
||||
'2', /* key1 */ \
|
||||
'3', /* key2 */ \
|
||||
0, /* key3 → 方向键 */ \
|
||||
'4', /* key4 */ \
|
||||
'5', /* key5 */ \
|
||||
'6', /* key6 */ \
|
||||
0, /* key7 → 方向键 */ \
|
||||
'7', /* key8 */ \
|
||||
'8', /* key9 */ \
|
||||
'9', /* key10 */ \
|
||||
0, /* key11 → 方向键 */ \
|
||||
'*', /* key12 */ \
|
||||
'0', /* key13 */ \
|
||||
'#', /* key14 */ \
|
||||
0, /* key15 → 方向键 */ \
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user