# CH395F 测试使用指南

## 目录

1. [快速开始](#1-快速开始)
2. [各阶段测试](#2-各阶段测试)
   - [Phase 1: 寄存器测试](#phase-1-寄存器测试)
   - [Phase 2: TCP Client 收发+重连](#phase-2-tcp-client)
   - [Phase 3: UDP Server Echo](#phase-3-udp-server-echo)
   - [Phase 4: NET 层 TCP Echo](#phase-4-net-层-tcp-echo)
   - [Phase 5: NET 层 UDP Echo](#phase-5-net-层-udp-echo)
   - [Phase 6: DHCP](#phase-6-dhcp)
   - [Phase 7: Select/Poll](#phase-7-selectpoll)
   - [Phase 8: 多客户端并发](#phase-8-多客户端并发)
3. [Python 脚本参考](#3-python-脚本参考)
4. [常见问题](#4-常见问题)
5. [陷阱记录](#5-陷阱记录)

---

## 1. 快速开始

### 启用/禁用测试

编辑 `Drivers/BSP/CH395F/ch395f_test.h`，取消注释想要测试的阶段：

```c
//#define ENABLE_PHASE1_TESTS     /* 取消注释即启用 */
//#define ENABLE_PHASE2_TESTS     /* 默认关闭 */
//#define ENABLE_PHASE3_TESTS     /* 默认关闭 */
#define ENABLE_PHASE4_TESTS      /* 当前默认启用（netTask TCP echo） */
//#define ENABLE_PHASE5_TESTS     /* 默认关闭 */
//#define ENABLE_PHASE6_TESTS     /* 默认关闭（会改 IP！） */
//#define ENABLE_PHASE7_TESTS     /* 默认关闭 */
```

### 测试拓扑

```
PC (192.168.1.2) ──── 交换机 ──── CH395F (192.168.1.100)
```

---

## 2. 各阶段测试

### Phase 1: 寄存器测试

| 项目 | 说明 |
|------|------|
| 类型 | 独立运行，无需 PC 配合 |
| 耗时 | ~1 秒 |
| 测试项 | 芯片版本、命令状态、IP/网关/掩码、全局中断一致性、Socket 中断、ARP、TTL |

**操作**：
```
1. 只启用 ENABLE_PHASE1_TESTS
2. 编译烧录
3. 看串口输出
```

**通过标准**：串口打印 `=== Phase 1 Tests Complete ===`，中间无 ERROR。

---

### Phase 2: TCP Client

| 项目 | 说明 |
|------|------|
| 类型 | 需 PC 配合 |
| 耗时 | ~60 秒 |
| 测试项 | TCP Client 连接、收发 64 字节、关闭重连 ×3 轮 |
| 默认端口 | CH395F → PC:8081 |

**操作**：
```
终端 1: python test/ch395f_socket_test.py tcp_server --port 8081 --timeout 180
终端 2: 编译烧录（只启用 ENABLE_PHASE2_TESTS）
```

**通过标准**：串口显示 3 轮 `closed: sock=0x00`，PC 显示 3 次连接且 PING echo 匹配。

---

### Phase 3: UDP Server Echo

| 项目 | 说明 |
|------|------|
| 类型 | 需 PC 配合 |
| 耗时 | ~30 秒 |
| 测试项 | UDP Server 模式、8 字节头解析、30 轮 echo（含 HELLO + PING + 大包） |
| 默认端口 | CH395F:60000，PC 发到 60000 |

**操作**：
```
终端 1: 编译烧录（只启用 ENABLE_PHASE3_TESTS）
终端 2: python test/ch395f_socket_test.py udp_client --ip 192.168.1.100 --port 60000 --timeout 30
```

**通过标准**：PC 显示 30/30 回显匹配（含 HELLO ×1 + PING ×5 + 大包 ×20 + 其他）。

> **关键发现**：CH395F UDP 分两种模式——DesIP=`255.255.255.255` 是 Server 模式（接受任意来源），否则只能收指定 IP:Port。每次 `write_send_buf` 后必须等 `SINT_STAT_SENDBUF_FREE`。

---

### Phase 4: NET 层 TCP Echo

| 项目 | 说明 |
|------|------|
| 类型 | 需 PC 配合，netTask 中运行 |
| 耗时 | 按需 |
| 测试项 | `net_recv()` + `net_send()` 非阻塞 echo |
| 默认端口 | CH395F TCP Server:8080，PC 连到 8080 |

> ⚠️ 该测试在 FreeRTOS netTask 中运行，需等调度器启动（`osKernelStart`）后约 2 秒。

**操作**：
```
终端 1: 编译烧录（只启用 ENABLE_PHASE4_TESTS），看到 "netTask: started"
终端 2: python test/ch395f_socket_test.py tcp_client --ip 192.168.1.100 --port 8080
```

**通过标准**：10/10 回显匹配 + 大数据传输完整。

---

### Phase 5: NET 层 UDP Echo

| 项目 | 说明 |
|------|------|
| 类型 | 需 PC 配合，netTask 中运行 |
| 耗时 | 按需 |
| 测试项 | `net_recvfrom()` + `net_sendto()` 非阻塞 echo |
| 默认端口 | CH395F UDP:60000 |

> ⚠️ 该测试在 FreeRTOS netTask 中运行，需等调度器启动后约 2 秒。

**操作**：
```
终端 1: 编译烧录（只启用 ENABLE_PHASE5_TESTS），看到 "netTask: started"
终端 2: python test/ch395f_socket_test.py udp_client --ip 192.168.1.100 --port 60000 --timeout 30
```

**通过标准**：PING 5/5 + 大包 10/10。

---

### Phase 6: DHCP

| 项目 | 说明 |
|------|------|
| 类型 | 需 PC 配合 |
| 耗时 | ~15 秒 |
| 测试项 | DHCP 自动获取 IP、HELLO echo 验证连通性 |

> ⚠️ **DHCP 会改变 CH395F IP！** 必须独立运行，不能与其他 Phase 同时启用。

PC 运行内置 DHCP Server，完整 DISCOVER → OFFER → REQUEST → ACK 握手，然后发送 HELLO 验证 UDP 连通性。

```
终端 1: python test/ch395f_socket_test.py dhcp_server --dhcp-iface 192.168.1.2 --ip 192.168.1.100 --dhcp-mask 255.255.255.0 --dhcp-gw 192.168.1.1
终端 2: 编译烧录（只启用 ENABLE_PHASE6_TESTS）
```

**通过标准**：PC 显示 `DHCP 分配完成!` + 连通性验证通过。

> **关键：** 需管理员权限（Windows 绑定端口 67）。xid 偏移为 `data[4:8]`。`--dhcp-iface` 为 PC 网卡 IP（用于区分网卡）。

**通过标准**：PC 显示 `收到 CH395F 广播宣告` + 3 轮 HELLO echo 成功率 100%。

> **关键：** CH395F 每次 `write_send_buf` 后必须等 `SENDBUF_FREE` 中断，否则后续接收操作失败。

---

### Phase 7: Select/Poll

| 项目 | 说明 |
|------|------|
| 类型 | 需 PC 配合，netTask 中运行 |
| 耗时 | 按需 |
| 测试项 | `net_select()` 多路复用检测可读 socket → echo |
| 默认端口 | CH395F TCP Server:8080 |

> ⚠️ 该测试在 FreeRTOS netTask 中运行，需等调度器启动后约 2 秒。

**操作**：同 Phase 4（TCP Client 连 8080）。

**通过标准**：同 Phase 4（10/10 回显匹配）。

> Phase 4 和 Phase 7 功能相同但实现不同，通常只启用一个。Phase 7 额外验证了 `NET_FD_SET/ISSET` 宏和 `net_select()`。

---

### Phase 8: 多客户端并发

| 项目 | 说明 |
|------|------|
| 类型 | 需 PC 配合，主循环运行 |
| 耗时 | ~2 秒 |
| 测试项 | 7 个 TCP 客户端并发，每客户端 10 轮 PING echo |
| 默认端口 | CH395F TCP Server:8080 |

**操作**：
```
终端 1: 编译烧录（启用 ENABLE_PHASE4_TESTS 或 ENABLE_PHASE7_TESTS）
终端 2: python test/ch395f_socket_test.py stress --ip 192.168.1.100 --port 8080 --count 10 --timeout 30
```

**通过标准**：7 并发 × 10 轮 = 70/70 成功率 100%。

> **关键陷阱**：`net_listen()` 中数据 Socket 1~7 的配置必须在监听 Socket 0 的 `OPEN + TCP_LISTEN` **之前**完成。CH395F 在 `TCP_LISTEN` 时刻一次性扫描可用 Socket，若颠倒顺序则 Socket 4~7 不被识别，最多只接受 3 个并发连接。详见 `docs/CH395F_Trap_Records.md → Trap 01`。

---

## 3. Python 脚本参考

| 命令 | 说明 |
|------|------|
| `tcp_server --port 8081` | PC 作 TCP Server，测试 CH395F TCP Client |
| `tcp_client --ip x.x.x.x --port 8080` | PC 作 TCP Client，测试 CH395F TCP Server echo |
| `udp_client --ip x.x.x.x --port 60000 --timeout 30` | PC 发 UDP PING，测试 CH395F UDP echo |
| `udp_server --port 8082 --timeout 60` | PC 作 UDP Server，接收 CH395F 数据 |
| `stress --ip x.x.x.x --port 8080 --count 10` | 7 并发压力测试 |
| `dhcp_server --dhcp-iface IP --dhcp-mask MASK --dhcp-gw GW` | 内置 DHCP Server，完整握手 + 连通性验证（需管理员权限） |

通用参数：
通用参数：
- `--ip`：目标 IP（默认 192.168.1.100）
- `--port`：端口（各模式默认不同）
- `--count`：测试次数（默认 10）
- `--timeout`：超时秒数（默认 30）
- `--size`：数据包大小（默认 1024）

---

## 4. 常见问题

| 问题 | 原因 | 解决 |
|------|------|------|
| Phase 2 `open_socket: 0x20 (FAIL)` | socket 未完全关闭 | 已修复：先 close（RST）不等 disconnect |
| Phase 2 收到 `\x00(\x00\x00...` 乱码 | socket 7（`NET_MAX_SOCKETS-1`）默认无发送缓冲 | 在 `open_socket` 前调用 `set_send_buf(sock, 28, 2)` 分配 |
| Phase 3 UDP 收不到数据 | DesIP 设为具体 IP 而非 255.255.255.255 | 设 DesIP=`0xFFFFFFFF` 进入 Server 模式 |
| Phase 3 HELLO 收到但 PING 收不到 | 没等 `SENDBUF_FREE` 就第二次写 | 每次 `write_send_buf` 后轮询 `SINT_STAT_SENDBUF_FREE` |
| Python 报 `WinError 10054` | STM32 还没启动 | 等串口看到阶段启动信息再跑 Python |
| Phase 5 UDP 不工作 | NET 层 `net_bind()` 没打开 CH395F Socket | 已修复：`net_bind()` 对 DGRAM 类型自动打开 |
| `net_recvfrom()` 源地址错误 | UDP 头解析偏移错了 | 已修复：端口读 `header[2-3]`，IP 读 `header[4-7]` |
| Phase 8 Socket 4~7 分配不到 | 数据 Socket 配置在 `TCP_LISTEN` 之后（CH395F 在 listen 时扫描） | 先配数据 Socket 1~7 的缓冲/端口/协议，再配监听 Socket 0 |

---

## 5. 陷阱记录

详细陷阱记录（含根因分析、解决方案、发现时间）见 `docs/CH395F_Trap_Records.md`。

| # | 陷阱 | 对应文件 |
|---|------|----------|
| 01 | Socket 4~7 自动分配不到（初始化顺序） | `net_socket.c` → `net_listen()` |
| 02 | 数据 Socket 缓冲区重叠 | `net_socket.c` → `net_listen()` |
| 03 | DHCP 包 xid 偏移错误 | `test/ch395f_socket_test.py` |
| 04 | UDP 发送未等待 SENDBUF_FREE | `ch395f.c` / `ch395f_test.c` |
| 05 | CH395F 与 RTL8305NBI 自动协商不兼容 | `ch395f.c` |
| 06 | TCP KeepAlive 参数非 500ms 倍数 | `net_socket.c` → `net_init()` |
| 07 | TCP 关闭误用 disconnect 导致 FIN_WAIT_2 | `ch395f.c` |
| 08 | RECV 中断电平触发无限循环 | `net_socket.c` → `net_poll()` |
