Skip to content

Instantly share code, notes, and snippets.

@klalle
Last active October 12, 2022 11:30
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save klalle/5652658 to your computer and use it in GitHub Desktop.
Save klalle/5652658 to your computer and use it in GitHub Desktop.
This is a simple code that waits for the USART interrupt, that receives the payload from the USART and then calls the send-function to broadcast the data to listening nRFs.
/*
* RF_Tranceiver.c
*
* Created: 2012-08-10 15:24:35
* Author: Kalle
* Atmega88
*/
#include <avr/io.h>
#include <stdio.h>
#define F_CPU 8000000UL // 8 MHz
#include <util/delay.h>
#include <avr/interrupt.h>
#include "nRF24L01.h"
#define dataLen 3 //längd på datapacket som skickas/tas emot
uint8_t *data;
uint8_t *arr;
/*****************ändrar klockan till 8MHz ist för 1MHz*****************************/
void clockprescale(void)
{
CLKPR = 0b10000000; //Prepare the chip for a change of clock prescale (CLKPCE=1 and the rest zeros)
CLKPR = 0b00000000; //Wanted clock prescale (CLKPCE=0 and the four first bits CLKPS0-3 sets division factor = 1)
//See page 38 in datasheet
}
////////////////////////////////////////////////////
/*****************USART*****************************/ //Skickar data från chip till com-port simulator på datorn
//Initiering
void usart_init(void)
{
DDRD |= (1<<1); //Set TXD (PD1) as output for USART
unsigned int USART_BAUDRATE = 9600; //Same as in "terminal.exe"
unsigned int ubrr = (((F_CPU / (USART_BAUDRATE * 16UL))) - 1); //baud prescale calculated according to F_CPU-define at top
/*Set baud rate */
UBRR0H = (unsigned char)(ubrr>>8);
UBRR0L = (unsigned char)ubrr;
/* Enable receiver and transmitter */
UCSR0B = (1<<RXEN0)|(1<<TXEN0);
/* Set frame format: 8data, 2stop bit, The two stop-bits does not seem to make any difference in my case!?*/
UCSR0C = (1<<USBS0)|(3<<UCSZ00);
}
//Funktionen som skickar iväg byten till datorn
void USART_Transmit(uint8_t data)
{
/* Wait for empty transmit buffer */
while ( !( UCSR0A & (1<<UDRE0)) );
/* Put data into buffer, sends the data */
UDR0 = data;
}
//Funktionen som Tar emot kommandon av datorn som senare ska skickas till transmittern
uint8_t USART_Receive( void )
{
/* Wait for data to be received */
while ( !(UCSR0A & (1<<RXC0)) ); //This loop is only needed if you not use the interrupt...
/* Get and return received data from buffer */
return UDR0; //Return the received byte
}
/*****************SPI*****************************/ //Skickar data mellan chip och nrf'ens chip
//initiering
void InitSPI(void)
{
//Set SCK (PB5), MOSI (PB3) , CSN (SS & PB2) & C as outport
//OBS!!! Måste sättas innan SPI-Enable neadn
DDRB |= (1<<DDB5) | (1<<DDB3) | (1<<DDB2) |(1<<DDB1);
/* Enable SPI, Master, set clock rate fck/16 .. kan ändra hastighet utan att det gör så mycket*/
SPCR |= (1<<SPE)|(1<<MSTR);// |(1<<SPR0) |(1<<SPR1);
SETBIT(PORTB, 2); //CSN IR_High to start with, vi ska inte skicka nåt till nrf'en ännu!
CLEARBIT(PORTB, 1); //CE low to start with, nrf'en ska inte sända/ta emot nåt ännu!
}
//Skickar kommando till nrf'en å får då tillbaka en byte
char WriteByteSPI(unsigned char cData)
{
//Load byte to Data register
SPDR = cData;
/* Wait for transmission complete */
while(!(SPSR & (1<<SPIF)));
//Returnera det som sänts tillbaka av nrf'en (första gången efter csn-låg kommer Statusregistert)
return SPDR;
}
////////////////////////////////////////////////////
/*****************in/out***************************/ //ställ in t.ex. LED
//sätter alla I/0 portar för t.ex. LED
void ioinit(void)
{
DDRB |= (1<<DDB0); //led
}
////////////////////////////////////////////////////
/*****************interrupt***************************/ //orsaken till att köra med interrupt är att de avbryter koden var den än är och kör detta som är viktigast!
//när data tas emot/skickas så går interr uptet INT0 näst längst ner igång
void INT0_interrupt_init(void)
{
DDRD &= ~(1<<DDD2); //Extern interrupt på INT0, dvs sätt den till input!
EICRA |= (1<<ISC01);// INT0 falling edge PD2
EICRA &= ~(1<<ISC00);// INT0 falling edge PD2
EIMSK |= (1<<INT0); //enablar int0
//sei(); // Enable global interrupts görs sen
}
//när chipets RX (usart) får ett meddelande fårn datorn går interruptet USART_RX igång längst ner.
void USART_interrupt_init(void)
{
UCSR0B |= (1<<RXCIE0); //Enable interrupt that triggers on USART-data is received,
}
//////////////////////////////////////////////////////
//funktion för att hämta nåt av nrf's register
uint8_t GetReg(uint8_t reg)
{
//andvändning: USART_Transmit(GetReg(STATUS)); //där status är registret du vill kolla
_delay_us(10);
CLEARBIT(PORTB, 2); //CSN low
_delay_us(10);
WriteByteSPI(R_REGISTER + reg); //Vilket register vill du läsa (nu med R_Register för att inget ska skrivas till registret)
_delay_us(10);
reg = WriteByteSPI(NOP); //Skicka NOP antalet byte som du vill hämta (oftast 1gång, men t.ex addr är 5 byte!) och spara isf inte i "reg" utan en array med en loop
_delay_us(10);
SETBIT(PORTB, 2); //CSN IR_High
return reg; // Returnerar registret förhoppningsvis med bit5=1 (tx_ds=lyckad sändning)
}
/*****************nrf-setup***************************/ //Ställer in nrf'en genoma att först skicka vilket register, sen värdet på registret.
uint8_t *WriteToNrf(uint8_t ReadWrite, uint8_t reg, uint8_t *val, uint8_t antVal) //tar in "ReadWrite" (W el R), "reg" (ett register), "*val" (en array) & "antVal" (antal integer i variabeln)
{
cli(); //disable global interrupt
if (ReadWrite == W) //W=vill skriva till nrf-en (R=läsa av den, R_REGISTER (0x00) ,så skiter i en else funktion)
{
reg = W_REGISTER + reg; //ex: reg = EN_AA: 0b0010 0000 + 0b0000 0001 = 0b0010 0001
}
//Static uint8_t för att det ska gå att returnera en array (lägg märke till "*" uppe på funktionen!!!)
static uint8_t ret[dataLen]; //antar att det längsta man vill läsa ut när man kallar på "R" är dataleng-långt, dvs använder man bara 1byte datalengd å vill läsa ut 5byte RF_Adress så skriv 5 här ist!!!
_delay_us(10); //alla delay är så att nrfen ska hinna med! (microsekunder)
CLEARBIT(PORTB, 2); //CSN low = nrf-chippet börjar lyssna
_delay_us(10);
WriteByteSPI(reg); //första SPI-kommandot efter CSN-låg berättar för nrf'en vilket av dess register som ska redigeras ex: 0b0010 0001 write to registry EN_AA
_delay_us(10);
int i;
for(i=0; i<antVal; i++)
{
if (ReadWrite == R && reg != W_TX_PAYLOAD)
{
ret[i]=WriteByteSPI(NOP); //Andra och resten av SPI kommandot säger åt nrfen vilka värden som i det här fallet ska läsas
_delay_us(10);
}
else
{
WriteByteSPI(val[i]); //Andra och resten av SPI kommandot säger åt nrfen vilka värden som i det här fallet ska skrivas till
_delay_us(10);
}
}
SETBIT(PORTB, 2); //CSN IR_High = nrf-chippet slutar lyssna
sei(); //enable global interrupt
return ret; //returnerar en array
}
//initierar nrf'en (obs nrfen måste vala i vila när detta sker CE-låg)
void nrf24L01_init(void)
{
_delay_ms(100); //allow radio to reach power down if shut down
uint8_t val[5]; //en array av integers som skickar värden till WriteToNrf-funktionen
//EN_AA - (auto-acknowledgements) - Transmittern får svar av recivern att packetet kommit fram, grymt!!! (behöver endast vara enablad på Transmittern!)
//Kräver att Transmittern även har satt SAMMA RF_Adress på sin mottagarkanal nedan ex: RX_ADDR_P0 = TX_ADDR
val[0]=0x01; //ger första integern i arrayen "val" ett värde: 0x01=EN_AA på pipe P0.
WriteToNrf(W, EN_AA, val, 1); //W=ska skriva/ändra nåt i nrfen, EN_AA=vilket register ska ändras, val=en array med 1 till 32 värden som ska skrivas till registret, 1=antal värden som ska läsas ur "val" arrayen.
//SETUP_RETR (the setup for "EN_AA")
val[0]=0x2F; //0b0010 00011 "2" sets it up to 750uS delay between every retry (at least 500us at 250kbps and if payload >5bytes in 1Mbps, and if payload >15byte in 2Mbps) "F" is number of retries (1-15, now 15)
WriteToNrf(W, SETUP_RETR, val, 1);
//Väljer vilken/vilka datapipes (0-5) som ska vara igång.
val[0]=0x01;
WriteToNrf(W, EN_RXADDR, val, 1); //enable data pipe 0
//RF_Adress width setup (hur många byte ska RF_Adressen bestå av? 1-5 bytes) (5bytes säkrare då det finns störningar men långsammare dataöverföring) 5addr-32data-5addr-32data....
val[0]=0x03;
WriteToNrf(W, SETUP_AW, val, 1); //0b0000 00011 motsvarar 5byte RF_Adress
//RF channel setup - väljer frekvens 2,400-2,527GHz 1MHz/steg
val[0]=0x01;
WriteToNrf(W, RF_CH, val, 1); //RF channel registry 0b0000 0001 = 2,401GHz (samma på TX å RX)
//RF setup - väljer effekt och överföringshastighet
val[0]=0x07;
WriteToNrf(W, RF_SETUP, val, 1); //00000111 bit 3="0" ger lägre överföringshastighet 1Mbps=Längre räckvidd, bit 2-1 ger effektläge hög (-0dB) ("11"=(-18dB) ger lägre effekt =strömsnålare men lägre range)
//RX RF_Adress setup 5 byte - väljer RF_Adressen på Recivern (Måste ges samma RF_Adress om Transmittern har EN_AA påslaget!!!)
int i;
for(i=0; i<5; i++)
{
val[i]=0x12; //RF channel registry 0b10101011 x 5 - skriver samma RF_Adress 5ggr för att få en lätt och säker RF_Adress (samma på transmitterns chip!!!)
}
WriteToNrf(W, RX_ADDR_P0, val, 5); //0b0010 1010 write registry - eftersom vi valde pipe 0 i "EN_RXADDR" ovan, ger vi RF_Adressen till denna pipe. (kan ge olika RF_Adresser till olika pipes och därmed lyssna på olika transmittrar)
//TX RF_Adress setup 5 byte - väljer RF_Adressen på Transmittern (kan kommenteras bort på en "ren" Reciver)
//int i; //återanvänder föregående i...
for(i=0; i<5; i++)
{
val[i]=0x12; //RF channel registry 0b10111100 x 5 - skriver samma RF_Adress 5ggr för att få en lätt och säker RF_Adress (samma på Reciverns chip och på RX-RF_Adressen ovan om EN_AA enablats!!!)
}
WriteToNrf(W, TX_ADDR, val, 5);
// payload width setup - Hur många byte ska skickas per sändning? 1-32byte
val[0]=dataLen; //"0b0000 0001"=1 byte per 5byte RF_Adress (kan välja upp till "0b00100000"=32byte/5byte RF_Adress) (definierat högst uppe i global variabel!)
WriteToNrf(W, RX_PW_P0, val, 1);
//CONFIG reg setup - Nu är allt inställt, boota upp nrf'en och gör den antingen Transmitter lr Reciver
val[0]=0x1E; //0b0000 1110 config registry bit "1":1=power up, bit "0":0=transmitter (bit "0":1=Reciver) (bit "4":1=>mask_Max_RT,dvs IRQ-vektorn reagerar inte om sändningen misslyckades.
WriteToNrf(W, CONFIG, val, 1);
//device need 1.5ms to reach standby mode
_delay_ms(100);
//sei();
}
void ChangeAddress(uint8_t adress)
{
_delay_ms(100);
uint8_t val[5];
//RX RF_Adress setup 5 byte - väljer RF_Adressen på Recivern (Måste ges samma RF_Adress om Transmittern har EN_AA påslaget!!!)
int i;
for(i=0; i<5; i++)
{
val[i]=adress; //RF channel registry 0b10101011 x 5 - skriver samma RF_Adress 5ggr för att få en lätt och säker RF_Adress (samma på transmitterns chip!!!)
}
WriteToNrf(W, RX_ADDR_P0, val, 5); //0b0010 1010 write registry - eftersom vi valde pipe 0 i "EN_RXADDR" ovan, ger vi RF_Adressen till denna pipe. (kan ge olika RF_Adresser till olika pipes och därmed lyssna på olika transmittrar)
//TX RF_Adress setup 5 byte - väljer RF_Adressen på Transmittern (kan kommenteras bort på en "ren" Reciver)
//int i; //återanvänder föregående i...
for(i=0; i<5; i++)
{
val[i]=adress; //RF channel registry 0b10111100 x 5 - skriver samma RF_Adress 5ggr för att få en lätt och säker RF_Adress (samma på Reciverns chip och på RX-RF_Adressen ovan om EN_AA enablats!!!)
}
WriteToNrf(W, TX_ADDR, val, 5);
_delay_ms(100);
}
/////////////////////////////////////////////////////
/*****************Funktioner***************************/ //Funktioner som används i main
//Resettar nrf'en för ny kommunikation
void reset(void)
{
_delay_us(10);
CLEARBIT(PORTB, 2); //CSN low
_delay_us(10);
WriteByteSPI(W_REGISTER + STATUS); //
_delay_us(10);
WriteByteSPI(0b01110000); //radedrar alla irq i statusregistret (för att kunna lyssna igen)
_delay_us(10);
SETBIT(PORTB, 2); //CSN IR_High
}
//Reciverfunktioner
/*********************Reciverfunktioner********************************/
//öppnar Recivern och "Lyssnar" i 1s
void receive_payload(void)
{
sei(); //Enable global interrupt
SETBIT(PORTB, 1); //CE IR_High = "Lyssnar"
_delay_ms(1000); //lyssnar i 1s och om mottaget går int0-interruptvektor igång
CLEARBIT(PORTB, 1); //ce låg igen -sluta lyssna
cli(); //Disable global interrupt
}
//Sänd data
void transmit_payload(uint8_t * W_buff)
{
WriteToNrf(R, FLUSH_TX, W_buff, 0); //skickar 0xE1 som flushar registret för att gammal data inte ska ligga å vänta på att bli skickad när man vill skicka ny data! R står för att W_REGISTER inte ska läggas till. skickar inget kommando efterråt eftersom det inte behövs! W_buff[]står bara där för att en array måste finnas där...
WriteToNrf(R, W_TX_PAYLOAD, W_buff, dataLen); //skickar datan i W_buff till nrf-en (obs går ej att läsa w_tx_payload-registret!!!)
sei(); //enable global interrupt- redan på!
//USART_Transmit(GetReg(STATUS));
_delay_ms(10); //behöver det verkligen vara ms å inte us??? JAAAAAA! annars funkar det inte!!!
SETBIT(PORTB, 1); //CE hög=sänd data INT0 interruptet körs när sändningen lyckats och om EN_AA är på, också svaret från recivern är mottagen
_delay_us(20); //minst 10us!
CLEARBIT(PORTB, 1); //CE låg
_delay_ms(10); //behöver det verkligen vara ms å inte us??? JAAAAAA! annars funkar det inte!!!
//cli(); //Disable global interrupt... ajabaja, då stängs USART_RX-lyssningen av!
}
/////////////////////////////////////////////////////
int main(void)
{
clockprescale();
usart_init();
InitSPI();
ioinit();
INT0_interrupt_init();
USART_interrupt_init();
nrf24L01_init();
SETBIT(PORTB,0); //För att se att dioden fungerar!
_delay_ms(1000);
CLEARBIT(PORTB,0);
while(1)
{
//Wait for USART-interrupt to send data...
}
return 0;
}
ISR(INT0_vect) //vektorn som går igång när transmit_payload lyckats sända eller när receive_payload fått data OBS: då Mask_Max_rt är satt i config registret så går den inte igång när MAX_RT är uppnåd å sändninge nmisslyckats!
{
cli(); //Disable global interrupt
CLEARBIT(PORTB, 1); //ce låg igen -sluta lyssna/sända
SETBIT(PORTB, 0); //led on
_delay_ms(500);
CLEARBIT(PORTB, 0); //led off
//Receiver function to print out on usart:
//data=WriteToNrf(R, R_RX_PAYLOAD, data, dataLen); //läs ut mottagen data
//reset();
//
//for (int i=0;i<dataLen;i++)
//{
//USART_Transmit(data[i]);
//}
//
sei();
}
ISR(USART_RX_vect) ///Vector that triggers when computer sends something to the Atmega88
{
uint8_t W_buffer[dataLen]; //Creates a buffer to receive data with specified length (ex. dataLen = 5 bytes)
int i;
for (i=0;i<dataLen;i++)
{
W_buffer[i]=USART_Receive(); //receive the USART
USART_Transmit(W_buffer[i]); //Transmit the Data back to the computer to make sure it was correctly received
//This probably should wait until all the bytes is received, but works fine in to send and receive at the same time... =)
}
reset(); //reset irq - kan skicka data på nytt
if (W_buffer[0]=='9') //om projektorduk
{
ChangeAddress(0x13); //change address to send to different receiver
transmit_payload(W_buffer); //Sänder datan
ChangeAddress(0x12); //tillbaka till ultimata fjärrisen
}
else
{
transmit_payload(W_buffer); //Sänder datan
}
USART_Transmit('#'); //visar att chipet mottagit datan...
}
@torhans
Copy link

