日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【转】TI蓝牙BLE 协议栈代码学习

發布時間:2025/4/14 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【转】TI蓝牙BLE 协议栈代码学习 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

  BLE就是低功率藍牙。要著重了解兩種設備:

  dual-mode雙模設備:簡單說就是向下兼容。   single-mode單模設備:僅僅支持BLE。 關于開發主要講的是單模設備,它可以只靠紐扣電池即可持續工作。

  TI的藍牙4.0BLE協議棧為BLE-CC254x-1.4.0,即現在的版本是1.4版本的。可以從TI官方下載或從附件中下載安裝,默認是安裝在C盤中。因為上一篇博文提到進行空中固件升級,當時沒有安裝在C盤下,死活生成不了bin文件,改在C盤下生成了。所以,我個人建議,還是默認安裝吧,也占不了多大空間。

  TI藍牙4.0BLE協議棧的結構如下圖所示:

  由控制器和主機兩部分構成,分層的思想很明晰。

  主機包括物理層PHY、數據鏈路層LL和主機控制器接口HCI構成。物理層PHY是1Mbps自適應跳頻的GFSK射頻,工作于免許可證的2.4G頻段。數據鏈路層LL用于控制設備的射頻狀態,使設備工作于Standby(準備)、Advertising(廣播)、Scanning(掃描)、Initiating(初始化)、Connected(連接)五種狀態中的一種。主機控制器接口HCI為主機和控制器之前提供標準的通信接口。

  主機包括邏輯鏈路控制及自適應協議層L2CAP、安全管理層SM、屬性協議層ATT、通用訪問配置文件層GAP、通用屬性配置文件層GATT構成。邏輯鏈路控制及自適應協議層L2CAP為上層提供數據封裝服務,允許邏輯上的點對點通信。安全管理層SM配對和密鑰分發服務,實現安全連接和數據交換。屬性協議層ATT允許設備向其他設備展示一塊特定的數據,這塊數據稱之為“屬性”。通用屬性配置文件層GATT定義了使用ATT的服務框架和配置文件(profile),BLE中所有數據的通信都要通過GATT層。通用訪問配置文件層GAP提供設備通信的接口,負責處理訪問模式和程序,包括設備發現、建立連接、終止連接、初始化安全和設備配置等。對于我們來說,直接接觸的是GAP和GATT兩個層。

  最早接觸這個項目的時候,聽說CC2540/2541是51內核的SOC,當時我心想,毛毛雨啦,51的東東還不簡單。等真接手了才發現,頭大了,TI的工程師把協議棧封裝和規劃得都很好,不能不佩服。

  先分析協議棧的流程吧,這里以TI的KeyFobDemo為例,該工程位于C:\Texas Instruments\BLE-CC254x-1.4.0\Projects\ble\KeyFob中。先看下工程的架構。對于我們開發來說,App和Profile兩個文件夾中的內容是最主要的。

  先從main()函數入手,打開App文件夾下的KeyFob_Main.c,找到main()函數:

int main(void) {/* Initialize hardware */HAL_BOARD_INIT();//初始化硬件// Initialize board I/OInitBoard( OB_COLD );//初始化板卡IO/* Initialze the HAL driver */HalDriverInit();//初始化HAL層驅動/* 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 //啟動OSALreturn 0; }

上述代碼,我加入了簡單的中文注釋,會發現有個很重要的東西——OSAL,Operation System Abstraction Layer,操作系統抽象層。OSAL還不是操作系統,但是實現了OS的很多功能。從前面的代碼中我們可以看到,跟OSAL相關的有兩個函數osal_init_system()和osal_start_system()(osal_pwrmgr_device()暫時先不去理會)。我們依次來看看。

在IAR環境中,可以在代碼中osal_init_system()上單擊鼠標右鍵,打開“Go to definition of osal_init_system”,

osal_init_system()在OSAL.c中,下面就是該函數的代碼:

uint8 osal_init_system( void ) {// Initialize the Memory Allocation Systemosal_mem_init();//初始化內存分配系統
// Initialize the message queueosal_qHead = NULL;//初始化消息隊列// Initialize the timersosalTimerInit();//初始化定時器// Initialize the Power Management Systemosal_pwrmgr_init();//初始化電源管理系統
// Initialize the system tasks.osalInitTasks();//初始化系統任務// Setup efficient search for the first free block of heap.osal_mem_kick();return ( SUCCESS ); }

該函數是完成一系列的初始化,跟操作系統有關的,仿佛是osalInitTasks(),我們進到這個函數里面。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 ); }

從每行代碼,可以看到整個事件初始化的過程也是分層的。從鏈路層任務初始化(LL_Init)到硬件抽象層(Hal_Init)、主機控制器接口(HCI_Init)、邏輯鏈路控制及自適應協議層(L2CAP_Init)、GAP層(GAP_Init)、GATT層(GATT_Init)、安全管理層(SM_Init)。然后完成GAP層的配制(GAPRole_Init)、藍牙綁定的管理初始化(GAPBondMgr_Init)及GATT層服務的初始化(GATTServApp_Init)。最后完成的是應用層的初始化(KeyFobApp_Init)。程序是一行一行地執行,各層的任務也是依次的完成初始化。??

接下來我們再看main()函數中另一個跟OSAL相關的函數——osal_start_system(),也位于OSAL.c中。

void osal_start_system( void ) { #if !defined ( ZBIT ) && !defined ( UBIT )for(;;) // Forever Loop #endif{osal_run_system();} }

一看這是個死循環,相當于單片機程序最后一行while(1);。這個函數最主要的部分還是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 }

  去掉條件編譯部分,最核心的是一個do-while循環,一個if判斷。

  do-while循環:

do-while循環: 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); }

  這部分代碼應該是OSAL最核心最精髓的部分了。前面的循環中已經確定有事件發生了。HAL_ENTER_CRITICAL_SECTION(intState);和HAL_EXIT_CRITICAL_SECTION(intState);分別是關中斷和使能中斷,以防止在執行代碼時被中斷打斷。將事件表tasksEvents[]中的事件賦給events,然后該事件清零。接下來events = (tasksArr[idx])( idx, events );就是去處理事件了,這里的tasksArr[]數組非常重要。下面的tasksEvents[idx] |= events;就是把沒有響應的事件再放回到tasksEvents[]中。

  我們來看看這個非常重要的數組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 };

  數組內的成員看起來很面熟。最上面一行的注釋也寫得很清楚,表中的順序要和osalInitTask()中定義的一致。再來看這個數組的類型,是pTaskEventHandlerFn,這是個什么東西?pTaskEventHandlerFn不是東西,是

typedef unsigned short (*pTaskEventHandlerFn)( unsigned char task_id, unsigned short event );

  這個定義是一個函數指針,看起著很頭疼,很蛋疼。如果換一下:

typedef pTaskEventHandlerFn unsigned short (*)( unsigned char task_id, unsigned short event );

  這樣或許理解起來要好一些。拿KeyFobApp_ProcessEvent的聲明來看,uint16 KeyFobApp_ProcessEvent( uint8 task_id, uint16 events ),這是符合pTaskEventHandlerFn的格式的,函數名就是指針,函數的地址。

  tasksArr[]是一個函數指針數組,里面保存了所有事件處理函數的地址。當有事件發生時,就執行events = (tasksArr[idx])( idx, events );一句,就是對應的tasksArr[]里相應的那個事件的處理函數。

  再看另一個數組,tasksEvents[]。tasksEvents[]聲明為全局變量,是在osalInitTasks()中定義和初始化的:

tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt); osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));

  這個數組的大小跟事件的數量是一致的,而且被osal_memset()初始化為0.

  這樣OSAL的運行機理基本清晰了,就是在do-while()循環中判斷tasksEvents[]中哪個事件非0,即事件被觸發了;然后在if中把該事件清0,執行tasksArr[]中定義好的事件處理函數,然后把沒有執行的事件再放回到tasksEvents[]中。這也是為什么在osalInitTask()中進行初始化的時候,初始化的順序要和tasksArr[]一致。

  以上是我對OSAL的理解,因為C語言的基本不夠瓷實,說得也很大白話。之所以敢這么大膽貼出來,也是請大家多批評指正,讓我能得到提高。

轉載于:https://www.cnblogs.com/cslunatic/p/5772207.html

《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的【转】TI蓝牙BLE 协议栈代码学习的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。