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

221
docs/CH395F_Trap_Records.md Normal file
View File

@@ -0,0 +1,221 @@
# CH395F 驱动陷阱记录
记录开发过程中遇到的所有坑、根因、解决方案,避免重复踩坑。
---
## Trap 01Socket 4~7 自动分配不到
### 现象
多连接模式下最多接受 3 个客户端Socket 1~3Socket 4~7 的 CONNECT 中断从不触发,新客户端 SYN 被静默丢弃。
### 根因
`net_listen()` 中数据 Socket 的配置顺序错误。CH395F 在 `TCP_LISTEN` 时刻**一次性扫描**当前所有可用 Socket 并建立内部查找表。如果数据 Socket 4~7 在 `TCP_LISTEN` **之后**才配置缓冲区/协议/端口,自动分配逻辑不认识它们。
### 正确顺序
1. 先配置数据 Socket 1~7`SET_SEND_BUF → SET_RECV_BUF → SET_SOUR_PORT → SET_PROTO_TYPE_TCP`
2. 最后配置监听 Socket 0`SET_SEND_BUF → SET_RECV_BUF → SET_PROTO_TYPE_TCP → SET_SOUR_PORT → OPEN_SOCKET → TCP_LISTEN`
### 注意事项
- 数据 Socket 不调 `OPEN_SOCKET`,由 CH395F 在连接到达时自动打开
- DISCONNECT 后 CH395F 自动关闭 Socket但保留协议/端口/缓冲区配置,可被再次分配
- 手册 §9.2.6 要求数据 Socket **先设源端口再设协议类型**(与监听 Socket 顺序相反)
### 对应文件
`Drivers/BSP/NET/net_socket.c``net_listen()`
### 发现时间
Phase 8 压力测试2025-07
---
## Trap 02数据 Socket 缓冲区重叠
### 现象
多连接模式下多个 Socket 数据互相覆盖,收到错误数据或客户端挂死。
### 根因
CH395F 默认只为 Socket 0~3 各分配 4 个缓冲区块(共 32 块用完Socket 4~7 为零块。`net_listen()` 未对 Socket 4~7 显式分配独立缓冲区块,后续代码误用相同块号(如 28~31导致数据重叠。
### 解决方案
每个数据 Socket 独占 4 个缓冲区块2 发 + 2 收),按 `ds * 4` 基址分配,确保不重叠:
| Socket | 发送块 | 接收块 |
|--------|--------|--------|
| 1 | 4~5 | 6~7 |
| 2 | 8~9 | 10~11 |
| 3 | 12~13 | 14~15 |
| 4 | 16~17 | 18~19 |
| 5 | 20~21 | 22~23 |
| 6 | 24~25 | 26~27 |
| 7 | 28~29 | 30~31 |
### 对应文件
`Drivers/BSP/NET/net_socket.c``net_listen()`
### 发现时间
Phase 8 压力测试2025-07
---
## Trap 03DHCP 包 xid 偏移错误
### 现象
Python DHCP Server 收到 DISCOVER 包后无法匹配 Offer/Ack 的 xid握手失败。
### 根因
错误使用了 `data[232:236]` 作为 xidWireshark 某旧版显示误导),实际 DHCP 标准偏移为 `data[4:8]`
### 正确偏移
- xid`data[4:8]`
- msg_type`data[242]`
- client_mac`data[28:34]`
- magic cookie`data[236:240]`
### 对应文件
`test/ch395f_socket_test.py` → DHCP server 模式
### 发现时间
Phase 6, 2025-07
---
## Trap 04UDP 发送后必须等待 SENDBUF_FREE
### 现象
UDP 发送后立即执行接收操作,接收长度始终为 0 或数据错误。
### 根因
CH395F 手册要求每次 `WRITE_SEND_BUF` 后必须等待 `SINT_STAT_SENBUF_FREE` 中断,否则下次写入或接收操作可能失败。
### 解决方案
每次 `ch395f_write_send_buf()` 后轮询 `ch395f_get_sock_int_status()` 检查 `SINT_STAT_SENBUF_FREE` 标志。
### 对应文件
`Drivers/BSP/CH395F/ch395f.c` / test code in `ch395f_test.c`
### 发现时间
Phase 6 广播宣告模式2025-07
---
## Trap 05CH395F 与 RTL8305NBI 自动协商不兼容
### 现象
`ch395f_get_phy_status()` 始终返回 `PHY_DISCONN`,链路无法建立。
### 根因
CH395F PHY 与 RTL8305NBI-CG 直连时自动协商失败。
### 解决方案
初始化协议栈后强制设为 100M 全双工:
```c
ch395f_set_phy(CH395F_PHY_100M_FULL);
```
### 对应文件
`Drivers/BSP/CH395F/ch395f.c`
### 发现时间
项目初期硬件联调
---
## Trap 06TCP KeepAlive 参数必须为 500ms 倍数
### 现象
设置 KeepAlive 后 TCP 连接几秒内 TIMEOUT 断开。
### 根因
CH395F 内部定时器以 500ms 为基准单位,传入非 500ms 倍数的值导致未定义行为。
`IDLE` 必须 > `INTVL`
### 正确值
```c
ch395f_set_keepalive_idle(60000); // 60s500 倍数)
ch395f_set_keepalive_intvl(5000); // 5s500 倍数)
ch395f_set_keepalive_cnt(3); // 3 次
```
### 对应文件
`Drivers/BSP/NET/net_socket.c``net_init()`
### 发现时间
Phase 2 测试2025-07
---
## Trap 07TCP 关闭重连直接 close不要 disconnect
### 现象
`disconnect → close` 后 Socket 卡在 `FIN_WAIT_2` 数分钟,无法重新打开。
### 根因
`disconnect` 发送 FIN 将 Socket 推入 `FIN_WAIT_2`,此后 `close` 不再发 RST必须等远端发 FIN 才能关闭。
### 解决方案
直接调用 `ch395f_close_socket()`,在 ESTABLISHED 或 CLOSE_WAIT 下会发 RST 立即终止。
关闭后轮询 `ch395f_get_socket_status()` 等待 `sock=0x00``open_socket`
### 发现时间
Phase 2 TCP Client 测试2025-07
---
## Trap 08RECV 中断电平触发,避免无限循环
### 现象
`net_poll()` 中 RECV 中断无限触发主循环echo 等)永远得不到执行。
### 根因
CH395F 的 RECV 中断是电平触发的——只要接收缓冲区有数据就保持 `INT#` 低电平。
`do { ... } while(int_status != 0)` 会无限循环。
### 解决方案
`net_poll()` 使用 `do { ... } while(0)` 每次只处理一批中断,由主循环负责读取数据。
### 对应文件
`Drivers/BSP/NET/net_socket.c``net_poll()`
---
## 附CH395F 驱动关键点
### 初始化顺序
- 必须按手册9.2.1节顺序:`SET_MAC → SET_IP/GWIP/MASK → INIT_CH395 → SET_PHY`
- **IP/网关/掩码必须在 `INIT_CH395` 之前设置**INIT 会读取并锁定当前寄存器值到协议栈
- **`SET_PHY` 必须在 `INIT_CH395` 之后**,复位 MAC/PHY 建立物理链路,不影响已锁定的协议栈参数
- **`CMD_PING_ENABLE` 不需要显式调用**INIT 后默认可用
### SPI 通信
- 每次 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 参数配置(必须在 INIT_CH395 之前)
- **重传参数**`SET_RETRAN_COUNT`默认12次最大20`SET_RETRAN_PERIOD`默认500ms最大1000ms总重传时间 = 次数 × 周期
- **KeepAlive 参数必须为 500ms 的倍数**`IDLE`默认20000ms`INTVL`默认15000ms且 IDLE > INTVL传入非 500 倍数的值导致 CH395F 内部定时器异常
- **KeepAlive 默认关闭**:需在 `SINT_STAT_CONNECT` 后调用 `ch395f_set_keepalive_enable(sock, 1)` 启用
### Socket 4~7 缓冲区
- CH395F 默认只给 Socket 0~3 各分配 4 个缓冲区块(共 32 块用完Socket 4~7 为零块
- 多连接模式下使用 Socket 4~7 时,**必须在 `open_socket` 之前**显式分配:
- `ch395f_set_send_buf(sock, start_block, count)`
- `ch395f_set_recv_buf(sock, start_block, count)`
- 不分配会导致发送数据为固定垃圾内容(`0x0028` 填充)、接收缓冲区无法存储数据
### TCP 关闭重连
- **直接调用 `ch395f_close_socket()`,不要先调 `ch395f_tcp_disconnect()`**
-`disconnect → close`disconnect 发 FIN 推入 FIN_WAIT_2close 不再发 RSTSocket 卡住数分钟
-`close` 直接:在 ESTABLISHED 或 CLOSE_WAIT 下 close 发 RST 立即终止,瞬间回到 CLOSED
- 关闭后需轮询 `ch395f_get_socket_status()` 等待 `sock=0x00``open_socket`
### SOCK_TIMEOUT 处理
- 长时间无数据时可能触发 `SINT_STAT_SOCK_TIMEOUT`**不应视为致命错误**——记录日志后继续操作即可,不要因此关闭 Socket
### UDP 模式区分
- **DesIP=0xFFFFFFFF → UDP Server 模式**:接受任意来源数据,接收数据前 8 字节为信息头(`reserved[2] src_port[2] src_ip[4]`),回发前需设置 `SET_DES_IP``SET_DES_PORT`
- **DesIP=具体 IP → UDP Client 模式**:只接收指定 IP:Port 的数据,接收数据无信息头
### UDP 发送缓冲
- 每次 `ch395f_write_send_buf()` 后必须等待 `SINT_STAT_SENBUF_FREE` 中断,否则下次写入会被 CH395F 静默丢弃