Chinaunix首页 | 论坛 | 博客
  • 博客访问: 173902
  • 博文数量: 63
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 181
  • 用 户 组: 普通用户
  • 注册时间: 2016-02-25 15:50
文章分类
文章存档

2020年(1)

2016年(62)

我的朋友

分类: 嵌入式

2016-03-03 15:28:32

在上几篇文章中我们分析了,整个OSAL的流程,还有添加自己的任务,以及如何运行到自己定义的任务。这一篇文章主要是分析一下,自己定义的任务中要完成的功能,需要的事件函数是怎样的。
   这个例子就是一个简单的点对点的数据发送,其中涉及到较少的网络配置,其中最主要的两个函数是SampleApp_ProcessEvent(uint8 task_id,uint16 events),SampleApp_InittaskID),一个是任务的处理函数,一个是初始化函数。SampleApp_Init(taskID)这个 函数在前面的文章中也已经分析过了,现在主要关注一下SampleApp_ProcessEvent(uint8 task_id,uint16 events)函数的实现。
   每个应用任务都通过SampleApp_ProcessEvent()函数来处理任务中的事件。一旦SampleApp_TaskID任务的某个OSAL 事件发生,那么就可以通过调用SampleApp_ProcessEvent()函数来处理。在SampleApp_ProcessEvent()中有一个事件处理循环,循环检测是哪个事件发生。
/*********************************************************************
 * @fn      SampleApp_ProcessEvent
 *
 * @brief   Generic Application Task event processor.  This function
 *          is called to process all events for the task.  Events
 *          include timers, messages and any other user defined events.
 *       这个函数被用来调用处理所有的事件,事件有 定时器 消息 用户自己定义的
 * @param   task_id  - The OSAL assigned task ID. 任务ID
 * @param   events - events to process.  This is a bit map and can
 *                   contain more than one event. 处理的事件,这是一个位图
 *
 * @return  none
 */
uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events )
{
  afIncomingMSGPacket_t *MSGpkt;
//系统事件号 SYS_EVENT_MSG = 0x8000
  if ( events & SYS_EVENT_MSG )
  {
  //检索收到的命令,没有收到返回NULL
    MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID );
    while ( MSGpkt ) //如果不为空时,判断消息的类型
    {
      switch ( MSGpkt->hdr.event ) //这里是判断SYS_EVENT_MSG事件类型,不同的SYS_EVENT_MSG类型需要不同的处理。
      {
        /* Received when a key is pressed这里判断是否是键盘事件,如果键盘事件就调用键盘处理函数。
如果一个OSAL任务已经被登记组侧,那么任何键盘事件都将接受一个KEY_CHANGE事件信息。可能有如下几种方式得到键盘事件信息
1)、HAL检测到键盘按下(中断或者查询检测)
2)、HALOSAL任务检测到一个键盘状态改变调用回叫函数产生
3)、OSAL键盘改变回叫函数发送一个OSAL系统事件信息(KEY_CHANGE)。*/
        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;
 
        // Received whenever the device changes state in the network 网络中的设备状态发生改变时,产生的事件消息
        case ZDO_STATE_CHANGE:
          SampleApp_NwkState = (devStates_t)(MSGpkt->hdr.status); //获得当前的状态
          if ( (SampleApp_NwkState == DEV_ZB_COORD) //判断其类型
              || (SampleApp_NwkState == DEV_ROUTER)
              || (SampleApp_NwkState == DEV_END_DEVICE) )
          {
            // Start sending the periodic message in a regular interval. 发送周期信息
            osal_start_timerEx( SampleApp_TaskID,
                              SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
                              SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT );
          }
          else
          {
            // Device is no longer in the network
          }
          break;
 
        default:
          break;
      }
 
      // Release the memory 释放内存
      osal_msg_deallocate( (uint8 *)MSGpkt );
 
      // Next - if one is available 得到任务中下一个等待处理的事件
      MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID );
    }
 
    // return unprocessed events 返回没有处理的事件
    return (events ^ SYS_EVENT_MSG);
  }
 
  // Send a message out - This event is generated by a timer
  //  (setup in SampleApp_Init()). 向外发送信息,该事件是由定时器产生
  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)afIncomingMSGPacket_t *MSGpkt; 其中afIncomingMSGPacket_t 是一个结构体,这个结构体中包括传输的帧的格式。
