Files
STM32F4-Base/AGENTS.md
2026-07-21 21:48:23 +08:00

226 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 CMSISCubeMX 生成)
├── 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 0CPOL=0, CPHA=0时钟 42MHz
- 初始化必须按顺序:复位 → 读 ID → 使能 ECCB0h=10h→ 解除块保护A0h=00h
- SET_FEATURE 命令前必须先发写使能06h
- 块擦除D8h参数是块首页地址块编号 × 64不是字节地址旧版错误
- 读取 ID9Fh返回 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 | 128MB | 128MB | Block 1024~2047 | dhara FTL + FatFS 文件系统 |
- FatFS 通过 dhara FTL 访问 `ftl_fatfs` 分区,`nand_ftl.c``FTL_START_BLOCK = FTL_FATFS_OFFSET / GD5F_BLOCK_SIZE`
- Block 0~1023128MB预留可供后续扩展
## 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µs64倍过采样时 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`