DAVID!..
New observation: active USB interface decreases radio reliability!
In the diagnostic radio test I discussed which is directly based on radio_test_rx & tx, I had added code to turn on the Yellow LED anytime a good packet ( w/good CRC) is received, or the Red LED anytime a packet is received with bad CRC. (a timer makes sure they are both turned off after about 100 ms). So the net result is that by simply observing the receiving wixel, you can get a reasonable idea of the ratio of good to bad packets. I also send some formatted diagnostics to a the PC 9as in radio_test_rx), which i can view via a terminal program.
So with that in mind, here is my new observation. If I disconnect the terminal program from the COM port and wait about 5 minutes, OR if I go into my device manager (Win-Xp), and disable that COM port, the radio communication improves tremendously. Likewise, if i re-enable the port, the LED pattern instantly reveals a return to the poor radio reliability.
Obviously without the diagnostic output its impossible to exactly quantify the improvement, but sending packets every 10 mS and seeing the Yellow LED nearly steadily, with RED (bad CRC) as seldom as a few per minute is VERY significant!!! I don’t know what other experimenters are experiencing, and I don’t know yet whether I’d have the same observation if I transmitted my diagnostic messages using one of the MCU’s internal UARTs directly without the USB. But one other person that posted a few messages back, whose code was based on the wireless serial app, had a similar case of unreliable radio communication.
It may be something as insidious as a collision of interrupts or something more benign. maybe the wixel has no mechanism for queuing up radio messages in a an interrupt driven circular buffer, or maybe the problem just that it hasn’t been done yet? I’d think anything less is not giving the best possible priority to processing incoming packets, so that would explain why other time consuming processes (like maybe some delays in USB maintenance) might cause lots of messages to overwrite each other.
One thing seems very clear and repeatable… if you use the wixel to develop a stand alone radio link application that does not involve USB->COM port or similar serial communication, the radio reliability is much much better. That’s fine for me right now, because my final application won’t need USB or serial communication. But for the benefit of other Wixel users, it would be advantageous if Polulo could duplicated these results and track down the underlying reason. If you don’t have time, but think I’m probably right about an ISR being needed to buffer incoming radio packets, I’ll be happy to explore it.
If Polulu (or anyone) is interested in perusing this further and seeing the problem I’m talking about, the source is here…
/* radio test experiment, Randy constan (PeterPan on wixel forum,
* email: r_constan@elfintechnologies.com
This project forms the basis of what will become a more complex remote control app,
but is currently used only to test the radio reliability. You use this same
application for either the transmitter and receiver, based on configuration variables.
If the app is set as a transmitter, you can set the number of milliseconds
between packets sent. You don't need to do that on the receiver side. The structure
being sent within the packet is largely unused right now. in the transmitter app, the RED LED
will toggle every time a packet is sent, but will pretty much seem to stay on
if the transmit rate is fast, such as 10mS
On the receive side, the Yellow LED will flash every time a GOOD packet is received, or
if a packet is received but fails the internal CRC check, the RED LED will flash.
If the transmit side is sending quickly, such as 10ms apart, it will not be obvious
when each packet occurs, as the LEDs are both reset to OFF only every 100mS. But
it is still a very good visual indicator of how prelaible the radion link is.
If the receive side is used via a USB port, you can also use a terminal program
to view some diagnostic information. Once per second, a report will be generated
like the below...
id=6703, rs= 610, lq=97 tp/s=98 ep/s=22
id=6803, rs= 605, lq=103 tp/s=99 ep/s=20
id=6903, rs= 611, lq=104 tp/s=98 ep/s=33
id=7003, rs= 611, lq=104 tp/s=98 ep/s=18
id=7103, rs= 605, lq=103 tp/s=99 ep/s=24
id=7203, rs= 612, lq=106 tp/s=98 ep/s=25
id=7302, rs= 598, lq=105 tp/s=100 ep/s=29
the id is simply the latest message number, which is one of the things
sent in the packet. in this case the transmitter was set to send a message every 10
mS, and since the receiver is generating a diagnostic msg every second,
you can see that the message IDs are about 100 apart. The "rs" and "lq" are the average
values of radio quality over the number of messages received since the last report.
You'll have to look up the library calls radioRssi() and radioLqi() to make sense of them.
The tp/s is the total packets received per second, and the ep/s is the number of
error packets per second.
Note that this rate of errors does seem disappointingly high! However, the author has noted
that the radio communication, as evidenced by observing the RED and YELLOW LEDs
at the receiving end is much much better when the diagnostic information is not being monitored.
If the COM port created by the receiving wixel in the PC (via the USB port)
is inactive, either because it has been disabled in the device manager (Windows), or
the terminal program used to monitor the diags has been shut down for more than about 5 minutes,
(or the receiver is simply being run from an external supply with no USB), the author has
observed a nearly constant YELLOW indication with only a few RED flickers over the course of
a minute, when there was no USB activity. Note that in the authors test, the transmitting wixel
was always unconnected to the PC, and powered via a 3 alkaline cell (4.5VDC) supply.
Another poster on the Pololu wixel forum has stated that he was also having poor radio reliability,
and I believe he was not using a PC link, but a UART.
It is possible that the wixel is doing too many things at once, or it may be that
interrupts and priorities are not being optimally managed in the particular
library calls used here. However, the calls, taken from radio_test_tx and radio_test_rx
examples, were chosen to enable the fastest possible transfer speed of raw packets.
The discussion thread I started on this topic is on the wixel forum, and is called
"short discussion on radio reliability ". If anyone has insights on what might be going on,
and can suggest ways of improving the ability of a receiver to be less affected by
USB activity (or perhaps UART activity), please post your results there! :-)
*/
#include <wixel.h>
#include <radio_registers.h>
#include <stdio.h>
#include <gpio.h>
#include <usb.h>
#include <usb_com.h>
int32 CODE param_radio_channel = 128;
int32 CODE param_xmit_rateMS = 100; // how often to send a packet
int32 CODE param_transmit = 1; // set 1 for transmitter, 0 for receiver
int32 CODE param_enabDiags = 1; // allows diagnostics via USB-> COMx. ( receiver only right now)
#define NUM_CTRLS 4
// This is a structure I'm planning on using for a future project. 90% of it
// is unused in this test.
typedef struct {
uint8 buttonBits; // will eventually contain bitmapped button states
uint8 ctrl_count[NUM_CTRLS]; // up/down counts for remote digital potentiometers
uint8 future1; // place holders, so backward compatible additions
uint8 future2;
// DIAGNOSTIC BYTES
uint8 msgDiagToggle; // packetsSent & 1;
uint8 msgSndsPerSecond; // unused currently
uint16 msgID; // incrementing packet counter
} MSG_BUF;
#define RADIO_PACKET_SIZE (sizeof(MSG_BUF))
// both xmit and recv need size byte, receive needs 2 additional
static volatile XDATA uint8 packet[1 + RADIO_PACKET_SIZE + 2 ];
MSG_BUF * pMsgBuf = (MSG_BUF *)&packet[1]; // struct ptr for convenience
// vars common to both xmit & recv
static uint16 DATA currMsgID = 0;
// receiver only vars, many are just for statistics right now.
enum {NO_PKT=0, GOOD_PKT, BAD_CRC_PKT};
static uint8 DATA newPktRecvd = NO_PKT;
static int16 DATA buttonBits = 0;
static int16 DATA rssiAvg = 0;
static uint16 DATA lqiAvg = 0;
static int16 DATA rssiTot = 0;
static uint16 DATA lqiTot = 0;
static uint16 DATA totPktsRecvdPerSec =0;
static uint16 DATA totErrorPktsRecvdPerSec =0;
static uint16 DATA badPktTempCount = 0;
static uint16 DATA goodPktTmpCount =0;
static uint16 DATA lastPacketReceivedTime = 0;
static uint16 DATA totPacketsRecvd = 0;
// transmitter only vars
static uint16 lastXmitTime = 0;
// a little over complicated, as it is used for both xmit and recv variations
void updateLeds(int x)
{
usbShowStatusWithGreenLed();
if (param_transmit)
{
LED_YELLOW(0);
LED_RED_TOGGLE(); // toggel red each time I send a packet
}
else
{
// Recv mode. Yellow = good packet, red = bad. Makes it easier to
// troubleshoot without diagnostics being sent over USB port.
if (x == GOOD_PKT) LED_YELLOW(1);
if( x == BAD_CRC_PKT) LED_RED(1);
if (x==0) { LED_YELLOW(0); LED_RED(0);}
}
}
/*
* Most of this is verbatum from the sample radio_test_rx/tx projects, except
* that code common to both xmit and recieve are separated from code used
* for each individual case. In addition, for the transmit code,
* the whole packet buffer is simply initialized to 0 instead of alpha characters.
*/
void perTestInit()
{
radioRegistersInit();
CHANNR = param_radio_channel;
PKTLEN = RADIO_PACKET_SIZE;
MCSM0 = 0x14; // Auto-calibrate when going from idle to RX or TX.
MCSM1 = 0x00; // Disable CCA. After RX, go to IDLE. After TX, go to IDLE.
// We leave MCSM2 at its default value.
// this DMA config is always the same for DMA->radio transfers
dmaConfig.radio.DC6 = 19; // WORDSIZE = 0, TMODE = 0, TRIG = 19 (always 19 for RADIO)
if (param_transmit) // here we'll have to add more I/O pins later
{
uint8 i;
IOCFG2 = 0b011011; // put out a PA_PD signal on P1_7 (active low when the radio is in TX mode)
dmaConfig.radio.SRCADDRH = (unsigned int)packet >> 8;
dmaConfig.radio.SRCADDRL = (unsigned int)packet;
dmaConfig.radio.DESTADDRH = XDATA_SFR_ADDRESS(RFD) >> 8;
dmaConfig.radio.DESTADDRL = XDATA_SFR_ADDRESS(RFD);
dmaConfig.radio.LENL = 1 + RADIO_PACKET_SIZE;
dmaConfig.radio.VLEN_LENH = 0b00100000; // Transfer length is FirstByte+1
dmaConfig.radio.DC7 = 0x40; // SRCINC = 1, DESTINC = 0, IRQMASK = 0, M8 = 0, PRIORITY = 0
for(i = 1; i < sizeof(packet); i++) packet[i] = 0; // flush whole array w/0 for now
packet[0] = RADIO_PACKET_SIZE;
RFST = 4; // Switch radio to Idle mode.
}
else
{
dmaConfig.radio.SRCADDRH = XDATA_SFR_ADDRESS(RFD) >> 8;
dmaConfig.radio.SRCADDRL = XDATA_SFR_ADDRESS(RFD);
dmaConfig.radio.DESTADDRH = (unsigned int)packet >> 8;
dmaConfig.radio.DESTADDRL = (unsigned int)packet;
dmaConfig.radio.LENL = 1 + RADIO_PACKET_SIZE + 2;
dmaConfig.radio.VLEN_LENH = 0b10000000; // Transfer length is FirstByte+3
dmaConfig.radio.DC7 = 0x10; // SRCINC = 0, DESTINC = 1, IRQMASK = 0, M8 = 0, PRIORITY = 0
DMAARM |= (1<<DMA_CHANNEL_RADIO); // Arm DMA channel
RFST = 2; // Switch radio to RX mode.
}
}
/*
* based on sendRadioBursts() in the test_radio_tx.c project,
* but here I am only sending a single packet. Also, for now
* the only thing changing within the packet is toggling (1->0)
* byte, and a rolling message id, also used
*/
int sendRadioPacket()
{
uint16 time = (uint16)getMs();
// nothing to do if configured time hasn't passed,
// or last transmission not complete,
if ((uint16)(time - lastXmitTime) < param_xmit_rateMS || MARCSTATE != 1)
{
return 0;
}
lastXmitTime = time; // reset timer.
// update some diagnostic data in packet
pMsgBuf->msgDiagToggle = currMsgID & 1;
pMsgBuf->msgID = currMsgID++;
packet[0] = RADIO_PACKET_SIZE; // redundant
RFIF &= ~(1<<4); // Clear IRQ_DONE
DMAARM |= (1<<DMA_CHANNEL_RADIO); // Arm DMA channel
RFST = 3; // Switch radio to TX
updateLeds(1); // toggle red led to show activity.
return 1;
}
/* based on receiveRadioBursts() from radio_test_rx project. in this case,
*
*
*/
// RF -> report buffer
// return non zero when its time for a report
int receiveRadioPacket()
{
static uint32 lastTime = 0;
static uint32 lastLedTime =0;
uint32 timeNow = getMs();
int reportDataReady = 0;
if (!(RFIF & (1<<4))) newPktRecvd = NO_PKT;
// if we're here, we got something!
else newPktRecvd = (radioCrcPassed()) ? GOOD_PKT : BAD_CRC_PKT;
switch(newPktRecvd)
{
case NO_PKT: break;
case GOOD_PKT:
goodPktTmpCount++;
totPacketsRecvd++;
currMsgID = pMsgBuf->msgID;
lastPacketReceivedTime = (uint16)timeNow;
rssiAvg += radioRssi();
lqiAvg += radioLqi();
buttonBits = pMsgBuf->buttonBits;
break;
case BAD_CRC_PKT:
badPktTempCount++;
totPacketsRecvd++;
break;
}
// keep track of good and bad packets/second.
if ((uint16)(timeNow - lastTime) > 1000)
{
lastTime = timeNow;
totPktsRecvdPerSec = goodPktTmpCount + badPktTempCount;
totErrorPktsRecvdPerSec = badPktTempCount;
if (!goodPktTmpCount) buttonBits = -1; // in case we never got a good one
goodPktTmpCount = badPktTempCount = 0;
rssiAvg = rssiTot / totPktsRecvdPerSec;
lqiAvg = lqiTot/ totPktsRecvdPerSec;
rssiTot = lqiTot = 0;
reportDataReady = 1;
}
if (newPktRecvd != NO_PKT) // prep for next msg.
{
rssiTot += radioRssi();
lqiTot += radioLqi();
RFIF &= ~(1<<4); // Clear IRQ_DONE
DMAARM |= (1<<DMA_CHANNEL_RADIO); // Arm DMA channel
RFST = 2; // Switch radio to RX mode.
updateLeds(newPktRecvd);
}
else
{
if ((uint16)(timeNow - lastLedTime) > 100)
{
lastLedTime = timeNow;
updateLeds(0);
}
}
return reportDataReady;
}
/*
* display some statistics to show how well communication is progressing.
* This is used for the receiver only.
*/
void reportResults()
{
static uint16 lastReportSentTime;
if (usbComTxAvailable() >= 64 && param_enabDiags != 0)
{
uint8 XDATA report[80];
uint8 reportLength = sprintf(report, "\r\n id=%-4d, rs=%5d, lq=%-5d tp/s=%-4d ep/s=%-4d" ,
currMsgID, rssiAvg, lqiAvg,
totPktsRecvdPerSec, totErrorPktsRecvdPerSec );
usbComTxSend(report, reportLength);
}
}
void main()
{
systemInit();
usbInit();
perTestInit();
while(1)
{
boardService();
usbComService();
if (param_transmit)
{
sendRadioPacket();
}
else
{
if (receiveRadioPacket())
{
reportResults();
}
}
}
}