EEVblog Electronics Community Forum

Electronics => Projects, Designs, and Technical Stuff => Topic started by: ulao on November 20, 2024, 04:38:46 pm

Title: looking for help with an old nrf2410l+ chip
Post by: ulao on November 20, 2024, 04:38:46 pm
Will try to keep this post lite at first, and gladly post what is asked for if needed.

I have developed a board with an nRF24L01+ ( no its not arduino) , it works rather well, and basic idea is it sends a command, and returns the pre set ack payload. All works well and does its job but it seems the ack reply is messed up now.

The board has gone thru many iterations as has the module. The module will not work if its a knock off, they just do not work right at all. so I'm pretty confident its legitimate. I also use an ipex antenna as I had a lot of issues with the standard PCB antenna. But happy to get details.

I was seeing issues with dropped packets so I opened up the project and traced out the logic, it lead me to the send and ack code.  I basically send a command, and I wait for the ack. This seemed to be bugged.

What I see happening here is the ack can be 8 or 30 ms. Also once I receive the ack, the buffer is empty.  That just seems wrong, so maybe my config is jacked up? I'm not sure what help to ask for and happy to share what i needed. I used to think I was pretty good with this chip, maybe I'm wrong.

example
Once I send data I do see it received on my receiver.
I also see the status 0x40 flip buts its long after the data was received.
and when I read the buffer after my ack was received, its empty checking with  (sendCommand(0xff) & 0x01);


anyone willing to help? Can show code or scope activity.










Title: Re: looking for help with an old nrf2410l+ chip
Post by: MarkF on November 20, 2024, 05:17:57 pm
I use the nRF24L01+ transceivers for my wireless throttles on my HO model railroad layout.  They have been a bit of a struggle to get working.  I use both a broadcast mode (i.e. no ACK) to send data to all nRF's and an 'ACK' mode to get data back.  My MCU is running at 5V while the nRF is 3.3V.  I thought I had some bad nRF's but found out that I needed a capacitor on the power right at the nRF.

The nRF has a three level deep FIFO on the receive and transmit.  Once you read the data, it's gone.

I don't remember actually setting the scope up to see the delay between transmit and receive.  I do know the transmit handshake with ACK and re-transmit fails more than you would expect.

Before I spend time digging into how mine are setup, a little description of what you have would be helpful.
  - brief hardware layout:  MCU to nRF connection and power voltages.
  - nRF configuration of both xmt and rcv sides.
Title: Re: looking for help with an old nrf2410l+ chip
Post by: ulao on November 20, 2024, 08:08:51 pm
I do appreciate you helping out. I connect to the module is a normal way but the nrf module its self is a ready made board. I attached the MCU part of the schematic. For all I know it maybe the module that is the issue. Its the only one I know of that will fit on my PCB. I can ask the part supplier for details.

In my case the board and nrf run from 3.3v but 5v is available on the board for other components.


config ( not sure comments are still matching config as I played a bit with it) both chips use the same code, just have a flag to set sender or receiver.
Code: [Select]
 
enable();
sendCommand(0x20);sendCommand(0x0A | receiver);//Config : 00001010 enalbe crc,  crc 1 byte, power up, ptx/rx
disable();
pulse();
_delay_ms(2); //power up delay

//enable();
//from what I can tell the default values for 21 is all on, and for 22 its 1 and 0, so no need to change any of this.
//sendCommand(0x22);sendCommand(0x33);//enable 0 and 1 RX pipes
//disable();

//address for 5 (0011) bytes
enable();
sendCommand(0x23);sendCommand(2);
disable();

enable();
sendCommand(0x24);sendCommand(0x0f);//no waiting, just keep trying.
disable();

//set channel from dips
unsigned char channelFromDip = ( (~PIND) & 0x1e)<<3; 
channelFromDip = __builtin_avr_insert_bits (0x01234567,channelFromDip,0);//reverse
enable();
sendCommand(0x25);sendCommand(channelFromDip);
disable();

//18db and  rate of 2mb speed
enable();
sendCommand(0x26);sendCommand(0x0E);//data sheet has an errors but I think this is right
disable();

enable();//rx address
sendCommand(0x2A);sendCommand(0xe7);sendCommand(0xe7);sendCommand(0xe7);sendCommand(0xe7);sendCommand(0xe7);
disable();
 
//must match to auto ack
enable();//tx address
sendCommand(0x30);sendCommand(0xe7);sendCommand(0xe7);sendCommand(0xe7);sendCommand(0xe7);sendCommand(0xe7);
disable();

//dynamic length for 0 and 1  pipes
enable();
sendCommand(0x3C);sendCommand(3);
disable();

//enable ack payload and dynamic
enable();
sendCommand(0x3D);sendCommand(7);
disable();


That actual code for the chips is huge, so I can just attach it all but happy to zoom in to the parts of concern.

note the comment about data sheet error. ( image bits)

