Problem using NRF24L01 and pic18F45k22

Status
Not open for further replies.

Octajul

Newbie level 2
Joined
May 7, 2018
Messages
2
Helped
0
Reputation
0
Reaction score
0
Trophy points
1
Activity points
38
I been trying to learn how to communicate two pic18f45k22 using NRF24L01 I did some research and found a tutorial for pic18F452 so i used this to build one for pic18F45K22 but when i try it doesnt works as it should and im running out of ideas to solve it so I would really appreciate some help, here's the link for a zip of both of my codes RX and TX and the PDF tutorial with the source files. I should add thatm im using and external clock of 8MHZ, MPLAB X, XC8 V.1.45, I downloaded the peripheral libraries for Pic18F and the connections are the following:

24L01 pins (1) Vcc -> +5Vdc supply
(2) CE -> uC pin PORTC.1, configured as GPIO/input
(3) CSN -> uC pin PORTC.2, configured as GPIO/output
(4) SCK -> uC pin PORTC.3/SCK, configured as SCK(SPI)/output
(5) MOSI -> uC pin PORTC.5/SDO, configured as SDO(SPI)/output
(6) MISO -> uC pin PORTC.4/SDI, configured as SDI(SPI)/input
(7) IRQ -> uC pin PORTB.0, configured as GPIO/input
(8) GND -> common digital ground

Thanks in advance
 