torhans commented Apr 18, 2016

Hi Karl

Thank you for sharing this code.

You have not written any terms of copyright. I'm hoping to (modify it and) include it in a fork of OpenHR20, which is released under GPL.

By the way where have you obtained nRF24L01.h? The version I'm using from TMRh20/RF24 is slightly different.

Best Regards
Torben

@yerih
Copy link

yerih commented Jul 6, 2016

I have a problem with receiver radio. i have do everything that you explain in Blogspot2013 but no result! this is my codes:

Transmitter code
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void init_nRF24L01()
{
delay(100); // allow the radio to reach power-down if the shutdown
uint8_t val[5]; // an array of integers that sends values ??to WriteToNrf function

    //EN_AA - (enable auto-acknowledgments) - Transmitter gets automatic response from receiver when successful transmission! (lovely function!)
    //Only works if Transmitter has identical RF_Adress on its channel ex: RX_ADDR_Po = TX_ADDR
    val[0]=0x01; //set value
    WriteToNrf(W, EN_AA, val, 1); //N=write mode, EN_AA=register to write to, val=data to write, 1=number of data bytes.

    //Choose number of enabled data pipes (1-5)
    val[0]=0x01;
    WriteToNrf(W, EN_RXADDR, val, 1); //enable data pipe 0

    //RF_Adress width setup (how many bytes is the receiver address, the more the merrier 1-5)
    val[0]=0x03; //0b0000 00011 = 5 bytes RF_Adress
    WriteToNrf(W, SETUP_AW, val, 1);

    // RF channel setup - select the frequency from 2.400 to 2.527 GHz 1MHz/steg
    val[0] = 0x01 ;
    WriteToNrf(W,RF_CH,val,1);  // RF channel registry 0b0000 0001 = 2.401 GHz (same on the TX RX)

    //RF setup - choose power mode and data speed. Here is the diference with the (+) version!!!
    val[0]=0x07; //00000111 bit 3="0" 1Mbps=longer range, bit 2-1 power mode ("11" = -odB ; "00"=-18dB)
    WriteToNrf(W, RF_SETUP, val, 1);

    //RF_Adress setup 5 byte - Set Receiver address (set RX_ADDR_Po = TX_ADDR if EN_AA is enabled!!!)
    int i=0;
    for(i=0; i<5; i++){
            val[i]=0x12; //ox12 x 5 to get a long and secure address.
    }
    WriteToNrf(W, RX_ADDR_P0, val, 5); //since we chose pipe 0 on EN_RXADDR we give this address to that channel.
    //Here you can give different addresses to different channels (if they are enabled in EN_RXADDR) to listen on several different transmitters)

    //TX RF_Adress setup 5 byte - Set Transmitter address (not used in a receiver but can be set anyway)
    for(i=0; i<5; i++){
            val[i]=0x12; //ox12 x 5 - same on the Receiver chip and the RX-RF_Address above if EN_AA is enabled!!!
    }
    WriteToNrf(W, TX_ADDR, val, 5);

    //Payload width Setup - 1-32byte (how many bytes to send per transmission)
    val[0]=0x05; //Send 5 bytes per package this time (same on receiver and transmitter)
    WriteToNrf(W,RX_PW_P0,val,1);

    val[0]=0x2F; //0b00l0 00011 "2" sets it up to 7SouS delay between every retry (at least Seeus at 25okbps and if payload >5bytes in 1Hbps,
    //and if payload >1Sbyte in 2Hbps) "F" is number of retries (1-15, now 15)
    WriteToNrf(W, SETUP_RETR, val, 1);

    //CONFIG reg setup - Now it's time to boot up the Qgf and choose if it's suppose to be a transmitter or receiver
    val[0]=0x3E; //0b0001 1110 - bit 0="0":transmitter bit 0="1":Receiver, bit 1="1"=power up,
    //bit 4="1"= mask_Max_RT i.e. IRQ-interrupt is not triggered if transmission failed.
    WriteToNrf(W, CONFIG, val, 1);

    //device need 1.5ms to reach standby mode (CE=low)
    delay(100);

}

uint8_t WriteToNrf(uint8_t ReadWrite, uint8_t reg, uint8_t *val, uint8_t antVal)
{
/
Esta funcion transfiere es capaz de leer y escribir en el nRF y deberia ser
capaz de ejecutar un arreglo de enteros y retornar un arreglo de enteros*/
if(ReadWrite == W) //Si es "W", significa que escribiras un registro
{
reg = W_REGISTER + reg;
}

//Crea un arreglo para ser retornado al final de la funcion
static uint8_t ret[32];

delayMicroseconds(10);
CLEARBIT(PORTB, 2); //CSN low - nRF empieza a escuchar por comandoo
delayMicroseconds(10);
WriteByteSPI(reg); //setea el nRF para modo escritura o de lectura del registro "reg""
delayMicroseconds(10);

int i;
for(i = 0; i< antVal; i++)
{
if(ReadWrite == R && reg != W_TX_PAYLOAD) //Lectura de registro?
{
ret[i] = WriteByteSPI(NOP); //envia comando "NOP" para leer los datos
delayMicroseconds(10);
}

else
{
  WriteByteSPI(val[i]);     //envia el comando al nRF 
  delayMicroseconds(10);
}

}
SETBIT(PORTB, 2); //CSN High - nRF vuelve a su estado de reposo
return ret; //Retorna el arreglo
}

void transmit_payload(uint8_t *W_buff)
{

WriteToNrf(R, FLUSH_TX, W_buff, 0);
WriteToNrf(R, W_TX_PAYLOAD, W_buff, 5);
//Serial.println(GetReg(FIFO_STATUS));
CE_HIGH; //CE high
delayMicroseconds(20);
CE_LOW; //CE low
//Serial.println(GetReg(FIFO_STATUS));
}

void reset(void)
{
delayMicroseconds(5);
CLEARBIT(PORTB, 2); //CSN LOW
delayMicroseconds(5);
WriteByteSPI(W_REGISTER + STATUS); //Escribe en el registro STATUS
delayMicroseconds(5);
WriteByteSPI(0x70); //Resetea todos los IRQ del STTATUS register
delayMicroseconds(5);
SETBIT(PORTB, 2); //CSN HIGH.
}

uint8_t RF_MOD_TX()
{
uint8_t *var;
var = WriteToNrf(R, CONFIG, var, 1); //Lee el registro CONFIG y almacena el resultado
*var &= PRIM_TX; //Colocar en modo PRIM_TX (bit0 LOW)
WriteToNrf(W, CONFIG, var, 1); //Escribe en el registro config
}

uint8_t RF_MOD_RX()
{
uint8_t *var;
var = WriteToNrf(R, CONFIG, var, 1); //Lee el registro CONFIG y almacena el resultado
*var |= PRIM_RX; //Colocar en modo PRIM_RX (bit0 HIGH)
WriteToNrf(W, CONFIG, var, 1); //Escribe en el registro config

}