Title: Re: looking for help with an old nrf2410l+ chip
Post by: MarkF on November 20, 2024, 09:33:47 pm
This is going to take me a little time to get back up to speed with this.  It's been a couple years.

Your overall concern is the time it takes to make a transfer?
Otherwise, everything is working?

Maybe I missed something...
I don't see the nRF.  Is the power supply for the ATMEGA32U2 is 5V and 3.3V for the nRF?
Are you using a nRF module or the bare chip?

You're doing 2Mbps transfers? 
What is your payload size?
Number of re-tries and re-transmit delay?

I haven't taken time to wade into your code yet.
Have you counted how many re-tries the nRF is doing?


I use Microchip PIC18's for my MCUs.  I can show you some code later if that will help?
My master nRF transmits 24 bytes and receives 8 bytes.  And configured for 1Mbps

I've attached a photo of my nRF and a screencapture of my xmt nRF configuration:
Title: Re: looking for help with an old nrf2410l+ chip
Post by: ulao on November 20, 2024, 10:26:25 pm
its all good, no rush, by virtual of question I may solve this. BTW: I made some small progress. I found the cause of the random ack, but the latency remains. Now that its a bit more smooth, I clearly see the ack is horribly slow.

see new image
There is a  trace that looks like
|||  |
in yellow. That the ack then the start of a new command. Then 12 ms apart. So:
ack from command... new command..................12 ms ...................ack from command... new command.


Your overall concern is the time it takes to make a transfer?
Otherwise, everything is working?


Yes, now that the variable length issue is gone, absolutely.  I can live with 4ms, but I read ack is like 250 us? either way, mine is 12ms..


You're doing 2Mbps transfers?  That is  my goal. 


What is your payload size?
sent 1 byte
get 13
dynamic payload



Number of re-tries and re-transmit delay?
Maybe my issue I never could figure this crap out. This is how I do it. Sorry this maybe a bit to unpack, see comments for some explanation. buts its just sending and resetting the transfer limit. It woudl be nice if the built in retry worked but it seem not to. I set it to wait 250us and 15 tries.

I send a command, then a NOP command to retrieve the data via ack.

example
send 1 no ack
send 0x1a with ACK to get payload.


the send function gets a ack or not. If there is no ack, it stops here. I do not intended to stack buffers, just get the data I meant to.

Code: [Select]
 
clearALLFIFO();/
flush(1); //we do not care about any other command, always send what we intend to.

enable();
sendCommand( (ACK)?0xA0:0xB0);
unsigned char status = sendCommand(command);
disable();
pulse();

if (!ACK)  return 1;// nothing more to do


if there is an ack, it does the same as above but then the code below. This is how I deal with retry. I will try for up to 7ms.



Code: [Select]
 
//wait for ACK.

while(count < 6500 )
{
count++;
if (status & 0x20)
{
return 1;  //command sent and ack received. return good.
}
else if (status & 0x10) //tries used up
{
clearTransLimit();//will give us up to 17 of these before max time is done. 7 MS or so.
}
status=NOP(); 
}
return 0;// we failed


Have you counted how many re-tries the nRF is doing? No because I never could make it work. Maybe I should look in to it a bit more?

I do not think looking at code will help me because I mostly did that to make this. And the config in my cause is mostly specific.  I will not hold you to anything, but form example, is not going to help as I find so many good examples on this chip.   If you feel like taking a dive let me know, if not, hey no harm in offering to help.








Title: Re: looking for help with an old nrf2410l+ chip
Post by: ulao on November 20, 2024, 10:41:00 pm
oh about the data sheet bug

you set to 06
so
‘01’ – 2Mbps ( data sheet says this is 1 bit not 2 ?)
10' – -6dBm

I tried
‘01’ – 2Mbps ( data sheet says this is 1 bit not 2 ?)
11' – -0dBm

is 18 better for longer range, or 0? Still confused on that one. I tired 18 and 0, but neither changed the result.


but Data sheets says

0 bit is not used
1,2 are db
3 is Mbps buyt it needs two bits
4 is PPL lock.

math is not right there?


Title: Re: looking for help with an old nrf2410l+ chip
Post by: MarkF on November 20, 2024, 11:18:12 pm
It might be faster to set the payload width to the max size you expect instead of using a variable length and have the nRF spend extra time figuring out how big the payload is.  I haven't tried this.  Also at 2 Mbps, there are some config settings that are NOT valid as far as ACK timing goes.  I don't think there is any limitations for slower data rates (I need to double check this).


There is no datasheet bug.
The data rate is two bits.  The two just are not adjacent to each other (bits 3 and 5).  Odd I know.

The RF power is adjustable from 0 attenuation to -18dBm attenuation.  The more negative the lower the power level.  For max power, set it to 0dBm.
Title: Re: looking for help with an old nrf2410l+ chip
Post by: MarkF on November 20, 2024, 11:34:20 pm
He's my write payload function with a fixed length.
It shows how I check for transmission failure and re-tries.

