eefocus_3914144 发表于 2026-7-31 21:58:01

【瑞萨RA-eco-RA6M4评测】Zephyr驱动DA16200 wifi模块



# DA16200 WiFi 模块移植文档

> 本文档记录在 RA-Eco-RA6M4V2.0(Zephyr `blinky` 工程)上通过 PMOD2 接口接入 DA16200MOD WiFi 模块的完整过程:硬件接线、设备树配置、AT 指令驱动实现、后台连接管理任务,以及调试过程中确认的所有固件差异和坑。
>
> 基础工程(LED/OLED/SHT30/滚动时钟)的移植见 [【瑞萨RA-eco-RA6M4评测】不用Zephyr了吗,我用他做了一个环境监测站 - #板卡评测 - 与非网](https://www.eefocus.com/forum/thread-235998-1-1.html)

!(https://www.eefocus.com/forum/data/attachment/forum/202607/31/215744ycghlrhevzf8loc9.jpg)



---

## 目录

1. [概述](#1-概述)
2. [硬件接线](#2-硬件接线)
3. [设备树配置](#3-设备树配置)
4. [驱动架构](#4-驱动架构)
5. (#5-at-指令流程)
6. [固件差异与踩坑记录](#6-固件差异与踩坑记录)
7. [应用集成](#7-应用集成)
8. [调试指南](#8-调试指南)
9. [参考资料](#9-参考资料)

---

## 1. 概述

| 项目 | 内容 |
|------|------|
| 主机 | RA-Eco-RA6M4V2.0(R7FA6M4AF3CFP,100-pin) |
| 模块 | DA16200MOD(出厂 AT 固件,UART 主机接口) |
| 物理接口 | PMOD2,UART(SCI0)+ 3 根控制 GPIO,**飞线连接** |
| 软件参考 | Renesas FSP 示例 `RA6M4_DA16200_MQTT_CLIENT`(`da16200_AT.c`) |
| Zephyr 版本 | v4.4.0(树内**无** DA16200 驱动,本驱动为应用层自研) |

Zephyr 4.4 的 `drivers/wifi/` 中没有 DA16200 支持,官方也从未合入。因此驱动以工程现有自研驱动(`ssd1306.c`/`sht30.c`)的风格写在 `src/drivers/` 下,协议与重试策略移植自 FSP 示例,收发路径重写为 Zephyr UART 中断驱动 API。

### 1.1 源码结构(新增部分)

blinky/
├── dts/
│   └── bindings/
│       └── wifi-da16200.yaml   ← 应用本地设备树 binding(GPIO 属性类型声明)
├── ek_ra6m4.overlay            ← 新增 wifi-da16200 节点(3 个控制 GPIO)
└── src/
├── main.c                  ← da16200_start() + OLED 实时状态行
└── drivers/
├── da16200.h             ← 对外 API(仅 2 个函数)
└── da16200.c             ← AT 收发 + 连接管理线程


---

## 2. 硬件接线

### 2.1 原理图

对比了DA16200 wifi模块与开发板的原理图:
!(https://www.eefocus.com/forum/data/attachment/forum/202607/31/215341ku64s9m4m9qf9cp0.png)
!(https://www.eefocus.com/forum/data/attachment/forum/202607/31/215424ihqlwkqdanda5qkh.png)



### 2.2 接线表

| PMOD2 引脚 | MCU 引脚 | 方向 | DA16200 引脚 | 说明 |
|------------|----------|------|--------------|------|
| 2 | P411 (TXD0) | → | 3 (RXD_HOST) | UART 主通道 |
| 3 | P410 (RXD0) | ← | 1 (TXD_HOST) | UART 主通道 |
| 7 | P414 (IRQ9) | ← | 2 (INTn) | 模块中断输出(当前软件未用,预留) |
| 8 | P608 | → | 4 (RESETn) | 硬件复位(**必须接**) |
| 9 | P609 | → | 6 (RTC_WAKE_UP) | DPM 休眠唤醒(**必须接**) |
| 6 或 12 | 3.3V | → | 11 (VCC) | 电平匹配,无需转换 |
| 5 或 11 | GND | — | 9 / 10 / 12 | 共地 |

### 2.3 硬件注意事项

- **供电**:WiFi 发射峰值电流约 300~400 mA,PMOD 的 3.3V 只有 C14(4.7µF)滤波。建议在模块 VCC(11 脚) 与 GND 之间就近加 **100µF 以上电解电容**;若板上 3.3V 稳压器余量不足,模块应改由外部 3.3V 供电(GND 必须与板子共地)。供电不足时症状是模块串口无响应 + 同板 I2C 外设(SHT30)报错。
- **流控不接**:模块的 CTS/RTS(5、7 脚)靠 0Ω 电阻选通,而 PMOD2 只引出 CTS0_RTS0 一根复用脚(P413),凑不齐双向流控。出厂 AT 固件 115200 8N1 无流控即可工作。
- **模块状态记忆**:WiFi 配置保存在模块 NVRAM 中,连接成功后每次上电模块会**自动重连**;进入 DPM 休眠的模块 UART 关闭,只有通过 RESETn 硬复位或 RTC_WAKE_UP 唤醒才能恢复通信——这就是两根控制线必须接的原因。

---

## 3. 设备树配置

### 3.1 UART:复用板级已有配置

官方 `ek_ra6m4.dts` 已经把 SCI0 配置为 PMOD2 串口(`pmod_serial = &uart0`),P410 RX / P411 TX @115200,无需在 overlay 重复配置。控制台/Shell 走的是 SCI9(P109/P110),与 SCI0 无冲突。

### 3.2 GPIO:overlay 新增节点

`ek_ra6m4.overlay`:

```dts
wifi_da16200: wifi-da16200 {
    compatible = "renesas,da16200";
    /* PMOD2 pin8P608 -> module pin4 RESETn (active low) */
    reset-gpios = <&ioport6 8 GPIO_ACTIVE_LOW>;
    /* PMOD2 pin9P609 -> module pin6 RTC_WAKE_UP (active high pulse) */
    wakeup-gpios = <&ioport6 9 GPIO_ACTIVE_HIGH>;
    /* PMOD2 pin7P414 <- module pin2 INTn (active low, input only) */
    irq-gpios = <&ioport4 14 GPIO_ACTIVE_LOW>;
};
```

### 3.3 为什么需要应用本地 binding

无 compatible 的设备树节点没有 binding,edtlib 不会为其属性生成宏(`DT_..._P_reset_gpios_IDX_0_VAL_pin` 不存在),编译报 undeclared 错误。工程内 `leds` 节点能用,是因为官方板级 dts 已声明 `compatible = "gpio-leds"`。

解决:应用目录会被 Zephyr 构建系统自动加入 `DTS_ROOT`,在工程内新建 `dts/bindings/wifi-da16200.yaml` 声明三个 `phandle-array` 属性即可,无需改动 Zephyr 树:

```yaml
description: DA16200 WiFi module control GPIOs (jumper-wired to PMOD2)
compatible: "renesas,da16200"
properties:
reset-gpios:
    type: phandle-array
    required: true
wakeup-gpios:
    type: phandle-array
    required: true
irq-gpios:
    type: phandle-array
    required: false
```

### 3.4 prj.conf

无需新增配置:`CONFIG_SERIAL` + `CONFIG_UART_INTERRUPT_DRIVEN` 在原工程中已开启。

---

## 4. 驱动架构

### 4.1 对外 API(`da16200.h`,仅两个函数)

```c
intda16200_start(void);      /* 启动连接管理线程,非阻塞 */
bool da16200_is_connected(void); /* 查询实时联网状态 */
```

WiFi 凭据在 `da16200.h` 顶部宏定义(`DA16200_WIFI_SSID` / `DA16200_WIFI_PW`)。`main.c` 中用 `DA16200_ENABLE` 宏整体开关。

### 4.2 收发路径

```
TX: uart_poll_out() 逐字节发送(AT 命令短,简单可靠)
RX: UART 中断 ISR → ring_buf (512B) → read_resp() 带超时收集
```

- `read_resp()` 收集响应时**命中期望子串立即返回**(early-exit),快命令不必等满超时;
- `send_cmd(cmd, expect1, expect2, timeout, retries, retry_delay)` 是统一原语:发命令 → 收集响应 → `strstr` 匹配 1~2 个期望子串 → 失败按间隔重试,全部失败打印原始响应(`da16200: got: ...`)并返回 `-ETIMEDOUT`;
- 与 FSP 示例的差异:FSP 的 `wifi_serial_read()` 是忙等逐字节读 + 1ms 定时器计数超时,会直接卡死调用线程(触发看门狗);本驱动的 ISR+ring buffer 方案不会。

### 4.3 连接管理线程

`K_THREAD_DEFINE(da16200_mgr, 2048, ..., 优先级 12)`,两态状态机:

```
离线态:da16200_init() → da16200_connect_ap()
         失败 → 等 10 s (RETRY_DELAY_MS) 重试
         成功 → link_up = true,转在线态
在线态:每 5 s (CHECK_PERIOD_MS) 发 AT+WFJAP=? 探测
         探测失败(连续2次) → link_up = false,转离线态自动重连
```

应用层(主循环、显示、将来的 MQTT 发布)只读 `da16200_is_connected()`,完全不感知重连过程。WiFi 的长时间 AT 重试都在自己的线程里,主循环 50ms 喂狗不受影响。

### 4.4 初始化时序(`da16200_init`)

```
RTC_WAKE_UP 拉高 ──┐
RESETn 拉低       │ 200 ms   (DPM 休眠中的模块 UART 关闭,先唤醒再复位)
RESETn 释放       │ 50 ms
RTC_WAKE_UP 释放│ 1500 ms(等模块启动)
清空 RX 缓冲      │ 500 ms   (丢掉 +INIT:DONE 启动横幅)
AT\r\n            → 等 "OK"(10 次 × 500ms 重试)
ATE\r\n         → 关回显(非致命,见 §6.2)
```

---

## 5. AT 指令流程

`da16200_connect_ap()` 完整序列(与 FSP 示例一致,去掉 WFCC,增加 DPMKA):

| 步骤 | 命令 | 期望响应 | 单次超时 | 重试 | 说明 |
|------|------|----------|----------|------|------|
| 1 | `AT+DEFAP` | `OK` + `+INIT:DONE,1` | 4000ms | 5×200ms | 清 NVRAM,模块重启进 Soft-AP 默认模式 |
| 2 | `AT+WFMODE=0` | `OK` | 200ms | 5×200ms | 设 station 模式(重启后生效) |
| 3 | `AT+RESTART` | `OK` + `+INIT:DONE,0` | 3000ms | 5×1000ms | 重启应用配置 |
| 4 | `ATE` | `OK` | 200ms | 3×200ms | 关回显(非致命) |
| 5 | `AT+DPMKA=500000` | `OK` | 500ms | 3×200ms | DPM 保活周期(非致命,见 §6.4) |
| 6 | `AT+NWDHC=1` | `OK` | 200ms | 5×200ms | 启动 DHCP 客户端 |
| 7 | `AT+WFJAPA='',''` | `OK` + `+WFJAP:1` | 4000ms | 5×1000ms | 连接 AP |
| 8 | `AT+NWIP=?` | `+NWIP:` + `OK` | 500ms | 3×200ms | 查询分配的 IP(见 §6.5) |

链路探测(在线态每 5 秒):`AT+WFJAP=?`,判定规则见 §6.5。

---

## 6. 固件差异与踩坑记录

以下全部在真机上验证过,**与 UM-WI-003 手册描述不一致之处以实测为准**。

### 6.1 `ATZ` 会重启模块 —— 握手必须用 `AT`

`ATZ` 不是简单的"初始化 AT 命令",它会让模块**重启**。FSP 示例用它做首条命令(它没有 RESETn 硬件复位线,只能靠 ATZ),但如果紧接着发下一条命令,会撞进模块重启窗口,字节被截断成非法命令,模块回 `ERROR:-1`。有 RESETn 硬件复位后,`ATZ` 是多余且有害的,握手改用不触发重启的 `AT`。

### 6.2 `ATE` 无参数 —— `ATE0`/`ATE1` 一律 `ERROR:-1`

DA16200 的 `ATE` 是**无参数的开关切换**(手册参数栏为 none),海思风格的 `ATE0`/`ATE1` 不存在,发了就是 `ERROR:-1`(未知命令)。驱动中只发裸 `ATE` 且非致命——回显状态对解析无影响(`strstr` 匹配)。

### 6.3 `AT+WFCC=CN` 被拒(`ERROR:-4`)

错误码 `-4` = 参数错误。`WFCC` 的地区码受固件管制域表限制,本模块固件不接受 `CN`。处理:整个 WFCC 步骤从流程中删除,模块用默认地区码(默认表覆盖 2.4GHz 1–11 信道)。**注意**:路由器若固定在 12/13 信道,默认地区码扫不到,需把路由器信道改到 1–11。

### 6.4 DPM 休眠 —— 模块会"装死"

DA16200 的卖点是超低功耗,DPM(动态电源管理)使模块空闲时休眠,**休眠期间 UART 关闭**,发什么 AT 命令都没有任何回应(不是 ERROR,是完全静默)。相关事实:

- DPM 配置存 NVRAM,`AT+DPM=0` 可关,但**仅在 station 模式下有效**(在 Soft-AP 模式发送无响应);
- `AT+DPMKA=<ms>` 设 DPM 保活周期(默认 30000ms),值越小模块醒来越频繁、AP 越不容易把睡眠站点踢下线;
- 休眠中的模块只能靠 RTC_WAKE_UP 脉冲或 RESETn 复位唤醒。

### 6.5 `+WFJAP` 响应格式与手册不符 —— 解析不能照抄手册

手册写 `AT+WFJAP=?` 返回 `+WFJAP:1,'<ssid>',<ip>`,本模块固件实际返回:

```
+WFJAP:'SSID',4,2,'pwd'
```

即 `+WFJAP:'<ssid>',<安全协议>,<加密算法>,'<密码>'`,**没有状态字段 1,也没有 IP**。照手册解析会出现两个 bug:

- 链路探测死等 `+WFJAP:1` → 永远失败 → 误判掉线、反复复位重连(在线/离线抖动);
- 从 `+WFJAP` 里按"最后一个逗号后是 IP"解析 → 显示出来的是 SSID。

驱动采用的兼容规则:

- **在线判定**:响应包含 `+WFJAP:` 且不包含 `+WFJAP:0`(断开)即在线——两种格式都满足;
- **IP 获取**:改用 `AT+NWIP=?`(返回 `+NWIP:0,<ip>,<掩码>,<网关>`,取第二字段),不从 `+WFJAP` 猜。

### 6.6 看门狗(WDT)两个坑

- **`install_timeout` 返回 -22 (-EINVAL)**:RA WDT 驱动要求 `callback == NULL` 时 `flags` 必须带复位标志。`main.c` 需显式 `.flags = WDT_FLAG_RESET_SOC`,否则看门狗从未真正武装(移植初期遗留 bug)。
- **武装时机**:WiFi AT 重试(最坏 ~15s)和 SHT30 初始化(~5s)都会超过 5s 窗口。若在这些阻塞流程之前武装,WDT 会在主循环第一次喂狗前复位 MCU(实测差 100ms 顶爆窗口)。当前结构:WiFi 在独立线程、SHT30 初始化完成后才 `wdt_setup_and_start()`。
- **失败路径不能 `return`**:SHT30 初始化失败若从 `main()` 返回,主线程退出 → 无人喂狗 → 5s 后复位 → 下次启动又失败,无限复位循环。失败必须降级运行(`sht30_en = false` 继续主循环)。

### 6.7 I2C 总线卡死只能断电恢复

WDT 复位若正好打断 I2C 传输,SHT30 会拉住 SDA 不放;MCU 复位清不掉(传感器无复位脚),RA 的 I2C 驱动也未实现 `i2c_recover_bus()`。症状是整片总线 `-5 (EIO)`,**只能整机断电 10 秒以上恢复**(按复位键无效)。

---

## 7. 应用集成

`main.c` 中的接入点:

```c
#define DA16200_ENABLE 1          /* 0 = 完全关闭 WiFi */

/* main() 启动阶段(非阻塞): */
da16200_start();

/* 主循环 OLED 状态行(PAGE_WIFI = 3, rows 24-30),实时刷新: */
ssd1306_fb_puts(0, PAGE_WIFI,
                da16200_is_connected() ? "WiFi connected" : "WiFi offline");
```

启动日志:

!(https://www.eefocus.com/forum/data/attachment/forum/202607/31/215545z18610my6r860186.png)



---

## 8. 调试指南

驱动失败时会把模块原始响应打到控制台(SCI9,115200):

| 日志 | 含义 | 排查方向 |
|------|------|----------|
| `da16200: uart0 not ready` | SCI0 设备未就绪 | 设备树/board 配置 |
| `no match for AT` + `got:` 为空 | 模块完全无响应 | 供电、TX/RX 交叉、RESETn 接线、模块 DPM 休眠(断电冷启动验证) |
| `got: ERROR:-1` | 未知命令 | 命令写法(ATE0、ATZ 重启窗口等,见 §6.1/§6.2) |
| `got: ERROR:-4` | 参数错误 | 参数是否超出固件支持范围(如 WFCC=CN,见 §6.3) |
| `no match for AT+WFJAPA` | 连不上 AP | SSID/密码、2.4GHz(不支持 5G)、路由器信道 1–11 |
| `da16200: connection lost` | 在线探测失败 | 看下一行 `got:` 是空(模块休眠/掉线)还是有响应(解析规则不匹配,见 §6.5) |
| `da16200: offline, retry in 10 s` | 离线态退避重试 | 属正常状态机行为,持续出现则按上面逐项查 |

验证自动重连:断开路由器电源或让模块远离信号源,OLED 应变 `WiFi offline` 并打印 `connection lost`;恢复后约 10 秒内自动重连。

---

## 9. 参考资料

- UM-WI-003《DA16200 AT Command User Manual》—— AT 指令权威参考(注意 §6 中实测与手册的差异)
- Renesas FSP 示例 `RA6M4_DA16200_MQTT_CLIENT`(`src/da16200_AT.c`)—— AT 序列与重试策略来源
- 本工程驱动:(./src/drivers/da16200.c) / (./src/drivers/da16200.h)
- 设备树:(./ek_ra6m4.overlay)、(./dts/bindings/wifi-da16200.yaml)
- 基础工程移植:(./PORTING.md)

---


页: [1]
查看完整版本: 【瑞萨RA-eco-RA6M4评测】Zephyr驱动DA16200 wifi模块