Tento článek popisuje, jak vytvořit ovladač nRF24L01(+) v jazyce C pro Raspberry Pi Pico, aby jej bylo možné použít ve vývojovém prostředí Pico SDK. Přerušení se spustí při odeslání nebo přijetí datového paketu, namísto toho dotazování, aby CPU neustále dotazoval.

Tento ovladač vyžaduje použití pinu IRQ na nRF24L01(+).


| levý sloupec | pravý sloupec |
|---|---|
Vcc (červená) |
GND (černá) |
CSN (žlutá) |
CE (hnědá) |
MOSI (bílá nebo oranžová) |
SCK (modrá) |
IRQ (fialová) |
MISO (zelená) |
Stručný úvod do specifikací produktu nRF24L01 a odpovídající pokyny k použití ovladače.
-
Role vysílače/přijímače:
-
Primární vysílač (PTX): Role odesílatele.
-
Primární přijímač (PRX): Role přijímače.
-
Použijte tuto funkci ovladače:
-
nRF24_config_mode(role) 1: PRX, 0: PTX.
-
-
Metoda adresování:
-
K dispozici v délkách 3, 4 nebo 5 bajtů, definovaných v REGISTER SETUP_AW (0x03).
-
nRF24_set_address_width(uint8_t aw)
-
RX adresa: Celkem 6 datových kanálů, což znamená, že může přijímat 6 datových kanálů současně. Definováno v RX_ADDR_P0 (0x0A), RX_ADDR_P1 (0x0B), RX_ADDR_P2 (0x0C), RX_ADDR_P3 (0x0D), RX_ADDR_P4 (0x0E) a RX_ADDR_P5 (0x0F). Registry RX_ADDR_P2~P5 mají délku jednoho bajtu, přičemž 4 bajty vyššího řádu jsou stejné jako u RX_ADDR_P1. Viz diagram níže.
-
funkce:
-
nRF24_set_RX_addr(uint8_t data_pipe, uint8_t *addr, uint8_t len);
-
TX adresa: obecně definována jako RX_ADDR_P0.
-
nRF24_set_TX_addr(uint8_t *addr, uint8_t len);
-
-

(zdroj ze specifikace produktu nRF24L01)
PTX a PRX jsou znázorněny na následujícím obrázku:

-
Formát přenosu dat: rozdělen na statické a dynamické datové části. Viz obrázek níže: Délka dynamického datového zatížení je definována v prvních 6 bitech řídicího pole paketu.
Preamble 1 byte |
Address 3—5 byte |
Packet Control Field 9bit |
Payload 0—32 byte |
CRC 1-2 byte |
-
Statické datové zatížení:
nRF24_set_recv_payload_width(); -
Dynamické datové zatížení:
-
(a) povolení funkce:
nRF24_enable_feature(FEATURE_EN_DPL, true) -
(b) povolení datového kanálu:
nRF24_enable_data_pipe_dynamic_payload_length(uint8_t data_pipe, bool enable); -
(c) Přijímající strana získá dynamickou šířku datového zatížení:
nRF24_get_RX_payload_width(uint8_t *width) -
(d) Odesílatel musí také nastavit datový kanál na 0:
nRF24_enable_data_pipe_dynamic_payload_length(0, true)-
Operace čtení a zápisu jsou znázorněny na následujícím diagramu:
-
-

Při příjmu dat je pin IRQ nRF24L01 aktivní v nízké úrovni.
nRF24_spi_default_init(20, 21, irq_callback );
` `irq_callback je definované uživatelem. Používá se ke zpracování RX_RD (připravenost příjmu dat), TX_DS (odeslání vysílaných dat) a MAX_RT (maximální počet přerušení opakovaného přenosu TX
).
void irq_callback(uint8_t event_type, uint8_t datapipe, uint8_t* data, uint8_t width)
{
static uint32_t ack_payload=0;
uint8_t ack_buff[15];
switch(event_type) {
case EVENT_RX_DR:
//...(data: received data, width: data width)
break;
case EVENT_TX_DS:
//...(data sent)
break;
case EVENT_MAX_RT:
//nRF24_flush_tx();
//...
break;
}
}
void user_gpio_irq_handler
{
if (gpio_get_irq_event_mask(gpio_no) == (enum gpio irq level) {
gpio_acknowledge_irq(gpio_no, (enum gpio irq level));
gpio_set_irq_enabled(gpio_no, (enum gpio irq level), false);
...
gpio_set_irq_enabled(gpio_no, (enum gpio irq level), true);
}
}
gpio_add_raw_irq_handler(gpio_no, user_gpio_irq_handler);
gpio_set_irq_enabled(gpio_no, (enum) gpio_irq_level, true);
Příklad použití ovladače
Následuje zjednodušená hlavní struktura programu. Další příklady aplikací síťové topologie nRF24L01(+) budou představeny v dalším článku.
Příklad hlavního programu
/* Příklad použití nRF24L01
*
*/
#include <stdio.h>
#include "pico/stdlib.h"
#include "nRF24L01.h"
#include "string.h"
#define TX_MODE
uint8_t role;
uint8_t addr[5][5] = {"0node", "1node", "2node","3node","4node"};
void irq_callback(uint8_t event_type, uint8_t datapipe, uint8_t* data, uint8_t width) {
switch(event_type) {
case EVENT_RX_DR:
break;
case EVENT_TX_DS:
break;
case EVENT_MAX_RT:
//nRF24_flush_tx();
break;
}
}
void user_gpio_irq_handler() {
if (gpio_get_irq_event_mask(gpio_no) == (enum gpio irq level) {
gpio_acknowledge_irq(gpio_no, (enum gpio irq level));
gpio_set_irq_enabled(gpio_no, (enum gpio irq level), false);
// ...
gpio_set_irq_enabled(gpio_no, (enum gpio irq level), true);
}
}
int main()
{
stdio_init_all();
nRF24_spi_default_init(20, 21, irq_callback);
#ifdef TX_MODE
role=TRANSMITTER;
#else
role = RECEIVER;
#endif
nRF24_config_mode(role);
nRF24_enable_feature(FEATURE_EN_DPL, true);
//nRF24_enable_feature(FEATURE_EN_ACK_PAY, true);
//nRF24_enable_feature(FEATURE_EN_DYN_ACK, true);
#ifdef TX_MODE
nRF24_set_TX_addr(addr[0], 5);
nRF24_set_RX_addr(0, addr[0], 5);
nRF24_enable_data_pipe_dynamic_payload_length(0, true);
#else
nRF24_enable_RXADDR(0b00001111);
nRF24_set_RX_addr(0, addr[0], 5);
nRF24_set_RX_addr(1, addr[1], 5);
nRF24_set_RX_addr(2, addr[2], 5);
nRF24_set_RX_addr(3, addr[3], 5);
nRF24_enable_data_pipe_dynamic_payload_length(0, true);
nRF24_enable_data_pipe_dynamic_payload_length(1, true);
nRF24_enable_data_pipe_dynamic_payload_length(2, true);
nRF24_enable_data_pipe_dynamic_payload_length(3, true);
#endif
while(1) {
}
return 0;
}
CMakeLists.txt programu obsahuje:add_subdirectory ( název_projektu )
target_link_libraries ( název_projektu
nRF24L01_drv )
Kód driveru
nRF24L01.c
#include <stdio.h>
#include "pico/stdlib.h"
#include "nRF24L01.h"
#include "string.h"
// SPI Defines
// We are going to use SPI 0, and allocate it to the following GPIO pins
// Pins can be changed, see the GPIO function select table in the datasheet for information on GPIO assignments
static spi_inst_t* SPI_PORT= spi0;
static uint PIN_MISO=16;
static uint PIN_CS=17;
static uint PIN_SCK=18;
static uint PIN_MOSI=19;
static uint SPI_SPEED=1000*1000;
static uint nRF24_CE;
static uint nRF24_IRQ;
nRF24_irq_callback_t nRF24_callback;
static uint8_t nRF24_data_buffer[260];
static void nRF24_standby_to_txrx_mode();
static void nRF24_standby_I();
static uint8_t nRF24_fifo_rx_full(uint8_t *full);
static uint8_t nRF24_fifo_tx_empty(uint8_t *empty);
static uint8_t nRF24_fifo_rx_empty(uint8_t *empty);
/*!
* \brief nRF24L01 IRQ callback function
* \param gpio: gpio number
* \param event_mask
*/
static void gpio_irq_callback(uint gpio, uint32_t event_mask)
{
if (gpio == nRF24_IRQ && event_mask == GPIO_IRQ_EDGE_FALL) {
gpio_acknowledge_irq(gpio, event_mask);
//gpio_set_irq_enabled(gpio, GPIO_IRQ_LEVEL_LOW, false);
uint8_t status;
uint8_t event_type, datapipe;
uint8_t data_buffer[32];
uint8_t width;
nRF24_status(&status);
memset(nRF24_data_buffer,0, 32);
datapipe = (status & 0x0E) >> 1;
if ((status & 0x40) >> EVENT_RX_DR) { // RX_DR
event_type = EVENT_RX_DR;
if (nRF24_get_RX_payload_width(&width)) {
if (width > 32) { // in nRF24L01+ product specification
printf("...in driver width > 32 datapipe:%d:%d\n", datapipe, width);
nRF24_standby_I();
nRF24_flush_rx(); //
nRF24_standby_to_txrx_mode();
}
else {
nRF24_read_payload(nRF24_data_buffer, width);
nRF24_callback(event_type, datapipe, nRF24_data_buffer, width);
}
nRF24_clear_RX_DR();
}
}
if ((status & 0x20) >> EVENT_TX_DS) { // TX_DS
event_type = EVENT_TX_DS;
nRF24_callback(event_type, datapipe, nRF24_data_buffer, 0);
nRF24_clear_TX_DS();
}
if ((status & 0x10) >> EVENT_MAX_RT) { // MAX_RT
event_type = EVENT_MAX_RT;
nRF24_callback(event_type, datapipe, nRF24_data_buffer, 0);
nRF24_clear_MAX_RT();
}
//gpio_set_irq_enabled(gpio, GPIO_IRQ_LEVEL_LOW, true);
}
}
/*!
* \brief set nRF24L01 PWR_UP in CONFIG register
*/
static void nRF24_config_PWR_UP()
{
uint8_t ret;
uint8_t mode;
ret = nRF24_read_REGISTER(CONFIG, &mode,1);
if (ret) {
mode |= (0x02);
ret = nRF24_write_REGISTER(CONFIG, &mode,1);
}
}
/*!
* \brief enable CE
* \param enable true: set CE high, false: set CE low
*/
static void nRF24_enable_CE(bool enable) {
gpio_put(nRF24_CE, enable);
}
static void nRF24_standby_I() {
nRF24_enable_CE(false);
}
/*!
* \brief nRF24L01 active FEATURE. nRF24L01+ does not need this function
*/
static uint8_t nRF24_activate() {
uint8_t ret;
uint8_t status;
uint8_t cmd[2]={ACTIVATE, 0x73};
nRF24_spi_enable(true);
ret = spi_write_blocking(SPI_PORT, cmd, 2);
nRF24_spi_enable(false);
return ret;
}
/*!
* \brief 1. set SPI pin and initialize.
* 2.set nRF24L01 to standby-I mode
* \param CE nRF24L01 CE pin no
* \param IRQ nRF24L01 IRQ pin no
* \param callbak IRQ active callback funcion
*/
void nRF24_spi_default_init(uint CE, uint IRQ, nRF24_irq_callback_t callback)
{
spi_init(SPI_PORT, SPI_SPEED);
gpio_set_function(PIN_MISO, GPIO_FUNC_SPI);
gpio_set_function(PIN_CS, GPIO_FUNC_SIO);
gpio_set_function(PIN_SCK, GPIO_FUNC_SPI);
gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI);
nRF24_callback = callback;
gpio_set_dir(PIN_CS, GPIO_OUT);
gpio_put(PIN_CS, 1);
nRF24_CE = CE;
gpio_init(nRF24_CE);
gpio_set_dir(nRF24_CE, GPIO_OUT);
nRF24_activate(); //nRF24L01 Product Specification
nRF24_config_PWR_UP();
nRF24_enable_CE(false); //standby-I
sleep_us(100);
nRF24_IRQ = IRQ;
gpio_pull_up(nRF24_IRQ);
gpio_set_irq_enabled_with_callback(nRF24_IRQ, GPIO_IRQ_EDGE_FALL, true, gpio_irq_callback);
}
/*!
* \brief 1. set SPI pin and initialize.
* 2.set nRF24L01 to standby-I mode
* \param spi_port SPI port number
* \param miso MISO pin no
* \param cs CS pin no
* \param ce nRF24L01 CE pin no
* \param speed SPI speed
* \param IRQ nRF24L01 IRQ pin no
* \param callbak IRQ active callback funcion
*/
void nRF24_spi_init(spi_inst_t* spi_port, uint miso, uint mosi, uint cs, uint ce, uint speed, uint IRQ, nRF24_irq_callback_t callback)
{
SPI_PORT = spi_port;
PIN_MISO=miso;
PIN_MOSI=mosi;
PIN_CS=cs;
nRF24_CE = ce;
SPI_SPEED=speed;
nRF24_spi_default_init(ce, IRQ, callback);
}
/*!
* \brief enable SPI
* \param true: active, set CS LOW. false deactive, set CS HIGH
*/
void nRF24_spi_enable(bool enable)
{
gpio_put(PIN_CS, !enable);
}
/*!
* \brief read nRF24L01 register
* \param REGISTER register address
* \param value read out data
* \param len read out bytes
* \return 0: failure, other successful
*/
uint8_t nRF24_read_REGISTER(uint8_t REGISTER, uint8_t *value, uint8_t len)
{
uint8_t ret;
uint8_t status;
uint8_t cmd=R_REGISTER|REGISTER;
nRF24_spi_enable(true);
ret = spi_write_read_blocking(SPI_PORT, &cmd, &status, 1);
if (ret) {
ret = spi_read_blocking(SPI_PORT, NOP, value, len);
}
nRF24_spi_enable(false);
return ret;
}
/*!
* \brief write data into nRF24L01 register
* \param REGISTER register address
* \param value write data
* \param len write bytes
* \return 0: failure, other successful
*/
uint8_t nRF24_write_REGISTER(uint8_t REGISTER, uint8_t *value, uint8_t len)
{
uint8_t ret;
uint8_t buff[len+1];
buff[0]=W_REGISTER | REGISTER;
memcpy(buff+1, value, len);
nRF24_spi_enable(true);
ret = spi_write_blocking(SPI_PORT, buff, len+1);
nRF24_spi_enable(false);
return ret;
}
/*!
* \brief R_RX_PL_WIN: nRF24L01 received payload width
*/
uint8_t nRF24_get_RX_payload_width(uint8_t *width)
{
uint8_t ret;
uint8_t status;
uint8_t cmd=R_RX_PL_WID;
nRF24_spi_enable(true);
ret = spi_write_read_blocking(SPI_PORT, &cmd, &status, 1);
if (ret) {
ret = spi_read_blocking(SPI_PORT, NOP, width, 1);
}
nRF24_spi_enable(false);
return ret;
}
/*!
* \brief R_RX_PAYLOAD: received payload
* \param payload received payload
* \param len received number of bytes
* \return 0:failure, others: successful
*/
uint8_t nRF24_read_payload(uint8_t *payload, uint8_t len)
{
uint8_t ret;
uint8_t status;
uint8_t cmd=R_RX_PAYLOAD;
nRF24_spi_enable(true);
ret = spi_write_read_blocking(SPI_PORT, &cmd, &status, 1);
if (ret) {
ret = spi_read_blocking(SPI_PORT, NOP, payload, len);
}
nRF24_spi_enable(false);
return ret;
}
/*!
* \brief W_TX_PAYLOAD: write payload
* \param payload received payload
* \param len received number of bytes
* \return 0:failure, others: successful
*/
uint8_t nRF24_write_payload(uint8_t *payload, uint8_t len)
{
uint8_t ret;
uint8_t buff[len+1];
buff[0] = W_TX_PAYLOAD;
memcpy(buff+1, payload, len);
nRF24_spi_enable(true);
ret = spi_write_blocking(SPI_PORT, buff, len+1);
nRF24_spi_enable(false);
return ret;
}
/*!
* \brief W_TX_PAYLOAD_NOACK: write payload without acknowledgment. enable EN_DYN_ACK in FEATURE
* \param payload received payload
* \param len received number of bytes
* \return 0:failure, others: successful
*/
uint8_t nRF24_write_payload_no_ack(uint8_t *payload, uint8_t len)
{
uint8_t ret;
uint8_t buff[len+1];
buff[0]=W_TX_PAYLOAD_NOACK;
memcpy(buff+1, payload, len);
nRF24_spi_enable(true);
ret = spi_write_blocking(SPI_PORT, buff, len+1);
nRF24_spi_enable(false);
return ret;
}
/*!
* \brief FLUSH RX FIFO
*/
uint8_t nRF24_flush_rx()
{
uint8_t ret;
uint8_t cmd=FLUSH_RX;
uint8_t status;
nRF24_spi_enable(true);
ret = spi_write_read_blocking(SPI_PORT, &cmd, &status, 1);
nRF24_spi_enable(false);
return ret;
}
/*!
* \brief FLUSH TX FIFO
*/
uint8_t nRF24_flush_tx()
{
uint8_t ret;
uint8_t cmd=FLUSH_TX;
uint8_t status;
nRF24_spi_enable(true);
ret = spi_write_read_blocking(SPI_PORT, &cmd, &status, 1);
nRF24_spi_enable(false);
return ret;
}
uint8_t nRF24_reuse_tx_pl()
{
uint8_t ret;
uint8_t cmd=REUSE_TX_PL;
uint8_t status;
nRF24_spi_enable(true);
ret = spi_write_read_blocking(SPI_PORT, &cmd, &status, 1);
nRF24_spi_enable(false);
return ret;
}
/*!
* \brief W_ACK_PAYLOAD
*/
uint8_t nRF24_write_ack_payload(uint8_t data_pipe, uint8_t *payload, uint8_t len)
{
uint8_t ret;
uint8_t buff[len+2];
buff[0]=W_ACK_PAYLOAD | (data_pipe&0x07);
memcpy(buff+1, payload, len);
nRF24_spi_enable(true);
ret = spi_write_blocking(SPI_PORT, buff, len+1);
nRF24_spi_enable(false);
return ret;
}
uint8_t nRF24_set_TX_addr(uint8_t *addr, uint8_t len)
{
return (nRF24_write_REGISTER(TX_ADDR, addr, len));
}
uint8_t nRF24_get_TX_addr(uint8_t *addr, uint8_t len)
{
return (nRF24_read_REGISTER(TX_ADDR, addr, len));
}
uint8_t nRF24_set_RX_addr(uint8_t data_pipe, uint8_t *addr, uint8_t len)
{
uint8_t rx_addr_reg = RX_ADDR_P0 + data_pipe;
if (data_pipe > 1) len = 1;
return (nRF24_write_REGISTER(rx_addr_reg, addr, len));
}
uint8_t nRF24_get_RX_addr(uint8_t data_pipe, uint8_t *addr, uint8_t len)
{
uint8_t rx_addr_reg = RX_ADDR_P0 + data_pipe;
if (data_pipe > 1) len = 1;
return (nRF24_read_REGISTER(rx_addr_reg, addr, len));
}
uint8_t nRF24_status(uint8_t *status)
{
uint8_t ret;
uint8_t cmd = NOP;
nRF24_spi_enable(true);
ret = spi_write_read_blocking(SPI_PORT, &cmd, status, 1);
nRF24_spi_enable(false);
return ret;
}
/*!
* \param channel 0~125
*/
uint8_t nRF24_set_RF_channel(uint8_t channel)
{
if (channel > 125) channel = 125;
return (nRF24_write_REGISTER(RF_CH, &channel,1));
}
/*!
* \param pa: PA_0dBM, PA_m_6dBm(-6dBm), PA_m_12dBm, PA_m_18dBm
*/
uint8_t nRF24_set_power_amplifier(uint8_t pa)
{
uint8_t rf_setup;
if(!nRF24_read_REGISTER(RF_SETUP, &rf_setup,1)) return false;
rf_setup &= 0xF9;
switch(pa) {
case PA_0dBm:
rf_setup |= 0x06;
break;
case PA_m_6dBm:
rf_setup |= 0x04;
break;
case PA_m_12dBm:
rf_setup |= 0x02;
break;
case PA_m_18dBm:
rf_setup |= 0x00;
break;
}
return (nRF24_write_REGISTER(RF_SETUP, &rf_setup,1));
}
/*!
* \param aw 3~5
*/
uint8_t nRF24_set_address_width(uint8_t aw)
{
if (aw >=3 && aw <= 5 ) {
aw -= 2;
return (nRF24_write_REGISTER(SETUP_AW, &aw,1));
}
else
return false;
}
uint8_t nRF24_get_address_width(uint8_t *aw)
{
if (!nRF24_read_REGISTER(SETUP_AW, aw,1)) {
*aw=0;
return false;
}
*aw = *aw+2;
return true;
}
/*!
* \brief EN_AA register
*/
uint8_t nRF24_enable_auto_ack(uint8_t data_pipe, bool enable)
{
uint8_t aa;
uint8_t mask;
if (data_pipe < 0 || data_pipe > 5) return false;
mask = 0x1 << data_pipe;
if(!nRF24_read_REGISTER(EN_AA, &aa, 1)) return false;
if (enable) {
aa |= mask;
} else {
aa &= (mask^0xFF);
}
return (nRF24_write_REGISTER(EN_AA, &aa,1));
}
/*!
* \param feature FEATURE_EN_DYN_ACK, FEATURE_EN_ACK_PAY, FEATURE_EN_DPL
* \param enable true:enable, false:disable
*/
uint8_t nRF24_enable_feature(uint8_t feature, bool enable)
{
uint8_t buff;
if(!nRF24_read_REGISTER(FEATURE, &buff, 1)) return false;
if (enable) {
buff |= (0x01 << feature);
} else {
buff &= ((0x01 << feature) ^ 0xFF);
}
return (nRF24_write_REGISTER(FEATURE, &buff,1));
}
/*!
* \brief DYNPD register
*/
uint8_t nRF24_enable_data_pipe_dynamic_payload_length(uint8_t data_pipe, bool enable)
{
if (data_pipe < 0 || data_pipe > 5) return false;
if (!nRF24_enable_feature(FEATURE_EN_DPL, true)) return false;
if (!nRF24_enable_auto_ack(data_pipe, true)) return false;
uint8_t dynpd;
uint8_t mask;
mask = 0x1 << data_pipe;
if(!nRF24_read_REGISTER(DYNPD, &dynpd, 1)) return false;
if (enable) {
dynpd |= mask;
} else {
dynpd &= (mask^0xff);
}
return (nRF24_write_REGISTER(DYNPD, &dynpd,1));
}
/*!
* \brief Received Power Detector (RPD)
*/
uint8_t nRF24_get_RPD(uint8_t *rpd_value)
{
return (nRF24_read_REGISTER(RPD, rpd_value, 1));
}
/*!
* \brief RX_PW_Px register, Number of bytes in RX payload in data pipe Px
*/
uint8_t nRF24_set_recv_payload_width(uint8_t data_pipe, uint8_t width)
{
if (width > 32 || width < 0) return false;
uint8_t rx_pw_px = RX_PW_P0+data_pipe;
return (nRF24_write_REGISTER(rx_pw_px, &width, 1));
}
/*!
* \brief enable RX Address at data pipe x
* \param mask 0b00xxxxxx: ex. 0b00010111 enable p0~2 & p4
*/
uint8_t nRF24_enable_RXADDR(uint8_t mask)
{
return (nRF24_write_REGISTER(EN_RXADDR, &mask, 1));
}
uint8_t nRF24_set_data_rate(uint8_t rate)
{
uint8_t rf_setup;
if(!nRF24_read_REGISTER(RF_SETUP, &rf_setup, 1)) return false;
rf_setup &= 0xD7;
switch(rate) {
case DATA_RATE_250K:
rf_setup |= 0x20;
break;
case DATA_RATE_1M:
rf_setup |= 0x00;
break;
case DATA_RATE_2M:
rf_setup |= 0x08;
break;
}
return (nRF24_write_REGISTER(RF_SETUP, &rf_setup,1));
}
/*!
* \brief set nRF24L01+ mode
* \param PRIM_RX 1:PRX, 0: PTX
* \return 0: falure, other success
*/
uint8_t nRF24_config_mode(uint8_t PRIM_RX)
{
uint8_t ret;
uint8_t mode;
ret = nRF24_read_REGISTER(CONFIG, &mode, 1);
if (ret) {
nRF24_enable_CE(false);
sleep_us(10);
mode = (mode & 0xFE) | (PRIM_RX & 0x01);
ret = nRF24_write_REGISTER(CONFIG, &mode,1);
nRF24_enable_CE(true);
sleep_us(2000);
}
return ret;
}
/*!
* \param irq_type EVENT_MAX_RT, EVENT_TX_DS, EVENT_RX_DR
* \param enable
*/
uint8_t nRF24_enable_IRQ(uint8_t irq_type, bool enable)
{
if (irq_type < EVENT_MAX_RT || irq_type > EVENT_RX_DR) return 0;
uint8_t config;
uint8_t mask=0x01 << irq_type;
if (!nRF24_read_REGISTER(CONFIG, &config, 1)) return 0;
if (enable) {
config &= (mask ^ 0xFF);
} else {
config |= mask;
}
return nRF24_write_REGISTER(CONFIG, &config, 1);
}
/*!
* \brief clear MAX_RT interrupt
*/
uint8_t nRF24_clear_MAX_RT()
{
uint8_t status;
if (!nRF24_status(&status)) return 0;
if (status & 0x10) {
status |= 0x10;
return (nRF24_write_REGISTER(STATUS, &status, 1));
}
return 0;
}
/*!
* \brief clear TX_DS interrupt
*/
uint8_t nRF24_clear_TX_DS()
{
uint8_t status;
if (!nRF24_status(&status)) return 0;
if (status & 0x20) {
status |= 0x20;
return (nRF24_write_REGISTER(STATUS, &status, 1));
}
return 0;
}
/*!
* \brief clear RX_DR interrupt
*/
uint8_t nRF24_clear_RX_DR()
{
uint8_t status;
if (!nRF24_status(&status)) return 0;
if (status & 0x40) {
status |= 0x40;
return (nRF24_write_REGISTER(STATUS, &status, 1));
}
return 0;
}
static void nRF24_standby_to_txrx_mode()
{
nRF24_enable_CE(true);
busy_wait_us(140); // Standby modes --> TX/RX mode : max 130us.
// Delay from CE positive edge to CSN low : min 4us
}
/*!
* \brief set SETUP_RETR register bit 7:4 Auto Retransmit Delay
* \param delay time = 250us*(delay+1), value:0x00~0x0f
*/
uint8_t nRF24_set_auto_retransmit_delay(uint8_t delay)
{
uint8_t ard;
if (delay > 0x0f) delay = 0x0f;
if (nRF24_read_REGISTER(SETUP_RETR, &ard,1)) {
ard = (ard&0x0f) | delay << 4;
return (nRF24_write_REGISTER(SETUP_RETR, &ard, 1));
}
return false;
}
uint8_t nRF24_fifo_tx_full(uint8_t *full)
{
uint8_t fifo_status;
uint8_t ret;
ret = nRF24_read_REGISTER(FIFO_STATUS, &fifo_status, 1);
if (ret) {
fifo_status &= 0x20;
*full = fifo_status >> 5;
}
return ret;
}
static uint8_t nRF24_fifo_tx_empty(uint8_t *empty)
{
uint8_t fifo_status;
uint8_t ret;
ret = nRF24_read_REGISTER(FIFO_STATUS, &fifo_status, 1);
if (ret) {
fifo_status &= 0x10;
*empty = fifo_status >> 4;
}
return ret;
}
static uint8_t nRF24_fifo_rx_full(uint8_t *full)
{
uint8_t fifo_status;
uint8_t ret;
ret = nRF24_read_REGISTER(FIFO_STATUS, &fifo_status, 1);
if (ret) {
fifo_status &= 0x02;
*full = fifo_status >> 1;
}
return ret;
}
static uint8_t nRF24_fifo_rx_empty(uint8_t *empty)
{
uint8_t fifo_status;
uint8_t ret;
ret = nRF24_read_REGISTER(FIFO_STATUS, &fifo_status, 1);
if (ret) {
fifo_status &= 0x01;
*empty = fifo_status;
}
return ret;
}
nRF24L01.h
#ifndef __nRF24L01_H__
#define __nRF24L01_H__
#include "hardware/spi.h"
// Command
#define R_REGISTER 0b00000000
#define W_REGISTER 0b00100000
#define R_RX_PAYLOAD 0b01100001
#define W_TX_PAYLOAD 0b10100000
#define FLUSH_TX 0b11100001
#define FLUSH_RX 0b11100010
#define REUSE_TX_PL 0b11100011
#define ACTIVATE 0b01010000 //nRF24L01 Product Specification
#define R_RX_PL_WID 0b01100000
#define W_ACK_PAYLOAD 0b10101000 // 0b10100PPP
#define W_TX_PAYLOAD_NOACK 0b10110000
#define NOP 0b11111111
//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
#define RX_ADDR_P1 0x0B
#define RX_ADDR_P2 0x0C
#define RX_ADDR_P3 0x0D
#define RX_ADDR_P4 0x0E
#define RX_ADDR_P5 0x0F
#define TX_ADDR 0x10
#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
#define TRANSMITTER 0
#define RECEIVER 1
enum {
DATA_RATE_250K=0,
DATA_RATE_1M,
DATA_RATE_2M
} nRF24_DATA_RATE;
enum {
PA_0dBm=0,
PA_m_6dBm,
PA_m_12dBm,
PA_m_18dBm,
} nRF24_PA;
enum {
AW_3=1,
AW_4,
AW_5,
} nRF24_ADDRESS_WIDTH;
enum {
FEATURE_EN_DYN_ACK=0, // W_TX_PAYLOAD_NOACK
FEATURE_EN_ACK_PAY,
FEATURE_EN_DPL,
}nRF24_FEATURE_MASK;
enum {
EVENT_MAX_RT=4,
EVENT_TX_DS,
EVENT_RX_DR,
} nRF24_IRQ_EVENT;
typedef void (*nRF24_irq_callback_t)(uint8_t evant_type, uint8_t datapipe, uint8_t* data, uint8_t width);
void nRF24_spi_default_init(uint CE, uint IRQ, nRF24_irq_callback_t callback);
void nRF24_spi_init(spi_inst_t* spi_port, uint miso, uint mosi, uint cs, uint ce, uint speed, uint IRQ, nRF24_irq_callback_t callback);
void nRF24_spi_enable(bool enable);
uint8_t nRF24_status(uint8_t *status);
uint8_t nRF24_read_REGISTER(uint8_t REGISTER, uint8_t *value, uint8_t len);
uint8_t nRF24_write_REGISTER(uint8_t REGISTER, uint8_t *value, uint8_t len);
uint8_t nRF24_config_mode(uint8_t PRIM_RX);
uint8_t nRF24_set_data_rate(uint8_t rate);
uint8_t nRF24_set_RF_channel(uint8_t channel);
uint8_t nRF24_set_power_amplifier(uint8_t pa);
uint8_t nRF24_set_address_width(uint8_t aw);
uint8_t nRF24_set_recv_payload_width(uint8_t data_pipe, uint8_t width);
uint8_t nRF24_set_RX_addr(uint8_t data_pipe, uint8_t *addr, uint8_t len);
uint8_t nRF24_set_TX_addr(uint8_t *addr, uint8_t len);
uint8_t nRF24_get_RPD(uint8_t *rpd_value);
uint8_t nRF24_get_RX_payload_width(uint8_t *width);
uint8_t nRF24_get_address_width(uint8_t *aw);
uint8_t nRF24_get_RX_addr(uint8_t data_pipe, uint8_t *addr, uint8_t len);
uint8_t nRF24_get_TX_addr(uint8_t *addr, uint8_t len);
uint8_t nRF24_enable_auto_ack(uint8_t data_pipe, bool enable);
uint8_t nRF24_enable_data_pipe_dynamic_payload_length(uint8_t data_pipe, bool enable);
uint8_t nRF24_enable_feature(uint8_t feature, bool enable);
uint8_t nRF24_enable_RXADDR(uint8_t mask);
uint8_t nRF24_read_payload(uint8_t *payload, uint8_t len);
uint8_t nRF24_write_payload(uint8_t *payload, uint8_t len);
uint8_t nRF24_write_payload_no_ack(uint8_t *payload, uint8_t len);
uint8_t nRF24_flush_rx();
uint8_t nRF24_flush_tx();
uint8_t nRF24_reuse_tx_pl();
uint8_t nRF24_write_ack_payload(uint8_t data_pipe, uint8_t *payload, uint8_t len);
uint8_t nRF24_clear_MAX_RT();
uint8_t nRF24_clear_TX_DS();
uint8_t nRF24_clear_RX_DR();
void nRF24_standby_I();
#endif
add_library(nRF24L01_drv INTERFACE)
target_sources(nRF24L01_drv INTERFACE
${CMAKE_CURRENT_LIST_DIR}/nRF24L01.c
)
target_include_directories(nRF24L01_drv INTERFACE
${CMAKE_CURRENT_LIST_DIR}
)
target_link_libraries(nRF24L01_drv INTERFACE
hardware_spi
)