Zigbee应用笔记

更新时间:2024-05-07 09:27:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

Zigbee应用笔记

本应用笔记是建立在对TI的“ZStack-1.4.2-1.1.0”理解的基础上的,可以帮助利用TI的Zigbee协议栈开发应用程序。 一 创建主函数

程序运行入口是主函数:main() 这部分程序代码包含在ZMain文件夹的ZMain.c中。 在mian()中包括初始化和正常运行两个部分。 1初始化

初始化期间不接受中断,故首先要屏蔽中断,调用osal_int_disable( INTS_ALL )实现屏蔽中断功能。完成初始化后调用osal_int_enable( INTS_ALL )使能中断。

初始化主要包括:

协议栈寄存器初始化zmain_ram_init()

试验板初始化InitBoard( OB_COLD )这包括各个通用I/O口的设置,确保与硬件电路设计的一致,操作系统时钟设置。这部分在HAL/Target/Config/hal_board_cfg.h中,用户可以根据自己的硬件设计加以修改。

硬件驱动初始化 HalDriverInit () 这包括TIMER、ADC、LED、LCD、KEY、AES、DMA、UART,用户可以根据需要,通过设置相应的宏定义(HAL_ *)初始化相应的硬件。驱动的宏定义设置在hal_board_cfg.h中的driver configeration部分。

非易失性存储器初始化 osal_nv_init( NULL ) MAC初始化ZMacInit()

设置64位地址zmain_ext_addr() 每一个设备都有一个唯一的64位地址。 初始化协议栈全局变量zgInit()。

初始化应用框架(AF)afInit(),初始化时没有应用端口

操作系统初始化osal_init_system() 这包括寄存器初始化;操作系统时钟初始化;能量管理系统初始化;操作系统初始化osalTaskInit();添加任务 osalAddTasks()这包括网络层任务、应用支持子层任务等,用户可在此添加自己的应用任务;任务初始化osalInitTasks() 这包括为每个任务分配一个任务ID号(task_ID)以及调用每一个任务的初始化函数。

在整个初始化过程中涉及到用户应用的就是试验板初始化,硬件驱动初始化,添加用户应用任务。

2启动系统

完成所有的初始化工作后就可以进入正常工作阶段了,启动操作系统osal_start_system(),这是一个死循环,包括时钟查询和串口通信查询,任务查询以及调用事件处理程序。当没有要处理的任务时系统进入节能模式。

二 创建用户应用程序

1 添加用户任务

创建一个用户应用程序,首先要在主函数中添加一个用户任务,操作系统初始化时,调用osalAddTasks()添加任务,osalAddTasks()在App/OSAL_Sample.c中调用osalTaskAdd()实现任务添加:

Void osalTaskAdd ( pTaskInitFn pfnInit,

pTaskEventHandlerFn pfnEventProcessor, byte taskPriority)

pfnInit 用户任务初始化函数地址。

1

pfnEventProcessor 用户任务事件处理函数地址。 taskPriority 用户任务优先级,一般设为低。 2 用户任务初始化

用户任务初始化首先是由操作系统分配一个任务号(task_id),其次是实现对端口的配置,对于有收发数据功能的应用包括端口描述、目标地址、端口注册等部分。

2.1端口描述 typedef struct {

byte endPoint; byte *task_id;

SimpleDescriptionFormat_t *simpleDesc; afNetworkLatencyReq_t latencyReq; } endPointDesc_t;

endPoint 端口号,Zigbee协议中用户应用任户的有效端口号是1——240,0分配给ZDO,255是广播端口号,241——244保留,以备以后使用。

task_id 任务号,操作系统分配的任务号,由系统生成。 simpleDesc 简单描述符,包含有端点的所有信息。 typedef struct {

byte EndPoint; 端口号,同上

uint16 AppProfId; 表示该端口的应用属于哪一类,ID由Zigbee联盟分配 uint16 AppDeviceId; 表示该端口支持的设备,ID由Zigbee联盟分配 byte AppDevVer: 表示该端口所支持的版本 byte Reserved:

byte AppNumInClusters; 输入簇的数量

cId_t *pAppInClusterList; 输入簇表,用一个数组表示 byte AppNumOutClusters; 输出簇的数量

cId_t *pAppOutClusterList; 输出簇表,用一个数组表示, } SimpleDescriptionFormat_t;

latencyReq 网络启动模式,根据需要可设置为 noLatencyReqs、fastBeacons、slowBeacons中的某一个,示例中选择noLatencyReqs(这部分不是很清楚)

