【ZigBee系列】1.浅析ZigBee工程

ZigBee原创系列-第 1 篇


作者注: ZigBee 系列共三篇,首先讲解 ZigBee 工程的结构,然后通过两个具体项目熟悉 ZigBee 的应用。在学习本系列内容前,您需要掌握单片机相关知识、 ZigBee 基础知识,希望本系列教程能对您开发 ZigBee 项目有所启发。如有任何问题,欢迎您随时与我联系:Hauyu.Chen@Gmail.com

版权声明:本文由 Hov 所有,发布于 http://chenhy.com ,转载请注明出处。



0 写在前面

大一暑假,我开始学习ZigBee技术。这篇文章是在大一暑假结束后完成的,主要是想写一下在学习ZigBee过程中的思路,以及一个ZigBee工程中的核心内容。因为在刚学习ZigBee的时候,面对着大量的代码,有种无从下手的感觉。

本文基于 Z-Stack 的例程 SampleApp 展开,旨在突出在一个ZigBee工程中比较核心的部分,建议大家看过 Z-Stack 中的例程后作参考之用。

注:Z-Stack 安装文件:https://github.com/HauyuChen/Z-Stack


1 概述

写过程序的人都知道每个程序都是从 main 函数开始执行的,在 ZigBee 程序中也不例外。在一个 Zigbee 工程中,程序从 main 函数开始执行,经过一步一步的函数调用(一些系统功能的初始化,建议跟踪一下从 main 函数到 SampleApp_ProcessEvent 函数的过程),程序最终来到了 SampleApp_ProcessEvent 函数, SampleApp_ProcessEvent(uint8 task_id,uint16 events) 是一个颇为重要的函数,整个工程更像是围绕着这个函数运行。

说白了,我们要通过这个函数来处理所有事件。

比如:
if ( events & SYS_EVENT_MSG ) 处理系统事件,if ( events & SAMPLEAPP_SEND_PERIODIC_MSG_EVT ) 处理用户定义的事件。在不同的事件处理代码中,通过一个 switch 语句把所要处理的任务分成几个 case 来处理,在每个 case 里面调用相应的函数,我们通过定义这些函数执行我们想要的功能即可。

其实,这就是如何在Zigbee工程里面的添加我们所需功能的解决思路,在不同的事件下添加我们的处理代码。我们要做的就是围绕事件处理函数修改代码,接下来以SampleApp例程进行讲解。

SampleApp代码结构如下:



2 ZigBee工程的核心内容

要想通过ZigBee协议栈实现我们需要的功能,最简单的方式就是基于协议栈提供的例程进行修改。那么我们应该修改哪些代码呢?其实,只要关注ZigBee工程中核心的内容即可,下面将逐个介绍ZigBee工程中比较重要的部分,我们要实现自己想要的功能,基本上只要修改这些代码即可。

2.1 簇ID

簇 ID 在 ZigBee 工程中作为数据收发的标识符,十分重要。在 ZigBee 程序中,通过定义一个结构体来声明本程序要用到的所有簇 ID 。

const cId_t SampleApp_ClusterList[SAMPLEAPP_MAX_CLUSTERS] =
{
    //簇ID:区分不同的消息
    SAMPLEAPP_PERIODIC_CLUSTERID,   //自定义的标识符名称
    SAMPLEAPP_FLASH_CLUSTERID       //自定义的标识符名称
};

2.2 初始化函数

在 SampleApp 例程中, SampleApp_Init 函数为整个工程的初始化函数。在 SampleApp_Init 函数里面主要写一些初始化代码,比如程序中要用到串口通讯,那么可以在 SampleApp_Init 函数里面添加相应的串口初始化代码,这样在程序中我们才能执行与串口相关的操作。串口初始化代码如下:

/**
* 初始化函数:SampleApp_Init
*/
void SampleApp_Init(uint8 task_id)  
{
    /* 串口配置结构体 */
    halUARTCfg_t uartConfig;
    /* 串口初始化 */
    uartConfig.configured = TRUE;
    uartConfig.baudRate = HAL_UART_BR_38400;    //波特率设置为38400
    uartConfig.flowControl = FALSE;
    uartConfig.flowControlThreshold = 64;
    uartConfig.rx.maxBufSize        = 128;
    uartConfig.tx.maxBufSize        = 128;
    uartConfig.idleTimeout          = 6;
    uartConfig.intEnable            = TRUE;
    uartConfig.callBackFunc = UART_CallBack;    //UART_CallBack是自定义的串口回调函数名称,对串口接收的数据进行处理
    HalUARTOpen(0,&uartConfig);                 //启动串口
}

2.3 事件处理函数

在 SampleApp 例程中, SampleApp_ProcessEvent 函数是整个工程的事件处理函数。 SampleApp_ProcessEvent 函数是一个 Zigbee 工程中最重要的函数,负责处理不同的事件,如系统事件、自定义事件。

  • 系统事件:绑定请求、处理按键、接受空中消息等;
  • 自定义事件:串口发送数据、传感器数据获取等。
/**
* 事件处理函数:SampleApp_ProcessEvent
*/
uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events )
{
    if ( events & SYS_EVENT_MSG )               //系统事件
    {
        while ( MSGpkt )
        {
            switch ( MSGpkt->hdr.event )
            {
                case KEY_CHANGE:                //按键操作
                    SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state,
                    ((keyChange_t *)MSGpkt)->keys );        //按键处理函数:对按键操作进行处理
                break;

                case AF_INCOMING_MSG_CMD:       //接收到空中消息
                    SampleApp_MessageMSGCB( MSGpkt );   //消息处理函数:对接受到的数据进行处理
                break;

                case ZDO_STATE_CHANGE:          //设备状态发生改变
                    //执行我们想要的操作,不同的设备(协调器、路由、终端)可以执行不同的事件
                break;

                default:
                break;
            }
        return (events ^ SYS_EVENT_MSG);
    }
  
    if ( events & SAMPLEAPP_SEND_PERIODIC_MSG_EVT )     //自定义事件
    {  
        Function(); //执行我们想要的操作,比如传感器数据采集
        osal_start_timerEx(SampleApp_TaskID,SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
        (SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT + (osal_rand() & 0x00FF)) ); 
        //通过osal_start_timerEx函数定时执行事件,如定时采集传感器的数据
        return (events ^ SAMPLEAPP_SEND_PERIODIC_MSG_EVT);
    }
    return 0;
}

2.4 消息处理函数

在 SampleApp 例程中, SampleApp_MessageMSGCB 函数负责对所有接收的消息进行处理,比如 ZigBee 终端向 ZigBee 协调器发送数据, ZigBee 协调器根据不同的数据进行处理。

/**
* 消息处理函数:SampleApp_MessageMSGCB
*/
void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
    uint16 flashTime;
    switch ( pkt->clusterId )
    {
        //看到这个簇ID吗?在前面我们自己定义的,当然你也可以改成任意名字,但是要和发送的簇ID相对应,这里是接受的簇ID。如ID1…
        //如果收到簇ID为 SAMPLEAPP_PERIODIC_CLUSTERID 的消息
        case SAMPLEAPP_PERIODIC_CLUSTERID:      
            //执行我们想要的操作,比如调用一个函数,在这个函数定义里面添加我们想要的功能代码,IO控制、串口发送数据等
        break;

        //如果收到簇ID为 SAMPLEAPP_FLASH_CLUSTERID 的消息
        case SAMPLEAPP_FLASH_CLUSTERID:     
            //这里执行了一个闪灯的功能
            flashTime = BUILD_UINT16(pkt->cmd.Data[1], pkt->cmd.Data[2] );
            HalLedBlink( HAL_LED_4, 4, 50, (flashTime / 4) );
        break;
    }
}

2.5 数据发送函数

通过调用 AF_DataRequest 函数把一个节点的数据通过 ZigBee 网络无线发送至目标节点,重点关注目的地址、 clusterID 、发送内容、数据大小。

下面这个函数发送一个簇ID为 SAMPLEAPP_SEND_DATA_PTOP_CLUSTERID ,内容为 NAME 的消息。

void SampleApp_SendPointToPointMessage( void )
{
    uint8 NAME[5];                  //数据定义
    if ( AF_DataRequest( &Point_To_Point_DstAddr,           //目的地址
                       &SampleApp_epDesc,
                       SAMPLEAPP_SEND_DATA_PTOP_CLUSTERID,  //簇ID,发送标识符
                       5,           //数据长度
                       NAME,        //数据数组
                       &SampleApp_TransID,
                       AF_DISCV_ROUTE,
                       AF_DEFAULT_RADIUS ) == afStatus_SUCCESS ){
    }
    else
    {
        // Error occurred in request to send.
    }
}


3 结语

上面提到的都是在 ZigBee 工程中我们需要注意的东西,我们根据项目需求作相应的修改。

虽然一个 ZigBee 工程的代码不少,但是大多数都是封装好的,不需要我们去考虑,我们要解决的是在合适的地方添加我们的代码,可以利用修改例程来实现我们想要的功能。

在刚接触 ZigBee 协议栈的时候,建议大家理解 ZigBee 工程中的主文件的执行流程,看懂主文件的代码是什么意思,看懂后基本就可以在例程上修改代码实现我们自己的项目了。


附录

SerialApp 工程的详细注释,SerialApp 实现 ZigBee 协议栈中的串口通信。

/********************************************
** 总结
** 在这个实验中,先绑定两个ZDO设备,组成ZigBee网络;
** 两个设备绑定后,串口通信的发送接受流程分成了九步(如注释)
**    第一步:SerialApp_CallBack(),当串口有数据时此函数即被调用。
**    第二步:在SerialApp_CallBack()调用SerialApp_Send(void),这是一个消息发送函数,实现将数据发送到ZigBee网络中的其他节点。
**    第三步:在SerialApp_Send(void)中,通过AF_DataRequest()函数,将数据从空中发送出去,簇ID为SERIALAPP_CLUSTERID1
**    第四步:SerialApp_ProcessMSGCmd()对从空中捕获到的信号进行处理,接收到发送过来的信息簇ID为 SERIALAPP_CLUSTERID1
**    第五步:case SERIALAPP_CLUSTERID1:HalUARTWrite通过串口发送数据到PC机
**    第六步:收到数据后,向节点1发送一个响应事件SERIALAPP_RESP_EVT,跳到SerialApp_ProcessEvent()执行
**    第七步:if ( events & SERIALAPP_RESP_EVT )  //串口响应事件,表示成功接受来自节点1的数据,此时调用SerialApp_Resp()
**    第八步:在SerialApp_Resp()中,通过AF_DataRequest函数,将接收成功响应从空中发送出去,簇ID为SERIALAPP_CLUSTERID2
**    第九步:同第四步,接收到发送过来的信息簇ID为 SERIALAPP_CLUSTERID2,所以不再继续发送
** 以上为该例程的功能流程。
*/

/********************************************
** 预处理:这里可以参照基础实验,直接在自己的程序中复制。
*/
#include "AF.h"
#include "OnBoard.h"
#include "OSAL_Tasks.h"
#include "SerialApp.h"
#include "ZDApp.h"
#include "ZDObject.h"
#include "ZDProfile.h"

#include "hal_drivers.h"
#include "hal_key.h"
#if defined ( LCD_SUPPORTED )
  #include "hal_lcd.h"
#endif
#include "hal_led.h"
#include "hal_uart.h"


#if !defined( SERIAL_APP_PORT )
#define SERIAL_APP_PORT  0
#endif

#if !defined( SERIAL_APP_BAUD )
#define SERIAL_APP_BAUD  HAL_UART_BR_38400
//#define SERIAL_APP_BAUD  HAL_UART_BR_115200
#endif

// When the Rx buf space is less than this threshold, invoke the Rx callback.
#if !defined( SERIAL_APP_THRESH )
#define SERIAL_APP_THRESH  64
#endif

#if !defined( SERIAL_APP_RX_SZ )
#define SERIAL_APP_RX_SZ  128
#endif

#if !defined( SERIAL_APP_TX_SZ )
#define SERIAL_APP_TX_SZ  128
#endif

// Millisecs of idle time after a byte is received before invoking Rx callback.
#if !defined( SERIAL_APP_IDLE )
#define SERIAL_APP_IDLE  6
#endif

// Loopback Rx bytes to Tx for throughput testing.
#if !defined( SERIAL_APP_LOOPBACK )
#define SERIAL_APP_LOOPBACK  FALSE
#endif

// This is the max byte count per OTA message.
#if !defined( SERIAL_APP_TX_MAX )
#define SERIAL_APP_TX_MAX  80
#endif

#define SERIAL_APP_RSP_CNT  4


/********************************************
** 结构体
*/
const cId_t SerialApp_ClusterList[SERIALAPP_MAX_CLUSTERS] =
{
    /* 函数所要用到的簇ID */
    SERIALAPP_CLUSTERID1,
    SERIALAPP_CLUSTERID2
};
 
const SimpleDescriptionFormat_t SerialApp_SimpleDesc =
{
  SERIALAPP_ENDPOINT,              //  int   Endpoint;
  SERIALAPP_PROFID,                //  uint16 AppProfId[2];
  SERIALAPP_DEVICEID,              //  uint16 AppDeviceId[2];
  SERIALAPP_DEVICE_VERSION,        //  int   AppDevVer:4;
  SERIALAPP_FLAGS,                 //  int   AppFlags:4;
  SERIALAPP_MAX_CLUSTERS,          //  byte  AppNumInClusters;
  (cId_t *)SerialApp_ClusterList,  //  byte *pAppInClusterList;
  SERIALAPP_MAX_CLUSTERS,          //  byte  AppNumOutClusters;
  (cId_t *)SerialApp_ClusterList   //  byte *pAppOutClusterList;
};
 
const endPointDesc_t SerialApp_epDesc =
{
  SERIALAPP_ENDPOINT,
 &SerialApp_TaskID,
  (SimpleDescriptionFormat_t *)&SerialApp_SimpleDesc,
  noLatencyReqs
};


/********************************************
** 变量
*/
uint8 SerialApp_TaskID;    // Task ID for internal task/event processing.

static uint8 SerialApp_MsgID;

static afAddrType_t SerialApp_TxAddr;
static uint8 SerialApp_TxSeq;
static uint8 SerialApp_TxBuf[SERIAL_APP_TX_MAX+1];
static uint8 SerialApp_TxLen;

static afAddrType_t SerialApp_RxAddr;
static uint8 SerialApp_RxSeq;
static uint8 SerialApp_RspBuf[SERIAL_APP_RSP_CNT];

/********************************************
** 函数声明
*/
static void SerialApp_ProcessZDOMsgs( zdoIncomingMsg_t *inMsg );
static void SerialApp_HandleKeys( uint8 shift, uint8 keys );
static void SerialApp_ProcessMSGCmd( afIncomingMSGPacket_t *pkt );
static void SerialApp_Send(void);
static void SerialApp_Resp(void);
static void SerialApp_CallBack(uint8 port, uint8 event);


/********************************************
** 函数定义
*/

//初始化函数
void SerialApp_Init( uint8 task_id )    
{
    SerialApp_TaskID = task_id;     //任务ID
    SerialApp_RxSeq = 0xC3;     //接收序列号

    afRegister( (endPointDesc_t *)&SerialApp_epDesc );  //注册端口描述符

    RegisterForKeys( task_id );   //注册按键事件

    halUARTCfg_t uartConfig;    //串口配置结构体
    uartConfig.configured           = TRUE;              // don't care 
    uartConfig.baudRate             = SERIAL_APP_BAUD;    //波特率
    uartConfig.flowControl          = TRUE;               // don't care
    uartConfig.flowControlThreshold = SERIAL_APP_THRESH;    // don't care
    uartConfig.rx.maxBufSize        = SERIAL_APP_RX_SZ;  //最大接收量
    uartConfig.tx.maxBufSize        = SERIAL_APP_TX_SZ;  //最大发送量
    uartConfig.idleTimeout          = SERIAL_APP_IDLE;   // don't care
    uartConfig.intEnable            = TRUE;              // don't care 
    //SerialApp_CallBack:当串口接收到数据后会自动执行这个函数
    uartConfig.callBackFunc= SerialApp_CallBack;    //接收回调函数  
    HalUARTOpen (SERIAL_APP_PORT, &uartConfig);   //打开串口

    #if defined ( LCD_SUPPORTED )
        HalLcdWriteString( "SerialApp", HAL_LCD_LINE_2 );
    #endif
  
    ZDO_RegisterForZDOMsg( SerialApp_TaskID, End_Device_Bind_rsp ); //注册绑定
    ZDO_RegisterForZDOMsg( SerialApp_TaskID, Match_Desc_rsp );      //注册相关事件
}

