ATMega328P and nRF24L01

Go To Last Post
3 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello everybody! 

 

I am planning to revive an old rc car I got from my grandfather back in the day (Its a Porsche 996 turbo if anybody is curious). Since I wanted to improve my skills regarding programming I decided it would be a good idea to "build" and program the control for the engine and steering from scratch (I am also planning on implementing lights). For the brain of the car I am using an ATMega328P (Arduino UNO) and for the remote an Arduino NANO (also with the 328P). And for the wireless transmission I got my self some nRF24L01s. To convert the process of reviving the car into a nice exercise for my self I decided not to use the available libraries (like RF24). But I can't get the modules to work. For the last three days I've been trying to set them up and also looked at guides/tutorials online, unfortunately nothing helped.

 

I used this guide and this forum post as a reference.

I am using platformIO with visual studio code to write and upload the programs to my MCUs.

 

My code (see below) is very identical to the reference ones. But there has to be an error somewhere which I can't find.

In my program I am waiting for the IRQ pin (Interrupt from the nRF24) to signal a successful transmission/reception in my main loop but my program never reaches this state (IRQ never goes low).

I already checked all the registers after the setup, all registers are set correctly (In the current version of my code I followed the suggestion from the forum post). Since waiting for the IRQ to go low didn't work I also tried to check the status register to see if data was transmitted/received, also no luck.

A weird thing I noticed while checking the register values was that the TX_FLUSH command doesn't work for some reason (but that should not block the module form transmitting data).

To verify that my modules are actually working I used the RF24 library, everthing worked fine. I also attached the nRF24 datasheet.

 

Since all the registers hold the correct value after setup (which verifies that my spi is working correctly) and cross referencing my setup with the tutorial and the forum post for probably 100 times now I really don't know where to start to find the mistake.

 

I hope someone can help me! 

Thanks in advance! 

 

EDIT:

Accidentally deleted the "Serial.begin()". At the moment I am using the Arduino library for USART but I will implement my own USART library in the future.

 

 

Here is my code:

 

Transmitter

 

#define F_CPU 16000000L

#include <stdio.h>
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <string.h>
#include <Arduino.h>
#include <nRF24L01.h>
#include <Serial.h> //my SPI library

#define CSN PB2
#define CE PD7

//Function Prototypes
void WriteNRF(uint8_t , uint8_t , uint8_t*, uint8_t);
void ResetIRQ();
void FlushNRF();
uint8_t getStatus();

//global variables
uint8_t status = 0;
uint8_t run = 0;

int main()
{
  //USART
  Serial.begin(9600);  //Arduino library used for testing purposes

  //SPI Setup
  SPI_MasterInit();

  //Ports
  DDRD = _BV(CE) | _BV(PD2) | _BV(PD3) | _BV(PD4) | _BV(PD5); // PD2 - PD5 used for "debug LEDs"
  PORTB |= _BV(CSN);  //defined as output via SPI_MasterInit()
  PORTD &= ~_BV(CE); 

  PORTD |=  _BV(PD2) | _BV(PD3) | _BV(PD4) | _BV(PD5);  //Check if LEDs are working

  _delay_ms(11);

  //nRF24 Init
  uint8_t FREQ[] = {0x09}; //Set frequency to 2,409 GHz
  WriteNRF(W_REGISTER, RF_CH, FREQ, 1); 

  uint8_t PLDW[] = {0x01}; //Set payload width, here 1 byte
  WriteNRF(W_REGISTER, RX_PW_P0, PLDW, 1);

  uint8_t ReDelay[] = {0xFA}; //4000us, 10 retries
  WriteNRF(W_REGISTER, SETUP_RETR, ReDelay, 1);

  uint8_t RFSet[] = {0x07};  //LNA Gain max, 0dBm, 1 Mbps
  WriteNRF(W_REGISTER, RF_SETUP, RFSet, 1); 

  uint8_t AutoACK[] = {0x01};  //Enable AutoACK for data pipe 0
  WriteNRF(W_REGISTER, EN_AA, AutoACK, 1); 

  uint8_t ENRXADDR[] = {0x01}; //Enable data pipe 0
  WriteNRF(W_REGISTER, EN_RXADDR, ENRXADDR, 1);

  uint8_t FEAT[] = {0x01};  //Enable W_TX_PAYLOAD_NOACK command
  WriteNRF(W_REGISTER, FEATURE, FEAT, 1);

  uint8_t CONF[] = {0x0E};  //Powerup, CRC 2 bytes, Enable CRC,
  WriteNRF(W_REGISTER, CONFIG, CONF, 1);

  _delay_ms(2); //Delay for state transition from power down to start up 

  PORTD &=  ~(_BV(PD2) | _BV(PD3) | _BV(PD4) | _BV(PD5)); //Turn off LEDs

  while(1)
  {
    //Write data to be transmitted to nRF via SPI
    uint8_t data[] = {5, 7};

    //Do not send the same data twice, chinese clones seem to discard repeated payloads!
    if(run == 1)
    {
      WriteNRF(0, W_TX_PAYLOAD, data, 1);
      run = 0;
      PORTD = _BV(PD2);
    }

    else if(run == 0)
    {
      WriteNRF(0, W_TX_PAYLOAD, (data+1), 1);
      run = 1;
      PORTD = _BV(PD3);
    }

    PORTD |= _BV(CE);
    _delay_us(150); //Tx settling, added 20 us to be sure

    while((PINB && _BV(PB0)) == 1)
    {
      PORTD |= _BV(PD4);
    }

    Serial.println("Ack received!");

    PORTD &= ~(_BV(PD4));

    _delay_us(10);

    PORTD &= ~_BV(CE);

    ResetIRQ();

    _delay_us(10);

  }

}