typedef struct
{
  osal_event_hdr_t hdr;
  uint16 groupId;
  uint16 clusterId;
  afAddrType_t srcAddr;
  byte endPoint;
  byte wasBroadcast;
  byte LinkQuality;
  byte SecurityUse;
  uint32 timestamp;
  afMSGCommandFormat_t cmd;
} afIncomingMSGPacket_t;
(2) 这里的SYS_EVENT_MSG是系统事件,也是协议栈已经定义好的系统事件,在文件ZcomDef.h中,事件号是一个16bit的常量,使用叫作独热码(one-hot code)编码,也是一个位表示一个事件,方便进行event的提取,这样一个task最多可以有16eventSYS_EVENT_MSG已经占用了0x8000,故自定义的个事件只能有15个,事件的提取和清除可以用简单的位操作指令实现,事件的提取可以用位与操作 events & SYS_EVENT_MSG,事件的清除可以用异或操作实现,evets ^ SYS_EVENT_MSG ,系统事件包括了各种系统消息(message,系统事件中的消息号是一个8bit常量,也就是一事件可以包括255个消息,定义在ZcomDef.h中。
#define SYS_EVENT_MSG               0x8000  // A message is waiting event
/*********************************************************************
 * Global System Messages
 */
#define SPI_INCOMING_ZTOOL_PORT   0x21    // Raw data from ZTool Port (not implemented)
#define SPI_INCOMING_ZAPP_DATA    0x22    // Raw data from the ZAPP port (see serialApp.c)
#define MT_SYS_APP_MSG            0x23    // Raw data from an MT Sys message
#define MT_SYS_APP_RSP_MSG        0x24    // Raw data output for an MT Sys message
 
#define AF_DATA_CONFIRM_CMD       0xFD    // Data confirmation
#define AF_INCOMING_MSG_CMD       0x1A    // Incoming MSG type message
#define AF_INCOMING_KVP_CMD       0x1B    // Incoming KVP type message
#define AF_INCOMING_GRP_KVP_CMD   0x1C    // Incoming Group KVP type message
 
#define KEY_CHANGE                0xC0    // Key Events
 
#define ZDO_NEW_DSTADDR           0xD0    // ZDO has received a new DstAddr for this app
#define ZDO_STATE_CHANGE          0xD1    // ZDO has changed the device's network state
#define ZDO_MATCH_DESC_RSP_SENT   0xD2    // ZDO match descriptor response was sent
#define ZDO_CB_MSG                0xD3    // ZDO incoming message callback
   用户自己定义的系统事件的消息范围为0xE0—0xFF,下面是几个比较常用的系统事件的消息。
              AF_DATA_CONFIRM_CMD
AF_DataRequest()函数数据请求成功的指示。Zsuccess确认数据请求传输成功,如果数据请求设置AF_ACK_REQUEST标志 位,那么,只有最终目的地址成功接收后,Zsuccess确认才返回。如果如果数据请求没有设置AF_ACK_REQUEST标志位,那么,数据请求只要 成功传输到下跳节点就返回Zsuccess确认信息。
              AF_INCOMING_MSG_CMD 收到MSG消息,通知任务进行处理
              KEY_CHANGE 按键处理事件
              ZDO_NEW_DSTADDR 新的目标地址接收到 指示自动匹配请求 绑定时经常使用
              ZDO_STATE_CHANGE  设备状态变化指示
(3)osal_start_timerEx( SampleApp_TaskID,
SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT );
osal_start_timerEx()的作用是启动一系统定时器,当其溢出 SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT)时,会触发taskSampleApp_TaskID)的事件 SAMPLEAPP_SEND_PERIODIC_MSG_EVT)。看到事件SAMPLEAPP_SEND_PERIODIC_MSG_EVT了,它是用户定义的事件,分析之
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); }
    以上就是SAMPLEAPP_SEND_PERIODIC_MSG_EVT的处理函数,先分析总的流程,SampleApp_SendPeriodicMessage();发送信息,osal_start_timerEx()重新启动一系统定时 器,同样是指向taskSampleApp_TaskID)的事件(SAMPLEAPP_SEND_PERIODIC_MSG_EVT),返回时要注意清除当前事件 (events ^ SAMPLEAPP_SEND_PERIODIC_MSG_EVT),否则会反复处理同一个事件,陷入死循环。
