freeRTOS支持

This commit is contained in:
2026-07-19 13:09:06 +08:00
parent 4eb728583b
commit 9ed4c22022
63 changed files with 45898 additions and 507 deletions

View File

@@ -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` 不再发送 RSTSocket 卡在 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=0xFFFFFFFF255.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 中断电平触发无限循环