Trasmissione dati Wireless con il modulo nRF24l01+

lm7812 alimentatori stabilizzatiIn Questo Articolo Viene Spiegato Come Avviare una Trasmissione Wireless tra Microcontrollori Grazie ai moduli nRF24l01+, Presentando Codice e Schema…

 
 
 
 




 

INTRO

Il modulo nRF24l01+ è un transceiver, permette quindi di essere usato come ricevitore o come trasmettitore, oppure in entrambi i modi non contemporaneamente. Posso quindi inviare un dato, cambiare la modalità e ricevere un dato. Nel codice seguente si va a convertire un valore dall’ADC e poi questo valore viene prima trasmesso e poi visualizzato sul microcontrollore che funge da trasmettitore. Nel ricevitore arriva il dato e viene visualizzato con dei led in uscita. È un codice molto semplice che permette di trasmettere un dato, la cui fonte non deve essere per forza il valore dell’ADC, può essere un valore fisso, il valore di un conteggio, un valore selezionabile da alcuni tasti, il valore di una memoria o altri valori.

Nel caso seguente inoltre il ricevitore è sempre ricevitore e il trasmettitore funziona sempre da trasmettitore, però, sia nel codice del ricevitore che nel codice del trasmettitore ci sono le inizializzazioni per fare funzionare il modulo in tutte e due le modalità. Quindi in questo caso il trasmettitore è inizializzato una volta sola da trasmettitore e poi invia i dati di continuo, ma nulla mi vieta di inizializzarlo come trasmettitore, trasmettere un dato, inserire un piccolo delay e poi inizializzarlo in modalità ricevitore e aspettare l’arrivo di un dato. Stesso discorso per il ricevitore, posso inizializzarlo come ricevitore, riceve il dato, elaborarlo e per esempio ritrasmetterlo.

Il codice è molto versatile e la prima parte è uguale per tutte e due le modalità, poi sta a voi richiamare in modo opportuno le funzioni presenti per fare ciò che desiderate.

Inoltre per la comunicazione, visto che è necessaria una comunicazione SPI per comunicare con il modulo, viene implementato un codice che emula la comunicazione SPI, quindi non è per forza necessario un microcontrollore con modulo SPI, anche se il microcontrollore da me usato ha questo modulo.

Per utilizzare il codice con un altro microcontrollore bisogna solo modificare i registri dell’ADC se si desidera utilizzare l’ADC e se i registri cambiano, e inoltre bisogna modificare le porte di I/O se queste differiscono dal microcontrollore da me utilizzato.
 





 
TRASMETTITORE

Il codice del per il modulo trasmettitore è il seguente:

sbit RF_IRQ_TRIS at TRISB2_bit;      //PIN RICHIESTA INTERRUPT
sbit RF_CS_TRIS at TRISC0_bit;       //PIN CHIP SELECT
sbit RF_CE_TRIS at TRISC2_bit;       //PIN ENABLE
sbit SCK_TRIS at TRISB1_bit;         //CLK SPI
sbit SDI_TRIS at TRISB0_bit;         //DATA IN
sbit SDO_TRIS at TRISC7_bit;         //DATA OUT
//----------------------------------------------------------------------------------------------------------------------------//
sbit IRQ_pin at RB2_bit;
sbit SS_pin at RC0_bit;
sbit CE_pin at RC2_bit;
sbit SCK_pin at RB1_bit;
sbit MOSI_pin at RC7_bit;
sbit MISO_pin at RB0_bit;
//----------------------------------------------------------------------------------------------------------------------------//
sbit Led at RC1_bit;                 //LED INDICAZIONE
//----------------------------------------------------------------------------------------------------------------------------//
#define R_REGISTER_cmd                0x00           //COMANDO DI SCRITTURA
#define W_REGISTER_cmd                0x20           //COMANDO DI LETTURA
#define R_RX_PL_WID_cmd               0x60           //LEGGI DIMENSIONE PAYLOAD
#define R_RX_PAYLOAD_cmd              0x61           //LEGGI PAYLOAD RICEVUTO
#define W_TX_PAYLOAD_cmd              0xA0           //SCRIVI PAYLOAD
#define W_ACK_PAYLOAD_cmd             0xA8           //ACK DEL PAYLOAD
#define W_TX_PAYLOAD_NO_ACK_cmd       0xB0           //DISABILITA AUTO ACK
#define FLUSH_TX_cmd                  0xE1           //SVUOTA LA FIFO DEL TX
#define FLUSH_RX_cmd                  0xE2           //SVUOTA LA FIFO DELL'RX
#define REUSE_TX_PL_cmd               0xE3           //RIUTILIZZA ULTIMO PAYLOAD INVIATO
#define NOP_cmd                       0xFF           //NESSUN COMANDO
//----------------------------------------------------------------------------------------------------------------------------//
#define CONFIG_reg                    0b00000000     //INTERRUPT , CRC, ON/OFF, TX MODE SETTING
#define EN_AA_reg                     0x01           //AUTO ACK PIPE
#define EN_RXADDR_reg                 0x02           //ABILITAZIONE INDIRIZZI RX
#define SETUP_AW_reg                  0x03           //NUMERO DI INDIRIZZI (5)
#define SETUP_RETR_reg                0x04           //SETTING TEMPO DI RITRASMISSIONE (250uS)
#define RF_CH_reg                     0x05           //CANALE 5
#define RF_SETUP_reg                  0b00000110     //settaggio velbitrate e potenza
#define STATUS_reg                    0x07           //REGISTRO DI STATO, INVIATO AL PIN MOSI
#define OBSERVE_TX_reg                0x08           //CONTA I LOST PACKET E I RETRASMITTED PACKET
#define RPD_reg                       0x09           //AVVERTE SE VI è UN TRASMETTITORE ATTIVO
#define RX_ADDR_P0_reg                0x0A           //RICEVE L'INDIRIZZO 0
#define RX_ADDR_P1_reg                0x0B           //
#define RX_ADDR_P2_reg                0x0C           //
#define RX_ADDR_P3_reg                0x0D           //
#define RX_ADDR_P4_reg                0x0E           //
#define RX_ADDR_P5_reg                0x0F           //
#define TX_ADDR_reg                   0x10           //TRASMISSIONE INDIRIZZI
#define RX_PW_P0_reg                  0x11           //NUMERO DI BYTE DEL PAYLOAD NEL DATA PIPE 0
#define RX_PW_P1_reg                  0x12           //
#define RX_PW_P2_reg                  0x13           //
#define RX_PW_P3_reg                  0x14           //
#define RX_PW_P4_reg                  0x15           //
#define RX_PW_P5_reg                  0x16           //
#define FIFO_STATUS_reg               0x17           //RIUTILIZZA TRASMISSIONE E STATO DELLA FIFO
#define DYNPD_reg                     0x1C           //ENABLE DYNAMIC PAYLOAD LENGHT
#define FEATURE_reg                   0x1D           //ABILITAZIONE MODALITà DYPLD, ACK, NO ACK

