# STM32F4-Base 项目架构总览 > 版本:v1.1 日期:2026-10-14 > 平台:STM32F407ZGTx @168MHz(HSE 8MHz → PLL M=4/N=168/P=2)+ FreeRTOS V10.3.1(CMSIS-RTOS2) > 用途:一页看清项目的分层、任务、数据流与目录组织。细节以各模块专项文档为准(见 §9)。 --- ## 1. 全景分层架构 ![STM32F4-Base 项目分层架构](../images/项目架构总览.svg) > 上图由 `tools/arch_diagram/gen_arch_svg.py` 生成(**手绘 SVG**,布局确定、零连线交叉);改架构后编辑脚本顶部 `ROWS` 数据重跑即可,源数据与 §1.1 表一致。 > 需要 PNG 预览(PPT/聊天分享):用 Edge 无头渲染 `docs/images/项目架构总览.svg` 即可(PNG 不入库)。 > 分层原则:**上层只依赖相邻下层(单向)**——2026-10-22 起这条方向已有机械检查(`layer-deps` 钩子 / `scripts/check_layer_deps.py`:`Drivers/BSP/**` 不得 include `App/**` 的头,存量 6 条反向边钉成基线、只减不增,三个横切叶子头 `console_log.h`/`sys_clock.h`/`sha256.h` 显式豁免);**① 业务层为顶层**(仅 `net_cfg`,被 UI 与基础服务共用),**② UI 层与 ③ 基础服务层同级并列**。跨层的具体调用关系见 §1.2 表。 ### 1.1 各层职责与关键模块 > ① 业务层(顶层,仅 `net_cfg`);② UI 层与 ③ 基础服务层**同级并列**(均使用 `net_cfg`);④ 中间件层 → ⑤ 驱动层 → ⑥ 内核/HAL → ⑦ 硬件 依次向下。 | 层 | 目录 | 职责 | 关键模块 | |---|---|---|---| | ① 业务层 | `Drivers/BSP/NETCFG/` | 顶层共享配置(被 UI 与基础服务共用) | `net_cfg` | | ② UI 层 | `Drivers/BSP/UI/` | LVGL 界面(与 PC 模拟器共用源码) | `ui_port` `ui_menu` `ui_menu_view` `ui_menu_tab` `ui_content_page` `ui_page_*` `ui_net_sink` `ui_font_cn_12` `ui_deco` | | ③ 基础服务层 | `App/`、`Drivers/BSP/NET/lftpd/` | 应用侧服务与编排 | `app_main` `sys_events` `app_cfg` `task/*` `algo/*`(语义取数与有效性~保护判据) `lftpd` `time_sync` | | ④ 中间件层 | `Drivers/BSP/NET/`、`Lib/`、`App/util/` | 通用库 / 协议栈 | `net_socket` `net_select` `net_events` `net_pending` `net_listener` `FatFs` `nand_ftl(dhara)` `LVGL` `sys_clock` | | ⑤ 驱动层 | `Drivers/BSP/{CH395F,GD5F2GQ5UE,TPAFE5160,SD2506,RS485}/` | 芯片寄存器 / 时序 / 中断 | `ch395f` `gd5f2gq5ue` `tpafe5160` `sd2506` `rs485` | | ⑥ 内核 / HAL | `Middlewares/`、`Drivers/STM32F4xx_HAL_Driver/`、`Drivers/CMSIS/` | 任务调度、外设 HAL、启动 | FreeRTOS V10.3.1 · STM32 HAL · CMSIS | | ⑦ 硬件 | — | STM32F407ZGTx @168MHz | SPI1 / SPI2 / I2C1 / UART4 / USART1 / ADC1 / TIM3 / DMA / GPIO | ### 1.2 关键跨层依赖(对应上图未画出的连线) | 依赖 | 说明 | |---|---| | `ui_page_net_cfg` → `net_cfg` | 网口设置页显示/编辑 RAM 副本(不直接触 CH395F SPI) | | `netTask` → `net_cfg` → `net_socket` | 上电只读 RAM 副本(defaultTask 已在屏障前 `net_cfg_load`)→ `net_init(ip,mask,gw,mac)`;配置保存后经 `SYS_EVT_NET_RECONFIG` 按**网络代次号**重建(新 IP 免重启生效) | | `net_socket` → `net_events` / `net_listener` / `net_pending` | socket 宿主调三个内部模块:芯片边沿事件 + 100ms 周期对账 / 监听生命周期(重建请求登记 + 唯一执行)/ 阻塞 recv·send 挂起请求表 | | `App/algo/*`(语义取数与有效性→全周傅氏相量→保护判据) → `adc_task` / `protect_task` | 采集与算法层为纯计算:adcTask 消费 语义取数与有效性/全周傅氏相量(TIM3 1kHz 节拍)、protectTask 消费 保护判据(20ms 判据节拍) | | `lftpd` / `net_cfg` / 测试 → `FatFs` → `nand_ftl` → `gd5f2gq5ue` | 文件与配置落 NAND | | `ui_net_sink` / `time_sync` → `net_socket` | 远程显示 :7003 / 对时 :8888 共用同一 TCP 栈 | | `ui_menu` → `ui_content_page` → `ui_page_*` | 框架 + 页面插件(描述符解耦) | | `time_sync` / `sys_clock` → `sd2506` | 墙钟基准与写回 RTC | | `ui_port` / `ui_page_*` → `LVGL` | UI 全部绘制走 LVGL | > `*` `rs485_task.c` 任务体已实现,但**当前未在 `freertos.c` 创建**(B2 暂缓,见 `docs/任务规划/暂缓-RS485.md`);`adc_task.c` 已于 2026-10-10 接入创建(`adcTask`)。 --- ## 2. 运行时任务与就绪屏障 ### 2.1 任务一览(`Src/freertos.c` 创建) | 任务 | 栈 (字节) | 优先级 | 职责 | 入口 | |---|---|---|---|---| | `defaultTask` | 5120 | Normal | FatFS 挂载(无卷 `f_mkfs`)→ 卷健康探针/自愈 → `net_cfg_load`(配置引擎属主)→ 置 `STORAGE_READY`(= 存储+配置就绪)→ 日志落盘(唯一写者,`syslog_drain`)+ 配置保存执行/2s poll + LED 闪烁 + **栈水位观测**(`stack_probe_poll`:首次 5s 后报告,此后仅在出现更深峰值时打印) | `StartDefaultTask` | | `netTask` | 3840 | Normal | 等存储(配置已由 defaultTask 就绪,本任务只读 RAM 副本)→ `net_stack_bring_up()`(不触碰配置文件引擎)→ 置 `NET_READY` → 循环 `net_poll` + `net_process_messages`;消费 `SYS_EVT_NET_RECONFIG` → 按网络代次号重建监听(CH395F 硬件层测试模式下入口自挂起) | `StartNetTask` | | `ftpTask` | 4096 | Normal | 等网络**且**存储(`WaitAll`)→ `lftpd_start("/", 21)` → 单客户端会话循环(断开后经 `net_listener` 重建监听) | `StartFtpTask` | | `testTask` | 8192 | BelowNormal | 测试套件入口(`test/board/test_config.h` 选择)。**产品构建不创建**:CubeMX 生成区无条件建后,`MX_FREERTOS_Init()` 在未选套件时(`!TEST_SUITE_SELECTED`)立即 `vTaskDelete` 回收其 8KB CCM 栈(2026-10-04) | `start_test_task` | | `uiTask` | 16384 | BelowNormal | 等网络 → `ui_port_init`/`ui_menu_init`/`ui_standby_init`(空闲 60s 切待机屏)/`ui_net_sink` → 10ms 刷新 | `ui_task_func` | | `timeSyncTask` | 2560 | Normal | 等网络 → TCP :8888 收 PC 推送时间 → 写 RTC | `time_sync_task` | | `adcTask` | 2560 | AboveNormal | 纯消费者:`osThreadFlagsWait` 等 TIM3 1kHz 唤醒(ISR 内 `adc_tick_irq_notify` 置位)→ `adc_input_poll()`(TPAFE 原子快照 + STM32 ADC1 DMA 双缓冲)→ 语义取数与有效性/全周傅氏相量消费,不取数不做保护运算 | `adc_task_func` | | `iec104Task` | 2560 | Normal | 等网络 → IEC 60870-5-104 上行(:2404 监听 + link 状态机,遥信/遥测/定值区,B4) | `iec104_serv_task` | | `protectTask` | 2048 | Normal | 保护判据 20ms 节拍:`prot_elm_run()` + 「自检失败闭锁」采样故障闭锁 + 定值校验和巡检(B5);不依赖网络,纳入 IWDG `required_mask` | `protect_task` | > `idle`(128 words)/ `timer`(256 words)由 FreeRTOS 内核创建。所有任务栈从 FreeRTOS `heap_4` 堆分配(**已迁 CCM**,2026-10-12 扩容至 56 KB,见 `docs/内存管理/RAM使用分析与优化方案.md` §4.1.2/§2.3)。 > **栈实测与预算**(2026-10-10~14,工具 `scripts/stack_report.py` = Keil 静态调用图 + 串口 `[STACK]` 水位;详见 `docs/内存管理/RAM使用分析与优化方案.md` §4.0.6/§2.3): > defaultTask 实测峰值 **2152B/5120B(余 41%)**、静态最坏链 3016B;netTask 实测峰 **1116~1164B/3840B**(开机~FTP 传输期,2026-10-14)、静态链 1912B;ftpTask 实测 740B/4096B、静态 1216B;timeSyncTask 实测 616B/2560B、静态 1264B;uiTask 实测 4016B/16384B;adcTask 实测 952B/2560B、静态 1320B;iec104Task 静态 1456B(未上板);protectTask 静态 872B(未上板);testTask 静态 496B(仅测试构建)。 > ⚠ 改栈或加大栈内局部变量前后**必须**重跑该脚本——静态分析低估含递归/间接调用的链(优化前 ftpTask:静态 2528B vs 实测 3480B)。 ### 2.2 就绪屏障(Event Flags,取代魔法延时) ![就绪屏障(Event Flags)](../images/就绪屏障.svg) > 三栏式:**左 = 生产者(set)· 中 = 事件组 `sys_evtHandle` · 右 = 消费者(wait)**。 > 任务卡与事件位对齐同一行,箭头 = `set` / `wait` 关系;`bit1 → ftpTask` 的额外依赖用 **All 汇合点**表达(`osFlagsWaitAll`)。 > 源码 `tools/arch_diagram/gen_barrier_svg.py`(改数据重跑即可);PNG 预览本地渲染、不入库。 **设计原则**:生产者与消费者互不知晓,只经事件组解耦;新增子系统只需定义新 bit + 各自 wait/set,不改已有任务。 (详见 `docs/架构设计/项目启动顺序管理.md`) **⚠ 就绪位必须 `osFlagsNoClear`(2026-09-21 实证,FTP 永不监听事故)**:`osEventFlagsWait` **默认消费**所等位(CMSIS 包装 → `xEventGroupWaitBits(xClearOnExit=pdTRUE)`,`cmsis_os2.c:1243-1249`),而就绪位表达的是**持久状态**——多人等待时必须人人可读。修前 `netTask` 等 `STORAGE_READY` 时清掉 bit1、`uiTask`/`timeSyncTask` 等 `NET_READY` 时清掉 bit0,导致 `ftpTask` 的 `WaitAll(bit0|bit1)` 两位永不同时为 1 → **任务永久阻塞、零日志零报错、21 端口从未监听**(靠栈水位异常才定位到)。 | 位类型 | 例子 | 等待方式 | |---|---|---| | **状态位**("环境已就绪") | `SYS_EVT_NET_READY` / `SYS_EVT_STORAGE_READY` | `osFlagsWaitAny\|osFlagsNoClear`、`osFlagsWaitAll\|osFlagsNoClear`——**任何消费者都不许消费它** | | **命令位**("请做一件事") | `SYS_EVT_CFG_SAVE` / `SYS_EVT_NET_RECONFIG` | 消费型等待(默认,不加 NoClear)——必须恰好被消费一次,否则会反复写盘/重建网络 | 本节图中 `ftpTask ← bit0 + bit1(All 汇合点)`依赖的正是"状态位常驻"这一前提。 ### 2.3 线程间通信 | 机制 | 对象 | 用途 | |---|---|---| | Event Flags | `sys_evtHandle` | 启动就绪屏障(§2.2) | | Message Queue | `netMsgQueue`(深度 8,`net_msg_t`) | 所有 CH395F 操作经队列**串行化**到 netTask 执行,保证 SPI 单线程访问 | --- ## 3. 启动时序 ```mermaid sequenceDiagram autonumber participant M as main participant A as app_main_init participant K as osKernelStart participant D as defaultTask participant N as netTask participant F as ftpTask participant U as uiTask participant T as timeSyncTask participant I as iec104Task participant P as protectTask participant C as adcTask M->>M: HAL_Init + CubeMX 外设初始化
(SPI1/SPI2/I2C1/UART4/USART1/ADC1/TIM3/DMA/GPIO/IWDG) M->>A: app_main_init() Note over A: 时间子系统优先,使后续日志带时间戳 A->>A: sd2506_init() → sys_clock_init()(墙钟基准) A->>A: rs485_init() → gd5f2gq5ue_init()(BBT 扫描)→ tpafe5160_init() A->>A: adc_input_init()(语义取数与有效性层的语义映射 + ADC1 DMA)→ phasor_init() → freq_init()
→ prot_set_init() + prot_elm_init()(保护判据内核前封存) A->>A: SD2506 诊断(ID/时间/温度/电池)→ HAL_TIM_Base_Start_IT(&htim3)(1kHz 节拍) A-->>M: "App init OK" M->>K: osKernelStart() Note over K: MX_FREERTOS_Init 已创建 9 任务 + 事件组 + 队列
(产品构建 testTask 创建即回收,实跑 8 任务) K->>D: 调度 D->>D: f_mount(无卷则 f_mkfs FAT32)→ 卷健康探针/自愈 → net_cfg_load()(配置引擎属主) D-->>N: set SYS_EVT_STORAGE_READY D->>D: 进入 LED / syslog / 配置 poll 循环(含 stack_probe_poll 栈水位采样) N->>N: wait STORAGE_READY(Any|NoClear,≤10s,超时回退工厂默认) N->>N: 读 RAM 配置副本 → net_init(ip,mask,gw,mac)
→ PHY 强制 100M 全双工 → 等链路 ≤3s → MAC 回写影子 N-->>F: set SYS_EVT_NET_READY N->>N: 循环 net_poll() + net_process_messages()
(消费 NET_RECONFIG → 按代次号重建监听) F->>F: wait NET_READY|STORAGE_READY(All|NoClear)→ lftpd_start("/", 21)
→ 串口输出:waiting for connection... U->>U: wait NET_READY(Any|NoClear)→ ui_port_init → ui_menu_init → ui_net_sink_start(7003) T->>T: wait NET_READY(Any|NoClear)→ TCP listen :8888 I->>I: wait NET_READY(Any|NoClear)→ IEC104 listen :2404 P->>P: 20ms 判据节拍 prot_elm_run()(保护判据,不依赖网络) C->>C: wait TIM3 1kHz 唤醒(ISR adc_tick_irq_notify)→ adc_input_poll() 纯消费 Note over D,N,F,U,T,I: 就绪位一律 osFlagsNoClear(状态常驻、可被多任务重复读取);
仅命令位 CFG_SAVE / NET_RECONFIG 用消费型等待 ``` --- ## 4. 存储栈 ```mermaid flowchart LR FS["FatFs (Lib/FatFs)
f_mount / f_open / f_mkfs"] DIO["diskio.c
BSP 适配层"] FTL["nand_ftl.c
dhara FTL(磨损均衡 + 逻辑块映射)"] NAND["gd5f2gq5ue.c
页读写 / ECC / BBT 坏块表"] CHIP[("GD5F2GQ5UE
NAND 2Gbit")] subgraph USERS["使用方"] NC["net_cfg
/config.ini(INI [network] 段)"] SLG["sys_log
/log/run.log · selftest.log"] LF["lftpd
FTP 文件读写"] ST["测试套件
STORAGE/GD5F"] end NC --> FS SLG --> FS LF --> FS ST --> FS FS --> DIO --> FTL --> NAND --> CHIP subgraph RTC["独立时间源"] SD["SD2506 RTC (I2C1)"] --> SC["sys_clock
墙钟 epoch"] end ``` - 掉电持久化:`net_cfg` 配置(通用配置文件 `/config.ini`,INI `[network]` 段,cfg_file 引擎),保存后下次上电生效、或立即生效(网络重建)、外部变更 4s 沉降后自动重载;旧版 NCFG 二进制(`/cfg/netcfg.bin`)**已不再读取**——存在则忽略(不读、不删、不迁移),配置回落工厂默认(自动迁移已于 2026-10-10 删除)。 - 日志记录:`sys_log` 引擎写定长 64B 文本记录到 `/log/run.log`(运行记录)与 `/log/selftest.log`(自检记录),defaultTask 唯一写者,FTP 可直下(详见 `docs/子系统设计/运行与自检记录设计说明.md`);实时调试流 = 控制台单写者日志(USART1,4KB 环形缓冲 + defaultTask 唯一发送,详见 `docs/子系统设计/控制台单写者日志设计说明.md`)。 - NAND 坏块表(BBT)持久化在保留块池,不交给 dhara 管理。 --- ## 5. 网络栈与 TCP 消费者 ```mermaid flowchart TB CHIP[("CH395F
MAC+PHY · SPI2 + DMA")] --> DRV["ch395f.c
寄存器/命令 + 中断集中读取"] DRV --> NS["net_socket.c
BSD Socket API(单连接模式)"] NS --> EVT["net_events.c
边沿事件处理 + 100ms 周期对账"] NS --> LIS["net_listener.c
监听生命周期(请求登记 + 唯一执行)"] NS --> PND["net_pending.c
阻塞 recv/send 挂起请求表"] DRV --> NSEL["net_select.c
select/poll 多路复用"] NS --> Q["netMsgQueue
(串行化到 netTask)"] Q --> NT["netTask
net_poll() + net_process_messages()"] NS --> C1["lftpd
FTP :21"] NS --> C2["time_sync
对时 :8888"] NS --> C3["ui_net_sink
RemoDispBus :7003"] NS --> C4["iec104_serv
104 上行 :2404"] C3 -.->|"回送 1bpp 帧 + 接收远端按键"| CLIENT["DTU-RemoteLCD 客户端"] NC["net_cfg
IP/掩码/网关/MAC"] --> NS style NT fill:#efe,stroke:#484 ``` **单连接约束**:CH395F 配置为 8 Socket(索引寻址)+ TCP Server 单连接(`set_fun_para(0x08)`,bit3 SOCKET_CLOSE;bit0 保留必须为 0),Socket 0 复用监听与数据通道;FTP / 对时 / 远程显示 / IEC104 四个 TCP 服务**串行**占用连接,连接断开后自动回到 LISTEN。 > **FTP 启动门(2026-09-21 修复)**:`ftpTask` 必须等 `NET_READY` **且** `STORAGE_READY`(两位均 `NoClear`)才 `lftpd_start`——此前就绪位被其它等待者消费,FTP **静默不监听**(详见 §2.2 与 `docs/测试说明/FTP_Test_Guide.md` §8.6)。 > **单客户端实测注意**:同一时刻只服务 1 个会话,旧会话异常断开后经 `net_listener` 重建监听(实测恢复窗 p50 **208 ms**,n=30;口径见 `docs/网络/网络自愈架构设计.md`)。测试脚本必须内置连接重试(`test/host/ftp_up_dl_test.py`、`test/host/ftp_regression.py` 已内置),且**不要用裸 TCP 工具探测 21 端口**(探测会占掉唯一会话,见 `docs/测试说明/FTP_Test_Guide.md` §8.7)。 > **阻塞模型(2026-10-14 SEND 二期落地)**:阻塞 recv/send 经 `net_pending` 挂起请求表续作,调用方铁律 `sent < len`(末块写完即返回、不得登记挂起);架构与五路语义见 `docs/网络/Net层阻塞模型架构设计.md`,监听重建/沉降隔离/周期对账见 `docs/网络/网络自愈架构设计.md`。 > 2026-10-14 实测:FTP 回归连续 19 次 11/11;`ftp_integrity_test` 20 轮 × 64 KB STOR/RETR SHA-256 20/20 一致,吞吐稳定 ≈80 KB/s。 --- ## 6. UI 层架构(框架 + 页面插件) ```mermaid flowchart TB subgraph LOGIC["逻辑层"] UM["ui_menu.c
级联框状态机 + 按键分发"] MT["ui_menu_tab.c
菜单树生成物(ui_menu_tree.txt)"] end subgraph VIEW["显示层"] MV["ui_menu_view.c
框体/行/装饰自绘"] DECO["ui_deco.c
浅 V 装饰"] end subgraph FRAME["内容页框架"] CP["ui_content_page.c
屏生命周期 + 描述符驱动绘制/按键"] end subgraph PLUGIN["页面插件(每页一文件)"] P1["ui_page_device_info"] P2["ui_page_ac"] P3["ui_page_dc"] P4["ui_page_password"] P5["ui_page_net_cfg"] P6["ui_page_runlog
运行记录(动态列表)"] P7["ui_page_selftest
自检记录(动态列表)"] end subgraph PORT["显示端口"] UP["ui_port.c
160x160 1bpp 缓冲"] SINK["ui_net_sink.c
远程推流"] end UM --> MT UM --> MV UM --> CP UM --> STBY["ui_standby.c
待机屏(空闲超时切入)"] MV --> DECO CP --> P1 & P2 & P3 & P4 & P5 & P6 & P7 MV --> UP SINK --> UP P5 -.->|"net_cfg RAM 副本"| CFG["net_cfg"] P4 -.->|"ui_pwd_def.h 编译期密码"| PWD["查看 000 / 修改 123"] P6 & P7 -.->|"/log/run.log · selftest.log"| SLG["App/log/sys_log
定长 64B 记录引擎(defaultTask 落盘)"] ``` - 内容页为**独立全屏屏**,由框架一次性自绘(**零 LVGL 子对象**),ESC 返回菜单屏。 - 菜单受保护叶子(`ui_menu_tree.txt` 行尾 ` #P`):先弹密码页,授权后 `menu_password_ok` **先关页再续进入动作**。 - 待机屏:空闲超时(默认 60s)自动切入,仅确认键回菜单;数据源同实时量页。 - 字体 `ui_font_cn_12.c` 由 `tools/font_gen/` 生成(宋体 12px 1bpp 子集,**224 字/319 字形**,2026-10-29 实测;字形数 = 字符数 + ASCII 95 个)。 --- ## 7. 目录组织 ``` STM32F4-Base/ ├── Src/ Inc/ CubeMX 生成:main.c / freertos.c / HAL 外设 / 中断 / 时基 │ ├── freertos.c 9 任务 + netMsgQueue + sys_evt 创建(CubeMX 接线 + 崩溃钩子;任务体在 App/task/) │ └── main.c 入口 → app_main_init → osKernelStart ├── App/ 应用层(业务编排,非 CubeMX) │ ├── app/ 启动编排与全局契约:app_main.c(时间子系统优先)、app_cfg.h、sys_events.h │ ├── algo/ 采集与算法层 语义取数与有效性~保护判据:adc_input / phasor / freq / prot_set / prot_elm │ ├── task/ default_task / net_task / ui_task / adc_task / iec104_serv / protect_task / rs485_task*(*未创建,B2 暂缓) │ ├── log/ sys_log 记录引擎(定长 64B;MCU 后端落盘 /log/*.log) │ ├── cfg/ 配置引擎:cfg_file(静态态单任务收敛于 defaultTask)、store(MCU=FreeRTOS 堆) │ ├── fs/ 卷锁 vol_lock(syslog / cfg / lftpd 三条路径 try 语义) │ ├── sync/ time_sync(TCP 对时服务) │ ├── protocol/ 协议编解码(MODBUS 等)与帧解析 │ └── util/ sys_clock(墙钟)、task_watchdog(IWDG 判定状态机) ├── Drivers/BSP/ 纯手工驱动/服务(不受 CubeMX 保护) │ ├── CH395F/ 以太网 MAC+PHY │ ├── GD5F2GQ5UE/ NAND + dhara FTL │ ├── TPAFE5160/ ADC │ ├── SD2506/ RTC │ ├── RS485/ 半双工串口 │ ├── NET/ net_socket / net_select / net_events / net_listener / net_pending / lftpd │ ├── NETCFG/ 网口配置(INI [network] 段) │ ├── UI/ LVGL UI(与模拟器共用) │ └── dbg_log.h/dbg_cfg.h 分级日志 ├── Lib/ 第三方:LVGL v9.2、FatFs、FreeRTOS、CMSIS ├── Middlewares/ ST 官方中间件 ├── MDK-ARM/ Keil 工程(uvprojx + scatter + build/download 脚本) ├── simulator/ PC 模拟器(CMake + SDL2,共用 UI 源码 + 无头回归测试) ├── test/ 测试套件(test_config.h 互斥选择,产品构建空转) │ ├── board/ 板端套件(.c/.h:CH395F / 存储 / RTC / ADC / RS485 / UI) │ └── host/ 主机侧脚本(.py:烧录闭环、日志校验、UI 回归) ├── tools/ font_gen(字体生成)/ menu_gen(菜单表生成) ├── scripts/ 代码规范/乱码检查、RAM 报告(ram_report.py)、栈报告(stack_report.py) └── docs/ 设计/测试/问题记录文档 ``` --- ## 8. 构建与双端 ```mermaid flowchart LR SRC["共享源码
Drivers/BSP/UI/*
Drivers/BSP/NETCFG/net_cfg.c
App/cfg/cfg_file.c"] SRC --> MCU["MCU 端
Keil MDK-ARM (ARMCC v5)
@build → BUILD_OK"] SRC --> SIM["PC 模拟器
CMake + SDL2 (MSVC)
lvgl_sim.exe --keys"] MCU --> TEST["@test:串口采集 + 烧录 + 日志校验"] SIM --> REG["UI 回归:
check_menu_rows.py
check_content_pages.py"] ``` | 目标 | 工具链 | 验证 | |---|---|---| | MCU 固件 | Keil MDK-ARM v5(`@build` = `keil-build` skill) | `@build` 0 错 0 警 → `@test` 串口闭环 | | PC 模拟器 | CMake + SDL2 + MSVC(`simulator/build/`) | `check_menu_rows.py` + `check_content_pages.py`(文本断言 + 像素黄金参考) | **功能编译开关**:当前**不存在**——`App/app/app_cfg.h` 自 2026-10-22 起不含任何宏(原 `APP_NET/ADC/RS485_ENABLE` 三个零消费点占位宏已删,接回方法记在该文件 v1.2),各模块一律编译进产品;裁剪测试套件的唯一开关是 `test/board/test_config.h` 互斥选择(产品构建全注释)。 --- ## 9. 相关专项文档 | 主题 | 文档 | |---|---| | 启动顺序 / 就绪屏障 | `docs/架构设计/项目启动顺序管理.md` | | 存储架构(NAND/FTL/FatFs) | `docs/架构设计/STM32F4-Base存储架构说明.md` | | NET 阻塞模型(挂起请求表五路语义) | `docs/网络/Net层阻塞模型架构设计.md` | | 网络自愈(监听重建 / 沉降隔离 / 周期对账) | `docs/网络/网络自愈架构设计.md` | | 采集与算法层 语义取数与有效性~保护判据(**设计与判据**) | `docs/算法/采集与算法层设计说明.md` | | 采集与算法层目录索引(文件清单 / 构建测试) | `App/algo/README.md` | | ADC 采样节拍(TIM3 1kHz 同源驱动) | `docs/子系统设计/ADC采样节拍设计说明.md` | | IEC 60870-5-104 上行(:2404) | `docs/子系统设计/IEC104上行设计说明.md` | | 墙钟时间维护 | `docs/子系统设计/系统墙钟时间维护说明.md` | | 控制台单写者日志(USART1 调试流) | `docs/子系统设计/控制台单写者日志设计说明.md` | | RAM 预算与优化 | `docs/内存管理/RAM使用分析与优化方案.md` | | 栈/内存观测工具 | `scripts/stack_report.py`(Keil 静态调用图 × 串口 `[STACK]` 水位,判定取两者更坏者)· `scripts/ram_report.py`(.map 静态占用)—— 用法与基线见内存方案 §4.0.6 | | FTP 功能与陷阱 | `docs/测试说明/FTP_Test_Guide.md` §8.6(启动门)/ §8.7(单客户端)· 脚本 `test/host/ftp_up_dl_test.py`、`test/host/ftp_regression.py`、`test/host/ftp_integrity_test.py` | | UI 设计架构 / 内容页 / 菜单 | `docs/UI设计/UI_设计架构.md` · `内容页架构.md` · `菜单页面设计.md` | | UI 自动化测试 | `docs/UI设计/UI_自动化测试流程.md` | | 驱动已知陷阱 | `docs/问题记录/{CH395F,GD5F2GQ5UE,SD2506}_Trap_Records.md` | | 各模块测试指南 | `docs/测试说明/` | | 代码规范 | `docs/代码规范/` |