第20章 CC2530无线传输质量检测
2018-03-16 10:23
1541 查看
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=1000*rxStats.lostPkts/(rxStats.lostPkts+rxStats.rcvdPkts)(forr
4000
xStats.rcvdPkts>=1)
RSSI计算方法
RSSI值(接收信号强度)理应是从收到的数据包里有效荷载的第一个字节开始计算的,这个值是二进制补码,它必须要用一个偏移值来进行修正,而这个偏移值详见CC2530数据手册。但是,我们通常都是将接收到的最后32个数据包的RSSI值的平均值作为整个过程的RSSI值,而这个值是存储在一个环形缓冲器里的。
2 实验详解
2.1实验目的
1)误包率检测2)掌握实验下载、测试的方法
3)串口发送函数
2.2 实验设备
硬件:PC 机一台ZB2530(底板、核心板、USB 线) 两套仿真器一个软件:2000/XP/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;/*装进要发送的数据*/ } //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
本章参考附件
点击进入相关文章推荐
- Zigbee 学习计划——第5天——无线传输质量检测
- 无线通信:保证传输质量的三种技术
- TP-Link 路由器 如何在现有的环境中改善无线信号传输质量
- ZigBee组网学习笔记(二)--信号传输质量检测
- 无线信号受哪些因素的影响及如何在现有的环境中改善信号传输质量
- Zigbee-cc2530 笔记---Z-stack 无线传输点亮LED
- 一步一步设计制作电路板教程3:温度检测和无线信号传输硬件配置计划
- 无线图像(视频)传输系统ARM9+Atmega16+OV7620+nrf24l01(一)
- [ZigBee] 14、Zigbee无线通信前奏——BasicRF 简单无线点对点传输协议
- 安卓手机与电脑无线传输文件(利用ftp服务)
- 网络视频传输的服务质量(QoS)
- ZStack-CC2530-2.5.1a协议栈数据传输
- MPS430F149单片机之_基于MSP430单片机实现的无线传输模块.c
- 视频质量诊断----雪花噪声检测
- ssh传输文件 命令 及libnids端口扫描攻击检测的实现
- Java代码质量检测评估工具-Findbugs
- 我的openwrt学习笔记(二十四):WIFI无线传输速率相对测试方法_netcat
- Android、pc文件无线双向传输软件
- Java代码质量检测评估工具PMD在线安装