//----------------------------------------------------------------------------------------------------------------------------//
unsigned char x = 0;                                 //CONTIENE IL BYTE DA INVIARE

//----------------------------------------------------------------------------------------------------------------------------//
unsigned char nRF24L01_read()                        //questo sottoprogramma simula una comunicazione SPI
{
  unsigned char s = 0;                               //variabile per esegguire 8 esecuzioni
  unsigned char msg = 0;

  for(s = 0; s < 8; s++){                            //esegue 8 volte
      msg <<= 1;                                     //shifto msg
      SCK_pin = 1;                                   //attivo il clock
      delay_us(8);                                   //aspetto 8us (f spi 62Kbps)
      if(MISO_pin != 0)                              //se in=0
      {
        msg |= 1;                                    //azzero il bit di msg
      }
      SCK_pin = 0;                                   //disattivo il clock
      delay_us(8);
  }

  return msg;                                       //ritorno il valore letto
}

//----------------------------------------------------------------------------------------------------------------------------//
void nRF24L01_write(unsigned char d){                //sottoprogramma che simula una scrittura spi
  unsigned char s = 0;

  for(s = 0; s < 8; s++)                            //questo for serve a trasmettere sul pin mosi bit per bit il dato d
  {
    if((d & 0x80) != 0)
    {
      MOSI_pin = 1;                                 //se il bit scandito è 1 trasmetto 1
    }
    else
    {
      MOSI_pin = 0;                                 //altrimenti 0
    }
    d <<= 1;                                        //shifto d per inviare il prossimo bit
    SCK_pin = 1;                                    //creo un clock
    delay_us(8);
    SCK_pin = 0;
    delay_us(8);
  }
}

//----------------------------------------------------------------------------------------------------------------------------//
void register_write(unsigned char reg, unsigned char value)                //sottoprogramma per scrivere i registri
{
  SS_pin = 0;                                                              //indico che sto per inviare un comando
  nRF24L01_write((reg | W_REGISTER_cmd));                                  //maschero il registro con il comando per scrivere
  nRF24L01_write(value);                                                   //invio il valore che voglio scrivere nel registro
  SS_pin = 1;
  delay_us(8);
}

//----------------------------------------------------------------------------------------------------------------------------//
void write_command(unsigned char cmd){                                      //sottoprogramma scrittura comandi
  SS_pin = 0;                                                              //indico che voglio inviare un comando
  nRF24L01_write(cmd);                                                     //scrivo il comando cmd
  SS_pin = 1;                                                              //ripristino il pin ss
  delay_us(8);
}

//----------------------------------------------------------------------------------------------------------------------------//
unsigned char register_read(unsigned char reg)                            //sottoprogramma lettura registri
{
  unsigned char value = 0;

  SS_pin = 0;
  nRF24L01_write((reg | R_REGISTER_cmd));                                 //indico che voglio leggere e quale registro leggere
  value = nRF24L01_read();                                                //leggo il dato che il modulo mi manda
  SS_pin = 1;
  delay_us(8);

  return value;
}