ISR(TIMER0_COMPA_vect)
{
cli();

if(estado<2)
{

int_timer0++;
if(int_timer0 == 31)
{
  int_timer0 = 0;
  estado = 1;

}

}

sei();
}

void init_timer0(byte i)
{
if(i == 1)
{

//Configuracion de Timer para operacion normal
//set timer0 interrupt at 2kHz
TCCR0A = 0;// set entire TCCR0A register to 0
TCCR0B = 0;// same for TCCR0B
TCNT0 = 0;//initialize counter value to 0
// set compare match register for 2khz increments
OCR0A = 99;// = (16_10^6) / (10000_8) - 1 (must be <256)
// turn on CTC mode
TCCR0A |= (1 << WGM01);
// Set CS21 and CS20 bits for 8 prescaler
TCCR0B |= (0 << CS22) | (1 << CS21) | (0 << CS20);
// enable timer compare interrupt
TIMSK0 |= (1 << OCIE0A);
TIMSK0 &= ~(1 << OCIE0B);
TIMSK0 &= ~(1 << TOIE0);

}
else if(i == 2)
{
//Configuracion de Timer para operacion: estabilizacion
//set timer0 interrupt
TCCR0A = 0;// set entire TCCR0A register to 0
TCCR0B = 0;// same for TCCR0B
TCNT0 = 0;//initialize counter value to 0
// set compare match register for 2khz increments
OCR0A = 255;// = (16_10^6) / (10000_8) - 1 (must be <256)
// turn on CTC mode
TCCR0A |= (1 << WGM01);
// Set CS21 and CS20 bits for 8 prescaler
TCCR0B |= (1 << CS22) | (0 << CS21) | (1 << CS20);
// enable timer compare interrupt
TIMSK0 |= (1 << OCIE0A);
TIMSK0 &= ~(1 << OCIE0B);
TIMSK0 &= ~(1 << TOIE0);
}
}

void state_timer0(byte i)
{
if(i == 0)
{
TIMSK0 &= ~(1<<OCIE0A); //Desactiva interrupcion timer
}
else if(i == 1)
{
TIMSK0 |= (1<<OCIE0A); //Activa interrupcion timer
}

}

void loop()
{

    //Inicializacion de perifericos

    initSPI();
    //transmit_init();
    init_nRF24L01();
    //init_interrupt();

    //Inicializacion de variables
    uint8_t w_buf[5], dato;
    //estado = 0;
    RF_MOD_TX();

    //Sincronizacion
    while (1/*estado == 0*/)
    {
        //PORTC ^= (1<<PC3);
        w_buf[0] = 0xAA;
        w_buf[1] = 0xAA;
        w_buf[2] = 0xAA;
        w_buf[3] = 0xAA;
        w_buf[4] = 0xAA;
        //Serial.println(GetReg(STATUS));
        transmit_payload(w_buf);
        //Serial.println(GetReg(STATUS));
        reset();
    }

}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Receiver code:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void init_nRF24L01()
{
delay(100); // allow the radio to reach power-down if the shutdown
uint8_t val[5]; // an array of integers that sends values ??to WriteToNrf function

    //EN_AA - (enable auto-acknowledgments) - Transmitter gets automatic response from receiver when successful transmission! (lovely function!)
    //Only works if Transmitter has identical RF_Adress on its channel ex: RX_ADDR_Po = TX_ADDR
    val[0]=0x01; //set value
    WriteToNrf(W, EN_AA, val, 1); //N=write mode, EN_AA=register to write to, val=data to write, 1=number of data bytes.

    //Choose number of enabled data pipes (1-5)
    val[0]=0x01;
    WriteToNrf(W, EN_RXADDR, val, 1); //enable data pipe 0

    //RF_Adress width setup (how many bytes is the receiver address, the more the merrier 1-5)
    val[0]=0x03; //0b0000 00011 = 5 bytes RF_Adress
    WriteToNrf(W, SETUP_AW, val, 1);

    // RF channel setup - select the frequency from 2.400 to 2.527 GHz 1MHz/steg
    val[0] = 0x01 ;
    WriteToNrf(W,RF_CH,val,1);  // RF channel registry 0b0000 0001 = 2.401 GHz (same on the TX RX)

    //RF setup - choose power mode and data speed. Here is the diference with the (+) version!!!
    val[0]=0x07; //00000111 bit 3="0" 1Mbps=longer range, bit 2-1 power mode ("11" = -odB ; "00"=-18dB)
    WriteToNrf(W, RF_SETUP, val, 1);

    //RF_Adress setup 5 byte - Set Receiver address (set RX_ADDR_Po = TX_ADDR if EN_AA is enabled!!!)
    int i=0;
    for(i=0; i<5; i++){
            val[i]=0x12; //ox12 x 5 to get a long and secure address.
    }
    WriteToNrf(W, RX_ADDR_P0, val, 5); //since we chose pipe 0 on EN_RXADDR we give this address to that channel.
    //Here you can give different addresses to different channels (if they are enabled in EN_RXADDR) to listen on several different transmitters)

    //TX RF_Adress setup 5 byte - Set Transmitter address (not used in a receiver but can be set anyway)
    for(i=0; i<5; i++){
            val[i]=0x12; //ox12 x 5 - same on the Receiver chip and the RX-RF_Address above if EN_AA is enabled!!!
    }
    WriteToNrf(W, TX_ADDR, val, 5);

    //Payload width Setup - 1-32byte (how many bytes to send per transmission)
    val[0]=0x05; //Send 5 bytes per package this time (same on receiver and transmitter)
    WriteToNrf(W,RX_PW_P0,val,1);

    val[0]=0x2F; //0b00l0 00011 "2" sets it up to 7SouS delay between every retry (at least Seeus at 25okbps and if payload >5bytes in 1Hbps,
    //and if payload >1Sbyte in 2Hbps) "F" is number of retries (1-15, now 15)
    WriteToNrf(W, SETUP_RETR, val, 1);

    //CONFIG reg setup - Now it's time to boot up the Qgf and choose if it's suppose to be a transmitter or receiver
    val[0]=0x3E; //0b0001 1110 - bit 0="0":transmitter bit 0="1":Receiver, bit 1="1"=power up,
    //bit 4="1"= mask_Max_RT i.e. IRQ-interrupt is not triggered if transmission failed.
    WriteToNrf(W, CONFIG, val, 1);

    //device need 1.5ms to reach standby mode (CE=low)
    delay(100);

}

