亚洲精品一二区_国产黄色片网站_99久久久成人国产精品_蜜臀网_国产精品一区二区三区免费_成人av中文字幕_91精品国产欧美一区二区成人

當前位置:首頁 > 嵌入式培訓 > 嵌入式學習 > 講師博文 > ZigBee協議棧之osal淺析

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

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

本文引用地址://m.gxqdgs.com/emb/Column/7541.html

說到ZigBee就不得不提IEEE802.15和ZigBee聯盟,他們公共制定了ZigBee協議棧的標準。組網過程就是基于ZigBee協議棧,協議棧完成了絕大部分的工作,留給用戶的就是應用程序接口。協議棧就像一個操作系統一樣,用戶只需要定制應用程序就可以使用。

先來看一下ZigBee協議棧架構,操作系統下面的可以當做BootLoader,操作系統上面的可以看做應用程序。對于用戶來說,只要了解操作系統,會定制task,那么就可以使用協議棧了。

接下來我們以TI公司的ZigBee協議棧為標準,了解一下osal操作系統機制,以方便后續定制task。

Osal源于一種簡單的操作系統思想---輪詢。在ZigBee協議棧中,OSAL負責調度各個任務的運行,如果有事件發生了,則會調用相應的事件處理函數進行處理。那么,事件和任務的事件處理函數是如何聯系起來的呢?

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

在ZigBee協議棧中,有三個變量至關重要。

● tasksCnt—該變量保存了任務的總個數。

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

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

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

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

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

事件表和函數表的關系如下圖

我們現在來總結下OSAL的工作原理:通過tasksEvents指針訪問事件表的每一項,如果有事件發生,則查找函數表找到事件處理函數進行處理,處理完后,繼續訪問事件表,查看是否有事件發生,無限循環。OSAL就是一種基于事件驅動的輪詢式操作系統。事件驅動是指發生事件后采取相應的事件處理方法,輪詢指的是不斷地查看是否有事件發生。

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

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

 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()函數之前的函數都是對板載硬件以及協議棧進行的初始化,直到調用osal_start_system()函數,整個ZigBee協議棧才算是真正地運行起來了。硬件驅動不需要多看,我們跳轉到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

}

}

首先看到,真個osal是在一個for循環中執行的,這就意味正后面的事件一直在不停的重復,也是osal的基礎。

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

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

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

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

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

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

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

在系統初始化時,所有任務的事件初始化為0,因此,第10行通過tasksEvents[idx]是否為0來判斷是否有事件發生,如果有事件發生了,則跳出循環。

29行執行完事件處理函數后,需要將未處理的事件返回,也就是說事件處理函數的返回值保存了未處理的事件,將該事件在寫入事件表中,以便于下次進行處理。看一下下面的處理函數

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;

}

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

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

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

提到事件,我們就不得不提到消息。事件是驅動任務去執行某些操作的條件,當系統中產生了一個事件,OSAL將這個事件傳遞給相應的任務后,任務才能執行一個相應的操作(調用事件處理函數去處理)。

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

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

在使用ZigBee協議棧進行應用程序開發時,如何在應用程序中添加一個新任務呢?

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

因此,要添加新任務,只需要編寫兩個函數:

 新任務的初始化函數。

 新任務的事件處理函數。

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

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

我們需要注意的是:

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

我們現在總結下OSAL的運行機理:

 通過不斷地查詢事件表來判斷每個任務中是否有事件發生,如果有事件發生,則查找函數表找到對應的事件處理函數對事件進行處理。

● 事件表使用數組來實現,數組的每一項對應一個任務的事件,每一位表示一個事件;函數表使用函數指針數組來實現,數組的每一項是一個函數指針,指向了事件處理函數。

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

下一篇:Linux內核驅動之gpio子系統的使用

熱點文章推薦
華清學員就業榜單
高薪學員經驗分享
熱點新聞推薦
前臺專線:010-82525158 企業培訓洽談專線:010-82525379 院校合作洽談專線:010-82525379 Copyright © 2004-2022 北京華清遠見科技集團有限公司 版權所有 ,京ICP備16055225號-5京公海網安備11010802025203號

回到頂部

主站蜘蛛池模板: 伦理片午夜视频在线观看免费 | 99热这里有免费国产精品 | 国产丝袜啪啪 | 一级毛片真人免费播放视频 | 国产成人综合一区人人 | 黄污视频网站 | 日本夜夜操 | 欧美视频在线观看 | 欧美精品在线一区二区三区 | 亚洲成人免费在线观看 | 欧美性生活一级 | 日本在线高清不卡 | 99精品这里只有精品高清视频 | 免费看啪啪的网站 | 女人午夜啪啪性刺激免费看 | 狠狠色婷婷丁香六月 | 九九精品视频在线观看九九 | 美女视频黄a视频全免费应用 | 99精品久久99久久久久 | 色吊丝国产永久免费网址 | 欧美精品一区二区三区在线 | 两个人免费观看www在线 | 日韩精品高清自在线 | 九九热只有精品 | 亚洲国产精久久久久久久 | 国产精品hd免费观看 | 久久大香香蕉国产免费网站 | 日韩在线视频线视频免费网站 | 91久久澡人人爽人人添 | 久久精品亚洲欧美日韩久久 | 日本欧洲95视频 | 色avv | 久久伊人男人的天堂网站 | 国产精品1页| 中文字幕一区二区三区在线不卡 | 欧美bbb| 久久99国产亚洲高清观看韩国 | 四虎影免看黄 | 欧美在线免费播放 | 国产伦精品一区二区三区视频小说 | 亚洲欧美日韩三级 |