I can't see the link but you also need to tell us what does work.
For example, can you 'flash a LED' on both boards - that will tell you that you can program the boards successfully and that you have a working set of CONFIG settings?
Can you use an oscilloscope to see the signals going between the PIC and the NRF24L01?
Can you read the registers on the NRF24L01, and can you alter them and then read back the altered values?
Next you should tell us what you have tried, what you expected to happen and what actually did (or didn't) happen. ("...it doesnt[sic] works[sic]..." tells us nothing.)
I've had a couple of these devices talking between a Raspberry Pi and a Cypress PSoC but using an PIC18 should actually be easier than either of those (once you have the basics set up). I do know that you need to get the timing right to have reliable communications between the MCU and the NRF24L01 to actually send values, but communicating with the various registers is fairly straight forward.
Susan
 

ok, i get it sorry for the way i post it's my first time and i have'nt practice my english in a while.

So i have tried many things first i'm able to flash a led using the function written in main file "ToggleLED", i tested it with delays to see if the times are ok. I dont have an oscilloscope right know but i wrote recently a code to use SPI module from the same PIC to read and write form a SD card successfully. I tried to send a value from one PIC to another using the NRF24L01 and if the other PIC receives something flash a LED but it does not work. i also tried to flash a LED when the while loop starts again in both PICs during my tries to send and receive data but for some reason it does not works it gets stuck or something. I dont really know what else could I try or what's the problem. Any suggestion or solution i would really appreciated.

that's the zip i mentioned earlier with all the stuff
View attachment NRF24l01.zip

Additionally here's the code

Code Main RX and TX
Code:
#include "mcc_generated_files/mcc.h"
#include "nrf24l01.h"
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#define _XTAL_FREQ 8000000
/*
                         Main application
 */
 
void Initialize(void);
void InitializeIO(void);
 
void ToggleLED(void); //toggle the current state of the on-board LED
 
 
void main(void)
{
    unsigned char data; //register to hold letter sent and received
	unsigned int count; //counter for for loop
 
 
    // Initialize the device
    Initialize(); //initialize IO, UART, SPI, set up nRF24L01 as TX
 
    while (1)
    {
 
        data = 4; //get data from UART
 
		nrf24l01_write_tx_payload(&data, 1, true); //transmit received char over RF
 
		//wait until the packet has been sent or the maximum number of retries has been reached
		while(!(nrf24l01_irq_pin_active() && nrf24l01_irq_tx_ds_active()));
 
		nrf24l01_irq_clear_all(); //clear all interrupts in the 24L01
		nrf24l01_set_as_rx(true); //change the device to an RX to get the character back from the other 24L01
 
		//wait a while to see if we get the data back (change the loop maximum and the lower if
		//  argument (should be loop maximum - 1) to lengthen or shorten this time frame
		for(count = 0; count < 20000; count++)
		{
            //check to see if the data has been received.  if so, get the data and exit the loop.
			//  if the loop is at its last count, assume the packet has been lost and set the data
			//  to go to the UART to "?".  If neither of these is true, keep looping.
            if((nrf24l01_irq_pin_active() && nrf24l01_irq_rx_dr_active()))
			{
                nrf24l01_read_rx_payload(&data, 1); //get the payload into data
				break;
			}
 
 
			//if loop is on its last iteration, assume packet has been lost.
			if(count == 19999)
			data = '?';
			}
 
		nrf24l01_irq_clear_all(); //clear interrupts again
		__delay_us(130);
		nrf24l01_set_as_tx(); //resume normal operation as a TX
 
		ToggleLED(); //toggle the on-board LED as visual indication that the loop has completed
 
    }
}
 
 
void Initialize(void)
{
    SYSTEM_Initialize();
	InitializeIO(); //set up IO (directions and functions)
 
 	OpenSPI(SPI_FOSC_16, MODE_00, SMPMID); //open SPI1
	nrf24l01_initialize_debug(false, 1, false); //initialize the 24L01 to the debug configuration as TX, 1 data byte, and auto-ack disabled
 
}
 
//initialize IO pins
void InitializeIO(void)
{
 
	PORTAbits.RA0 = 1; //turn on LED
 
}	
 
//toggles on-board LED
void ToggleLED(void)
{
	PORTAbits.RA0 = ~PORTAbits.RA0; //invert the bit that controls the LED
}
/**
 End of File
*/

Next

Code:
#include "mcc_generated_files/mcc.h"
#include "nrf24l01.h"
#include <stdint.h>
#include <stdbool.h>
#include <spi.h>
#include <stddef.h>
#define _XTAL_FREQ 8000000
/*
                         Main application
 */
 
void Initialize(void);
void InitializeIO(void);
 
void ToggleLED(void); //toggle the current state of the on-board LED
 
 
void main(void)
{
    unsigned char data; //register to hold letter sent and received
 
 
 
    // Initialize the device
    Initialize(); //initialize IO, UART, SPI, set up nRF24L01 as TX
 
    while (1)
    {
 
        while(!(nrf24l01_irq_pin_active() && nrf24l01_irq_rx_dr_active())); 
 
        nrf24l01_read_rx_payload(&data, 1); //read the packet into data 
        nrf24l01_irq_clear_all(); //clear all interrupts in the 24L01 
 
        __delay_us(130); //wait for the other 24L01 to come from standby to RX 
 
         nrf24l01_set_as_tx(); //change the device to a TX to send back from the other 24L01 
        nrf24l01_write_tx_payload(&data, 1, true); //transmit received char over RF 
 
        //wait until the packet has been sent 
        while(!(nrf24l01_irq_pin_active() && nrf24l01_irq_tx_ds_active())); 
 
         nrf24l01_irq_clear_all(); //clear interrupts again 
        nrf24l01_set_as_rx(true); //resume normal operation as an RX 
 
        ToggleLED(); //toggle the on-board LED as visual indication that the loop has completed 
 
    }
}
 
 
void Initialize(void)
{
    SYSTEM_Initialize();
	InitializeIO(); //set up IO (directions and functions)
//	
 	//OpenSPI(SPI_FOSC_16, MODE_00, SMPMID); //open SPI1
	nrf24l01_initialize_debug(true, 1, false); //initialize the 24L01 to the debug configuration as TX, 1 data byte, and auto-ack disabled
}
 
//initialize IO pins
void InitializeIO(void)
{
	PORTAbits.RA0 = 1; //turn on LED
 
}	
 
//toggles on-board LED
void ToggleLED(void)
{
	PORTAbits.RA0 = ~PORTAbits.RA0; //invert the bit that controls the LED
}
/**
 End of File
*/

This is the NRF24L01 .C and .h files

Code:
#include "nrf24l01.h"
#include "mcc_generated_files/mcc.h"
#include <xc.h> // include processor files - each processor file is guarded.  
//Arguments except opt_rx_standby_mode fill the actual register they are named 
//  after. Registers that do not need to be initialized are not included here.
//The argument opt_rx_active_mode is only used if the user is initializing the
//  24L01 as a receiver.  If the argument is false, the receiver will remain in
//  standby mode and not monitor for packets.  If the argument is true, the CE
//  pin will be set and the 24L01 will monitor for packets.  In TX mode, the value
//  of this argument is insignificant.
//If the user wants to leave any 1-byte register in its default state, simply put
//  as that register's argument nrf24l01_<reg>_DEFAULT_VAL, where <reg> is the register
//  name.
//If the user wants to leave any of the 5-byte registers RX_ADDR_P0, RX_ADDR_P1, or 
//  TX_ADDR in its default state, simply put NULL in the argument for that address value.
#define _XTAL_FREQ 8000000
void nrf24l01_initialize(unsigned char config,
						 unsigned char opt_rx_active_mode,  
						 unsigned char en_aa, 
						 unsigned char en_rxaddr, 
						 unsigned char setup_aw, 
						 unsigned char setup_retr, 
						 unsigned char rf_ch, 
						 unsigned char rf_setup, 
						 unsigned char * rx_addr_p0, 
						 unsigned char * rx_addr_p1, 
						 unsigned char rx_addr_p2, 
						 unsigned char rx_addr_p3, 
						 unsigned char rx_addr_p4, 
						 unsigned char rx_addr_p5, 
						 unsigned char * tx_addr, 
						 unsigned char rx_pw_p0, 
						 unsigned char rx_pw_p1, 
						 unsigned char rx_pw_p2, 
						 unsigned char rx_pw_p3, 
						 unsigned char rx_pw_p4, 
						 unsigned char rx_pw_p5)
{
	unsigned char data[5];
 
	data[0] = en_aa;
	nrf24l01_write_register(nrf24l01_EN_AA, data, 1);
 
	data[0] = en_rxaddr;
	nrf24l01_write_register(nrf24l01_EN_RXADDR, data, 1);
 
	data[0] = setup_aw;
	nrf24l01_write_register(nrf24l01_SETUP_AW, data, 1);
 
	data[0] = setup_retr;
	nrf24l01_write_register(nrf24l01_SETUP_RETR, data, 1);
 
	data[0] = rf_ch;
	nrf24l01_write_register(nrf24l01_RF_CH, data, 1);
 
	data[0] = rf_setup;
	nrf24l01_write_register(nrf24l01_RF_SETUP, data, 1);
 
	if(rx_addr_p0 != NULL)
		nrf24l01_set_rx_addr(rx_addr_p0, 5, 0);
	else
	{
		data[0] = nrf24l01_RX_ADDR_P0_B0_DEFAULT_VAL;
		data[1] = nrf24l01_RX_ADDR_P0_B1_DEFAULT_VAL;
		data[2] = nrf24l01_RX_ADDR_P0_B2_DEFAULT_VAL;
		data[3] = nrf24l01_RX_ADDR_P0_B3_DEFAULT_VAL;
		data[4] = nrf24l01_RX_ADDR_P0_B4_DEFAULT_VAL;
 
		nrf24l01_set_rx_addr(data, 5, 0);
	}
 
	if(rx_addr_p1 != NULL)
		nrf24l01_set_rx_addr(rx_addr_p1, 5, 1);
	else
	{
		data[0] = nrf24l01_RX_ADDR_P1_B0_DEFAULT_VAL;
		data[1] = nrf24l01_RX_ADDR_P1_B1_DEFAULT_VAL;
		data[2] = nrf24l01_RX_ADDR_P1_B2_DEFAULT_VAL;
		data[3] = nrf24l01_RX_ADDR_P1_B3_DEFAULT_VAL;
		data[4] = nrf24l01_RX_ADDR_P1_B4_DEFAULT_VAL;
 
		nrf24l01_set_rx_addr(data, 5, 1);
	}
 
	data[0] = rx_addr_p2;
	nrf24l01_set_rx_addr(data, 1, 2);
 
	data[0] = rx_addr_p3;
	nrf24l01_set_rx_addr(data, 1, 3);
 
	data[0] = rx_addr_p4;
	nrf24l01_set_rx_addr(data, 1, 4);
 
	data[0] = rx_addr_p5;
	nrf24l01_set_rx_addr(data, 1, 5);
 
	if(tx_addr != NULL)
		nrf24l01_set_tx_addr(tx_addr, 5);
	else
	{
		data[0] = nrf24l01_TX_ADDR_B0_DEFAULT_VAL;
		data[1] = nrf24l01_TX_ADDR_B1_DEFAULT_VAL;
		data[2] = nrf24l01_TX_ADDR_B2_DEFAULT_VAL;
		data[3] = nrf24l01_TX_ADDR_B3_DEFAULT_VAL;
		data[4] = nrf24l01_TX_ADDR_B4_DEFAULT_VAL;
 
		nrf24l01_set_tx_addr(data, 5);
	}
 
	data[0] = rx_pw_p0;
	nrf24l01_write_register(nrf24l01_RX_PW_P0, data, 1);
 
	data[0] = rx_pw_p1;
	nrf24l01_write_register(nrf24l01_RX_PW_P1, data, 1);
 
	data[0] = rx_pw_p2;
	nrf24l01_write_register(nrf24l01_RX_PW_P2, data, 1);
 
	data[0] = rx_pw_p3;
	nrf24l01_write_register(nrf24l01_RX_PW_P3, data, 1);
 
	data[0] = rx_pw_p4;
	nrf24l01_write_register(nrf24l01_RX_PW_P4, data, 1);
 
	data[0] = rx_pw_p5;
	nrf24l01_write_register(nrf24l01_RX_PW_P5, data, 1);
 
	if((config & nrf24l01_CONFIG_PWR_UP) != 0)
		nrf24l01_power_up_param(opt_rx_active_mode, config);
	else
		nrf24l01_power_down_param(config);
}
 
//initializes the 24L01 to all default values except the PWR_UP and PRIM_RX bits
//this function also disables the auto-ack feature on the chip (EN_AA register is 0)
//bool rx is true if the device should be a receiver and false if it should be
//  a transmitter.
//unsigned char payload_width is the payload width for pipe 0.  All other pipes
//  are left in their default (disabled) state.
//bool enable_auto_ack controls the auto ack feature on pipe 0.  If true, auto-ack will
//  be enabled.  If false, auto-ack is disabled.
void nrf24l01_initialize_debug(bool rx, unsigned char p0_payload_width, bool enable_auto_ack)
{
	unsigned char config;
	unsigned char en_aa;
 
	config = nrf24l01_CONFIG_DEFAULT_VAL | nrf24l01_CONFIG_PWR_UP;
 
	if(enable_auto_ack != false)
		en_aa = nrf24l01_EN_AA_ENAA_P0;
	else
		en_aa = nrf24l01_EN_AA_ENAA_NONE;
 
	if(rx == true)
		config = config | nrf24l01_CONFIG_PRIM_RX;
 
	nrf24l01_initialize(config, 
						true,
						en_aa, 
						nrf24l01_EN_RXADDR_DEFAULT_VAL, 
						nrf24l01_SETUP_AW_DEFAULT_VAL, 
						nrf24l01_SETUP_RETR_DEFAULT_VAL, 
						nrf24l01_RF_CH_DEFAULT_VAL, 
						nrf24l01_RF_SETUP_DEFAULT_VAL,  
						NULL, 
						NULL, 
						nrf24l01_RX_ADDR_P2_DEFAULT_VAL, 
						nrf24l01_RX_ADDR_P3_DEFAULT_VAL, 
						nrf24l01_RX_ADDR_P4_DEFAULT_VAL, 
						nrf24l01_RX_ADDR_P5_DEFAULT_VAL, 
						NULL, 
						p0_payload_width, 
						nrf24l01_RX_PW_P1_DEFAULT_VAL, 
						nrf24l01_RX_PW_P2_DEFAULT_VAL, 
						nrf24l01_RX_PW_P3_DEFAULT_VAL, 
						nrf24l01_RX_PW_P4_DEFAULT_VAL, 
						nrf24l01_RX_PW_P5_DEFAULT_VAL);
}
 
//initializes only the CONFIG register and pipe 0's payload width
//the primary purpose of this function is to allow users with microcontrollers with
//  extremely small program memories to still be able to init their 24L01.  This code
//  should have a smaller footprint than the above init functions.
//when using this method, the 24L01 MUST have its default configuration loaded
//  in all registers to work.  It is recommended that the device be reset or
//  have its power cycled immediately before this code is run.
//in normal circumstances, the user should use nrf24l01_initialize() rather than this
//  function, since this function does not set all of the register values.
void nrf24l01_initialize_debug_lite(bool rx, unsigned char p0_payload_width)
{
	unsigned char config;
 
	config = nrf24l01_CONFIG_DEFAULT_VAL;
 
	if(rx != false)
		config |= nrf24l01_CONFIG_PRIM_RX;
 
	nrf24l01_write_register(nrf24l01_RX_PW_P0, &p0_payload_width, 1);
	nrf24l01_power_up_param(true, config);
}
 
//powers up the 24L01 with all necessary delays
//this function takes the existing contents of the CONFIG register and sets the PWR_UP 
//the argument rx_active_mode is only used if the user is setting up the
//  24L01 as a receiver.  If the argument is false, the receiver will remain in
//  standby mode and not monitor for packets.  If the argument is true, the CE
//  pin will be set and the 24L01 will monitor for packets.  In TX mode, the value
//  of this argument is insignificant.
//note: if the read value of the CONFIG register already has the PWR_UP bit set, this function
//  exits in order to not make an unecessary register write.
void nrf24l01_power_up(bool rx_active_mode)
{
	unsigned char config;
 
	nrf24l01_read_register(nrf24l01_CONFIG, &config, 1);
 
	if((config & nrf24l01_CONFIG_PWR_UP) != 0)
		return;
 
	config |= nrf24l01_CONFIG_PWR_UP;
 
	nrf24l01_write_register(nrf24l01_CONFIG, &config, 1);
 
	__delay_us(1500);
 
	if((config & nrf24l01_CONFIG_PRIM_RX) == 0)
		nrf24l01_clear_ce();
	else
	{
		if(rx_active_mode != false)
			nrf24l01_set_ce();
		else
			nrf24l01_clear_ce();
	}
}
 
//powers up the 24L01 with all necessary delays
//this function allows the user to set the contents of the CONFIG register, but the function
//  sets the PWR_UP bit in the CONFIG register, so the user does not need to.
//the argument rx_active_mode is only used if the user is setting up the
//  24L01 as a receiver.  If the argument is false, the receiver will remain in
//  standby mode and not monitor for packets.  If the argument is true, the CE
//  pin will be set and the 24L01 will monitor for packets.  In TX mode, the value
//  of this argument is insignificant.
void nrf24l01_power_up_param(bool rx_active_mode, unsigned char config)
{
	unsigned char test, test2;
 
	config |= nrf24l01_CONFIG_PWR_UP;
 
	nrf24l01_write_register(nrf24l01_CONFIG, &config, 1);
 
	__delay_us(1500);
 
	if((config & nrf24l01_CONFIG_PRIM_RX) == 0)
		nrf24l01_clear_ce();
	else
	{
		if(rx_active_mode != false)
			nrf24l01_set_ce();
		else
			nrf24l01_clear_ce();
	}
}
 
//powers down the 24L01
//this function takes the existing contents of the CONFIG register and simply
//  clears the PWR_UP bit in the CONFIG register.
//note: if the read value of the CONFIG register already has the PWR_UP bit cleared, this 
//  function exits in order to not make an unecessary register write.
void nrf24l01_power_down()
{
	unsigned char config;
 
	nrf24l01_read_register(nrf24l01_CONFIG, &config, 1);
 
	if((config & nrf24l01_CONFIG_PWR_UP) == 0)
		return;
 
	config &= (~nrf24l01_CONFIG_PWR_UP);
 
	nrf24l01_write_register(nrf24l01_CONFIG, &config, 1);
 
	nrf24l01_clear_ce();
}
 
//powers down the 24L01
//this function allows the user to set the contents of the CONFIG register, but the function
//  clears the PWR_UP bit in the CONFIG register, so the user does not need to.
void nrf24l01_power_down_param(unsigned char config)
{
	config &= (~nrf24l01_CONFIG_PWR_UP);
 
	nrf24l01_write_register(nrf24l01_CONFIG, &config, 1);
 
	nrf24l01_clear_ce();
}
 
 
//sets up the 24L01 as a receiver with all necessary delays
//this function takes the existing contents of the CONFIG register and sets the PRIM_RX 
//  bit in the CONFIG register.
//if the argument rx_active_mode is false, the receiver will remain in standby mode
//  and not monitor for packets.  If the argument is true, the CE pin will be set 
//  and the 24L01 will monitor for packets.
//note: if the read value of the CONFIG register already has the PRIM_RX bit set, this function
//  exits in order to not make an unecessary register write.
void nrf24l01_set_as_rx(bool rx_active_mode)
{
	unsigned char config;
	unsigned char status;
 
	status = nrf24l01_read_register(0, &config, 1);
 
	if((config & nrf24l01_CONFIG_PRIM_RX) != 0)
		return;
 
	config |= nrf24l01_CONFIG_PRIM_RX;
 
	nrf24l01_write_register(nrf24l01_CONFIG, &config, 1);
 
	if(rx_active_mode != false)
		nrf24l01_set_ce();
	else
		nrf24l01_clear_ce();
}
 
//sets up the 24L01 as a receiver with all necessary delays
//this function allows the user to set the contents of the CONFIG register, but the function
//  sets the PRIM_RX bit in the CONFIG register, so the user does not need to.
//if the argument rx_active_mode is false, the receiver will remain in standby mode
//  and not monitor for packets.  If the argument is true, the CE pin will be set 
//  and the 24L01 will monitor for packets.
void nrf24l01_set_as_rx_param(bool rx_active_mode, unsigned char config)
{
	config |= nrf24l01_CONFIG_PRIM_RX;
 
	if((config & nrf24l01_CONFIG_PWR_UP) != 0)
		nrf24l01_power_up_param(rx_active_mode, config);
	else
		nrf24l01_power_down_param(config);
}
 
//takes a 24L01 that is already in RX standby mode and puts it in active RX mode
void nrf24l01_rx_standby_to_active()
{
	nrf24l01_set_ce();
}
 
//takes a 24L01 that is already in active RX mode and puts it in RX standy mode
void nrf24l01_rx_active_to_standby()
{
	nrf24l01_clear_ce();
}
 
//sets up the 24L01 as a transmitter
//this function takes the existing contents of the CONFIG register and simply
//  clears the PRIM_RX bit in the CONFIG register.
//note: if the read value of the CONFIG register already has the PRIM_RX bit cleared, this 
//  function exits in order to not make an unecessary register write.
void nrf24l01_set_as_tx()
{
	unsigned char config;
 
	nrf24l01_read_register(nrf24l01_CONFIG, &config, 1);
 
	if((config & nrf24l01_CONFIG_PRIM_RX) == 0)
		return;
 
	config &= (~nrf24l01_CONFIG_PRIM_RX);
 
	nrf24l01_write_register(nrf24l01_CONFIG, &config, 1);
 
	nrf24l01_clear_ce();
}
 
//sets up the 24L01 as a transmitter
//this function allows the user to set the contents of the CONFIG register, but the function
//  clears the PRIM_RX bit in the CONFIG register, so the user does not need to.
void nrf24l01_set_as_tx_param(unsigned char config)
{
	config &= ~(nrf24l01_CONFIG_PRIM_RX);
 
	if((config & nrf24l01_CONFIG_PWR_UP) != 0)
		nrf24l01_power_up_param(false, config);
	else
		nrf24l01_power_down_param(config);
}
 
//executes the W_REGISTER SPI operation
//unsigned char regnumber indicates the register number assigned by the nrf24l01 specification.
//  For regnumber values, see section titled "register definitions" in nrf24l01.h.
//unsigned char * data should be of size 1 for all register writes except for RX_ADDR_P0, RX_ADDR_P1,
//	and TX_ADDR.  The size of data should be set according to the user-specified size of the address
//  length for the register the address is being sent to.
//unsigned int len is always the size of unsigned char * data.  For example, if data is declared as
//  data[6], len should equal 6.
//returns the value of the STATUS register
unsigned char nrf24l01_write_register(unsigned char regnumber, unsigned char * data, unsigned int len)
{
	return nrf24l01_execute_command(nrf24l01_W_REGISTER | (regnumber & nrf24l01_W_REGISTER_DATA), data, len, false);
}
 
//executes the R_REGISTER SPI operation
//unsigned char regnumber indicates the register number assigned by the nrf24l01 specification.
//  For regnumber values, see section titled "register definitions" in nrf24l01.h.
//unsigned char * data should be of size 1 for all register writes except for RX_ADDR_P0, RX_ADDR_P1,
//	and TX_ADDR.  The size of data should be set according to the user-specified size of the address
//  length for the register the address is being read from.
//unsigned int len is always the size of unsigned char * data.  For example, if data is declared as 
//  data[6], len = 6.
//returns the value of the STATUS register
unsigned char nrf24l01_read_register(unsigned char regnumber, unsigned char * data, unsigned int len)
{
	return nrf24l01_execute_command(regnumber & nrf24l01_R_REGISTER_DATA, data, len, true);
}
 
//executes the W_TX_PAYLOAD operation
//unsigned char * data is the actual payload to be sent to the nrf24l01.
//unsigned int len is the length of the payload being sent (this should be sized
//	according to the payload length specified by the receiving nrf24l01).
//if bool transmit is true, the nrf24l01 immediately transmits the data in the payload.
//	if false, the user must use the nrf24l01_transmit() function to send the payload.
//returns the value of the STATUS register
unsigned char nrf24l01_write_tx_payload(unsigned char * data, unsigned int len, bool transmit)
{
	unsigned char status;
 
	status = nrf24l01_execute_command(nrf24l01_W_TX_PAYLOAD, data, len, false);
 
	if(transmit == true)
		nrf24l01_transmit();
 
	return status;
}
 
//executes the R_RX_PAYLOAD instruction
//unsigned char * data is the actual payload that has been received by the nrf24l01.
//	The user must size data according to the payload width specified to the nrf24l01.
//	This variable is filled by this function, so individual byte values need not be
//	initialized by the user.
//unsigned int len is the length of the payload being clocked out of the nrf24l01 (this
//	should be sized according to the payload length specified to the nrf24l01).
//returns the value of the STATUS register
unsigned char nrf24l01_read_rx_payload(unsigned char * data, unsigned int len)
{
	unsigned char status;
 
	nrf24l01_clear_ce();
	status = nrf24l01_execute_command(nrf24l01_R_RX_PAYLOAD, data, len, true);
	nrf24l01_set_ce();
 
	return status;
}
 
//executes the FLUSH_TX SPI operation
//this funciton empties the contents of the TX FIFO
//returns the value of the STATUS register
unsigned char nrf24l01_flush_tx()
{
	return nrf24l01_execute_command(nrf24l01_FLUSH_TX, NULL, 0, true);
}
 
//executes the FLUSH_RX SPI operation
//this funciton empties the contents of the RX FIFO
//returns the value of the STATUS register
unsigned char nrf24l01_flush_rx()
{
	return nrf24l01_execute_command(nrf24l01_FLUSH_RX, NULL, 0, true);
}
 
//executes the REUSE_TX_PL SPI operation
//this funciton allows the user to constantly send a packet repeatedly when issued.
//returns the value of the STATUS register
unsigned char nrf24l01_reuse_tx_pl()
{
	return nrf24l01_execute_command(nrf24l01_REUSE_TX_PL, NULL, 0, true);
}
 
//executes the FLUSH_TX SPI operation
//this funciton does nothing
//returns the value of the STATUS register
unsigned char nrf24l01_nop()
{
	return nrf24l01_execute_command(nrf24l01_NOP, NULL, 0, true);
}
 
//transmits the current tx payload
void nrf24l01_transmit()
{
	nrf24l01_set_ce();
	__delay_us(10);
	nrf24l01_clear_ce();
}
 
//clears the pin on the host microcontroller that is attached to the 24l01's CE pin
void nrf24l01_clear_ce()
{
	nrf24l01_CE_IOREGISTER &= ~nrf24l01_CE_PINMASK;
}
 
//sets the pin on the host microcontroller that is attached to the 24l01's CE pin
void nrf24l01_set_ce()
{
	nrf24l01_CE_IOREGISTER |= nrf24l01_CE_PINMASK;
}
 
//returns true if CE is high, false if not
bool nrf24l01_ce_pin_active()
{
	if((nrf24l01_CE_IOREGISTER & nrf24l01_CE_PINMASK) != 0)
		return true;
	else
		return false;
}
 
//sets the pin on the host microcontroller that is attached to the 24l01's CSN pin
void nrf24l01_clear_csn()
{
	nrf24l01_CSN_IOREGISTER &= ~nrf24l01_CSN_PINMASK;
}
 
//clears the pin on the host microcontroller that is attached to the 24l01's CSN pin
void nrf24l01_set_csn()
{
	nrf24l01_CSN_IOREGISTER |= nrf24l01_CSN_PINMASK;
}
 
//returns true if CSN is high, false if not
bool nrf24l01_csn_pin_active()
{
	if((nrf24l01_CSN_IOREGISTER & nrf24l01_CSN_PINMASK) != 0)
		return true;
	else
		return false;	
}
 
//sets the TX address in the TX_ADDR register
//unsigned char * address is the actual address to be used.  It should be sized
//	according to the tx_addr length specified to the nrf24l01.
//unsigned int len is the length of the address.  Its value should be specified
//	according to the tx_addr length specified to the nrf24l01.
void nrf24l01_set_tx_addr(unsigned char * address, unsigned int len)
{		
	nrf24l01_write_register(nrf24l01_TX_ADDR, address, len);
}
 
//sets the RX address in the RX_ADDR register that is offset by rxpipenum
//unsigned char * address is the actual address to be used.  It should be sized
//	according to the rx_addr length that is being filled.
//unsigned int len is the length of the address.  Its value should be specified
//	according to the rx_addr length specified to the nrf24l01.
//unsigned char rxpipenum is the pipe number (zero to five) whose address is being
//	specified.  If an invalid address (greater than five) is supplied, the function
//  does nothing.
void nrf24l01_set_rx_addr(unsigned char * address, unsigned int len, unsigned char rxpipenum)
{	
	if(rxpipenum > 5)
		return;
 
	nrf24l01_write_register(nrf24l01_RX_ADDR_P0 + rxpipenum, address, len);
}
 
//sets the RX payload width on the pipe offset by rxpipenum
//unsigned char payloadwidth is the length of the payload for the pipe referenced in
//  rxpipenum.  It must be less than or equal to 32.  If an invalid payload width is
//  specified, the function does nothing.
//unsigned char rxpipenum is the pipe number (zero to five) whose address is being
//	specified.  If an invalid address (greater than five) is supplied, the function
//  does nothing.
void nrf24l01_set_rx_pw(unsigned char payloadwidth, unsigned char rxpipenum)
{
	if((rxpipenum > 5) || (payloadwidth > 32))
		return;
 
	nrf24l01_write_register(nrf24l01_RX_PW_P0 + rxpipenum, &payloadwidth, 1);
}
 
//gets the RX payload width on the pipe offset by rxpipenum
//unsigned char rxpipenum is the pipe number (zero to five) whose address is being
//	specified.  If an invalid address (greater than five) is supplied, the function
//  does nothing.
unsigned char nrf24l01_get_rx_pw(unsigned char rxpipenum)
{
	unsigned char data;
 
	if((rxpipenum > 5))
		return 0;
 
	nrf24l01_read_register(nrf24l01_RX_PW_P0 + rxpipenum, &data, 1);
 
	return data;
}
 
//returns the value of the CONFIG register
unsigned char nrf24l01_get_config()
{
	unsigned char data;
 
	nrf24l01_read_register(nrf24l01_CONFIG, &data, 1);
 
	return data;
}
 
//sets the value of the CONFIG register
void nrf24l01_set_config(unsigned char config)
{
	nrf24l01_write_register(nrf24l01_CONFIG, &config, 1);
}
 
//returns the current RF channel in RF_CH register
unsigned char nrf24l01_get_rf_ch()
{
	unsigned char data;
 
	nrf24l01_read_register(nrf24l01_RF_CH, &data, 1);
 
	return data;
}
 
//unsigned char channel is the channel to be changed to.
void nrf24l01_set_rf_ch(unsigned char channel)
{
	unsigned char data;
 
	data = channel & ~nrf24l01_RF_CH_RESERVED;
 
	nrf24l01_write_register(nrf24l01_RF_CH, &data, 1);
}
 
//returns the value of the OBSERVE_TX register
unsigned char nrf24l01_get_observe_tx()
{
	unsigned char data;
 
	nrf24l01_read_register(nrf24l01_OBSERVE_TX, &data, 1);
 
	return data;
}
 
//returns the current PLOS_CNT value in OBSERVE_TX register
unsigned char nrf24l01_get_plos_cnt()
{
	unsigned char data;
 
	nrf24l01_read_register(nrf24l01_OBSERVE_TX, &data, 1);
 
	return ((data & nrf24l01_OBSERVE_TX_PLOS_CNT) >> 4);
}
 
//clears the PLOS_CNT field of the OBSERVE_TX register
//this function makes a read of the current value of RF_CH and
//  simply writes it back to the register, clearing PLOS_CNT
void nrf24l01_clear_plos_cnt()
{
	unsigned char data;
 
	nrf24l01_read_register(nrf24l01_RF_CH, &data, 1);
	nrf24l01_write_register(nrf24l01_RF_CH, &data, 1);
}
 
//clears the PLOS_CNT field of the OBSERVE_TX register
//this function allows the user to set the RF_CH register by using
//  the argument in the function during the PLOS_CNT clearing process
void nrf24l01_clear_plos_cnt_param(unsigned char rf_ch)
{
	nrf24l01_write_register(nrf24l01_RF_CH, &rf_ch, 1);
}
 
//returns the current ARC_CNT value in OBSERVE_TX register
unsigned char nrf24l01_get_arc_cnt()
{
	unsigned char data;
 
	nrf24l01_read_register(nrf24l01_OBSERVE_TX, &data, 1);
 
	return (data & nrf24l01_OBSERVE_TX_ARC_CNT);
}
 
//returns true if auto-ack is enabled on the pipe that is offset by rxpipenum
//unsigned char rxpipenum is the pipe number (zero to five) whose address is being
//	specified.  If an invalid address (greater than five) is supplied, the function
//  returns false.
bool nrf24l01_aa_enabled(unsigned char rxpipenum)
{
	unsigned char data;
 
	if(rxpipenum > 5)
		return false;
 
	nrf24l01_read_register(nrf24l01_EN_AA, &data, 1);
 
	return (data & (0x01 << rxpipenum));
}
 
//enables auto-ack is enabled on the pipe that is offset by rxpipenum
//unsigned char rxpipenum is the pipe number (zero to five) whose address is being
//	does nothing.
void nrf24l01_aa_enable(unsigned char rxpipenum)
{
	unsigned char data;
 
	if(rxpipenum > 5)
		return;
 
	nrf24l01_read_register(nrf24l01_EN_AA, &data, 1);
 
	if((data & (0x01 << rxpipenum)) != 0)
		return;
 
	data |= 0x01 << rxpipenum;
 
	nrf24l01_write_register(nrf24l01_EN_AA, &data, 1);
}
 
//disables auto-ack is enabled on the pipe that is offset by rxpipenum
//unsigned char rxpipenum is the pipe number (zero to five) whose address is being
//	does nothing.
void nrf24l01_aa_disable(unsigned char rxpipenum)
{
	unsigned char data;
 
	if(rxpipenum > 5)
		return;
 
	nrf24l01_read_register(nrf24l01_EN_AA, &data, 1);
 
	if((data & (0x01 << rxpipenum)) == 0)
		return;
 
	data &= ~(0x01 << rxpipenum);
 
	nrf24l01_write_register(nrf24l01_EN_AA, &data, 1);
}
 
//returns true if the pipe is enabled that is offset by rxpipenum
//unsigned char rxpipenum is the pipe number (zero to five) whose address is being
//	specified.  If an invalid address (greater than five) is supplied, the function
//  returns false.
bool nrf24l01_rx_pipe_enabled(unsigned char rxpipenum)
{
	unsigned char data;
 
	if((rxpipenum > 5))
		return false;
 
	nrf24l01_read_register(nrf24l01_EN_RXADDR, &data, 1);
 
	return (data & (0x01 << rxpipenum));
}
 
//enables the pipe that is offset by rxpipenum
//unsigned char rxpipenum is the pipe number (zero to five) whose address is being
//	specified.  If an invalid address (greater than five) is supplied, the function
//  does nothing.
void nrf24l01_rx_pipe_enable(unsigned char rxpipenum)
{
	unsigned char data;
 
	if(rxpipenum > 5)
		return;
 
	nrf24l01_read_register(nrf24l01_EN_RXADDR, &data, 1);
 
	if((data & (0x01 << rxpipenum)) != 0)
		return;
 
	data |= 0x01 << rxpipenum;
 
	nrf24l01_write_register(nrf24l01_EN_RXADDR, &data, 1);
}
 
//disables the pipe that is offset by rxpipenum
//unsigned char rxpipenum is the pipe number (zero to five) whose address is being
//	specified.  If an invalid address (greater than five) is supplied, the function
//  does nothing.
void nrf24l01_rx_pipe_disable(unsigned char rxpipenum)
{
	unsigned char data;
 
	if(rxpipenum > 5)
		return;
 
	nrf24l01_read_register(nrf24l01_EN_RXADDR, &data, 1);
 
	if((data & (0x01 << rxpipenum)) == 0)
		return;
 
	data &= ~(0x01 << rxpipenum);
 
	nrf24l01_write_register(nrf24l01_EN_RXADDR, &data, 1);
}
 
//returns the status of the CD register (true if carrier detect [CD] is
//  active, false if not)
bool nrf24l01_cd_active()
{
	unsigned char data;
 
	nrf24l01_read_register(nrf24l01_CD, &data, 1);
 
	return data;
}
 
//returns the value of the FIFO_STATUS register
unsigned char nrf24l01_get_fifo_status()
{
	unsigned char data;
 
	nrf24l01_read_register(nrf24l01_FIFO_STATUS, &data, 1);
 
	return data;
}
 
//return the value of the status register
unsigned char nrf24l01_get_status()
{
	return nrf24l01_nop();
}
 
//returns true if TX_REUSE bit in FIFO_STATUS register is set, false otherwise
bool nrf24l01_fifo_tx_reuse()
{
	unsigned char data;
 
	nrf24l01_read_register(nrf24l01_FIFO_STATUS, &data, 1);
 
	return (bool)(data & nrf24l01_FIFO_STATUS_TX_REUSE);
}
 
//returns true if TX_FULL bit in FIFO_STATUS register is set, false otherwise
bool nrf24l01_fifo_tx_full()
{
	unsigned char data;
 
	nrf24l01_read_register(nrf24l01_FIFO_STATUS, &data, 1);
 
	return (bool)(data & nrf24l01_FIFO_STATUS_TX_FULL);
}
 
//returns true if TX_EMPTY bit in FIFO_STATUS register is set, false otherwise
bool nrf24l01_fifo_tx_empty()
{
	unsigned char data;
 
	nrf24l01_read_register(nrf24l01_FIFO_STATUS, &data, 1);
 
	return (bool)(data & nrf24l01_FIFO_STATUS_TX_EMPTY);
}
 
//returns true if RX_FULL bit in FIFO_STATUS register is set, false otherwise
bool nrf24l01_fifo_rx_full()
{
	unsigned char data;
 
	nrf24l01_read_register(nrf24l01_FIFO_STATUS, &data, 1);
 
	return (bool)(data & nrf24l01_FIFO_STATUS_RX_FULL);
}
 
//returns true if RX_EMPTYE bit in FIFO_STATUS register is set, false otherwise
bool nrf24l01_fifo_rx_empty()
{
	unsigned char data;
 
	nrf24l01_read_register(nrf24l01_FIFO_STATUS, &data, 1);
 
	return (bool)(data & nrf24l01_FIFO_STATUS_RX_EMPTY);
}
 
//returns true if IRQ pin is low, false otherwise
bool nrf24l01_irq_pin_active()
{
	if((nrf24l01_IRQ_IOREGISTER & nrf24l01_IRQ_PINMASK) != 0)
		return false;
	else
		return true;
}
 
//returns true if RX_DR interrupt is active, false otherwise
bool nrf24l01_irq_rx_dr_active()
{
	return (nrf24l01_get_status() & nrf24l01_STATUS_RX_DR);
}
 
//returns true if TX_DS interrupt is active, false otherwise
bool nrf24l01_irq_tx_ds_active()
{
	return (nrf24l01_get_status() & nrf24l01_STATUS_TX_DS);
}
 
//returns true if MAX_RT interrupt is active, false otherwise
bool nrf24l01_irq_max_rt_active()
{
	return (nrf24l01_get_status() & nrf24l01_STATUS_MAX_RT);
}
 
//clear all interrupts in the status register
void nrf24l01_irq_clear_all()
{
	unsigned char data = nrf24l01_STATUS_RX_DR | nrf24l01_STATUS_TX_DS | nrf24l01_STATUS_MAX_RT;
 
	nrf24l01_write_register(nrf24l01_STATUS, &data, 1); 
}
 
//clears only the RX_DR interrupt
void nrf24l01_irq_clear_rx_dr()
{
	unsigned char data = nrf24l01_STATUS_RX_DR;
 
	nrf24l01_write_register(nrf24l01_STATUS, &data, 1); 
}
 
//clears only the TX_DS interrupt
void nrf24l01_irq_clear_tx_ds()
{
	unsigned char data = nrf24l01_STATUS_TX_DS;
 
	nrf24l01_write_register(nrf24l01_STATUS, &data, 1); 
}
 
//clears only the MAX_RT interrupt
void nrf24l01_irq_clear_max_rt()
{
	unsigned char data = nrf24l01_STATUS_MAX_RT;
 
	nrf24l01_write_register(nrf24l01_STATUS, &data, 1); 
}
 
//returns the current pipe in the 24L01's STATUS register
unsigned char nrf24l01_get_rx_pipe()
{
	return nrf24l01_get_rx_pipe_from_status(nrf24l01_get_status());
}
 
unsigned char nrf24l01_get_rx_pipe_from_status(unsigned char status)
{
	return ((status & 0xE) >> 1);
}
 
//flush both fifos and clear interrupts
void nrf24l01_clear_flush()
{
	nrf24l01_flush_rx();
	nrf24l01_flush_tx();
	nrf24l01_irq_clear_all();
}
 
//unsigned char * data must be at least 35 bytes long
void nrf24l01_get_all_registers(unsigned char * data)
{
	unsigned int outer;
	unsigned int inner;
	unsigned int dataloc = 0;
	unsigned char buffer[5];
 
	for(outer = 0; outer <= 0x17; outer++)
	{
		nrf24l01_read_register(outer, buffer, 5);
 
		for(inner = 0; inner < 5; inner++)
		{
			if(inner >= 1 && (outer != 0x0A && outer != 0x0B && outer != 0x10))
				break;
 
			data[dataloc] = buffer[inner];
			dataloc++;
		}
	}
}
 
//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 = instruction;
	nrf24l01_spi_send_read(&status, 1, true);
	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;
	unsigned char tempbyte;
 
	for(count = 0; count < len; count++)
	{
		if(copydata != false)
			data[count] = spi_send_read_byte(data[count]);
		else
		{
			tempbyte = data[count];
			spi_send_read_byte(tempbyte);
		}
	}
}

