
拿到这块瑞萨的BLE/WiFi评估板已经有一阵子了断断续续折腾了几个晚上总算把WIFI例程跑通并且用TCP通讯把数据从前端设备传到了PC上的网络调试助手。整个过程有惊喜也有踩坑今天把完整的测试过程、代码层面的关键点、环境搭建的细节以及那几个折腾我最久的坑全部整理出来希望能给正在做物联网设备选型或者调试瑞萨无线模块的朋友一点参考。先说结论如果你做的是IoT节点、传感器网关、智能家居这类需要稳定联网但不追求超高吞吐的产品瑞萨这个BLE/WiFi二合一模块的完成度是挺高的。尤其是它把射频前端、协议栈、低功耗策略都封装好了MCU端只需要通过串口或者SPI读写数据基本不用碰射频调试这种麻烦事。下面进入正题。1. 瑞萨BLE/WiFi模块的本质一块把复杂留给自己、简单留给用户的射频板1.1 为什么我会关注这个二合一方案做嵌入式联网产品最常见的一个纠结就是WiFi和蓝牙怎么选。蓝牙功耗低、配对简单但传输速率有限穿墙能力弱WiFi吞吐高、覆盖好但传统的WiFi方案在协议栈、射频匹配、天线设计上都有门槛。我以前用某成熟方案自研过WiFi模组天线匹配和阻抗调试那块真是费了大劲手里一台频谱仪都快被我用秃了。瑞萨这块板子的思路不一样它在板级把WiFi SoC和BLE SoC都集成了并且通过一套FSPFlexible Software Package统一管理。也就是说你不需要自己去画射频电路、匹配天线、集成协议栈主控MCU只需通过串口或SPI发AT指令或调用底层APIWiFi和蓝牙的能力就直接开放出来了。这种“通信黑盒化”的做法对产品开发极其友好——你按功能调函数就行射频指标和无线协议栈基本不用碰。我在实际测试中一直用到的组合是RA6M5主控 DA16200 WiFi SoC 蓝牙子系统的评估套件。RA6M5是瑞萨的Arm Cortex-M33核MCU跑FSP生态DA16200则是瑞萨收购Dialog之后拿到的超低功耗WiFi SoC内部已经集成了TCP/IP协议栈和WPA2安全引擎。BLE部分则依托Dialog系的无线上层框架。之所以选这个组合来写测评是因为它的开发链路足够典型RASC工具生成底层配置、Keil MDK做编译调试最后在串口日志里观察WiFi扫描、连接、DHCP获取IP再到TCP socket收发。这套链路摸熟了以后换其他瑞萨RA系列的无线方案也基本是同样的玩法。1.2 模块硬件架构与我的选型理由既然标题是测评还是先把硬件层面的评估讲清楚。评估板的主体是一颗主控MCU具体型号是RA6M5系列芯片Cortex-M33内核带DSP指令集和TrustZone。WiFi能力来自DA16200 SoC它最大的特点是低功耗做得非常极端MCU几乎全程休眠只靠WiFi模块维持连接的场景也能跑很久这是很多传统WiFi方案做不到的。BLE部分则由板载蓝牙SoC承担支持BLE 5.x规范可以和手机App做近场配置、OTA升级之类的辅助工作。选用这个方案我最看重的是以下三件事。第一WiFi模块内嵌TCP/IP协议栈主控MCU不需要承担繁重的网络协议处理任务。这意味着MCU的主频和Flash资源可以更多地留给业务逻辑。第二模块与MCU之间用UART/SPI隔离软件层面天然解耦MCU崩溃重启不会把射频状态搞乱。第三瑞萨的FSP框架把外设驱动、无线协议栈、RTOS集成到了一起配置界面里勾选一下就能生成代码省掉了大量手写驱动和调试驱动的功夫。从实测来看这套板子给我的印象是“稳”字当头。扫描周围AP、连接WPA2加密路由器的过程都很干脆没有看到掉线重连或者丢包率异常的情况。不过它也有不太完美的地方模块的例程框架比较“干净”默认的Demo没有把高级调试接口都打开很多运行细节要靠串口日志去观察初学者刚上手的时候可能要想一会儿才能找到log入口在哪里。这部分我在后面实操章节里给出了具体定位方法。2. 开发环境搭建RASC生成工程 Keil MDK编译的完整流程2.1 先把工具链配齐版本匹配是头号问题搭建瑞萨RA系列开发环境我用的工具是这样一套Keil MDK 5.x建议用较新的5.38以上版本对Cortex-M33和TrustZone的支持更友好RASCRA Smart Configurator独立配置工具版本建议与FSP包版本对应我用的是RASC v5.x FSP 5.x的组合FSP包由瑞萨提供的外设驱动库和无线协议栈组件装完RASC后会自动关联板载调试器驱动一般J-Link或者板载自带调试通道都行串口调试助手SSCOM或者MobaXterm的串口模式都行我当时折腾最久的就是工具版本匹配问题。RASC生成的工程文件如果FSP版本和RASC内部集成的版本不一致生成出来的代码可能在Keil里编译报一堆结构体未定义、宏没找到的错误。后来我统一了RASC和FSP版本再把Keil的RTE插件也更新到对应版本问题才消失。这里强烈建议你下载工具时直接看瑞萨官方发布列表把三者版本对齐不要急着用最新版。2.2 RASC里配置外设的关键步骤打开RASC新建工程选择对应的评估板型号比如我选的是RA6M5系列评估板对应的Board项工具链选择Keil MDK。创建完成后会进入FSP配置界面。在这个界面里我需要做以下几件关键配置系统时钟配置主时钟源。评估板默认有外部晶振一般把系统主频配到240MHz即可。如果时钟配错串口波特率会漂移打印日志直接乱码。串口调试通道把板载的USART配置为115200-8-N-1。这个串口用来打印WiFi例程的日志同时也是我观察模块状态的主要窗口。WiFi模块通信接口DA16200与主控之间通过UART交互我在FSP里新建一个UART或SCI通道配置波特率通常为115200或者更高具体看模块固件要求并打开DTC中断收发防止大数据包把串口缓冲打爆。BLE子系统初始化评估板带有蓝牙功能例程默认会初始化BLE部分。如果只测WiFi可以把BLE部分暂时跳过省掉一部分初始化时间。中断和RTOS配置例程用了FreeRTOS做任务调度网络收发、按键事件、LED指示分别在不同任务里处理RASC会自动帮这部分生成线程栈分配。这些都是图形化界面里勾选或者填数字就能完成的操作但对背后的原理要有清晰认识。比如串口波特率配置本质是给串口外设分配时钟分频系数如果分频后的实际波特率和预期差异超过3%高速收发就会出现误码。RASC界面里会直接显示配置后的实际波特率值你只要确保它显示的数值接近115200就行。2.3 Keil里编译、烧录与串口验证RASC生成完代码后直接打开生成的Keil工程文件在“Manage Run-Time Environment”里确认用到的组件都已经勾上然后执行编译。第一次编译会花一些时间因为FSP会把一堆驱动源文件都编进去。编译通过后用板载调试器连接目标板我习惯用Keil自带的Flash Download功能直接烧录然后再按复位键让程序从头跑。串口连上调试通道打开115200波特率查看日志如果看到类似“WIFI module initialization done”之类启动信息说明环境搭建已经完成接下来才是重头戏——真正把WiFi联网跑起来。这里特别提醒一点如果串口打出来是满屏的乱码不要急着怀疑线接错了先看波特率是不是真的有115200再看系统时钟是不是没配好。我那次乱码就是因为时钟树里没选对外部晶振频率导致串口实际波特率偏移到了107000左右肉眼当然看不出乱码和正规字符的区别重新配置时钟后一切正常。3. WIFI例程测试从扫描AP到成功联上网3.1 例程的主流程到底做了什么WiFi例程的源码结构其实很清晰核心逻辑从连接上电后开始按顺序执行。第一步初始化DA16200模块的硬件接口并发送复位命令让SoC进入工作状态第二步调用扫描接口搜索周围可用的无线接入点第三步根据代码里预设的SSID和密码连接指定路由器第四步启动DHCP客户端获取IP地址最后把获取到的网络信息通过串口打印出来。用一个简单的代码逻辑来概括大致是这个样子示例代码实际例程里函数名会有差异明白流程是关键wifi_init(); // 初始化串口或SPI链路复位模块 wifi_scan(); // 扫描AP返回AP列表 wifi_connect(MySSID, MyPass, SECURITY_WPA2); // 连接路由器 wifi_dhcp_start(); // 启动DHCP等待获取IP printf(IP: %s\r\n, wifi_get_ip()); // 打印得到的IP地址这段逻辑看起来简单但背后有两个值得展开的点。第一个是扫描AP的过程。DA16200扫描后会把每个AP的SSID、BSSID就是AP的MAC地址、信号强度RSSI、加密方式和信道都返回回来。在复杂射频环境下例程里要做一次排序或者过滤优先选择信号强且信道空闲的AP连接。不过例程为了方便通常直接按代码里写死的SSID去匹配。第二个是WPA2安全认证。连接加密路由器时模块需要完成4次握手以验证密码并协商加密密钥。这个阶段如果密码不对或者路由器不支持模块默认的加密方式就会卡住然后返回连接失败。我测试的路由器用的是WPA2-PSKAES模式DA16200对这种最常见配置支持得非常成熟几乎是一秒内就能完成认证。3.2 修改SSID和密码连接自己的路由器在实际测试中我需要把例程里预设的SSID改成我工作室的无线网络。位置通常在例程文件的配置头文件里比如wifi_config.h。修改项是一个结构体数组里面定义了SSID、密码和加密模式static wifi_ap_config_t g_ap_config { .ssid TestRouter_2G, .password 12345678, .security WIFI_SEC_WPA2, };这里有几个细节要特别注意算是这次测试踩过的坑。第一路由器一定要使用2.4G频段例程默认只扫2.4G的WiFi。你的路由器如果开了5G和2.4G合一的双频段模块扫描到的可能是5G那个SSID导致连接失败。最稳的做法是在路由器管理后台把2.4G单独开一个SSID或者暂时关闭5G来测。第二加密方式尽量用WPA2-PSK。一些新的路由器默认是WPA3或者WPA2/WPA3混合模式DA16200老固件不一定支持会返回认证失败。我把路由器改成WPA2-PSK后连接一次成功。第三SSID不要有特殊字符纯字母数字最保险。修改完成后重新编译烧录打开串口观察日志这次看到的关键输出大致是Start WiFi scan... Scan done. 6 AP found. AP[0]: TestRouter_2G ch:6 rssi:-42 security:WPA2 ... Connecting to TestRouter_2G... WiFi connected! IP: 192.168.31.45看到这样的输出就说明模块已经成功连上路由器并从路由器的DHCP服务器拿到了IP地址。3.3 模块为什么能拿到IPDHCP与MAC地址那些事拿到IP这个动作很多人觉得理所当然但实际背后包含一个DHCP的四阶段交互。模块广播一条DHCP Discover消息寻找网络里的DHCP服务器一般是家用路由器路由器回复Offer给出可用的IP地址和子网掩码模块再发Request确认自己要这个IP最后路由器回ACK分配完成。整个过程也是通过抓包可以清晰看到的。这里有个有趣的技术点模块的MAC地址是出厂固化的路由器通过MAC地址来标识这个设备。所以哪怕你断电重启再上电路由器DHCP服务大概率仍会把同一个IP分配给它。同时如果产品要过大规模部署MAC地址管理是个容易忽略的环节——出厂的MAC地址段可以通过AT指令读取生产时最好逐一记录方便售后定位设备。在例程里还可以看到静态IP的配置接口如果不想依赖DHCP可以直接手动指定IP、掩码和网关。我调试时为了简化网络环境一度想把IP固定为192.168.31.100例程里预留了关闭DHCP并设置静态IP的函数注释写得也很清楚按需打开即可。3.4 验证网络连通性Ping网关还不够连接上WiFi之后要验证网络链路是不是真的通了最直接的方法是用模块的ping功能去探测网关或者服务器。例程里有一个ping示例也有对应的AT指令方式不过从应用维度看比ping网关更有意义的是向公网服务器发起DNS解析测试。我在测试中让模块去解析一个外网域名如果解析成功说明不仅局域网链路正常DNS转发也有效模块的上网通道才是真正打通了。此时我打开电脑用命令行ping模块获取到的IP192.168.31.45得到了几个正常的响应说明板卡到PC之间的二层链路也没问题。无线链路稳定性的直观判断可以看连续ping几十次的丢包率。我目前测试的固件版本下从PC ping模块所在的IP大概发了30个包全部收到回复延迟在2到5ms之间具备继续跑TCP通讯的良好基础。4. TCP通讯测试让数据真正跑起来并看懂三次握手4.1 搞清协议栈在哪一层工作才能少走弯路TCP通讯的实现在WiFi模块里分为两种常见架构。一种是模块只负责把IP层数据跑通TCP/IP协议栈由MCU自己运行比如MCU上跑LwIP模块承担“射频网卡”的角色。另一种是模块内部直接集成了完整的TCP/IP协议栈MCU通过socket API或者AT指令控制模块建立连接这种方式下MCU完全不碰TCP状态机。DA16200走的是第二种路线。它的内部固件自带TCP/IP协议栈甚至支持多路socket并发。使用中我只需要在MCU侧调用模块SDK封装好的socket接口比如创建socket、连接远端IP和端口、发送数据、接收数据模块会把TCP分段、ACK确认、重传等机制全部消化掉。用生活化的类比来讲MCU层像是打电话的人它只管拿起电话拨号、开口说话而DA16200的协议栈像是运营商网络负责接通线路、保证语音连续和断线重连。你不需要自己去铺设电话线也不需要理解交换机信号是怎么转发的。这个架构的优点显而易见——大幅降低MCU开发难度尤其适合主控MCU资源有限的应用场景。4.2 TCP Client代码解析从socket到收发数据例程里的TCP Demo结构很典型我用代码形式带大家过一遍关键流程。它先创建一个TCP socket然后连接目标服务器的IP和端口连接成功后进入数据收发循环int sock socket(AF_INET, SOCK_STREAM, 0); // 创建TCP socket struct sockaddr_in server; server.sin_family AF_INET; server.sin_port htons(8080); // 服务器端口 server.sin_addr.s_addr inet_addr(192.168.31.88); // 服务器IP int ret connect(sock, (struct sockaddr *)server, sizeof(server)); if (ret 0) { send(sock, Hello, Renesas WiFi!, 20, 0); int len recv(sock, buffer, sizeof(buffer), 0); } close(sock);这里面的几个细节比代码本身更值得展开。第一socket()返回的是一个非负整数作为句柄。如果返回-1说明模块内部的socket资源已经耗尽通常是上一个连接的TIME_WAIT状态还没释放或者连接数超过模块固件的限制。释放连接后最好间隔几秒再重连。第二connect()是阻塞阻塞式还是非阻塞式取决于模块SDK的配置。例程里默认是阻塞模式如果远端服务器不可达这个函数可能会长时间不返回。实际产品里一定要设置连接超时比如10秒超时后主动关闭socket并重新发起连接避免出现“死等”现象。第三send()和recv()的返回值要检查。TCP是流协议发送方调用一次send()并不保证对端能一次性收到完整数据接收方recv()也可能只返回一截数据。正确做法是循环收发直到收满预期字节数或者超时。第四端口字节序问题。服务器端口号在网络字节序中是大端存储这里用htons()转换是必须的否则后端服务器看到的端口会变成一个莫名其妙的数字。我在初学阶段栽过好几次跟头排查到最后都是这个细节。4.3 PC端搭建TCP Server与模块联调实测要在PC上模拟一个TCP服务器方式非常多。最简单的就是打开网络调试助手开一个TCP Server服务监听8080端口。我这次测试用的还有自己写的一个Python脚本用自带的socket库完全够用而且方便观察收发数据的时间戳import socket srv socket.socket(socket.AF_INET, socket.SOCK_STREAM) srv.bind((0.0.0.0, 8080)) srv.listen(1) print(TCP Server listening on 8080...) conn, addr srv.accept() print(Client connected from:, addr) conn.sendall(bWelcome to TCP Server!) while True: data conn.recv(1024) if not data: break print(Recv:, data.decode(errorsignore)) conn.sendall(bACK: data) conn.close()启动脚本后模块例程会自动发起TCP连接PC端终端会打印出连接来源和收到的数据。如果此时PA使用网络调试助手界面里那个“连接状态”会从“未连接”变成“已连接”然后就可以用界面上的发送框给模块下发数据。我实测中把模块的串口日志打开能看到它在收到数据的同时也会打印“Socket data received”的log。这样双端验证才能确认TCP通路是双向可靠的。需要重点检查的是PC防火墙。第一次测试时模块连接超时了整整一分钟我下意识以为是固件问题后来差点要换板。直到打开PC防火墙的入站规则发现Python进程没有被允许监听TCP端口正是这一点导致模块发来的SYN包直接卡在防火墙外。修改防火墙规则或者在防火墙上放行8080端口后重新运行模块Demo连接立刻成功。排查这种问题最快的办法是在PC上跑一个抓包工具看看有没有收到模块发来的SYN请求——有SYN请求却没有后续那就是PC侧拦截了连SYN请求都没有那就是网络链路或者AP隔离的问题。4.4 用Wireshark抓包亲眼看看TCP三次握手长什么样TCP三次握手是几乎所有嵌入式网络开发者都听过但很少亲眼验证过的知识点。在这次实测中我特意在PC端用Wireshark抓取了模块连接TCP Server的完整报文序列。过程是这样的模块发起连接时Wireshark的报文列表里出现了三条典型的报文。第一条是模块IP向服务器IP发送的SYN报文TCP头部里SYN位为1序列号是一个随机的初始序列号。第二条是服务器回发的SYNACK报文SYN位和ACK位同时为1确认号是刚才那个序列号加1。第三条是模块发出的ACK报文确认号也相应加1。三条报文来回交错一个TCP连接就这样建立了。从应用角度看三次握手最大的意义在于确认双方的收发能力都正常。SYN是“我能听到你吗”SYNACK是“我听到了你能听到我吗”最后的ACK是“我也听到你了”。确认完之后才开始传业务数据这就是为什么TCP是可靠连接而UDP是尽力而为最直观的区别。我还顺手查看了后续数据传输报文的序列号关系。每发一条数据序列号都会在前一条基础上增加数据字节数接收方回复的ACK号则指向期望收到的下一个字节位置。比如第一次发送20字节数据下一个包的序列号就比上一个包大20服务器回包的ACK号也是这个偏移后的值。这就是所谓的“可靠传输”的底层机制——通过序号和确认号双方可以精确地知道哪些数据已被正确接收哪些需要重传。抓包除了好玩更大的价值是定位问题。有一次我测试数据总是不定时丢失用Wireshark一看发现服务器已经回ACK了但模块没有重传任何报文。后来定位到是模块侧接收缓冲太小应用层调用recv()不及时导致模块协议栈把数据丢了。抓包能帮你把问题定位到“对端没发”还是“发了但本端没处理好”这两个完全不同的排查方向上。5. 常见问题与排查经验实录5.1 连接不上AP的几类典型原因WiFi连接失败是我在测试中遇到最多的问题基本上每次换一个网络环境都会碰到一次。我把常见的失败原因整理成了一张速查表方便你按顺序排查现象可能原因排查方法扫描不到目标AP路由器开了5G频段模块只支持2.4G关闭双频合一单独开启2.4G扫描不到任何AP模块天线未接好或射频环境异常检查天线连接换到路由器旁测试连接超时/认证失败加密方式不匹配路由器改成WPA2-PSKAES模式密码错误SSID或密码配置不一致逐字符核对特别注意空格字符连接上但拿不到IPDHCP服务问题或MAC过滤查看路由器DHCP客户端列表检查是否启用MAC过滤连接不稳定频繁掉线信号弱或路由器信道拥挤查看RSSI数值调整模块位置或路由器信道排查第一个问题时最蠢但最有效的办法就是先放下代码直接用手机连一下同一个路由器确认这个网络本身是好的再回过来看模块。5.2 TCP连接失败IP、端口、防火墙三板斧TCP连接失败的问题我在第一次跟踪抓包的时候几乎全部遇到了但排查思路很固定。第一步先确保模块和PC能互相Ping通。如果Ping不通回到网络层去查这大概率是AP隔离、IP不在同一网段或者防火墙拦了ICMP。第二步检查TCP Server有没有真的在监听。我一般在PC端运行netstat -an | findstr 8080看监听状态和地址是否正常。如果显示监听在127.0.0.1而模块连的是192.168.31.88那肯定是绑定地址写错了应该绑定0.0.0.0。第三步再回过头看模块的日志终端返回的socket返回值是不是负数以及错误码到底是在连接阶段还是读写阶段跳出来的。三次握手超时是另一类麻烦。有一次换了台电脑跑TCP Server怎么连都失败。抓包后发现模块发出SYN后PC端一直没有回应SYNACK。我一看果然这台电脑的防火墙拦了入站TCP连接。打开入站规则后问题消失。如果你手里的设备不能开放防火墙端口可以临时把网络配置文件从“专用网络”改成“公共网络”并允许应用这是联调阶段最省时间的做法。5.3 数据收发不正常粘包、缓冲和线程调度TCP通讯跑通之后数据处理成了新的关注点。第一次往模块发长数据包时我在PC端一次发送了500字节模块端recv()返回的数据长度并不是500而是分成了好几段比如前128字节一段后面几次收到的是剩余数据的一部分。这是TCP流协议的本质特征所谓“粘包”和“拆包”都是数据流被应用层读写节奏切割后的表现不是WiFi模块丢了数据。解决办法是在应用层做数据边界管理。可以约定一个固定长度的包头里面带上整个数据的长度字段也可以用一个简单的协议帧以帧头和CRC校验为标志来切割完整报文。我在例程中看到的做法是用了一个环形缓冲区把recv()收到的数据先暂存起来处理任务再从缓冲区里按字节流读取解析直到提取出一个完整协议帧后才交给业务逻辑。这个模式非常实用建议做无线产品应用层协议的时候直接抄。还要注意recv()的调用节奏。如果MCU在收发大数据的同时还有频繁的串口打印和LED刷新任务而模块的接收缓冲区不够大模块协议栈可能会因为来不及把数据交给MCU而丢弃部分报文。降低串口日志的打印频率或者把接收线程优先级调高能明显缓解这个问题。5.4 我踩过的其他小坑与操作心得再补充几条更偏“经验”层面的东西这些内容一般文档里看不到。第一测WiFi模块一定不要选在办公区这种满是AP的环境里做密集度测试因为2.4G信道拥挤会导致扫描列表极其杂乱干扰判断。最好在家里或者实验室这种AP数量少、信道干净的环境先把基础用例跑通。第二天线方向和位置对RSSI影响很大尤其是模块放在金属桌面或者机箱旁边的时候。测试时尽量把天线竖直摆放与路由器之间不要有厚墙遮挡信号强度低于-70dBm时不要直接断言模块性能差先调整位置再复测。第三如果模块出现连不上AP或者TCP不稳定的情况最简单有效的恢复手段是断电重启整个板子而不仅仅是按复位按键。我遇到过模块内部状态机在异常重连后进入僵死状态的情况完全断电可以恢复到初始状态。还有一点是关于调试日志的。DA16200模块支持不同的日志等级从错误到详细调试信息都能输出。默认例程开的日志等级比较简洁其实可以打开debug级别的日志来观察更多协议栈内部状态。我当时把日志级别调高之后看到了模块尝试DHCP未收到ACK、WPA握手重传等更底层的细节对定位问题帮助巨大。日志开关的位置通常在模块驱动层的配置宏里花点时间翻一下源码就能找到。结语这半个多月折腾下来的一些体会说实话把WiFi例程跑通不算什么难事真正有价值的是把TCP通讯过程中每一个看不到的环节都弄明白。从RASC构建工程到烧录后串口打印IP地址再到Wireshark里亲眼看到SYN/SYN-ACK/ACK三条报文这一整套链路走完你对嵌入式联网产品的理解会比看十篇文档都深。最后再分享一个小技巧在跑TCP通讯联调时尽量让模块端定期发送心跳包到服务器服务器端也做个超时断连检测两边都有日志和状态指示。这样当问题发生时你能快速清楚是哪一端先出了问题不至于两边同时怀疑对方。我这次测试的例程没有默认带心跳机制是自己在Demo基础上加了几行定时器代码之后才体会到它的好处。如果你接下来想做更复杂的产品原型建议一开始就把断线重连和心跳保活这两个机制加进去省得后面回头来补。