# STM32F4-Base 系统架构与硬件规格 > **文档定位**:全仓**系统运行时架构**与**硬件规格(V0.1)**的权威表档——自 > `docs/产品需求/产品需求设计文档.md` v1.9 的原 §3/§4 拆出(需求与验收口径仍在需求档)。 > 与 [`docs/架构设计/项目架构总览.md`](项目架构总览.md) 的分工:总览是**叙述性全景**, > 本档是**完整口径表**——就绪屏障位表(bit0~8)、线程间通信机制表、启动时序图、 > 引脚分配表、硬件能力边界表以**本档为准**,两处冲突以本档为准并回改总览。 > **硬件口径**:V0.1(`docs/硬件平台/V0.1_网表详细分析报告.md` + `Inc/main.h`)。 ## 1. 系统运行时架构 ### 1.1 六层分层架构 ![STM32F4-Base 项目分层架构](../images/项目架构总览.svg) > 上图由 `tools/arch_diagram/gen_arch_svg.py` 生成(手绘 SVG,零连线交叉);改架构后编辑脚本顶部 > `ROWS` 数据重跑即可,源数据与下表一致。 分层原则:**上层只依赖相邻下层(单向)**;**① 业务层为顶层**(仅 `net_cfg`,被 UI 与基础服务共用), **② UI 层与 ③ 基础服务层同级并列**。 | 层 | 目录 | 职责 | 关键模块 | |---|---|---|---| | ① 业务层 | `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/*`(语义取数与有效性~保护判据)`protocol/iec104/*` `log/soe_store` `lftpd` `time_sync` | | ④ 中间件层 | `Drivers/BSP/NET/`、`Lib/`、`App/util/` | 通用库 / 协议栈 | `net_socket` `net_select` `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 / DMA / GPIO / TIM7 | ### 1.2 运行时任务模型 **九个** FreeRTOS 任务(`Src/freertos.c` 创建,任务体在 `App/task/`、`App/sync/`): | 任务 | 栈(words / B) | 优先级 | 职责 | |---|---|---|---| | `defaultTask` | 1280 / 5120 | Normal | FatFS 挂载(无卷 `f_mkfs`)→ 卷健康探针/自愈 → `net_cfg_load` + 定值/运行参数/通讯参数装载(**配置引擎属主**)→ 置 `STORAGE_READY` → 日志落盘(唯一写者)+ 2 s 配置轮询 + 各模块脏位落盘(`prot_set` / `run_par` / `comm`)+ SOE 排空 + LED 闪烁 + 栈水位观测 + **看门狗唯一喂狗者** | | `netTask` | 960 / 3840 | Normal | 等存储 → `net_stack_bring_up()` → 置 `NET_READY` → `net_poll` 轮询 + CH395F 操作串行化(消息队列执行者) | | `ftpTask` | 1024 / 4096 | Normal | 等网络**且**存储(`WaitAll`)→ `lftpd_start("/", 21)` → 单客户端会话循环 | | `testTask` | 2048 / 8192 | BelowNormal | 测试套件入口(`test/board/test_config.h` 选择)。**产品构建不创建**(未选套件时立即 `vTaskDelete` 回收其 8 KB CCM 栈) | | `uiTask` | 4096 / 16384 | BelowNormal | 等网络 → `ui_port_init`/`ui_menu_init`/`ui_standby_init`(空闲 60 s 切待机屏)/`ui_net_sink` → 10 ms 刷新 + 数据注入 | | `timeSyncTask` | 640 / 2560 | Normal | 等网络 → TCP :8888 收 PC 推送时间 → 写 RTC | | `adcTask` | 640 / 2560 | AboveNormal | **采集主线唯一消费者**:每拍 `adc_input_poll()`(TPAFE 快照 + ADC1 DMA 帧)→ `adc_health` 有效性 → `phasor_run()` 相量 → `freq_run()` 测频 → 每 200 ms `meas_tick()` 发布遥测快照 | | `iec104Task` | 640 / 2560 | Normal | 等网络 → TCP :2404 监听 → 104 链路状态机(总召/变位/周期/遥控/定值/对时)+ 断链重听 + 网络代次自愈 | | `protectTask` | 512 / 2048 | Normal | **20 ms 判据节拍**:采样故障闭锁判定 → 定值校验和巡检 → `prot_elm_run()` 元件判据 → 动作边沿投递 SOE(**不做 NAND I/O**) | > `idle`(128 words)/ `timer`(256 words)由 FreeRTOS 内核创建。所有任务栈从 **56 KB** `heap_4` 堆 > (CCM)分配;CCM 水位 87.9%(<92% 门禁)。 > 任务表与 `Src/freertos.c` 的 `osThreadAttr_t` 一致,由 `scripts/gen_task_table.py` 生成并门禁校验; > **栈配置与实测余量**见 `../产品需求/产品需求设计文档.md` §7.3,改栈前后必须重跑 `scripts/stack_report.py`。 > > ⚠ **节拍同源**:TIM3 更新事件(1 kHz)同时触发 TPAFE `CONVST` 与(经 TRGO)ADC1 一轮 9 通道扫描, > 并在 ISR 内唤醒 `adcTask`——采集节拍与任务唤醒**不允许**各自计时。 > ⚠ **ADC1 必须在挂 DMA 的瞬间处于关闭态**:`app_main_init()` 步骤 4b-pre 的 `adc1_disable()` 不可删、不可后移, > 否则 DMA 乒乓缓冲整体旋转 2 格(表象与"映射表写错"一致)。证据见 > [`docs/子系统设计/ADC采样节拍设计说明.md`](../子系统设计/ADC采样节拍设计说明.md) §4.5。 ### 1.3 就绪屏障(Event Flags) ![就绪屏障(Event Flags)](../images/就绪屏障.svg) 用 CMSIS-RTOS2 Event Flags(`sys_evtHandle`)表达"子系统就绪",取代魔法延时与全局标志轮询。 生产者与消费者互不知晓,只经事件组解耦;新增子系统只需定义新 bit + 各自 wait/set。 | 位 | 名称 | 类型 | 语义 | |---|---|---|---| | bit0 | `SYS_EVT_NET_READY` | 状态位 | netTask:`net_init` + PHY 稳定完成 | | bit1 | `SYS_EVT_STORAGE_READY` | 状态位 | defaultTask:FTL/FatFS 挂载 + 配置就绪 | | bit2 | `SYS_EVT_FTP_READY` | 状态位 | ftpTask:`lftpd_start` 完成 | | bit3 | `SYS_EVT_TIMESYNC_READY` | 状态位 | timeSyncTask:首次时间同步完成 | | bit4 | `SYS_EVT_NET_RECONFIG` | 命令位 | UI:请求立即应用新网口配置 | | bit5 | `SYS_EVT_CFG_SAVE` | 命令位 | UI:网口配置保存请求(defaultTask 消费并写盘) | | bit6 | `SYS_EVT_CONSOLE_TX` | 命令位 | 控制台单写者:生产者入环形缓冲后置位 | | bit7 | `SYS_EVT_PROT_SET_SAVE` | 命令位 | 保护定值落盘请求:104 检测到定值脏后置位,defaultTask 原子写 `/prot_set.bin` | | bit8 | `SYS_EVT_RUN_PAR_SAVE` | 命令位 | 运行参数段落盘请求:104 写运行参数后置位,defaultTask 整档外科保存 | > ⚠️ **铁律**:状态位(bit0~3)表达**持久状态**,任何消费者都**不许消费**——必须 > `osFlagsWaitAny|osFlagsNoClear`。命令位(bit4~8)表达"请做一件事",必须**恰好被消费一次**(消费型等待)。 > 历史事故:就绪位被先到的等待者消费,导致 `ftpTask` 的 `WaitAll(bit0|bit1)` 永不成立、21 端口从未监听(静默)。 ### 1.4 启动时序 ```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 S as adcTask / protectTask participant F as ftpTask participant U as uiTask participant I as iec104Task / timeSyncTask M->>M: HAL_Init + CubeMX 外设初始化
(SPI1/SPI2/I2C1/UART4/USART1/DMA/GPIO/TIM3/TIM7) M->>A: app_main_init() Note over A: 时间子系统优先,使后续日志带时间戳 A->>A: sd2506_init() → sys_clock_init()(墙钟基准) A->>A: rs485_init() → gd5f2gq5ue_init()(BBT 扫描) A->>A: tpafe5160_init() → adc_input_init()(语义映射自检) A->>A: 配置段注册(adc_cal / run_par / prot_set / comm)→ 定值/元件初始化 A->>A: adc1_disable() → ADC1 挂 DMA(4b-pre,顺序不可变)→ 启动 TIM3 1 kHz 节拍 A-->>M: "App init OK" M->>K: osKernelStart() K->>D: 调度 D->>D: f_mount(无卷则 f_mkfs FAT32)→ 卷探针/自愈 → 配置装载
→ 定值整表原子装载(损坏则保出厂默认 + 自检留痕) D-->>N: set SYS_EVT_STORAGE_READY S->>S: adcTask:等 TIM3 节拍 → 采集 → 相量/测频 → 200 ms 遥测快照 S->>S: protectTask:20 ms 判据(未标定/采样故障则停走) N->>N: wait STORAGE_READY(Any|NoClear)→ net_init(ip,mask,gw,mac)
→ PHY 强制 100M 全双工 → 等链路 ≤3s N-->>F: set SYS_EVT_NET_READY F->>F: wait NET_READY|STORAGE_READY(All|NoClear)→ lftpd_start("/", 21) U->>U: wait NET_READY(Any|NoClear)→ ui_port_init → ui_menu_init → ui_net_sink_start(7003) I->>I: wait NET_READY(Any|NoClear)→ TCP listen :2404 / :8888 ``` > **顺序铁律**:① 配置段注册必须**先于** `cfg_file_load`(否则 FTP 上传的段不生效); > ② `adc1_disable()` 必须在挂 DMA 之前;③ `osKernelStart` 之前禁调任何内核 API(含 `osDelay` / 队列)。 ### 1.5 线程间通信 | 机制 | 对象 | 用途 | |---|---|---| | Event Flags | `sys_evtHandle` | 启动就绪屏障 + 命令位(§1.3) | | Message Queue | `netMsgQueue`(深度 8,`net_msg_t`) | 所有 CH395F 操作经队列**串行化**到 netTask 执行,保证 SPI 单线程访问 | | 双区 + 序号(无锁) | `phasor` 相量区、`meas` 遥测快照 | 写侧填满计算区后单字节翻转活跃索引;读者先取索引再整份拷贝,`seq` 判"是否换帧" | | SPSC 环(无锁) | `soe_store`(深度 8) | protectTask 只投递动作边沿,defaultTask 排空落盘——**保护路径零 NAND I/O** | | 脏位 + 事件位 | `prot_set` / `run_par` / `comm` | 写入方置脏;104 侧 peek 后投递事件位,配置属主(defaultTask)落盘,失败保留脏位下轮重试 | | 单字请求标志 | `prot_elm_request_reset()` | 跨上下文复归请求(104 遥控 YK11 在 iec104Task 落请求,protectTask 下一拍消费) | --- ## 2. 硬件规格 ### 2.1 主控与内存 | 项 | 值 | 说明 | |---|---|---| | MCU | STM32F407ZGTx | Cortex-M4(FPU),LQFP144 | | 主频 | 168 MHz | HSE 8 MHz → PLL M=4 / N=168 / P=2 | | Flash | 1 MB(0x08000000) | 片内 | | SRAM | 192 KB(128 KB 主 SRAM1+SRAM2 + 64 KB CCM) | 链接器映射 128 KB 主 SRAM;CCM 用于 FreeRTOS 堆(任务栈) | | RTOS | FreeRTOS V10.3.1(CMSIS-RTOS2) | HAL 时基 TIM7,tick = 1 ms;NVIC 4 位抢占,外设中断优先级 ≥5 | | 工具链 | Keil MDK-ARM v5(ARMCC v5) | 工程锁 `uAC6=0`,**不支持 armclang** | | CubeMX | 6.17.0 + STM32Cube FW_F4 V1.28.3 | 与 `.ioc` 记录一致,再生成须用此组合 | ### 2.2 外设与引脚分配 > **权威来源**:V0.1 网表分析报告 + `Inc/main.h`(代码已迁移至 V0.1)。下表为 **V0.1 硬件**的引脚。 | 外设 | 接口 | 引脚 | 说明 | |---|---|---|---| | **CH395F 以太网** | SPI2 | PB12 CS / PB13 SCK / PB14 SDO / PB15 SDI | 10/100M 以太网控制器;PHY 强制 100M 全双工;**SPI 分频系数必须为 8**(PCB 设计约束) | | **GD5F2GQ5UE NAND** | SPI1 | PE0 CS / PB3 SCK / PB4 MISO / PB5 MOSI / PB8 WP / PE1 HOLD | 256 MB SPI NAND(2 Gbit) | | **TPAFE5160 ADC** | 并行 16 位 | PG0~PG15 数据 / **PC6 FRSTDATA** / **PC7 BUSY**(EXTI) / **PC8 RD** / **PC9 CONVST** / **PA8 OS2** / **PA11 OS1** / **PA12 OS0** | 高精度多通道 ADC | | **SD2506 RTC** | I2C1 | PB6 SCL / PB7 SDA | 提供墙钟基准与日志时间戳 | | **USART1(U5 直连 TTL)** | UART | PA9 TX / PA10 RX | **700000 8N1 → 屏板链路**(TTL 直连、无 DE,`../产品需求/产品需求设计文档.md` §5.16.2);PA10 双向均已接线;日志已迁 UART5 | | **UART4(RS-485)** | UART | PC10 TX / PC11 RX / **PA15 DE** | 半双工通信(V0.1 轮换后 RS485#4 = UART4,`../硬件平台/V0.1_网表详细分析报告.md` §4.2) | | **USART2(RS-232 #3)** | UART | PD5 TX / PD6 RX | 经 U10 隔离 → U13 TPT3232E → CN1;固件目前未占用(日志实际走 UART5/RS232#1) | | **UART5(RS-232 #1)** | UART | PC12 TX / PD2 RX | 经 U9 隔离 → U13 → CN1;**日志口**(115200,`/serial-boot-test` 采集通道;台架端子 `../子系统设计/屏板链路设计说明.md` §3.2) | | **状态 LED ×6** | GPIO | PC5 / PB0 / PB1 / PB2 / PF11 / PF12 | **低电平点亮** | | SWD | — | PA13 SWDIO / PA14 SWCLK | V0.1 已修复 V0.0 的 SWCLK 引脚缺陷 | | 调试口 | — | CN2(XH-3A,1=SWDIO/2=SWCLK/3=GND) | V0.1 由 H2 排针改为 XH 端子 | > ⚠️ **V0.0 / V0.1 差异**:V0.1 相比 V0.0 做了**接口裁剪(删 8080 LCD 与 8 键)+ U17 大面积引脚重排 + > ADC 前端通道置换(17 路中 13 路变化)+ 调试口改型**。固件 BSP 与 ADC 通道映射表**必须按 V0.1 重映射**; > 详见 `docs/硬件平台/V0.1_网表详细分析报告.md`。 ### 2.3 存储器件 | 器件 | 容量 | 组织 | 软件栈 | |---|---|---|---| | GD5F2GQ5UE | 2 Gbit(256 MB) | 2048 块 × 64 页 × 2048 B | FatFS(FAT32)→ dhara FTL(磨损均衡 / 坏块管理 / GC)→ NAND 驱动 | | SD2506 | — | I2C1 | RTC(时间/温度/电池诊断) | - **BBT 持久化**:坏块表存于保留块池(顶部 4 块),不交给 dhara 管理。 - **单写者纪律**:`defaultTask` 拥有存储;`netTask` 只读 RAM 配置副本,不触卷。 - **FatFs 约束**:`FF_USE_LFN=0`(严格 8.3 文件名);`FF_FS_REENTRANT=1` + `ffsystem.c` 互斥钩子(超时 1 s);`FF_FS_NORTC=0`(`get_fattime` 直引用墙钟)。 ### 2.4 硬件版本与兼容性 | 版本 | 状态 | 说明 | |---|---|---| | V0.0 | 历史 | 含 8080 LCD、8 按键;存在 2 处设计缺陷(SWCLK 引脚错误致 SWD 不通、ADC17 落在非 ADC 引脚) | | **V0.1** | **当前** | 架构与 V0.0 一致;裁剪 LCD/按键、U17 引脚重排、修复上述 2 处缺陷;固件已迁移 | | V0.2 | 规划关注 | 新增关注项:VCAP 电容值未标注、PC13/14/15 子板 LED 3 mA 限流、SPI1 无法双落点 | ### 2.5 平台约束 | 约束 | 内容 | |---|---| | **CubeMX 生成文件** | `Inc/`、`Src/` 中带 `USER CODE BEGIN/END` 标记;自定义代码**只能**写标记区内 | | **生成区外的定制** | 仅限 `Src/iwdg.c` 延后启动、4 个 Fault Handler 实体(`scripts/check_cubemx_survival.py` 审计兜底) | | **`osKernelStart` 前** | 禁止任何 FreeRTOS / CMSIS-RTOS API 调用 | | **BOM / 编码** | 部分源文件带 BOM(`Drivers/BSP/UI/*`、`App/task/ui_task.c`、`App/cfg/cfg_file.c`、`Lib/lv_conf.h`),被工具改写丢 BOM 会导致 ARMCC 按 CP936 解码、中文字面量编译失败 | | **SDIO** | V0.1 主板 SDIO 固定引脚组被 TPAFE/UART 全部占用 → **无 SDIO 能力**(当前设计不受影响,FatFs 卷在 SPI NAND 上) | | **新源文件** | 必须双端注册(Keil `.uvprojx` + `simulator/CMakeLists.txt`),否则不参与编译 | ### 2.6 V0.1 硬件能力边界(对产品的硬约束) 下列五条是**硬件事实**,不是软件缺陷;需求档 §5 的功能范围与 需求档 §9 的"等条件"项均由它们推导: | # | 约束 | 具体内容 | 产品影响 | 解除条件 | |---|---|---|---|---| | H1 | **双转换器无共同采样触发** | 8 路电气量分属 TPAFE5160(16 位、8 通道同步、±5 V 双极性)与 STM32 ADC1(12 位、顺序扫描、0~3.3 V 单极性)两片;两片无共同触发,STM32 侧 9 通道一轮约 67 µs | 跨转换器的**相位 / 功率 / 功率因数 / 对称分量 / 三相同时刻判据**一律不成立 → 方向元件、PT/CT 断线、三相过流、零序过流延后 | **V0.2 硬件**:8 路电气量全部接 TPAFE(与备用路对调串联电阻,料号不变,走线级改动);固件只改映射表内容 | | H2 | **STM32 侧无中点偏置** | 单极性 0~3.3 V,交流量负半周削顶 | `UB/UC/IB/IC/IN` 无有效值/相量,只能读码值**监视**;104 对应点恒置 IV | V0.2 补偏置 | | H3 | **无出口继电器与开入回路** | 无跳闸/告警出口、无硬压板与开入遥信 | 保护动作只体现为**遥信 + 日志 + SOE**;104 遥控 `YK1~4/13/14` 否定确认 | 开入开出独立立项 + 硬件 | | H4 | **无本地屏与按键** | 160×160 单色屏仅存在于远端客户端 | UI 以 TCP :7003 推流 + 远端按键回注(`../产品需求/产品需求设计文档.md` §8) | 产品形态即如此,不解除 | | H5 | **无 SDIO** | SDIO 固定引脚组被 TPAFE/UART 占用 | FatFs 卷只能落 SPI NAND(当前设计不受影响) | 不解除 | > **量纲口径**(由 H1/H2 派生):在端子/子板变比确认与两点标定完成前,遥测与保护读数的单位是 > **ADC 引脚电压**(`eng=0`),不是二次值;此时 `ADC_CAL_F_VALID` 未置位, > **保护判据整体停走**(`../产品需求/产品需求设计文档.md` §5.12)。这是刻意设计,不是"保护不动作"的缺陷。