(SOLVED) Can't read payload nRF24"01+ & ATtiny84

I am attempting to program a pair of ATtiny84s to communicate via a pair of nRF24L01+ modules (http://www.amazon.com/nRF24L01-Wireless-Transceiver-Arduino-Compatible/dp/B00E594ZX0).

So far, I have gotten the RX device to beep in response to receiving a packet from the TX device. However, I cannot read correct payload data from the RX device. I would like to know what, if anything, I am doing wrong.

This is so far the only thing I have noticed wrong. My ATtiny's are able to read the status register correctly, the correct RX datapipe, even the correct payload width. The only thing I am unable to read is the payload data. It's either not being loaded into the transmitter right, or not being read out of the receiver correctly. Instead of the appropriate locator_ping-packet, I am receiving 01 f6 01 f5 as the data. I've tried for a couple days now to figure this out, and can't.

Even though I'm transmitting at -18 dBm, the radios are less than 2 inches apart. I can't imagine the transmission ebing corrupted over that short a distance. furthermore, the value I get is consistent.

Note that I am not using an Arduino core to program these devices. I wanted to try just using AVR LibC

Here are some relevant snippets of code

The transmitted packets:

// Command packets for transmission
const uint8_t heartbeat_packet[] PROGMEM = { 0xBE, 0xEF, 0xCA, 0xFE };
const uint8_t locator_ping_packet[] PROGMEM = { 0xDE, 0xAD, 0xBE, 0xEF };

TX's main function

int main(void)
{
	// Change Clock divider to run from /2 instead of /8.
	// Chip can run all the way down to 1.8 V @ 4 MHz
	clock_prescale_set( clock_div_2 );
	
	// Set buzzer and LED to output.
	sbi( BUZZ_DDR, BUZZ_PIN );
	turn_off_buzzer();
	sbi( LED_DDR, LED_PIN );
	turn_off_LED();
	
	// Set button to input with pullup resistor enabled
	cbi( BTN_DDR, BTN_PIN );
	sbi( BTN_PORT, BTN_PIN );
	
	// Power down unused modules
	ADCSRA = 0; // Disable ADC
	power_adc_disable();
	power_timer1_disable();
	ACSR = _BV(ACD); // Disable Analog Comparator
	
	// enable global interrupts
	sei();
	
	// enable LOW level INT0 interrupt
	// Set to input with pullup enabled.
	cbi( DDRB, PB2 );
	sbi( PORTB, PB2 );
	// Set input to LOW level
	cbi( MCUCR, ISC01 );
	cbi( MCUCR, ISC00 );
	// Enable INT0 interrupt
	sbi( GIMSK, INT0 );

	init_millis_timer();
	init_usi_spi_master();
	_delay_ms(150);
	init_nrf24l01p(ADDRESS_WIDTH, COMMAND_PACKET_LENGTH);
	
	set_address_from_eeprom();
	
	uint8_t payload[COMMAND_PACKET_LENGTH];
	
	set_tx_mode();
	power_up();
	memcpy_P( payload, locator_ping_packet, COMMAND_PACKET_LENGTH );
	turn_on_LED();
	_delay_ms(500);
	turn_off_LED();
	_delay_ms(1000);
	
    for(;;)
    {
		turn_on_LED();
		_delay_ms( 50 );
		turn_off_LED();
		write_tx_payload(payload, COMMAND_PACKET_LENGTH);
		set_active();
		_delay_us(15);
		set_standby();
		_delay_ms(1000);
		write_command_register(REG_STATUS, _BV(TX_DS));
    }
}

RX's main function

int main(void)
{
	// Change Clock divider to run from /2 instead of /8.
	// Chip can run all the way down to 1.8 V @ 4 MHz
	clock_prescale_set( clock_div_2 );
	
	// Set buzzer and LED to output.
	sbi( BUZZ_DDR, BUZZ_PIN );
	turn_off_buzzer();
	sbi( LED_DDR, LED_PIN );
	turn_off_LED();
	
	// Power down unused modules
	ADCSRA = 0; // Disable ADC
	power_adc_disable();
	power_timer1_disable();
	ACSR = _BV(ACD); // Disable Analog Comparator
	
	// enable global interrupts
	sei();
	
	// enable LOW level INT0 interrupt
	// Set to input with pullup enabled.
	cbi( DDRB, PB2 );
	sbi( PORTB, PB2 );
	// Set input to LOW level
	cbi( MCUCR, ISC01 );
	cbi( MCUCR, ISC00 );
	// Enable INT0 interrupt
	sbi( GIMSK, INT0 );

	init_millis_timer();
	init_usi_spi_master();
	_delay_ms(150);
	init_nrf24l01p(ADDRESS_WIDTH, COMMAND_PACKET_LENGTH);
	
	set_address_from_eeprom();
	
	set_rx_mode();
	power_up();
	set_active();
	
    for(;;)
    {
		if( last_irq == INTZERO )
		{
			set_standby();
			turn_on_LED();
			_delay_ms(50);
			turn_off_LED();
			uint8_t payload[COMMAND_PACKET_LENGTH];
			read_rx_payload(payload, COMMAND_PACKET_LENGTH);
			uint8_t status = read_status_register();
			if( status & _BV(RX_DR) )
			{
				uint8_t rx_pl_length = read_rx_payload_width();
				eeprom_update_block( payload, payload_eeprom, COMMAND_PACKET_LENGTH );
				uint8_t pipe = 0b111 & (status >> RX_P_NO);
				if( pipe==1 && rx_pl_length==COMMAND_PACKET_LENGTH /*!memcmp_P(payload, heartbeat_packet, COMMAND_PACKET_LENGTH)*/ )
				{
					BUZZ_PORT ^= _BV(BUZZ_PIN);
				}
			}
			// write to status register to clear 
			write_command_register(REG_STATUS, status);
			flush_rx_fifo()
			
			// Clear last_irq
			last_irq = NONE;
			// Reenable INT0 Interrupt
			sbi( GIMSK, INT0 );;
			set_active();
		}
    }
}

last_irq is a flag set in ISR(EXT_INT0_vect), which also disables the INT0 interrupt when triggered.

From nrf24l.c (shared between both devices). note that I have turned off auto-retransmit and auto-acknowledge. It's not critical that the packets are received (since they're just a heartbeat and a ping), I just want them to be sent.

