AVR-Lib

nRF24L01

The Nordic Semiconductor nRF24L01 is a transceiver chip operating in the 2.4GHz band. The following code is my first driver for Atmel ATMEGA8 and ATMEGA168. In my test scenario the ATMEGA168 and ATMEGA8 were connected different. The reason for this is that the ATMEGA168 supports Pin Change Interrupt which the ATMEGA8 doesn't. The chips were connected as follows:

nRF24L01

AVR ATMEGA8

AVR ATMEGA168

Description

SCK

PB5 (SCK)

PB5 (SCK)

SPI clock signal

MISO

PB4 (MISO)

PB4 (MISO)

SPI master in, slave out

MOSI

PB3 (MOSI)

PB3 (MOSI)

SPI master out, slave in

CSN

PB1

PB1

SPI Chip Select

CE

PB0

PB0

Chip Enable Activates RX or TX mode

IRQ

PD2 (INT0)

PD6 (PCINT22)

Interrupt pin. Active low

nRF24L01.h

Instruction, memory and bit maps from the datasheet.

Download RF24L01.h

   1 /* Memory Map */
   2 #define CONFIG      0x00
   3 #define EN_AA       0x01
   4 #define EN_RXADDR   0x02
   5 #define SETUP_AW    0x03
   6 #define SETUP_RETR  0x04
   7 #define RF_CH       0x05
   8 #define RF_SETUP    0x06
   9 #define STATUS      0x07
  10 #define OBSERVE_TX  0x08
  11 #define CD          0x09
  12 #define RX_ADDR_P0  0x0A
  13 #define RX_ADDR_P1  0x0B
  14 #define RX_ADDR_P2  0x0C
  15 #define RX_ADDR_P3  0x0D
  16 #define RX_ADDR_P4  0x0E
  17 #define RX_ADDR_P5  0x0F
  18 #define TX_ADDR     0x10
  19 #define RX_PW_P0    0x11
  20 #define RX_PW_P1    0x12
  21 #define RX_PW_P2    0x13
  22 #define RX_PW_P3    0x14
  23 #define RX_PW_P4    0x15
  24 #define RX_PW_P5    0x16
  25 #define FIFO_STATUS 0x17
  26 
  27 /* Bit Mnemonics */
  28 #define MASK_RX_DR  6
  29 #define MASK_TX_DS  5
  30 #define MASK_MAX_RT 4
  31 #define EN_CRC      3
  32 #define CRCO        2
  33 #define PWR_UP      1
  34 #define PRIM_RX     0
  35 #define ENAA_P5     5
  36 #define ENAA_P4     4
  37 #define ENAA_P3     3
  38 #define ENAA_P2     2
  39 #define ENAA_P1     1
  40 #define ENAA_P0     0
  41 #define ERX_P5      5
  42 #define ERX_P4      4
  43 #define ERX_P3      3
  44 #define ERX_P2      2
  45 #define ERX_P1      1
  46 #define ERX_P0      0
  47 #define AW          0
  48 #define ARD         4
  49 #define ARC         0
  50 #define PLL_LOCK    4
  51 #define RF_DR       3
  52 #define RF_PWR      1
  53 #define LNA_HCURR   0        
  54 #define RX_DR       6
  55 #define TX_DS       5
  56 #define MAX_RT      4
  57 #define RX_P_NO     1
  58 #define TX_FULL     0
  59 #define PLOS_CNT    4
  60 #define ARC_CNT     0
  61 #define TX_REUSE    6
  62 #define FIFO_FULL   5
  63 #define TX_EMPTY    4
  64 #define RX_FULL     1
  65 #define RX_EMPTY    0
  66 
  67 /* Instruction Mnemonics */
  68 #define R_REGISTER    0x00
  69 #define W_REGISTER    0x20
  70 #define REGISTER_MASK 0x1F
  71 #define R_RX_PAYLOAD  0x61
  72 #define W_TX_PAYLOAD  0xA0
  73 #define FLUSH_TX      0xE1
  74 #define FLUSH_RX      0xE2
  75 #define REUSE_TX_PL   0xE3
  76 #define NOP           0xFF

mirf.h

Header file with public methods and definitions

Download mirf.h

   1 #ifndef _MIRF_H_
   2 #define _MIRF_H_
   3 
   4 #include <avr/io.h>
   5 
   6 // Mirf settings
   7 #define mirf_CH         2
   8 #define mirf_PAYLOAD    16
   9 #define mirf_CONFIG     ( (1<<MASK_RX_DR) | (1<<EN_CRC) | (0<<CRCO) )
  10 
  11 // Pin definitions for chip select and chip enabled of the MiRF module
  12 #define CE  PB0
  13 #define CSN PB1
  14 
  15 // Definitions for selecting and enabling MiRF module
  16 #define mirf_CSN_hi     PORTB |=  (1<<CSN);
  17 #define mirf_CSN_lo     PORTB &= ~(1<<CSN);
  18 #define mirf_CE_hi      PORTB |=  (1<<CE);
  19 #define mirf_CE_lo      PORTB &= ~(1<<CE);
  20 
  21 // Public standart functions
  22 extern void mirf_init();
  23 extern void mirf_config();
  24 extern void mirf_send(uint8_t * value, uint8_t len);
  25 extern void mirf_set_RADDR(uint8_t * adr);
  26 extern void mirf_set_TADDR(uint8_t * adr);
  27 extern uint8_t mirf_data_ready();
  28 extern void mirf_get_data(uint8_t * data);
  29 
  30 // Public extended functions
  31 extern void mirf_config_register(uint8_t reg, uint8_t value);
  32 extern void mirf_read_register(uint8_t reg, uint8_t * value, uint8_t len);
  33 extern void mirf_write_register(uint8_t reg, uint8_t * value, uint8_t len);
  34 
  35 #endif /* _MIRF_H_ */

mirf.c

Download mirf.c

   1 #include "mirf.h"
   2 #include "nRF24L01.h"
   3 #include "spi.h"
   4 #include <avr/io.h>
   5 #include <avr/interrupt.h>
   6 
   7 // Defines for setting the MiRF registers for transmitting or receiving mode
   8 #define TX_POWERUP mirf_config_register(CONFIG, mirf_CONFIG | ( (1<<PWR_UP) | (0<<PRIM_RX) ) )
   9 #define RX_POWERUP mirf_config_register(CONFIG, mirf_CONFIG | ( (1<<PWR_UP) | (1<<PRIM_RX) ) )
  10 
  11 // Flag which denotes transmitting mode
  12 volatile uint8_t PTX;
  13 
  14 void mirf_init() 
  15 // Initializes pins ans interrupt to communicate with the MiRF module
  16 // Should be called in the early initializing phase at startup.
  17 {
  18     // Define CSN and CE as Output and set them to default
  19     DDRB |= ((1<<CSN)|(1<<CE));
  20     mirf_CE_lo;
  21     mirf_CSN_hi;
  22 
  23 #if defined(__AVR_ATmega8__)
  24     // Initialize external interrupt 0 (PD2)
  25     MCUCR = ((1<<ISC11)|(0<<ISC10)|(1<<ISC01)|(0<<ISC00)); // Set external interupt on falling edge
  26     GICR  = ((0<<INT1)|(1<<INT0));                         // Activate INT0
  27 #endif // __AVR_ATmega8__
  28 
  29 #if defined(__AVR_ATmega168__)
  30     // Initialize external interrupt on port PD6 (PCINT22)
  31     DDRB &= ~(1<<PD6);
  32     PCMSK2 = (1<<PCINT22);
  33     PCICR  = (1<<PCIE2);
  34 #endif // __AVR_ATmega168__    
  35 
  36     // Initialize spi module
  37     spi_init();
  38 }
  39 
  40 void mirf_config() 
  41 // Sets the important registers in the MiRF module and powers the module
  42 // in receiving mode
  43 {
  44     // Set RF channel
  45     mirf_config_register(RF_CH,mirf_CH);
  46 
  47     // Set length of incoming payload 
  48     mirf_config_register(RX_PW_P0, mirf_PAYLOAD);
  49 
  50     // Start receiver 
  51     PTX = 0;        // Start in receiving mode
  52     RX_POWERUP;     // Power up in receiving mode
  53     mirf_CE_hi;     // Listening for pakets
  54 }
  55 
  56 void mirf_set_RADDR(uint8_t * adr) 
  57 // Sets the receiving address
  58 {
  59     mirf_CE_lo;
  60     mirf_write_register(RX_ADDR_P0,adr,5);
  61     mirf_CE_hi;
  62 }
  63 
  64 void mirf_set_TADDR(uint8_t * adr)
  65 // Sets the transmitting address
  66 {
  67     mirf_write_register(TX_ADDR, adr,5);
  68 }
  69 
  70 #if defined(__AVR_ATmega8__)
  71 SIGNAL(SIG_INTERRUPT0) 
  72 #endif // __AVR_ATmega8__
  73 #if defined(__AVR_ATmega168__)
  74 SIGNAL(SIG_PIN_CHANGE2) 
  75 #endif // __AVR_ATmega168__  
  76 // Interrupt handler 
  77 {
  78     uint8_t status;   
  79     // If still in transmitting mode then finish transmission
  80     if (PTX) {
  81     
  82         // Read MiRF status 
  83         mirf_CSN_lo;                                // Pull down chip select
  84         status = spi_fast_shift(NOP);               // Read status register
  85         mirf_CSN_hi;                                // Pull up chip select
  86 
  87         mirf_CE_lo;                             // Deactivate transreceiver
  88         RX_POWERUP;                             // Power up in receiving mode
  89         mirf_CE_hi;                             // Listening for pakets
  90         PTX = 0;                                // Set to receiving mode
  91 
  92         // Reset status register for further interaction
  93         mirf_config_register(STATUS,(1<<TX_DS)|(1<<MAX_RT)); // Reset status register
  94     }
  95 }
  96 
  97 extern uint8_t mirf_data_ready() 
  98 // Checks if data is available for reading
  99 {
 100     if (PTX) return 0;
 101     uint8_t status;
 102     // Read MiRF status 
 103     mirf_CSN_lo;                                // Pull down chip select
 104     status = spi_fast_shift(NOP);               // Read status register
 105     mirf_CSN_hi;                                // Pull up chip select
 106     return status & (1<<RX_DR);
 107 }
 108 
 109 extern void mirf_get_data(uint8_t * data) 
 110 // Reads mirf_PAYLOAD bytes into data array
 111 {
 112     mirf_CSN_lo;                               // Pull down chip select
 113     spi_fast_shift( R_RX_PAYLOAD );            // Send cmd to read rx payload
 114     spi_transfer_sync(data,data,mirf_PAYLOAD); // Read payload
 115     mirf_CSN_hi;                               // Pull up chip select
 116     mirf_config_register(STATUS,(1<<RX_DR));   // Reset status register
 117 }
 118 
 119 void mirf_config_register(uint8_t reg, uint8_t value)
 120 // Clocks only one byte into the given MiRF register
 121 {
 122     mirf_CSN_lo;
 123     spi_fast_shift(W_REGISTER | (REGISTER_MASK & reg));
 124     spi_fast_shift(value);
 125     mirf_CSN_hi;
 126 }
 127 
 128 void mirf_read_register(uint8_t reg, uint8_t * value, uint8_t len)
 129 // Reads an array of bytes from the given start position in the MiRF registers.
 130 {
 131     mirf_CSN_lo;
 132     spi_fast_shift(R_REGISTER | (REGISTER_MASK & reg));
 133     spi_transfer_sync(value,value,len);
 134     mirf_CSN_hi;
 135 }
 136 
 137 void mirf_write_register(uint8_t reg, uint8_t * value, uint8_t len) 
 138 // Writes an array of bytes into inte the MiRF registers.
 139 {
 140     mirf_CSN_lo;
 141     spi_fast_shift(W_REGISTER | (REGISTER_MASK & reg));
 142     spi_transmit_sync(value,len);
 143     mirf_CSN_hi;
 144 }
 145 
 146 void mirf_send(uint8_t * value, uint8_t len) 
 147 // Sends a data package to the default address. Be sure to send the correct
 148 // amount of bytes as configured as payload on the receiver.
 149 {
 150     while (PTX) {}                  // Wait until last paket is send
 151 
 152     mirf_CE_lo;
 153 
 154     PTX = 1;                        // Set to transmitter mode
 155     TX_POWERUP;                     // Power up
 156     
 157     mirf_CSN_lo;                    // Pull down chip select
 158     spi_fast_shift( FLUSH_TX );     // Write cmd to flush tx fifo
 159     mirf_CSN_hi;                    // Pull up chip select
 160     
 161     mirf_CSN_lo;                    // Pull down chip select
 162     spi_fast_shift( W_TX_PAYLOAD ); // Write cmd to write payload
 163     spi_transmit_sync(value,len);   // Write payload
 164     mirf_CSN_hi;                    // Pull up chip select
 165     
 166     mirf_CE_hi;                     // Start transmission
 167 }

Usage

   1 // Initialize AVR for use with mirf
   2 mirf_init();
   3 // Wait for mirf to come up
   4 _delay_ms(50);
   5 // Activate interrupts
   6 sei();
   7 // Configure mirf
   8 mirf_config();
   9 // Test communication
  10 mirf_send(buffer,buffersize);
  11 while (!mirf_data_ready());
  12 mirf_get_data(buffer);

AVRLib/nRF24L01 (last edited 2009-09-15 18:33:03 by StefanEngelke)