PER(误包率检测)实验是 BasicRF 的第二个实验,和无线点灯一样是没有使用协议栈的点对点通讯。
通过无线点灯大家应该对 zigbee 的发射和接收有个感性的认识,本次实验讲解不会像无线点灯一样讲得那么详细,因为接收发射的过程基本上是一样的,
但也希望初学者能自己认真学习一下这个实验,相信会对无线传输会有一个更清晰的认识。
实验现象:
两块 WeBee 模块通信,一个模块作发射,另外一个模块接收,接收模块通过串口不在 PC 机上显示当前的误包率、 RSSI 值和接收到数据包的个数
实验详解:
例程的源代码 CC2530 BasicRF.rar 是 TI 官网上下载的,打开CC2530BasicRFidesrf05_cc2530iar 里面的 per_test.eww 工程,
由于我们的硬件平台不同于 TI 的开发板,所以我们需要在 per_test 中加入串口发送函数,才能在串口调试助手上看到我们的实验现象。
打开工程,在 application 文件夹点 per_test.c 我们的主要功能函数都在这里 ,在这个.c 文件中添加串口发送函数 INCLUDES 中添加: #include “string.h”
然后继续添加串口初始化和发送函数:
/**************************************************************** 串口初始化函数 ****************************************************************/ void initUART(void) { PERCFG = 0x00; //位置1 P0口 P0SEL = 0x0c; //P0_2,P0_3用作串口(外部设备功能) P2DIR &= ~0XC0; //P0优先作为UART0 U0CSR |= 0x80; //设置为UART方式 U0GCR |= 11; U0BAUD |= 216; //波特率设为115200 UTX0IF = 0; //UART0 TX中断标志初始置位0 } /**************************************************************** 串口发送字符串函数 ****************************************************************/ void UartTX_Send_String(int8 *Data,int len) { int j; for(j=0;j<len;j++) { U0DBUF = *Data++; while(UTX0IF == 0); UTX0IF = 0; } }
很简单地就把串口搞定了,因为只有接收模块才会用到串口,所以串口的初始化只需要放在 appReceiver ( )函数中
下面分析整个工程的发送和接收过程
首先还是要先找到 main.c
1 /*********************************************************************************** 2 * @fn main 3 * 4 * @brief This is the main entry of the "PER test" application. 5 * 6 * @param basicRfConfig - file scope variable. Basic RF configuration data 7 * appState - file scope variable. Holds application state 8 * appStarted - file scope variable. Used to control start and stop of 9 * transmitter application. 10 * 11 * @return none 12 */ 13 void main (void) 14 { 15 uint8 appMode; 16 17 appState = IDLE; 18 appStarted = TRUE;//*****************************by boo 19 20 // Config basicRF 配置 Basic RF 21 basicRfConfig.panId = PAN_ID; 22 basicRfConfig.ackRequest = FALSE; 23 24 // Initialise board peripherals 初始化外围硬件 25 halBoardInit(); 26 27 // Initalise hal_rf初始化 hal_rf 28 if(halRfInit()==FAILED) { 29 HAL_ASSERT(FALSE); 30 } 31 32 // Indicate that device is powered 33 halLedSet(1); 34 35 // Print Logo and splash screen on LCD 36 utilPrintLogo("PER Tester"); 37 38 // Wait for user to press S1 to enter menu 39 // while (halButtonPushed()!=HAL_BUTTON_1);*********************** 40 halMcuWaitMs(350); 41 // halLcdClear();************************************ 42 43 // Set channel
//设置信道,规范要求信道只能为为 11——25。这里选择信道 11 44 // basicRfConfig.channel = appSelectChannel(); 45 basicRfConfig.channel = 0x0B; 46 47 48 // Set mode 49 // appMode = appSelectMode();****************** 50 51 #ifdef MODE_SEND 52 appMode = MODE_TX; 53 #else 54 appMode = MODE_RX; 55 #endif 56 // Transmitter application 57 if(appMode == MODE_TX) { 58 // No return from here 59 appTransmitter(); 60 } 61 // Receiver application 62 else if(appMode == MODE_RX) { 63 // No return from here 64 appReceiver(); 65 } 66 // Role is undefined. This code should not be reached 67 HAL_ASSERT(FALSE); 68 }
大家看注释也应该知道 main.c 做了哪些事情:
1、 一大堆的初始化(都是必须的)
2、 设置信道,发射和接收模块的信道必须一致
3、 选择为发射或者接收模式
发射函数 define MODE_SEND 则进入 appTransmitter();
/*********************************************************************************** * @fn appTransmitter * * @brief Application code for the transmitter mode. Puts MCU in endless * loop * * @param basicRfConfig - file scope variable. Basic RF configuration data * txPacket - file scope variable of type perTestPacket_t * appState - file scope variable. Holds application state * appStarted - file scope variable. Controls start and stop of * transmission * * @return none */ 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(5000); //} } 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 函数完成的任务:
1、初始化 Basic RF
2、设置发射功率
3、设定测试的数据包量
4、配置定时器和 IO
5、初始化数据包载荷
6、进行循环函数,不断地发送数据包,每发送完一次,下一个数据包的序列号自加 1 再发送
接收函数没有 define MODE_SEND 则进入 appReceiver () ///接收函数比较长,
/*********************************************************************************** * @fn appReceiver * * @brief Application code for the receiver mode. Puts MCU in endless loop * * @param basicRfConfig - file scope variable. Basic RF configuration data * rxPacket - file scope variable of type perTestPacket_t * * @return none */ 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 Myper[5]; int8 Myrssi[2]; int8 Myreceive[4]; int32 temp_per; //存放掉包率 int32 temp_receive; //存放接收的包的个数 int32 temp_rssi; //存放前32个rssi值的平均值 initUART(); // 初始化串口 #ifdef INCLUDE_PA uint8 gain; // Select gain (for modules with CC2590/91 only) gain =appSelectGain(); halRfSetGain(gain); #endif // Initialize BasicRF 初始化Basic RF basicRfConfig.myAddr = RX_ADDR; if(basicRfInit(&basicRfConfig)==FAILED) { 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) { halLedToggle(1);//*************P1_0 // 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 } // Check if received packet is the expected packet 检查接收到的数据包是否是所期望收到的数据包 if(rxStats.expectedSeqNum == segNumber) // 是所期望收到的数据包 { rxStats.expectedSeqNum++; } // If there is a jump in the sequence numbering this means some packets in // between has been lost. 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 temp_receive=(int32)rxStats.rcvdPkts; if(temp_receive>1000) { if(halButtonPushed()==HAL_BUTTON_1){ resetStats = TRUE; rxStats.rcvdPkts = 1; rxStats.lostPkts = 0; } } Myreceive[0]=temp_receive/100+'0'; Myreceive[1]=temp_receive%100/10+'0'; Myreceive[2]=temp_receive%10+'0'; Myreceive[3]='