//事件处理函数
UINT16 SerialApp_ProcessEvent( uint8 task_id, UINT16 events )
{
    (void)task_id;  // Intentionally unreferenced parameter

    if ( events & SYS_EVENT_MSG )       //系统事件
    {
        afIncomingMSGPacket_t *MSGpkt;
        while((MSGpkt=(afIncomingMSGPacket_t*)osal_msg_receive( SerialApp_TaskID )) )
        {
            switch ( MSGpkt->hdr.event )
            {
                case ZDO_CB_MSG:  //ZDO层接收到消息
                    SerialApp_ProcessZDOMsgs( (zdoIncomingMsg_t *)MSGpkt ); //响应消息函数
                break;
          
                case KEY_CHANGE:  //按键改变
                    SerialApp_HandleKeys(((keyChange_t*)MSGpkt)->state,((keyChange_t *)MSGpkt)->keys );     //按键处理函数
                break;

                case AF_INCOMING_MSG_CMD:   //接收到命令,然后执行
                    SerialApp_ProcessMSGCmd( MSGpkt );  //执行进来消息命令的回调函数
                break;

                default:    //默认
                break;
            }
            osal_msg_deallocate( (uint8 *)MSGpkt );   //释放内存
        }
        return ( events ^ SYS_EVENT_MSG );  //返回未处理的事件
    }

    if ( events & SERIALAPP_SEND_EVT )  //自定义事件:串口发送事件
    {
        SerialApp_Send();  //向节点1发送成功接受的response
        return ( events ^ SERIALAPP_SEND_EVT );
    }
    
    ////step 7////
    if ( events & SERIALAPP_RESP_EVT )  //自定义事件:串口响应事件,表示成功接受来自节点1的数据
    {
        SerialApp_Resp();
        return ( events ^ SERIALAPP_RESP_EVT );
    }
    return ( 0 );  // Discard unknown events.
}

//ZigBee设备对象消息处理
static void SerialApp_ProcessZDOMsgs( zdoIncomingMsg_t *inMsg )
{
    switch ( inMsg->clusterID )
    {
        case End_Device_Bind_rsp:   //终端绑定请求
            if ( ZDO_ParseBindRsp( inMsg ) == ZSuccess )  //绑定成功
            {
                HalLedSet( HAL_LED_4, HAL_LED_MODE_ON );  //LED1常亮
            }
            #if defined(BLINK_LEDS)
            else  //绑定失败
            {
                HalLedSet ( HAL_LED_4, HAL_LED_MODE_FLASH );  //LED1闪烁
            }
            #endif
        break;
      
        case Match_Desc_rsp:   //匹配描述符响应(自动匹配消息)
        {
            ZDO_ActiveEndpointRsp_t *pRsp = ZDO_ParseEPListRsp( inMsg );
            if ( pRsp )
            {
                if ( pRsp->status == ZSuccess && pRsp->cnt )
                {
                    SerialApp_TxAddr.addrMode = (afAddrMode_t)Addr16Bit;
                    SerialApp_TxAddr.addr.shortAddr = pRsp->nwkAddr;
                    // Take the first endpoint, Can be changed to search through endpoints
                    SerialApp_TxAddr.endPoint = pRsp->epList[0];
            
                    HalLedSet( HAL_LED_4, HAL_LED_MODE_ON );
                }
                osal_mem_free( pRsp );
            }
        }
        break;
    }
}

