【转】TI蓝牙BLE 协议栈代码学习
BLE就是低功率藍(lán)牙。要著重了解兩種設(shè)備:
dual-mode雙模設(shè)備:簡單說就是向下兼容。 single-mode單模設(shè)備:僅僅支持BLE。 關(guān)于開發(fā)主要講的是單模設(shè)備,它可以只靠紐扣電池即可持續(xù)工作。TI的藍(lán)牙4.0BLE協(xié)議棧為BLE-CC254x-1.4.0,即現(xiàn)在的版本是1.4版本的。可以從TI官方下載或從附件中下載安裝,默認(rèn)是安裝在C盤中。因?yàn)樯弦黄┪奶岬竭M(jìn)行空中固件升級(jí),當(dāng)時(shí)沒有安裝在C盤下,死活生成不了bin文件,改在C盤下生成了。所以,我個(gè)人建議,還是默認(rèn)安裝吧,也占不了多大空間。
TI藍(lán)牙4.0BLE協(xié)議棧的結(jié)構(gòu)如下圖所示:
由控制器和主機(jī)兩部分構(gòu)成,分層的思想很明晰。
主機(jī)包括物理層PHY、數(shù)據(jù)鏈路層LL和主機(jī)控制器接口HCI構(gòu)成。物理層PHY是1Mbps自適應(yīng)跳頻的GFSK射頻,工作于免許可證的2.4G頻段。數(shù)據(jù)鏈路層LL用于控制設(shè)備的射頻狀態(tài),使設(shè)備工作于Standby(準(zhǔn)備)、Advertising(廣播)、Scanning(掃描)、Initiating(初始化)、Connected(連接)五種狀態(tài)中的一種。主機(jī)控制器接口HCI為主機(jī)和控制器之前提供標(biāo)準(zhǔn)的通信接口。
主機(jī)包括邏輯鏈路控制及自適應(yīng)協(xié)議層L2CAP、安全管理層SM、屬性協(xié)議層ATT、通用訪問配置文件層GAP、通用屬性配置文件層GATT構(gòu)成。邏輯鏈路控制及自適應(yīng)協(xié)議層L2CAP為上層提供數(shù)據(jù)封裝服務(wù),允許邏輯上的點(diǎn)對(duì)點(diǎn)通信。安全管理層SM配對(duì)和密鑰分發(fā)服務(wù),實(shí)現(xiàn)安全連接和數(shù)據(jù)交換。屬性協(xié)議層ATT允許設(shè)備向其他設(shè)備展示一塊特定的數(shù)據(jù),這塊數(shù)據(jù)稱之為“屬性”。通用屬性配置文件層GATT定義了使用ATT的服務(wù)框架和配置文件(profile),BLE中所有數(shù)據(jù)的通信都要通過GATT層。通用訪問配置文件層GAP提供設(shè)備通信的接口,負(fù)責(zé)處理訪問模式和程序,包括設(shè)備發(fā)現(xiàn)、建立連接、終止連接、初始化安全和設(shè)備配置等。對(duì)于我們來說,直接接觸的是GAP和GATT兩個(gè)層。
最早接觸這個(gè)項(xiàng)目的時(shí)候,聽說CC2540/2541是51內(nèi)核的SOC,當(dāng)時(shí)我心想,毛毛雨啦,51的東東還不簡單。等真接手了才發(fā)現(xiàn),頭大了,TI的工程師把協(xié)議棧封裝和規(guī)劃得都很好,不能不佩服。
先分析協(xié)議棧的流程吧,這里以TI的KeyFobDemo為例,該工程位于C:\Texas Instruments\BLE-CC254x-1.4.0\Projects\ble\KeyFob中。先看下工程的架構(gòu)。對(duì)于我們開發(fā)來說,App和Profile兩個(gè)文件夾中的內(nèi)容是最主要的。
先從main()函數(shù)入手,打開App文件夾下的KeyFob_Main.c,找到main()函數(shù):
int main(void) {/* Initialize hardware */HAL_BOARD_INIT();//初始化硬件// Initialize board I/OInitBoard( OB_COLD );//初始化板卡IO/* Initialze the HAL driver */HalDriverInit();//初始化HAL層驅(qū)動(dòng)/* Initialize NV system */osal_snv_init();//初始化Flash/* Initialize LL *//* Initialize the operating system */osal_init_system();//初始化OSAL/* Enable interrupts */HAL_ENABLE_INTERRUPTS();//使能中斷// Final board initializationInitBoard( OB_READY );//完成板卡初始化#if defined ( POWER_SAVING )osal_pwrmgr_device( PWRMGR_BATTERY );//開啟低功耗模式#endif/* Start OSAL */osal_start_system(); // No Return from here //啟動(dòng)OSALreturn 0; }上述代碼,我加入了簡單的中文注釋,會(huì)發(fā)現(xiàn)有個(gè)很重要的東西——OSAL,Operation System Abstraction Layer,操作系統(tǒng)抽象層。OSAL還不是操作系統(tǒng),但是實(shí)現(xiàn)了OS的很多功能。從前面的代碼中我們可以看到,跟OSAL相關(guān)的有兩個(gè)函數(shù)osal_init_system()和osal_start_system()(osal_pwrmgr_device()暫時(shí)先不去理會(huì))。我們依次來看看。
在IAR環(huán)境中,可以在代碼中osal_init_system()上單擊鼠標(biāo)右鍵,打開“Go to definition of osal_init_system”,
osal_init_system()在OSAL.c中,下面就是該函數(shù)的代碼:
uint8 osal_init_system( void ) {// Initialize the Memory Allocation Systemosal_mem_init();//初始化內(nèi)存分配系統(tǒng)// Initialize the message queueosal_qHead = NULL;//初始化消息隊(duì)列// Initialize the timersosalTimerInit();//初始化定時(shí)器// Initialize the Power Management Systemosal_pwrmgr_init();//初始化電源管理系統(tǒng)
// Initialize the system tasks.osalInitTasks();//初始化系統(tǒng)任務(wù)// Setup efficient search for the first free block of heap.osal_mem_kick();return ( SUCCESS ); }
該函數(shù)是完成一系列的初始化,跟操作系統(tǒng)有關(guān)的,仿佛是osalInitTasks(),我們進(jìn)到這個(gè)函數(shù)里面。osalInitTasks()在OSAL_KeyfobDemo.c中。
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 */KeyFobApp_Init( taskID ); }從每行代碼,可以看到整個(gè)事件初始化的過程也是分層的。從鏈路層任務(wù)初始化(LL_Init)到硬件抽象層(Hal_Init)、主機(jī)控制器接口(HCI_Init)、邏輯鏈路控制及自適應(yīng)協(xié)議層(L2CAP_Init)、GAP層(GAP_Init)、GATT層(GATT_Init)、安全管理層(SM_Init)。然后完成GAP層的配制(GAPRole_Init)、藍(lán)牙綁定的管理初始化(GAPBondMgr_Init)及GATT層服務(wù)的初始化(GATTServApp_Init)。最后完成的是應(yīng)用層的初始化(KeyFobApp_Init)。程序是一行一行地執(zhí)行,各層的任務(wù)也是依次的完成初始化。??
接下來我們?cè)倏磎ain()函數(shù)中另一個(gè)跟OSAL相關(guān)的函數(shù)——osal_start_system(),也位于OSAL.c中。
void osal_start_system( void ) { #if !defined ( ZBIT ) && !defined ( UBIT )for(;;) // Forever Loop #endif{osal_run_system();} }一看這是個(gè)死循環(huán),相當(dāng)于單片機(jī)程序最后一行while(1);。這個(gè)函數(shù)最主要的部分還是osal_run_system(),找到它,也在OSAL.c中。
void osal_run_system( void ) {uint8 idx = 0;#ifndef HAL_BOARD_CC2538osalTimeUpdate(); #endifHal_ProcessPoll();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 }去掉條件編譯部分,最核心的是一個(gè)do-while循環(huán),一個(gè)if判斷。
do-while循環(huán):
do-while循環(huán): do {if (tasksEvents[idx]) // Task is highest priority that is ready. {break;}} while (++idx < tasksCnt);這個(gè)循環(huán)就是完成判斷當(dāng)前的事件表中有沒有事件發(fā)生,如果有就跳出來,執(zhí)行下面的代碼。
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); }這部分代碼應(yīng)該是OSAL最核心最精髓的部分了。前面的循環(huán)中已經(jīng)確定有事件發(fā)生了。HAL_ENTER_CRITICAL_SECTION(intState);和HAL_EXIT_CRITICAL_SECTION(intState);分別是關(guān)中斷和使能中斷,以防止在執(zhí)行代碼時(shí)被中斷打斷。將事件表tasksEvents[]中的事件賦給events,然后該事件清零。接下來events = (tasksArr[idx])( idx, events );就是去處理事件了,這里的tasksArr[]數(shù)組非常重要。下面的tasksEvents[idx] |= events;就是把沒有響應(yīng)的事件再放回到tasksEvents[]中。
我們來看看這個(gè)非常重要的數(shù)組taskArr[],它被定義在OSAL_KeyFobDemo.c中。
// The order in this table must be identical to the task initialization calls below in osalInitTask. const pTaskEventHandlerFn tasksArr[] = {LL_ProcessEvent, // task 0Hal_ProcessEvent, // task 1HCI_ProcessEvent, // task 2 #if defined ( OSAL_CBTIMER_NUM_TASKS )OSAL_CBTIMER_PROCESS_EVENT( osal_CbTimerProcessEvent ), // task 3 #endifL2CAP_ProcessEvent, // task 4GAP_ProcessEvent, // task 5GATT_ProcessEvent, // task 6SM_ProcessEvent, // task 7GAPRole_ProcessEvent, // task 8GAPBondMgr_ProcessEvent, // task 9GATTServApp_ProcessEvent, // task 10KeyFobApp_ProcessEvent // task 11 };數(shù)組內(nèi)的成員看起來很面熟。最上面一行的注釋也寫得很清楚,表中的順序要和osalInitTask()中定義的一致。再來看這個(gè)數(shù)組的類型,是pTaskEventHandlerFn,這是個(gè)什么東西?pTaskEventHandlerFn不是東西,是
typedef unsigned short (*pTaskEventHandlerFn)( unsigned char task_id, unsigned short event );這個(gè)定義是一個(gè)函數(shù)指針,看起著很頭疼,很蛋疼。如果換一下:
typedef pTaskEventHandlerFn unsigned short (*)( unsigned char task_id, unsigned short event );這樣或許理解起來要好一些。拿KeyFobApp_ProcessEvent的聲明來看,uint16 KeyFobApp_ProcessEvent( uint8 task_id, uint16 events ),這是符合pTaskEventHandlerFn的格式的,函數(shù)名就是指針,函數(shù)的地址。
tasksArr[]是一個(gè)函數(shù)指針數(shù)組,里面保存了所有事件處理函數(shù)的地址。當(dāng)有事件發(fā)生時(shí),就執(zhí)行events = (tasksArr[idx])( idx, events );一句,就是對(duì)應(yīng)的tasksArr[]里相應(yīng)的那個(gè)事件的處理函數(shù)。
再看另一個(gè)數(shù)組,tasksEvents[]。tasksEvents[]聲明為全局變量,是在osalInitTasks()中定義和初始化的:
tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt); osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));這個(gè)數(shù)組的大小跟事件的數(shù)量是一致的,而且被osal_memset()初始化為0.
這樣OSAL的運(yùn)行機(jī)理基本清晰了,就是在do-while()循環(huán)中判斷tasksEvents[]中哪個(gè)事件非0,即事件被觸發(fā)了;然后在if中把該事件清0,執(zhí)行tasksArr[]中定義好的事件處理函數(shù),然后把沒有執(zhí)行的事件再放回到tasksEvents[]中。這也是為什么在osalInitTask()中進(jìn)行初始化的時(shí)候,初始化的順序要和tasksArr[]一致。
以上是我對(duì)OSAL的理解,因?yàn)镃語言的基本不夠瓷實(shí),說得也很大白話。之所以敢這么大膽貼出來,也是請(qǐng)大家多批評(píng)指正,讓我能得到提高。
轉(zhuǎn)載于:https://www.cnblogs.com/cslunatic/p/5772207.html
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的【转】TI蓝牙BLE 协议栈代码学习的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 连载《一个程序猿的生命周期》-《发展篇》
- 下一篇: 移动端字体单位该使用px还是rem?