TI 系列的蓝牙芯片在协议栈支持上比较完善,最近使用CC254x系列BLE芯片,发现其协议栈的实现挺有意思,这里做点简单分析。
1.协议栈结构
CC2540 集成了增强型的 8051 内核, TI 为 BLE 协议栈搭建了一个简单的操作系统OSAL,即一种任务轮询机制。帮你做好了底层和蓝牙协议深层的内容,将复杂部分屏蔽掉。让用户通过 API 函数就可以轻易用蓝牙 4.0,是开发起来更加方便,开发周期也可以相应缩短。
安装完 BLE 协议栈之后,会在安装目录下看到以下文件结构:
其中,Accessories存放附件,如:USB驱动和hex文件。Components即为OSAL操作系统的底层实现,里面包括OSAL的各层具体实现。
Documents为协议栈相关说明文档,Projects为官方提供的demos,很多demo都是可以直接修改使用的,其中重点关心四个demo:
SimpleBLEBroadcaster、SimpleBLECentral、SimpleBLEObserver、SimpleBLEPeripheral。
这四个代码是CC254x开发的基本模板,他们都有自己的特点。
• Broadcaster 广播员 —— 非连接性的信号装置
• Observer 观察者 —— 扫描得到,但不能链接
• Peripheral 从机 —— 可链接,在单个链路层链接中作为从机
• Central 主机 —— 扫描设备并发起链接,在单链路层或多链路层中作为主机。
2.协议栈OSAL原理分析
协议栈中所谓的OSAL就是一个小型的操作系统,实现了最基本的任务轮询。
直接从main函数开始解剖:
int main(void)
{
/* Initialize hardware */
HAL_BOARD_INIT();
// Initialize board I/O
InitBoard( OB_COLD );
/* Initialze the HAL driver */
HalDriverInit();
/* Initialize NV system */
osal_snv_init();
/* Initialize LL */
/* Initialize the operating system */
osal_init_system();
/* Enable interrupts */
HAL_ENABLE_INTERRUPTS();
// Final board initialization
InitBoard( OB_READY );
#if defined ( POWER_SAVING )
osal_pwrmgr_device( PWRMGR_BATTERY );
#endif
/* Start OSAL */
osal_start_system(); // No Return from here
return 0;
}
主函数一进去就是各种系统初始化:包括硬件、GATT、 GAP 层、任务等的初始化。然后执行 osal_start_system();操作系统。我们重点关心2 个函数:
初始化操作系统
osal_init_system();
运行操作系统
osal_start_system();
我们先来看 osal_init_system();系统初始化函数,进入函数。发现里面有 6个初始化函数,这里我们只关心osalInitTasks();任务初始化函数。继续由该函数进入,进入后发现终于看到各层任务的添加,taskID 依次递增表示优先级降低,即越底层优先级越高(LL、HAL、HCI、L2CAP、GAP、GATT、SM、Profiles、Application)
void osalInitTasks( void )
{
uint8 taskID = 0;
tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
/* LL Task */
LL_Init( taskID++ );
/* Hal Task */
Hal_Init( taskID++ );
/* HCI Task */
HCI_Init( taskID++ );
#if defined ( OSAL_CBTIMER_NUM_TASKS )
/* Callback Timer Tasks */
osal_CbTimerInit( taskID );
taskID += OSAL_CBTIMER_NUM_TASKS;
#endif
/* L2CAP Task */
L2CAP_Init( taskID++ );
/* GAP Task */
GAP_Init( taskID++ );
/* GATT Task */
GATT_Init( taskID++ );
/* SM Task */
SM_Init( taskID++ );
/* Profiles */
GAPCentralRole_Init( taskID++ );
GAPBondMgr_Init( taskID++ );
GATTServApp_Init( taskID++ );
/* Application */
SimpleBLECentral_Init( taskID );
}
最高层即应用层优先级最低,最后执行应用层的任务初始化。
我们再来看第二个函数 osal_start_system();运行操作系统。同样用 go to definition 的方法进入该函数。再进入 osal_run_system() ,我们欣喜地发现这里就是任务轮询的基本轮廓,源码和分析如下:
void osal_run_system( void )
{
uint8 idx = 0;
#ifndef HAL_BOARD_CC2538
osalTimeUpdate();
#endif
Hal_ProcessPoll();
//这段代码扫描触发的任务
do {
if (tasksEvents[idx]) //优先级高的任务被置位,说明有任务触发
{
break;//跳出任务扫描,得到的idx即为任务ID!
}
} while (++idx < tasksCnt);//idx从0开始递增,先查询高优先级的任务
if (idx < tasksCnt)
{
uint16 events;
halIntState_t intState;
//然后进入临界保护区,提取事件后清清除
HAL_ENTER_CRITICAL_SECTION(intState);
events = tasksEvents[idx];
tasksEvents[idx] = 0; // 清除
HAL_EXIT_CRITICAL_SECTION(intState);
//然后通过函数指针调用对应的任务处理函数
activeTaskID = idx;
events = (tasksArr[idx])( idx, events );
activeTaskID = TASK_NO_TASK;
//taskArr[]即为函数指针数组,存放所有定义好的处理任务函数的入口地址
HAL_ENTER_CRITICAL_SECTION(intState);
tasksEvents[idx] |= events; // 保存未处理的事件
HAL_EXIT_CRITICAL_SECTION(intState);
}
#if defined( POWER_SAVING )//如果定义了节能模式
else
{
osal_pwrmgr_powerconserve(); //进入睡眠
}
#endif
#if defined (configUSE_PREEMPTION) && (configUSE_PREEMPTION == 0)
{
osal_task_yield();
}
#endif
}
可见协议栈就是一个被称为OSAL的小型操作系统,基本流程如下:
各种初始化---运行操作系统---有任务触发---执行任务
以上只是对协议栈OSAL系统的简单分析,关于蓝牙通信的API 部分后文再续。