【WCH蓝牙系列芯片】-基于CH592开发板—蓝牙广播回调函数和连接回调函数
------------------------------------------------------------------------------------------------------------------------------------
在蓝牙从机使用过程中,经常需要判断蓝牙底层是否还在正常运行,可以利用蓝牙的链路层回调来做判断。
在CH592的BLE库提供了两个链路层回调注册接口,一个是广播回调和连接回调
extern void LL_AdvertiseEventRegister( pfnEventCB advEventCB); extern void LL_ConnectEventRegister( pfnEventCB connEventCB);

作用: 注册一个广播事件的回调函数。
触发: 当设备处于广播状态时,每次底层完成一次完整的广播包发送过程之后,就会触发调用你注册的 advEventCB 函数。
可以实现动态广播数据的更新,可以在每次广播结束后,利用这个回调函数去修改下一次即将发送的广播数据。还有可以在回调函数中打印出时间,来计算每次的广播间隔的时间是多少

作用: 注册一个连接事件的回调函数。
触发: 当蓝牙处于连接状态时,主机和从机会在一个固定的时间间隔内醒来交互一次数据。每次底层链路层(LL)完成一次这样的连接交互事件之后,协议栈就会自动调用你在这里注册的 connEventCB 函数。
注:在注释中提到了 Only effect in single connection,在这个回调函数中,这个只对于一对一的连接设备,如果多个连接不使用此回调函数。
可以利用这个回调函数去测量两个连接事件之间的精确时间,或者在连接事件结束后,立即处理一些数据之后,让芯片进入深度睡眠模式。
在正常使用中,可以通过这两个回调函数来距离一些调试日志,也可以做一个底层蓝牙状态监控逻辑程序,利用一个TMOS的任务事件,将一个变量按照一定的时间间隔进行累加计数,但是累加到设置的目标值后,就会进行软件复位,可以在注册的广播回调函数和连接回调函数中进行对这个标志清楚的功能,如果底层死机了、射频卡死了,回调函数不再执行,bleState_detectionCount 就会一直累加,直到超过一个阈值,系统就会判断“蓝牙出故障了”,从而触发芯片复位或重启蓝牙广播,保证设备的长期稳定性。
// BLE状态检测任务,每10000ms执行一次 #define SBP_BLE_State_detection_PERIOD MS1_TO_SYSTEM_TIME(10000) // 连续30次没有被回调清零就复位,约300秒 #define SBP_BLE_State_detection_LIMIT 30 // 新增:距离上一次广播/连接状态回调经过的 static uint16_t bleState_detectionCount = 0; #if 0 static void BLE_AdvertiseEventCB(uint32_t timeUs); void BLE_AdvertiseEventCB(uint32_t timeUs){ PRINT("timeUs=%d\n",timeUs); } static void BLE_ConnectEventCB(uint32_t timeUs); void BLE_ConnectEventCB(uint32_t timeUs) { // 表示底层刚刚执行完成一次连接事件 PRINT("timeUs=%d\n",timeUs); } #endif typedef enum { BLE_LL_ACTIVITY_NONE = 0, // 没有发生过LL广播事件或连接事件 BLE_LL_ACTIVITY_ADVERTISING, // LL广播事件 BLE_LL_ACTIVITY_CONNECTED //LL连接事件 } bleLlActivity_t; // 最近一次完成的是广播事件还是连接事件 static volatile bleLlActivity_t g_bleLlActivity = BLE_LL_ACTIVITY_NONE; // 记录回调传入的时间 static volatile uint32_t g_bleEventTimeUs = 0; #if 1 static void BLE_AdvertiseEventCB(uint32_t timeUs) { // 底层广播事件正常完成,超时计数清零 bleState_detectionCount = 0; // 记录最近活动类型 g_bleLlActivity = BLE_LL_ACTIVITY_ADVERTISING; // 仅记录,不在回调中打印 g_bleEventTimeUs = timeUs; } static void BLE_ConnectEventCB(uint32_t timeUs) { // 底层连接事件正常完成,超时计数清零 bleState_detectionCount = 0; // 记录最近活动类型 g_bleLlActivity = BLE_LL_ACTIVITY_CONNECTED; // 仅记录,不在回调中打印 g_bleEventTimeUs = timeUs; } #endif // 记录最近活动类型 g_bleLlActivity = BLE_LL_ACTIVITY_ADVERTISING; // 记录最近活动类型 g_bleLlActivity = BLE_LL_ACTIVITY_CONNECTED;
这是一个全局状态标志。因为回调函数是在底层中断里执行的, 这样做是为了让主程序知道当前蓝牙处于什么状态。
// 仅记录,不在回调中打印 g_bleEventTimeUs = timeUs;
将底层传上来的时间戳保存到一个全局变量 g_bleEventTimeUs 中

