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.
Hey, love the work!!, can I get the git clone link please, Thanks!!
ReplyDeleteThank you for sharing this Edwin. This is the best tutorial on the web.
ReplyDelete