Files
STM32F4-Base/AGENTS.md
T

171 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# STM32F4-Base
> **编译验证:所有代码修改后必须执行 `@build` 验证编译通过(0 错误 0 警告),否则不要提交。**
## 构建
- **仅支持 Keil MDK-ARM v5 (ARMCC)**,无其他工具链
- 命令行编译:`@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"`)
## Python 环境(跑脚本用)
- **统一入口:仓库内 `.venv\Scripts\python.exe`**(Python 3.12.4,fontTools + Pillow 已装),所有依赖第三方库的脚本一律用它;依赖清单 `requirements.txt`(仓库根目录)
- 重建/换机:`& 'C:\ProgramData\anaconda3\python.exe' -m venv .venv; & '.venv\Scripts\python.exe' -m pip install -r requirements.txt`(种子解释器可用任意带 venv 的 python ≥3.10;`.venv/` 已 gitignore,不入库)
- `requirements.txt` 必须**纯 ASCII**:Windows 下 pip 按系统代码页(GBK)解码该文件,中文注释会 UnicodeDecodeError(2026-09-09 实测)
- PATH 默认 `python` 是 msys64 的(`C:\msys64\ucrt64\bin\python.exe`),**不要用它**跑依赖第三方库的脚本;纯标准库脚本(回归测试、`extract_cn.py`、`gen_menu.py`)任意 python3 均可
- 字体生成:`& '.venv\Scripts\python.exe' tools/font_gen/make_font.py`
## 硬件
| 外设 | 接口 | 引脚 |
|------|------|------|
| CH395F 以太网 | SPI2 | PB12 CS, PB13 SCK, PB14 SDO, PB15 SDI |
| 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`)
## 架构
```
Src/main.c → CubeMX 外设初始化 → app_main_init → osKernelStart
→ [FreeRTOS tasks: defaultTask, netTask, ftpTask, testTask, uiTask, timeSyncTask]
```
- **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**:等 `SYS_EVT_STORAGE_READY`(≤10s)→ `net_stack_bring_up()`(`net_cfg_load` 读 Flash 网口配置 → `net_init(ip,mask,gw,mac)` + PHY 强制 100M 全双工 + 等链路 ≤3s → 回写芯片实际 MAC 影子 → 递增网络代次号)→ 置 `SYS_EVT_NET_READY` → 循环 `net_poll()` + `net_process_messages()` + `osDelay(1)`(tick=1ms);循环内消费 `SYS_EVT_NET_RECONFIG`(UI 保存网口配置后请求)→ 立即重建网络(新 IP 生效,无需重启;各 TCP 服务据代次号重建监听)
- **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`)
- **全景架构图**:`docs/项目架构总览.md`(六层分层 / 任务与就绪屏障 / 启动时序 / 存储·网络·UI 栈 / 目录组织)
### 任务栈
| Task | 栈大小(words) | 优先级 |
|---|---|---|
| defaultTask | 2048 | Normal |
| netTask | 4096 | Normal |
| ftpTask | 4096 | Normal |
| testTask | 4096 | BelowNormal |
| uiTask | 16384 | BelowNormal |
| timeSyncTask | 2048 | Normal |
## CH395F 设计原则
- **单连接 socket**:Socket 0 复用监听和数据通道,收到 CONNECT 后切换为数据通道,DISCONNECT/TIMEOUT 后重新 OPEN → LISTEN
- **中断统一处理**:所有 CH395F 中断在 `net_poll()` 中通过 `GET_GLOB_INT_STATUS` 集中读取并清除,不得在 ISR 或其他地方单独处理
## NET 网络层
- `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 中串行处理
- `net_init` 顺序:check_exist → reset → `set_fun_para(0x09)` → MSS/重传/KeepAlive → IP → `ch395f_init()` → `ch395f_set_mac_addr()`(mac 非空且非全零时)→ Socket 0~7 缓冲分配 → ping 使能
- 网口参数(IP/掩码/网关/MAC)取自 Flash 配置(`Drivers/BSP/NETCFG/`:magic 'NCFG' + ver + CRC-16/MODBUS;工厂默认 192.168.1.100/24、gw .1,MAC 全零 = 芯片内置),保存后**下次上电生效**;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 主循环(网络初始化 + 轮询) |
| `test/test_config.h` | 测试套件选择(互斥开关) |
| `Drivers/BSP/NET/` | BSD Socket API 网络抽象层 + lftpd |
| `Drivers/BSP/NETCFG/` | 网口配置(Flash 持久化 + CRC,网口设置内容页数据源) |
| `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/` 目录:
- `docs/项目架构总览.md` — 六层全景架构(分层/任务/启动时序/存储·网络·UI 栈)
- `docs/网络配置说明.md` — 网口设置页配置(UI 可改的 IP/掩码/网关/MAC:持久化 / 立即生效 / 页面操作)
- `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)
通过 **Adancurusul/serial-mcp-server** 自主读取/控制 **USART1 串口**(PA9 TX / PA10 RX,115200bps)的打印流。
- **串口端口单一配置源:`.opencode/serial_config.json`**(当前本机为 `COM6`)。端口变化只改这个文件。
- 用法、采集窗口模式、已知陷阱等详见 **`docs/测试说明/serial-mcp-server_串口调试.md`**
## 自动化测试(AI 迭代闭环)
改完代码后运行 **`@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=采集或烧录错误 / 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 已有 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),tick = 1ms
- NVIC 优先级分组 4 位,外设中断优先级 ≥ 5(`configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY`)
- **内存预算紧张**:任务栈目前普遍偏大(见 `待处理内容.md`),新增任务/缓冲/栈前必须先核算 RAM 余量
## 源码编辑与编码安全(铁律)
本工程源码含大量中文注释,曾因错误的文本重写方式导致整文件乱码(`U+FFFD` 替换符,不可逆)。以下规则必须遵守:
- **绝不用 `Get-Content` / `Set-Content` / `cmd` 文本重写含中文的源码文件**。这类"整文件重写"会按系统默认编码重新解码再写回,多字节中文必被 `U+FFFD` 替换且不可逆。
- **改代码只用 Edit 工具**(精确子串替换,不重新编解码整个文件,安全);批量替换用 Edit 的 `replaceAll`。
- 必须脚本级替换时,使用显式且一致的编码(**工程源码为 UTF-8**:多数文件无 BOM,但 UI 相关文件 `Drivers/BSP/UI/*`、`App/task/ui_task.c`、`Lib/lv_conf.h` 为 UTF-8 **带 BOM**——如 Python `open(p, encoding="utf-8"/"utf-8-sig")` 按原样同编码回写,保持 BOM 状态不变)——但**最稳的是别碰**,只用 Edit / git。
- **发现乱码先别急着 `git checkout`**:`git checkout` 会丢弃所有未提交工作,使损失扩大。先评估能否用 Edit 仅改坏掉的那几行。
- 验证是否真损坏用字节级检查(统计 `EF BF BD` 出现次数),不要依赖终端/Read 渲染判断。