干嵌入式开发这些年,我一直觉得“给设备加蓝牙”这件事,看起来简单,做起来全是细节。最近刚好用瑞萨 RA4M2 做主控、Dialog DA14531 做 BLE 从机,搭了一条从串口到手机 App 的双向透传链路,也就是标题里写的这条:RA4M2 通过 UART 发数据给 DA14531,DA14531 用 BLE 通知发到手机;手机写特征值,数据再从 DA14531 的 UART 吐回给 RA4M2。整个过程踩了不少坑,也沉淀了不少经验,这篇就当作一次完整的实战复盘,把我调试 UART 驱动、配置 DA14531 透传工程、用手机工具联调抓包的思路都梳理出来,给正在做类似低功耗采集设备或者蓝牙桥接方案的朋友一个参考。
先交代一下场景。我手上是一块基于 RA4M2 的低功耗数据采集板,负责采集传感器数据、做简单处理,需要把数据实时送给手机看,同时也要能接收手机下发的配置命令。RA4M2 是 Cortex-M33 内核,性能和功耗平衡得不错,瑞萨的 FSP 配置工具用起来也顺手;但让 MCU 自己去跑 BLE 协议栈,还得过射频认证、调天线匹配,对项目周期不友好。所以我把蓝牙剥离开,单独用 DA14531 这颗业界出了名的低功耗 BLE SoC 来承担无线链路,两片芯片之间只用 UART 连接,各司其职。这个方案最直接的好处是:主控端只需要把串口调稳,蓝牙端只关心怎么把串口数据“搬到” BLE 上,两边可以并行开发、独立验证,联调时也容易定位问题。
1. 项目整体架构与方案选型思考
1.1 为什么是 RA4M2 加 DA14531 的组合
先说 RA4M2。这颗芯片在瑞萨 RA 家族里属于入门到中端的定位,Cortex-M33 内核,主频可以跑到 100MHz 左右,片上带了 UART、SPI、I2C、ADC、定时器这些常规外设,运行模式和休眠模式下的功耗数据都挺好看。FSP(Flexible Software Package)这套配置工具是我比较推荐的原因,图形化界面里勾选外设、分配引脚、生成初始化代码,比对着寄存器手册初始化省太多事,而且代码分层清晰,后期维护成本低。选它做主控,主要是看中了它的低功耗表现和丰富的外设资源,毕竟采集设备随时可能要进休眠,又不能因为进了休眠把串口数据丢掉。
DA14531 则是 Dialog 半导体主推的超低功耗 BLE SoC,封装极小,还出了模块版本,内置天线和晶振,非常适合空间受限的产品。它对标的应用场景就是智能家居、信标、便携医疗设备这类对功耗苛刻的东西。相比直接用 MCU 片上的 BLE,或者用 ESP32 这种 WiFi/BLE 双模方案,DA14531 的优势在于 BLE 协议栈由芯片厂商做好了,功耗经过深度优化,TX 峰值电流能做到毫安级别,待机电流更是可以做到微安级别。而且它的 SDK 里带了不少官方应用例程,其中就有串口透传(SPS,Serial Port Service)的完整工程,几乎就是为这种“MCU + 蓝牙桥接”的场景准备的,我在这篇文章里也会重点讲这个例程的改造。
还有一个很现实的原因:如果让 RA4M2 自己带上 BLE 协议栈,射频前端匹配、天线区域布局、蓝牙认证这些都是额外的工作量,对大多数项目来说是不划算的。用 UART 桥接两颗芯片,等于把“无线连接能力”模块化,哪颗蓝牙芯片有问题就换哪颗,主控逻辑完全不受影响。这种解耦在量产选型的时候尤其重要——今天用 DA14531,明天想换成 nRF52832,主控端代码基本不用动。
1.2 双向透传数据通路设计
整个系统的数据流可以分成两条方向,理解清楚这两条链路,后面的驱动调试才有参照系。
下行方向(手机到设备):手机 App 向透传服务的 RX 特征值写入数据,DA14531 在 GATT 写请求回调里拿到这包数据,马上通过 UART 的 TX 引脚发送给 RA4M2 的 RX 引脚。RA4M2 在串口中断里收到字节,按自己的协议解析处理。这条路径的关键在于:GATT 写请求如果是 Write Without Response,速度很快,但 DA14531 往 UART 写是串行的,如果手机连续写入的速度超过 UART 发送速度,缓冲就会积压,必须有流控机制来兜底。
上行方向(设备到手机):RA4M2 通过 UART TX 把数据发给 DA14531 的 RX,DA14531 在串口中断里把字节收进缓冲区,然后通过 GATT 的 Notification 把数据作为通知包推送给手机。BLE 一次通知能承载的字节数是受 MTU(Maximum Transmission Unit)限制的,默认 23 字节,协商提升后能到 247 字节,所以固件里要做分帧逻辑,把一串串口数据按 MTU 大小切片,逐包发出去。
这两条链路拼起来,就是一个“双向透传”。需要注意,透传不等于两条 UART 直连线那么简单,BLE 和 UART 的速率天然不匹配:UART 是连续字节流,BLE 是分包突发。所以 DA14531 端必须设计合理的缓冲区,RA4M2 端也要做好串口接收缓存,否则高负载下必丢数据。
硬件连接上,RA4M2 的 UART 引脚和 DA14531 的 UART 引脚要交叉连接:RA4M2 TX 接 DA14531 RX,RA4M2 RX 接 DA14531 TX,两边共地。如果模块支持硬件流控,建议把 RTS/CTS 也接上,其中 RA4M2 的 RTS 接 DA14531 的 CTS,RA4M2 的 CTS 接 DA14531 的 RTS。我后面会专门说流控这件事,因为它在透传场景里的作用很容易被忽略。
2. RA4M2 端 UART 驱动开发全流程
2.1 UART 协议参数确认与工程配置
UART 本质上就是两根线,一根发送一根接收,双方事先约定好波特率、数据位、停止位、校验位,才能把电平信号还原成正确字节。我习惯用一个生活化的比喻:UART 就像两个人隔着窗户传纸条,纸条上的字是数据,传递速度是波特率,双方得约定好“多少秒传一个字”“一句话几个字”“最后一句话怎么结尾”,否则对方打开纸条看到的全是乱码。
在这个项目里,我使用的是标准 8N1 配置:8 个数据位,无奇偶校验(None),1 个停止位。波特率方面,常规 115200 是默认选择,但如果你对吞吐率有要求,建议直接按 230400 或 460800 配置,原因后面我分析吞吐率瓶颈时会提到。表格里是我这套系统最终确认的 UART 参数,建议直接参照:
| 参数项 | 配置值 | 说明 |
|---|---|---|
| 波特率 | 460800 | 高吞吐率需要,115200 只适合低频小数据量 |
| 数据位 | 8 | 标准字节宽度 |
| 校验位 | None | 透传场景不需要额外校验,BLE 层已有 L2CAP 校验 |
| 停止位 | 1 | 标准 8N1 |
| 流控 | RTS/CTS 硬件流控 | 防止高负载下 UART 缓冲溢出 |
在 RA4M2 上,我用瑞萨官方 e² studio 配合 FSP 配置器完成初始化。新建工程选好 MCU 型号后,在 FSP 的 Stacks 页面添加一个 UART 驱动模块,然后在 Pins 页面把对应引脚分配出来。以我手头这块板子为例,UART 引脚分配情况类似这样:发送引脚和接收引脚各占一个 GPIO 复用功能,RTS/CTS 也是独立的复用脚。具体引脚号取决于你用的开发板,EK-RA4M2 上一般把串口引出到了扩展接口,原理图里会标得很清楚。
FSP 里配置 UART 的关键字段包括:Baud Rate填 460800,Data Bits选 8,Parity选 None,Stop Bits选 1,Flow Control选 RTS/CTS,然后给回调函数起一个名字,比如uart0_callback。完成配置后 Ctrl+S 保存,FSP 会自动生成初始化代码,你不用手写寄存器操作,但建议点开hal_data.c看一眼驱动的实现,因为后面调试中断和 DMA 时,你要知道底层到底走了什么逻辑。
2.2 中断接收与发送完成事件
RA4M2 的 FSP UART 驱动是事件驱动模型,初始化完成后,串口数据到达会触发回调函数。我在工程里没有用轮询读取,因为采集设备要兼顾低功耗,CPU 不能一直死等。回调函数里我做的主要工作是:收到一个字节就存入环形缓冲区,同时置一个标志位通知应用层有数据到达;发送完成时置另一个标志位,这样业务层可以安全地释放发送缓冲区。
下面这段代码是 FSP 生成框架下的典型回调写法,我做了简化去掉业务细节,重点展示结构:
#define UART_RX_BUF_LEN 512 static volatile uint8_t uart_rx_buf[UART_RX_BUF_LEN]; static volatile uint16_t uart_rx_head = 0; static volatile uint16_t uart_rx_tail = 0; static volatile bool uart_tx_done = false; void uart0_callback(uart_callback_args_t *p_args) { if (p_args->event == UART_EVENT_RX_CHAR) { uint16_t next = (uint16_t)((uart_rx_head + 1) % UART_RX_BUF_LEN); if (next != uart_rx_tail) { uart_rx_buf[uart_rx_head] = (uint8_t)p_args->data; uart_rx_head = next; } } else if (p_args->event == UART_EVENT_TX_DATA_COMPLETE) { uart_tx_done = true; } }调用发送时,我使用的是R_UART_Write(),传入待发送缓冲区指针和字节数。这里有个容易踩的坑:R_UART_Write()是异步接口,函数返回只代表数据已经交给硬件 FIFO 开始发送,并不代表发送完成了。很多新手在发送完立刻修改缓冲区内容,结果发现发出去的数据是错的。所以我的习惯是,发送前清空uart_tx_done标志,调用R_UART_Write()后等待回调置位,再进入下一次发送。
接收方面,虽然 FSP 也支持 DMA 接收和R_UART_Read()批量读取,但我实测下来,在透传场景里最稳的还是逐字节中断接收加软件环形缓冲。原因有两个:DMA 接收需要提前设置接收长度,而透传的数据长度不可预知,用 DMA 做不定长接收反而复杂;逐字节中断的开销在 460800 波特率下完全可控,一个字节中断间隔约 20 微秒,Cortex-M33 处理这点中断毫无压力。
2.3 硬件连接与电平匹配细节
RA4M2 的 IO 电平是 3.3V,而 DA14531 这颗芯片比较特殊,它的标准工作电压是 1.8V,直接拿 3.3V 的 UART 电平去怼 DA14531 的 RX 引脚是有风险的。很多第三方 DA14531 模块会内置 LDO 和电平转换电路,输入输出可以直接兼容 3.3V MCU,但如果你用的是裸芯片或者最小系统板,就一定要注意电平匹配,必要时加一颗电平转换芯片或者用电阻分压。
我的习惯是拿到模块先看数据手册,重点确认“UART 引脚电平容忍度”这一项。如果模块标注的是 1.8V 逻辑电平,又不想额外加芯片,也可以用两个电阻做分压:RA4M2 TX 到 DA14531 RX 之间串一个 1k 电阻,DA14531 RX 对地再接一个 2k 电阻,3.3V 分压后得到约 2.2V 的电压。不过这种电阻分压只适合低速场景,高速通信时信号边沿会变差,想要稳定可靠最好还是直接用 TXB0104 这类电平转换芯片。
还有地线问题。UART 通信是单端信号,必须保证两片芯片共地,如果两块板子各自供电,地线没接好,轻则乱码,重则烧 IO。如果系统里还有电机、继电器这类干扰源,UART 线建议绞合缩短,工业场景甚至可以加光耦隔离,我见过不少设备在强干扰环境下串口误码率飙升,把波特率降下来或加隔离后立刻稳定。虽然透传场景一般不会这么极端,但这个意识要有。
3. DA14531 蓝牙端固件开发与透传实现
3.1 开发环境与 SPS 例程工程结构
DA14531 的官方 SDK 目前主推 6.0.x 版本,开发环境是 SEGGER Embedded Studio。SDK 包解压后,在projects/target_apps/ble_examples目录下可以找到sps工程,这个例程的全称是 Serial Port Service,官方就是拿它演示串口和 BLE 互通的,与我们的需求高度吻合。
打开 SPS 工程后,你会发现它就是一个完整的透传固件:DA14531 通过 UART 接收外部 MCU 发来的数据,打包成 GATT Notification 发送给手机;手机写入特征值的数据,则从 DA14531 的 UART 输出。工程里最核心的几个文件包括:sps_server.c(串口服务逻辑)、uart.c(UART 驱动)、sps_queue.c(数据缓冲队列)、user_app.c(应用入口)。
需要强调一点:我不是从零写 BLE 协议栈,而是在官方例程基础上改配置。SDK 的 SPS 工程已经处理好了 GATT 服务的注册、连接事件、通知发送等机制,我们需要做的只是确认 UART 参数、调整缓冲区大小、按自己板子的引脚定义改宏。这也是我强烈不建议自己从零写 BLE GATT 的原因——Dialog 官方例程经过充分测试,稳定性远比自己硬撸协议栈可靠。
3.2 SPS 服务机制拆解:两个特征值的配合
SPS 服务之所以能实现透传,核心在于它定义了两个特征值:一个用于接收手机下发数据,另一个用于向手机推送数据。我理解它的方式很简单:手机下发通道是“写”方向,设备推送通道是“通知(Notify)”方向,两个方向各有一个专属特征值,互不干扰。
手机向设备发数据时,实际上是往 RX 特征值写入数据,DA14531 的协议栈会触发一个写请求回调,这个回调里会把数据转交给 UART 发送函数。设备向手机发数据时,则是 DA14531 的 UART 接收中断把数据收进来,填入发送队列,然后由 GATT 层按 MTU 大小分片,通过 TX 特征值以 Notification 形式发出去。
这里有个关键机制是 Notification 的分片逻辑。BLE 的 ATT 层一次最多传输 MTU 大小的数据,默认 MTU 是 23 字节,其中还要扣除 ATT 头部的 3 字节,所以用户数据一次最多 20 字节。如果串口一次收到 200 字节,DA14531 就要把数据切分成多个 20 字节的小包依次发送。速率要求高时,可以在连接建立后由主从双方协商提升 MTU,例如提升到 247 字节,这样一次通知就能携带更多用户数据,吞吐率明显上升。SDK 里通常支持在user_config.h或 GATT 配置中开启 MTU 协商,我在后面会给出具体配置位置。
3.3 固件改造关键点:缓冲区、流控与吞吐率
我拿到 SPS 例程后第一件事,就是把默认参数改成符合自己系统需求的配置。第一个改动点是 UART 波特率,DA14531 的 UART 驱动初始化在uart.c里,默认可能是 115200,要同步改成 460800,保持与 RA4M2 一致。第二个改动点是硬件流控,如果设计上接了 RTS/CTS,需要在初始化代码里打开流控标志,同时把对应引脚配置成复用功能。
第三个改动点是缓冲区大小。SPS 例程里有一个sps_queue机制用来缓存 UART 接收的数据,默认大小可能只有几十字节,对高吞吐率场景完全不够。我实测在 460800 波特率下,如果 App 端处理不及时,几十字节的缓冲瞬间就会被填满,然后数据开始丢失。所以我把队列缓冲改成 1024 字节或者更大,同时手机端也要配合,不能让数据在 DA14531 端积压。缓冲区大小和延迟是矛盾的,缓冲越大,数据堆积越多,延迟越高;缓冲太小,突发数据又会丢。具体按你的业务场景调,没有绝对正确答案。
第四个关键改动是 MTU 协商与 DLE(Data Length Extension)。DA14531 支持 BLE 5.1,DL E 可以扩展单包数据长度,配合大 MTU 能显著提高吞吐率。SDK 里一般通过配置宏开启,例如允许的最大 MTU 设为 247,连接建立后的连接间隔也按需调短。这些参数会直接影响手机收到的数据流畅度,我后面实测时会给出性能对比。
我整理一下 SPS 固件改造中通常需要动的宏和函数位置,实际代码逻辑因 SDK 版本会有差异,但思路一致:
| 改动点 | 配置文件 | 说明 |
|---|---|---|
| UART 波特率 | uart.c | 默认 115200,改为 460800 |
| 硬件流控 | uart.c | 确认 RTS/CTS 引脚使能与初始化 |
| 缓冲区大小 | sps_queue.c | 增大队列深度,避免高吞吐丢包 |
| 最大 MTU | user_config.h | 开启协商,数值改为 247 |
| 连接间隔 | user_config.h | 需要低延迟时缩短连接间隔 |
这里补充一个非常重要的认知:SPS 透传是“尽力而为”的透明通道,协议本身不保证应用层数据的完整性和有序性。BLE 协议栈在底层虽然有重传机制,但应用层如果数据量太大、缓冲溢出,依然会丢数据。所以真正常用的做法,是在你的业务协议里加入帧头、长度、序号、校验字段,把它当成一个可靠的字节流来设计,而不是指望透传固件替你保证完整性。
4. 手机端联调、抓包与性能验证
4.1 nRF Connect 做基础联通测试
固件烧进 DA14531、RA4M2 的串口驱动也调通之后,先用手机验证最基本的链路。我常用 Nordic 官方的 nRF Connect 工具,它同时支持 Android 和 iOS,扫描、连接、读写特征值、订阅通知这些操作都有图形化界面,是目前调试 BLE 最省事的工具。
测试步骤很直接。打开 nRF Connect 扫描,找到以你固件里设置的广播名出现的设备,点击连接。连接成功后,工具会列出设备的所有服务,找到 SPS 服务对应的 UUID,展开后能看到两个特征值。先订阅 TX 特征值的 Notification,然后在 RX 特征值上执行写操作,随便写几个字节,观察 RA4M2 侧的串口是否收到数据。反过来,用 RA4M2 往 DA14531 发一串数据,看手机端是否弹出 Notification 消息。
这一步有个细节要注意:nRF Connect 的写操作有两种模式,Write 和 Write Without Response。SPS 例程一般两者都支持,但透传场景为了速度通常用 Write Without Response。如果发现写操作报错,有可能是手机默认用 Write 模式,而固件没有实现带响应的写回调;这时切一下模式再试就能区分问题。
我第一次联调的时候就遇到过这种情况:手机端写数据,RA4M2 那边完全没反应。排查了很久,最后发现是 nRF Connect 默认用了带响应的写入,而我的固件里只实现了无响应写回调。把写入模式切换后,数据就通了。所以不要一上来就怀疑硬件,先把工具层面的模式搞清楚。
4.2 Wireshark 抓包看 BLE 交互细节
手机端能收发数据只是第一步,真正想搞明白数据为什么慢、为什么丢,还是得抓包。抓 BLE 包不像抓 WiFi 那么简单,普通蓝牙适配器是收不到 BLE 广播和连接数据的,需要一个专门的硬件嗅探器。我目前用的方案是 Nordic 的 nRF Sniffer,刷了抓包固件的 nRF52840 Dongle,配合 Wireshark 的 extcap 插件使用。
抓包能看到的东西非常有用。连接建立时,你能看到连接间隔协商成了多少毫秒;MTU 交换请求和响应里,能看到双方协商出来的最终 MTU 是多大;数据交互时,能清楚看到每一包 ATT 数据的长度和间隔,还能看到有没有重传包。如果感觉吞吐率上不去,抓包结果能直接告诉你瓶颈在哪个参数上。
热词里提到“Wireshark 只抓指定蓝牙 BLE”,这里顺便说一下。nRF Sniffer 插件支持按设备地址过滤,在 Wireshark 里启动抓包后,可以在 Sniffer 工具的配置界面填入目标设备的蓝牙地址,这样抓包结果只会保留该设备的通信包,不会被周边环境的大量广播淹没。没有指定过滤的话,打开 Wireshark 后在显示过滤器里输入btatt或者btle也能把范围缩小。
抓包这件事,我建议在联调阶段就做一次。不要等到吞吐率出问题才去想,平时连接不上的时候,抓包也能快速定位是广播有问题、还是手机端主动发起了连接但没成功。比如有一次我改了广播间隔,手机总是找不到设备,抓包一看广播包确实在发,但间隔长到 1 秒以上,手机扫描自然费劲。这种问题不抓包根本猜不到。
4.3 吞吐率实测与瓶颈定位
透传方案最终要过性能关。我在固件参数调整前后分别测了一组数据,测试方法是 RA4M2 定时向 DA14531 发送 1000 字节的数据块,手机端记录收到完整数据块所需时间,然后换算成 KB/s。
默认参数下(115200 波特率、MTU 23、连接间隔 30ms),实测吞吐率只有大约 2-4 KB/s,这个速度传简单状态数据够用,但如果要传稍大一点的日志或者波形数据,就会明显感觉卡顿。把波特率提到 460800、MTU 提到 247、连接间隔缩短到 7.5ms 之后,实测吞吐率能到 15-25 KB/s,提升非常可观。
瓶颈其实很容易分析。UART 的 460800 波特率理论速率约 46KB/s,BLE 在理想条件下的速率比这个高得多,所以在这个配置下,UART 已经不再是主要瓶颈。但如果保持 115200 波特率,UART 理论速率只有约 11.5KB/s,BLE 再快也没用,数据只能在 UART 端排队。这也是我坚持把波特率调高的原因——透传系统里,最慢的那一段决定整体吞吐,而 UART 往往就是最慢的那一段。
连接间隔和 MTU 也很关键。连接间隔越长,单位时间内能传输的包数越少;MTU 越大,每包能携带的数据越多。具体调参时,优先级是:先确保 UART 波特率不落后,再协商大 MTU,最后才调连接间隔。因为连接间隔调太短会增加功耗,而很多场景对功耗更敏感。
手机 App 端也要注意,如果自己写 App,Android 上可以通过requestMtu和requestConnectionPriority主动请求大 MTU 和短连接间隔,iOS 则相对封闭,系统会根据应用需求自动协调。我用 nRF Connect 测试时,它默认会尝试协商高 MTU,这可能是手机端能跑出高吞吐率的原因之一。
5. 高频问题速查与避坑心得
5.1 问题排查速查表
整套方案做下来,我整理了一个高频问题排查表,基本覆盖了从硬件到固件到手机端的绝大多数问题,强烈建议收藏。遇到问题先对号入座,比自己瞎猜效率高很多。
| 现象 | 可能原因 | 解决思路 |
|---|---|---|
| 手机扫描不到设备 | 广播间隔太长/广播数据超长 | 缩短广播间隔,检查广播包长度是否合法 |
| 手机能搜到但连不上 | 设备已被其他手机连接(单连接) | 断开其他连接,或重启设备 |
| 连接后频繁断连 | 看门狗复位、供电不稳定、天线区域干扰 | 检查供电纹波,抓包看断连原因码 |
| 串口收到乱码 | 波特率不一致、电平不匹配 | 两端统一波特率,检查电平转换电路 |
| 设备到手机丢数据 | UART 缓冲溢出、通知分片丢失 | 加大 SPS 队列缓冲,检查流控是否生效 |
| 手机到设备丢数据 | 手机写入过快、UART 发送堵塞 | 打开硬件流控,或降低手机写入节奏 |
| 下行延迟很大 | MTU 小、连接间隔长 | 协商大 MTU,缩短连接间隔 |
| 修改配置后不生效 | 宏定义未重新编译、缓存未清理 | 全量重新编译,检查宏是否被条件编译忽略 |
5.2 几个值得记住的实战排查经验
第一,分阶段验证比整链路调式容易得多。我每次遇到数据不通,都先断开 DA14531,把 RA4M2 的 UART 直接接到电脑的 USB 转串口上,用串口助手验证 RA4M2 收发是否正常;再把 DA14531 单独用手机连接,用 nRF Connect 验证 BLE 通路;两边都确认没问题,再合到一起联调。这样一旦出错,问题范围能缩小一半以上。曾经有次数据不通,我排查了半天 MCU 代码,最后才发现是 DA14531 模块上的串口引脚和调试串口复用冲突了,如果一开始就分阶段验证,几分钟就能定位。
第二,硬件流控必须真接真开,不能只开不接。DA14531 和 RA4M2 如果都开启了 RTS/CTS 流控,但 PCB 上没走这两根线,或者飞线没接,那么 UART 的逻辑会被悬空引脚上的电平干扰,表现就是数据完全乱掉或者收发卡死。我吃过一次亏,飞线的时候漏接了 CTS 引脚,结果 DA14531 一直以为对端缓冲区满了,压根不发送数据,整条链路静默得像石头。后来用万用表一个个测引脚电平才发现问题。
第三,缓冲区不是越大越好。调 SPS 队列缓冲时,我刚开始贪心把它调到 4096 字节,结果延迟反而变高,因为数据在缓冲里排队的时间长了。对于这个项目,最终 1024 字节足够,既能缓存突发数据,又不至于让数据在 DA14531 端积压。建议根据你实际的单次数据块大小来定,大概是最大数据块的两倍左右比较合理。
第四,天线区域和电源纹波对 BLE 的影响容易被忽略。DA14531 的射频性能受周围铺铜和电源质量影响很大,我有一版板子把天线下方走了电源线,结果通信距离缩水一半。重新调整布局、把天线区域净空后,信号才恢复正常。如果遇到 RSSI 很低或者连接不稳定的情况,别急着改软件,先检查硬件设计。
5.3 这套方案的后续扩展空间
这个 RA4M2 + DA14531 的透传架构,做完基础功能后还可以往几个方向扩展。一个是加 OTA 固件升级,利用 BLE 通道把固件数据下发到 DA14531,再通过 UART 传给 RA4M2 引导程序,实现远程升级,很多量产产品都有这个需求。另一个是安全机制,当前透传链路默认没有配对绑定,任何人都能连接,如果设备部署在公共环境,建议增加配对和绑定流程,甚至白名单过滤。还有低功耗策略的细化,RA4M2 和 DA14531 都有各自的休眠模式,可以设计一套“串口唤醒 + BLE 唤醒”的联动机制,让整体功耗再降一个台阶。
如果想进一步推高吞吐率,还可以研究 DA14531 的 DLE 和 2M PHY 模式。DA14531 支持 BLE 5.x 的特性,开启 2M PHY 后理论上实际速率可以再提升不少。不过注意,手机端也必须支持 2M PHY 才有效果,并且老旧手机可能不支持,选择时要做兼容性测试。
写在最后
我个人在实际操作中最大的体会是,所谓“透传”,真正的难点不在“传”,而在“透”。UART 和 BLE 各自都有自己的脾气,UART 讲究电平、波特率、流控,BLE 讲究 MTU、连接间隔、通知分片,任何一段没调好,另一段再稳定也会被拖下水。我最后再分享一个小技巧:调试时可以给 RA4M2 的 UART 接收每一帧数据打上时间戳,同时用手机记录收到通知的时间戳,两边时间线对齐后去分析整条链路的延迟分布。我就是靠这个办法,最终定位到瓶颈竟然不在 BLE 连接参数,而在 RA4M2 应用层轮询发送的调度太慢。这种“链路级+时间线”的调试思路,比单点看代码要高效得多,希望对你也有帮助。