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
- void SerialApp_Init(
uint8 task_id )
- {
- halUARTCfg_t uartConfig;
-
-
- SerialApp_TaskID = task_id;
- SerialApp_RxSeq =
0xC3;//不知为何????
- //在AF层注册该端口描述符
- afRegister( (endPointDesc_t
*)&SerialApp_epDesc );
-
- //按键事件交给本任务处理
- RegisterForKeys( task_id );
-
- //串口配置
- uartConfig.configured
= TRUE;
// 2x30 don't care -
see uart driver.
- uartConfig.baudRate
= SERIAL_APP_BAUD;
- uartConfig.flowControl
=
FALSE;
- uartConfig.flowControlThreshold =
SERIAL_APP_THRESH; // 2x30 don't
care - see uart driver.
- uartConfig.rx.maxBufSize
=
SERIAL_APP_RX_SZ; // 2x30
don't care - see uart
driver.
- uartConfig.tx.maxBufSize
=
SERIAL_APP_TX_SZ; // 2x30
don't care - see uart
driver.
- uartConfig.idleTimeout
=
SERIAL_APP_IDLE; // 2x30
don't care - see uart
driver.
- uartConfig.intEnable
= TRUE;
// 2x30 don't care
- see uart driver.
[cpp] view plaincopy
- uartConfig.callBackFunc
=
SerialApp_CallBack;//回调函数
[cpp] view plaincopy
- HalUARTOpen (SERIAL_APP_PORT,
&uartConfig);
-
-
- #if defined ( LCD_SUPPORTED
)
- HalLcdWriteString(
'SerialApp', HAL_LCD_LINE_2
);
- #endif
- //注册节点绑定响应事件和匹配描述符响应事件
- ZDO_RegisterForZDOMsg(
SerialApp_TaskID, End_Device_Bind_rsp );
- ZDO_RegisterForZDOMsg(
SerialApp_TaskID, Match_Desc_rsp );
- #if defined
(ZDO_COORDINATOR)//有三种设备不知怎样定义????
-
HalUARTWrite(SERIAL_APP_PORT,szWelcomeMsgCoor,sizeof(szWelcomeMsgCoor));
- #else
-
HalUARTWrite(SERIAL_APP_PORT,szWelcomeMsgRouter,sizeof(szWelcomeMsgRouter));
- #endif
-
-
- }
#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
-
- ZStatus_t ZDO_RegisterForZDOMsg( uint8
taskID, uint16 clusterID )
- {
- ZDO_MsgCB_t *pList;
- ZDO_MsgCB_t *pLast;
- ZDO_MsgCB_t *pNew;
-
- // Look for
duplicate
- pList = pLast =
zdoMsgCBs;
- while (
pList )
- {
- if
( pList->taskID == taskID
&& pList->clusterID ==
clusterID )
-
return ( ZSuccess );
- pLast =
pList;
- pList =
(ZDO_MsgCB_t *)pList->next;
- }
-
- // Add to the
list
- pNew = (ZDO_MsgCB_t
*)osal_mem_alloc( sizeof (
ZDO_MsgCB_t ) );
- if ( pNew
)
- {
- pNew->taskID =
taskID;
- pNew->clusterID
= clusterID;
- pNew->next =
NULL;
- if
( zdoMsgCBs )
- {
-
pLast->next = pNew;
- }
- else
- zdoMsgCBs
= pNew;
- return
( ZSuccess );
- }
- else
- return
( ZMemError );
- }
#define ZDO_CB_MSG
0xD3 // ZDO incoming message
callback
什么时候会调用下面这个函数呢???是在UINT16 SerialApp_ProcessEvent( uint8
task_id, UINT16 events )中
[cpp] view plaincopy
-
- static void
SerialApp_ProcessZDOMsgs( zdoIncomingMsg_t
*inMsg )
- {
- switch (
inMsg->clusterID )
- {
- case
End_Device_Bind_rsp:
-
if ( ZDO_ParseBindRsp(
inMsg ) == ZSuccess )
- {
-
// Light LED
-
HalLedSet( HAL_LED_4, HAL_LED_MODE_ON
);
- }
- #if defined(BLINK_LEDS)
-
else
- {
-
// Flash LED to show
failure
-
HalLedSet ( HAL_LED_4,
HAL_LED_MODE_FLASH );
- }
- #endif
-
break;
-
- case
Match_Desc_rsp:
- {
-
ZDO_ActiveEndpointRsp_t *pRsp =
ZDO_ParseEPListRsp( inMsg );
-
if ( pRsp )
-
{
-
if (
pRsp->status == ZSuccess &&
pRsp->cnt )
-
{
-
SerialApp_TxAddr.addrMode
= (afAddrMode_t)Addr16Bit;
-
SerialApp_TxAddr.addr.shortAddr =
pRsp->nwkAddr;
-
// Take
the first endpoint, Can be
changed to search through
endpoints
-
SerialApp_TxAddr.endPoint
= pRsp->epList[0];
-
-
// Light
LED
-
HalLedSet( HAL_LED_4,
HAL_LED_MODE_ON );
-
}
-
osal_mem_free( pRsp );
-
}
- }
-
break;
- }
- }
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
-
第一、pHalKeyProcessFunction这个函数指针为何指向了OnBoard_KeyCallback函数。
- 在HAL\Commen\
hal_drivers.c这个文件中,我们找到了HalDriverInit这个函数,在这个函数中,按键的初始化函数HalKeyInit被调用。在HalKeyInit中有这样的语句:
- {
-
pHalKeyProcessFunction = NULL;
- }
-
这说明在初始化以后pHalKeyProcessFunction并没有指向任何一个函数。那pHalKeyProcessFunction是什么时候被赋值的呢?
- 就在HalKeyInit的下方有一个这样的函数HalKeyConfig。其中有这样一条语句:
- pHalKeyProcessFunction = cback;
-
cback是HalKeyConfig所传进来的参数,所以,想要知道它所指向的函数,必须找到其调用的地方。经过简单的搜索我们不难找出答案。在main函数中有这样一个函数调用:InitBoard(
OB_READY );此函数中做了如下调用:
- {
-
HalKeyConfig( OnboardKeyIntEnable,
OnBoard_KeyCallback);
- }
- 第二、registeredKeysTaskID为什么标识了GenericApp这个任务?
-
由于OSAL是一个支持多任务的调度机制,所以在同一时间内将会有多个任务同时运行。但是从逻辑上来讲,一个事件只能由一个任务来处理。按键事件也不例外。
- 那么如何向OSAL声明处理按键事件的任务是GenericApp呢?
- 在GenericApp_Init(GenericApp的任务初始化函数)中有这么一个语句:
- {
-
RegisterForKeys( GenericApp_TaskID );
- }
-
RegisterForKeys函数向OSAL声明按键事件将由GenericApp任务来处理。在RegisterForKeys函数中:
- {
-
registeredKeysTaskID = task_id;
- }
说实话下面这句不懂??是回调函数,就是要向SerialApp任务发送按键消息。
[cpp] view plaincopy
-
- if (keys
&& (pHalKeyProcessFunction))
- {
- (pHalKeyProcessFunction)
(keys, HAL_KEY_STATE_NORMAL);
- }