Issues interfacing PIC32MX to NRF24l01 - Page 1
Forum section: electronics / general

author: BurnedResistor
author_rank: Regular Contributor
timestamp: January 23, 2016, 06:46:02 pm
content: Hey all,I have been having one heck of a day trying to interface between my PIC32MX120F064H and a cheap NRF24L01 module I grabbed off ebay. I have configured the SPI module, and am able to send out any data I want, but I am not able to read any data back from the chip. I am using the PicKit 3 as a programmer, and I am using XC32 free for compiling.I have finished setting up the SPI, and to test that it works I decided to send a NOP to the NRF (0b11111111), which, according to the data sheet, should return the content of the STATUS register of the chip. I do this with the following 4 lines in NRF.c:Code:ncsn = 0;test = SPI_trans(SPI3, 0b11111111);test = SPI_trans(SPI3, 0x00);ncsn = 1;to check the content of the test variable, I have setup breakpoints on "test = SPI_trans(SPI3, 0b11111111);", "test = SPI_trans(SPI3, 0x00);" , and one on "ncsn = 1;":However, no matter at what point I am, the test variable remains at 0:I am however, quite sure that the chip is responding, as there is a signal present on the MISO line. Here is a picture of the signal without stopping at the breakpoints:Trace 1: MOSITrace 2: MISOTraco 3: SCKI have checked that stopping between the two packets with a breakpoint does not change the signal with the oscilloscope.Currently I am using the PICKIT to power the circuit as I do not have acces to my powersupply. Ill test it with external as soon as I am able to.Can anyone help me out? Any help is appreciated.Here is the code:Main File:Code:#include <stdio.h>#include <stdlib.h>#include <p32xxxx.h>#include <xc.h>#include "config.h"#include "NRF.h"void init_IO();void shake_NRF();int main(int argc, char** argv) {init_IO();//TESTING SPIshake_NRF();}void init_IO() {//Disable JTAG as it conflicts with some of the pinsCFGCONbits.JTAGEN = 0;//NRF SPI:ANSELA = 0x0000;TRISBbits.TRISB9 = 1; //MISOSDI3Rbits.SDI3R = 0b0101;TRISBbits.TRISB10 = 0; //MOSIRPB10Rbits.RPB10R = 0b1110;TRISBbits.TRISB13 = 0; //CETRISBbits.TRISB14 = 0; //SCKTRISBbits.TRISB15 = 0; //CSN}void shake_NRF() {NRF_init(100);}config.h:Code:#ifndef CONFIG_H#defineCONFIG_H#ifdef__cplusplusextern "C" {#endif#include <p32xxxx.h>#include <xc.h>typedef unsigned char BOOL;#define TRUE 1#define FALSE 0// ======== GLOBAL OPTIONS ==========//System clock frequency#define F_OSC 48000000//Peripheral Clock Divider#define F_PER_DIV 8//Peripheral Clock frequency#define F_PER F_OSC/F_PER_DIV#ifdef__cplusplus}#endif#endif/* CONFIG_H */config.c:Code:// DEVCFG3// USERID = No Setting#pragma config PMDL1WAY = OFF // Peripheral Module Disable Configuration (Allow multiple reconfigurations)#pragma config IOL1WAY = OFF // Peripheral Pin Select Configuration (Allow multiple reconfigurations)// DEVCFG2#pragma config FPLLIDIV = DIV_2 // PLL Input Divider (2x Divider)#pragma config FPLLMUL = MUL_24 // PLL Multiplier (24x Multiplier)#pragma config FPLLODIV = DIV_2 // System PLL Output Clock Divider (PLL Divide by 2)// DEVCFG1#pragma config FNOSC = FRCPLL // Oscillator Selection Bits (Fast RC Osc with PLL)#pragma config FSOSCEN = OFF // Secondary Oscillator Enable (Disabled)#pragma config IESO = OFF // Internal/External Switch Over (Disabled)#pragma config POSCMOD = HS // Primary Oscillator Configuration (HS osc mode)#pragma config OSCIOFNC = OFF // CLKO Output Signal Active on the OSCO Pin (Disabled)#pragma config FPBDIV = DIV_8 // Peripheral Clock Divisor (Pb_Clk is Sys_Clk/8)#pragma config FCKSM = CSDCMD // Clock Switching and Monitor Selection (Clock Switch Disable, FSCM Disabled)#pragma config WDTPS = PS1048576 // Watchdog Timer Postscaler (1:1048576)#pragma config WINDIS = OFF // Watchdog Timer Window Enable (Watchdog Timer is in Non-Window Mode)#pragma config FWDTEN = OFF // Watchdog Timer Enable (WDT Disabled (SWDTEN Bit Controls))#pragma config FWDTWINSZ = WINSZ_25 // Watchdog Timer Window Size (Window Size is 25%)// DEVCFG0#pragma config JTAGEN = OFF // JTAG Enable (JTAG Disabled)#pragma config ICESEL = ICS_PGx2 // ICE/ICD Comm Channel Select (Communicate on PGEC2/PGED2)#pragma config PWP = OFF // Program Flash Write Protect (Disable)#pragma config BWP = OFF // Boot Flash Write Protect bit (Protection Disabled)#pragma config CP = OFF // Code Protect (Protection Disabled)SPI.h:Code:#ifndef SPI_H#defineSPI_H#ifdef__cplusplusextern "C" {#endif#include "config.h"/** ========= Structures and Enums =========*/typedef enum {SPI_INITF_FRM_EN = 0x80000000,SPI_INITF_FRMSYNC_EN = 0x40000000,SPI_INITF_FRMPOL_ACTH = 0x20000000,SPI_INITF_FRMPOL_ACTL = 0x0,SPI_INITF_SPIFE_CO = 0x20000,SPI_INITF_SPIFE_PRE = 0x0,SPI_INITF_FRZ = 0x4000,SPI_INITF_SIDL = 0x2000,SPI_INITF_DISSDO = 0x1000,SPI_INITF_MODE_8 = 0x0,SPI_INITF_MODE_16 = 0x400,SPI_INITF_MODE_32 = 0x800,SPI_INITF_SMP_END = 0x200,SPI_INITF_SMP_MID = 0x0,SPI_INITF_CKE_ACTIVE_TO_IDLE = 0x100,SPI_INITF_CKE_IDLE_TO_ACTIVE = 0x0,SPI_INITF_SSEN = 0x80,SPI_INITF_CKP_IDLE_IS_LO = 0x40,SPI_INITF_CKP_ACTI_IS_HI = 0x40,SPI_INITF_CKP_IDLE_IS_HI = 0x0,SPI_INITF_CKP_ACTI_IS_LO = 0x0,SPI_INITF_MSTEN = 0x20,SPI_INITF_MASTER = 0x20,SPI_INITF_SLAVE = 0x0} SPI_INITF;typedef enum {SPI1 = 1,SPI2 = 2,SPI3 = 3} SPI_CHANNEL;/** ======= Functions =======*/void SPI_init(SPI_CHANNEL, SPI_INITF, int baud);int SPI_trans(SPI_CHANNEL, int data);#ifdef__cplusplus}#endif#endif/* SPI_H */SPI.c:Code:#include "config.h"#include <xc.h>void SPI_init(SPI_CHANNEL channel, SPI_INITF flag, int baud) {//Calc Prescalar for Baudint prsclr;prsclr = F_PER / baud / 2 - 1;if (prsclr < 1 || prsclr > 512) {while (1) {//Prescalar too large/small__asm__("nop");}} else {//Available Prescalar. Continue:if (channel == SPI1) {__asm__("nop"); //Just to be sureSPI1CONbits.ON = 0; //Disable SPI__asm__("nop"); //Nop as SFR should not be accesed 1 cycle after ON clearSPI1BUF = 0; //Clear BufferSPI1BRG = prsclr;SPI1CON = flag; //Setup options determined by flagsSPI1CONbits.ENHBUF = 1;__asm__("nop"); //Just to be sureSPI1CONbits. ON = 1;__asm__("nop"); //Just to be sure} else if (channel == SPI2) {__asm__("nop"); //Just to be sureSPI2CONbits.ON = 0; //Disable SPI__asm__("nop"); //Nop as SFR should not be accesed 1 cycle after ON clearSPI2BUF = 0; //Clear BufferSPI2BRG = prsclr;SPI2CON = flag; //Setup options determined by flagsSPI2CONbits.ENHBUF = 1;__asm__("nop"); //Just to be sureSPI2CONbits. ON = 1;__asm__("nop"); //Just to be sure} else if (channel == SPI3) {__asm__("nop"); //Just to be sureSPI3CONbits.ON = 0; //Disable SPI__asm__("nop"); //Nop as SFR should not be accesed 1 cycle after ON clearSPI3BUF = 0; //Clear BufferSPI3BRG = prsclr;SPI3CON = flag; //Setup options determined by flagsSPI3CONbits.ENHBUF = 1;__asm__("nop"); //Just to be sureSPI3CONbits. ON = 1;__asm__("nop"); //Just to be sure}}}int SPI_trans(SPI_CHANNEL channel, int data) {if (channel == SPI1) {if (SPI1STATbits.SPIRBF) {int temp = (char) SPI1BUF;}SPI1BUF = data;while (SPI1STATbits.SPIBUSY) {__asm__("nop");}return SPI1BUF;} else if (channel == SPI2) {if (SPI2STATbits.SPIRBF) {int temp = (char) SPI2BUF;}SPI2BUF = data;while (SPI2STATbits.SPIBUSY) {__asm__("nop");}return SPI2BUF;} else if (channel == SPI3) {if (SPI3STATbits.SPIRBF) {int temp = (char) SPI3BUF;}SPI3BUF = data;while (SPI3STATbits.SPIBUSY) {__asm__("nop");}__asm__("nop");return SPI3BUF;} else {//?while (1) {__asm__("nop");}}}NRF.h:Code:#defineNRF_H#ifdef__cplusplusextern "C" {#endif#include "SPI.h"#include "config.h"#include <xc.h>// ====== Commands ========#define NRF_CMD__R_REGISTER 0b00000000#define NRF_CMD__W_REGISTER 0b00100000#define NRF_CMD__R_RX_PAYLOAD 0b01100000#define NRF_CMD__W_TX_PAYLOAD 0b10100000#define NRF_CMD__FLUSH_TX 0b11100001#define NRF_CMD__FLUSH_RX 0b11100010#define NRF_CMD__REUSE_TX_PL 0b11100011#define NRF_CMD__ACTIVATE 0b01010000#define NRF_CMD__R_RX_PL_WID 0b01100000#define NRF_CMD__W_ACK_PAYLOAD 0b10101000#define NRF_CMD__W_TX_PAYLOAD_NO_ACK 0b10110000#define NRF_CMD__NOP 0b11111111// ====== Registers =======#define NRF_RG__CONFIG 0x00#define NRF_RG__EN_AA 0x01#define NRF_RG__EN_RXADDR 0x02#define NRF_RG__SETUP_AW 0x03#define NRF_RG__SETUP_RETR 0x04#define NRF_RG__RF_CH 0x05#define NRF_RG__RF_SETUP 0x06#define NRF_RG__STATUS 0x07#define NRF_RG__OBSERVE_TX 0x08#define NRF_RG__CD 0x09#define NRF_RG__RX_ADDR_P0 0x0A#define NRF_RG__RX_ADDR_P1 0x0B#define NRF_RG__RX_ADDR_P2 0x0C#define NRF_RG__RX_ADDR_P3 0x0D#define NRF_RG__RX_ADDR_P4 0x0E#define NRF_RG__RX_ADDR_P5 0x0F#define NRF_RG__TX_ADDR 0x10#define NRF_RG__RX_PW_P0 0x11#define NRF_RG__RX_PW_P1 0x12#define NRF_RG__RX_PW_P2 0x13#define NRF_RG__RX_PW_P3 0x14#define NRF_RG__RX_PW_P4 0x15#define NRF_RG__RX_PW_P5 0x16#define NRF_RG__FIFO_STATUS 0x17#define NRF_RG__DYNPD 0x1C#define NRF_RG__FEATURE 0x1Dvoid NRF_init(char channel);#define nce LATBbits.LATB13#define ncsn LATBbits.LATB15#define ADDRESS0 0b00101011#define ADDRESS1 0b00101011#define ADDRESS2 0b00101011#ifdef__cplusplus}#endif#endif/* NRF_H */NRF.c:Code:char test;void NRF_init(char channel) {int x;for (x = 0; x < 100; x++) {__asm__("nop");}//Active SPI module with corrct edge, mode, phase etcSPI_init(SPI3,SPI_INITF_MASTER |SPI_INITF_MODE_8 |SPI_INITF_CKE_ACTIVE_TO_IDLE /*|SPI_INITF_SMP_END*/, 9600);ncsn = 1;nce = 1;ncsn = 0;test = SPI_trans(SPI3, 0b11111111);test = SPI_trans(SPI3, 0x00);ncsn = 1;}

