looking for help with an old nrf2410l+ chip - Page 1
Forum section: electronics / projects

author: ulao
author_rank: Contributor
timestamp: November 20, 2024, 04:38:46 pm
content: 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.exampleOnce 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.

author: MarkF
author_rank: Super Contributor
timestamp: November 20, 2024, 05:17:57 pm
content: 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.

author: ulao
author_rank: Contributor
timestamp: November 20, 2024, 08:08:51 pm
content: 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:enable();sendCommand(0x20);sendCommand(0x0A | receiver);//Config : 00001010 enalbe crc, crc 1 byte, power up, ptx/rxdisable();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) bytesenable();sendCommand(0x23);sendCommand(2);disable();enable();sendCommand(0x24);sendCommand(0x0f);//no waiting, just keep trying.disable();//set channel from dipsunsigned char channelFromDip = ( (~PIND) & 0x1e)<<3;channelFromDip = __builtin_avr_insert_bits (0x01234567,channelFromDip,0);//reverseenable();sendCommand(0x25);sendCommand(channelFromDip);disable();//18db and rate of 2mb speedenable();sendCommand(0x26);sendCommand(0x0E);//data sheet has an errors but I think this is rightdisable();enable();//rx addresssendCommand(0x2A);sendCommand(0xe7);sendCommand(0xe7);sendCommand(0xe7);sendCommand(0xe7);sendCommand(0xe7);disable();//must match to auto ackenable();//tx addresssendCommand(0x30);sendCommand(0xe7);sendCommand(0xe7);sendCommand(0xe7);sendCommand(0xe7);sendCommand(0xe7);disable();//dynamic length for 0 and 1 pipesenable();sendCommand(0x3C);sendCommand(3);disable();//enable ack payload and dynamicenable();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)

author: MarkF
author_rank: Super Contributor
timestamp: November 20, 2024, 09:33:47 pm
content: 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 1MbpsI've attached a photo of my nRF and a screencapture of my xmt nRF configuration:

author: ulao
author_rank: Contributor
timestamp: November 20, 2024, 10:26:25 pm
content: 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 imageThere 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 byteget 13dynamic payloadNumber 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.examplesend 1 no acksend 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: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 doif 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://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 failedHave 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.

author: ulao
author_rank: Contributor
timestamp: November 20, 2024, 10:41:00 pm
content: oh about the data sheet bugyou set to 06so‘01’ – 2Mbps ( data sheet says this is 1 bit not 2 ?)10' – -6dBmI tried‘01’ – 2Mbps ( data sheet says this is 1 bit not 2 ?)11' – -0dBmis 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 says0 bit is not used1,2 are db3 is Mbps buyt it needs two bits4 is PPL lock.math is not right there?

author: MarkF
author_rank: Super Contributor
timestamp: November 20, 2024, 11:18:12 pm
content: 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.

author: MarkF
author_rank: Super Contributor
timestamp: November 20, 2024, 11:34:20 pm
content: He's my write payload function with a fixed length.It shows how I check for transmission failure and re-tries.Code:uint8_t FailCnt=0;uint8_t RxReady=0, TxOkay=0, TxFail=0;uint8_t rfStatus = {nRF_RD_REGISTER|nRF_STATUS, 0};uint8_t rfObserve = {nRF_RD_REGISTER|nRF_OBSERVE_TX, 0};void readPayload(void){uint8_t rfPayload = {nRF_RD_RX_PL};nrfRead(rfPayload, 25);}void writePayload(void){uint8_t timeout;// Set PwrUp and PTXuint8_t rfXmtMode = {nRF_WR_REGISTER|nRF_CONFIG, nRF_CRC1|nRF_PwrUp|nRF_PTX};nrfWrite(rfXmtMode, 2);delay_us(150);// Fill payloaduint8_t rfPayload={nRF_WR_TX_PL};rfPayload = vSeq; // Sequence numberrfPayload = mItem; // Item selectedrfPayload = vCab; // CAB numberrfPayload = vSpd; // CAB speedrfPayload = vRly; // Relay numberrfPayload = vStt; // Relay staterfPayload = FailCnt; // Fail countrfPayload = vEmrg | vStop | vCnfltCab | vCnfltRly; // eStop + Cab_conflict + Relay_conflict// Increment message sequence numberif (++vSeq > 99) vSeq=0;// Send payloadnrfWrite(rfPayload, 9);// Strobe CEnRF_CEpin=1;delay_us(16);nRF_CEpin=0;// Wait for completetimeout=100;do {delay_ms(1);rfStatus = nrfRead(rfObserve, 2);} while ( (rfStatus & 0x30) == 0 && --timeout > 0 );// Save TX_DS and MAX_RT statesTxOkay = (rfStatus >> 5) & 0x01;TxFail = (rfStatus >> 4) & 0x01;if (TxFail) {uint8_t rfFlush = {nRF_FLUSH_TX};nrfWrite(&rfFlush, 1);// debug codeif (++FailCnt > 99) FailCnt=0;}}

author: ulao
author_rank: Contributor
timestamp: November 21, 2024, 12:12:32 am
content: sure its the same concept.this just waits for sent flag or tries used up flag.while ( (rfStatus & 0x30) == 0 && --timeout > 0 );not really much different then what I do, and yes these flags do work. Just painfully slow for me.

author: ulao
author_rank: Contributor
timestamp: November 21, 2024, 12:14:12 am
content: ok, I see that now, high and low byte,.1MB made it 2ms faster so from 12 to 10 LOLyou 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 registermust 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.

author: MarkF
author_rank: Super Contributor
timestamp: November 21, 2024, 01:14:04 am
content: Quote from: ulao on November 21, 2024, 12:14:12 amI'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)?

author: ulao
author_rank: Contributor
timestamp: November 21, 2024, 03:03:35 am
content: I mean this ( see attached )So I think I see my issue but can not explain it yet.if I do this..Code: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 thisCode: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 addresssendCommand(0x2A);sendCommand(0xe7);sendCommand(0xe7);sendCommand(0xe7);sendCommand(0xe7);sendCommand(0xe7);disable();enable();//tx address - must match to auto acksendCommand(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 okIf ACK packet payload is activated, ACK packets have dynamic payload lengths and the Dynamic PayloadLength feature should be enabled for pipe 0 on the PTX and PRX. This is to ensure that they receive theACK packets with payloads. If the ACK payload is more than 15 byte in 2Mbps mode the ARD must be500μS or more, and if the ACK payload is more than 5 byte in 1Mbps mode the ARD must be 500μS ormore. In 250kbps mode (even when the payload is not in ACK) the ARD must be 500μS or more.

