查看: 17029|回复: 18

zstack 第一个例子

[复制链接]
mchen 发表于 2011-3-23 10:08:32 | 显示全部楼层 |阅读模式
1、        协议栈构架
首先打开程序代码,找到IAR工程 ,打开后可以看到TI ZStack的大体框架,如下图所示:

§ App:应用层目录,这也是用户创建各种不同工程的区域;
§ HAL:硬件层目录,包括着与硬件相关的配置及操作函数;
§ MAC:MAC层目录,包括着MAC层配置参数文件及MAC LIB库的函数接口文件;
§ MT:包括基于AF层的调试函数文件,主要包括串口等通信函数;§ NWK:网络层目录,包括着网络层配置参数文件及MAC LIB库的函数接口文件;
§ OSAL:系统目录,包括协议栈系统文档;
§ Profile:AF层目录,包括AF层处理函数文件;
§ Security:安全层目录,安全层处理函数,比如加密函数等;
§ Services:地址处理函数目录,包括着地址模式的定义及地址处理函数文档;Tools:工程配置目录,包括协议栈等配置文档;
§ ZDO ZDO ZDO ZDO:层目录,包括层处理函数文档;
§ ZMac:MAC层目录,包括MAC层参数配置及MAC层LIB库函数回调处理函数
§ ZMain:主函数目录,包括入口函数及硬件配置文件;
§ Output:输出文件目录,这是EW8051IDE自动生成的;

2、        程序的编译和下载
(1)        项目属性设置
选择菜单Project->Options、右击菜单options或者通过热键(ALT+F7)打开工程属性设置。

也可以鼠标右击workspace中的工程名,如下图:


打开属性设置后如图:

一般来说,如果是在TI的协议栈的进行修改,里面的设置就不用修改。如果要具体了解各参数,请参照IAR文档。
这里值得注意的是chipcon下的Download的标签,如果第一次用建议像如图那样选择,这样是将整个FALSH擦除后再Download,写完后最好用

软件来更改芯片的IEEE地址,否则可能带来你的程序无法运行。

具体见Zmain.c函数中的如下程序段:
led = HAL_LED_MODE_OFF;
  
  while ( HAL_KEY_SW_5 == HalKeyRead() )
  {
    MicroWait( 62500 );
    HalLedSet( HAL_LED_1, led^=HAL_LED_MODE_ON );  // Toggle the LED
    MicroWait( 62500 );
  }

  HalLedSet( HAL_LED_1, HAL_LED_MODE_OFF );

如果你使用的硬件平台没有对这个按键5进行设置,或者就没有这个按键5,这段将过不去,LED1灯将一直闪。
如果你的系统没有按键,建议将上面的语句注释掉。
(2)        编译与烧写
选择Project->Debug或者热键(Ctrl+D)给开发板上的Zigbee模块下载程序。
也可以直接点IAR的如下图标来编译和下载程序 ,最后一个是编译并下载;
同时也可以在workspace中的工程名上点击鼠标右键来选择编译。
下图显示为DEBUG时的选项:

在调试程序时,DEBUG还是非常有用的,它能告诉你程序运行到哪了,程序为什么没有出现你预期的结果等,因此,选择一个仿真器还是有用的,并且有些仿真器加上一个节点就可以充当packet sniffer工具,对空中的包能进行实时跟踪。下图是在宁波中科仿真器抓到的本例子的数据包,以后会对这些包结构做一定解析,如果也可以参考ZigBee协议栈文档,上面对这些包结构有详细的说明。

从图中的RSSI中可以看到节点的性能,负数越接近0表明节点性能越好,通信距离越长,并且协调器已经给一个终端节点分配了短地址0x796F(该地址可以通过计算得到,见IEEE 802.15.4文档)。
3、        实验程序简单分析
在本实验中用户涉及的程序主要有OSAL_SendTest.c,SendTest.c,SendTest.h。其他程序协议栈程序只做简单修改就行。比如我使用的是GAINST-CC2430需要修改一下串口函数等。因为硬件平台的差异性,大家可以根据实际进行修改。下面主要介绍上面提到的三个函数。
OSAL_SendTest.c函数是协议栈操作系统处理函数,这个函数实现对本实验中需要的任务的添加,具体函数如下:
osalTaskAdd( nwk_init, nwk_event_loop, OSAL_TASK_PRIORITY_MED );
osalTaskAdd( APS_Init, APS_event_loop, OSAL_TASK_PRIORITY_LOW );
osalTaskAdd( ZDApp_Init, ZDApp_event_loop, OSAL_TASK_PRIORITY_LOW );
osalTaskAdd( SendTest_Init, SendTest_ProcessEvent, OSAL_TASK_PRIORITY_LOW );

