加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

zstack协议栈解读(转)

(2014-05-28 19:00:53)
分类: 嵌入式学习

一个设备可以有240个端点,每个端点必须有一个端点描述符endPointDesc,端点描述符里包括一个简单描述符。在看Zstack的例子时应该详细看一下应用层,3个文件,SerialApp.h SerialApp.c OSAL_SerialApp.c

 

端点描述符结构:

typedef struct
{
  byte endPoint;
  byte *task_id;  // Pointer to location of the Application task ID.
  SimpleDescriptionFormat_t *simpleDesc;
  afNetworkLatencyReq_t latencyReq;  //延时请求
} endPointDesc_t;

//初始化后的一个端点描述符结构

const endPointDesc_t SerialApp_epDesc =
{
  SERIALAPP_ENDPOINT,
 &SerialApp_TaskID,
  (SimpleDescriptionFormat_t *)&SerialApp_SimpleDesc,
  noLatencyReqs
};

简单描述符的结构

typedef struct
{
  byte          EndPoint;     //端点号1-240
  uint16        AppProfId;     //支持的Profile ID
  uint16        AppDeviceId; //支持的设备ID
  byte          AppDevVer:4; //执行的设备描述的版本
  byte          Reserved:4;             // AF_V1_SUPPORT uses for AppFlags:4.
  byte          AppNumInClusters; //终端支持的输入簇数目
  cId_t         *pAppInClusterList; //指向输入Cluster ID列表的指针
  byte          AppNumOutClusters;//终端支持的输出簇数目
  cId_t         *pAppOutClusterList;//指向输出Cluster ID列表的指针
} SimpleDescriptionFormat_t;

//初始化后的一个简单描述符结构

const SimpleDescriptionFormat_t SerialApp_SimpleDesc =
{
  SERIALAPP_ENDPOINT,              //  int   Endpoint;
  SERIALAPP_PROFID,                //  uint16 AppProfId[2];
  SERIALAPP_DEVICEID,              //  uint16 AppDeviceId[2];
  SERIALAPP_DEVICE_VERSION,        //  int   AppDevVer:4;
  SERIALAPP_FLAGS,                 //  int   AppFlags:4;
  SERIALAPP_MAX_CLUSTERS,          //  byte  AppNumInClusters;
  (cId_t *)SerialApp_ClusterList,  //  byte *pAppInClusterList;
  SERIALAPP_MAX_CLUSTERS,          //  byte  AppNumOutClusters;
  (cId_t *)SerialApp_ClusterList   //  byte *pAppOutClusterList;
};

AF.c 中的afStatus_t afRegister( endPointDesc_t *epDesc ),//在AF层注册该端口描述符。

只有分别在两个节点的简单描述结构体中,同时注册了相同的命令标示符并且方向相反才能建立绑定。

SerialApp_TxBuf[0]存放的是发送的序列号SerialApp_TxSeq,即通过串口发送的数据SerialApp_TxBuf,是从SerialApp_TxBuf[1]开始的,同样接收数组SerialApp_RxBuf[0]存放的是接收的序列号SerialApp_RxSeq,为什么会这样?

其实这里加了个保险措施,其实就是发送设备将发送序列号加在数据中一块发送,接收设备接收到数据后,将发送序列号回传给发送设备,发送设备将该序列号和发送时的序列号比较一下,如果相等则说明数据发送成功。

通过上面的分析可知,接收设备收到数据后,将接数据有关的状态以及接收序列号(其实就是发送设备的发送序列号)等信息放在一个4字节的数组里面,然后将该数组发送给发送设备。

发送设备接收到接收设备回传的响应信息后,将发送序列号和接收序列号比较,如果相等,则说明原来的数据发送成功了。

参考http://www.feibit.com/forum.php?mod=viewthread&tid=2928

