《ZigBee开发笔记》第三部分 进阶篇-第3章 CC2530无线传输质量检测

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013162035/article/details/79577983

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有点不同,就是数据发送部分必须要设置发射功率。要不然无法完成数据发送。

1.2 Packet Error Rate tester流程图

这里写图片描述

图1 Packet Error Rate tester流程图

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=1000rxStats.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值,而这个值是存储在一个环形缓冲器里的。

2 实验详解

2.1实验目的

1)误包率检测
2)掌握实验下载、测试的方法
3)串口发送函数

2.2 实验设备

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

2.3实验功能

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

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

2.4实验相关电路图

这里写图片描述

图2 LED电路图

2.5实验代码分析

本实验的工作流程,读者可以看回上文图所示的流程图;那么,下面我们就来讲解这个实验本身的难点:PER、RSSI值计算。

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

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

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

 接收函数主要做的工作:

(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里有详细介绍。

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 Logo and splash screen on LCD
    utilPrintLogo("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);
}

 一大堆的初始化(都是必须的);
 设置信道,发射和接收模块的信道必须一致;
 选择为发射或者接收模式。

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");
		}
	}
}

appTransmitter 函数完成的任务:

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

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);*/
	}
		
  }
}

接收函数的作用:

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

2.6实验现象

和无线点灯一样,一个EB2530(终端A)定义为发射模块,另一个EB2530(终端B)定义为接收模块。接收模块通过串口不断发数据到PC 机上显示当前的误包率、RSSI 值和接收到数据包的个数。

第一步:下载发射模块,在 per_test.c 中,不要屏蔽#define MODE_SEND编译下载到发射模块(A板)
这里写图片描述
第二步:下载接收模块,屏蔽#define MODE_SEND编译下载到接收模块(B板)
这里写图片描述
开发板分别上电,B 开发板通过USB 线接到电脑上,同时打开串口工具,就会看到串口显示出掉包情况,由于距离较近,所以丢包不是很明显。

这里写图片描述

图3

本章参考附件

点击进入

展开阅读全文

没有更多推荐了,返回首页