BR1601A02 低功耗休眠实战:`br_sleep` 机制、按键 / BLE 双唤醒

BR1601A02 低功耗休眠实战:`br_sleep` 机制、按键 / BLE 双唤醒
本文基于 Barrot百瑞互联BR1601xx SDK V1.3、官方BR1601A01-DK 原理图以及 开发板实测整理。BR1601A02 与 DK 板所用 BR1601A01 同属BR1601 系列休眠管理机制与 SDK API 完全一致文中所有代码、寄存器与唤醒逻辑在 A02 上可直接复用涉及引脚编号时以 A02 实际封装为准。1. 这颗芯片凭什么睡得好做电池供电的 BLE 产品BMS、手环、信标、传感器节点待机功耗直接决定续航。BR1601 系列的几项硬件特性是它能把休眠电流压到 µA 级的底气特性参数对休眠的意义内核 / 主频32 位高性能低功耗处理器最高 64 MHz唤醒后能快速处理完事务再睡休眠可保持 SRAM48 KB睡后上下文不丢唤醒无需冷启动Flash256 KB—射频BLE 5.11M/2M/125k(S8)/500k(S2)AoA/AoDBLE 链路在休眠期间仍可维持/唤醒关键外设KEYSCAN、RTC、IWDG/WWDG、LPUART、EXTIRTC/LPUART/EXTI 都可作唤醒源调试SWD进入休眠后 SWD 失效后面重点讲一句话总结CPU 和大部分外设时钟可关但 48 KB SRAM、BLE modem、RTC、LPUART、EXTI 在休眠时保持供电/监听——这就是 BR1601 的浅睡保活模型。⚠️ 注意SDK 文档未在公开章节给出确切的休眠电流数值实际 µA 量级与 GPIO 配置、是否保持 BLE 连接、32.768 kHz 晶振、LDO/DC-DC 模式强相关务必以芯片 datasheet 与实测为准。文末第 7 节给了测量方法。2. 看懂原理图唤醒的硬件伏笔低功耗不是纯软件话题——唤醒源必须在硬件上真实连通。下面把 DK 原理图BR1601A原理图.pdf共 2 页第 2 页为主体拆开看。整页概览左侧是 USB 转串口与 5V→3V3 电源中间是 BR1601A01 主芯片右侧是 RF 匹配与 RESET / WAKEUP 按键下方是指示灯与 SWD 调试口。2.1 主芯片与供电 / 调试芯片左侧外接晶体与复位左下引出SWD 调试口右上为 RF 走线需 50 Ω 阻抗匹配与 DC-DCVDCDC/SWITCH引脚、电感 L7、R23 0R。供电可在LDO 模式 / DC-DC 模式间选择——追求极限待机功耗时建议 DC-DC 模式。2.2 关键中的关键WAKEUP 按键接到了 PB3这张图是全文最重要的硬件证据请盯紧右下角K2 是 “WAKEUP” 贴片按键一端接网络标号PB3另一端接地。按下 → PB3 被拉低 → 产生下降沿。K1 是 RESET 按键RESET网络按下接地用于烧录与复位。左下排针CON5把PB3引出方便外接唤醒信号同组排针还引出了PB1/PB2/PB4/PB5/PB6(TX)。主芯片右侧引脚可见PB13/MICP、PB11/MICN、PB12/MIC_BIAS、PB10、PB7/RX等经CON15/CON16引出。结论硬件上 PB3 就是官方预留的唤醒脚且默认接了 K2 按键到 GND。这正好解释了后文代码里为什么用PB3 EXTI3 下降沿做唤醒——软硬件完全对得上。2.3 串口、电源与板载 LED / SWD左上USB 转串口经PB6(TX)/PB7(RX)到芯片是 DTS 透传所用的USART15V→3V3LDO 与 LDO/DC-DC 跳线选择下方板载指示灯VDD_3V3 → LED → 限流电阻 → 控制脚低电平点亮与SWD 调试口。提示本文示例程序里的两颗状态灯是外接在PA6/PB0、高电平点亮自定义接线与板载低电平点亮的 LED 不是一回事下文代码以注释Pin mapping per user request为准。原理图主要用来佐证PB3 唤醒按键与调试/串口的物理连接。3. SDK 休眠管理机制5 个函数撑起整个低功耗框架BR1601 的休眠由 SDK 的“休眠管理模块”文档第 7.4 节提供声明在bsp/common/inc/co_printf.h是的和日志在同一个头里。一共就 5 个接口分工极其清晰接口原型作用返回值加锁uint8_t br_sleep_lock_acquire(void)休眠计数锁 1阻止系统入睡true上锁成功false上锁溢出解锁uint8_t br_sleep_lock_release(void)计数锁 -1true释放成功false已全部释放入睡void br_sleep(void)休眠管理主函数无事件则睡有事件则唤醒无入睡前钩子void app_sleep_prepare_proc(void)用户实现、固定名字睡前回调无唤醒后钩子void app_sleep_resume_proc(void)用户实现、固定名字醒后立即回调无文档给出的标准主循环写法第 7.4.3 节示例while(1){cs_schedule();// 处理 BLE / 协议栈调度other_task();// 用户其它任务br_sleep();// 没事就睡有事件自动醒来继续跑}3.1 三个关键设计点①br_sleep()是有条件入睡只有协议栈空闲、且sleep_lock计数为 0 时CPU 才真正进入低功耗只要 BLE 在广播/连接、或某个模块持锁它就会跳过睡眠继续cs_schedule()。这意味着你不需要自己判断该不该睡把该做的事做完、该持的锁持好剩下的交给br_sleep()。②sleep_lock是计数锁不是布尔锁可嵌套 acquire/release要成对使用。典型场景是外设 DMA 传输期间持锁、传完释放app_usart.c里就保留了这对被注释掉的锁调用见第 6 节。注意 acquire 可能因计数溢出返回 false健壮代码应检查返回值。③ 两个 hook 是弱符号占位符app_sleep_prepare_proc/app_sleep_resume_proc名字必须一字不差SDK 在睡前/醒后会自动调用。前者用来关外设、配唤醒源、切 GPIO 到省电态后者用来恢复时钟/外设/日志。几乎所有低功耗相关的睡前收摊子、醒后重建现场都该放这里。调用关系一目了然main 循环 │ ├─ cs_schedule() ──► BLE 事件连接完成/断开/收数──► 触发 RequestWakeFlash() │ └─ br_sleep() ├─【允许入睡? 锁0 且协议栈空闲】 │ ├─ 调 app_sleep_prepare_proc() ← 关LED / 配PB3 EXTI唤醒 / 清挂起位 │ ├─ CPU 进入低功耗SRAM/BLE/RTC/LPUART/EXTI 保持 │ │ ↑ PB3下降沿 或 BLE事件 唤醒 │ └─ 调 app_sleep_resume_proc() ← 恢复日志 / 恢复LED / 置唤醒闪灯标志 └─【不允许入睡】直接返回继续下一轮 cs_schedule()4. 实战ble_dtss休眠例程逐段拆解下面这份main.cdemo/ble/ble_dtss/src/main.c就是一份可烧录、可验证的最小休眠模板上电闪灯 3 秒 → 启动 BLE 广播 → 主循环br_sleep()→PB3 按键或 BLE 连接唤醒后闪灯 5 秒再睡。4.1 引脚定义与原理图第 2.2 节对应// main.c:97-108#defineLED1_PORTGPIOA#defineLED1_PINGPIO_PIN_6// PA6状态灯1外接高电平点亮#defineLED2_PORTGPIOB#defineLED2_PINGPIO_PIN_0// PB0状态灯2外接高电平点亮/* PB3: key wake input, pull-up, wake on falling edge */#defineKEY_WAKE_PORTGPIOB#defineKEY_WAKE_PINGPIO_PIN_3// PB3唤醒键原理图 K2 → GND#defineKEY_WAKE_EXTI_LINEEXTI_LINE3#defineKEY_WAKE_PORT_SOURCEGPIOB_PORT_SOURCE#defineKEY_WAKE_PIN_SOURCEGPIO_PIN_SOURCE3PB3做唤醒硬件内部上拉 外部按键到 GND见原理图 K2常态高、按下低 →下降沿触发。4.2 睡前钩子关外设、开唤醒、清挂起位// main.c:202-213voidapp_sleep_prepare_proc(void){/* 1) 睡觉期间灭灯省电 */GPIO_ResetBits(LED1_PORT,LED1_PIN);GPIO_ResetBits(LED2_PORT,LED2_PIN);/* 2) 配置 PB3 为 EXTI3 下降沿唤醒源 */KeyWake_Init();/* 3) 清掉睡前残留的挂起边沿防止刚一睡下就被旧事件唤醒 */EXTI_ClrITPendBit(KEY_WAKE_EXTI_LINE);}KeyWake_Init()main.c:144-174做了三件事开 GPIOB/AFIO 时钟 → PB3 配置上拉输入 →GPIO_ConfigEXTILine把 PB3 映射到 EXTI3下降沿触发并开EXTI2_3_IRQn中断// main.c:160-173GPIO_ConfigEXTILine(KEY_WAKE_PORT_SOURCE,KEY_WAKE_PIN_SOURCE);exti.EXTI_LineKEY_WAKE_EXTI_LINE;exti.EXTI_ModeEXTI_Mode_Interrupt;exti.EXTI_TriggerEXTI_Trigger_Falling;// 与原理图 K2 按下拉低对应exti.EXTI_LineCmdENABLE;EXTI_InitPeripheral(exti);nvic.NVIC_IRQChannelEXTI2_3_IRQn;nvic.NVIC_IRQChannelPriority1;nvic.NVIC_IRQChannelCmdENABLE;NVIC_Init(nvic);为什么清挂起位很关键如果睡前 PB3 已经有过抖动边沿EXTI_Line3的挂起位会置 1不清的话br_sleep()刚把 CPU 睡下就会被这个陈旧事件立刻唤醒表现为睡不下去 / 电流降不下来。这是低功耗调试里最高频踩的坑之一。4.3 唤醒后钩子恢复现场 标记唤醒源// main.c:219-235voidapp_sleep_resume_proc(void){co_log_init();// 恢复调试日志PB12 / LPUART TXLED_Init();// 恢复 LED GPIOif(g_pb3_wake_flag){// 若是 PB3 把我们叫醒g_pb3_wake_flag0;g_wake_flash_active1;// 申请一次唤醒闪灯 5sco_printf(Wake: PB3 key detected, flash 5 s\r\n);}}PB3 的中断服务程序只做置标志 清挂起把耗时动作踢回主循环符合快进快出的中断原则// main.c:250-257voidEXTI2_3_IRQHandler(void){if(EXTI_GetITStatus(KEY_WAKE_EXTI_LINE)!RESET){EXTI_ClrITPendBit(KEY_WAKE_EXTI_LINE);g_pb3_wake_flag1;}}4.4 主循环br_sleep() 唤醒闪灯 计数锁// main.c:262-317节选intmain(void){delay_n_10us(50*1000);// 上电 500ms 延时co_log_set_baud(921600);co_log_init();LED_Init();LED_Flash(3000,200);// 上电闪 3s提示我活了ble_stack_init();// 协议栈ble_gap_init();ble_adv_init();ble_dtss_init();ble_adv_start();// 开广播BLE 保持 → 可被连接唤醒while(1){cs_schedule();if(g_wake_flash_active){g_wake_flash_active0;br_sleep_lock_acquire();// ★闪灯期间持锁禁止入睡for(uint32_ti0;i25;i){// 25 × 200ms 5sGPIO_SetBits(LED1_PORT,LED1_PIN);GPIO_SetBits(LED2_PORT,LED2_PIN);delay_n_ms(100);GPIO_ResetBits(LED1_PORT,LED1_PIN);GPIO_ResetBits(LED2_PORT,LED2_PIN);delay_n_ms(100);cs_schedule();// ★循环内继续喂协议栈BLE 不断线}br_sleep_lock_release();// ★释放锁允许重新入睡}br_sleep();// 没事就睡}}这段把第 3 节的三个设计点全用上了持锁闪灯闪灯是用户可感知的活跃期必须br_sleep_lock_acquire()撑住否则刚亮一下br_sleep()就把灯灭了。25 次循环里每次都要调cs_schedule()保证 5 秒内 BLE 广播/连接不被饿死。锁成对释放闪完br_sleep_lock_release()下一轮br_sleep()才睡得着。如果漏 release设备会永远醒着、电流居高不下——这是第二高频的坑。入睡点唯一主循环末尾只有一个br_sleep()逻辑清晰便于排查到底睡没睡。5. 两种唤醒源按键本地与 BLE 连接无线这个 demo 同时演示了嵌入式低功耗里最常见的两类唤醒。5.1 本地按键唤醒PB3 / EXTI3 / 下降沿链路K2 按下 → PB3 拉低原理图 → EXTI3 挂起 → CPU 唤醒 → EXTI2_3_IRQHandler 置 g_pb3_wake_flag → app_sleep_resume_proc 转 g_wake_flash_active → 主循环闪灯 5s。要点EXTI 是异步唤醒源不依赖任何运行中的时钟所以即便 CPU 深睡也能响应。除了按键你也可以把 PB3 接到 AFE 的报警脚、充电器的插入检测脚实现事件即唤醒。5.2 无线唤醒BLE 连接完成BR1601 的 BLE modem 在休眠期保持工作远端主机发起连接、连接建立完成时协议栈经 GAP 回调通知应用。本例在ble_gap.c里把连接完成事件直接接到唤醒闪灯// ble_gap.c:43-49externvoidRequestWakeFlash(void);voidble_gap_connected(gap_event_tev,void*ctx,gap_conn_cmpl_t*arg){(void)ev;(void)ctx;(void)arg;RequestWakeFlash();// 连接一建立 → 申请唤醒闪灯 5s}而RequestWakeFlash()只是置同一个标志位main.c:241-244voidRequestWakeFlash(void){g_wake_flash_active1;}ble_gap_connected注册在GAP_EV_CONN_CMPL事件上ble_gap.c:98。于是按键和蓝牙连接两条路径殊途同归都汇到g_wake_flash_active主循环统一处理——这是个很干净的唤醒源归一化写法新增唤醒源RTC 定时、LPUART 收数时只需多置一个标志。注意要保持可被 BLE 连接唤醒前提是休眠期间广播或已有连接仍在维持。br_sleep()内部会协同 BLE 调度应用层无需额外操作但若你在app_sleep_prepare_proc里手动停了广播/断了连接无线唤醒自然也就没了。6. 总结当前工程如此配置之后实测休眠功耗1.7uA左右。BR1601含 A02的休眠由 SDK 五个接口搞定br_sleep/br_sleep_lock_acquire/br_sleep_lock_release/app_sleep_prepare_proc/app_sleep_resume_proc主循环里cs_schedule()br_sleep()即可。唤醒源要软硬件对齐原理图上 PB3 经 K2 按键接地、产生下降沿代码里PB3 EXTI3 下降沿一一对应BLE 连接完成则经ble_gap_connected → RequestWakeFlash归一到同一套唤醒闪灯逻辑。睡前收摊子关外设、配唤醒、清挂起、醒后重建现场恢复日志/外设全部放进两个固定名字的 hook。牢记两个最高频的坑睡前不清挂起位睡不下去、持锁忘释放永远醒着。把ble_dtss这份main.c当作低功耗骨架按需替换唤醒源与业务任务就能快速搭出可量产的长续航 BLE 设备。参考资料《BARROT_BR1601xx_SDK说明_V1.3》—— 第 1 章概述、第 7.4 节休眠管理模块《BR1601A原理图》BR1601A01-DKBARROTV1.02023-03-21本文只做芯片休眠技术分享如有侵权联系删除