更新一下文档
This commit is contained in:
@@ -3,10 +3,10 @@ description: Build the STM32F4-Base Keil MDK project. Use ONLY when the user ask
|
||||
agent: build
|
||||
---
|
||||
|
||||
Run the Keil MDK-ARM build by executing the build script with the bash tool. Use the absolute path, NOT relative cd:
|
||||
Run the Keil MDK-ARM build by executing the build script with the bash tool. The repo root is the working directory; use the repo-relative path (the old absolute path `D:\Code\DTU 程序\...` is stale):
|
||||
|
||||
```powershell
|
||||
& "D:\Code\DTU 程序\STM32F4-Base\MDK-ARM\build.bat"
|
||||
& "MDK-ARM\build.bat"
|
||||
```
|
||||
|
||||
Then read the tail of `MDK-ARM/build_log.txt` and report the result.
|
||||
|
||||
@@ -3,10 +3,10 @@ description: Download (flash) the program to the STM32 via Keil MDK. Use ONLY wh
|
||||
agent: build
|
||||
---
|
||||
|
||||
Run the Keil MDK flash download by executing the download script with the bash tool. Use the absolute path, NOT relative cd:
|
||||
Run the Keil MDK flash download by executing the download script with the bash tool. The repo root is the working directory; use the repo-relative path (the old absolute path `D:\Code\DTU 程序\...` is stale):
|
||||
|
||||
```powershell
|
||||
& "D:\Code\DTU 程序\STM32F4-Base\MDK-ARM\download.bat"
|
||||
& "MDK-ARM\download.bat"
|
||||
```
|
||||
|
||||
Then read the tail of `MDK-ARM/download_log.txt` and report the result.
|
||||
|
||||
@@ -24,10 +24,10 @@ Read `.opencode/serial_config.json` for the authoritative `portName` (COM6) and
|
||||
|
||||
### Step 1 — Build verify
|
||||
|
||||
Run the build script (absolute path):
|
||||
Run the build script (repo-relative; the repo root is the working directory):
|
||||
|
||||
```powershell
|
||||
& "D:\Code\DTU 程序\STM32F4-Base\MDK-ARM\build.bat"
|
||||
& "MDK-ARM\build.bat"
|
||||
```
|
||||
|
||||
Interpretation (per AGENTS.md): exit code 0 = PASS (0 errors, 0 warnings). 1/2+ = FAIL. If it fails, read `MDK-ARM/build_log.txt`, fix, and rerun `@build` until exit code 0 **before** touching the hardware.
|
||||
@@ -39,10 +39,10 @@ then flashes, then collects and verifies — all in one call so the capture neve
|
||||
times out before flashing:
|
||||
|
||||
```powershell
|
||||
powershell -ExecutionPolicy Bypass -File "D:\Code\DTU 程序\STM32F4-Base\test\run_boot_test.ps1" `
|
||||
powershell -ExecutionPolicy Bypass -File "test\run_boot_test.ps1" `
|
||||
-Port COM6 -BaudRate 115200 `
|
||||
-DurationMs 30000 -InitialTimeoutMs 90000 -IdleTimeoutMs 8000 -MaxBytes 65536 `
|
||||
-FlashCmd "D:\Code\DTU 程序\STM32F4-Base\MDK-ARM\download.bat" `
|
||||
-FlashCmd "MDK-ARM\download.bat" `
|
||||
-Required "App init OK;Network Init;net init: OK" `
|
||||
-Forbidden "FAIL;ERROR;HardFault" `
|
||||
-OutDir "C:\Users\16241\AppData\Local\Temp\opencode"
|
||||
@@ -70,5 +70,5 @@ For interactive command/response (not boot-log capture):
|
||||
- **⚠️ 无历史缓冲**:serial-mcp-server 的 `read` 只能读调用后的字节。抓开机日志必须走 `run_boot_test.ps1`(先采集等 first-byte → 再烧录触发)。
|
||||
- **⚠️ EOF 陷阱**:COM 刚释放立即重开会报 `UnexpectedEof`(tokio-serial 读 0 字节)。`serial_capture.ps1` 内置自动重试;别裸跑 CLI capture。
|
||||
- **参数**:`IdleTimeoutMs` 需 ≥ boot 自然静默间隙(本机 ~3-4s,用 8000);`InitialTimeoutMs` 需覆盖编译+烧录耗时(用 90000)。
|
||||
- UI (LVGL) task `ui_task` 增加 `UI port init: OK` / `UI menu init: OK` 打印后,即可用 `-Required "UI port init: OK;UI menu init: OK"` 自动回归 UI 初始化。
|
||||
- UI (LVGL) task `ui_task` 已有 `UI port init: OK` / `UI menu init: OK` 打印,可用 `-Required "UI port init: OK;UI menu init: OK"` 自动回归 UI 初始化。
|
||||
- Full capture/analysis details: `docs/测试说明/serial-mcp-server_串口调试.md` and `docs/测试说明/自动化测试流程.md`.
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
## 构建
|
||||
|
||||
- **仅支持 Keil MDK-ARM v5 (ARMCC)**,无其他工具链
|
||||
- 命令行编译:`@build`(自动执行 `MDK-ARM/build.bat`)
|
||||
- 退出码:0 = 成功 | 1 = 有警告(不通过)| 2+ = 错误,查看 `MDK-ARM/build_log.txt`
|
||||
- 命令行编译:`@build`(执行 `MDK-ARM/build.bat`,从仓库根目录用**相对路径**调用)
|
||||
- `build.bat` 硬编码 Keil 路径 `C:\Keil_v5\UV4\UV4.exe`(本机已验证存在;换机器需改)
|
||||
- 退出码:0 = 0 错误 0 警告 | 1 = 有警告或错误(查看 `MDK-ARM/build_log.txt`)
|
||||
- ⚠️ **旧绝对路径陷阱**:仓库曾位于 `D:\Code\DTU 程序\STM32F4-Base`,`.opencode/command/*.md` 与 `test/run_boot_test.ps1` 中若出现该路径一律视为失效,改用仓库相对路径(如 `& "MDK-ARM\build.bat"`)
|
||||
|
||||
## 硬件
|
||||
|
||||
@@ -16,6 +18,7 @@
|
||||
| GD5F2GQ5UE NAND | SPI1 | PE0 CS, PB3 SCK, PB4 MISO, PB5 MOSI, PB8 WP, PE1 HOLD |
|
||||
| TPAFE5160 ADC | 并行16位 | PG0-PG15 数据, PD3 RD, PD4 CONVST, PD7 BUSY |
|
||||
| USART1 | 串口 | PA9 TX, PA10 RX (115200bps) |
|
||||
| RS-485 | UART5 | PC12 TX, PD2 RX, PD0 DE(半双工) |
|
||||
| 6x LED | GPIO | PC4, PC5, PB1, PB2, PF11, PF12(低电平点亮) |
|
||||
|
||||
MCU: STM32F407ZGTx @ 168MHz (HSE 8MHz → PLL M=4/N=168/P=2,见 `*.ioc`/`stm32f4xx_hal_conf.h`)
|
||||
@@ -23,22 +26,30 @@ MCU: STM32F407ZGTx @ 168MHz (HSE 8MHz → PLL M=4/N=168/P=2,见 `*.ioc`/`stm32
|
||||
## 架构
|
||||
|
||||
```
|
||||
Src/main.c → 外设初始化 → app_main_init → gd5f2gq5ue_init → tpafe5160_init
|
||||
→ osKernelInitialize → MX_FREERTOS_Init → osKernelStart
|
||||
→ [FreeRTOS tasks: defaultTask, netTask, ftpTask]
|
||||
Src/main.c → CubeMX 外设初始化 → app_main_init → osKernelStart
|
||||
→ [FreeRTOS tasks: defaultTask, netTask, ftpTask, testTask, uiTask, timeSyncTask]
|
||||
```
|
||||
|
||||
- **defaultTask**:FTL + FatFS 初始化测试(开机一次性)
|
||||
- **netTask**:`net_init()` → `net_poll()` + `net_process_messages()` 每 10ms
|
||||
- **ftpTask**:`lftpd_start("/", 21, &ftp)` 延迟 7s 后启动
|
||||
- **app_main_init 顺序**(时间子系统优先,使后续日志带时间戳):`sd2506_init` → `sys_clock_init`(墙钟基准)→ `rs485_init` → `gd5f2gq5ue_init` → `tpafe5160_init` → SD2506 诊断 → `tpafe5160_start_conv_irq`
|
||||
- **就绪屏障**(`App/sys_events.h` Event Flags,取代魔法延时/全局轮询):`SYS_EVT_NET_READY`(bit0, netTask 置位) / `SYS_EVT_STORAGE_READY`(bit1, defaultTask 置位) / `SYS_EVT_FTP_READY`(bit2) / `SYS_EVT_TIMESYNC_READY`(bit3);消费者用 `osEventFlagsWait` 阻塞等待
|
||||
- **defaultTask**:FatFS 挂载(`f_mount` forced,无卷自动 `f_mkfs` FAT32)→ 置 `SYS_EVT_STORAGE_READY` → LED 闪烁循环
|
||||
- **netTask**:延时 2s → `net_stack_bring_up()`(net_init + MAC + PHY 强制 100M 全双工 + 等链路 ≤3s)→ 置 `SYS_EVT_NET_READY` → 循环 `net_poll()` + `net_process_messages()` + `osDelay(1)`(tick=1ms)
|
||||
- **ftpTask**:`osEventFlagsWait(NET_READY | STORAGE_READY, WaitAll)` 后 `lftpd_start("/", 21, &ftp)`(已无 7s 魔法延时)
|
||||
- **testTask**:测试套件入口(`test/test_config.h` 选择;产品构建下空转)
|
||||
- **uiTask**:等 `SYS_EVT_NET_READY` 后 `ui_port_init` → `ui_menu_init` → 启动 UI net sink
|
||||
- **timeSyncTask**:TCP 墙钟时间同步(PC 推送)
|
||||
- 功能编译开关:`App/app_cfg.h`(`APP_NET_ENABLE` / `APP_ADC_ENABLE` / `APP_RS485_ENABLE`)
|
||||
|
||||
### 任务栈
|
||||
|
||||
| Task | 栈大小 |
|
||||
|---|---|
|
||||
| defaultTask | 2048 |
|
||||
| netTask | 4096 |
|
||||
| ftpTask | 4096 |
|
||||
| Task | 栈大小(words) | 优先级 |
|
||||
|---|---|---|
|
||||
| defaultTask | 2048 | Normal |
|
||||
| netTask | 4096 | Normal |
|
||||
| ftpTask | 4096 | Normal |
|
||||
| testTask | 4096 | BelowNormal |
|
||||
| uiTask | 16384 | BelowNormal |
|
||||
| timeSyncTask | 2048 | Normal |
|
||||
|
||||
## CH395F 设计原则
|
||||
|
||||
@@ -47,32 +58,45 @@ Src/main.c → 外设初始化 → app_main_init → gd5f2gq5ue_init → tpafe51
|
||||
|
||||
## NET 网络层
|
||||
|
||||
- `net_socket.h/c` — BSD Socket API,多连接模式下 Socket 0 监听、Socket 1~7 数据
|
||||
- `net_socket.h/c` — BSD Socket API;**单连接模式**(`ch395f_set_fun_para(0x09)`:多 Socket + TCP Server 单连接 + 手动关闭),连接停留在 Socket 0
|
||||
- `net_select.h/c` — select/poll I/O 多路复用
|
||||
- 线程安全通过消息队列(`netMsgQueue`)实现,所有 CH395F 操作在 netTask 中串行处理
|
||||
- 网络初始化顺序(多连接):`ch395f_set_fun_para(0x02)` → `ch395f_init()` → 配置 Socket 1~7(缓冲区+端口+协议,不 OPEN)→ 配置 Socket 0(TCP LISTEN)
|
||||
- `net_init` 顺序:check_exist → reset → `set_fun_para(0x09)` → MSS/重传/KeepAlive → IP → `ch395f_init()` → Socket 0~7 缓冲分配 → ping 使能
|
||||
- 静态 IP 192.168.1.100/24(gw 192.168.1.1),PHY 强制 100M 全双工
|
||||
- **新文件需手动添加到 Keil MDK 工程**才能编译
|
||||
|
||||
## 测试构建模式
|
||||
|
||||
- `test/test_config.h`:`TEST_SUITE_CH395F` / `TEST_SUITE_STORAGE` / `TEST_SUITE_GD5F` / `TEST_SUITE_RTC` **互斥**(一次构建只能启用一个,`#error`);全部注释 = 正常产品构建
|
||||
- CH395F 套件沿用 `test/ch395f_test.h` 阶段宏:硬件层 Phase 1~5 与 NET 层 Phase 6~11 **互斥**(`#error`);硬件层模式下 netTask 不创建,testTask 独占 CH395F
|
||||
- 各模块测试指南见 `docs/测试说明/`(CH395F / GD5F / SD2506 / FTP)
|
||||
|
||||
## 关键文件
|
||||
|
||||
| 路径 | 说明 |
|
||||
|---|---|
|
||||
| `Src/main.c` | 入口,CubeMX 生成 + USER CODE 区域 |
|
||||
| `Src/freertos.c` | FreeRTOS task 创建,CubeMX 生成 + USER CODE 区域 |
|
||||
| `App/app_main.c` | 应用初始化流程编排 |
|
||||
| `App/sys_events.h` | 系统就绪事件(Event Flags 就绪屏障) |
|
||||
| `App/app_cfg.h` | 功能模块编译开关 |
|
||||
| `App/task/net_task.c` | netTask 主循环(网络初始化 + 轮询) |
|
||||
| `Drivers/BSP/NET/` | BSD Socket API 网络抽象层 |
|
||||
| `test/test_config.h` | 测试套件选择(互斥开关) |
|
||||
| `Drivers/BSP/NET/` | BSD Socket API 网络抽象层 + lftpd |
|
||||
| `Drivers/BSP/CH395F/` | CH395F SPI 驱动 |
|
||||
| `Drivers/BSP/GD5F2GQ5UE/` | NAND Flash 驱动 + dhara FTL + FatFS diskio |
|
||||
| `Drivers/BSP/TPAFE5160/` | ADC 并行接口驱动 |
|
||||
| `Drivers/BSP/SD2506/` | RTC 驱动 |
|
||||
| `Drivers/BSP/RS485/` | RS-485 通信(UART5 + PD0 DE) |
|
||||
| `Drivers/BSP/UI/` | LVGL UI 组件(与 PC 模拟器共用源码) |
|
||||
|
||||
## 驱动关键点
|
||||
|
||||
详见 `docs/` 目录:
|
||||
- `CH395F_Trap_Records.md` — CH395F 已知陷阱
|
||||
- `GD5F2GQ5UE_Trap_Records.md` — GD5F2GQ5UE 已知陷阱
|
||||
- `CH395F_Test_Guide.md` — 网络测试说明
|
||||
- `docs/问题记录/CH395F_Trap_Records.md` — CH395F 已知陷阱
|
||||
- `docs/问题记录/GD5F2GQ5UE_Trap_Records.md` — GD5F2GQ5UE 已知陷阱
|
||||
- `docs/问题记录/SD2506_Trap_Records.md` — SD2506 RTC 已知陷阱
|
||||
- `docs/测试说明/CH395F_Test_Guide.md` — 网络测试说明(含构建模式与执行顺序)
|
||||
|
||||
## 串口调试(serial-mcp-server MCP/CLI)
|
||||
|
||||
@@ -85,20 +109,43 @@ Src/main.c → 外设初始化 → app_main_init → gd5f2gq5ue_init → tpafe51
|
||||
|
||||
改完代码后运行 **`@test`** 可自动执行完整闭环:**构建验证 → 串口捕获武装(serial-mcp-server 采集窗口)→ 烧录(`MDK-ARM/download.bat`,下载后 Reset & Run)→ 解析捕获 JSON → `test/verify_log.ps1` 标记校验 → PASS/FAIL 判定并迭代**。
|
||||
|
||||
- **推荐一键命令**:`test/run_boot_test.ps1` = 采集(含 EOF 重试)→ 烧录 → 提取日志 → verify 全流程,退出码 0=PASS/1=FAIL/2=错误
|
||||
- **推荐一键命令**:`test/run_boot_test.ps1` = 采集(含 EOF 重试)→ 烧录 → 提取日志 → verify 全流程,退出码 0=PASS / 1=FAIL / 2=采集或烧录错误 / 3=日志文件错误
|
||||
- 判定标准:`@test` 内部先 `@build`(0 错误 0 警告),再 `@download`(退出码 0),日志用 `verify_log.ps1` 校验(默认 Required:`App init OK`/`Network Init`/`net init: OK`;Forbidden:`FAIL`/`ERROR`/`HardFault`)
|
||||
- **采集必须在烧录前武装**(采集进程先打开 COM 等 first-byte,烧录触发复位后才抓得到开机日志);`run_boot_test.ps1` 的 `-FlashCmd` 分支已保证顺序
|
||||
- 每次按模块定制标记(网络/存储/RTC/ADC/RS485)见 `docs/测试说明/自动化测试流程.md`
|
||||
- UI(`App/task/ui_task.c`)当前无 DBG 打印,无法从串口自动验证;如需 UI 回归,先在固件加 `UI port init: OK` / `UI menu init: OK` 打印再纳入 Required
|
||||
- UI 已有 DBG 打印(`UI port init: OK` / `UI menu init: OK` / `UI net sink: started`),可用 `-Required "UI port init: OK;UI menu init: OK"` 自动回归 UI 初始化;屏幕内容仍需人工目检
|
||||
|
||||
## 代码规范与 pre-commit
|
||||
|
||||
- **提交被 pre-commit 钩子门禁**,任一失败即拦截:cppcheck 静态分析(warning/performance/portability/style)+ 代码规范机械检查 + U+FFFD 乱码检查 + gitlint commit message
|
||||
- 代码规范 V1.0(`docs/代码规范/嵌入式C语言代码规范(V1.0).md`):命名小写下划线(`g_`/`s_`/`p_`/`a_` 前缀)、头文件保护宏 `XXX_H`、常量在右、文件头模块注释块等;可机械校验项由 `scripts/check_coding_standard.py` 强制
|
||||
- 检查范围仅项目自有代码;第三方(`Lib/`、`Middlewares/`、CMSIS/HAL、`lftpd`、`Drivers/BSP/UI`、`test/`)被排除
|
||||
- 新机器启用:`pre-commit install`;全库扫描:`pre-commit run --all-files`
|
||||
- 详见 `docs/代码规范/pre-commit使用说明.md`、`docs/代码规范/cppcheck使用说明.md`
|
||||
|
||||
## LVGL PC 模拟器
|
||||
|
||||
`simulator/`(CMake + SDL2 2.32.10 预编译库)可在 PC 上开发调试 UI,**与 MCU 端共用 UI 源码**(`Drivers/BSP/UI/ui_menu.c`、`ui_font_cn_12.c`),无需烧录:
|
||||
|
||||
```powershell
|
||||
cd simulator
|
||||
cmake -B build -G "Visual Studio 16 2019" -A x64
|
||||
cmake --build build --config Debug
|
||||
# 运行 simulator\build\Debug\lvgl_sim.exe
|
||||
```
|
||||
|
||||
- 详见 `docs/UI 设计/LVGL模拟器部署与使用说明.md`
|
||||
- 中文字体:`ui_cn_chars.txt`(字符集)+ `tools/font_gen/` 生成 `Drivers/BSP/UI/ui_font_cn_12.c`,见 `docs/UI 设计/UI_中文字体生成与配置.md`
|
||||
|
||||
## 注意
|
||||
|
||||
- `Inc/` 和 `Src/` 中 CubeMX 生成的文件带有 `USER CODE BEGIN`/`END` 标记,自定义代码写在这些区域之间
|
||||
- `Drivers/BSP/` 下的驱动文件为纯手工代码,不受 CubeMX 保护
|
||||
- CH395F 每次 SPI 事务需用 `ch395f_spi_begin()` / `ch395f_spi_end()` 包裹
|
||||
- CH395F SPI PCB设计问题,现在分频系数必须为 8,速度相对较低
|
||||
- FreeRTOS V10.3.1 via CMSIS-RTOS V2,HAL 时基用 TIM7(非 SysTick)
|
||||
- CH395F SPI PCB 设计问题,分频系数必须为 8,速度相对较低
|
||||
- FreeRTOS V10.3.1 via CMSIS-RTOS V2,HAL 时基用 TIM7(非 SysTick),tick = 1ms
|
||||
- NVIC 优先级分组 4 位,外设中断优先级 ≥ 5(`configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY`)
|
||||
- **内存预算紧张**:任务栈目前普遍偏大(见 `待处理内容.md`),新增任务/缓冲/栈前必须先核算 RAM 余量
|
||||
|
||||
## 源码编辑与编码安全(铁律)
|
||||
|
||||
@@ -106,6 +153,6 @@ Src/main.c → 外设初始化 → app_main_init → gd5f2gq5ue_init → tpafe51
|
||||
|
||||
- **绝不用 `Get-Content` / `Set-Content` / `cmd` 文本重写含中文的源码文件**。这类"整文件重写"会按系统默认编码重新解码再写回,多字节中文必被 `U+FFFD` 替换且不可逆。
|
||||
- **改代码只用 Edit 工具**(精确子串替换,不重新编解码整个文件,安全);批量替换用 Edit 的 `replaceAll`。
|
||||
- 必须脚本级替换时,使用显式且一致的编码(如 Python `open(p, encoding="utf-8")` 同编码回写),并先确认工程编码(Keil 工程常用 GBK)——但**最稳的是别碰**,只用 Edit / git。
|
||||
- 必须脚本级替换时,使用显式且一致的编码(**本工程源码为 UTF-8 无 BOM**,如 Python `open(p, encoding="utf-8")` 同编码回写)——但**最稳的是别碰**,只用 Edit / git。
|
||||
- **发现乱码先别急着 `git checkout`**:`git checkout` 会丢弃所有未提交工作,使损失扩大。先评估能否用 Edit 仅改坏掉的那几行。
|
||||
- 验证是否真损坏用字节级检查(统计 `EF BF BD` 出现次数),不要依赖终端/Read 渲染判断。
|
||||
|
||||
@@ -43,7 +43,7 @@ python tools/font_gen/extract_cn.py
|
||||
|
||||
```
|
||||
scanned 9 files; unique non-ASCII chars: 83
|
||||
-> D:\Code\DTU 程序\STM32F4-Base\ui_cn_chars.txt
|
||||
-> D:\Code\DTU Code\STM32F4-Base\ui_cn_chars.txt
|
||||
```
|
||||
|
||||
产物 `ui_cn_chars.txt` 为本工程实际显示的汉字清单(当前 83 字),可留作字符集记录。
|
||||
@@ -1,199 +0,0 @@
|
||||
# UI 图形界面实施规划(160×160 单色 · 树形菜单 · 离屏渲染 + 网络推流)
|
||||
|
||||
> 本文档用于在我(实现方)与你(需求方)之间对齐"如何规划与实施 UI"。
|
||||
> 当前阶段:**需求澄清 + 方案设计**,尚未写任何 UI 源码。
|
||||
> ✅ = 已根据你的最近答复冻结;【待确认】= 仍需你拍板;**(建议)** = 我的默认推荐。
|
||||
|
||||
---
|
||||
|
||||
## 1. 背景与现状
|
||||
|
||||
| 项 | 现状 |
|
||||
|---|---|
|
||||
| MCU | STM32F407ZGTx,168MHz,192KB RAM,1MB Flash |
|
||||
| 工具链 | 仅 Keil MDK-ARM v5 (ARMCC) |
|
||||
| OS | FreeRTOS (CMSIS-RTOS V2),已有 `defaultTask/netTask/ftpTask` |
|
||||
| 网络 | CH395F 以太网(SPI2),单连接 socket 模型(Socket0 复用监听/数据) |
|
||||
| 显示 | **无真实屏**:UI 在内存中离屏渲染,按请求经网络返回帧 |
|
||||
| 目标屏规格 | ✅ **160×160 像素,单色(1bpp,黑白)**,"少占用 RAM" |
|
||||
| 参考项目 | `D:\Code\DTU 程序\DTU-HMI`(自建 MVP 菜单,过于复杂,改用成熟方案复现菜单) |
|
||||
| 参考协议 | ✅ **RemoDispBus**(`DTU-HMI/src/remoteDisplay.c`,帧格式见第 3.4 节) |
|
||||
| 源码规范 | 中文注释;新增 .c/.h 需手动加入 Keil 工程;禁止整文件文本重写 |
|
||||
|
||||
### 关键推论
|
||||
- 我们目标 **1bpp**:整帧 = 160×160÷8 = **3200 字节(3.1KB)**,直接整帧作绘制缓冲。git
|
||||
- 参考项目 `key.h` 已有 `g_tRemoteKey`(`bUseRkey` 远程按键)→ ✅ 菜单导航由远端发键值驱动(双向交互已定)。
|
||||
- 参考项目 `def.h`:✅ 中文 12px、行高 14px、可视约 11 行。
|
||||
|
||||
---
|
||||
|
||||
## 2. 复现目标(已明确)
|
||||
|
||||
1. MCU 内存中渲染 160×160、1bpp 画面。
|
||||
2. ✅ **菜单驱动、多页面可切换**:复现 DTU-HMI 树形菜单(第 3.3 节),菜单进入不同页。
|
||||
3. ✅ **不含折线/柱状图**:内容页以文字 + 数值列表 + 简单指示为主;YC(遥测)页数值列表,无曲线。
|
||||
4. ✅ **A 复现范围**:仅复现**菜单树结构与导航**;所有内容页先**占位**(含装置信息/遥测也先占位)。
|
||||
5. ✅ **双向交互(pull 模型)**:远端按 RemoDispBus 发 `CMD_LCDMEM` 拉帧、`CMD_KEY` 下发键;MCU 渲染后应答帧。
|
||||
6. 全程 RAM 占用尽量低,不挤占现有 netTask/ftp/FatFs/dhara。
|
||||
|
||||
---
|
||||
|
||||
## 3. 参考项目与协议
|
||||
|
||||
### 3.1 输入键码(来自 `DTU-HMI/src/Drv/key.h`)
|
||||
| 键 | 键值 | LVGL 映射 |
|
||||
|---|---|---|
|
||||
| 上 KEY_U | 0x02 | LV_KEY_UP |
|
||||
| 下 KEY_D | 0x40 | LV_KEY_DOWN |
|
||||
| 左 KEY_L | 0x10 | LV_KEY_LEFT |
|
||||
| 右 KEY_R | 0x08 | LV_KEY_RIGHT |
|
||||
| 确认 KEY_ENT | 0x20 | LV_KEY_ENTER |
|
||||
| 返回 KEY_ESC | 0x01 | LV_KEY_ESC |
|
||||
|
||||
### 3.2 视觉规格(来自 `def.h`)
|
||||
- ✅ 160×160,1bpp;中文 12px,行高 14px,可视约 11 行。
|
||||
- ✅ 菜单视觉:采用 LVGL `lv_menu` 整屏列表 + 返回栈(小屏最清晰),不复原级联框。
|
||||
|
||||
### 3.3 菜单树(取自 `model.c` 的 `menuTab[]`,仅复现结构与导航)
|
||||
```
|
||||
[0] 主界面 (root)
|
||||
├[1] 装置信息 → 占位页
|
||||
├[1] 实时数据
|
||||
│ ├[2] 交流量 / [2] 直流量 / [2] 遥信量 → 占位页(YC类)
|
||||
├[1] 参数定值
|
||||
│ ├[2] 整定 → [3]参数/定值/控制字/软压板 → 占位
|
||||
│ └[2] 查看 → [3]参数/定值/控制字/软压板 → 占位
|
||||
├[1] 三遥设置 → [2]遥测死区/系数/遥信类型/防抖/双点遥信 → 占位
|
||||
├[1] 装置维护 → [2]时钟设置/强制复归/手动录波/清除记录 → 占位
|
||||
├[1] 通讯参数 → [2]通讯设置/网口设置/SNTP设置 → 占位
|
||||
└[1] 记录查询 → [2]SOE/事故/操作/保护告警/保护启动/遥控/自检/运行记录/运行报告 → 占位
|
||||
[0] 厂家设置 (root, 密码 CN_COP_PWD)
|
||||
├[1] 元件配置 / [1]恢复默认([2]全部/默认参数/定值/软压板/元件配置) / [1]交流显示
|
||||
├[1] 装置调试 → [2]虚拟遥信/交流虚遥测/直流虚遥测/电度虚遥测/动作虚事件/告警虚事件/动作出口/信号出口 → 占位
|
||||
└[1] 版本信息 → 占位
|
||||
```
|
||||
> 叶子(含"装置信息""实时数据"等)统一进入**占位页**(显示菜单路径 + "功能未实现"),仅验证导航与返回栈。
|
||||
|
||||
### 3.4 RemoDispBus 协议(✅ E 与参考项目一致,来自 `remoteDisplay.c`)
|
||||
|
||||
**帧格式(请求/应答通用)**:`[TAG][cmd][len_hi][len_lo][data...][crc]`
|
||||
- `TAG`:客户端→设备 `0xAA`;设备→客户端 `0xBB`
|
||||
- `cmd`:`0` KEEPLIVE,`1` INIT,`2` KEY,`3` LCDMEM
|
||||
- `len`:data 区长度,**大端 16 位**
|
||||
- `crc`:data 区逐字节**异或**取低 8 位
|
||||
- 整帧长度 = `4 + data_len + 1`
|
||||
|
||||
**命令语义**
|
||||
| cmd | 方向 | data | 说明 |
|
||||
|---|---|---|---|
|
||||
| INIT(1) | 设备应答 | `[W_hi,W_lo,H_hi,H_lo, memsize(4B)]` = 8B | 回报分辨率与显存大小 |
|
||||
| LCDMEM(3) | 客户端请求 | `4B 大端起始偏移` | 请求从 offset 开始的显存 |
|
||||
| LCDMEM(3) | 设备应答 | `[4B 偏移][显存数据]` | 回当前帧缓冲内容 |
|
||||
| KEY(2) | 客户端请求 | `[键值]`(见 3.1) | 设备写入按键队列驱动导航 |
|
||||
| KEEPLIVE(0) | 双向 | 空 | 保活 ACK |
|
||||
|
||||
**我们的落地适配**
|
||||
- ✅ **传输层完全照搬** RemoDispBus(TAG/cmd/len/crc、四命令)。
|
||||
- ✅ **1bpp 内容**:帧缓冲 3200 字节。INIT 回报 `memsize=3200`;远端客户端据 `bpp = memsize*8/(W*H)` 自行推导为 1bpp(=1),无需改协议即可兼容。
|
||||
- ✅ **pull 模型**:设备持续用 `lv_timer_handler()` 刷新帧缓冲;收到 `CMD_LCDMEM` 即回当前 3200 字节(通常 offset=0 全帧)。
|
||||
- ⚠️ **位序约定**:1bpp 行优先、每字节 **MSB-first**(pixel(x,y) 存于 `byte(y*W/8 + x/8)` 的 bit `7-(x&7)`)。LVGL `LV_COLOR_FORMAT_L1` 的位序若与此不同,在 `ui_net_sink` 发送前做位重排(实现期用一块临时缓冲转换,或在 `lv_conf` 选匹配格式)。
|
||||
- ⚠️ **与旧客户端兼容说明**:参考项目旧 `remo_disp_server.py` 期望 8bpp(25600B)。若你要求**字节级完全兼容旧客户端**(不改客户端),则需改为 8bpp(25600B)——但会多占 ~22KB RAM,与"少占用 RAM"冲突。默认按 1bpp(3200B) 实施;如需兼容旧客户端请告知(见第 8 节备注)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 总体架构方案(建议)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ 应用数据层 (现有: ADC/RTC/网络/NAND/LED,本阶段仅占位不接) │
|
||||
└───────────────┬──────────────────────────────────────────┘
|
||||
│ (本阶段不接数据;仅菜单导航)
|
||||
┌───────────────▼──────────────────────────────────────────┐
|
||||
│ UI 引擎 (LVGL v9, LV_COLOR_DEPTH=1, 关动画) │
|
||||
│ - lv_menu 主菜单(复现 3.3 树) + 占位内容页 │
|
||||
│ - lv_indev(KEYPAD) 读远端键队列 → 导航/切换 │
|
||||
│ - lv_timer_handler() 周期调用 → 渲染到 3200B 帧缓冲 │
|
||||
└───────────────┬──────────────────────────────────────────┘
|
||||
│ flush_cb → 写入 fbuf[3200] (CCM RAM)
|
||||
┌───────────────▼──────────────────────────────────────────┐
|
||||
│ 推流/应答适配层 (Drivers/BSP/UI/ui_net_sink.c) │
|
||||
│ - 解析 RemoDispBus 请求(INIT/LCDMEM/KEY/KEEPLIVE) │
|
||||
│ - LCDMEM → 回当前 fbuf;KEY → 入键队列;INIT → 报 3200 │
|
||||
└───────────────┬──────────────────────────────────────────┘
|
||||
│ net_socket_send() / recv()
|
||||
┌───────────────▼──────────────────────────────────────────┐
|
||||
│ 网络层 (CH395F,✅ 新开专用 TCP 监听 socket,端口 7070) │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 模块落点(贴合现有 BSP 风格)
|
||||
| 新增文件 | 职责 |
|
||||
|---|---|
|
||||
| `Lib/lvgl/` | LVGL v9 源码(放入后**手动加进 Keil 工程**) |
|
||||
| `Drivers/BSP/UI/lv_conf.h` | 1bpp、关动画/日志、字号、键盘 indev 使能 |
|
||||
| `Drivers/BSP/UI/ui_port.c/.h` | `lv_display` + `flush_cb`(写 fbuf);`lv_indev` 读键队列 |
|
||||
| `Drivers/BSP/UI/ui_menu.c/.h` | 复现 3.3 菜单树(lv_menu 构建) |
|
||||
| `Drivers/BSP/UI/ui_page_placeholder.c` | 占位内容页(显示路径+"未实现") |
|
||||
| `Drivers/BSP/UI/ui_net_sink.c/.h` | RemoDispBus 解析/应答(INIT/LCDMEM/KEY/KEEPLIVE) |
|
||||
| `App/task/ui_task.c/.h` | 独立 FreeRTOS 任务:跑 `lv_timer_handler` + 网络收发 |
|
||||
|
||||
> **不要**把 UI 渲染塞进 `netTask` 的 10ms 轮询——渲染 CPU 密集。新建 `ui_task`(栈 1024–1536,1bpp 下很轻),优先级低于 `netTask`。网络收发走现有 `net_socket` 串行消息队列。
|
||||
|
||||
---
|
||||
|
||||
## 5. 需讨论条件 — 当前状态
|
||||
|
||||
| 项 | 状态 | 结论 |
|
||||
|---|---|---|
|
||||
| A 复现范围 | ✅ 已定 | 仅菜单树+导航;所有内容页占位 |
|
||||
| B 菜单视觉 | ✅ 已定 | lv_menu 整屏列表+返回栈 |
|
||||
| C socket 策略 | ✅ 已定 | 新开专用 TCP(端口 7070) |
|
||||
| D 刷新/动画 | ✅ 已定 | pull 模型(按 LCDMEM 应答);关过渡动画 |
|
||||
| E 帧编码 | ✅ 已定 | RemoDispBus(见 3.4);1bpp 内容,memsize=3200 |
|
||||
| F 单色定义 | ✅ 默认 | 纯 1bpp,无灰阶抖动 |
|
||||
| G 字体/中文 | ✅ 默认 | 中文 12px;仅收录菜单/占位用到的字(fontconverter 造 1bpp 字库) |
|
||||
| H 旋转/扫描 | ✅ 已定 | 默认不旋转,行优先 |
|
||||
| I RAM/Flash | ✅ 按建议 | 帧缓冲+draw buffer 放 **CCM RAM**;集成时实测空闲余量 |
|
||||
| J 库选型 | ✅ 默认 | LVGL v9(LV_COLOR_DEPTH=1) |
|
||||
| K 数据通道 | ⏸ 本期不涉及 | 占位阶段业务数据不接 UI;后续事件/共享结构体 |
|
||||
| L 持久化 | ✅ 默认 | 不需;每次上电回根菜单 |
|
||||
| M 异常 | ✅ 建议 | 客户端掉线暂停应答,不影响主业务;ui_task 软看门狗;键队列溢出丢 oldest |
|
||||
|
||||
---
|
||||
|
||||
## 6. 实施阶段分解(建议顺序)
|
||||
|
||||
1. **阶段 0 · 决策冻结**:本文件第 5 节已冻结(仅 K/L/M 默认)。
|
||||
2. **阶段 1 · 骨架接入**:导入 LVGL v9 到 Keil;`lv_conf.h`(1bpp/关动画/字号);`ui_task` 渲染测试图;帧缓冲在 CCM。
|
||||
3. **阶段 2 · RemoDispBus 应答**:`ui_net_sink` 实现 INIT/LCDMEM/KEY/KEEPLIVE;新开 CH395F TCP(7070);远端按协议拉到 3200B 帧。
|
||||
4. **阶段 3 · 菜单复现**:`lv_menu` 复现 3.3 树(中文 12px 字库);远端 `CMD_KEY` 驱动上下/确认/返回导航。
|
||||
5. **阶段 4 · 占位页**:叶子进占位页(显示路径+"未实现"),验证返回栈与多级导航。
|
||||
6. **阶段 5 · 健壮性**:掉线恢复、软看门狗、RAM 余量复测、`@build` 0 警告验证。
|
||||
7. (后续)阶段 6 · 真实数据联动:按需把 ADC/RTC/网络/NAND/LED 接入 AppInfo/YC 等页。
|
||||
|
||||
---
|
||||
|
||||
## 7. 风险与约束
|
||||
|
||||
- **RAM 最紧**:3200B 帧缓冲 + LVGL 运行时(约 4–8KB)+ draw buffer(整帧即可,同 3200B 或更小)放 CCM;集成时实测 `netTask/ftp/FatFs/dhara` 后空闲堆/栈。
|
||||
- **ARMCC 兼容**:LVGL 纯 C99 可编译;`lv_conf.h` 关浮点/动画等单色屏用不到特性。
|
||||
- **不阻塞 netTask**:UI 渲染/收发在独立任务,走 `net_socket` 串行队列。
|
||||
- **位序对齐**:发送前确保 1bpp 位序(MSB-first 行优先)与客户端一致(见 3.4 ⚠️)。
|
||||
- **编码安全**:含中文,仅用 Edit/Write 工具改,绝不用 PowerShell 文本重写。
|
||||
|
||||
---
|
||||
|
||||
## 8. 决策待办清单
|
||||
|
||||
- [x] A 复现范围(仅菜单树+导航,其余占位)
|
||||
- [x] B 菜单视觉(lv_menu 整屏列表)
|
||||
- [x] C UI 通道(新开专用 TCP 7070)
|
||||
- [x] D 刷新/动画(pull 应答,关动画)
|
||||
- [x] E 帧编码(RemoDispBus,1bpp memsize=3200)
|
||||
- [x] F 单色(纯 1bpp,无抖动)
|
||||
- [x] G 字号(中文 12px)
|
||||
- [x] H 朝向(不旋转)
|
||||
- [x] I RAM(CCM 帧缓冲,集成实测)
|
||||
- [x] J 库(LVGL v9 1bpp)
|
||||
- [x] L 持久化(不需)
|
||||
- [x] M 异常(掉线暂停+软看门狗)
|
||||
@@ -53,11 +53,11 @@ serial-mcp-server read --port COM6 --baud 115200 \
|
||||
**顺序必须:先启动采集(打开 COM 等 first-byte)→ 再烧录触发复位 → 采集自动抓取。**
|
||||
|
||||
```powershell
|
||||
# 一键:采集(含 EOF 重试)→ 烧录 → 提取日志 → verify 校验
|
||||
powershell -ExecutionPolicy Bypass -File "D:\Code\DTU 程序\STM32F4-Base\test\run_boot_test.ps1" `
|
||||
# 一键:采集(含 EOF 重试)→ 烧录 → 提取日志 → verify 校验(在仓库根目录执行,路径用仓库相对路径)
|
||||
powershell -ExecutionPolicy Bypass -File "test\run_boot_test.ps1" `
|
||||
-Port COM6 -BaudRate 115200 -DurationMs 30000 -InitialTimeoutMs 90000 `
|
||||
-IdleTimeoutMs 8000 -MaxBytes 65536 `
|
||||
-FlashCmd "D:\Code\DTU 程序\STM32F4-Base\MDK-ARM\download.bat" `
|
||||
-FlashCmd "MDK-ARM\download.bat" `
|
||||
-Required "App init OK;Network Init;net init: OK;PHY: 100M FULL" `
|
||||
-Forbidden "FAIL;ERROR;HardFault"
|
||||
```
|
||||
|
||||
@@ -33,7 +33,7 @@ param(
|
||||
[int]$InitialTimeoutMs = 90000,
|
||||
[int]$IdleTimeoutMs = 8000,
|
||||
[int]$MaxBytes = 65536,
|
||||
[string]$FlashCmd = "D:\Code\DTU 程序\STM32F4-Base\MDK-ARM\download.bat",
|
||||
[string]$FlashCmd = "",
|
||||
[string]$Required = "App init OK;Network Init;net init: OK",
|
||||
[string]$Forbidden = "FAIL;ERROR;HardFault",
|
||||
[string]$OutDir = "$env:TEMP\opencode",
|
||||
@@ -46,6 +46,12 @@ $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$captureScript = Join-Path $scriptDir "serial_capture.ps1"
|
||||
$verifyScript = Join-Path $scriptDir "verify_log.ps1"
|
||||
|
||||
# Default flash command: resolve relative to this script's repo location
|
||||
# (the old hardcoded absolute path pointed at a stale checkout location).
|
||||
if (-not $FlashCmd) {
|
||||
$FlashCmd = Join-Path $scriptDir "..\MDK-ARM\download.bat"
|
||||
}
|
||||
|
||||
if (-not (Test-Path -LiteralPath $captureScript)) {
|
||||
Write-Output "RUN_TEST_ERROR: serial_capture.ps1 not found"
|
||||
exit 2
|
||||
|
||||
Reference in New Issue
Block a user