This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Wrong DPL and misaligned payloads for received packets after writing ACK Payload. Misaligned SPI communication?

Dear Team Nordic,

dear Community,

I'm working on a project with nRF24L01+ transceivers where I have two types of devices: a so called Basestation which acts as a Primary Transmitter (PTX) and a Robot which acts as a Primary Receiver (PRX) in the regards of the Enhanced Shockburst features.

In an earlier version we were using payloads with a static length, which were sent to the Robot. The Robot would then use the Auto-Acknowledgement feature to answer with empty ACKs.
That worked with no problems.

For the new version the job was to implement the ACK Payload feature to send sensor data back to the Basestation.
In order to use ACK Payloads, I initialize the Basestation as follows:
Enable Data Pipe 0 (for ACKs).
Set Auto-Retransmit-Count to 0.
Set the FEATURE register to use Dynamic Payload Length, Enable ACK Payload and Enable Dynamic Acks (allowing packets which don't require ACKs).
Enable Dynamic Payload Length for Pipe 0.
Write to the Config Register: PRIM_RX to 0 (making it a PRX) and PWR_UP.

The Robot is initialized as follows:
Enable Data Pipe 1.
Writing the same values to the FEATURE register as the Basestation did.
Enable Auto-Ack for Pipe 1.
Enable Dynamic Payload Length for Pipe 1.


I would now have the Basestation send a test packet with 13 Bytes to the robot in an interval of 1 second.

The Robot now uses a loop to do the following:
Read the Dynamic Payload Length by issuing the corresponding SPI command. (It does that unconditionally in every loop iteration) and save it to a var (DPL).
Read the STATUS register and print its contents.
If RX_DR is set (when a packet was received) then
{
Print dynamic payload length.
Read payload with the length of DPL.
Write the RX_DR flag to the STATUS register to clear the interrupt flag.
Print the payload contents with the length of DPL.
}
Delay of 500ms.



Now the situation is the following.
In the previous scenario I get the expected output:
Since I send packets every second and I poll the reception of packets every 500ms, I get an alternating pattern of
either receiving a packet with 13 Bytes or I would not receive a new packet yet (RX_DR is not set).
Also the payload prints correctly.

Now I change the Robot Init code by a few lines which just write four Bytes of Data to the ACK Payload.
I only do this once during init.
The expected outcome is that this payload would be sent at the moment of receiving the first packet form the Basestation.
Every consecutive packet from the Basestation would then be answered with an empty ACK since I'm not "filling up the ACK payload" again.

What I get is something different, though...
As soon as I receive the first packet from the Basestation, the DPL now reports 14 Bytes (instead of the expected 13 Bytes).
When reading those 14 Bytes, I notice that my data packet has 3 leading Bytes of garbage data.
The original data that I send goes like this: 0x50 34 56 78 90 ab cd ef 01 02 03 04 ff.
What I receive is this: 62 62 62 50 34 56 78 90 ab cd ef 01 02 03.

The second receptions gives the very same result.
Now, the third reception reports a DPL of 46 Bytes, which is obviously an error since any payload cannot be larger than 32 Bytes.
When reading out those 46 Bytes I would get:
0x62 62 62 50 34 56 78 90 ab cd ef 01 02 03 04 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff.

Running that code for longer, it would occasionally also report a DPL of 110 Bytes which read like:
0x62 62 62 50 34 56 78 90 ab cd ef 01 02 03 04 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff.

I'm not totally surprised that I get garbage appended to my data when I attempt to read more payload than there could possibly be.
The pre-appended Bytes, however, are not supposed to be there. And as it turned out after running another test: those Bytes represent the status register.

As we know: whenever the module does not have anything useful to send during SPI communication, it would just send the status register by default.
So, what I believe is happening here is that I happen to get a misalignment in the SPI communication where the module expects me to send more data or read more data
before I would issue the next SPI command.

This misalignment seems to happen after calling my function that writes an ACK payload, so I will give you a description of that:

int8_t writeACKpayload(uint8_t* payloadBytes, uint8_t payload_length, uint8_t pipeNo) {
	//This function should be called as often as a packet was received (either before or after reception),
	//because the module can only hold up to 3 ACK packets.
	//It will use up one of the packets as a response when it receives a packet.
	//See: https://shantamraj.wordpress.com/2014/11/30/auto-ack-completely-fixed/ (visited 13th March, 2018)

	//you may want to call writeACKpayload() in the procedure which reads a packet


	if(readReg(FIFO_STATUS) & FIFO_STATUS_TX_FULL) {
		return -1; //error: FIFO full
	}

	nssLow();

	uint8_t spi_command = NRF_W_ACK_PAYLOAD | pipeNo;
	//activate spi command
	if(HAL_SPI_Transmit(spiHandle, &spi_command,1, 100) != HAL_OK)
		return -1; //HAL/SPI error

	//transmit values for spi command (send payload to nRF module)
	if(HAL_SPI_Transmit(spiHandle, payloadBytes, payload_length, 100) != HAL_OK)
		return -1; //HAL/SPI error

	nssHigh();

	return 0; //success
}

And in case you ask, here is the prototype for the SPI_Transmit function:

HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout)