//----------------------------------------------------------------------------------------------------------------------------//
void set_TX_RX_address(unsigned char *addr, unsigned char bytes, unsigned char reg)      //sottoprogramma per scrivere gli indirizzi
{
  unsigned char n = 0;

  SS_pin = 0;
  nRF24L01_write((reg | W_REGISTER_cmd));                                               //indico che voglio scrivere i registri
  for(n = 0; n < bytes; n++)
  {
    nRF24L01_write(addr[n]);                                                            //bytes contiene il numero di indirizzi
  }                                                                                     //scrivo gli n indirizzi
  SS_pin = 1;
  delay_us(8);
}

//----------------------------------------------------------------------------------------------------------------------------//
void flush_TX_RX()                                      //sottoprogramma per cancellare le FIFO
{
  register_write(STATUS_reg, 0x70);
  write_command(FLUSH_TX_cmd);
  write_command(FLUSH_RX_cmd);
}

//----------------------------------------------------------------------------------------------------------------------------//
void send_data(unsigned char bytes, unsigned char *value)        //sottoprogramma per inviare i dati
{
  unsigned char s = 0;

  flush_TX_RX();                                                //cancello le FIFO
  register_write(CONFIG_reg, 0x3A);                             //setto la modalità tx da CONFIG_reg e attivo il modulo

  SS_pin = 0;
  nRF24L01_write(W_TX_PAYLOAD_cmd);                             //scrivo nel registo di trasmissione i bit da inviare
  for(s = 0; s < bytes; s++)
  {
    nRF24L01_write(value[s]);
  }
  SS_pin = 1;
  delay_us(8);

  CE_pin = 1;                                                 //faccio partire l'invio, aspetto 60us e poi disattivo il modulo
  delay_us(60);
  CE_pin = 0;

  register_write(CONFIG_reg, 0x38); //En modo RX se quita
}

//----------------------------------------------------------------------------------------------------------------------------//
void receive_data(unsigned char bytes, unsigned char *value)         //sottoprogramma per la rx, indico numero di bytes del dato
{                                                                    //value contiene il dato letto
  unsigned char s = 0;

  SS_pin = 0;                                                        //scrivo il comando che indica che voglio leggere
  nRF24L01_write(R_RX_PAYLOAD_cmd);
  for (s = 0; s < bytes; s++)
  {
    value[s] = nRF24L01_read();                                      //leggo il dato che il modlo mi sta inviando
  }
  SS_pin = 1;
  delay_us(8);
}

//----------------------------------------------------------------------------------------------------------------------------//
void nrF24L01_init_TX()                                             //sottoprogramma per settare la modalità trasmissione
{
  unsigned char address[5] = {0x99, 0x99, 0x99, 0x99, 0x99};        //indirizzo a 5bytes, valore a piacere(devo coincidere con rx)

  CE_pin = 0;

  register_write(SETUP_RETR_reg, 0x00);                             //ritrasmissione disabilitata
  register_write(SETUP_AW_reg, 0x03);                               //setto che ho indirizzo a 5bytes
  register_write(RF_SETUP_reg, 0x0E);                               //potenza 0dbm e velocità 250Kbps
  register_write(RF_CH_reg, 0x09);                                  //setto il ch di tx, canale 9 in questo caso(rx deve avere uguale canale)
  register_write(EN_AA_reg, 0x00);                                  //auto ack disabilitato
  register_write(CONFIG_reg, 0x38);                                 //interrupt abilitati su TX_DS e MAX_RT, crc abilitato
  set_TX_RX_address(address, 5, TX_ADDR_reg);                       //inserisco gli indirizzi nell'apposito registro
  set_TX_RX_address(address, 5, RX_ADDR_P0_reg);
  flush_TX_RX();                                                    //cancella le fifo

  CE_pin = 1;
}

//----------------------------------------------------------------------------------------------------------------------------//
void nrF24L01_init_RX()                                             //programma per settare la modalità RX
{
  unsigned char address[5] = {0x99, 0x99, 0x99, 0x99, 0x99};        //indirizzo a 5bytes uguale a quello del TX

  CE_pin = 0;

  register_write(CONFIG_reg, 0x38);                                 //Crc abilitato, no interrup di ricezione
  register_write(SETUP_RETR_reg, 0x00);                             //ritrasmissione non richiesta
  register_write(SETUP_AW_reg, 0x03);                               //indirizzo a 5bytes
  register_write(RF_SETUP_reg, 0x0E);                               //potenza 0dbm e velocità 250Kbps
  register_write(RF_CH_reg, 0x09);                                  //canale9
  register_write(EN_AA_reg, 0x00);                                  //no ack
  register_write(RX_PW_P0_reg, 0x01);                               //dato a 1byte
  register_write(CONFIG_reg, 0x3B);                                 //setto il modulo come RX
  set_TX_RX_address(address, 5, TX_ADDR_reg);                       //scrivo gli indirizzi nell'apposito registro
  set_TX_RX_address(address, 5, RX_ADDR_P0_reg);
  flush_TX_RX();                                                    //cancello le fifo

  CE_pin = 1;
}

//----------------------------------------------------------------------------------------------------------------------------//
unsigned char get_Status_Reg()                       //sottoprogramma per leggere lo status register che mi fornisce info sul modulo
{
  return register_read(STATUS_reg);
}

