本系列教程將結(jié)合TI推出的CC254x SoC 系列,講解從環(huán)境的搭建到藍(lán)牙4.0協(xié)議棧的開發(fā)來深入學(xué)習(xí)藍(lán)牙4.0的開發(fā)過程。教程共分為六部分,本文為第三部分:
第三部分知識點(diǎn):
第十一節(jié) 串口通信
第十二節(jié) Flash的讀寫
第十三節(jié) BLE協(xié)議棧簡介
第十四節(jié) OSAL工作原理
第十五節(jié) BLE藍(lán)牙4.0協(xié)議棧啟動(dòng)分析
?
有關(guān)TI 的CC254x芯片介紹,可點(diǎn)擊下面鏈接查看:
主流藍(lán)牙BLE控制芯片詳解(1):TI CC2540
?
?
由淺入深,藍(lán)牙4.0/BLE協(xié)議棧開發(fā)攻略大全(1)
由淺入深,藍(lán)牙4.0/BLE協(xié)議棧開發(fā)攻略大全(2)
?
有關(guān)本文的工具下載,大家可以到以下這個(gè)地址:
朱兆祺ForARM?
第十一節(jié) 串口通信
在軟件開發(fā)過程中調(diào)試是一個(gè)很關(guān)鍵的過程,而調(diào)試用的最多的手段就是打印Log,嵌入式平臺很少有顯示設(shè)備,所以我們需要將信息通過串口打印到PC端。
MT254xboard上已經(jīng)通過RS232芯片將UART0連接到DB9,我們只需要將DB9連接到電腦即可,UART0 對應(yīng)的外部設(shè)備 IO 引腳關(guān)系為:P0_2------RX,P0_3------TX。
我們需要將這兩個(gè)IO配置為復(fù)用功能,CC2540的USART可以配置為SPI模式或者異步UART模式,這里我們需要配置為異步UART模式。
首先配置IO為UART模式:
PERCFG &= ~0x01; // 配置UART為位置 1
P0SEL = 0x3c; // P0_2,P0_3,P0_4,P0_5用作串口功能
P2DIR &= ~0XC0; // P0 優(yōu)先作為UART0
配置UART0寄存器,將UART0配置為8N1模式,波特率為115200。
U0CSR |= 0x80; // UART 方式
U0GCR |= 11; // U0GCR與U0BAUD配合
U0BAUD |= 216; // 波特率設(shè)為115200
UTX0IF = 0; // 清除中斷標(biāo)志
U0CSR |= 0X40; // 允許接收
IEN0 |= 0x84; // 開總中斷,接收中斷
這里采用中斷方式來接收串口數(shù)據(jù),并在中斷中回調(diào)應(yīng)用層的接收處理函數(shù)。
#pragma vector = URX0_VECTOR
__interrupt void UART0_ISR(void)
{
uint8 ch;
URX0IF = 0; // 清中斷標(biāo)志
ch = U0DBUF;
if ( NULL != RecvCb ) // 調(diào)用回調(diào)函數(shù)
{
RecvCb(ch);
}
}
為了測試串口的通訊功能,這里我們通過串口接收命令的方式來控制LED的亮滅和蜂鳴器的響和停止,并且顯示當(dāng)前的狀態(tài)。根據(jù)串口輸出提示,發(fā)送對應(yīng)字符可以實(shí)現(xiàn)相應(yīng)功能,并且顯示狀態(tài)。
第十二節(jié) Flash的讀寫
嵌入式系統(tǒng)中需要存儲數(shù)據(jù),而片內(nèi)的Flash資源很匱乏,所以我們經(jīng)常需要使用SpiFlash來存儲數(shù)據(jù),MT254xboard中板載了一個(gè) 512Kbyte的Flash,下面我們來驅(qū)動(dòng)此Flash。上一小節(jié)中我們用SPI的方式驅(qū)動(dòng)了LCD12864,這節(jié)我們繼續(xù)用SPI來驅(qū)動(dòng)板載的 Flash,
下面我們來檢測這個(gè)Flash,檢測的方法為,全部寫入0xAA,然后再讀出,對比是否為0xAA,如果是,那Flash是沒有問題的,否則Flash可能已經(jīng)有壞塊。具體的代碼見例程,這個(gè)過程所需要的時(shí)間取決于我們需要檢測的區(qū)域大小,如果完全檢測,則可能需要幾分鐘的時(shí)間。
int main(void)
{
SysStartXOSC();
LCD12864_Init(); // LCD初始化
GD25Q40_Init(); // Flash初始化
LCD12864_DisStr(0, “Flash Check.。。。”);
sprintf(LCDBuf, “Flash ID :%04X”, GD25Q40_ReadID()); // 讀取器件ID
LCD12864_DisStr(1, LCDBuf);
GD25Q40_EraseChip(); // 擦除整片F(xiàn)lash 大約需要10S
LCD12864_DisStr(2, “Erase Chip Complete”);
uint32 iCnt = 0;
// 全部寫入0xAA
const uint8 Write = 0xAA;
for(iCnt=0; iCnt < CHECK_ADDR_RANGE; iCnt++)
{
GD25Q40_Write(&Write, iCnt, 1); // 寫入0xAA
}
// 讀取Flash內(nèi)部的值,與寫入的值對比
uint8 Read;
for(iCnt=0; iCnt < CHECK_ADDR_RANGE; iCnt++)
{
GD25Q40_Read(&Read, iCnt, 1);
if(Read != Write)
{
LCD12864_DisStr(3, “Flash Error”);
break;
}
}
// 寫入的值與讀出的值完全一樣
if(iCnt >= CHECK_ADDR_RANGE)
{
LCD12864_DisStr(3, “Flash Check Success”);
}
GD25Q40_EraseChip(); // 再次擦除
while(1);
return 0;
}
MT254X藍(lán)牙4.0開發(fā)板Flash效果:
第十三節(jié) BLE協(xié)議棧簡介
TI的協(xié)議棧分為兩部分:控制器和主機(jī)。對于4.0以前的藍(lán)牙,這兩部分是分開的。所有profile和應(yīng)用都建構(gòu)在GAP或GATT之上。根據(jù)這張圖,我們從底層開始介紹。TI的這款CC2540器件可以單芯片實(shí)現(xiàn)BLE藍(lán)牙協(xié)議棧結(jié)構(gòu)圖的所有組件,包括應(yīng)用程序。
1.1.1 PHY層
1Mbps自適應(yīng)跳頻GFSK(高斯頻移鍵控),運(yùn)行在免證的2.4GHz。
1.1.2 LL層
LL層為RF控制器,控制設(shè)備處于準(zhǔn)備(standby)、廣播、監(jiān)聽/掃描(scan)、初始化、連接,這五種狀態(tài)中一種。五種狀態(tài)切換描述為:未連接時(shí),設(shè)備廣播信息,另外一個(gè)設(shè)備一直監(jiān)聽或按需掃描,兩個(gè)設(shè)備連接初始化,設(shè)備連接上了。發(fā)起聊天的設(shè)備為主設(shè)備,接受聊天的設(shè)備為從設(shè)備,同一次聊天只能有一個(gè)意見領(lǐng)袖,即主設(shè)備和從設(shè)備不能切換。
1.1.3 HCI層
HCI層為接口層,向上為主機(jī)提供軟件應(yīng)用程序接口(API),對外為外部硬件控制接口,可以通過串口、SPI、USB來實(shí)現(xiàn)設(shè)備控制。
1.1.4 L2CAP層
L2CAP層提供數(shù)據(jù)封裝服務(wù),允許邏輯上的點(diǎn)對點(diǎn)通訊。
1.1.5 SM層
SM層提供配對和密匙分發(fā),實(shí)現(xiàn)安全連接和數(shù)據(jù)交換。
1.1.6 ATT層
ATT層負(fù)責(zé)數(shù)據(jù)檢索,允許設(shè)備向另外一個(gè)設(shè)備展示一塊特定的數(shù)據(jù)稱之為屬性,在ATT環(huán)境中,展示屬性的設(shè)備稱之為服務(wù)器,與它配對的設(shè)備稱之為客戶端。鏈路層的主機(jī)從機(jī)和這里的服務(wù)器、客服端是兩種概念,主設(shè)備既可以是服務(wù)器,也可以是客戶端。從設(shè)備毅然。
1.1.7 GATT層
GATT層定義了使用 ATT 的服務(wù)框架和配置文件(profiles)的結(jié)構(gòu)。BLE 中所有的數(shù)據(jù)通信都需要經(jīng)過 GATT。GATT負(fù)責(zé)處理向上與應(yīng)用打交道,其關(guān)鍵工作是把為檢索工作提供合適的profile結(jié)構(gòu),而profile由檢索關(guān)鍵詞(characteristics)組成。
1.1.8 GAP層
GAP直接與應(yīng)用程序或配置文件(profiles)通信的接口,處理設(shè)備發(fā)現(xiàn)和連接相關(guān)服務(wù)。另外還處理安全特性的初始化。對上級,提供應(yīng)用程序接口,對下級,管理各級職能部門,尤其是指示LL層控制室五種狀態(tài)切換,指導(dǎo)保衛(wèi)處做好機(jī)要工作。
1.2 TI協(xié)議棧源碼介紹
在第二章我們講解了源碼的安裝,這里我們就來剖析源碼的結(jié)構(gòu)。打開協(xié)議棧目錄我們可以看到下圖:
BLE源碼:
目錄名
內(nèi)容說明
Accessories一些工具和已經(jīng)編譯好的Hex文件此文件夾中有Btool的安裝包、USB-CDC的驅(qū)動(dòng)。
ComponentsHal驅(qū)動(dòng),OSAL源碼、協(xié)議棧通用源碼此文件夾是OSAL各層組件的實(shí)現(xiàn)
Documents幫助文檔協(xié)議棧說明文檔,這是學(xué)習(xí)BLE最好的資料。
Projects工程文件這里有一些TI的Demo,我們開發(fā)一般是在Demo的基礎(chǔ)上進(jìn)行
這里TI給出了很多Demo,這些例程都是經(jīng)過了SIG評審的,ble 文件夾中有很多工程文件,有些是具體的應(yīng)用,例如BloodPressure、GlucoseCollector 、GlucoseSensor 、 HeartRate 、HIDEmuKbd 等都為傳感器的實(shí)際應(yīng)用,有相應(yīng)標(biāo)準(zhǔn)的 Profile。
其中有4種角色: SimpleBLEBroadcaster 、 SimpleBLECentral 、SimpleBLEObserver、SimpleBLEPeripheral。
他們都有自己的特點(diǎn)。
1.Broadcaster 廣播員 —— 非連接性的信號裝置
2.Observer 觀察者 —— 掃描得到,但不能鏈接
3.Peripheral 從機(jī) —— 可鏈接,在單個(gè)鏈路層鏈接中作為從機(jī)
4.Central 主機(jī) —— 掃描設(shè)備并發(fā)起鏈接,在單鏈路層或多鏈路層中作為主機(jī)。
我們的講解將圍繞這主機(jī)和從機(jī)進(jìn)行。因?yàn)槠渌脑O(shè)備都是基于這兩種設(shè)備擴(kuò)展開來的。
第十四節(jié) OSAL工作原理
藍(lán)牙為了實(shí)現(xiàn)同多個(gè)設(shè)備相連,或?qū)崿F(xiàn)多功能,也實(shí)現(xiàn)了功能擴(kuò)充,這就產(chǎn)生了調(diào)度問題。因?yàn)椋m然軟件和協(xié)議棧可擴(kuò)充,但終究最底層的執(zhí)行部門只有一個(gè)。為了實(shí)現(xiàn)多事件和多任務(wù)切換,需要把事件和任務(wù)對應(yīng)的應(yīng)用,并起一個(gè)名字OSAL操作系統(tǒng)抽象層。
OSAL管理的實(shí)現(xiàn)
如果實(shí)現(xiàn)軟件和硬件的低耦合,使軟件不經(jīng)改動(dòng)或很少改動(dòng)即可應(yīng)用在另外的硬件上,這樣就方便硬件改造、升級、遷移后,軟件的移植。HAL硬件抽象層正是用來抽象各種硬件的資源,告知給軟件。其作用類似于嵌入式系統(tǒng)設(shè)備驅(qū)動(dòng)的定義硬件資源的h頭文件。
BLE低功耗藍(lán)牙系統(tǒng)架構(gòu):
OSAL作為調(diào)度核心,BLE協(xié)議棧、profile定義、所有的應(yīng)用都圍繞它來實(shí)現(xiàn)。OSAL不是傳統(tǒng)大家使用的操作系統(tǒng),而是一個(gè)允許軟件建立和執(zhí)行事件的循環(huán)。
軟件功能是由任務(wù)事件來實(shí)現(xiàn)的,創(chuàng)建一個(gè)任務(wù)事件需要以下工作:
1. 創(chuàng)建task identifier任務(wù)ID;
2. 編寫任務(wù)初始化(task initialization routine)進(jìn)程,并需要添加到OSAL初始化進(jìn)程中,這就是說系統(tǒng)啟動(dòng)后不能動(dòng)態(tài)添加功能;
3. 編寫任務(wù)處理程序;
4. 如有需要提供消息服務(wù)。
BLE協(xié)議棧的各層都是以O(shè)SAL任務(wù)方式實(shí)現(xiàn),由于LL控制室的時(shí)間要求最為迫切,所以其任務(wù)優(yōu)先級最高。為了實(shí)現(xiàn)任務(wù)管理,OSAL通過消息處理(messageprocess),存儲管理,計(jì)時(shí)器定時(shí)等附加服務(wù)實(shí)現(xiàn)。
系統(tǒng)啟動(dòng)流程:
為了使用OSAL,在main函數(shù)的最后要啟動(dòng)一個(gè)名叫osal_start_system的進(jìn)程,該進(jìn)程會調(diào)用由特定應(yīng)用決定的啟動(dòng)函數(shù) osalInitTasks(來啟動(dòng)系統(tǒng))。osalInitTasks逐個(gè)調(diào)用BLE協(xié)議棧各層的啟動(dòng)進(jìn)程來初始化協(xié)議棧。隨后,設(shè)置一個(gè)任務(wù)的 8bit任務(wù)ID(task ID),跳入循環(huán)等待執(zhí)行任務(wù),系統(tǒng)啟動(dòng)完成。
1. 任務(wù)優(yōu)先級決定于任務(wù)ID,任務(wù)ID越小,優(yōu)先級越高
2. BLE協(xié)議棧各層的任務(wù)優(yōu)先級比應(yīng)用程序的高
3. 初始化協(xié)議棧后,越早調(diào)入的任務(wù),任務(wù)ID越高,優(yōu)先級越低,即系統(tǒng)傾向于處理新到的任務(wù)
每個(gè)事件任務(wù)由對應(yīng)的16bit事件變量來標(biāo)示,事件狀態(tài)由旗號(taskflag)來標(biāo)示。如果事件處理程序已經(jīng)完成,但其旗號并沒有移除,OSAL會認(rèn)為事情還沒有完成而繼續(xù)在該程序中不返回。比如,在SimpleBLEPeripheral實(shí)例工程中,當(dāng)事件START_DEVICE_EVT發(fā)生,其處理函數(shù)SimpleBLEPeripheral_ProcessEvent就運(yùn)行,結(jié)束后返回16bit事件變量,并清除旗語 SBP_START_DEVICE_EVT。
每當(dāng)OSAL事件檢測到了有任務(wù)事件,其相應(yīng)的處理進(jìn)程將被添加到由處理進(jìn)程指針構(gòu)成的事件處理表單中,該表單名叫taskArr(taskarray)。taskArr中各個(gè)事件進(jìn)程的順序和osalInitTasks初始化函數(shù)中任務(wù)ID的順序是對應(yīng)的。
有兩種,最簡單的方法是使用osal_set_event函數(shù)(函數(shù)原型在OSAL.h文件中),在這個(gè)函數(shù)中,用戶可以像定義函數(shù)參數(shù)一樣設(shè)置任務(wù)ID 和事件旗語。第二種方法是使用osal_start_timerEx函數(shù)(函數(shù)原型在OSAL_Timers.h文件中),使用方法同 osal_set_event函數(shù),而第三個(gè)以毫秒為單位的參數(shù)osal_start_timerEx則指示該事件處理必須要在這個(gè)限定時(shí)間內(nèi),通過定時(shí)器來為事件處理計(jì)時(shí)。
類似于Linux嵌入式系統(tǒng)內(nèi)存分配C函數(shù)mem_alloc,OSAL利用osal_mem_alloc提供基本的存儲管理,但osal_mem_alloc只有一個(gè)用于定義byte數(shù)的參數(shù)。對應(yīng)的內(nèi)存釋放函數(shù)為osal_mem_free。
不同的子系統(tǒng)通過OSAL的消息機(jī)制通信。消息即為數(shù)據(jù),數(shù)據(jù)種類和長度都不限定。消息收發(fā)過程描述如下:
接收信息,調(diào)用函數(shù)osal_msg_allocate創(chuàng)建消息占用內(nèi)存空間(已經(jīng)包含了osal_mem_alloc函數(shù)功能),需要為該函數(shù)指定空間大小,該函數(shù)返回內(nèi)存空間地址指針,利用該指針就可把所需數(shù)據(jù)拷貝到該空間。
發(fā)送數(shù)據(jù),調(diào)用函數(shù)osal_msg_send,需為該函數(shù)指定發(fā)送目標(biāo)任務(wù),OSAL通過旗語SYS_EVENT_MSG告知目標(biāo)任務(wù),目標(biāo)任務(wù)的處理函數(shù)調(diào)用osal_msg_receive來接收發(fā)來的數(shù)據(jù)。建議每個(gè)OSAL任務(wù)都有一個(gè)消息處理函數(shù),每當(dāng)任務(wù)收到一個(gè)消息后,通過消息的種類來確定需要本任務(wù)做相應(yīng)處理。消息接收并處理完成,調(diào)用函數(shù)osal_msg_deallocate來釋放內(nèi)存(已經(jīng)包含了osal_mem_free函數(shù)功能)。
為了實(shí)現(xiàn)更好的移植性,協(xié)議棧將硬件層抽象出了一個(gè)HAL硬件抽象層,當(dāng)新的硬件平臺做好后,只需修改HAL,而不需修改HAL之上的協(xié)議棧的其他組件和應(yīng)用程序。
第十五節(jié) BLE藍(lán)牙4.0協(xié)議棧啟動(dòng)分析
TI的這款CC2540/CC2541器件可以單芯片實(shí)現(xiàn)BLE藍(lán)牙協(xié)議棧結(jié)構(gòu)圖的所有組件,包括應(yīng)用程序。從這章開始我們來剖析協(xié)議棧源碼,我們選用 SimpleBLEPeripheral工程開刀,這是一個(gè)從機(jī)的例程,基本的工作是對外廣播,等待主機(jī)來連接,讀寫展示的屬性。
首先打開工程文件,打開后可以看到整個(gè)工程的結(jié)構(gòu)。
我們按照系統(tǒng)的啟動(dòng)順序來一步一步走,我們都知道在C代碼中,一般啟動(dòng)的首個(gè)函數(shù)為main,這個(gè)函數(shù)在 SimpleBLEPeripheral_Main.c中,打開文件,可以看到這個(gè)文件只有一個(gè)main函數(shù)和一個(gè)函數(shù)的申明,我們暫時(shí)不理會那個(gè)申明的函數(shù),先看main都做了些什么工作:
Int main(void)
{
/* Initialize hardware */
HAL_BOARD_INIT(); // 硬件初始化
// Initialize board I/O
InitBoard( OB_COLD ); // 板級初始化
/* Initialze the HAL driver */
HalDriverInit(); // Hal驅(qū)動(dòng)初始化
/* Initialize NV system */
osal_snv_init(); // Flash存儲SNV初始化
/* Initialize LL */
/* Initialize the operating system */
osal_init_system(); // OSAL初始化
/* Enable interrupts */
HAL_ENABLE_INTERRUPTS(); // 使能總中斷
// Final board initialization
InitBoard( OB_READY ); // 板級初始化
#if defined ( POWER_SAVING )
osal_pwrmgr_device( PWRMGR_BATTERY ); // 低功耗管理
#endif
/* Start OSAL */
osal_start_system(); // No Return from here 啟動(dòng)OSAL
return 0;
}
通過代碼我們可以看到,系統(tǒng)啟動(dòng)的過程,主要是做了一些初始化,如果開啟了低功耗,則還需要開啟低功耗管理。我們先不去理會初始化做了什么,但是我們知道在main函數(shù)的最后啟動(dòng)了OSAL,那么我們就進(jìn)去看看OSAL是如何運(yùn)作的。
在IAR中如果需要跳轉(zhuǎn)到某個(gè)函數(shù)或變量的定義,可以在此函數(shù)名中右擊然后選擇Go To Definition……就可以調(diào)到相應(yīng)的定義。
void osal_start_system( void )
{
#if !defined ( ZBIT ) && !defined ( UBIT )
for(;;) // Forever Loop
#endif
{
osal_run_system();
}
}
這里看到我們進(jìn)入了一個(gè)死循環(huán),并且一直調(diào)用osal_run_system(),那我們再進(jìn)入此函數(shù)。
void osal_run_system( void ){
uint8 idx = 0;
#ifndef HAL_BOARD_CC2538
osalTimeUpdate(); // 定時(shí)器更新
#endif
Hal_ProcessPoll(); // Hal層信息處理
do {
if (tasksEvents[idx]) // Task is highest priority that is ready.
{
break;
}
} while (++idx < tasksCnt); // 檢查每個(gè)人任務(wù)是否有事件
if (idx < tasksCnt) // 有事件發(fā)生
{
uint16 events;
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState); // 進(jìn)入臨界區(qū)
events = tasksEvents[idx];
tasksEvents[idx] = 0; // Clear the Events for this task. 清除事件標(biāo)志
HAL_EXIT_CRITICAL_SECTION(intState); // 退出臨界區(qū)
activeTaskID = idx;
events = (tasksArr[idx])( idx, events ); // 執(zhí)行事件處理函數(shù)
activeTaskID = TASK_NO_TASK;
HAL_ENTER_CRITICAL_SECTION(intState); // 進(jìn)入臨界區(qū)
tasksEvents[idx] |= events; // Add back unprocessed events to the current task.
HAL_EXIT_CRITICAL_SECTION(intState); // 退出臨界區(qū)
}
#if defined( POWER_SAVING ) // 沒有事件發(fā)生,并且開啟了低功耗模式
else // Complete pass through all task events with no activity?
{ // 系統(tǒng)進(jìn)入低功耗模式
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è)OSAL的核心,整個(gè)OSAL通過檢測每個(gè)任務(wù)是否有事件發(fā)生,如果有則執(zhí)行相應(yīng)的任務(wù),處理相應(yīng)的事件。如果沒有事件需要處理并且開啟了低功耗模式,則系統(tǒng)就會進(jìn)入低功耗模式。
這里有一個(gè)很關(guān)鍵的地方,OSAL是如何知道哪個(gè)事件需要哪個(gè)任務(wù)來處理呢?
events = (tasksArr[idx])( idx, events ); // 執(zhí)行事件處理函數(shù)
我們看這里有一個(gè)很關(guān)鍵的數(shù)組tasksArr,很顯然,這是一個(gè)函數(shù)指針數(shù)組,我們看看它的定義。
const pTaskEventHandlerFn tasksArr[] =
{
LL_ProcessEvent, // task 0
Hal_ProcessEvent, // task 1
HCI_ProcessEvent, // task 2
#if defined ( OSAL_CBTIMER_NUM_TASKS )
OSAL_CBTIMER_PROCESS_EVENT( osal_CbTimerProcessEvent ), // task 3
#endif
L2CAP_ProcessEvent, // task 4
GAP_ProcessEvent, // task 5
GATT_ProcessEvent, // task 6
SM_ProcessEvent, // task 7
GAPRole_ProcessEvent, // task 8
GAPBondMgr_ProcessEvent, // task 9
GATTServApp_ProcessEvent, // task 10
SimpleBLEPeripheral_ProcessEvent // task 11
};
可以看到在這個(gè)數(shù)組的定義中,每個(gè)成員都是任務(wù)的執(zhí)行函數(shù),按照任務(wù)的優(yōu)先級排序,并且在osalInitTasks中初始化的時(shí)候,我們可以看到每個(gè)任務(wù)都有一個(gè)對應(yīng)的初始化函數(shù),并且傳遞了一個(gè)taskID,此ID從0開始自增,這里有一點(diǎn)非常重要,初始化的順序和任務(wù)數(shù)組的定義順序是一樣的,這就保證了我們給任務(wù)發(fā)生消息或事件時(shí)能夠準(zhǔn)確的傳遞到相應(yīng)的任務(wù)處理函數(shù)。
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 */
SimpleBLEPeripheral_Init( taskID );
}
應(yīng)用層的初始化SimpleBLEPeripheral_Init,SimpleBLEPeripheral_Init( uint8task_id )主要對 GAP 和 GATT 進(jìn)行配置,最后調(diào)用osal_set_event(simpleBLEPeripheral_TaskID, SBP_START_DEVICE_EVT )啟動(dòng)設(shè)備。
設(shè)備啟動(dòng)后應(yīng)用層就能接收到這個(gè)設(shè)置的事件并進(jìn)行處理,可以看到設(shè)備啟動(dòng)中主要是啟動(dòng)設(shè)備,注冊綁定管理,并且啟動(dòng)了一個(gè)定時(shí)器,這個(gè)定時(shí)器是一個(gè)周期事件的第一次啟動(dòng)。
周期事件中每次都會重啟這個(gè)定時(shí)器,并且處理周期事件。
在初始化的時(shí)候我們注冊了一個(gè)很重要的函數(shù),設(shè)備狀態(tài)改變時(shí)的回調(diào)函數(shù),這個(gè)函數(shù)在設(shè)備的狀態(tài)改變時(shí)會被底層的協(xié)議棧回調(diào),我們可以從這個(gè)回調(diào)函數(shù)中看的設(shè)備的狀態(tài)的改變。
static void peripheralStateNotificationCB( gaprole_States_t newState);
從函數(shù)的定義可以看出,設(shè)備的狀態(tài)類型都在數(shù)據(jù)類型gaprole_States_t中定義了,我們看一下這個(gè)數(shù)據(jù)類型的定義:
typedef enum
{
GAPROLE_INIT = 0, //!< Waiting to be started
GAPROLE_STARTED, //!< Started but not advertising
GAPROLE_ADVERTISING, //!< Currently Advertising
GAPROLE_WAITING, //!< Device is started but not advertising, is in waiting period before advertising again
GAPROLE_WAITING_AFTER_TIMEOUT, //!< Device just timed out from a connection but is not yet advertising, is in waiting period before advertising again
GAPROLE_CONNECTED, //!< In a connection
GAPROLE_CONNECTED_ADV, //!< In a connection + advertising
GAPROLE_ERROR //!< Error occurred - invalid state
} gaprole_States_t;
看到這個(gè)定義就很明確了,設(shè)備的狀態(tài)就在這幾種狀態(tài)間切換。
本文導(dǎo)航
- TI公司(73141)
- 協(xié)議棧(33351)
- 藍(lán)牙BLE(23960)
評論
查看更多