Testing nRF24L01 SPI functionality

The following program for the Microchip C18 compiler based on the software SPI function posted here recently might be useful for testing that your SPI interface to the nRF24L01 is working:

/*
** test.c
** SPI test program for PIC18F4520 and nRF24L01
*/

#include <p18f4520.h>

//function prototypes
unsigned char spi_Send_Read(unsigned char);

// Defines
#define SPI_SCK         LATCbits.LATC3		// Clock pin, PORTC pin 3 
#define SPI_SO          LATCbits.LATC5		// Serial output pin, PORTC pin 5 
#define SPI_SI          PORTCbits.RC4       	// Serial input pin, PORTC pin 4 
#define SPI_CSN		LATCbits.LATC2		// CSN output pin, PORTC pin 2
#define SPI_CE		LATCbits.LATC1		// CE output pin, PORTC pin 1
#define SPI_IRQ		PORTBbits.RB0		// IRQ input pin, PORTB pin 0
#define SPI_SCALE	4              		// postscaling of signal 

// Macros
#define nop() _asm nop _endasm

void main(void)
{
	unsigned char status = 0;
	unsigned char data[5];

	PORTA = 0x00;
	ADCON1 = 0x0F;		// set up PORTA to be digital I/Os
	TRISA = 0x00;		// set up all PORTA pins to be digital outputs
	TRISCbits.TRISC3 = 0;	// SDO output
	TRISCbits.TRISC5 = 0;   // SCK output
	TRISCbits.TRISC2 = 0;	// CSN output
	TRISCbits.TRISC1 = 0;	// CE output
	SPI_CSN = 1;		// CSN high
	SPI_SCK = 0;		// SCK low
	SPI_CE	= 0;		// CE low
	nop();


	//write TX_ADDRESS register
	SPI_CSN = 0;			//CSN low
	spi_Send_Read(0x30);
	spi_Send_Read(0x11);
	spi_Send_Read(0x22);
	spi_Send_Read(0x33);
	spi_Send_Read(0x44);
	spi_Send_Read(0x55);
	SPI_CSN = 1;			//CSN high



	//read TX_ADDRESS register
	SPI_CSN = 0;			//CSN low
	status = spi_Send_Read(0x10);
	data[0] = spi_Send_Read(0x00);
	data[1] = spi_Send_Read(0x00);
	data[2] = spi_Send_Read(0x00);
	data[3] = spi_Send_Read(0x00);
	data[4] = spi_Send_Read(0x00);
	SPI_CSN = 1;			//CSN high

	while (1)
		;
}

/******************************************************************** 
 * Function:        unsigned char spi_Send_Read(unsigned char bytein) 
 * 
 * Description:     This function outputs a single byte onto the 
 *                  SPI bus, MSb first. And it gets the response. 
 *******************************************************************/ 
unsigned char spi_Send_Read(unsigned char bytein) 
{ 
    static unsigned char i;         // Loop counter 
   static unsigned char j;         // Delay counter 
   unsigned char byteout = 0;      // return bit 

    SPI_SCK = 0;                        // Ensure SCK is low 
    for (i = 0; i < 8; i++)         // Loop through each bit 
    { 
        if (bytein & 0x80)          // Check if next outbit is a 1 
        { 
            SPI_SO = 1;                 // If a 1, pull SO high 
        } 
        else 
        { 
            SPI_SO = 0;                 // If a 0, pull SO low 
        } 
        byteout = byteout << 1;     // Shift outbyte left for next bit 
        if (SPI_SI == 1)                // Check if next inbit is a 1 
        { 
            byteout |= 0x01;         // If a 1, set next bit to 1 
        } 
        else 
        { 
            byteout &= 0xFE;         // If a 0, set next bit to 0 
        } 
        SPI_SCK = 1;                    // Bring SCK high to latch bit 
        for (j = 0; j < SPI_SCALE; j++) 
      { 
         nop();                      // Avoid violating Thi 
      } 
        SPI_SCK = 0;                    // Bring SCK low for next bit 
        bytein = bytein << 1;       // Shift byte left for next bit 
    } 
   return byteout; 
} // end of spi_Send_Read(...)

Run the program under the MPLAB debugger with the data array displayed in the Watch window; you should see the values written to the TX_ADDRESS register read back correctly.

Leon

I’ve improved upon my Software SPI function, because it was too slow… I only got around 350 kbps with a 44Mhz clock. These functions run much faster (around 4 to 6 times). I had to unravel the for loop and do things a bit different, but it works.

There are 3 functions. One for reading and writing, one for reading and one for writing.

Oh, if somebody can help me with speeding up the UART aswell, please do… I’m stuck at that (see Projects forum)…

I changed the SPI code of Brennen to accommodate these changes:

//low-level spi send function for library use
//the user should not call this function directly, but rather use one of the 8 SPI data instructions
unsigned char nrf24l01_execute_command(unsigned char instruction, unsigned char * data, unsigned int len, bool copydata)
{
	unsigned char status;
	
	nrf24l01_clear_csn();

	status = spi_Send_Read(instruction);
	nrf24l01_spi_send_read(data, len, copydata);
	
	nrf24l01_set_csn();		

	return status;
}


//low-level spi send function for library use
//the user should not call this function directly, but rather use one of the 8 SPI data instructions
void nrf24l01_spi_send_read(unsigned char * data, unsigned int len, bool copydata)
{
	unsigned int count;
	
	for(count = 0; count < len; count++)
	{
		if(copydata == true)
			data[count] = spi_Read(); // only read
		else
		{
			spi_Send(data[count]); // only write
		}
	}
}

Here are the SPI functions:

void spi_Send(unsigned char bytein) // Written by Jaap van den Bosch
{
    SPI_SCK = 0;                    // Ensure SCK is low
    if (bytein & 0x80)          	// Check if next outbit is a 1
    {
        SPI_SO = 1;                 // If a 1, pull SO high
    }
    SPI_SCK = 1;                    // Bring SCK high to latch bit
	SPI_SO = 0; 					// Reset SO to zero



    SPI_SCK = 0;                    // Bring SCK low for next bit
    if (bytein & 0x40)          	// Check if next outbit is a 1
    {
        SPI_SO = 1;                 // If a 1, pull SO high
    }
    SPI_SCK = 1;                    // Bring SCK high to latch bit
	SPI_SO = 0; 					// Reset SO to zero



    SPI_SCK = 0;                    // Bring SCK low for next bit
    if (bytein & 0x20)          	// Check if next outbit is a 1
    {
        SPI_SO = 1;                 // If a 1, pull SO high
    }
    SPI_SCK = 1;                    // Bring SCK high to latch bit
	SPI_SO = 0; 					// Reset SO to zero



    SPI_SCK = 0;                    // Bring SCK low for next bit
    if (bytein & 0x10)          	// Check if next outbit is a 1
    {
        SPI_SO = 1;                 // If a 1, pull SO high
    }
    SPI_SCK = 1;                    // Bring SCK high to latch bit
	SPI_SO = 0; 					// Reset SO to zero



    SPI_SCK = 0;                    // Bring SCK low for next bit
    if (bytein & 0x08)          	// Check if next outbit is a 1
    {
        SPI_SO = 1;                 // If a 1, pull SO high
    }
    SPI_SCK = 1;                    // Bring SCK high to latch bit
	SPI_SO = 0; 					// Reset SO to zero



    SPI_SCK = 0;                    // Bring SCK low for next bit
    if (bytein & 0x04)          	// Check if next outbit is a 1
    {
        SPI_SO = 1;                 // If a 1, pull SO high
    }
    SPI_SCK = 1;                    // Bring SCK high to latch bit
	SPI_SO = 0; 					// Reset SO to zero



    SPI_SCK = 0;                    // Bring SCK low for next bit
    if (bytein & 0x02)          	// Check if next outbit is a 1
    {
        SPI_SO = 1;                 // If a 1, pull SO high
    }
    SPI_SCK = 1;                    // Bring SCK high to latch bit
	SPI_SO = 0; 					// Reset SO to zero



    SPI_SCK = 0;                    // Bring SCK low for next bit
    if (bytein & 0x01)          	// Check if next outbit is a 1
    {
        SPI_SO = 1;                 // If a 1, pull SO high
    }
    SPI_SCK = 1;                    // Bring SCK high to latch bit
	SPI_SO = 0; 					// Reset SO to zero
    SPI_SCK = 0;                    // Bring SCK low for next bit

} // end of spi_Send(...)

unsigned char spi_Read(void) // Written by Jaap van den Bosch
{
	unsigned char byteout = 0x00;	// return byte

	SPI_SO = 0; 					// Reset SO to zero

    SPI_SCK = 0;                    // Ensure SCK is low
	Nop();
    if (SPI_SI == 1)                // Check if next inbit is a 1
    {
        byteout |= 0x80;         	// If a 1, set next bit to 1
    }
    SPI_SCK = 1;                    // Bring SCK high to latch bit


    SPI_SCK = 0;                    // Bring SCK low for next bit
	Nop();
    if (SPI_SI == 1)                // Check if next inbit is a 1
    {
        byteout |= 0x40;         	// If a 1, set next bit to 1
    }
    SPI_SCK = 1;                    // Bring SCK high to latch bit


    SPI_SCK = 0;                    // Bring SCK low for next bit
	Nop();
    if (SPI_SI == 1)                // Check if next inbit is a 1
    {
        byteout |= 0x20;         	// If a 1, set next bit to 1
    }
    SPI_SCK = 1;                    // Bring SCK high to latch bit


    SPI_SCK = 0;                    // Bring SCK low for next bit
	Nop();
    if (SPI_SI == 1)                // Check if next inbit is a 1
    {
        byteout |= 0x10;         	// If a 1, set next bit to 1
    }
    SPI_SCK = 1;                    // Bring SCK high to latch bit


    SPI_SCK = 0;                    // Bring SCK low for next bit
	Nop();
    if (SPI_SI == 1)                // Check if next inbit is a 1
    {
        byteout |= 0x08;         	// If a 1, set next bit to 1
    }
    SPI_SCK = 1;                    // Bring SCK high to latch bit


    SPI_SCK = 0;                    // Bring SCK low for next bit
	Nop();
    if (SPI_SI == 1)                // Check if next inbit is a 1
    {
        byteout |= 0x04;         	// If a 1, set next bit to 1
    }
    SPI_SCK = 1;                    // Bring SCK high to latch bit


    SPI_SCK = 0;                    // Bring SCK low for next bit
	Nop();
    if (SPI_SI == 1)                // Check if next inbit is a 1
    {
        byteout |= 0x02;         	// If a 1, set next bit to 1
    }
    SPI_SCK = 1;                    // Bring SCK high to latch bit


    SPI_SCK = 0;                    // Bring SCK low for next bit
	Nop();
    if (SPI_SI == 1)                // Check if next inbit is a 1
    {
        byteout |= 0x01;         	// If a 1, set next bit to 1
    }
    SPI_SCK = 1;                    // Bring SCK high to latch bit
    SPI_SCK = 0;                    // Bring SCK low for next bit

	return byteout;
} // end of spi_Read(...)


unsigned char spi_Send_Read(unsigned char bytein) // Written by Jaap van den Bosch
{
	unsigned char byteout = 0x00;	// return byte

    SPI_SCK = 0;                    // Ensure SCK is low
    if (bytein & 0x80)          	// Check if next outbit is a 1
    {
        SPI_SO = 1;                 // If a 1, pull SO high
    }
    if (SPI_SI == 1)                // Check if next inbit is a 1
    {
        byteout |= 0x80;         	// If a 1, set next bit to 1
    }
    SPI_SCK = 1;                    // Bring SCK high to latch bit
	SPI_SO = 0; 					// Reset SO to zero



    SPI_SCK = 0;                    // Bring SCK low for next bit
    if (bytein & 0x40)          	// Check if next outbit is a 1
    {
        SPI_SO = 1;                 // If a 1, pull SO high
    }
    if (SPI_SI == 1)                // Check if next inbit is a 1
    {
        byteout |= 0x40;         	// If a 1, set next bit to 1
    }
    SPI_SCK = 1;                    // Bring SCK high to latch bit
	SPI_SO = 0; 					// Reset SO to zero



    SPI_SCK = 0;                    // Bring SCK low for next bit
    if (bytein & 0x20)          	// Check if next outbit is a 1
    {
        SPI_SO = 1;                 // If a 1, pull SO high
    }
    if (SPI_SI == 1)                // Check if next inbit is a 1
    {
        byteout |= 0x20;         	// If a 1, set next bit to 1
    }
    SPI_SCK = 1;                    // Bring SCK high to latch bit
	SPI_SO = 0; 					// Reset SO to zero



    SPI_SCK = 0;                    // Bring SCK low for next bit
    if (bytein & 0x10)          	// Check if next outbit is a 1
    {
        SPI_SO = 1;                 // If a 1, pull SO high
    }
    if (SPI_SI == 1)                // Check if next inbit is a 1
    {
        byteout |= 0x10;         	// If a 1, set next bit to 1
    }
    SPI_SCK = 1;                    // Bring SCK high to latch bit
	SPI_SO = 0; 					// Reset SO to zero



    SPI_SCK = 0;                    // Bring SCK low for next bit
    if (bytein & 0x08)          	// Check if next outbit is a 1
    {
        SPI_SO = 1;                 // If a 1, pull SO high
    }
    if (SPI_SI == 1)                // Check if next inbit is a 1
    {
        byteout |= 0x08;         	// If a 1, set next bit to 1
    }
    SPI_SCK = 1;                    // Bring SCK high to latch bit
	SPI_SO = 0; 					// Reset SO to zero



    SPI_SCK = 0;                    // Bring SCK low for next bit
    if (bytein & 0x04)          	// Check if next outbit is a 1
    {
        SPI_SO = 1;                 // If a 1, pull SO high
    }
    if (SPI_SI == 1)                // Check if next inbit is a 1
    {
        byteout |= 0x04;         	// If a 1, set next bit to 1
    }
    SPI_SCK = 1;                    // Bring SCK high to latch bit
	SPI_SO = 0; 					// Reset SO to zero



    SPI_SCK = 0;                    // Bring SCK low for next bit
    if (bytein & 0x02)          	// Check if next outbit is a 1
    {
        SPI_SO = 1;                 // If a 1, pull SO high
    }
    if (SPI_SI == 1)                // Check if next inbit is a 1
    {
        byteout |= 0x02;         	// If a 1, set next bit to 1
    }
    SPI_SCK = 1;                    // Bring SCK high to latch bit
	SPI_SO = 0; 					// Reset SO to zero



    SPI_SCK = 0;                    // Bring SCK low for next bit
    if (bytein & 0x01)          	// Check if next outbit is a 1
    {
        SPI_SO = 1;                 // If a 1, pull SO high
    }
    if (SPI_SI == 1)                // Check if next inbit is a 1
    {
        byteout |= 0x01;         	// If a 1, set next bit to 1
    }
    SPI_SCK = 1;                    // Bring SCK high to latch bit
	SPI_SO = 0; 					// Reset SO to zero
    SPI_SCK = 0;                    // Bring SCK low for next bit

	return byteout;
} // end of spi_Send_Read(...)

[/code]

Hi JaapvdBosch,

Thanks for the code! It’s really speedup a lot! I have improved your code to have the 50% duty cycle for the clock.

unsigned char spi_send_read_byte(unsigned char bytein)
{ 
	unsigned char byteout = 0x00;

    SCK = 0;
    if (bytein & 0x80)
    	MOSI = 1;
	else
		MOSI = 0;
    SCK = 1;
	if (MISO == 1)
        byteout |= 0x80;
	else
		byteout &= 0x7f;
    SCK = 0;
    if (bytein & 0x40)
       MOSI = 1;
	else
	   MOSI = 0;
    SCK = 1;
	if (MISO == 1)
        byteout |= 0x40;
	else
		byteout &= 0xbf;
    SCK = 0;
    if (bytein & 0x20)
       MOSI = 1;
	else
	   MOSI = 0;
    SCK = 1;
	if (MISO == 1)
        byteout |= 0x20;
	else
		byteout &= 0xdf;
    SCK = 0;
    if (bytein & 0x10)
       MOSI = 1;
	else
	   MOSI = 0;
    SCK = 1;
	if (MISO == 1)
        byteout |= 0x10;
	else
		byteout &= 0xef;
    SCK = 0;
    if (bytein & 0x08)
       MOSI = 1;
	else
	   MOSI = 0;
    SCK = 1;
	if (MISO == 1)
        byteout |= 0x08;
	else
		byteout &= 0xf7;
    SCK = 0;
    if (bytein & 0x04)
       MOSI = 1;
	else
	   MOSI = 0;
    SCK = 1;
	if (MISO == 1)
        byteout |= 0x04;
	else
		byteout &= 0xfb;
    SCK = 0;
    if (bytein & 0x02)
       MOSI = 1;
	else
	   MOSI = 0;
    SCK = 1;
	if (MISO == 1)
        byteout |= 0x02;
	else
		byteout &= 0xfd;
    SCK = 0;
    if (bytein & 0x01)
       MOSI = 1;
	else
	   MOSI = 0;
    SCK = 1;
	if (MISO == 1)
        byteout |= 0x01;
	else
		byteout &= 0xfe;
    SCK = 0;
	MOSI = 1;
	return	byteout;
}

You’re welcome. The shifting in the original code (i.e. byteout = byteout << 1; ) was very costly instuctioncyclewise. This speeded things up a lot!