//----------------------------------------------------------------------------------------------------------------------------//
unsigned char val = 0;

//----------------------------------------------------------------------------------------------------------------------------//
void main(){                                 //programma principale


TRISA     =  0x00;                         //configuro inizialmente tutte le porte uscite e le setto a 0
TRISB     =  0x00;
TRISC     =  0x00;
TRISD     =  0x00;
ADCON1 = 0x0F;
PORTA = 0X00;
PORTB = 0X00;
PORTC = 0x00;
LATD= 0;

//----------------------------------------------------------------------------------------------------------------------------//
RF_IRQ_TRIS = 1;                                 //configurazione I/O microprocessore per comunicare col modulo
RF_CS_TRIS = 0;
RF_CE_TRIS = 0;
SCK_TRIS = 0;
SDI_TRIS = 1;
SDO_TRIS = 0;

CE_pin = 0;
SS_pin = 0;
SCK_pin = 0;
MOSI_pin = 0;

LED = 1;
delay_ms(10);            //faccio lampeggiare una volta il led
LED = 0;

//----------------------------------------------------------------------------------------------------------------------------//
 delay_ms(10);

 nrF24L01_init_RX();                //inizializzo il modulo come rx

 delay_ms(100);                     //delay assestamento

//----------------------------------------------------------------------------------------------------------------------------//
 while(1){                          //ciclo infinito
  if(get_Status_Reg() == 0x40)      //controllo nello status register se il bit di segnalazione di dato ricevuto è alto
  {
    receive_data(1, &x);            //se si vado a leggere il dato
    LATD = x;                       //e in questo caso lo visualizzo sulle portD
  }
 }
 
}//fine main

Prima di tutto si va a definire come il modulo è connesso al microcontrollore, in particolare IRQ è connesso a RB2, CSN è connesso a RC0, CE a RC2, SCK a RB1, il pin di MOSI a RC7 e il MISO a RB0. Queste connessioni possono essere variate a piacimento. Inoltre si definisce LED il pin RC1 a quale verrà connesso un led per fornire alcune informazioni che poi vedremo.

Dopo di che vengono definiti tutti gli indirizzi dei registri e tutti i comandi, in questo modo scrivendo il nome del registro o del comando si ottiene il relativo codice, semplificando la lettura, la modifica e la scrittura del codice. Inoltre viene inizializzata la variabile che contiene il dato da inviare, in questo caso, mentre il dato ricevuto nel caso del codice del ricevitore.

Il primo sottoprogramma che viene creato è quello per simulare la lettura tramite SPI ed è chiamato “nRF24L01_read”. Questo sottoprogramma non riceve nessun dato ma restituisce il byte letto dopo la comunicazione. Nei commenti è spiegato passo passo come funziona la routine.

Il secondo sottoprogramma, molto simile al primo, emula la scrittura tramite SPI.

Le due successive routine servono per scrivere o leggere nei registri del modulo nRF24l01+. Per la scrittura in particolare si pone il pin SS a 0 per indicare una scrittura di un comando, poi si invia il comando di scrittura mascherato dall’indirizzo del registro che si vuole scrivere e successivamente si scrive il valore che si vuole inserire. La routine riceve quindi il nome del registro e il valore. Si esce dalla routine di scrittura ponendo SS=0 e inserendo un delay. La routine per la lettura è identica solo che si usa il comando di lettura.

La routine “write_command” riceve semplicemente un comando, richiama la routine di scrittura e scrive un comando verso il modulo nRF24l01+.

La routine “set_TX_RX_address” inserisce nell’apposito registro l’indirizzo, e vale sia per il ricevitore che il trasmettitore. Il valore dell’indirizzo viene scritto in seguito. La routine “flush_TX_RX” invece, va semplicemente a richiamare i comandi per cancellare le memorie fifo.

La routine principale è “send_data” ovvero quella che invia il dato. Questa routine riceve il dato da mandare e il numero di byte in cui è composto, prima di tutti cancella le memorie fifo poi setta la modalità TX e attiva il modulo. Ponendo a 0 il pin SS, scrivendo il comando W_TX_PAYLOAD_cmd e poi inviando i byte uno alla volta, riesco a scrivere i dati da trasmettere nella memoria del modulo. Poi pongo il pin SS a valore alto e poi creando un impulso di 60uS del pin CE invio il pacchetto. Infine spengo il modulo.

Nel codice del trasmettitore è presente anche la routine per ricevere i dati.

La routine per ricevere i dati ha bisogno di conoscere il numero di byte, dopo di che invia il comando per la lettura e si mette in ricezione dei dati, che inserisce nella variabile “value”.

Le due routine successive servono per inizializzare i registri del modulo nRF24l01+. Una routine è chiamata “nrF24L01_init_TX” e contiene l’inizializzazione degli indirizzi, poi dei registri e infine la cancellazione delle memorie fifo. La routine “nrF24L01_init_RX” invece esegue le stesse operazioni ma configura il modulo come ricevitore. Anche queste due routine insieme alla precedente e alla successiva, che serve per sapere se un dato è arrivato (get_Status_Reg), sono presenti in tutti e due i codici, ovvero sia nel codice del trasmettitore che del ricevitore, in modo tale da poter creare un transceiver.