uint8_t RF_MOD_RX()
{
uint8_t *var;
var = WriteToNrf(R, CONFIG, var, 1); //Lee el registro CONFIG y almacena el resultado
*var |= PRIM_RX; //Colocar en modo PRIM_RX (bit0 HIGH)
WriteToNrf(W, CONFIG, var, 1); //Escribe en el registro config

}

uint8_t RF_MOD_TX()
{
uint8_t *var;
var = WriteToNrf(R, CONFIG, var, 1); //Lee el registro CONFIG y almacena el resultado
*var &= PRIM_TX; //Colocar en modo PRIM_TX (bit0 LOW)

WriteToNrf(W, CONFIG, var, 1); //Escribe en el registro config

}

void transmit_payload(uint8_t *W_buff)
{
WriteToNrf(R, FLUSH_TX, W_buff, 0); //Se usa "R" para enviar un comando al nRF.
WriteToNrf(R, W_TX_PAYLOAD, W_buff, 1);

delayMicroseconds(10); //Tiempo de espera para que el nRF cargue el payload.
SETBIT(PORTB, 1); //CE high
delayMicroseconds(5);
CLEARBIT(PORTB, 1); //CE low
}

void receive_payload(void)
{
SETBIT(PORTB, 1); //CE IR_HIGH = leer datos.
//delayMicroseconds(70);
delay(1000);
CLEARBIT(PORTB, 1); //CE LOW - deja la lectura de datos.
}

void init_interrupt(){
cli(); // switch interrupts off while messing with their settings
EICRA = 0x0A; // Falling edge. Interrupcion por Flancos de bajada.
EIMSK = 0x00; // Deshabilita interrupciones externas standard.
PCICR = 0x03; // Habilita las interrupciones 8 - 14 y 1 - 7.
PCMSK0 = 0x01; // Habilita unicamente la interrupcion 0.
PCMSK1 = 0b00000100; // Habilita unicamente la interrupcion 10.
sei(); // turn interrupts back on
}

//Rutina de interrupcion externa (Boton de Llamada)
ISR(PCINT1_vect)
{
/Segun el manual del AVR, La interrupcion
por cambio de puerto se genera por cualquier
cambio generado en el pin de interrupcion externa
/

cli(); //desactiva interrupciones
int_count1++;
if(int_count1 == 2)
{
int_boton = 1; //Cambio de estado: reposo - llamada
int_count1 = 0; //reinicio de contador
PORTD ^= (1<<PB6); //toggle de bit 0 de puerto B.
}
sei(); //Activa interrupciones
}

//Interrupcion para IRQ
///*
ISR(PCINT0_vect)
{
//Segun el manual del AVR, La interrupcion
//por cambio de puerto se genera por cualquier
//cambio generado en el pin de interrupcion externa
int_count0++;
cli(); //desactiva interrupciones
if(int_count0 == 2)
{
PORTD ^= (1<<PD7);
int_count0 = 0;
CLEARBIT(PORTB, 1); //CE LOW - deja de leer o enviar.

   CSN_LOW;
   //WriteByteSPI(R_RX_PAYLOAD);


   WriteByteSPI(R_RX_PAYLOAD);

   //recibido = WriteToNrf(R, R_RX_PAYLOAD, recibido, 1); //Lee el dato recibido
   CSN_HIGH;

   //*arreglo[0] = WriteByteSPI(0xfe);
}

sei(); //Activa interrupciones
}
///
/