.h

Code:
#ifndef NRF24L01_H_
#define NRF24L01_H_
 
#include <xc.h> // include processor files - each processor file is guarded. 
#include "mcc_generated_files/mcc.h"
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
 
#ifndef bool
#define bool unsigned char
#endif
#ifndef false
#define false 0
#endif
#ifndef true
#define true !false
#endif
 
/////////////////////////////////////////////////////////////////////////////////
// SPI function requirements
//
// The user must define a function to send one byte of data and also return the
//   resulting byte of data data through the SPI port. The function used here 
//   has the function prototype
//
//	    unsigned char spi_send_read_byte(unsigned char byte);
//
// This function should take the argument unsigned char byte and send it through
//   the SPI port to the 24L01.  Then, it should wait until the 24L01 has returned
//   its response over SPI.  This received byte should be the return value of the
//   function.
//
// You should also change the include file name below to whatever the name of your 
//   SPI include file is.
//////////////////////////////////////////////////////////////////////////////////
 
#define spi_send_read_byte(byte)	spi1_send_read_byte(byte)
 
 
/////////////////////////////////////////////////////////////////////////////////
// Delay function requirements
//
// The user must define a function that delays for the specified number of 
//	 microseconds. This function needs to be as precise as possible, and the use
//   of a timer module within your microcontroller is highly recommended. The 
//   function used here has the prototype
//
//	    void delay_us(unsigned int microseconds);
//
// You should also change the include file name below to whatever the name of your 
//   delay include file is.
 
 
 
