固件与软件架构规划
| 项目 | 内容 |
|---|---|
| 文档版本 | 0.1 |
| 状态 | ESP-IDF、C++17、LVGL已确认;模块接口为规划稿 |
| 更新日期 | 2026-08-28 |
1. 技术基线
- 原生ESP-IDF,正式启动时锁定经过验证的具体版本。
- C++17用于业务、状态和服务接口;芯片驱动保持C接口兼容。
- LVGL 9负责静态页面、列表、焦点和后续触摸适配;DisplayService拥有最终刷新权,卡面允许使用专用渲染路径。
- FreeRTOS使用ESP-IDF内置能力,不增加额外调度框架。
- FatFS管理MicroSD,NVS管理设置和对局状态,LittleFS保存系统资源和当前卡面缓存。
- Python负责资源转换、校验和打包。
- 16MB Flash从首版规划双OTA分区。
不建议以Arduino或PlatformIO作为正式工程基础。依赖版本必须固定,不能自动追随最新版。
2. 仓库规划
CardHelper/
├─ core/ 可移植C++17核心
│ ├─ game_core/
│ ├─ game_sanguosha/
│ ├─ card_model/
│ ├─ resource_model/
│ └─ session_model/
├─ firmware/
│ ├─ main/
│ └─ components/
│ ├─ board/
│ ├─ display/
│ ├─ display_st7305/
│ ├─ input/
│ ├─ storage/
│ ├─ persistence/
│ ├─ rtc/
│ ├─ power/
│ ├─ usb/
│ ├─ wireless/
│ ├─ ui/
│ ├─ diagnostics/
│ └─ updater/
├─ simulator/
├─ tools/cardpack/
├─ schemas/
├─ sample-data/
├─ tests/
├─ hardware/
└─ docs/
core不得引用ESP-IDF、FreeRTOS或LVGL头文件,使游戏规则和状态恢复能够在电脑端直接测试。
3. 分层和依赖
产品应用层
↓
游戏领域层
↓
产品服务层
↓
UI与显示层 / 硬件适配层
依赖只能从上层指向公开接口。GPIO只能出现在Board层,ST7305命令只能出现在对应驱动,三国杀规则只能出现在游戏适配器,UI不能直接写NVS或MicroSD。
4. 单向数据流
建议采用Command/Event模型:
五键、系统或后台结果
↓
Command
↓
ApplicationController
↓
GameSession
↓
StateChanged Event
├─ UI刷新
├─ 延迟保存
└─ 日志记录
ApplicationController唯一拥有并修改当前对局。UI、存储和无线任务不能直接修改GameSession。
典型Command包括StartGame、ResumeGame、SelectCard、AdjustHealth、AdjustShield、AdjustToken、UndoLastChange、LockGame、EndGame和EnterSleep。
典型Event包括GameStarted、CardChanged、HealthChanged、SessionSaved、BatteryLow、SdRemoved、DisplayTimeout和OperationRejected。
5. 任务模型
Rev.A保持有限任务数量:
- UI与应用任务:LVGL、页面、应用控制器和按键意图。
- 后台工作任务:SD读取、索引、图片和资源校验。
- 无线任务:Wi-Fi或BLE开启时运行。
- 系统定时服务:RTC、电量、自动锁定、延迟保存和睡眠判断。
耗时任务通过消息队列返回结果。UI不得等待大文件读取、屏幕BUSY或网络连接。
建议初始队列容量:应用事件32项、输入16项、后台工作8项;显示刷新请求合并。游戏操作、SD拔出、低电和更新结果不得静默丢弃。
6. 组件职责
Board
管理Rev.A引脚、总线、电源控制、硬件版本、唤醒原因和开机按键组合。
DisplayDevice
管理初始化、睡眠、唤醒、能力查询、区域提交、BUSY等待和错误恢复。
DisplayService
管理LVGL、卡面合成、状态层、脏区域合并和刷新策略。业务不直接调用ST7305。
Input
输出KeyDown、KeyUp、ShortPress、LongPress、VeryLongPress和Repeat事件,不自行切页或修改体力。
StorageService
只理解挂载、文件、目录、流和校验,不理解武将规则。
ResourceRepository
理解Catalog、卡牌包、卡池、武将和显示资源,不向UI暴露SD路径。
Persistence
管理设置、A/B对局快照、延迟保存、恢复和版本迁移。
GameCore
管理游戏会话、体力、护盾、Token、撤销、开始和结束。
SanguoshaAdapter
管理三国杀模式、身份、基础体力解释和主公/地主修正。
Diagnostics
提供屏幕、按键、SD、RTC、电池、USB、无线、版本和错误记录诊断。
7. 显示抽象
业务通过产品Display接口使用宽高、颜色能力、局刷能力、刷新边界和刷新成本。ST7305与未来六色屏分别实现接口。
显示链路:
LVGL页面和卡面组件
↓
画面合成层
↓
刷新调度器
↓
具体屏幕驱动
LVGL禁止动画、惯性滚动、高帧率效果和依赖灰色的禁用态。刷新调度器负责合并脏区域、局刷/全刷选择、BUSY等待、超时和重试。阶段0必须规划1-bit、外部字体、局部Flush和未来六色刷新模型的最小验证;若不适配,LVGL仅保留控制页面。
8. 游戏插件边界
建议SD卡只承载内容插件:卡牌图片、属性、卡池、布局和声明式数据。任意机器代码不得从SD加载。
游戏适配器编译进固件并注册到静态Registry。第一阶段由内置SanguoshaAdapter解释SD上的三国杀内容包。未来开放SDK时,适配器仍应经过编译、测试和签名后随固件发布。
9. 对局模型
GameSession至少包含:
- schemaVersion、sessionId、revision和状态。
- gameId、packId、cardId、poolId、modeId和roleId。
- baseHp、maxHp、currentHp和currentShield。
- Token的ID、名称、数值和范围。
- startedAt、lastUpdatedAt和elapsedSeconds。
- locked和最近一次UndoRecord。
生命周期为None、Preparing、Active、Ending和Ended。锁定是Active中的标志,不是独立生命周期。
Rev.A数值上限建议为体力0~99、体力上限1~99、护盾0~99、Token 0~99。资源可以声明更小范围。
10. 异步请求
资源请求携带RequestId和发起时的页面/状态版本。用户快速切换武将时,较早结果即使最后返回也必须丢弃,不能覆盖新选择。
大图片不复制进事件队列,只传受控缓冲区句柄。当前、前一张和后一张可形成有限缓存,不一次加载整个卡牌包。
11. 时间和持久化
ClockService同时提供RTC时间和单调时间。RTC用于日期,单调时间用于按键长按、撤销窗口和持续时长;RTC无效不能影响本次对局。
对局不能直接序列化C++内存结构。使用版本化JSON或CBOR载荷,外层包含Magic、Schema、序号、长度和CRC,并写入A/B快照槽。
12. 错误模型
统一错误包含domain、code、severity、retryable、userMessageId和technicalContext。错误域包括System、Display、Storage、Resource、Game、Persistence、RTC、Power、USB、Wireless和Update。
底层返回稳定错误码,UI根据userMessageId显示中文提示;驱动不得直接返回面向用户的文案。
13. 内存边界
内部SRAM优先存放任务栈、中断数据、DMA缓冲、核心状态和高频对象。PSRAM存放帧缓冲、图片、LVGL大缓冲、索引和JSON解析内存。MicroSD存放卡牌资源。
大缓冲尽量在启动时分配;运行中限制动态分配;DMA缓冲必须位于兼容内存。MVP按500张验收、1,000张压力测试;更大规模需要重新评估JSON解析、字体和缓存。
14. 构建配置
- rev_a_debug:完整日志、断言、诊断和性能统计。
- rev_a_release:保留错误日志,关闭高频调试输出。
- factory_test:自动进行硬件测试,不进入正常游戏。
- simulator:电脑端300×400屏幕、键盘五键、本地目录模拟SD和文件模拟NVS。
15. Flash分区方向
16MB Flash预留NVS、OTA元数据、Firmware A、Firmware B、LittleFS、Coredump和少量余量。A/B固件各按约4MB级空间规划,具体地址在第一次真实构建后依据固件体积冻结。
16. 开发规则
- 不使用大量全局单例,依赖在启动入口统一组装。
- 每个可变状态或硬件资源只有一个任务所有者。
- 所有硬件操作有超时。
- 所有外部数据有尺寸、版本和路径校验。
- 无SD、无网和RTC无效属于正常可处理状态。
- 每个公开接口同时定义成功、失败和取消行为。
- 每个设计决策通过decisions.md记录状态。