Zstack协议栈(三)

ZStack 串口操作

​ 串口是开发板和用户电脑互交的一种工具,正确地使用串口对于 Zigbee 无线网络的学习和使用具有较大的促进作用,使用串口的基本步骤:
​ ①初始化串口,包括设置波特率、终端等
​ ②向串口缓冲区写入发送数据或从串口缓冲区读取数据

​ 上述方法是使用串口的常用方法,但是由于 ZStack 协议栈的存在,使得穿偶的使用略有不同在 ZStack 协议栈中已经对串口初始化所需要的函数进行了实现,用户只需要传递几个参数就可以使用串口,此外,Zigbee 协议栈还实现了串口的读取函数和写入函数。因此,用户在使用串口是,只需要掌握 ZStack 协议栈提供的串口操作相关的三个函数即可。
ZStack 协议栈中提供的与串口操作有关的三个函数为:

uint8 HalUARTOpen(uint8 port, halUARTCfg t *config);
uint16 HalUARTRead(uint8 port, uint8 *buf, uint16 len);
uint16 HalUARTWrite(uint8 port, uint8 *buf, uint16 len);

具体操作:

1)首先在 HelloApp 应用层初始化函数中添加串口相关代码,负责配置串口和打开串口功能。通过给 halUARTCfg t结构体变量赋值,配置串口的使能、波特率、流控、回调、读写等参数,然后调用 HalUARTOpen()函数打开串口即可。

void ChatApp_Init( byte task id )
{
halUARTCfg_t_uartConfig;
ChatApp_TasklD = task id;
ChatApp_NwkState = DEV_INIT;
ChatApp_TransID = 0;
ChatApp_DstAddr.addrMode = (afAddrMode_t)AddrNotPresent;
ChatApp_DstAddr.endPoint = 0;
ChatApp_DstAddr.addr.shortAddr = 0;

ChatApp_epDesc.endPoint = ChatApp_ENDPOINT;
ChatApp_epDesc.task_id = &ChatApp_TaskID;
ChatApp_epDesc.simpleDesc
=(SimpleDescriptionFormat_t *)&ChatApp_SimpleDesc;
ChatApp_epDesc.latencyReq = noLatencyReqs;
// Register the endpoint description with the AF
afRegister( &ChatApp_epDesc );
//配置串口并打开
uartConfig.configured = TRUE;//使能串口
uartConfig.baudRate = HAL_UART_BR_115200;//波特率 115200
uartConfig.flowControl = FALSE://关闭流控
uartConfig.callBackFunc = ChatApp_rxCB; //回调函数
HalUARTOpen(HAL_UART_PORT_0,&uartConfig); //打开串口
}

2)串口回调函数主要功能是获取用户串口输入的数据,并手动触发 ChatApp_UART_RXCB_EVT 用户自定义事件。

uint16_RxLen;//串口接收数据长度
uint8 UartDataBuf[128];//串口数据缓存区指针
... ...
static void ChatApp_rxCB(uint8 port, uint8 event)
{
if ((event & (HAL_UART_RX_FULL | HAL_UART_RX_ABOUT_FULL | 
HAL_UART_RX_TIMEOUT)))
{
RxLen = Hal UART_RxBufLen(HAL UART PORT 0);//接收缓冲区数据长度,字节为单位
HalUARTRead( HAL_UART_PORT_0,&UartDataBuf[0],RxLen);//读接收缓冲区数据到内存
osal_set_event(ChatApp_TaskID,ChatApp_UART_RX_CB_EVT);//有串口数据时产生相应事件
}
}

3)在事件处理函数的 ChatApp_UART_RX_CB_EVT 事件部分,首先通过串口打印出要发送的内容,然后再调用 ChatApp_SendTheMessage()函数将这些内容通过无线发送出去。
同时,当节点接收到无线数据时会触发 AF_INCOMING_MSG_CMD 系统事件,它会去调用ChatApp_MessageMSGCB()函数对无线数据进行处理。

