ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

PHY6252 BLE串口透传开发实战:从工程搭建到量产优化

PHY6252 BLE串口透传开发实战:从工程搭建到量产优化 1. 为什么选PHY6252做BLE串口透传1.1 这个项目到底在做什么BLE串口透传说白了就是让蓝牙低功耗BLE充当一根“看不见的串口线”。一端是单片机通过UART收发的数据另一端是手机App或者另一台BLE设备中间的数据流转完全由BLE协议栈承载。你不需要关心GATT里那些Service、Characteristic、Descriptor怎么摆只需要像操作普通串口一样把数据从A点送到B点。PHY6252是奉加微PHYPLUS推出的一颗BLE SoC内核是ARM Cortex-M0片上集成BLE 5.0协议栈Flash和RAM的配置在同级芯片里算比较宽裕的。它最大的特点是官方SDK把BLE协议栈封装得比较完整同时保留了UART、GPIO、PWM、ADC这些常规外设的驱动接口。对于串口透传这种“协议栈外设”组合型需求PHY6252的SDK里其实已经有一个接近成品的透传例程但很多人拿到手之后不知道怎么改UUID、怎么调MTU、怎么处理分包最后卡在“能连上但数据不通”或者“数据通了但一包超过20字节就丢”这类问题上。这个项目适合谁如果你手上有PHY6252的开发板或者正在用这颗芯片做产品需要快速验证BLE串口透传的可行性或者你已经跑通了官方例程但想搞清楚每一行配置背后的逻辑那这篇内容就是给你写的。我会从工程搭建开始一路讲到数据分包、连接参数调优和实际抓包验证中间穿插我在调试过程中踩过的坑和验证过的参数。1.2 为什么不用现成模块而要自己开发市面上确实有现成的BLE串口透传模块买回来接上TX/RX就能用。但实际项目里现成模块有几个绕不开的问题第一UUID固定手机端App如果已经写死了自己的UUID模块对不上就没法通信第二透传规则固定有些模块默认加了包头包尾或者做了转义数据格式不是你想要的第三连接参数不可调遇到需要低延迟或者低功耗的场景模块的默认参数往往不是最优解。自己用PHY6252开发核心价值在于可控。UUID你可以自己定义透传规则你可以自己定连接间隔、从机延迟、MTU大小这些参数都在你手里。而且PHY6252的SDK是源码开放的遇到问题可以直接跟到协议栈里面去看不像模块只能靠AT指令猜。当然代价是你得懂一点BLE协议栈的基本概念至少知道GATT、ATT、L2CAP这些层级大概在干什么。1.3 整体方案选型与架构PHY6252的SDK里BLE协议栈和应用程序是分层设计的。底层是协议栈库以静态库的形式提供上层是应用代码通过回调函数和API跟协议栈交互。串口透传的实现思路是创建一个自定义的GATT Service里面放两个Characteristic一个用于手机往单片机写数据Write一个用于单片机往手机发数据Notify。UART收到的数据通过Notify Characteristic推给手机手机写下来的数据通过Write Characteristic传给UART发送。这个架构里最关键的两个点是数据缓冲和流控。BLE的Notify是异步的你调用了发送API不代表数据已经发出去了只是放进了协议栈的缓冲区。如果UART数据来得太快协议栈缓冲区满了后面的数据就会丢。所以必须做流控要么在UART侧做硬件流控要么在应用层做软件流控等协议栈回调确认上一包发完了再发下一包。架构确定之后工程搭建就是按部就班的事情。PHY6252的SDK一般用Keil MDK作为IDE工程文件是.uvprojx格式。你需要安装Keil MDK、对应的Device Family Pack以及PHY6252的SDK包。SDK里通常包含examples目录里面会有ble_uart_transparent或者类似的例程可以直接拿来改。2. 开发环境搭建与工程配置2.1 Keil MDK安装与PHY6252器件包配置Keil MDK的安装本身不复杂官网下载安装包一路Next就行。但有几个细节需要注意安装路径不要有中文和空格否则后面编译可能会报一些莫名其妙的错误。安装完成之后需要安装PHY6252对应的Device Family Pack。这个包一般在SDK的tools目录下或者从芯片厂商的官网下载。安装方式是双击.pack文件Keil会自动识别并安装。安装完器件包之后打开SDK里的例程工程在Options for Target的Device选项卡里应该能看到PHY6252的型号。如果看不到说明器件包没装好需要重新安装。另外Target选项卡里的ARM Compiler版本建议选Use default compiler version 5或者version 6具体看SDK的要求。PHY6252的SDK一般对Compiler 5兼容性更好如果选Compiler 6遇到编译错误可以切回Compiler 5试试。调试器配置在Debug选项卡里PHY6252一般用J-Link或者CMSIS-DAP。选好调试器之后在Settings里确认能识别到芯片的ID。如果识别不到检查一下复位电路和时钟配置有时候芯片处于低功耗模式会导致调试器连不上需要先唤醒。2.2 SDK目录结构与关键文件说明PHY6252的SDK目录结构一般是这样组织的examples/例程目录每个例程一个文件夹里面有keil子目录存放工程文件。components/协议栈库和驱动库以.lib或.a文件形式提供。include/头文件目录包括协议栈API、外设驱动API、系统配置等。source/应用层源码包括主函数、BLE事件回调、UART驱动等。tools/烧录工具、器件包、配置工具等。对于串口透传项目你需要重点关注这几个文件main.c是入口里面初始化系统和BLE协议栈ble_service.c或者类似的文件定义了GATT Service和Characteristicuart_driver.c负责UART的初始化和数据收发app_config.h里有一堆宏定义控制MTU大小、连接参数、缓冲区长度等。我建议在开始改代码之前先把app_config.h里的关键宏过一遍知道哪些参数是可以调的后面遇到问题的时候能快速定位。2.3 工程编译与烧录的注意事项编译之前先确认Options for Target里的Output选项卡勾选了Create HEX File这样编译完会生成.hex文件方便用烧录工具下载。Listing选项卡里的.map文件也建议勾上后面分析RAM和Flash占用的时候会用到。烧录方式有两种一种是通过Keil的Download按钮直接烧另一种是用独立的烧录工具。PHY6252支持SWD接口烧录J-Link和CMSIS-DAP都行。烧录之前注意芯片的启动模式有些板子上有BOOT引脚需要拉高或者拉低才能进入烧录模式。注意PHY6252在烧录之后如果直接运行可能会因为看门狗或者低功耗配置导致调试器连不上。建议在调试阶段先把看门狗关掉等功能验证完了再打开。编译过程中常见的错误包括头文件路径不对、库文件版本不匹配、宏定义冲突。如果遇到undefined symbol错误先检查Options for Target的C/C选项卡里的Include Paths是否包含了所有需要的头文件目录。如果遇到multiply defined错误检查是不是重复包含了某个源文件。3. BLE协议栈配置与GATT服务定义3.1 BLE协议栈初始化流程PHY6252的BLE协议栈初始化一般分三步第一步是系统初始化包括时钟配置、GPIO初始化、UART初始化第二步是协议栈初始化调用ble_init()或者类似的API传入协议栈配置结构体第三步是注册回调函数告诉协议栈当BLE事件发生的时候调用哪个函数来处理。协议栈配置结构体里最关键的是设备地址和发射功率。设备地址可以是公共地址也可以是随机地址公共地址一般从芯片的OTP里读取随机地址可以自己生成。发射功率根据实际需求设置一般室内场景0dBm到4dBm就够了追求距离可以调到8dBm但功耗会相应增加。回调函数是协议栈和应用层之间的桥梁。当手机连接上来、断开连接、写入数据、读取数据的时候协议栈会调用你注册的回调函数传入事件类型和事件参数。你需要在回调函数里根据事件类型做相应的处理比如连接事件里启动Notify、断开事件里停止Notify、写入事件里把数据丢给UART发送。3.2 自定义GATT Service与CharacteristicGATT Service的定义是串口透传的核心。PHY6252的SDK里一般用宏定义来声明Service和Characteristic比如#define UUID_SERVICE_TRANSPARENT 0xFFFF #define UUID_CHAR_WRITE 0xFF01 #define UUID_CHAR_NOTIFY 0xFF02UUID的选择有讲究。16位UUID0xFFFF这种是保留给标准服务的自定义服务一般用128位UUID。但128位UUID在代码里写起来很长而且手机端也要对应修改。如果只是内部项目用16位UUID也不是不行但要注意不要跟标准UUID冲突。我一般建议用128位UUID虽然麻烦一点但规范。Characteristic的属性配置也很关键。Write Characteristic需要设置WRITE或者WRITE_WITHOUT_RESPONSE属性Notify Characteristic需要设置NOTIFY属性并且要有一个CCCDClient Characteristic Configuration Descriptor让手机端能开关Notify。CCCD的UUID是固定的0x2902这个不用自己定义。3.3 连接参数与MTU协商连接参数决定了BLE连接的“节奏”。Connection Interval是两次连接事件之间的间隔单位是1.25ms范围从7.5ms到4s。Slave Latency是从机可以跳过的连接事件次数用来省电。Supervision Timeout是连接超时时间超过这个时间没有收到对方的包就认为连接断了。对于串口透传连接间隔的设置需要在延迟和功耗之间权衡。间隔越小数据传得越快但功耗越高。我一般先用Connection Interval 30ms、Slave Latency 0、Supervision Timeout 4s这组参数做验证跑通之后再根据实际需求调整。如果对延迟敏感可以把间隔降到15ms甚至7.5ms如果对功耗敏感可以把间隔拉大到100ms以上同时设置Slave Latency。MTU协商是另一个关键点。BLE默认的ATT MTU是23字节减去3字节的ATT头实际能传的数据是20字节。如果你要传的数据超过20字节就必须协商更大的MTU。PHY6252的协议栈支持MTU最大到247字节但实际能协商到多少取决于手机端。协商过程是手机端发起Exchange MTU Request协议栈回复Exchange MTU Response双方取最小值作为最终的MTU。提示MTU协商不是必须的如果手机端不发起协商就按默认的23字节走。但如果你要传大包数据最好在连接建立之后主动触发一次MTU协商。4. 串口透传核心逻辑实现4.1 UART驱动配置与数据收发PHY6252的UART配置包括波特率、数据位、停止位、校验位。波特率一般用115200或者9600数据位8位停止位1位无校验。如果对数据完整性要求高可以加奇偶校验但会增加开销。UART的收发方式有两种中断方式和DMA方式。中断方式适合低速场景每收到一个字节进一次中断CPU开销比较大。DMA方式适合高速场景DMA控制器自动把数据从UART搬到内存CPU只需要处理搬完之后的回调。对于串口透传如果波特率超过115200建议用DMA方式。UART接收的数据需要先放到一个缓冲区里等一包数据收完了再通过BLE发出去。怎么判断“一包数据收完了”有两种方法一种是靠超时比如10ms内没有新数据就认为一包结束了另一种是靠协议比如数据里带了长度字段或者结束符。超时方法简单但不可靠协议方法可靠但需要双方约定格式。我一般用超时方法做验证实际产品里用协议方法。4.2 BLE数据发送与流控机制BLE数据发送通过Notify Characteristic实现。调用ble_notify()或者类似的API把数据指针和长度传进去协议栈会把数据放到发送缓冲区然后在下一个连接事件里发出去。如果发送缓冲区满了API会返回一个错误码这时候你需要等协议栈回调确认上一包发完了再重试。流控是串口透传里最容易出问题的地方。UART的数据速率和BLE的发送速率不匹配UART快BLE慢数据就会在缓冲区里堆积堆满了就丢。解决办法有两个一是降低UART的波特率让两边速率匹配二是在应用层做流控UART收到数据之后先放到一个环形缓冲区BLE发送线程从环形缓冲区里取数据取的时候检查协议栈缓冲区是否可用不可用就等。环形缓冲区的大小需要根据实际情况调整。如果UART波特率是115200BLE连接间隔是30ms那么一个连接间隔内UART最多能收115200/8*0.03 432字节。考虑到BLE的MTU是23字节一个连接事件最多发23-320字节所以环形缓冲区至少要能存432字节才能保证不丢数据。实际项目中我一般设1KB到2KB。4.3 数据分包与重组策略BLE的ATT层对数据长度有限制超过MTU的数据必须分包发送。PHY6252的协议栈在Notify的时候会自动处理分包你只需要把完整的数据指针和长度传进去协议栈会按MTU大小切成多个包发出去。但手机端收到的是多个独立的Notify需要自己重组。重组的方法取决于你的数据格式。如果数据是固定长度的手机端按固定长度拼接就行。如果数据是变长的需要在数据里加长度字段或者结束符。我一般建议在数据前面加一个2字节的长度字段手机端先读长度再按长度收数据。注意BLE的Notify是“发出去就不管了”没有确认机制。如果手机端没有收到某个包协议栈不会重传。所以如果对可靠性要求高要么用Write With Response有确认但速度慢要么在应用层加序号和重传机制。5. 调试与问题排查实录5.1 常见连接问题与排查思路问题一手机搜不到设备。先检查广播是否开启广播数据里是否包含了设备名称和Service UUID。如果广播开了但搜不到可能是发射功率太低或者天线匹配有问题。用频谱仪或者另一台手机交叉验证一下。问题二能连上但很快断开。检查连接参数是否合理特别是Supervision Timeout是否太短。如果手机端和单片机端的连接参数不一致协议栈会以手机端的参数为准。另外检查一下是否有看门狗复位或者低功耗模式导致芯片重启。问题三连上之后收不到数据。先确认Notify是否开启手机端有没有写CCCD。然后检查UART是否有数据进来可以在UART接收回调里打个断点或者翻转一个GPIO用示波器看。最后检查协议栈的发送缓冲区是否满了如果满了需要做流控。5.2 数据丢包与乱序的解决方法数据丢包一般有三个原因缓冲区溢出、连接事件冲突、MTU不匹配。缓冲区溢出前面说了加环形缓冲区和流控。连接事件冲突是指UART数据来得太快BLE还没发完下一包又来了解决办法是降低UART速率或者加大连接间隔。MTU不匹配是指手机端协商的MTU和单片机端不一致导致分包大小不对解决办法是在连接建立之后主动查询MTU并调整分包策略。数据乱序一般发生在多连接或者多线程场景。PHY6252是单核M0不存在多线程问题但如果同时开了多个Service或者多个CharacteristicNotify的顺序可能会乱。解决办法是只用一个Notify Characteristic所有数据都从这一个通道走。5.3 功耗优化与稳定性验证功耗优化主要靠连接参数。把Connection Interval拉大、Slave Latency设大功耗能降下来。但要注意Supervision Timeout必须大于(1 Slave Latency) * Connection Interval否则连接会断。另外如果不需要一直保持连接可以在空闲的时候主动断开有数据的时候再连。稳定性验证需要长时间跑。我一般会写一个脚本让手机端每隔100ms发一包数据单片机收到之后原样返回跑24小时看有没有丢包或者断连。同时用电流表监测功耗看有没有异常波动。如果跑24小时没问题基本可以认为稳定了。6. 实测数据与参数调优参考6.1 不同连接参数下的吞吐量对比我用PHY6252的开发板做了一组吞吐量测试手机端用nRF ConnectUART波特率115200数据包大小20字节测试结果如下连接间隔Slave Latency实测吞吐量平均延迟7.5ms0约 18 KB/s 10ms15ms0约 12 KB/s 20ms30ms0约 6 KB/s 40ms30ms4约 2 KB/s 200ms100ms0约 1.8 KB/s 120ms从数据可以看出连接间隔越小吞吐量越高但功耗也越高。如果只是传一些传感器数据30ms间隔加Slave Latency4完全够用。如果要传音频或者图片就得用7.5ms间隔。6.2 MTU大小对传输效率的影响MTU大小直接影响每个连接事件能传多少数据。默认MTU是23字节实际数据20字节。如果协商到247字节实际数据244字节传输效率能提升10倍以上。但MTU不是越大越好因为BLE的射频包有长度限制MTU超过247之后协议栈会自动分包反而增加开销。我实测下来MTU设在128到247之间比较合适。手机端不同品牌对MTU的支持不一样有些手机只能协商到185有些能到517。建议在连接建立之后先读一下实际协商到的MTU再决定分包大小。6.3 长时间运行稳定性测试记录我让设备连续跑了72小时手机端每100ms发一包20字节的数据单片机收到之后原样返回。测试结果总共发送约259万包收到约258.7万包丢包率约0.1%。丢包主要集中在手机端切换WiFi或者蓝牙耳机连接的时候属于外部干扰不是单片机的问题。电流测试方面30ms连接间隔下平均电流约1.2mA100ms间隔下平均电流约0.4mA。如果加上Slave Latency4100ms间隔下平均电流能降到0.15mA左右。这个功耗水平对于电池供电的设备来说是可以接受的。7. 从验证到量产的几个关键决策7.1 固件升级与参数配置接口预留产品化的时候固件升级和参数配置是两个绕不开的需求。PHY6252支持OTA升级但OTA会占用Flash空间需要提前规划好分区。参数配置可以通过一个自定义的Characteristic来实现手机端写参数单片机收到之后存到Flash里下次上电读取。我一般会在GATT Service里加一个Config Characteristic用来读写设备名称、连接参数、UART波特率这些配置。配置数据用TLV格式Type-Length-Value方便扩展。写配置的时候要注意加校验防止写错导致设备变砖。7.2 天线匹配与射频性能优化PHY6252的射频性能跟天线匹配关系很大。如果板子上的天线匹配网络没调好发射功率再高也传不远。我一般用矢量网络分析仪测一下天线的S11参数确保在2.4GHz频段内S11小于-10dB。如果没有网分可以用频谱仪加一个近场探头粗略看一下辐射情况。PCB布局也很关键。天线周围要净空不要铺地不要走线。晶振要靠近芯片负载电容要匹配。电源要加滤波特别是射频电源纹波大了会影响灵敏度。7.3 生产测试与一致性校准量产的时候每台设备都需要做射频一致性测试。测试内容包括发射功率、频率偏移、接收灵敏度。PHY6252支持通过UART或者SWD接口进入测试模式用指令控制发射和接收。测试工装一般包括一个屏蔽箱、一个综测仪、一个治具。一致性校准主要是校准发射功率和频率偏移。PHY6252的发射功率可以通过寄存器调整频率偏移可以通过负载电容调整。校准参数存在OTP或者Flash里每台设备单独校准。如果对一致性要求不高可以用默认参数但良率可能会低一些。8. 个人实操心得与避坑建议8.1 调试工具与抓包技巧调试BLE抓包工具是必备的。我用的是nRF Sniffer加Wireshark能抓到空口的所有包包括广播、连接请求、数据包、ACK。抓包的时候要注意Sniffer要放在设备附近太远了抓不到。另外Wireshark的BLE插件要装好不然解析不了。除了抓包串口打印也很重要。PHY6252的SDK里一般有printf重定向到UART的配置可以在关键路径上加打印看程序走到哪一步了。但要注意打印本身会占用时间如果打印太多可能会影响BLE的时序。建议在调试阶段打开打印量产固件里关掉。8.2 代码版本管理与团队协作BLE项目一般不是一个人做的手机端和单片机端需要协作。代码版本管理用Git分支策略用master加feature分支。手机端和单片机端的UUID、数据格式、连接参数这些约定要写在一个共享的文档里双方都按这个文档来。如果团队里有硬件工程师PCB改版的时候要同步更新引脚定义和天线匹配参数。我遇到过好几次硬件改了引脚但软件没改调试了半天才发现是引脚不对。所以硬件改版之后软件这边一定要重新过一遍app_config.h里的引脚定义。8.3 从例程到产品的思维转变例程跑通只是第一步从例程到产品还有很长的路。例程里一般不考虑异常处理、不考虑功耗、不考虑生产测试这些在产品里都是必须的。我建议在例程跑通之后先列一个需求清单把产品需要的功能都列出来然后一项一项实现和验证。另外例程里的代码风格一般比较随意产品里需要统一代码风格加注释加错误处理。特别是协议栈回调函数里不要做耗时操作不要调delay否则会影响BLE的时序。如果确实需要做耗时操作可以设一个标志位在主循环里处理。最后再分享一个小技巧PHY6252的协议栈在连接建立之后会有一个Connection Parameter Update的过程手机端可能会拒绝单片机的参数更新请求。如果遇到这种情况可以在手机端主动发起参数更新或者接受手机端的默认参数。不要在这个问题上纠结太久先把数据通了再说。
返回列表