查看: 60905|回复: 109

基于Z-Stack2006的简单应用开发

[复制链接]
cyril3 发表于 2010-9-9 00:03:12 | 显示全部楼层 |阅读模式
本帖最后由 cyril3 于 2010-9-9 00:38 编辑

    例程中的代码:  
  我想很多人都会对“Z-Stack 2006”这个说法产生怀疑。没错!在Z-Stack的所有版本中,并没有“Z-Stack 2006”这个版本。在这里我所指的是Z-Stack针对ZigBee 2006协议的版本。而我所使用的是Z-Stack 1-4-3版本。
  我们所熟知的是,Z-Stack是基于OSAL的支持多任务的ZigBee协议栈,在《深入浅出Z-Stack 2006 OSAL多任务资源分配机制》中,我向大家简要分析了OSAL的运行机制。但是对于开发一个满足需求的应用,了解这样一个机制仅仅是一个开始。下面,我将通过一个简单实例,向大家介绍如何进行基于Z-Stack的应用开发。
一、建立工程
  我认为反复地使用GenericApp是一件十分恶心的事情。这一次,我们需要拥有一个属于我们自己的工程。
  使用IAR建立一个Z-Stack工程是一件十分繁琐并且枯燥的事情,每一层都有太多的文件需要被添加进去。所幸的是,我们有一个很好的投机取巧的办法。
  找到GenericApp工程的文件夹,复制此文件夹并且重命名Cyril3App。删除Source目录下的所有文件,然后建立如下几个文件:
  Cyril3App.h:定义应用程序全局宏
  Cyril3AppCoordManage.c:协调器代码
  Cyril3AppCoordManage.h:协调器宏定义
  OSAL_Cyril3AppCoord.c:协调器应用框架代码
  Cyril3AppRouterManage.c:路由器代码
  Cyril3AppRouterManage.h:路由器宏定义
  OSAL_Cyril3AppRouter.c:路由器应用框架代码
  进入CC2430DB目录,分别将GenericApp.ewp和GenericApp.eww重命名为Cyril3App.ewp和Cyril3App.eww。使用记事本打开这两个文件,将里面所有的GenericApp都改为Cyril3App。我们可以使用查找替换功能来解决。用IAR打开这个工程,将App组里面的文件全部移除。在App组里面新建“Coodinator”和“Router”两个组。分别将路由器和协调器的三个文件加入对应的两个组。这样,一个空的工程就建立好了。
  接下来我们就可以在这个工程里面开发我们的应用了。
  抛开实用性,我们仅仅来关注技术层面所发生的事情。所以,请大家不要来指责我这个东西没有任何实用性,我只是抛砖引玉。功能更加强大,更实用的应用程序需要大家去完成。
  既然OSAL可以支持多任务,那么我们就为我们的应用设计两个任务。第一个任务在应用层面上为我们管理整个网络的节点,第二个任务我们来做一些简单的操作。接下来我们要干点实事了。
二、建立应用程序。
  OSAL框架在背后为我们做了很多我们不容易发现却很重要的事情,但是即使这样,我们仍然需要自己做一些事情来完成这个应用程序框架。切换工作空间配置到CoordinatorDB,将Router组排除编译(右键单击组名,选择Options,将左上角的“Exclude from build”的勾选去除)。打开“OSAL_Cyril3AppCoord.c”在这个文件里面,我们完成OSAL应用程序框架的最后一些事情。
  1,添加如下代码:

  const pTaskEventHandlerFn tasksArr[] =
  {
    macEventLoop,
    nwk_event_loop,
    Hal_ProcessEvent,
    APS_event_loop,
   ZDApp_event_loop,
  };
  uint16 *tasksEvents = NULL;
  const uint8 tasksCnt = sizeof(tasksArr) / sizeof(pTaskEventHandlerFn);

  此代码包含了OSAL的两个十分重要的全局变量。tasksArr保存了当前应用程序中所有的任务处理函数,当某个任务有需要响应的事件时,此任务处理函数将会被调用。当然每一个任务都只有一个任务处理函数,所以任务处理函数可以标识一个任务。tasksEvents记录了当前所有任务的状态,它的初始值为0,当某个任务接收到某个事件时,相对应的位会置1。由于任务的数量一个不确定的值,所以tasksEvents的空间应该是动态分配的。这里我们仅仅定义了一个这样的指针,稍后再为它分配空间。tasksCnt记录了任务数量。
  2,设计我们的应用程序任务初始化函数:
  在OSAL_Cyril3.c中添加如下函数,

  void osalInitTasks( void )
  {
    uint8 taskID = 0;
    tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
   osal_memset( tasksEvents, 0, (sizeof( uint16 ) *tasksCnt));
  
   macTaskInit( taskID++ );
   nwk_init( taskID++ );
   Hal_Init( taskID++ );
    APS_Init( taskID++ );
   ZDApp_Init( taskID++ );
  }

  在这个函数中,我们为tasksEvents分配好了足够大的空间,并且将它们全部初始化为0。然后调用所有已经存在的任务初始化函数,为他们依次递增地分配任务ID。
  在OSAL中,为了保证OSAL的正常运行,已经有了很多任务。我们仅仅按照如上方法添加就好。
  到此,我们的应用程序框架就完成了,但是这样一个程序没有任何功能,我们需要添加我们自定义的任务。
