rw007 0.1.0

RW007 SPI WiFi module driver for embedded Rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
# 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

// 示例组合值
SECURITY_WPA2_AES_PSK = WPA2_SECURITY | AES_ENABLED  // = 0x00400004
```

**解决方案**:
```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`,包含:

| Job | 说明 |
|-----|------|
| 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