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.
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)
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.
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.
//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.
He's my write payload function with a fixed length.
It shows how I check for transmission failure and re-tries.
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;
}
}
I mean this ( see attached )
So I think I see my issue but can not explain it yet.
if I do this..
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
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.
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
#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)
// 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();
}
}
}
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
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
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;
}
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 . . .
#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 );
}
}