Files
STM32F4-Base/AGENTS.md

14 KiB
Raw Blame History

STM32F4-Base

项目概述

STM32F407ZGTx (Cortex-M4 FPU) 基础固件项目,集成 CH395F 以太网控制器 SPI 驱动、GD5F2GQ5UE SPI NAND Flash 存储(含 FlashDB KVDB/TSDB 数据库)和 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 驱动
│   │   │   ├── fal_flash_gd5f2gq5ue.c  # FAL 设备适配层
│   │   │   ├── fal_cfg.h               # FAL 设备表 + 分区表
│   │   │   └── fdb_cfg.h               # FlashDB 功能配置
│   │   └── TPAFE5160/          # TPAFE5160 ADC 并行接口驱动(手写)
│   ├── STM32F4xx_HAL_Driver/  # ST HAL 库CubeMX 生成)
│   └── CMSIS/                 # ARM CMSISCubeMX 生成)
├── Lib/
│   └── FlashDB/               # FlashDB 数据库库v2.2.99
│       ├── src/               # FlashDB 核心源码
│       ├── inc/               # FlashDB 头文件
│       └── port/fal/          # FAL 抽象层
├── 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/fal_flash_gd5f2gq5ue.c FAL 设备适配层
Drivers/BSP/TPAFE5160/tpafe5160.c/h TPAFE5160 ADC 并行接口驱动
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
  • C 标准C99
  • 全局宏定义:USE_HAL_DRIVER, STM32F407xx

命令行编译Agent 使用)

修改代码后必须执行命令行编译验证,编译脚本位于 MDK-ARM/build.bat

cmd /c "cd /d "工作目录" && build.bat"

实际执行示例(从项目根目录):

cmd /c "cd /d "D:\Code\DTU 程序\STM32F4-Base\MDK-ARM" && build.bat"

编译结果判读:

  • 退出码 0无错误无警告编译成功
  • 退出码 1有警告无错误编译失败警告不可接受需修复
  • 退出码 2+:编译失败,需查看 MDK-ARM/build_log.txt 定位错误

UV4.exe 路径: C:\Keil_v5\UV4\UV4.exe 工程文件: MDK-ARM/STM32F407-Demo.uvprojx 目标名称: STM32F407-Demo

硬件配置

  • 主频: 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() → fdb_kvdb_init()

代码规范

参考 嵌入式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/hgd5f2gq5ue.c/hfal_flash_gd5f2gq5ue.c 为纯手工代码,不受 CubeMX 保护
  • tpafe5160.c/h 为纯手工代码,不受 CubeMX 保护
  • CH395F 每次 SPI 事务需调用 ch395f_spi_begin() / ch395f_spi_end() 包裹
  • GD5F2GQ5UE 的 gd5f2gq5ue.c 中声明了 extern SPI_HandleTypeDef hspi1,需确保 SPI1 已初始化
  • FlashDB 使用 FAL 模式,fdb_cfg.h 中定义 FDB_USING_FAL_MODEfal_cfg.h 中定义分区表
  • FlashDB 详细使用说明见 FlashDB使用说明.md
  • sd2506.c/h 为纯手工代码,不受 CubeMX 保护

CH395F 驱动关键点

  • 初始化必须按手册9.2.1节顺序:SET_MACSET_IP/GWIP/MASKINIT_CH395SET_PHY
  • IP/网关/掩码必须在 INIT_CH395 之前设置INIT 会读取并锁定当前寄存器值到协议栈,之后再设 IP 无效
  • SET_PHY 必须在 INIT_CH395 之后,它会复位 MAC/PHY 建立物理链路,不影响已锁定的协议栈参数
  • CMD_PING_ENABLE 不需要显式调用INIT 后默认可用
  • 每次 SPI 事务需调用 ch395f_spi_begin() / ch395f_spi_end() 包裹
  • 大数据量收发使用 SPI2 DMAch395f_write_send_buf()ch395f_read_recv_buf() 已改造为 DMA 批量传输DMA1_Stream3 RX, DMA1_Stream4 TX命令和配置操作仍使用逐字节轮询
  • DMA 缓冲区:s_spi2_dma_tx_buf[1500] / s_spi2_dma_rx_buf[1500]4 字节对齐
  • TCP 重传参数SET_RETRAN_COUNT默认12次最大20SET_RETRAN_PERIOD默认500ms最大1000ms必须在 INIT_CH395 之前设置,总重传时间 = 次数 × 周期
  • KeepAlive 参数必须为 500ms 的倍数SET_KEEPALIVE_IDLE默认20000msSET_KEEPALIVE_INTVL默认15000ms单位为 ms且 IDLE 必须 > INTVL均为 500 的倍数;传入非 500 倍数的值会导致未定义行为CH395F 内部定时器异常TCP 连接几秒内 TIMEOUT
  • KeepAlive 默认关闭:需在 SINT_STAT_CONNECT 后调用 ch395f_set_keepalive_enable(sock, 1) 启用
  • Socket 4-7 默认无收发缓冲CH395F 默认只为 Socket 0-3 各分配 4 个缓冲区块2048 字节)。多连接模式下使用 Socket 4-7 时,必须在 open_socket 之前显式分配:
    • ch395f_set_send_buf(sock, 28, 2) — 分配发送缓冲(例:块 28-29共 1024 字节)
    • ch395f_set_recv_buf(sock, 30, 2) — 分配接收缓冲(例:块 30-31共 1024 字节)
    • 不分配会导致发送数据为固定垃圾内容(0x0028 填充)、接收缓冲区无法存储数据
  • TCP 关闭重连的正确方法直接调用 ch395f_close_socket(),不要先调 ch395f_tcp_disconnect()
    • disconnectclosedisconnect 发送 FIN 将 Socket 推入 FIN_WAIT_2此后 close 不再发送 RSTSocket 卡在 FIN_WAIT_2 必须等远端发 FIN 才能关闭(可能需数分钟超时)
    • close 直接:在 ESTABLISHED 或 CLOSE_WAIT 状态下调用 close 会发送 RST 立即终止连接Socket 瞬间回到 CLOSED已验证 closed: sock=0x00 tcp=0x00
    • 关闭后需轮询 ch395f_get_socket_status() 等待 sock=0x00open_socket,典型耗时 < 1s
  • SOCK_TIMEOUT 中断:长时间无数据时 CH395F 可能触发 SINT_STAT_SOCK_TIMEOUT,不应将其视为致命错误——记录日志后继续操作即可,不要因此关闭 Socket
  • UDP 客户端/服务器模式(手册 §9.2.4):通过 SET_DES_IP_SN 的 IP 地址区分:
    • DesIP=0xFFFFFFFF255.255.255.255)→ UDP Server 模式:接受任意来源的数据,接收缓冲区中数据前 8 字节为信息头([0-1]reserved [2-3]src_port(LE) [4-7]src_ip [8+]payload),回发前需设置 SET_DES_IPSET_DES_PORT 指定目标
    • DesIP=具体 IP → UDP Client 模式:只接受指定 IP:Port 的数据,接收数据无信息头,只能向预设的目标发送
  • UDP 发送缓冲必须等 SENDBUF_FREE(手册要求):每次 ch395f_write_send_buf() 后必须等待 SINT_STAT_SENBUF_FREE 中断0x01否则下次写入会被 CH395F 静默丢弃。可通过轮询 ch395f_get_sock_int_status() 检查此标志

测试说明

  • 测试代码文件Drivers/BSP/CH395F/ch395f_test.h / ch395f_test.c
  • PC 端测试脚本test/ch395f_socket_test.py,支持 Python 3.7+
  • 调用入口Src/main.cUSER CODE BEGIN 2 区域
  • Phase 19 项寄存器测试): 通过
  • Phase 2TCP Client 收发 + 关闭重连 ×3 轮): 通过
  • Phase 3UDP Server Echo + PING + 大包): 通过
  • Phase 4NET 层 TCP Echo 通过,主循环永久运行
  • Phase 5NET 层 UDP Echo 通过
  • Phase 6DHCP 自动获取 IP 通过
  • Phase 7Select/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参数是字节地址块编号 × 128KB不是块编号
  • 读取 ID9Fh返回 3 字节,第 0 字节无意义,第 1 字节 MID第 2 字节 DID

FlashDB 分区规划

分区名 偏移 大小 用途
fdb_kvdb1 0 64MB KVDB 键值数据库
fdb_tsdb1 64MB 64MB TSDB 时序数据库

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() 轮询所有 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 0: SET_PROTO_TCP → SET_SOUR_PORT → OPEN → TCP_LISTEN
    4. Socket 1~7: SET_PROTO_TCP → SET_SOUR_PORT相同端口(不 OPEN
    • 如果不配置数据 Socket 的协议类型和源端口CH395F 不知道哪些 Socket 可用于多连接分配,客户端连接会被静默拒绝(无 CONNECT 中断)
  • TCP Server 单连接模式Socket 先 OPENSET_PROTO_TCPSET_SOUR_PORTOPENTCP_LISTEN;收到 SINT_STAT_CONNECT 后 Socket 自动切换为数据通道;DISCONNECT/TIMEOUT 后需重新 OPENTCP_LISTEN

已知问题

CH395F 与 RTL8305NBI 自动协商不兼容

CH395F 与 RTL8305NBI-CG 直连(经网络变压器)时,自动协商始终失败(返回 PHY_DISCONN),但强制 100M 全双工工作正常。强制 10M 全双工同样失败。

解决方案: 初始化协议栈后调用 ch395f_set_phy(CH395F_PHY_100M_FULL) 跳过自动协商。