Code: [Select]
uint8_t FailCnt=0;
uint8_t RxReady=0, TxOkay=0, TxFail=0;
uint8_t rfStatus[2]  = {nRF_RD_REGISTER|nRF_STATUS, 0};
uint8_t rfObserve[2] = {nRF_RD_REGISTER|nRF_OBSERVE_TX, 0};


void readPayload(void)
{
   uint8_t rfPayload[25] = {nRF_RD_RX_PL};
   nrfRead(rfPayload, 25);
}

void writePayload(void)
{
   uint8_t timeout;

   // Set PwrUp and PTX
   uint8_t rfXmtMode[2] = {nRF_WR_REGISTER|nRF_CONFIG, nRF_CRC1|nRF_PwrUp|nRF_PTX};
   nrfWrite(rfXmtMode, 2);
   delay_us(150);

   // Fill payload
   uint8_t rfPayload[9]={nRF_WR_TX_PL};
   rfPayload[1] = vSeq;       // Sequence number
   rfPayload[2] = mItem;      // Item selected
   rfPayload[3] = vCab;       // CAB number
   rfPayload[4] = vSpd;       // CAB speed
   rfPayload[5] = vRly;       // Relay number
   rfPayload[6] = vStt;       // Relay state
   rfPayload[7] = FailCnt;    // Fail count
   rfPayload[8] = vEmrg | vStop | vCnfltCab | vCnfltRly;    // eStop + Cab_conflict + Relay_conflict
   // Increment message sequence number
   if (++vSeq > 99) vSeq=0;
   // Send payload
   nrfWrite(rfPayload, 9);

   // Strobe CE
   nRF_CEpin=1;
   delay_us(16);
   nRF_CEpin=0;

   // Wait for complete
   timeout=100;
   do {
      delay_ms(1);
      rfStatus[1] = nrfRead(rfObserve, 2);
   } while ( (rfStatus[1] & 0x30) == 0 && --timeout > 0 );

   // Save TX_DS and MAX_RT states
   TxOkay  = (rfStatus[1] >> 5) & 0x01;
   TxFail  = (rfStatus[1] >> 4) & 0x01;

   if (TxFail) {
      uint8_t rfFlush = {nRF_FLUSH_TX};
      nrfWrite(&rfFlush, 1);
      // debug code
      if (++FailCnt > 99) FailCnt=0;
   }
}

Title: Re: looking for help with an old nrf2410l+ chip
Post by: ulao on November 21, 2024, 12:12:32 am
sure its the same concept.

this just waits for sent flag or tries used up flag.

 while ( (rfStatus[1] & 0x30) == 0 && --timeout > 0 );

not really much different then what I do, and yes these flags do work.  Just painfully slow for me.
Title: Re: looking for help with an old nrf2410l+ chip
Post by: ulao on November 21, 2024, 12:14:12 am
ok, I see that now, high and low byte,.


1MB made it 2ms faster so from 12 to 10 LOL

you didnt need "Enables Payload with ACK" in feature byte? that byte seem redundant, I do not get what it is for.

"n order to enable Auto Acknowledgement with payload the EN_ACK_PAY bit in the FEATURE register
must be set."


FYI: found this on Nordic board.

Typically transmitting 32byte data with 4byte address + 2byte crc @ 2Mbps will take: ((32+4+2)bytes*8bit/byte)/2Mbps= 152us.

I'm no where near that.
Title: Re: looking for help with an old nrf2410l+ chip
Post by: MarkF on November 21, 2024, 01:14:04 am

you didnt need "Enables Payload with ACK" in feature byte? that byte seem redundant, I do not get what it is for.

"n order to enable Auto Acknowledgement with payload the EN_ACK_PAY bit in the FEATURE register
must be set."


I'm confused.  Aren't these both the same bit in the FEATURE register?
Are you referring to the "EN_AA Enhanced ShockBurst™" register (addr 01)?
Title: Re: looking for help with an old nrf2410l+ chip
Post by: ulao on November 21, 2024, 03:03:35 am
I mean this ( see attached )

So I think I see my issue but can not explain it yet.

if I do this..

Code: [Select]
enable();
sendCommand( (ACK)?0xA0:0xB0);
unsigned char status = sendCommand(command);
disable();
pulse();
DDRD |= 1;
PORTD |= 0x01;
PORTD &= ~0x01;
while (! NOP() & 0x20 )//wait for this to send.
PORTD |= 0x01;
DDRD &= ~1;


its shows about 4us time took. but it appears the ack is also cleared? if I check the status after the 0x20 bit is no longer high? Something cleared it and it was not me.  I tested before the send and 20 was off. 4 us later it came on, then after its off again? I thought it woudl be high until I cleared it.  ( add image )