//////////////////////////////////////////////////////////////////////////////////
// IO pin definitions
//
// Below you will find several definitions and includes.  The first is an #include
//   for your microcontroller's include file to allow you to use register names 
//   rather than numbers.  The next three are to allow you to control the pins on
//   the 24L01 that aren't automatically handled by SPI.  These are CE, CSN, and
//   IRQ.
//
// The general format of these defines is a define for the IO register the pin is
//   attached to.  The second define is a mask for the pin.  For example, say that
//   your CE pin is tied to an IO port with the register name IOPORT1. Also, let's
//   say that the IO port is 8-bits wide, and you have attached the pin to pin 0 of
//   the port.  Then your define would look like this:
//
//	 #define nrf24l01_CE_IOREGISTER		IOPORT1
//   #define nrf24l01_CE_PINMASK		0x01
//
// If you have defines in your include file for individual IO pins, you could use
//   this define in this file, as well.  Using the previous example, assume that in
//   your microcontroller's include file, pin 0 of IOPORT1 has a define like this
//
//   #define IOPORT1_PIN0	0x01
//
// Then, you could make your defines for the CE pin in this file look like this:
//
//	 #define nrf24l01_CE_IOREGISTER		IOPORT1
//   #define nrf24l01_CE_PINMASK		IOPORT1_PIN0
//
// You should also change the include file name below to whatever the name of your 
//   processor's register definition include file is.
/////////////////////////////////////////////////////////////////////////////////////
 
 
//defines for uC pins CE pin is connected to
//This is used so that the routines can send TX payload data and 
//	properly initialize the nrf24l01 in TX and RX states.
//Change these definitions (and then recompile) to suit your particular application.
#define nrf24l01_CE_IOREGISTER		PORTC
#define nrf24l01_CE_PINMASK			0x02
 
//defines for uC pins CSN pin is connected to
//This is used so that the routines can send properly operate the SPI interface
// on the nrf24l01.
//Change these definitions (and then recompile) to suit your particular application.
#define nrf24l01_CSN_IOREGISTER		PORTC
#define nrf24l01_CSN_PINMASK		0x04
 
//defines for uC pins IRQ pin is connected to
//This is used so that the routines can poll for IRQ or create an ISR.
//Change these definitions (and then recompile) to suit your particular application.
#define nrf24l01_IRQ_IOREGISTER		PORTB
#define nrf24l01_IRQ_PINMASK		0x01
 
 
////////////////////////////////////////////////////////////////////////////////////
// SPI commands
//
// The following are defines for all of the commands and data masks on the SPI 
//   interface.
////////////////////////////////////////////////////////////////////////////////////
//SPI command defines
#define nrf24l01_R_REGISTER		0x00
#define nrf24l01_W_REGISTER		0x20
#define nrf24l01_R_RX_PAYLOAD	0x61
#define nrf24l01_W_TX_PAYLOAD	0xA0
#define nrf24l01_FLUSH_TX		0xE1
#define nrf24l01_FLUSH_RX		0xE2
#define nrf24l01_REUSE_TX_PL	0xE3
#define nrf24l01_NOP			0xFF
 
//SPI command data mask defines
#define nrf24l01_R_REGISTER_DATA	0x1F
#define nrf24l01_W_REGISTER_DATA	0x1F
 
////////////////////////////////////////////////////////////////////////////////////
// Register definitions
//
// Below are the defines for each register's address in the 24L01.
////////////////////////////////////////////////////////////////////////////////////
#define nrf24l01_CONFIG			0x00
#define nrf24l01_EN_AA			0x01
#define nrf24l01_EN_RXADDR		0x02
#define nrf24l01_SETUP_AW		0x03
#define nrf24l01_SETUP_RETR		0x04
#define nrf24l01_RF_CH			0x05
#define nrf24l01_RF_SETUP		0x06
#define nrf24l01_STATUS			0x07
#define nrf24l01_OBSERVE_TX		0x08
#define nrf24l01_CD				0x09
#define nrf24l01_RX_ADDR_P0		0x0A
#define nrf24l01_RX_ADDR_P1		0x0B
#define nrf24l01_RX_ADDR_P2		0x0C
#define nrf24l01_RX_ADDR_P3		0x0D
#define nrf24l01_RX_ADDR_P4		0x0E
#define nrf24l01_RX_ADDR_P5		0x0F
#define nrf24l01_TX_ADDR		0x10
#define nrf24l01_RX_PW_P0		0x11
#define nrf24l01_RX_PW_P1		0x12
#define nrf24l01_RX_PW_P2		0x13
#define nrf24l01_RX_PW_P3		0x14
#define nrf24l01_RX_PW_P4		0x15
#define nrf24l01_RX_PW_P5		0x16
#define nrf24l01_FIFO_STATUS	0x17
 
////////////////////////////////////////////////////////////////////////////////////
// Default register values
//
// Below are the defines for each register's default value in the 24L01. Multi-byte
//   registers use notation B<X>, where "B" represents "byte" and <X> is the byte
//   number.
////////////////////////////////////////////////////////////////////////////////////
#define nrf24l01_CONFIG_DEFAULT_VAL			0x08
#define nrf24l01_EN_AA_DEFAULT_VAL			0x3F
#define nrf24l01_EN_RXADDR_DEFAULT_VAL		0x03
#define nrf24l01_SETUP_AW_DEFAULT_VAL		0x03
#define nrf24l01_SETUP_RETR_DEFAULT_VAL		0x03
#define nrf24l01_RF_CH_DEFAULT_VAL			0x02
#define nrf24l01_RF_SETUP_DEFAULT_VAL		0x0F
#define nrf24l01_STATUS_DEFAULT_VAL			0x0E
#define nrf24l01_OBSERVE_TX_DEFAULT_VAL		0x00
#define nrf24l01_CD_DEFAULT_VAL				0x00
#define nrf24l01_RX_ADDR_P0_B0_DEFAULT_VAL	0xE7
#define nrf24l01_RX_ADDR_P0_B1_DEFAULT_VAL	0xE7
#define nrf24l01_RX_ADDR_P0_B2_DEFAULT_VAL	0xE7
#define nrf24l01_RX_ADDR_P0_B3_DEFAULT_VAL	0xE7
#define nrf24l01_RX_ADDR_P0_B4_DEFAULT_VAL	0xE7
#define nrf24l01_RX_ADDR_P1_B0_DEFAULT_VAL	0xC2
#define nrf24l01_RX_ADDR_P1_B1_DEFAULT_VAL	0xC2
#define nrf24l01_RX_ADDR_P1_B2_DEFAULT_VAL	0xC2
#define nrf24l01_RX_ADDR_P1_B3_DEFAULT_VAL	0xC2
#define nrf24l01_RX_ADDR_P1_B4_DEFAULT_VAL	0xC2
#define nrf24l01_RX_ADDR_P2_DEFAULT_VAL		0xC3
#define nrf24l01_RX_ADDR_P3_DEFAULT_VAL		0xC4
#define nrf24l01_RX_ADDR_P4_DEFAULT_VAL		0xC5
#define nrf24l01_RX_ADDR_P5_DEFAULT_VAL		0xC6
#define nrf24l01_TX_ADDR_B0_DEFAULT_VAL		0xE7
#define nrf24l01_TX_ADDR_B1_DEFAULT_VAL		0xE7
#define nrf24l01_TX_ADDR_B2_DEFAULT_VAL		0xE7
#define nrf24l01_TX_ADDR_B3_DEFAULT_VAL		0xE7
#define nrf24l01_TX_ADDR_B4_DEFAULT_VAL		0xE7
#define nrf24l01_RX_PW_P0_DEFAULT_VAL		0x00
#define nrf24l01_RX_PW_P1_DEFAULT_VAL		0x00
#define nrf24l01_RX_PW_P2_DEFAULT_VAL		0x00
#define nrf24l01_RX_PW_P3_DEFAULT_VAL		0x00
#define nrf24l01_RX_PW_P4_DEFAULT_VAL		0x00
#define nrf24l01_RX_PW_P5_DEFAULT_VAL		0x00
#define nrf24l01_FIFO_STATUS_DEFAULT_VAL	0x11
 
////////////////////////////////////////////////////////////////////////////////////
// Register bitwise definitions
//
// Below are the defines for each register's bitwise fields in the 24L01.
////////////////////////////////////////////////////////////////////////////////////
//CONFIG register bitwise definitions
#define nrf24l01_CONFIG_RESERVED	0x80
#define	nrf24l01_CONFIG_MASK_RX_DR	0x40
#define	nrf24l01_CONFIG_MASK_TX_DS	0x20
#define	nrf24l01_CONFIG_MASK_MAX_RT	0x10
#define	nrf24l01_CONFIG_EN_CRC		0x08
#define	nrf24l01_CONFIG_CRCO		0x04
#define	nrf24l01_CONFIG_PWR_UP		0x02
#define	nrf24l01_CONFIG_PRIM_RX		0x01
 
//EN_AA register bitwise definitions
#define nrf24l01_EN_AA_RESERVED		0xC0
#define nrf24l01_EN_AA_ENAA_ALL		0x3F
#define nrf24l01_EN_AA_ENAA_P5		0x20
#define nrf24l01_EN_AA_ENAA_P4		0x10
#define nrf24l01_EN_AA_ENAA_P3		0x08
#define nrf24l01_EN_AA_ENAA_P2		0x04
#define nrf24l01_EN_AA_ENAA_P1		0x02
#define nrf24l01_EN_AA_ENAA_P0		0x01
#define nrf24l01_EN_AA_ENAA_NONE	0x00
 
//EN_RXADDR register bitwise definitions
#define nrf24l01_EN_RXADDR_RESERVED	0xC0
#define nrf24l01_EN_RXADDR_ERX_ALL	0x3F
#define nrf24l01_EN_RXADDR_ERX_P5	0x20
#define nrf24l01_EN_RXADDR_ERX_P4	0x10
#define nrf24l01_EN_RXADDR_ERX_P3	0x08
#define nrf24l01_EN_RXADDR_ERX_P2	0x04
#define nrf24l01_EN_RXADDR_ERX_P1	0x02
#define nrf24l01_EN_RXADDR_ERX_P0	0x01
#define nrf24l01_EN_RXADDR_ERX_NONE	0x00
 
//SETUP_AW register bitwise definitions
#define nrf24l01_SETUP_AW_RESERVED	0xFC
#define nrf24l01_SETUP_AW			0x03
#define nrf24l01_SETUP_AW_5BYTES	0x03
#define nrf24l01_SETUP_AW_4BYTES	0x02
#define nrf24l01_SETUP_AW_3BYTES	0x01
#define nrf24l01_SETUP_AW_ILLEGAL	0x00
 
//SETUP_RETR register bitwise definitions
#define nrf24l01_SETUP_RETR_ARD			0xF0
#define nrf24l01_SETUP_RETR_ARD_4000	0xF0
#define nrf24l01_SETUP_RETR_ARD_3750	0xE0
#define nrf24l01_SETUP_RETR_ARD_3500	0xD0
#define nrf24l01_SETUP_RETR_ARD_3250	0xC0
#define nrf24l01_SETUP_RETR_ARD_3000	0xB0
#define nrf24l01_SETUP_RETR_ARD_2750	0xA0
#define nrf24l01_SETUP_RETR_ARD_2500	0x90
#define nrf24l01_SETUP_RETR_ARD_2250	0x80
#define nrf24l01_SETUP_RETR_ARD_2000	0x70
#define nrf24l01_SETUP_RETR_ARD_1750	0x60
#define nrf24l01_SETUP_RETR_ARD_1500	0x50
#define nrf24l01_SETUP_RETR_ARD_1250	0x40
#define nrf24l01_SETUP_RETR_ARD_1000	0x30
#define nrf24l01_SETUP_RETR_ARD_750		0x20
#define nrf24l01_SETUP_RETR_ARD_500		0x10
#define nrf24l01_SETUP_RETR_ARD_250		0x00
#define nrf24l01_SETUP_RETR_ARC			0x0F
#define nrf24l01_SETUP_RETR_ARC_15		0x0F
#define nrf24l01_SETUP_RETR_ARC_14		0x0E
#define nrf24l01_SETUP_RETR_ARC_13		0x0D
#define nrf24l01_SETUP_RETR_ARC_12		0x0C
#define nrf24l01_SETUP_RETR_ARC_11		0x0B
#define nrf24l01_SETUP_RETR_ARC_10		0x0A
#define nrf24l01_SETUP_RETR_ARC_9		0x09
#define nrf24l01_SETUP_RETR_ARC_8		0x08
#define nrf24l01_SETUP_RETR_ARC_7		0x07
#define nrf24l01_SETUP_RETR_ARC_6		0x06
#define nrf24l01_SETUP_RETR_ARC_5		0x05
#define nrf24l01_SETUP_RETR_ARC_4		0x04
#define nrf24l01_SETUP_RETR_ARC_3		0x03
#define nrf24l01_SETUP_RETR_ARC_2		0x02
#define nrf24l01_SETUP_RETR_ARC_1		0x01
#define nrf24l01_SETUP_RETR_ARC_0		0x00
 
//RF_CH register bitwise definitions
#define nrf24l01_RF_CH_RESERVED	0x80
 
//RF_SETUP register bitwise definitions
#define nrf24l01_RF_SETUP_RESERVED	0xE0
#define nrf24l01_RF_SETUP_PLL_LOCK	0x10
#define nrf24l01_RF_SETUP_RF_DR		0x08
#define nrf24l01_RF_SETUP_RF_PWR	0x06
#define nrf24l01_RF_SETUP_RF_PWR_0	0x06
#define nrf24l01_RF_SETUP_RF_PWR_6 	0x04
#define nrf24l01_RF_SETUP_RF_PWR_12	0x02
#define nrf24l01_RF_SETUP_RF_PWR_18	0x00
#define nrf24l01_RF_SETUP_LNA_HCURR	0x01
 
//STATUS register bitwise definitions
#define nrf24l01_STATUS_RESERVED					0x80
#define nrf24l01_STATUS_RX_DR						0x40
#define nrf24l01_STATUS_TX_DS						0x20
#define nrf24l01_STATUS_MAX_RT						0x10
#define nrf24l01_STATUS_RX_P_NO						0x0E
#define nrf24l01_STATUS_RX_P_NO_RX_FIFO_NOT_EMPTY	0x0E
#define nrf24l01_STATUS_RX_P_NO_UNUSED				0x0C
#define nrf24l01_STATUS_RX_P_NO_5					0x0A
#define nrf24l01_STATUS_RX_P_NO_4					0x08
#define nrf24l01_STATUS_RX_P_NO_3					0x06
#define nrf24l01_STATUS_RX_P_NO_2					0x04
#define nrf24l01_STATUS_RX_P_NO_1					0x02
#define nrf24l01_STATUS_RX_P_NO_0					0x00
#define nrf24l01_STATUS_TX_FULL						0x01
 
//OBSERVE_TX register bitwise definitions
#define nrf24l01_OBSERVE_TX_PLOS_CNT	0xF0
#define nrf24l01_OBSERVE_TX_ARC_CNT		0x0F
 
//CD register bitwise definitions
#define nrf24l01_CD_RESERVED	0xFE
#define nrf24l01_CD_CD			0x01
 
//RX_PW_P0 register bitwise definitions
#define nrf24l01_RX_PW_P0_RESERVED	0xC0
 
//RX_PW_P0 register bitwise definitions
#define nrf24l01_RX_PW_P0_RESERVED	0xC0
 
//RX_PW_P1 register bitwise definitions
#define nrf24l01_RX_PW_P1_RESERVED	0xC0
 
//RX_PW_P2 register bitwise definitions
#define nrf24l01_RX_PW_P2_RESERVED	0xC0
 
//RX_PW_P3 register bitwise definitions
#define nrf24l01_RX_PW_P3_RESERVED	0xC0
 
//RX_PW_P4 register bitwise definitions
#define nrf24l01_RX_PW_P4_RESERVED	0xC0
 
//RX_PW_P5 register bitwise definitions
#define nrf24l01_RX_PW_P5_RESERVED	0xC0
 
//FIFO_STATUS register bitwise definitions
#define nrf24l01_FIFO_STATUS_RESERVED	0x8C
#define nrf24l01_FIFO_STATUS_TX_REUSE	0x40
#define nrf24l01_FIFO_STATUS_TX_FULL	0x20
#define nrf24l01_FIFO_STATUS_TX_EMPTY	0x10
#define nrf24l01_FIFO_STATUS_RX_FULL	0x02
#define nrf24l01_FIFO_STATUS_RX_EMPTY	0x01
 
////////////////////////////////////////////////////////////////////////////////////
// Function declarations
//
// Below are all function definitions contained in the library.  Please see
//   nrf24l01.c for comments regarding the usage of each function.
////////////////////////////////////////////////////////////////////////////////////
//initialization functions
void nrf24l01_initialize(unsigned char config,
						 unsigned char opt_rx_standby_mode, 
						 unsigned char en_aa, 
						 unsigned char en_rxaddr, 
						 unsigned char setup_aw, 
						 unsigned char setup_retr, 
						 unsigned char rf_ch, 
						 unsigned char rf_setup, 
						 unsigned char * rx_addr_p0, 
						 unsigned char * rx_addr_p1, 
						 unsigned char rx_addr_p2, 
						 unsigned char rx_addr_p3, 
						 unsigned char rx_addr_p4, 
						 unsigned char rx_addr_p5, 
						 unsigned char * tx_addr, 
						 unsigned char rx_pw_p0, 
						 unsigned char rx_pw_p1, 
						 unsigned char rx_pw_p2, 
						 unsigned char rx_pw_p3, 
						 unsigned char rx_pw_p4, 
						 unsigned char rx_pw_p5);
void nrf24l01_initialize_debug(bool rx, unsigned char p0_payload_width, bool enable_auto_ack);
void nrf24l01_initialize_debug_lite(bool rx, unsigned char p0_payload_width);
 
//power-up, power-down functions
void nrf24l01_power_up(bool rx_active_mode);
void nrf24l01_power_up_param(bool rx_active_mode, unsigned char config);
void nrf24l01_power_down(void);
void nrf24l01_power_down_param(unsigned char config);
 
//SPI commands defined by the spec
//for regnumber values, see section above titled "register definitions"
//all functions return the STATUS register
unsigned char nrf24l01_write_register(unsigned char regnumber, unsigned char * data, unsigned int len);
unsigned char nrf24l01_read_register(unsigned char regnumber, unsigned char * data, unsigned int len);
unsigned char nrf24l01_write_tx_payload(unsigned char * data, unsigned int len, bool transmit);
unsigned char nrf24l01_read_rx_payload(unsigned char * data, unsigned int len);
unsigned char nrf24l01_flush_tx(void);
unsigned char nrf24l01_flush_rx(void);
unsigned char nrf24l01_reuse_tx_pl(void);
unsigned char nrf24l01_nop(void);
 
//RX/TX setting functions
void nrf24l01_set_as_rx(bool rx_active_mode);
void nrf24l01_set_as_rx_param(bool rx_active_mode, unsigned char config);
void nrf24l01_rx_standby_to_active(void);
void nrf24l01_rx_active_to_standby(void);
void nrf24l01_set_as_tx(void);
void nrf24l01_set_as_tx_param(unsigned char config);
 
//register-oriented get/set functions for commonly-used registers during operation
unsigned char nrf24l01_get_config(void);
void nrf24l01_set_config(unsigned char config);
unsigned char nrf24l01_get_rf_ch(void);
void nrf24l01_set_rf_ch(unsigned char channel);
unsigned char nrf24l01_get_status(void);
unsigned char nrf24l01_get_observe_tx(void);
void nrf24l01_set_rx_addr(unsigned char * address, unsigned int len, unsigned char rxpipenum);
void nrf24l01_set_tx_addr(unsigned char * address, unsigned int len);
void nrf24l01_set_rx_pw(unsigned char payloadwidth, unsigned char rxpipenum);
unsigned char nrf24l01_get_rx_pw(unsigned char rxpipenum);
unsigned char nrf24l01_get_fifo_status(void);
 
//auto-ack and pipe-related functions
bool nrf24l01_aa_enabled(unsigned char rxpipenum);
void nrf24l01_aa_enable(unsigned char rxpipenum);
void nrf24l01_aa_disable(unsigned char rxpipenum);
bool nrf24l01_rx_pipe_enabled(unsigned char rxpipenum);
void nrf24l01_rx_pipe_enable(unsigned char rxpipenum);
void nrf24l01_rx_pipe_disable(unsigned char rxpipenum);
unsigned char nrf24l01_get_plos_cnt(void);
void nrf24l01_clear_plos_cnt(void);
void nrf24l01_clear_plos_cnt_param(unsigned char rf_ch);
unsigned char nrf24l01_get_arc_cnt(void);
 
//utility functions
bool nrf24l01_cd_active(void);
void nrf24l01_clear_flush(void);
unsigned char nrf24l01_get_rx_pipe(void);
unsigned char nrf24l01_get_rx_pipe_from_status(unsigned char status);
void nrf24l01_get_all_registers(unsigned char * data);
 
//interrupt check/clear functions
bool nrf24l01_irq_pin_active(void);
bool nrf24l01_irq_rx_dr_active(void);
bool nrf24l01_irq_tx_ds_active(void);
bool nrf24l01_irq_max_rt_active(void);
void nrf24l01_irq_clear_all(void);
void nrf24l01_irq_clear_rx_dr(void);
void nrf24l01_irq_clear_tx_ds(void);
void nrf24l01_irq_clear_max_rt(void);
 
//FIFO_STATUS check functions
bool nrf24l01_fifo_tx_reuse(void);
bool nrf24l01_fifo_tx_full(void);
bool nrf24l01_fifo_tx_empty(void);
bool nrf24l01_fifo_rx_full(void);
bool nrf24l01_fifo_rx_empty(void);
 
//IO interface-related functions
void nrf24l01_transmit(void);
void nrf24l01_clear_ce(void);
void nrf24l01_set_ce(void);
void nrf24l01_clear_csn(void);
void nrf24l01_set_csn(void);
bool nrf24l01_ce_pin_active(void);
bool nrf24l01_csn_pin_active(void);
 
//low-level functions for library use only
unsigned char nrf24l01_execute_command(unsigned char instruction, unsigned char * data, unsigned int len, bool copydata);
void nrf24l01_spi_send_read(unsigned char * data, unsigned int len, bool copydata);
 
#endif /*NRF24L01_H_*/

he rest of the code was generated by MCC of MPLAB and in the SPI1 File the next function was added to be used

Code:
unsigned char spi1_send_read_byte(unsigned char byte)
{
	SSP1BUF = byte;
 
	while(!DataRdySPI());
 
	return SSP1BUF;
}
 

Hi,
Visit nordic semiconductor website. They have exaple application note with firmware. Use the example as a skeleton to write your code.

https://www.nordicsemi.com/eng/Products/2.4GHz-RF/nRF24L01P

You might not have money to buy an oscilloscope but you could buy a cheap logic analyzer sigrok or some thing which would be useful for debugging
project work.
 

Not really addressing the issue, and I can't see the 'SYSTEM_Initialize()' function and it might be in there but the 'InitializeIO()' function will not do what the function name suggests. It needs to set the pin to 'digital' mode and also set the TRIS register bit to 0.
Further, the whenever you are writing to the port, you should be using the LAT register to avoid RMW issues on the register as a whole.
I can't see how the SPI peripheral is initialised. My personal preference is never to use library functions for MCUs such as these (except for the USB and Ethernet stacks) becasue there can be a lot of problems hidden in code that you can't see or understand how it works.
This applies to MCC as well as, in my opinion, it is very new and there are all sorts of bugs that have been found. Is the code you have been given written specifically for MCC or is this part of the wrapper code that you have generated?
As you are sure that the SPI code is correct, rather than just calling the NRF24L01 initialisation function at the start, can I suggest that you simply read the (say) CONFIG register or some other register where you know the reset value (and it is not all zeros) and make sure that you get the expected value. That way you will know that you can talk to the chip correctly. Then try writing something to it and reading that back.
Susan
 

Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…