代码分析:

 

  1. void SerialApp_Init( uint8 task_id  
  2.  
  3.   halUARTCfg_t uartConfig;  
  4.   
  5.   
  6.   SerialApp_TaskID task_id;  
  7.   SerialApp_RxSeq 0xC3;//不知为何????  
  8.    //在AF层注册该端口描述符  
  9.   afRegister( (endPointDesc_t *)&SerialApp_epDesc );  
  10.   
  11. //按键事件交给本任务处理  
  12.   RegisterForKeys( task_id );  
  13.   
  14. //串口配置  
  15.   uartConfig.configured           TRUE;              // 2x30 don't care see uart driver.  
  16.   uartConfig.baudRate             SERIAL_APP_BAUD;  
  17.   uartConfig.flowControl          FALSE;  
  18.   uartConfig.flowControlThreshold SERIAL_APP_THRESH; // 2x30 don't care see uart driver.  
  19.   uartConfig.rx.maxBufSize        SERIAL_APP_RX_SZ;  // 2x30 don't care see uart driver.  
  20.   uartConfig.tx.maxBufSize        SERIAL_APP_TX_SZ;  // 2x30 don't care see uart driver.  
  21.   uartConfig.idleTimeout          SERIAL_APP_IDLE;   // 2x30 don't care see uart driver.  
  22.   uartConfig.intEnable            TRUE;              // 2x30 don't care see uart driver.  
  1. uartConfig.callBackFunc         SerialApp_CallBack;//回调函数  
  1.   HalUARTOpen (SERIAL_APP_PORT, &uartConfig);  
  2.   
  3.   
  4. #if defined LCD_SUPPORTED )  
  5.   HalLcdWriteString( "SerialApp"HAL_LCD_LINE_2 );  
  6. #endif  
  7.   //注册节点绑定响应事件和匹配描述符响应事件  
  8.   ZDO_RegisterForZDOMsg( SerialApp_TaskID, End_Device_Bind_rsp );  
  9.   ZDO_RegisterForZDOMsg( SerialApp_TaskID, Match_Desc_rsp );  
  10. #if defined (ZDO_COORDINATOR)//有三种设备不知怎样定义????  
  11.   HalUARTWrite(SERIAL_APP_PORT,szWelcomeMsgCoor,sizeof(szWelcomeMsgCoor));  
  12. #else  
  13.   HalUARTWrite(SERIAL_APP_PORT,szWelcomeMsgRouter,sizeof(szWelcomeMsgRouter));  
  14. #endif  
  15.   
  16.   
  17.  

#define End_Device_Bind_rsp     (End_Device_Bind_req | ZDO_RESPONSE_BIT)

 

#define End_Device_Bind_req     ((uint16)0x0020)

#define ZDO_RESPONSE_BIT        ((uint16)0x8000)

#define Match_Desc_rsp          (Match_Desc_req | ZDO_RESPONSE_BIT)

#define Match_Desc_req          ((uint16)0x0006)

 

  1.   
  2. ZStatus_t ZDO_RegisterForZDOMsg( uint8 taskID, uint16 clusterID  
  3.  
  4.   ZDO_MsgCB_t *pList;  
  5.   ZDO_MsgCB_t *pLast;  
  6.   ZDO_MsgCB_t *pNew;  
  7.   
  8.   // Look for duplicate  
  9.   pList pLast zdoMsgCBs;  
  10.   while pList  
  11.    
  12.     if pList->taskID == taskID && pList->clusterID == clusterID  
  13.       return ZSuccess );  
  14.     pLast pList;  
  15.     pList (ZDO_MsgCB_t *)pList->next;  
  16.    
  17.   
  18.   // Add to the list  
  19.   pNew (ZDO_MsgCB_t *)osal_mem_alloc( sizeof ZDO_MsgCB_t );  
  20.   if pNew  
  21.    
  22.     pNew->taskID taskID;  
  23.     pNew->clusterID clusterID;  
  24.     pNew->next NULL;  
  25.     if zdoMsgCBs  
  26.      
  27.       pLast->next pNew;  
  28.      
  29.     else  
  30.       zdoMsgCBs pNew;  
  31.     return ZSuccess );  
  32.    
  33.   else  
  34.     return ZMemError );  
  35.  

#define ZDO_CB_MSG                0xD3    // ZDO incoming message callback

 

什么时候会调用下面这个函数呢???是在UINT16 SerialApp_ProcessEvent( uint8 task_id, UINT16 events )中

 

  1.   
  2. static void SerialApp_ProcessZDOMsgs( zdoIncomingMsg_t *inMsg  
  3.  
  4.   switch inMsg->clusterID  
  5.    
  6.     case End_Device_Bind_rsp:  
  7.       if ZDO_ParseBindRsp( inMsg == ZSuccess  
  8.        
  9.         // Light LED  
  10.         HalLedSet( HAL_LED_4, HAL_LED_MODE_ON );  
  11.        
  12. #if defined(BLINK_LEDS)  
  13.       else  
  14.        
  15.         // Flash LED to show failure  
  16.         HalLedSet HAL_LED_4, HAL_LED_MODE_FLASH );  
  17.        
  18. #endif  
  19.       break 
  20.         
  21.     case Match_Desc_rsp:  
  22.        
  23.         ZDO_ActiveEndpointRsp_t *pRsp ZDO_ParseEPListRsp( inMsg );  
  24.         if pRsp  
  25.          
  26.           if pRsp->status == ZSuccess && pRsp->cnt  
  27.            
  28.             SerialApp_TxAddr.addrMode (afAddrMode_t)Addr16Bit;  
  29.             SerialApp_TxAddr.addr.shortAddr pRsp->nwkAddr;  
  30.             // Take the first endpoint, Can be changed to search through endpoints  
  31.             SerialApp_TxAddr.endPoint pRsp->epList[0];  
  32.               
  33.             // Light LED  
  34.             HalLedSet( HAL_LED_4, HAL_LED_MODE_ON );  
  35.            
  36.           osal_mem_free( pRsp );  
  37.          
  38.        
  39.       break 
  40.    
  41.  

MT这又是哪一层?

 

开始时所有任务的状态都被初始化为0,代表了当前任务没有需要响应的事件。事件是如何被OSAL捕获的,即taskEvents数组中的元素是什么时候设定为非零的?

参考http://bbs.feibit.com/thread-280-1-1.html

以按键响应为例,处理按键响应的函数是 Hal_ProcessEvent,采用扫描的方式。那么 Hal_ProcessEvent对应的taskEvents是在什么时候被置为非0的呢?

大体思路是这样的:

1、 // Final board initialization
  InitBoard( OB_READY );中的
 HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback);     


2、在void HalKeyConfig (bool interruptEnable, halKeyCBack_t cback)中最后有 osal_set_event(Hal_TaskID, HAL_KEY_EVENT);将事件HAL_KEY_EVENT交给任务Hal_TaskID处理,即taskEvents中的与Hal_TaskID相对的元素就被置为了非0,从而调用了

uint16 Hal_ProcessEvent( uint8 task_id, uint16 events )

3、在uint16 Hal_ProcessEvent( uint8 task_id, uint16 events )中又调用了   HalKeyPoll();并且启动了osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);即每隔100微秒就要调用uint16 Hal_ProcessEvent( uint8 task_id, uint16 events )一次,而在HalKeyPoll();中来扫描判断按键是否变化。HalKeyPoll()中(pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);

static halKeyCBack_t pHalKeyProcessFunction;

pHalKeyProcessFunction这个函数指针指向了 void OnBoard_KeyCallback ( uint8 keys, uint8 state )在这个函数中,又调用了byte OnBoard_SendKeys( byte keys, byte state ),在这个函数中,按键的状态信息被封装到了一个消息结构体中(对于消息,我们稍后再说)。最后有一个极其重要的函数被调用了。osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr );registeredKeysTaskID所指示的任务正是我们需要响应按键的SerialApp这个任务。在osal_msg_send函数中osal_set_event( destination_task, SYS_EVENT_MSG );

HalKeyPoll()获取当前按键的状态,并且通过调用OnBoard_KeyCallback函数向SerialApp任务发送一个按键消息,并且设置tasksEvents中SerialApp所对应的值为非零。

大神的解答:

 

  1. 第一、pHalKeyProcessFunction这个函数指针为何指向了OnBoard_KeyCallback函数。  
  2.   在HAL\Commen\ hal_drivers.c这个文件中,我们找到了HalDriverInit这个函数,在这个函数中,按键的初始化函数HalKeyInit被调用。在HalKeyInit中有这样的语句:  
  3.   {  
  4.         pHalKeyProcessFunction  NULL;  
  5.   }  
  6.   这说明在初始化以后pHalKeyProcessFunction并没有指向任何一个函数。那pHalKeyProcessFunction是什么时候被赋值的呢?  
  7.   就在HalKeyInit的下方有一个这样的函数HalKeyConfig。其中有这样一条语句:  
  8.   pHalKeyProcessFunction cback;  
  9.   cback是HalKeyConfig所传进来的参数,所以,想要知道它所指向的函数,必须找到其调用的地方。经过简单的搜索我们不难找出答案。在main函数中有这样一个函数调用:InitBoard( OB_READY );此函数中做了如下调用:  
  10.   {  
  11.         HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);  
  12.   }  
  13.   第二、registeredKeysTaskID为什么标识了GenericApp这个任务?  
  14.   由于OSAL是一个支持多任务的调度机制,所以在同一时间内将会有多个任务同时运行。但是从逻辑上来讲,一个事件只能由一个任务来处理。按键事件也不例外。  
  15.   那么如何向OSAL声明处理按键事件的任务是GenericApp呢?  
  16.   在GenericApp_Init(GenericApp的任务初始化函数)中有这么一个语句:  
  17.   {  
  18.         RegisterForKeys( GenericApp_TaskID );  
  19.   }  
  20.   RegisterForKeys函数向OSAL声明按键事件将由GenericApp任务来处理。在RegisterForKeys函数中:  
  21.   {  
  22.         registeredKeysTaskID task_id;  
  23.   }  


 

说实话下面这句不懂??是回调函数,就是要向SerialApp任务发送按键消息。

 

  1.   
  2.   if (keys && (pHalKeyProcessFunction))  
  3.    
  4.     (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);  
  5.   }  

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有