void init_nrf24l01p(uint8_t add_w, uint8_t pack_w)
{
	// Set SPI Chip Enable pin to output.
	sbi(CSN_DDR, CSN_PIN);
	// Set SPI Chip Enable pin HIGH.
	unlatch_chip();
	// Set TX/RX Chip Enable pin to output.
	sbi(CE_DDR,CE_PIN);
	set_standby();
	
	set_tx_power(TX_POWER_M18_DBM);
	set_tx_speed(TX_SPEED_2M);
	
	// Turn off auto-acknowledge for all channels
	write_command_register(REG_EN_AA, 0);
	
	// Set addres width
	write_command_register(REG_SETUP_AW, add_w-2);
	
	// Set payload packet width
	write_command_register(REG_RX_PW_P0, pack_w);
	write_command_register(REG_RX_PW_P1, pack_w);
	
	// Disable auto-retransmit
	write_command_register(REG_SETUP_RETR, 0);
}
void write_tx_payload(uint8_t payload_buffer[], uint8_t size)
{
	latch_chip();
	
	usi_spi_transfer(NRF24_W_TX_PAYLOAD);
	uint8_t i;
	for( i=0; i<size; i++ )
	{
		usi_spi_transfer(payload_buffer[i]);
	}
	
	unlatch_chip();
	
}

