|
本帖最后由 cyril3 于 2010-9-9 00:17 编辑
六,添加按键消息处理
要使我们的任务能够接收到按键消息,我们必须在应用程序框架做一个简单的注册,告诉应用程序框架,如果有按键消息的话,请传递过来。
在Cyril3AppCoordManage_Init函数中加入如下代码:
RegisterForKeys( Cyril3AppCoordManage_TaskID );
此函数就是执行了这样的注册。具体情况请参考《深入浅出Z-Stack 2006 OSAL多任务资源分配机制》。这样,按键消息就会被发送至Cyril3AppCoordManage这个任务。我们按照处理ZDO_STATE_CHANGE消息类似的方法添加如下代码:
case KEY_CHANGE:
Cyril3AppCoordManage_HandleKeys ( ( (keyChange_t *)MSGpkt)->keys );
break;
Cyril3AppCoordManage_HandleKeys的代码如下:
void Cyril3AppCoordManage_HandleKeys (byte keys )
{
if(keys & HAL_KEY_SW_6)
{
HalLedSet( HAL_LED_2, HAL_LED_MODE_TOGGLE );
}
}
在我的板子上只有一个六号按键,我们在按键处理函数中让LED2进行翻转。
七、发送与接收无线数据
我们先来讨论如何接收无线数据。
当我们的任务在ZigBee应用程序框架注册过后,我们的任务将会对应一个端点。当此端点接收到任务时,相对应的任务将会接收到一个AF_DATA_CONFIRM_CMD消息。注意:AF_DATA_CONFIRM_CMD是一个消息而不是一个事件。下面我们来加入对此消息的处理。
与ZDO_STATE_CHANGE类似,我们添加如下代码:
case AF_INCOMING_MSG_CMD:
Cyril3AppCoordManage_ProcessMSGData ( MSGpkt );
break;
然后我们来添加Cyril3AppCoordManage_ProcessMSGData函数:
void Cyril3AppCoordManage_ProcessMSGData ( afIncomingMSGPacket_t *msg )
{
switch ( msg->clusterId )
{
case CYRIL3APPMANAGE_CLUSTERID:
break;
}
}
当接收到无线数据的时候,我们需要判断此数据是属于哪一个簇的,这样可以让我们对不同簇的消息进行不一样的处理。现在我们暂时不去处理,稍后再说。
既然现在我们可以接收数据,那么当然我们也需要发送数据。发送无线数据,我们使用
afStatus_t AF_DataRequest( afAddrType_t *dstAddr, endPointDesc_t *srcEP,
uint16 cID, uint16 len, uint8 *buf, uint8 *transID,
uint8 options, uint8 radius )
这个函数。此函数并不是一个完整的发送函数,它只是向应用程序框架发出数据请求,数据的发送仍需传递至下层。所以,很多人会遇到这种情况:在单步调试程序时,执行过此语句后,接收端并没有接收到数据。这是因为执行完了这个函数只是完成了向应用框架发出数据请求,但是数据的发送仍然需要等待硬件调用的完成。
dstAddr:目标结点的地址。srcEP:目标结点的端点。cID:信息所属的簇。Len:发送的数据长度。Buf:数据所在的缓冲区。transID:消息ID,类似于计数器的的作用。Options:发送选项。Radius:一般保持默认AF_DEFAULT_RADIUS。当成功发出数据请求(请注意:仅仅是向应用程序框架发出请求)以后,函数返回afStatus_SUCCESS。
明白了此函数的作用和使用方法,我们就可以很容易地在我们的代码中加入无线数据发送功能。我们来完成这样一个功能:当路由节点(以下简称R结点)成功入网以后,向协调器结点(以下简称C结点)发送一个登记请求,然后C结点记录其网络地址并且分配给它一个ID,发送此ID给请求登记的R结点。R结点接收到此ID后保存起来。请注意:刚刚我们所说的“登记”、“ID”之类的事情都是我们自己在应用层上所定义的,与ZigBee协议无关。请不要混淆了。
首先,我们将App\Coordirator 组下的所有文件的内容,复制到App\Router组下的相对应的文件中。这样我们就为路由节点的应用程序快速地创建了代码。我们将三个文件中所有“Cyril3AppCoordManage”字样替换为“Cyril3AppRouterManage”。
其次,在R结点登记与C结点返回ID这个过程中,所有发送和接受的数据,我们可以将其归为一类。我们为此类创建一个簇。在App组的“Cyril3App.h”文件中,我们定义了“CYRIL3APPMANAGE_ CLUSTERID”这样一个簇。当时定义它的时候我们并不知道它的实际意义,现在我们将它的名字改为“CYRIL3APPMANAGE_REGISTER”。然后在代码中,我们也将相应的地方改过来,这里不再赘述。
要使R结点在加入网络后自动向C结点发送登记请求,我们需要在处理“ZDO_STATE_CHANGE”消息时完成。在Cyril3AppRouterManage_ProcessZDOStateChange函数中,我们删除点亮LED灯的代码,添加如下代码:
RegisterRequest_t cmd;
cmd.RegisterCMD = CYRIL3APPMANAGE_REGISTER_REQUEST;
afAddrType_t TargetAddr;
TargetAddr.addrMode = afAddr16Bit;
TargetAddr.addr.shortAddr = 0x0000;
TargetAddr.endPoint = CYRIL3APPMANAGE_ENDPOINT;
AF_DataRequest( &TargetAddr, &Cyril3AppRouterManage_epDesc,
CYRIL3APPMANAGE_REGISTER,
sizeof(RegisterRequest_t),
(byte *)&cmd,
&Cyril3AppRouterManage_TransID,
AF_DISCV_ROUTE, AF_DEFAULT_RADIUS );
RegisterRequest_t是登记请求的数据结构体,定义如下:
typedef struct regreq
{
uint8 RegisterCMD;
}RegisterRequest_t;
CYRIL3APPMANAGE_REGISTER_REQUEST是登记请求所代表的数值。我们定义如下命令:
#define CYRIL3APPMANAGE_REGISTER_REQUEST 0x01
#define CYRIL3APPMANAGE_REGISTER_CONFIRM 0x02
#define CYRIL3APPMANAGE_REGISTER_REFUSE 0x03
它们分别代表“请求”、“确认”、“拒绝”。这里我们将请求命令发送给了C结点(C结点网络地址为0x0000)。
在C结点,我们处理R结点的登记请求。在Cyril3AppCoordManage_ProcessMSGData函数中,我们判断出CYRIL3APPMANAGE_REGISTER簇时调用如下函数:
void Cyril3AppCoordManage_ProcessRegisterCMD(afIncomingMSGPacket_t *msg)
{
RegisterReply_t cmd;
afAddrType_t add;
add.addr.shortAddr=msg->srcAddr.addr.shortAddr;
add.addrMode=afAddr16Bit;
add.endPoint=CYRIL3APPMANAGE_ENDPOINT;
cmd.RegisterCMD = CYRIL3APPMANAGE_REGISTER_REFUSE;
cmd.NodeID = 0;
RegisterRequest_t *recv = (RegisterRequest_t *)msg->cmd.Data;
if( recv->RegisterCMD==CYRIL3APPMANAGE_REGISTER_REQUEST)
{
for(int i=0;i<= MAX_NODES-1;i++)
{
if(NodeList==0x0000)
{
NodeList=msg->srcAddr.addr.shortAddr;
cmd.RegisterCMD = CYRIL3APPMANAGE_REGISTER_CONFIRM;
cmd.NodeID = i+1;
HalLedSet ( HAL_LED_3, HAL_LED_MODE_BLINK );
break;
}
}
AF_DataRequest(&add,
&Cyril3AppCoordManage_epDesc,
CYRIL3APPMANAGE_REGISTER,
sizeof(RegisterReply_t),
(byte *)&cmd,
&Cyril3AppCoordManage_TransID,
AF_DISCV_ROUTE, AF_DEFAULT_RADIUS );
}
}
RegisterReply_t结构体表示登记请求的回复,定义如下:
typedef struct regrep
{
uint8 RegisterCMD;
uint8 NodeID;
}RegisterReply_t;
add.addr.shortAddr=msg->srcAddr.addr.shortAddr;获取请求登记的结点网络地址,然后作为目标地址发送回复数据。
当R结点接收到登记回复数据后做如下处理:
void Cyril3AppRouterManage_ProcessRegisterCMD(afIncomingMSGPacket_t *msg)
{
RegisterReply_t *cmd;
cmd = (RegisterReply_t *)msg->cmd.Data;
if(cmd->RegisterCMD == CYRIL3APPMANAGE_REGISTER_CONFIRM)
{
NodeID = cmd->NodeID;
HalLedSet( HAL_LED_1, HAL_LED_MODE_ON );
}
if(cmd->RegisterCMD == CYRIL3APPMANAGE_REGISTER_REFUSE)
{
HalLedSet( HAL_LED_1, HAL_LED_MODE_FLASH );
}
}
如果是确认命令,则记录下结点ID并且点亮LED1,表示登记确认。否则,LED1闪烁,表示登记失败。
这里有很多的宏和函数定义我并没有一一详细说明,一方面因为其太过于繁琐,另外一方面我认为讲述到这里我们应该有能力并且很清楚地知道我们应该在哪里(如何)定义。不过我依然将完整的代码发布出来。 |
|