all nop does is this
Code: [Select]
unsigned char NOP( void )
{
enable();
unsigned char status=sendCommand(0xFF); //nop to see if data is ready.
disable();
return status; 
}


so it just wait for the next send to check the flag, no wonder it takes so damn long.

Also looks like I only configured 1 pipe address pair. but I enabled both pipe 0 and 1.  I kinda for get what pipes where for but I only need to send one set of data at a time.

   enable();//rx address
   sendCommand(0x2A);sendCommand(0xe7);sendCommand(0xe7);sendCommand(0xe7);sendCommand(0xe7);sendCommand(0xe7);
   disable();

   enable();//tx address - must match to auto ack
   sendCommand(0x30);sendCommand(0xe7);sendCommand(0xe7);sendCommand(0xe7);sendCommand(0xe7);sendCommand(0xe7);
   disable();

This part is a bit much but I'm only at 13 so I think I'm ok
If ACK packet payload is activated, ACK packets have dynamic payload lengths and the Dynamic Payload
Length feature should be enabled for pipe 0 on the PTX and PRX. This is to ensure that they receive the
ACK packets with payloads. If the ACK payload is more than 15 byte in 2Mbps mode the ARD must be
500μS or more, and if the ACK payload is more than 5 byte in 1Mbps mode the ARD must be 500μS or
more. In 250kbps mode (even when the payload is not in ACK) the ARD must be 500μS or more.


Title: Re: looking for help with an old nrf2410l+ chip
Post by: MarkF on November 21, 2024, 04:25:38 am
The last major update I did to speed up my code, I found that when you do clearing/cleanup greatly changes the status bits.  Maybe a little more of my processing will help...

Instead of doing a 'NOP' to get status send an 'OBSERVE_TX'.  You will be sending an extra byte but you get back more info for debugging.

nRF.c file
Code: [Select]
#define READ(s)                  \
   PIR1bits.SSPIF = 0;           \
   SSPBUF = 0;                   \
   while ( !PIR1bits.SSPIF ) {}  \
   s = SSPBUF

#define WRITE(c, s)              \
   PIR1bits.SSPIF = 0;           \
   SSPBUF = c;                   \
   while ( !PIR1bits.SSPIF ) {}  \
   s = SSPBUF


//------------------------------------------------------------
uint8_t nrfRead(uint8_t *buf, uint8_t num_bytes)
{
   uint8_t stat;

   nRF_CSNpin = 0;
   WRITE(buf[0], stat);
   for (int i=num_bytes-1;i>0;i--) {
      READ(buf[i]);
   }
   nRF_CSNpin = 1;

   return(stat);
}

//------------------------------------------------------------
uint8_t nrfWrite(uint8_t *buf, uint8_t num_bytes)
{
   uint8_t stat;
   uint8_t tmp;

   nRF_CSNpin = 0;
   WRITE(buf[0], stat);
   for (int i=num_bytes-1;i>0;i--) {
      WRITE(buf[i], tmp);
   }
   nRF_CSNpin = 1;

   return(stat);
}

//------------------------------------------------------------
void nrfPowerUp(void)
{
   uint8_t rfConfig[2];

   rfConfig[0] = nRF_RD_REGISTER|nRF_CONFIG;
   nrfRead(rfConfig, 2);

   rfConfig[0] = nRF_WR_REGISTER|nRF_CONFIG;
   rfConfig[1] = rfConfig[1] | nRF_PwrUp;
   nrfWrite(rfConfig, 2);
}

//------------------------------------------------------------
void nrfPowerDn(void)
{
   uint8_t rfConfig[2];

   rfConfig[0] = nRF_RD_REGISTER|nRF_CONFIG;
   nrfRead(rfConfig, 2);

   rfConfig[0] = nRF_WR_REGISTER|nRF_CONFIG;
   rfConfig[1] = rfConfig[1] & (~nRF_PwrUp);
   nrfWrite(rfConfig, 2);
}

//------------------------------------------------------------
void nrfStartListening(void)
{
   // Set PwrUp and PRX
   uint8_t rfRcvMode[2] = {nRF_WR_REGISTER|nRF_CONFIG, nRF_CRC1|nRF_PwrUp|nRF_PRX};
   nrfWrite(rfRcvMode, 2);

   // Clear RX_DR, TX_DS, MAX_RT bits
   uint8_t rfClear[2] = {nRF_WR_REGISTER|nRF_STATUS, nRF_RX_DR|nRF_TX_DS|nRF_MAX_RT};
   nrfWrite(rfClear, 2);

   // Set CE high
   nRF_CEpin = 1;
   delay_us(130);
}

//------------------------------------------------------------
void nrfStopListening(void)
{
   // Set CE low
   nRF_CEpin = 0;

   // Flush TX FIFO
   uint8_t rfFlush = {nRF_FLUSH_TX};
   nrfWrite(&rfFlush, 1);
}

//------------------------------------------------------------
uint8_t nrfStatus(void)
{
   uint8_t rfStatus[2] = {nRF_RD_REGISTER|nRF_STATUS, 0};
   nrfRead(rfStatus, 2);

   return( rfStatus[1] );
}

//------------------------------------------------------------
uint8_t nrfFIFOstatus(void)
{
   uint8_t rfFIFO[2] = {nRF_RD_REGISTER|nRF_FIFO_STATUS, 0};
   nrfRead(rfFIFO, 2);

   return( rfFIFO[1] );
}

//------------------------------------------------------------
void nrfSetup(void)
{
   nRF_IRQtris = 1;
   nRF_CSNtris = 0;
   nRF_CEtris = 0;

   // Initialize nRF24L01+
   nRF_CSNpin = 1;
   nRF_CEpin = 0;
   delay_ms(5);

   uint8_t rfBuf[] = {
      // Configuration
      nRF_WR_REGISTER|nRF_CONFIG, nRF_CRC1|nRF_PwrDn|nRF_PRX,
      // Auto Ack Enable
      nRF_WR_REGISTER|nRF_EN_AA, 0x00,
      // RX Pipe Enable
      nRF_WR_REGISTER|nRF_EN_RXADDR, 0x03,
      // Address Width
      nRF_WR_REGISTER|nRF_SETUP_AW, nRF_AW5,
      // Auto Retransmit (ARD=500us, ARC=3 retries)
      nRF_WR_REGISTER|nRF_SETUP_RETR, 0x13,
      // RF Channel (2484 MHz, 802.11 ch.14)
      nRF_WR_REGISTER|nRF_RF_CH, 84,
      // RF Setup (1Mbps data rate, 0dBm output power)
      nRF_WR_REGISTER|nRF_RF_SETUP, nRF_1Mbps|nRF_PWR0,
      // RX Address - Pipe 0-5
      nRF_WR_REGISTER|nRF_RX_ADDR_P0, 'N', 'o', 'd', 'e', 'A',
      nRF_WR_REGISTER|nRF_RX_ADDR_P1, 'N', 'o', 'd', 'e', 'B',
      nRF_WR_REGISTER|nRF_RX_ADDR_P2, 'C',
      nRF_WR_REGISTER|nRF_RX_ADDR_P3, 'D',
      nRF_WR_REGISTER|nRF_RX_ADDR_P4, 'E',
      nRF_WR_REGISTER|nRF_RX_ADDR_P5, 'F',
      // TX Address
      nRF_WR_REGISTER|nRF_TX_ADDR, 'N', 'o', 'd', 'e', 'A',
      // RX Payload Width - Pipe 0-5 (default to 8 bytes)
      nRF_WR_REGISTER|nRF_RX_PW_P0, 8,
      nRF_WR_REGISTER|nRF_RX_PW_P1, 8,
      // Dynamic Payload Enable
      nRF_WR_REGISTER|nRF_DYNPD, 0,
      // Feature Setup
      nRF_WR_REGISTER|nRF_FEATURE, 0,
      // Status (Reset interrupt bits)
      nRF_WR_REGISTER|nRF_STATUS, nRF_RX_DR|nRF_TX_DS|nRF_MAX_RT,
      // Flush FIFOs
      nRF_FLUSH_TX,
      nRF_FLUSH_RX
   };

   nrfWrite(&rfBuf[ 0],2);    // Configuration
   nrfWrite(&rfBuf[ 2],2);    // Auto Ack Enable
   nrfWrite(&rfBuf[ 4],2);    // RX Pipe Enable
   nrfWrite(&rfBuf[ 6],2);    // Address Width
   nrfWrite(&rfBuf[ 8],2);    // Auto Retransmit
   nrfWrite(&rfBuf[10],2);    // RF Channel
   nrfWrite(&rfBuf[12],2);    // RF Setup
   nrfWrite(&rfBuf[14],6);    // RX Address - Pipe 0
   nrfWrite(&rfBuf[20],6);    // RX Address - Pipe 1
   nrfWrite(&rfBuf[26],2);    // RX Address - Pipe 2
   nrfWrite(&rfBuf[28],2);    // RX Address - Pipe 3
   nrfWrite(&rfBuf[30],2);    // RX Address - Pipe 4
   nrfWrite(&rfBuf[32],2);    // RX Address - Pipe 5
   nrfWrite(&rfBuf[34],6);    // TX Address
   nrfWrite(&rfBuf[40],2);    // RX Payload Width - Pipe 0
   nrfWrite(&rfBuf[42],2);    // RX Address - Pipe 1
   nrfWrite(&rfBuf[44],2);    // RX Payload Width - Pipe 1
   nrfWrite(&rfBuf[46],2);    // Dynamic Payload Enable
   nrfWrite(&rfBuf[48],2);    // Feature Setup
   nrfWrite(&rfBuf[50],2);    // Status
   nrfWrite(&rfBuf[52],1);    // Flush TX_FIFOs
   nrfWrite(&rfBuf[51],1);    // Flush RX_FIFOs
}


