Files
STM32F4-Base/AGENTS.md
T
2026-07-21 22:08:11 +08:00

13 KiB
Raw Blame History

STM32F4-Base

⚠️ 编译验证:所有代码修改后必须执行 @build 验证编译通过(0 错误 0 警告),否则不要提交。

项目概述

STM32F407ZGTx (Cortex-M4 FPU) 基础固件项目,集成 CH395F 以太网控制器 SPI 驱动、GD5F2GQ5UE SPI NAND Flash 存储(dhara FTL + FatFS 文件系统)和 TPAFE5160 16位8通道同步采样 ADC 并行接口驱动。

目录结构

STM32F4-Base/
├── Src/                        # CubeMX 生成的外设初始化 + main
├── Inc/                        # CubeMX 生成的头文件
├── Drivers/
│   ├── BSP/
│   │   ├── CH395F/             # CH395F 以太网芯片驱动(手写)
│   │   ├── NET/                # BSD Socket API 网络抽象层(手写)
│   │   │   ├── net_config.h    # 网络配置宏定义
│   │   │   ├── net_types.h     # 类型定义、地址结构、Socket 控制块
│   │   │   ├── net_socket.h    # BSD Socket API 头文件
│   │   │   ├── net_socket.c    # API 核心实现 + 状态机
│   │   │   ├── net_select.h    # select/poll API 头文件
│   │   │   └── net_select.c    # select/poll 实现
│   │   ├── GD5F2GQ5UE/        # GD5F2GQ5UE NAND Flash 驱动(手写)
│   │   │   ├── gd5f2gq5ue.h/c         # 底层 SPI 驱动
│   │   │   └── nand_ftl.h/c            # dhara FTL + FatFS diskio 适配层
│   │   └── TPAFE5160/          # TPAFE5160 ADC 并行接口驱动(手写)
│   ├── STM32F4xx_HAL_Driver/  # ST HAL 库(CubeMX 生成)
│   └── CMSIS/                 # ARM CMSIS(CubeMX 生成)
├── Lib/
│   ├── dhara/                 # dhara FTL (Flash Translation Layer)
│   └── FatFs/                 # FatFs 文件系统 (v0.15)
├── MDK-ARM/                   # Keil MDK 工程文件
├── docs/                      # 参考文档
└── STM32F407-Demo.ioc         # STM32CubeMX 项目源文件

关键文件

路径 说明
Src/main.c 程序入口,初始化序列及主循环
Drivers/BSP/CH395F/ch395f.c/h CH395F 以太网芯片 SPI 驱动
Drivers/BSP/NET/net_socket.c/h BSD Socket API 网络抽象层
Drivers/BSP/NET/net_select.c/h select/poll I/O 多路复用
Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.c/h GD5F2GQ5UE NAND Flash SPI 驱动
Drivers/BSP/GD5F2GQ5UE/nand_ftl.c dhara FTL + FatFS diskio 适配层
Drivers/BSP/TPAFE5160/tpafe5160.c/h TPAFE5160 ADC 并行接口驱动
Src/freertos.c FreeRTOS task 创建和任务函数(CubeMX 生成 + 手写)
Inc/FreeRTOSConfig.h FreeRTOS 内核配置
MDK-ARM/STM32F407-Demo.uvprojx Keil MDK 项目文件
STM32F407-Demo.ioc STM32CubeMX 项目源文件

构建

仅支持 Keil MDK-ARM v5 (ARMCC)。打开 MDK-ARM/STM32F407-Demo.uvprojx 编译。

  • 编译器:ARMCC V5.06 update 7,优化 -O4(spi.c/usart.c/HAL 源文件 -O0),C99,USE_HAL_DRIVER, STM32F407xx

命令行编译(Agent 使用)

修改代码后必须执行命令行编译验证,仅使用 @build,不得直接调用 UV4.exe:

@build

该命令定义在 .opencode/command/build.md,自动执行 MDK-ARM/build.bat 进行全量编译(-r)。

退出码: 0 = 成功(0 Error, 0 Warning)| 1 = 有警告(同样不通过)| 2+ = 错误,需查看 MDK-ARM/build_log.txt 定位

