NRF24L01+ Driver : Part 7 : Usage Sample




So here we are at the final chapter. I have already showed you the hardware specific functions related to SPI. Here I will show you exactly how to use the driver. I will not go over every detail or rather every command, I will leave that for you to explore and maybe even further develop the driver for your own needs.
First thing is first I recommend enabling a GPIO interrupt on the pin you connect to the IRQ pin of the module for the obvious reason that when an interrupt fires on the module it will also trigger one on your microcontroller. 

My interrupt functions is the one below:

1
2
3
4
5
6
void EXTI4_IRQHandler(void)
{
	EXTI->PR = EXTI_PR_PIF4; //clear pending interrupt
	flag = 0x55;
	GPIOC->ODR ^= 1 << 13;	
}

I simply clear the interrupt on my MCU EXTI line. Next I have a flag which I user later in my for-loop to know that an interrupt has been triggered. Its always great practice to keep interrupt routines short and use flags wherever possible. The last line is just toggling the LED on the blue pill.

I will repost my SPI init function as well as the function to init the pins for the sake of completeness 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
void init_pins(void)
{
	//clock enable GPIOA
	RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
	
	LL_GPIO_InitTypeDef nrfPins;	
	LL_GPIO_StructInit(&nrfPins);	
	
	// CE pin as output : active high/ normally low
	// CSN chip select for spi active low / normally high
	nrfPins.Pin			= NRF_CE_PIN | NRF_CSN_PIN;
	nrfPins.Mode		= LL_GPIO_MODE_OUTPUT;
	nrfPins.OutputType	= LL_GPIO_OUTPUT_PUSHPULL;	
	nrfPins.Speed		= LL_GPIO_SPEED_FREQ_HIGH;
	LL_GPIO_Init(NRF_CE_PORT, &nrfPins); // CE & CSN on same port only need this once

	
	// IRQ pin as input with interrupt enabled
	nrfPins.Pin			= NRF_IRQ_PIN;
	nrfPins.Mode = LL_GPIO_MODE_INPUT;
	nrfPins.Pull = LL_GPIO_PULL_UP;
	LL_GPIO_Init(NRF_IRQ_PORT, &nrfPins);
		
	LL_EXTI_InitTypeDef myEXTI = { 0 };
	LL_EXTI_StructInit(&myEXTI);
	myEXTI.Line_0_31		= LL_EXTI_LINE_4;
	myEXTI.LineCommand		= ENABLE;
	myEXTI.Mode				= LL_EXTI_MODE_IT;
	myEXTI.Trigger			= LL_EXTI_TRIGGER_FALLING;
	LL_EXTI_Init(&myEXTI);	
	
	LL_GPIO_SetOutputPin(GPIOA, NRF_CSN_PIN);
	LL_GPIO_ResetOutputPin(GPIOA, NRF_CE_PIN);
	NVIC_EnableIRQ(EXTI4_IRQn);	//enable IRQ on Pin 4
}

void init_spi1(void)
{
	// CLOCK  [ Alt Function ] [ GPIOA ] [ SPI1 ]
	RCC->APB2ENR |= RCC_APB2ENR_AFIOEN | RCC_APB2ENR_IOPAEN | RCC_APB2ENR_SPI1EN; 	
	
	// GPIO [ PA5:SCK:output:push ] [ PA6:MISO:input:float/pullup ] [ PA7:MOSI:output:push ]
	LL_GPIO_InitTypeDef spiGPIO;
	LL_GPIO_StructInit(&spiGPIO);
	
	spiGPIO.Pin			= NRF_MOSI_PIN | NRF_CLK_PIN;
	spiGPIO.Mode		= LL_GPIO_MODE_ALTERNATE;
	spiGPIO.OutputType	= LL_GPIO_OUTPUT_PUSHPULL;
	spiGPIO.Speed		= LL_GPIO_SPEED_FREQ_MEDIUM;
	
	LL_GPIO_Init(GPIOA, &spiGPIO);
	
	spiGPIO.Pin		= NRF_MISO_PIN;
	spiGPIO.Mode	= LL_GPIO_MODE_FLOATING;
	spiGPIO.Pull	= LL_GPIO_PULL_UP;
	LL_GPIO_Init(GPIOA, &spiGPIO);
		
	// SPI
	LL_SPI_InitTypeDef mySPI;
	LL_SPI_StructInit(&mySPI);
	
	mySPI.Mode		= LL_SPI_MODE_MASTER;
	mySPI.NSS		= LL_SPI_NSS_SOFT;
	mySPI.BaudRate	= LL_SPI_BAUDRATEPRESCALER_DIV32;

	LL_SPI_Init(NRF_SPI, &mySPI);
	
	LL_SPI_Enable(NRF_SPI);	
	
}

