Files
STM32F4-Base/AGENTS.md
T
2026-09-07 10:48:18 +08:00

11 KiB
Raw Blame History

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 环境(跑脚本用)

  • PATH 默认 python 是 msys64 的(C:\msys64\ucrt64\bin\python.exe,无 pip/fonttools),不要用它跑依赖第三方库的脚本
  • 本机已验证可用解释器:conda base 环境 C:\ProgramData\anaconda3\python.exe(Python 3.12.4,fontTools 4.51.0 预装)
  • 字体生成需 fontTools:& 'C:\ProgramData\anaconda3\python.exe' tools/font_gen/make_font.py;extract_cn.py / tools/menu_gen/gen_menu.py 纯标准库,任意 python3 均可

硬件

外设 接口 引脚
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:延时 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 栈大小(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() → 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 15 与 NET 层 Phase 611 互斥(#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/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/问题记录/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),无需烧录:

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 渲染判断。