main.c file (abridged) (readPayload and writePayload where shown earlier)
Code: [Select]
// Register read globals
uint8_t rfAddr[6]    = {nRF_RD_REGISTER|nRF_RX_ADDR_P0, 0, 0, 0, 0, 0};
uint8_t rfAux[6]     = {nRF_RD_REGISTER|nRF_RX_ADDR_P1, 0, 0, 0, 0, 0};
uint8_t rfCh[2]      = {nRF_RD_REGISTER|nRF_RF_CH, 0};
uint8_t rfStatus[2]  = {nRF_RD_REGISTER|nRF_STATUS, 0};
uint8_t rfObserve[2] = {nRF_RD_REGISTER|nRF_OBSERVE_TX, 0};
uint8_t rfFIFO[2]    = {nRF_RD_REGISTER|nRF_FIFO_STATUS, 0};

void main(void)
{
   nrfSetup();
   nrfStartListening();

   // Main loop
   while (1) {

         // Read nRF status and FIFO status
         rfStatus[1] = nrfRead(rfFIFO, 2);

         // Save RX_DR state
         RxReady = (rfStatus[1] >> 6) & 0x01;

         // Read available payloads                         <--<--<--<--<-- Payload <--<--<--<--<--
         cnt = 10;
         while ( --cnt > 0 && (rfFIFO[1] & 0x03) != 1 )
         {
            readPayload();                      // Get payload
            rfStatus[1] = nrfRead(rfFIFO, 2);   // Get FIFO status
         }

         // Write payload                                   -->-->-->-->--> Payload -->-->-->-->-->
         // If a change or 1 Hz, then report
         if ( forceXmt==1 || frame==0 )
         {
            forceXmt = 0;
            nrfStopListening();
            writePayload();                     // Send payload
            nrfStartListening();
         }

   }
}
Title: Re: looking for help with an old nrf2410l+ chip
Post by: MarkF on November 21, 2024, 04:31:04 am
This part is a bit much but I'm only at 13 so I think I'm ok
If ACK packet payload is activated, ACK packets have dynamic payload lengths and the Dynamic Payload
Length feature should be enabled for pipe 0 on the PTX and PRX. This is to ensure that they receive the
ACK packets with payloads. If the ACK payload is more than 15 byte in 2Mbps mode the ARD must be
500μS or more, and if the ACK payload is more than 5 byte in 1Mbps mode the ARD must be 500μS or
more. In 250kbps mode (even when the payload is not in ACK) the ARD must be 500μS or more.


The default value for ARD is 250uS.
I didn't see where you set it.  If you left it at the default, it is not long enough.

Is 13 your payload width?
Title: Re: looking for help with an old nrf2410l+ chip
Post by: ulao on November 21, 2024, 04:50:11 am
I set 250, and 15 retries.

Yes 12 is my width.


so to reset the PLOS_CNT , you wire the channel back to RF_CH ?
Title: Re: looking for help with an old nrf2410l+ chip
Post by: MarkF on November 21, 2024, 05:16:10 pm
To clear the PLOS_CNT, just re-write the RF_CH number.  However, I wouldn't do this all the time.  Not sure if writing the RF_CH does any housekeeping that may cause you to miss something.  I believe the key piece of info is how many retries it took.  You can externally keep track of lost packets just from the STATUS register bits.

I'm having a little trouble following what you do during transmission.  Can you give me a little long code snippet for the whole sequence?  What does enable() and disable() functions do?

If I understand your scope captures, it's doing two NOPs before seeing the ACK?  To speed things up, you might add a small delay before sending the first NOP.  With a little experimentation, you should be able to determine the minimum time you need to wait for the transmission to complete.  You could catch the completion on the first NOP and finish earlier. 

You might be better off by setting the ARD to 500us in order to give whatever caused the retry more time to clear.  Be it RF noise or something else.  I believe doing 5 retries every 500us would be better then 10 retires every 250us.  Not putting a bunch of RF energy out there.  In my case, I have three nRF transceivers all trying to talk at once to the master nRF transceiver.
Title: Re: looking for help with an old nrf2410l+ chip
Post by: ulao on November 21, 2024, 11:27:46 pm
In this case.

send 1 byte
nop (wait )
send 1 byte
nop (wait )

reason for that is for how I send data, but I guess my point was why ack is set in 4us, and then the bit self clears.


its hard to put all code in here but most of these functions should be enough.

the first send is this
 
Code: [Select]
	clearALLFIFO();//I guess just to be sure. 	
flush(1); //we do not care about any other command, always send what we intend to.

enable();
sendCommand( (ACK)?0xA0:0xB0);
unsigned char status = sendCommand(command);
disable();
pulse();


then I do what wait.

