打印
[Zigbee]

CC2530无线传输质量检测

[复制链接]
871|14
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
gwsan|  楼主 | 2019-8-5 11:56 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
1 理论分析
1.1 Packet Error Rate tester工作流程
Packet Error Rate Tester工作流程:启动、发射、接收。
        启动
(1)板载外设、射频IO、系统时钟、中断等初始化(halBoardInit();
(2)BasicRF数据结构体配置(basicRfCfg_t结构体位于basic_rf.h);
(3)BasicRF协议初始化(halRfInit()。

        发射(appTransmitter();)
(1)将刚才配置的BasicRF结构体数据进行初始化;
(2)设置发射功率(halRfSetTxPower(2);
(3)调用basicRfSendPacket();将数据包发送出去。

        接收(appReceiver();)
(1)将刚才配置的BasicRF结构体数据进行初始化;
(2)调用basicRfPacketIsReady();函数检测是否接收到数据;
(3)如果接受到数据,调用basicRfReceive();函数接受数据。
(4)对数据包进行计算,得出PER、RSSI。
这里需要说明一下,Packet Error Rate Tester .eww跟light_switch.eww有点不同,就是数据发送部分必须要设置发射功率。要不然无法完成数据发送。

使用特权

评论回复

相关帖子

沙发
gwsan|  楼主 | 2019-8-5 11:57 | 只看该作者
1.2 Packet Error Rate tester流程图

图1 Packet Error Rate tester流程图

使用特权

评论回复
板凳
gwsan|  楼主 | 2019-8-5 11:57 | 只看该作者
1.3丢包、PER、RSSI计算方法
该实验,虽然重点在于BasicRF协议的讲解,但是,若要好好理解实验本身的内容,那么,最关键的还是关于PER、RSSI的计算,所以下面讲解这两个参数是怎么计算的。为了能够完成PER、RSSI的计算,计算过程中,接收器必须要保持以下的几个变量在计算过程不变。

说明:变量rxStats是perRxStats_t类型的,在per_test.h里定义的。
rxStats.expectedSeqNum        是下一次预期到达的数据包的序列号
(rxStats.expectedSeqNum=收到的数据包+丢失的数据包+1)
rxStats.rssiSum        这是最后的32个数据包的RSSI值的总和。
rxStats.rcvdPkts        收到的正确的数据包量
rxStats.lostPkts        丢失的数据包量

        丢包计算方法
丢失的数据包是通过在序列号跳跃检测出来的。如果一个数据包有一个比变量rxStats.expectedSeqNum还要大的序列号,则两个序列号之间的数据包就会被认为是丢包。这意味着一系列丢失的数据包将不会被检测,直到有一个数据包被正确接收为止。另外,需要说明的是,有错误的数据包不能算是丢包。
        PER计算方法

下面是误包率(PER)的计算公式:
PER=1000∗rxStats.lostPkts/(rxStats.lostPkts+rxStats.rcvdPkts)(forrxStats.rcvdPkts>=1) PER=1000*rxStats.lostPkts/(rxStats.lostPkts+rxStats.rcvdPkts)(forrxStats.rcvdPkts>=1)
PER=1000∗rxStats.lostPkts/(rxStats.lostPkts+rxStats.rcvdPkts)(forrxStats.rcvdPkts>=1)

        RSSI计算方法
RSSI值(接收信号强度)理应是从收到的数据包里有效荷载的第一个字节开始计算的,这个值是二进制补码,它必须要用一个偏移值来进行修正,而这个偏移值详见CC2530数据手册。但是,我们通常都是将接收到的最后32个数据包的RSSI值的平均值作为整个过程的RSSI值,而这个值是存储在一个环形缓冲器里的。


使用特权

评论回复
地板
gwsan|  楼主 | 2019-8-5 11:58 | 只看该作者
2 实验详解
2.1实验目的
1)误包率检测
2)掌握实验下载、测试的方法
3)串口发送函数

2.2 实验设备
硬件:PC 机一台ZB2530(底板、核心板、USB 线) 两套仿真器一个
软件:win7 系统,IAR 8.20 集成开发环境

2.3实验功能
一个模块作发射,每隔一段时间发送一个数据包,每发送一次LED闪烁一次。另外一个模块接收负责接收数据包,每收一个数据包,LED闪烁一次,然后,接收模块通过串口在电脑上显示当前的误包率(PER)、接收信号强度(RSSI)和接收到数据包数量。

注意:如果接收模块接收到的数据不正常,请复位一下发送模块即可。

使用特权

评论回复
5
gwsan|  楼主 | 2019-8-5 11:58 | 只看该作者
2.4实验相关电路图

图2 LED电路图

使用特权

评论回复
6
gwsan|  楼主 | 2019-8-5 11:59 | 只看该作者
2.5实验代码分析
本实验的工作流程,读者可以看回上文图所示的流程图;那么,下面我们就来讲解这个实验本身的难点:PER、RSSI值计算。

下面我们先来看看发送函数,然后,看看接收函数。【具体代码请打开工程阅读。】

        发送函数主要做的工作:

(1)初始化刚才main函数配置的BasicRF结构体数据;
(2)设置发射功率(这个影响到发送距离的);
(3)设置进行一次测试所发送的数据包数量(burstSize=1000;);
(4)配置定时器和IO;
(5)配置定时器和IO(定时发送);
(6)初始化数据包载荷;
(7)将数据包一个一个发送出去。(每发送一个数据,就改变发送序号的字节顺序,然后,在增加序号前将字节顺序改回为主机顺序,最后,增加序号。)

使用特权

评论回复
7
gwsan|  楼主 | 2019-8-5 11:59 | 只看该作者
        接收函数主要做的工作:

(1)初始化串口;
(2)初始化刚才main函数配置的BasicRF结构体数据;
(3)打开接收器;
(4)进入主循环,接收数据包,并且做相关质量检测(即数据处理);
(5)最后,将检测结果发送给电脑显示。

相信大多数读者搞不懂的地方就是第(4)步,那么,究竟怎么去理解?其实,我们可以注意到,第(4)步的数据处理,就是那几个变量、结构体成员之间的加减运算。所以,只要搞懂它们代表什么,各自之间有什么关系,问题就会迎刃而解。

下面列出函数里的几个变量、结构体成员所代表的含义:
segNumber        //数据包序列号
perRssiBuf[RSSI_AVG_WINDOW_SIZE]        //存储RSSI的环形缓冲区
perRssiBufCount        //用于RSSI缓冲区统计
rxStats.expectedSeqNum        //期望接收到的数据包序列号
rxStats.rssiSum        //总的RSSI
perRssiBuf[perRssiBufCounter]        //环形缓冲区
rssi        //新的RSSI
rxStats.lostPkts        //丢包数
rxStats.rcvdPkts        //存放接收的包的个数
程序里PER和RSSI值的计算公式,可以看到,它们跟理论讲解里的计算公式是一
致的。
PER=(int32)((rxStats.lostPkts*1000)/(rxStats.lostPkts+rxStats.rcvdPkts))
RSSI=(0-(int32)rxStats.rssiSum/32)
在CC2530_Software_Examples.pdf里有详细介绍。

使用特权

评论回复
8
gwsan|  楼主 | 2019-8-5 12:01 | 只看该作者
main函数

void main (void)
{
    uint8 appMode ;  
   
    appState = IDLE;
    appStarted = TRUE;
       
    // Config basicRF配置 Basic RF
    basicRfConfig.panId = PAN_ID;//个域网ID
    basicRfConfig.ackRequest = FALSE;//目标确认---FALSE
       
    // Initialise board peripherals
    halBoardInit();//时钟、中断初始化
       
    // Initalise hal_rf
    if(halRfInit()==FAILED) { //BasicRF协议 初始化
                HAL_ASSERT(FALSE);
    }
    // Indicate that device is powered
    halLedSet(1);
       
    // Print ** and splash screen on LCD
    utilPrint**("PER Tester");
    // Wait for user to press S1 to enter menu
        //    while (halButtonPushed()!=HAL_BUTTON_1);***********************
    halMcuWaitMs(350);
        //    halLcdClear();************************************
    // Set channel
    /*
     *basicRfConfig.channel = appSelectChannel();
     *设置信道,发射和接收模块的信道必须一致
     */
    basicRfConfig.channel = 0x0B;
    #ifdef MODE_SEND
     appMode = MODE_TX;
    #else
     appMode = MODE_RX;
    #endif  
    // Transmitter application
    if(appMode == MODE_TX) {
        // No return from here
        appTransmitter(); //数据发送
    }
    // Receiver application
    else if(appMode == MODE_RX) {
        // No return from here
        appReceiver();    //数据接收
    }
    // Role is undefined. This code should not be reached
    HAL_ASSERT(FALSE);
}

