CE on NRF24L01--must be zero to receive data on SPI?

I’m chasing some intermittent problems with hardware that interfaces with NRF24L01 chips, so I’ve been reviewing my firmware. I found what seems to be a bug in the sample receiver code referenced on the web site, or a mis-statement in a referenced tutorial.

The Nordic products sold by SparkFun reference tutorials here:

http://www.diyembedded.com/

Tutorial 0 says:

… R_RX_PAYLOAD. This

operation allows you to read the contents of the RX FIFO if you have received a packet

(when you are in RX mode), which is generally signaled by the RX_DR interrupt. Using

this operation is a little more involved than the others, because it requires you to also use

CE.

When you are receiving packets, CE is held high. Once you have received a

packet you MUST bring CE low to disable the receiver, and then you execute the

R_RX_PAYLOAD operation…

The Nordic FOB page refers to example receiver code (http://www.sparkfun.com/datasheets/Wire … er-v10.zip). Here’s the code for reading the RX payload:

//Reads the current RX buffer into the data array
//Forces an RX buffer flush
void receive_data(void)
{
	cbi(RX_PORT, RX_CSN); //Stand by mode
    rx_spi_byte(0x61); //Read RX Payload
	data_array[0] = rx_spi_byte(0xFF);
	data_array[1] = rx_spi_byte(0xFF);
	data_array[2] = rx_spi_byte(0xFF);
	data_array[3] = rx_spi_byte(0xFF);
	sbi(RX_PORT, RX_CSN);
	
	rx_send_byte(0xE2); //Flush RX FIFO
	
	rx_send_command(0x27, 0x40); //Clear RF FIFO interrupt

    sbi(RX_PORT, RX_CE); //Go back to receiving!
}

Note that while CE is set high at the end of the function, it is never set low. It’s not set low before calling this function either.

Is this a bug, or is it not really necessary to power off the RF front end to receive a payload? I can’t find anything in the NRF24L01 spec that says you must use CE to do a R_RX_PAYLOAD.

I’ve been using this code fragment for some time and it seems to work without bringing CE low to receive the payload. I now use two receivers for frequency diversity, so it would be convenient to just leave CE high while I read received payloads, selecting the receiver with the appropriate CSN input.

Thanks,

Dave Thomas

That code works because it calls the FLUSH_RX command [rx_send_byte(0xE2); //Flush RX FIFO]. If you keep CE high while you read out a payload with the R_RX_PAYLOAD command, the L01 (for whatever reason) doesn’t remove that packet from the RX FIFO. Therefore, to get rid of the just-read payload, you have to call the FLUSH_RX command. The problem with doing it this way is that, if there are any other payloads in your RX FIFO, they get flushed, too.

It’s possible this was fixed in the L01+. I haven’t tested it since I started working with the L01, so I’m not sure. The easy way to test this is to comment out the FLUSH_RX command in the code above, then read FIFO_STATUS.RX_EMPTY (register 0x17, bit 0) after you’ve read out the RX payload (be sure to leave CE high the entire time). If RX_EMPTY is 0 after the RX payload has been read, then the issue is fixed and the device will now remove payloads from the RX FIFO even if CE is high. If this bit is a 1, then you still either have to flush the RX FIFO if you’re leaving CE high, or you can bring CE low while reading the payload and the device will remove the payload from the RX FIFO automatically. Be absolutely sure that whatever device is sending the payload only sends one payload, or else your RX FIFO could have two legitimate packets in it and the test results will be wrong.

That’s really helpful!

I’m already doing multiple calls to the receive code to throw away any additional packets after the first received. I guess this isn’t really necessary since the flush command was there anyway.

I think this means I can actually put two transceivers on the same SPI bus, with only their CSN inputs being separate. It would seem that a requirement to set CE low would pretty much defeat the idea of having multiple slaves on the same SPI bus. Now we’d have MISO, MOSI, CSN, and CE, and SCLK (ugh).

I just sent a new pcb to fab that provide both separate CSN and CE inputs, but I could really use that extra IO line. I’ll post what I find out when I get the new hardware and test with and without the FLUSH_RX command.

Thanks!

Dave Thomas

I just realized that my values for FIFO_STATUS.RX_EMPTY were backwards. ‘1’ means the RX FIFO is empty (the desired state after you read out the payload), and ‘0’ means that there is at least 1 payload still there.

davethomaspilot:
I’m already doing multiple calls to the receive code to throw away any additional packets after the first received. I guess this isn’t really necessary since the flush command was there anyway.

You are correct that you only need one flush command to empty all the extra packets out.

davethomaspilot:
I think this means I can actually put two transceivers on the same SPI bus, with only their CSN inputs being separate. It would seem that a requirement to set CE low would pretty much defeat the idea of having multiple slaves on the same SPI bus. Now we’d have MISO, MOSI, CSN, and CE, and SCLK (ugh).

There's no reason you couldn't be doing this already. As long as the devices have their own dedicated CSN line, you can put as many on a single SPI bus as you want (provided you still get good electrical signals on the pins at each unit). The CE pin really has nothing to do with SPI proper, so you could use one SPI bus (MOSI, MISO, and CLK) for both devices, with each device having its own CSN (always required) and (potentially) CE. As long as you're OK with flushing the RX FIFO after a read, though, then I see no reason you can't tie CE to VCC (unless you're concerned with power consumption), which would free up one CE pin per L01.

I see no reason you can’t tie CE to VCC (unless you’re concerned with power consumption),

Really?

That would really simplify things for me–just tying CE high. But, this doesn’t seem consistent with the code examples I’ve seen.

Is this true if I want to both transmit and receive? The examples I’ve seen always bring CE low to send set up commands to the NRF24L01.

Is toggling CE really not necessary? Doing it for power savings doesn’t make sense, if CE is brought back up at the end of the function–the time in actual functions like “configure_receiver” would be so short as to be neglible from a power dissipation viewpoint. So why is this done in the example code–just the multiple packets lost issue?

Thanks,

Dave Thomas

Hmm…you may have a good point. I’m not sure what the internal rules are on doing initial setup with CE high, but you could always test this out. Once initial setup is done, both TX and RX could work with CE being high from then on. The TX should work properly if CE is always high (be sure to observe the requirement of limiting continuous TX time to intervals of no more than 4 milliseconds, though). The RX (as I currently understand it) will require the FLUSH_RX command to remove payloads from the RX FIFO if CE is left high.

The reason that TX example code tends to pulse the CE line high briefly is because that’s how the datasheet tends to show it done. My example code, at least, brings CE low while reading an RX packet because that’s the only way I know of that the device will remove the packet from the RX FIFO. Since this is the desired behavior, it is the process I viewed as the correct one, so that’s how I wrote my code.

My comment about power consumption was if you ever wanted to stop receiving packets for some amount of time. You’re right that the power savings from clearing CE while reading out a payload would be pretty small, unless you get packets extremely frequently (when time on while receiving and time off while reading payloads might approach parity).

Ok, I’ll try commenting out all the lines that assert CE low and see what happens.

I due to the CE pulsing thing on the other side of the most transmitting/mostly receiving pair of transceivers. That’s because I need absolute lowest possible latency, so I use the REUSE_TX_PAYLOAD command and just pulse CE to do the transmit.

It’s at the “mostly receiving” side that I have two transceivers on the same SPI bus and would like to leave CE high. It (obviously) doesn’t help if I have to de-select using CE for setup, since I’d still need two unique signals per transceiver (CE and CSN) instead of just the one chip select signal that is normally required for SPI interfaces.

Thanks,

Dave Thomas

So, I got my pcbs back that have two nrf24l01 transceivers on the same SPI bus.

I don’t think you can leave CE high. As Brennen said, you have to pulse CE to do a transmit.

Also, it seems not all the SPI commands work with CE high. For example, I could only change RF channel when CE was low.

I haven’t yet decided whether I can tie the CE for both transceivers together. I’m hoping that if the transmit FIFO is empty, bringing the CE input high does nothing. But, I haven’t done that experiment yet.

I’m debugging some strange behavior. The firmware receives packets and I’ve implemented a simple command protocol. One command says re-transmit the packet on another RF channel, then go back to listening for more commands on the original channel.

The two nrf transceivers are pluggable. I monitor the frequency I request re-transmission on using a serial transceiver on a PC. It works 100% if only one card is plugged in, regardless of slot. It works 100% of the time with both cards plugged in, when the request is made to transceiver in SLOT A. But only some cards will work in Slot b, when slot a has a card. Those same cards work fine if they are the only card, or if in slot A with two cards.

The only difference in Slot a versus slot be is separated CSN and CE signals.

Also, if the programmer is on the SPI bus, it works 100% with both cards populated. Seems like a maybe marginal SPI timing, but I’ve increased the “RF_DELAY” in the sample code to 55 from 5 with no change in symptoms.

Both cards always receive the packet (I know this from an LED that I turn on when the command is received), but it’s like he transmit never happens.

I’ve also played with the CE pulse duration. Making it very long (1 ms) has no affect either.

I’ll post when I figure it out.

Dave Thomas

According to the main question “CE on NRF24L01–must be zero to receive data on SPI?”, i think I’ve got the answer for a nrf24l01+ chip which I have.

In the datasheet of the nrf24l01+ the R_RX_PAYLOAD command is described as:

Read RX-payload: 1 – 32 bytes. A read operation always starts at byte 0. Payload is deleted from FIFO after it is read. Used in RX mode.

This last sentence suggests that you don’t have to leave RX mode while reading RX payload, which means that CE_pin can remain high while reading the rx payload.

I tested this with my nrf24l01+ chip and it really is true.

And the chip itself sets the RX_EMPTY flag in FIFO register, so there is no need to flush.