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