新浪博客

zstack协议栈解读(转)

2014-05-28 19:00阅读:
一个设备可以有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
br> 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
代码分析:

[cpp] view plaincopy
  1. void SerialApp_Init( uint8 task_id )
  2. {
  3. halUARTCfg_t uartConfig;
  4. SerialApp_TaskID = task_id;
  5. SerialApp_RxSeq = 0xC3;//不知为何????
  6. //在AF层注册该端口描述符
  7. afRegister( (endPointDesc_t *)&SerialApp_epDesc );
  8. //按键事件交给本任务处理
  9. RegisterForKeys( task_id );
  10. //串口配置
  11. uartConfig.configured = TRUE; // 2x30 don't care - see uart driver.
  12. uartConfig.baudRate = SERIAL_APP_BAUD;
  13. uartConfig.flowControl = FALSE;
  14. uartConfig.flowControlThreshold = SERIAL_APP_THRESH; // 2x30 don't care - see uart driver.
  15. uartConfig.rx.maxBufSize = SERIAL_APP_RX_SZ; // 2x30 don't care - see uart driver.
  16. uartConfig.tx.maxBufSize = SERIAL_APP_TX_SZ; // 2x30 don't care - see uart driver.
  17. uartConfig.idleTimeout = SERIAL_APP_IDLE; // 2x30 don't care - see uart driver.
  18. uartConfig.intEnable = TRUE; // 2x30 don't care - see uart driver.

[cpp] view plaincopy
  1. uartConfig.callBackFunc = SerialApp_CallBack;//回调函数

[cpp] view plaincopy
  1. HalUARTOpen (SERIAL_APP_PORT, &uartConfig);
  2. #if defined ( LCD_SUPPORTED )
  3. HalLcdWriteString( 'SerialApp', HAL_LCD_LINE_2 );
  4. #endif
  5. //注册节点绑定响应事件和匹配描述符响应事件
  6. ZDO_RegisterForZDOMsg( SerialApp_TaskID, End_Device_Bind_rsp );
  7. ZDO_RegisterForZDOMsg( SerialApp_TaskID, Match_Desc_rsp );
  8. #if defined (ZDO_COORDINATOR)//有三种设备不知怎样定义????
  9. HalUARTWrite(SERIAL_APP_PORT,szWelcomeMsgCoor,sizeof(szWelcomeMsgCoor));
  10. #else
  11. HalUARTWrite(SERIAL_APP_PORT,szWelcomeMsgRouter,sizeof(szWelcomeMsgRouter));
  12. #endif
  13. }

#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)


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

#define ZDO_CB_MSG 0xD3 // ZDO incoming message callback

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

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

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所对应的值为非零。

大神的解答:

[cpp] view plaincopy
  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任务发送按键消息。

[cpp] view plaincopy
  1. if (keys && (pHalKeyProcessFunction))
  2. {
  3. (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
  4. }

我的更多文章

下载客户端阅读体验更佳

APP专享