Le istruzioni successive, partendo dall’inizializzazione della variabile che contiene il dato da inviare, servono solo nel caso si vuole trasmettere un dato a 1 byte che proviene dalla conversione analogico digitale da parte dell’ADC interno al PIC.

Quindi nel main del codice si vanno ad inizializzare ingressi e uscite, poi si inizializza il modulo come trasmettitore e l’ADC. Nel ciclo infinito invece si va a leggere il dato dall’ADC con una routine personalizzata, senza usare la libreria, questo dato viene mandato alla routine per la scrittura del dato nel modulo, poi viene visualizzato sulle uscite del PIC e prima di avviare di nuovo il ciclo infinito si inserisce un delay di un secondo.

Lo schema è il seguente:

Nrf24L01+ SCHEMA TRASMETTITORE TRASMITTER TX

Viene utilizzato un risuonatore al quarzo con frequenza di 8MHz insieme a due capacità da 22pF. La tensione di alimentazione è 3,3V che si può ottenere grazie un L7833 a partire da una tensione di 5V. il modulo è connesso al PIC come indicato nel codice. il PIC da me scelto è il PIC18F458 visto che ne avevo due identici, potete usare qualsiasi altro microcontrollore PIC che dispone di un numero opportuno di pin.

La resistenza R1 è una resistenza di pull-up per il pin di IRQ visto che ha un uscita open drain. La resistenza R3 evita che il PIC si resetti, mentre la resistenza RV1 con il condensatore C3 vanno a fare una partizione della tensione di alimentazione così da fornire un valore variabile tra 0 e VCC all’ingresso dell’ADC. Infine vi sono 8 led con le relative 8 resistenze serie in modo tale da visualizzare il valore convertito e il valore atteso sul ricevitore.

Realizzando su breadboard lo schema si ottiene:

nrf24l01+ trasmettitore trasmitter tx
 





 
RICEVITORE

Il codice del per il modulo ricevitore dispone di tutte le subroutine viste prime, ovviamente escluse quelle per l’ADC visto che è una funzione extra da me inserita. Quindi la prima parte del codice è identica a prima, cambia solo il main. Si avrà:

sbit RF_IRQ_TRIS at TRISB2_bit;      //PIN RICHIESTA INTERRUPT
sbit RF_CS_TRIS at TRISC0_bit;       //PIN CHIP SELECT
sbit RF_CE_TRIS at TRISC2_bit;       //PIN ENABLE
sbit SCK_TRIS at TRISB1_bit;         //CLK SPI
sbit SDI_TRIS at TRISB0_bit;         //DATA IN
sbit SDO_TRIS at TRISC7_bit;         //DATA OUT
//----------------------------------------------------------------------------------------------------------------------------//
sbit IRQ_pin at RB2_bit;
sbit SS_pin at RC0_bit;
sbit CE_pin at RC2_bit;
sbit SCK_pin at RB1_bit;
sbit MOSI_pin at RC7_bit;
sbit MISO_pin at RB0_bit;
//----------------------------------------------------------------------------------------------------------------------------//
sbit Led at RC1_bit;                 //LED INDICAZIONE
//----------------------------------------------------------------------------------------------------------------------------//
#define R_REGISTER_cmd                0x00           //COMANDO DI SCRITTURA
#define W_REGISTER_cmd                0x20           //COMANDO DI LETTURA
#define R_RX_PL_WID_cmd               0x60           //LEGGI DIMENSIONE PAYLOAD
#define R_RX_PAYLOAD_cmd              0x61           //LEGGI PAYLOAD RICEVUTO
#define W_TX_PAYLOAD_cmd              0xA0           //SCRIVI PAYLOAD
#define W_ACK_PAYLOAD_cmd             0xA8           //ACK DEL PAYLOAD
#define W_TX_PAYLOAD_NO_ACK_cmd       0xB0           //DISABILITA AUTO ACK
#define FLUSH_TX_cmd                  0xE1           //SVUOTA LA FIFO DEL TX
#define FLUSH_RX_cmd                  0xE2           //SVUOTA LA FIFO DELL'RX
#define REUSE_TX_PL_cmd               0xE3           //RIUTILIZZA ULTIMO PAYLOAD INVIATO
#define NOP_cmd                       0xFF           //NESSUN COMANDO
//----------------------------------------------------------------------------------------------------------------------------//
#define CONFIG_reg                    0b00000000     //INTERRUPT , CRC, ON/OFF, TX MODE SETTING
#define EN_AA_reg                     0x01           //AUTO ACK PIPE
#define EN_RXADDR_reg                 0x02           //ABILITAZIONE INDIRIZZI RX
#define SETUP_AW_reg                  0x03           //NUMERO DI INDIRIZZI (5)
#define SETUP_RETR_reg                0x04           //SETTING TEMPO DI RITRASMISSIONE (250uS)
#define RF_CH_reg                     0x05           //CANALE 5
#define RF_SETUP_reg                  0b00000110     //settaggio potenza e bitrate
#define STATUS_reg                    0x07           //REGISTRO DI STATO, INVIATO AL PIN MOSI
#define OBSERVE_TX_reg                0x08           //CONTA I LOST PACKET E I RETRASMITTED PACKET
#define RPD_reg                       0x09           //AVVERTE SE VI è UN TRASMETTITORE ATTIVO
#define RX_ADDR_P0_reg                0x0A           //RICEVE L'INDIRIZZO 0
#define RX_ADDR_P1_reg                0x0B           //
#define RX_ADDR_P2_reg                0x0C           //
#define RX_ADDR_P3_reg                0x0D           //
#define RX_ADDR_P4_reg                0x0E           //
#define RX_ADDR_P5_reg                0x0F           //
#define TX_ADDR_reg                   0x10           //TRASMISSIONE INDIRIZZI
#define RX_PW_P0_reg                  0x11           //NUMERO DI BYTE DEL PAYLOAD NEL DATA PIPE 0
#define RX_PW_P1_reg                  0x12           //
#define RX_PW_P2_reg                  0x13           //
#define RX_PW_P3_reg                  0x14           //
#define RX_PW_P4_reg                  0x15           //
#define RX_PW_P5_reg                  0x16           //
#define FIFO_STATUS_reg               0x17           //RIUTILIZZA TRASMISSIONE E STATO DELLA FIFO
#define DYNPD_reg                     0x1C           //ENABLE DYNAMIC PAYLOAD LENGHT
#define FEATURE_reg                   0x1D           //ABILITAZIONE MODALITà DYPLD, ACK, NO ACK