硬件配置

  • 主频: HSE 25MHz → PLL 168MHz (4/168/2)
  • 6 个 LED: PC4, PC5, PB1, PB2, PF11, PF12(低电平点亮)
  • CH395F: SPI2 (PB12 CS, PB13 SCK, PB14 SDO, PB15 SDI)
  • GD5F2GQ5UE: SPI1 (PE0 CS, PB3 SCK, PB4 MISO, PB5 MOSI, PB8 WP, PE1 HOLD)
  • TPAFE5160: 并行16位 (PG0-PG15 数据, PD3 RD, PD4 CONVST, PD7 BUSY, PD1 FRSTDATA, PF13-15 OS[2:0])
  • USART1: PA9 TX, PA10 RX (115200bps)

启动顺序

HAL_Init() → SystemClock_Config() → MX_GPIO_Init() → MX_USART1_UART_Init() → MX_SPI2_Init() → MX_SPI1_Init()
→ gd5f2gq5ue_init() → [USER CODE: net_init + TCP listen + 其他外设初始化]
→ osKernelInitialize() → MX_FREERTOS_Init() → osKernelStart()  ← FreeRTOS 调度器启动
→ [FreeRTOS tasks: defaultTask, netTask 运行]

代码规范

参考 嵌入式C语言代码规范(V1.0).md,关键要点:

  • 缩进:4 空格,禁止 Tab
  • 命名:小写字母+下划线;全局变量 g_ 前缀,静态 s_,指针 p_,数组 a_
  • 函数注释块需包含:函数功能、入口参数、返回值、限定条件、函数说明
  • 大括号:K&R 风格(左大括号不换行)
  • 文件头注释:模块名称、功能、平台、作者、日期、修改记录
  • 头文件保护宏:__MODULE_NAME_H 格式,带 extern "C"

注意

  • Inc/ 和 Src/ 中 CubeMX 生成的文件带有 USER CODE BEGIN/END 标记,自定义代码应写在这些区域之间

  • ch395f.c/h、gd5f2gq5ue.c/h、nand_ftl.c/h 为纯手工代码,不受 CubeMX 保护

  • tpafe5160.c/h 为纯手工代码,不受 CubeMX 保护

  • CH395F 每次 SPI 事务需调用 ch395f_spi_begin() / ch395f_spi_end() 包裹

  • GD5F2GQ5UE 的 gd5f2gq5ue.c 中声明了 extern SPI_HandleTypeDef hspi1,需确保 SPI1 已初始化

  • sd2506.c/h 为纯手工代码,不受 CubeMX 保护

  • FreeRTOS Kernel V10.3.1 via CMSIS-RTOS V2 接口,HAL 时基使用 TIM7(非 SysTick,避免与 FreeRTOS 冲突)

  • NVIC 优先级分组 4 位,外设中断优先级全部 ≥ configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY(5),确保 ISR 可调 FreeRTOS API

  • 网络处理在 netTask 中运行,net_poll() 每 10ms 调用一次,TCP echo 在 netTask 中完成

  • freeertos.c 中 USER CODE 区域可添加自定义 task、mutex、semaphore、queue

CH395F 驱动关键点

驱动初始化顺序、SPI/DMA 通信、TCP/UDP 参数配置、Socket 缓冲区分配等完整关键点见 docs/CH395F_Trap_Records.md → 附:CH395F 驱动关键点。

测试说明

  • 测试代码文件:Drivers/BSP/CH395F/ch395f_test.h / ch395f_test.c
  • PC 端测试脚本:test/ch395f_socket_test.py,支持 Python 3.7+
  • 调用入口:Src/main.c 的 USER CODE BEGIN 2 区域
  • Phase 1(9 项寄存器测试):✅ 通过
  • Phase 2(TCP Client 收发 + 关闭重连 ×3 轮):✅ 通过
  • Phase 3(UDP Server Echo + PING + 大包):✅ 通过
  • Phase 4(NET 层 TCP Echo):✅ 通过(netTask 中永久运行)
  • Phase 5(NET 层 UDP Echo):✅ 通过
  • Phase 6(DHCP 自动获取 IP):✅ 通过(MCU 广播宣告 + PC 监听发现)
  • Phase 7(Select/Poll I/O 多路复用):✅ 通过
  • Phase 8(多客户端 7 并发):✅ 通过,成功率 100%
  • 测试拓扑:PC (192.168.1.2) ↔ 交换机 ↔ CH395F (192.168.1.100)
  • 详细使用说明:见 docs/CH395F_Test_Guide.md

GD5F2GQ5UE 驱动关键点

  • SPI Mode 0(CPOL=0, CPHA=0),时钟 42MHz
  • 初始化必须按顺序:复位 → 读 ID → 使能 ECC(B0h=10h)→ 解除块保护(A0h=00h)
  • SET_FEATURE 命令前必须先发写使能(06h)
  • 块擦除(D8h)参数是块首页地址(块编号 × 64),不是字节地址(旧版错误)
  • 读取 ID(9Fh)返回 3 字节,第 0 字节无意义,第 1 字节 MID,第 2 字节 DID
  • BBT 扫描必须在 ECC 禁用时完成(gd5f_bbt_scan()),ECC 使能后 reading spare area 会被 ECC 引擎干扰
  • 运行时坏块标记只来源于真实擦除/编程失败,不要在 ECC 使能后调用 gd5f2gq5ue_bbt_rescan() 重建 BBT
  • BBT 厂标坏块不可清除:gd5f_bbt_scan() 在 ECC 禁用时扫到的 786 个坏块是真实厂标标记(保守标记,最坏条件)。验证擦除+编程通过不代表不是坏块。初始 BBT 必须保留,只追加运行时损坏块
  • Block 0 有特殊行为:"Factory good block0" / "Power on Read"。大量相邻块操作后 block 0 数据可能被擦除,避免将其用于跨大批量操作的持久化存储
  • ECC 校验码生成不可靠:PROGRAM LOAD 02h + PROGRAM EXEC 10h 在 ECC 开启时可能不生成有效 ECC 校验码(ECCS=2)。应用层应使用独立数据校验(CRC/Checksum),不可依赖 chip 内部 ECC 状态位
  • 写验证:以擦除/编程状态位(E_FAIL/P_FAIL)为准,不要回读数据确认(ECCS 不可靠,block 0 不可靠)
  • 详细陷阱记录见 docs/GD5F2GQ5UE_Trap_Records.md

Flash 分区规划(256MB GD5F2GQ5UE)

分区名 偏移 大小 块范围 用途
ftl_fatfs 0 256MB Block 0~2047 dhara FTL + FatFS 文件系统
  • FatFS 通过 dhara FTL 访问全部 2048 个块,nand_ftl.c 中 FTL_START_BLOCK = 0
  • dhara FTL 自动管理坏块(dhara_nand_is_bad / dhara_nand_mark_bad)

TPAFE5160 驱动关键点

  • AD7606 P2P 兼容替代品,并行接口协议一致
  • 并行模式:CS 接地(始终选中),PAR/SER/BYTE SEL 接 GND(并行),DB15/BYTE SEL 接 GND(非字节模式)
  • 数据总线 DB[15:0] 接 GPIOG[15:0],通过 (uint16_t)GPIOG->IDR 一次读取16位
  • CONVST 上升沿触发全部8通道同步采样,BUSY 高电平表示转换中
  • RD 下降沿输出通道数据,按通道1~8顺序依次输出
  • FRSTDATA 在第一个 RD 下降沿变高,指示通道1数据就绪
  • 过采样 OS[2:0] 在 BUSY 下降沿锁存,无过采样时 tCONV=1.74µs,64倍过采样时 tCONV=167µs
  • 读取时序:168MHz 下 GPIO 写操作 + 5个 NOP (~30ns) 覆盖 t10=22ns 和 t14=21ns 要求
  • 硬件 RANGE 接 GND → ±5V 量程,LSB=152.59µV

NET 网络层关键点

  • 文件结构:Drivers/BSP/NET/ 下包含 net_config.h、net_types.h、net_socket.h/c、net_select.h/c
  • 使用前必须调用 net_init(ip, mask, gateway) 初始化网络子系统
  • net_poll() 在 FreeRTOS netTask 中定期调用(10ms 周期),轮询所有 Socket 状态,处理中断事件
  • TCP Server 多连接模式:1 个监听 Socket + 最多 7 个数据 Socket
  • select/poll 支持:net_select() 和 net_poll_events() 用于 I/O 多路复用
  • 事件回调:可通过 net_set_event_cb() 注册连接/断开/数据到达等事件回调
  • 非阻塞模式:net_recv() 使用 NET_MSG_DONTWAIT 标志,或使用 select/poll
  • 字节序转换:使用 net_htons()/net_ntohs()/net_htonl()/net_ntohl() 进行主机序/网络序转换
  • IP 地址转换:net_inet_addr("192.168.1.100") 字符串转网络序,net_inet_ntoa() 反向转换
  • 注意:新文件需手动添加到 Keil MDK 工程中才能编译
  • RECV 中断是电平触发的:net_poll() 中 do { ... } while(0) 只处理一批中断,由主循环读取数据,避免无限循环导致主循环饿死
  • TCP Server 多连接模式:Socket 0 专职监听,Socket 1~7 自动分配
  • 多连接模式初始化顺序(关键!遗漏将导致 TCP 连接失败):
    1. ch395f_set_fun_para(0x02) — 启用多连接(在 INIT_CH395 之前)
    2. ch395f_init() — 协议栈初始化
    3. 先配置数据 Socket 1~7(必须!必须在监听 Socket 之前配置,CH395F 在 TCP_LISTEN 时刻扫描可用 Socket):
      • SET_SEND_BUF → SET_RECV_BUF → SET_SOUR_PORT(与监听端口相同) → SET_PROTO_TCP
      • 数据 Socket 不调用 OPEN_SOCKET,由 CH395F 在连接到达时自动打开
    4. 再配置监听 Socket 0:
      • SET_PROTO_TCP → SET_SOUR_PORT → OPEN → TCP_LISTEN
    • 如果不按此顺序,Socket 4~7 不可用,最多只接受 3 个并发连接
  • TCP Server 单连接模式:Socket 先 OPEN → SET_PROTO_TCP → SET_SOUR_PORT → OPEN → TCP_LISTEN;收到 SINT_STAT_CONNECT 后 Socket 自动切换为数据通道;DISCONNECT/TIMEOUT 后需重新 OPEN → TCP_LISTEN

测试状态

Phase 描述 状态 备注
1 寄存器测试 ✅ 19/19
2 TCP Client ✅ 3/3
3 UDP Server ✅ 通过
4 NET TCP Echo ✅ 通过 netTask 中永久运行
5 NET UDP Echo ✅ 通过
6 DHCP ✅ 通过 支持广播宣告 + DHCP Server 两种模式
7 Select/Poll ✅ 通过
8 多客户端 7 并发 ✅ 70/70 100% 修复顺序后通过

陷阱记录

详细陷阱记录(含根因分析、解决方案、发现时间)见 docs/CH395F_Trap_Records.md 和 docs/GD5F2GQ5UE_Trap_Records.md。

当前已记录陷阱(CH395F):

  • Trap 01 Socket 4~7 自动分配不到(初始化顺序)
  • Trap 02 数据 Socket 缓冲区重叠
  • Trap 03 DHCP 包 xid 偏移错误
  • Trap 04 UDP 发送未等待 SENDBUF_FREE
  • Trap 05 CH395F 与 RTL8305NBI 自动协商不兼容
  • Trap 06 TCP KeepAlive 参数非 500ms 倍数
  • Trap 07 TCP 关闭误用 disconnect 导致 FIN_WAIT_2
  • Trap 08 RECV 中断电平触发无限循环

当前已记录陷阱(GD5F2GQ5UE):

  • Trap 01 块擦除地址错误(byte_addr → page_addr),导致先前所有实验结论无效。详见 docs/GD5F2GQ5UE_Trap_Records.md