做智能硬件年头长了,蓝牙这个老熟人是绕不过去的。尤其这两年,你随便打开一个产品需求书,低功耗、小体积、稳定连接、好调试,几乎成了标配。我自己做“蓝牙转串口+蓝牙HID键盘”二合一的小设备时,先在HC05这个级别的经典蓝牙模块上踩了一圈坑,又试过ESP32、STM32外挂蓝牙的方案,最后换到沁恒的CH592这颗蓝牙MCU,才算把整套集成方案和低功耗设计梳理顺了。这篇就当一次完整复盘:从芯片选型逻辑、硬件最小系统、协议栈与TMOS任务框架,到低功耗参数怎么抠、常见故障怎么查,全按我实做的顺序来写。正在选型蓝牙MCU的硬件工程师、做穿戴设备和HID外设的开发者,应该能从中直接拿走不少东西。
1. 集成方案设计思路:先告别“MCU+蓝牙模块”的旧套路
1.1 传统蓝牙模块方案的三个死穴
早年做蓝牙透传,大家最爱用HC05/HC06这类串口蓝牙模块,主控只要有UART,发几条AT指令就能改名、配对、连手机,确实傻瓜。但这套东西放到产品里,问题一个比一个明显。
第一个是功耗。经典蓝牙(BR/EDR)本身就不是为低功耗设计的,SPP透传跑起来电流常年十几毫安往上,纽扣电池想都不要想。第二个是双芯片带来的不可靠——主控和模块之间靠UART握手,波特率不匹配、电平不一致、电源纹波一大,就会出现连不上、传乱码这些“鬼问题”,你查代码查半天,最后发现是接线太长导致信号变形。第三个是手机端兼容性,iOS对SPP基本不开放,App想直连得走额外授权流程,麻烦到让人怀疑人生。
所以从产品化角度,BLE是必然方向,而BLE SoC又是BLE方案里集成度最高的形态。
1.2 一颗CH592替代了原来哪些东西
CH592这颗芯片很有意思,它把一块完整的单芯片方案该有的东西都塞进去了:32位RISC-V内核、BLE 5.4射频收发器、512KB Flash和32KB RAM、UART/SPI/I2C/ADC/PWM这些常用外设,连USB都带了。也就是以前“一颗主控MCU+一颗蓝牙模块”两套系统要干的活,它一颗芯片全包。
用CH592做透传设备,串口直接挂在芯片上,软件里把BLE协议栈收到的数据丢给UART,再把UART收上来的数据通过BLE Notify发出去,中间不需要任何外部协议转换。做HID键盘鼠标也一样,GPIO直接读按键矩阵,协议栈走HID Over GATT Profile(HOGP),物理层、链路层、L2CAP这些全部内置,应用层只需要关心报告格式。省掉的不仅是一颗物料,更是两个系统之间沟通的所有隐患。
1.3 集成度和低功耗为什么是同一件事
很多人把“集成方案”和“低功耗设计”当成两件事,其实在CH592这种SoC上,它们是同一件事的两面。
集成度高,意味着CPU可以直接访问射频寄存器、直接配置广播参数、直接控制外设时钟。低功耗最核心的优化手段——精确控制每个外设的开关时机、精确设定睡眠唤醒周期、精确调整广播间隔——全都是靠寄存器级操作实现的。如果是双芯片方案,主控和蓝牙模块之间只能靠UART发指令,粒度粗、延迟大、不可控,功耗优化自然做不细致。
我的习惯是先定功耗预算,再反推软件流程。比如目标是纽扣电池撑一年,那睡眠电流不能超过10uA,广播平均电流不能超过150uA,连接状态下的平均电流也要控制在几百微安级别。预算先算清楚,后面每一步设计都有依据,不会做着做着就放飞。
2. 方案选型:CH592和常见替代方案的真实差异
2.1 横向对比:HC05、杰理蓝牙、ESP32、STM32外挂BLE、CH592
| 方案 | 蓝牙类型 | 集成度 | 典型电流 | 开发难度 | 适合场景 |
|---|---|---|---|---|---|
| HC05/HC06 | 经典蓝牙SPP | 双芯片 | 高 | 容易(AT指令) | 原型验证、玩具、对功耗无要求 |
| 杰理蓝牙SoC | 经典+BLE,偏音频 | 单芯片 | 中高 | SDK偏音频 | 蓝牙音箱、耳机、语音设备 |
| ESP32 | WiFi+BLE双模 | 单芯片 | 偏高 | 中等,生态丰富 | 网关、需要WiFi的场景 |
| STM32+外挂BLE | BLE 4.x/5.x | 双芯片 | 中等 | 中等,复用STM32代码 | 已有大量STM32代码的存量项目 |
| CH592 | BLE 5.4,主从一体 | 单芯片 | 低 | 中低,上手快 | 穿戴、HID、透传、传感器采集 |
每行展开说几句。
HC05属于经典蓝牙,连手机发数据很方便,但iOS上SPP是个大坎,功耗也高。杰理这种以音频为核心的蓝牙SoC,做耳机音箱很强,做数据透传不是它的主场,协议栈和文档都更偏向音频链路。ESP32性能强、资源多,但WiFi和BLE双模的天生功耗摆在那里,做低功耗电池设备很吃亏。STM32外挂BLE的好处是能复用存量代码,但两套系统联调、两套电源、两片PCB,成本和出问题的概率都往上涨。CH592算是把BLE这个领域的集成度和功耗做到了比较均衡的位置,单芯片能跑主从一体,BLE 5.4的特性也齐全。
2.2 选型必看的四个硬指标
选蓝牙MCU,我只盯四个硬指标。
第一是协议栈成熟度和License。有些芯片协议栈要额外收费,有些封在库里的代码常年不更新。CH592的BLE协议栈在SDK里直接提供,主从一体、多连接都支持,这是做产品的硬基础。第二是睡眠电流和唤醒时间。睡眠电流直接决定电池续航,唤醒时间决定系统响应速度,CH592带RTC保持的睡眠模式电流在微安级,唤醒时间足够支撑HID这种对延迟敏感的应用。第三是射频指标,发射功率可调范围和接收灵敏度。接收灵敏度差1dB,室内穿墙能力就差一大截。第四是外设资源和封装,UART、SPI、PWM、ADC够不够用,封装是否方便小体积设计,这些直接影响到电路板尺寸和BOM成本。
2.3 什么时候不应该选CH592
选型不是“选最好的”,而是“选最匹配的”。有几类场景我不建议用CH592。
一是需要蓝牙音频流传输,比如蓝牙音箱、耳机,这类产品对音频编解码和A2DP的支持要求很高,杰理这类音频蓝牙SoC更专业。二是需要WiFi和BLE同时在线,比如家庭网关、配网设备,ESP32的双模优势无可替代。三是存量代码量巨大的项目,比如已经有十万行STM32固件,硬迁到RISC-V内核,移植和测试成本可能高于外挂一颗BLE芯片。四是产品对认证有特殊要求,不同芯片厂商提供BQB等认证支持的程度差别很大,这个要在立项阶段问清楚。
3. 低功耗设计核心要点:抠细节才是关键
3.1 先算功耗账:平均电流不是“睡眠电流”一个字就能说清的
很多人一看数据手册,睡眠电流多少微安,就觉得产品一定省电,实际做出来差一大截。原因很简单,系统不是一直睡着的,广播、连接、传感器采集这些事件会周期性打断睡眠,真实功耗必须按占空比算。
平均电流 = 各状态电流 × 该状态时间占比 之和。
我实际用CH592做过一个广播应用,广播间隔设100ms,每个广播事件持续约2.5ms,广播期间电流约4.5mA,其余时间睡眠电流约3uA。平均电流算下来是:
(2.5ms / 100ms) × 4500uA + (97.5ms / 100ms) × 3uA ≈ 112.5uA + 2.9uA ≈ 115uA
如果客户要求两个钮扣电池撑一年,这个值就超预算了。把广播间隔拉到1秒,平均电流立马降到14uA左右,整整差了8倍。这就是为什么设计阶段必须把“平均电流”当核心指标,而不是盯着数据手册上的单个睡眠值。
3.2 睡眠模型与唤醒源配置
CH592这类BLE SoC,睡眠通常分几个层级。Idle模式CPU停转但外设时钟可配,适合短时间等待。Sleep模式保留RAM和RTC,蓝牙事件可以定时唤醒,这是低功耗设备的主力模式。Shutdown模式电流最低,但唤醒等于系统复位,RAM内容丢失,用之前必须想好参数存哪里。
这里有个软件框架的配合点。CH592的SDK里跑的是TMOS调度器,它的核心思路是空闲自动睡、事件来了再醒。你注册一个事件,处理完就返回调度器,调度器发现任务队列空了,就会自动进睡眠。开发时不要用“while(1)空转”这种写法,让CPU尽量处于自律睡眠状态,这是低功耗应用的基本功。
唤醒源方面,常用的有三个:RTC定时、GPIO外部中断、BLE协议栈的连接事件。RTC定时适合周期采集传感器的场景,GPIO中断适合按键唤醒,BLE连接事件由协议栈自动维护,你只需要保证连接参数设置合理,系统会在连接事件到来前自动醒来。
3.3 广播与连接参数配置:影响功耗最大的几个旋钮
BLE功耗的大头往往在广播和连接阶段,我整理过一张调参对照表,直接分享给大家。
| 参数 | 取值范围示例 | 对功耗的影响 | 我的推荐 |
|---|---|---|---|
| 广播间隔 | 20ms~10.24s | 越短功耗越高,同时可发现性越好 | 无连接需求时1s以上,等待连接时100ms |
| 连接间隔 | 7.5ms~4s | 越短数据吞吐越高,但CPU唤醒次数多 | 非高速传输时30ms起步 |
| Slave Latency | 0~499个连接事件 | 越大从机睡眠时间越长 | 视应用设4~8个即可 |
| Supervision Timeout | 100ms~32s | 太长掉线检测慢,太短误判 | 建议设为连接间隔的20倍以上 |
连接间隔和Slave Latency是省电利器。比如连接间隔30ms,Slave Latency设为4,意味着主机每150ms才轮询一次从机。从机在不收发数据的中间时段可以一直睡觉,电流曲线拉出来非常漂亮。代价是数据延迟变高,如果做双向遥控这种对延迟敏感的应用,就得相应调短。
我贴一段在SDK里配置广播和连接参数的代码片段,方便参考。
// 设置广播参数 gap_role_t role = GAP_PERIPHERAL_ROLE; uint16_t adv_interval = 160; // 单位:0.625ms,160=100ms uint16_t timeout = 0; // 持续广播 GAP_AdvertisePara_t advPara = { .advIntervalMin = adv_interval, .advIntervalMax = adv_interval, .advType = GAP_ADTYPE_ADV_IND, .ownAddrType = GAP_ADDRTYPE_PUBLIC, .channelMap = GAP_ADVCH_ALL, }; GAP_SetParameter(GAP_PARAM_ADVERTISE_INTERVAL_MIN, 4, &adv_interval); GAP_SetParameter(GAP_PARAM_ADVERTISE_INTERVAL_MAX, 4, &adv_interval); // 设置连接参数 uint16_t intervalMin = 48; // 30ms uint16_t intervalMax = 52; uint16_t slaveLatency = 4; uint16_t superTimeout = 1000; // 1s GAP_SetParameter(GAP_PARAM_LINK_CONN_INTERVAL_MIN, 2, &intervalMin); GAP_SetParameter(GAP_PARAM_LINK_CONN_INTERVAL_MAX, 2, &intervalMax); GAP_SetParameter(GAP_PARAM_LINK_SLAVE_LATENCY, 2, &slaveLatency); GAP_SetParameter(GAP_PARAM_LINK_SUPERVISION_TIMEOUT, 2, &superTimeout);注意参数单位,BLE协议栈里连接间隔和广播间隔的单位都是1.25ms和0.625ms这种“非人类时间”,写代码时一定看清楚SDK注释,我在这上面栽过跟头,把30ms写成了30个tick,结果连接慢得像蜗牛。
3.4 GPIO与外设:低功耗最容易翻车的地方
很多人把功耗异常算在射频头上,其实真正把睡眠电流拉高的,往往是几个看起来无关紧要的GPIO和外设配置。
我这次项目中就遇到一个典型案例:睡眠电流设计目标3uA,实测12uA,排查半天,最后发现是一个悬空的按键检测引脚没有配置模式,内部漏电把电流拉高了近4倍。GPIO悬空时电平不稳定,数字输入缓冲器会反复翻转,电流消耗远超想象。正确做法是,所有不用的引脚要么配置成模拟输入,要么配置成输出低电平,功能上用到的输入引脚必须加上确定的上拉或下拉电阻,让电平稳定。
还有一个隐藏坑是内部上拉电阻。某些GPIO默认使能内部上拉,一颗两颗没事,按键多、引脚多的产品,几十颗上拉同时挂在待机电路上,每颗按几十微安算,加起来就是不小的数字。设计时要把GPIO初始化和睡眠前配置分开写,睡眠前统一把这些引脚恢复成低功耗状态。
ADC和传感器也一样。ADC不断采样、传感器LDO持续供电、LED的限流电阻,这些看起来单电流不大,在微安级睡眠目标下都是“大漏勺”。LED不要直接放长供电,传感器尽量由GPIO控制电源轨,需要时才上电,测完就断。
3.5 供电方案与功耗测量:数据要能量出来才算数
低功耗设计做得再好,测不出来就是白做。测量方法我按精度需求分三档。
第一档用万用表串联测平均电流,适合看广播或连接状态下的长时间均值,注意万用表串联会引入压降,量程选的太大会导致芯片实际电压不足,从而出现莫名其妙的重启。第二档用示波器测瞬态电流曲线,方法是串一个小阻值采样电阻,比如10欧姆,测电阻两端电压波动,再除以阻值得到电流,能清楚看到睡眠-唤醒-广播的波形。第三档用专业功耗分析仪,能自动积分出平均功耗,还能自动记录不同状态的时间占比,调试时比自己拿表去盯省太多事,如果项目组有条件,强烈建议配一台。
供电方案上,电池直接供电和LDO/DCDC的选择也很关键。BLE设备瞬时峰值电流可以达到毫安到十几毫安,钮扣电池瞬间压降大,配套电容要舍得放,我一般在电源端放一个4.7uF陶瓷电容加一个小容量RF去耦电容,效果稳定。如果电池电压偏高,需要降压,优先选低静态电流的LDO,因为DCDC自身功耗在睡眠状态反而可能成为主角。
4. 集成方案实操:从最小系统到功能落地
4.1 硬件最小系统与天线匹配
CH592的硬件最小系统很精简:电源、晶振、射频匹配网络、烧录调试接口。晶振是关键,高频晶振给射频提供参考时钟,频率不准直接导致信号偏差、连不上、掉线。建议直接照官方参考设计画,晶振负载电容按手册值放,不要自己“优化”出花样。
天线部分,CH592这类单芯片BLE要用到PCB天线或外置天线。我做的是板载PCB天线设计,芯片射频输出引脚到天线之间加了一节π型匹配网络,就是两个电容加一个电感的位置,方便调试时调阻抗。天线周围的地平面处理直接影响信号强度,天线区域铺地要净空,馈线附近的地要连续。打样回来后先用网络分析仪看驻波比,没条件的话至少用nRF Connect这类手机App测RSSI,多走几米看信号衰减是否均匀,方向性太强说明天线附近金属件或铺地有问题。
4.2 TMOS与BLE协议栈分层
软件开发前,我建议花半小时先理解CH592的SDK结构,不要拿到工程就一顿乱改。SDK大体分成三层:HAL层管硬件外设,BLE协议栈层管链路、GATT、SM这些蓝牙标准协议,APP层跑你的业务逻辑。任务和事件由TMOS调度器统一管理。
TMOS是CH592开发绕不开的东西。它像一个轻量级事件循环,你注册一个任务,绑定事件回调,协议栈收到蓝牙数据后会丢事件给APP层,APP层也能定时发事件给任务做周期采集。每个事件处理函数必须快速返回,不能在里面跑耗时循环——因为调度器要维护低功耗睡眠,事件处理完就睡,这是TMOS和业务代码协作的基本节奏。
初始化流程大致是:系统主频和外设时钟配置、GPIO初始化、TMOS任务注册、GAP初始化(角色、MAC、广播参数)、GATT服务注册(透传服务/HID服务)、启动广播。这套流程每款BLE芯片大同小异,理解了CH592的顺序,以后换其他芯片也基本能触类旁通。
4.3 串口透传与AT指令集
我的项目里主体功能是BLE转串口,为了兼容HC05那套使用习惯,我设计了一套AT指令。基本思路是:芯片上电默认做透传,收到的BLE数据从串口出,串口收到的数据通过BLE Notify发给手机,同时留一个指令模式入口,发AT指令进入配置模式,配置完再回透传。
| AT指令 | 功能 | 参数示例 |
|---|---|---|
| AT+RESET | 复位模块 | AT+RESET |
| AT+NAME | 设置广播名 | AT+NAME=MY_BLE |
| AT+ADVON / AT+ADVOFF | 开关广播 | AT+ADVON |
| AT+BAUD | 设置串口波特率 | AT+BAUD=115200 |
| AT+UUID | 修改服务UUID | AT+UUID=FFE0 |
| AT+ROLE | 切换主从角色 | AT+ROLE=PERIPH |
实现时要注意一个细节:AT指令数据可能和透传数据混在同一个串口数据流里,我用的是前导字符+超时判断,串口连续收到“AT\r\n”才进入指令解析,否则全当透传数据处理。这个机制如果没做好,透传过程中手机发个带“AT”字符的数据就被误判成指令,设备直接抽搐。另外透传通道建议把BLE的MTU协商到最大,默认在20字节左右,性能会很憋屈。
4.4 HID键盘上报实现
第二个功能是蓝牙HID键盘。手机、平板、电脑对HID设备支持非常友好,配对后当普通蓝牙键盘用,不需要额外装App。HOGP协议栈由BLE协议层处理,应用层主要搞定两件事:注册HID服务和上报键盘报告。
键盘报告格式是业界标准:8字节的报告里,第0字节是修饰键(Ctrl/Alt/Shift/Windows),第1字节保留,后面6字节是同时按下的按键码。我贴一段上报示例:
uint8_t report[8] = { 0x00, // 修饰键,0x02表示Shift被按下 0x00, // 保留 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 // 按键码数组,0x04对应A键 }; // 需要按下时,把按键码填入report[2]~report[7] // 调用BLE HID服务发送接口上报 HID_Notify(report, sizeof(report));HID键盘最怕的是重复连续按键时按键码填写回溯不干净。比如用户按A松开再按B,第二帧必须是“A键释放、B键按下”的状态,不能残留A的按键码,否则键盘会一直输出A。我调试时直接用手机备忘录当测试工具,每按一次都观察光标是否多输出字符,排查效率很高。
如果还要做鼠标就够了则类似,报告结构换成X/Y位移和按键位,核心逻辑一样。
5. 常见问题与排查技巧实录
5.1 搜不到设备、连接掉线
设备广播半天手机搜不到,先排除三类原因。
第一是距离和天线问题,把设备凑近手机再试,还不行就用频谱仪或另一台手机看是否有射频信号。第二是晶振问题,晶振没起振或频偏大,设备看起来在上电,但射频链路根本没工作。第三是广播数据和连接白名单问题,比如设置了定向广播地址过滤,普通手机自然就搜不到。用nRF Connect这款App扫一下周边设备,如果CH592根本没出现在列表里,问题基本在射频和广播参数;如果出现在列表里但连接失败,重点查连接参数和安全参数。
连接掉线方面,我踩过最隐蔽的坑是Supervision Timeout设置太短,主机稍微卡顿一下,链路就超时断开。排查办法是拉长超时时间测试,如果掉线频率明显下降,就是超时和连接间隔的乘积余量不足。另外供电跌落也会导致掉线,用示波器抓射频发射瞬间的电源波形,看到明显跌落就是电容容量不够或电池内阻过大。
5.2 透传丢包、速率上不去
透传丢包最典型的原因是串口波特率和BLE链路速率不匹配。BLE单包默认最多20字节,就算协商好MTU,连续大流量时底层带宽也有限。如果串口一次性灌进来几百个字节,而BLE没发完,后面的数据就会丢。解决思路是加一个软件环形缓冲区,串口收数据先进缓冲,BLE从缓冲里取数据发,同时用BLE的发送完成回调做流控——当发送缓冲区满时,让串口侧暂时停一下或者做软件流控。
速率上不去的另一个限制是MTU没协商。CH592把MTU提到247字节后,单包能装的用户数据从20字节涨到接近240字节,同样一条链路的吞吐几乎提升十倍。前提是手机端也要支持GATT MTU协商,现在主流手机系统都默认支持,应用层调用一下设置MTU的接口就行。
5.3 HID配对失败或者连接后没反应
HID设备配对后在系统里能看到,但按键没反应,先查Report Map。Report Map如果语法有误、报告ID对应不上,操作系统会加载HID服务失败。我调试时的方法是先用手机系统连接,再用PC连接对比,如果PC连接能看到HID描述符但发送无效果,基本就是报告数据格式和描述符里的长度不一致。
还有个容易忽略的点是绑定重连。HID设备一般都要做Bonding(绑定),绑定信息保存在芯片Flash里,如果每次重连都要求重新配对,有些系统的体验会很糟糕。要确保协议栈保存了长期密钥,重连时不需要再次输入PIN码。注意Flash保存的密钥在批量烧录时可能包含之前测试的配对残留,量产前要考虑清空绑定信息,或者用出厂唯一MAC地址做管理。
5.4 功耗异常排查流程
功耗异常不要瞎猜,我按这个顺序排查,基本都能找到问题。
| 症状 | 可能原因 | 排查方法 |
|---|---|---|
| 睡眠电流偏高 | GPIO悬空、内部上拉、外设未关 | 逐组GPIO配置为低功耗模式,二分法定位 |
| 广播平均电流超高 | 广播间隔过密、发射功率过大 | 拉长广播间隔测试,对比平均电流 |
| 连接时电流异常高 | 连接间隔太短、Slave Latency为0 | 调整连接参数,抓电流曲线看唤醒频率 |
| 偶发瞬态电流尖峰 | 电容容量不足、电源路径阻抗高 | 用示波器抓射频发送瞬间电源波形 |
| 待机后无法唤醒 | 唤醒源配置错误、GPIO中断冲突 | 检查中断标志是否被残留事件占用 |
二分法定位GPIO问题是最实用的技巧:一半引脚配置成低功耗状态,测一次电流,再换另一半,几次就能把问题引脚圈出来。实测下来,低功耗项目的“最后一公里”几乎都是被这种小问题卡住的。
6. 写在最后的几句实在经验
CH592这套方案做下来,我的感受是:单芯片蓝牙MCU的集成度确实解决了传统“MCU+蓝牙模块”方案里最麻烦的链路可靠性问题,低功耗能力在设计参数合理、外设管理到位的情况下也完全能打。调低功耗时记住那句话,电流曲线不会说谎,每一个异常峰值的背后都对应一个具体的代码行为,二分法、抓波形、看日志,总能找到源头。最后再分享一个我从老工程师那里学到的习惯:每次调完一版固件,都顺手记录当前的广播间隔、连接参数、实测平均电流,这组数据积累起来,就是团队最宝贵的选型基础和排障参照,远比文档里的“参考值”有价值。