RFTXRXIF never asserted

Hi,

I’d like to write a packet sniffer for a device I don’t know the protocol of. I know the frequencies and data transmission rates though, so I thought that simply dumping every byte that’s received might help me to understand the protocol. I know that the Wixel comes with a radio_sniffer app, but I’d like to understand better how the radio works and in particular don’t want to rely on the assumed packet logic in there (which of course is perfect for most applications, but not ideal when you simply want to dump everything and for example don’t even know whether the packet length is encoded in the first byte of the packet).

However, I can’t even get the very simple example below to receive anything at all. It doesn’t use interrupts or DMA transfers and I’m really stumped as to why RFTXRXIF is never asserted. It’s inspired by the simple test code here, so I would hope that it’s not completely wrong. But it doesn’t even receive a single byte. The standard radio_sniffer app works fine though, meaning that it does receive something, thus I’m sure it must be an error in the code. I’ve marked the registers I’ve changed w.r.t. radio_sniffer app below with “(!)”. Any help much appreciated!

#include <cc2511_map.h>
#include <board.h>
#include <usb.h>
#include <usb_com.h>

#include <stdio.h>
#include <string.h>
#include <ctype.h>

// The RFST register is how we tell the radio to do something, and these are the
// command strobes we can write to it:
#define SFSTXON 0
#define SCAL    1
#define SRX     2
#define STX     3
#define SIDLE   4

void putchar(char c)
{
    usbComTxSendByte(c);
}

char nibbleToAscii(uint8 nibble)
{
  nibble &= 0xF;
  return (nibble <= 0x9) ? ('0' + nibble) : ('A' + nibble - 0xA);
}

void main()
{
  unsigned counter;

  systemInit();
  usbInit();

  // Transmit power: one of the highest settings, but not the highest.
  PA_TABLE0 = 0xFE;

  // Set the center frequency of channel 0 to 2403.47 MHz.
  // Freq = 24/2^16*(FREQ[23:0]) = 24/2^16*(0x642500) = 2403.47 MHz
  FREQ2 = 0x64;
  FREQ1 = 0x25;
  FREQ0 = 0x00;

  // Note: We had to modify MDMCFG1 from the settings given by
  // SmartRF Studio to be compatible with the datasheet.
  // (NUM_PREAMBLE should be 8 at 500 kbps and having it be high is a good idea in general).
  // MDMCFG1.FEC_EN = 0 : Disable Forward Error Correction
  // MDMCFG1.NUM_PREAMBLE = 100 : Minimum number of preamble bytes is 8.
  // MDMCFG1.CHANSPC_E = 11 : Channel spacing exponent.
  // MDMCFG0.CHANSPC_M = 0x87 : Channel spacing mantissa.
  // Channel spacing = (256 + CHANSPC_M)*2^(CHANSPC_E) * f_ref / 2^18
  // So the center of channel 255 is
  //   2403.47 + 255 * ((256 + 0x87)*2^(3) * 24/2^18) = 2476.50 MHz
  // NOTE: The radio's Forward Error Correction feature requires CLKSPD=000.
  MDMCFG1 = 0x43;
  MDMCFG0 = 0x87;  // Modem Configuration

  // Controls the FREQ_IF used for RX.
  // This is affected by MDMCFG2.DEM_DCFILT_OFF according to p.212 of datasheet.
  FSCTRL1 = 0x0A;  // Frequency Synthesizer Control
  FSCTRL0 = 0x00;  // Frequency Synthesizer Control

  // Sets the data rate (symbol rate) used in TX and RX.  See Sec 13.5 of the datasheet.
  // Also sets the channel bandwidth.
  // We tried different data rates: 375 kbps was pretty good, but 400 kbps and above caused lots of packet errors.
  // NOTE: If you change this, you must change RSSI_OFFSET in radio_registers.h
  MDMCFG4 = 0x1D;  MDMCFG3 = 0xDE; // Modem configuration (data rate = 350 kbps, bandwidth = 600 kHz).

  // MDMCFG2.DEM_DCFILT_OFF = 0, enable digital DC blocking filter before
  //   demodulator.  This affects the FREQ_IF according to p.212 of datasheet.
  // MDMCFC2.MANCHESTER_EN = 0 is required because we are using MSK (see Sec 13.9.2)
  // MDMCFG2.MOD_FORMAT = 111: MSK modulation
  // MDMCFG2.SYNC_MODE = 000: No preamble/sync threshold, for sniffing (!)
  MDMCFG2 = 0x70;  // Modem Configuration

  //DEVIATN = 0x00;  // Modem Deviation Setting.  No effect because we are using MSK.
  // See Sec 13.9.2.

  FREND1 = 0xB6;   // Front End RX Configuration (adjusts various things, not well documented)
  FREND0 = 0x10;   // Front End TX Configuration (adjusts current TX LO buffer, not well documented)

  // F0CFG and BSCFG configure details of the PID loop used to correct the
  // bit rate and frequency of the signal (RX only I believe).
  FOCCFG = 0x1D;  // Frequency Offset Compensation Configuration
  BSCFG = 0x1C;   // Bit Synchronization Configuration

  // AGC Control:
  // This affects many things, including:
  //    Carrier Sense Absolute Threshold (Sec 13.10.5).
  //    Carrier Sense Relative Threshold (Sec 13.10.6).
  AGCCTRL2 = 0xC7;
  AGCCTRL1 = 0x00;
  AGCCTRL0 = 0xB2;

  // Frequency Synthesizer registers that are not fully documented.
  FSCAL3 = 0xEA;
  FSCAL2 = 0x0A;
  FSCAL1 = 0x00;
  FSCAL0 = 0x11;

  // Mostly-undocumented test settings.
  // NOTE: The datasheet says TEST1 must be 0x31, but SmartRF Studio recommends 0x11.
  TEST2 = 0x88;
  TEST1 = 0x31;//0x31;//0x11;
  TEST0 = 0x09;//0x09;//0x0B;

  // Packet control settings.
  //PKTCTRL1 = 0x04;
  //PKTCTRL0 = 0x45; // Enable data whitening, CRC, and variable length packets.
  PKTCTRL1 = 0; // No address checks, no append (!).
  PKTCTRL0 = 0x01; // No whitening, no CRC checks, variable length packets (!).

  // MCSM.FS_AUTOCAL = 1: Calibrate freq when going from IDLE to RX or TX (or FSTXON).
  MCSM0 = 0x14;    // Main Radio Control State Machine Configuration
  MCSM1 = 0x05;    // Disable CCA.  After RX, go to FSTXON.  After TX, go to FSTXON.
  MCSM2 = 0x07;    // NOTE: MCSM2 also gets set every time we go into RX mode.

  //IEN2 |= 0x01;    // Enable RF general interrupt (!)
  //RFIM = 0xF0;     // Enable these interrupts: DONE, RXOVF, TXUNF, TIMEOUT (!)

  //EA = 1;          // Enable interrupts in general (!)

  PKTLEN = 0xFF; // Set max variable packet length (!).
  CHANNR = 0; // Set radio channel.

  //S1CON |= 3; // Disabled because we don't use the RF general interrupt (!).

  RFST = SRX; // Switch radio to RX.

  for (counter = 0; ; ++counter) {
    boardService();
    usbShowStatusWithGreenLed();
    usbComService();

    // Signal that we're still alive.
    if ((counter & ((1u << 20) - 1)) == 0) {
      LED_RED(1);
      putchar('.');
      putchar('\r');
      putchar('\n');
    } else {
      LED_RED(0);
    }

    if (RFTXRXIF) { // Attempt to read packets.
      uint8 byte;
      RFTXRXIF = 0;
      byte = RFD;
      putchar(nibbleToAscii(byte >> 4));
      putchar(nibbleToAscii(byte));
      putchar('\r');
      putchar('\n');
    }
  }
}

Cheers,
squid

Hello.

Your calls to usbComTxSendByte might be causing trouble because you are never checking usbComTxAvailable to make sure there is enough space in the buffer. The documentation of those functions is here:

pololu.github.io/wixel-sdk/usb__com_8h.html

Are you able to share more details about the device you are reverse engineering? Are you sure it has a CC2511 or a compatible radio based on the CC2500? It sounds like this device uses a lot of the same settings that we use in the Wixel SDK; was it developed with the Wixel SDK?

It took a lot of patience and experimentation to get the Wixel’s radio working properly, and it is easy for one incorrect setting to mess it up. Of the settings you changed, changing MDMCFG2.SYNC_MODE to 0 to disable the preamble and sync word seems like it might be the most likely to cause problems. When that is disabled, I am not sure how the radio detects the start of a packet. You might try different values of that to see if it works.

You said the radio_sniffer app was receiving some data. Is there some problem with the data it was reporting? Since radio_sniffer was working better than your code, the way I would approach this would be to make a new program that is the same a radio_sniffer, and then gradually change the code in small steps to be more like your code. Eventually, you should encounter a small step that causes the code to stop working, and that will help you narrow down the problem.

–David

Hi David,

thank you very much for your help! Good catch about the missing calls to usbComTxAvailable, but unfortunately it didn’t fix the issue.

The sending device is based on the CC2510-F32 and I’m happy to share more details if that’s helpful (it’s all public). As far as I know it wasn’t developed based on the Wixel and I’d actually need to modify more register settings (like bandwidth and frequencies) to match this, but for the example I tried to stick as close to the original Wixel settings as possible. I realize that this wouldn’t actually let me receive the data that I’m interested in right now.

However, when disabling the preamble / sync word checking completely in the original radio sniffer (which uses TX, DMA, etc.), I’m constantly receiving, just because those frequencies are so busy (at least that’s what I’m assuming, with multiple WiFi networks, phones, Bluetooth devices etc. around). With the carrier-sense threshold enabled (MDMCFG2.SYNC_MODE = 100) this is filtered a bit, but I’m still receiving quite regularly.

That’s why I’m so surprised that RFTXRXIF isn’t triggered at all in my code and assume that it’s not a particular frequency settings but something more fundamental like the interrupt setup or similar that I’m doing wrong. Does that make sense? I also tried enabling the carrier-sense threshold, but it doesn’t change things.

The “problem” with the original radio sniffer app is that it assumes preambles, sync words, packet lengths, sends ACK packets, etc. All of this is of course very reasonable in general, and I started out modifying the original radio app. However, there’s quite some complexity involved with the DMA setup, the mode changing between RX and TX, etc., so I thought it would be nice to get something very simple running that just receives in a trivial way before adding more complexity.

But probably you’re right and I should start over again with the original radio_sniffer. I was just hoping that there’s something wrong with the example code that is easy to spot for someone more experienced than me.

Cheers,
Leo

Well, I don’t see anything about your interrupt setup that jumps out as being wrong. The radio_sniffer app does not send any acknowledgment packets; that would be very bad. A simpler starting point for you might be the test_radio_signal_rx app, which uses DMA but does not use interrupts. I hope you can figure it out! The CC2511’s radio has some debug signals that you can output to pins of the microcontroller which might help you figure out what is going on.

–David

Hi David,

thank you very much, both of your hints have been really helpful. I’ve got it working by now! :slight_smile:

It’s actually possible to stream everything the radio receives using the serial synchronous mode. It’s not documented in the data sheet, but explained in TI’s application note AN095. This outputs a data and clock signal on two debug pins and can be read e.g. using the SPI on an Arduino.

Furthermore it’s possible to route the carrier-sense above threshold signal to another debug pin. Using this as a trigger for capturing I was able to identify the transmission, which then also includes the preamble and sync words.

The test_radio_signal_rx example was an ideal follow-up starting point to write a small customized receiver. It worked right away with the DMA code in there. I really like the Wixel :).

Thanks again for your help!
squid