国产成人精品三级麻豆,色综合天天综合高清网,亚洲精品夜夜夜,国产成人综合在线女婷五月99播放,色婷婷色综合激情国产日韩

當(dāng)前位置:首頁 > 嵌入式培訓(xùn) > 嵌入式學(xué)習(xí) > 講師博文 > ZigBee協(xié)議棧之osal淺析

ZigBee協(xié)議棧之osal淺析 時間:2018-09-27      來源:未知

ZigBee是目前比較流行的一種低功耗無線組網(wǎng)技術(shù),主要用于智能家居控制以及智能工業(yè)生產(chǎn)。ZigBee大的特點就是低功耗、自組網(wǎng)。

本文引用地址://www.mairao.cn/emb/Column/7541.html

說到ZigBee就不得不提IEEE802.15和ZigBee聯(lián)盟,他們公共制定了ZigBee協(xié)議棧的標(biāo)準(zhǔn)。組網(wǎng)過程就是基于ZigBee協(xié)議棧,協(xié)議棧完成了絕大部分的工作,留給用戶的就是應(yīng)用程序接口。協(xié)議棧就像一個操作系統(tǒng)一樣,用戶只需要定制應(yīng)用程序就可以使用。

先來看一下ZigBee協(xié)議棧架構(gòu),操作系統(tǒng)下面的可以當(dāng)做BootLoader,操作系統(tǒng)上面的可以看做應(yīng)用程序。對于用戶來說,只要了解操作系統(tǒng),會定制task,那么就可以使用協(xié)議棧了。

接下來我們以TI公司的ZigBee協(xié)議棧為標(biāo)準(zhǔn),了解一下osal操作系統(tǒng)機(jī)制,以方便后續(xù)定制task。

Osal源于一種簡單的操作系統(tǒng)思想---輪詢。在ZigBee協(xié)議棧中,OSAL負(fù)責(zé)調(diào)度各個任務(wù)的運(yùn)行,如果有事件發(fā)生了,則會調(diào)用相應(yīng)的事件處理函數(shù)進(jìn)行處理。那么,事件和任務(wù)的事件處理函數(shù)是如何聯(lián)系起來的呢?

ZigBee中采用的方法是:建立一個事件表,保存各個任務(wù)的對應(yīng)的事件,建立另一個函數(shù)表,保存各個任務(wù)的事件處理函數(shù)的地址,然后將這兩張表建立某種對應(yīng)關(guān)系,當(dāng)某一事件發(fā)生時則查找函數(shù)表找到對應(yīng)的事件處理函數(shù)即可。

在ZigBee協(xié)議棧中,有三個變量至關(guān)重要。

● tasksCnt—該變量保存了任務(wù)的總個數(shù)。

該變量的聲明為:uint8 tasksCnt,其中uint8的定義為:typedef unsigned char uint8

 tasksEvents—這是一個指針,指向了事件表的首地址。

該變量的聲明為:uint16 *tasksEvents,其中uint16的定義為:typedef unsigned short uint16

 tasksArr—這是一個數(shù)組,數(shù)組的每一項都是一個函數(shù)指針,指向了事件處理函數(shù)。

該數(shù)組的聲明為:const pTaskEventHandlerFn tasksArr[],其中pTaskEventHandlerFn的定義為:typedef unsigned short (*pTaskEventHandlerFn)( unsigned char task_id, unsigned short event ),這是定義了一個函數(shù)指針。tasksArr數(shù)組的每一項都是一個函數(shù)指針,指向了事件處理函數(shù)。

事件表和函數(shù)表的關(guān)系如下圖

我們現(xiàn)在來總結(jié)下OSAL的工作原理:通過tasksEvents指針訪問事件表的每一項,如果有事件發(fā)生,則查找函數(shù)表找到事件處理函數(shù)進(jìn)行處理,處理完后,繼續(xù)訪問事件表,查看是否有事件發(fā)生,無限循環(huán)。OSAL就是一種基于事件驅(qū)動的輪詢式操作系統(tǒng)。事件驅(qū)動是指發(fā)生事件后采取相應(yīng)的事件處理方法,輪詢指的是不斷地查看是否有事件發(fā)生。

下面從代碼中看一下osal運(yùn)行機(jī)制。在Zmain文件夾下有個Zmain.c文件,打開該文件可以

找到main()函數(shù),這就是整個協(xié)議棧的入口點。main()函數(shù)原型如下:

 int main( void )

{

// Turn off interrupts

osal_int_disable( INTS_ALL );

// Initialization for board related stuff such as LEDs

HAL_BOARD_INIT();

// Make sure supply voltage is high enough to run

zmain_vdd_check();

// Initialize board I/O

InitBoard( OB_COLD );

// Initialze HAL drivers

HalDriverInit();

// Initialize NV System

osal_nv_init( NULL );

// Initialize the MAC

ZMacInit();

// Determine the extended address

zmain_ext_addr();

// Initialize basic NV items

zgInit();

#ifndef NONWK

// Since the AF isn't a task, call it's initialization routine

afInit();

#endif

// Initialize the operating system

osal_init_system();

// Allow interrupts

osal_int_enable( INTS_ALL );

// Final board initialization

InitBoard( OB_READY );

// Display information about this device

zmain_dev_info();

/* Display the device info on the LCD */

#ifdef LCD_SUPPORTED

zmain_lcd_init();

#endif

#ifdef WDT_IN_PM1

/* If WDT is used, this is a good place to enable it. */

WatchDogEnable( WDTIMX );

#endif

osal_start_system(); // No Return from here

return 0; // Shouldn't get here.

} // main()

在osal_start_system()函數(shù)之前的函數(shù)都是對板載硬件以及協(xié)議棧進(jìn)行的初始化,直到調(diào)用osal_start_system()函數(shù),整個ZigBee協(xié)議棧才算是真正地運(yùn)行起來了。硬件驅(qū)動不需要多看,我們跳轉(zhuǎn)到osal_start_system(),查看一下它的原型

void osal_start_system( void )

{

#if !defined ( ZBIT ) && !defined ( UBIT )

for(;;) // Forever Loop

#endif

{

uint8 idx = 0;

osalTimeUpdate();

// This replaces MT_SerialPoll() and osal_check_timer().

Hal_ProcessPoll();

do {

// Task is highest priority that is ready.

if (tasksEvents[idx])

{

break;

}

} while (++idx < tasksCnt);

if (idx < tasksCnt)

{

uint16 events;

halIntState_t intState;

HAL_ENTER_CRITICAL_SECTION(intState);

events = tasksEvents[idx];

// Clear the Events for this task.

tasksEvents[idx] = 0;

HAL_EXIT_CRITICAL_SECTION(intState);

events = (tasksArr[idx])( idx, events );

HAL_ENTER_CRITICAL_SECTION(intState);

// Add back unprocessed events to the current task.

tasksEvents[idx] |= events;

HAL_EXIT_CRITICAL_SECTION(intState);

}

#if defined( POWER_SAVING )

// Complete pass through all task events with no activity?

else

{

// Put the processor/system into sleep

osal_pwrmgr_powerconserve();

}

#endif

}

}

首先看到,真?zhèn)osal是在一個for循環(huán)中執(zhí)行的,這就意味正后面的事件一直在不停的重復(fù),也是osal的基礎(chǔ)。

第6行,定義了一個變量idx,用來在事件表中索引。

第7-9行,更新系統(tǒng)時鐘,同時查看硬件方面是否有事件發(fā)生,如串口是否收到數(shù)據(jù)、是否有按鍵按下等信息,這部分內(nèi)容在此可以暫時不予考慮。

第10-16行,使用do-while循環(huán)查看事件表是否有事件發(fā)生。分析一下這個循環(huán),如果有事件發(fā)生,那么就跳出循環(huán),去后面的代碼處理事件。如果沒有事件,那么久繼續(xù)掃描表中的下一項。當(dāng)然真?zhèn)循環(huán)的次數(shù)不得多于tasksCnt,因為它記錄著事件的總數(shù)。

第18~35行是事件的處理過程,23和27行規(guī)定了一個臨界區(qū)。24行取出事件,保存在events變量。26行則將表中的事件清零,因為事件已經(jīng)被取出將要處理。27行根據(jù)id找到對應(yīng)的函數(shù)表,執(zhí)行處理函數(shù),而且拿到返回值(后面解釋返回值)。31和34行又是一個臨界區(qū),33行又將事件重新賦值。

上面了解了osal的基本原理,就是兩張表的查詢和對應(yīng)。那么其中又有一個events成了重點,為什么處理完事件時候又返回一個events(24行),而且還把events放回到事件表(33行)。

ZigBee協(xié)議棧使用一個unsigned short型的變量,因為unsigned short類型占兩個字節(jié),即16個二進(jìn)制位,因此,可以使用每個二進(jìn)制位表示一個事件,我們來看下協(xié)議棧定義的系統(tǒng)事件SYS_EVENT_MSG,十六進(jìn)制:0x8000,二進(jìn)制:0b1000000000000000。它用的就是高位來表示該事件:

可以看出在一個任務(wù)中多只能有16個事件,因為events是一個16位數(shù)據(jù)。

在系統(tǒng)初始化時,所有任務(wù)的事件初始化為0,因此,第10行通過tasksEvents[idx]是否為0來判斷是否有事件發(fā)生,如果有事件發(fā)生了,則跳出循環(huán)。

29行執(zhí)行完事件處理函數(shù)后,需要將未處理的事件返回,也就是說事件處理函數(shù)的返回值保存了未處理的事件,將該事件在寫入事件表中,以便于下次進(jìn)行處理?匆幌孪旅娴奶幚砗瘮(shù)

uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events )

{

if ( events & SAMPLEAPP_SEND_PERIODIC_MSG_EVT )

{

// Send the periodic message

SampleApp_SendPeriodicMessage();

// Setup to send message again in normal period (+ a little jitter)

osal_start_timerEx( SampleApp_TaskID, SAMPLEAPP_SEND_PERIODIC_MSG_EVT,

(SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT + (osal_rand() & 0x00FF)) );

// return unprocessed events

return (events ^ SAMPLEAPP_SEND_PERIODIC_MSG_EVT);

}

// Discard unknown events

return 0;

}

