您的位置:首页 > 其它

第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

本章参考附件

点击进入
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: