ARTICLE DETAIL

资讯详情

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

BK3431 BLE透传模块低功耗与OAD升级实战解析

BK3431 BLE透传模块低功耗与OAD升级实战解析 简介本资源是BK3431芯片官方BLE SDK的深度优化实践包面向嵌入式蓝牙开发工程师及IoT固件开发者聚焦低功耗串行通信与空中固件升级OAD两大核心需求。压缩包含147个文件以108个头文件.h和30个源文件.c为主体涵盖GATT服务实现如LE_gatt_server.c、CTS时间服务、UART透传tra_uart.c、HCI传输层tra_hcit.c、系统中断与事件调度sys_irq.c、tc_event_gen.c等关键模块另有启动脚本.s、链接脚本.sct、Keil工程.uvproj及库文件.lib完整支撑编译、调试与量产部署。资源包仅389KB结构紧凑、模块职责清晰便于快速切入低功耗BLE串口桥接与远程升级功能开发。目前已有396人学习下载提供可直接复用的低功耗策略集成方案如深度睡眠唤醒调度、能量感知通信节律、OAD服务框架及配套底层驱动显著降低电池供电类终端如传感器节点、穿戴设备的开发门槛与调试周期。1. 项目缘起为什么最后选了BK3431这颗芯片先交代一下背景。我之前做的项目是一个小体积的BLE透传模块硬件上就是一颗蓝牙SoC加一个晶振加几个电容电阻对外提供UART接口让客户的主控板通过串口和手机App通信。这类需求在物联网设备里太常见了基本就是蓝牙串口透传模块的标准用法设备端MCU发串口数据给蓝牙模块蓝牙模块走BLE协议栈发给手机App反向同样。选型的时候我在几家国产BLE芯片之间纠结了很久。BK3431这个芯片最初进入视野是因为它的成本确实低而且官方SDK给出的资料相对完整。这里多说一句很多国产BLE芯片的SDK其实是能跑Demo但想真正落地还有很长一段路的状态BK3431的SDK算是矮子里拔将军资料虽然不算精美但核心代码都在不至于让你从零开始猜寄存器。BK3431的关键参数大概是这么个情况ARM968E-S内核不是Cortex-M系列这点后面还会提到因为它直接影响你写中断和低功耗代码的思维、BLE 5.0协议栈、支持OADOver-the-Air Download空中固件升级、片上Flash可用来存用户程序RAM空间我记得是128KB级别。这类芯片的性能上限不高Flash和RAM都有限但做串口透传这种轻量级应用是绰绰有余的。如果你的需求是更复杂的产品比如需要跑算法、需要大容量存储那BK3431肯定不合适选它纯粹是冲着成本和够用去的。这个项目有意思的地方在于标题里提到了两个关键词AddLowPower添加低功耗和OAD。这两个恰恰是BLE设备从能通到能用之间的两道坎。很多开发者拿官方SDK跑通了透传Demo以为完事了结果一测功耗电流几十毫安电池几天就没了这才意识到低功耗不是默认就有的功能是要自己一点点扣出来的。OAD也是一样Demo里可能没有完整的升级流程真到产品要批量更新固件的时候才发现水很深。所以这篇博文我不打算泛泛地讲BK3431是什么而是把我在这个项目里实际做过的三件事掰开揉碎地讲清楚第一怎么把官方串口透传Demo吃透并改造成自己的业务代码第二低功耗改造到底改了哪些地方为什么这些地方会耗电第三OAD空中升级怎么配、怎么调、坑在哪。最后再补一段我调试过程中总结的实战经验和排查方法这部分是文档里找不到的。2. 串口透传Demo的代码骨架先把官方SDK吃透再动手拿到官方SDK第一件事不是急着改代码而是先把工程整体结构看懂。BK3431的SDK在不同版本里目录结构有差异但核心模块基本一致。我记得当时拿到的工程里主要涉及这么几块协议栈相关BLE协议栈以库的形式提供SDK里能看到ble相关的源文件或头文件这部分一般不需要动。应用层包括GATT服务定义、广播配置、连接事件处理。驱动层UART、GPIO、定时器、Flash、ADC等外设驱动。主循环和任务调度BK3431的软件架构里有一个简单的主循环调度不是完全的事件驱动RTOS所以你的业务逻辑需要放进主循环或者中断回调里执行。这里要特别提醒一点BK3431的SDK虽然看起来像标准C工程但它不是用标准STM32那套HAL库思维去写的。它的代码风格更接近老式MCU——直接操作寄存器的地方很多主循环里轮询标志位的逻辑很常见。如果你习惯了HAL库、回调函数满天飞的开发模式刚开始看这个SDK会不太适应。但反过来这种风格的好处是逻辑直白你想查一个UART中断是怎么被处理的跟着标志位走一遍就能看清。2.1 透传服务的GATT结构UUID、特征值、权限串口透传的核心是一个自定义GATT服务。官方Demo的做法通常是定义一对特征值一个用于接收手机发下来的数据Write特征一个用于向手机上报数据Notify/Indicate特征。有的Demo还会加一个附加特征值比如修改设备名称、修改广播间隔之类的这些属于后续功能扩展。UUID方面官方Demo一般使用厂商自定义的128位UUID常见的是0000FFF0-0000-1000-8000-00805F9B34FB这个格式也就是128位UUID中出现频率最高的那个私有服务基础。Write特征通常是FFF1或FFF2Notify特征是FFF3或FFF4这种结构。这些不是行业标准完全是厂商自己定义的所以你的手机App端需要跟固件端保持一致。对于透传模块来说GATT服务的长度和缓冲区大小是重中之重。BLE单包数据最长只有20字节经典BLE 4.0/4.2时代虽然BLE 5.0支持更长的数据包最大251字节但要取决于协议栈是否启用DLEData Length Extension功能还要看信道质量。BK3431的SDK默认配置里单包数据我给你个参考值20字节是保底启用DLE之后可以到247字节左右。但注意DLE不是你想开就能开的要确认对端手机也支持而且在连接后需要通过LL层协商。我一般建议产品设计时默认按20字节分包处理兼容性最好然后在一定条件下再尝试DLE提速。在透传的代码实现上最关键的是两个缓冲区一个是从UART接收、暂存、等待手机端拉取的发送缓冲另一个是手机下发、等待UART口发送的接收缓冲。很多低功耗透传模块产品化做不好都是因为这两个缓冲区的管理出了问题——要么缓冲区满了直接丢包要么读写指针竞争导致数据错乱。2.2 UART接收逻辑中断缓冲区别用轮询UART这部分我踩过一个坑。官方Demo的UART接收在单独的uart.c或类似文件名里实现的默认是用中断接收同时维护一个环形缓冲区。这个设计其实是合理的因为BLE透传模块的串口对端是用户MCU数据是随时可能进来的如果轮询接收主循环稍微有点延迟就会丢数据尤其当BLE在忙广播、忙连接事件处理时轮询根本跟不上。我刚接手时自作聪明地改成过轮询接收测试低速数据没问题一上高速率比如115200波特率对端连续发几百字节立刻丢包。排查来排查去定位到原因主循环里做的事太多了轮询间隔不可控UART硬件FIFO很快溢出。这说明一个道理在BLE协议栈占用大量CPU时间的情况下费字节级的UART接收必须靠中断同时配合足够大的环形缓冲区。改造后的UART接收流程大致是这个逻辑UART中断触发把数据从硬件FIFO读出来存入环形缓冲区。在主循环里判断环形缓冲区是否有数据如果有切包按20字节或协商好的MTU切分通过BLE的Notify特征发给手机。发送完一个包之后检查缓冲区是否还有剩余数据有则继续发下一个包。这套逻辑本身不难难点在于缓冲区大小怎么定、数据快慢怎么协调。我的经验是环形缓冲区至少要有发送包长的4倍以上比如每包20字节缓冲区就预留128字节这样可以有效缓冲UART连续到达的数据避免因为BLE发一个包的时间一个连接事件里可能只能发1-2包导致UART数据溢出。2.3 BLE发送背压手机端处理不均时的丢包处理这算是透传开发中最容易忽略的问题。UART数据进来固件往手机Notify一切都正常——但手机端不一定每秒都在接收Android系统的BLE回调不是实时的可能出现BLE缓冲区堆积的情况。这时蓝牙协议栈会返回缓冲区满的错误你的代码如果无视这个错误继续往栈里塞数据等着你的就是内存溢出或者系统挂掉。我在代码里加了一个简单的发送队列状态判断逻辑大概是如果BLE协议栈返回发送失败进入一个等待重发的状态。此时UART仍在接收数据进来的数据继续进环形缓冲区。主循环在下一次循环中检测到等待重发标志且在等待期间缓冲区没有溢出就继续尝试发送。本质上这是一个生产者-消费者模式UART生成者BLE是消费者。这个模型在任何透传方案里都是通用的BK3431也好、其他芯片也好思路是一致的。如果接触过RTOS开发你会觉得这个架构很眼熟。3. 低功耗改造从能跑到真省电的AddLowPower标题里的AddLowPower才是这个项目的灵魂。官方自带的透传Demo默认情况下是可以正常工作但功耗比较迷惑的状态广播电流、连接电流没问题但待机时如果不做处理电流可能停留在毫安级。对于电池供电的产品来说这是不可接受的。所以拿到SDK第一件事情就是做低功耗改造。在BLE领域功耗是一个系统工程不是改一个寄存器就完事。要把功耗打下来必须从芯片的睡眠模式、广播参数、连接参数、GPIO配置、外设开关这几个维度逐一排查和调整。以下是这个项目里我实际做的每一项改动和背后的理由。3.1 睡眠模式的触发与唤醒机制主循环里加什么、为什么BK3431支持多种低功耗模式最常用的是sleep睡眠模式。要让芯片进入睡眠不是简单调一个API而是要在没有任务可做时主循环主动调用睡眠函数。它的流程是主循环跑完一轮检查所有事件标志位UART接收、BLE事件、定时器事件等如果没有需要处理的事情就调用睡眠函数让MCU进入sleep状态。这里有个关键细节蓝牙协议栈必须有独立的唤醒机制。BK3431的BLE协议栈运行时会内部维护一个RTC/定时器用来处理连接间隔和广播事件。也就是说即使你的主循环睡了到连接事件发生时芯片必须醒来处理BLE协议栈的工作。如果这个唤醒机制没有正确配置会出现两种情况要么芯片睡了睡不醒连接断掉要么睡眠没效果电流根本没下来。我在实际代码里确认了睡眠模式的几个要点进入睡眠前要关掉不用的外设时钟尤其是UART、I2C、SPI这些外设的时钟。不是说调一个低功耗API就够了外设模块如果不关唤醒后还在耗电。UART唤醒要保留如果模块需要通过串口接收对端MCU的数据那UART必须在睡眠时保持接收唤醒能力这需要配置UART的唤醒中断和对应的GPIO。睡眠之前把未发送完的数据处理掉否则唤醒后数据可能丢失。实际的代码修改位置不同SDK版本不一样但大体思路是写一个app_sleep_handle()之类的函数在主循环末尾调用。如果确认所有任务都空闲就进入sleep。这个函数的判断条件一定不要漏掉BLE协议栈的事件调度否则会出大问题。3.2 广播间隔与连接间隔功耗与响应速度的权衡功耗和响应速度永远是矛盾的。广播间隔越长发现设备越慢功耗越低连接间隔越长手机与设备通信延迟越大功耗越低。这个道理很多人都懂但到底选多大的值需要根据产品场景来权衡项目里我把参数分别做了验证。我基于这个场景做了一个测试表格这是当时的实测功耗参考使用电流表或者功耗分析仪:参数组合广播间隔连接间隔平均电流连接态场景感受高性能模式100ms20ms约130uA左右未优化数据响应秒到延迟低均衡模式250ms30ms约80uA左右日常够用延迟可接受低功耗模式500ms50ms约45uA左右延迟明显但省电这个表格不是绝对的不同芯片、不同协议栈版本、不同测量条件下数值会有差异但趋势就是连接间隔每翻一倍连接态平均电流基本能降百分之二三十。需要强调一个容易被忽略的点连接参数不是固件单方面决定的。手机App发起连接后可以请求改变连接参数固件也可以被动接受参数。如果你想要低功耗就要在连接参数更新请求里把Minimum Connection Interval和Maximum Connection Interval都设置得比较大同时设置合适的Slave Latency从机延迟。Slave Latency是纯赚的参数它允许从机跳过若干个连接事件而不被主机认定为超时比如设置Slave Latency4那么主机每发4次连接事件从机才需要回应一次。这能让功耗下降很多但代价是单向数据延迟可能会变长。3.3 GPIO和外设的功耗陷阱那些你以为关了其实还在跑的模块低功耗改造中最隐蔽的坑不是你不会配睡眠模式而是你以为已经关掉的东西其实还在运行。我数一下这个项目里实际遇到过的调试串口没有关闭开发板上原本用于打印log的UART如果不显式关闭它的时钟和引脚即使不打印它也在耗电。有些SDK的log模块默认是开启的而且会在低功耗模式下把输出引脚拉高这部分电流虽然不大几毫安级别但对低功耗产品来说就是致命伤。GPIO悬空如果某个GPIO既没有接外设又没有配置为内部上拉/下拉浮空输入会导致漏电流。低功耗模式下GPIO的状态要逐脚检查该拉高的拉高该拉低的拉低不能留浮空。定时器没有停止有的定时器即使不触发中断只要没有关闭使能位时钟源就一直开着。SDK的低功耗API通常会自动关闭部分定时器但如果你自己用了一个通用定时器做周期任务别指望API会替你关。Flash仍在等待写完Flash之后Flash模块本身需要时间进入低功耗状态。SDK里一般有专门的Flash断电或idle处理函数主循环进入睡眠前要调用。这些坑不会让你程序跑崩但电流就是下不去。排查手段就是逐个模块关闭测试先全开测一个基准电流然后逐个关闭外设观察电流变化。一开始电流十几毫安关掉log串口后电流可能降到几毫安再关掉定时器可能降到几百微安最后睡眠正常了才能到几十微安。这个过程很枯燥但必须做。3.4 实测功耗与电池寿命估算怎么算才靠谱低功耗改完肯定要做整机实测。我在项目里用的方法是用一个精确的低功耗电流测量设备记录不同状态下的电流再把产品使用场景拆成几个状态按时间占比加权计算平均功耗。假设一个典型的温湿度传感器产品每10秒广播一次每次广播持续20ms广播电流约2mA每天被手机连接10次每次连接持续5秒连接态平均电流是100uA其他时间都在深睡眠状态睡眠电流是5uA。那么一天的平均电流大概是这样广播消耗10秒一次一天8640次每次20ms、2mA相当于每天消耗 8640 x 0.02s x 2mA 345.6mAs。连接消耗10次 x 5s x 0.1mA 5mAs。睡眠消耗剩余时间大约 86400s取5uA计算约 432mAs。合计每天约 782.6mAs折合成平均电流约9uA。用一节250mAh的CR2032电池理论上能用 250mAh / 0.009mA ≈ 27777小时约3.2年。但实际要打很多折电池自放电、极端温度、广播失败重试等最后可能打了五折但一年半是能扛住的。这个估算方法建议所有做低功耗产品的开发者都用一下避免拍脑袋说能用一年。4. OAD空中升级从分区配置到App联调的全过程OAD是标题里的另一个关键词。拿一个支持BLE的设备如果没有空中升级每次改固件都要开壳接烧录器这在产品落地阶段是不可接受的。我这次在BK3431上把OAD从SDK默认配置到后台上位机联调都过了一遍写一下关键节点。4.1 BK3431 OAD的原理为什么需要BootloaderApp分区BK3431的OAD实现和大多数可OTA的BLE芯片类似Flash被划分为Bootloader区和App区。Bootloader负责引导和接收升级固件App是正常运行的业务程序。具体到BK3431因为有内部Flash所以分区是直接在内部Flash上做的。SDK会根据link.ld或者编译脚本里的分区地址来链接Bootloader和App。如果分区链接地址没配好升级的时候写错地址轻则App启动不了重则把Bootloader覆盖掉整个芯片变砖。这一点比UART升级直刷要更小心。OAD的基本流程是这样的设备上电后Bootloader先跑检查App区是否有合法固件。如果有跳转到App区运行如果没有停在Bootloader等待升级。App运行时通过BLE发出服务广播手机App发现这个OAD服务后传输新固件。新固件按块传输每块校验CRC全部分块传完后写一个升级完成标志重启。重启后Bootloader检测到新固件标志执行固件搬移如果是从临时区搬到App区的话然后跳转到新App运行。BK3431的SDK里OAD是这样一个机制通过BLE服务接收固件到内部的一个临时Flash区域完成后重启Bootloader负责把一个区域的数据搬到App区。这个搬移耗时可能几百毫秒到几秒具体取决于固件大小和Flash擦写速度。4.2 OAD工程的编译配置内存分区和链接脚本怎么改编译OAD版工程时有两个地方是必须改的Bootloader工程的Flash起始地址和大小通常Bootloader占据Flash开头的一段区域比如BK3431的内部Flash如果Bootloader占16KB那App的起始地址就是Flash基址16KB。App工程的链接脚本必须把App的起始地址设置到Bootloader之后的地址而且中断向量表也需要重映射如果有这个机制。如果中断向量表没有重定位App运行时会跳到错误的中断处理函数表现为功能异常。我踩过一次坑第一次编译OAD版App时链接脚本里起始地址没改编译出来的App比Bootloader大的话直接覆盖了Bootloader区。上电后连Bootloader都跑不起来只能重新接烧录器用烧录工具全片擦除再烧Bootloader。这个教训总结成一句话凡是涉及分区和地址的配置编译前先算一遍Flash布局别盲目相信Demo的默认配置。4.3 OAD上位机App联调固件格式、分包大小、进度显示OAD调试中最耗时间的其实是上位机配合。手机App端要实现在线升级必须和固件端协议匹配。BK3431官方SDK通常会提供一个配套的OAD AppAndroid或者iOS但如果你自己写App关键点这几个固件格式固件bin文件通常要被加上包头包含固件版本号、固件大小、校验码等信息。Android App读取bin后解析包头判断是否支持升级然后逐包发送。分包大小OAD传输一般每包20字节或按MTU协商值拆分。每包结构通常包含一个包的序号、数据、CRC。发送时App要等待设备端回包确认上一包写入成功再发下一包。如果连续发、不等应答BLE的可靠传输特性虽然能保证不丢包但发送速度会受限。升级中断处理如果App升级到一半断电了设备重启。这时要有一个标识量标记当前升级状态App重新连接后能识别到设备处于升级未完成状态继续升级。这要求在固件里处理复位后仍然停留在升级流程中的状态。这部分我需要多说一句设备端一定要做固件合法性校验。收到一包先校验长度对不对、CRC对不对再决定是否写入Flash。如果非法的固件也照收照写最后刷了一个坏固件进去Bootloader启动检测时会判定固件不合法停在Bootloader继续等待升级——这还算好的如果校验逻辑写错把坏固件当成合法固件启动了那行为就不可预期了。5. 调试过程中的那些坑实测一个月才总结出来的排查方法从拿到SDK到最终整机通过测试我大概花了一个多月。中间遇到的问题不少有些是芯片特有的有些是BLE开发中通用的。这个章节记录下来也可以作为一份排查手册遇到类似问题可以按图索骥。5.1 用低功耗电流分析仪定位耗电模块的排查顺序低功耗电流降不下去时不要瞎猜要按照固定的排查序列走。我的排查顺序是这样的先测整机待机电流不做任何通信操作只上电看电流是多少。如果这一步电流就很高毫安级说明睡眠没有正常进入或者有些外设一直在跑。优先检查主循环是否真的调用了睡眠函数、睡眠前是否有任务没有处理完。关闭log输出SDK的log输出口一定要关掉否则它大概率是罪魁祸首。逐个关闭外设时钟逐个排查UART、SPI、I2C、ADC等外设的时钟使能位。注意有些外设的时钟不是直接一个使能位而是需要把GPIO配置成低功耗模式。测量广播态电流开一个广播任务观察广播周期的电流波形。正常应该看到一对对的脉冲对应广播收发平台电流很低。如果整片电流在广播间隔期间没有掉下去说明睡眠唤醒有问题。测量连接态电流连上手机后观察一个连接间隔内的电流波形。正常应该是在连接事件收发时有一个较高的电流脉冲然后迅速回落到低电流。如果连接事件后的回落慢可能是协议栈里有定时任务在频繁跑。5.2 一加调试器电流就变大那是调试接口在拖后腿一个很容易被忽略的现象只要连着调试器J-Link、ST-Link这类的虽然BK3431通常用UART烧录调试但道理一样芯片的电流就会偏大。因为调试器通常会让内核保持在调试状态或者强制时钟常开这样芯片就无法正常进入睡眠模式。所以低功耗测量时务必要断开调试器给板子纯电池或稳压源供电再测。我见过有人接调试器测得电流一直有6mA怎么改代码都下不去最后断开调试器电流瞬间掉到几十微安。不是代码的问题纯粹是测量环境不对。5.3 触发断连的常见原因连接参数更新失败、广播冲突、从机延迟过大做透传产品手机连接不稳定是最让人头大的问题。我梳理一下这个项目里遇到的断连场景和解决办法连接参数更新失败后设备主动断连有些安卓手机会在连接后尝试把连接参数设成一个比较小的值以提高通信速率但某些手机和BK3431协议栈协商失败设备端如果配置了连接参数更新失败断连的策略就会断开连接。解决办法是固件端允许参数更新请求或者干脆接受手机的参数不主动拒绝。广播与连接冲突设备在既广播又连接的时候如果广播间隔设置得太短而协议栈在连接事件和广播事件之间调度不过来芯片会忙不过来导致某个事件被丢弃。这种现象在高负载时会出现诡异的断连问题。解决办法是适当拉长广播间隔或者在不广播后再进入连接状态。从机延迟设得太大从机延迟大能省电但在手机发数据给设备时设备要等好几个连接间隔才响应如果手机端的超时时间短于这个累计延迟会被判定为连接超时。所以从机延迟不是越大越好通常控制在4-8之间比较稳妥。这类问题排查的基本功是抓空中包。Windows下可以用USB dongle配合工具抓BLE广播包和连接包看断连前的最后一个包是什么是Master发起的断连还是Slave发起的断连错误码是多少。有了错误码对照蓝牙核心规范里的错误码表定位方向基本就定了。这个技能做BLE开发早晚要会建议入门第一天就装好工具链。5.4 升级失败后设备变砖的恢复方法OAD最可怕的结果就是设备变砖。但好在BK3431这类芯片有内置Bootloader机制只要Bootloader没被覆盖变砖后可以通过UART串口或专用烧录工具重新烧Bootloader。我遇到过一次升级失败后无法进入Bootloader的情况排查下来原因是App区的固件头部的合法性标志写错了导致Bootloader在启动时误判App为合法跳转进去后程序崩溃然后没有机制切回Bootloader。解决办法重新用烧录器把Flash全片擦除再烧Bootloader和App。这提醒我一件事在做OAD功能时不要只测正常升级流程一定要把各种异常流程都测一遍升级一半断电、升级过程中手机App被杀掉、升级到一半BLE断连、发送一个坏固件包等。这些异常处理越完善产品上市后需要返修的几率就越低。6. 实测数据和后续扩展这个方案还能往哪走这个项目最后测试通过时的实测数据大概是广播态500ms间隔平均电流约20uA连接态30ms连接间隔Slave Latency2平均电流约70uA深睡眠电流约3uAOAD升级整个流程没有出现升级一半断连的情况。整套方案的模组面积也控制得比较小适合直接拿去做产品原型。如果后续要继续基于BK3431扩展我个人觉得有几个方向值得做加一个电量检测服务通过ADC采集电池电压用GATT特征值上报让App端显示剩余电量。这个功能在低功耗产品上几乎必备。增加多连接支持BK3431的协议栈是否支持多连接要查具体型号如果支持Central和Peripheral同时工作可以做手机和设备间的桥接场景。加密配对与绑定如果产品涉及隐私数据需要支持BLE的安全配对和绑定SDK里会有对应的安全管理模块但配置和使用上比透传复杂一些。把透传协议改造成AT指令集很多串口透传模块的原始形态是AT指令风格让客户的主控MCU通过AT指令控制模块行为调试更方便也更贴近现有客户的使用习惯。最后再分享一个小技巧BK3431这类国产BLE芯片的SDK文档质量参差不齐但它们的寄存器手册、Demo工程、官方应用笔记往往比想象中有价值。遇到问题时优先在芯片厂商提供的SDK目录里搜关键字很多时候能在Demo工程里找到和你需求类似的参考实现。我这次改低功耗时很多配置参考就来自它自带的低功耗例程花点时间读懂SDK自带的代码比漫无目的地上网搜要高效得多。本文还有配套的精品资源点击获取
返回列表