freeRTOS支持
This commit is contained in:
77
AGENTS.md
77
AGENTS.md
@@ -49,6 +49,8 @@ STM32F4-Base/
|
||||
| `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 并行接口驱动 |
|
||||
| `Src/freertos.c` | FreeRTOS task 创建和任务函数(CubeMX 生成 + 手写) |
|
||||
| `Inc/FreeRTOSConfig.h` | FreeRTOS 内核配置 |
|
||||
| `MDK-ARM/STM32F407-Demo.uvprojx` | Keil MDK 项目文件 |
|
||||
| `STM32F407-Demo.ioc` | STM32CubeMX 项目源文件 |
|
||||
|
||||
@@ -96,7 +98,10 @@ cmd /c "cd /d "D:\Code\DTU 程序\STM32F4-Base\MDK-ARM" && build.bat"
|
||||
## 启动顺序
|
||||
|
||||
```
|
||||
HAL_Init() → SystemClock_Config() → MX_GPIO_Init() → MX_USART1_UART_Init() → MX_SPI2_Init() → MX_SPI1_Init() → gd5f2gq5ue_init() → fdb_kvdb_init()
|
||||
HAL_Init() → SystemClock_Config() → MX_GPIO_Init() → MX_USART1_UART_Init() → MX_SPI2_Init() → MX_SPI1_Init()
|
||||
→ gd5f2gq5ue_init() → fdb_kvdb_init() → [USER CODE: net_init + TCP listen + 其他外设初始化]
|
||||
→ osKernelInitialize() → MX_FREERTOS_Init() → osKernelStart() ← FreeRTOS 调度器启动
|
||||
→ [FreeRTOS tasks: defaultTask, netTask 运行]
|
||||
```
|
||||
|
||||
## 代码规范
|
||||
@@ -120,32 +125,14 @@ HAL_Init() → SystemClock_Config() → MX_GPIO_Init() → MX_USART1_UART_Init()
|
||||
- FlashDB 使用 FAL 模式,`fdb_cfg.h` 中定义 `FDB_USING_FAL_MODE`,`fal_cfg.h` 中定义分区表
|
||||
- FlashDB 详细使用说明见 `FlashDB使用说明.md`
|
||||
- `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 驱动关键点
|
||||
|
||||
- 初始化必须按手册9.2.1节顺序:`SET_MAC` → `SET_IP/GWIP/MASK` → `INIT_CH395` → `SET_PHY`
|
||||
- **IP/网关/掩码必须在 `INIT_CH395` 之前设置**,INIT 会读取并锁定当前寄存器值到协议栈,之后再设 IP 无效
|
||||
- **`SET_PHY` 必须在 `INIT_CH395` 之后**,它会复位 MAC/PHY 建立物理链路,不影响已锁定的协议栈参数
|
||||
- **`CMD_PING_ENABLE` 不需要显式调用**,INIT 后默认可用
|
||||
- 每次 SPI 事务需调用 `ch395f_spi_begin()` / `ch395f_spi_end()` 包裹
|
||||
- **大数据量收发使用 SPI2 DMA**:`ch395f_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次,最大20)和 `SET_RETRAN_PERIOD`(默认500ms,最大1000ms)必须在 `INIT_CH395` 之前设置,总重传时间 = 次数 × 周期
|
||||
- **KeepAlive 参数必须为 500ms 的倍数**:`SET_KEEPALIVE_IDLE`(默认20000ms)、`SET_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()`**
|
||||
- ❌ `disconnect` → `close`:`disconnect` 发送 FIN 将 Socket 推入 FIN_WAIT_2,此后 `close` 不再发送 RST,Socket 卡在 FIN_WAIT_2 必须等远端发 FIN 才能关闭(可能需数分钟超时)
|
||||
- ✅ `close` 直接:在 ESTABLISHED 或 CLOSE_WAIT 状态下调用 `close` 会发送 RST 立即终止连接,Socket 瞬间回到 CLOSED(已验证 `closed: sock=0x00 tcp=0x00`)
|
||||
- 关闭后需轮询 `ch395f_get_socket_status()` 等待 `sock=0x00` 再 `open_socket`,典型耗时 < 1s
|
||||
- **SOCK_TIMEOUT 中断**:长时间无数据时 CH395F 可能触发 `SINT_STAT_SOCK_TIMEOUT`,不应将其视为致命错误——记录日志后继续操作即可,不要因此关闭 Socket
|
||||
- **UDP 客户端/服务器模式**(手册 §9.2.4):通过 `SET_DES_IP_SN` 的 IP 地址区分:
|
||||
- **DesIP=0xFFFFFFFF(255.255.255.255)→ UDP Server 模式**:接受任意来源的数据,接收缓冲区中数据前 8 字节为信息头(`[0-1]reserved [2-3]src_port(LE) [4-7]src_ip [8+]payload`),回发前需设置 `SET_DES_IP` 和 `SET_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()` 检查此标志
|
||||
驱动初始化顺序、SPI/DMA 通信、TCP/UDP 参数配置、Socket 缓冲区分配等完整关键点见 `docs/CH395F_Trap_Records.md` → 附:CH395F 驱动关键点。
|
||||
|
||||
## 测试说明
|
||||
|
||||
@@ -155,9 +142,9 @@ HAL_Init() → SystemClock_Config() → MX_GPIO_Init() → MX_USART1_UART_Init()
|
||||
- **Phase 1**(9 项寄存器测试):✅ 通过
|
||||
- **Phase 2**(TCP Client 收发 + 关闭重连 ×3 轮):✅ 通过
|
||||
- **Phase 3**(UDP Server Echo + PING + 大包):✅ 通过
|
||||
- **Phase 4**(NET 层 TCP Echo):✅ 通过,主循环永久运行
|
||||
- **Phase 4**(NET 层 TCP Echo):✅ 通过(netTask 中永久运行)
|
||||
- **Phase 5**(NET 层 UDP Echo):✅ 通过
|
||||
- **Phase 6**(DHCP 自动获取 IP):✅ 通过
|
||||
- **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)
|
||||
@@ -194,7 +181,7 @@ HAL_Init() → SystemClock_Config() → MX_GPIO_Init() → MX_USART1_UART_Init()
|
||||
|
||||
- **文件结构**:`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 状态,处理中断事件
|
||||
- **`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()` 注册连接/断开/数据到达等事件回调
|
||||
@@ -207,15 +194,37 @@ HAL_Init() → SystemClock_Config() → MX_GPIO_Init() → MX_USART1_UART_Init()
|
||||
- **多连接模式初始化顺序(关键!遗漏将导致 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 中断)
|
||||
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`
|
||||
|
||||
## 已知问题
|
||||
## 测试状态
|
||||
|
||||
### CH395F 与 RTL8305NBI 自动协商不兼容
|
||||
| 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% | 修复顺序后通过 |
|
||||
|
||||
CH395F 与 RTL8305NBI-CG 直连(经网络变压器)时,自动协商始终失败(返回 `PHY_DISCONN`),但强制 100M 全双工工作正常。强制 10M 全双工同样失败。
|
||||
## 陷阱记录
|
||||
|
||||
**解决方案:** 初始化协议栈后调用 `ch395f_set_phy(CH395F_PHY_100M_FULL)` 跳过自动协商。
|
||||
详细陷阱记录(含根因分析、解决方案、发现时间)见 `docs/CH395F_Trap_Records.md`。
|
||||
|
||||
当前已记录陷阱:
|
||||
- **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 中断电平触发无限循环
|
||||
|
||||
Reference in New Issue
Block a user