在蓝牙初始化Peripheral_Init()函数中,将广播回调函数和连接回调函数进行注册。并且启动蓝牙状态检测的任务函数。
/*************************BLE运行状态监控事件************************/ if(events & SBP_BLE_State_detection_EVT) { uint8_t gapState; bStatus_t getStateResult; uint16_t State_detectionCountSnapshot; uint32_t eventTimeSnapshot; bleLlActivity_t llActivitySnapshot; /* * 先把回调中更新的变量保存到局部变量。 */ llActivitySnapshot = g_bleLlActivity; eventTimeSnapshot = g_bleEventTimeUs; // 每执行一次事件,计数加一 bleState_detectionCount++; State_detectionCountSnapshot = bleState_detectionCount; // 将计数器保存到局部变量 /* * 主动查询协议栈当前真实GAP Role状态。 */ gapState = GAPROLE_ERROR; getStateResult = GAPRole_GetParameter(GAPROLE_STATE, &gapState); if(getStateResult == SUCCESS) { // gapRole_States_t的低4位表示普通广播/连接状态 gapState &= GAPROLE_STATE_ADV_MASK; } PRINT("\r\n"); PRINT("========== BLE DEBUG ==========\r\n"); /* * 打印最近一次底层回调的类型和时间参数。 */ switch(llActivitySnapshot) { case BLE_LL_ACTIVITY_ADVERTISING: PRINT("Last LL callback : ADVERTISING\r\n"); //广播事件回调 PRINT("Advertise timeUs : %lu\r\n", (unsigned long)eventTimeSnapshot); break; case BLE_LL_ACTIVITY_CONNECTED: PRINT("Last LL callback : CONNECTED\r\n"); //连接事件回调 PRINT("Connect timeUs : %lu\r\n", (unsigned long)eventTimeSnapshot); break; case BLE_LL_ACTIVITY_NONE: default: PRINT("Last LL callback : NONE\r\n"); // 没有收到广播或连接LL回调 PRINT("Event timeUs : %lu\r\n", (unsigned long)eventTimeSnapshot); break; } /* * GAP状态查询成功后,打印当前协议栈逻辑状态。 */ if(getStateResult == SUCCESS) { PRINT("GAP state value : %u\r\n", gapState); //先打印状态原始数值 switch(gapState) { case GAPROLE_INIT: PRINT("Current state : INIT\r\n"); //GAP Role尚未正式启动 break; case GAPROLE_STARTED: PRINT("Current state : STARTED, not advertising\r\n"); //GAP Role已经启动,但当前还没有广播 break; case GAPROLE_ADVERTISING: PRINT("Current state : ADVERTISING\r\n"); //当前处于广播状态 break; case GAPROLE_WAITING: PRINT("Current state : WAITING\r\n"); //当前没有广播,正在等待重新进入广播状态 break; case GAPROLE_CONNECTED: PRINT("Current state : CONNECTED\r\n"); //当前已经建立BLE连接 break; case GAPROLE_CONNECTED_ADV: PRINT("Current state : CONNECTED + ADVERTISING\r\n"); // 当前已经连接,同时仍然在广播 break; case GAPROLE_ERROR: PRINT("Current state : ERROR\r\n"); // GAP Role内部状态异常 break; default: PRINT("Current state : UNKNOWN\r\n"); //当前库版本没有定义或程序没有识别的状态值 break; } } else { PRINT("Get GAP state failed: %u\r\n",getStateResult); // GAPRole_GetParameter()调用失败 } PRINT("State_detection count : %u\r\n",State_detectionCountSnapshot); //打印计数值 PRINT("===============================\r\n"); /* * 长时间没有广播事件或连接事件回调,或者异常状态下/ * 长时间没有广播LL回调或连接LL回调 */ if(bleState_detectionCount >= SBP_BLE_State_detection_LIMIT) { PRINT("BLE callback timeout, system reset...\r\n"); // // 调试时让串口有时间输出 // DelayMs(10); SYS_ResetExecute(); } // 下一次的TMOS运行 tmos_start_task(Peripheral_TaskID, SBP_BLE_State_detection_EVT, SBP_BLE_State_detection_PERIOD); return (events ^ SBP_BLE_State_detection_EVT); }
可以在任务事件中读取 LL 状态和读取 GAP 状态,
如果是bleState_detectionCount一直在累加,到达设定的阈值,
系统判定蓝牙模块已彻底失去响应,就会调用,SYS_ResetExecute()让整个芯片重启。
可以通过串口日志来查看整个效果,
正常状态下:


异常状态下:

浙公网安备 33010602011771号