//----------------------------------------------------------------------------------------------------------------------------//
unsigned char x = 0;                                 //CONTIENE IL BYTE ricevuto

//----------------------------------------------------------------------------------------------------------------------------//
unsigned char nRF24L01_read()                        //questo sottoprogramma simula una comunicazione SPI
{
  unsigned char s = 0;                               //variabile per esegguire 8 esecuzioni
  unsigned char msg = 0;

  for(s = 0; s < 8; s++){                            //esegue 8 volte
      msg <<= 1;                                     //shifto msg
      SCK_pin = 1;                                   //attivo il clock
      delay_us(8);                                   //aspetto 8us (f spi 62Kbps)
      if(MISO_pin != 0)                              //se in=0
      {
        msg |= 1;                                    //azzero il bit di msg
      }
      SCK_pin = 0;                                   //disattivo il clock
      delay_us(8);
  }

  return msg;                                       //ritorno il valore letto
}

//----------------------------------------------------------------------------------------------------------------------------//
void nRF24L01_write(unsigned char d)                //sottoprogramma che simula una scrittura spi
  unsigned char s = 0;

  for(s = 0; s < 8; s++)                            //questo for serve a trasmettere sul pin mosi bit per bit il dato d
  {
    if((d & 0x80) != 0)
    {
      MOSI_pin = 1;                                 //se il bit scandito è 1 trasmetto 1
    }
    else
    {
      MOSI_pin = 0;                                 //altrimenti 0
    }
    d <<= 1;                                        //shifto d per inviare il prossimo bit
    SCK_pin = 1;                                    //creo un clock
    delay_us(8);
    SCK_pin = 0;
    delay_us(8);
  }
}

//----------------------------------------------------------------------------------------------------------------------------//
void register_write(unsigned char reg, unsigned char value)                //sottoprogramma per scrivere i registri
{
  SS_pin = 0;                                                              //indico che sto per inviare un comando
  nRF24L01_write((reg | W_REGISTER_cmd));                                  //maschero il registro con il comando per scrivere
  nRF24L01_write(value);                                                   //invio il valore che voglio scrivere nel registro
  SS_pin = 1;
  delay_us(8);
}

//----------------------------------------------------------------------------------------------------------------------------//
unsigned char register_read(unsigned char reg)                            //sottoprogramma lettura registri
{
  unsigned char value = 0;

  SS_pin = 0;
  nRF24L01_write((reg | R_REGISTER_cmd));                                 //indico che voglio leggere e quale registro leggere
  value = nRF24L01_read();                                                //leggo il dato che il modulo mi manda
  SS_pin = 1;
  delay_us(8);

  return value;
}

//----------------------------------------------------------------------------------------------------------------------------//
void write_command(unsigned char cmd)                                      //sottoprogramma scrittura comandi
  SS_pin = 0;                                                              //indico che voglio inviare un comando
  nRF24L01_write(cmd);                                                     //scrivo il comando cmd
  SS_pin = 1;                                                              //ripristino il pin ss
  delay_us(8);
}


//----------------------------------------------------------------------------------------------------------------------------//
void set_TX_RX_address(unsigned char *addr, unsigned char bytes, unsigned char reg)      //sottoprogramma per scrivere gli indirizzi
{
  unsigned char n = 0;

  SS_pin = 0;
  nRF24L01_write((reg | W_REGISTER_cmd));                                               //indico che voglio scrivere i registri
  for(n = 0; n < bytes; n++)
  {
    nRF24L01_write(addr[n]);                                                            //bytes contiene il numero di indirizzi
  }                                                                                     //scrivo gli n indirizzi
  SS_pin = 1;
  delay_us(8);
}