2.2、目标地址

在初始化时应根据需要确定目标地址,数据格式采用结构体:afAddrType_t typedef struct {

union {

uint16 shortAddr; } addr;

afAddrMode_t addrMode; byte endPoint; } afAddrType_t;

shortAddr 网络地址,这是设备加入网络时有些挑起或路由器分配的 addrMode 地址模式,

2

endpoint 端口号,包括afAddrNotPresent、afAddr16Bit、afAddrGroup、afAddrBroadcast四种模式,如果是广播数据则选择afAddrBroadcast ,网络地址设为0xFFFF;如果是单播数据,间接寻址时选择afAddrNotPresent,网络地址设为0,如果已知目标地址,采用直接寻址方式则选择afAddr16Bit,网络地址设备已知的目标地址。在TI协议栈中增加了设备分组这一功能,某些设备分在一个组,每个组有一个组地址,若要向该组广播数据,则选择afAddrGroup,网络地址设为0xFFFF。

2.3、端口注册

完成上述端口设置后,保存端口信息,调用afRegister(),这样接收到数据后就可以根据保存侧端口信息将数据送到对应的端口。

另外在初始化过程中还应初始化两个变量 SampleApp_NwkState = DEV_INIT,表示网络状态,SampleApp_TransID = 0;发送数据ID号,没法送一次加1。

同时如果在你的应用程序中使用到了其它的功能模块,比如AD转换,串口通信等,也需要在此进行相应的设置。

3 事件处理函数 3.1信息传递

任务内部和任务之间是通过事件来进行通信的,系统事件每一个任务都可能接收到,根据自己的功能作出相应处理,内部事件仅局限于某一个任务内部

3.1.1任务之间的信息交换

任务之间的信息交换内容统称为为系统事件,通过消息来实现的。任务之间通过osal_msg_send()发送消息,当接收到系统事件后,调用osal_msg_receive()获取消息内容,并采用循环的方式处理全部的系统事件,全部完成后释放信息空间,重新进入操作系统循环。对系统事件的处理由用户自己定义。系统事件定义在ZComDef.h中。消息一般采用如下的结构体,在应用函数的头文件中定义。

例:typedef struct

{

osal_event_hdr_t hdr; uint8 *msg;

} mtOSALSerialData_t;

hdr 系统事件,每个消息都必须包含这个变量,

typedef struct {

uint8 event; 系统事件 uint8 status; 状态

} osal_event_hdr_t;

Msg 地址指针,指向消息所包含的信息的地址,比如数据等

在消息的结构体中还可以添加其它的内容,比数据数据长度等,根据需要。

3.1.1.1 byte osal_msg_send( byte destination_task, byte *msg_ptr ) 向其它任务发送消息:

destination_task 接收该消息的任务ID号

msg_ptr 地址指针,指向要发送的信息首地址。 3.1.1.2 byte *osal_msg_receive( byte task_id )

任务收到系统任务后调用该函数获取消息内容,返回的是消息的地址。 Task_id 系统分配的任务ID号,即调用该函数的任务的ID号。 3.1.1.3 byte osal_msg_deallocate( byte *msg_ptr )

3

处理完所有的任务后调用该函数释放信息空间。 Msg_ptr 地址指针,指向已经处理完的消息地址。 3.1.2事件设置

在任务内部也存在信息的交换,这通过内部事件来实现,这里定义他们为一般事件,一般事件只在定义他的函数中有效,一般事件与系统事件处理方式不同,每处理完一个一般事件后即进入操作系统的大循环,未处理的事件被挂起,直到该任务再次激活,如果有优先级更高的则先处理优先级高的,否则按照排队的先后顺序。调用osal_set_event()可对每一位置位。

3.1.2.1 byte osal_set_event ( byte task_id, UINT16 event_flag ) Osal_set_event() 只告知发生了某个事件,没有附加信息。 Task_id 任务ID号,事件所属的任务ID号 Event_flag 一般事件。

3.1.2.2osal_start_timerEx( byte taskID, UINT16 event_id, UINT16 timeout_value ) 某一事件可能需要等待一定的时间再进行处理,比如发送数据时,如果在一定的时间内没有接收到确认帧,需要请求重发,则可以调用这样的函数。

taskID 任务ID event_id 一般事件

timeout_value 有效时间,以毫秒为单位 3.1. 2.3byte osal_stop_timer( UINT16 event_id ) 如果在规定的事件内收到了确认帧,则不需要重新发送,这时就需要取消之前定义的冲发事件,调用osal_stop_timer()实现。

