標籤:ble 主程式 低功耗 藍芽 流程詳解
看原始碼的時候,一般都是從整個代碼的入口處開始,TI BLE 協議棧源碼也不例外。它的入口main()函數就是整個程式的入口,由系統上電時自動調用。
它主要做了以下幾件事情:
(一)底層硬體初始化配置
(二)建立任務並初始化任務配置
(三)檢測並執行有效任務事件
Main() 函數源碼如下:
一:底層硬體初始化設定
75行,設定系統時鐘,使能記憶體緩衝功能。
78行,關中斷,剛啟動時,系統運行不穩定,一般會首先關中斷。
81行,硬體相關的I/O 口配置。
84行,初始化mcu 內部的flash。
92行,開中斷,當系統運行到這裡的時候,狀態已經很穩定,可以將中斷開啟。
95行,I/O功能配置,及設定按鍵回呼函數指標。
98行,配置省電模式。
二:建立任務並初始化任務配置
89行,最主要的功能就是給建立所有的任務。
三:檢測並執行有效任務事件
102行,此函數是整個程式啟動並執行核心,一旦進入,就迴圈執行所有的任務,永遠不會結束。
下面分別列出這3個部分的主要原始碼,並以按鍵KEY的配置為線索進行分析:
===================================================================
一:底層硬體初始化設定
a. HalDriverInit() 主要是硬體抽象層初始化,配置PIN腳的工作模式。
比如ADC,UART,KEY,LCD等。
147行,HalKeyInit()是按鍵I/O配置,下面以此舉例說明:
211-223行,將相應的PIN配置為GPIO,輸入模式。
226行,初始化按鍵回呼函數指標pHalKeyProcessFunction為NULL。
此回呼函數在程式中的作用是,當driver層檢測到按鍵中斷後,只需要調用回呼函數指標即可,至於函數執行什麼功能則完全由使用者層自己決定,這樣的做的好處是將使用者層與driver層分離,提高代碼的模組化及可操作特性。
b. InitBoard( OB_READY ),配置KEY GPIO的中斷功能
126行,HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback)此函數中配置中斷使能,設定按鍵回呼函數指標pHalKeyProcessFunction為OnBoard_KeyCallback。
OnBoard_KeyCallback()裡面會繼續調用函數OnBoard_SendKeys(),其功能是發送按鍵message給相應的任務。
硬體設定好後,只能說明硬體具備完成相應功能的條件,但是如何讓它的功能實現,那就需要建立相應的應用程式來讓硬體工作起來,這個程式就叫task,那task是如何建立的呢?
下面接著分析,如何建立task。
二:建立任務並初始化任務配置
前面講過主函數main()89行osal_init_system(),主要功能是初始化系統設定,其中最重要的一個功能就是建立task,下面是建立task的原始碼
122行,申請tasksEvents記憶體空間
123行,清零tasksEvents記憶體空間
126行,鏈路層task初始化
129行,硬體抽象層task初始化
132行,主機控制介面層task初始化
141行,邏輯鏈路控制及自適應協議層task初始化
144-156行,通用屬性設定檔層task初始化
150行,安全管理層task初始化
159行,客戶應用程式層task初始化
每個task 初始化時都會分配一個taskID,而且是從0 遞增。
以Hal_Init(taskID++ )為例,從上面代碼可以看出來,硬體抽象層的taskID值是1
92行,將task_id形參賦值給Hal_TaskID,故初始化後Hal_TaskID等於1
由於Hal_TaskID是定義為一個全域變數,因此,整個程式中,只要是與Hal_TaskID有關係的事情,都會交給Hal 層的task處理。
同樣的道理,SimpleBLECentral_Init(taskID++)中會定義一個simpleBLETaskId,那麼所有與simpleBLETaskId有關係的事情,也都會交給應用程式層任務函數處理。
那各層的任務函數是在哪裡定義的呢?
在OSAL_simpleBLECentral.c檔案中,定義了一個函數指標數組如下:
87行,Hal_ProcessEvent即為Hal層的任務處理函數指標,它是數組的第2個元素tasksArr[1],也就是說如果程式中要調用Hal層任務,直接寫語句“tasksArr[1]();”
函數Hal_ProcessEvent()就會被執行了。
Task雖然被建立好了,但是task是要執行我們給它規定的功能的,那它是在哪裡執行的呢?
接下來詳細分析task 的執行的問題。
三:檢測並執行有效任務事件
系統中task 的執行,是由事件(evnet)來驅動的,程式會迴圈檢測所有的task,如果發現某個task有新的event未被處理,那麼這個task就會被調用。
osal_start_system()是整個程式的核心,裡面是一個for死迴圈,不停調用函數osal_run_system(),它的實際功能就是不停檢測是否有event產生,如果有event,就執行對應的task,請看源碼:
1105行,定時器查詢函數,它會檢查所有的定時器,如果某個定時時間到達,就將相應的event 加入到tasksEvents[task_id],這裡task_id的值是添加定時器時設定好的,具體請看osal_set_event()函數代碼。
1110-1115行,檢查所有任務,是否有需要執行的event發生,並記錄這個event的索引idx。
1117行,taskCnt 是系統添加的任務個數,也就是tasksArr[]數組中元素的個數。
1123行,儲存當前任務的將要執行的所有event。
1124行,清除當前任務的所有event。
1127行,儲存當前任務的idx,供系統自己使用。
1128行,根據當前任務索引idx,在指標數組tasksArr[]中定址當前任務的函數指標,調用當前任務函數,處理其中一個event,處理完畢後,返回還未處理的event。
1132行,將未處理的event恢複給當前任務事件變數儲存,等待下一次再處理,直至處理問所有的event。
總結:
本節只是講解了協議棧的主體架構,協議棧只是一個基礎平台,在不同的方案中,就有不同的應用功能,相應的就必須為應用添加不同的task來實現實際的功能。
通過本節講解,添加一個task的基本步驟如下:
1. 在HAL層配置任務要用到的I/O 的屬性(如果不涉及I/O操作,則可省略此步驟)
2. 在數組tasksArr[]中新增工作(task)處理函數
3. 在函數osalInitTasks()中初始化任務task
根據上面的步驟我們可以很容易建立一個task。但是,如果程式中沒有產生task的事件(event),task永遠都不會運行。
這就有一個新的問題:event在什麼情況下產生?它又是如何產生?
我們將在【事件和訊息工作機制】一節中詳細分解。