# RW007 WiFi Driver Development Log
## 当前进度
### 已完成功能
| `reset()` | ✅ 完成 | 硬件复位模块 |
| `init()` | ✅ 完成 | 初始化模块 |
| `get_version()` | ✅ 完成 | 获取固件版本 |
| `get_mac()` | ✅ 完成 | 获取 MAC 地址 |
| `scan()` | ✅ 完成 | WiFi 网络扫描 |
| `join()` | ✅ 完成 | 连接 WiFi |
| `disconnect()` | ✅ 完成 | 断开连接 |
| `get_rssi()` | ✅ 完成 | 获取信号强度 |
| `drain_pending_data()` | ✅ 完成 | 清空待处理网络数据 |
### 测试结果
```
Firmware: RW007_2.1.0-a7a0d089-57
MAC: fc:58:4a:2c:47:e9
Found 18+ networks with correct SSID, Security, Channel, RSSI
WiFi join: 成功连接,稳定运行 200+ 秒
WiFi disconnect: 成功断开并重连
RSSI: 持续读取正常 (-72 ~ -73 dBm)
```
---
## 核心问题与解决方案
### 问题 1: SPI 通信返回全零
**现象**:
- 直接使用 `spi.blocking_transfer()` 成功
- 通过 `SpiBus` trait 的 `transfer()` 方法返回全零
- Magic 校验失败:`Invalid magic: 00000000 00000000`
**根本原因**:
CS 拉低后的延迟时间不足。直接测试用了 1ms,驱动只用了 10µs。
**解决方案**:
```rust
// device.rs - spi_transfer_packet 函数
// 修改前
delay.delay_us(10); // 太短!
// 修改后
delay.delay_ms(1); // RW007 需要足够时间准备
```
**教训**:
- SPI slave 设备通常需要 CS 后的建立时间
- 不要假设 "越快越好",要参考工作示例的时序
---
### 问题 2: WiFi 事件枚举值不匹配
**现象**:
- 收到 callback 数据,`event_cmd=9` 和 `event_cmd=10`
- 但 `WifiEvent::try_from()` 返回 `Err`,事件被忽略
- 扫描超时,返回 0 个结果
**根本原因**:
WifiEvent 枚举值错误。我最初假设:
```rust
// 错误的假设
ScanReport = 0,
ScanDone = 1,
```
实际 RT-Thread `rt_wlan_dev_event_t` 定义:
```c
// wlan_dev.h
typedef enum {
RT_WLAN_DEV_EVT_INIT_DONE = 0,
RT_WLAN_DEV_EVT_CONNECT, // 1
RT_WLAN_DEV_EVT_CONNECT_FAIL, // 2
RT_WLAN_DEV_EVT_DISCONNECT, // 3
RT_WLAN_DEV_EVT_AP_START, // 4
RT_WLAN_DEV_EVT_AP_STOP, // 5
RT_WLAN_DEV_EVT_AP_ASSOCIATED, // 6
RT_WLAN_DEV_EVT_AP_DISASSOCIATED, // 7
RT_WLAN_DEV_EVT_AP_ASSOCIATE_FAILED, // 8
RT_WLAN_DEV_EVT_SCAN_REPORT, // 9 <-- 这里!
RT_WLAN_DEV_EVT_SCAN_DONE, // 10 <-- 这里!
} rt_wlan_dev_event_t;
```
**解决方案**:
```rust
// commands.rs
pub enum WifiEvent {
InitDone = 0,
StaConnected = 1,
StaConnectFail = 2,
StaDisconnected = 3,
ApStart = 4,
ApStop = 5,
ApAssociated = 6,
ApDisassociated = 7,
ApAssociateFailed = 8,
ScanReport = 9, // 修正
ScanDone = 10, // 修正
}
```
**教训**:
- 不要猜测协议值,必须查阅原始定义
- RT-Thread 源码中的 `wlan_dev.h` 是权威来源
---
### 问题 3: AP 信息解析全错
**现象**:
- SSID 显示为 `<hidden>` 或空
- Channel 显示为 255
- RSSI 显示为 0
- Security 显示为 Open
**根本原因**:
`parse_ap_info()` 函数的字段偏移量和顺序错误。
Callback 数据格式:
```
[0-3]: event_cmd (u32)
[4-7]: len (u32)
[8-11]: result (u32)
[12+]: rt_wlan_info 结构体
```
`rt_wlan_info` 结构体布局:
```c
struct rt_wlan_info {
rt_wlan_security_t security; // offset 0, 4 bytes
rt_802_11_band_t band; // offset 4, 4 bytes
rt_uint32_t datarate; // offset 8, 4 bytes
rt_int16_t channel; // offset 12, 2 bytes
rt_int16_t rssi; // offset 14, 2 bytes
rt_wlan_ssid_t ssid; // offset 16, 34 bytes (1 len + 33 val)
rt_uint8_t bssid[6]; // offset 50, 6 bytes
rt_uint8_t hidden; // offset 56, 1 byte
};
```
**解决方案**:
```rust
// commands.rs - parse_ap_info()
let base = 12; // 跳过 callback header
// 正确的偏移量
let security = ... // base + 0
let band = ... // base + 4
// datarate // base + 8 (跳过)
let channel = ... // base + 12
let rssi = ... // base + 14
let ssid_len = buf[base + 16];
let ssid_val = &buf[base + 17..];
let bssid = &buf[base + 50..];
let hidden = buf[base + 56];
```
**教训**:
- 打印原始数据是调试的关键
- C 结构体的内存布局要考虑对齐
- 用已知数据验证偏移量(如 SSID "feather" 在数据中的位置)
---
### 问题 4: Security 类型全显示为 Unknown
**现象**:
- 所有网络的 Security 显示为 "?" (Unknown)
- 原始 security 值为 `0x00400004` 而不是简单的 0-6
**根本原因**:
RT-Thread 的 security 使用**位掩码**,不是简单枚举:
```c
#define WEP_ENABLED 0x0001
#define TKIP_ENABLED 0x0002
#define AES_ENABLED 0x0004
#define WPA_SECURITY 0x00200000
#define WPA2_SECURITY 0x00400000
// 示例组合值
**解决方案**:
```rust
// types.rs
impl From<u32> for Security {
fn from(value: u32) -> Self {
let is_wpa = (value & 0x00200000) != 0;
let is_wpa2 = (value & 0x00400000) != 0;
let is_wep = (value & 0x0001) != 0;
let is_aes = (value & 0x0004) != 0;
let is_tkip = (value & 0x0002) != 0;
if value == 0 {
Security::Open
} else if is_wep {
Security::Wep
} else if is_wpa2 {
Security::Wpa2Psk
} else if is_wpa {
Security::WpaPsk
} else {
Security::Unknown
}
}
}
```
**教训**:
- 枚举值可能不是连续的简单整数
- 位掩码是嵌入式中常见的模式
- 查看 SDK 头文件中的 #define 定义
---
### 问题 5: INT 引脚检测无效
**现象**:
- `has_data()` 始终返回 false
- `wait_response()` 超时,`INT triggered 0 times`
- 但直接轮询 `receive()` 可以收到数据
**根本原因**:
不确定具体原因,可能是:
1. INT 引脚时序问题
2. RW007 固件版本差异
3. 引脚电平检测时机问题
**解决方案**:
改用轮询方式,不依赖 INT 引脚检测:
```rust
// device.rs - scan()
// 修改前:依赖 INT
for _ in 0..iterations {
if self.has_data() { // 检查 INT 低
let data = self.receive(...)?;
// ...
}
delay.delay_ms(50);
}
// 修改后:轮询 receive
for _ in 0..iterations {
let (len, data_type, _) = self.receive(...)?;
if len > 0 {
// 处理数据
}
delay.delay_ms(100);
}
```
同样修改了 `get_mac()`:
```rust
// 修改前
self.wait_response(Command::MacGet, ...)?; // 等待 INT
// 修改后
delay.delay_ms(100); // 固定延迟
self.receive(...)?; // 直接读取
```
**教训**:
- 有时候简单的轮询比中断检测更可靠
- 如果一种方法不工作,尝试其他方法
- `get_version()` 工作而 `get_mac()` 不工作是重要线索
---
### 问题 6: Join 命令返回 result=-10 (参数错误)
**现象**:
- 发送 join 命令后立即返回 `result=-10`
- `-10` 在 RT-Thread 中是 `-RT_EINVAL` (无效参数)
**根本原因**:
`JoinParams::to_bytes()` 的结构体布局完全错误:
1. 字段顺序错误(把 SSID 放在最前面)
2. 密码长度错误(64 字节 vs 32 字节)
实际 `rw007_ap_info_value` 结构体布局:
```c
struct rw007_ap_info_value {
struct rt_wlan_info info; // 60 bytes
char passwd[32]; // 32 bytes (RT_WLAN_PASSWORD_MAX_LENGTH)
};
```
`rt_wlan_info` 正确布局:
```c
struct rt_wlan_info {
security: u32 // offset 0
band: u32 // offset 4
datarate: u32 // offset 8
channel: i16 // offset 12
rssi: i16 // offset 14
ssid.len: u8 // offset 16
ssid.val: [u8; 33] // offset 17
bssid: [u8; 6] // offset 50
hidden: u8 // offset 56
padding: 3 bytes // offset 57-59
};
```
**解决方案**:
```rust
// commands.rs - JoinParams::to_bytes()
pub const JOIN_PARAMS_MIN_SIZE: usize = 92; // 60 + 32
pub fn to_bytes(&self, buf: &mut [u8]) -> usize {
// 正确顺序:security, band, datarate, channel, rssi, ssid, bssid, hidden, passwd
buf[0..4].copy_from_slice(&(self.security as u32).to_le_bytes());
buf[4..8].copy_from_slice(&(self.band as u32).to_le_bytes());
// ... 按正确偏移量填充
}
```
**教训**:
- C 结构体的布局必须精确匹配
- 参考 SDK 源码中的实际赋值代码 (`spi_wifi_rw007.c` 中的 `wlan_join`)
---
### 问题 7: 连接后 RSSI 读取失败
**现象**:
- WiFi 连接成功
- 第一次 `get_rssi()` 成功
- 后续调用收到 `StaEthData` 类型数据,返回错误
**根本原因**:
WiFi 连接后,RW007 会持续发送网络数据包(ARP、广播等)。这些数据包与命令响应混在一起,如果不处理会干扰后续操作。
RT-Thread 有后台线程持续接收处理这些数据:
```c
// spi_wifi_rw007.c
if (data_packet->data_type == DATA_TYPE_STA_ETH_DATA) {
rt_wlan_dev_report_data(wifi_sta.wlan, data_packet->buffer, data_packet->data_len);
}
```
**解决方案**:
1. `get_rssi()` 添加重试逻辑,跳过网络数据:
```rust
for _ in 0..20 {
let (len, data_type, _) = self.receive(&mut rx_buf, delay)?;
match data_type {
DataType::Resp => return parse_rssi_response(...),
DataType::StaEthData => continue, _ => {}
}
}
```
2. 新增 `drain_pending_data()` 函数,定期清空积累的数据:
```rust
pub fn drain_pending_data(&mut self, delay: &mut D) -> Result<usize, Error> {
for _ in 0..10 {
let (len, _, _) = self.receive(&mut rx_buf, delay)?;
if len == 0 { break; }
}
Ok(drained)
}
```
3. 应用层定期调用 drain:
```rust
loop {
wifi.drain_pending_data(&mut delay)?;
delay.delay_ms(1000);
}
```
**教训**:
- WiFi 连接后需要持续处理网络数据
- 简单驱动没有后台任务,需要应用层配合
- 命令响应和网络数据可能交错到达
---
## 调试技巧总结
1. **对比工作代码**:`rw007_simple.rs` 直接测试成功,对比找差异
2. **打印原始数据**:
```rust
defmt::debug!("data[0..20]={:02x}", &rx_buf[..20]);
```
3. **添加计数器**:
```rust
let mut has_data_count = 0u32;
has_data_count += 1;
defmt::warn!("INT triggered {} times", has_data_count);
```
4. **逐步排除**:
- 直接 SPI 测试 → 排除硬件问题
- 对比时序参数 → 找到 CS 延迟问题
- 打印事件值 → 发现枚举不匹配
5. **查阅源码**:
- RT-Thread `wlan_dev.h` - 事件定义
- RT-Thread `spi_wifi_rw007.c` - 数据处理流程
- SDK 头文件 - 位掩码定义
---
## 文件修改清单
| `rw007/src/device.rs` | CS 延迟 10µs→1ms, 轮询方式替代 INT 检测, join/disconnect/get_rssi 轮询实现, drain_pending_data 新增 |
| `rw007/src/commands.rs` | WifiEvent 枚举值修正, parse_ap_info 重写, JoinParams 结构体布局修正 (92字节) |
| `rw007/src/types.rs` | Security 位掩码解析 |
| `hpm-hal/src/spi/mod.rs` | SpiBus::transfer 添加 dummy_cnt: 2 |
| `examples/.../rw007_wifi_scan.rs` | WiFi 扫描示例 |
| `examples/.../rw007_wifi_join.rs` | WiFi 连接/断开测试示例 |
---
## 异步驱动 (Async Driver)
### 架构设计
异步驱动采用与 Embassy cyw43 (WiFi) 驱动类似的分离式架构:
| `State` | `async_device.rs` | 共享状态,包含 channel 和 ioctl 状态 |
| `NetDriver` | `async_device.rs` | 实现 `embassy-net-driver-channel` 接口 |
| `Control` | `async_control.rs` | WiFi 控制接口 (init/connect/disconnect/scan) |
| `Runner` | `async_runner.rs` | SPI 通信任务,独立运行处理收发 |
| `IoctlState` | `async_ioctl.rs` | 命令/响应的异步同步机制 |
### 使用示例
```rust
use rw007::{new_async, State, Control, Runner, Security};
use static_cell::StaticCell;
static STATE: StaticCell<State> = StaticCell::new();
#[embassy_executor::main]
async fn main(spawner: Spawner) {
let state = STATE.init(State::new());
let (device, mut control, runner) = new_async(state, spi, rst);
// Spawn runner task
spawner.spawn(wifi_task(runner)).unwrap();
// Initialize
control.init().await.unwrap();
// Connect to WiFi
control.connect("SSID", "password", Security::Wpa2Psk).await.unwrap();
// Use device with embassy_net::Stack
let stack = Stack::new(device, config, resources, seed);
}
#[embassy_executor::task]
async fn wifi_task(runner: Runner<'static, SPI, RST, INT, CS>) -> ! {
runner.run().await
}
```
### 实现细节
1. **Cache-Line 对齐**: 使用 64 字节对齐的 buffer 避免 DMA 缓存问题
2. **两阶段 SPI 协议**: 与阻塞驱动相同的协议实现
3. **Ioctl 机制**: 使用 `Signal` 实现命令/响应的异步等待
4. **网络数据通道**: 使用 `embassy-net-driver-channel` 的 RX/TX channel
### 当前状态
- [x] Runner SPI 通信循环
- [x] Control 基础命令 (init/get_version/get_mac)
- [x] Control WiFi 操作 (connect/disconnect)
- [x] 网络数据收发测试 ✅
- [x] 与 embassy-net 集成测试 ✅
- [ ] Control scan 实现
### 测试程序
测试程序位于 `hpm-hal/examples/hpm6750evkmini/src/bin/`:
| `rw007_simple.rs` | 基础 SPI 通信测试 |
| `rw007_wifi_scan.rs` | WiFi 扫描测试 (阻塞) |
| `rw007_wifi_join.rs` | WiFi 连接测试 (阻塞) |
| `rw007_async.rs` | 异步驱动基础测试 |
| `rw007_tcp_echo.rs` | **TCP Echo 服务器** - 完整网络栈集成测试 |
**TCP Echo 测试结果**:
- WiFi 连接: ✅ WPA2-PSK
- DHCP: ✅ 自动获取 IP
- TCP 服务器: ✅ 监听端口 1234
- 数据收发: ✅ Echo 回显正常
---
## GitHub CI
已配置 `.github/workflows/ci.yml`,包含:
| fmt | 代码格式检查 (`cargo fmt --check`) |
| clippy | 代码质量检查,所有 feature 组合 |
| build | Release 编译,矩阵测试所有 feature 组合 |
**目标平台**: `riscv32imac-unknown-none-elf`
**Feature 矩阵**:
- `default` (无 feature)
- `defmt` (日志支持)
- `async` (异步驱动)
- `defmt,async` (全部)
---
## 代码统计
| `device.rs` | 797 | 阻塞驱动核心 |
| `async_runner.rs` | 684 | 异步 SPI 任务 |
| `commands.rs` | 420 | 命令定义与解析 |
| `types.rs` | 340 | 数据类型 |
| `async_control.rs` | 297 | 异步控制接口 |
| `protocol.rs` | 262 | SPI 协议定义 |
| `async_ioctl.rs` | 190 | Ioctl 状态机 |
| `async_device.rs` | 139 | 异步设备封装 |
| `lib.rs` | 95 | 模块导出 |
| `error.rs` | 37 | 错误类型 |
| **总计** | **3261** | |
---
## 待完成任务
- [x] 测试 WiFi 连接功能 (`join`)
- [x] 测试断开连接功能 (`disconnect`)
- [x] 添加异步模式支持 (Embassy) - 基础框架完成
- [x] 添加 GitHub CI
- [x] 集成网络协议栈 (embassy-net) 实现 TCP/UDP ✅
- [x] 网络数据收发测试 ✅ (`rw007_tcp_echo.rs`)
- [x] 清理未使用的 `wait_response` 函数 (已添加 `#[allow(dead_code)]`)
- [ ] 异步驱动 scan 实现
- [ ] 优化扫描超时处理
- [ ] 添加错误重试机制
- [ ] 发布到 crates.io