Event_id 要取消的事件。 3.2事件(event)处理

evnet是一个16位的变量,每一位对应一个事件,由前面的介绍可以知道分为系统事件(SYS_EVENT_MSG)和一般事件,SYS_EVENT_MSG在ZComDef.h定义为0x8000,是一个全局变量,在每一个任务中都是有效的,在系统事件之下再定义了若干事件,暂且定义为系统子事件;一般事件由用户自己定义在头文件中,每个任务可以定义15个一般事件。任务接收到事件后系统调用事件处理函数,函数主要包括系统事件处理和一般事件处理两大部分。在进入处理之前首先要定义一个接收数据的变量:

afIncomingMSGPacket_t *MSGpkt typedef struct {

osal_event_hdr_t hdr; uint16 groupId; uint16 clusterId;

afAddrType_t srcAddr; byte endPoint; byte wasBroadcast; byte LinkQuality; byte SecurityUse; uint32 timestamp;

afMSGCommandFormat_t cmd;

} afIncomingMSGPacket_t; 函数可以从中提取相应的信息。 3.2系统事件处理

4

这部分以TI应用实例中的部分程序为例说明,每个任务对系统事件是一次性处理的,所有事件处理完成之后才进入系统循环。

if ( events & SYS_EVENT_MSG )

{ 接收到系统事件,提取消息内容,根据系统子事件调用相应的处理函数

MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( NewSample_TaskID ); while ( MSGpkt ) {

switch ( MSGpkt->hdr.event ) {

如果有按键发生,调用按键处理程序,完成后跳出switch语句,提取下一个系统子事件。

case KEY_CHANGE:

NewSample_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys );

break;

接收到其他设备发送的数据,调用处理函数,完成后跳出switch语句,提取下一个系统子事件。

case AF_INCOMING_MSG_CMD:

NewSample_MessageMSGCB( MSGpkt ); break;

default: break; }

处理完一个事件后,释放存储空间

osal_msg_deallocate( (uint8 *)MSGpkt )

提取下一个系统子事件

MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( NewSample_TaskID ); }

处理完所有的系统事件后返回除系统事件以外的其他未处理事件,用异或算法实现

return (events ^ SYS_EVENT_MSG); }

由于对外部设备发送的数据处理是比较普遍的,所以这里再补充说明一下该处理程序。前面已经给出了afIncomingMSGPacket_t,具体内容不在多说,用户根据自己的需要,提取相应的变量。数据在被AF根据初始化时注册的端口信息发送到了相应的端口,端口根据簇ID号调用相应的处理函数。

void NewSample_MessageMSGCB( afIncomingMSGPacket_t *pkt ) {

uint8 ssLevel;

// Figure out the incoming signal strength if ( pkt->LinkQuality < SS_LOW ) ssLevel = 0;

5

else if ( pkt->LinkQuality < SS_MED ) ssLevel = 1;

else if ( pkt->LinkQuality < SS_HIGH ) ssLevel = 2; else

ssLevel = 3;

writeLCDIcon( ICON_SIGNAL_STRENGTH, ssLevel );

counter = pkt->cmd.Data[0];

switch ( pkt->clusterId ) {

case CLUSTERID1: HandleProcessor1(); case CLUSTERID2: HandleProcessor2(); case CLUSTERID3: HandleProcessor3(); } }

3.3一般事件处理

一般事件定义在应用程序头文件中。所有一般事件处理流程是一致的,举一例说明

if ( events & NewSample_SEND_PERIODIC_MSG_EVT )

{接收到发送周期信息的事件,调用函数请求发送数据,函数返回请求结果 if ( AF_DataRequest( &NewSample_Periodic_DstAddr, &NewSample_epDesc, NewSample_PERIODIC_CLUSTERID, 1,

(uint8*)&NewSamplePeriodicCounter, &NewSample_TransID, AF_DISCV_ROUTE,

AF_DEFAULT_RADIUS ) == afStatus_SUCCESS ) {发送成功,在液晶屏上显示发送图标 // Successfully requested to be sent.

NewSample_FlashIcon( FLASH_TX_ICON ); } else

{发送不成功,则修改错误,重新发送。 // Error occurred in request to send. }

返回未处理的事件

return (events ^ NewSample_SEND_PERIODIC_MSG_EVT); }

每个Zigbee设备都回有数据发送请求,这里对发送请求函数作一说明 AF_DataRequest (

6

afAddrType_t *dstAddr, 目标地址,初始化时设置的, endPointDesc_t *srcEP, 请求发送数据的端口

uint16 cID, 数据簇ID

uint16 len, 要发送的数据长度 uint8 *buf, 要发送的数据地址

uint8 *transID, 发送ID号,每发送一次加1

uint8 options, 路由选项,0表示使用已有路由;1表示若无

路由则启用路由发现机制

uint8 radius 发送半径,即广播数据时单跳的节数

)

最后,系统处理完所有事件后,若没有新的事件,则进入节能模式。 三 串口通信

串口通信是最简单,故使用比较频繁的通信协议。串口通信流程包括打开串口、读取/写入数据、关闭串口。与串口有关的函数都在hal_uart.c文件中。

1打开串口

应用程序中使用到串口模块,则在初始化时应对串口进行设置,并打开串口。调用HalUARTOpen ( uint8 port, halUARTCfg_t *config )函数实现。

Port 使用的串口

Config 对串口的设置参数 typedef struct {

bool configured; uint8 baudRate; bool flowControl;

uint16 flowControlThreshold; uint8 idleTimeout; halUARTBufControl_t rx; halUARTBufControl_t tx;

bool intEnable; uint32 rxChRvdTime; halUARTCBack_t callBackFunc; }halUARTCfg_t;

Configured true/false true表示已经对串口进行了设置,初始化时设置

baudRate 波特率设置 有9种可供选择,实例中选择HAL_UART_BR_38400(HAL_UART_BR_1200 、HAL_UART_BR_2400、HAL_UART_BR_4800、HAL_UART_BR_9600、HAL_UART_BR_19200、HAL_UART_BR_31250、HAL_UART_BR_38400、HAL_UART_BR_57600、HAL_UART_BR_115200 )初始化时设置。

flowControl 流控制,当前不支持。初始化时设置。 flowControlThreshold 初始化时设置。

idleTimeout 空闲时间,超出这一时间表示完成一次数据的发送或接收,初始化时设置 intEnable 中断使能,true,初始化时设置。

rxChRvdTime 读完一个字节的系统时间,两次时间差大于idleTimeout(即超时)调用回调函数,初始化时设置。

7

callBackFunc 回调函数地址,当接收缓存满或发生超时都调用该函数,初始化时设置。 rx\\tx

typedef struct

{

uint16 bufferHead;

uint16 bufferTail; 两者相等时表示没有数据

uint16 maxBufSize; 收发缓存的大小,实例中为128

uint8 *pBuffer; 缓存地址,发送或接收的数据都存放在这里。

}halUARTBufControl_t;

HalUARTOpen()根据上述设置打开串口,帧格式默认的是:一位停止位,无奇偶校验,八位数据位,用户可以在HalUARTOpen()种修改。

2读写数据

打开串口之后就可以通过串口读写数据了,作为用户这一级,不需要了解其具体工作方式,只需将数据写入发送缓存,或从接收缓存读取数据。

2.1读取数据

uint16 HalUARTRead ( uint8 port, uint8 *pBuffer, uint16 length) port 串口

pBuffer 读取数据的存放地址 length 读取的数据长度

从port口的接受缓存读取length长度的数据存放到*pBuffer。

操作系统的主循环中,每一次循环都回有一次串口查询,如果发现接受缓存已经满了,或者超时,则调用回调函数,在回调函数中一般都要调用HalUARTRead()读取数据。

2.2发送数据

uint16 HalUARTWrite ( uint8 port, uint8 *pBuffer, uint16 length ) port 串口

pBuffer 发送数据的存放地址 length 发送的数据长度

将存放在pBuffer处长度为length的数据写入到port口的发送缓存中去。 2.3数据协议

定义的数据协议如下表所示,用户也可以自己定义 长度(字节) 内容 节点间传递的数据有两种类型,一是控制命令,二是普通数据。

普通数据是指计算机之间利用Zigbee通信模块进行的数据传输,节点只传送,不需要作任何处理,定义为0x0000,Zigbee模块从串口接收到这样的数据直接请求数据发送,接收到其他设备传递过来的数据则通过串口发送给计算机。

控制命令是指人通过计算机的操作界面向网络中的节点发出的控制命令,节点根据命令做相应的操作,具体的由用户定义。

3关闭串口

void HalUARTClose ( uint8 port ) port 要关闭的串口

1 数据类型 X 数据 8

本文来源:https://www.bwwdw.com/article/2yxg.html

Top