void WriteNRF(uint8_t RW, uint8_t reg, uint8_t* regVal, uint8_t ValLen)
{
  _delay_us(10);
  PORTB &= ~(_BV(CSN));         //Slave select
  reg = RW + reg;               //combine command
  uint8_t slave_data = 125;     //Data send from slave
  _delay_us(10);

  slave_data = SPI_MasterTX(reg); //Send command

  _delay_us(10);

  uint8_t i = 0;
  for(i = 0; i < ValLen; i++)
  {
    slave_data = SPI_MasterTX(regVal[i]); //Send value
    _delay_us(10);
  }

  PORTB |= _BV(CSN);

  _delay_us(1);
}

void ResetIRQ()
{
  _delay_us(10);

  PORTB &= ~(_BV(CSN));     

  uint8_t slave_data = 125;

  _delay_us(10);

  slave_data = SPI_MasterTX(W_REGISTER + STATUS);

  _delay_us(10);

  slave_data = SPI_MasterTX(0x70);

  _delay_us(10);

  PORTB |= _BV(CSN);

  _delay_us(10);
}

void FlushNRF()   //Flush transmit FIFO
{
  volatile uint8_t slave_data = 125;
  _delay_us(10);
  slave_data = SPI_MasterTX(FLUSH_TX);
  _delay_us(10);
}

uint8_t getStatus() //Function to check status register, used for debugging
{
  _delay_us(10);

  PORTB &= ~(_BV(CSN));     

  uint8_t slave_data = 125;
  _delay_us(10);

  slave_data = SPI_MasterTX(0);

  _delay_us(10);

  PORTB |= _BV(CSN);

  _delay_us(10);

  return slave_data;
}

 

Receiver

 

#define F_CPU 16000000L

#include <stdio.h>
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <string.h>
#include <Arduino.h>
#include <nRF24L01.h>
#include <Serial.h>
//#include <SCOMM.h>

#define CSN PB2
#define CE PD7

//Function Prototypes
uint8_t WriteNRF(uint8_t , uint8_t , uint8_t*, uint8_t);
void ResetIRQ();
void FlushNRF();
uint8_t getStatus();

//global variables
uint8_t status = 0;
int i = 0;
uint8_t RData = 0;

int main()
{
   //USART
   Serial.begin(9600); //Arduino library used for testing purposes

  //SPI Setup
  SPI_MasterInit();

  //Ports
  DDRD = _BV(PD7) | _BV(PD2) | _BV(PD3) | _BV(PD4) | _BV(PD5);
  PORTB |= _BV(CSN);  //defined as output via SPI_MasterInit()
  PORTD &= ~_BV(CE);

  PORTD |=  _BV(PD2) | _BV(PD3) | _BV(PD4) | _BV(PD5);

  _delay_ms(100);

  //nRF24 Init
  uint8_t FREQ[] = {0x09}; //Set frequency to 2,409 GHz
  WriteNRF(W_REGISTER, RF_CH, FREQ, 1); 

  uint8_t PLDW[] = {0x01}; //Set payload width, here 1 byte
  WriteNRF(W_REGISTER, RX_PW_P0, PLDW, 1);

  uint8_t ReDelay[] = {0xFA}; //4000us, 10 retries
  WriteNRF(W_REGISTER, SETUP_RETR, ReDelay, 1);

  uint8_t RFSet[] = {0x07};  //LNA Gain max, 0dBm, 1 Mbps
  WriteNRF(W_REGISTER, RF_SETUP, RFSet, 1); 

  uint8_t AutoACK[] = {0x01};  //Enable AutoACK for data pipe 0
  WriteNRF(W_REGISTER, EN_AA, AutoACK, 1); 

  uint8_t ENRXADDR[] = {0x01}; //Enable data pipe 0
  WriteNRF(W_REGISTER, EN_RXADDR, ENRXADDR, 1);

  uint8_t FEAT[] = {0x01};  //Enable W_TX_PAYLOAD_NOACK command
  WriteNRF(W_REGISTER, FEATURE, FEAT, 1);

  uint8_t CONF[] = {0x0F};  //Powerup, CRC 2 bytes, Enable CRC,
  WriteNRF(W_REGISTER, CONFIG, CONF, 1);

  _delay_ms(2); 

  PORTD &=  ~(_BV(PD2) | _BV(PD3) | _BV(PD4) | _BV(PD5));

  while(1)
  {

    //main loop
    PORTD |= _BV(CE);
    _delay_us(150);

    uint8_t k = 1;

    while((PINB && _BV(PB0)) == 1)
    {
      //Serial.print("Waiting");
      //i++;
      PORTD |= _BV(PD4);
    }

    PORTD &= ~_BV(PD4);

    _delay_us(10);

    PORTD &= ~_BV(CE);

    RData = WriteNRF(R_RX_PAYLOAD, 0, 0, 1);

    ResetIRQ();

    _delay_us(10);
  }

}

uint8_t WriteNRF(uint8_t RW, uint8_t reg, uint8_t* regVal, uint8_t Vallen)
{
  _delay_us(10);
  PORTB &= ~(_BV(CSN));
  reg = RW + reg;
  uint8_t slave_data = 125;
  _delay_us(10);

  slave_data = SPI_MasterTX(reg);

  _delay_us(10);

  uint8_t i = 0;
  for(i = 0; i < Vallen; i++)
  {
    slave_data = SPI_MasterTX(regVal[i]);

    _delay_us(10);
  }

  PORTB |= _BV(CSN);

  _delay_us(10);

  return slave_data;
}

void ResetIRQ()
{
  _delay_us(10);

  PORTB &= ~(_BV(CSN));     

  uint8_t slave_data = 125;

  _delay_us(10);

  slave_data = SPI_MasterTX(W_REGISTER + STATUS);

  _delay_us(10);

  slave_data = SPI_MasterTX(0x70);

  _delay_us(10);

  PORTB |= _BV(CSN);

  _delay_us(10);
}

void FlushNRF()
{
  volatile uint8_t slave_data = 125;
  _delay_us(10);
  slave_data = SPI_MasterTX(FLUSH_TX);
  _delay_us(10);
}

uint8_t getStatus()
{
  _delay_us(10);

  PORTB &= ~(_BV(CSN));     

  uint8_t slave_data = 125;
  _delay_us(10);

  slave_data = SPI_MasterTX(0);

  _delay_us(10);

  PORTB |= _BV(CSN);

  _delay_us(10);

  return slave_data;
}

 

My SPI library

 

/*
    SPI port overview of ATMega328P
    PB3 = MOSI
    PB4 = MISO
    PB5 = SCK
    PB2 = CSN = SS --> Chip Select
*/

#include <stdio.h>
#include <stdlib.h>
#include <avr/io.h>
#include <util/delay.h>
#include "Serial.h"

void SPI_MasterInit(void)
{
  //Setup device as Master
  DDRB |= _BV(PB3) | _BV(PB5) | _BV(PB2);

  //Enable SPI, set device as master, SCK = f_osc/16
  SPCR |= _BV(SPE) | _BV(MSTR) | _BV(SPR0);
}

void SPI_SlaveInit(void)
{
  //Setup device as slave
  DDRB |= _BV(PB4);

  //Enable SPI
  SPCR |= _BV(SPE);
}

uint8_t SPI_MasterTX(uint8_t MData)
{
  //Writing a byte to SPDR starts the SPI clock generator, transmission starts
  SPDR = MData;
  //SPIF in SPSR marks completed transmission
  while(!(SPSR & (1<<SPIF)));

  return SPDR;
}

uint8_t SPI_MasterRX(void)
{
  //Return received byte from slave
  while(!(SPSR & (1<<SPIF)));
  return SPDR;
}

void SlaveTransmit(uint8_t SData)
{
  SPDR = SData;
  while(!(SPSR & (1<<SPIF)));
}

uint8_t SlaveReceive(void)
{
  while(!(SPSR & (1<<SPIF)));
  return SPDR;
}

void SlaveSelect(uint8_t SSPortReg, uint8_t SSPortBit)
{
  //Iniate Slave Select pins, need to be high to prevent communication
  //Refers to PB2 ON THE SLAVE, free choice on Master!
  SSPortReg |= _BV(SSPortBit);
}

void InitComm(uint8_t SSPortReg, uint8_t SSPortBit)
{
  //Select Slave to communicate with
  SSPortReg &= ~(1<< SSPortBit);
}

 

EDIT:

Accidentally deleted the "Serial.begin()". At the moment I am using the Arduino library for USART but I will implement my own USART library in the future.

Attachment(s): 

This topic has a solution.
Last Edited: Sun. Aug 11, 2019 - 08:47 PM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Mr.Tappy wrote:

while((PINB && _BV(PB0)) == 1)
    {
      PORTD |= _BV(PD4);
    }

Should be & rather than &&.

Might have something to do with your problem.

Also,  testing  == 1 is not great since it only works for pin 0 mask, and you will change the pin at some point,  and you will forget :)

So better as

while ((PINB & _BV(PB0)) != 0) { }

or just

while (PINB & _BV(PB0)) { }

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello MrKendo!

 

Thank you! That completely solved my problem.

And thanks for the lesson on the & and && operator. 

I remembered the && completely wrong.