void read_rx_payload(uint8_t payload_buffer[], uint8_t size)
{
	latch_chip();
	
	usi_spi_transfer(NRF24_R_RX_PAYLOAD);
	uint8_t i;
	for( i=0; i<size; i++ );
	{
		payload_buffer[i] = usi_spi_transfer(0x00);
	}
	
	unlatch_chip();
}

Full code for both devices is attached below.

EDIT: I've verified that I can read the status register correctly. It's only the RX FIFO that I have trouble with. I can't understand what's going on. Now I'm reading 01 f8 01 f7 from the payload, instead of what I was getting before. Does anyone know if these are error values?

Project.zip (87.6 KB)

I've modified the main code to blink the LED if the RX_EMPTY flag was not set. The LED blinks twice, idicating the packet was received and the RX FIFO is not empry. I don't know why I can't read data out.

for(;;)
    {
		if( irq_INT0 )
		{
			set_standby();
			turn_on_LED();
			_delay_ms(50);
			turn_off_LED();
			uint8_t status = read_status_register();
			if( status & _BV(RX_DR) )
			{
				uint8_t fifo_status = read_command_register(REG_FIFO_STATUS);
				if( !(fifo_status&_BV(RX_EMPTY)) )
				{
					_delay_ms(100);
					turn_on_LED();
					_delay_ms(50);
					turn_off_LED();
				}
				uint8_t rx_pl_length = read_rx_payload_width();
				uint8_t payload[COMMAND_PACKET_LENGTH];
				read_rx_payload(payload, COMMAND_PACKET_LENGTH);
				eeprom_update_block( payload, payload_eeprom, COMMAND_PACKET_LENGTH );
				uint8_t pipe = 0b111 & (status >> RX_P_NO);
				if( pipe==1 && rx_pl_length==COMMAND_PACKET_LENGTH /*!memcmp_P(payload, heartbeat_packet, COMMAND_PACKET_LENGTH)*/ )
				{
					BUZZ_PORT ^= _BV(BUZZ_PIN);
				}
				write_command_register(REG_STATUS, _BV(RX_DR));
			}
			flush_rx_fifo();
			
			// Clear last_irq
			irq_INT0 = 0;
			// Reenable INT0 Interrupt
			sbi( GIMSK, INT0 );;
			set_active();
		}
    }

I've done some more testing on this problem, and it appears that the payload array is not being loaded with any valules from the read_rx_payload function. I have tried a few different ways to rewrite the function, but none of them seem to work. Here's another way I tried:

void read_rx_payload(uint8_t* payload_buffer, uint8_t size)
{
	latch_chip();
	
	usi_spi_transfer(NRF24_R_RX_PAYLOAD);
	uint8_t i;
	for( i=0; i<size; i++ );
	{
		*(payload_buffer+i) = usi_spi_transfer(NRF24_NOP);
	}
	
	unlatch_chip();
}

However, I get the same data after read_rx_payload(payload, COMMADN_PACKET_SIZE) as I did before. I think it's the unintialized SRAM values that where in tehre before, and aren't being updated. To test this, I made payload a global array, and now I'm just getting 0x00s in the array, even after read_rx_payload is run.

This seems to be a programming issue. I am able to read any other registers, just not this one, and this is the only one I've tried that passes an array as an argument.

I am using avr-gcc (GCC) 4.8.2 to compile this on a Linux Mint 17 machine.

I've figured out the problem. It was with the read_rx_payload function. For some bizzare reason, a for loop wouldn't work, but a while loop with the same variables does. Here's the working function:

void read_rx_payload(uint8_t payload_buffer[], uint8_t size)
{
	latch_chip();
	
	usi_spi_transfer(NRF24_R_RX_PAYLOAD);
	uint8_t index=0;
	while( index < size )
	{
		payload_buffer[index] = usi_spi_transfer(NRF24_NOP);
		index++;
	}
	
	unlatch_chip();
}

Does anyone know what the cause of this might be? Is it a bug in the gcc compiler or something?