UINT16 ChatApp_ProcessEvent( byte task_id, UINT16 events )
{
aflncomingMSGPacket_t *MSGpk;
afDataConfirm_t *afDataConfirm;
... ...
if ( events & SYS_EVENT_MSG )
{
MSGpkt = (aflncomingMSGPacket_t *)osal_msg_receive( ChatApp_TaskID);
while ( MSGpkt )
{
switch ( MSGpkt->hdr.event )
{
case ZDO_CB_MSG:
	break;
case KEY CHANGE:
	break;
case AF_DATA_CONFIRM_CMD:
... ...
	break;
case AF_INCOMING_MSG_CMD://接收到无线消息时处理
	ChatApp_MessageMSGCB(MSGpkt);
	break;
case ZDO_STATE_CHANGE:
	ChatApp_NwkState = (devStates_t)(MSGpkt->hdr.status);
	if ( (ChatApp_NwkState == DEV_ZB_COORD)
		||(ChatApp_NwkState == DEV_ROUTER)
		||(ChatApp_NwkState == DEV_END_DEVICE) )
	{
	// 加入网络成功,点亮 LED2
	HalLedSet( HAL_LED_2, HAL_LED_MODE_ON );
	}
	break;
default:
	break;
}
// Release the memory
osal_msg_deallocate( (uint8 *)MSGpkt );
//Next
MSGpkt = (aflncomingMSGPacket_t *)osal_msg_receive( ChatApp_TaskID );
}
// return unprocessed events
return (events ^ SYS_EVENT_MSG);
}
// action UART RX CB EVENT events
if ( events & ChatApp_UART_RX_CB_EVT )
{
HalUARTWrite(HAL_UART_PORT_0,&UartDataBuf[0], RxLen);//串口显示发送内容
ChatApp_SendTheMessage(&UartDataBuf[0], RxLen);//无线发送消息
return (events ^ ChatApp_UART_RX_CB_EVT);
}
return 0;
}

以上的两个用户自定义事件的宏需要自己去声明,注意它们的值应与系统定义的值区分开来并不能超过 16 位。

//可以在 ChatApp.h 文件中定义自定义事件:
#define ChatAPP_SEND_MSG_EVT 0x0001 //上章定时器触发的消息发送事件
#define ChatApP_UART_RX_CB_EVT 0x0002 //本章串口触发的消息发送事件
//系统已定义的事件位于 ZComDef.h 文件中:
#define SPI_INCOMING_ZTOOL_PORT 0x21 // Raw data from ZTool Port (notimplemented)
#define SPI_INCOMING_ZAPP_DATA Ox22 // Raw data from the ZAPP port (seeserialApp.c)
#define MT_SYS_APP_MSG 0x23 // Raw data from an MT Sys message
#define MT_SYS_APP_RSP_MSG 0x24 // Raw data output for an MT Sys message
#define MT_SYS_OTA_MSG 0x25 // Raw data output for an MT OTA Rsp

4)在 ChatApp_SendTheMessage()函数函数中,首先配置 afAddrMode_t 结构体中的通信方式为广播以及设定广播地址,然后直接调用系统的 AF_DataRequest 函数将传入参数 buf通过无线发送出去。

void ChatApp_SendTheMessage( uint8 buf, uint16 len )
{
//char theMessageDatal] = "Hello",
//广播方式发送消息
ChatApp_DstAddr.addrMode = (afAddrMode_t)AddrBroadcast;//消息发送方式,广播
ChatApp_DstAddr.endPoint = ChatApp_ENDPOINT;//目标终端编号,由哪个终端处理消息
ChatApp_DstAddr.addr.shortAddr = NWK_BROADCAST_SHORTADDR://默认的广播地址,OXFFFF
if( AF_DataRequest( &ChatApp_DstAddr, &ChatApp_epDesc,
			ChatApp_CLUSTERID,
			len,
			buf,
			&ChatApp_TransID,
			AF_DISCV_ROUTE,AF_DEFAULT_RADIUS ) == afStatus_SUCCESS)
    {
    // 发送成功后闪烁 LED1
    HalLedBlink( HAL LED 1, 1, 50,250 );
    osal_memset(buf,0,128); //清楚缓冲区
    }
else
    {
    // Error occurred in request to send.
    }
}

5)在 ChatApp_MessageMSGCB()函数中,直接调用系统的 HalUARTWrite 函数将接收到的无线数据通过调试串口打印出来。

void ChatApp_MessageMSGCB(aflncomingMSGPacket t *pkt)
{
switch(pkt->clusterld)
  {
  case ChatApp CLUSTERID:
  // 接收成功后闪烁 LED1
  HalLedBlink( HAL LED 1, 1, 50, 250 );
  HalUARTWrite(HAL_UART_PORT_0,&(pkt->cmd.Data[0]),pkt->cmd.DataLength); //将收到的数据通过串口打印出来
  break;
  }
 }

