一、簡介
本文介紹如何在SimpleBLEPeripheral工程中,使用串口。
二、實驗平臺
協議棧版本:BLE-CC254x-1.4.0
編譯軟件:IAR 8.20.2
硬件平臺:Smart RF(主芯片CC2541)
三、版權聲明
聲明:喝水不忘挖井人,轉載請註明出處。
原文地址:http://write.blog.csdn.NET/postedit
聯繫方式:[email protected]
四軸開源羣:84342712
四、基礎知識
摘要:
1、任務調度:osal採用一個鏈表結構來管理協議棧各層相應任務。相關操作函數有,添加任務到鏈表中;獲取下一個活動任務;根據taskID查找下一個任務。osal採用輪詢任務調度隊列(任務鏈表),通過兩個函數:調度程序主循環函數和設置事件發生標誌函數。
2、時間管理:通過爲事件設置超時等待時間,一旦等待時間結束,便爲對應任務設置事件發生標誌,從而達到對事件進行延時處理目的。
3、原語通信:請求響應原語操作:一旦調用了下層相關函數後,就立即返回。下層處理函數在操作結束後,將結果以消息的形式發送到上層併產生一個系統事件,調度程序發現這個事件後就會調用相應的事件處理函數對它進行處理。兩個相關函數:向目標任務發送消息的函數;消息提取函數。
一、操作系統介紹
現有的嵌入式操作系統可以分爲兩類,即通用的多任務操作系統(General—purpose
Multi-tasking OS)和事件驅動的操作系統(Event-driven OS)。前者能夠很好地支持多任務或者多線程,但是會隨着內部任務切換頻率的增加而產生很大的開銷,這類操作系統有:uC/OS-II、嵌入式Linux、WinCE等。後者支持數據流的高效併發,並且考慮了系統的低功耗要求,在功耗、運行開銷等方面具有優勢。典型的代表如TinyOSl291。
目前TinyOS操作系統支持的平臺有ATMEL公司的AVR系列、TI公司的MSP430系列。由於TinyOS操作系統還沒有對Chipcon公司(才知道TI把它收購了)提供CC2430開發平臺提供支持,因此,要在CC2430開發平臺上使用TinyOS系統來開發Zigbee協議棧軟件,就必須首先對TinyOS進行移植。灰常麻煩……
因此Chipcon公司爲自己設計的ZStack協議棧中提供了一個名爲操作系統抽象層OSAL的協議棧調度程序。
//-------------------------------------------------------------------------------------
二、下面分析下這個協議棧調度程序(OSAL)的調度機制。
三部分:1、任務調度
2、時間管理
3、原語通信
(一)任務調度
//每層任務=對應事件處理函數
//任務鏈表,任務按優先級插入
ZigBee協議棧中的每一層都有很多原語操作要執行,因此對於整個協議棧來說,就會有很多併發操作要執行。協議棧的每一層都設計了一個事件處理函數,用來處理與這一層操作相關的各種事件。這些事件處理函數可以看成是與協議棧每一層相對應的任務,由ZigBee協議棧中調度程序OSAL來進行管理。這樣,對於協議棧來說,無論何時發生了何種事件,我們都可以通過調度協議棧相應層的任務,即事件處理函數來進行處理。這樣,整個協議棧便會按照時間順序有條不紊的運行。
ZigBee協議棧的實時性要求並不高,因此在設計任務調度程序時,OSAL只採用了輪詢任務調度隊列的方法來進行任務調度管理。
OSAL採用一個鏈表結構來管理協議棧各層相應的任務。鏈表中的每一項是一個結構體,用來記錄鏈表中相關任務的基本信息。鏈表的建立是按照任務優先級從高到低的順序進行插入的。優先級高的任務將被插入到優先級低的任務前面。如果倆任務優先級相同,則按照時間順序加入到鏈表中。那麼這個任務鏈表在系統啓動的時候建立,一旦建立後便一直存在於事個系統運行的過程中,直到系統關閉或硬件復位才被銷燬。
鏈表中的每一項數據結構聲明:
typedef void (*pTaskInitFn)(unsigned char task_id) ; //指向任務初始化函數
typedef void (*pTaskEventHandlerFn)(usigned char task_id unsigned short event_flag); //指向事件處理函數
typedef struct osalTaskRec
{
struct osalTaskRec *next; //指向鏈表中下一個結構體
pTaskInitFn pfnInit; //指向相關層任務初始化函數
pTaskEventHandlerFn pfnEventProcessor; //指向相關層事件處理函數
byte taskID; //對應當前任務ID
byte taskPriority; //當前任務優先級
uint16 events; //需要被處理的事件,0表示沒有要被處理事件
} osalTaskRec_t; //鏈表中的每一項數據結構
//-------------------------------------------------------------------------------------
對於pfnInit,是指向相關層任務初始化函數的指針,比如:
網絡層中,任務初始化函數爲nwkInit(),用來對網絡層相關數據進行初始化操作。函數聲明爲
extern void nwkInit(byte task_id);
//task_id表示爲網絡層任務分配的唯一任務號
//-------------------------------------------------------------------------------------
對於 pfnEventProcessor是指向協議棧相關層的事件處理函數指針。比如:
網絡層中,事件處理函數nwk_event_loop(),用來處理與網絡層相關的各種事件。函數聲明爲
extern void nwk_event_loop(byte task_id, uint16 event_flag);
//event_flag標誌需要在網絡層處理的事件
//-------------------------------------------------------------------------------------
上面記錄的是鏈表中的每一項數據結構,與任務鏈表有關的主要操作有:添加任務到列表中;獲取下一個活動任務;根據taskID值查找相應的任務。
(1)在任務管理列表中添加任務
這個函數遍歷整個任務隊列鏈表,並按照優先級的高低將優先級高的任務插入到優先級低的任務前面;否則,就將任務插入到鏈表的尾部。在這個過程中,將爲每個任務分配一個唯一的任務號。函數聲明爲:
Extern osalTaskAdd(pTaskInitFn pfnInit,pTaskEventHandleFn pfnEventProcessor,bytetaskPriorty);
(2)獲取下一個活動任務
這個函數將根據osalTaskRec_t結構中的events標記來獲取任務隊列中下一個要執行的任務。函數聲明:
Extern osalTaskRec_t *osalNextActiveTask(void) ;
(3)根據taskID查找任務
這個函數將根據任務列表在建立過程中爲協議棧中每個任務分配的任務號,來查找對應任務。函數聲明:
Extern osalTaskRec_t *osalFindTask(byte taskID);
當任務鏈表建立成功後,系統便開始運行。如果在系統運行的過程中有事件發生,系統就會通過調用相應的任務,即事件處理函數,對所發生的事件進行相應處理。在整個運行過程中,調度程序(OSAL)始終不停地輪詢任務隊列鏈表,以發現需要處理的事件。這個過程涉及兩個函數操作:
1、調度程序主循環函數
2、設置事件發生標誌函數
(1)系統主循環
這個函數始終不停地輪詢隊列鏈表,來處理系統發生的各種事件。函數聲明和部分實現如下:
extern void osal_start_system(void);
//無限循環
for(; ; )
{
activeTask=osalNextActiveTask();
if(activeTask)
{
StoreDisableInts;
events=activeTask—>events;
activeTask—>events=0;
if(events!=0)
{
(activeTask—>pfnEventProcessor)(activeTask—>taskID,events);
RestoreInts;
}
}
}
(2)設置事件發生標誌
當協議棧中有任何事件發生時,我們可以通過設置osalTaskRec_t結構中的events來標記有事件發生,以便主循環函數能夠及時加以處理。函數聲明如下:
extern byte osal_set_event(byte task_id,uint16 event_flag);
(二)時間管理
協議棧中的每層都會有很多不同的事件發生,這些事件發生的時間順序各不相同。很多時候,事件並不要求立即得到處理,而是經過一定的時間後再進行處理。OSAL調度程序設計了與時間管理相關的函數,用來各種不同的要被處理的事件。
對事件進行時間管理,OSAL也採用了鏈表的方式進行,有時發生一個要被處理的事件,就啓動一個邏輯上的定時器,並將此定時器添加到鏈表當中。利用硬件定時器作爲時間操作的基本單元。設置時間操作的最小精度爲1ms,每1ms硬件定時器便產生一個時間中斷,在時間中斷處理程序中去更新定時器鏈表。每次更新,就將鏈表中的每一項時間計數減1,如果發現定時器鏈表中有某一表項時間計數已減到0,則將這個定時器從鏈表中刪除,並設置相應的事件標誌。這樣任務調度程序便可以根據事件標誌進行相應的事件處理。具體參見關於“系統時鐘”的記錄。
時間管理函數:
extern byte osal_start_timer(byte task_id, uint16 event_id, uint16 timeout_value);
這個函數爲事件event_id設置超時等待時間timeout_value。一旦等待結束,便爲task_id所對應的任務設置相應的事件發生標記,再對事件進行相應處理。
(三)原語通信
(原語只是一個理論層面上的術語,描述了服務層次的關係,以及兩個通信的N用戶和它們相連的N層(子層)對待協議實體之間的關係。初學時總是想不通原語跟協議棧的代碼有什麼關係,後來才瞭解了原語只是規範裏面的一個術語,反映到協議棧代碼裏就是一個個具體的函數了!例如我們可以看到很多原語是以request,confirm等爲後綴的,到了程序裏面就是相應的request請求函數,confirm確認函數了。)
對請求(request)、響應(response)原語可以直接使用函數調用來實現
對確認(confirm)、指示(indication)原語則需採用間接處理機制來完成
一個原語的操作往往需要逐層調用下層函數並根據下層返回的結果來進行進一步的操作。在這種情況下,一個原主的操作從發起到完成需要很長時間。因此,如果讓程序一直等待下層返回的結果再進一步處理,會使微處理器大部分時間處於循環等待之中,無法及時處理其它請求。
因此,與請求、響應原語操作相對應的函數,一旦調用了下層相關函數後,就立即返回。下層處理函數在操作結束後,將結果以消息的形式發送到上層併產生一個系統事件,調度程序發現這個事件後就會調用相應的事件處理函數對它進行處理。(調用就返回,而不管函數有沒有處理完成。當函數處理完成後將結果以消息的形式發送到上層產生一個系統事件)。
OSAL調度程序用兩個相關的函數來完成這個過程:
1、向目標任務發送消息的函數
這個函數主要用來將原語操作結果以消息的形式往上層任務發送,併產生一個系統事件來通知調度程序。函數聲明如下:
extern byte osal_msg_send(byte destination_task,byte *msg_ptr,byte len);
參數destination_task是目標任務的任務號,參數指針msg_ptr指向要被髮送的消息,參數len爲消息長度
2、消息提取函數
這個消息用來從內存空間中提取相應的消息。其中消息結構和函數聲明如下:
typedef struct
{
byte task_id;
byte dst_task_id;
byte send_len;
}osal_msg_rec_header_t;
typedef struct
{
osal_msg_rec_header_t hdr;
byte *msg_ptr;
}osal_msg_received_t; //消息結構(上面結構體包含在裏面)
extern osal_msg_received_t osal_rcvd; //全局變量
extern osal_msg_received_t *osal_msg_receive(byte task_id); //接收任務
這個函數返回一個指向osal_msg_received結構的指針,通過msg_ptr這個指針就可以提取出所需要的信息。