修改测试

This commit is contained in:
2026-07-22 11:14:22 +08:00
parent c646df5c2a
commit a7f367f6a3
77 changed files with 1691 additions and 950 deletions

276
AGENTS.md
View File

@@ -1,229 +1,83 @@
# STM32F4-Base
> **⚠️ 编译验证:所有代码修改后必须执行 `@build` 验证编译通过0 错误 0 警告),否则不要提交。**
> **编译验证:所有代码修改后必须执行 `@build` 验证编译通过0 错误 0 警告),否则不要提交。**
## 项目概述
## 构建
STM32F407ZGTx (Cortex-M4 FPU) 基础固件项目,集成 CH395F 以太网控制器 SPI 驱动、GD5F2GQ5UE SPI NAND Flash 存储dhara FTL + FatFS 文件系统)和 TPAFE5160 16位8通道同步采样 ADC 并行接口驱动。
- **仅支持 Keil MDK-ARM v5 (ARMCC)**,无其他工具链
- 命令行编译:`@build`(自动执行 `MDK-ARM/build.bat`
- 退出码0 = 成功 | 1 = 有警告(不通过)| 2+ = 错误,查看 `MDK-ARM/build_log.txt`
## 目录结构
## 硬件
| 外设 | 接口 | 引脚 |
|------|------|------|
| 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) |
| 6x LED | GPIO | PC4, PC5, PB1, PB2, PF11, PF12低电平点亮 |
MCU: STM32F407ZGTx @ 168MHz (HSE 25MHz → PLL 4/168/2)
## 架构
```
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 → 外设初始化 → app_main_init → gd5f2gq5ue_init → tpafe5160_init
→ osKernelInitialize → MX_FREERTOS_Init → osKernelStart
→ [FreeRTOS tasks: defaultTask, netTask, ftpTask]
```
- **defaultTask**FTL + FatFS 初始化测试(开机一次性)
- **netTask**`net_init()``net_poll()` + `net_process_messages()` 每 10ms
- **ftpTask**`lftpd_start("/", 21, &ftp)` 延迟 7s 后启动
### 任务栈
| Task | 栈大小 |
|---|---|
| defaultTask | 2048 |
| netTask | 4096 |
| ftpTask | 4096 |
## 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多连接模式下 Socket 0 监听、Socket 1~7 数据
- `net_select.h/c` — select/poll I/O 多路复用
- 线程安全通过消息队列(`netMsgQueue`)实现,所有 CH395F 操作在 netTask 中串行处理
- 网络初始化顺序(多连接):`ch395f_set_fun_para(0x02)``ch395f_init()` → 配置 Socket 1~7缓冲区+端口+协议,不 OPEN→ 配置 Socket 0TCP LISTEN
- **新文件需手动添加到 Keil MDK 工程**才能编译
## 关键文件
| 路径 | 说明 |
|---|---|
| `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 项目源文件 |
| `Src/main.c` | 入口,CubeMX 生成 + USER CODE 区域 |
| `Src/freertos.c` | FreeRTOS task 创建CubeMX 生成 + USER CODE 区域 |
| `App/task/net_task.c` | netTask 主循环(网络初始化 + 轮询) |
| `Drivers/BSP/NET/` | BSD Socket API 网络抽象层 |
| `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 |
## 构建
## 驱动关键点
仅支持 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"`
详见 `docs/` 目录:
- `CH395F_Trap_Records.md` — CH395F 已知陷阱
- `GD5F2GQ5UE_Trap_Records.md` — GD5F2GQ5UE 已知陷阱
- `CH395F_Test_Guide.md` — 网络测试说明
## 注意
- `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 | 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µ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 中断电平触发无限循环
- **Trap 09** PHY link up 后立即 open_socket 失败(需启动延时 + 重试兜底)
- **Trap 10** 消息队列值拷贝导致结果回传丢失(改用 xTaskNotify 通知值)
- **Trap 11** `net_accept_locked` 拒绝 standalone_accept 的 ESTABLISHED 状态
- **Trap 12** FatFS FF_USE_LFN=0 且不接受 `/` 前缀路径(用 `to_fatfs_path()` 剥离)
当前已记录陷阱GD5F2GQ5UE
- **Trap 01** 块擦除地址错误(`byte_addr → page_addr`),导致先前所有实验结论无效。详见 `docs/GD5F2GQ5UE_Trap_Records.md`
- `Inc/``Src/` 中 CubeMX 生成的文件带有 `USER CODE BEGIN`/`END` 标记,自定义代码写在这些区域之间
- `Drivers/BSP/` 下的驱动文件为纯手工代码,不受 CubeMX 保护
- CH395F 每次 SPI 事务需用 `ch395f_spi_begin()` / `ch395f_spi_end()` 包裹
- FreeRTOS V10.3.1 via CMSIS-RTOS V2HAL 时基用 TIM7非 SysTick
- NVIC 优先级分组 4 位,外设中断优先级 ≥ 5`configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY`