Autor Tema: Una aportación de librería sencilla para el nrf24L01  (Leído 1514 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado Alcaparrones

  • PIC10
  • *
  • Mensajes: 17
Una aportación de librería sencilla para el nrf24L01
« en: 22 de Junio de 2021, 16:59:48 »
Buenas a todos!

Quería compartir con ustedes la librería que he usado para mi último proyecto con nrf24L01.

Recientemente he acabado un proyecto de riego automático para un jardín. Este riego automático enciende y apaga una bomba de pozo a distancia, y para comunicar ambos pic he usado como transceptor el nrf24l01. A lo largo del tiempo he venido usando un par de librerías, las dos con sus ventajas y desventajas, pero nunca me convenció completamente ninguna de ellas. Adjunto ambas librerías para referencias. La librería "nrf24L01" la conseguí hace años en el foro de CCS, y la llamada "nrf24L01_bizintek" es una librería open source perteneciente a esa misma empresa. Para más referencias, consultad: https://sites.google.com/site/proyectosroboticos/nrf24l01/16f876-nrf24l01

Así que basándome en estas dos librerías he creado la mía propia. La que conseguí en el foro de CCS es muy simple pero muy configurable, aunque no incluye ACK. La de bizintek va muy bien, pero tiene demasiadas instrucciones, por lo que llena la RAM, tanto que se hace dificil de usar en un pic pequeño.

Aquí está mi aportación, nrf24L01_remix.c

Código: [Seleccionar]
/**************** Preprocessor Constants ***************************************/
#define CONFIG             0x20
#define EN_AA              0x21
#define EN_RXADDR          0x22
#define SETUP_AW           0x23
#define SETUP_RETR         0x24
#define RF_CH              0x25
#define RF_SETUP           0x26
#define STATUS             0x27
#define RX_ADDR_P0         0x2A
#define TX_ADDR            0x30
#define RX_PW_P0           0x31
#define RX_PW_P1           0x32
#define RX_PW_P2           0x33
#define FLUSH_TX           0xE1
#define FLUSH_RX           0xE2
#define TX_PAYLOAD         0xA0
#define RX_PAYLOAD         0x61
#define NO_OP              0xFF
#define ZERO               0x00
#define EOT                0x04
#define PAYLOAD_SIZE       0x20

//*****************
//*   VARIABLES   *
//*****************
#BYTE INTCON    =  0x0B

//Variables internas
//static int         INTCON=0x0B;
static int1        interRF;
static int16       noRF;
static int1        RCVNW=0;
static int8        DATA_N_SND=0;
static int8        DATA_N_RCV=0;
static int16       RF_CE,RF_CS,RF_IRQ;

//Variables configurables
static int8        RF_DATA[8];
static int8        RF_DIR;


//**************
//*   CÓDIGO   *
//**************

/**************** Write To NRF24L01 Configuration Register *********************/
void set_register(unsigned int command,unsigned int data)
{
   output_low(RF_CS);                                                          // Engage SPI chip select
   spi_write(command);                                                         // Write to register
   if(data!=NO_OP){spi_write(data);}                                           // Write data to register
   output_high(RF_CS);                                                         // Dis-engage SPI chip select
   delay_us(10);
}


//*****************************************************
//*               RF_CONFIG_PINES()                   *
//*****************************************************
//*****************************************************
void RF_CONFIG_PINES(int16 CE, int16 CS, int16 IRQ)
{
   RF_CE=CE;
   RF_CS=CS;
   RF_IRQ=IRQ;
}


//*****************************************************


//*****************************************************
//* RF_CONFIG(int canal,int dir,boolean Mbps,int dBm) *
//*****************************************************
//*Descripción:Esta función se encarga de configurar  *
//*el transceptor habilitando su propia dirección de  *
//*escucha y el canal entre otros parámetros.         *
//*****************************************************
//*Variables de entrada:- Canal                       *
//*                     - Direccion                   *
//*                     - Velocidad de transmisión    *
//*                     - Potencia                    *
//*Variables de salida:                               *
//*****************************************************

void RF_CONFIG(int canal, int dir, unsigned boolean Mbps, unsigned int dBm)
{
   output_low(RF_CE);

   // TX_ADDR (0xFF)
   //Configuración de la dirección de envio aleatoria.
   //En la función de enviar se configura la direccion
   //deseada por el usuario.
   output_low(RF_CS);
   spi_write(0x30);
   spi_write(0xFF);
   spi_write(0xC2);
   spi_write(0xC2);
   spi_write(0xC2);
   spi_write(0xC2);
   output_high(RF_CS);
   
   // RX_ADDR_P0 (0xFF) ACK
   //Configuración de la direccióndel Pipe0 para la
   //recepción de ACK.
   output_low(RF_CS);
   spi_write(0x2A);
   spi_write(0xFF);
   spi_write(0xC2);
   spi_write(0xC2);
   spi_write(0xC2);
   spi_write(0xC2);
   output_high(RF_CS);
   
   // RX_ADDR_P1 (dir)
   //Configuración de la direccióndel Pipe1 para la
   //recepción de tramas.
   output_low(RF_CS);
   spi_write(0x2B);
   spi_write(dir);
   spi_write(0xC2);
   spi_write(0xC2);
   spi_write(0xC2);
   spi_write(0xC2);
   output_high(RF_CS); 
   
   // RX_ADDR_P2 (0x00) BROADCAST
   //Configuración de la direccióndel Pipe2 para la
   //recepción de tramas
   set_register(0x2C,0x00);
   
   // EN_AA
   //Habilitar AutoAck en los Pipe0,Pipe1 y Pipe2.
   set_register(EN_AA,0x07);
   
   // EN_RXADDR
   //Habilitar los Pipe0,Pipe1 y Pipe2.
   set_register(EN_RXADDR,0x07);

   // SETUP_AW
   //Configuración de la longitud de las direcciones.
   //Direcciones de 5 bytes.
   set_register(SETUP_AW,0x03);

   //SETUP_RETR
   //Configuración de las retrasmisiones en la transmisión.
   //Diez retransmisiones cada 336us.
   set_register(SETUP_RETR,0x0A);

   //RF_CH
   //Configuración del canal.
   //Canal elegido por el usuario (0x01 - 0x7F).
   set_register(RF_CH,canal);

   //RF_SETUP
   //Configuración aspectos RF.
   //Ganancia máxima de LNA, 0dBm potencia de salida y 1Mbps de velocidad.
//   set_register(RF_SETUP,0x07);
   set_register(RF_SETUP,(Mbps<<3)|(dBm<<1)|0x01);                              // Setea velocidad y nivel de potencia. Velocidad puede ser 250kbs(0), 1mbps(1) o 2mbps(2). Potencia puede ser 0dbm(3, el más potente), -6dbm(2), -12dbm(1), -18dbm(0)   

   //STATUS
   //Reseteo del registro STATUS
   set_register(STATUS,0x70);

   //RX_PW_P0
   //Nº de bytes en Pipe0.
   //1 byte (ACK).
   set_register(RX_PW_P0,0x01);

   //RX_PW_P1
   //Nº de bytes en Pipe1.
   //10 byte (Direccion emisor y trama).
   set_register(RX_PW_P1,0x0A);

   //RX_PW_P2
   //Nº de bytes en Pipe2.
   //10 byte (Direccion emisor y trama).
   set_register(RX_PW_P2,0x0A);
}   
//*****************************************************


//*****************************************************
//*                    RF_ON()                         *
//*****************************************************
//*Descripción:Esta rutina activa el módulo de        *
//*radiofrecuencia en modo escucha para poder recibir *
//*datos enviados a su dirección.                     *
//*****************************************************
//*Variables de entrada:                              *
//*Variables de salida:                               *
//*****************************************************
void RF_ON()
{

   output_low(RF_CE);

   // CONFIG
   //Se activa el modulo, se pone en recepción,
   //se activa el CRC para que utilice 2 bytes.
   set_register(CONFIG,0x0F);

   delay_ms(2);
   output_high(RF_CE);
   delay_us(150);
}
//*****************************************************


//*****************************************************
//*                 RF_OFF()                         *
//*****************************************************
//*Descripción:Este procedimiento desactiva el módulo *
//*de radiofrecuencia.                                *
//*****************************************************
//*Variables de entrada:                              *
//*Variables de salida:                               *
//*****************************************************
void RF_OFF()
{
   output_low(RF_CE);

   // CONFIG
   //Se desactiva el modulo
   set_register(CONFIG,0x0C);
}
//*****************************************************


//*****************************************************
//*                 RF_SEND()                         *
//*****************************************************
//*Descripción:Esta función envía 8 Bytes de datos a  *
//*la dirección indicada informando de la correcta    *
//*recepción en el destinatario.                      *
//*****************************************************
//*Variables de entrada:- RF_DATA[]                   *
//*                     - RF_DIR
//*Variables de salida: -                             *
//*Salida:              - 0: Envío correcto (ACK OK)  *
//*                     - 1: No recepcibido (NO ACK)  *
//*                     - 2: No enviado               *
//*****************************************************
int RF_SEND()
{
   int i;
   int estado;


   if(bit_test(INTCON,7))
      interRF=1;
   else
      interRF=0;

//   disable_interrupts(GLOBAL);                                               //Deshabilitar las interrupciones arruina el funcionamiento del control de riego

   // INICIO
   output_low(RF_CE);

   //STATUS
   //Reseteo del registro STATUS
   set_register(STATUS,0x70);

   // EN_RXADDR
   //Se habilita el Pipe0 para la recepción del ACK
   set_register(EN_RXADDR,0x01);

   // TX_ADDR
   //Se configura la dirección de transmisión=RF_DIR
   output_low(RF_CS);
   spi_write(0x30);
   spi_write(RF_DIR);
   spi_write(0xC2);
   spi_write(0xC2);
   spi_write(0xC2);
   spi_write(0xC2);
   output_high(RF_CS);

   // RX_ADDR_P0
   //Para la recepción del ACK se debe configurar el Pipe0 con
   //la misma dirección a trasmitir.
   output_low(RF_CS);
   spi_write(0x2A);
   spi_write(RF_DIR);
   spi_write(0xC2);
   spi_write(0xC2);
   spi_write(0xC2);
   spi_write(0xC2);
   output_high(RF_CS);

   // RX_ADDR_P1
   //Se mete en RF_DIR la direccion propia.
   //De esta manera el receptor sabe la dirección
   //del transmisor.
   output_low(RF_CS);
   spi_write(0x0B);
   RF_DIR=spi_read(0);
   spi_read(0);
   spi_read(0);
   spi_read(0);
   spi_read(0);
   output_high(RF_CS);
   
   // W_TX_PAYLOAD
   //Se manda los datos al transductor
   output_low(RF_CS);
   spi_write(0xA0);

   DATA_N_SND++;
   spi_write(DATA_N_SND);
   spi_write(RF_DIR);
   for (i=0;i<8;i++)
      spi_write(RF_DATA[i]);

   output_high(RF_CS);

   // CONFIG
   //Se pasa a modo transmisión.
   set_register(CONFIG,0x0E);

   // Pulso de comienzo de envío
   output_high(RF_CE);
   delay_us(15);
   output_low(RF_CE);

   noRF=0;

   while (input(RF_IRQ)==1) {
      noRF++;
      //Si no da respuesta en 7ms, no se ha enviado.
      if(noRF==500){
      break;
         }
   }


   // STATUS
   //Lectura del estado en el registro estatus.
   output_low(RF_CS);
   estado=spi_read(0x27);
   spi_write(0x70);
   output_high(RF_CS);


   // EN_RXADDR
   //Habilitar los Pipe0,Pipe1 y Pipe2.
   set_register(EN_RXADDR,0x07);

      // TX_FLUSH
   //Limpieza de la FIFO de salida
   output_low(RF_CS);
   spi_write(0xE1);
   output_high(RF_CS);

   // CONFIG
   //Paso a modo recepción
   set_register(CONFIG,0x0F);

   // FIN
   output_high(RF_CE);

   delay_us(150);

   //Si no da respuesta en 7ms, no se ha enviado.
   if(noRF==500){
        if(interRF==1)
        enable_interrupts(GLOBAL);
        clear_interrupt(int_ext);
        return(2);
   }

   //estado
   //Chequeo de los bit del registro STATUS que indican si se ha recibido
   //ACK y si se ha terminado las retrasmisiones sin ningun ACK.
   if ((bit_test(estado,4)==0) && (bit_test(estado,5)==1)){
      if(interRF==1)
      enable_interrupts(GLOBAL);
      clear_interrupt(int_ext);
      return(0);
      }
   else{
      if(interRF==1)
      enable_interrupts(GLOBAL);
      clear_interrupt(int_ext);
      return(1);
      }     
}
//*****************************************************


//*****************************************************
//*                 RF_RECEIVE()                      *
//*****************************************************
//*Descripción: Esta rutina se encarga de comprobar si*
//*se ha producido una recepción y de ser así,        *
//*devuelve la trama recibida.                        *
//*****************************************************
//*Variables de entrada:-                             *
//*Variables de salida: - RF_DATA[]                   *
//*                     - RF_DIR                      *
//*Salida:         - 0: Recepción correcta y única    *
//*                - 1: Recepción correcta y múltiple *
//*                - 2: No se ha producido recepción  *
//*                - 3: No se ha producido recepción  *
//*****************************************************
int RF_RECEIVE()
{

   int i;
   int mas;
   int estado;

   if (input(RF_IRQ)==1 && RCVNW==0){
      return (2);
      }

   //STATUS
   //Lectura y reseteo del registro STATUS
   output_low(RF_CS);
   estado=spi_read(0x27);
   spi_write(0x70);
   output_high(RF_CS);

   //estado
   //Chequeo de la interrupción de recepción.
   if (bit_test(estado,6)==0 && RCVNW==0){
      return(3);
      }

   //R_RX_PAYLOAD
   //Lectura de los datos recibidos.
   output_low(RF_CS);
   spi_write(0x61);
   DATA_N_RCV=spi_read(0);
   RF_DIR=spi_read(0);
   for (i=0;i<8;i++)
   {
      RF_DATA[i]=spi_read(0);
   }
   output_high(RF_CS);

   //FIFO_STATUS
   //Comprobación del estado de la FIFO de
   //recepción para comprobar si hay más datos
   output_low(RF_CS);
   spi_write(0x17);
   mas=spi_read(0);
   output_high(RF_CS);

   if (bit_test(mas,0)==0){
      RCVNW=1;
      return(1);
   }
      RCVNW=0;
      return(0);
}
//*****************************************************

También incluyo el código de un comprobador de RF, que lo que hace es mandar un byte cualquiera a ambos pic y comprueba si se recibe el ack, encendiendo unos led cuando existe conexión. Aquí podeis ver cómo configuro yo toda la vaina:

Código: [Seleccionar]
#include <16F88.h>
#device PASS_STRINGS=IN_RAM
#device ADC=10

#FUSES NOMCLR                   //Master Clear pin used for I/O
#FUSES NOBROWNOUT               //No brownout reset
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOIESO                   //Internal External Switch Over mode disabled

#use delay(internal=4000000)

#include <nrf24L01_remix.c>

#define boton PIN_A6                                                           //Boton para mandar ACK
#define led1 PIN_A1
#define led2 PIN_A0
#define testigo PIN_A2


//Variables RF
const int Mbps = 0;                                                            //Velocidad. 250kbs(0), 1mbps(1) o 2mbps(2)
const int dBm = 3;                                                             //Potencia. 0dbm(3, el más potente), -6dbm(2), -12dbm(1), -18dbm(0)     
const int canal = 0x64;                                                        //Canal de comunicación (aquí, canal 100). Tiene que ser el mismo para todos los pic en comunicación. Canales posibles: 0-127 (0x00-7F)
const int direccionPropia=0x20;                                                //Dirección de este PIC.   
const int direccionBomba=0x10;                                                 //Dirección del PIC conectado a la bomba de agua.
const int direccionRiego=0x11;                                                 //Dirección del PIC conectado al riego
const int ack=0x0C;                                                            //Variable sin valor real, solo para probar ACK
unsigned int* entradaPaquete;                                                  //Paquete que entra, 8 bytes máximo.
int1 flagEntrada=0;                                                            //Flag que indica que hay un paquete listo para leer. No lo uso en este código

/*
Se envia la orden de on/off a la bomba de agua. Solo se envia un byte cada vez.
Salida:              - 0: Envío correcto (ACK OK) 
                     - 1: No recepcibido (NO ACK) 
                     - 2: No enviado
*/
int enviarPaquete(int direccionReceptor,int mensaje) {
   int ret2;
   RF_DATA[0]=mensaje;
   RF_DIR=direccionReceptor;
   ret2=RF_SEND();   
   return(ret2);
}

#INT_TIMER0
void  TIMER0_isr(void)
{                                                               
   static int1 flagBoton=0;                                                    //Flag para evitar código redundante
   int recepcion=5;                                                            //Reseteo la variable a un valor no usado
   
   if(!input(boton)&&flagBoton==0) {                                           //Pulso el botón
      RF_ON();
      delay_ms(3);
      flagBoton=1;     
   }
   if(!input(boton)) {                                                         //GND activa el boton
      recepcion=enviarPaquete(direccionBomba,ack);
      if(recepcion==0)                                                         //Recepción correcta
         output_high(led1);
      else
         output_low(led1);
      delay_ms(5);
      recepcion=enviarPaquete(direccionRiego,ack);
      if(recepcion==0)
         output_high(led2);
      else
         output_low(led2);     
   }
   if(input(boton)&&flagBoton==1) {                                            //Dejo de pulsar el botón
      RF_OFF();                                                                //Apago transceptor
      delay_ms(3);
      output_low(led1);
      output_low(led2);
      flagBoton=0;
   }
}

/*Interrupción usada para la recepción RF
ret1:           - 0: Recepción correcta y única (una tabla, de 8 bytes)   
                - 1: Recepción correcta y múltiple (máximo 3 tablas seguidas), no lo uso
                - 2: No se ha producido recepción 
                - 3: No se ha producido recepción
*/
#INT_EXT
void  EXT_isr(void)
{
   int8 ret1;
   ret1 = RF_RECEIVE();                                                        //Recibo la primera tabla. Sólo voy a recibir una, y con sólo un byte
   if(ret1 == 0)                                         
   {   
      entradaPaquete=RF_DATA;                                                  //Sólo voy a usar RF_DATA[0], tenlo en cuenta. Por cierto, esto es un puntero           
      flagEntrada=1;
   }
}

void main()
{
   disable_interrupts(GLOBAL);
   
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_256|RTCC_8_bit);                       //65,5 ms overflow
   enable_interrupts(INT_TIMER0);
   
   //Configuración RF
   enable_interrupts(INT_EXT);
   ext_int_edge( H_TO_L );   
   RF_CONFIG_PINES(PIN_B6,PIN_B7,PIN_B0);                                      //CE,CS,IRQ
   setup_spi(SPI_MASTER|SPI_L_TO_H|SPI_XMIT_L_TO_H
   |SPI_CLK_DIV_4|SPI_SAMPLE_AT_END);
   delay_ms(100);                                                              //Voy a dejar tiempo de sobra para que el módulo RF se encienda y esté preparado
   RF_CONFIG(canal,direccionPropia,Mbps,dBm); 
   RF_OFF();                                                                   //Apago el transceptor hasta que haga falta
   delay_ms(3);
   
   enable_interrupts(GLOBAL);
   
   output_low(led1);
   output_low(led2);


   while(TRUE)
   {
      output_high(testigo);   
      delay_ms(500);
      output_low(testigo);
      delay_ms(500);
   }

}

En el código de ejemplo podéis ver que uso RF_ON() y RF_OFF(), esto enciende y apaga el transceptor y ayuda a ahorrar batería, pero he notado que en algunos proyectos provoca comportamientos erráticos en el pic. Además, si teneis el pic en off no podreis recibir nada, claro está  :-/. En caso de duda, usad RF_ON() y punto, no os compliquéis.

En definitiva, este sigue siendo un gran transceptor para mí, barato y potente. Espero os sirva. Un saludo!

Desconectado kpena30

  • PIC10
  • *
  • Mensajes: 1
Re:Una aportación de librería sencilla para el nrf24L01
« Respuesta #1 en: 28 de Marzo de 2022, 00:00:14 »
Me gustaria saber como fue tu codigo fuente para el nevio de datos ya que yo estoy haciendo un proyecto similar, donde se hace la letura de dos sensores y se debe de enviar para asi poder activar una bomba de riego.

Gracias y espero puedas ayudar.


 

anything