使用特权

评论回复
9
gwsan|  楼主 | 2019-8-5 12:01 | 只看该作者
        一大堆的初始化(都是必须的);
        设置信道,发射和接收模块的信道必须一致;
        选择为发射或者接收模式。

使用特权

评论回复
10
gwsan|  楼主 | 2019-8-5 12:01 | 只看该作者
appTransmitter()函数

static void appTransmitter()
{
        uint32 burstSize=0;//数据包数量
        uint32 pktsSent=0;//记录当前所发的数据的个数
        uint8 appTxPower;
        uint8 n;
       
        // Initialize BasicRF               /* 初始化Basic RF */
        basicRfConfig.myAddr = TX_ADDR;
        if(basicRfInit(&basicRfConfig)==FAILED)
        {
                HAL_ASSERT(FALSE);
        }
        // Set TX output power                      /* 设置输出功率 */
        //appTxPower = appSelectOutputPower();
        halRfSetTxPower(2);//HAL_RF_TXPOWER_4_DBM
        //  halRfSetTxPower(appTxPower);
        // Set burst size                 /* 设置进行一次测试所发送的数据包数量 */
        //burstSize = appSelectBurstSize();
        burstSize = 1000;
       
        // Basic RF puts on receiver before transmission of packet, and turns off
        // after packet is sent
        basicRfReceiveOff();
       
        // Config timer and IO          /* 配置定时器和IO */
        //n= appSelectRate();
        appConfigTimer(0xC8);
        //halJoystickInit();
       
        // Initalise packet payload            /* 初始化数据包载荷 */
        txPacket.seqNumber = 0;
        for(n = 0; n < sizeof(txPacket.padding); n++)
        {
                txPacket.padding[n] = n;/*装进要发送的数据*/
        }
       
        //halLcdClear();
        //halLcdWriteCharString(0,HAL_LCD_LINE_1, "Mode:Transmitter");
        //halLcdWriteCharString(0,HAL_LCD_LINE_2, "CENTER to start/stop");
       
    // Main loop          /* 主循环 */
        while (TRUE)
        {
                // Wait for user to start application
                //while(!halJoystickPushed());  // 等待用户启动应用
                //appStartStop();   
               
                while(appStarted)
                {
                        //{
                        //if( halJoystickPushed())
                        //{
                        //  appStartStop();
                        //}
                       
                        if (pktsSent < burstSize)
                        {
                                //if( appState == TRANSMIT_PACKET )
                                //{
                                // Make sure sequence number has network byte order
                                UINT32_HTON(txPacket.seqNumber);  // 改变发送序号的字节顺序
                                basicRfSendPacket(RX_ADDR, (uint8*)&txPacket, PACKET_SIZE);
                               
                                // Change byte order back to host order before increment     /* 在增加序号前将字节顺序改回为主机顺序 */
                                UINT32_NTOH(txPacket.seqNumber);
                                txPacket.seqNumber++;
                               
                                pktsSent++;
                                /*      #ifdef SRF04EB
                                utilLcdDisplayValue(HAL_LCD_LINE_2, "Sent: ", (int32)pktsSent, NULL);
                                #else
                                utilLcdDisplayValue(HAL_LCD_LINE_3, "Sent: ", (int32)pktsSent, NULL);
                #endif*/
                                appState = IDLE;
                                halLedToggle(1);   //改变LED1的亮灭状态
                                halMcuWaitMs(500);
                                //}
                        }
                        else
                                appStarted = !appStarted;
                       
                        //}
                       
                        // Reset statistics and sequence number/* 复位统计和序号 */
                        pktsSent = 0;
                        //txPacket.seqNumber = 0;
                        //halLcdClear();
                        //halLedClear(3);
                        //halLcdWriteCharString(0,HAL_LCD_LINE_1, "Mode:Transmitter");
                        //halLcdWriteCharString(0,HAL_LCD_LINE_2, "CENTER to start/stop");
                }
        }
}

使用特权

评论回复
11
gwsan|  楼主 | 2019-8-5 12:01 | 只看该作者
appTransmitter 函数完成的任务:

        初始化 Basic RF;
        设置发射功率;
        设定测试的数据包量;
        配置定时器和 IO;
        初始化数据包载荷;
        进行循环函数,不断地发送数据包,每发送完一次,下一个数据包的序列号自加 1 再发送。

使用特权

评论回复
12
gwsan|  楼主 | 2019-8-5 12:02 | 只看该作者
appReceiver ()函数

static void appReceiver()
{
        uint32 segNumber=0;                              // 数据包序列号
        int16 perRssiBuf[RSSI_AVG_WINDOW_SIZE] = {0};    // Ring buffer for RSSI 存储RSSI的环形缓冲区
        uint8 perRssiBufCounter = 0;                     // Counter to keep track of the 计数器用于RSSI缓冲区统计
        // oldest newest byte in RSSI
        // ring buffer
        perRxStats_t rxStats = {0,0,0,0};      
        int16 rssi;
        uint8 resetStats=FALSE;
        int8 strPer[5];       //用于串口发送之前的数据处理---掉包率   
        int8 strRssi[2];        //用于串口发送之前的数据处理---接收的包的个数
        int8 strCount[4];       //用于串口发送之前的数据处理---rssi值
        int32 nPer;           //存放掉包率
        int32 nRssi;          //存放前32个rssi值的平均值   
        int32 nCount;         //存放接收的包的个数
        initUART();           // 初始化串口
#ifdef INCLUDE_PA
        uint8 gain;
       
        // Select gain (for modules with CC2590/91 only)
        gain =appSelectGain();
        halRfSetGain(gain);
#endif
   
    // Initialize BasicRF
        basicRfConfig.myAddr = RX_ADDR;
        if(basicRfInit(&basicRfConfig)==FAILED) /* 初始化 BasicRF 结构体数据*/
        {
                HAL_ASSERT(FALSE);
        }
        basicRfReceiveOn();
       
        //halLcdClear();
   
        //halLcdWriteCharString(0,HAL_LCD_LINE_1, "Mode:Receiver");
        //halLcdWriteCharString(0,HAL_LCD_LINE_3, "Ready");
        /* 主循环 */
        UartTX_Send_String("PER_test: ",strlen("PER_test: "));
    // Main loop
        while (TRUE)
        {
                while(!basicRfPacketIsReady());  // 等待新的数据包
                if(basicRfReceive((uint8*)&rxPacket, MAX_PAYLOAD_LENGTH, &rssi)>0) {
                        halLedSet(3);//P1_4
                       
                        // Change byte order from network to host order
                        UINT32_NTOH(rxPacket.seqNumber);  // 改变接收序号的字节顺序
                        segNumber = rxPacket.seqNumber;
            
                        // If statistics is reset set expected sequence number to
                        // received sequence number 若统计被复位,设置期望收到的数据包序号为已经收到的数据包序号     
                        if(resetStats)
                        {
                                rxStats.expectedSeqNum = segNumber;
                               
                                resetStats=FALSE;
                        }      
                        // Subtract old RSSI value from sum
                        rxStats.rssiSum -= perRssiBuf[perRssiBufCounter];  // 从sum中减去旧的RSSI值
                        // Store new RSSI value in ring buffer, will add it to sum later
                        perRssiBuf[perRssiBufCounter] =  rssi;  // 存储新的RSSI值到环形缓冲区,之后它将被加入sum
                       
                        rxStats.rssiSum += perRssiBuf[perRssiBufCounter];  // 增加新的RSSI值到sum
                        if(++perRssiBufCounter == RSSI_AVG_WINDOW_SIZE) {
                                perRssiBufCounter = 0;      // Wrap ring buffer counter
                        }
                       
                       
                        // 检查接收到的数据包是否是所期望收到的数据包
                        if(rxStats.expectedSeqNum == segNumber)  // 是所期望收到的数据包
                        {
                                rxStats.expectedSeqNum++;  
                        }
                       
            // 不是所期望收到的数据包(大于期望收到的数据包的序号)认为丢包
                        else if(rxStats.expectedSeqNum < segNumber)  
                        {
                                rxStats.lostPkts += segNumber - rxStats.expectedSeqNum;
                                rxStats.expectedSeqNum = segNumber + 1;
                        }
                        // If the sequence number is lower than the previous one, we will assume a
                        // new data burst has started and we will reset our statistics variables.
                        else  // (小于期望收到的数据包的序号)
                        {      
                                // Update our expectations assuming this is a new burst 认为是一个新的测试开始,复位统计变量
                                rxStats.expectedSeqNum = segNumber + 1;
                                rxStats.rcvdPkts = 0;
                                rxStats.lostPkts = 0;
                        }
                        rxStats.rcvdPkts++;
                       
                        // reset statistics if button 1 is pressed
            nCount = (int32)rxStats.rcvdPkts;
                        if(nCount > 1000)
            {
                                if(halButtonPushed()==HAL_BUTTON_1){
                                        resetStats = TRUE;
                                        rxStats.rcvdPkts = 1;
                                        rxStats.lostPkts = 0;
                                }
            }
                       
            //串口输出
            strCount[0]=nCount/100+'0';
            strCount[1]=nCount%100/10+'0';
            strCount[2]=nCount%10+'0';
            strCount[3]='\0';
            UartTX_Send_String("RECV:", strlen("RECV:"));
            UartTX_Send_String(strCount, 4);
            UartTX_Send_String("  ", strlen("  "));   
            
            nPer = (int32)((rxStats.lostPkts*1000)/(rxStats.lostPkts+rxStats.rcvdPkts));
            strPer[0]=nPer/100+'0';
            strPer[1]=nPer%100/10+'0';
            strPer[2]='.';
            strPer[3]=nPer%10+'0';
            strPer[4]='%';
            UartTX_Send_String("PER:",strlen("PER:"));
            UartTX_Send_String(strPer,5);
            UartTX_Send_String("  ",strlen("  "));
                       
            nRssi=(0-(int32)rxStats.rssiSum/32);
            strRssi[0]=nRssi/10+'0';
            strRssi[1]=nRssi%10+'0';
            UartTX_Send_String(" RSSI:-",strlen(" RSSI:-"));
            UartTX_Send_String(strRssi,2);        
            UartTX_Send_String("\r\n",strlen("\r\n"));
                       
            halLedClear(3);
                               
            halMcuWaitMs(300);
            // Update LCD
            // PER in units per 1000
                        /*         utilLcdDisplayValue(HAL_LCD_LINE_1, "PER: ", (int32)((rxStats.lostPkts*1000)/(rxStats.lostPkts+rxStats.rcvdPkts)), " /1000");
            utilLcdDisplayValue(HAL_LCD_LINE_2, "RSSI: ", (int32)rxStats.rssiSum/32, "dBm");
            #ifndef SRF04EB
            utilLcdDisplayValue(HAL_LCD_LINE_3, "Recv'd: ", (int32)rxStats.rcvdPkts, NULL);
            #endif
            halLedClear(3);*/
        }
               
  }
}

使用特权

评论回复
13
gwsan|  楼主 | 2019-8-5 12:02 | 只看该作者
接收函数的作用:

        串口在此初始化
        初始化 Basic RF
        不断地接收数据包,并检查数据包序号是否为期望值,作出相应处理
        串口打印出,接收包的个数\误包率及上 32 个数据包的 RSSI 值的平均值

使用特权

评论回复
14
gwsan|  楼主 | 2019-8-5 12:03 | 只看该作者
2.6实验现象
和无线点灯一样,一个EB2530(终端A)定义为发射模块,另一个EB2530(终端B)定义为接收模块。接收模块通过串口不断发数据到PC 机上显示当前的误包率、RSSI 值和接收到数据包的个数。

第一步:下载发射模块,在 per_test.c 中,不要屏蔽#define MODE_SEND编译下载到发射模块(A板)

第二步:下载接收模块,屏蔽#define MODE_SEND编译下载到接收模块(B板)

开发板分别上电,B 开发板通过USB 线接到电脑上,同时打开串口工具,就会看到串口显示出掉包情况,由于距离较近,所以丢包不是很明显。



图3


使用特权

评论回复
15
phosphate| | 2019-8-6 14:41 | 只看该作者
感谢分享!学习一下

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

68

主题

3426

帖子

1

粉丝