|
一、前言:为什么要手动配置Suspend休眠? Telink 原生BLE SDK没有BLE 任务时候 ,不会自动进入休眠,需要用户管理休眠状态。 这里分享一套实测可用的应用层Suspend休眠方案,通过六状态机管控 + BLE任务空闲校验机制,搭配GPIO引脚唤醒、定时自动唤醒双模式,实现闭环稳定的休眠唤醒逻辑。
二、方案核心设计思路 采用状态机分层管控 + BLE状态校验 + 休眠前置预处理 + 唤醒完整恢复的闭环
三、核心代码框架与变量说明 3.1 全局变量与状态枚举定义 自定义六段休眠状态机,所有状态变量、计时变量添加 _attribute_data_retention_ 保留属性,确保Suspend休眠过程中数据不丢失、状态不紊乱。 c
// 休眠计时变量,休眠状态保留数据
_attribute_data_retention_ u32 tickLoop_1 = 1;
_attribute_data_retention_ u32 AA_tickLoop_2 = 0;
// BLE状态、任务状态、休眠测试标志
u8 ble_sataus=0;
u8 ble_task_status=0;
u8 sleep_test=0;
/**
* @brief 休眠调试状态机枚举(核心六状态闭环)
*/
typedef enum
{
DEBUG_RUNNING, // 正常运行态:设备正常广播、任务运行
DEBUG_SLEEP_PREPARE, // 休眠准备态:关闭广播、配置唤醒源
DEBUG_WAIT_BLE_IDLE, // 中间态:循环等待BLE任务彻底空闲
DEBUG_SUSPEND, // 休眠态:进入Suspend低功耗模式
DEBUG_WAKEUP, // 唤醒态:执行射频、外设恢复
DEBUG_IDLE_RESET // 重置态:恢复初始状态,新一轮循环
} eDebugSleepState;
// 休眠状态机全局变量,休眠数据保留
_attribute_data_retention_ eDebugSleepState debug_sleep_state; |
休眠逻辑挂载在SDK空闲主循环中执行,不抢占、不阻塞BLE核心任务,完全贴合SDK原生架构,兼容性好、移植简单。 c
/**
* @brief BLE主空闲循环,所有应用层逻辑、休眠逻辑入口
*/
int main_idle_loop(void)
{
// 1. BLE SDK核心任务调度(必须优先执行)
blc_sdk_main_loop();
// 2. 调试日志输出
#if (TLKAPI_DEBUG_ENABLE)
tlkapi_debug_handler();
#endif
// 3. 电池检测、按键/按钮外设逻辑
#if (BATT_CHECK_ENABLE)
if (battery_get_detect_enable() && clock_time_exceed(lowBattDet_tick, 500000)) {
lowBattDet_tick = clock_time();
user_battery_power_check(BAT_DEEP_THRESHOLD_MV);
}
#endif
#if (UI_KEYBOARD_ENABLE)
proc_keyboard(0, 0, 0);
#elif (UI_BUTTON_ENABLE)
proc_button();
#endif
// 4. 核心休眠状态机逻辑(下文详细解析)
sleep_state_machine_process();
// 5. SDK底层功耗管理兜底
app_process_power_management();
return 0; // SDK固定返回值,不可修改
}
/**
* @brief 主循环入口函数
*/
_attribute_no_inline_ void main_loop(void)
{
main_idle_loop();
} |
四、核心休眠状态机逻辑逐段解析 下面逐段拆解状态机流转逻辑,覆盖设备运行、休眠准备、BLE空闲检测、低功耗休眠、射频唤醒恢复、状态重置完整流程。 4.1 正常运行态(DEBUG_RUNNING) 设备正常上电运行,每500ms打印一次设备状态,稳定运行10s后再进入休眠准备阶段,规避上电初期BLE状态不稳定导致的异常休眠问题。 c
case DEBUG_RUNNING:
{
// 500ms周期打印运行状态
if (tickLoop_1 && clock_time_exceed(tickLoop_1, 500*1000))
{
tickLoop_1 = clock_time() | 1;
AA_tickLoop_2++;
// 打印BLE当前状态与任务空闲状态
tlkapi_printf(APP_JERRYLOG_EN, "[JERRY][LOOP] test_cnt=%d\r\n", AA_tickLoop_2);
tlkapi_printf(APP_JERRYLOG_EN, "[JERRY][LOOP] ble_state=%d\r\n", ble_sataus);
tlkapi_printf(APP_JERRYLOG_EN, "[JERRY][LOOP] ble_task_idle=%d\r\n", ble_task_status);
// 稳定运行10s后切入休眠准备状态
if(AA_tickLoop_2 > 20)
{
debug_sleep_state = DEBUG_SLEEP_PREPARE;
}
}
break;
} |
4.2 休眠准备态(DEBUG_SLEEP_PREPARE) 休眠前置预处理,主动关闭BLE广播、配置GPIO上下拉和唤醒引脚,清除休眠干扰条件,保证后续可以正常进入低功耗。 c
case DEBUG_SLEEP_PREPARE:
{
// 关闭BLE广播,避免广播持续运行导致休眠失败
blc_ll_setAdvEnable(BLC_ADV_DISABLE);
ble_sataus = blc_ll_getBleCurrentState();
// 配置PE0为上拉,低电平触发唤醒
gpio_set_up_down_res(GPIO_PE0, GPIO_PIN_PULLUP_1M);
pm_set_gpio_wakeup(GPIO_PE0, WAKEUP_LEVEL_LOW, 1);
// 切换状态:等待BLE任务完全空闲
debug_sleep_state = DEBUG_WAIT_BLE_IDLE;
break;
} |
4.3 等待BLE空闲态(DEBUG_WAIT_BLE_IDLE) 实测关键容错逻辑,循环轮询BLE任务运行状态,必须等待BLE任务完全空闲才会进入休眠 c
case DEBUG_WAIT_BLE_IDLE:
{
ble_task_status = blc_ll_isBleTaskIdle();
// BLE任务空闲,正式进入休眠模式
if((ble_task_status == 1))
{
debug_sleep_state = DEBUG_SUSPEND;
}
break;
} |
4.4 休眠态(DEBUG_SUSPEND) 设备进入Suspend低功耗模式,配置双唤醒机制,支持PE0引脚低电平唤醒、15秒定时自动唤醒,适配按键唤醒、定时复位等常规场景。 c
case DEBUG_SUSPEND:
{
// 进入Suspend休眠,双唤醒源配置
cpu_sleep_wakeup(SUSPEND_MODE,
PM_WAKEUP_PAD|PM_WAKEUP_TIMER,
(clock_time() + 15*1000*SYSTEM_TIMER_TICK_1MS));
// 休眠唤醒后切入唤醒处理状态
debug_sleep_state = DEBUG_WAKEUP;
break;
} |
4.5 唤醒恢复态(DEBUG_WAKEUP) 设备唤醒后,统一恢复射频底层、中断配置、发射功率,重新开启BLE广播,保证唤醒后广播、连接、数据通信功能正常,不会出现射频异常。 c
case DEBUG_WAKEUP:
{
// 射频底层初始化恢复
rf_drv_ble_init();
rf_set_irq_mask(FLD_RF_IRQ_RX | FLD_RF_IRQ_TX
| FLD_RF_IRQ_CMD_DONE
| FLD_RF_IRQ_FIRST_TIMEOUT
| FLD_RF_IRQ_RX_TIMEOUT
| FLD_RF_IRQ_RX_CRC_2);
// 射频参数与链路参数恢复
ble_rf_fast_settle_recover();
blt_ll_phy_param_reset();
rf_set_power_level_index(RF_POWER_P0dBm);
// 重新开启BLE广播
blc_ll_setAdvEnable(BLC_ADV_ENABLE);
// 切入状态重置流程
debug_sleep_state = DEBUG_IDLE_RESET;
break;
} |
4.6 状态重置态(DEBUG_IDLE_RESET) 单次休眠唤醒流程结束后,清空计数变量、重置状态机,设备恢复正常运行状态,形成完整闭环。默认分支做异常兜底,避免状态机卡死。 c
case DEBUG_IDLE_RESET:
{
// 重置计时标记,复位状态机
AA_tickLoop_2 = 0;
tickLoop_1 = clock_time() | 1;
debug_sleep_state = DEBUG_RUNNING;
break;
}
// 异常兜底,防止状态卡死
default:
{
debug_sleep_state = DEBUG_RUNNING;
break;
} |
五、关键移植与调试要点 1、变量保留属性必填:休眠相关状态、计数变量必须开启 _attribute_data_retention_,否则休眠后状态丢失,逻辑运行异常。 2、休眠前置校验不能省:必须关闭广播、等待BLE任务空闲,直接休眠会造成BLE时序错乱、整机功耗异常。 3、唤醒底层恢复必填:唤醒后必须复位射频与中断,否则会出现无广播、无法连接、数据丢包等问题。 4、参数可按需修改:15s定时唤醒时长、PE0唤醒引脚、唤醒电平逻辑,均可根据需求自行调整。
六、日志打印配置(解决休眠打印丢失问题) 工程调试过程中,休眠唤醒阶段容易出现日志丢失、打印截断的问题。在配置头文件开启以下宏定义,开启实时打印与全模块日志输出,可完整抓取休眠状态机、BLE状态切换日志,方便问题排查。 c
// 调试串口与实时打印配置
#define TLKAPI_DEBUG_ENABLE 1
#define TLKAPI_DEBUG_CHANNEL TLKAPI_DEBUG_CHANNEL_GSUART
#define APP_REAL_TIME_PRINTF 1 // 开启实时打印,解决休眠日志丢失问题
// 全模块日志使能
#define APP_LOG_EN 1
#define APP_CONTR_EVT_LOG_EN 1 // 控制器事件日志
#define APP_HOST_EVT_LOG_EN 1 // 主机事件日志
#define APP_KEY_LOG_EN 1 // 按键日志
#define APP_BUTTON_LOG_EN 1 // 按钮日志 |
核心配置要点:APP_REAL_TIME_PRINTF 1 是解决休眠日志丢失的关键,关闭日志缓存积压,休眠、唤醒全过程打印完整不缺失,可快速定位休眠失败、任务检测异常、唤醒故障等问题。
|