//----------------------------------------------------------------------------------------------------------------------------//
void flush_TX_RX()                                      //sottoprogramma per cancellare le FIFO
{
  register_write(STATUS_reg, 0x70);
  write_command(FLUSH_TX_cmd);
  write_command(FLUSH_RX_cmd);
}

//----------------------------------------------------------------------------------------------------------------------------//
void send_data(unsigned char bytes, unsigned char *value)        //sottoprogramma per inviare i dati
{
  unsigned char s = 0;

  flush_TX_RX();                                                //cancello le FIFO
  register_write(CONFIG_reg, 0x3A);                             //setto la modalità tx da CONFIG_reg e attivo il modulo

  SS_pin = 0;
  nRF24L01_write(W_TX_PAYLOAD_cmd);                             //scrivo nel registo di trasmissione i bit da inviare
  for(s = 0; s < bytes; s++)
  {
    nRF24L01_write(value[s]);
  }
  SS_pin = 1;
  delay_us(8);

  CE_pin = 1;                                                 //faccio partire l'invio, aspetto 60us e poi disattivo il modulo
  delay_us(60);
  CE_pin = 0;

  register_write(CONFIG_reg, 0x38); //En modo RX se quita
}

//----------------------------------------------------------------------------------------------------------------------------//
void receive_data(unsigned char bytes, unsigned char *value)         //sottoprogramma per la ricezione, indico il numero di bytes del dato
{                                                                    //value contiene il dato letto
  unsigned char s = 0;

  SS_pin = 0;                                                        //scrivo il comando che indica che voglio leggere
  nRF24L01_write(R_RX_PAYLOAD_cmd);
  for (s = 0; s < bytes; s++)
  {
    value[s] = nRF24L01_read();                                      //leggo il dato che il modlo mi sta inviando
  }
  SS_pin = 1;
  delay_us(8);
}

//----------------------------------------------------------------------------------------------------------------------------//
void nrF24L01_init_TX()                                             //sottoprogramma per settare la modalità trasmissione
{
  unsigned char address[5] = {0x99, 0x99, 0x99, 0x99, 0x99};        //indirizzo a 5bytes, valore a piacere(devo coincidere con rx)

  CE_pin = 0;

  register_write(SETUP_RETR_reg, 0x00);                             //ritrasmissione disabilitata
  register_write(SETUP_AW_reg, 0x03);                               //setto che ho indirizzo a 5bytes
  register_write(RF_SETUP_reg, 0x0E);                               //potenza 0dbm e velocità 250Kbps
  register_write(RF_CH_reg, 0x09);                                  //setto ch di rx, canale 9 in questo caso(il tx deve avere uguale ch)
  register_write(EN_AA_reg, 0x00);                                  //auto ack disabilitato
  register_write(CONFIG_reg, 0x38);                                 //interrupt abilitati su TX_DS e MAX_RT, crc abilitato
  set_TX_RX_address(address, 5, TX_ADDR_reg);                       //inserisco gli indirizzi nell'apposito registro
  set_TX_RX_address(address, 5, RX_ADDR_P0_reg);
  flush_TX_RX();                                                    //cancella le fifo

  CE_pin = 1;
}

//----------------------------------------------------------------------------------------------------------------------------//
void nrF24L01_init_RX()                                             //programma per settare la modalità RX
{
  unsigned char address[5] = {0x99, 0x99, 0x99, 0x99, 0x99};        //indirizzo a 5bytes uguale a quello del TX

  CE_pin = 0;

  register_write(CONFIG_reg, 0x38);                                 //Crc abilitato, no interrup di ricezione
  register_write(SETUP_RETR_reg, 0x00);                             //ritrasmissione non richiesta
  register_write(SETUP_AW_reg, 0x03);                               //indirizzo a 5bytes
  register_write(RF_SETUP_reg, 0x0E);                               //potenza 0dbm e velocità 250Kbps
  register_write(RF_CH_reg, 0x09);                                  //canale9
  register_write(EN_AA_reg, 0x00);                                  //no ack
  register_write(RX_PW_P0_reg, 0x01);                               //dato a 1byte
  register_write(CONFIG_reg, 0x3B);                                 //setto il modulo come RX
  set_TX_RX_address(address, 5, TX_ADDR_reg);                       //scrivo gli indirizzi nell'apposito registro
  set_TX_RX_address(address, 5, RX_ADDR_P0_reg);
  flush_TX_RX();                                                    //cancello le fifo

  CE_pin = 1;
}

//----------------------------------------------------------------------------------------------------------------------------//
unsigned char get_Status_Reg()                       //sottoprogramma per leggere lo status register che mi fornisce info sul modulo
{
  return register_read(STATUS_reg);
}

//----------------------------------------------------------------------------------------------------------------------------//
unsigned char val = 0;

//----------------------------------------------------------------------------------------------------------------------------//
void main(){                                 //programma principale


TRISA     =  0x00;                         //configuro inizialmente tutte le porte uscite e le setto a 0
TRISB     =  0x00;
TRISC     =  0x00;
TRISD     =  0x00;
ADCON1 = 0x0F;
PORTA = 0X00;
PORTB = 0X00;
PORTC = 0x00;
LATD= 0;

//----------------------------------------------------------------------------------------------------------------------------//
RF_IRQ_TRIS = 1;                                 //configurazione I/O microprocessore per comunicare col modulo
RF_CS_TRIS = 0;
RF_CE_TRIS = 0;
SCK_TRIS = 0;
SDI_TRIS = 1;
SDO_TRIS = 0;

CE_pin = 0;
SS_pin = 0;
SCK_pin = 0;
MOSI_pin = 0;

LED = 1;
delay_ms(10);            //faccio lampeggiare una volta il led
LED = 0;

//----------------------------------------------------------------------------------------------------------------------------//
 delay_ms(10);

 nrF24L01_init_RX();                //inizializzo il modulo come rx

 delay_ms(100);                     //delay assestamento

//----------------------------------------------------------------------------------------------------------------------------//
 while(1){                          //ciclo infinito
  if(get_Status_Reg() == 0x40)      //controllo nello status register se il bit di segnalazione di dato ricevuto è alto
  {
    receive_data(1, &x);            //se si vado a leggere il dato
    LATD = x;                       //e in questo caso lo visualizzo sulle portD
  }
 }
 
}//fine main

Nel main, per il modulo ricevitore, si vanno prima di tutto a configurare i pin di I/O, dopo di che si un lampeggio del Led e si inizializza il modulo come ricevitore.  Nel ciclo infinito si va a controllare se nello status register vi è un valore pari a 0x40, in caso affermativo si va a leggere e portare in uscita il dato ricevuto. In questo caso si fa un controllo della ricezione con un ciclo di polling, ovvero si va a controllare continuamente un valore e se diverso da quello atteso ricomincia il controllo. Si potrebbe anche usare un interrupt attivando dall’apposito registro del modulo, in questo modo il microcontrollore può svolgere altre operazioni e appena arriva il dato lo va a leggere. In questo caso va bene il ciclo di polling visto che il microcontrollore non  deve svolgere altre funzioni.

Lo schema è il seguente:

Nrf24L01+ SCHEMA ricevitore receiver RX

La connessione tra microcontrollore e modulo nRF24l01+ è identica al caso precedente e anche i componenti per creare il clock, ovviamente non è presente la resistenza variabile per creare una tensione variabile. I led sno stati sostituiti da led di colore verde o blu solo per distinguerli da quelli del circuito precedenti. I led blu o verdi assorbono meno corrente quindi richiedono una resistenza serie maggiore del caso precedente.

Lo schema montato su breadboard ha il seguente aspetto:

nrf24l01+ ricevitore receiver RXSi può notare che sono accesi il primo led, il terzo e il quinto come nel caso dei led del trasmettitore. Variando la resistenza variabile sul trasmettitore varieranno entrambi i led, quelli sul trasmettitore e quelli sul ricevitore.  Se si vuole usare un display LCD bisogna alimentarlo a 5V però i segnali possono essere anche a 3,3V.

 

CONFIGURAZIONE FUSES

A meno di funzioni speciali del vostro circuito, per quanto riguarda i fuses bisogna disattivare tutte le voci tranne “Brownout detect” e “Stack Overflow Reset”. La tensione di Brownout reset è fissata a 2V ma potete arrivare a 2,7V mentre per l’oscillatore si può selezionare la voce “XT” o “HS”.
 
DOWNLOAD

Purtroppo non esiste un modello per la simulazione del modulo nRF24l01+, non è stata fatta ancora nessuna libreria, quindi non è possibile simularlo di conseguenza non troverete nella cartella la simulazione dei due circuiti. Per scaricare però i codici compilati del ricevitore e trasmettitore cliccate su questo LINK!!!





 

[Voti Totali: 1 Media Voti: 5]
Segui la Nostra Pagina Facebook: Facebook

2 pensieri su “Trasmissione dati Wireless con il modulo nRF24l01+

  1. ottimo articolo , volevo chiedere se si vuole utilizzare la libreria di MikroC come devo iniziare la SPI_Init _Advanced ()
    sapendo che utilizzo un pic 18f4550 con quarzo di 20Mhz.
    Massimo

  2. Ciao 🙂
    Nel seguente codice viene simulata la scrittura SPI con frequenza di 62Kbps, non si usa la libreria mikroc.

    Se hai una frequenza di 20MHz, puoi usare una velocità di 32Kbps configurando l’SPI con la seguente stringa. In ogni caso per questo codice conviene lasciare la simulzaione dell’SPI con il codice qui presentato.
    SPI1_Init_Advanced(_SPI_MASTER_OSC_DIV64, _SPI_DATA_SAMPLE_MIDDLE, _SPI_CLK_IDLE_LOW, _SPI_LOW_2_HIGH);

    Per altre domande può commentare l’articolo 🙂

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *