Problem with CONFIG register at nRF24L01 radio modules

Hey all.

I have a big problem on my nRF24L01 radio modules. I am using a ATmega128 with the mirf code. I can read and write to all registers but not the Config register (reg. 0). I can’t get anything out of it but I can get the status though, which is telling me that I can’t write to it either. I am using the exact same code to change the other registers which works like a charm. On a logic tester I can see that everything works as it is suppose to. The funny thing is that both my modules are set to be Tx according to the status, and I believe they are Rx from the production. Right? Since they are both Tx I can’t get any connection between them so I am not able to tell whether the rest of my code is working.

Everyone says these module are so easy to use and by using the mirf code they sure should be. I have used 3 weeks now and still no result

Have anyone experienced the same problem? Should I r/w to that specific register in a different way that the others?.

You must send CSN low, then clock in 8 0s over SPI for a read command and config register. This will return the Status. Then you must clock another 8 bits and this time it will return the Config data, then bring CSN high again.

I use this:

void mirf_config_register(uint8_t reg, uint8_t value)

// Clocks only one byte into the given MiRF register

{

mirf_CSN_lo;

spi_fast_shift(W_REGISTER | (REGISTER_MASK & reg));

spi_fast_shift(value);

mirf_CSN_hi;

}

The wird part is that I can write to all other registers but the config (reg 0). Should that register be accessed in a different way than the others ?

No it is accessed the same way. I would try ignoring the defined values and just clocking 2 sets of 0x00 as that is all you need to do. If that works then take a look at the predefined function it may be faulty.

It doesn’t matter what value I am trying to write to it. 2 sets of 0’s is the same fault.

I just discovered that when I read from the registers (trough the uart). I define the length of data which when set to 1 allways return 14 (including reg 0). The same as the Status return is. If I change the length to 2, all the registers give out the correct value, exept reg 0. It gives a d which i am sure comes from my %d function.

I have these two commands to test writing to the registers:

mirf_config_register(RF_CH,5);

mirf_config_register(CONFIG,5);

The first one does the job, but the next one doesn’t. And I am sure that the RF_CH and CONFIG is correct references.

I wrote the entire library myself in ASM (don’t like C on PICs when I don’t have to, I like to keep close to the core as much as possible).

The library is for PICs however, and you are using ATmega so it wouldn’t really help.

Could you upload the files you are using and I can take a look at the code.

main.c (for reading the registers)

	//Test routine
	delay_us(500000);
	mirf_init();
	mirf_config();
	delay_us(500000);

    //spi_transmit_sync(mydata,buffersize);   // Write payload
	//delay_us(1);

    //spi_transfer_sync(buffer,buffer,buffersize); // Read payload
	//delay_us(1);
	//mirf_read_register(5,1,2);

	nrf_read_register(CONFIG,2); //Read registers
	mirf_data_ready(); //Status

mirf.c

#include "io.h"
#include "buffer.h"
#include "rprintf.h"
#include "a2d.h"
#include "uart2.h"
#include "signal.h"
#include "spi.h"
#include "timer128.h"
#include "mirf.h"
#include "interrupt.h"
#include "global.h"

#include "mirf.h"
#include "nRF24L01.h"
#include "spi.h"
#include <avr/io.h>
#include <avr/interrupt.h>


// Defines for setting the MiRF registers for transmitting or receiving mode
#define TX_POWERUP mirf_config_register(CONFIG, mirf_CONFIG | ( (1<<PWR_UP) | (0<<PRIM_RX) ) )
#define RX_POWERUP mirf_config_register(CONFIG, mirf_CONFIG | ( (1<<PWR_UP) | (1<<PRIM_RX) ) )


//#define __AVR_ATmega128__


// Flag which denotes transmitting mode
volatile uint8_t PTX;

void mirf_init() 
// Initializes pins ans interrupt to communicate with the MiRF module
// Should be called in the early initializing phase at startup.
{
    // Define CSN and CE as Output and set them to default
    DDRB |= ((1<<CSN)|(1<<CE));
    mirf_CE_lo;
    mirf_CSN_hi;

#if defined(__AVR_ATmega8__)
    // Initialize external interrupt 0 (PD2)
    MCUCR = ((1<<ISC11)|(0<<ISC10)|(1<<ISC01)|(0<<ISC00)); // Set external interupt on falling edge
    GICR  = ((0<<INT1)|(1<<INT0));                         // Activate INT0
#endif // __AVR_ATmega8__

#if defined(__AVR_ATmega168__)
    // Initialize external interrupt on port PD6 (PCINT22)
    //DDRB &= ~(1<<PD6);
    //PCMSK2 = (1<<PCINT22);
    //PCICR  = (1<<PCIE2);
#endif // __AVR_ATmega168__   

#if defined(__AVR_ATmega128__)
    // Initialize external interrupt on port PE7
    DDRE &= ~(1<<PE7);
	EICRB = ((1<<ISC71)|(0<<ISC70));
	//EIMSK = (1<<INT7_vect);
	EIMSK = (1<<INT7);
#endif // __AVR_ATmega128__   

    // Initialize spi module
    spi_init();
}


void mirf_config() 
// Sets the important registers in the MiRF module and powers the module
// in receiving mode
{
    // Set RF channel
    mirf_config_register(RF_CH,mirf_CH);
	//mirf_config_register(CONFIG,5); //test of writing to register

    // Set length of incoming payload 
    mirf_config_register(RX_PW_P0, mirf_PAYLOAD);

    // Start receiver 
    PTX = 0;        // Start in receiving mode
    RX_POWERUP;     // Power up in receiving mode
	//delay_us(100);
    mirf_CE_hi;     // Listening for pakets
}

void mirf_set_RADDR(uint8_t * adr) 
// Sets the receiving address
{
    mirf_CE_lo;
    mirf_write_register(RX_ADDR_P0,adr,5);
    mirf_CE_hi;
}

void mirf_set_TADDR(uint8_t * adr)
// Sets the transmitting address
{
    mirf_write_register(TX_ADDR, adr,5);
}

#if defined(__AVR_ATmega8__)
SIGNAL(SIG_INTERRUPT0) 
#endif // __AVR_ATmega8__
#if defined(__AVR_ATmega168__)
SIGNAL(SIG_PIN_CHANGE2) 
#endif // __AVR_ATmega168__  
#if defined(__AVR_ATmega128__)
SIGNAL(SIG_INTERRUPT7)
#endif // __AVR_ATmega128__ 

// Interrupt handler 
{
    uint8_t status;   
    // If still in transmitting mode then finish transmission
    if (PTX) {
    
        // Read MiRF status 
        mirf_CSN_lo;                                // Pull down chip select
        status = spi_fast_shift(NOP);               // Read status register
        mirf_CSN_hi;                                // Pull up chip select

        mirf_CE_lo;                             // Deactivate transreceiver
        RX_POWERUP;                             // Power up in receiving mode
        mirf_CE_hi;                             // Listening for pakets
        PTX = 0;                                // Set to receiving mode

        // Reset status register for further interaction
        mirf_config_register(STATUS,(1<<TX_DS)|(1<<MAX_RT)); // Reset status register
    }
}

extern uint8_t mirf_data_ready() 
// Checks if data is available for reading
{
    if (PTX) return 0;
	 uint8_t status;
    // Read MiRF status 
    mirf_CSN_lo;                                // Pull down chip select
    status = spi_fast_shift(0x00);               // Read status register
    mirf_CSN_hi;                                // Pull up chip select
	rprintf("Status register 0: %d\r\n", status);
    return status & (1<<RX_DR);
}

extern void mirf_get_data(uint8_t * data) 
// Reads mirf_PAYLOAD bytes into data array
{
    mirf_CSN_lo;                               // Pull down chip select
    spi_fast_shift( R_RX_PAYLOAD );            // Send cmd to read rx payload
    spi_transfer_sync(data,data,mirf_PAYLOAD); // Read payload
    mirf_CSN_hi;                               // Pull up chip select
    mirf_config_register(STATUS,(1<<RX_DR));   // Reset status register
}

void mirf_config_register(uint8_t reg, uint8_t value)
// Clocks only one byte into the given MiRF register
{
    mirf_CSN_lo;
    spi_fast_shift(W_REGISTER | (REGISTER_MASK & reg));
    spi_fast_shift(value);
    mirf_CSN_hi;
}

void nrf_read_register(uint8_t reg,uint8_t len)
{
	//RX_POWERUP;
	delay_us(50000);
    mirf_CSN_lo;
	
    spi_fast_shift(R_REGISTER | (REGISTER_MASK & reg)); //Status
	nrf_transfer_sync(reg, len); //Read register
	mirf_CSN_hi;
	delay_us(500000); 
    rprintf("Value register %d: %d\r\n",reg, SPDR);
}


void mirf_read_register(uint8_t reg, uint8_t * value, uint8_t len)
// Reads an array of bytes from the given start position in the MiRF registers.
{
    mirf_CSN_lo;
    spi_fast_shift(R_REGISTER | (REGISTER_MASK & reg));
    spi_transfer_sync(value,value,len);
    mirf_CSN_hi;
}

void mirf_write_register(uint8_t reg, uint8_t * value, uint8_t len) 
// Writes an array of bytes into inte the MiRF registers.
{
    mirf_CSN_lo;
    spi_fast_shift(W_REGISTER | (REGISTER_MASK & reg));
    spi_transmit_sync(value,len);
    mirf_CSN_hi;
}


void mirf_send(uint8_t * value, uint8_t len) 
// Sends a data package to the default address. Be sure to send the correct
// amount of bytes as configured as payload on the receiver.
{
	//rprintf("test");
    while (PTX) {}                  // Wait until last paket is send

    mirf_CE_lo;

    PTX = 1;                        // Set to transmitter mode
    TX_POWERUP;                     // Power up
    
    mirf_CSN_lo;                    // Pull down chip select
    spi_fast_shift( FLUSH_TX );     // Write cmd to flush tx fifo
    mirf_CSN_hi;                    // Pull up chip select
    
    mirf_CSN_lo;                    // Pull down chip select
    spi_fast_shift( W_TX_PAYLOAD ); // Write cmd to write payload
    spi_transmit_sync(value,len);   // Write payload
    mirf_CSN_hi;                    // Pull up chip select
    
    mirf_CE_hi;                     // Start transmission
	PTX = 0;
}

mirf.h

#ifndef _MIRF_H_
#define _MIRF_H_

#include <avr/io.h>

// Mirf settings
#define mirf_CH         22
#define mirf_PAYLOAD    16
#define mirf_CONFIG     ( (1<<MASK_RX_DR) | (1<<EN_CRC) | (0<<CRCO) )

// Pin definitions for chip select and chip enabled of the MiRF module
#define CE  PA3
#define CSN PB0

// Definitions for selecting and enabling MiRF module
#define mirf_CSN_hi     PORTB |=  (1<<CSN);
#define mirf_CSN_lo     PORTB &= ~(1<<CSN);
#define mirf_CE_hi      PORTA |=  (1<<CE);
#define mirf_CE_lo      PORTA &= ~(1<<CE);

// Public standart functions
extern void mirf_init();
extern void mirf_config();
extern void mirf_send(uint8_t * value, uint8_t len);
extern void mirf_set_RADDR(uint8_t * adr);
extern void mirf_set_TADDR(uint8_t * adr);
extern uint8_t mirf_data_ready();
extern void mirf_get_data(uint8_t * data);


// Public extended functions
extern void mirf_config_register(uint8_t reg, uint8_t value);
extern void mirf_read_register(uint8_t reg, uint8_t * value, uint8_t len);
extern void mirf_write_register(uint8_t reg, uint8_t * value, uint8_t len);

#endif /* _MIRF_H_ */

spi.c

#include "spi.h"

#include <avr/io.h>
#include <avr/interrupt.h>

#define PORT_SPI    PORTB
#define DDR_SPI     DDRB
#define DD_MISO     DDB3
#define DD_MOSI     DDB2
#define DD_SS       DDB0 //CSN
#define DD_SCK      DDB1


void spi_init()
// Initialize pins for spi communication
{
    DDR_SPI &= ~((1<<DD_MOSI)|(1<<DD_MISO)|(1<<DD_SS)|(1<<DD_SCK));
    // Define the following pins as output
    DDR_SPI |= ((1<<DD_MOSI)|(1<<DD_SS)|(1<<DD_SCK));

    
    SPCR = ((1<<SPE)|               // SPI Enable
            (0<<SPIE)|              // SPI Interupt Enable
            (0<<DORD)|              // Data Order (0:MSB first / 1:LSB first)
            (1<<MSTR)|              // Master/Slave select   
            (1<<SPR1)|(0<<SPR0)|    // SPI Clock Rate
            (0<<CPOL)|              // Clock Polarity (0:SCK low / 1:SCK hi when idle)
            (0<<CPHA));             // Clock Phase (0:leading / 1:trailing edge sampling)

    SPSR = (0<<SPI2X);              // Double Clock Rate
    
}

void nrf_transfer_sync (uint8_t * dataout, uint8_t len)
{
       uint8_t i;      
       for (i = 1; i < len; i++) {
             SPDR = 1		;
             while((SPSR & (1<<SPIF))==0);
			 dataout[i] = SPDR;
			 }
			 return SPDR;
}


void spi_transfer_sync (uint8_t * dataout, uint8_t * datain, uint8_t len)
// Shift full array through target device
{
       uint8_t i;      
       for (i = 0; i < len; i++) {
             SPDR = dataout[i];
             while((SPSR & (1<<SPIF))==0);
             datain[i] = SPDR;
			 }
			 return SPDR;
}

void spi_transmit_sync (uint8_t * dataout, uint8_t len)
// Shift full array to target device without receiving any byte
{
       uint8_t i;      
       for (i = 0; i < len; i++) {
             SPDR = dataout[i];
             while((SPSR & (1<<SPIF))==0);
       }
}

uint8_t spi_fast_shift (uint8_t data)
// Clocks only one byte to target device and returns the received one
{
    SPDR = data;
    while((SPSR & (1<<SPIF))==0);
    return SPDR;
}

nRF24L01.h

/* Memory Map */
#define CONFIG      0x00
#define EN_AA       0x01
#define EN_RXADDR   0x02
#define SETUP_AW    0x03
#define SETUP_RETR  0x04
#define RF_CH       0x05
#define RF_SETUP    0x06
#define STATUS      0x07
#define OBSERVE_TX  0x08
#define CD          0x09
#define RX_ADDR_P0  0x0A
#define RX_ADDR_P1  0x0B
#define RX_ADDR_P2  0x0C
#define RX_ADDR_P3  0x0D
#define RX_ADDR_P4  0x0E
#define RX_ADDR_P5  0x0F
#define TX_ADDR     0x10
#define RX_PW_P0    0x11
#define RX_PW_P1    0x12
#define RX_PW_P2    0x13
#define RX_PW_P3    0x14
#define RX_PW_P4    0x15
#define RX_PW_P5    0x16
#define FIFO_STATUS 0x17

/* Bit Mnemonics */
#define MASK_RX_DR  6
#define MASK_TX_DS  5
#define MASK_MAX_RT 4
#define EN_CRC      3
#define CRCO        2
#define PWR_UP      1
#define PRIM_RX     0
#define ENAA_P5     5
#define ENAA_P4     4
#define ENAA_P3     3
#define ENAA_P2     2
#define ENAA_P1     1
#define ENAA_P0     0
#define ERX_P5      5
#define ERX_P4      4
#define ERX_P3      3
#define ERX_P2      2
#define ERX_P1      1
#define ERX_P0      0
#define AW          0
#define ARD         4
#define ARC         0
#define PLL_LOCK    4
#define RF_DR       3
#define RF_PWR      1
#define LNA_HCURR   0        
#define RX_DR       6
#define TX_DS       5
#define MAX_RT      4
#define RX_P_NO     1
#define TX_FULL     0
#define PLOS_CNT    4
#define ARC_CNT     0
#define TX_REUSE    6
#define FIFO_FULL   5
#define TX_EMPTY    4
#define RX_FULL     1
#define RX_EMPTY    0

/* Instruction Mnemonics */
#define R_REGISTER    0x00
#define W_REGISTER    0x20
#define REGISTER_MASK 0x1F
#define R_RX_PAYLOAD  0x61
#define W_TX_PAYLOAD  0xA0
#define FLUSH_TX      0xE1
#define FLUSH_RX      0xE2
#define REUSE_TX_PL   0xE3
#define NOP           0xFF

Well I see no code directly there to read the config register, I presume you would basically call nrf_read_register(0x00, 1); to read the 0x00 register (config) and read 1 byte of data, which would be the config information.

Now I’m not to sure as the code isn’t there but is SPDR an in built SPI value and if so is that not destroyed on read so you have to store it?

If you take a look at the nrf_transfer_sync function that is called when reading a register, it passes in the register to the function (which is called dataout in the function), which is used to store the returned value:

dataout = SPDR;
So back in the nrf_read_register function after this call:
nrf_transfer_sync(reg, len); //Read register
The reg would then contain the actual config value, not the original register, yet it then does:
rprintf(“Value register %d: %d\r\n”,reg, SPDR);
Which would print the value, and then SPDR, not what I presume it mean to print which is the address of the register and THEN the value, as reg should be overwrote in the nrf_transfer_sync function.
Now what gets me is you say all the other registers work, so I’m a little puzzled as to how they could when it looks fundamentally wrong… unless the SPDR register is not the internal destroyable value?

SPI.c

#include "spi.h"

#include <avr/io.h>
#include <avr/interrupt.h>

#define PORT_SPI    PORTB
#define DDR_SPI     DDRB
#define DD_MISO     DDB3
#define DD_MOSI     DDB2
#define DD_SS       DDB0 //CSN
#define DD_SCK      DDB1


void spi_init()
// Initialize pins for spi communication
{
    DDR_SPI &= ~((1<<DD_MOSI)|(1<<DD_MISO)|(1<<DD_SS)|(1<<DD_SCK));
    // Define the following pins as output
    DDR_SPI |= ((1<<DD_MOSI)|(1<<DD_SS)|(1<<DD_SCK));

    
    SPCR = ((1<<SPE)|               // SPI Enable
            (0<<SPIE)|              // SPI Interupt Enable
            (0<<DORD)|              // Data Order (0:MSB first / 1:LSB first)
            (1<<MSTR)|              // Master/Slave select   
            (1<<SPR1)|(0<<SPR0)|    // SPI Clock Rate
            (0<<CPOL)|              // Clock Polarity (0:SCK low / 1:SCK hi when idle)
            (0<<CPHA));             // Clock Phase (0:leading / 1:trailing edge sampling)

    SPSR = (0<<SPI2X);              // Double Clock Rate
    
}

void nrf_transfer_sync (uint8_t * dataout, uint8_t len)
{
       uint8_t i;      
       for (i = 1; i < len; i++) {
             SPDR = 1		;
             while((SPSR & (1<<SPIF))==0);
			 dataout[i] = SPDR;
			 }
			 return SPDR;
}


void spi_transfer_sync (uint8_t * dataout, uint8_t * datain, uint8_t len)
// Shift full array through target device
{
       uint8_t i;      
       for (i = 0; i < len; i++) {
             SPDR = dataout[i];
             while((SPSR & (1<<SPIF))==0);
             datain[i] = SPDR;
			 }
			 return SPDR;
}

void spi_transmit_sync (uint8_t * dataout, uint8_t len)
// Shift full array to target device without receiving any byte
{
       uint8_t i;      
       for (i = 0; i < len; i++) {
             SPDR = dataout[i];
             while((SPSR & (1<<SPIF))==0);
       }
}

uint8_t spi_fast_shift (uint8_t data)
// Clocks only one byte to target device and returns the received one
{
    SPDR = data;
    while((SPSR & (1<<SPIF))==0);
    return SPDR;
}

Well… If I use the following:

nrf_read_register(CONFIG,2); it gives:

Value register d: d ← Which comes from the %d

nrf_read_register(RF_CH,2); it gives:

Value register 5: 21 ← Which is correct

That is what I think is wird. I can’t really see the difference between these two functions. Furthermore if I change the second parameter (lenght) to 1 they both return 14 which is wrong.

It’s very unlikely its going to be the nordic with a faulty register I’d say its more than likely the code but the way I follow that code it shouldn’t even work as it does so I can’t really say what to fix other that writing your own functions instead of that library it only took me about 6-7 hours from scratch after reading the datasheet and that was in ASM so C should be less tedious.

Also, try adding a breakpoint in debugging right before bytes are clocked out of SPI and check that at that point the correct info for the config register and another example register that does work, and see what they show.