ZStack OSAL的事件(event)與消息(message)——part1本文轉(zhuǎn)載自:http://blog.csdn.net/ceci_zhou/article/details/9787349
在zstack中,有兩種方式在OSAL的任務(wù)(task)中添加自定義的功能:事件(event)和消息(message)。
這篇主要講講和event有關(guān)的事,和message有關(guān)的事請(qǐng)移步
ZStack OSAL的事件(event)與消息(message)——part2 這里http://blog.csdn.net/ceci_prayer/article/details/9835399
一、事件
事件是驅(qū)動(dòng)任務(wù)去執(zhí)行某些操作的條件,當(dāng)系統(tǒng)產(chǎn)生了一個(gè)事件,將這個(gè)觸發(fā)傳遞給相應(yīng)的任務(wù)后,任務(wù)才能執(zhí)行一個(gè)相應(yīng)的操作。 OSAL通過一個(gè)16位寬度的數(shù)組來管理事件,意味著OSAL最多可以支持16個(gè)事件,其中最高位(0x08000,SYS_EVENT_MSG)系統(tǒng)保留,用戶可以使用的事件有15個(gè)。 事件的使用很簡(jiǎn)單:1)需要找個(gè)地方定義事件的ID,實(shí)際上是指定該事件占用事件數(shù)組的哪個(gè)位。如#define MY_EVENT 0x02,占用bit1。 2)在需要觸發(fā)事件的地方調(diào)用osal_set_event(task_id, event_flag) ,這個(gè)函數(shù)有兩個(gè)參數(shù),一個(gè)是接收事件任務(wù)的ID,另一個(gè)參數(shù)指定事件ID. 3)在相應(yīng)任務(wù)的處理函數(shù),檢查該事件執(zhí)行相應(yīng)代碼即可。 4)清除事件標(biāo)識(shí)。
一個(gè)event被調(diào)用的過程: 1. main() -> osal_start_system()在ZMain.c中; 2.osal_start_system() -> XX_event在OSAL.C中。 第一步簡(jiǎn)單易懂,重要的是第二步的實(shí)現(xiàn)。
void osal_start_system( void )
typedef unsigned short (*pTaskEventHandlerFn)( unsigned char task_id, unsigned short event ); 又見函數(shù)指針類型。所以,tasksArr[]中的每一個(gè)元素都是一個(gè)指向函數(shù)的指針。但是,這個(gè)結(jié)構(gòu)體還是沒有解決我們調(diào)用_ProcessEvent或者別的event的問題。 這時(shí)候,就要回到我們之前加粗的那個(gè)語句了:events = (tasksArr[idx])( idx, events )??梢钥闯?,這個(gè)語句是調(diào)用tasksArr[]中第idx個(gè)函數(shù),然后把返回值賦給events(events是若干個(gè)標(biāo)志位,每個(gè)標(biāo)志位代表一個(gè)事件的發(fā)生與否)。至于調(diào)用的是哪個(gè)函數(shù),就跟參數(shù)(idx,events)有關(guān)了,而這個(gè)參數(shù)的選取,具體見這行代碼之前的若干代碼。
-----------------------------------------補(bǔ)充的分割線---------------------------------------- 之前我們講到了一個(gè)事件的處理函數(shù)是怎么被調(diào)用的,但是一個(gè)事件是怎么被觸發(fā)的,我只是寥寥寫了一些。這種事,不寫下來總會(huì)忘記的,所以還是補(bǔ)充一下有關(guān)事件觸發(fā)的那些事。 還記得events這個(gè)參數(shù)嗎?events里對(duì)應(yīng)的標(biāo)志位決定著相應(yīng)的事件處理函數(shù)被調(diào)用,只要某一個(gè)事件的標(biāo)志位變成了1,處理函數(shù)就被調(diào)用。那么什么時(shí)候這個(gè)標(biāo)志位會(huì)從0變成1呢?
(以下代碼顯示了這些標(biāo)志位都被初始化為0
void osalInitTasks( void )
)
這是由最開始時(shí)提到的那個(gè)重要的API osal_set_event(task_id, event_flag)完成的。你想要在哪些動(dòng)作完成后觸發(fā)這個(gè)事件,就調(diào)用這個(gè)函數(shù),將相應(yīng)的標(biāo)志位置1,那么在之后協(xié)議棧運(yùn)行的過程中就會(huì)根據(jù)需要調(diào)用這個(gè)事件的處理函數(shù)了。
ps:如果是消息的話,使用osal_msg_send可以完成類似的功能。有關(guān)方面,請(qǐng)移步 這里http://blog.csdn.net/ceci_prayer/article/details/9835399
ZStack OSAL的事件(event)與消息(message)——part2本文轉(zhuǎn)載自:http://blog.csdn.net/ceci_zhou/article/details/9835399
ZStack OSAL的事件(event)與消息(message)——part 1 (有關(guān)event的那些事)
在這里 http://blog.csdn.net/ceci_prayer/article/details/9787349
二、消息 消息可以理解為帶有附加信息的事件。最典型的一類便是按鍵消息,它同時(shí)產(chǎn)生了一個(gè)哪個(gè)按鍵被按下了附加信息。所以在OnBoard_SendKeys這個(gè) 函數(shù)中,不僅向GenericApp發(fā)送了事件,還通過調(diào)用osal_msg_send函數(shù)向GenericApp發(fā)送了一個(gè)消息,這個(gè)消息記錄了這個(gè)事件的附加信息。 一般來說,一個(gè)消息總是和一個(gè)事件對(duì)應(yīng)。當(dāng)協(xié)議棧接收到消息后,在ProcessEvent函數(shù)中有以下語句:
if ( events & SYS_EVENT_MSG ) { case …… } 可以看出,消息是被當(dāng)作系統(tǒng)事件接收的,接收到之后會(huì)找到消息對(duì)應(yīng)的event,之后進(jìn)行相應(yīng)的處理。 消息的使用與事件類似,但是使用了不同的函數(shù)觸發(fā):
byte osal_msg_send( byte destination_task, byte *msg_ptr ) 這個(gè)函數(shù)有兩個(gè)參數(shù)分別為接收事件任務(wù)的ID,另一個(gè)是指向消息的指針。它的功能是向一個(gè)任務(wù)發(fā)送命令或數(shù)據(jù)消息,此外,這個(gè)函數(shù)也會(huì)觸發(fā)目標(biāo)任務(wù)的SYS_EVENT_MSG(系統(tǒng)消息任務(wù))。
----------------------------以上是之前學(xué)習(xí)內(nèi)容的分割線-----------------------------------
之前對(duì)于消息的分析是正確的,但過于籠統(tǒng)了。今天我重新梳理了一下關(guān)于消息(message)的思路,在這里記錄一下。 首先我們需要明確一個(gè)概念,,msg是用來干什么的?msg和event有什么不同? 我們已經(jīng)知道了,event是一個(gè)事件,當(dāng)這個(gè)事件發(fā)生以后,會(huì)觸發(fā)相應(yīng)的事件處理函數(shù)。即event是事先定義好的,但不知道會(huì)在哪個(gè)確定時(shí)間點(diǎn)被觸發(fā)。 而消息不同。顧名思義,消息是用來傳遞信息的,即有兩個(gè)主體(如下圖中的task1和task2),在這兩個(gè)主體想要通信的時(shí)候,就會(huì)用到消息。
上面這個(gè)圖是我自已總結(jié)的有關(guān)zstack中消息的用法(如果有不正確的地方希望大家指正^_^)。 step1:osal_msg_allocate 可以看出,task1想要給task2發(fā)送消息(這兩個(gè)任務(wù)可能屬于一個(gè)設(shè)備,也可能屬于不同的設(shè)備,這一點(diǎn)稍后再說),于是task1就得先產(chǎn)生一個(gè)msg,這時(shí)候就要用到osal_msg_allocation給這個(gè)消息分配一個(gè)緩存:
uint8 * osal_msg_allocate( uint16 len)
if ( len == 0 )
hdr = (osal_msg_hdr_t *) osal_mem_alloc( (short)(len sizeof( osal_msg_hdr_t )) ); 注意加粗的代碼。首先看一下這個(gè)函數(shù)的參數(shù)len,是什么呢?查手冊(cè)可以知道,len是msg的長(zhǎng)度。那osal_msg_hdr_t *hdr又是什么呢?看一下osal_msg_hdr_t這個(gè)數(shù)據(jù)結(jié)構(gòu)的定義:
typedef struct 這個(gè)實(shí)際上是消息的頭部。再看下一句:
hdr = (osal_msg_hdr_t *) osal_mem_alloc( (short)(len sizeof( osal_msg_hdr_t )) ); 這句代碼是給消息分配緩存區(qū)的,而緩存區(qū)的大小是len sizeof( osal_msg_hdr_t )),也就是消息的大小 消息頭的大小。
到這里我們隱約能感覺到,一個(gè)消息應(yīng)該是由兩部分組成:消息頭和消息的實(shí)際內(nèi)容,消息頭是協(xié)議棧定義好的,而消息的內(nèi)容則應(yīng)該是我們自己添加的。大家再關(guān)注一下osal_msg_allocate的代碼,可以看出,除了分配緩存以外,它還賦給了消息頭一個(gè)初始值,但是卻沒有對(duì)消息本身做什么處理。因?yàn)橄⑹且艚o大家自己定義的,所以osal_msg_allocate將指向消息頭hdr下一位的指針做為函數(shù)的返回值,以便大家添加自己的消息代碼。 至于消息的具體定義,可以分為兩種:系統(tǒng)消息和用戶消息。系統(tǒng)消息什么的拜托協(xié)議棧就好啦,而用戶消息就需要大家根據(jù)實(shí)際的要求動(dòng)手了^0^ (在接下來的內(nèi)容中,完整的消息被認(rèn)為是已經(jīng)定義好的,直接拿來用就可以了。) step2:osal_msg_send
send函數(shù)有兩個(gè)參數(shù),一個(gè)是目標(biāo)task的地址,另一個(gè)是指向消息的指針。
uint8 osal_msg_send( uint8 destination_task, uint8 *msg_ptr )
{
if ( msg_ptr == NULL ) return ( INVALID_MSG_POINTER ); if ( destination_task >= tasksCnt ) { osal_msg_deallocate( msg_ptr ); return ( INVALID_TASK ); } // Check the message header if ( OSAL_MSG_NEXT( msg_ptr ) != NULL || OSAL_MSG_ID( msg_ptr ) != TASK_NO_TASK ) { osal_msg_deallocate( msg_ptr ); return ( INVALID_MSG_POINTER ); } OSAL_MSG_ID( msg_ptr ) = destination_task; // queue message osal_msg_enqueue( &osal_qHead, msg_ptr ); // Signal the task that a message is waiting osal_set_event( destination_task, SYS_EVENT_MSG ); return ( SUCCESS ); }
其他內(nèi)容就不用過過關(guān)注了,看看加粗的代碼:osal_msg_enqueue
這句看起來是入棧操作,嗯嗯,就是這樣^0^~其實(shí)在協(xié)議棧內(nèi)部維護(hù)著一個(gè)消息隊(duì)列,也就是說,消息是按照隊(duì)列的方式被操作的,所以就有了入棧出棧的內(nèi)容,具體實(shí)現(xiàn)就不關(guān)注了,這里只要明白發(fā)送的消息被放到(應(yīng)該是目標(biāo)任務(wù)的)消息隊(duì)列里就好啦,別的東西用到的時(shí)候再說吧。
再看一下 osal_set_event( destination_task, SYS_EVENT_MSG );這句很重要啊,因?yàn)樗嬖V我們,在發(fā)送了一個(gè)消息之后,會(huì)觸發(fā)目標(biāo)協(xié)議棧的系統(tǒng)事件SYS_EVENT_MSG,也就是說,雖然消息的處理方式與事件類似,但是有關(guān)消息的事件是系統(tǒng)事件哦~想要處理消息的話知道去哪找了吧?
step3:osal_msg_receive 又是一個(gè)重量級(jí)的API,這個(gè)API關(guān)系到我們?cè)趺慈〕鱿?。先看看在哪里?huì)調(diào)用這個(gè)API,預(yù)告一下,會(huì)是我們十分熟悉的地方哦~
uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events )
{ afIncomingMSGPacket_t *MSGpkt; if ( events & SYS_EVENT_MSG ) { MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID ); while ( MSGpkt ) { switch ( MSGpkt->hdr.event ) { // Received when a key is pressed case KEY_CHANGE: SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys ); break; // Received when a messages is received (OTA) for this endpoint case AF_INCOMING_MSG_CMD: SampleApp_MessageMSGCB( MSGpkt ); break; ProcessEvent!這個(gè)是大家最早熟悉的部分了吧?在這個(gè)API中,首先定義了一個(gè)數(shù)據(jù)結(jié)構(gòu):afIncomingMSGPacket_t *MSGpkt,具體看一下:
typedef struct
{ osal_event_hdr_t hdr; /* OSAL Message header */ uint16 groupId; /* Message's group ID - 0 if not set */ uint16 clusterId; /* Message's cluster ID */ afAddrType_t srcAddr; /* Source Address, if endpoint is STUBAPS_INTER_PAN_EP, it's an InterPAN message */ uint16 macDestAddr; /* MAC header destination short address */ uint8 endPoint; /* destination endpoint */ uint8 wasBroadcast; /* TRUE if network destination was a broadcast address */ uint8 LinkQuality; /* The link quality of the received data frame */ uint8 correlation; /* The raw correlation value of the received data frame */ int8 rssi; /* The received RF power in units dBm */ uint8 SecurityUse; /* deprecated */ uint32 timestamp; /* receipt timestamp from MAC */ afMSGCommandFormat_t cmd; /* Application Data */ } afIncomingMSGPacket_t; 有點(diǎn)嚇人?。〔贿^仔細(xì)看一眼,除了第一句osal_event_hdr_t hdr之外,別的好像都不是很重要嘛~對(duì)的,以af_開頭,說明這個(gè)數(shù)據(jù)是經(jīng)過無線傳輸后收到的,也就是由另一個(gè)設(shè)備上的task發(fā)送給本設(shè)備的,所以必然會(huì)在我們?cè)瓉硐⒌幕A(chǔ)上層層打包,加上許多其他內(nèi)容了。
再來看一下關(guān)鍵的osal_event_hdr_t結(jié)構(gòu)體:
typedef struct
{ uint8 event; uint8 status; } osal_event_hdr_t; 并不是很復(fù)雜,只包括了一個(gè)事件發(fā)生的標(biāo)志(event)和狀態(tài)(status)。那它又是用來做什么的呢?
答案是,它是用來找到事件對(duì)應(yīng)的消息的。
前面說過,在發(fā)送一個(gè)消息之后osal_mem_send,會(huì)觸發(fā)一個(gè)系統(tǒng)事件。我們現(xiàn)在遇到的情況是,有了這個(gè)事件之后,怎么找到它對(duì)應(yīng)的消息,這時(shí)就需要上門介紹的機(jī)制了,定義一個(gè)指向osal_event_hdr_t類型的指針,然后用osal_msg_receive找到這個(gè)消息并讓前面的指針MSGpkt指向它。(在代碼給出的例子中指向消息的指針類型是afIncomingMSGPacket_t,但是從剛才的分析可以看出,如果是在同一個(gè)設(shè)備中的兩個(gè)task通信的話,也就是不需要無線傳輸?shù)臅r(shí)候,用osal_event_hdr_t類型的指針就可以滿足要求了)。
至于這一步所說的重要的函數(shù)osal_msg_receive,它的功能就是從協(xié)議棧維護(hù)的消息隊(duì)列中取出相應(yīng)的消息,想要了解就看看源代碼吧~
現(xiàn)在我們既有事件,又有消息,接下來就進(jìn)入愉快的處理帶有消息的事件部分吧~
以case AF_INCOMING_MSG_CMD為例,嗯嗯,只要以MSGpkt為參數(shù),調(diào)用相應(yīng)的處理函數(shù)就好啦:SampleApp_MessageMSGCB( MSGpkt );很愉快哦~
呼,終于結(jié)束了,寫出來居然有這么長(zhǎng)~最后附上前一篇有關(guān)event的地址,方便學(xué)習(xí)咯~
|
|