三、添加任务
  我们的第一个任务的基本功能是管理网络上的节点。在《深入浅出Z-Stack 2006 OSAL多任务资源分配机制》中,我已经比较详细的介绍了添加任务的方法,下面我们就来具体实现。
  打开Cyril3AppCoordManage.c源文件,添加如下代码:

  byte Cyril3AppCoordManage_TaskID;
  void Cyril3AppCoordManage_Init( byte task_id )
  {
    Cyril3AppCoordManage_TaskID = task_id;
  }

  UINT16 Cyril3AppCoordManage_ProcessEvent( byte task_id, UINT16 events )
  {
   return 0;
  }

  在任务初始化函数中,我们仅仅将分配给任务的ID保存下来。在事件处理函数中,我们先什么也不干。简单的返回0代表所有的事件已经处理完毕。不过即使这样,谁也不能否认它是一个有效的事件处理函数。接着,我们在tasksArr数组里面加入Cyril3AppCoordManage_ProcessEvent这个事件处理函数,然后在osalInitTasks函数中调用任务的初始化函数。需要注意的是,tasksArr数组里面各任务事件处理程序的排列顺序,必须要与osalInitTasks所调用任务初始化函数的顺序一致。
我们第一个任务的添加到此就完成了。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
 楼主| cyril3 发表于 2010-9-9 00:03:32 | 显示全部楼层
本帖最后由 cyril3 于 2010-9-9 00:16 编辑

四、加入网络
  在把我们的设备加入网络之前,我需要介绍几个概念。
  1,IEEE地址,也叫64位长地址。在网络中,每一个节点都是一个具体的物理设备。每个物理设备都是独一无二的。如何从物理上标识网络上的设备呢?我们引入了IEEE这样一个地址。IEEE地址是一个64位地址,这个地址是直接烧写进FLASH里面的,它不会因为程序的运行而改变,每个设备都有一个独一无二的IEEE地址,从而从物理上对设备加以区分。在PC机上,每个网卡都有一个世界独一无二的MAC地址,道理是一样的。
  2,网络地址,也叫16位短地址。当设备加入网络中以后,为了在这个网络中对节点进行标识,网络会分配给设备一个16位短地址,以此来标识此设备在网络中的地位。类似于我们常说的IP地址。
  3,ENDPOINT。很多资料将其翻译为“端点”,我们不如也这么叫。不过问题的关键不是它如何称呼,而是如何认识它。我们来研究这样一个事实:outman在看我的帖子的同时他又使用QQ和别人聊天。假设他的电脑IP地址为192.168.1.2。那么当他的QQ好友向他发送了一句话的时候,这个信息里面包含了目的IP地址,所以通过TCP/TP协议可以到达outman的电脑。但是问题随之而来。当outman电脑上的操作系统接收到此条信息时,它将把这个信息交给浏览器(我们刚才说了,他在看帖子,所以肯定开着浏览器)呢,还是交给QQ?操作系统通过怎么样的方法作出裁决呢?显然,只通过IP地址是没有办法决定的,所以这条消息除了包含IP地址以外,还要告诉目的机,这条消息应该交由哪个应用程序来处理。于是端口(Port)的概念产生了。操作系统为应用程序提供了很多端口,消息由IP地址到达操作系统,再由端口找到处理消息的应用程序。同样的道理,在ZigBee的应用程序框架里(结构图请看《深入浅出Z-Stack 2006 OSAL多任务资源分配机制》)包含了最多240个应用程序对象,每个应用程序对象在OSAL中对应了一个任务,当网络层接收到信息以后如何决定将此信息传递给哪个任务呢?ENDPOINT决定了传递方向,于是我们可以说ENDPOINT的作用与TCP/IP协议中的端口的作用是一样的。
  4,Cluster。中文翻译为“簇”。我们如何去理解“簇”这个概念呢?当一个任务接收到消息(这里所说的消息是指无线网络之中的数据)之后,一定会对消息进行处理。但是我们的应用肯定不会盲目的处理所有消息,消息一定会被分门别类。“簇”代表了消息的类型,为与相同簇的消息具有相同的类型,而这个类型可以被用户自定义。
  了解了这些,我们可以考虑如何将设备加入网络中。
  在我们的设备加电运行以后,设备会对网络发送广播消息请求入网,协调器确认其入网请求以后会给它分配网络地址,此设备将会作为网络中的一个节点运行。
  设备加入网络由协议栈自动完成,然而将任务作为一个ZigBee应用程序对象加入到ZigBee的应用程序框架中是如何完成的呢?
  要将任务加入网络需要在任务重做一下两件事情:
  1,定义此任务作为ZigBee应用程序对象所需要的信息。
  2,将这些信息注册到应用程序框架中。
  接下来,我们就在我们的例子中实现它。
  在Cyril3App.h中定义如下宏:
  #define CYRIL3APPMANAGE_ENDPOINT                  15
  #define CYRIL3APPMANAGE_PROFID                     0x0005
  #define CYRIL3APPMANAGE_DEVICEID                   0x0001
  #define CYRIL3APPMANAGE_DEVICE_VERSION            0
  #define CYRIL3APPMANAGE_FLAGS                     0

  #define CYRIL3APPMANAGE_MAX_CLUSTERS                        1
  #define CYRIL3APPMANAGE_CLUSTERID                          1

  CYRIL3APPMANAGE_ENDPOINT定义了当前任务的端点,CYRIL3APPMANAGE_PROFID和CYRIL3APPMANAGE_DEVICEID是一些有关识别的定义,在学习实验中,我们可以任意取值。CYRIL3APPMANAGE_DEVICE_VERSION和CYRIL3APPMANAGE_FLAGS代表了硬件版本和自定义标志,我们定义为0,不影响我们的应用开发。CYRIL3APPMANAGE_MAX_CLUSTERS定义了当前任务的簇数量。CYRIL3APPMANAGE_CLUSTERID是我们自定义的一个簇,ID取为1。
  在Cyril3AppCoordManage.c中,我们定义如下三个全局变量:

  const uint16 Cyril3AppCoordManage_ClusterList[CYRIL3APPMANAGE_MAX_CLUSTERS] =
  {
    CYRIL3APPMANAGE_ CLUSTERID
  };

  endPointDesc_t Cyril3AppCoordManage_epDesc;

  const SimpleDescriptionFormat_t Cyril3AppCoordManage_SimpleDesc =
  {
    CYRIL3APPMANAGE_ENDPOINT,
   CYRIL3APPMANAGE_PROFID,
   CYRIL3APPMANAGE_DEVICEID,
    CYRIL3APPMANAGE_DEVICE_VERSION,
    CYRIL3APPMANAGE_FLAGS,
    CYRIL3APPMANAGE_MAX_CLUSTERS,
   (uint16 *)Cyril3AppCoordManage_ClusterList,
   CYRIL3APPMANAGE_MAX_CLUSTERS,
   (uint16 *)Cyril3AppCoordManage_ClusterList
  };
  Cyril3AppCoordManage_ClusterList保存了一个簇ID的列表,所有有关于此任务的簇都需要在此定义。
  Cyril3AppCoordManage_epDesc是此任务的端点描述,它定义了此任务作为ZigBee应用程序对象的所有信息,我们将会在初始化函数中为它赋值。然后将他注册到应用程序框架中。
  Cyril3AppCoordManage_SimpleDesc是此任务的简单描述,它保存了此任务的一些基本信息。具体赋值我不再赘述。
  在任务初始化函数中,我们为端点描述赋值,并且注册到应用程序框架中。

    Cyril3AppCoordManage_epDesc.endPoint = CYRIL3APPMANAGE_ENDPOINT;
   Cyril3AppCoordManage_epDesc.task_id = &Cyril3AppCoordManage_TaskID;
   Cyril3AppCoordManage_epDesc.simpleDesc= (SimpleDescriptionFormat_t *)&Cyril3AppCoordManage_SimpleDesc;
   Cyril3AppCoordManage_epDesc.latencyReq = noLatencyReqs;
   afRegister( &Cyril3AppCoordManage_epDesc );

  这样,我们的任务就顺利地作为ZigBee应用程序对象被加入到了应用程序框架中。这意味着我们可以处理来自网络层的消息了。
五、处理消息
  下面,我们来试图实现这样一个功能:当设备加入网络之后,点亮一个LED灯,表示该节点成功加入网络,并处于就绪状态。
  当设备成功加入网络以后,ZigBee设备对象(ZDO)会给所有已经在应用程序框架中注册过的任务(也就是作为ZigBee的应用程序对象存在的任务)发送一个“ZDO_STATE_CHANGE”消息,消息的状态指示了当前网络的状态。我们可以通过处理此消息时点亮LED等来实现这个功能。
  当一个消息被发送给任务时,“SYS_EVENT_MSG”事件会被传递给任务。“SYS_EVENT_MSG”这个事件通知任务有一个消息等待处理。所以,我们必须添加对于“SYS_EVENT_MSG”这个事件的响应,然后从消息队列中取出消息,判断此消息是否为“ZDO_STATE_CHANGE”。在处理“ZDO_STATE_CHANGE”消息时点亮LED灯。
  在Cyril3AppCoordManage.c中我们定义如下全局变量:

  devStates_t Cyril3AppCoordManage_NwkState=DEV_INIT;

  Cyril3AppCoordManage_NwkState保存了当前节点的网络状态,我们将他初始化为“未加入网络”。

  在Cyril3AppCoordManage_ProcessEvent函数中,我们添加如下代码:

   if ( events & SYS_EVENT_MSG )
   {
     afIncomingMSGPacket_t *MSGpkt;
      MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( Cyril3AppManage_TaskID );
      while ( MSGpkt )
     {
        switch ( MSGpkt->hdr.event )
        {
          case ZDO_STATE_CHANGE:
            Cyril3AppCoordManage_NwkState = (devStates_t)MSGpkt->hdr.status;
            if ( Cyril3AppCoordManage_NwkState == DEV_ZB_COORD )
            {
              HalLedSet( HAL_LED_1, HAL_LED_MODE_ON );
              HalLedSet ( HAL_LED_2, HAL_LED_MODE_OFF );
              HalLedSet ( HAL_LED_3, HAL_LED_MODE_OFF );
              Cyril3AppCoordManage_ProcessZDOStateChange(
                                                                                                (devStates_t)MSGpkt->hdr.status);
            }
            break;
         default:
            break;
        }
        osal_msg_deallocate( (uint8 *)MSGpkt );
        MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( Cyril3AppManage_TaskID );
      }
      return (events ^ SYS_EVENT_MSG);
    }

  osal_msg_receive获取消息队列上相应任务的消息。osal_msg_deallocate释放消息所在的消息缓冲区,回收内存资源。
  还是在Cyril3AppCoordManage.c中我们添加Cyril3AppCoordManage_ProcessZDOStateChange函数:

  void Cyril3AppCoordManage_ProcessZDOStateChange(devStates_t state)
  {
     HalLedSet( HAL_LED_1, HAL_LED_MODE_ON );
  }

  运行我们的代码,我们将可以看到在开机稍微延迟一会之后,LED1被点亮了。这说明成功地响应了“ZDO_STATE_CHANGE”消息。
 楼主| cyril3 发表于 2010-9-9 00:03:50 | 显示全部楼层
本帖最后由 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闪烁,表示登记失败。
  这里有很多的宏和函数定义我并没有一一详细说明,一方面因为其太过于繁琐,另外一方面我认为讲述到这里我们应该有能力并且很清楚地知道我们应该在哪里(如何)定义。不过我依然将完整的代码发布出来。
 楼主| cyril3 发表于 2010-9-9 00:04:09 | 显示全部楼层
本帖最后由 cyril3 于 2012-1-6 14:43 编辑

占楼待续。。。。。。
 楼主| cyril3 发表于 2010-9-9 00:04:29 | 显示全部楼层
占楼待序。。。
xingqing 发表于 2010-9-9 00:10:50 | 显示全部楼层
楼主思路真是清晰  我有一个小小的请求 楼主能不能在实际的代码中添加下   然后把代码共享出来 嘿嘿
 楼主| cyril3 发表于 2010-9-9 00:44:50 | 显示全部楼层
回复 6# xingqing


    呵呵,就怕你这种手快的回帖,呵呵。我先占楼了
xingqing 发表于 2010-9-9 11:27:41 | 显示全部楼层
回复 7# cyril3


    没事  让坛主把我的帖子删掉了吧  嘿嘿 这样你的大作就有连续性了  我中间这么插过来一句  影响大家的视觉 呵呵
xingqing 发表于 2010-9-9 11:29:20 | 显示全部楼层
回复 8# xingqing


    楼主的精神我很是佩服  啥都不说了  顶了
leewei 发表于 2010-9-9 18:50:09 | 显示全部楼层
顶 cyril3大神的大作
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表