author: MarkF
author_rank: Super Contributor
timestamp: November 21, 2024, 04:25:38 am
content: 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 fileCode:#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, stat);for (int i=num_bytes-1;i>0;i--) {READ(buf);}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, stat);for (int i=num_bytes-1;i>0;i--) {WRITE(buf, tmp);}nRF_CSNpin = 1;return(stat);}//------------------------------------------------------------void nrfPowerUp(void){uint8_t rfConfig;rfConfig = nRF_RD_REGISTER|nRF_CONFIG;nrfRead(rfConfig, 2);rfConfig = nRF_WR_REGISTER|nRF_CONFIG;rfConfig = rfConfig | nRF_PwrUp;nrfWrite(rfConfig, 2);}//------------------------------------------------------------void nrfPowerDn(void){uint8_t rfConfig;rfConfig = nRF_RD_REGISTER|nRF_CONFIG;nrfRead(rfConfig, 2);rfConfig = nRF_WR_REGISTER|nRF_CONFIG;rfConfig = rfConfig & (~nRF_PwrUp);nrfWrite(rfConfig, 2);}//------------------------------------------------------------void nrfStartListening(void){// Set PwrUp and PRXuint8_t rfRcvMode = {nRF_WR_REGISTER|nRF_CONFIG, nRF_CRC1|nRF_PwrUp|nRF_PRX};nrfWrite(rfRcvMode, 2);// Clear RX_DR, TX_DS, MAX_RT bitsuint8_t rfClear = {nRF_WR_REGISTER|nRF_STATUS, nRF_RX_DR|nRF_TX_DS|nRF_MAX_RT};nrfWrite(rfClear, 2);// Set CE highnRF_CEpin = 1;delay_us(130);}//------------------------------------------------------------void nrfStopListening(void){// Set CE lownRF_CEpin = 0;// Flush TX FIFOuint8_t rfFlush = {nRF_FLUSH_TX};nrfWrite(&rfFlush, 1);}//------------------------------------------------------------uint8_t nrfStatus(void){uint8_t rfStatus = {nRF_RD_REGISTER|nRF_STATUS, 0};nrfRead(rfStatus, 2);return( rfStatus );}//------------------------------------------------------------uint8_t nrfFIFOstatus(void){uint8_t rfFIFO = {nRF_RD_REGISTER|nRF_FIFO_STATUS, 0};nrfRead(rfFIFO, 2);return( rfFIFO );}//------------------------------------------------------------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[] = {// ConfigurationnRF_WR_REGISTER|nRF_CONFIG, nRF_CRC1|nRF_PwrDn|nRF_PRX,// Auto Ack EnablenRF_WR_REGISTER|nRF_EN_AA, 0x00,// RX Pipe EnablenRF_WR_REGISTER|nRF_EN_RXADDR, 0x03,// Address WidthnRF_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-5nRF_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 AddressnRF_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 EnablenRF_WR_REGISTER|nRF_DYNPD, 0,// Feature SetupnRF_WR_REGISTER|nRF_FEATURE, 0,// Status (Reset interrupt bits)nRF_WR_REGISTER|nRF_STATUS, nRF_RX_DR|nRF_TX_DS|nRF_MAX_RT,// Flush FIFOsnRF_FLUSH_TX,nRF_FLUSH_RX};nrfWrite(&rfBuf[ 0],2); // ConfigurationnrfWrite(&rfBuf[ 2],2); // Auto Ack EnablenrfWrite(&rfBuf[ 4],2); // RX Pipe EnablenrfWrite(&rfBuf[ 6],2); // Address WidthnrfWrite(&rfBuf[ 8],2); // Auto RetransmitnrfWrite(&rfBuf,2); // RF ChannelnrfWrite(&rfBuf,2); // RF SetupnrfWrite(&rfBuf,6); // RX Address - Pipe 0nrfWrite(&rfBuf,6); // RX Address - Pipe 1nrfWrite(&rfBuf,2); // RX Address - Pipe 2nrfWrite(&rfBuf,2); // RX Address - Pipe 3nrfWrite(&rfBuf,2); // RX Address - Pipe 4nrfWrite(&rfBuf,2); // RX Address - Pipe 5nrfWrite(&rfBuf,6); // TX AddressnrfWrite(&rfBuf,2); // RX Payload Width - Pipe 0nrfWrite(&rfBuf,2); // RX Address - Pipe 1nrfWrite(&rfBuf,2); // RX Payload Width - Pipe 1nrfWrite(&rfBuf,2); // Dynamic Payload EnablenrfWrite(&rfBuf,2); // Feature SetupnrfWrite(&rfBuf,2); // StatusnrfWrite(&rfBuf,1); // Flush TX_FIFOsnrfWrite(&rfBuf,1); // Flush RX_FIFOs}main.c file (abridged) (readPayload and writePayload where shown earlier)Code:// Register read globalsuint8_t rfAddr = {nRF_RD_REGISTER|nRF_RX_ADDR_P0, 0, 0, 0, 0, 0};uint8_t rfAux = {nRF_RD_REGISTER|nRF_RX_ADDR_P1, 0, 0, 0, 0, 0};uint8_t rfCh = {nRF_RD_REGISTER|nRF_RF_CH, 0};uint8_t rfStatus = {nRF_RD_REGISTER|nRF_STATUS, 0};uint8_t rfObserve = {nRF_RD_REGISTER|nRF_OBSERVE_TX, 0};uint8_t rfFIFO = {nRF_RD_REGISTER|nRF_FIFO_STATUS, 0};void main(void){nrfSetup();nrfStartListening();// Main loopwhile (1) {// Read nRF status and FIFO statusrfStatus = nrfRead(rfFIFO, 2);// Save RX_DR stateRxReady = (rfStatus >> 6) & 0x01;// Read available payloads <--<--<--<--<-- Payload <--<--<--<--<--cnt = 10;while ( --cnt > 0 && (rfFIFO & 0x03) != 1 ){readPayload(); // Get payloadrfStatus = nrfRead(rfFIFO, 2); // Get FIFO status}// Write payload -->-->-->-->--> Payload -->-->-->-->-->// If a change or 1 Hz, then reportif ( forceXmt==1 || frame==0 ){forceXmt = 0;nrfStopListening();writePayload(); // Send payloadnrfStartListening();}}}

author: MarkF
author_rank: Super Contributor
timestamp: November 21, 2024, 04:31:04 am
content: Quote from: ulao on November 21, 2024, 03:03:35 amThe 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?

author: ulao
author_rank: Contributor
timestamp: November 21, 2024, 04:50:11 am
content: 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 ?

author: MarkF
author_rank: Super Contributor
timestamp: November 21, 2024, 05:16:10 pm
content: 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.

