diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index b0d4c66af1b8..65537ebd81d3 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -101,6 +101,10 @@ ifneq (,$(filter nrfmin,$(USEMODULE))) USEMODULE += netif endif +ifneq (,$(filter nrf24l01p_ng_%,$(USEMODULE))) + USEMODULE += nrf24l01p_ng +endif + ifneq (,$(filter periph_ptp_timer periph_ptp_speed_adjustment,$(FEATURES_USED))) FEATURES_REQUIRED += periph_ptp endif diff --git a/drivers/include/net/netdev.h b/drivers/include/net/netdev.h index 929b00ed50ea..e5d3623d6ac9 100644 --- a/drivers/include/net/netdev.h +++ b/drivers/include/net/netdev.h @@ -222,6 +222,7 @@ enum { NETDEV_TYPE_CC110X, NETDEV_TYPE_LORA, NETDEV_TYPE_NRFMIN, + NETDEV_TYPE_NRF24L01P_NG, NETDEV_TYPE_SLIP, NETDEV_TYPE_ESP_NOW, }; @@ -315,6 +316,7 @@ typedef enum { NETDEV_SX127X, NETDEV_SAM0_ETH, NETDEV_ESP_NOW, + NETDEV_NRF24L01P_NG, /* add more if needed */ } netdev_type_t; /** @} */ diff --git a/drivers/include/nrf24l01p_ng.h b/drivers/include/nrf24l01p_ng.h new file mode 100644 index 000000000000..e0a361e8d87d --- /dev/null +++ b/drivers/include/nrf24l01p_ng.h @@ -0,0 +1,468 @@ +/* + * Copyright (C) 2019 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +/** + * @defgroup drivers_nrf24l01p_ng NRF24L01+ (NG) 2.4 GHz trasceiver driver + * @ingroup drivers_netdev + * + * This module contains the driver for the NRF24L01+ (NG) 2.4 GHz + * transceiver. + * + * @{ + * @file + * @brief Public interface for NRF24L01+ (NG) devices + * + * @author Fabian Hüßler + */ +#ifndef NRF24L01P_NG_H +#define NRF24L01P_NG_H + +#include + +#include "board.h" +#include "kernel_defines.h" +#include "net/gnrc/nettype.h" +#include "net/netdev.h" +#include "periph/gpio.h" +#include "periph/spi.h" + +#include "nrf24l01p_ng_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Minimum width of a NRF24L01+ layer-2 address + */ +#define NRF24L01P_NG_MIN_ADDR_WIDTH (3) + +/** + * @brief Maximum width of a NRF24L01+ layer-2 address + */ +#define NRF24L01P_NG_MAX_ADDR_WIDTH (5) + +/** + * @brief Pass the compiler a definition of NRF24L01P_NG_ADDR_WIDTH to configure + * the layer-2 address width of this transceiver. + */ +#ifndef NRF24L01P_NG_ADDR_WIDTH +#define NRF24L01P_NG_ADDR_WIDTH NRF24L01P_NG_MAX_ADDR_WIDTH +#elif (NRF24L01P_NG_ADDR_WIDTH < NRF24L01P_NG_MIN_ADDR_WIDTH) || \ + (NRF24L01P_NG_ADDR_WIDTH > NRF24L01P_NG_MAX_ADDR_WIDTH) +#error "NRF24L01P_NG_ADDR_WIDTH must be within [3, 5]" +#endif + +/** + * @brief Agreed layer-2 address to send broadcast frames to + * + * The broadcast address will be assigned to pipe 0. + */ +#define NRF24L01P_NG_BROADCAST_ADDR { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } + +/** + * @brief Shortcut to access the address of pipe 0 + */ +#define NRF24L01P_NG_ADDR_P0(dev) ((dev)->arxaddr.rx_addr_long[NRF24L01P_NG_P0]) + +/** + * @brief Shortcut to access the address of pipe 1 + */ +#define NRF24L01P_NG_ADDR_P1(dev) ((dev)->arxaddr.rx_addr_long[NRF24L01P_NG_P1]) + +/** + * @brief Shortcut to access the least significant byte of the + * addresses pipe 2, pipe 3, pipe 4 or pipe 5 + * + * The other bytes are shared with the address of pipe 1 + */ +#define NRF24L01P_NG_ADDR_PX_LSB(dev, pipe) ((dev)->arxaddr.rx_addr_short[(pipe) - 2]) + +/** + * @brief Struct that holds all active configuration values + */ +typedef struct __attribute__((packed)) { + /** + * @brief padding + */ + uint8_t : 1; + /** + * @brief Current channel + */ + uint8_t cfg_channel : 7; + /** + * @brief padding + */ + uint8_t : 2; + /** + * @brief Current CRC length configuration value + * @see nrf24l01p_ng_crc_t + */ + uint8_t cfg_crc : 2; + /** + * @brief Current tx power configuration value + * @see nrf24l01p_ng_tx_power_t + */ + uint8_t cfg_tx_power : 2; + /** + * @brief Current data rate configuration value + * @see nrf24l01p_ng_rfdr_t + */ + uint8_t cfg_data_rate : 2; + /** + * @brief Current maximum number of retransmissions + */ + uint8_t cfg_max_retr : 4; + /** + * @brief Current retransmission delay configuration value + * @see nrf24l01p_ng_ard_t + */ + uint8_t cfg_retr_delay : 4; +} nrf24l01p_ng_cfg_t; + +/** + * @brief Struct of NRF24L01+ initialization parameters + */ +typedef struct { + spi_t spi; /**< SPI bus */ + spi_clk_t spi_clk; /**< SPI clock speed */ + gpio_t pin_cs; /**< SPI chip select gpio pin */ + gpio_t pin_ce; /**< NRF24L01+ chip enable gpio pin */ + gpio_t pin_irq; /**< NRF24L01+ IRQ gpio pin */ + nrf24l01p_ng_cfg_t config; /**< Current configuration values */ +} nrf24l01p_ng_params_t; + +/** + * @brief NRF24L01+ device struct + */ +struct nrf24l01p_ng { + netdev_t netdev; /**< Netdev member */ + nrf24l01p_ng_params_t params; /**< Parameters */ + uint8_t state; /**< Current operation state */ + uint8_t idle_state; /**< State to return to after sending */ + /** + * @brief Rx addresses + * + * The NRF24L01+ has 6 data pipes. + * Each data pipe has a unique address. + * The address of a pipe can be 3 to 5 bytes long + * but must be equal for all pipes. + * The addresses of pipe 0 and pipe 1 can be arbitrary while the addresses + * of pipe 2 to pipe 5 share the most significant bytes, thus they only + * differ in the least significant byte. + * The least significant byte must be different for all pipes. + * + * Data pipe 0 is also the Tx pipe. + * That means if you want to send a frame, you must assign to pipe 0 + * the address of the recipient. After a transmission if you want to + * continue listening on pipe 0, the address of pipe 0 must be restored. + */ + struct { + /** + * @brief Array to access the addresses of pipe 0 and pipe 1 via + * pipe indices @see nrf24l01p_ng_pipe_t + */ + uint8_t rx_addr_long[2][NRF24L01P_NG_ADDR_WIDTH]; + /** + * @brief Array to access the addresses of pipe 2, pipe 3, + * pipe 4 and pipe 5 via pipe indices + * @see nrf24l01p_ng_pipe_t + */ + uint8_t rx_addr_short[4]; + } arxaddr; +}; + +/** + * @brief Setup the NRF24L01+ driver, but perform no initialization + * + * @ref netdev_driver_t::init can be used after this call to initialize the + * transceiver. + * + * @param[in] dev NRF24L01+ device handle + * @param[in] params Parameters of the device to setup + * @param[in] index Index of @p params in a global parameter struct array. + * If initialized manually, pass a unique identifier instead. + * + * @retval 0 Device successfully set up + */ +int nrf24l01p_ng_setup(nrf24l01p_ng_t *dev, + const nrf24l01p_ng_params_t *params, + uint8_t index); + +/** + * @brief Enable or disable data pipe @p pipe + * + * If @p pipe should be enabled, it is advised to + * set an address in advance. + * @see nrf24l01p_ng_set_rx_addr + * For Enhanced ShockBurst, auto ACK and dynamic + * payload length are enabled + * + * @param[in] dev NRF24L01+ device handle + * @param[in] pipe Pipe index + * @param[in] enable If pipe should be enabled or disabled + * + * @retval 0 Success + * @retval -EINVAL Bad pipe index + */ +int nrf24l01p_ng_set_enable_pipe(nrf24l01p_ng_t *dev, nrf24l01p_ng_pipe_t pipe, + bool enable); + +/** + * @brief Query if data pipe @p pipe is enabled + * + * @param[in] dev NRF24L01+ device handle + * @param[in] pipe Pipe index + * @param[out] enable If pipe is enabled or disabled + * + * @retval 0 Success + * @retval -EINVAL Bad pipe index + */ +int nrf24l01p_ng_get_enable_pipe(nrf24l01p_ng_t *dev, nrf24l01p_ng_pipe_t pipe, + bool* enable); + +/** + * @brief Configure air data rate + * + * @param[in] dev NRF24L01+ device handle + * @param[in] data_rate Data rate configuration value + * + * @retval 0 Success + * @retval -EINVAL Bad data rate value + * @retval -EAGAIN Current state does not permit changing data rate + */ +int nrf24l01p_ng_set_air_data_rate(nrf24l01p_ng_t *dev, + nrf24l01p_ng_rfdr_t data_rate); + +/** + * @brief Get currently configured data rate + * + * @param[in] dev NRF24L01+ device handle + * @param[out] data_rate Configuration data rate value (may be NULL) + * + * @return Data rate in [kbit/s] + */ +uint16_t nrf24l01p_ng_get_air_data_rate(const nrf24l01p_ng_t *dev, + nrf24l01p_ng_rfdr_t *data_rate); + +/** + * @brief Configure CRC length + * + * @param[in] dev NRF24L01+ device handle + * @param[in] crc Configuration CRC value + * + * @retval 0 Success + * @retval -EINVAL Bad CRC configuration value + * @retval -ENOTSUP CRC length not supported + * @retval -EAGAIN Current state does not permit changing CRC length + */ +int nrf24l01p_ng_set_crc(nrf24l01p_ng_t *dev, nrf24l01p_ng_crc_t crc); + +/** + * @brief Get currently configured CRC length + * + * @param[in] dev NRF24L01+ device handle + * @param[out] crc Configuration crc value (may be NULL) + * + * @return Current CRC length + */ +uint8_t nrf24l01p_ng_get_crc(const nrf24l01p_ng_t *dev, + nrf24l01p_ng_crc_t *crc); + +/** + * @brief Configure Tx trasceiver power + * + * @param[in] dev NRf24L01+ device handle + * @param[in] power Configuration Tx power value + * + * @retval 0 Success + * @retval -EINVAL Bad Tx power configuration value + * @retval -EAGAIN Current state does not permit changin Tx power + */ +int nrf24l01p_ng_set_tx_power(nrf24l01p_ng_t *dev, + nrf24l01p_ng_tx_power_t power); + +/** + * @brief Get currently configured Tx transceiver power + * + * @param[in] dev NRF24L01+ device handle + * @param[out] power Configuration Tx power value + * + * @return Tx power in [dbm] + */ +int8_t nrf24l01p_ng_get_tx_power(const nrf24l01p_ng_t *dev, + nrf24l01p_ng_tx_power_t *power); + +/** + * @brief Set transceiver channel + * + * @param[in] dev NRF24l01+ device handle + * @param[in] channel Channel [0; 15] + * + * @retval 0 Success + * @retval -EINVAL Bad channel + * @retval -EAGAIN Current state does not permit switching channel + */ +int nrf24l01p_ng_set_channel(nrf24l01p_ng_t *dev, uint8_t channel); + +/** + * @brief Get currently configured transceiver channel + * + * @param[in] dev NRF24L01+ device handle + * + * @return Transceiver channel + */ +uint8_t nrf24l01p_ng_get_channel(const nrf24l01p_ng_t *dev); + +/** + * @brief Set Rx address of a certain data pipe + * + * If you want to set the address of pipe 2, 3, 4 or 5 + * you only set the LSB because the other bytes are + * equal to the address of pipe 1. + * + * For pipe 0 and 1 NRF24L01P_NG_ADDR_WIGTH bytes are expected + * + * @param[in] dev NRF24L01+ device handle + * @param[in] addr Rx address + * @param[in] pipe Pipe index + * + * @retval 0 Success + * @retval -EINVAL Bad address length + * @return -EAGAIN Current state does not permit changin Rx address + */ +int nrf24l01p_ng_set_rx_address(nrf24l01p_ng_t *dev, const uint8_t *addr, + nrf24l01p_ng_pipe_t pipe); + +/** + * @brief Get current Rx address of a certain data pipe + * + * @p addr must be at least NRF24L01P_NG_ADDR_WIDTH wide. + * + * @param[in] dev NRF24L01+ device handle + * @param[out] addr Rx address + * @param[in] pipe Pipe index + * + * @return Address width + * @retval -EINVAL Bad pipe index + */ +int nrf24l01p_ng_get_rx_address(const nrf24l01p_ng_t *dev, uint8_t *addr, + nrf24l01p_ng_pipe_t pipe); + +/** + * @brief Configure maximum number of retransmissions for ESB + * + * @param[in] dev NRF24L01+ device handle + * @param[in] max_rt Number of maximum retransmissions [0; 15] + * + * @retval 0 Success + * @retval -EINVAL Unsupported number of retransmissions + * @retval -EAGAIN Current state does not permit changing + * the maximum number of retransmissions + */ +int nrf24l01p_ng_set_max_retransm(nrf24l01p_ng_t *dev, uint8_t max_rt); + +/** + * @brief Get currently configured number of maximum retransmissions for ESB + * + * @param[in] dev NRF24L01+ device handle + * + * @return Maximum number of retransmissions + */ +uint8_t nrf24l01p_ng_get_max_retransm(const nrf24l01p_ng_t *dev); + +/** + * @brief Set retransmission delay for ESB + * + * @param[in] dev NRF24L01+ device handle + * @param[in] rt_delay Configuration retransmission delay value + * + * @return 0 + * @return -EINVAL Bad retransmission delay value + * @return -EAGAIN Current state does not permit changing + * retransmission delay + */ +int nrf24l01p_ng_set_retransm_delay(nrf24l01p_ng_t *dev, + nrf24l01p_ng_ard_t rt_delay); + +/** + * @brief Get retransmission delay for ESB + * + * @param[in] dev NRF24L01+ device handle + * @param[out] rt_delay Configuration retransmission delay value + * + * @return Retransmission delay in [us] + */ +uint16_t nrf24l01p_ng_get_retransm_delay(const nrf24l01p_ng_t *dev, + nrf24l01p_ng_ard_t *rt_delay); + +/** + * @brief Put device into + * sleep mode(@ref NRF24L01P_NG_STATE_POWER_DOWN), + * standby mode (@ref NRF24L01P_NG_STATE_STANDBY_1), + * or Rx mode (@ref NRF24L01P_NG_STATE_RX_MODE) + * + * @param[in] dev NRF24L01+ device handle + * @param[in] state State + * + * @return Old state + * @retval -EAGAIN Device is currently not permitted to change state + * @retval -ENOTSUP Device is not permitted to change state to @p state + */ +int nrf24l01p_ng_set_state(nrf24l01p_ng_t *dev, nrf24l01p_ng_state_t state); + +/** + * @brief Get current device state + * + * @param[in] dev NRf24L01+ device handle + * + * @return Device state + */ +nrf24l01p_ng_state_t nrf24l01p_ng_get_state(const nrf24l01p_ng_t *dev); + +#if IS_USED(MODULE_NRF24L01P_NG_DIAGNOSTICS) +/** + * @brief Get state variable as a string + * + * @param[in] state State + * + * @return @p state as a string + */ +const char * +nrf24l01p_ng_diagnostics_state_to_string(nrf24l01p_ng_state_t state); + +/** + * @brief Convert string to state variable + * + * @param[in] sstate State string + * + * @return State variable + */ +nrf24l01p_ng_state_t +nrf24l01p_ng_diagnostics_string_to_state(const char *sstate); + +/** + * @brief Print all registers + * + * @param[in] dev NRf24L01+ device handle + */ +void nrf24l01p_ng_print_all_regs(nrf24l01p_ng_t *dev); + +/** + * @brief Print device parameters + * + * @param[in] dev NRf24L01+ device handle + */ +void nrf24l01p_ng_print_dev_info(const nrf24l01p_ng_t *dev); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* NRF24L01P_NG_H */ +/** @} */ diff --git a/drivers/nrf24l01p_ng/Makefile b/drivers/nrf24l01p_ng/Makefile new file mode 100644 index 000000000000..4801d1c5d4ee --- /dev/null +++ b/drivers/nrf24l01p_ng/Makefile @@ -0,0 +1,10 @@ +# Makefile for the NRF24L01+ radio driver +MODULE = nrf24l01p_ng + +# exclude submodule sources from *.c wildcard source selection +SRC := $(filter-out diagnostics.c,$(wildcard *.c)) + +# enable submodules +SUBMODULES := 1 + +include $(RIOTBASE)/Makefile.base diff --git a/drivers/nrf24l01p_ng/Makefile.dep b/drivers/nrf24l01p_ng/Makefile.dep new file mode 100644 index 000000000000..8c7dc0b1ed7a --- /dev/null +++ b/drivers/nrf24l01p_ng/Makefile.dep @@ -0,0 +1,10 @@ +USEMODULE += luid +USEMODULE += netif +USEMODULE += xtimer +ifneq (,$(filter gnrc_ipv6,$(USEMODULE))) + USEMODULE += gnrc_sixlowpan_default +endif + +FEATURES_REQUIRED += periph_gpio +FEATURES_REQUIRED += periph_gpio_irq +FEATURES_REQUIRED += periph_spi diff --git a/drivers/nrf24l01p_ng/Makefile.include b/drivers/nrf24l01p_ng/Makefile.include new file mode 100644 index 000000000000..ac822dddd579 --- /dev/null +++ b/drivers/nrf24l01p_ng/Makefile.include @@ -0,0 +1,2 @@ +USEMODULE_INCLUDES_nrf24l01p_ng := $(LAST_MAKEFILEDIR)/include +USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_nrf24l01p_ng) diff --git a/drivers/nrf24l01p_ng/diagnostics.c b/drivers/nrf24l01p_ng/diagnostics.c new file mode 100644 index 000000000000..f5f788a131c4 --- /dev/null +++ b/drivers/nrf24l01p_ng/diagnostics.c @@ -0,0 +1,391 @@ +/* + * Copyright (C) 2019 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +/** + * @ingroup drivers_nrf24l01p_ng + * @{ + * + * @file + * @brief Functions to debug the NRF24L01+ (NG) transceiver + * + * @author Fabian Hüßler + * @} + */ +#include +#include +#include "nrf24l01p_ng_registers.h" +#include "nrf24l01p_ng_communication.h" +#include "nrf24l01p_ng_diagnostics.h" + +#if NRF24L01P_NG_ADDR_WIDTH == 3 +#define ADDR_FMT "%02x%02x%02x" +#define ADDR_P0(dev) \ + NRF24L01P_NG_ADDR_P0(dev)[0], \ + NRF24L01P_NG_ADDR_P0(dev)[1], \ + NRF24L01P_NG_ADDR_P0(dev)[2] +#define ADDR_P1(dev) \ + NRF24L01P_NG_ADDR_P1(dev)[0], \ + NRF24L01P_NG_ADDR_P1(dev)[1], \ + NRF24L01P_NG_ADDR_P1(dev)[2] +#elif NRF24L01P_NG_ADDR_WIDTH == 4 +#define ADDR_FMT "%02x%02x%02x%02x" +#define ADDR_P0(dev) \ + NRF24L01P_NG_ADDR_P0(dev)[0], \ + NRF24L01P_NG_ADDR_P0(dev)[1], \ + NRF24L01P_NG_ADDR_P0(dev)[2], \ + NRF24L01P_NG_ADDR_P0(dev)[3] +#define ADDR_P1(dev) \ + NRF24L01P_NG_ADDR_P1(dev)[0], \ + NRF24L01P_NG_ADDR_P1(dev)[1], \ + NRF24L01P_NG_ADDR_P1(dev)[2], \ + NRF24L01P_NG_ADDR_P1(dev)[3] +#else +#define ADDR_FMT "%02x%02x%02x%02x%02x" +#define ADDR_P0(dev) \ + NRF24L01P_NG_ADDR_P0(dev)[0], \ + NRF24L01P_NG_ADDR_P0(dev)[1], \ + NRF24L01P_NG_ADDR_P0(dev)[2], \ + NRF24L01P_NG_ADDR_P0(dev)[3], \ + NRF24L01P_NG_ADDR_P0(dev)[4] +#define ADDR_P1(dev) \ + NRF24L01P_NG_ADDR_P1(dev)[0], \ + NRF24L01P_NG_ADDR_P1(dev)[1], \ + NRF24L01P_NG_ADDR_P1(dev)[2], \ + NRF24L01P_NG_ADDR_P1(dev)[3], \ + NRF24L01P_NG_ADDR_P1(dev)[4] +#endif + +const char * +nrf24l01p_ng_diagnostics_state_to_string(nrf24l01p_ng_state_t state) +{ + if (state == NRF24L01P_NG_STATE_POWER_DOWN) { + return "POWER_DOWN"; + } + if (state == NRF24L01P_NG_STATE_STANDBY_1) { + return "STANDBY_1"; + } + if (state == NRF24L01P_NG_STATE_STANDBY_2) { + return "STANDBY_2"; + } + if (state == NRF24L01P_NG_STATE_TX_MODE) { + return "TX_MODE"; + } + if (state == NRF24L01P_NG_STATE_RX_MODE) { + return "RX_MODE"; + } + return "UNDEFINED"; +} + +nrf24l01p_ng_state_t +nrf24l01p_ng_diagnostics_string_to_state(const char *sstate) +{ + if (!strcmp(sstate, "POWER_DOWN")) { + return NRF24L01P_NG_STATE_POWER_DOWN; + } + if (!strcmp(sstate, "STANDBY_1")) { + return NRF24L01P_NG_STATE_STANDBY_1; + } + if (!strcmp(sstate, "STANDBY_2")) { + return NRF24L01P_NG_STATE_STANDBY_2; + } + if (!strcmp(sstate, "TX_MODE")) { + return NRF24L01P_NG_STATE_TX_MODE; + } + if (!strcmp(sstate, "RX_MODE")) { + return NRF24L01P_NG_STATE_RX_MODE; + } + return NRF24L01P_NG_STATE_UNDEFINED; +} + +void nrf24l01p_ng_diagnostics_print_all_regs(const nrf24l01p_ng_t *dev) +{ + uint8_t config; + + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_CONFIG, &config, 1); + puts(""); + printf( + "CONFIG [MASK_RX_DR %u " + "MASK_TX_DS %u " + "MASK_MAX_RT %u " + "EN_CRC %u " + "CRCO %u " + "PWR_UP %u " + "PRIM_RX %u]\n", + (unsigned)NRF24L01P_NG_VAL_MASK_RX_DR(config), + (unsigned)NRF24L01P_NG_VAL_MASK_TX_DS(config), + (unsigned)NRF24L01P_NG_VAL_MAX_RT(config), + (unsigned)NRF24L01P_NG_VAL_EN_CRC(config), + (unsigned)NRF24L01P_NG_VAL_CRCO(config), + (unsigned)NRF24L01P_NG_VAL_PWR_UP(config), + (unsigned)NRF24L01P_NG_VAL_PRIM_RX(config) + ); + uint8_t en_aa; + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_EN_AA, &en_aa, 1); + printf( + "EN_AA [ENAA_P5 %u " + "ENAA_P4 %u " + "ENAA_P3 %u " + "ENAA_P2 %u " + "ENAA_P1 %u " + "ENAA_P0 %u]\n", + (unsigned)NRF24L01P_NG_VAL_ENAA_P5(en_aa), + (unsigned)NRF24L01P_NG_VAL_ENAA_P4(en_aa), + (unsigned)NRF24L01P_NG_VAL_ENAA_P3(en_aa), + (unsigned)NRF24L01P_NG_VAL_ENAA_P2(en_aa), + (unsigned)NRF24L01P_NG_VAL_ENAA_P1(en_aa), + (unsigned)NRF24L01P_NG_VAL_ENAA_P0(en_aa) + ); + uint8_t en_rxaddr; + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_EN_RXADDR, &en_rxaddr, 1); + printf( + "EN_RXADDR [ERX_P5 %u " + "ERX_P4 %u " + "ERX_P3 %u " + "ERX_P2 %u " + "ERX_P1 %u " + "ERX_P0 %u]\n", + (unsigned)NRF24L01P_NG_VAL_ERX_P5(en_rxaddr), + (unsigned)NRF24L01P_NG_VAL_ERX_P4(en_rxaddr), + (unsigned)NRF24L01P_NG_VAL_ERX_P3(en_rxaddr), + (unsigned)NRF24L01P_NG_VAL_ERX_P2(en_rxaddr), + (unsigned)NRF24L01P_NG_VAL_ERX_P1(en_rxaddr), + (unsigned)NRF24L01P_NG_VAL_ERX_P0(en_rxaddr) + ); + uint8_t setup_aw; + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_SETUP_AW, &setup_aw, 1); + printf( + "SETUP_AW [AW %u]\n", + (unsigned)NRF24L01P_NG_VAL_AW(setup_aw) + ); + uint8_t setup_retr; + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_SETUP_RETR, &setup_retr, 1); + printf( + "SETUP_RETR [ARD %u ARC %u]\n", + (unsigned)NRF24L01P_NG_VAL_ARD(setup_retr), + (unsigned)NRF24L01P_NG_VAL_ARC(setup_retr) + ); + uint8_t rf_ch; + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_RF_CH, &rf_ch, 1); + printf( + "RF_CH [RF_CH %u]\n", + (unsigned)NRF24L01P_NG_VAL_RF_CH(rf_ch) + ); + uint8_t rf_setup; + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_RF_SETUP, &rf_setup, 1); + printf( + "RF_SETUP [CONT_WAVE %u " + "RF_DR_LOW %u " + "PLL_LOCK %u " + "RF_DR_HIGH %u " + "RF_PWR %u]\n", + (unsigned)NRF24L01P_NG_VAL_CONT_WAVE(rf_setup), + (unsigned)NRF24L01P_NG_VAL_RF_DR_LOW(rf_setup), + (unsigned)NRF24L01P_NG_VAL_PLL_LOCK(rf_setup), + (unsigned)NRF24L01P_NG_VAL_RF_DR_HIGH(rf_setup), + (unsigned)NRF24L01P_NG_VAL_RF_PWR(rf_setup) + ); + uint8_t status; + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_STATUS, &status, 1); + printf( + "STATUS [RX_DR %u " + "TX_DS %u " + "MAX_RT %u " + "RX_P_NO %u " + "TX_FULL %u]\n", + (unsigned)NRF24L01P_NG_VAL_RX_DR(status), + (unsigned)NRF24L01P_NG_VAL_TX_DS(status), + (unsigned)NRF24L01P_NG_VAL_MAX_RT(status), + (unsigned)NRF24L01P_NG_VAL_RX_P_NO(status), + (unsigned)NRF24L01P_NG_VAL_TX_FULL(status) + ); + uint8_t observe_tx; + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_OBSERVE_TX, &observe_tx, 1); + printf( + "OBSERVE_TX [PLOS_CNT %u ARC_CNT %u]\n", + (unsigned)NRF24L01P_NG_VAL_PLOS_CNT(observe_tx), + (unsigned)NRF24L01P_NG_VAL_ARC_CNT(observe_tx) + ); + uint8_t rpd; + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_RPD, &rpd, 1); + printf( + "RPD [RPD %u]\n", + (unsigned)NRF24L01P_NG_VAL_RPD(rpd) + ); + uint8_t rx_addr_px_40[2][NRF24L01P_NG_MAX_ADDR_WIDTH]; /* Pipe 0/1 */ + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_RX_ADDR_P0, rx_addr_px_40[0], + NRF24L01P_NG_MAX_ADDR_WIDTH); + printf( + "RX_ADDR_P0 [RX_ADDR_P0 %02x:%02x:%02x:%02x:%02x]\n", + rx_addr_px_40[0][0], + rx_addr_px_40[0][1], + rx_addr_px_40[0][2], + rx_addr_px_40[0][3], + rx_addr_px_40[0][4] + ); + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_RX_ADDR_P1, rx_addr_px_40[1], + NRF24L01P_NG_MAX_ADDR_WIDTH); + printf( + "RX_ADDR_P1 [RX_ADDR_P1 %02x:%02x:%02x:%02x:%02x]\n", + rx_addr_px_40[1][0], + rx_addr_px_40[1][1], + rx_addr_px_40[1][2], + rx_addr_px_40[1][3], + rx_addr_px_40[1][4] + ); + uint8_t rx_addr_px_8[4]; /* Pipe 2/3/4/5 */ + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_RX_ADDR_P2, + &rx_addr_px_8[0], 1); + printf( + "RX_ADDR_P2 [RX_ADDR_P2 %02x]\n", + rx_addr_px_8[0] + ); + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_RX_ADDR_P3, + &rx_addr_px_8[1], 1); + printf( + "RX_ADDR_P3 [RX_ADDR_P3 %02x]\n", + rx_addr_px_8[1] + ); + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_RX_ADDR_P4, + &rx_addr_px_8[2], 1); + printf( + "RX_ADDR_P4 [RX_ADDR_P4 %02x]\n", + rx_addr_px_8[2] + ); + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_RX_ADDR_P5, + &rx_addr_px_8[3], 1); + printf( + "RX_ADDR_P5 [RX_ADDR_P5 %02x]\n", + rx_addr_px_8[3] + ); + uint8_t tx_addr[NRF24L01P_NG_MAX_ADDR_WIDTH]; + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_TX_ADDR, tx_addr, + NRF24L01P_NG_MAX_ADDR_WIDTH); + printf( + "TX_ADDR [TX_ADDR %02x:%02x:%02x:%02x:%02x]\n", + tx_addr[0], + tx_addr[1], + tx_addr[2], + tx_addr[3], + tx_addr[4] + ); + uint8_t rx_pw_px[6]; + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_RX_PW_P0, &rx_pw_px[0], 1); + printf( + "RX_PW_P0 [RX_PW_P0 %u]\n", + (unsigned)NRF24L01P_NG_VAL_RX_PW_PX(rx_pw_px[0]) + ); + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_RX_PW_P1, &rx_pw_px[1], 1); + printf( + "RX_PW_P1 [RX_PW_P1 %u]\n", + (unsigned)NRF24L01P_NG_VAL_RX_PW_PX(rx_pw_px[1]) + ); + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_RX_PW_P2, &rx_pw_px[2], 1); + printf( + "RX_PW_P2 [RX_PW_P2 %u]\n", + (unsigned)NRF24L01P_NG_VAL_RX_PW_PX(rx_pw_px[2]) + ); + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_RX_PW_P3, &rx_pw_px[3], 1); + printf( + "RX_PW_P3 [RX_PW_P3 %u]\n", + (unsigned)NRF24L01P_NG_VAL_RX_PW_PX(rx_pw_px[3]) + ); + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_RX_PW_P4, &rx_pw_px[4], 1); + printf( + "RX_PW_P4 [RX_PW_P4 %u]\n", + (unsigned)NRF24L01P_NG_VAL_RX_PW_PX(rx_pw_px[4]) + ); + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_RX_PW_P5, &rx_pw_px[5], 1); + printf( + "RX_PW_P5 [RX_PW_P5 %u]\n", + (unsigned)NRF24L01P_NG_VAL_RX_PW_PX(rx_pw_px[5]) + ); + uint8_t fifo_status; + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_FIFO_STATUS, &fifo_status, 1); + printf( + "FIFO_STATUS [TX_REUSE %u " + "TX_FULL %u " + "TX_EMPTY %u " + "RX_FULL %u " + "RX_EMPTY %u]\n", + (unsigned)NRF24L01P_NG_VAL_TX_REUSE(fifo_status), + (unsigned)NRF24L01P_NG_VAL_TX_FULL_(fifo_status), + (unsigned)NRF24L01P_NG_VAL_TX_EMPTY(fifo_status), + (unsigned)NRF24L01P_NG_VAL_RX_FULL(fifo_status), + NRF24L01P_NG_VAL_RX_EMPTY(fifo_status) + ); + uint8_t dynpd; + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_DYNPD, &dynpd, 1); + printf( + "DYNDP [DPL_P5 %u " + "DPL_P4 %u " + "DPL_P3 %u " + "DPL_P2 %u " + "DPL_P1 %u " + "DPL_P0 %u]\n", + (unsigned)NRF24L01P_NG_VAL_DPL_P5(dynpd), + (unsigned)NRF24L01P_NG_VAL_DPL_P4(dynpd), + (unsigned)NRF24L01P_NG_VAL_DPL_P3(dynpd), + (unsigned)NRF24L01P_NG_VAL_DPL_P2(dynpd), + (unsigned)NRF24L01P_NG_VAL_DPL_P1(dynpd), + (unsigned)NRF24L01P_NG_VAL_DPL_P0(dynpd) + ); + uint8_t features; + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_FEATURES, &features, 1); + printf( + "FEATURES [EN_DPL %u EN_ACK_PAY %u DYN_ACK %u]\n", + (unsigned)NRF24L01P_NG_VAL_EN_DPL(features), + (unsigned)NRF24L01P_NG_VAL_EN_ACK_PAY(features), + (unsigned)NRF24L01P_NG_VAL_EN_DYN_ACK(features) + ); +} + +void nrf24l01p_ng_diagnostics_print_dev_info(const nrf24l01p_ng_t *dev) +{ + printf("device: %p\n", (const void *)dev); + printf("address length: %u\n", (unsigned)NRF24L01P_NG_ADDR_WIDTH); + printf("device params:\n"); + printf("\tChannel: %u\n", + dev->params.config.cfg_channel); + printf("\tCRC length: %u bytes\n", + (unsigned)nrf24l01p_ng_etoval_crc(dev->params.config.cfg_crc)); + printf("\tData rate: %u kbps\n", + (unsigned) + nrf24l01p_ng_etoval_rfdr(dev->params.config.cfg_data_rate)); + printf("\tMax. retransmissions: %u\n", + dev->params.config.cfg_max_retr); + printf("\tRetransmission delay: %u us\n", + (unsigned) + nrf24l01p_ng_etoval_ard(dev->params.config.cfg_retr_delay)); + printf("\tTx power: %d dbm\n", + nrf24l01p_ng_etoval_tx_power(dev->params.config.cfg_tx_power)); + printf("\tRx address p0: "ADDR_FMT"\n", + ADDR_P0(dev)); + printf("\tRX address p1: "ADDR_FMT"\n", + ADDR_P1(dev)); + printf("\tRX address p2: %02x\n", + NRF24L01P_NG_ADDR_PX_LSB(dev, NRF24L01P_NG_P2)); + printf("\tRX address p3: %02x\n", + NRF24L01P_NG_ADDR_PX_LSB(dev, NRF24L01P_NG_P3)); + printf("\tRX address p4: %02x\n", + NRF24L01P_NG_ADDR_PX_LSB(dev, NRF24L01P_NG_P4)); + printf("\tRX address p5: %02x\n", + NRF24L01P_NG_ADDR_PX_LSB(dev, NRF24L01P_NG_P5)); + + printf("State: %s\n", + nrf24l01p_ng_diagnostics_state_to_string(dev->state)); +} + +void nrf24l01p_ng_diagnostics_print_frame(const nrf24l01p_ng_t *dev, + const void *frame, size_t len) +{ + (void)dev; + puts("Rx frame"); + for (uint8_t i = 0; i < len; i++) { + printf("0x%02X ", ((uint8_t *)frame)[i]); + } + puts(""); +} diff --git a/drivers/nrf24l01p_ng/gnrc_netif_nrf24l01p_ng.c b/drivers/nrf24l01p_ng/gnrc_netif_nrf24l01p_ng.c new file mode 100644 index 000000000000..f1cb2f91a4ec --- /dev/null +++ b/drivers/nrf24l01p_ng/gnrc_netif_nrf24l01p_ng.c @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2019 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +/** + * @ingroup drivers_nrf24l01p_ng + * @{ + * + * @file + * @brief Implementation of gnrc_netif Rx/Tx adaptation functions + * for the NRF24L01+ (NG) transceiver + * + * @author Fabian Hüßler + * @} + */ +#include + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#include "net/gnrc.h" +#include "gnrc_netif_nrf24l01p_ng.h" +#include "nrf24l01p_ng.h" + +/** + * @brief Broadcast/Multicast flag + */ +#define BCAST (GNRC_NETIF_HDR_FLAGS_BROADCAST | GNRC_NETIF_HDR_FLAGS_MULTICAST) + +static gnrc_pktsnip_t *_nrf24l01p_ng_pkt_recv(gnrc_netif_t *netif) +{ + /* get frame size */ + int frame_len = netif->dev->driver->recv(netif->dev, NULL, 1, NULL); + + /* allocate space for the packet in the pktbuf */ + gnrc_pktsnip_t *frame = gnrc_pktbuf_add(NULL, NULL, frame_len, + NRF24L01P_NG_UPPER_LAYER_PROTOCOL); + if (!frame) { + DEBUG_PUTS("[nrf24l01p_ng] _pkt_recv: unable to allocate space"); + return NULL; + } + + /* copy the payload into the packet buffer */ + frame_len = netif->dev->driver->recv(netif->dev, frame->data, + frame_len, NULL); + if (frame_len <= 0) { + DEBUG_PUTS("[nrf24l01p_ng] _pkt_recv: driver error"); + gnrc_pktbuf_release(frame); + return NULL; + } + return frame; +} + +/** + * @brief Receives a @ref net_gnrc_pkt "packet" from the network interface + * + * @pre `netif != NULL` + * + * @note The function takes the bytes received via netdev_driver_t::recv() + * from gnrc_netif_t::dev and re-formats it to a + * @ref net_gnrc_pkt "packet" containing a @ref net_gnrc_netif_hdr + * and a payload header in receive order. + * + * @param[in] netif The network interface. + * + * @return The packet received. Contains the payload (with the type marked + * accordingly) and a @ref net_gnrc_netif_hdr in receive order. + * @return NULL, if @ref net_gnrc_pktbuf was full. + */ +static gnrc_pktsnip_t *_nrf24l01p_ng_adpt_recv(gnrc_netif_t *netif) +{ + assert(netif); + assert(netif->dev); + + gnrc_pktsnip_t *frame; + gnrc_pktsnip_t *snip; + gnrc_netif_hdr_t *netif_hdr; + uint8_t dst_addr[NRF24L01P_NG_ADDR_WIDTH]; + uint8_t src_addr[NRF24L01P_NG_ADDR_WIDTH]; + uint8_t src_addr_len; + + if (!(frame = _nrf24l01p_ng_pkt_recv(netif))) { + DEBUG_PUTS("[nrf24l01p_ng] _adpt_recv: no frame"); + return NULL; + } + if (!(snip = gnrc_pktbuf_mark(frame, sizeof(dst_addr), GNRC_NETTYPE_UNDEF))) { + DEBUG_PUTS("[nrf24l01p_ng] _adpt_recv: unable to mark header snip"); + gnrc_pktbuf_release(frame); + return NULL; + } + memcpy(dst_addr, snip->data, sizeof(dst_addr)); + gnrc_pktbuf_remove_snip(frame, snip); + + src_addr_len = ((uint8_t *)frame->data)[0]; + if (src_addr_len < NRF24L01P_NG_MIN_ADDR_WIDTH || + src_addr_len > NRF24L01P_NG_MAX_ADDR_WIDTH) { + DEBUG_PUTS("[nrf24l01p_ng] _adpt_recv: Invalid source address length"); + gnrc_pktbuf_release(frame); + return NULL; + } + if (!(snip = gnrc_pktbuf_mark(frame, 1 + src_addr_len, GNRC_NETTYPE_UNDEF))) { + DEBUG_PUTS("[nrf24l01p_ng] _adpt_recv: unable to mark src header snip"); + gnrc_pktbuf_release(frame); + return NULL; + } + memcpy(src_addr, ((uint8_t *)snip->data) + 1, src_addr_len); + gnrc_pktbuf_remove_snip(frame, snip); + + if (!(snip = gnrc_netif_hdr_build(src_addr, src_addr_len, + dst_addr, sizeof(dst_addr)))) { + DEBUG_PUTS("[nrf24l01p_ng] _adpt_recv: unable to allocate netif header"); + gnrc_pktbuf_release(frame); + return NULL; + } + netif_hdr = (gnrc_netif_hdr_t *)snip->data; + const uint8_t bcast_addr[] = NRF24L01P_NG_BROADCAST_ADDR; + if (!memcmp(dst_addr, bcast_addr, sizeof(dst_addr))) { + netif_hdr->flags |= GNRC_NETIF_HDR_FLAGS_BROADCAST; + } + gnrc_netif_hdr_set_netif(netif_hdr, netif); + LL_APPEND(frame, snip); +#if IS_USED(MODULE_NETSTATS_L2) + netif->stats.rx_count++; + netif->stats.rx_bytes += frame->size; +#endif + return frame; +} + +/** + * @brief Send a @ref net_gnrc_pkt "packet" over the network interface + * + * @pre `netif != NULL && pkt != NULL` + * + * @note The function re-formats the content of @p pkt to a format expected + * by the netdev_driver_t::send() method of gnrc_netif_t::dev and + * releases the packet before returning (so no additional release + * should be required after calling this method). + * + * @param[in] netif The network interface. + * @param[in] pkt A packet to send. + * + * @return The number of bytes actually sent on success + * @return -EBADMSG, if the @ref net_gnrc_netif_hdr in @p pkt is missing + * or is in an unexpected format. + * @return -ENOTSUP, if sending @p pkt in the given format isn't supported + * (e.g. empty payload with Ethernet). + * @return Any negative error code reported by gnrc_netif_t::dev. + */ +static int _nrf24l01p_ng_adpt_send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt) +{ + assert(netif); + assert(pkt); + assert(netif->dev); + + netdev_t *netdev = (netdev_t *)netif->dev; + gnrc_netif_hdr_t *netif_hdr = (gnrc_netif_hdr_t *)pkt->data; + if (!netif_hdr) { + return -EBADMSG; + } + const uint8_t bcast_addr[] = NRF24L01P_NG_BROADCAST_ADDR; + const uint8_t *dst_addr = gnrc_netif_hdr_get_dst_addr(netif_hdr); + uint8_t dst_addr_len = netif_hdr->dst_l2addr_len; + + if (netif_hdr->flags & BCAST) { + DEBUG_PUTS("[nrf24l01p_ng] _adpt_send: preparing to send broadcast"); + dst_addr = bcast_addr; + dst_addr_len = NRF24L01P_NG_ADDR_WIDTH; +#if IS_USED(MODULE_NETSTATS_L2) + netif->stats.tx_mcast_count++; +#endif + } + else { + if (!dst_addr || + dst_addr_len > NRF24L01P_NG_MAX_ADDR_WIDTH || + dst_addr_len < NRF24L01P_NG_MIN_ADDR_WIDTH) { + return -EBADMSG; + } + if (!memcmp(dst_addr, bcast_addr, dst_addr_len)) { + DEBUG_PUTS("[nrf24l01p_ng] _adpt_send: preparing to send broadcast"); +#if IS_USED(MODULE_NETSTATS_L2) + netif->stats.tx_mcast_count++; +#endif + } + } + + uint8_t src[1 + NRF24L01P_NG_ADDR_WIDTH]; + src[0] = NRF24L01P_NG_ADDR_WIDTH; + memcpy(src + 1, + NRF24L01P_NG_ADDR_P1((nrf24l01p_ng_t *)netdev), + NRF24L01P_NG_ADDR_WIDTH); + iolist_t iolist_src_addr = { + .iol_next = ((iolist_t *)pkt->next), + .iol_base = src, + .iol_len = sizeof(src) + }; + iolist_t iolist = { + .iol_next = &iolist_src_addr, + .iol_base = (uint8_t *)dst_addr, + .iol_len = dst_addr_len + }; + int res; + while ((res = netdev->driver->send(netdev, &iolist)) < 0) { + if (res == -EAGAIN) { + /* pending interrupts? */ + continue; + } + else if (res == -EBUSY) { + if (!IS_ACTIVE(MODULE_GNRC_NETIF_PKTQ)) { + /* busy send */ + continue; + } + } + break; + } + gnrc_pktbuf_release(pkt); + return res; +} + +static const gnrc_netif_ops_t nrf24l01p_ng_netif_ops = { + .init = gnrc_netif_default_init, + .send = _nrf24l01p_ng_adpt_send, + .recv = _nrf24l01p_ng_adpt_recv, + .get = gnrc_netif_get_from_netdev, + .set = gnrc_netif_set_from_netdev, +}; + +int gnrc_netif_nrf24l01p_ng_create(gnrc_netif_t *netif, char *stack, + int stacksize, char priority, char *name, + netdev_t *dev) +{ + return gnrc_netif_create(netif, stack, stacksize, priority, name, + dev, &nrf24l01p_ng_netif_ops); +} diff --git a/drivers/nrf24l01p_ng/include/gnrc_netif_nrf24l01p_ng.h b/drivers/nrf24l01p_ng/include/gnrc_netif_nrf24l01p_ng.h new file mode 100644 index 000000000000..c9a5a335517f --- /dev/null +++ b/drivers/nrf24l01p_ng/include/gnrc_netif_nrf24l01p_ng.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2019 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +/** + * @ingroup drivers_nrf24l01p_ng + * @ingroup drivers_netdev + * @ingroup net_gnrc_netif + * @{ + * + * @file + * @brief NRF24L01+ (NG) adaptation for @ref net_gnrc_netif + * + * @author Fabian Hüßler + */ +#ifndef GNRC_NETIF_NRF24L01P_NG_H +#define GNRC_NETIF_NRF24L01P_NG_H + +#include "net/gnrc/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef NRF24L01P_NG_UPPER_LAYER_PROTOCOL +#if IS_USED(MODULE_GNRC_SIXLOWPAN) +#define NRF24L01P_NG_UPPER_LAYER_PROTOCOL (GNRC_NETTYPE_SIXLOWPAN) +#else +/** + * @brief type of layer 2 payload + */ +#define NRF24L01P_NG_UPPER_LAYER_PROTOCOL (GNRC_NETTYPE_UNDEF) +#endif +#endif + +/** + * @brief Creates an NRF24L01+ (NG) network interface + * + * @param[out] netif The interface. May not be `NULL`. + * @param[in] stack The stack for the network interface's thread + * @param[in] stacksize Size of @p stack + * @param[in] priority Priority for the network interface's thread + * @param[in] name Name for the network interface. May be NULL + * @param[in] dev Device for the interface + * + * @see @ref gnrc_netif_create() + * + * @return 0 on success + * @return negative number on error + */ +int gnrc_netif_nrf24l01p_ng_create(gnrc_netif_t *netif, char *stack, + int stacksize, char priority, char *name, + netdev_t *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* GNRC_NETIF_NRF24L01P_NG_H */ +/** @} */ diff --git a/drivers/nrf24l01p_ng/include/nrf24l01p_ng_communication.h b/drivers/nrf24l01p_ng/include/nrf24l01p_ng_communication.h new file mode 100644 index 000000000000..94984b666c0e --- /dev/null +++ b/drivers/nrf24l01p_ng/include/nrf24l01p_ng_communication.h @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2019 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +/** + * @ingroup drivers_nrf24l01p_ng + * @{ + * + * @file + * @brief Functions to communicate with the NRF24L01+ (NG) transceiver + * via SPI + * + * @author Fabian Hüßler + */ +#ifndef NRF24L01P_NG_COMMUNICATION_H +#define NRF24L01P_NG_COMMUNICATION_H + +#include "nrf24l01p_ng.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Mask for 5 bit register address + */ +#define NRF24L01P_NG_ADDR_MASK (0x1F) + +/** + * @brief Mask for 3 bit pipe number + */ +#define NRF24L01P_NG_PIPE_MASK (0x07) + +/** + * @brief Read register value + * (1 to 5 bytes) + */ +#define NRF24L01P_NG_CMD_R_REGISTER(reg) \ + (0x00 | ((reg) & NRF24L01P_NG_ADDR_MASK)) + +/** + * @brief Write register value + * (1 to 5 bytes) + */ +#define NRF24L01P_NG_CMD_W_REGISTER(reg) \ + (0x20 | ((reg) & NRF24L01P_NG_ADDR_MASK)) + +/** + * @brief Read RX payload buffer + * (1 to 32 bytes) + */ +#define NRF24L01P_NG_CMD_R_RX_PAYLOAD (0x61) + +/** + * @brief Write TX payload buffer + * (1 to 32 bytes) + */ +#define NRF24L01P_NG_CMD_W_TX_PAYLOAD (0xA0) + +/** + * @brief Flush TX buffer + * (0 bytes data) + */ +#define NRF24L01P_NG_CMD_FLUSH_TX (0xE1) + +/** + * @brief Flush RX buffer + * (0 bytes data) + */ +#define NRF24L01P_NG_CMD_FLUSH_RX (0xE2) + +/** + * @brief Reuse last transmitted payload + * (0 bytes data) + */ +#define NRF24L01P_NG_CMD_REUSE_TX_PL (0xE3) + +/** + * @brief Read RX payload width for the top R_RX_PAYLOAD in RX FIFO + * (1 byte data) + */ +#define NRF24L01P_NG_CMD_R_RX_PL_WID (0x60) + +/** + * @brief Write payload to be transmitted together with ACK for certain pipe + * (1 to 32 bytes data) + */ +#define NRF24L01P_NG_CMD_W_ACK_PAYLOAD(pipe) \ + (0xA8 | ((pipe) & NRF24L01P_NG_PIPE_MASK)) + +/** + * @brief Disable auto ACK on this specific frame + */ +#define NRF24L01P_NG_CMD_W_TX_PAYLOAD_NO_ACK (0xB0) + +/** + * @brief No operation + */ +#define NRF24L01P_NG_CMD_NOP (0xFF) + +/** + * @brief Acquire the SPI bus of the transceiver + * + * @param[in] dev NRF24L01+ device handle + * + * @return @see spi_acquire + */ +int nrf24l01p_ng_acquire(nrf24l01p_ng_t *dev); + +/** + * @brief Release the SPI bus of the transceiver + * + * @param[in] dev NRF24L01+ device handle + * + * @return @see spi_release + */ +void nrf24l01p_ng_release(nrf24l01p_ng_t *dev); + +/** + * @brief Read a register value + * + * @param[in] dev NRF24L01+ device handle + * @param[in] reg Register address + * @param[out] dest Output register + * @param[in] len Output register width + * + * @return Status register value + */ +uint8_t nrf24l01p_ng_read_reg(const nrf24l01p_ng_t *dev, uint8_t reg, + uint8_t *dest, size_t len); + +/** + * @brief Write a register value + * + * @param[in] dev NRF24L01+ device handle + * @param[in] reg Register address + * @param[in] src Data to be written into the register + * @param[in] len Register width + * + * @return Status register value + */ +uint8_t nrf24l01p_ng_write_reg(const nrf24l01p_ng_t *dev, uint8_t reg, + const uint8_t *src, size_t len); + +/** + * @brief Read received payload + * + * @param[in] dev NRF24L01+ device handle + * @param[out] dest Output payload buffer + * @param[in] len Output payload length + * + * @return Status register value + */ +uint8_t nrf24l01p_ng_read_rx_payload(const nrf24l01p_ng_t *dev, void *dest, + size_t len); + +/** + * @brief Write payload to be transmitted + * + * @param[in] dev NRF24L01+ device handle + * @param[in] src Input payload buffer + * @param[in] len Input payload length + * + * @return Status register value + */ +uint8_t nrf24l01p_ng_write_tx_payload(const nrf24l01p_ng_t *dev, + const void *src, size_t len); + +/** + * @brief Flush TX FIFO + * + * @param[in] dev NRF24L01+ device handle + * + * @return Status register value + */ +uint8_t nrf24l01p_ng_flush_tx(const nrf24l01p_ng_t *dev); + +/** + * @brief Flush RX FIFO + * + * @param[in] dev NRF24L01+ device handle + * + * @return Status register value + */ +uint8_t nrf24l01p_ng_flush_rx(const nrf24l01p_ng_t *dev); + +/** + * @brief Reuse TX payload in FIFO + * + * This function can be used to reattempt to transmit a frame, after a MAX_RT + * interrupt was triggered. + * + * @param[in] dev NRF24L01+ device handle + * + * @return Status register value + */ +uint8_t nrf24l01p_ng_reuse_tx_pl(const nrf24l01p_ng_t *dev); + +/** + * @brief Read received payload width + * + * @param[in] dev NRF24L01+ device handle + * @param[out] dest Payload width + * + * @return Status register value + */ +uint8_t nrf24l01p_ng_read_rx_pl_width(const nrf24l01p_ng_t *dev, + uint8_t *dest); + +/** + * @brief Write Payload to be transmitted in an ACK frame + * + * @param[in] dev NRF24L01+ device handle + * @param[in] src Input payload buffer + * @param[in] len Input payload length + * @param[in] pipe Index of data pipe + * + * @return Status register value + */ +uint8_t nrf24l01p_ng_write_ack_pl(const nrf24l01p_ng_t *dev, const void *src, + size_t len, uint8_t pipe); + +/** + * @brief Write payload to be transmitted but do not expect an ACK for + * this frame + * + * @param[in] dev NRF24L01+ device handle + * @param[in] src Input payload buffer + * @param[in] len Input payload length + * + * @return Status register value + */ +uint8_t nrf24l01p_ng_write_tx_pl_no_ack(const nrf24l01p_ng_t *dev, + const void *src, size_t len); + +/** + * @brief Get status register value + * + * @param[in] dev NRF24L01+ device handle + * + * @return Status register value + */ +uint8_t nrf24l01p_ng_get_status(const nrf24l01p_ng_t *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* NRF24L01P_NG_COMMUNICATION_H */ +/** @} */ diff --git a/drivers/nrf24l01p_ng/include/nrf24l01p_ng_constants.h b/drivers/nrf24l01p_ng/include/nrf24l01p_ng_constants.h new file mode 100644 index 000000000000..f7aa65d6346f --- /dev/null +++ b/drivers/nrf24l01p_ng/include/nrf24l01p_ng_constants.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2019 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +/** + * @ingroup drivers_nrf24l01p_ng + * @{ + * + * @file + * @brief Constants from the datasheet of the NRF24L01+ (NG) transceiver + * + * @author Fabian Hüßler + */ +#ifndef NRF24L01P_NG_CONSTANTS_H +#define NRF24L01P_NG_CONSTANTS_H + +#include "nrf24l01p_ng.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Maximum width of a payload, restricted by + * a FIFO size of 32 bytes + */ +#define NRF24L01P_NG_MAX_PAYLOAD_WIDTH (32) + +/** + * @brief Maximum number of retransmissions, if + * ESB is used as protocol + */ +#define NRF24L01P_NG_MAX_RETRANSMISSIONS (15) + +/** + * @brief Base frequency + */ +#define NRF24L01P_NG_BASE_FRQ_MHZ (2400) + +/** + * @brief Maximum supported frequency + */ +#define NRF24L01P_NG_MAX_FRQ_MHZ (2525) + +/** + * @brief Number of supported channels + * + * frequency range: 2400 - 2525 Mhz + * occupied bandwidth for each channel: + * 250 kbps: < 1Mhz + * 1000 kbps: < 1Mhz + * 2000 kbps: < 2Mhz + * + * -> spaces between each channel´s + * base frequency must be at least + * 2 Mhz, to be sufficient for all + * data rates. + */ +#define NRF24L01P_NG_NUM_CHANNELS (NRF24L01P_NG_MAX_FRQ_MHZ - \ + NRF24L01P_NG_BASE_FRQ_MHZ) + +/** + * @brief Reset value of TX_ADDR register + */ +#define NRF24L01P_NG_DEFAULT_TX_ADDR { 0xE7, 0xE7, 0xE7, 0xE7, 0xE7 } + +/** + * @brief Reset value of RX_ADDR_P0 register + */ +#define NRF24L01P_NG_DEFAULT_ADDR_P0 { 0xE7, 0xE7, 0xE7, 0xE7, 0xE7 } + +/** + * @brief Reset value of RX_ADDR_P1 register + */ +#define NRF24L01P_NG_DEFAULT_ADDR_P1 { 0xC2, 0xC2, 0xC2, 0xC2, 0xC2 } + +/** + * @brief Reset value of RX_ADDR_P2 register + */ +#define NRF24L01P_NG_DEFAULT_ADDR_P2 (0xC3) + +/** + * @brief Reset value of RX_ADDR_P3 register + */ +#define NRF24L01P_NG_DEFAULT_ADDR_P3 (0xC4) + +/** + * @brief Reset value of RX_ADDR_P4 register + */ +#define NRF24L01P_NG_DEFAULT_ADDR_P4 (0xC5) + +/** + * @brief Reset value of RX_ADDR_P5 register + */ +#define NRF24L01P_NG_DEFAULT_ADDR_P5 (0xC6) + +#ifdef __cplusplus +} +#endif + +#endif /* NRF24L01P_NG_CONSTANTS_H */ +/** @} */ diff --git a/drivers/nrf24l01p_ng/include/nrf24l01p_ng_diagnostics.h b/drivers/nrf24l01p_ng/include/nrf24l01p_ng_diagnostics.h new file mode 100644 index 000000000000..f110248b845b --- /dev/null +++ b/drivers/nrf24l01p_ng/include/nrf24l01p_ng_diagnostics.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2019 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +/** + * @ingroup drivers_nrf24l01p_ng + * @{ + * + * @file + * @brief Functions to print NRF24L01+ (NG) debug information + * + * @author Fabian Hüßler + */ +#ifndef NRF24L01P_NG_DIAGNOSTICS_H +#define NRF24L01P_NG_DIAGNOSTICS_H + +#include "nrf24l01p_ng.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Print all NRF24L01+ register values + * + * @param[in] dev NRF24L01+ device handle + */ +void nrf24l01p_ng_diagnostics_print_all_regs(const nrf24l01p_ng_t *dev); + +/** + * @brief Print NRF24L01+ device handle information + * + * @param[in] dev NRF24L01+ device handle + */ +void nrf24l01p_ng_diagnostics_print_dev_info(const nrf24l01p_ng_t *dev); + +/** + * @brief Print NRF24L01+ frame + * + * @param[in] dev NRF24L01+ device handle + * @param[in] frame Frame + * @param[in] len Frame length + */ +void nrf24l01p_ng_diagnostics_print_frame(const nrf24l01p_ng_t *dev, + const void *frame, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* NRF24L01P_NG_DIAGNOSTICS_H */ +/** @} */ diff --git a/drivers/nrf24l01p_ng/include/nrf24l01p_ng_netdev.h b/drivers/nrf24l01p_ng/include/nrf24l01p_ng_netdev.h new file mode 100644 index 000000000000..cc1cd82ff618 --- /dev/null +++ b/drivers/nrf24l01p_ng/include/nrf24l01p_ng_netdev.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +/** + * @ingroup drivers_nrf24l01p_ng + * @{ + * + * @file + * @brief Netdev driver interface for NRF24L01+ (NG) device driver + * + * @author Fabian Hüßler + */ +#ifndef NRF24L01P_NG_NETDEV_H +#define NRF24L01P_NG_NETDEV_H + +#include "net/netdev.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Struct that holds functions of NRF24L01+ device driver + */ +extern const netdev_driver_t nrf24l01p_ng_driver; + +#ifdef __cplusplus +} +#endif + +#endif /* NRF24L01P_NG_NETDEV_H */ +/** @} */ diff --git a/drivers/nrf24l01p_ng/include/nrf24l01p_ng_params.h b/drivers/nrf24l01p_ng/include/nrf24l01p_ng_params.h new file mode 100644 index 000000000000..00cae43dcabe --- /dev/null +++ b/drivers/nrf24l01p_ng/include/nrf24l01p_ng_params.h @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2019 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +/** + * @ingroup drivers_nrf24l01p_ng + * @{ + * + * @file + * @brief Board specific configuration for all NRF24L01+ (NG) devices + * + * @author Fabian Hüßler + */ +#ifndef NRF24L01P_NG_PARAMS_H +#define NRF24L01P_NG_PARAMS_H + +#include "board.h" +#include "periph/gpio.h" +#include "periph/spi.h" +#include "kernel_defines.h" +#include "nrf24l01p_ng_constants.h" +#include "nrf24l01p_ng.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef NRF24L01P_NG_PARAM_SPI +/** + * @brief SPI bus + */ +#define NRF24L01P_NG_PARAM_SPI SPI_DEV(0) +#endif + +#ifndef NRF24L01P_NG_PARAM_SPI_CLK +/** + * @brief SPI clock speed + */ +#define NRF24L01P_NG_PARAM_SPI_CLK SPI_CLK_5MHZ +#endif + +#ifndef NRF24L01P_NG_PARAM_CS +/** + * @brief SPI CS gpio pin + */ +#define NRF24L01P_NG_PARAM_CS GPIO_UNDEF +#endif + +#ifndef NRF24L01P_NG_PARAM_CE +/** + * @brief NRF24L01+ chip enable gpio pin + */ +#define NRF24L01P_NG_PARAM_CE GPIO_UNDEF +#endif + +#ifndef NRF24L01P_NG_PARAM_IRQ +/** + * @brief NRF24L01+ interrupt gpio pin + */ +#define NRF24L01P_NG_PARAM_IRQ GPIO_UNDEF +#endif + +#ifndef NRF24L01P_NG_PARAM_CRC_LEN +/** + * @brief Default CRC length + * @see nrf24l01p_ng_crc_t + */ +#define NRF24L01P_NG_PARAM_CRC_LEN (NRF24L01P_NG_CRC_2BYTE) +#endif + +#ifndef NRF24L01P_NG_PARAM_TX_POWER +/** + * @brief Default TX power + * @see nrf24l01p_ng_tx_power_t + */ +#define NRF24L01P_NG_PARAM_TX_POWER (NRF24L01P_NG_TX_POWER_0DBM) +#endif + +#ifndef NRF24L01P_NG_PARAM_DATA_RATE_LVL +/** + * @brief Default data rate + * @see nrf24l01p_ng_rfdr_t + */ +#define NRF24L01P_NG_PARAM_DATA_RATE (NRF24L01P_NG_RF_DR_2MBPS) +#endif + +#ifndef NRF24L01P_NG_PARAM_CHANNEL +/** + * @brief Default channel in [0; 124] + */ +#define NRF24L01P_NG_PARAM_CHANNEL (4) +#endif + +#ifndef NRF24L01P_NG_PARAM_MAX_RETRANSM +/** + * @brief Default number of retransmissions + */ +#define NRF24L01P_NG_PARAM_MAX_RETRANSM (5) +#endif + +#ifndef NRF24L01P_NG_PARAM_RETRANSM_DELAY +/** + * @brief Default retransmission delay + * @see nrf24l01p_ng_ard_t + */ +#define NRF24L01P_NG_PARAM_RETRANSM_DELAY (NRF24L01P_NG_ARD_2750US) +#endif + +#ifndef NRF24L01P_NG_PARAMS +/** + * @brief Default NRF24L01+ device parameters + */ +#define NRF24L01P_NG_PARAMS { \ + .spi = NRF24L01P_NG_PARAM_SPI, \ + .spi_clk = NRF24L01P_NG_PARAM_SPI_CLK, \ + .pin_cs = NRF24L01P_NG_PARAM_CS, \ + .pin_ce = NRF24L01P_NG_PARAM_CE, \ + .pin_irq = NRF24L01P_NG_PARAM_IRQ, \ + .config = { \ + .cfg_crc = NRF24L01P_NG_PARAM_CRC_LEN, \ + .cfg_tx_power = NRF24L01P_NG_PARAM_TX_POWER, \ + .cfg_data_rate = NRF24L01P_NG_PARAM_DATA_RATE, \ + .cfg_channel = NRF24L01P_NG_PARAM_CHANNEL, \ + .cfg_max_retr = NRF24L01P_NG_PARAM_MAX_RETRANSM, \ + .cfg_retr_delay = NRF24L01P_NG_PARAM_RETRANSM_DELAY, \ + } \ +} +#endif + +/** + * @brief Static array that holds NRF24L01+ device configurations + */ +static const nrf24l01p_ng_params_t nrf24l01p_ng_params[] = { + NRF24L01P_NG_PARAMS +}; + +/** + * @brief Number of NRF24L01+ device configurations + */ +#define NRF24L01P_NG_NUM ARRAY_SIZE(nrf24l01p_ng_params) + +#ifdef __cplusplus +} +#endif + +#endif /* NRF24L01P_NG_PARAMS_H */ +/** @} */ diff --git a/drivers/nrf24l01p_ng/include/nrf24l01p_ng_registers.h b/drivers/nrf24l01p_ng/include/nrf24l01p_ng_registers.h new file mode 100644 index 000000000000..557068033270 --- /dev/null +++ b/drivers/nrf24l01p_ng/include/nrf24l01p_ng_registers.h @@ -0,0 +1,972 @@ +/* + * Copyright (C) 2019 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +/** + * @ingroup drivers_nrf24l01p_ng + * @{ + * + * @file + * @brief Register map of NRF24L01+ (NG) devices + * + * @author Fabian Hüßler + */ +#ifndef NRF24L01P_NG_REGISTERS_H +#define NRF24L01P_NG_REGISTERS_H + +#include + +#include "nrf24l01p_ng.h" +#include "nrf24l01p_ng_communication.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name NRF24L01+ CONFIG register + * + * Address and layout of NRF24L01+ configuration register + * @{ + */ +/** + * @brief CONFIG register address + */ +#define NRF24L01P_NG_REG_CONFIG (0x00) +/** + * @brief Flag: MASK_RX_DR + */ +#define NRF24L01P_NG_FLG_MASK_RX_DR (0x40) +/** + *@brief Value of: MASK_RX_DR + */ +#define NRF24L01P_NG_VAL_MASK_RX_DR(reg) (((reg) & \ + NRF24L01P_NG_FLG_MASK_RX_DR) >> 6) +/** + * @brief Flag: MASK_TX_DS + */ +#define NRF24L01P_NG_FLG_MASK_TX_DS (0x20) +/** + * @brief Value of: MASK_TX_DS + */ +#define NRF24L01P_NG_VAL_MASK_TX_DS(reg) (((reg) & \ + NRF24L01P_NG_FLG_MASK_TX_DS) >> 5) +/** + * @brief Flag: MASK_MAX_RT + */ +#define NRF24L01P_NG_FLG_MASK_MAX_RT (0x10) +/** + * @brief Value of: MASK_MAX_RT + */ +#define NRF24L01P_NG_VAL_MASK_MAX_RT (((reg) & \ + NRF24L01P_NG_FLG_MASK_MAX_RT) >> 4) +/** + * @brief Flag: EN_CRC + */ +#define NRF24L01P_NG_FLG_EN_CRC (0x08) +/** + * @brief Value of: EN_CRC + */ +#define NRF24L01P_NG_VAL_EN_CRC(reg) (((reg) & \ + NRF24L01P_NG_FLG_EN_CRC) >> 3) +/** + * @brief Flag: CRCO - 1 byte + */ +#define NRF24L01P_NG_FLG_CRCO_1_BYTE (0x00) +/** + * @brief Flag: CRCO - 2 byte + */ +#define NRF24L01P_NG_FLG_CRCO_2_BYTE (0x04) +/** + * @brief Mask to configure CRC + */ +#define NRF24L01P_NG_MSK_CRC (0x0c) +/** + * @brief Flag: CRCO + */ +#define NRF24L01P_NG_FLG_CRCO(val) (((val) << 2) & \ + NRF24L01P_NG_MSK_CRC) +/** + * @brief Value of: CRCO + */ +#define NRF24L01P_NG_VAL_CRCO(reg) (((reg) & \ + NRF24L01P_NG_FLG_CRCO_2_BYTE) >> 2) +/** + * @brief Flag: PWR_UP + */ +#define NRF24L01P_NG_FLG_PWR_UP (0x02) +/** + * @brief Value of: PWR_UP + */ +#define NRF24L01P_NG_VAL_PWR_UP(reg) (((reg) & \ + NRF24L01P_NG_FLG_PWR_UP) >> 1) +/** + * @brief Flag: PRIM_RX + */ +#define NRF24L01P_NG_FLG_PRIM_RX (0x01) +/** + * @brief Value of: PRIM_RX + */ +#define NRF24L01P_NG_VAL_PRIM_RX(reg) ((reg) & NRF24L01P_NG_FLG_PRIM_RX) +/** @} */ + +/** + * @name NRF24L01+ EN_AA register + * + * Address and layout of NRF24L01+ EN_AA register + * @{ + */ +/** + * @brief EN_AA register address + */ +#define NRF24L01P_NG_REG_EN_AA (0x01) +/** + * @brief Flag: ENAA_P5 + */ +#define NRF24L01P_NG_FLG_ENAA_P5 (0x20) +/** + * @brief Value of: ENAA_P5 + */ +#define NRF24L01P_NG_VAL_ENAA_P5(reg) (((reg) & \ + NRF24L01P_NG_FLG_ENAA_P5) >> 5) +/** + * @brief Flag: ENAA_P4 + */ +#define NRF24L01P_NG_FLG_ENAA_P4 (0x10) +/** + * @brief Value of: ENAA_P4 + */ +#define NRF24L01P_NG_VAL_ENAA_P4(reg) (((reg) & \ + NRF24L01P_NG_FLG_ENAA_P4) >> 4) +/** + * @brief Flaga: ENAA_P3 + */ +#define NRF24L01P_NG_FLG_ENAA_P3 (0x08) +/** + * @brief Value of: ENAA_P3 + */ +#define NRF24L01P_NG_VAL_ENAA_P3(reg) (((reg) & \ + NRF24L01P_NG_FLG_ENAA_P3) >> 3) +/** + * @brief Flag: ENAA_P2 + */ +#define NRF24L01P_NG_FLG_ENAA_P2 (0x04) +/** + * @brief Value of: ENAA_P2 + */ +#define NRF24L01P_NG_VAL_ENAA_P2(reg) (((reg) & \ + NRF24L01P_NG_FLG_ENAA_P2) >> 2) +/** + * @brief Flag: ENAA_P1 + */ +#define NRF24L01P_NG_FLG_ENAA_P1 (0x02) +/** + * @brief Value of: ENAA_P1 + */ +#define NRF24L01P_NG_VAL_ENAA_P1(reg) (((reg) & \ + NRF24L01P_NG_FLG_ENAA_P1) >> 1) +/** + * @brief Flag: ENAA_P0 + */ +#define NRF24L01P_NG_FLG_ENAA_P0 (0x01) +/** + * @brief Value of: ENAA_P0 + */ +#define NRF24L01P_NG_VAL_ENAA_P0(reg) ((reg) & NRF24L01P_NG_FLG_ENAA_P0) +/** @} */ + +/** + * @name NRF24L01+ EN_RXADDR register + * + * Address and layout of NRF24L01+ EN_RXADDR register + * @{ + */ +/** + * @brief EN_RXADDR register address + */ +#define NRF24L01P_NG_REG_EN_RXADDR (0x02) +/** + * @brief Flag: ERX_P5 + */ +#define NRF24L01P_NG_FLG_ERX_P5 (0x20) +/** + * @brief Value of: ERX_P5 + */ +#define NRF24L01P_NG_VAL_ERX_P5(reg) (((reg) & \ + NRF24L01P_NG_FLG_ERX_P5) >> 5) +/** + * @brief Flag: ERX_P4 + */ +#define NRF24L01P_NG_FLG_ERX_P4 (0x10) +/** + * @brief Value of ERX_P4 + */ +#define NRF24L01P_NG_VAL_ERX_P4(reg) (((reg) & \ + NRF24L01P_NG_FLG_ERX_P4) >> 4) +/** + * @brief Flag: ERX_P3 + */ +#define NRF24L01P_NG_FLG_ERX_P3 (0x08) +/** + * @brief Value of: ERX_P3 + */ +#define NRF24L01P_NG_VAL_ERX_P3(reg) (((reg) & \ + NRF24L01P_NG_FLG_ERX_P3) >> 3) +/** + * @brief Flag: ERX_P2 + */ +#define NRF24L01P_NG_FLG_ERX_P2 (0x04) +/** + * @brief Value of: ERX_P2 + */ +#define NRF24L01P_NG_VAL_ERX_P2(reg) (((reg) & \ + NRF24L01P_NG_FLG_ERX_P2) >> 2) +/** + * @brief Flag: ERX_P1 + */ +#define NRF24L01P_NG_FLG_ERX_P1 (0x02) +/** + * @brief Value of: ERX_P1 + */ +#define NRF24L01P_NG_VAL_ERX_P1(reg) (((reg) & \ + NRF24L01P_NG_FLG_ERX_P1) >> 1) +/** + * @brief Flag: ERX_P0 + */ +#define NRF24L01P_NG_FLG_ERX_P0 (0x01) +/** + * @brief Value of: ERX_P0 + */ +#define NRF24L01P_NG_VAL_ERX_P0(reg) ((reg) & NRF24L01P_NG_FLG_ERX_P0) +/** @} */ + +/** + * @name NRF24L01+ SETUP_AW register + * + * Address and layout of NRF24L01+ SETUP_AW register + * @{ + */ +/** + * @brief SETUP_AW register address + */ +#define NRF24L01P_NG_REG_SETUP_AW (0x03) +/** + * @brief Flag: AW - 3 bytes + */ +#define NRF24L01P_NG_FLG_AW_3 (0x01) +/** + * @brief Flag: AW - 4 bytes + */ +#define NRF24L01P_NG_FLG_AW_4 (0x02) +/** + * @brief Flag: AW - 5 bytes + */ +#define NRF24L01P_NG_FLG_AW_5 (0x03) +/** + * @brief Mask to configure AW + */ +#define NRF24L01P_NG_MSK_AW (0x03) +/** + * @brief Flag AW from value [1; 3] + */ +#define NRF24L01P_NG_FLG_AW(val) ((val) & NRF24L01P_NG_MSK_AW) +/** + * @brief Value of: AW + */ +#define NRF24L01P_NG_VAL_AW(reg) ((reg) & NRF24L01P_NG_MSK_AW) +/** @} */ + +/** + * @name NRF24L01+ SETUP_RETR register + * + * Address and layout of NRF24L01+ SETUP_AW register + * @{ + */ +/** + * @brief SETUP_RETR register address + */ +#define NRF24L01P_NG_REG_SETUP_RETR (0x04) +/** + * @brief Flag: ARD - 250 us + */ +#define NRF24L01P_NG_FLG_ARD_250_US (0x00) +/** + * @brief Flag: ARD - 500 us + */ +#define NRF24L01P_NG_FLG_ARD_500_US (0x10) +/** + * @brief Flag: ARD - 750 us + */ +#define NRF24L01P_NG_FLG_ARD_750_US (0x20) +/** + * @brief Flag: ARD - 1000 us + */ +#define NRF24L01P_NG_FLG_ARD_1000_US (0x30) +/** + * @brief Flag: ARD - 1250 us + */ +#define NRF24L01P_NG_FLG_ARD_1250_US (0x40) +/** + * @brief Flag: ARD - 1500 us + */ +#define NRF24L01P_NG_FLG_ARD_1500_US (0x50) +/** + * @brief Flag: ARD - 1750 us + */ +#define NRF24L01P_NG_FLG_ARD_1750_US (0x60) +/** + * @brief Flag: ARD - 2000 us + */ +#define NRF24L01P_NG_FLG_ARD_2000_US (0x70) +/** + * @brief Flag: ARD - 2250 us + */ +#define NRF24L01P_NG_FLG_ARD_2250_US (0x80) +/** + * @brief Flag: ARD - 2500 us + */ +#define NRF24L01P_NG_FLG_ARD_2500_US (0x90) +/** + * @brief Flag: ARD - 2750 us + */ +#define NRF24L01P_NG_FLG_ARD_2750_US (0xa0) +/** + * @brief Flag: ARD - 3000 us + */ +#define NRF24L01P_NG_FLG_ARD_3000_US (0xb0) +/** + * @brief Flag: ARD - 3250 us + */ +#define NRF24L01P_NG_FLG_ARD_3250_US (0xc0) +/** + * @brief Flag: ARD - 3500 us + */ +#define NRF24L01P_NG_FLG_ARD_3500_US (0xd0) +/** + * @brief Flag: ARD - 3750 us + */ +#define NRF24L01P_NG_FLG_ARD_3750_US (0xe0) +/** + * @brief Flag: ARD - 4000 us + */ +#define NRF24L01P_NG_FLG_ARD_4000_US (0xf0) +/** + * @brief Mask to configure ARD + */ +#define NRF24L01P_NG_MSK_ARD (0xf0) +/** + * @brief Flag ARD from value [0; 15] + */ +#define NRF24L01P_NG_FLG_ARD(val) (((val) << 4) & \ + NRF24L01P_NG_MSK_ARD) +/** + * @brief Value of: ARD + */ +#define NRF24L01P_NG_VAL_ARD(reg) (((reg) & \ + NRF24L01P_NG_MSK_ARD) >> 4) +/** + * @brief Mask to configure ARC + */ +#define NRF24L01P_NG_MSK_ARC (0x0f) +/** + * @brief Flag ARC from value [0; 15] + */ +#define NRF24L01P_NG_FLG_ARC(val) ((val) & NRF24L01P_NG_MSK_ARC) +/** + * @brief Value of: ARC + */ +#define NRF24L01P_NG_VAL_ARC(reg) ((reg) & NRF24L01P_NG_MSK_ARC) +/** @} */ + +/** + * @name NRF24L01+ RF_CH register + * + * Address and layout of NRF24L01+ SETUP_AW register + * @{ + */ +/** + * @brief RF_CH register address + */ +#define NRF24L01P_NG_REG_RF_CH (0x05) +/** + * @brief Mask to configure RF_CH + */ +#define NRF24L01P_NG_MSK_RF_CH (0x7f) +/** + * @brief Flag RF_CH from value [0; 124] + */ +#define NRF24L01P_NG_FLG_RF_CH(val) ((val) & NRF24L01P_NG_MSK_RF_CH) +/** + * @brief Value of: RF_CH + */ +#define NRF24L01P_NG_VAL_RF_CH(reg) ((reg) & NRF24L01P_NG_MSK_RF_CH) +/** @} */ + +/** + * @name NRF24L01+ RF_SETUP register + * + * Address and layout of NRF24L01+ RF_SETUP register + * @{ + */ +/** + * @brief RF_SETUP register address + */ +#define NRF24L01P_NG_REG_RF_SETUP (0x06) +/** + * @brief Flag: CONT_WAVE + */ +#define NRF24L01P_NG_FLG_CONT_WAVE (0x80) +/** + * @brief Value of: CONT_WAVE + */ +#define NRF24L01P_NG_VAL_CONT_WAVE(reg) (((reg) & \ + NRF24L01P_NG_FLG_CONT_WAVE) >> 7) +/** + * @brief Flag: RF_DR_LOW + */ +#define NRF24L01P_NG_FLG_RF_DR_LOW (0x20) +/** + * @brief Value of: RF_DR_LOW + */ +#define NRF24L01P_NG_VAL_RF_DR_LOW(reg) (((reg) & \ + NRF24L01P_NG_FLG_RF_DR_LOW) >> 5) +/** + * @brief Flag: PLL_LOCK + */ +#define NRF24L01P_NG_FLG_PLL_LOCK (0x10) +/** + * @brief Value of: PLL_LOCK + */ +#define NRF24L01P_NG_VAL_PLL_LOCK(reg) (((reg) & \ + NRF24L01P_NG_FLG_PLL_LOCK) >> 4) +/** + * @brief Flag: RF_DR_HIGH + */ +#define NRF24L01P_NG_FLG_RF_DR_HIGH (0x08) +/** + * @brief Value of: RF_DR_HIGH + */ +#define NRF24L01P_NG_VAL_RF_DR_HIGH(reg) (((reg) & \ + NRF24L01P_NG_FLG_RF_DR_HIGH) >> 3) +/** + * @brief Flag: RF_DR - 250 kbit/s + */ +#define NRF24L01P_NG_FLG_RF_DR_250_KBPS (0x08) +/** + * @brief Flag: RF_DR - 1000 kbit/s + */ +#define NRF24L01P_NG_FLG_RF_DR_1_MBPS (0x00) +/** + * @brief Flag: RF_DR - 2000 kbit/s + */ +#define NRF24L01P_NG_FLG_RF_DR_2_MBPS (0x20) +/** + * @brief Mask to configure RF_DR + */ +#define NRF24L01P_NG_MSK_RF_DR (0x28) +/** + * @brief FLG RF_DR + */ +#define NRF24L01P_NG_FLG_RF_DR(val) ((((val) & 1) << 5) | \ + (((val) & 2) << 2)) +/** + * @brief Value of: RF_DR + */ +#define NRF24L01P_NG_VAL_RF_DR(reg) ((((reg) & \ + NRF24L01P_NG_FLG_RF_DR_LOW) >> 5) \ + | \ + (((reg) & \ + NRF24L01P_NG_FLG_RF_DR_HIGH) >> 2)) +/** + * @brief Flag: RF_PWR - -18 dbm + */ +#define NRF24L01P_NG_FLG_RF_PWR_MINUS_18 (0x00) +/** + * @brief Flag: RF_PWR - -12 dbm + */ +#define NRF24L01P_NG_FLG_RF_PWR_MINUS_12 (0x02) +/** + * @brief Flag: RF_PWR - -6 dbm + */ +#define NRF24L01P_NG_FLG_RF_PWR_MINUS_6 (0x04) +/** + * @brief Flag: RF_PWR - 0 dbm + */ +#define NRF24L01P_NG_FLG_RF_PWR_0 (0x06) +/** + * @brief Mask to configure RF_PWR + */ +#define NRF24L01P_NG_MSK_RF_PWR (0x06) +/** + * @brief Flag RF_PWR from value [0; 3] + */ +#define NRF24L01P_NG_FLG_RF_PWR(val) (((val) << 1) & \ + NRF24L01P_NG_MSK_RF_PWR) +/** + * @brief Value of: RF_PWR + */ +#define NRF24L01P_NG_VAL_RF_PWR(reg) (((reg) & \ + NRF24L01P_NG_MSK_RF_PWR) >> 1) +/** @} */ + +/** + * @name NRF24L01+ STATUS register + * + * Address and layout of NRF24L01+ STATUS register + * @{ + */ +/** + * @brief STATUS register address + */ +#define NRF24L01P_NG_REG_STATUS (0x07) +/** + * @brief Flag: RX_DR + */ +#define NRF24L01P_NG_FLG_RX_DR (0x40) +/** + * @brief Value of: RX_DR + */ +#define NRF24L01P_NG_VAL_RX_DR(reg) (((reg) & \ + NRF24L01P_NG_FLG_RX_DR) >> 6) +/** + * @brief Flag: TX_DS + */ +#define NRF24L01P_NG_FLG_TX_DS (0x20) +/** + * @brief Value of: TX_DS + */ +#define NRF24L01P_NG_VAL_TX_DS(reg) (((reg) & \ + NRF24L01P_NG_FLG_TX_DS) >> 5) +/** + * @brief Flag: MAX_RT + */ +#define NRF24L01P_NG_FLG_MAX_RT (0x10) +/** + * @brief Value of: MAX_RT + */ +#define NRF24L01P_NG_VAL_MAX_RT(reg) (((reg) & \ + NRF24L01P_NG_FLG_MAX_RT) >> 4) + +/** + * @brief Flag: RX_P_NO - Rx FIFO empty + */ +#define NRF24L01P_NG_FLG_RX_P_NO_NONE (0x0e) +/** + * @brief Mask to read RX_P_NO + */ +#define NRF24L01P_NG_MSK_RX_P_NO (0x0e) +/** + * @brief Flag RX_P_NO from value [0; 7] + */ +#define NRF24L01P_NG_FLG_RX_P_NO(val) (((val) << 1) & \ + NRF24L01P_NG_MSK_RX_P_NO) +/** + * @brief Value of: RX_P_NO + */ +#define NRF24L01P_NG_VAL_RX_P_NO(reg) (((reg) & \ + NRF24L01P_NG_MSK_RX_P_NO) >> 1) + +/** + * @brief Flag: TX_FULL + */ +#define NRF24L01P_NG_FLG_TX_FULL (0x01) +/** + * @brief Value of: TX_FULL + */ +#define NRF24L01P_NG_VAL_TX_FULL(reg) ((reg) & NRF24L01P_NG_FLG_TX_FULL) +/** @} */ + +/** + * @name NRF24L01+ OBSERVE_TX register + * + * Address and layout of NRF24L01+ STATUS register + * @{ + */ +/** + * @brief OBSERVE_TX register address + */ +#define NRF24L01P_NG_REG_OBSERVE_TX (0x08) +/** + * @brief Mask to read PLOS_CNT + */ +#define NRF24L01P_NG_MSK_PLOS_CNT (0xf0) +/** + * @brief Flag PLOS_CNT from value [0; 15] + */ +#define NRF24L01P_NG_FLG_PLOS_CNT(val) (((val) << 4) & \ + NRF24L01P_NG_MSK_PLOS_CNT) +/** + * @brief Value of: PLOS_CNT + */ +#define NRF24L01P_NG_VAL_PLOS_CNT(reg) (((reg) & \ + NRF24L01P_NG_MSK_PLOS_CNT) >> 4) + +/** + * @brief Mask to read ARC_CNT + */ +#define NRF24L01P_NG_MSK_ARC_CNT (0x0f) +/** + * @brief Flag ARC_CNT from value [0; 15] + */ +#define NRF24L01P_NG_FLG_ARC_CNT(val) ((val) & NRF24L01P_NG_MSK_ARC_CNT) +/** + * @brief Value of: ARC_CNT + */ +#define NRF24L01P_NG_VAL_ARC_CNT(reg) ((reg) & NRF24L01P_NG_MSK_ARC_CNT) +/** @} */ + +/** + * @name NRF24L01+ RPD register + * + * Address and layout of NRF24L01+ RPD register + * @{ + */ +/** + * @brief RPD register address + */ +#define NRF24L01P_NG_REG_RPD (0x09) +/** + * @brief Flag: RPD + */ +#define NRF24L01P_NG_FLG_RPD (0x01) +/** + * @brief Value of: RPD + */ +#define NRF24L01P_NG_VAL_RPD(reg) ((reg) & NRF24L01P_NG_FLG_RPD) +/** @} */ + +/** + * @name NRF24L01+ RX_ADDR_Px registers + * + * Addresses of NRF24L01+ RX_ADDR_Px registers and TX_ADDR register + * @{ + */ +/** + * @brief RX_ADDR_P0 register address + */ +#define NRF24L01P_NG_REG_RX_ADDR_P0 (0x0A) +/** + * @brief RX_ADDR_P1 register address + */ +#define NRF24L01P_NG_REG_RX_ADDR_P1 (0x0B) +/** + * @brief RX_ADDR_P2 register address + */ +#define NRF24L01P_NG_REG_RX_ADDR_P2 (0x0C) +/** + * @brief RX_ADDR_P3 register address + */ +#define NRF24L01P_NG_REG_RX_ADDR_P3 (0x0D) +/** + * @brief RX_ADDR_P4 register address + */ +#define NRF24L01P_NG_REG_RX_ADDR_P4 (0x0E) +/** + * @brief RX_ADDR_P5 register address + */ +#define NRF24L01P_NG_REG_RX_ADDR_P5 (0x0F) +/** + * @brief TX_ADDR register address + */ +#define NRF24L01P_NG_REG_TX_ADDR (0x10) +/** @} */ + +/** + * @name NRF24L01+ RX_PW_Px registers + * + * Address and layout of NRF24L01+ RX_PW_Px registers + * @{ + */ +/** + * @brief RX_PW_P0 register address + */ +#define NRF24L01P_NG_REG_RX_PW_P0 (0x11) +/** + * @brief RX_PW_P1 register address + */ +#define NRF24L01P_NG_REG_RX_PW_P1 (0x12) +/** + * @brief RX_PW_P2 register address + */ +#define NRF24L01P_NG_REG_RX_PW_P2 (0x13) +/** + * @brief RX_PW_P3 register address + */ +#define NRF24L01P_NG_REG_RX_PW_P3 (0x14) +/** + * @brief RX_PW_P4 register address + */ +#define NRF24L01P_NG_REG_RX_PW_P4 (0x15) +/** + * @brief RX_PW_P5 register address + */ +#define NRF24L01P_NG_REG_RX_PW_P5 (0x16) +/** + * @brief Mask to configure RX_PW_PX + */ +#define NRF24L01P_NG_MSK_RX_PW_PX (0x1f) +/** + * @brief Flag RX_PW_Px from value [0; 32] + */ +#define NRF24L01P_NG_FLG_RX_PW_PX(val) ((val) & NRF24L01P_NG_MSK_RX_PW_PX) +/** + * @brief Value of: RX_PW_PX + */ +#define NRF24L01P_NG_VAL_RX_PW_PX(reg) ((reg) & NRF24L01P_NG_MSK_RX_PW_PX) +/** @} */ + +/** + * @name NRF24L01+ FIFO_STATUS register + * + * Address and layout of NRF24L01+ FIFO_STATUS register + * @{ + */ +/** + * @brief FIFO_STATUS register address + */ +#define NRF24L01P_NG_REG_FIFO_STATUS (0x17) +/** + * @brief Flag: TX_REUSE + */ +#define NRF24L01P_NG_FLG_TX_REUSE (0x40) +/** + * @brief Value of: TX_REUSE + */ +#define NRF24L01P_NG_VAL_TX_REUSE(reg) (((reg) & \ + NRF24L01P_NG_FLG_TX_REUSE) >> 6) +/** + * @brief Flag: TX_FULL + * (also indicated in the STATUS register) + */ +#define NRF24L01P_NG_FLG_TX_FULL_ (0x20) +/** + * @brief Value of: TX_FULL + */ +#define NRF24L01P_NG_VAL_TX_FULL_(reg) (((reg) & \ + NRF24L01P_NG_FLG_TX_FULL_) >> 5) +/** + * @brief Flag: TX_EMPTY + */ +#define NRF24L01P_NG_FLG_TX_EMPTY (0x10) +/** + * @brief Value of: TX_EMPTY + */ +#define NRF24L01P_NG_VAL_TX_EMPTY(reg) (((reg) & \ + NRF24L01P_NG_FLG_TX_EMPTY) >> 4) +/** + * @brief Flag: RX_FULL + */ +#define NRF24L01P_NG_FLG_RX_FULL (0x02) +/** + * @brief Value of: RX_FULL + */ +#define NRF24L01P_NG_VAL_RX_FULL(reg) (((reg) & \ + NRF24L01P_NG_FLG_RX_FULL) >> 1) +/** + * @brief Flag: RX_EMPTY + */ +#define NRF24L01P_NG_FLG_RX_EMPTY (0x01) +/** + * @brief Value of: RX_EMPTY + */ +#define NRF24L01P_NG_VAL_RX_EMPTY(reg) ((reg) & NRF24L01P_NG_FLG_RX_EMPTY) +/** @} */ + +/** + * @name NRF24L01+ DYNPD register + * + * Address and layout of NRF24L01+ DYNPD register + * @{ + */ +/** + * @brief DYPD register address + */ +#define NRF24L01P_NG_REG_DYNPD (0x1C) +/** + * @brief Flag: DPL_P5 + */ +#define NRF24L01P_NG_FLG_DPL_P5 (0x20) +/** + * @brief Value of: DPL_P5 + */ +#define NRF24L01P_NG_VAL_DPL_P5(reg) (((reg) & \ + NRF24L01P_NG_FLG_DPL_P5) >> 5) +/** + * @brief Flag: DPL_P4 + */ +#define NRF24L01P_NG_FLG_DPL_P4 (0x10) +/** + * @brief Value of DPL_P4 + */ +#define NRF24L01P_NG_VAL_DPL_P4(reg) (((reg) & \ + NRF24L01P_NG_FLG_DPL_P4) >> 4) +/** + * @brief Flag: DPL_P3 + */ +#define NRF24L01P_NG_FLG_DPL_P3 (0x08) +/** + * @brief Value of DPL_P3 + */ +#define NRF24L01P_NG_VAL_DPL_P3(reg) (((reg) & \ + NRF24L01P_NG_FLG_DPL_P3) >> 3) +/** + * @brief Flag: DPL_P2 + */ +#define NRF24L01P_NG_FLG_DPL_P2 (0x04) +/** + * @brief Value of DPL_P2 + */ +#define NRF24L01P_NG_VAL_DPL_P2(reg) (((reg) & \ + NRF24L01P_NG_FLG_DPL_P2) >> 2) +/** + * @brief Flag: DPL_P1 + */ +#define NRF24L01P_NG_FLG_DPL_P1 (0x02) +/** + * @brief Value of: DPL_P1 + */ +#define NRF24L01P_NG_VAL_DPL_P1(reg) (((reg) & \ + NRF24L01P_NG_FLG_DPL_P1) >> 1) +/** + * @brief Flag: DPL_P0 + */ +#define NRF24L01P_NG_FLG_DPL_P0 (0x01) +/** + * @brief Value of: DPL_P0 + */ +#define NRF24L01P_NG_VAL_DPL_P0(reg) ((reg) & NRF24L01P_NG_FLG_DPL_P0) +/** @} */ + +/** + * @name NRF24L01+ FEATURES register + * + * Address and layout of NRF24L01+ FEATURES register + * @{ + */ +/** + * @brief FEATURES register address + */ +#define NRF24L01P_NG_REG_FEATURES (0x1D) +/** + * @brief Flag: EN_DPL + */ +#define NRF24L01P_NG_FLG_EN_DPL (0x04) +/** + * @brief Value of: EN_DPL + */ +#define NRF24L01P_NG_VAL_EN_DPL(reg) (((reg) & \ + NRF24L01P_NG_FLG_EN_DPL) >> 2) +/** + * @brief Flag: EN_ACK_PAY + */ +#define NRF24L01P_NG_FLG_EN_ACK_PAY (0x02) +/** + * @brief Value of: EN_ACK_PAY + */ +#define NRF24L01P_NG_VAL_EN_ACK_PAY(reg) (((reg) & \ + NRF24L01P_NG_FLG_EN_ACK_PAY) >> 1) +/** + * @brief Flag: EN_DYN_ACK + */ +#define NRF24L01P_NG_FLG_EN_DYN_ACK (0x01) +/** + * @brief Value of: EN_DYN_ACK + */ +#define NRF24L01P_NG_VAL_EN_DYN_ACK(reg) ((reg) & \ + NRF24L01P_NG_FLG_EN_DYN_ACK) +/** @} */ + +/** + * @brief Read the contents of an 8 bit register + * + * @param[in] dev NRF24L01+ device handle + * @param[in] reg_addr Address of the register to be written + * + * @return Register value + */ +static inline +uint8_t nrf24l01p_ng_reg8_read(const nrf24l01p_ng_t *dev, uint8_t reg_addr) { + uint8_t reg_val; + nrf24l01p_ng_read_reg(dev, reg_addr, ®_val, 1); + return reg_val; +} + +/** + * @brief Write the contents of an 8 bit register + * + * @param[in] dev NRF24L01+ device handle + * @param[in] reg_addr Address of the register to be written + * @param[in] reg_val Value to be written to the register + * + * @return Status register value + */ +static inline +uint8_t nrf24l01p_ng_reg8_write(const nrf24l01p_ng_t *dev, + uint8_t reg_addr, uint8_t reg_val) { + return nrf24l01p_ng_write_reg(dev, reg_addr, ®_val, 1); +} + +/** + * @brief Set bits in a certain 8-bit register + * + * @param[in] dev NRF24L01+ device handle + * @param[in] reg_addr Address of the register to be modified + * @param[in, out] reg_val Register bits that are being set + * + * @return Status register value + */ +static inline +uint8_t nrf24l01p_ng_reg8_set(const nrf24l01p_ng_t *dev, + uint8_t reg_addr, uint8_t *reg_val) { + uint8_t reg_val_old; + nrf24l01p_ng_read_reg(dev, reg_addr, ®_val_old, sizeof(reg_val_old)); + *reg_val = reg_val_old | *reg_val; + return nrf24l01p_ng_write_reg(dev, reg_addr, reg_val, sizeof(*reg_val)); +} +/** + * @brief Clear bits in a certain 8-bit register + * + * @param[in] dev NRF24L01+ device handle + * @param[in] reg_addr Address of the register to be modified + * @param[in, out] reg_val Register bits that are being cleared + * + * @return Status register value + */ +static inline +uint8_t nrf24l01p_ng_reg8_clear(const nrf24l01p_ng_t *dev, + uint8_t reg_addr, uint8_t *reg_val) { + uint8_t reg_val_old; + nrf24l01p_ng_read_reg(dev, reg_addr, ®_val_old, sizeof(reg_val_old)); + *reg_val = reg_val_old &= ~(*reg_val); + return nrf24l01p_ng_write_reg(dev, reg_addr, reg_val, sizeof(*reg_val)); +} + +/** + * @brief Modify bits in a certain 8-bit register + * + * @param[in] dev NRF24L01+ device handle + * @param[in] reg_addr Address of the register to be modified + * @param[in] mask Mask of bits to be modified + * @param[in, out] reg_val Register bits that are being modified + * + * @return Status register value + */ +static inline +uint8_t nrf24l01p_ng_reg8_mod(const nrf24l01p_ng_t *dev, uint8_t reg_addr, + uint8_t mask, uint8_t *reg_val) { + uint8_t reg_val_old; + nrf24l01p_ng_read_reg(dev, reg_addr, ®_val_old, sizeof(reg_val_old)); + reg_val_old &= ~mask; + *reg_val = reg_val_old | *reg_val; + return nrf24l01p_ng_write_reg(dev, reg_addr, reg_val, sizeof(*reg_val)); +} + +#ifdef __cplusplus +} +#endif + +#endif /* NRF24L01P_NG_REGISTERS_H */ +/** @} */ diff --git a/drivers/nrf24l01p_ng/include/nrf24l01p_ng_states.h b/drivers/nrf24l01p_ng/include/nrf24l01p_ng_states.h new file mode 100644 index 000000000000..e3f819dc8eae --- /dev/null +++ b/drivers/nrf24l01p_ng/include/nrf24l01p_ng_states.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2019 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +/** + * @ingroup drivers_nrf24l01p_ng + * @{ + * + * @file + * @brief Operation states of NRF24L01+ (NG) devices + * + * @author Fabian Hüßler + */ +#ifndef NRF24L01P_NG_STATES_H +#define NRF24L01P_NG_STATES_H + +#include "nrf24l01p_ng.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Wakeup delay + */ +#define NRF24L01P_NG_DELAY_US_START_UP (1500) + +/** + * @brief Transition delay to RX mode + */ +#define NRF24L01P_NG_DELAY_US_RX_SETTLING (130) + +/** + * @brief Transition delay to TX mode + */ +#define NRF24L01P_NG_DELAY_US_TX_SETTLING (130) + +/** + * @brief CE gpio pin pull-low to trigger a transmission + */ +#define NRF24L01P_NG_DELAY_US_CE_HIGH_PULSE (10) + +/** + * @brief Put device to sleep + * + * @param[in] dev NRF24L01+ device handle + */ +void nrf24l01p_ng_transition_to_power_down(nrf24l01p_ng_t *dev); + +/** + * @brief Go to idle state, wake up device + * + * @param[in] dev NRF24L01+ device handle + */ +void nrf24l01p_ng_transition_to_standby_1(nrf24l01p_ng_t *dev); + +/** + * @brief Go to "be ready to transmit" state + * + * @param[in] dev NRF24L01+ device handle + */ +void nrf24l01p_ng_transition_to_standby_2(nrf24l01p_ng_t *dev); + +/** + * @brief Go to Rx mode + * + * @param[in] dev NRF24L01+ device handle + */ +void nrf24l01p_ng_transition_to_rx_mode(nrf24l01p_ng_t *dev); + +/** + * @brief Go to Tx mode + * + * @param[in] dev NRF24L01+ device handle + */ +void nrf24l01p_ng_transition_to_tx_mode(nrf24l01p_ng_t *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* NRF24L01P_NG_STATES_H */ +/** @} */ diff --git a/drivers/nrf24l01p_ng/include/nrf24l01p_ng_types.h b/drivers/nrf24l01p_ng/include/nrf24l01p_ng_types.h new file mode 100644 index 000000000000..d21fb55668fb --- /dev/null +++ b/drivers/nrf24l01p_ng/include/nrf24l01p_ng_types.h @@ -0,0 +1,363 @@ +/* + * Copyright (C) 2019 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +/** + * @ingroup drivers_nrf24l01p + * @{ + * + * @file + * @brief Definitions of user visible types for the NRF24L01+ (NG) + * device driver + * + * @author Fabian Hüßler + */ +#ifndef NRF24L01P_NG_TYPES_H +#define NRF24L01P_NG_TYPES_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Flag that indicates that a state transition to + * POWER_DOWN can be done + */ +#define NRF24L01P_NG_TRANSITION_TO_POWER_DOWN (1 << 3) +/** + * @brief Flag that indicates that a state transition to + * STANDBY_1 can be done + */ +#define NRF24L01P_NG_TRANSITION_TO_STANDBY_1 (1 << 4) +/** + * @brief Flag that indicates that a state transition to + * STANDBY_2 can be done + */ +#define NRF24L01P_NG_TRANSITION_TO_STANDBY_2 (1 << 5) +/** + * @brief Flag that indicates that a state transition to + * RX_MODE can be done + */ +#define NRF24L01P_NG_TRANSITION_TO_RX_MODE (1 << 6) +/** + * @brief Flag that indicates that a state transition to + * TX_MODE can be done + */ +#define NRF24L01P_NG_TRANSITION_TO_TX_MODE (1 << 7) + +/** + * @brief NRF24L01+ operation states + */ +typedef enum nrf24l01p_ng_state { + /** @brief State right after voltage supply */ + NRF24L01P_NG_STATE_UNDEFINED = (0 | + (NRF24L01P_NG_TRANSITION_TO_POWER_DOWN)), + /** @brief Register values are available and maintained, SPI active */ + NRF24L01P_NG_STATE_POWER_DOWN = (1 | + (NRF24L01P_NG_TRANSITION_TO_STANDBY_1)), + /** @brief Idle */ + NRF24L01P_NG_STATE_STANDBY_1 = (2 | + (NRF24L01P_NG_TRANSITION_TO_POWER_DOWN | + NRF24L01P_NG_TRANSITION_TO_STANDBY_2 | + NRF24L01P_NG_TRANSITION_TO_RX_MODE | + NRF24L01P_NG_TRANSITION_TO_TX_MODE)), + /** @brief TX FIFO empty, fill up TX FIFO again */ + NRF24L01P_NG_STATE_STANDBY_2 = (3 | + (NRF24L01P_NG_TRANSITION_TO_POWER_DOWN | + NRF24L01P_NG_TRANSITION_TO_TX_MODE)), + /** @brief Constantly search for a valid packet */ + NRF24L01P_NG_STATE_RX_MODE = (4 | + (NRF24L01P_NG_TRANSITION_TO_POWER_DOWN | + NRF24L01P_NG_TRANSITION_TO_STANDBY_1)), + /** @brief Transmit next packet */ + NRF24L01P_NG_STATE_TX_MODE = (5 | + (NRF24L01P_NG_TRANSITION_TO_POWER_DOWN | + NRF24L01P_NG_TRANSITION_TO_STANDBY_1 | + NRF24L01P_NG_TRANSITION_TO_STANDBY_2)), +} nrf24l01p_ng_state_t; + +/** + * @brief Enumeration of NRF24L01+ data pipes + */ +typedef enum nrf24l01p_ng_pipe { + NRF24L01P_NG_P0 = 0, /**< Pipe 0 */ + NRF24L01P_NG_P1 = 1, /**< Pipe 1 */ + NRF24L01P_NG_P2 = 2, /**< Pipe 2 */ + NRF24L01P_NG_P3 = 3, /**< Pipe 3 */ + NRF24L01P_NG_P4 = 4, /**< Pipe 4 */ + NRF24L01P_NG_P5 = 5, /**< Pipe 5 */ + NRF24L01P_NG_PX_NUM_OF /**< Number of supported pipes */ +} nrf24l01p_ng_pipe_t; + +/** + * @brief Possible values to configure the layer-2 address width + */ +typedef enum nrf24l01p_ng_aw { + NRF24L01P_NG_AW_3BYTE = 1, /**< Use a 3 bytes long layer-2 address */ + NRF24L01P_NG_AW_4BYTE = 2, /**< Use a 4 bytes long layer-2 address */ + NRF24L01P_NG_AW_5BYTE = 3, /**< Use a 5 bytes long layer-2 address */ + NRF24L01P_NG_AW_NUM_OF /**< Number of possible values to configure + the layer-2 address width */ +} nrf24l01p_ng_aw_t; + +/** + * @brief Possible values to configure the retransmission delay in ESB + */ +typedef enum nrf24l01p_ng_ard { + NRF24L01P_NG_ARD_250US = 0, /**< 250 us */ + NRF24L01P_NG_ARD_500US = 1, /**< 500 us */ + NRF24L01P_NG_ARD_750US = 2, /**< 750 us */ + NRF24L01P_NG_ARD_1000US = 3, /**< 1000 us */ + NRF24L01P_NG_ARD_1250US = 4, /**< 1250 us */ + NRF24L01P_NG_ARD_1500US = 5, /**< 1500 us */ + NRF24L01P_NG_ARD_1750US = 6, /**< 1750 us */ + NRF24L01P_NG_ARD_2000US = 7, /**< 2000 us */ + NRF24L01P_NG_ARD_2250US = 8, /**< 2250 us */ + NRF24L01P_NG_ARD_2500US = 9, /**< 2500 us */ + NRF24L01P_NG_ARD_2750US = 10, /**< 2750 us */ + NRF24L01P_NG_ARD_3000US = 11, /**< 3000 us */ + NRF24L01P_NG_ARD_3250US = 12, /**< 3250 us */ + NRF24L01P_NG_ARD_3500US = 13, /**< 3500 us */ + NRF24L01P_NG_ARD_3750US = 14, /**< 3750 us */ + NRF24L01P_NG_ARD_4000US = 15, /**< 4000 us */ + NRF24L01P_NG_ARD_NUM_OF /**< Number of possible values to configure + the retransmission delay */ +} nrf24l01p_ng_ard_t; + +/** + * @brief Possible values to configure the CRC length + */ +typedef enum nrf24l01p_ng_crc { + NRF24L01P_NG_CRC_0BYTE = 1, /**< 0 bytes CRC length */ + NRF24L01P_NG_CRC_1BYTE = 2, /**< 1 byte CRC length */ + NRF24L01P_NG_CRC_2BYTE = 3, /**< 2 bytes CRC length */ +} nrf24l01p_ng_crc_t; + +/** + * @brief Possible values to configure the radio power + */ +typedef enum nrf24l01p_ng_tx_power { + NRF24L01P_NG_TX_POWER_MINUS_18DBM = 0, /**< -18 dBm */ + NRF24L01P_NG_TX_POWER_MINUS_12DBM = 1, /**< -12 dBm */ + NRF24L01P_NG_TX_POWER_MINUS_6DBM = 2, /**< -6 dBm */ + NRF24L01P_NG_TX_POWER_0DBM = 3, /**< 0 dBm */ + NRF24L01P_NG_TX_POWER_NUM_OF /**< Number of possible values + to configure the radio power */ +} nrf24l01p_ng_tx_power_t; + +/** + * @brief Possible values to configure the data rate + */ +typedef enum nrf24l01p_ng_rfdr { + NRF24L01P_NG_RF_DR_1MBPS = 0, /**< 1 Mbit/s */ + NRF24L01P_NG_RF_DR_250KBPS = 1, /**< 250 kbit/s */ + NRF24L01P_NG_RF_DR_2MBPS = 2, /**< 2 Mbit/s */ + NRF24L01P_NG_RF_DR_NUM_OF /**< Number of possible values to configure + the data rate */ +} nrf24l01p_ng_rfdr_t; + +struct nrf24l01p_ng; +typedef struct nrf24l01p_ng nrf24l01p_ng_t; /**< typedef of + forward declaration */ + +/** + * @brief Convert @ref nrf24l01p_ng_aw_t to actual address width + * + * @param[in] address_width Address width enum + * + * @return Address width in [bytes] + */ +static inline +uint8_t nrf24l01p_ng_etoval_aw(nrf24l01p_ng_aw_t address_width) +{ + if (address_width <= NRF24L01P_NG_AW_3BYTE) { + return 3; + } + if (address_width == NRF24L01P_NG_AW_4BYTE) { + return 4; + } + return 5; +} + +/** + * @brief Convert address width in [bytes] to @ref nrf24l01p_ng_aw_t + * + * @param[in] address_width Address width in [bytes] + * + * @return Corresponding enum + */ +static inline +nrf24l01p_ng_aw_t nrf24l01p_ng_valtoe_aw(uint8_t address_width) +{ + if (address_width <= 3) { + return NRF24L01P_NG_AW_3BYTE; + } + if (address_width == 4) { + return NRF24L01P_NG_AW_4BYTE; + } + return NRF24L01P_NG_AW_5BYTE; +} + +/** + * @brief Convert @ref nrf24l01p_ng_ard_t to actual retransmission delay + * + * @param[in] retr_delay Retransmission delay enum + * + * @return Retransmission delay in [us] + */ +static inline +uint16_t nrf24l01p_ng_etoval_ard(nrf24l01p_ng_ard_t retr_delay) +{ + if (retr_delay >= NRF24L01P_NG_ARD_4000US) { + return 4000; + } + return (retr_delay + 1) * 250; +} + +/** + * @brief Convert retransmission delay in [us] to @ref nrf24l01p_ng_ard_t + * + * @param[in] retr_delay Retransmission delay in [us] + * + * @return Corresponding enum + */ +static inline +nrf24l01p_ng_ard_t nrf24l01p_ng_valtoe_ard(uint16_t retr_delay) +{ + if (retr_delay >= 4000) { + return NRF24L01P_NG_ARD_4000US; + } + return (nrf24l01p_ng_ard_t)(retr_delay / 250); +} + +/** + * @brief Convert @ref nrf24l01p_ng_crc_t to actual CRC length + * + * @param[in] crc_len CRC length enum + * + * @return CRC length in [bytes] + */ +static inline +uint8_t nrf24l01p_ng_etoval_crc(nrf24l01p_ng_crc_t crc_len) +{ + if (crc_len <= NRF24L01P_NG_CRC_0BYTE) { + return 0; + } + if (crc_len == NRF24L01P_NG_CRC_1BYTE) { + return 1; + } + return 2; +} + +/** + * @brief Convert CRC length in [bytes] to @ref nrf24l01p_ng_crc_t + * + * @param[in] crc_len CRC length in [bytes] + * + * @return Corresponding enum + */ +static inline +nrf24l01p_ng_crc_t nrf24l01p_ng_valtoe_crc(uint8_t crc_len) +{ + if (!crc_len) { + return NRF24L01P_NG_CRC_0BYTE; + } + if (crc_len == 1) { + return NRF24L01P_NG_CRC_1BYTE; + } + return NRF24L01P_NG_CRC_2BYTE; +} + +/** + * @brief Convert @ref nrf24l01p_ng_tx_power_t to actual Tx power + * + * @param[in] power RF power enum + * + * @return RF power in [dbm] + */ +static inline +int8_t nrf24l01p_ng_etoval_tx_power(nrf24l01p_ng_tx_power_t power) +{ + if (power == NRF24L01P_NG_TX_POWER_MINUS_18DBM) { + return -18; + } + if (power == NRF24L01P_NG_TX_POWER_MINUS_12DBM) { + return -12; + } + if (power == NRF24L01P_NG_TX_POWER_MINUS_6DBM) { + return -6; + } + return 0; +} + +/** + * @brief Convert RF power in [dbm] to @ref nrf24l01p_ng_tx_power_t + * + * @param[in] power RF power in [dbm] + * + * @return Corresponding enum + */ +static inline +nrf24l01p_ng_tx_power_t nrf24l01p_ng_valtoe_tx_power(int16_t power) +{ + if (power <= -18) { + return NRF24L01P_NG_TX_POWER_MINUS_18DBM; + } + if (power <= -12) { + return NRF24L01P_NG_TX_POWER_MINUS_12DBM; + } + if (power <= -6) { + return NRF24L01P_NG_TX_POWER_MINUS_6DBM; + } + return NRF24L01P_NG_TX_POWER_0DBM; +} + +/** + * @brief Convert @ref nrf24l01p_ng_rfdr_t to actual air data rate + * + * @param[in] data_rate Air data rate enum + * + * @return Air data rate in [kbit/s] + */ +static inline +uint16_t nrf24l01p_ng_etoval_rfdr(nrf24l01p_ng_rfdr_t data_rate) +{ + if (data_rate == NRF24L01P_NG_RF_DR_1MBPS) { + return 1000; + } + if (data_rate == NRF24L01P_NG_RF_DR_250KBPS) { + return 250; + } + return 2000; +} + +/** + * @brief Convert Air data rate in [kbit/s] to @ref nrf24l01p_ng_rfdr_t + * + * @param[in] data_rate Air data rate in [kbit/s] + * + * @return Corresponding enum + */ +static inline +nrf24l01p_ng_rfdr_t nrf24l01p_ng_valtoe_rfdr(uint16_t data_rate) +{ + if (data_rate <= 250) { + return NRF24L01P_NG_RF_DR_250KBPS; + } + if (data_rate <= 1000) { + return NRF24L01P_NG_RF_DR_1MBPS; + } + return NRF24L01P_NG_RF_DR_2MBPS; +} + +#ifdef __cplusplus +} +#endif + +#endif /* NRF24L01P_NG_TYPES_H */ +/** @} */ diff --git a/drivers/nrf24l01p_ng/nrf24l01p_ng.c b/drivers/nrf24l01p_ng/nrf24l01p_ng.c new file mode 100644 index 000000000000..13b0e2faea0e --- /dev/null +++ b/drivers/nrf24l01p_ng/nrf24l01p_ng.c @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2019 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +/** + * @ingroup drivers_nrf24l01p_ng + * @{ + * + * @file + * @brief Implementation of the public NRF24L01+ (NG) device interface + * + * @author Fabian Hüßler + * @} + */ + +#include +#include + +#define ENABLE_DEBUG (0) +#include "debug.h" +#include "kernel_defines.h" + +#include "nrf24l01p_ng_netdev.h" +#include "nrf24l01p_ng_constants.h" +#include "nrf24l01p_ng_communication.h" +#include "nrf24l01p_ng_registers.h" +#include "nrf24l01p_ng_states.h" +#if IS_USED(MODULE_NRF24L01P_NG_DIAGNOSTICS) +#include "nrf24l01p_ng_diagnostics.h" +#endif + +#define NRF24L01P_NG_REG_RX_ADDR_PX(x) (NRF24L01P_NG_REG_RX_ADDR_P0 + (x)) +#define NRF24L01P_NG_REG_RX_PW_PX(x) (NRF24L01P_NG_REG_RX_PW_P0 + (x)) + +int nrf24l01p_ng_setup(nrf24l01p_ng_t *dev, + const nrf24l01p_ng_params_t *params, + uint8_t index) +{ + assert(dev); + assert(params); + memset((char *)dev + sizeof(netdev_t), 0x00, + sizeof(nrf24l01p_ng_t) - sizeof(netdev_t)); + dev->state = NRF24L01P_NG_STATE_UNDEFINED; + dev->idle_state = NRF24L01P_NG_STATE_RX_MODE; + dev->params = *params; + dev->netdev.driver = &nrf24l01p_ng_driver; + netdev_register(&dev->netdev, NETDEV_NRF24L01P_NG, index); + return 0; +} + +int nrf24l01p_ng_set_enable_pipe(nrf24l01p_ng_t *dev, nrf24l01p_ng_pipe_t pipe, + bool enable) +{ + assert(dev); + if (pipe >= NRF24L01P_NG_PX_NUM_OF) { + return -EINVAL; + } + uint8_t en_aa = (1 << pipe); + uint8_t dynpd = (1 << pipe); + uint8_t en_rx_addr = (1 << pipe); + nrf24l01p_ng_acquire(dev); + if (enable) { + nrf24l01p_ng_reg8_set(dev, NRF24L01P_NG_REG_EN_AA, &en_aa); + nrf24l01p_ng_reg8_set(dev, NRF24L01P_NG_REG_DYNPD, &dynpd); + nrf24l01p_ng_reg8_set(dev, NRF24L01P_NG_REG_EN_RXADDR, &en_rx_addr); + } + else { + nrf24l01p_ng_reg8_clear(dev, NRF24L01P_NG_REG_EN_AA, &en_aa); + nrf24l01p_ng_reg8_clear(dev, NRF24L01P_NG_REG_DYNPD, &dynpd); + nrf24l01p_ng_reg8_clear(dev, NRF24L01P_NG_REG_EN_RXADDR, &en_rx_addr); + } + nrf24l01p_ng_release(dev); + return 0; +} + +int nrf24l01p_ng_get_enable_pipe(nrf24l01p_ng_t *dev, nrf24l01p_ng_pipe_t pipe, + bool* enable) +{ + assert(dev); + if (pipe >= NRF24L01P_NG_PX_NUM_OF) { + return -EINVAL; + } + uint8_t en_rx_addr; + nrf24l01p_ng_acquire(dev); + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_EN_RXADDR, &en_rx_addr, 1); + nrf24l01p_ng_release(dev); + *enable = !!(en_rx_addr & (1 << pipe)); + return 0; +} + +int nrf24l01p_ng_set_air_data_rate(nrf24l01p_ng_t *dev, + nrf24l01p_ng_rfdr_t data_rate) +{ + assert(dev); + if (data_rate >= NRF24L01P_NG_RF_DR_NUM_OF) { + return -EINVAL; + } + switch (dev->state) { + case NRF24L01P_NG_STATE_POWER_DOWN: + case NRF24L01P_NG_STATE_STANDBY_1: + case NRF24L01P_NG_STATE_RX_MODE: + break; + default: + return -EAGAIN; + } + uint8_t rf_setup = NRF24L01P_NG_FLG_RF_DR(data_rate); + nrf24l01p_ng_acquire(dev); + nrf24l01p_ng_reg8_mod(dev, NRF24L01P_NG_REG_RF_SETUP, + NRF24L01P_NG_MSK_RF_DR, &rf_setup); + nrf24l01p_ng_release(dev); + dev->params.config.cfg_data_rate = data_rate; + return 0; +} + +uint16_t nrf24l01p_ng_get_air_data_rate(const nrf24l01p_ng_t *dev, + nrf24l01p_ng_rfdr_t *data_rate) +{ + assert(dev); + if (data_rate) { + *data_rate = dev->params.config.cfg_data_rate; + } + return nrf24l01p_ng_etoval_rfdr(dev->params.config.cfg_data_rate); +} + +int nrf24l01p_ng_set_crc(nrf24l01p_ng_t *dev, nrf24l01p_ng_crc_t crc) +{ + assert(dev); + if (crc > NRF24L01P_NG_CRC_2BYTE) { + return -EINVAL; + } + if (crc == NRF24L01P_NG_CRC_0BYTE) { + return -ENOTSUP; + } + switch (dev->state) { + case NRF24L01P_NG_STATE_POWER_DOWN: + case NRF24L01P_NG_STATE_STANDBY_1: + case NRF24L01P_NG_STATE_RX_MODE: + break; + default: + return -EAGAIN; + } + uint8_t config = NRF24L01P_NG_FLG_CRCO(crc); + nrf24l01p_ng_acquire(dev); + nrf24l01p_ng_reg8_mod(dev, NRF24L01P_NG_REG_CONFIG, NRF24L01P_NG_MSK_CRC, + &config); + nrf24l01p_ng_release(dev); + dev->params.config.cfg_crc = crc; + return 0; +} + +uint8_t nrf24l01p_ng_get_crc(const nrf24l01p_ng_t *dev, + nrf24l01p_ng_crc_t *crc) +{ + assert(dev); + if (crc) { + *crc = dev->params.config.cfg_crc; + } + return nrf24l01p_ng_etoval_crc(dev->params.config.cfg_crc); +} + +int nrf24l01p_ng_set_tx_power(nrf24l01p_ng_t *dev, + nrf24l01p_ng_tx_power_t power) +{ + assert(dev); + if (power >= NRF24L01P_NG_TX_POWER_NUM_OF) { + return -EINVAL; + } + switch (dev->state) { + case NRF24L01P_NG_STATE_POWER_DOWN: + case NRF24L01P_NG_STATE_STANDBY_1: + case NRF24L01P_NG_STATE_RX_MODE: + break; + default: + return -EAGAIN; + } + uint8_t rf_setup = NRF24L01P_NG_FLG_RF_PWR(power); + nrf24l01p_ng_acquire(dev); + nrf24l01p_ng_reg8_mod(dev, NRF24L01P_NG_REG_RF_SETUP, + NRF24L01P_NG_MSK_RF_PWR, &rf_setup); + nrf24l01p_ng_release(dev); + dev->params.config.cfg_tx_power = power; + return 0; +} + +int8_t nrf24l01p_ng_get_tx_power(const nrf24l01p_ng_t *dev, + nrf24l01p_ng_tx_power_t *power) +{ + assert(dev); + if (power) { + *power = dev->params.config.cfg_tx_power; + } + return nrf24l01p_ng_etoval_tx_power(dev->params.config.cfg_tx_power); +} + +int nrf24l01p_ng_set_channel(nrf24l01p_ng_t *dev, uint8_t channel) +{ + assert(dev); + if (channel >= NRF24L01P_NG_NUM_CHANNELS) { + return -EINVAL; + } + switch (dev->state) { + case NRF24L01P_NG_STATE_POWER_DOWN: + case NRF24L01P_NG_STATE_STANDBY_1: + case NRF24L01P_NG_STATE_RX_MODE: + break; + default: + return -EAGAIN; + } + uint8_t rf_ch = NRF24L01P_NG_FLG_RF_CH(channel); + nrf24l01p_ng_acquire(dev); + nrf24l01p_ng_reg8_mod(dev, NRF24L01P_NG_REG_RF_CH, + NRF24L01P_NG_MSK_RF_CH, &rf_ch); + nrf24l01p_ng_release(dev); + dev->params.config.cfg_channel = channel; + return 0; +} + +uint8_t nrf24l01p_ng_get_channel(const nrf24l01p_ng_t *dev) +{ + assert(dev); + return dev->params.config.cfg_channel; +} + +int nrf24l01p_ng_set_rx_address(nrf24l01p_ng_t *dev, const uint8_t *addr, + nrf24l01p_ng_pipe_t pipe) +{ + assert(dev); + assert(addr); + if (pipe >= NRF24L01P_NG_PX_NUM_OF) { + return -EINVAL; + } + switch (dev->state) { + case NRF24L01P_NG_STATE_POWER_DOWN: + case NRF24L01P_NG_STATE_STANDBY_1: + case NRF24L01P_NG_STATE_RX_MODE: + break; + default: + return -EAGAIN; + } + nrf24l01p_ng_acquire(dev); + if (pipe == NRF24L01P_NG_P0) { + nrf24l01p_ng_write_reg(dev, NRF24L01P_NG_REG_RX_ADDR_PX(pipe), + addr, NRF24L01P_NG_ADDR_WIDTH); + memcpy(NRF24L01P_NG_ADDR_P0(dev), addr, NRF24L01P_NG_ADDR_WIDTH); + } + else if (pipe == NRF24L01P_NG_P1) { + nrf24l01p_ng_write_reg(dev, NRF24L01P_NG_REG_RX_ADDR_PX(pipe), + addr, NRF24L01P_NG_ADDR_WIDTH); + memcpy(NRF24L01P_NG_ADDR_P1(dev), addr, NRF24L01P_NG_ADDR_WIDTH); + } + else { + nrf24l01p_ng_write_reg(dev, NRF24L01P_NG_REG_RX_ADDR_PX(pipe), + addr, 1); + NRF24L01P_NG_ADDR_PX_LSB(dev, pipe) = *addr; + } + nrf24l01p_ng_release(dev); + return 0; +} + +int nrf24l01p_ng_get_rx_address(const nrf24l01p_ng_t *dev, uint8_t *addr, + nrf24l01p_ng_pipe_t pipe) +{ + assert(dev); + assert(addr); + if (pipe >= NRF24L01P_NG_PX_NUM_OF) { + return -EINVAL; + } + if (pipe == NRF24L01P_NG_P0) { + memcpy(addr, NRF24L01P_NG_ADDR_P0(dev), NRF24L01P_NG_ADDR_WIDTH); + } + else { + memcpy(addr, NRF24L01P_NG_ADDR_P1(dev), NRF24L01P_NG_ADDR_WIDTH); + if (pipe > NRF24L01P_NG_P1) { + addr[NRF24L01P_NG_ADDR_WIDTH - 1] = + NRF24L01P_NG_ADDR_PX_LSB(dev, pipe); + } + } + return NRF24L01P_NG_ADDR_WIDTH; +} + +int nrf24l01p_ng_set_max_retransm(nrf24l01p_ng_t *dev, uint8_t max_rt) +{ + assert(dev); + if (max_rt > NRF24L01P_NG_MAX_RETRANSMISSIONS) { + return -EINVAL; + } + switch (dev->state) { + case NRF24L01P_NG_STATE_POWER_DOWN: + case NRF24L01P_NG_STATE_STANDBY_1: + case NRF24L01P_NG_STATE_RX_MODE: + break; + default: + return -EAGAIN; + } + uint8_t setup_retr = NRF24L01P_NG_FLG_ARC(max_rt); + nrf24l01p_ng_acquire(dev); + nrf24l01p_ng_reg8_mod(dev, NRF24L01P_NG_REG_SETUP_RETR, + NRF24L01P_NG_MSK_ARC, &setup_retr); + nrf24l01p_ng_release(dev); + dev->params.config.cfg_max_retr = max_rt; + return 0; +} + +uint8_t nrf24l01p_ng_get_max_retransm(const nrf24l01p_ng_t *dev) +{ + assert(dev); + return dev->params.config.cfg_max_retr; +} + +int nrf24l01p_ng_set_retransm_delay(nrf24l01p_ng_t *dev, + nrf24l01p_ng_ard_t rt_delay) +{ + assert(dev); + if (rt_delay >= NRF24L01P_NG_ARD_NUM_OF) { + return -EINVAL; + } + switch (dev->state) { + case NRF24L01P_NG_STATE_POWER_DOWN: + case NRF24L01P_NG_STATE_STANDBY_1: + case NRF24L01P_NG_STATE_RX_MODE: + break; + default: + return -EAGAIN; + } + uint8_t setup_retr = NRF24L01P_NG_FLG_ARD(rt_delay); + nrf24l01p_ng_acquire(dev); + nrf24l01p_ng_reg8_mod(dev, NRF24L01P_NG_REG_SETUP_RETR, + NRF24L01P_NG_MSK_ARD, &setup_retr); + nrf24l01p_ng_release(dev); + dev->params.config.cfg_retr_delay = rt_delay; + return 0; +} + +uint16_t nrf24l01p_ng_get_retransm_delay(const nrf24l01p_ng_t *dev, + nrf24l01p_ng_ard_t *rt_delay) +{ + assert(dev); + if (rt_delay) { + *rt_delay = dev->params.config.cfg_retr_delay; + } + return nrf24l01p_ng_etoval_ard(dev->params.config.cfg_retr_delay); +} + +int nrf24l01p_ng_set_state(nrf24l01p_ng_t *dev, nrf24l01p_ng_state_t state) +{ + switch (dev->state) { + case NRF24L01P_NG_STATE_POWER_DOWN: + case NRF24L01P_NG_STATE_STANDBY_1: + case NRF24L01P_NG_STATE_RX_MODE: + break; + default: + return -EAGAIN; + } + nrf24l01p_ng_state_t old = dev->state; + nrf24l01p_ng_acquire(dev); + if (state == NRF24L01P_NG_STATE_POWER_DOWN) { + if (dev->state != NRF24L01P_NG_STATE_POWER_DOWN) { + nrf24l01p_ng_transition_to_power_down(dev); + } + } + else if (state == NRF24L01P_NG_STATE_STANDBY_1) { + if (dev->state != NRF24L01P_NG_STATE_STANDBY_1) { + nrf24l01p_ng_transition_to_standby_1(dev); + } + } + else if (state == NRF24L01P_NG_STATE_RX_MODE) { + if (dev->state != NRF24L01P_NG_STATE_RX_MODE) { + if (state != NRF24L01P_NG_STATE_STANDBY_1) { + nrf24l01p_ng_transition_to_standby_1(dev); + } + nrf24l01p_ng_transition_to_rx_mode(dev); + } + } + else { + nrf24l01p_ng_release(dev); + return -ENOTSUP; + } + nrf24l01p_ng_release(dev); + return (int)old; +} + +nrf24l01p_ng_state_t nrf24l01p_ng_get_state(const nrf24l01p_ng_t *dev) +{ + assert(dev); + return dev->state; +} + +#if IS_USED(MODULE_NRF24L01P_NG_DIAGNOSTICS) +void nrf24l01p_ng_print_all_regs(nrf24l01p_ng_t *dev) +{ + nrf24l01p_ng_acquire(dev); + nrf24l01p_ng_diagnostics_print_all_regs(dev); + nrf24l01p_ng_release(dev); +} + +void nrf24l01p_ng_print_dev_info(const nrf24l01p_ng_t *dev) +{ + nrf24l01p_ng_diagnostics_print_dev_info(dev); +} +#endif diff --git a/drivers/nrf24l01p_ng/nrf24l01p_ng_communication.c b/drivers/nrf24l01p_ng/nrf24l01p_ng_communication.c new file mode 100644 index 000000000000..daefb8bd4d7b --- /dev/null +++ b/drivers/nrf24l01p_ng/nrf24l01p_ng_communication.c @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2019 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +/** + * @ingroup drivers_nrf24l01p_ng + * @{ + * + * @file + * @brief Functions to communicate with the NRF24L01+ (NG) transceiver + * via SPI + * + * @author Fabian Hüßler + * @} + */ +#include +#include + +#include "periph/gpio.h" +#include "periph/spi.h" +#include "nrf24l01p_ng_constants.h" +#include "nrf24l01p_ng_communication.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define SPI_BUS (dev->params.spi) +#define SPI_PIN_CS (dev->params.pin_cs) + +#define NRF24L01P_NG_REG_MAX_WIDTH NRF24L01P_NG_MAX_ADDR_WIDTH + +/** + * @brief Reverse @p len bytes in @p buffer + * + * The NRF24L01+ expects data to be send over SPI from LSByte to MSByte + * and will output data bytes also from LSByte to MSByte! + * See the datasheet page 47, 8.3.1 SPI comands. + * + * This shall be the address register of pipe 0 inside the transceiver: + * [ x, x, x, x, x] (x = undefined content) + * + * Suppose you want to give pipe 0 the address 01:02:03:04. + * If you send: + * 01 ---> [ x, x, x, x, 01] + * 02 ---> [ x, x, x, 02, 01] + * 03 ---> [ x, x, 03, 02, 02] + * 04 ---> [ x, 04, 03, 02, 01] + * + * The transceiver does not have the address you expected. + * But over SPI you would read: + * [ x, 04, 03, 02, 01] ---> 01:02:03:04 + * because it sends the LSByte first. But it will not + * receice frames that are actually addressed to 01:02:03:04. + * + * So if you want to assign the address 01:02:03:04 + * you first have to swap the bytes, before you send data over SPI. + * + */ +static void _nrf24l01p_ng_swap_bytes(uint8_t* buffer, size_t len) { + for (size_t i = 0; i < len / 2; i++) { + uint8_t tmp = buffer[i]; + buffer[i] = buffer[len - 1 - i]; + buffer[len - 1 - i] = tmp; + } +} +static void _nrf24l01p_ng_copy_and_swap_bytes(uint8_t* dst, const uint8_t* src, size_t len) { + const uint8_t* end = dst + len; + src += len; + while (dst != end) { + *dst++ = *--src; + } +} + +int nrf24l01p_ng_acquire(nrf24l01p_ng_t *dev) +{ + return spi_acquire(dev->params.spi, dev->params.pin_cs, SPI_MODE_0, + dev->params.spi_clk); +} + +void nrf24l01p_ng_release(nrf24l01p_ng_t *dev) +{ + spi_release(dev->params.spi); +} + +uint8_t nrf24l01p_ng_read_reg(const nrf24l01p_ng_t *dev, uint8_t reg, + uint8_t *dest, size_t len) +{ + uint8_t cmd = NRF24L01P_NG_CMD_R_REGISTER(reg); + uint8_t status = spi_transfer_byte(SPI_BUS, SPI_PIN_CS, true, cmd); + spi_transfer_bytes(SPI_BUS, SPI_PIN_CS, false, NULL, dest, len); + if (len > 1) { + _nrf24l01p_ng_swap_bytes(dest, len); + } + return status; +} + +uint8_t nrf24l01p_ng_write_reg(const nrf24l01p_ng_t *dev, uint8_t reg, + const uint8_t *src, size_t len) +{ + assert(len <= NRF24L01P_NG_REG_MAX_WIDTH); + uint8_t cmd = NRF24L01P_NG_CMD_W_REGISTER(reg); + uint8_t status; + if (len > 1) { + uint8_t lsrc[NRF24L01P_NG_REG_MAX_WIDTH]; + _nrf24l01p_ng_copy_and_swap_bytes(lsrc, src, len); + status = spi_transfer_byte(SPI_BUS, SPI_PIN_CS, true, cmd); + spi_transfer_bytes(SPI_BUS, SPI_PIN_CS, false, lsrc, NULL, len); + } + else { + status = spi_transfer_byte(SPI_BUS, SPI_PIN_CS, true, cmd); + spi_transfer_bytes(SPI_BUS, SPI_PIN_CS, false, src, NULL, len); + } + return status; +} + +uint8_t nrf24l01p_ng_read_rx_payload(const nrf24l01p_ng_t *dev, void *dest, + size_t len) +{ + uint8_t cmd = NRF24L01P_NG_CMD_R_RX_PAYLOAD; + uint8_t status = spi_transfer_byte(SPI_BUS, SPI_PIN_CS, true, cmd); + spi_transfer_bytes(SPI_BUS, SPI_PIN_CS, false, &cmd, dest, len); + if (len > 1) { + _nrf24l01p_ng_swap_bytes(dest, len); + } + return status; +} + +uint8_t nrf24l01p_ng_write_tx_payload(const nrf24l01p_ng_t *dev, + const void *src, size_t len) +{ + assert(len <= NRF24L01P_NG_MAX_PAYLOAD_WIDTH); + uint8_t cmd = NRF24L01P_NG_CMD_W_TX_PAYLOAD; + uint8_t status; + if (len > 1) { + uint8_t lsrc[NRF24L01P_NG_MAX_PAYLOAD_WIDTH]; + _nrf24l01p_ng_copy_and_swap_bytes(lsrc, src, len); + status = spi_transfer_byte(SPI_BUS, SPI_PIN_CS, true, cmd); + spi_transfer_bytes(SPI_BUS, SPI_PIN_CS, false, lsrc, NULL, len); + } + else { + status = spi_transfer_byte(SPI_BUS, SPI_PIN_CS, true, cmd); + spi_transfer_bytes(SPI_BUS, SPI_PIN_CS, false, src, NULL, len); + } + return status; +} + +uint8_t nrf24l01p_ng_flush_tx(const nrf24l01p_ng_t *dev) +{ + uint8_t cmd = NRF24L01P_NG_CMD_FLUSH_TX; + return spi_transfer_byte(SPI_BUS, SPI_PIN_CS, false, cmd); +} + +uint8_t nrf24l01p_ng_flush_rx(const nrf24l01p_ng_t *dev) +{ + uint8_t cmd = NRF24L01P_NG_CMD_FLUSH_RX; + return spi_transfer_byte(SPI_BUS, SPI_PIN_CS, false, cmd); +} + +uint8_t nrf24l01p_ng_reuse_tx_pl(const nrf24l01p_ng_t *dev) +{ + uint8_t cmd = NRF24L01P_NG_CMD_REUSE_TX_PL; + return spi_transfer_byte(SPI_BUS, SPI_PIN_CS, false, cmd); +} + +uint8_t nrf24l01p_ng_read_rx_pl_width(const nrf24l01p_ng_t *dev, uint8_t *dest) +{ + uint8_t cmd = NRF24L01P_NG_CMD_R_RX_PL_WID; + uint8_t status = spi_transfer_byte(SPI_BUS, SPI_PIN_CS, true, cmd); + spi_transfer_bytes(SPI_BUS, SPI_PIN_CS, false, NULL, dest, 1); + return status; +} + +uint8_t nrf24l01p_ng_write_ack_pl(const nrf24l01p_ng_t *dev, const void *src, + size_t len, uint8_t pipe) +{ + assert(len <= NRF24L01P_NG_MAX_PAYLOAD_WIDTH); + assert(pipe < NRF24L01P_NG_PX_NUM_OF); + uint8_t cmd = NRF24L01P_NG_CMD_W_ACK_PAYLOAD(pipe); + uint8_t status; + if (len > 1) { + uint8_t lsrc[NRF24L01P_NG_MAX_PAYLOAD_WIDTH]; + _nrf24l01p_ng_copy_and_swap_bytes(lsrc, src, len); + status = spi_transfer_byte(SPI_BUS, SPI_PIN_CS, true, cmd); + spi_transfer_bytes(SPI_BUS, SPI_PIN_CS, false, lsrc, NULL, len); + } + else { + status = spi_transfer_byte(SPI_BUS, SPI_PIN_CS, true, cmd); + spi_transfer_bytes(SPI_BUS, SPI_PIN_CS, false, src, NULL, len); + } + return status; +} + +uint8_t nrf24l01p_ng_write_tx_pl_no_ack(const nrf24l01p_ng_t *dev, + const void *src, size_t len) +{ + assert(len <= NRF24L01P_NG_MAX_PAYLOAD_WIDTH); + uint8_t cmd = NRF24L01P_NG_CMD_W_TX_PAYLOAD_NO_ACK; + uint8_t status; + if (len > 1) { + uint8_t lsrc[NRF24L01P_NG_MAX_PAYLOAD_WIDTH]; + _nrf24l01p_ng_copy_and_swap_bytes(lsrc, src, len); + status = spi_transfer_byte(SPI_BUS, SPI_PIN_CS, true, cmd); + spi_transfer_bytes(SPI_BUS, SPI_PIN_CS, false, lsrc, NULL, len); + } + else { + status = spi_transfer_byte(SPI_BUS, SPI_PIN_CS, true, cmd); + spi_transfer_bytes(SPI_BUS, SPI_PIN_CS, false, src, NULL, len); + } + return status; +} + +uint8_t nrf24l01p_ng_get_status(const nrf24l01p_ng_t *dev) +{ + uint8_t cmd = NRF24L01P_NG_CMD_NOP; + return spi_transfer_byte(SPI_BUS, SPI_PIN_CS, false, cmd); +} diff --git a/drivers/nrf24l01p_ng/nrf24l01p_ng_netdev.c b/drivers/nrf24l01p_ng/nrf24l01p_ng_netdev.c new file mode 100644 index 000000000000..a1bf08af69c0 --- /dev/null +++ b/drivers/nrf24l01p_ng/nrf24l01p_ng_netdev.c @@ -0,0 +1,671 @@ +/* + * Copyright (C) 2019 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +/** + * @ingroup drivers_nrf24l01p_ng + * @{ + * + * @file + * @brief Implementation of RIOT's netdev_driver API + * for the NRF24L01+ (NG) transceiver + * + * @author Fabian Hüßler + * @} + */ +#include +#include +#include + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#include "kernel_defines.h" +#include "iolist.h" +#include "irq.h" +#include "luid.h" +#include "mutex.h" +#include "net/eui64.h" +#include "net/netdev.h" +#include "xtimer.h" + +#include "gnrc_netif_nrf24l01p_ng.h" +#include "nrf24l01p_ng_constants.h" +#include "nrf24l01p_ng_registers.h" +#include "nrf24l01p_ng_communication.h" +#include "nrf24l01p_ng_states.h" +#include "nrf24l01p_ng_netdev.h" +#if IS_USED(MODULE_NRF24L01P_NG_DIAGNOSTICS) +#include "nrf24l01p_ng_diagnostics.h" +#endif + +#define NRF24L01P_NG_FLG_IRQ \ + (NRF24L01P_NG_FLG_MAX_RT | NRF24L01P_NG_FLG_TX_DS | NRF24L01P_NG_FLG_RX_DR) + +static int _init(netdev_t *netdev); +static int _recv(netdev_t *netdev, void *buf, size_t len, void *info); +static int _send(netdev_t *netdev, const iolist_t *iolist); +static void _isr(netdev_t *netdev); +static int _get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len); +static int _set(netdev_t *netdev, netopt_t opt, const void *val, size_t max_len); + +const netdev_driver_t nrf24l01p_ng_driver = { + .init = _init, + .recv = _recv, + .send = _send, + .isr = _isr, + .get = _get, + .set = _set +}; + +static inline +void _trigger_send(const nrf24l01p_ng_t *dev) +{ + gpio_set(dev->params.pin_ce); + xtimer_usleep(NRF24L01P_NG_DELAY_US_CE_HIGH_PULSE); + gpio_clear(dev->params.pin_ce); + xtimer_usleep(NRF24L01P_NG_DELAY_US_TX_SETTLING); +} + +static int _assert_awake(const nrf24l01p_ng_t *dev) +{ + return nrf24l01p_ng_reg8_read(dev, NRF24L01P_NG_REG_CONFIG) & + NRF24L01P_NG_FLG_PWR_UP; +} + +static netopt_state_t _state_to_netif(nrf24l01p_ng_state_t state) +{ + if (state == NRF24L01P_NG_STATE_POWER_DOWN) { + return NETOPT_STATE_SLEEP; + } + if (state == NRF24L01P_NG_STATE_STANDBY_1) { + return NETOPT_STATE_STANDBY; + } + if (state == NRF24L01P_NG_STATE_STANDBY_2) { + return NETOPT_STATE_TX; + } + if (state == NRF24L01P_NG_STATE_TX_MODE) { + return NETOPT_STATE_TX; + } + if (state == NRF24L01P_NG_STATE_RX_MODE) { + return NETOPT_STATE_RX; + } + return NETOPT_STATE_OFF; /* error */ +} + +nrf24l01p_ng_state_t _state_from_netif(netopt_state_t state) +{ + if (state == NETOPT_STATE_SLEEP) { + return NRF24L01P_NG_STATE_POWER_DOWN; + } + if (state == NETOPT_STATE_STANDBY) { + return NRF24L01P_NG_STATE_STANDBY_1; + } + if (state == NETOPT_STATE_TX) { + return NRF24L01P_NG_STATE_TX_MODE; + } + if (state == NETOPT_STATE_RX) { + return NRF24L01P_NG_STATE_RX_MODE; + } + return NRF24L01P_NG_STATE_UNDEFINED; +} + +static void _nrf24l01p_ng_irq_handler(void *_dev) +{ + nrf24l01p_ng_t *dev = (nrf24l01p_ng_t *)_dev; + /* Once the IRQ pin has triggered, + do not congest the thread´s + message queue with IRQ events */ + gpio_irq_disable(dev->params.pin_irq); + netdev_trigger_event_isr((netdev_t *)dev); +} + +static void _isr_max_rt(nrf24l01p_ng_t *dev) +{ + assert(dev->state == NRF24L01P_NG_STATE_STANDBY_1 || + dev->state == NRF24L01P_NG_STATE_STANDBY_2 || + dev->state == NRF24L01P_NG_STATE_RX_MODE || + dev->state == NRF24L01P_NG_STATE_TX_MODE); + DEBUG_PUTS("[nrf24l01p_ng] IRS MAX_RT"); + nrf24l01p_ng_acquire(dev); + nrf24l01p_ng_flush_tx(dev); + nrf24l01p_ng_release(dev); + dev->netdev.event_callback(&dev->netdev, NETDEV_EVENT_TX_NOACK); +} + +static void _isr_rx_dr(nrf24l01p_ng_t *dev) +{ + assert(dev->state == NRF24L01P_NG_STATE_STANDBY_1 || + dev->state == NRF24L01P_NG_STATE_STANDBY_2 || + dev->state == NRF24L01P_NG_STATE_RX_MODE || + dev->state == NRF24L01P_NG_STATE_TX_MODE); + DEBUG_PUTS("[nrf24l01p_ng] IRS RX_DR"); + /* read all RX data */ + nrf24l01p_ng_acquire(dev); + while (!(nrf24l01p_ng_reg8_read(dev, NRF24L01P_NG_REG_FIFO_STATUS) & + NRF24L01P_NG_FLG_RX_EMPTY)) { + DEBUG_PUTS("[nrf24l01p_ng] ISR: read pending Rx frames"); + dev->netdev.event_callback(&dev->netdev, NETDEV_EVENT_RX_COMPLETE); + } + nrf24l01p_ng_release(dev); +} + +static void _isr_tx_ds(nrf24l01p_ng_t *dev) +{ + assert(dev->state == NRF24L01P_NG_STATE_STANDBY_1 || + dev->state == NRF24L01P_NG_STATE_STANDBY_2 || + dev->state == NRF24L01P_NG_STATE_RX_MODE || + dev->state == NRF24L01P_NG_STATE_TX_MODE); + DEBUG_PUTS("[nrf24l01p_ng] IRS TX_DS"); + dev->netdev.event_callback(&dev->netdev, NETDEV_EVENT_TX_COMPLETE); +} + +static int _init(netdev_t *netdev) +{ + nrf24l01p_ng_t *dev = (nrf24l01p_ng_t *)netdev; + if (dev->params.config.cfg_data_rate >= NRF24L01P_NG_RF_DR_NUM_OF || + dev->params.config.cfg_crc == NRF24L01P_NG_CRC_0BYTE || + dev->params.config.cfg_channel >= NRF24L01P_NG_NUM_CHANNELS) { + return -ENOTSUP; + } + if (spi_init_cs(dev->params.spi, dev->params.pin_cs) != SPI_OK) { + DEBUG_PUTS("[nrf24l01p_ng] _init(): spi_init_cs() failed"); + return -EIO; + } + if (gpio_init(dev->params.pin_ce, GPIO_OUT) < 0) { + DEBUG_PUTS("[nrf24l01p_ng] _init(): gpio_init() failed"); + return -EIO; + } + gpio_clear(dev->params.pin_ce); + if (nrf24l01p_ng_acquire(dev) < 0) { + DEBUG_PUTS("[nrf24l01p_ng] _init(): nrf24l01p_ng_acquire() failed"); + return -EIO; + } + if (dev->state != NRF24L01P_NG_STATE_POWER_DOWN) { + nrf24l01p_ng_transition_to_power_down(dev); + } + /* flush internal Tx and Rx FIFO */ + nrf24l01p_ng_flush_tx(dev); + nrf24l01p_ng_flush_rx(dev); + uint8_t aw = NRF24L01P_NG_ADDR_WIDTH; + const uint8_t bc[] = NRF24L01P_NG_BROADCAST_ADDR; + memcpy(NRF24L01P_NG_ADDR_P0(dev), bc, aw); + /* assign to pipe 0 the broadcast address*/ + nrf24l01p_ng_write_reg(dev, NRF24L01P_NG_REG_RX_ADDR_P0, + NRF24L01P_NG_ADDR_P0(dev), aw); + luid_get_lb(NRF24L01P_NG_ADDR_P1(dev), aw); + /* "The LSByte must be unique for all six pipes" [datasheet p.38] */ + if (NRF24L01P_NG_ADDR_P1(dev)[aw - 1] == bc[aw - 1]) { + luid_get_lb(NRF24L01P_NG_ADDR_P1(dev), aw); + } + /* assign to pipe 0 the "main" listening address */ + nrf24l01p_ng_write_reg(dev, NRF24L01P_NG_REG_RX_ADDR_P1, + NRF24L01P_NG_ADDR_P1(dev), aw); + /* set the address width */ + nrf24l01p_ng_reg8_write(dev, NRF24L01P_NG_REG_SETUP_AW, + NRF24L01P_NG_FLG_AW(nrf24l01p_ng_valtoe_aw(aw))); + /* set Tx power and Tx data rate */ + nrf24l01p_ng_reg8_write(dev, NRF24L01P_NG_REG_RF_SETUP, + NRF24L01P_NG_FLG_RF_DR(dev->params.config.cfg_data_rate) | + NRF24L01P_NG_FLG_RF_PWR(dev->params.config.cfg_tx_power)); + /* set retransmission delay and the maximum number of retransmisisons */ + nrf24l01p_ng_reg8_write(dev, NRF24L01P_NG_REG_SETUP_RETR, + NRF24L01P_NG_FLG_ARD(dev->params.config.cfg_retr_delay) | + NRF24L01P_NG_FLG_ARC(dev->params.config.cfg_max_retr)); + /* set the radio channel */ + nrf24l01p_ng_reg8_write(dev, NRF24L01P_NG_REG_RF_CH, + NRF24L01P_NG_FLG_RF_CH(dev->params.config.cfg_channel)); + /* enable pipe 0 and pipe 1 */ + nrf24l01p_ng_reg8_write(dev, NRF24L01P_NG_REG_EN_RXADDR, + NRF24L01P_NG_FLG_ERX_P0 | + NRF24L01P_NG_FLG_ERX_P1); + /* set CRC length */ + nrf24l01p_ng_reg8_write(dev, NRF24L01P_NG_REG_CONFIG, + NRF24L01P_NG_FLG_CRCO(dev->params.config.cfg_crc)); + /* enable NRF24L01+ features: + automatic acknowledgements, + dynamic payload lengths, + piggyback acknowledgements */ + nrf24l01p_ng_reg8_write(dev, NRF24L01P_NG_REG_FEATURES, + NRF24L01P_NG_FLG_EN_DYN_ACK | + NRF24L01P_NG_FLG_EN_DPL | + NRF24L01P_NG_FLG_EN_ACK_PAY); + /* enable automatic acknowledgements for pipe 0 and pipe 1 */ + nrf24l01p_ng_reg8_write(dev, NRF24L01P_NG_REG_EN_AA, + NRF24L01P_NG_FLG_ENAA_P0 | + NRF24L01P_NG_FLG_ENAA_P1); + /* enable dynamic payload lengths for pipe 0 and pipe 1 */ + nrf24l01p_ng_reg8_write(dev, NRF24L01P_NG_REG_DYNPD, + NRF24L01P_NG_FLG_DPL_P0 | + NRF24L01P_NG_FLG_DPL_P1); + /* clear interrupts */ + nrf24l01p_ng_reg8_write(dev, NRF24L01P_NG_REG_STATUS, NRF24L01P_NG_FLG_IRQ); + /* go to standby */ + nrf24l01p_ng_transition_to_standby_1(dev); +#if IS_USED(MODULE_NRF24L01P_NG_DIAGNOSTICS) && ENABLE_DEBUG + nrf24l01p_ng_diagnostics_print_all_regs(dev); + nrf24l01p_ng_diagnostics_print_dev_info(dev); +#endif + /* check if the transceiver responds */ + if (!_assert_awake(dev)) { + nrf24l01p_ng_release(dev); + return -ENODEV; + } + /* go to listening state */ + nrf24l01p_ng_transition_to_rx_mode(dev); + nrf24l01p_ng_release(dev); + /* enable interrupt pins */ + if (gpio_init_int(dev->params.pin_irq, GPIO_IN, GPIO_FALLING, + _nrf24l01p_ng_irq_handler, dev) < 0) { + DEBUG_PUTS("[nrf24l01p_ng] _init(): gpio_init_int() failed"); + return -EIO; + } + return 0; +} + +/** + * @brief NRF24L01+ @ref netdev_driver_t::recv routine + * + * @pre @see nrf24l01p_ng_acquire must have been called before. + * @pre Interrupts should be disabled + * + * The SPI bus is not acquired in this function because it is called from + * @ref netdev_driver_t::isr, possibly for multiple times. If another + * device acquired the SPI bus within the ISR, the ISR would block + * until that device releases the bus. + * + * @param[in] netdev Abstract network device handle + * @param[out] buf Rx buffer + * @param[in] len Size of Rx buffer + * @param[out] LQI and RSSI information (unused) + * + * @return Size of received frame in @p buf + * @return Upper estimation of the frame width, + * if @p buf == NULL and len == 0 + * @return Actual frame width, + * if @p buf == NULL and @p len != 0 + * (frame is NOT dropped) + * @retval -ENOBUFS @p buf != NULL and @p len < actual frame width + * (frame is dropped) + * @retval -EINVAL @p buf == NULL + * (and none of the above cases are true) + * @retval -ENOTSUP Malformed header + * @retval 0 No data to read from Rx FIFO + */ +static int _recv(netdev_t *netdev, void *buf, size_t len, void *info) +{ + (void)info; /* nrf24l01+ supports neither lqi nor rssi */ + + /* return upper estaimation bound of frame size */ + if (!buf && !len) { + DEBUG_PUTS("[nrf24l01p_ng] Return upper frame estimation"); + return NRF24L01P_NG_ADDR_WIDTH + NRF24L01P_NG_MAX_PAYLOAD_WIDTH; + } + nrf24l01p_ng_t *dev = (nrf24l01p_ng_t *)netdev; + uint8_t pl_width; + uint8_t status = nrf24l01p_ng_read_rx_pl_width(dev, &pl_width); + uint8_t pno = NRF24L01P_NG_VAL_RX_P_NO(status); + if (!pl_width || + pl_width > NRF24L01P_NG_MAX_PAYLOAD_WIDTH || + pno >= NRF24L01P_NG_PX_NUM_OF) { + DEBUG_PUTS("[nrf24l01p_ng] RX error, flush RX FIFO"); +/* In some rare cases the RX payload width (R_RX_PL_WID) exceeds + the maximum of 32 bytes. In that case it must be flushed. + See https://devzone.nordicsemi.com/f/nordic-q-a/26489/nrf24l01-the-length-of-received-data-exceed-32 + and https://www.mikrocontroller.net/articles/NRF24L01_Tutorial */ + nrf24l01p_ng_flush_rx(dev); + return 0; + } + uint8_t frame_len = NRF24L01P_NG_ADDR_WIDTH + pl_width; + /* do NOT drop frame and return exact frame size */ + if (!buf) { + DEBUG_PUTS("[nrf24l01p_ng] Return exact frame length"); + return frame_len; + } + /* drop frame, content in buf becomes invalid and return -ENOBUFS */ + if (len < frame_len) { + DEBUG("[nrf24l01p_ng] Buffer too small: %u < %u, dropping frame\n", + len, frame_len); + uint8_t garbage[pl_width]; + nrf24l01p_ng_read_rx_payload(dev, garbage, pl_width); + return -ENOBUFS; + } + /* get received frame */ + uint8_t dst_addr[NRF24L01P_NG_ADDR_WIDTH]; + if (pno == NRF24L01P_NG_P0) { + memcpy(dst_addr, NRF24L01P_NG_ADDR_P0(dev), sizeof(dst_addr)); + } + else { + memcpy(dst_addr, NRF24L01P_NG_ADDR_P1(dev), sizeof(dst_addr)); + if (pno > NRF24L01P_NG_P1) { + dst_addr[NRF24L01P_NG_ADDR_WIDTH - 1] = + NRF24L01P_NG_ADDR_PX_LSB(dev, pno); + } + } + DEBUG_PUTS("[nrf24l01p_ng] Handle received frame"); + uint8_t *frame = (uint8_t *)buf; + memcpy(frame, dst_addr, sizeof(dst_addr)); + frame += sizeof(dst_addr); + nrf24l01p_ng_read_rx_payload(dev, frame, pl_width); +#if IS_USED(MODULE_NRF24L01P_NG_DIAGNOSTICS) && ENABLE_DEBUG + nrf24l01p_ng_diagnostics_print_frame(dev, (uint8_t *)buf, frame_len); +#endif + DEBUG("[nrf24l01p_ng] Received frame length: %u\n", frame_len); + return (int)frame_len; +} + +/** + * @brief NRF24L01+ @ref netdev_driver_t::send routine + * + * @param[in] netdev Abstract network device handle + * @param[in] iolist Linked list of data to be sent, where + * the base must be the destination address + * + * @return Size of sent payload + * @retval -ENOTSUP @p iolist had no base and no next link, + * or address was too big, or too short + * @retval -EAGAIN Pending interrupts have been handled first + * @retval -EBUSY The internal Tx FIFO is full + * @retval -E2BIG Resulting frame from iolist was too big to be sent + */ +static int _send(netdev_t *netdev, const iolist_t *iolist) +{ + assert(netdev && iolist); + if (!(iolist->iol_base) || !(iolist->iol_next)) { + DEBUG_PUTS("[nrf24l01p_ng] No Tx address or no payload"); + return -ENOTSUP; + } + nrf24l01p_ng_t *dev = (nrf24l01p_ng_t *)netdev; + uint8_t pl_width = 0; + const uint8_t bcast_addr[] = NRF24L01P_NG_BROADCAST_ADDR; + uint8_t payload[NRF24L01P_NG_MAX_PAYLOAD_WIDTH]; + nrf24l01p_ng_acquire(dev); + uint8_t fifo_status; + uint8_t status = + nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_FIFO_STATUS, + &fifo_status, 1); + if (status & NRF24L01P_NG_FLG_IRQ) { + DEBUG_PUTS("[nrf24l01p_ng] Handle pending IRQ, before sending new data"); + nrf24l01p_ng_release(dev); + _isr(&dev->netdev); + return -EAGAIN; + } + if (fifo_status & NRF24L01P_NG_FLG_TX_FULL_) { + /* If the TX FIFO is full, but no ACK has arrived yet, + so no TX_DS / MAX_RT interrupt has triggered so far that + could clean the TX FIFO. So we need to wait until an interrupt + occurs, before we can send a new frame. This is done + while this _send() function is called in a loop and the + interrupt status is polled. + If you flush the FIFO here, pending content will + be lost. */ + DEBUG_PUTS("[nrf24l01p_ng] TX FIFO full"); + nrf24l01p_ng_release(dev); + return -EBUSY; /* for gnrc_netif_pktq */ + } + uint8_t *dst_addr = iolist->iol_base; + uint8_t dst_addr_len = iolist->iol_len; + if (dst_addr_len > NRF24L01P_NG_MAX_ADDR_WIDTH || + dst_addr_len < NRF24L01P_NG_MIN_ADDR_WIDTH) { + nrf24l01p_ng_release(dev); + DEBUG("[nrf24l01p_ng] Destination address has an invalid length: %u\n", + dst_addr_len); + return -ENOTSUP; + } + for (const iolist_t *iol = iolist->iol_next; iol; iol = iol->iol_next) { + if (pl_width + iol->iol_len > sizeof(payload)) { + nrf24l01p_ng_release(dev); + DEBUG_PUTS("[nrf24l01p_ng] frame too big"); + return -E2BIG; + } + memcpy(payload + pl_width, iol->iol_base, iol->iol_len); + pl_width += iol->iol_len; + } + nrf24l01p_ng_write_reg(dev, NRF24L01P_NG_REG_TX_ADDR, + dst_addr, dst_addr_len); + if (!memcmp(dst_addr, bcast_addr, dst_addr_len)) { + /* do not expect ACK for broadcast */ + nrf24l01p_ng_write_tx_pl_no_ack(dev, payload, pl_width); + } + else { + nrf24l01p_ng_write_tx_payload(dev, payload, pl_width); + /* A PTX node must change pipe 0 Rx address to Tx address + * in order to receive ACKs. + * If node switches back to Rx mode, pipe 0 Rx address + * must be restored from params. */ + nrf24l01p_ng_write_reg(dev, NRF24L01P_NG_REG_RX_ADDR_P0, + dst_addr, dst_addr_len); + nrf24l01p_ng_reg8_write(dev, NRF24L01P_NG_REG_SETUP_AW, + NRF24L01P_NG_FLG_AW(nrf24l01p_ng_valtoe_aw(dst_addr_len))); + } + if (dev->state != NRF24L01P_NG_STATE_TX_MODE && + dev->state != NRF24L01P_NG_STATE_STANDBY_2) { + dev->idle_state = dev->state; + if (dev->state != NRF24L01P_NG_STATE_STANDBY_1) { + nrf24l01p_ng_transition_to_standby_1(dev); + } + nrf24l01p_ng_transition_to_tx_mode(dev); + } + nrf24l01p_ng_release(dev); + _trigger_send(dev); + DEBUG("[nrf24l01p_ng] Sending %u bytes\n", pl_width); + return (int)pl_width; +} + +/** + * @brief NRF24L01+ @ref netdev_driver_t::isr + * + * @param[in] netdev Abstract network device + */ +static void _isr(netdev_t *netdev) +{ + nrf24l01p_ng_t *dev = (nrf24l01p_ng_t *)netdev; + + nrf24l01p_ng_acquire(dev); + gpio_irq_enable(dev->params.pin_irq); + uint8_t status = nrf24l01p_ng_get_status(dev); + /* clear interrupt flags */ + nrf24l01p_ng_write_reg(dev, NRF24L01P_NG_REG_STATUS, &status, 1); + nrf24l01p_ng_release(dev); + + if (status & NRF24L01P_NG_FLG_RX_DR) { + _isr_rx_dr(dev); + } + if (status & NRF24L01P_NG_FLG_MAX_RT) { + _isr_max_rt(dev); + } + if (status & NRF24L01P_NG_FLG_TX_DS) { + _isr_tx_ds(dev); + } + + nrf24l01p_ng_acquire(dev); + if (dev->state == NRF24L01P_NG_STATE_TX_MODE || + dev->state == NRF24L01P_NG_STATE_STANDBY_2) { + /* frame in FIFO is not an ACK */ + if (!(nrf24l01p_ng_reg8_read(dev, NRF24L01P_NG_REG_FIFO_STATUS) & + NRF24L01P_NG_FLG_TX_EMPTY)) { + nrf24l01p_ng_release(dev); + _trigger_send(dev); + return; + } + } + /* no more data to transmit */ + if (dev->state != NRF24L01P_NG_STATE_STANDBY_1) { + nrf24l01p_ng_transition_to_standby_1(dev); + } + /* go to idle state */ + if (dev->idle_state != NRF24L01P_NG_STATE_STANDBY_1) { + if (dev->idle_state == NRF24L01P_NG_STATE_POWER_DOWN) { + nrf24l01p_ng_transition_to_power_down(dev); + } + else { + dev->idle_state = NRF24L01P_NG_STATE_RX_MODE; + nrf24l01p_ng_transition_to_rx_mode(dev); + } + } + nrf24l01p_ng_release(dev); +} + +/** + * @brief @ref netdev_driver_t::get + * + * @param[in] netdev Abstract network device + * @param[in] opt netdev option type + * @param[out] val Option value + * @param[in] max_len Maximum option length + * + * @return Size of written option value + * @retval -ENOTSUP Unsupported netdev option @p opt + */ +static int _get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len) +{ + nrf24l01p_ng_t *dev = (nrf24l01p_ng_t *)netdev; + + (void)max_len; /* only used in assert() */ + switch (opt) { + case NETOPT_ADDR_LEN: + case NETOPT_SRC_LEN: { + assert(max_len == sizeof(uint16_t)); + *((uint16_t *)val) = NRF24L01P_NG_ADDR_WIDTH; + return sizeof(uint16_t); + } break; + case NETOPT_ADDRESS: { + assert(max_len >= NRF24L01P_NG_ADDR_WIDTH); + memcpy(val, NRF24L01P_NG_ADDR_P1(dev), NRF24L01P_NG_ADDR_WIDTH); + return NRF24L01P_NG_ADDR_WIDTH; + } break; + case NETOPT_AUTOACK: { + assert(max_len == sizeof(netopt_enable_t)); + /* mandatory for Enhanced ShockBurst */ + *((netopt_enable_t *)val) = NETOPT_ENABLE; + return sizeof(netopt_enable_t); + } break; + case NETOPT_CHANNEL: { + assert(max_len == sizeof(uint16_t)); + *((uint16_t *)val) = (uint16_t)nrf24l01p_ng_get_channel(dev); + return sizeof(uint16_t); + } break; + case NETOPT_CHECKSUM: + case NETOPT_INTEGRITY_CHECK: { + assert(max_len == sizeof(netopt_enable_t)); + /* mandatory for Enhanced ShockBurst */ + *((netopt_enable_t *)val) = NETOPT_ENABLE; + return sizeof(netopt_enable_t); + } break; + case NETOPT_DEVICE_TYPE: { + assert(max_len == sizeof(uint16_t)); + *((uint16_t *)val) = NETDEV_TYPE_NRF24L01P_NG; + return sizeof(uint16_t); + } break; + case NETOPT_PROTO: { + assert(max_len == sizeof(gnrc_nettype_t)); + *((gnrc_nettype_t *)val) = NRF24L01P_NG_UPPER_LAYER_PROTOCOL; + return sizeof(gnrc_nettype_t); + } break; + case NETOPT_MAX_PDU_SIZE: { + assert(max_len == sizeof(uint16_t)); + *((uint16_t *)val) = NRF24L01P_NG_MAX_PAYLOAD_WIDTH - + NRF24L01P_NG_ADDR_WIDTH + - 1; + return sizeof(uint16_t); + } + case NETOPT_RETRANS: { + assert(max_len == sizeof(uint8_t)); + *((uint8_t *)val) = nrf24l01p_ng_get_max_retransm(dev); + return sizeof(uint8_t); + } + case NETOPT_RX_TIMEOUT: { + assert(max_len == sizeof(uint32_t)); + *((uint32_t *)val) = + (uint32_t)nrf24l01p_ng_get_retransm_delay(dev, NULL); + return sizeof(uint32_t); + } break; + case NETOPT_STATE: { + assert(max_len == sizeof(netopt_state_t)); + *((netopt_state_t *)val) = _state_to_netif(dev->state); + return sizeof(netopt_state_t); + } break; + case NETOPT_TX_POWER: { + assert(max_len == sizeof(int16_t)); + *((int16_t *)val) = (int16_t)nrf24l01p_ng_get_tx_power(dev, NULL); + return sizeof(uint16_t); + } break; + default: + DEBUG("[nrf24l01p_ng] Unsupported netdev option %d\n", opt); + return -ENOTSUP; + } +} + +/** + * @brief @ref netdev_driver_t::set + * + * @param[in] netdev Abstract network device handle + * @param[in] opt netdev option type + * @param[in] val Option value + * @param[in] len Size of option value + * + * @return Size of written option value + * @return negative number, on failure + * @retval -ENOTSUP Unsupported netdev option @p opt + */ +static int _set(netdev_t *netdev, netopt_t opt, const void *val, size_t len) +{ + nrf24l01p_ng_t *dev = (nrf24l01p_ng_t *)netdev; + + switch (opt) { + case NETOPT_ADDRESS: { + /* common address length for all pipes */ + assert(len == NRF24L01P_NG_ADDR_WIDTH); + int ret = nrf24l01p_ng_set_rx_address(dev, val, NRF24L01P_NG_P0); + return ret ? ret : (int)len; + } break; + case NETOPT_CHANNEL: { + assert(len == sizeof(uint16_t)); + uint16_t ch = *((uint16_t *)val); + int ret = nrf24l01p_ng_set_channel(dev, (uint8_t)ch); + return ret ? ret : (int)sizeof(uint16_t); + } break; + case NETOPT_CHECKSUM: + case NETOPT_INTEGRITY_CHECK: { + assert(len == sizeof(netopt_enable_t)); + nrf24l01p_ng_crc_t crc = + (*((netopt_enable_t *)val) == NETOPT_ENABLE) + ? NRF24L01P_NG_CRC_2BYTE : NRF24L01P_NG_CRC_0BYTE; + int ret = nrf24l01p_ng_set_crc(dev, crc); + return ret ? ret : (int)sizeof(netopt_enable_t); + } break; + case NETOPT_RETRANS: { + assert(len == sizeof(uint8_t)); + uint8_t n = *((uint8_t *)val); + int ret = nrf24l01p_ng_set_max_retransm(dev, n); + return ret ? ret : (int)sizeof(uint8_t); + } break; + case NETOPT_RX_TIMEOUT: { + assert(len == sizeof(uint32_t)); + uint32_t us = *((uint32_t *)val); + nrf24l01p_ng_ard_t ard = nrf24l01p_ng_valtoe_ard(us); + int ret = nrf24l01p_ng_set_retransm_delay(dev, ard); + return ret ? ret : (int)sizeof(uint32_t); + } break; + case NETOPT_STATE: { + assert(len == sizeof(netopt_state_t)); + nrf24l01p_ng_state_t s = + _state_from_netif(*((netopt_state_t *)val)); + int ret = nrf24l01p_ng_set_state(dev, s); + return ret < 0 ? ret : (int)sizeof(netopt_state_t); + } break; + case NETOPT_TX_POWER: { + assert(len == sizeof(int16_t)); + int16_t dbm = *((int16_t *)val); + nrf24l01p_ng_tx_power_t txp = nrf24l01p_ng_valtoe_tx_power(dbm); + int ret = nrf24l01p_ng_set_tx_power(dev, txp); + return ret ? ret : (int)sizeof(int16_t); + } break; + default: + DEBUG("[nrf24l01p_ng] Unsupported netdev option %d\n", opt); + return -ENOTSUP; + } +} diff --git a/drivers/nrf24l01p_ng/nrf24l01p_ng_states.c b/drivers/nrf24l01p_ng/nrf24l01p_ng_states.c new file mode 100644 index 000000000000..96991ca29359 --- /dev/null +++ b/drivers/nrf24l01p_ng/nrf24l01p_ng_states.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2019 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +/** + * @ingroup drivers_nrf24l01p_ng + * @{ + * + * @file + * @brief Implementation of state transition procedures + * for the NRF24L01+ (NG) transceiver + * + * @author Fabian Hüßler + * @} + */ +#include +#include + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#include "xtimer.h" +#include "net/netopt.h" + +#include "nrf24l01p_ng_registers.h" +#include "nrf24l01p_ng_communication.h" +#include "nrf24l01p_ng_states.h" + +static void _restore_address_p0(const nrf24l01p_ng_t *dev) +{ + uint8_t addr_buffer[NRF24L01P_NG_ADDR_WIDTH]; + memcpy(addr_buffer, NRF24L01P_NG_ADDR_P0(dev), NRF24L01P_NG_ADDR_WIDTH); + nrf24l01p_ng_write_reg(dev, NRF24L01P_NG_REG_RX_ADDR_P0, addr_buffer, + NRF24L01P_NG_ADDR_WIDTH); + nrf24l01p_ng_reg8_write(dev, NRF24L01P_NG_REG_SETUP_AW, + NRF24L01P_NG_FLG_AW(nrf24l01p_ng_valtoe_aw(NRF24L01P_NG_ADDR_WIDTH))); +} + +void nrf24l01p_ng_transition_to_power_down(nrf24l01p_ng_t *dev) +{ + DEBUG_PUTS("[nrf24l01p_ng] transition to POWER_DOWN"); + assert(dev->state & NRF24L01P_NG_TRANSITION_TO_POWER_DOWN); + uint8_t config = NRF24L01P_NG_FLG_PWR_UP; + nrf24l01p_ng_reg8_clear(dev, NRF24L01P_NG_REG_CONFIG, &config); + dev->state = NRF24L01P_NG_STATE_POWER_DOWN; +} + +void nrf24l01p_ng_transition_to_standby_1(nrf24l01p_ng_t *dev) +{ + DEBUG_PUTS("[nrf24l01p_ng] transition to STANDBY_1"); + assert(dev->state & NRF24L01P_NG_TRANSITION_TO_STANDBY_1); + switch (dev->state) { + case NRF24L01P_NG_STATE_POWER_DOWN: + gpio_clear(dev->params.pin_ce); + uint8_t config = NRF24L01P_NG_FLG_PWR_UP; + nrf24l01p_ng_reg8_set(dev, NRF24L01P_NG_REG_CONFIG, &config); + xtimer_usleep(NRF24L01P_NG_DELAY_US_START_UP); + break; + case NRF24L01P_NG_STATE_RX_MODE: + gpio_clear(dev->params.pin_ce); + break; + case NRF24L01P_NG_STATE_STANDBY_2: + case NRF24L01P_NG_STATE_TX_MODE: + gpio_clear(dev->params.pin_ce); + _restore_address_p0(dev); + /* TX finished with one packet */ + break; + default: + return; + } + dev->state = NRF24L01P_NG_STATE_STANDBY_1; +} + +void nrf24l01p_ng_transition_to_standby_2(nrf24l01p_ng_t *dev) +{ + DEBUG_PUTS("[nrf24l01p_ng] transition to STANDBY_2"); + assert(dev->state & NRF24L01P_NG_TRANSITION_TO_STANDBY_2); + switch (dev->state) { + case NRF24L01P_NG_STATE_STANDBY_1:; + /* TX FIFO empty */ + uint8_t config = NRF24L01P_NG_FLG_PRIM_RX; + nrf24l01p_ng_reg8_clear(dev, NRF24L01P_NG_REG_CONFIG, &config); + gpio_set(dev->params.pin_ce); + break; + case NRF24L01P_NG_STATE_TX_MODE: + gpio_set(dev->params.pin_ce); + break; + default: + return; + } + dev->state = NRF24L01P_NG_STATE_STANDBY_2; +} + +void nrf24l01p_ng_transition_to_rx_mode(nrf24l01p_ng_t *dev) +{ + DEBUG_PUTS("[nrf24l01p_ng] transition to RX_MODE"); + assert(dev->state & NRF24L01P_NG_TRANSITION_TO_RX_MODE); + if (nrf24l01p_ng_reg8_read(dev, NRF24L01P_NG_REG_FIFO_STATUS) & + NRF24L01P_NG_FLG_RX_FULL) { + nrf24l01p_ng_flush_rx(dev); + } + uint8_t config = NRF24L01P_NG_FLG_PRIM_RX; + nrf24l01p_ng_reg8_set(dev, NRF24L01P_NG_REG_CONFIG, &config); + gpio_set(dev->params.pin_ce); + xtimer_usleep(NRF24L01P_NG_DELAY_US_RX_SETTLING); + dev->state = NRF24L01P_NG_STATE_RX_MODE; +} + +void nrf24l01p_ng_transition_to_tx_mode(nrf24l01p_ng_t *dev) +{ + DEBUG_PUTS("[nrf24l01p_ng] transition to TX_MODE"); + assert(dev->state & NRF24L01P_NG_TRANSITION_TO_TX_MODE); + /* TX FIFI not empty */ + uint8_t config = NRF24L01P_NG_FLG_PRIM_RX; + nrf24l01p_ng_reg8_clear(dev, NRF24L01P_NG_REG_CONFIG, &config); + dev->state = NRF24L01P_NG_STATE_TX_MODE; +} diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 381672f858a0..0960db51d4f9 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -97,6 +97,7 @@ PSEUDOMODULES += nimble_autoconn_% PSEUDOMODULES += newlib PSEUDOMODULES += newlib_gnu_source PSEUDOMODULES += newlib_nano +PSEUDOMODULES += nrf24l01p_ng_diagnostics PSEUDOMODULES += openthread PSEUDOMODULES += picolibc PSEUDOMODULES += picolibc_stdout_buffered diff --git a/sys/include/net/gnrc/netif/conf.h b/sys/include/net/gnrc/netif/conf.h index 45d9ad9262cd..1ade1687996c 100644 --- a/sys/include/net/gnrc/netif/conf.h +++ b/sys/include/net/gnrc/netif/conf.h @@ -147,6 +147,8 @@ extern "C" { #define GNRC_NETIF_L2ADDR_MAXLEN (IEEE802154_LONG_ADDRESS_LEN) #elif MODULE_NETDEV_ETH #define GNRC_NETIF_L2ADDR_MAXLEN (ETHERNET_ADDR_LEN) +#elif MODULE_NRF24L01P +#define GNRC_NETIF_L2ADDR_MAXLEN (5U) #elif MODULE_CC110X #define GNRC_NETIF_L2ADDR_MAXLEN (1U) #else diff --git a/sys/include/net/netopt.h b/sys/include/net/netopt.h index 60a7f701a69a..c5db2808ba06 100644 --- a/sys/include/net/netopt.h +++ b/sys/include/net/netopt.h @@ -68,6 +68,7 @@ typedef enum { * Ethernet | 6 | device MAC address * nrfmin | 2 | device short address * CC110x | 1 | device address + * NRF24L01+ | 5 | device address * LoRaWAN | 4 | device address */ NETOPT_ADDRESS, diff --git a/sys/net/gnrc/netif/gnrc_netif.c b/sys/net/gnrc/netif/gnrc_netif.c index 94dc8dc212cc..3ea3b5abe18c 100644 --- a/sys/net/gnrc/netif/gnrc_netif.c +++ b/sys/net/gnrc/netif/gnrc_netif.c @@ -95,6 +95,7 @@ bool gnrc_netif_dev_is_6lo(const gnrc_netif_t *netif) case NETDEV_TYPE_CC110X: case NETDEV_TYPE_BLE: case NETDEV_TYPE_NRFMIN: + case NETDEV_TYPE_NRF24L01P_NG: case NETDEV_TYPE_ESP_NOW: return true; default: @@ -1358,6 +1359,10 @@ static void _test_options(gnrc_netif_t *netif) assert(netif->ipv6.mtu < UINT16_MAX); #endif /* IS_USED(MODULE_GNRC_NETIF_IPV6) */ break; + case NETDEV_TYPE_NRF24L01P_NG: + assert(netif->flags & GNRC_NETIF_FLAGS_HAS_L2ADDR); + assert(netif->l2addr_len >= 3U && netif->l2addr_len <= 5U); + break; case NETDEV_TYPE_LORA: /* LoRa doesn't provide L2 ADDR */ case NETDEV_TYPE_SLIP: assert(!(netif->flags & GNRC_NETIF_FLAGS_HAS_L2ADDR)); diff --git a/sys/net/gnrc/netif/gnrc_netif_device_type.c b/sys/net/gnrc/netif/gnrc_netif_device_type.c index 15e319d86a15..4d0e9ab842df 100644 --- a/sys/net/gnrc/netif/gnrc_netif_device_type.c +++ b/sys/net/gnrc/netif/gnrc_netif_device_type.c @@ -115,6 +115,9 @@ void gnrc_netif_init_6ln(gnrc_netif_t *netif) #endif #ifdef MODULE_ESP_NOW case NETDEV_TYPE_ESP_NOW: +#endif +#ifdef MODULE_NRF24L01P_NG + case NETDEV_TYPE_NRF24L01P_NG: #endif case NETDEV_TYPE_NRFMIN: #if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_6LN) @@ -136,10 +139,12 @@ void gnrc_netif_ipv6_init_mtu(gnrc_netif_t *netif) switch (netif->device_type) { #if defined(MODULE_NETDEV_IEEE802154) || defined(MODULE_NRFMIN) || \ defined(MODULE_XBEE) || defined(MODULE_ESP_NOW) || \ - defined(MODULE_GNRC_SIXLOENC) || defined(MODULE_CC110X) + defined(MODULE_GNRC_SIXLOENC) || defined(MODULE_CC110X) || \ + defined(MODULE_NRF24L01P_NG) case NETDEV_TYPE_IEEE802154: case NETDEV_TYPE_NRFMIN: case NETDEV_TYPE_CC110X: + case NETDEV_TYPE_NRF24L01P_NG: #ifdef MODULE_GNRC_SIXLOWPAN_IPHC netif->flags |= GNRC_NETIF_FLAGS_6LO_HC; #endif diff --git a/sys/net/gnrc/netif/init_devs/auto_init_nrf24l01p_ng.c b/sys/net/gnrc/netif/init_devs/auto_init_nrf24l01p_ng.c new file mode 100644 index 000000000000..2aae0e430615 --- /dev/null +++ b/sys/net/gnrc/netif/init_devs/auto_init_nrf24l01p_ng.c @@ -0,0 +1,87 @@ + /* + * Copyright (C) 2019 OvGU Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + * + */ + +/** + * @ingroup sys_auto_init_gnrc_netif + * @{ + * + * @file + * @brief Auto initialization for NRF24L01+ (NG) network interfaces + * + * @author Fabian Hüßler + */ +#ifdef MODULE_NRF24L01P_NG + +#include "nrf24l01p_ng.h" +#include "nrf24l01p_ng_params.h" +#include "gnrc_netif_nrf24l01p_ng.h" +#include "log.h" +#include "msg.h" +#include "net/gnrc/netif/conf.h" +#define ENABLE_DEBUG (0) +#include "debug.h" + +#ifndef NRF24L01P_NG_EXTRA_STACKSIZE +/** + * @brief Additional stack size required by the driver + * + * With increasing of CONFIG_GNRC_NETIF_MSG_QUEUE_SIZE the required stack size + * increases as well. A queue size of 8 messages works with default stack size, + * so we increase the stack by `sizeof(msg_t)` for each additional element + */ +#define NRF24L01P_NG_EXTRA_STACKSIZE ((GNRC_NETIF_MSG_QUEUE_SIZE - 8) \ + * sizeof(msg_t)) +#endif + +/** + * @brief Calculate the stack size for the MAC layer thread(s) + */ +#define NRF24L01P_NG_MAC_STACKSIZE (THREAD_STACKSIZE_DEFAULT + \ + NRF24L01P_NG_EXTRA_STACKSIZE + \ + DEBUG_EXTRA_STACKSIZE) +#ifndef NRF24L01P_NG_MAC_PRIO +/** + * @brief The priority of the MAC layer thread + */ +#define NRF24L01P_NG_MAC_PRIO (GNRC_NETIF_PRIO) +#endif + +/** + * @brief Statically allocate memory for device descriptors + */ +nrf24l01p_ng_t _nrf24l01p_ng_devs[NRF24L01P_NG_NUM]; + +/** + * @brief Statically allocate memory for interfaces + */ +static gnrc_netif_t _netif[NRF24L01P_NG_NUM]; + +/** + * @brief Statically allocate memory for the MAC layer thread(s) + */ +static char stacks[NRF24L01P_NG_NUM][NRF24L01P_NG_MAC_STACKSIZE]; + +void auto_init_nrf24l01p_ng(void) +{ + for (unsigned i = 0; i < NRF24L01P_NG_NUM; i++) { + LOG_DEBUG("[auto_init_netif] initializing nrf24l01p #%u\n", i); + + nrf24l01p_ng_setup(&_nrf24l01p_ng_devs[i], &nrf24l01p_ng_params[i], i); + gnrc_netif_nrf24l01p_ng_create(&_netif[i], stacks[i], + NRF24L01P_NG_MAC_STACKSIZE, + NRF24L01P_NG_MAC_PRIO, + "nrf24l01p_ng", + (netdev_t *)&_nrf24l01p_ng_devs[i]); + } +} + +#else +typedef int dont_be_pedantic; +#endif /* MODULE_NRF24L01P_NG */ +/** @} */ diff --git a/sys/net/gnrc/netif/init_devs/init.c b/sys/net/gnrc/netif/init_devs/init.c index beb590cd07a7..e164d6263c4e 100644 --- a/sys/net/gnrc/netif/init_devs/init.c +++ b/sys/net/gnrc/netif/init_devs/init.c @@ -143,7 +143,10 @@ void gnrc_netif_init_devs(void) extern void auto_init_socket_zep(void); auto_init_socket_zep(); } - + if (IS_USED(MODULE_NRF24L01P_NG)) { + extern void auto_init_nrf24l01p_ng(void); + auto_init_nrf24l01p_ng(); + } if (IS_USED(MODULE_NRFMIN)) { extern void gnrc_nrfmin_init(void); gnrc_nrfmin_init(); diff --git a/sys/net/link_layer/l2util/l2util.c b/sys/net/link_layer/l2util/l2util.c index 1a7c07bacba9..b4da4f62debf 100644 --- a/sys/net/link_layer/l2util/l2util.c +++ b/sys/net/link_layer/l2util/l2util.c @@ -53,6 +53,26 @@ static void _create_eui64_from_short(const uint8_t *addr, size_t addr_len, } #endif /* defined(MODULE_CC110X) || defined(MODULE_NRFMIN) */ +#if defined(MODULE_NRF24L01P_NG) +/* create EUI64 from (Enhanced) ShockBurst l2-addr + with 3 Byte to 5 Byte length */ +static void _create_eui64_from_shockburst(const uint8_t *addr, size_t addr_len, + eui64_t *eui64) +{ + memset(eui64->uint8, 0, sizeof(eui64->uint8)); + eui64->uint8[3] = 0xff; + eui64->uint8[4] = 0xfe; + eui64->uint8[0] = ((uint8_t)addr_len) << 5; /* encode length */ + if (addr_len > 3) { + memcpy(&eui64->uint8[1 + (5 - addr_len)], + addr, addr_len - 3); + addr += (addr_len - 3); + addr_len -= (addr_len - 3); + } + memcpy(&eui64->uint8[5 + (3 - addr_len)], addr, addr_len); +} +#endif /* defined(MODULE_NRF24L01P_NG) */ + int l2util_eui64_from_addr(int dev_type, const uint8_t *addr, size_t addr_len, eui64_t *eui64) { @@ -93,6 +113,16 @@ int l2util_eui64_from_addr(int dev_type, const uint8_t *addr, size_t addr_len, return -EINVAL; } #endif /* defined(MODULE_CC110X) || defined(MODULE_NRFMIN) */ +#if defined (MODULE_NRF24L01P_NG) + case NETDEV_TYPE_NRF24L01P_NG: + if (addr_len <= 5 && addr_len >= 3) { + _create_eui64_from_shockburst(addr, addr_len, eui64); + return addr_len; + } + else { + return -EINVAL; + } +#endif /* defined (MODULE_NRF24L01P_NG) */ default: (void)addr; (void)addr_len; @@ -190,6 +220,16 @@ int l2util_ipv6_iid_to_addr(int dev_type, const eui64_t *iid, uint8_t *addr) addr[0] = iid->uint8[7]; return sizeof(uint8_t); #endif /* MODULE_CC110X */ +#if defined(MODULE_NRF24L01P_NG) + case NETDEV_TYPE_NRF24L01P_NG: + memset(addr, 0, sizeof(eui64_t)); + uint8_t addr_len = iid->uint8[0] >> 5; + if (addr_len > 3) { + memcpy(addr, &iid->uint8[1 + (5 - addr_len)], addr_len - 3); + } + memcpy(&addr[addr_len - 3], &iid->uint8[5], 3); + return addr_len; +#endif /* defined(MODULE_NRF24L01P_NG) */ default: (void)iid; (void)addr; @@ -243,6 +283,11 @@ int l2util_ndp_addr_len_from_l2ao(int dev_type, return -EINVAL; } #endif /* defined(MODULE_NETDEV_IEEE802154) || defined(MODULE_XBEE) */ +#if defined(MODULE_NRF24L01P_NG) + case NETDEV_TYPE_NRF24L01P_NG: + (void)opt; + return 5; /* maximum length */ +#endif /* defined(MODULE_NRF24L01P_NG) */ default: (void)opt; #ifdef DEVELHELP diff --git a/tests/driver_nrf24l01p_ng/Makefile b/tests/driver_nrf24l01p_ng/Makefile new file mode 100644 index 000000000000..50ef24806d89 --- /dev/null +++ b/tests/driver_nrf24l01p_ng/Makefile @@ -0,0 +1,16 @@ +DEBUG ?= 0 + +include ../Makefile.tests_common + +USEMODULE += shell +USEMODULE += shell_commands +USEMODULE += ps +USEMODULE += gnrc +USEMODULE += auto_init_gnrc_netif +USEMODULE += nrf24l01p_ng +USEMODULE += nrf24l01p_ng_diagnostics +USEMODULE += netstats_l2 +USEMODULE += gnrc_txtsnd + +CFLAGS += -DDEBUG_ASSERT_VERBOSE=1 +include $(RIOTBASE)/Makefile.include diff --git a/tests/driver_nrf24l01p_ng/Makefile.ci b/tests/driver_nrf24l01p_ng/Makefile.ci new file mode 100644 index 000000000000..04ee6f36fad5 --- /dev/null +++ b/tests/driver_nrf24l01p_ng/Makefile.ci @@ -0,0 +1,27 @@ +BOARD_INSUFFICIENT_MEMORY := \ + arduino-duemilanove \ + arduino-leonardo \ + arduino-mega2560 \ + arduino-nano \ + arduino-uno \ + atmega1284p \ + atmega128rfa1 \ + atmega328p \ + derfmega128 \ + i-nucleo-lrwan1 \ + mega-xplained \ + microduino-corerf \ + nucleo-f031k6 \ + nucleo-f042k6 \ + nucleo-l011k4 \ + nucleo-l031k6 \ + nucleo-l053r8 \ + samd10-xmini \ + slstk3400a \ + stk3200 \ + stm32f030f4-demo \ + stm32f0discovery \ + stm32l0538-disco \ + telosb \ + waspmote-pro \ + # diff --git a/tests/driver_nrf24l01p_ng/main.c b/tests/driver_nrf24l01p_ng/main.c new file mode 100644 index 000000000000..2a9c08f16be4 --- /dev/null +++ b/tests/driver_nrf24l01p_ng/main.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019 Otto-von-Guericke Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +/** + * @ingroup tests + * @{ + * + * @file + * @brief Test application for the NRF24L01+ transceiver + * + * @author Fabian Hüßler + * + * @} + */ +#include +#include + +#include "shell.h" +#include "shell_commands.h" +#include "net/gnrc.h" + +#define MAIN_QUEUE_SIZE (8) + +int sc_nrf24ctl(int argc, char *argv[]); + +static const shell_command_t shell_commands[] = { + { "nrf24ctl", "Configure an NRF24L01+ (NG) device", sc_nrf24ctl }, + { NULL, NULL, NULL } +}; + +static msg_t _main_msg_queue[MAIN_QUEUE_SIZE]; + +int main(void) +{ + msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE); + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + return 0; +} diff --git a/tests/driver_nrf24l01p_ng/nrf24ctl.c b/tests/driver_nrf24l01p_ng/nrf24ctl.c new file mode 100644 index 000000000000..057b689626ac --- /dev/null +++ b/tests/driver_nrf24l01p_ng/nrf24ctl.c @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2019 Otto-von-Guericke Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Tool to configure NRF24L01+ (NG) transceiver + * + * @author Fabian Hüßler + * + * @} + */ +#include +#include +#include +#include +#include "nrf24l01p_ng.h" +#include "nrf24l01p_ng_params.h" + +#ifndef EXIT_SUCCESS +#define EXIT_SUCCESS (0) +#endif +#ifndef EXIT_FAILURE +#define EXIT_FAILURE (1) +#endif + +#define MIN_ARGC (2) + +extern nrf24l01p_ng_t _nrf24l01p_ng_devs[NRF24L01P_NG_NUM]; + +static void print_help(void) +{ + puts( + "Usage:\n" + + "nrf24ctl " + "--set " + "
\n" + + "nrf24ctl " + "--set " + " " + "\n" + + "nrf24ctl " + "--get " + "
\n" + + "nrf24ctl " + "--get " + "\n" + + "nrf24ctl " " --regs\n" + "nrf24ctl " " --info\n" + ); +} + +int sc_nrf24ctl(int argc, char *argv[]) +{ + if (argc < MIN_ARGC) { + printf("[nrf24ctl] Expect at least %d arguments\n", MIN_ARGC); + goto PRINT_HELP_EXIT; + } + int dev_index = atoi(argv[1]); + if (dev_index < 0 || dev_index >= (int)NRF24L01P_NG_NUM) { + printf("[nrf24ctl] Invalid device index: %d\n", dev_index); + goto PRINT_HELP_EXIT; + } + nrf24l01p_ng_t *dev = &_nrf24l01p_ng_devs[dev_index]; + int ret = 0; + switch (argc) { + case 6: { + /* nrf24ctl --set */ + int pipe = atoi(argv[5]); + if (pipe < 0 || pipe >= NRF24L01P_NG_PX_NUM_OF) { + printf("[nrf24ctl] bad pipe index\n"); + goto PRINT_HELP_EXIT; + } + if ((!strcmp(argv[2], "-s")) || (!strcmp(argv[2], "--set"))) { + if (!(strcmp(argv[3], "address"))) { + uint8_t address[NRF24L01P_NG_MAX_ADDR_WIDTH]; + char *tok = NULL; + unsigned a = 0; + while (a < sizeof(address) && + (tok = strtok(a ? NULL : argv[4], ":"))) { + if (strlen(tok) > 2) { + printf("[nrf24ctl] Invalid address token: %s\n", + tok); + goto PRINT_HELP_EXIT; + } + address[a++] = (int)strtol(tok, NULL, 16); + } + ret = nrf24l01p_ng_set_rx_address(dev, address, pipe); + if (ret < 0) { + printf("[nrf24ctl] Could not set address (%d)\n", ret); + goto PRINT_HELP_EXIT; + } + } + else if (!(strcmp(argv[3], "pipe"))) { + bool enable = !!(atoi(argv[4])); + ret = nrf24l01p_ng_set_enable_pipe(dev, pipe, enable); + if (ret < 0) { + printf("[nrf24ctl] " + "Could not enable/disable pipe %d\n", pipe); + goto PRINT_HELP_EXIT; + } + } + else { + printf("[nrf24ctl] Invalid attribute: --set %s\n", + argv[3]); + goto PRINT_HELP_EXIT; + } + } + else { + printf("[nrf24ctl] Invalid command: %s\n", argv[2]); + goto PRINT_HELP_EXIT; + } + } break; + case 5: { + /* nrf24ctl --set */ + if ((!strcmp(argv[2], "-s")) || (!strcmp(argv[2], "--set"))) { + if (!(strcmp(argv[3], "channel"))) { + int ch = atoi(argv[4]); + ret = nrf24l01p_ng_set_channel(dev, ch); + if (ret < 0) { + printf("[nrf24ctl] Could not switch to channel (%d)\n", + ret); + goto PRINT_HELP_EXIT; + } + } + else if (!(strcmp(argv[3], "crc"))) { + int crc_len = atoi(argv[4]); + nrf24l01p_ng_crc_t crco = nrf24l01p_ng_valtoe_crc(crc_len); + ret = nrf24l01p_ng_set_crc(dev, crco); + if (ret < 0) { + printf("[nrf24ctl] Could not set CRC length (%d)\n", + ret); + goto PRINT_HELP_EXIT; + } + } + else if (!(strcmp(argv[3], "data_rate"))) { + int dr = atoi(argv[4]); + nrf24l01p_ng_rfdr_t adr = nrf24l01p_ng_valtoe_rfdr(dr); + ret = nrf24l01p_ng_set_air_data_rate(dev, adr); + if (ret < 0) { + printf("[nrf24ctl] Could not set air data rate (%d)\n", + ret); + goto PRINT_HELP_EXIT; + } + } + else if (!(strcmp(argv[3], "power"))) { + int pwr = atoi(argv[4]); + nrf24l01p_ng_tx_power_t txp = + nrf24l01p_ng_valtoe_tx_power(pwr); + ret = nrf24l01p_ng_set_tx_power(dev, txp); + if (ret < 0) { + printf("[nrf24ctl] Could not set Tx power (%d)\n", + ret); + goto PRINT_HELP_EXIT; + } + } + else if (!(strcmp(argv[3], "retr_delay"))) { + int del = atoi(argv[4]); + nrf24l01p_ng_ard_t ard = nrf24l01p_ng_valtoe_ard(del); + ret = nrf24l01p_ng_set_retransm_delay(dev, ard); + if (ret < 0) { + printf("[nrf24ctl] " + "Could not set retransmission delay (%d)\n", + ret); + goto PRINT_HELP_EXIT; + } + } + else if (!(strcmp(argv[3], "state"))) { + nrf24l01p_ng_state_t s = + nrf24l01p_ng_diagnostics_string_to_state(argv[4]); + ret = nrf24l01p_ng_set_state(dev, s); + if (ret < 0) { + printf("[nrf24l01p] " + "Could not change device state (%d)", + ret); + goto PRINT_HELP_EXIT; + } + } + else if (!(strcmp(argv[3], "max_retr"))) { + int retr = atoi(argv[4]); + ret = nrf24l01p_ng_set_max_retransm(dev, retr); + if (ret < 0) { + printf("[nrf24ctl] " + "Could not set max. retransmissions (%d)\n", + ret); + goto PRINT_HELP_EXIT; + } + } + else { + printf("[nrf24ctl] Invalid attribute: --set %s\n", + argv[3]); + goto PRINT_HELP_EXIT; + } + } + /* nrf24ctl --get */ + else if ((!strcmp(argv[2], "-g")) || + (!strcmp(argv[2], "--get"))) { + int pipe = atoi(argv[4]); + if (pipe < 0 || pipe >= NRF24L01P_NG_PX_NUM_OF) { + printf("[nrf24ctl] bad pipe index\n"); + goto PRINT_HELP_EXIT; + } + if (!(strcmp(argv[3], "address"))) { + uint8_t address[2 * NRF24L01P_NG_MAX_ADDR_WIDTH + 5]; + address[sizeof(address) - 1] = '\0'; + ret = nrf24l01p_ng_get_rx_address(dev, address, pipe); + uint8_t i; + for (i = sizeof(address) - 2; ret > 0;) { + char hx[3] = { '0', '0', 0 }; + sprintf(hx, "%2x", address[--ret]); + address[i--] = hx[1]; + address[i--] = hx[0]; + if (ret) { + address[i--] = ':'; + } + } + printf("%s\n", address + (i += 1)); + } + else if (!(strcmp(argv[3], "pipe"))) { + bool enable = false; + nrf24l01p_ng_get_enable_pipe(dev, pipe, &enable); + printf("nrf24l01p device %d - pipe %d: %s\n", + dev_index, pipe, + (enable ? "enabled" : "disabled")); + } + else { + printf("[nrf24ctl] Invalid attribute: --get %s\n", + argv[3]); + goto PRINT_HELP_EXIT; + } + } + else { + printf("[nrf24ctl] Invalid command: %s\n", argv[2]); + goto PRINT_HELP_EXIT; + } + } break; + case 4: { + /* nrf24ctl --get */ + if ((!strcmp(argv[2], "-g")) || (!strcmp(argv[2], "--get"))) { + if (!(strcmp(argv[3], "channel"))) { + uint8_t ch = nrf24l01p_ng_get_channel(dev); + printf("nrf24l01p device %d - Channel: %u (%d MHz)\n", + dev_index, ch, NRF24L01P_NG_BASE_FRQ_MHZ + ch); + } + else if (!(strcmp(argv[3], "crc"))) { + uint8_t crc_len = nrf24l01p_ng_get_crc(dev, NULL); + printf("nrf24l01p device %d - " + "CRC length: %u [bytes]\n", + dev_index, crc_len); + } + else if (!(strcmp(argv[3], "data_rate"))) { + uint16_t data_rate = + nrf24l01p_ng_get_air_data_rate(dev, NULL); + printf("nrf24l01p device %d - " + "Air data rate: %u [kbit/s]\n", + dev_index, data_rate); + } + else if (!(strcmp(argv[3], "power"))) { + int8_t power = nrf24l01p_ng_get_tx_power(dev, NULL); + printf("nrf24l01p device %d - " + "Tx Power: %d [dbm]\n", + dev_index, power); + } + else if (!(strcmp(argv[3], "retr_delay"))) { + uint16_t delay = + nrf24l01p_ng_get_retransm_delay(dev, NULL); + printf( + "nrf24l01p device %d - " + "Retransmission delay: %u [us]\n", + dev_index, delay); + } + else if (!(strcmp(argv[3], "state"))) { + const char *sstate = + nrf24l01p_ng_diagnostics_state_to_string(dev->state); + printf("nrf24l01p device %d - State: %s\n", dev_index, + sstate); + } + else if (!(strcmp(argv[3], "max_retr"))) { + uint8_t retransm = nrf24l01p_ng_get_max_retransm(dev); + printf("nrf24l01p device %d - " + "Maximum retransmission: %u\n", + dev_index, retransm); + } + else { + printf("[nrf24ctl] Invalid attribute: --get %s\n", + argv[3]); + goto PRINT_HELP_EXIT; + } + } + else { + printf("[nrf24ctl] Invalid command: %s\n", argv[2]); + goto PRINT_HELP_EXIT; + } + } break; + case 3: { + /* nrf24ctl --regs */ + if ((!strcmp(argv[2], "-r")) || (!strcmp(argv[2], "--regs"))) { + nrf24l01p_ng_print_all_regs(dev); + } + /* nrf24ctl --info */ + else if ((!strcmp(argv[2], "-i")) || + (!strcmp(argv[2], "--info"))) { + nrf24l01p_ng_print_dev_info(dev); + } + else { + printf("[nrf24ctl] Invalid command: %s\n", argv[2]); + goto PRINT_HELP_EXIT; + } + } break; + default: +PRINT_HELP_EXIT: + print_help(); + return EXIT_FAILURE; + } + puts("[nrf24ctl] Success"); + return EXIT_SUCCESS; +}