最后一条语句是添加本实验任务的。osalTaskAdd()函数的参数请参照OSAL API_F8W-2003-0002_.PDF文档。
SendTest.c是本实验用户程序的具体实现。下面来分析一下该程序。
首先,本程序不考虑协议栈绑定等相关内容,在程序中屏蔽了绑定,实验数据只是从终端节点传送到协调器节点,对于ZIGBEE网络协调器节点的短地址始终为0x0000,因此在用发送函数时,目的地址写上0x0000就能传到协调器。
其次,两个设备的 endPoint要保持一致,否则将不能通信。在程序中将SendTest_DstAddr.endPoint = SendTest_ENDPOINT;(SendTest_ENDPOINT=10)。
程序实现结果是:协调器的串口上输出如图内容:

注意,要使用该串口助手才能显示中文,其他的串口可能不支持。
只有在终端节点加入网络后才能发送数据给协调器,一旦网络中断数据就不再发,这个机制是通过判断网络状态来实现的,代码如下:
if ( (SendTest_NwkState == DEV_END_DEVICE) ) //只在设备为终端节点时,才发送数据
          {
            // Start sending "the" message in a regular interval.
            osal_start_timer( SendTest_SEND_MSG_EVT,
                              SendTest_SEND_MSG_TIMEOUT );
          }
上面语句中SendTest_SEND_MSG_EVT是一个事件,只在定时器时间到才触发该事件,在程序中设置
#define SendTest_SEND_MSG_TIMEOUT   1000
也就是在网络建成后每一秒向协调器发送数据。
对于操作系统的介绍请参考文档,以后我也会就这个做些介绍。
SendTest_ProcessEvent函数为操作系统的一任务(上面已经介绍了怎么加入任务),总是被周期性轮询。当检测到一个event就执行相关程序。
在一个任务中有16个事件,用16位来表示,每一位代表一个事件,其中0x8000为系统事件SYS_EVENT_MSG,任务还和task_id有关。
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SendTest_TaskID );
程序段是将收到的消息存放到MSGpkt指定的区域中。
while ( MSGpkt )//消息不为空
{ switch ( MSGpkt->hdr.event )
      {

        case KEY_CHANGE://按键状态改(本实验不涉及)
          SendTest_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys );
          break;

        case AF_DATA_CONFIRM_CMD://AF层数据发送完成后确认报告
          // This message is received as a confirmation of a data packet sent.
          // The status is of ZStatus_t type [defined in ZComDef.h]
          // The message fields are defined in AF.h
          afDataConfirm = (afDataConfirm_t *)MSGpkt;
          sentEP = afDataConfirm->endpoint;
          sentStatus = afDataConfirm->hdr.status;
          sentTransID = afDataConfirm->transID;
          (void)sentEP;
          (void)sentTransID;
          HAL_TOGGLE_LED1();//加入LED1来指示数据发送出
          // Action taken when confirmation is received.
          if ( sentStatus != ZSuccess )
          {
            // The data wasn't delivered -- Do something
          }
          break;

        case AF_INCOMING_MSG_CMD://新的报文来
          HAL_TOGGLE_LED1();//加入LED1来指示新报文来
          SendTest_MessageMSGCB( MSGpkt );//新报文回调函数
          break;

        case ZDO_NEW_DSTADDR://ZDO终端地址改变,这个是在加入绑定后引起的,本实验不涉及
          ZDO_NewDstAddr = (ZDO_NewDstAddr_t *)MSGpkt;

          dstEP = ZDO_NewDstAddr->dstAddrDstEP;
          dstAddr = &ZDO_NewDstAddr->dstAddr;
          SendTest_DstAddr.addrMode = (afAddrMode_t)dstAddr->addrMode;
          SendTest_DstAddr.endPoint = dstEP;

          SendTest_DstAddr.addr.shortAddr = dstAddr->addr.shortAddr;
          break;

        case ZDO_STATE_CHANGE://网络状态改变,在这里启动第一次数据传输
          SendTest_NwkState = (devStates_t)(MSGpkt->hdr.status);
          if ( (SendTest_NwkState == DEV_END_DEVICE) ) //只在设备为终端节点时,才发送数据
          {
            // Start sending "the" message in a regular interval.
            osal_start_timer( SendTest_SEND_MSG_EVT,
                              SendTest_SEND_MSG_TIMEOUT );
          }//这个在上面已经有介绍
          break;

        default:
          break;
      }

      // Release the memory
      osal_msg_deallocate( (uint8 *)MSGpkt );

      // Next
      MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SendTest_TaskID );
}


if ( events & SendTest_SEND_MSG_EVT )//如果事件为消息传送事件
  {
    // Send "the" message
    SendTest_SendTheMessage();//调用消息发送函数

    // Setup to send message again
    if ( (SendTest_NwkState == DEV_END_DEVICE) )//解释同前
    osal_start_timer( SendTest_SEND_MSG_EVT,
                      SendTest_SEND_MSG_TIMEOUT );

    // return unprocessed events
    return (events ^ SendTest_SEND_MSG_EVT);
  }
消息回调函数实现:
void SendTest_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
  switch ( pkt->clusterId )
  {
    case SendTest_CLUSTERID:
      // "the" message
#if defined( LCD_SUPPORTED )
      HalLcdWriteScreen( (char*)pkt->cmd.Data, "rcvd" );
#elif defined( WIN32 )
      WPRINTSTR( pkt->cmd.Data );
#endif
      HalUARTWrite( HAL_UART_PORT_1,(pkt->cmd).Data, (pkt->cmd).DataLength);//将接收到的数打印到串口上
      break;
  }
}
对于串口的初始化在SendTest_Init函数中,上面的红色显示函数就是将收到的数据信息打印在串口终端上。
下面介绍一下,发送函数AF_DataRequest
该函数用于发送数据
函数声明:
afStatus_t AF_DataRequest( afAddrType_t *dstAddr, endPointDesc_t *srcEP,
uint16 cID, uint16 len, uint8 *buf, uint8 *transID,
uint8 options, uint8 radius );
具体参数:
dstAddr:目的地址指针,其中的地址模式是:afAddrNotPresent(用于绑定)、afAddrGroup(传送到一组目的节点)afAddrBroadcast(广播发送)、afAddr16Bit(直接发送),在本实验中afAddr16Bit地址模式,在SendTest_Init初始化中将SendTest_DstAddr.addrMode 设置为(afAddrMode_t)Addr16Bit;
srcEP:发送的endpoint的Endpoint描述的指针
cID:Cluster ID,实验中使用SendTest_CLUSTERID
len:发送数据包长度,该长度不包括ZIGBEE包中的帧头和帧尾,只是用户数据的长度
buf:用户发送数据区指针
transID:传输序号指针,该序号将随发送的次数增加而增加
options:具体参数参照下表:

Radius:最大跳数,协议栈默认值为10,可以根据实际情况修改,见nwk.h文件中
// the default network radius set twice the value of <nwkMaxDepth>
#define DEF_NWK_RADIUS 10

本帖子中包含更多资源

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

x
outman 发表于 2011-3-23 22:08:55 | 显示全部楼层
写得很详细,楼主辛苦了。
已经加了5点威望,并为您升级为vip会员了,多谢您对飞比的支持!
tracy 发表于 2011-4-27 12:20:33 | 显示全部楼层
请教一下,我对SampleApp工程进行调试,在SampleApp_MessageMSGCB函数中加入HalUARTWrite()函数,用意是把无线接收到的信息发送到串口中,但是串口调试助手没有接收到,请问有人知道什么原因吗?

本帖子中包含更多资源

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

x
刀锋 发表于 2011-4-27 21:04:21 | 显示全部楼层
可以用memcpy(RxBuf.pkt->Data,pkt->cmy.Datalength);获取物理地址
如果判别是路由器可以用Uart_Send_String(RxBuf,pkt->cmy.Datalength)通过串口发送出去得。
我只是建议而已。。。。呵呵~~
xiaoyou1314 发表于 2011-5-7 13:18:53 | 显示全部楼层
表示膜拜,先顶后学
homphie 发表于 2011-5-9 20:44:28 | 显示全部楼层
很不错的一个教程,看完很有启发啊
roderick0307 发表于 2011-5-19 11:37:24 | 显示全部楼层
回复 4# tracy


    这个问题你现在解决了吗?在cc2430上,我已经解决了,但是在CC2530上还有点问题!可以交流一下,我的qq号:1464743419
roderick0307 发表于 2011-5-19 11:38:53 | 显示全部楼层
亲爱的楼主,您做的这个是CC2430的吧? 2530的串口配置是什么样的?
约会花 发表于 2011-7-22 17:09:54 | 显示全部楼层
楼主辛苦了!
toddhan 发表于 2011-8-16 13:00:55 | 显示全部楼层
MARK!谢谢分享
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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