ISR(PCINT0_vect)
{
int_count0++;
cli(); //desactiva interrupciones
if(int_count0 == 2)
{
uint8_t _val[3];
cli();
CE_LOW;
*val = WriteToNrf(R,R_RX_PAYLOAD,_val,3);
delay(100);
for(int i=0;i<3;i++)
{
Serial.println(*val[i]);
}
reset();
delay(1);
sei();
}
sei();
}
*/

uint8_t GetReg(uint8_t reg)
{
delayMicroseconds(10);
CLEARBIT(PORTB, 2); //CSN LOW
delayMicroseconds(5);
WriteByteSPI(R_REGISTER + reg);
delayMicroseconds(10);
reg = WriteByteSPI(NOP);
delayMicroseconds(10);
SETBIT(PORTB, 2);
return reg;
}

void reset(void)
{
uint8_t val;
*val=0x70;
WriteToNrf(R, FLUSH_RX, val, 1);
CLEARBIT(PORTB, 2); //CSN LOW
delayMicroseconds(5);
WriteByteSPI(W_REGISTER + STATUS); //Escribe en el registro STATUS
delayMicroseconds(5);
WriteByteSPI(0x70); //Resetea todos los IRQ del STATUS register
delayMicroseconds(5);
SETBIT(PORTB, 2); //CSN HIGH.
/

WriteToNrf(W, STATUS, val, 1);
delay(1000);
*val = 0x40;
WriteToNrf(W, STATUS, val, 1);
*/

}

void state_timer0(byte i)
{
if(i == 0)
{
TIMSK0 &= ~(1<<OCIE0A); //Desactiva interrupcion timer
}
else if(i == 1)
{
TIMSK0 |= (1<<OCIE0A); //Activa interrupcion timer
}

}

void init_timer0(byte i)
{
if(i == 1)
{

//Configuracion de Timer para operacion normal
//set timer0 interrupt at 2kHz
TCCR0A = 0;// set entire TCCR0A register to 0
TCCR0B = 0;// same for TCCR0B
TCNT0 = 0;//initialize counter value to 0
// set compare match register for 2khz increments
OCR0A = 99;// = (16_10^6) / (10000_8) - 1 (must be <256)
// turn on CTC mode
TCCR0A |= (1 << WGM01);
// Set CS21 and CS20 bits for 8 prescaler
TCCR0B |= (0 << CS22) | (1 << CS21) | (0 << CS20);
// enable timer compare interrupt
TIMSK0 |= (1 << OCIE0A);
TIMSK0 &= ~(1 << OCIE0B);
TIMSK0 &= ~(1 << TOIE0);

}
else if(i == 2)
{
//Configuracion de Timer para operacion: estabilizacion
//set timer0 interrupt
TCCR0A = 0;// set entire TCCR0A register to 0
TCCR0B = 0;// same for TCCR0B
TCNT0 = 0;//initialize counter value to 0
// set compare match register for 2khz increments
OCR0A = 255;// = (16_10^6) / (10000_8) - 1 (must be <256)
// turn on CTC mode
TCCR0A |= (1 << WGM01);
// Set CS21 and CS20 bits for 8 prescaler
TCCR0B |= (1 << CS22) | (0 << CS21) | (1 << CS20);
// enable timer compare interrupt
TIMSK0 |= (1 << OCIE0A);
TIMSK0 &= ~(1 << OCIE0B);
TIMSK0 &= ~(1 << TOIE0);
}
}

ISR(TIMER0_COMPA_vect)
{
cli();

//condicion para etapa de sincronizacion
if(estado<2)
{

int_timer0++;
if(int_timer0 == 31)
{
  int_timer0 = 0;
  estado = 2;

}

}

sei();
}

void loop() {

//Inicializacion de perifericos
initSPI();
init_nRF24L01();

//init_interrupt();
//receive_init();
CE_LOW;

//Configura el NRF en Modo Rx
RF_MOD_RX();

//Inicializacion de variables
//int_boton = 0; int_count0 = int_count1 = 0;
*recibido = 0;
uint8_t dato[5], w_buf[5]; estado = 0;

while(1/estado == 0/)
{
//PORTC ^= (1<<PC3);
CE_HIGH;
//delay(1000);
delayMicroseconds(700);
CE_LOW;

while(1){Serial.println(GetReg(STATUS));}

CSN_LOW;
  //recibido = WriteToNrf(R,R_RX_PAYLOAD,recibido,1);
  dato[0] = ReadToNrf(R_RX_PAYLOAD);
  /*dato[1] = WriteByteSPI(NOP);
  dato[2] = WriteByteSPI(NOP);
  dato[3] = WriteByteSPI(NOP);
  dato[4] = WriteByteSPI(NOP);
  */
CSN_HIGH;
    Serial.println(dato[0]);
    /*Serial.println(dato[1]);
    Serial.println(dato[2]);
    Serial.println(dato[3]);
    Serial.println(dato[4]);
    */
    reset();

}

}

Help me please, from Venezuela!

@vaizq
Copy link

vaizq commented Mar 21, 2021

Thanks for the example. You should consider to use #defines to make code easier to read.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment