This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Comms Between NRF52 Dev Kits using NRF24L01+ modules

Hello, this is a continuation from a different question I previously had answered: https://devzone.nordicsemi.com/f/nordic-q-a/40452/nrf24l01-spi-communication-with-nrf52-dk

I am using two NRF52 Dev Kits and two NRF24L01+ modules to try and communicate wirelessly without needing to use the NRF52832's radio as I would like to leave open the option of also using a soft device and BLE in the future when working with these products. I am using the Nordic SDK 14.2.0 and have started from some base code written by a Nordic engineer from the previous question: link . My end goal is to have two NRF52832s writing to/ reading from NRF24L01+ modules to communicate with each other custom 5B addresses with auto acknowledgement, 2B CRC, 2Mbps data rate, and ideally also dynamic payload length but for now static is fine as well.

I can read and write from the modules over SPI seemingly successfully: I have compared against the datasheet and everything is set as expected. All the functions from the example set up given to me appear to work correctly as I can set any specific register to whatever values I need it to be easily in my NRF24L_init. I have taken out most of the settings I plan on using in the future (RF channels, data rates, custom addresses, etc.) and have left the init mostly empty to leave the modules on as default the settings as I can get them since I keep reading that they should be pretty good to go from just powering up. Here are the settings for my two units set in their init functions:

//PRX
#define PAYLOAD_WIDTH 32    //1-32B

#define SPI_INSTANCE  0     //enabled in config & using EASY DMA, 6 IRQ priority, 8MHz
static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE);    //SPI instance

l01_instance_t NRF24L;
l01_config_t NRF24L_config = L01_MODULE_CONFIG(SPI_INSTANCE, CE_PIN, CSN_PIN, SPI_SCK_PIN,
                                               SPI_MOSI_PIN, SPI_MISO_PIN, IRQ_PIN);

void NRF24L_init(void)
{
    NRF24L.spi_instance = spi;
    NRF24L.pin_ce = CE_PIN;
    NRF24L.pin_irq = IRQ_PIN;   //GPIOTE 6 IRQ priority
    NRF24L.pin_csn = CSN_PIN;

    hal_nrf_nrf52_init(&NRF24L, &NRF24L_config, NRF24L_irq_handler); //Hardware prepared

    nrf_delay_ms(100);                        //delay 100ms for coming online after power loss
    hal_nrf_set_power_mode(HAL_NRF_PWR_DOWN); //power down internals, entering power down mode
    nrf_delay_us(1500);                       //wait 1.5ms to settle
    hal_nrf_set_power_mode(HAL_NRF_PWR_UP);   //power up internals, entering standby mode I
    nrf_delay_us(1500);                       //wait 1.5ms to ensure in standby mode I

    hal_nrf_clear_irq_flags_get_status(); //clear all IRQ flags in case any are set
    hal_nrf_flush_rx(); //empty out everything in RX FIFO in case they contain data
    hal_nrf_flush_tx(); //empty out everything in TX FIFO in case they contain data

    for(uint8_t i = 0; i < 6; i++){
        hal_nrf_set_rx_payload_width(i, PAYLOAD_WIDTH); //sets payload witdh for all rx pipes (0-5)
    }

    hal_nrf_set_operation_mode(HAL_NRF_PRX);  //Set as receiver
    hal_nrf_chip_enable(CE_ENABLE); //Enter Rx mode
    nrf_delay_ms(10);
}
//PTX
#define PAYLOAD_WIDTH 32    //1-32B

#define SPI_INSTANCE  0     //enabled in config & using EASY DMA, 6 IRQ priority, 8MHz
static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE);    //SPI instance

l01_instance_t NRF24L;
l01_config_t NRF24L_config = L01_MODULE_CONFIG(SPI_INSTANCE, CE_PIN, CSN_PIN, SPI_SCK_PIN,
                                               SPI_MOSI_PIN, SPI_MISO_PIN, IRQ_PIN);

void NRF24L_init(void)
{
    NRF24L.spi_instance = spi;
    NRF24L.pin_ce = CE_PIN;
    NRF24L.pin_irq = IRQ_PIN;   //GPIOTE 6 IRQ priority
    NRF24L.pin_csn = CSN_PIN;

    hal_nrf_nrf52_init(&NRF24L, &NRF24L_config, NRF24L_irq_handler); //Hardware prepared

    nrf_delay_ms(100);                        //delay 100ms for coming online after power loss
    hal_nrf_set_power_mode(HAL_NRF_PWR_DOWN); //power down internals, entering power down mode
    nrf_delay_us(1500);                       //wait 1.5ms to settle
    hal_nrf_set_power_mode(HAL_NRF_PWR_UP);   //power up internals, entering standby mode I
    nrf_delay_us(1500);                       //wait 1.5ms to ensure in standby mode I

    hal_nrf_clear_irq_flags_get_status(); //clear all IRQ flags in case any are set
    hal_nrf_flush_rx(); //empty out everything in RX FIFO in case they contain data
    hal_nrf_flush_tx(); //empty out everything in TX FIFO in case they contain data

    for(uint8_t i = 0; i < 6; i++){
        hal_nrf_set_rx_payload_width(i, PAYLOAD_WIDTH); //sets payload witdh for all rx pipes (0-5)
    }

    hal_nrf_set_operation_mode(HAL_NRF_PTX);  //Set as receiver

    nrf_delay_ms(10);
}

My previous setup with this works when I replace the transmitter NRF24L01+ module with an NRF52 Dev Kit running a slightly modified ESB Tx example from the proprietary RF folder, including custom addresses, address widths, payload widths, RF channels, data rates, transmitting powers, 2B CRC, auto ACKs, basically everything EXCEPT dynamic payload lengths, which was breaking it for some reason.

I've been experiencing some anomalies when leaving the transmitter and receiver both running for several minutes where in the PTX will detect a successful transmission (TX data sent IRQ read from status register in the interrupt handler) in a sea of failures to transmit (max retransmits hit without an acknowledge). When debugging the receiver the PRX will trigger a data received interrupt properly (RX data received IRQ) and most of the data is correct, but in seemingly random spots the values will change randomly. I could not find any rhyme or reason to the change; I tried repeatedly sending a 32B array with value 0 in the first index incrementing up to 31 in the final index as constants and additionally incrementing index 1 every time. I have checked on the transmitter side and the data being sent is as expected:

Packet No. | 0 | 1 | 2 | 3 | ... | 31
     0     | 0 | 1 | 2 | 3 | ... | 31
     1     | 0 | 2 | 2 | 3 | ... | 31
     2     | 0 | 3 | 2 | 3 | ... | 31
     etc.

I've tried changing settings one at a time with this setup and thus far I've found is RF channels need to be the same for this to occur (obvious) and enabling dynamic payload length or disabling auto acknowledges break it. The only significant change I noticed was when changing the CRC, but this only confused me further since my results are contradictory to what I understand about CRC.

  • No CRC [ hal_nrf_set_crc_mode(HAL_NRF_CRC_OFF); ] => no successful packets are sent
  • 1B CRC [ hal_nrf_set_crc_mode(HAL_NRF_CRC_8BIT); ] => periodic "successful" packets sent, one every couple of seconds
  • 2B CRC [ hal_nrf_set_crc_mode(HAL_NRF_CRC_16bit); ] => rarely "successful" packets sent, one maybe every 30 seconds to a minute

At this point I'm fairly confident I do not have any errors with my wiring (SPI & IRQ seem to work correct for both reading & writing + interrupt triggers properly) or power supply since I often read that adding capacitance by the modules helps stabilize them: both modules have a 0.1uF ceramic cap & 100uF electrolytic cap nearby and there doesn't appear to be much ripple on the voltage seen on an oscilloscope. I've also tested with multiple modules to ensure that the units I had weren't damaged and even fresh ones I hadn't touched before behave in the same manner.

My understanding of how these modules works is as follows:

Set PTX's TX_ADDR address to match whatever receiving pipe is used on PRX as that is the address the PRX will transmit to for sending an ack
Match the PTX's pipe 0 address to match the TX_ADDR before sending a packet, this will be scanned after transmitting for any incoming ack
If no ack is received after a set delay reattempt this process for the set number of retransmit attempts until either receive an ack or hit the max limit
Generate an interrupt appropriate for whether data was sent proper or not on PTX & generate a data received interrupt on PRX when a non-repeat packet is received

All the documentation, threads, and articles I've read on this seem to be pretty clear on how things must be set up (though it's unfortunate a lot of Arduino related stuff refer to "reading pipes" & "writing pipes" when most nothing else I found does) and I believe that I am correctly setting things up, especially since I am able to use an NRF24L01+ as a receiver when using my NRF52 Dev Kit running ESB as the transmitter. The settings for everything should be basically identical on both the PTX and PRX modules except for the PRIM_RX bit in the configuration register (Reg 0x00) as that controls whether it is acting as a PTX or PRX. To send data, use the W_TX_PAYLOAD SPI command then send the data bytes over SPI continuously to write a packet into the TX FIFO and then pulse the CE pin high for at least 10us to begin transmitting following the flow chart from page 33. For receiving, set the device as a PRX & set the CE pin high to enter receiving mode where it will continuously check the air for incoming packets following the flow chart from page 35.

If I have something basic wrong in here, PLEASE let me know. We have quite a few of these modules on hand at the moment and we would like to use these in various projects but just can't get the darn things to work!

  • Hi Andy

    Thanks for the comprehensive report Slight smile

    I have done some testing on my own end, but have been held back by SPI configuration issues and other assignments/travel. 

    I realized there were some errors in the nrf5dk_l01.c library, most notably the hal_nrf_read_rx_payload_width() function, which tried to read from configuration register 0x60 rather than running SPI command 0x60. 

    I started work on an updated version of the L01 test example, ported to SDK v15.2.0, and included the changes to nrf5dk_lo1.c

    I also moved a lot of the library configuration and use into a separate file, app_radio.c, to clean up the main file a little. 

    All of this seems to work very fine when using the nRF52DK as the transmitter and the nRF52840DK as receiver, but for some reason it stopped working when I reversed the roles. I need to do some more investigation to figure out why I can't use the nRF52DK as receiver and the nRF52840DK as transmitter, as I would expect all permutations of boards to work fine.  

    Either way I attached the code so you can have a look at it. The changes to the core library might be of benefit to you as well:
    l01_test_app_19_03_22.zip

    I am happy to give your code a try, if you can just remind me what SDK version you targeted ;)

    I am tempted to believe the issues you had with longer payloads were caused by supply issues, as the PA can draw a lot of current when on, but you might have seen similar issues also on the non PA modules?
    There is no reason longer packets should fail that often, so this is something worth investigating. 

    Unfortunately I am out in travel next week, but I would like to continue this when I'm back so that the remaining issues with the library can be identified. 

    Regarding your final comment I totally agree that using the nRF52 stand alone is a more efficient solution, and if you are planning to make a commercial product that would be my suggestion. There are plenty of nRF52 modules out there too if you don't want to do the hardware design. 

    Best regards
    Torbjørn

  • Hey Ovrebekk,

    Sorry for missing that, I'm still using the SDK 14.2.0.

    Sincerely,

    Andy

  • Hi Andy

    Finally I am back in the office with some more time at my hands. 

    I got your project running again in SDK v14.2.0, but unless I change your address configuration I am unable to get any communication running. 

    Is this to be expected?

    Maybe you are not pairing these two projects together, but using an nRF52 device as the peer?

    Best regards
    Torbjørn

Related