author: dannyf
author_rank: Super Contributor
timestamp: January 23, 2016, 08:27:35 pm
content: I didn't look at your code. It looks like the spi transmission routine is faulty. I would test it against a software spi routine for sure.QuoteIt will depends on if the probe is on the mcu's MISO line or the NRF's output line -> the physical connection may not be good.

author: BurnedResistor
author_rank: Regular Contributor
timestamp: January 23, 2016, 09:36:47 pm
content: I have checked the physical connection at the pin of the mcu. Could you expand on testing it against a software routine? I am fairly new to embedded design...Quote from: dannyf on January 23, 2016, 08:27:35 pmSent from my SM-N7500Q using Tapatalk

author: dannyf
author_rank: Super Contributor
timestamp: January 23, 2016, 10:05:02 pm
content: 1. write a bit banged version of the spi; and see if it returns faulty results as well;2. put the scope's probe on the mcu's MISO pin and see if the waveform is the same.hope it helps.

author: BloodyCactus
author_rank: Frequent Contributor
timestamp: January 24, 2016, 12:08:24 am
content: is there a reason your not using the PLIB SPI code?

author: BurnedResistor
author_rank: Regular Contributor
timestamp: January 24, 2016, 12:21:55 am
content: I was trying to do it myself just to do a bit of learning but if I can't find the mistake I really won't have an option.Sent from my SM-N7500Q using Tapatalk

author: Sal Ammoniac
author_rank: Super Contributor
timestamp: January 24, 2016, 01:31:31 am
content: Your code looks reasonable. From the scope trace, you should be seeing 0x0e on the receive side when you write 0xff to it. The SPI peripheral is generating clocks, and the device is driving MISO, so you should be seeing something in the receive buffer.Have you tried single-stepping the code in SPI_trans()?

author: Howardlong
author_rank: Super Contributor
timestamp: January 24, 2016, 02:12:58 am
content: Quote from: Sal Ammoniac on January 24, 2016, 01:31:31 amOooh, I know this one, wait, ooh, with a PICkit3?....sorry, I just couldn't resist!

author: RogerRowland
author_rank: Regular Contributor
timestamp: January 24, 2016, 07:19:35 am
content: I've also not studied all of the code, but I see you're using RB9 as MISO, in which case don't you need to zero out ANSELB appropriately? Your code zeroes ANSELA.

author: BurnedResistor
author_rank: Regular Contributor
timestamp: January 24, 2016, 09:16:14 am
content: Quote from: RogerRowland on January 24, 2016, 07:19:35 amGood Catch, but sadly that was just a mistake when copying, I deleted the wrong line. I was trying to get rid of anything not related to SPI.

author: niekvs
author_rank: Contributor
timestamp: January 24, 2016, 09:50:24 am
content: You may want to declare that test variable 'volatile' to ensure it wasn't optimised away.

author: BurnedResistor
author_rank: Regular Contributor
timestamp: January 24, 2016, 10:00:13 am
content: Quote from: niekvs on January 24, 2016, 09:50:24 amChanging it to volatile did it. Yay! Thanks. Could you elaborate on why this is important?

author: Howardlong
author_rank: Super Contributor
timestamp: January 24, 2016, 10:16:23 am
content: Indeed, I just ran it on a 150F128B on a breadboard that I had in stock and switched the code to SPI1 (150 doesn't have SPI3).I didn't have to set anything to volatile, but I did need to fix a few copy and paste errors, and select the right version of XC32: your code is dependent on an older PLIB which was deprecated in 1.40 of the compiler, so I switched back to 1.34.I also added a test1 and test2, plumbed in the scope, looped back the MOSI/MISO (I don't have an nRF) and it worked.while (1){static unsigned char uc=0;ncsn = 0;test1 = SPI_trans(SPI1, uc);test2 = SPI_trans(SPI1, 0x00);uc++;ncsn = 1;}By the way, xc.h has a nice Nop() function!

author: niekvs
author_rank: Contributor
timestamp: January 24, 2016, 10:45:13 am
content: Quote from: BurnedResistor on January 24, 2016, 10:00:13 amGlad it fixed it! It's important because you're not using that test variable anywhere (only setting it) and so the compiler may conclude it's not needed and optimize it out. Setting it to volatile prevents it from doing that. You also need it when writing to a variable from an ISR and expecting the written value to be available in the non-ISR code. Basically, volatile ensures it's written to memory straight away.By the way, if you had actually used the variable somewhere else (which you probably would have if you relied on the result and weren't just testing), volatile most likely wouldn't be necessary.

author: BurnedResistor
author_rank: Regular Contributor
timestamp: January 24, 2016, 11:51:09 am
content: Wow Thanks!Sent from my SM-N7500Q using Tapatalk

author: dannyf
author_rank: Super Contributor
timestamp: January 24, 2016, 02:33:39 pm
content: QuoteBecause the variable "test" is never referenced again in the code so it is optimized away.You should always turn optimization off for debugging.

author: Howardlong
author_rank: Super Contributor
timestamp: January 24, 2016, 05:05:51 pm
content: Quote from: dannyf on January 24, 2016, 02:33:39 pm^ this, unless you are optimising (or debugging the optimiser which thankfully these days is very rare, but it wasn't always so).

author: Sal Ammoniac
author_rank: Super Contributor
timestamp: January 25, 2016, 01:20:24 am
content: Quote from: Howardlong on January 24, 2016, 02:12:58 amMaybe he's got a lot of time on his hands.

author: BurnedResistor
author_rank: Regular Contributor
timestamp: January 25, 2016, 04:26:22 pm
content: Quote from: Sal Ammoniac on January 25, 2016, 01:20:24 amAny suggestions for better options under 100 euro?Sent from my SM-N7500Q using Tapatalk

author: Sal Ammoniac
author_rank: Super Contributor
timestamp: January 25, 2016, 04:45:54 pm
content: Quote from: BurnedResistor on January 25, 2016, 04:26:22 pmThe best option is the ICD-3, but that's about USD$200. Sometimes MC has it on sale, so look for that. There are ICD-3 clones, but those raise the same ethical questions that the recent discussions of Segger J-Link clones did.

author: Howardlong
author_rank: Super Contributor
timestamp: January 25, 2016, 06:54:30 pm
content: To be fair, the ICD-3 necessarily four times as good as a PK3 in shear value terms although it might be four times as much on sticker price. It does upload code more quickly and it does software breakpoints, which by itself can be of significant value (no skidding and unlimited number of BPs: some lower end PICs only have one hardware breakpoint).Some very quick timings with two deliberately extreme examples, one a 2.5KB object file and the other 1.6MB on a PIC32MZ2048EF144:2.5KB PK3 8s; ICD3 3s.1.6MB PK3 104s; ICD3 21s.For some devices, like the PIC32MX570F512L there seems to be little difference between a small object file and a very large one when programming on the ICD3 (about 8s either way). On a PK3, it's 40s. There is definitely something to be said for choosing devices with smaller flash if you don't need it in this case, smaller devices of the same family program much more quickly, roughly in line with flash size.These tests were all conducted with default debugger settings in MPLAB X 3.20.This isn't the whole story, as if you're debugging with a lot of large watch variables this can slow things down particularly when single stepping.

author: neslekkim
author_rank: Super Contributor
timestamp: January 25, 2016, 09:06:50 pm
content: regarding ICD3, i guess you people have seen this:https://www.eevblog.com/forum/buysellwanted/fs-microchip-mplab-icd-3-(new-factory-sealed)/msg838011/#new

author: Sal Ammoniac
author_rank: Super Contributor
timestamp: January 25, 2016, 10:50:35 pm
content: Quote from: Howardlong on January 25, 2016, 06:54:30 pmThis nicely illustrates the difference between a PK3 and an ICD3. Professionals, for whom time is money, will always choose the ICD3 over the PK3, but hobbyists may have different priorities, for which the PK3 is a valid choice.I have both, but only use the ICD3 due to its speed advantages. I bought the PK3 first, but quickly tired of its sluggishness.In a quick look around the lab at work I saw many ICDs, but not a single PK.

author: BloodyCactus
author_rank: Frequent Contributor
timestamp: January 26, 2016, 03:18:56 am
content: I got a PK3 then got an ICD3 and would never go back to PK3! the difference is really night and day. It also seems more stable, with PK3 I would often have to disconnect usb/reconnect a lot of times. ICD3 has been rock solid for me. I do a lot of pic32 stuff so it was worth the investment.

author: Brutte
author_rank: Frequent Contributor
timestamp: January 26, 2016, 11:39:41 am
content: QuoteWhat about its battery life?