Basically: send the content of a ByteArray with the specified length over SPI.

I believe that I'm doing something wrong with setting the pins of the module correctly when I issue my commands.
The pins like CSN (NSS) or CE. All the other SPI pins are handled by the SPI library.

But for a comparison, here is a working function for reading a register:

uint8_t readReg(uint8_t reg){
	if(reg > 0x1D){
		return -2; //error: invalid register
	}

	//commands can only be given after a falling edge of the nss pin
	//see figure 23 of datasheet
	nssLow();

	uint8_t sendData = reg; //R_REGISTER = 000A AAAA -> AAAAA = 5 bit register address
	uint8_t receiveData;
	//command: read from register reg
	if(HAL_SPI_Transmit(spiHandle, &sendData, 1, 100) != HAL_OK)
		return 0xff; //error: SPI problem

	//read data from the register
	if(HAL_SPI_Receive(spiHandle, &receiveData, 1, 100) != HAL_OK)
		return 0xff; //error: SPI problem

	nssHigh();

	return receiveData;
}

(I actually notice right now that I'm returning a negative value in an unsigned int function. Oops..)

So, I hope there are any ideas on this. I'm really stuck here. If there are any questions about my library or test case scenarios, then let me know.
I will try whatever you suggest.
Just a side note: I was mostly working with the library in an abstracted way. That means: I never had to actually implement SPI commands myself until a later stage of the project. And when I had to, I was rather confused about which Pin I need to set when. I never really checked the state diagram properly for that and I'm somewhat confused about the purpose and the difference between the CSN and the CE pin.

Best regards,

Ulf

Parents
  • I see that you have disabled automatic re-transmits by setting ARC to 0, but could you try increasing the ard value? Not sure what you are currently using. Also is this 250kbps 1mbps or 2 mbps mode?

  • Thank You for your reply.

    I'm using 2 Mbps mode. And I'm actually using the maximum value for ARD (0b1111) even though I have ARC on 0.

    After doing some more tests, I have to admit that the original title is partly misleading: it is not an issue with DPL, nor is it directly connected to reading a payload. In fact: every command gives me wrong values when it should return any value. (Register Read or Payload Read). But only when the device ends up in this broken state after using my writeAckPayload routine.

    If I never use this command or if I limit my ack Payload to a single Byte, then everything is fine.

  • When are you calling the writeAckPayload routine? You have to write the payload prior to receiving a packet (or after depending on how you look at it). The state machine could get into trouble if you call the writeAckPayload routine in the rx interrupt.

Reply Children
  • Oh, that could very well be the issue.
    I'm basically waiting for the interrupt pin to announce a new interrupt, then I would usually read the status register to check which interrupt fired; then I read the dynamic payload length, then issue the payload read command. I would write RX_DR to the status register to clear the flag. Then I would re-enter my Interrupt routine when TX_DS is up. At that point I would clear the TX_DS flag on the status register and then issue the writeAckPayload routine. 

    Could you give me some guidance how I would put the module into a state where writing the ACK Payload is safe? I guess it's something about the CE pin? Or do I need to write to a register to change the state? 

  • How often do you receive packets? Wait until the PRX finish acking the received packet before you write a new payload to the tx_fifo. Or alternatively if you make sure there is always at least on packet in the fifo you can write a new one during the interrupt as well.

  • After porting the code to the new hardware design, which uses an STM32F4xx microcontroller, the issue was resolved. The previous board had a very similar design with an STM32F3xx microcontroller using the same toolchain. Unfortunately I cannot say what the cause of the issue was and how it was resolved, but I'm glad it is gone now.

    Thank you for your support! 

Related