while (! NOP() & 0x20 ) {}//wait for this to send.


for testing I just repeat that for now.

here are the functions

Code: [Select]

void clearALLFIFO()
{
enable();
sendCommand(0x27);  sendCommand(0x70);//allow more reaing writing.
disable();
pulse();
}

void flush(unsigned char mode)
{
if ( mode & 1)
{
enable();
sendCommand(0xE1);//flush TX
disable();
}
if (mode & 2)
{
enable();
sendCommand(0xE2);//flush RX
disable();
}
pulse();
}
void pulse (void)
{
if (_receiver_) return;
ATT_ON();_delay_us(10); ATT_OFF();//minium is 10us
}
#define enable()  do { PORTB &= ~0x01; } while(0)
#define disable()  do { PORTB |= 0x01; } while(0)
unsigned char sendCommand(unsigned char data)
{
SPDR = data;
while(!(SPSR & (1<<SPIF)));
return SPDR;
}


Title: Re: looking for help with an old nrf2410l+ chip
Post by: MarkF on November 22, 2024, 01:55:42 am
So, sendCommand() function is the SPI in/out function,
the enable() sets nRF 'chip select', and disable() clears nRF 'chip select'?

If I'm not mistaken (code is not clear), the sendCommand() function will ONLY return status when you send ACK to start the transfer.  Otherwise, you will actually receive the payload data???  Not sure how SPI registers work on the ATMEGA.   :-//


Let me put this all together and try to summarize what you're doing for my understanding . . .
Code: [Select]
#define nRF_WR_REGISTER       0x20
#define nRF_WR_TX_PL          0xA0
#define nRF_W_TX_PL_NOACK     0xB0
#define nRF_FLUSH_TX          0xE1
#define nRF_FLUSH_RX          0xE2
#define nRF_NOP               0xFF
#define nRF_STATUS            0x07

unsigned char sendCommand(unsigned char data)
{
   // set CE pin
   PORTB &= ~0x01;

   // do SPI transfer
   SPDR = data;
   while ( !(SPSR & (1<<SPIF)) );

   // clear CE pin
   PORTB |= 0x01;

   return SPDR;
}

void pulse(void)
{
   if ( _receiver_ ) return;
   ATT_ON();
   _delay_us(10); //minium is 10us
   ATT_OFF();
}

void clearALLFIFO(void)
{
   // Clear RX_DR, TX_DS, and MAX_RT in the STATUS register
   sendCommand( nRF_WR_REGISTER | nRF_STATUS );
   sendCommand( 0x70 );
   pulse();
}

void flush(unsigned char mode)
{
   // flush TX FIFO
   if (mode & 1) sendCommand( nRF_FLUSH_TX );

   // flush RX FIFO
   if (mode & 2) sendCommand( nRF_FLUSH_RX );

   pulse();
}

unsigned char NOP(void)
{
   // send NOP to read status register
   unsigned char status = sendCommand( nRF_NOP );   // see if data is ready
   return status;
}

//========================================

void main(void)
{
   while(1) {

      clearALLFIFO();//I guess just to be sure.
      flush(1); //we do not care about any other command, always send what we intend to.

      sendCommand( (ACK) ? W_TX_PAYLOAD : W_TX_PAYLOAD_NOACK );
      unsigned char status = sendCommand(command);
      pulse();

      // wait for this to send
      while (! NOP() & 0x20 );

   }
}

Title: Re: looking for help with an old nrf2410l+ chip
Post by: ulao on November 22, 2024, 07:15:38 pm
yes
databack = sendCommand( dataset ) 

disable and enable are because the SPI could not do it, itself. I think normally the SPI uses SS to do this. So the MCU does it in this case.


will ONLY return status when you send ACK to start the transfer.
Unsure what you mean, the SPI is bi directional, so it receives and sends at the same time. The first byte received is status  since it can't possibly know what was sent in. so when you see

status=sendCommand(0xFF);

 means what's in the status, not get status, since it cant know that. Otherwise we'd have to do

sendCommand(0x17);tell nrf to get status
status=sendCommand(0xFF);NOP, to read returned byte.

At least I think that is how it works, I do not do many reads. Except for the obvious like data.
sendCommand(0x61);//read data
returnNrf2Payload[d...]=sendCommand(0x00);


 But that does bring up a good point how do I read OBSERVE_TX ?


sendCommand(0x18);tell nrf to get OBSERVE_TX
OBSERVE_TX =sendCommand(0xFF);NOP, to read returned byte.

?






Title: Re: looking for help with an old nrf2410l+ chip
Post by: MarkF on November 22, 2024, 10:14:32 pm
I think you're a little confused on how SPI works.
Title: Re: looking for help with an old nrf2410l+ chip
Post by: ulao on November 22, 2024, 11:31:01 pm
What is received on that first SPI transfer is specific to the device you're communicating with.  The nRF sends its status.  Not all devices will do that.