无线接收的数据都是存储在 aflncomingMSGPacket_t这个结构体类型的变量(此函数传入参数)中的,从如下结构体定义可知发送过来的应用层的数据内容保存在 afMSGCommandFormat_t结构体中的。所以在代码中可以通过&(pkt-cmd.Data[0])获取到无线数据。

typedef struct
{
osal_event_hdr_t hdr; /*OSAL Message header */
uint16 groupld;/* Message's group ID - 0 if not set */
uint16 clusterld;/* Message's cluster ID */
afAddrType_t srcAddr; /*Source Address, if endpoint is STUBAPS INTER PAN EPit's an InterPAN message */
uint16 macDestAddr;/* MAC header destination short address */
uint8 endPoint;/* destination endpoint */
uint8 wasBroadcast;/* TRUE if network destination was a broadcast address */
uint8 LinkQuality;/* The link quality of the received data frame */
uint8 correlation;/* The raw correlation value of the received data frame */
uint8 rssi;/* The received RF power in units dBm */
uint8 SecurityUse;/* deprecated */
uint32 timestamp;/* receipt timestamp from MAC */
uint8 nwkSegNum;/* network header frame sequence number */
afMSGCommandFormat_t cmd; /* Application Data */
}aflncomingMSGPacket_t;
// Generalized MSG Command Format

typedef struct
{
uint8 TransSegNumber;//发送队列编号
uint16 DataLength;// 传输数据的长度
uint8 *Data;//保存数据内容的指针
}afMSGCommandFormat_t;

ZStack 网络拓扑

Zigbee 网络支持星状、树状和网状三种网络拓扑结构:

在这里插入图片描述

星状网络:由一个 PAN 协调器和多个终端设备组成,只存在 PAN 协调器与终端的通讯,终端设备间的通讯都需通过 PAN 协调器的转发。
树状网络:由一个协调器和一个或多个星状结构连接而成,设备除了能与自己的父节点或子节点进行点对点直接通讯外,其他只能通过树状路由完成消息传输。
网状网络:是树状网络基础上实现的,与树状网络不同的是,它允许网络中所有具有路由功能的节点直接互连,由路由器中的路由表实现消息的网状路由。该拓扑的优点是减少了消息延时,增强了可靠性,缺点是需要更多的存储空间开销。

#define NWK_MODE_STAR O
#define NWK_MODE_TREE 1
#define NWK_MODE_MESH 2
#if ( STACK_PROFILE_ID == ZIGBEEPRO_PROFILE )
#define NWK_MODE NWK_MODE_MESH
#elif ( STACK_PROFILE_ID == HOME_CONTROLS )
#define NWK_MODE NWK_MODE_MESH
#elif ( STACK_PROFILE_ID == GENERIC_STAR )
#define NWK_MODE NWK_MODE_STAR
#elif ( STACK_PROFILE_ID == NETWORK_SPECIFIC )
#define NWK_MODE NWK_MODE_MESH
#endif

ZStack 拓扑类型

1、星状网

​ 星状网 (star network) 是指网络中的各节点设备通过一个网络集中设备 (如集线器 HUB 或者交换机 Switch) 连接在一起,各节点呈星状分布的网络连接方式。这种拓扑结构主要应用于 IEEE802.2、IEEE 802.3 标准的以太网中。

星状网的基本特点:

1)容易实现,但安装、维护工作量,成本较大:它所采用的传输介质一般都是采用通用的双绞线或同轴电缆。但是每个站点都要和中央网络集中设备直接连接,需要耗费大量的线缆,并且安装,维护的工作量也剧增。

2)节点扩展、移动方便:节点扩展时只需要从集线器或交换机等集中设备中拉一条电缆即可,而要移动一个节点只需要把相应节点设备移到新节点即可。故障诊断和隔离容易:一个节点出现故障不会影响其它节点的连接,可任意拆走故障节点。

3)中央节点的负担较重,易形成瓶颈:各站点的分布处理能力较低: 中央节点一旦发生故障,则整个网络都受到影响。在星型网中,一个功能强大的全功能设备(FFD)位于网络的中心,作为网络协调者(Coordinator),其它的全功能设备(FFD)或精简功能设备(RFD)分布在其覆盖范围内。由于网络协调者定义了整个网络的时分复用和多址接人方式,因此星型网的控制和同步都比较简单,通常用在设备数量比较少的场合。示意图如下图所示:

在这里插入图片描述

2、树状网

​ 树状网结构是利用路由器对星形结构的扩充,其节点可以采用 Cluster-Tree 路由传输数据和控制信息。如下图所示,处于网络最末端的称为“叶”节点,它们是网络的终端设备。若干个叶节点设备连接在一个全功能设备 FFD 上形成一个 “簇”,若千个“簇”连接再形成“树”。该结构中有一个协调器,其余大部分设备是 FFD,而 RFD 只能作为叶节点。网络建立后协调器短地址自动设置为0。当有设备以路由器的身份接入时,协调器会分配一地址块,若是终端设备接入只会分配唯一的 16 位的短地址。树状网络是一种层次结构,节点按层次连接,信息交换主要在上下节点之间进行,相邻节点或同层节点之间一般不进行数据交换。在树状网络中,协调器可以有多个子节点,终端设备只有一个父节点,路由器可以有多个子节点但只能有一个父节点。任意两个节点的数据传输都只有一条路由路径,任何一个路由器节点的加入或离开都会影响网络的数据传输。树状网络拓扑结构简单,维护方便,适用于汇集信息的应用要求。但资源共享能力较低,可靠性不高,任何一个路由节点或链路的故障都会影响整个网络的运行。

在这里插入图片描述

3、网状网

​ 无线网状网中的每个节点和路由器都能接受和传送数据,通过中继处理,数据包用可靠的通信链路,贯穿于中间的各节点,达到制定目标。网状网络具有很高的可靠性,主要有赖于多跳技术,多跳是指数据不断从一个节点跳到另一个节点,因为数据从一个节点不断传送到另一个节点,又因为网状网络有好多节点,所以任何一个节点到另一个节点之间的路径总是不止一条,无线网状网拥有多个元余的通信路径,如果一条路径中断,网状网将自动选择另一条路径维持正常通信。一般情况下,网状网能自动地选择最短路径,提高了连接的质量。实践证明,如果距离减小两倍,则接收端的信号强度会增加四倍,不增加节点发射功率的同时还使链路更加可靠。网状网网络里,只要增加节点数目,就可以增加可及范围,或从冗余链路的增加上,带来更多的可靠性。多跳还大大延长了网状网络节点的电池寿命&传送所需功率与距离的立方大致成比例。所以三次短距离传输跳 (每段距离为 3)所耗功率仅为单次远距离传输跳(距离为 43) 的 2!5。在较远距离上跳数越多,节能效果越是惊人。
​ 在传统星状网络中,新节点可能并不是随时都能与中心节点进行通信。可能两者之间距离过远,可能隔着物理障碍,也可能该节点处在多路径干扰造成的盲点。在无线网状网络中,一旦有了为数不少的节点,一个新节点几乎总是能够找到邻近节点进行通信。它能收听邻近节点,如果它找到了一个或若干个,就会要求加入网络,并获得准入,前提是要满足准入标准,譬如安全状况。节点加入网络后,这些节点和目的地节点之间就会自动生成路径,这往往为构建更庞大的传统网络提供了一条途径。节点本身就能够建立路由,只要发送信息、邻近信号收到信息后,转发出去。这个转发过程会逐个节点地重复下去,直到信息到达目的地&目的地节点提供的确认信号告诉始发节点:一条完全路径已建成,信息已成功抵达。

在这里插入图片描述

Zstack 网络地址

​ 在 Zigbee 网络中,关于节点的网络信息主要有这几个方面:本节点的网络地址、MAC 地址、父节点的网络地址以及父节点的 MAC 地址等内容。Zigbee 协议栈中已经实现了查询节点上述网络信息的相关函数,用户只需要熟悉各个函数的使用方法即可。

uint16 NLME_GetShortAddr(void) 该函数返回该节点的网络地址。
byte *NLME_GetExtAddr(void) 该函数返回指向该节点MAC地址的指针。
uint16 NLME_GetCoordShortAddr(void) 该函数返回父节点的网络地址。
void NLME_GetCoordExtAddr(byte *buf) 该函数返回指向父节点MAC地址的缓冲区的指针
  • 19
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

渺若星辰_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值