SampleApp.h中定义
#define SAMPLEAPP_SEND_PERIODIC_MSG_EVT       0x0001
   在这个例子中,调用了osal_start_timerEx()函数来定时产生发送周期信息事件,而定时器的运行是设备一旦加入网络就不停的运行,用函数SampleApp_SendPeriodicMessage();发送周期信息,而用函数osal_start_timerEx( SampleApp_TaskID,               SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
(SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT + (osal_rand() & 0x00FF)) );
来继续运行定时器定时发送一个周期信息,osal_start_timerEx()函数,第一个参数是处理事件的任务ID号,第二个参数是事件的类型,也就是事件是一个什么事件,第三个参数是需要定时时间,发送周期信息的时间周期。
4void SampleApp_SendPeriodicMessage(void)定时器发送周期信息的函数里调用了AF_DataRequest()函数用来发送数据,发送数据的过程是把数据从应用层传到网络层,再传到MAC,再传到物理层,最后通过OTA发送出去,接收的过程是相反的过程,在接收到消息后在应用层的反应就是,会发送一个AF_INCOMING_MSG_CMD消息事件,case AF_INCOMING_MSG_CMD:
SampleApp_MessageSGCB(MSGpkt);
Break;
这里表示收到某个信息,然后在里面调用了收到信息后的处理函数。SampleApp_MessageSGCB(MSGpkt);
 
这个函数主要就是调用发送数据的函数 AF_DataRequest()对数据进行发送。
/*********************************************************************
 * @fn      SampleApp_SendPeriodicMessage
 *
 * @brief   Send the periodic message.
 *
 * @param   none
 *
 * @return  none
 */
void SampleApp_SendPeriodicMessage( void )
{
  if ( AF_DataRequest( &SampleApp_Periodic_DstAddr, &SampleApp_epDesc,
                       SAMPLEAPP_PERIODIC_CLUSTERID,
                       1,
                       (uint8*)&SampleAppPeriodicCounter,
                       &SampleApp_TransID,
                       AF_DISCV_ROUTE,
                       AF_DEFAULT_RADIUS ) == afStatus_SUCCESS )
  {
  }
  else
  {
    // Error occurred in request to send.
  }
}
5case AF_INCOMING_MSG_CMD:
SampleApp_MessageMSGCB( MSGpkt );
break;
这里表示收到某个信息,然后在里面调用了收到信息的信息处理函数 SampleApp_MessageMSGCB( MSGpkt )
/*********************************************************************
 * LOCAL FUNCTIONS
 */
 
/*********************************************************************
 * @fn      SampleApp_MessageMSGCB
 *
 * @brief   Data message processor callback.  This function processes
 *          any incoming data - probably from other devices.  So, based
 *          on cluster ID, perform the intended action.
 *
 * @param   none
 *
 * @return  none
 */
void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
  uint16 flashTime;
 
  switch ( pkt->clusterId )
  {
    case SAMPLEAPP_PERIODIC_CLUSTERID:
      break;
     
    case SAMPLEAPP_FLASH_CLUSTERID:
      flashTime = BUILD_UINT16(pkt->cmd.Data[1], pkt->cmd.Data[2] );
      HalLedBlink( HAL_LED_4, 4, 50, (flashTime / 4) );
      break; 
  }
}
   这里判断了两种信息: 周期信息; 闪灯信息,根据它们的簇ID号的不同做出不同的处理。
不同的信息就相当于收到了不同的命令,然后根据不同的命令做出了不同的处理
6SampleApp_ProcessEvent()函数实际上是由(tasksArr[idx])( idx, events );调用的,tasksEvents[idx]可以有底层按键 串口等等处理函数中调用osal_set_event()设置,或者有定时器,还有就是任务间发送消息时候会触发SYS_EVENT_MSG事件。这就涉及到了别外一个问题,就是任务中的每个事件是通过什么方式触发的?
 
阅读(1769) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~