16 KiB
16 KiB
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.hEvent 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_mountforced,无卷自动f_mkfsFAT32)→ 置SYS_EVT_STORAGE_READY→ LED 闪烁循环 - netTask:等
SYS_EVT_STORAGE_READY(≤10s)→net_stack_bring_up()(net_cfg_load读网口配置(/config.ini[network] 段,首启自动迁旧档 NCFG)→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 0net_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)取自通用配置文件
/config.ini的 [network] 段(Drivers/BSP/NETCFG/+App/cfg_file引擎:工厂默认 192.168.1.100/24、gw .1,MAC 全零 = 芯片内置),保存后立即生效(网络重建)或下次上电生效;旧版/cfg/netcfg.bin(NCFG)首启自动迁移;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/NETCFG/ |
网口配置(通用配置文件 /config.ini [network] 段 + 旧档 NCFG 迁移;网口设置内容页数据源) |
App/cfg_file.* + App/cfg_file_store* |
通用配置文件引擎(双端共享核心 + 平台存储后端:INI 解析/校验/外科保存/模板/沉降轮询) |
App/log/sys_log.h + sys_log_mcu.c |
运行/自检记录引擎(定长 64B 文本记录,落盘 /log/run.log·selftest.log,defaultTask 唯一写者;UI 页与 FTP 数据源) |
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— 通用配置文件(INI/config.ini:设计决策 D1–D15 / 双端支持 / 阶段规划 P1–P8 / 测试与风险)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— 网络测试说明(含构建模式与执行顺序)
串口调试(Python 串口助手 serial_tool)
通过仓库内置 Python 串口助手(tools/serial_tool/serial_tool.py,常驻 daemon + 薄 CLI)自主读取/控制 USART1 串口(PA9 TX / PA10 RX,115200bps)的打印流。
- 统一入口:
& '.venv\Scripts\python.exe' 'tools/serial_tool/serial_tool.py' <命令> [参数](daemon 未运行自动拉起;全部命令带硬超时 5s,JSON 输出,exit 0/1/2/3) - 全量历史 = 日志文件
tools/serial_tool/state/serial.log(RX/TX/SYS 时间戳行;Agent 直接 Read/Grep 读文件,无采集窗口仪式);20MB 上限自动截头保尾 10MB - 常用命令:
ports(枚举+配置口标注)/open(幂等,不存在/占用 ≤1s 报错)/send(位置参数文本 或--hex 0D0A,--nl加 CRLF)/status(全状态)/log --tail N --grep P/clear(代际+1)/wait-idle --settle-ms 8000 --max-ms 30000/close/start|stop|restart/selftest(无头自检) - 串口端口单一配置源:
.opencode/serial_config.json(当前本机为COM6)。端口变化只改这个文件。 - 命令契约、日志行格式、daemon 行为、已知陷阱详见
docs/测试说明/串口助手设计说明.md
自动化测试(AI 迭代闭环)
改完代码后运行 @test 可自动执行完整闭环:构建验证 → 开串口 + 清空日志(serial_tool,daemon 常驻,全程不关端口)→ 烧录(MDK-ARM/download.bat,下载后 Reset & Run)→ wait-idle(等 RX 静默 8s 或超时 30s)→ 提取 [RX] 行 → test/verify_log.ps1 标记校验 → PASS/FAIL 判定并迭代。
- 推荐一键命令:
test/run_boot_test.ps1= open → clear → 烧录 → wait-idle → 提取日志 → 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) - daemon 常驻全量日志文件:开串口 + clear 必须在烧录前完成(
run_boot_test.ps1已保证顺序);旧 serial-mcp-server 的"采集窗口/first-byte/EOF 重试"仪式已整体移除 - 每次按模块定制标记(网络/存储/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 - 配置自检:
simulator\build\Debug\lvgl_sim.exe --cfgtest(无头、进程内闭环:加载/旧档迁移 → 2s 轮询沉降 → 重载断言 + 外科保存无损回环;0=PASS,详见simulator/README.md§3.7) - 日志引擎自检:
lvgl_sim.exe --logtest(post N 条 → count/get 回读断言 + 宿主文件字节级校验;0=PASS);待机屏截图:--standby shot.bin;窗口模式空闲超时覆盖--idle <秒>
图形绘制(文档配图)
文档里的技术图形统一用 SVG 生成器(tools/arch_diagram/,纯标准库)或 Mermaid,勿手绘 ASCII 框图:
- 精确布局图(协议帧 / 分层架构 / 依赖 / 就绪屏障)→
tools/arch_diagram/gen_*.py:改脚本顶部数据 →& '.venv\Scripts\python.exe' tools/arch_diagram/gen_xxx.py重新生成docs/images/*.svg(脚本内置 XML 自检)。 - 拓扑类图(流程 / 时序 / 状态机)→ Markdown 内嵌
mermaid代码块。 - 全流程 / 选型 / 约定 / 检查清单见
docs/图形绘制流程.md(已沉淀为draw-diagramskill);产物统一放docs/images/专门目录;脚本与生成的.svg一并提交。
注意
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——如 Pythonopen(p, encoding="utf-8"/"utf-8-sig")按原样同编码回写,保持 BOM 状态不变)——但最稳的是别碰,只用 Edit / git。 - 发现乱码先别急着
git checkout:git checkout会丢弃所有未提交工作,使损失扩大。先评估能否用 Edit 仅改坏掉的那几行。 - 验证是否真损坏用字节级检查(统计
EF BF BD出现次数),不要依赖终端/Read 渲染判断。