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