//按键处理
void SerialApp_HandleKeys( uint8 shift, uint8 keys )
{
    zAddrType_t txAddr;
  
    if ( shift )
    {
        if ( keys & HAL_KEY_SW_1 )
        {
        }
        if ( keys & HAL_KEY_SW_2 )
        {
        }
        if ( keys & HAL_KEY_SW_3 )
        {
        }
        if ( keys & HAL_KEY_SW_4 )
        {
        }
    }
    else
    {
        if ( keys & HAL_KEY_SW_1 )
        {
        }

        if ( keys & HAL_KEY_SW_2 )      //如果Joystick向右的按键被按下
        {
            HalLedSet ( HAL_LED_4, HAL_LED_MODE_OFF );
            //执行绑定
            txAddr.addrMode = Addr16Bit;
            txAddr.addr.shortAddr = 0x0000; // Coordinator
      
            //终端绑定请求
            ZDP_EndDeviceBindReq( &txAddr, NLME_GetShortAddr(), 
                            SerialApp_epDesc.endPoint,
                            SERIALAPP_PROFID,
                            SERIALAPP_MAX_CLUSTERS,(cId_t *)SerialApp_ClusterList,
                            SERIALAPP_MAX_CLUSTERS,(cId_t *)SerialApp_ClusterList,
                            FALSE );
        }
        if ( keys & HAL_KEY_SW_3 )
        {
        }
        if ( keys & HAL_KEY_SW_4 )  // Joystick向左的按键被按下
        {
            HalLedSet ( HAL_LED_4, HAL_LED_MODE_OFF );
      
            // Initiate a Match Description Request (Service Discovery)
            txAddr.addrMode = AddrBroadcast;
            txAddr.addr.shortAddr = NWK_BROADCAST_SHORTADDR;
      
            //自动匹配
            ZDP_MatchDescReq( &txAddr, NWK_BROADCAST_SHORTADDR,
                        SERIALAPP_PROFID,
                        SERIALAPP_MAX_CLUSTERS,(cId_t *)SerialApp_ClusterList,
                        SERIALAPP_MAX_CLUSTERS,(cId_t *)SerialApp_ClusterList,
                        FALSE );
        }
    }
}

//空中消息处理
void SerialApp_ProcessMSGCmd( afIncomingMSGPacket_t *pkt )
{
    uint8 stat;
    uint8 seqnb;
    uint8 delay;

    switch ( pkt->clusterId )
    {
        ////step 4////
        case SERIALAPP_CLUSTERID1:  //接收到无线数据,发送过来的信息簇ID为 SERIALAPP_CLUSTERID1
            osal_memcpy(&SerialApp_RxAddr, &(pkt->srcAddr), sizeof( afAddrType_t ));

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

            // Keep message if not a repeat packet
            if ( (seqnb > SerialApp_RxSeq) ||                    // Normal
            ((seqnb < 0x80 ) && ( SerialApp_RxSeq > 0x80)) ) // Wrap-around
            {
                ////step 5////
                ////通过串口发送数据到PC机
                if(HalUARTWrite(SERIAL_APP_PORT,pkt->cmd.Data+1,(pkt->cmd.DataLength-1) ) )
                {
                    // Save for next incoming message
                    SerialApp_RxSeq = seqnb;
                    stat = OTA_SUCCESS;
                }
                else
                {
                    stat = OTA_SER_BUSY;  //没写成功状态信号
                }
            }
            else
            {
                stat = OTA_DUP_MSG;     //有重复数据
            }

            // Select approproiate OTA flow-control delay.
            delay=(stat==OTA_SER_BUSY)?SERIALAPP_NAK_DELAY:SERIALAPP_ACK_DELAY;

            SerialApp_RspBuf[0] = stat;
            SerialApp_RspBuf[1] = seqnb;
            SerialApp_RspBuf[2] = LO_UINT16( delay );
            SerialApp_RspBuf[3] = HI_UINT16( delay );
            ////step 6////
            //收到数据后,向节点1发送一个响应事件,跳到SerialApp_ProcessEvent()
            osal_set_event( SerialApp_TaskID, SERIALAPP_RESP_EVT ); //SERIALAPP_RESP_EVT
            osal_stop_timerEx(SerialApp_TaskID, SERIALAPP_RESP_EVT);
        break;
  
        ////step 9////
        //SERIALAPP_CLUSTERID2代表接收到发送成功的response,取消自动重发,如果不,自动重发
        case SERIALAPP_CLUSTERID2:  //接收到响应,发送过来的信息簇ID为 SERIALAPP_CLUSTERID2
            if ((pkt->cmd.Data[1] == SerialApp_TxSeq) &&
            ((pkt->cmd.Data[0]==OTA_SUCCESS)||(pkt->cmd.Data[0]== OTA_DUP_MSG)))
            {
                SerialApp_TxLen = 0;
                //当收到发送成功的response,停止自动重发
                osal_stop_timerEx(SerialApp_TaskID, SERIALAPP_SEND_EVT);
            }
            else
            {
                delay = BUILD_UINT16( pkt->cmd.Data[2], pkt->cmd.Data[3] );
                //没有收到成功的response,自动重发
                osal_start_timerEx( SerialApp_TaskID, SERIALAPP_SEND_EVT, delay );
            }
        break;

        default:
        break;
    }
}

////step 2////
//串口发送函数,将从串口接受到的数据发送出去
static void SerialApp_Send(void)  
{
    #if SERIAL_APP_LOOPBACK         //false,下面的if不执行,此段跳过
    if (SerialApp_TxLen < SERIAL_APP_TX_MAX)
    {
        SerialApp_TxLen+=HalUARTRead(SERIAL_APP_PORT,SerialApp_TxBuf+SerialApp_TxLen+1,                                                    SERIAL_APP_TX_MAX-SerialApp_TxLen);
    }

    if (SerialApp_TxLen)
    {
        (void)SerialApp_TxAddr;
        if (HalUARTWrite(SERIAL_APP_PORT, SerialApp_TxBuf+1, SerialApp_TxLen))
        {
            SerialApp_TxLen = 0;
        }
        else
        {
            osal_set_event(SerialApp_TaskID, SERIALAPP_SEND_EVT);
        }
    }
    #else //执行此段

    //只有当SerialApp_TxLen为0时,才有可能条件为真,不执行
    if (!SerialApp_TxLen && (SerialApp_TxLen = HalUARTRead(SERIAL_APP_PORT, SerialApp_TxBuf+1, SERIAL_APP_TX_MAX)))
    {
        SerialApp_TxBuf[0] = ++SerialApp_TxSeq;
    }

    ////step 3////
    if (SerialApp_TxLen)  //通过AF_DataRequest()函数,将数据从空中发送出去
    {
        if (afStatus_SUCCESS != AF_DataRequest  (&SerialApp_TxAddr,
            (endPointDesc_t *)&SerialApp_epDesc,
            SERIALAPP_CLUSTERID1,
            SerialApp_TxLen+1, SerialApp_TxBuf,
            &SerialApp_MsgID, 0, AF_DEFAULT_RADIUS))
        {
            //发送不成功,才执行此段代码,重新发送
            osal_set_event(SerialApp_TaskID, SERIALAPP_SEND_EVT);
            HalLedSet ( HAL_LED_2, HAL_LED_MODE_FLASH );
        }
    }
    #endif
}

////step 8////
//响应函数
static void SerialApp_Resp(void)
{
    //通过AF_DataRequest函数,将接收成功响应从空中发送出去
    if (afStatus_SUCCESS != AF_DataRequest(&SerialApp_RxAddr,
        (endPointDesc_t *)&SerialApp_epDesc,
        SERIALAPP_CLUSTERID2,
        SERIAL_APP_RSP_CNT, SerialApp_RspBuf,
        &SerialApp_MsgID, 0, AF_DEFAULT_RADIUS))
    {
        osal_set_event(SerialApp_TaskID, SERIALAPP_RESP_EVT); //如果发送失败,重新发送
    }
}

////step 1////
static void SerialApp_CallBack(uint8 port, uint8 event)
{
    (void)port;
    //如果 DMA 中接收到了数据
    if ((event & (HAL_UART_RX_FULL | HAL_UART_RX_ABOUT_FULL | HAL_UART_RX_TIMEOUT)) &&
    #if SERIAL_APP_LOOPBACK
      (SerialApp_TxLen < SERIAL_APP_TX_MAX))
    #else
      !SerialApp_TxLen)
    #endif
    {
        SerialApp_Send();    //调用串口发送函数,将从串口接受到的数据,发送出去
    }
}


 
comments powered by Disqus