一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

ZStack OSAL的事件(event)與消息(message)——part1 & par...

 一起聽海520 2016-10-06

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 )
{
#if !defined ( ZBIT ) && !defined ( UBIT )
  for(;;)  // Forever Loop
#endif
  {
    uint8 idx = 0;

    osalTimeUpdate();
    Hal_ProcessPoll();  // This replaces MT_SerialPoll() and osal_check_timer().
    
    do {
      if (tasksEvents[idx])  // Task is highest priority that is ready.
      {                      /* idx 越小,任務(wù)的優(yōu)先級(jí)越高   */
        break;               /* 較高優(yōu)先級(jí)的任務(wù)總是優(yōu)先處理 */
      }
    } while ( idx < tasksCnt);
    /* 得到了待處理的具有最高優(yōu)先級(jí)的任務(wù)索引號(hào) idx  */

    if (idx < tasksCnt)
    {
      uint16 events;
      halIntState_t intState;

      HAL_ENTER_CRITICAL_SECTION(intState); /* 進(jìn)入臨界區(qū)---保存EA狀態(tài)然后置EA = 0 */
      events = tasksEvents[idx];  //處理該idx的task的event
      tasksEvents[idx] = 0;  // Clear the Events for this task.
      HAL_EXIT_CRITICAL_SECTION(intState);  /* 退出臨界區(qū)---恢復(fù)EA狀態(tài) */

      events = (tasksArr[idx])( idx, events );  //調(diào)用該idx個(gè)任務(wù)的事件處理函數(shù)(函數(shù)指針指向的函數(shù))

      HAL_ENTER_CRITICAL_SECTION(intState);
      tasksEvents[idx] |= events;  // Add back unprocessed events to the current task.
      HAL_EXIT_CRITICAL_SECTION(intState);
    }


加粗的代碼就是上述的第二步,可以看出,這一步并不是直接調(diào)用的,而是根據(jù)不同的參數(shù)(idx,events)來動(dòng)態(tài)調(diào)用的。這里涉及到一個(gè)很重要的結(jié)構(gòu)體tasksArr[]:


const pTaskEventHandlerFn tasksArr[] = { 
  macEventLoop,
  nwk_event_loop,
  Hal_ProcessEvent,
#if defined( MT_TASK )
  MT_ProcessEvent,
#endif
  APS_event_loop,
  ZDApp_event_loop,
  ControlWifi_ProcessEvent, 
  GenericApp_ProcessEvent
};


可以看出這個(gè)結(jié)構(gòu)體是一個(gè)數(shù)組,但是是一個(gè)什么類型的數(shù)組呢?pTaskEventHandlerFn類型的。這個(gè)類型有代表什么的?接下來我們找到這個(gè)類型的定義看一下:

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 )
{
  uint8 taskID = 0;


  tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
  osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));


這是由最開始時(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 )
  {
    MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( GenericApp_TaskID );
    while ( MSGpkt )
    {
      switch ( MSGpkt->hdr.event )

     {

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
{
  osal_msg_hdr_t *hdr;

  if ( len == 0 )
    return ( NULL );

 hdr = (osal_msg_hdr_t *) osal_mem_alloc( (short)(len sizeof( osal_msg_hdr_t )) );
  if ( hdr )
  {
    hdr->next = NULL;
    hdr->len = len;
    hdr->dest_id = TASK_NO_TASK;
    return ( (uint8 *) (hdr 1) );
  }
  else
    return ( NULL );
}

注意加粗的代碼。首先看一下這個(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
{
  void   *next;
  uint16 len;
  uint8  dest_id;
} osal_msg_hdr_t;

這個(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í)咯~

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    日韩一区二区三区有码| 丰满的人妻一区二区三区| 久久精品少妇内射毛片| 自拍偷拍福利视频在线观看| 欧美中文日韩一区久久| 人体偷拍一区二区三区| 日韩黄色大片免费在线| 色综合久久六月婷婷中文字幕| 日本精品视频一二三区| 黄色美女日本的美女日人| 国产又粗又猛又长又大| 91偷拍裸体一区二区三区| 一区二区三区免费公开| 国产水滴盗摄一区二区| 黄色激情视频中文字幕| 日韩一区二区三区有码| 国产肥妇一区二区熟女精品| 开心久久综合激情五月天| 日韩精品综合免费视频| 尹人大香蕉一级片免费看| 国产精品丝袜一二三区| 熟妇久久人妻中文字幕| 日本av在线不卡一区| 九九热这里只有免费精品| 国产精品激情对白一区二区| 欧美黄色黑人一区二区| 99久只有精品免费视频播放 | 日本人妻熟女一区二区三区 | 内射精品欧美一区二区三区久久久| 日本丰满大奶熟女一区二区| 欧美国产亚洲一区二区三区| 日韩欧美一区二区不卡看片| 91在线爽的少妇嗷嗷叫| 国产又大又硬又粗又湿| 字幕日本欧美一区二区| 亚洲欧美一二区日韩高清在线| 东京热一二三区在线免| 精品熟女少妇av免费久久野外| 中文字幕亚洲精品人妻| 五月的丁香婷婷综合网| 91人妻人澡人人爽人人精品|