Skip to content

Instantly share code, notes, and snippets.

@nuft
Last active August 29, 2015 14:06
Show Gist options
  • Save nuft/62a82a600d9500f54b2b to your computer and use it in GitHub Desktop.
Save nuft/62a82a600d9500f54b2b to your computer and use it in GitHub Desktop.
nRF24L01+ example code
#include "nRF24L01p.h"
/* addr is 5 bytes long (SETUP_AW = AW_5) */
void nrf_setup_ptx(uint8_t channel, uint8_t addr[])
{
ce_low();
// PRX, CRC, PWR_UP
nRF24L01p_write_register(CONFIG, PWR_UP | EN_CRC);
// 0dBm pwr, 250Kbps
nRF24L01p_write_register(RF_SETUP, RF_PWR(3) | RF_DR_250K);
// Enhanced ShockBurst Auto Acknowledgment on
nRF24L01p_write_register(EN_AA, ENAA_P0);
// enable dynamic packet length
nRF24L01p_write_register(FEATURE, EN_DPL | EN_ACK_PAY);
// enable dpl for pipe 0
nRF24L01p_write_register(DYNPD, DPL_P0);
// 1 retransmit, 1500us auto retransmit delay
nRF24L01p_write_register(SETUP_RETR, ARD(6) | ARC(1));
// channel 0..127
nRF24L01p_write_register(RF_CH, channel);
// address
nRF24L01p_write_register(SETUP_AW, AW_5);
nRF24L01p_set_addr(TX_ADDR, addr);
nRF24L01p_set_addr(RX_ADDR_P0, addr);
nRF24L01p_write_register(EN_RXADDR, ERX_P0);
nRF24L01p_write_register(RX_PW_P0, 32);
// clear all interrupts
nRF24L01p_write_register(STATUS, RX_DR | TX_DS | MAX_RT);
nRF24L01p_flush_tx();
nRF24L01p_flush_rx();
}
void nrf_send_packet_ptx(uint8_t *pkt)
{
nRF24L01p_write_register(CONFIG, PWR_UP | EN_CRC);
if (nRF24L01p_get_status_register() & TX_FULL)
nRF24L01p_flush_tx();
nRF24L01p_write_register(STATUS, TX_DS | MAX_RT);
// fill payload fifo
nRF24L01p_write_tx_payload(pkt, 32);
// send packet
ce_high();
os_delay_ms(1);
while(!(nRF24L01p_get_status_register() & (TX_DS | MAX_RT)));
ce_low();
}
void nrf_setup_prx(uint8_t channel, uint8_t addr[])
{
ce_low();
// PRX, CRC, PWR_UP
nRF24L01p_write_register(CONFIG, PWR_UP | PRIM_RX | EN_CRC);
// 0dBm pwr, 250Kbps
nRF24L01p_write_register(RF_SETUP, RF_PWR(3) | RF_DR_250K);
// Enhanced ShockBurst Auto Acknowledgment on
nRF24L01p_write_register(EN_AA, ENAA_P0);
// enable dynamic packet length
nRF24L01p_write_register(FEATURE, EN_DPL | EN_ACK_PAY);
// enable dpl for pipe 0
nRF24L01p_write_register(DYNPD, DPL_P0);
// 1 retransmit, 1500us auto retransmit delay
nRF24L01p_write_register(SETUP_RETR, ARD(6) | ARC(1));
// channel 0..127
nRF24L01p_write_register(RF_CH, channel);
// address
nRF24L01p_write_register(SETUP_AW, AW_5);
nRF24L01p_set_addr(TX_ADDR, addr);
nRF24L01p_set_addr(RX_ADDR_P0, addr);
nRF24L01p_write_register(EN_RXADDR, ERX_P0);
nRF24L01p_write_register(RX_PW_P0, 32);
// clear all interrupts
nRF24L01p_write_register(STATUS, RX_DR | TX_DS | MAX_RT);
nRF24L01p_flush_tx();
nRF24L01p_flush_rx();
}
void radio_receive_prx(uint8_t *rx_pkt, uint8_t rx_pkt_len, uint8_t *tx_pkt, uint8_t *tx_pkt_len)
{
nRF24L01p_write_register(STATUS, RX_DR | TX_DS | MAX_RT);
if (!(nRF24L01p_read_register(FIFO_STATUS) & FIFO_TX_FULL))
nRF24L01p_write_ack_payload(0, tx_pkt, tx_pkt_len);
ce_high();
while(true) {
uint8_t sreg = nRF24L01p_get_status_register();
if ((sreg & RX_DR) == RX_DR && (sreg & RX_P_NO) != RX_P_NO) {
ce_low();
*rx_pkt_len = nRF24L01p_read_rx_payload_len();
if (rx_pkt != NULL)
nRF24L01p_read_rx_payload(rx_pkt, *rx_pkt_len);
else
nRF24L01p_flush_rx();
break;
}
os_delay_ms(1);
}
}
#include <stdint.h>
#include <gpio.h>
#include <spi.h>
#include "nRF24L01p.h"
/*
* nRF24L01p HW interface:
* IRQ: interrupt (out)
* CE: chip enable (in)
* SPI: CSN, SCK, MOSI, MISO
* config: MSBit first, LSByte first, CSN active low,
* max 10Mbps, cpol=0 (idle=low), cpha=0 (first edge)
*/
// !!!
// CS inactive time min 50ns
// nRF24L01p commands
#define R_REGISTER 0x00 // 000A AAAA
#define W_REGISTER 0x20 // 001A AAAA
#define R_RX_PAYLOAD 0x61
#define W_TX_PAYLOAD 0xA0
#define FLUSH_TX 0xE1
#define FLUSH_RX 0xE2
#define REUSE_TX_PL 0xE3
#define R_RX_PL_WID 0x60
#define W_ACK_PAYLOAD 0xA8 // 1010 1PPP
#define W_TX_PAYLOAD_NOACK 0xB0
#define NOP 0xFF
struct nRF24L01p *nrf;
void ce_high(void)
{
gpio_set(&nrf->ce);
}
void ce_low(void)
{
gpio_clear(&nrf->ce);
}
//
// Low level commands
//
uint8_t nRF24L01p_get_status_register(void)
{
uint8_t cmd = NOP;
spi_single_access(&nrf->spi, &cmd, &cmd, 1);
return cmd;
}
uint8_t nRF24L01p_read_register(uint8_t addr)
{
addr = R_REGISTER | (addr & 0x1F);
uint8_t reg;
struct spi_transfer t[] = {
{.r=NULL, .t=&addr, .len=1},
{.r=&reg, .t=NULL, .len=1}};
spi_access(&nrf->spi, t, 2);
return reg;
}
void nRF24L01p_write_register(uint8_t addr, uint8_t val)
{
uint8_t buf[] = {W_REGISTER | (addr & 0x1F), val};
spi_single_access(&nrf->spi, NULL, buf, 2);
}
void nRF24L01p_read_multibyte_register(uint8_t addr, uint8_t *buf, uint8_t len)
{
uint8_t cmd = R_REGISTER | (addr & 0x1F);
struct spi_transfer t[] = {
{.r=NULL, .t=&cmd, .len=1},
{.r=buf, .t=NULL, .len=len}};
spi_access(&nrf->spi, t, 2);
}
void nRF24L01p_write_multibyte_register(uint8_t addr, uint8_t *buf, uint8_t len)
{
uint8_t cmd = W_REGISTER | (addr & 0x1F);
struct spi_transfer t[] = {
{.r=NULL, .t=&cmd, .len=1},
{.r=NULL, .t=buf, .len=len}};
spi_access(&nrf->spi, t, 2);
}
void nRF24L01p_read_rx_payload(uint8_t *buf, uint8_t len)
{
if (len == 0)
return;
uint8_t cmd = R_RX_PAYLOAD;
struct spi_transfer t[] = {
{.r=NULL, .t=&cmd, .len=1},
{.r=buf, .t=NULL, .len=len}};
spi_access(&nrf->spi, t, 2);
}
void nRF24L01p_write_tx_payload(uint8_t *buf, uint8_t len)
{
uint8_t cmd = W_TX_PAYLOAD;
struct spi_transfer t[] = {
{.r=NULL, .t=&cmd, .len=1},
{.r=NULL, .t=buf, .len=len}};
spi_access(&nrf->spi, t, 2);
}
void nRF24L01p_flush_tx(void)
{
uint8_t cmd = FLUSH_TX;
spi_single_access(&nrf->spi, NULL, &cmd, 1);
}
void nRF24L01p_flush_rx(void)
{
uint8_t cmd = FLUSH_RX;
spi_single_access(&nrf->spi, NULL, &cmd, 1);
}
void nRF24L01p_reuse_tx_pl(void)
{
uint8_t cmd = REUSE_TX_PL;
spi_single_access(&nrf->spi, NULL, &cmd, 1);
}
uint8_t nRF24L01p_read_rx_payload_len(void)
{
//*
uint8_t cmd = R_RX_PL_WID;
uint8_t l;
struct spi_transfer t[] = {
{.r=NULL, .t=&cmd, .len=1},
{.r=&l, .t=NULL, .len=1}};
spi_access(&nrf->spi, t, 2);
return l;
}
void nRF24L01p_write_ack_payload(uint8_t pipe_nb, uint8_t *buf, uint8_t len)
{
uint8_t cmd = W_ACK_PAYLOAD | (pipe_nb & 0x07);
struct spi_transfer t[] = {
{.r=NULL, .t=&cmd, .len=1},
{.r=NULL, .t=buf, .len=len}};
spi_access(&nrf->spi, t, 2);
}
void nRF24L01p_write_tx_payload_noack(uint8_t *buf, uint8_t len)
{
uint8_t cmd = W_TX_PAYLOAD_NOACK;
struct spi_transfer t[] = {
{.r=NULL, .t=&cmd, .len=1},
{.r=NULL, .t=buf, .len=len}};
spi_access(&nrf->spi, t, 2);
}
//
// High level functions
//
void nRF24L01p_power_up(void)
{
uint8_t config = nRF24L01p_read_register(CONFIG);
nRF24L01p_write_register(CONFIG, config | PWR_UP);
}
void nRF24L01p_power_down(void)
{
uint8_t config = nRF24L01p_read_register(CONFIG);
nRF24L01p_write_register(CONFIG, config & ~PWR_UP);
}
void nRF24L01p_set_addr(uint8_t addr_reg, uint8_t *addr)
{
nRF24L01p_write_multibyte_register(addr_reg, addr, 5);
}
void nRF24L01p_set_channel(uint8_t ch)
{
nRF24L01p_write_register(RF_CH, (ch & 0b01111111));
}
void nRF24L01p_set_tx_pwr(uint8_t pwr)
{
if (pwr > 3)
pwr = 3;
uint8_t setup = nRF24L01p_read_register(RF_SETUP);
setup &= ~RF_PWR(3);
setup |= RF_PWR(pwr);
nRF24L01p_write_register(RF_SETUP, setup);
}
bool nRF24L01p_get_rx_pwr(void)
{
return nRF24L01p_read_register(RPD);
}
void nRF24L01p_rx_mode(void)
{
uint8_t config = nRF24L01p_read_register(CONFIG);
nRF24L01p_write_register(CONFIG, config | PRIM_RX);
}
void nRF24L01p_tx_mode(void)
{
uint8_t config = nRF24L01p_read_register(CONFIG);
nRF24L01p_write_register(CONFIG, config & ~PRIM_RX);
}
#ifndef NRF24L01P_H
#define NRF24L01P_H
#include <stdint.h>
#include <gpio.h>
#include <spi.h>
struct nRF24L01p {
spi_dev_t spi;
gpio_pin_t mosi;
gpio_pin_t miso;
gpio_pin_t sck;
gpio_pin_t ce;
gpio_pin_t irq;
};
extern struct nRF24L01p *nrf;
// Registers
#define CONFIG 0x00
#define EN_AA 0x01
#define EN_RXADDR 0x02
#define SETUP_AW 0x03
#define SETUP_RETR 0x04
#define RF_CH 0x05
#define RF_SETUP 0x06
#define STATUS 0x07
#define OBSERVE_TX 0x08
#define RPD 0x09
#define RX_ADDR_P0 0x0A // multy byte register
#define RX_ADDR_P1 0x0B // multy byte register
#define RX_ADDR_P2 0x0C
#define RX_ADDR_P3 0x0D
#define RX_ADDR_P4 0x0E
#define RX_ADDR_P5 0x0F
#define TX_ADDR 0x10 // multy byte register
#define RX_PW_P0 0x11
#define RX_PW_P1 0x12
#define RX_PW_P2 0x13
#define RX_PW_P3 0x14
#define RX_PW_P4 0x15
#define RX_PW_P5 0x16
#define FIFO_STATUS 0x17
#define DYNPD 0x1C
#define FEATURE 0x1D
// bits
// CONFIG
#define MASK_RX_DR (1<<6)
#define MASK_TX_DS (1<<5)
#define MASK_MAX_RT (1<<4)
#define EN_CRC (1<<3)
#define CRCO (1<<2)
#define PWR_UP (1<<1)
#define PRIM_RX (1<<0)
// EN_AA
#define ENAA_P5 (1<<5)
#define ENAA_P4 (1<<4)
#define ENAA_P3 (1<<3)
#define ENAA_P2 (1<<2)
#define ENAA_P1 (1<<1)
#define ENAA_P0 (1<<0)
// EN_RXADDR
#define ERX_P5 (1<<5)
#define ERX_P4 (1<<4)
#define ERX_P3 (1<<3)
#define ERX_P2 (1<<2)
#define ERX_P1 (1<<1)
#define ERX_P0 (1<<0)
// SETUP_AW
#define AW(x) ((x)<<0) // 1:0
#define AW_3 0x01
#define AW_4 0x02
#define AW_5 0x03
// SETUP_RETR
#define ARD(x) ((x)<<4) // 7:4
#define ARC(x) ((x)<<0) // 3:0
// RF_SETUP
#define CONT_WAVE (1<<7)
#define RF_DR_LOW (1<<5)
#define PLL_LOCK (1<<4)
#define RF_DR_HIGH (1<<3)
#define RF_PWR(x) ((x)<<1) // 2:1
#define RF_DR_1M 0x00
#define RF_DR_2M 0x08
#define RF_DR_250K 0x20
// STATUS
#define RX_DR (1<<6)
#define TX_DS (1<<5)
#define MAX_RT (1<<4)
#define RX_P_NO (0x7<<1) // 3:1
#define GET_RX_P_NO(x) (((x)>>RX_P_NO) & 0x7)
#define TX_FULL (1<<0)
// OBSERVE_TX
#define PLOS_CNT(x) (((x)>>4) & 0xF) // 7:4
#define ARC_CNT(x) (((x)>>0) & 0xF) // 3:0
// FIFO_STATUS
#define FIFO_TX_REUSE (1<<6)
#define FIFO_TX_FULL (1<<5)
#define FIFO_TX_EMPTY (1<<4)
#define FIFO_RX_FULL (1<<1)
#define FIFO_RX_EMPTY (1<<0)
// DYNPD
#define DPL_P5 (1<<5)
#define DPL_P4 (1<<4)
#define DPL_P3 (1<<3)
#define DPL_P2 (1<<2)
#define DPL_P1 (1<<1)
#define DPL_P0 (1<<0)
// FEATURE
#define EN_DPL (1<<2)
#define EN_ACK_PAY (1<<1)
#define EN_DYN_ACK (1<<0)
void ce_high(void);
void ce_low(void);
uint8_t nRF24L01p_get_status_register(void);
uint8_t nRF24L01p_read_register(uint8_t addr);
void nRF24L01p_write_register(uint8_t addr, uint8_t val);
void nRF24L01p_read_multibyte_register(uint8_t addr, uint8_t *buf, uint8_t len);
void nRF24L01p_write_multibyte_register(uint8_t addr, uint8_t *buf, uint8_t len);
void nRF24L01p_read_rx_payload(uint8_t *buf, uint8_t len);
void nRF24L01p_write_tx_payload(uint8_t *buf, uint8_t len);
void nRF24L01p_flush_tx(void);
void nRF24L01p_flush_rx(void);
void nRF24L01p_reuse_tx_pl(void);
uint8_t nRF24L01p_read_rx_payload_len(void);
void nRF24L01p_write_ack_payload(uint8_t pipe_nb, uint8_t *buf, uint8_t len);
void nRF24L01p_write_tx_payload_noack(uint8_t *buf, uint8_t len);
void nRF24L01p_power_up(void);
void nRF24L01p_power_down(void);
void nRF24L01p_set_addr(uint8_t addr_reg, uint8_t *addr);
void nRF24L01p_set_channel(uint8_t ch);
void nRF24L01p_set_tx_pwr(uint8_t pwr);
bool nRF24L01p_get_rx_pwr(void);
void nRF24L01p_rx_mode(void);
void nRF24L01p_tx_mode(void);
#endif // NRF24L01P_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment