我們采用BLE-CC254x-1.3.2中的KeyFob工程展開分析.
我們都知道在C代碼中,程序的入口都是main()函數,這個函數在KeyFob_Main.c中
打開文件,可以看到這個文件包含了一些必要的頭文件和一個函數的申明,我們暫時不理會那個申明的函數,先看main都做了些什么工作:
通過代碼我們可以看到,系統啟動的過程,主要是做了一些初始化,如果開啟了低功耗,則還需要開啟低功耗管理。我們先不去理會初始化做了什么,但是我們知道main函數終啟動了OSAL,所以我們只分析相關的兩個函數osal_init_system()和osal_start_system().
我們進入osal_init_system()
我們從TI官方的注釋就能大致知道每個函數的作用.我再簡單介紹一下:
1044:初始化內存分配系統
1047:初始化消息隊列
1050:初始化定時器
1053:初始化電源管理系統
1056:初始化系統任務,這個函數添加了所有的任務,我們在以后詳細分析它.
1059: 設置有效的查找堆上的第一個空閑塊
我們進入osal_start_system(),發現這里會循環調用osal_run_system()
osal_run_system()才是整個協議棧的核心,進入到osal_run_system()后發現他只有1102-1147這些行代碼.這些代碼就是整個協議棧運轉的大腦,我們務必要把這里搞清楚.
我們發現代碼里邊有好多預編譯宏,而這些宏我們都沒有定義,所以對應的函數代碼也沒有執行,我們再次精簡一下代碼,
好了,我們就詳細得分析下這段代碼吧.
首先是一個do{}while()的組合,我們上來就執行do{}中得語句
我們先來解析下tasksEvents[idx],idx在1102行被賦值為0,那tasksEvents又是什么呢?我們右鍵點擊變量然后go to definition或者按快捷鍵F12查看變量定義的位置
追到以后發現是個指針,那它何時被賦值呢 ,被賦值成什么呢??我們要用到高級搜索這個功能了,把它找出來.
我們選中以后,按下組合鍵ctrl+shift+f,然后點擊find即可
我們發現有以下地方用到了這個指針
用到的地方不少,別害怕,照著我的步驟你你就會看見前方一片光明.
首先我們來看osalInitTasks()函數,在126行用malloc()分配了一片堆空間,后將這片空間的首地址賦值給tasksEvents.這里呢我們就可以將
tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);這句話等價于你定義了一個數組uint16 tasksEvents[tasksCnt],tasksCnt是幾呢?我一會告訴你.
127行將這片空間的數據全部清零,也就是將數組的全部元素清零.
這里我們先不去管這片內存空間中要存儲什么數據,我們知道這里邊全部是0;
再回到
do {
if (tasksEvents[idx]) // Task is highest priority that is ready.
{
break;
}
} while (++idx < tasksCnt);
剛開始idx得值是0, tasksEvents[idx]也就是0;那if (tasksEvents[idx])就不成立.隨后會執行while()中得語句++idx < tasksCnt, 這里我們需要再去跟蹤下tasksCnt這個變量,發現他原來是個常量:
問題又來了, tasksArr又是啥呢?我們繼續跟蹤
我們發現這是個數組,那數組中又存的是什么類型呢>?我們跟下pTaskEventHandlerFn,原來這是函數指針啊,那我們就清楚了tasksArr這個數組中全部存的是函數指針啊.至于這些函數指針是干什么得,又指向誰我們待會來分析.但我相信你已經知道了tasksCnt的值.
有點亂,我們現在先回憶下,剛才有幾個重要的變量.
其中一個是存函數指針得數組tasksArr,這些函數指針具體干嘛我們一會分析.
還有一個是這個數組中函數指針的個數tasksCnt.
還有一個指針變量tasksEvents.我們知道這個函數指針指向了一片堆空間,并且空間中全是0;我們就把它當成一個數組,數組中有tasksCnt個成員,每個成員的大小是兩個字節.再看do{}while()這個組合.
do {
if (tasksEvents[idx]) // Task is highest priority that is ready.
{
break;
}
} while (++idx < tasksCnt);
根據上邊所說的,你應該能想到這個循環結束的時候idx的數值了吧.對的,他后是等于tasksCnt的,結束循環后就進入1136行得分支,這里就是系統在檢測到沒有事件發生的時候進入低功耗模式.
那什么時候才會進入1119行這個分支來處理發生的事件呢.我們再來認識一個新的函數osal_set_event();這個函數得功能就是標識有事件發生,我們來分析下代碼.
904行判斷傳參task_id是否小于tasksCnt(你好還記得這個常量).907行關閉中斷,進入臨界區.908行有用到剛才那個函數指針tasksEvents了(其實也是數組首地址),那這個數組存這些變量有什么意義呢?
一句話告訴你,這個數組的每一個成員都代表一個任務,每個成員是兩個字節就是16位,每一位都代表這個任務中得一個事件,那一個任務能有幾個事件呢?怎么能表示事件發生還是沒發生呢?聰明的你應該能想到是16個了.事件發生就將對應的位置一就好了.
我們只需要知道協議棧里會通過中斷,函數調用等方式調osal_set_event()將系統事件置一.好了我們再回到下面的代碼:
do {
if (tasksEvents[idx]) // Task is highest priority that is ready.
{
break;
}
} while (++idx < tasksCnt);
這時候tasksEvents[idx]隨著idx得增加if (tasksEvents[idx])會成立了吧,這時候break出來idx的值剛好對應的是任務的編號.
終于能執行這段分支了,我們來分析下;
1122:進入臨界區,關中斷;
1123:將待處理的任務中所有的事件賦值給變量events
1124:將tasksEvents數組中該任務的事件全部清空
1125:退出臨界區,開中斷;
1127:將當前任務的標號復制給全局變量,它代表當前正在處理的任務的標號
1128:還記得tasksArr()存的是什么嗎?是函數指針,他其實就是對應任務的處理函數,至于這個處理函數都做了什么?我們下次分析,現在大家只用知道你把 事件傳進去,他會幫你處理,但每次只能處理一個事件,然后將處理完的事件對應的位清零隨后將新的事件變量返回.再賦值給events
1129:將activeTaskID賦值為TASK_NO_TASK代表當前沒有任務處理
1131: 進入臨界區,關中斷;
1132: 由于你剛才將事件變量清空了,你每次又只能處理一個事件,你總要把剩下沒處理的變量告訴我吧.
1133: 退出臨界區,開中斷;
好了,到這里該停了,我們先來總結下:
一個任務中如何表示不同的事件呢?
用一個unsigned short類型的變量來記錄不同的事件,unsigned short占兩個字節,總共16位,每一位二進制表示一個事件.
任務對應的處理函數放在哪呢?
放在const pTaskEventHandlerFn tasksArr[];
如果知道某個任務發生了某個事件,怎么記錄?
用這個函數 uint8 osal_set_event( uint8 task_id, uint16 event_flag )
下次我們會分析osalInitTasks()這個函數,還有任務和處理任務事件的函數是怎么聯系起來的? 如何在協議棧添加一個自己的任務,處理任務里的事件?