Low level 802.15.4 transmit code

I am trying to make a simple MAC-layer transceiver using Arduino environment.  I have some code derived from this example: https://github.com/IoTS-P/nRF52-802154-Raw-Transmitter
and have discovered that it hangs after one to a few hundred transmissions.  I have gone over the datasheet for the 802.15.4 radio section and I do not see where the problem is.I am using a Zena receiver to monitor the transmissions.  I noticed that the CCA appears to take some tens-of-mS fairly often while the Zena does not show any other messages arrived during that time.  What am I doing wrong with the radio hardware?

<project files attached>

  • My uploaded files went to some bit bucket.  Here is the cleaned up code (3 files):

    //Raw802.ino
    #include <Arduino.h>
    #include <stdint.h>
    //#include <stdbool.h>
    #include "radio.h"
    #include <nrf52840.h>
    #include <Adafruit_TinyUSB.h>// Seems to need this without Serial or USB. Maybe DFU boot loader needs it?
    
    #define PIN_LEDX 22 // Red LED
    
    // channel 
    #define DEFAULT_INITIAL_CHANNEL 19
    
    // read buffer  
    uint8_t *rx_buffer;
    uint8_t u8SeqNum = 0;
    // the frame length in phy layer header 
    uint8_t pkt_len = 0;
    uint8_t u8PktBuf[100];
    
    // has data transmitting
    uint8_t bTransmitting = false;
    
    // packets
    #define MAX_PKTS_IN 12 // Maximum packets in the queue
    struct __attribute__((packed)) packets_structure
    {
      uint16_t size[MAX_PKTS_IN] = {0};
      uint8_t *pkt_buffer[MAX_PKTS_IN];
      uint8_t head = 0;
      uint8_t tail = 0;
    } packets;
    
    // channel
    uint8_t radio_channel = DEFAULT_INITIAL_CHANNEL;
    
    
    /**
     * nRF51822 RADIO handler.
     *
     * This handler is called whenever a RADIO event occurs (IRQ).
     **/
    extern "C" void RADIO_IRQHandler(void)
    {
      // if (NRF_RADIO->EVENTS_FRAMESTART)
      if (*(uint32_t *)(0x40001138)) //Events-FrameStart
      {
        *(uint32_t *)(0x40001138) = 0;
      }
    
      if (NRF_RADIO->EVENTS_READY)
      {
        NRF_RADIO->EVENTS_READY = 0;
        NRF_RADIO->TASKS_START = 1;
      }
    
      if (NRF_RADIO->EVENTS_END)
      {
        NRF_RADIO->EVENTS_END = 0;
        // NRF_RADIO->TASKS_START = 1;
        // If transmitt end
        if (bTransmitting)
        {
          bTransmitting = false;
          //Serial.write("SENDED ");
           //radio_tx_to_rx();
          //return;// maybe interrupt needs special return?
          goto exit;
        }
      }
      if (*(uint32_t *)(0x40001148))//CCA-Busy
      {
        Serial.println("CCABUSY");
        *(uint32_t *)(0x40001148) = 0;
        NRF_RADIO->TASKS_DISABLE = 1;
        while (NRF_RADIO->EVENTS_DISABLED == 0);
        //Serial.println("CCABUSY: disable the radio");
        sig_ccabusy = true;
      }
      if (*(uint32_t *)(0x4000114C))
      {
        Serial.println("CCASTOPPED");
      }
      if (*(uint32_t *)(0x40001144))//CCA-IDLE
      {
        Serial.println("CCAIDLE");
        sig_ccabusy = false;
        *(uint32_t *)(0x40001144) = 0;
        // NRF_RADIO->TASKS_CCASTART = 0
        //*(uint32_t *)(0x4000102c) = 1;
      }
      exit:
      {
      
      }
    }
    
    void setup()
    {
      pinMode(LED_BUILTIN, OUTPUT);
      pinMode(PIN_LEDX, OUTPUT); // Going for the RED Led now
      Serial.begin(115200);
      rx_buffer = (uint8_t *)malloc(IEEE802154_FRAME_LEN_MAX + 3);
    
      radio_set_sniff(radio_channel, 0); // Setup the radio
    }
    
    uint32_t ctr = 0;
    void loop()
    {
      if (NRF_RADIO->EVENTS_READY)
      {
        NRF_RADIO->EVENTS_READY = 0;
        // NRF_RADIO->TASKS_START;
      }
      delay(1);
      ctr++;
      if(ctr > 900)// Transmit every 200mS
      {
        ctr = 0;
        pkt_len = 17; 
        memcpy(u8PktBuf, "\x11\x00\x00\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff",19);
        u8PktBuf[3] = u8SeqNum++;
      }
    
    
      // memcpy pkt_buffer to tx_buffer
      if (!bTransmitting && pkt_len)
      {
        Serial.write(".");
        digitalWrite(PIN_LEDX, HIGH);
        bTransmitting = true;
        radio_send_custom(u8PktBuf, radio_channel);
        pkt_len = 0;
        digitalWrite(PIN_LEDX, LOW);
      }
    }

    //radio.cpp
    #include "radio.h"
    #include <Arduino.h>
    #include <assert.h>
    
    // signal for CCABUSY
    volatile uint8_t sig_ccabusy = false;
    
    /**
     * channel_to_freq(int channel)
     *
     * Convert a BLE channel number into the corresponding frequency offset
     * for the nRF51822.
     **/
    uint8_t channel_to_freq(int channel) {
      assert(channel > 10);
      assert(channel < 27);
      return 5 * (channel - 10);
    }
    
    /**
     * radio_disable()
     *
     * Disable the radio.
     **/
    
    void radio_disable(void) {
      if (NRF_RADIO->STATE > 0) {
        NVIC_DisableIRQ(RADIO_IRQn);
    
        NRF_RADIO->EVENTS_DISABLED = 0;
        NRF_RADIO->TASKS_DISABLE = 1;
        while (NRF_RADIO->EVENTS_DISABLED == 0);
      }
    }
    
    /**
     * @brief   Convert from dBm to the internal representation, when the
     *          radio operates as a IEEE802.15.4 transceiver.
     */
    static inline uint8_t _dbm_to_ieee802154_hwval(int8_t dbm) {
      return ((dbm - ED_RSSIOFFS) / ED_RSSISCALE);
    }
    
    static int set_cca_threshold(int8_t threshold) {
    
      if (threshold < ED_RSSIOFFS) {
        return -1;
      }
    
      uint8_t hw_val = _dbm_to_ieee802154_hwval(threshold);
    
       NRF_RADIO->CCACTRL &= ~RADIO_CCACTRL_CCAEDTHRES_Msk;
      //*(uint32_t *)(0x4000166c) &= ~RADIO_CCACTRL_CCAEDTHRES_Msk;
    
       NRF_RADIO->CCACTRL |= hw_val << RADIO_CCACTRL_CCAEDTHRES_Pos;
      //*(uint32_t *)(0x4000166c) |= hw_val << RADIO_CCACTRL_CCAEDTHRES_Pos;
      return 0;
    }
    
    /**
     * radio_set_sniff(int channel)
     *
     * Configure the nRF51822 to sniff on a specific channel.
     **/
    
    void radio_set_sniff(int channel, uint32_t access_address) {
      // Disable radio
      radio_disable();
    
      // Enable the High Frequency clock on the processor. This is a pre-requisite
      // for the RADIO module. Without this clock, no communication is possible.
      NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
      NRF_CLOCK->TASKS_HFCLKSTART = 1;
      while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);
    
      // power should be one of: -30, -20, -16, -12, -8, -4, 0, 4
      NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg20dBm;//Pos4dBm;
      NRF_RADIO->TXADDRESS = 0;
    
      /* Set 802154 data rate. */
      NRF_RADIO->MODE = RADIO_MODE_MODE_Ieee802154_250Kbit;
      /* Listen on channel . */
      NRF_RADIO->FREQUENCY = channel_to_freq(channel);
    
      /* set start frame delimiter */
       //NRF_RADIO->RESERVED12[4] = IEEE802154_SFD;// Gets a read-only error on ths
      *(uint32_t *)(0x40001660) = IEEE802154_SFD;
    
      /* set MHR filters */
       NRF_RADIO->MHRMATCHCONF = 0;         /* Search Pattern Configuration */
      //*(uint32_t *)(0x40001644) = 0;
    
       NRF_RADIO->MHRMATCHMAS = 0xff0007ff; /* Pattern mask */
      //*(uint32_t *)(0x40001648) = 0xff0007ff;
    
      /* and set some fitting configuration */
      NRF_RADIO->PCNF0 = ((8 << RADIO_PCNF0_LFLEN_Pos) |
                          (RADIO_PCNF0_PLEN_32bitZero << RADIO_PCNF0_PLEN_Pos) |
                          (RADIO_PCNF0_CRCINC_Include << RADIO_PCNF0_CRCINC_Pos));
    
      NRF_RADIO->PCNF1 = IEEE802154_FRAME_LEN_MAX;
    
      NRF_RADIO->MODECNF0 |=
          RADIO_MODECNF0_RU_Fast; // Enable fast mode for radio ramp up
    
      // Enable CRC
      NRF_RADIO->CRCCNF =
          ((RADIO_CRCCNF_LEN_Two << RADIO_CRCCNF_LEN_Pos) |
           (RADIO_CRCCNF_SKIPADDR_Ieee802154 << RADIO_CRCCNF_SKIPADDR_Pos));
      NRF_RADIO->CRCPOLY = 0x11021;
      NRF_RADIO->CRCINIT = 0;
    
      // DATAWHITE
      NRF_RADIO->DATAWHITEIV = 0x40;
    
      NRF_RADIO->PACKETPTR = (uint32_t)(rx_buffer);
    
      // configure CCA
      set_cca_threshold(CONFIG_IEEE802154_CCA_THRESH_DEFAULT);
    
      // Configure interrupts
      NRF_RADIO->INTENSET = RADIO_INTENSET_END_Msk | RADIO_INTENSET_FRAMESTART_Msk |
                            // RADIO_INTENSET_CCAIDLE_Msk |
                            RADIO_INTENSET_CCABUSY_Msk;
      // Enable NVIC Interrupt for Radio
      NVIC_SetPriority(RADIO_IRQn, IRQ_PRIORITY_LOW);
      NVIC_ClearPendingIRQ(RADIO_IRQn);
      NVIC_EnableIRQ(RADIO_IRQn);
    
      // Enable receiver hardware
      NRF_RADIO->EVENTS_READY = 0;
      NRF_RADIO->TASKS_RXEN = 1;
      while (NRF_RADIO->EVENTS_READY == 0);
      NRF_RADIO->EVENTS_END = 0;
      NRF_RADIO->TASKS_START = 1;
    }
    
    /**
     * Send raw data asynchronously.
     **/
    
    void radio_send_custom(uint8_t *pBuffer, uint8_t channel) {
    
      /* No shortcuts on disable. */
      NRF_RADIO->SHORTS = 0x0;
      /* Switch radio to TX. */
      radio_disable();
    
      for (size_t try_ = 0; try_ < MAX_RETRIES; try_++) 
      {
        /* Switch packet buffer to tx_buffer. */
        NRF_RADIO->PACKETPTR = (uint32_t)pBuffer;
        NRF_RADIO->INTENSET = (1 << RADIO_INTENSET_END_Pos);
        NVIC_ClearPendingIRQ(RADIO_IRQn);
        NVIC_EnableIRQ(RADIO_IRQn);
    
        NRF_RADIO->EVENTS_READY = 0;
        NRF_RADIO->TASKS_RXEN = 1;
        Serial.write("While 1 ");// NEEDS THIS DELAY
        while (!NRF_RADIO->EVENTS_READY);
    
         NRF_RADIO->EVENTS_CCAIDLE = 0;
        //*(uint32_t *)(0x40001144) = 0;
        NRF_RADIO->TASKS_CCASTART = 1;
        //*(uint32_t *)(0x4000102c) = 1;
    
         Serial.write("While 2 "); // Needs this delay
        // while(!(NRF_RADIO->EVENTS_CCAIDLE || CCABUSY));
        while(!(NRF_RADIO->EVENTS_CCAIDLE || NRF_RADIO->EVENTS_CCABUSY));
        // wait for CCAIDLE or CCABUSY
        Serial.write("While 3 ");
        while (!(*(uint32_t *)(0x40001144) || sig_ccabusy));//CCA_IDLE
        if (sig_ccabusy) 
        {
          Serial.println("WARNING: retry for CCABUSY");
          sig_ccabusy = false;
          delay(100);
          continue;
        }
    
        /* Transmit with max power. */
        NRF_RADIO->TXPOWER =
            (RADIO_TXPOWER_TXPOWER_Pos4dBm << RADIO_TXPOWER_TXPOWER_Pos);
        NRF_RADIO->FREQUENCY = channel_to_freq(channel);
    
        // enable receiver
        NRF_RADIO->EVENTS_READY = 0;
        NRF_RADIO->TASKS_TXEN = 1;
    
         Serial.write("While 4\r\n");//needs this delay
        while (!NRF_RADIO->EVENTS_READY);
        NRF_RADIO->TASKS_START = 1;
        /* From now, radio will send data and notify the result to Radio_IRQHandler */
        return;
      }
      Serial.println("ERROR: retry too many times");
    }
    
    /**
     * Change radio from TX to RX, while keeping last configuration of
     *radio_send_custom or radio_set_sniff
     **/
    void radio_tx_to_rx() {
      NRF_RADIO->PACKETPTR = (uint32_t)(rx_buffer);
      NVIC_DisableIRQ(RADIO_IRQn);
      NRF_RADIO->SHORTS = RADIO_SHORTS_READY_START_Msk;
    
      // NRF_RADIO->INTENSET = 0;
      NVIC_ClearPendingIRQ(RADIO_IRQn);
      NVIC_EnableIRQ(RADIO_IRQn);
    
      NRF_RADIO->EVENTS_DISABLED = 0;
      NRF_RADIO->TASKS_DISABLE = 1;
    
      while (NRF_RADIO->EVENTS_DISABLED == 0);
    
      NRF_RADIO->EVENTS_READY = 0;
      NRF_RADIO->TASKS_RXEN = 1;
    }

    //radio.h
    /**
     * Radio module
     *
     * This module provides all the required functions to manage the nRF51822
     * transceiver.
     **/
    
    #pragma once
    #include <Arduino.h>
    
    extern uint8_t *rx_buffer; /* Rx buffer used by RF to store packets. */
    
    #define IRQ_PRIORITY_HIGHEST 0
    #define IRQ_PRIORITY_HIGH 1
    #define IRQ_PRIORITY_MEDIUM 2
    #define IRQ_PRIORITY_LOW 3
    
    #define RADIO_MODE_MODE_Ieee802154_250Kbit (15UL)
    
    
    #define IEEE802154_FRAME_LEN_MAX (127U)   /**< maximum 802.15.4 frame length */
    #define IEEE802154G_FRAME_LEN_MAX (2047U) /**< maximum 802.15.4g-2012 frame length */
    #define IEEE802154_ACK_FRAME_LEN (5U)     /**< ACK frame length */
    #define IEEE802154_FCS_LEN                  (2U)
    
    
    #define RADIO_PCNF0_PLEN_32bitZero (2UL) /*!< 32-bit zero preamble - used for IEEE 802.15.4 */
    #define RADIO_PCNF0_CRCINC_Include (1UL) /*!< LENGTH includes CRC */
    #define RADIO_PCNF0_CRCINC_Pos (26UL) /*!< Position of CRCINC field. */
    #define RADIO_CRCCNF_SKIPADDR_Ieee802154 (2UL) /*!< CRC calculation as per 802.15.4 standard. Starting at first byte after length field. */
    
    
    #define RADIO_INTENSET_FRAMESTART_Pos (14UL) /*!< Position of FRAMESTART field. */
    #define RADIO_INTENSET_FRAMESTART_Msk (0x1UL << RADIO_INTENSET_FRAMESTART_Pos) /*!< Bit mask of FRAMESTART field. */
    #define RADIO_INTENSET_CCAIDLE_Pos (17UL) /*!< Position of CCAIDLE field. */
    #define RADIO_INTENSET_CCAIDLE_Msk (0x1UL << RADIO_INTENSET_CCAIDLE_Pos) /*!< Bit mask of CCAIDLE field. */
    #define RADIO_INTENSET_CCABUSY_Pos (18UL) /*!< Position of CCABUSY field. */
    #define RADIO_INTENSET_CCABUSY_Msk (0x1UL << RADIO_INTENSET_CCABUSY_Pos) /*!< Bit mask of CCABUSY field. */
    
    
    #define ED_RSSISCALE        (4U)    /**< RSSI scale for internal HW value */
    #define ED_RSSIOFFS         (-92)   /**< RSSI offset for internal HW value */
    
    /**
     * @brief IEEE802.15.4 default value for CCA threshold (in dBm)
     */
    #ifndef CONFIG_IEEE802154_CCA_THRESH_DEFAULT
    #define CONFIG_IEEE802154_CCA_THRESH_DEFAULT       (-70)
    #endif
    
    /* Bits 15..8 : CCA energy busy threshold. Used in all the CCA modes except CarrierMode. */
    #define RADIO_CCACTRL_CCAEDTHRES_Pos (8UL) /*!< Position of CCAEDTHRES field. */
    #define RADIO_CCACTRL_CCAEDTHRES_Msk (0xFFUL << RADIO_CCACTRL_CCAEDTHRES_Pos) /*!< Bit mask of CCAEDTHRES field. */
    
    /**
     * @brief   Default start frame delimiter
     */
    #define IEEE802154_SFD (0xa7)
    
    // signal for CCABUSY
    extern volatile uint8_t sig_ccabusy;
    // max retries
    #define MAX_RETRIES 5
    
    // functions
    uint8_t channel_to_freq(int channel);
    void radio_disable(void);
    void radio_set_sniff(int channel, uint32_t access_address);
    void radio_send_custom(uint8_t *pBuffer, uint8_t channel);
    void radio_tx_to_rx();

  • Hi DU_radio,

    The repo looks lt uses PlatformIO and the API is a unknow to me. It is hard for us to review something that is using external SDKs, too many unknows to properly give you a proper debugging direction. 

    Regarding the radio handling, I can say one thing that you did not seem to have handled failed CCA scenario properly, it is wise to do a cleanup, deinit and reinit in that case. Also there are a lot of hardcoded values in your code, not very pleasant to read or review. I suggest you take this question to a proper forum API you are using here to implement this.

  • Susheel,

        Thank you for your advice on CCA cleanup.  Does it need a full powerup init (and if so why?) or just the CCA threshold and clear events and re-enable the tasks? I ask because a full powerup init when CCA_IDLE was false did not help.

         The code has most of the constants from the original GIT repo (not my project), these are the register addresses in the nRFD52840 datasheet so I thought they would be more meaningfull to the Nordic hardware folks.  Also I was trying to avlod introducing more problems by incorrectly editing the register references.  Yes it is certainly ugly. Once it is working I will do a major code cleanup.

    Since this is a Nordic radio issue and not related to the platform it was built on, is there another forum that would be better suited to troubleshooting the radio?

    Darryl

  • Susheel,

         If you have any low level transmit example code in C, for any platform, that would likely provide the quickest solution.   There was some on the Nordic site at one time but it seems to have been taken down some years ago.

  • So DU_Radio, 

    We unfortunately do not have that as we are working with nRF Connect SDK now. 

Related