前面已經(jīng)說到,事件用一個二進(jìn)制位1來表示,那么一個“與”操作就可以判斷出來到底有沒有事件。后return采用“異或”,這樣可以將已經(jīng)處理的事件清除掉。不清楚的小伙伴可以舉一個實際的例子,仔細(xì)計算一番之后你會發(fā)現(xiàn)的確是這種寫法。。。

在ZigBee協(xié)議棧中,用戶可以定義自己的事件,但是,協(xié)議棧同時也給出了幾個已經(jīng)定義好的事件,由協(xié)議棧定義的事件成為系統(tǒng)強(qiáng)制事件(Mandatory Events),SYS_EVENT_MSG就是其中的一個事件,SYS_EVENT_MSG的定義如下:

那SAMPLEAPP_SEND_PERIODIC_MSG_EVT事件 是不是就是用戶自己定義的事件了?是的!我們來看下它的定義:

提到事件,我們就不得不提到消息。事件是驅(qū)動任務(wù)去執(zhí)行某些操作的條件,當(dāng)系統(tǒng)中產(chǎn)生了一個事件,OSAL將這個事件傳遞給相應(yīng)的任務(wù)后,任務(wù)才能執(zhí)行一個相應(yīng)的操作(調(diào)用事件處理函數(shù)去處理)。

通常某些事件發(fā)生時,又伴隨著一些附加信息的產(chǎn)生,例如:從天線接收到數(shù)據(jù)后,會產(chǎn)生AF_INCOMING_MSG_CMD消息,但是任務(wù)的事件處理函數(shù)在處理這個事件的時候,還需要得到收到的數(shù)據(jù)。

因此,這就需要將事件和數(shù)據(jù)封裝成一個消息,將消息發(fā)送到消息隊列,然后在事件處理函數(shù)中就可以使用osal_msg_receive,從消息隊列中得到該消息。如下代碼可以獲得指向從消息隊列中得到消息的指針。

在使用ZigBee協(xié)議棧進(jìn)行應(yīng)用程序開發(fā)時,如何在應(yīng)用程序中添加一個新任務(wù)呢?

打開OSAL_SampleApp.c文件,可以找到數(shù)組tasksArr[]和函數(shù)osalInitTasks()。tasksArr[]數(shù)組里存放了所有的事件處理函數(shù)的地址;osalInitTasks()是OSAL的任務(wù)初始化函數(shù),所有任務(wù)的初始化工作都在這里邊完成,并且自動給每個任務(wù)分配一個ID。

因此,要添加新任務(wù),只需要編寫兩個函數(shù):

 新任務(wù)的初始化函數(shù)。

 新任務(wù)的事件處理函數(shù)。

將事件處理函數(shù)的地址加入tasksArr[]數(shù)組,如下代碼所示。

將新任務(wù)的初始化函數(shù)添加在osalInitTasks()函數(shù)的后,如下代碼所示。

我們需要注意的是:

tasksArr[]數(shù)組里各事件處理函數(shù)的排列順序要與osalInitTasks函數(shù)中調(diào)用各任務(wù)初始化函數(shù)的順序保持一致,只有這樣才能保證當(dāng)任務(wù)有事件發(fā)生時會調(diào)用每個任務(wù)對應(yīng)的事件處理函數(shù)。為了保存osalInitTasks()函數(shù)所分配的任務(wù)ID,需要給每一個任務(wù)定義一個全局變量。如在SampleApp.c文件中定義了一個全局變量SampleApp_TaskID,并且在osalInitTasks()函數(shù)中進(jìn)行了賦值。

我們現(xiàn)在總結(jié)下OSAL的運(yùn)行機(jī)理:

 通過不斷地查詢事件表來判斷每個任務(wù)中是否有事件發(fā)生,如果有事件發(fā)生,則查找函數(shù)表找到對應(yīng)的事件處理函數(shù)對事件進(jìn)行處理。

● 事件表使用數(shù)組來實現(xiàn),數(shù)組的每一項對應(yīng)一個任務(wù)的事件,每一位表示一個事件;函數(shù)表使用函數(shù)指針數(shù)組來實現(xiàn),數(shù)組的每一項是一個函數(shù)指針,指向了事件處理函數(shù)。

上一篇:ARM處理器異常返回地址

下一篇:Linux內(nèi)核驅(qū)動之gpio子系統(tǒng)的使用

熱點文章推薦
華清學(xué)員就業(yè)榜單
高薪學(xué)員經(jīng)驗分享
熱點新聞推薦
前臺專線:010-82525158 企業(yè)培訓(xùn)洽談專線:010-82525379 院校合作洽談專線:010-82525379 Copyright © 2004-2022 北京華清遠(yuǎn)見科技集團(tuán)有限公司 版權(quán)所有 ,京ICP備16055225號-5,京公海網(wǎng)安備11010802025203號

回到頂部