# nRF24L01 timing issues

**URL:** <https://community.sparkfun.com/t/nrf24l01-timing-issues/2166>\
**Category:** Wireless & IoT\
**Created:** [July 5, 2006, 7:13pm UTC](https://community.sparkfun.com/t/nrf24l01-timing-issues/2166 "2006-07-05T19:13:01Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![mchang](https://avatars.discourse-cdn.com/v4/letter/m/eb8c5e/32.png) [@mchang](https://community.sparkfun.com/u/mchang)\
**Post date:** [July 5, 2006, 7:13pm UTC](https://community.sparkfun.com/t/nrf24l01-timing-issues/2166/1 "2006-07-05T19:13:01Z")

</div>

Using PIC 18F2455 right now with [[LMC6482A](http://www.national.com/pf/LM/LMC6482.html) and an MOSFET to supply 3V. I “ported” the sparkfun PIC code to C18 and am using the C18 SPI functions from spi.h.

It **seems** that the RX side only actually receives data from the transmitter if it is sent three (or more) times (sending a 4-byte payload like the SF example). The RX\_IRQ goes low on the first transmit, but nothing gets read out of the RX FIFO (but all zeros) unless I wait for the third transmission.

Code can get pasted here if ya’ll want it.]([http://www.national.com/pf/LM/LMC6482.html](http://www.national.com/pf/LM/LMC6482.html))

---

<div class="post-metadata">

**Author:** ![mchang](https://avatars.discourse-cdn.com/v4/letter/m/eb8c5e/32.png) [@mchang](https://community.sparkfun.com/u/mchang)\
**Post date:** [July 5, 2006, 10:01pm UTC](https://community.sparkfun.com/t/nrf24l01-timing-issues/2166/2 "2006-07-05T22:01:51Z")

</div>

Fixed. Seems like the nRF24L01 in ShockBurst RX mode needs you to lower CE to read out of the RX FIFOs. Doesn’t seem to be very clear in the data sheet except maybe:

> p11: “The MCU can access the FIFOs at any time, in power down mode, in standby modes, and during RF packet transmission.”

or

> p15: 6. MCU sets the CE pin low to enter Standby-I mode.
> 
> 1. MCU can clock out the payload data at a suitable rate via the SPI interface.

Not terribly obvious to me, but what do I know :), I just started playing with these.

---

<div class="post-metadata">

**Author:** ![jimlake](https://avatars.discourse-cdn.com/v4/letter/j/e99b99/32.png) [@jimlake](https://community.sparkfun.com/u/jimlake)\
**Post date:** [July 5, 2006, 10:31pm UTC](https://community.sparkfun.com/t/nrf24l01-timing-issues/2166/3 "2006-07-05T22:31:27Z")

</div>

Welcome to the clunkiest data sheet around. There is a whole lot of stuff that is not in there.

---

<div class="post-metadata">

**Author:** ![mchang](https://avatars.discourse-cdn.com/v4/letter/m/eb8c5e/32.png) [@mchang](https://community.sparkfun.com/u/mchang)\
**Post date:** [July 5, 2006, 10:46pm UTC](https://community.sparkfun.com/t/nrf24l01-timing-issues/2166/4 "2006-07-05T22:46:32Z")

</div>

There doesn’t seem to be much of a repository of working code for these things. Am I missing something?

---

<div class="post-metadata">

**Author:** ![jimlake](https://avatars.discourse-cdn.com/v4/letter/j/e99b99/32.png) [@jimlake](https://community.sparkfun.com/u/jimlake)\
**Post date:** [July 5, 2006, 11:18pm UTC](https://community.sparkfun.com/t/nrf24l01-timing-issues/2166/5 "2006-07-05T23:18:07Z")

</div>

No you are not. I have some C code I will share, but it is all for Atmel AVR and it probably won’t work on anything else. I found out the hard way that porting code can be problematic for the RF2401 becaue of timing issues.

Jim Lake

---

<div class="post-metadata">

**Author:** ![brennen](https://avatars.discourse-cdn.com/v4/letter/b/54ee81/32.png) [@brennen](https://community.sparkfun.com/u/brennen)\
**Post date:** [July 6, 2006, 11:41am UTC](https://community.sparkfun.com/t/nrf24l01-timing-issues/2166/6 "2006-07-06T11:41:35Z")

</div>

I’m guessing that the post I made about a month ago with .h and .c include files for the nRF24L01 went unnoticed. It implements pretty much every function available on the chip and is very well-commented.

---

<div class="post-metadata">

**Author:** ![leon\_heller](https://yyz1.discourse-cdn.com/sparkfun/user_avatar/community.sparkfun.com/leon_heller/32/7602_2.png) [@leon\_heller](https://community.sparkfun.com/u/leon_heller)\
**Post date:** [July 6, 2006, 12:23pm UTC](https://community.sparkfun.com/t/nrf24l01-timing-issues/2166/7 "2006-07-06T12:23:17Z")

</div>

> brennen:  
> I’m guessing that the post I made about a month ago with .h and .c include files for the nRF24L01 went unnoticed. It implements pretty much every function available on the chip and is very well-commented.

I downloaded it and tried compiling it, but some header files were missing. What compiler did you use?

Leon

---

<div class="post-metadata">

**Author:** ![brennen](https://avatars.discourse-cdn.com/v4/letter/b/54ee81/32.png) [@brennen](https://community.sparkfun.com/u/brennen)\
**Post date:** [July 6, 2006, 1:32pm UTC](https://community.sparkfun.com/t/nrf24l01-timing-issues/2166/8 "2006-07-06T13:32:00Z")

</div>

I used the 4.1 version of GCC that came with the newer version of WinARM to compile. Upon re-inspecting the code today, I noticed that in nrf24l01.c, you can take out the “ lcd.h” line since that code isn’t used (I was testing the code using an LCD to spit values out and forgot to remove the include).

As far as nrf24l01.h, lines 44-46 must be changed for your own personal chip. CE\_IOREGISTER must be defined as whatever register on your microcontroller that handles the actual pin output for your CE pin. CE\_PINMASK is the mask that is used to clear and set this pin within CE\_IOREGISTER. Since I’m using the LPC2148, I have included the file lpc214x.h since it contains the definitions for the registers I’m defining.

For example, my project is using pin P0.21 on my microcontroller to control the CE pin on the nRF24L01. Hence, register FIO0PIN controls that pins on or off status, and that is why I have the statement “#define CE\_IOREGISTER FIO0PIN”. Also, the mask for pin 21 is 0x200000 (this is a 32 bit register), which is the reason for the statement “#define CE\_PINMASK 0x200000”.

The last thing you have to do yourself is provide the function “void spi1\_send(unsigned char \* data, unsigned int len)”. The purpose of this function is to allow you to send an array of characters (unsigned char \* data) with a specified length (unsigned int len) while constantly holding CSN low. I had to do this because the LPC2148 wouldn’t work properly in any of its automatic modes, so I made the function manually hold CSN low.

Other than that, I can’t think of anything else right off hand that you should need. In the case that anyone is using an LPC21xx with an SSP controller on it, I can provide you with my SPI code as well.

---

<div class="post-metadata">

**Author:** ![leon\_heller](https://yyz1.discourse-cdn.com/sparkfun/user_avatar/community.sparkfun.com/leon_heller/32/7602_2.png) [@leon\_heller](https://community.sparkfun.com/u/leon_heller)\
**Post date:** [July 6, 2006, 2:33pm UTC](https://community.sparkfun.com/t/nrf24l01-timing-issues/2166/9 "2006-07-06T14:33:29Z")

</div>

> brennen:  
> I used the 4.1 version of GCC that came with the newer version of WinARM to compile. Upon re-inspecting the code today, I noticed that in nrf24l01.c, you can take out the “ lcd.h” line since that code isn’t used (I was testing the code using an LCD to spit values out and forgot to remove the include).
> 
> As far as nrf24l01.h, lines 44-46 must be changed for your own personal chip. CE\_IOREGISTER must be defined as whatever register on your microcontroller that handles the actual pin output for your CE pin. CE\_PINMASK is the mask that is used to clear and set this pin within CE\_IOREGISTER. Since I’m using the LPC2148, I have included the file lpc214x.h since it contains the definitions for the registers I’m defining.
> 
> For example, my project is using pin P0.21 on my microcontroller to control the CE pin on the nRF24L01. Hence, register FIO0PIN controls that pins on or off status, and that is why I have the statement “#define CE\_IOREGISTER FIO0PIN”. Also, the mask for pin 21 is 0x200000 (this is a 32 bit register), which is the reason for the statement “#define CE\_PINMASK 0x200000”.
> 
> The last thing you have to do yourself is provide the function “void spi1\_send(unsigned char \* data, unsigned int len)”. The purpose of this function is to allow you to send an array of characters (unsigned char \* data) with a specified length (unsigned int len) while constantly holding CSN low. I had to do this because the LPC2148 wouldn’t work properly in any of its automatic modes, so I made the function manually hold CSN low.
> 
> Other than that, I can’t think of anything else right off hand that you should need. In the case that anyone is using an LPC21xx with an SSP controller on it, I can provide you with my SPI code as well.

Thanks. I’m using the CrossWorks tools which are based on gcc. I’ve just got a bit further with it.

The following routines actually seem to be required: spi1\_clear\_fifo(), DelayUS(), spi1\_send() and spi1\_read().

Inserting stubs for them and a dummy main(), the whole thing seems to build OK.

Leon

---

<div class="post-metadata">

**Author:** ![mchang](https://avatars.discourse-cdn.com/v4/letter/m/eb8c5e/32.png) [@mchang](https://community.sparkfun.com/u/mchang)\
**Post date:** [July 6, 2006, 4:25pm UTC](https://community.sparkfun.com/t/nrf24l01-timing-issues/2166/10 "2006-07-06T16:25:13Z")

</div>

> brennen:  
> I’m guessing that the post I made about a month ago with .h and .c include files for the nRF24L01 went unnoticed. It implements pretty much every function available on the chip and is very well-commented.

I looked at your code. Thanks for posting it. A few more prototypes are needed for spi reads, but otherwise, it seems nicely complete.

However, a quick glance at it yesterday I didn’t notice the flipping of the CE pin during RX FIFO read. Of course, it was just a cursory glance, so I may be wrong. Did it work for you without putting the chip out of enable?

---

<div class="post-metadata">

**Author:** ![brennen](https://avatars.discourse-cdn.com/v4/letter/b/54ee81/32.png) [@brennen](https://community.sparkfun.com/u/brennen)\
**Post date:** [July 6, 2006, 5:26pm UTC](https://community.sparkfun.com/t/nrf24l01-timing-issues/2166/11 "2006-07-06T17:26:23Z")

</div>

As far as I can tell, there are no problems with the read code (I’m using it as I type this message). You do have to be careful about clearing your interrupts and fifos when reading from and writing to the nRF24L01s (this is done automatically in my code).

---

<div class="post-metadata">

**Author:** ![mchang](https://avatars.discourse-cdn.com/v4/letter/m/eb8c5e/32.png) [@mchang](https://community.sparkfun.com/u/mchang)\
**Post date:** [July 6, 2006, 5:34pm UTC](https://community.sparkfun.com/t/nrf24l01-timing-issues/2166/12 "2006-07-06T17:34:59Z")

</div>

> brennen:  
> As far as I can tell, there are no problems with the read code (I’m using it as I type this message). You do have to be careful about clearing your interrupts and fifos when reading from and writing to the nRF24L01s (this is done automatically in my code).

Hrm. The version of your code does not clear any interrupts during the nrf24l01\_read\_rx\_payload(…) operation. It does clear the spi fifo, which I will look into on my side. Also, I figure you’re talking about the code that _uses_ your nrf library.

My procedure for RX is:

- CE = 0

- spi send 0x61 (R\_RX\_PAYLOAD)

- spi read payload

- reset interrupt (0x27)

- flush RX FIFO (0xE2)

- CE = 1

Do you see anything wrong with that? The version of your library (just the RX function) I have is pasted below.

Thanks.

```auto
void nrf24l01_read_rx_payload(unsigned char * data, unsigned int len, bool includestatus)
{
	char buffer[33];
	unsigned int count;
	unsigned int numbytes;
	
	spi1_clear_fifo();
	
	if(includestatus == false)	
		numbytes = len + 1;
	else
		numbytes = len;
	
	buffer[0] = nrf24l01_R_RX_PAYLOAD;

	spi1_send(buffer, numbytes);
	
	for(count = 0; count <= len; count++)
		buffer[count + 1] = data[count];
		
	if(includestatus == false)
		spi1_read();
	
	for(count = 0; count < len; count++)
		data[count] = spi1_read();
}

```

---

<div class="post-metadata">

**Author:** ![brennen](https://avatars.discourse-cdn.com/v4/letter/b/54ee81/32.png) [@brennen](https://community.sparkfun.com/u/brennen)\
**Post date:** [July 6, 2006, 6:44pm UTC](https://community.sparkfun.com/t/nrf24l01-timing-issues/2166/13 "2006-07-06T18:44:48Z")

</div>

Looks like you have pointed out a discrepancy in my comments. Instead of calling nrf24l01\_clear\_flush() in my call to nrf24l01\_read\_rx\_payload() in the library, I actually call it in my main() function in my main.c file. I don’t make this call when I transmit at all though, as the TX\_DS interrupt apparently clears itself when you write to the TX FIFO again.

As far as CE goes, the only time I make any change to it in my receiver code is in the nrf24l01\_initialize() function (you’re not supposed to make changes to registers with it high). I can’t find anywhere else that changes it at all for the receiver, be it in the library or my other code. The transmitter will toggle CE in order to send the data payload, but, like I said, I don’t have to change CE to read from the 24L01 (at least not with the ones I have). YMMV, though.

---

<div class="post-metadata">

**Author:** ![mchang](https://avatars.discourse-cdn.com/v4/letter/m/eb8c5e/32.png) [@mchang](https://community.sparkfun.com/u/mchang)\
**Post date:** [July 6, 2006, 6:48pm UTC](https://community.sparkfun.com/t/nrf24l01-timing-issues/2166/14 "2006-07-06T18:48:58Z")

</div>

Thanks for the insight. It may still be a timing problem on my side that is alleviated (slightly) by toggling the CE. I will try messing with my SPI buffer flush now and report back.

---

<div class="post-metadata">

**Author:** ![mchang](https://avatars.discourse-cdn.com/v4/letter/m/eb8c5e/32.png) [@mchang](https://community.sparkfun.com/u/mchang)\
**Post date:** [July 6, 2006, 7:08pm UTC](https://community.sparkfun.com/t/nrf24l01-timing-issues/2166/15 "2006-07-06T19:08:20Z")

</div>

ok, update.

my method of turning of CE to read from RX FIFOs seems to work.

brennen’s method of clearing the RX\_FIFO before reading the payload works as well.

so, i have code that does

1. flush rx fifo

2. read rx payload

3. reset interrupts

and that works fine.

It seems counter-intuitive to me to flush the FIFOs before reading the payload. I thought the payload would already be in the FIFOs? I will revisit the datasheet.

thanks for the help, brennen. I now have code that works and is fairly clean. I didn’t end up using your code, but it is definitely a good starting point now that I understand the RX FIFO should be cleared.

---

<div class="post-metadata">

**Author:** ![brennen](https://avatars.discourse-cdn.com/v4/letter/b/54ee81/32.png) [@brennen](https://community.sparkfun.com/u/brennen)\
**Post date:** [July 6, 2006, 7:20pm UTC](https://community.sparkfun.com/t/nrf24l01-timing-issues/2166/16 "2006-07-06T19:20:27Z")

</div>

If you’re talking about flushing the SPI FIFOs, you want to flush them first just in case there’s something in there that you don’t want (my spi\_read function depends on the FIFO being empty to terminate reading).

If you’re talking about flushing the nRF24L01’s FIFOs before reading, however, that would be a bad thing. I flush the RX FIFO on the 24L01 AFTER I read it, but certainly not before. The reason I do this is that there seems to be a silicon error in the 24L01 in that the RX FIFO does not clear itself like the datasheet says it is supposed to. You must clear the FIFO yourself in order to get the chip to continue reading stuff.

When I think about it, however, this may be a good reason why you do your CE toggle. Maybe it’s not a silicon error, but simply that I don’t take CE low to read from the RX FIFO that is the reason my RX FIFO never empties. I will try implementing the CE toggle on receive in my next round of code to see if it allows me to not have to clear the FIFO manually.

Looks like we have helped each other out on this one. Thanks, mchang.

---

<div class="post-metadata">

**Author:** ![mchang](https://avatars.discourse-cdn.com/v4/letter/m/eb8c5e/32.png) [@mchang](https://community.sparkfun.com/u/mchang)\
**Post date:** [July 6, 2006, 7:37pm UTC](https://community.sparkfun.com/t/nrf24l01-timing-issues/2166/17 "2006-07-06T19:37:11Z")

</div>

So we’re on the same page, here is a code snippet **that works** for me. It is called when the RX\_IRQ goes low. Nothing else is called before this routine.

```auto
void rx_getdata(void) {
  unsigned char instruction, data, i;
  
  // Flush RX FIFO
  instruction = 0xE2;
  RX_CSN = 0;
  spi_write(instruction);
  RX_CSN = 1;

  // read the RX payload
  instruction = 0x61;
  RX_CSN = 0;
  spi_write(instruction);

  // read the four byte payload
  for( i=0; i<4; i++ ) {
    spi_read(&data_array[i]);
  }
  RX_CSN = 1;

  // reset interrupt
  instruction = 0x27;
  data = 0x70;
  RX_CSN = 0;
  spi_write(instruction);
  spi_write(data);
  RX_CSN = 1;
}

```

The same with CE toggle and no FIFO flush works as well:

```auto
void rx_getdata(void) {
  unsigned char instruction, data, i;

  RX_CE = 0;
  
  // read the RX payload
  instruction = 0x61;
  RX_CSN = 0;
  spi_write(instruction);

  // read the four byte payload
  for( i=0; i<4; i++ ) {
    spi_read(&data_array[i]);
  }
  RX_CSN = 1;

  // reset interrupt
  instruction = 0x27;
  data = 0x70;
  RX_CSN = 0;
  spi_write(instruction);
  spi_write(data);
  RX_CSN = 1;  
    
  // Flush RX FIFO
  instruction = 0xE2;
  RX_CSN = 0;
  spi_write(instruction);
  RX_CSN = 1;

  RX_CE = 1;
}

```

But in the second case, not flushing the FIFO after reading causes the error you talk about. I am not flushing the SPI interface before the read, at the moment, and it works okay.

I don’t know why it works to flush the RX FIFO before reading from it. Like I said (and you said), it doesn’t seem right.

For now, I’m going to stick with:

1. CE = 0

2. Flush SPI buffers

3. Read RX payload

4. Reset interrupts

5. Flush RX FIFO

That seems to work reliably. Any thoughts on the **before read** flush working?

---

<div class="post-metadata">

**Author:** ![mchang](https://avatars.discourse-cdn.com/v4/letter/m/eb8c5e/32.png) [@mchang](https://community.sparkfun.com/u/mchang)\
**Post date:** [July 6, 2006, 7:44pm UTC](https://community.sparkfun.com/t/nrf24l01-timing-issues/2166/18 "2006-07-06T19:44:37Z")

</div>

Also, one thing that might be different is in how we initiate the R\_RX\_PAYLOAD.

Yours has:

```auto
spi1_send(buffer, numbytes);

```

where only buffer[0] has the R\_RX\_PAYLOAD command and the rest of the bytes are garbage.

Mine just sends the 0x61 command (R\_RX\_PAYLOAD) and follows it with the appropriate number of SPI reads.

The datasheet is unclear from Table 8, since it says the R\_RX\_PAYLOAD instruction can carry a number of data bytes (which should mean nothing).

Probably not a big deal, just an observation.

---

<div class="post-metadata">

**Author:** ![brennen](https://avatars.discourse-cdn.com/v4/letter/b/54ee81/32.png) [@brennen](https://community.sparkfun.com/u/brennen)\
**Post date:** [July 7, 2006, 1:40am UTC](https://community.sparkfun.com/t/nrf24l01-timing-issues/2166/19 "2006-07-07T01:40:51Z")

</div>

From what I’ve gathered from the datasheet, what you clock into MOSI on the nRF24L01 doesn’t really matter after you get the R\_RX\_PAYLOAD instruction in there. What matters is how many bytes you clock in. That essentially decides how many bytes you get out of it. I can’t really find any reason to clock any formatted or initialized data into the 24L01 in this situation once the command byte is clocked in.

Apparently it doesn’t make any difference when you flush the RX FIFO when you don’t toggle CE, since you do it before the read and I do it after the read. This leads me to believe that when CE is left high, the actual bytes in the FIFO don’t change at all. This makes sense to me, since the datasheet says that you shouldn’t change register status while CE is left high (apparently the chip won’t let you change these registers). The net effect is simply that in combination with clearing the RX\_DS interrupt flag, you are able to receive data again.

It still seems strange that even when you do the CE toggle routine you still have to clear the RX FIFO. I would think that with the CE pin being low, you could read the RX FIFO until it emptied itself (like the datasheet says it will).

As far as the spi\_clear\_fifo(), I only do that so that I am sure to only have the just-read data in the SPI FIFO. Like I pointed out, my spi1\_read function simply returns all the data in the SPI FIFO until it has been emptied. For this reason, I doubt that it would matter if you cleared out your SPI FIFO or not unless your spi\_read works like mine (which it does not appear to).

---

<div class="post-metadata">

**Author:** ![mchang](https://avatars.discourse-cdn.com/v4/letter/m/eb8c5e/32.png) [@mchang](https://community.sparkfun.com/u/mchang)\
**Post date:** [July 26, 2006, 1:38am UTC](https://community.sparkfun.com/t/nrf24l01-timing-issues/2166/20 "2006-07-26T01:38:48Z")

</div>

Yay. Forum spam.

[Next page](https://community.sparkfun.com/t/nrf24l01-timing-issues/2166.md?page=2)
