第十五节 BLE蓝牙4.0协议栈启动分析
TI的这款CC2540/CC2541器件可以单芯片实现BLE蓝牙协议栈结构图的所有组件,包括应用程序。从这章开始我们来剖析协议栈源码,我们选用 SimpleBLEPeripheral工程开刀,这是一个从机的例程,基本的工作是对外广播,等待主机来连接,读写展示的属性。
首先打开工程文件,打开后可以看到整个工程的结构。
我们按照系统的启动顺序来一步一步走,我们都知道在C代码中,一般启动的首个函数为main,这个函数在 SimpleBLEPeripheral_Main.c中,打开文件,可以看到这个文件只有一个main函数和一个函数的申明,我们暂时不理会那个申明的函数,先看main都做了些什么工作:
Int main(void)
{
/* Initialize hardware */
HAL_BOARD_INIT(); // 硬件初始化
// Initialize board I/O
InitBoard( OB_COLD ); // 板级初始化
/* Initialze the HAL driver */
HalDriverInit(); // Hal驱动初始化
/* Initialize NV system */
osal_snv_init(); // Flash存储SNV初始化
/* Initialize LL */
/* Initialize the operating system */
osal_init_system(); // OSAL初始化
/* 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 启动OSAL
return 0;
}
通过代码我们可以看到,系统启动的过程,主要是做了一些初始化,如果开启了低功耗,则还需要开启低功耗管理。我们先不去理会初始化做了什么,但是我们知道在main函数的最后启动了OSAL,那么我们就进去看看OSAL是如何运作的。
在IAR中如果需要跳转到某个函数或变量的定义,可以在此函数名中右击然后选择Go To Definition……就可以调到相应的定义。
void osal_start_system( void )
{
#if !defined ( ZBIT ) && !defined ( UBIT )
for(;;) // Forever Loop
#endif
{
osal_run_system();
}
}
这里看到我们进入了一个死循环,并且一直调用osal_run_system(),那我们再进入此函数。
void osal_run_system( void ){
uint8 idx = 0;
#ifndef HAL_BOARD_CC2538
osalTimeUpdate(); // 定时器更新
#endif
Hal_ProcessPoll(); // Hal层信息处理
do {
if (tasksEvents[idx]) // Task is highest priority that is ready.
{
break;
}
} while (++idx < tasksCnt); // 检查每个人任务是否有事件
if (idx < tasksCnt) // 有事件发生
{
uint16 events;
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState); // 进入临界区
events = tasksEvents[idx];
tasksEvents[idx] = 0; // Clear the Events for this task. 清除事件标志
HAL_EXIT_CRITICAL_SECTION(intState); // 退出临界区
activeTaskID = idx;
events = (tasksArr[idx])( idx, events ); // 执行事件处理函数
activeTaskID = TASK_NO_TASK;
HAL_ENTER_CRITICAL_SECTION(intState); // 进入临界区
tasksEvents[idx] |= events; // Add back unprocessed events to the current task.
HAL_EXIT_CRITICAL_SECTION(intState); // 退出临界区
}
#if defined( POWER_SAVING ) // 没有事件发生,并且开启了低功耗模式
else // Complete pass through all task events with no activity?
{ // 系统进入低功耗模式
osal_pwrmgr_powerconserve(); // Put the processor/system into sleep
}
#endif
/* Yield in case cooperative scheduling is being used. */
#if defined (configUSE_PREEMPTION) && (configUSE_PREEMPTION == 0)
{
osal_task_yield();
}
#endif
}
在这里可以看到这个OSAL的核心,整个OSAL通过检测每个任务是否有事件发生,如果有则执行相应的任务,处理相应的事件。如果没有事件需要处理并且开启了低功耗模式,则系统就会进入低功耗模式。
这里有一个很关键的地方,OSAL是如何知道哪个事件需要哪个任务来处理呢?
events = (tasksArr[idx])( idx, events ); // 执行事件处理函数
我们看这里有一个很关键的数组tasksArr,很显然,这是一个函数指针数组,我们看看它的定义。
const pTaskEventHandlerFn tasksArr[] =
{
LL_ProcessEvent, // task 0
Hal_ProcessEvent, // task 1
HCI_ProcessEvent, // task 2
#if defined ( OSAL_CBTIMER_NUM_TASKS )
OSAL_CBTIMER_PROCESS_EVENT( osal_CbTimerProcessEvent ), // task 3
#endif
L2CAP_ProcessEvent, // task 4
GAP_ProcessEvent, // task 5
GATT_ProcessEvent, // task 6
SM_ProcessEvent, // task 7
GAPRole_ProcessEvent, // task 8
GAPBondMgr_ProcessEvent, // task 9
GATTServApp_ProcessEvent, // task 10
SimpleBLEPeripheral_ProcessEvent // task 11
};
可以看到在这个数组的定义中,每个成员都是任务的执行函数,按照任务的优先级排序,并且在osalInitTasks中初始化的时候,我们可以看到每个任务都有一个对应的初始化函数,并且传递了一个taskID,此ID从0开始自增,这里有一点非常重要,初始化的顺序和任务数组的定义顺序是一样的,这就保证了我们给任务发生消息或事件时能够准确的传递到相应的任务处理函数。
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 */
GAPRole_Init( taskID++ );
GAPBondMgr_Init( taskID++ );
GATTServApp_Init( taskID++ );
/* Application */
SimpleBLEPeripheral_Init( taskID );
}
应用层的初始化SimpleBLEPeripheral_Init,SimpleBLEPeripheral_Init( uint8task_id )主要对 GAP 和 GATT 进行配置,最后调用osal_set_event(simpleBLEPeripheral_TaskID, SBP_START_DEVICE_EVT )启动设备。
设备启动后应用层就能接收到这个设置的事件并进行处理,可以看到设备启动中主要是启动设备,注册绑定管理,并且启动了一个定时器,这个定时器是一个周期事件的第一次启动。
周期事件中每次都会重启这个定时器,并且处理周期事件。
在初始化的时候我们注册了一个很重要的函数,设备状态改变时的回调函数,这个函数在设备的状态改变时会被底层的协议栈回调,我们可以从这个回调函数中看的设备的状态的改变。
static void peripheralStateNotificationCB( gaprole_States_t newState);
从函数的定义可以看出,设备的状态类型都在数据类型gaprole_States_t中定义了,我们看一下这个数据类型的定义:
typedef enum
{
GAPROLE_INIT = 0, //!< Waiting to be started
GAPROLE_STARTED, //!< Started but not advertising
GAPROLE_ADVERTISING, //!< Currently Advertising
GAPROLE_WAITING, //!< Device is started but not advertising, is in waiting period before advertising again
GAPROLE_WAITING_AFTER_TIMEOUT, //!< Device just timed out from a connection but is not yet advertising, is in waiting period before advertising again
GAPROLE_CONNECTED, //!< In a connection
GAPROLE_CONNECTED_ADV, //!< In a connection + advertising
GAPROLE_ERROR //!< Error occurred - invalid state
} gaprole_States_t;
看到这个定义就很明确了,设备的状态就在这几种状态间切换。
本文导航
- 第 1 页:由浅入深,蓝牙4.0/BLE协议栈开发攻略大全(3)
- 第 2 页:第十二节 Flash的读写
- 第 3 页:第十三节 BLE协议栈简介
- 第 4 页:第十四节 OSAL工作原理
- 第 5 页:第十五节 BLE蓝牙4.0协议栈启动分析
- TI公司(73141)
- 协议栈(33351)
- 蓝牙BLE(23960)
评论
查看更多