author: ulao
author_rank: Contributor
timestamp: November 21, 2024, 11:27:46 pm
content: In this case.send 1 bytenop (wait )send 1 bytenop (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 thisCode: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 functionsCode: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 TXdisable();}if (mode & 2){enable();sendCommand(0xE2);//flush RXdisable();}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;}

author: MarkF
author_rank: Super Contributor
timestamp: November 22, 2024, 01:55:42 am
content: 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:#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 0x07unsigned char sendCommand(unsigned char data){// set CE pinPORTB &= ~0x01;// do SPI transferSPDR = data;while ( !(SPSR & (1<<SPIF)) );// clear CE pinPORTB |= 0x01;return SPDR;}void pulse(void){if ( _receiver_ ) return;ATT_ON();_delay_us(10); //minium is 10usATT_OFF();}void clearALLFIFO(void){// Clear RX_DR, TX_DS, and MAX_RT in the STATUS registersendCommand( nRF_WR_REGISTER | nRF_STATUS );sendCommand( 0x70 );pulse();}void flush(unsigned char mode){// flush TX FIFOif (mode & 1) sendCommand( nRF_FLUSH_TX );// flush RX FIFOif (mode & 2) sendCommand( nRF_FLUSH_RX );pulse();}unsigned char NOP(void){// send NOP to read status registerunsigned char status = sendCommand( nRF_NOP ); // see if data is readyreturn 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 sendwhile (! NOP() & 0x20 );}}

author: ulao
author_rank: Contributor
timestamp: November 22, 2024, 07:15:38 pm
content: yesdataback = 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 seestatus=sendCommand(0xFF);means what's in the status, not get status, since it cant know that. Otherwise we'd have to dosendCommand(0x17);tell nrf to get statusstatus=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 datareturnNrf2Payload[d...]=sendCommand(0x00);But that does bring up a good point how do I read OBSERVE_TX ?sendCommand(0x18);tell nrf to get OBSERVE_TXOBSERVE_TX =sendCommand(0xFF);NOP, to read returned byte.?

author: MarkF
author_rank: Super Contributor
timestamp: November 22, 2024, 10:14:32 pm
content: I think you're a little confused on how SPI works.The SS is only used when the SPI hardware is in 'slave mode'. In that case, the control of the SPI is done by the opposite MCU. The other MCU starts the transfer. The CE pin of ALL devices using SPI must be done with a MCU pin. The selection is not part of the SPI hardware transfer."The SPI is bi directional, so it receives and sends at the same time. The first byte received is status."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.To get the OBSERVE_TX, take a look at the nrfRead() function I use.// Wait for completetimeout=100;do {delay_ms(1);rfStatus = nrfRead(rfObserve, 2);} while ( (rfStatus & 0x30) == 0 && --timeout > 0 );I would suggest you take a second look at your sendCommand() function keeping in mind what my nrfWrite() function is doing.My initial approach was to do overall nRF clear like you're doing. That didn't work out very well for me.clearALLFIFO();//I guess just to be sure.flush(1);I'm not telling you not to do this. In doing my last code optimization, I switched to a more pointed and specific clearing only when required. My nRFs seem to be running smoother or my perception of them is clearer. I do seem to have sped up my throughput. There were other significant changes though.

author: ulao
author_rank: Contributor
timestamp: November 22, 2024, 11:31:01 pm
content: 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 = nrfRead(rfObserve, 2);rfStatus = nrfRead(rfObserve, 2);rfStatus = nrfRead(rfObserve, 2);rfStatus = nrfRead(rfObserve, 2);rfStatus = nrfRead(rfObserve, 2);rfStatus = nrfRead(rfObserve, 2);rfStatus = 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 simplenrfRead(rfObserve, 2);//didnt look up what your , was bit this sends a byte and gets back the status bytenrfRead(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.

author: MarkF
author_rank: Super Contributor
timestamp: November 23, 2024, 12:28:37 am
content: 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.QuoteYou don't need two calls as the first one also returns the status just like a NOP.

author: MarkF
author_rank: Super Contributor
timestamp: November 23, 2024, 06:45:28 am
content: 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();ThesendCommand()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.YoursendCommand()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 0x08status = sendCommand( nRF_RD_REGISTER|nRF_OBSERVE_TX );data = sendCommand( 0 );plos_cnt = (data>>4) & 0x0f;arc_cnt = data & 0x0f;

author: mtwieg
author_rank: Frequent Contributor
timestamp: November 23, 2024, 03:20:22 pm
content: ~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...

author: ulao
author_rank: Contributor
timestamp: December 23, 2024, 10:25:07 pm
content: 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.QuoteAccording to the data sheet a 0xff command is all you need?NOP 1111 1111No Operation. Might be used to read the STATUS registersoenable();//turn chip onstatus = sendCommand( 0xff );disable();//turn chip offdoing this failswhile ( NOP() & 0x20 ) {}where this workswhile ( NOP() & 0x20 ) { _delay_us(25)} //anything under 25 randomly fails.