Yeah I got that part. I do indeed not work with SPI hardware much. Normally I just hook up the pins, but the way it sends commands makes sense to me. Either way, I know this is working because I  see it responds and does what it should.

3) I think you just spamming until you see the data, am I wrong?

 rfStatus[1] = nrfRead(rfObserve, 2);
 rfStatus[1] = nrfRead(rfObserve, 2);
 rfStatus[1] = nrfRead(rfObserve, 2);
 rfStatus[1] = nrfRead(rfObserve, 2);
 rfStatus[1] = nrfRead(rfObserve, 2);
 rfStatus[1] = nrfRead(rfObserve, 2);
 rfStatus[1] = nrfRead(rfObserve, 2);  //Ok I saw the bit, done.

when I developed it I used an analyzer to see the data, it all worked quite well. I can always put it back on but its hard to get at the pins now that its on a PCB.

but it was simple
 nrfRead(rfObserve, 2);//didnt look up what your , was bit this sends a byte and gets back the status byte
 nrfRead(rfObserve, 2);//this reseeds the same byte but the SPI returns the rfObserve.

my idea should do the same thing.
 nrfRead(rfObserve, 2);
 nrfRead(0xff, 2);

At least its worked like this up to this point. But I guess I can figure out a way to put the analyzer back on.




4), that is weird, sounds like this chip is buggy?


--------------------------------------------------------
Hey thx for all the help up to this point, it definitely helped for understanding.  I have to leave for a vacation, and I will return. I may shoot you a few specific questions, but I still need to get answers to my unknowns, Its how I learn best. I contacted Nordic, I have a friend there, he is going to try to get someone to help ( since support is done).  I just need these unknown answers to get any work done.






Title: Re: looking for help with an old nrf2410l+ chip
Post by: MarkF on November 23, 2024, 12:28:37 am
I'm suggesting you make your SPI function(s) more robust.  You can configure your function arguments as needed.  I would always insure you returned STATUS and have arguments for the "command" and any additional information required like: byte count, a data buffer, etc.

You haven't shown me how you read the payload.  But, you should be able to make a single function call to get back the entire payload in a unsigned char string.  The same for requesting OBSERVE_TX to return the STATUS and the OBSERVE_TX via an argument.

I actually required two functions to accomplish my needs.

Quote
my idea should do the same thing.
 nrfRead(rfObserve, 2);
 nrfRead(0xff, 2);

You don't need two calls as the first one also returns the status just like a NOP.
Title: Re: looking for help with an old nrf2410l+ chip
Post by: MarkF on November 23, 2024, 06:45:28 am
I finally see what's been bothering me all along . . .

Your functions:

  enable();
  disable();
  sendCommand();

would be more descriptive of what's been done if they were called:

  turnChipSelectOn();
  turnChipSelectOff();
  doOneSPItransfer();

The sendCommand() function IS NOT sending a command to the nRF.
In reality it's only controlling the MCU hardware to transfer one byte and wait for completion. 
Your sendCommand() does not care if it's a nRF command or payload data or nRF status that is being transfered.

The overhead of pushing and popping info to implement a function call must be eating up a lot of MCU execution time.
Unless, the compiler optimized out the function call and put the code inline.


Therefore, you could replace your:

   NOP();

with:

   #define nRF_RD_REGISTER       0x00
   #define nRF_OBSERVE_TX        0x08

   status = sendCommand( nRF_RD_REGISTER|nRF_OBSERVE_TX );
   data   = sendCommand( 0 );
   plos_cnt = (data>>4) & 0x0f;
   arc_cnt = data & 0x0f;

Title: Re: looking for help with an old nrf2410l+ chip
Post by: mtwieg on November 23, 2024, 03:20:22 pm
~15 years ago I did a project based on the nrf24L01+. I was sending packets every 1ms (using the highest available bandwidth), and I relied on ACK packets being immediate and having very repeatable latency. I don't have the data in front of me, but it worked quite well (I can't recall what the ACK latency was, but its jitter was around 1us).

So I'm surprised you're seeing such enormous latency. I'll see if I can dig up my project report or code...
Title: Re: looking for help with an old nrf2410l+ chip
Post by: ulao on December 23, 2024, 10:25:07 pm
my issue was that sending a NOP command (0xff)  too often yielded random results. It looks like I have to wait a minimum of 25 us. So I'm assuming there is a max time in between commands. I'd like to know this spec, but I can not find it in the datasheet.

Quote
Therefore, you could replace your:

   NOP();

According to the data sheet a 0xff command is all you need?

NOP 1111 1111
No Operation. Might be used to read the STATUS register



so
enable();//turn chip on
status =   sendCommand( 0xff );
disable();//turn chip off


doing this fails
while ( NOP() & 0x20 ) {}

where this works
while ( NOP() & 0x20 ) { _delay_us(25)} //anything under 25 randomly  fails.