Ok so here is a neat function I wrote to print the registers, it uses a printMsg function that I wrote you can check that out HERE 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void printRegister(uint8_t reg)
{
	uint8_t temp; 
	switch(reg)
	{
	case  NRF_STATUS : 
		temp = NRF_cmd_read_single_byte_reg(reg);
	
		CL_printMsg("\n________________Status Register : 0x%02X________________\n" , temp);
		CL_printMsg(" RX_DR  |  TX_DS  |  MAX_RT  |   RX_P_NO   |  TX_FULL \n");
		CL_printMsg("  %d         %d         %d           %d %d %d        %d    \n", (temp >> 6 & 0x01), (temp >> 5 & 0x01), (temp >> 4 & 0x01), (temp >> 3 & 0x01), (temp >> 2 & 0x01), (temp >> 1 & 0x01), (temp >> 0 & 0x01));
		CL_printMsg("------------------------------------------------------\n");
		break;
		
	case NRF_CONFIG :
		temp = NRF_cmd_read_single_byte_reg(NRF_CONFIG);
		CL_printMsg("\n_____________________________________Config Register : 0x%02X____________________________________\n", temp);
		CL_printMsg(" RES  |  MASK_RX_DR  |  MASK_TX_DS  |   MASK_MAX_RT   |  EN_CRC  |  CRCO  |  PWR_UP  |  PRIM_RX \n");
		CL_printMsg("  %d           %d             %d                %d              %d          %d        %d           %d \n", (temp >> 7 & 0x01),(temp >> 6 & 0x01), (temp >> 5 & 0x01), (temp >> 4 & 0x01), (temp >> 3 & 0x01), (temp >> 2 & 0x01), (temp >> 1 & 0x01), (temp >> 0 & 0x01));
		CL_printMsg("----------------------------------------------------------------------------------------------\n");
		break;
		
	default :
		temp = NRF_cmd_read_single_byte_reg(reg);
		CL_printMsg("\n__________Register Value : 0x%02X_____________\n", temp);
		CL_printMsg(" 7  |  6  |  5  |   4   |  3  |  2  |  1  |  0 \n");
		CL_printMsg(" %d     %d     %d      %d      %d     %d     %d     %d\n", (temp >> 7 & 0x01), (temp >> 6 & 0x01), (temp >> 5 & 0x01), (temp >> 4 & 0x01), (temp >> 3 & 0x01), (temp >> 2 & 0x01), (temp >> 1 & 0x01), (temp >> 0 & 0x01));
		CL_printMsg("------------------------------------------------\n");
		break;
	}
}

That is really just for debugging purposes but  I thought it was cool.

Ok now on how to use the driver:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
int main(void)
{

	//------ NON related code --------------
	setSysClockTo72();
	CL_delay_init();
	CL_printMsg_init_Default(false);
	init_debug_led();
	//-------------------------------------
	
	
	
	uint8_t payload[32] = "Edwin";
	init_pins();
	init_spi1();	
	delayMS(200); //give it a second to power up / stabalize etc.....
	
	CL_nrf24l01p_init_type myRX; // make an instance of the driver
	
	//common stuff
	myRX.set_address_width = FIVE_BYTES;
	myRX.set_crc_scheme = ENCODING_SCHEME_1_BYTE;
	myRX.set_enable_auto_ack = true;
	myRX.set_enable_crc = true;
	myRX.set_rf_channel = 0x7B;
	myRX.set_enable_dynamic_pl_width = true;		
	
	//rx stuff
	myRX.set_enable_rx_dr_interrupt = true;
	myRX.set_enable_rx_mode = true; 
	myRX.set_rx_pipe = PIPE_5;
	myRX.set_rx_addr_byte_1 = 0x28;
	myRX.set_rx_addr_byte_2_5 = 0xAABBCCDD;
	myRX.set_payload_width = 2;	
	
	//tx stuff
	myRX.set_enable_tx_mode = true ; 
	myRX.set_enable_max_rt_interrupt = true;
	myRX.set_enable_tx_ds_interrupt = true;
	myRX.set_tx_addr_byte_1 = 0x28;
	myRX.set_tx_addr_byte_2_5 = 0xAABBCCDD;
	
	//hardware specific functions
	myRX.spi_spiSend = &spiSend;	
	myRX.spi_spiRead = &spiRead;
	myRX.spi_spiSendMultiByte = &spiSendMultiByte;
	
	myRX.pin_CE_HIGH = &CE_pin_HIGH;
	myRX.pin_CE_LOW  = &CE_pin_LOW;
	myRX.pin_CSN_HIGH = &CSN_pin_HIGH;
	myRX.pin_CSN_LOW = &CSN_pin_LOW;	
	
	NRF_init(&myRX); // initialize
	
#ifdef BERX
	// act as rx and start listening
	myRX.cmd_act_as_RX(true);
	myRX.cmd_listen();
	
#endif 
	
#ifdef BETX
	//act as TX and send data
	myRX.cmd_act_as_RX(false);	
	myRX.cmd_transmit(payload, strlen(payload));
	uint8_t counter = 0x00;
#endif

	
	 
	
	for (;;)
	{

		#ifdef BETX
				if (flag == 0x55)
				{			
					flag = 0x00;	
					//printRegister(NRF_STATUS);
					myRX.cmd_clear_interrupts();						
					sprintf(payload, "Edwin %d", counter++);
					
					myRX.cmd_transmit(payload, strlen(payload));
				
					delayMS(200);
				}
	
		#endif

		#ifdef BERX
		
				if (flag == 0x55) // flag will be changed to 0x55 in interrupt, meaning we have data to read
				{		
					flag = 0x00; //reset flag					
					myRX.cmd_clear_interrupts();
					uint8_t len = myRX.cmd_get_payload_width();
					
					 // payload width is 32bytes max, if its longer ignore becasue
					 // it is corrupt data , so just  flush rx
					if (len < 33)
						{		
							//loop through payload and print to UART
							myRX.cmd_read_payload(rx_data_buff, len);
							NRF_cmd_FLUSH_RX();					
							for (int i = 0; i < len; i++)
							{
								CL_printMsg(" %c ", rx_data_buff[i]);
								rx_data_buff[i] = 0;
							}
							CL_printMsg("\n-\n");						
						}
					else
					{
						myRX.cmd_flush_rx();
					}
					
					myRX.cmd_listen();					
				}
		#endif
		
	}
}

Ok so there it is the main function using the driver in all its glory, or not so glory depending who you ask. 

If you are reading this right now I will finish this post tomorrow explaining the main function but honestly if you know C it should be pretty easy to understand whats going on here. And sorry about the formating again its the blog theme messing it all up. I will proof read and edit this entire series one day...sorry for typos.



<<PREVIOUS


Comments

  1. Hey, love the work!!, can I get the git clone link please, Thanks!!

    ReplyDelete
  2. Thank you for sharing this Edwin. This is the best tutorial on the web.

    ReplyDelete
Share your comments with me

Archive

Contact Form

Send