diff --git a/drivers/Makefile b/drivers/Makefile index 25ea5ea785..f26f528a09 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -4,6 +4,9 @@ endif ifneq (,$(filter netdev_802154,$(USEMODULE))) DIRS += netdev/802154 endif +ifneq (,$(filter nrf24l01p,$(USEMODULE))) + DIRS += nrf24l01p +endif DIRS += $(dir $(wildcard $(addsuffix /Makefile, ${USEMODULE}))) diff --git a/drivers/Makefile.include b/drivers/Makefile.include index eab624e91e..7df51fbdf2 100644 --- a/drivers/Makefile.include +++ b/drivers/Makefile.include @@ -22,3 +22,6 @@ endif ifneq (,$(filter l3g4200d,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/l3g4200d/include endif +ifneq (,$(filter nrf24l01p,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/drivers/nrf24l01p/include +endif diff --git a/drivers/include/nrf24l01p.h b/drivers/include/nrf24l01p.h new file mode 100644 index 0000000000..fc464569fe --- /dev/null +++ b/drivers/include/nrf24l01p.h @@ -0,0 +1,586 @@ +/* + * Copyright (C) 2014 Hamburg University of Applied Sciences + * + * 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 NRF24L01+ Driver Interface + * @ingroup drivers + * @{ + * + * @file + * @brief Low-level driver for nrf24l01+ transceiver + * + * @author Hauke Petersen + * @author Peter Kietzmann + * + */ + +#ifndef __NRF24L01P_H +#define __NRF24L01P_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "periph/gpio.h" +#include "periph/spi.h" + +/** + * @brief Structure that represents the hardware setup of the nrf24l01+ transceiver. + */ +typedef struct { + spi_t spi; /**< SPI device to initialize */ + gpio_t ce; /**< GPIO pin to initialize as chip enable */ + gpio_t cs; /**< GPIO pin to initialize as chip select */ + gpio_t irq; /**< GPIO pin to initialize as interrupt request */ + int listener; /**< Place to store an ID in */ +} nrf24l01p_t; + +/** + * @brief Defines the address width of the nrf24l01+ transceiver. + */ +typedef enum { + NRF24L01P_AW_3BYTE, /**< address width is 3 Byte */ + NRF24L01P_AW_4BYTE, /**< address width is 4 Byte */ + NRF24L01P_AW_5BYTE /**< address width is 5 Byte */ +} nrf24l01p_aw_t; + + +/** + * @brief Defines the RF datarate. + */ +typedef enum { + NRF24L01P_DR_250KBS,/**< datarate is 250 kbps */ + NRF24L01P_DR_1MBS, /**< datarate is 1 Mbps */ + NRF24L01P_DR_2MBS /**< datarate is 2 Mbps */ +} nrf24l01p_dr_t; + +/** + * @brief Defines the RF power level. + */ +typedef enum { + NRF24L01P_PWR_N18DBM = 0, /**< power is -18dBm */ + NRF24L01P_PWR_N12DBM, /**< power is -12dBm */ + NRF24L01P_PWR_N6DBM, /**< power is - 6dBm */ + NRF24L01P_PWR_0DBM /**< power is 0dBm */ +} nrf24l01p_pwr_t; + +/** + * @brief Defines the datapipe on which the receiver searches for packets. + */ +typedef enum { + NRF24L01P_PIPE0 = 0,/**< RX pipe 0 */ + NRF24L01P_PIPE1, /**< RX pipe 1 */ + NRF24L01P_PIPE2, /**< RX pipe 2 */ + NRF24L01P_PIPE3, /**< RX pipe 3 */ + NRF24L01P_PIPE4, /**< RX pipe 4 */ + NRF24L01P_PIPE5 /**< RX pipe 5 */ +} nrf24l01p_rx_pipe_t; + +/** + * @brief Defines the error detection encoding scheme for the nrf24l01p transceiver. + */ +typedef enum { + NRF24L01P_CRC_1BYTE = 0, /**< encoding scheme generates 1 Byte redundancy */ + NRF24L01P_CRC_2BYTE, /**< encoding scheme generates 2 Bytes redundancy */ +} nrf24l01p_crc_t; + +/** + * @brief Defines the automatic retransmission delay defined from end of transmission + * to start of next treansmission. + */ +typedef enum { + NRF24L01P_RETR_250US = 0, /**< retransmit delay is 250us */ + NRF24L01P_RETR_500US, /**< retransmit delay is 500us */ + NRF24L01P_RETR_750US, /**< retransmit delay is 750us */ + NRF24L01P_RETR_1000US, /**< retransmit delay is 1000us */ + NRF24L01P_RETR_1250US, /**< retransmit delay is 1250us */ + NRF24L01P_RETR_1500US, /**< retransmit delay is 1500us */ + NRF24L01P_RETR_1750US, /**< retransmit delay is 1750us */ + NRF24L01P_RETR_2000US, /**< retransmit delay is 2000us */ + NRF24L01P_RETR_2250US, /**< retransmit delay is 2250us */ + NRF24L01P_RETR_2500US, /**< retransmit delay is 2500us */ + NRF24L01P_RETR_2750US, /**< retransmit delay is 2750us */ + NRF24L01P_RETR_3000US, /**< retransmit delay is 3000us */ + NRF24L01P_RETR_3250US, /**< retransmit delay is 3250us */ + NRF24L01P_RETR_3500US, /**< retransmit delay is 3500us */ + NRF24L01P_RETR_3750US, /**< retransmit delay is 3750us */ + NRF24L01P_RETR_4000US, /**< retransmit delay is 4000us */ +} nrf24l01p_retransmit_delay_t; + +/** + * @brief Defines states for the nrf24l01+ transceiver + */ +typedef enum { + RCV_PKT_NRF24L01P = 0, /**< transceiver received data */ +} nrf24l01p_rx_event_t ; + + +/** +* @brief Read one register of the nrf24l01+ transceiver. +* +* @param[in] dev Transceiver device to use. +* @param[in] reg Register address to read from. +* @param[in] answer Byte to read. +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_read_reg(nrf24l01p_t *dev, char reg, char *answer); + +/** +* @brief Write one register to the nrf24l01+ transceiver. +* +* @param[in] dev Transceiver device to use. +* @param[in] reg Register address to write to. +* @param[in] answer Byte to write. +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_write_reg(nrf24l01p_t *dev, char reg, char write); + +/** +* @brief Initialize the nrf24l01+ transceiver. +* +* @ note +* This function initializes the transceiver so that it is ready to use. +* +* @param[in] dev Transceiver device to use. +* @param[in] spi SPI device to use. +* @param[in] ce GPIO pin to use for chip enable. +* @param[in] cs GPIO pin to use for chip select. +* @param[in] irq GPIO pin to use for interrupt request. +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_init(nrf24l01p_t *dev, spi_t spi, gpio_t ce, gpio_t csn, gpio_t irq); + +/** +* @brief Power on the nrf24l01+ transceiver. +* +* @param[in] dev Transceiver device to use. +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_on(nrf24l01p_t *dev); + +/** +* @brief Power off the nrf24l01+ transceiver. +* +* @param[in] dev Transceiver device to use. +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_off(nrf24l01p_t *dev); + +/** +* @brief Transmit payload laying in TX FIFO of the nrf24l01+ transceiver. +* +* @param[in] dev Transceiver device to use. +* +*/ +void nrf24l01p_transmit(nrf24l01p_t *dev); + +/** +* @brief Read payload from RX FIFO of the nrf24l01+ transceiver. +* +* @param[in] dev Transceiver device to use. +* @param[in] answer Buffer to receive bytes to. +* @param[in] size Number of bytes to transfer. For nrf24l01+ in general 32. +* +* @return Number of bytes that were transfered. +* @return -1 on error. +*/ +int nrf24l01p_read_payload(nrf24l01p_t *dev, char *answer, unsigned int size); + +/** +* @brief Register a given ID to the nrf24l01+ transceiver. +* +* @param[in] dev Transceiver device to use. +* @param[in] pid ID to register. +* +*/ +void nrf24l01p_register(nrf24l01p_t *dev, unsigned int *pid); + +/** +* @brief Unregister the nrf24l01+ transceiver from his ID. +* +* @param[in] dev Transceiver device to use. +* @param[in] pid Actual ID to unregister. +* +* @return 0 on success. +* @return -1 on error. +*/ +int nrf24l01p_unregister(nrf24l01p_t *dev, unsigned int pid); + +/** +* @brief Get ID from the nrf24l01p transceiver. +* +* @param[in] dev Transceiver device to use. +* @param[in] pid T.ransceiver ID +* +*/ +void nrf24l01p_get_id(nrf24l01p_t *dev, unsigned int *pid); + +/** +* @brief Start searching packets while in RX mode. +* +* @param[in] dev Transceiver device to use. +* +*/ +void nrf24l01p_start(nrf24l01p_t *dev); + +/** +* @brief Stop searching packets while in RX mode. +* +* @param[in] dev Transceiver device to use. +* +*/ +void nrf24l01p_stop(nrf24l01p_t *dev); + +/** +* @brief Preload TX FIFO with payload to transmit. +* +* @param[in] dev Transceiver device to use. +* @param[in] data Buffer to preload. +* @param[in] size Number of bytes in buffer. For nrf24l01+ e.g. 32 +* +* @return 0 on success. +* @return -1 on error. +*/ +int nrf24l01p_preload(nrf24l01p_t *dev, char *data, unsigned int size); + +/** +* @brief Set the RF channel for the nrf24l01+ transceiver. +* +* @note +* To ensure non-overlapping channels in 2Mbps mode, don't use directly +* neighbouring channels in this mode. +* +* @param[in] dev Transceiver device to use. +* @param[in] chan Buffer to preload. +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_set_channel(nrf24l01p_t *dev, uint8_t chan); + +/** +* @brief Set the address width for the nrf24l01+ transceiver. +* +* @param[in] dev Transceiver device to use. +* @param[in] aw Address width (type nrf24l01p_aw_t). +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_set_address_width(nrf24l01p_t *dev, nrf24l01p_aw_t aw); + +/** +* @brief Set the RX payload width for the nrf24l01+ transceiver +* +* @ note +* This function sets the payload width for one packet. If the maximum of 32 bytes is +* exeeded, this value is set to 32. +* +* @param[in] dev Transceiver device to use. +* @param[in] pipe RX pipe to set the payload width. +* @param[in] width Numer of bytes per packet in RX payload. +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_set_payload_width(nrf24l01p_t *dev, nrf24l01p_rx_pipe_t pipe, char width); + +/** +* @brief Set the TX address for the nrf24l01+ transceiver (byte array). +* +* @note +* You can either use this function and give it a pointer to a byte array which +* holds the address to set, or use "nrf24l01p_set_tx_address_long" which requires +* a uint64_t which holds the address in the LSBs. +* +* @param[in] dev Transceiver device to use. +* @param[in] saddr Byte array which holds the TX address. +* @param[in] length Number of bytes in address array. +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_set_tx_address(nrf24l01p_t *dev, char *saddr, unsigned int length); + +/** +* @brief Set the TX address for the nrf24l01+ transceiver (long int). +* +* @param[in] dev Transceiver device to use. +* @param[in] saddr Long integer which holds the TX address in LSBs. +* @param[in] length Number of relevant bytes in uint64_t. +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_set_tx_address_long(nrf24l01p_t *dev, uint64_t saddr, unsigned int length); + +/** +* @brief Set the RX address for the nrf24l01+ transceiver (byte array). +* +* @note +* You can either use this function and give it a pointer to a byte array which +* holds the address to set, or use "nrf24l01p_set_rx_address_long" which requires +* a uint64_t which holds the address in the LSBs. +* +* @param[in] dev Transceiver device to use. +* @param[in] pipe RX pipe to set the address. +* @param[in] saddr Byte array which holds the RX address. +* @param[in] length Number of bytes in address array. +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_set_rx_address(nrf24l01p_t *dev, nrf24l01p_rx_pipe_t pipe, char *saddr, unsigned int length); + +/** +* @brief Set the RX address for the nrf24l01+ transceiver (long int). +* +* @param[in] dev Transceiver device to use. +* @param[in] pipe RX pipe to set the address. +* @param[in] saddr Long integer which holds the RX address in LSBs. +* @param[in] length Number of relevant bytes in uint64_t. +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_set_rx_address_long(nrf24l01p_t *dev, nrf24l01p_rx_pipe_t pipe, uint64_t saddr, unsigned int length); + +/** +* @brief Get the TX address for the nrf24l01+ transceiver (long int). +* +* @param[in] dev Transceiver device to use. +* +* @return TX address of the nrf24l01+ transceiver. +*/ +uint64_t nrf24l01p_get_tx_address_long(nrf24l01p_t *dev); + +/** +* @brief Get the RX address for the nrf24l01+ transceiver (long int). +* +* @param[in] dev Transceiver device to use. +* @param[in] pipe RX pipe to get the address from. +* +* @return RX address of the nrf24l01+ transceiver. +*/ +uint64_t nrf24l01p_get_rx_address_long(nrf24l01p_t *dev, nrf24l01p_rx_pipe_t pipe); + +/** +* @brief Get the TX address for the nrf24l01+ transceiver (long int). +* +* @note +* If you chose 2Mbps you should not allocate directly neighboring RF channels. +* +* @param[in] dev Transceiver device to use. +* @param[in] dr Datarate (of type nrf24l01p_dr_t). +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_set_datarate(nrf24l01p_t *dev, nrf24l01p_dr_t dr); + +/** +* @brief Get the status (register) of the nrf24l01+ transceiver device. +* +* @param[in] dev Transceiver device to use.s of the. +* +* @return Value of the status register. +*/ +int nrf24l01p_get_status(nrf24l01p_t *dev); + +/** +* @brief Set the transmit power for the nrf24l01+ transceiver device. +* +* @note +* This function rounds the input values to the nearest possible setting. +* +* @param[in] dev Transceiver device to use. +* @param[in] pwr TX power for the nrf24l01p transceiver. +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_set_power(nrf24l01p_t *dev, int *pwr); + +/** +* @brief Get the transmit power for the nrf24l01+ transceiver device. +* +* @param[in] dev Transceiver device to use. +* +* @return TX power value of the nrf24l01+ transceiver. +*/ +int nrf24l01p_get_power(nrf24l01p_t *dev); + +/** +* @brief Set the nrf24l01+ into TX mode. +* +* @param[in] dev Transceiver device to use. +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_set_txmode(nrf24l01p_t *dev); + +/** +* @brief Set the nrf24l01+ into RX mode. +* +* @param[in] dev Transceiver device to use. +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_set_rxmode(nrf24l01p_t *dev); + +/** +* @brief Reset all interrupts on the nrf24l01+ transceiver. +* +* @param[in] dev Transceiver device to use. +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_reset_all_interrupts(nrf24l01p_t *dev); + +/** +* @brief Mask one interrupt on the nrf24l01+ transceiver. +* +* @note +* There are three interrupts on the nrf24l01+ which can be masked: +* "MASK_RX_DR", "MASK_TX_DS" and "MASK_MAX_RT". Theay are defined +* in "include/nrf24l01p_settings.h". +* +* @param[in] dev Transceiver device to use. +* @param[in] intr Transceiver device to use. +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_mask_interrupt(nrf24l01p_t *dev, char intr); + +/** +* @brief Unmask one interrupt on the nrf24l01+ transceiver. +* +* @note +* There are three interrupts on the nrf24l01+ which can be unmasked: +* "MASK_RX_DR", "MASK_TX_DS" and "MASK_MAX_RT". Theay are defined +* in "include/nrf24l01p_settings.h". +* +* @param[in] dev Transceiver device to use. +* @param[in] intr Transceiver device to use. +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_unmask_interrupt(nrf24l01p_t *dev, char intr); + +/** +* @brief Enable RX datapipe on the nrf24l01+ transceiver. +* +* @param[in] dev Transceiver device to use. +* @param[in] pipe RX pipe to enable. +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_enable_pipe(nrf24l01p_t *dev, nrf24l01p_rx_pipe_t pipe); + +/** +* @brief Disable RX datapipe on the nrf24l01+ transceiver. +* +* @param[in] dev Transceiver device to use. +* @param[in] pipe RX pipe to disable. +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_disable_pipe(nrf24l01p_t *dev, nrf24l01p_rx_pipe_t pipe); + +/** +* @brief Enable CRC error detection on the nrf24l01+ transceiver. +* +* @param[in] dev Transceiver device to use. +* @param[in] crc Length of cyclic redundancy check (type nrf24l01p_crc_t). +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_enable_crc(nrf24l01p_t *dev, nrf24l01p_crc_t crc); + +/** +* @brief Setup and enable automatic ACK and retransmission on the nrf24l01+ transceiver. +* +* @note +* This function enables automatic acknowledgement for a given RX data pipe and also sets up the +* mautomatic retransmission behavior. +* +* @param[in] dev Transceiver device to use. +* @param[in] pipe RX pipe to setup auto ack. +* @param[in] delay_retrans Automatic retransmission delay + (type nrf24l01p_retransmit_delay_t) +* @param[in] count_retrans Auto retransmit count. +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_setup_auto_ack(nrf24l01p_t *dev, nrf24l01p_rx_pipe_t pipe, nrf24l01p_retransmit_delay_t delay_retrans, char count_retrans); + +/** +* @brief Disable automatic ACK on the nrf24l01+ transceiver. +* +* @param[in] dev Transceiver device to use. +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_disable_all_auto_ack(nrf24l01p_t *dev); + +/** +* @brief Flush TX FIFO on the nrf24l01+ transceiver. +* +* @param[in] dev Transceiver device to use. +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_flush_tx_fifo(nrf24l01p_t *dev); + +/** +* @brief Flush RX FIFO on the nrf24l01+ transceiver. +* +* @param[in] dev Transceiver device to use. +* +* @return 1 on success. +* @return -1 on error. +*/ +int nrf24l01p_flush_rx_fifo(nrf24l01p_t *dev); + +/** +* @brief Callback that is called when interrupt occurs on interrupt +* pin from the nrf24l01+ transceiver. +* +* @param[in] arg Used to pass transceiver device "dev". +*/ +void nrf24l01p_rx_cb(void *arg); + +#ifdef __cplusplus +} +#endif + +#endif /* __NRF24L01P_H */ +/** @} */ diff --git a/drivers/nrf24l01p/Makefile b/drivers/nrf24l01p/Makefile new file mode 100644 index 0000000000..69838c7c2e --- /dev/null +++ b/drivers/nrf24l01p/Makefile @@ -0,0 +1,4 @@ +# Makefile for the NRF24L01+ radio driver +MODULE = nrf24l01p + +include $(RIOTBASE)/Makefile.base diff --git a/drivers/nrf24l01p/include/nrf24l01p_settings.h b/drivers/nrf24l01p/include/nrf24l01p_settings.h new file mode 100644 index 0000000000..5d45767301 --- /dev/null +++ b/drivers/nrf24l01p/include/nrf24l01p_settings.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2014 Hamburg University of Applied Sciences + * + * 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 Low-level driver for nrf24l01+ transceiver + * + * @author Hauke Petersen + * @author Peter Kietzmann + * + * @} + */ + +#ifndef __NRF24L01P_SETTINGS_H +#define __NRF24L01P_SETTINGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define INITIAL_ADDRESS_WIDTH 5 +#define NRF24L01P_MAX_DATA_LENGTH 32 +#define INITIAL_RF_CHANNEL 5 + +#define DELAY_CS_TOGGLE_TICKS 2 +#define DELAY_AFTER_FUNC_TICKS 2 +#define DELAY_CE_HIGH_US HWTIMER_TICKS(20) +#define DELAY_CHANGE_PWR_MODE_US HWTIMER_TICKS(1500) +#define DELAY_CHANGE_TXRX_US HWTIMER_TICKS(130) +#define DELAY_CE_START_US HWTIMER_TICKS(5) +/* + * This is the time which is needed to physically transmit the data. + * Compare nrf24l01+ pruduct specification p.42. It is computed just + * for this setup + */ +#define DELAY_DATA_ON_AIR HWTIMER_TICKS(1300) + + +#define CMD_R_REGISTER 0x00 +#define CMD_W_REGISTER 0x20 +#define CMD_R_RX_PAYLOAD 0x61 +#define CMD_W_TX_PAYLOAD 0xa0 +#define CMD_FLUSH_TX 0xe1 +#define CMD_FLUSH_RX 0xe2 +#define CMD_REUSE_TX_PL 0xe3 +#define CMD_R_RX_PL_WID 0x60 +#define CMD_W_ACK_PAYLOAD 0xa8 +#define CMD_W_TX_PAYLOAD_NOACK 0xb0 +#define CMD_NOP 0xff + +#define REGISTER_MASK 0x1F + + +#define REG_CONFIG 0x00 /* config */ +#define REG_EN_AA 0x01 /* enhanced shockburst */ +#define REG_EN_RXADDR 0x02 +#define REG_SETUP_AW 0x03 +#define REG_SETUP_RETR 0x04 +#define REG_RF_CH 0x05 +#define REG_RF_SETUP 0x06 +#define REG_STATUS 0x07 +#define REG_OBSERVE_TX 0x08 +#define REG_RPD 0x09 +#define REG_RX_ADDR_P0 0x0a +#define REG_RX_ADDR_P1 0x0b +#define REG_RX_ADDR_P2 0x0c +#define REG_RX_ADDR_P3 0x0d +#define REG_RX_ADDR_P4 0x0e +#define REG_RX_ADDR_P5 0x0f +#define REG_TX_ADDR 0x10 +#define REG_RX_PW_P0 0x11 +#define REG_RX_PW_P1 0x12 +#define REG_RX_PW_P2 0x13 +#define REG_RX_PW_P3 0x14 +#define REG_RX_PW_P4 0x15 +#define REG_RX_PW_P5 0x16 +#define REG_FIFO_STATUS 0x17 +#define REG_DYNPD 0x1c +#define REG_FEATURE 0x1d + +/* Bits in CONFIG register */ +#define MASK_RX_DR 0x40 +#define MASK_TX_DS 0x20 +#define MASK_MAX_RT 0x10 +#define EN_CRC 0x08 +#define CRCO 0x04 +#define PWR_UP 0x02 +#define PRIM_RX 0x01 + +/* Bits in STATUS register */ +#define RX_DR 0x40 +#define TX_DS 0x20 +#define MAX_RT 0x10 +#define RX_P_NO 0x0e +#define TX_FULL 0x01 +#define ALL_INT_MASK 0x70 + +#define RF_SETUP_CONT_WAVE (1 << 7) +#define RF_SETUP_RF_DR_LOW (1 << 5) +#define RF_SETUP_PLL_LOCK (1 << 4) +#define RF_SETUP_RF_DR_HIGH (1 << 3) +#define RF_SETUP_RF_PWR (3 << 1) + +#define RF_CH_MASK 0x7f + +#define DYNPD_DPL_P5 (1 << 5) +#define DYNPD_DPL_P4 (1 << 4) +#define DYNPD_DPL_P3 (1 << 3) +#define DYNPD_DPL_P2 (1 << 2) +#define DYNPD_DPL_P1 (1 << 1) +#define DYNPD_DPL_P0 (1 << 0) + +#define FEATURE_EN_DPL (1 << 2) +#define FEATURE_EN_ACK_PAY (1 << 1) +#define FEATURE_EN_DYN_ACK (1 << 0) + +#ifdef __cplusplus +} +#endif + +#endif /* __NRF24L01P_SETTINGS_H */ diff --git a/drivers/nrf24l01p/nrf24l01p.c b/drivers/nrf24l01p/nrf24l01p.c new file mode 100644 index 0000000000..ade1589e51 --- /dev/null +++ b/drivers/nrf24l01p/nrf24l01p.c @@ -0,0 +1,883 @@ +/* + * Copyright (C) 2014 Hamburg University of Applied Sciences + * + * 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 + * @{ + * @author Peter Kietzmann + * @} + */ +#include "nrf24l01p.h" +#include "nrf24l01p_settings.h" +#include "periph/gpio.h" +#include "periph/spi.h" +#include "hwtimer.h" +#include "thread.h" +#include "msg.h" + + +#define ENABLE_DEBUG (1) +#include "debug.h" + + +int nrf24l01p_read_reg(nrf24l01p_t *dev, char reg, char *answer) +{ + int status; + + gpio_clear(dev->cs); + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + status = spi_transfer_reg(dev->spi, (CMD_R_REGISTER | (REGISTER_MASK & reg)), CMD_NOP, answer); + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + gpio_set(dev->cs); + + hwtimer_spin(DELAY_AFTER_FUNC_TICKS); + + return status; +} + +int nrf24l01p_write_reg(nrf24l01p_t *dev, char reg, char write) +{ + int status; + char reg_content; + + gpio_clear(dev->cs); + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + status = spi_transfer_reg(dev->spi, (CMD_W_REGISTER | (REGISTER_MASK & reg)), write, ®_content); + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + gpio_set(dev->cs); + + hwtimer_spin(DELAY_AFTER_FUNC_TICKS); + + return status; +} + + +int nrf24l01p_init(nrf24l01p_t *dev, spi_t spi, gpio_t ce, gpio_t cs, gpio_t irq) +{ + int status; + char INITIAL_TX_ADDRESS[] = {0xe7, 0xe7, 0xe7, 0xe7, 0xe7,}; + char INITIAL_RX_ADDRESS[] = {0xe7, 0xe7, 0xe7, 0xe7, 0xe7,}; + + dev->spi = spi; + dev->ce = ce; + dev->cs = cs; + dev->irq = irq; + dev->listener = KERNEL_PID_UNDEF; + + /* Init CE pin */ + gpio_init_out(dev->ce, GPIO_NOPULL); + + /* Init CS pin */ + gpio_init_out(dev->cs, GPIO_NOPULL); + gpio_set(dev->cs); + + /* Init IRQ pin */ + gpio_init_int(dev->irq, GPIO_PULLUP, GPIO_FALLING, nrf24l01p_rx_cb, dev); + + + /* Init SPI */ + spi_poweron(dev->spi); + status = spi_init_master(dev->spi, SPI_CONF_FIRST_RISING, SPI_SPEED_400KHZ); + + if (status < 0) { + return status; + } + + hwtimer_spin(DELAY_AFTER_FUNC_TICKS); + + /* Flush TX FIFIO */ + status = nrf24l01p_flush_tx_fifo(dev); + + if (status < 0) { + return status; + } + + /* Flush RX FIFIO */ + status = nrf24l01p_flush_tx_fifo(dev); + + if (status < 0) { + return status; + } + + /* Setup adress width */ + status = nrf24l01p_set_address_width(dev, NRF24L01P_AW_5BYTE); + + if (status < 0) { + return status; + } + + /* Setup payload width */ + status = nrf24l01p_set_payload_width(dev, NRF24L01P_PIPE0, NRF24L01P_MAX_DATA_LENGTH); + + if (status < 0) { + return status; + } + + /* Set RF channel */ + status = nrf24l01p_set_channel(dev, INITIAL_RF_CHANNEL); + + if (status < 0) { + return status; + } + + /* Set RF power */ + status = nrf24l01p_set_power(dev, 0); + + if (status < 0) { + return status; + } + + /* Set RF datarate */ + status = nrf24l01p_set_datarate(dev, NRF24L01P_DR_250KBS); + + if (status < 0) { + return status; + } + + /* Set TX Address */ + status = nrf24l01p_set_tx_address(dev, INITIAL_TX_ADDRESS, INITIAL_ADDRESS_WIDTH); + + if (status < 0) { + return status; + } + + /* Set RX Adress */ + status = nrf24l01p_set_rx_address(dev, NRF24L01P_PIPE0, INITIAL_RX_ADDRESS, INITIAL_ADDRESS_WIDTH); + + if (status < 0) { + return status; + } + + /* Reset auto ack for all pipes */ + status = nrf24l01p_disable_all_auto_ack(dev); + + if (status < 0) { + return status; + } + + /* Setup Auto ACK and retransmission */ + status = nrf24l01p_setup_auto_ack(dev, NRF24L01P_PIPE0, NRF24L01P_RETR_750US, 15); + + if (status < 0) { + return status; + } + + /* Setup CRC */ + status = nrf24l01p_enable_crc(dev, NRF24L01P_CRC_2BYTE); + + if (status < 0) { + return status; + } + + /* Reset all interrupt flags */ + status = nrf24l01p_reset_all_interrupts(dev); + + if (status < 0) { + return status; + } + + return nrf24l01p_on(dev); +} + +int nrf24l01p_on(nrf24l01p_t *dev) +{ + char read; + int status; + + gpio_clear(dev->cs); + nrf24l01p_read_reg(dev, REG_CONFIG, &read); + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + status = nrf24l01p_write_reg(dev, REG_CONFIG, (read | PWR_UP)); + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + gpio_set(dev->cs); + + hwtimer_wait(DELAY_CHANGE_PWR_MODE_US); + + return status; +} + +int nrf24l01p_off(nrf24l01p_t *dev) +{ + char read; + int status; + + gpio_clear(dev->cs); + nrf24l01p_read_reg(dev, REG_CONFIG, &read); + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + status = nrf24l01p_write_reg(dev, REG_CONFIG, (read & ~PWR_UP)); + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + gpio_set(dev->cs); + + hwtimer_wait(DELAY_CHANGE_PWR_MODE_US); + + return status; +} + +void nrf24l01p_transmit(nrf24l01p_t *dev) +{ + gpio_set(dev->ce); + hwtimer_wait(DELAY_CE_HIGH_US); /* at least 10 us high */ + gpio_clear(dev->ce); + + hwtimer_spin(DELAY_CHANGE_TXRX_US); +} + +int nrf24l01p_read_payload(nrf24l01p_t *dev, char *answer, unsigned int size) +{ + int status; + + gpio_clear(dev->cs); + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + status = spi_transfer_regs(dev->spi, CMD_R_RX_PAYLOAD, 0, answer, size); + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + gpio_set(dev->cs); + hwtimer_spin(DELAY_AFTER_FUNC_TICKS); + + return status; +} + +void nrf24l01p_register(nrf24l01p_t *dev, unsigned int *pid) +{ + dev->listener = *pid; +} + +int nrf24l01p_unregister(nrf24l01p_t *dev, unsigned int pid) +{ + if (dev != NULL && dev->listener == pid) { + dev->listener = 0; + return 0; + } + else { + return -1; + } +} + +void nrf24l01p_get_id(nrf24l01p_t *dev, unsigned int *pid) +{ + *((int *)pid) = dev->listener; +} + +void nrf24l01p_start(nrf24l01p_t *dev) +{ + gpio_set(dev->ce); + hwtimer_wait(DELAY_CE_START_US); +} + +void nrf24l01p_stop(nrf24l01p_t *dev) +{ + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + gpio_clear(dev->ce); +} + +int nrf24l01p_preload(nrf24l01p_t *dev, char *data, unsigned int size) +{ + int status; + + size = (size <= 32) ? size : 32; + + gpio_clear(dev->cs); + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + status = spi_transfer_regs(dev->spi, CMD_W_TX_PAYLOAD, data, NULL, size); + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + gpio_set(dev->cs); + + hwtimer_spin(DELAY_AFTER_FUNC_TICKS); + + return status; +} + + +int nrf24l01p_set_channel(nrf24l01p_t *dev, uint8_t chan) +{ + if (chan > 125) { + chan = 125; + } + + return nrf24l01p_write_reg(dev, REG_RF_CH, chan); +} + +int nrf24l01p_set_address_width(nrf24l01p_t *dev, nrf24l01p_aw_t aw) +{ + char aw_setup; + nrf24l01p_read_reg(dev, REG_SETUP_AW, &aw_setup); + + hwtimer_spin(DELAY_AFTER_FUNC_TICKS); + + switch (aw) { + case NRF24L01P_AW_3BYTE: + aw_setup &= ~(3); + aw_setup |= 1; + break; + + case NRF24L01P_AW_4BYTE: + aw_setup &= ~(3); + aw_setup |= 2; + break; + + case NRF24L01P_AW_5BYTE: + aw_setup &= ~(3); + aw_setup |= 3; + break; + + default: + return -1; + } + + return nrf24l01p_write_reg(dev, REG_SETUP_AW, aw_setup); +} + +int nrf24l01p_set_payload_width(nrf24l01p_t *dev, nrf24l01p_rx_pipe_t pipe, char width) +{ + char pipe_pw_address; + + switch (pipe) { + case NRF24L01P_PIPE0: + pipe_pw_address = REG_RX_PW_P0; + break; + + case NRF24L01P_PIPE1: + pipe_pw_address = REG_RX_PW_P1; + break; + + case NRF24L01P_PIPE2: + pipe_pw_address = REG_RX_PW_P2; + break; + + case NRF24L01P_PIPE3: + pipe_pw_address = REG_RX_PW_P3; + break; + + case NRF24L01P_PIPE4: + pipe_pw_address = REG_RX_PW_P4; + break; + + case NRF24L01P_PIPE5: + pipe_pw_address = REG_RX_PW_P5; + break; + + default: + return -1; + } + + if (width < 0) { + return -1; + } + + if (width > 32) { + width = 32; + } + + return nrf24l01p_write_reg(dev, pipe_pw_address, width); +} + + + +int nrf24l01p_set_tx_address(nrf24l01p_t *dev, char *saddr, unsigned int length) +{ + int status; + + gpio_clear(dev->cs); + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + status = spi_transfer_regs(dev->spi, (CMD_W_REGISTER | (REGISTER_MASK & REG_TX_ADDR)), saddr, NULL, length); /* address width is 5 byte */ + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + gpio_set(dev->cs); + + hwtimer_spin(DELAY_AFTER_FUNC_TICKS); + + return status; +} + +int nrf24l01p_set_tx_address_long(nrf24l01p_t *dev, uint64_t saddr, unsigned int length) +{ + int status; + + char buf[length]; + + if (length <= INITIAL_ADDRESS_WIDTH) { + for (int i = 0; i < length; i++) { + + buf[i] = (uint8_t)(saddr >> (((length - 1) - i) * sizeof(uint64_t))); + } + } + else { + return -1; + } + + gpio_clear(dev->cs); + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + status = spi_transfer_regs(dev->spi, (CMD_W_REGISTER | (REGISTER_MASK & REG_TX_ADDR)), buf, NULL, length); /* address width is 5 byte */ + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + gpio_set(dev->cs); + + hwtimer_spin(DELAY_AFTER_FUNC_TICKS); + + return status; +} + +uint64_t nrf24l01p_get_tx_address_long(nrf24l01p_t *dev) +{ + int status; + uint64_t saddr_64 = 0; + char addr_array[INITIAL_ADDRESS_WIDTH]; + + gpio_clear(dev->cs); + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + status = spi_transfer_regs(dev->spi, (CMD_R_REGISTER | (REGISTER_MASK & REG_TX_ADDR)), 0, addr_array, INITIAL_ADDRESS_WIDTH); /* address width is 5 byte */ + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + gpio_set(dev->cs); + + hwtimer_spin(DELAY_AFTER_FUNC_TICKS); + + if (status < 0) { + return -1; + } + + hwtimer_spin(DELAY_AFTER_FUNC_TICKS); + + for (int i = 0; i < INITIAL_ADDRESS_WIDTH; i++) { + saddr_64 |= (((uint64_t) addr_array[i]) << (8 * (INITIAL_ADDRESS_WIDTH - i - 1))); + } + + return saddr_64; +} + + +int nrf24l01p_set_rx_address(nrf24l01p_t *dev, nrf24l01p_rx_pipe_t pipe, char *saddr, unsigned int length) +{ + int status; + char pipe_addr; + + switch (pipe) { + case NRF24L01P_PIPE0: + pipe_addr = REG_RX_ADDR_P0; + break; + + case NRF24L01P_PIPE1: + pipe_addr = REG_RX_ADDR_P1; + break; + + case NRF24L01P_PIPE2: + pipe_addr = REG_RX_ADDR_P2; + break; + + case NRF24L01P_PIPE3: + pipe_addr = REG_RX_ADDR_P3; + break; + + case NRF24L01P_PIPE4: + pipe_addr = REG_RX_ADDR_P4; + break; + + case NRF24L01P_PIPE5: + pipe_addr = REG_RX_ADDR_P5; + break; + + default: + return -1; + } + + gpio_clear(dev->cs); + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + status = spi_transfer_regs(dev->spi, (CMD_W_REGISTER | (REGISTER_MASK & pipe_addr)), saddr, NULL, length); /* address width is 5 byte */ + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + gpio_set(dev->cs); + + hwtimer_spin(DELAY_AFTER_FUNC_TICKS); + + /* Enable this pipe */ + nrf24l01p_enable_pipe(dev, pipe); + return status; +} + +int nrf24l01p_set_rx_address_long(nrf24l01p_t *dev, nrf24l01p_rx_pipe_t pipe, uint64_t saddr, unsigned int length) +{ + char buf[length]; + + if (length <= INITIAL_ADDRESS_WIDTH) { + for (int i = 0; i < length; i++) { + + buf[i] = (uint8_t)(saddr >> (((length - 1) - i) * 8)); + } + } + else { + return -1; + } + + return nrf24l01p_set_rx_address(dev, pipe, buf, length); +} + + +uint64_t nrf24l01p_get_rx_address_long(nrf24l01p_t *dev, nrf24l01p_rx_pipe_t pipe) +{ + int status; + char pipe_addr; + uint64_t saddr_64 = 0; + + char addr_array[INITIAL_ADDRESS_WIDTH]; + + switch (pipe) { + case NRF24L01P_PIPE0: + pipe_addr = REG_RX_ADDR_P0; + break; + + case NRF24L01P_PIPE1: + pipe_addr = REG_RX_ADDR_P1; + break; + + case NRF24L01P_PIPE2: + pipe_addr = REG_RX_ADDR_P2; + break; + + case NRF24L01P_PIPE3: + pipe_addr = REG_RX_ADDR_P3; + break; + + case NRF24L01P_PIPE4: + pipe_addr = REG_RX_ADDR_P4; + break; + + case NRF24L01P_PIPE5: + pipe_addr = REG_RX_ADDR_P5; + break; + + default: + return -1; + } + + gpio_clear(dev->cs); + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + status = spi_transfer_regs(dev->spi, (CMD_R_REGISTER | (REGISTER_MASK & pipe_addr)), 0, addr_array, INITIAL_ADDRESS_WIDTH); /* address width is 5 byte */ + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + gpio_set(dev->cs); + + if (status < 0) { + return -1; + } + + hwtimer_spin(DELAY_AFTER_FUNC_TICKS); + + for (int i = 0; i < INITIAL_ADDRESS_WIDTH; i++) { + saddr_64 |= (((uint64_t) addr_array[i]) << (8 * (INITIAL_ADDRESS_WIDTH - i - 1))); + } + + return saddr_64; +} + + +int nrf24l01p_set_datarate(nrf24l01p_t *dev, nrf24l01p_dr_t dr) +{ + char rf_setup; + + nrf24l01p_read_reg(dev, REG_RF_SETUP, &rf_setup); + + switch (dr) { + case NRF24L01P_DR_250KBS: + rf_setup |= RF_SETUP_RF_DR_LOW; + rf_setup &= ~(RF_SETUP_RF_DR_HIGH); + break; + + case NRF24L01P_DR_1MBS: + rf_setup &= ~(RF_SETUP_RF_DR_LOW | RF_SETUP_RF_DR_HIGH); + break; + + case NRF24L01P_DR_2MBS: + rf_setup &= ~RF_SETUP_RF_DR_LOW; + rf_setup |= RF_SETUP_RF_DR_HIGH; + break; + + default: + return -1; + } + + return nrf24l01p_write_reg(dev, REG_RF_SETUP, rf_setup); +} + +int nrf24l01p_get_status(nrf24l01p_t *dev) +{ + char status; + gpio_clear(dev->cs); + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + spi_transfer_byte(dev->spi, CMD_NOP, &status); + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + gpio_set(dev->cs); + + hwtimer_spin(DELAY_AFTER_FUNC_TICKS); + + return (int)status; +} + +int nrf24l01p_set_power(nrf24l01p_t *dev, int *pwr) +{ + char rf_setup; + + nrf24l01p_read_reg(dev, REG_RF_SETUP, &rf_setup); + + if (*pwr >= -3) { + rf_setup &= ~(3 << 1); + rf_setup |= (NRF24L01P_PWR_0DBM << 1); + } + + if (*pwr < -3) { + rf_setup &= ~(3 << 1); + rf_setup |= (NRF24L01P_PWR_N6DBM << 1); + } + + if (*pwr < -9) { + rf_setup &= ~(3 << 1); + rf_setup |= (NRF24L01P_PWR_N12DBM << 1); + } + + if (*pwr < -15) { + rf_setup &= ~(3 << 1); + } + + return nrf24l01p_write_reg(dev, REG_RF_SETUP, rf_setup); +} + +int nrf24l01p_get_power(nrf24l01p_t *dev) +{ + char rf_setup; + int pwr; + + nrf24l01p_read_reg(dev, REG_RF_SETUP, &rf_setup); + + if ((rf_setup & 0x6) == 0) { + pwr = -18; + } + + if ((rf_setup & 0x6) == 2) { + pwr = -12; + } + + if ((rf_setup & 0x6) == 4) { + pwr = -6; + } + + if ((rf_setup & 0x6) == 6) { + pwr = 0; + } + + return pwr; +} + + +int nrf24l01p_set_txmode(nrf24l01p_t *dev) +{ + char conf; + int status; + + nrf24l01p_stop(dev); + + nrf24l01p_mask_interrupt(dev, (MASK_RX_DR | MASK_TX_DS | MASK_MAX_RT)); + + nrf24l01p_flush_tx_fifo(dev); + + nrf24l01p_read_reg(dev, REG_CONFIG, &conf); + conf &= ~(PRIM_RX); + status = nrf24l01p_write_reg(dev, REG_CONFIG, conf); + + hwtimer_wait(DELAY_CHANGE_TXRX_US); + + return status; +} + +int nrf24l01p_set_rxmode(nrf24l01p_t *dev) +{ + char conf; + int status; + + nrf24l01p_unmask_interrupt(dev, MASK_RX_DR); + nrf24l01p_mask_interrupt(dev, (MASK_TX_DS | MASK_MAX_RT)); + + nrf24l01p_flush_rx_fifo(dev); + + nrf24l01p_read_reg(dev, REG_CONFIG, &conf); + conf |= PRIM_RX; + status = nrf24l01p_write_reg(dev, REG_CONFIG, conf); + + nrf24l01p_start(dev); + + hwtimer_wait(DELAY_CHANGE_TXRX_US); + + return status; +} + +int nrf24l01p_reset_all_interrupts(nrf24l01p_t *dev) +{ + return nrf24l01p_write_reg(dev, REG_STATUS, ALL_INT_MASK); +} + +int nrf24l01p_mask_interrupt(nrf24l01p_t *dev, char intr) +{ + char conf; + + nrf24l01p_read_reg(dev, REG_CONFIG, &conf); + conf |= intr; + + return nrf24l01p_write_reg(dev, REG_CONFIG, conf); +} + +int nrf24l01p_unmask_interrupt(nrf24l01p_t *dev, char intr) +{ + char conf; + + nrf24l01p_read_reg(dev, REG_CONFIG, &conf); + conf &= ~intr; + + return nrf24l01p_write_reg(dev, REG_CONFIG, conf); +} + +int nrf24l01p_enable_pipe(nrf24l01p_t *dev, nrf24l01p_rx_pipe_t pipe) +{ + char pipe_conf; + + nrf24l01p_read_reg(dev, REG_EN_RXADDR, &pipe_conf); + pipe_conf |= (1 << pipe); + + return nrf24l01p_write_reg(dev, REG_EN_RXADDR, pipe_conf); +} + +int nrf24l01p_disable_pipe(nrf24l01p_t *dev, nrf24l01p_rx_pipe_t pipe) +{ + char pipe_conf; + + nrf24l01p_read_reg(dev, REG_EN_RXADDR, &pipe_conf); + pipe_conf &= ~(1 << pipe); + + return nrf24l01p_write_reg(dev, REG_EN_RXADDR, pipe_conf); +} + + + +int nrf24l01p_enable_crc(nrf24l01p_t *dev, nrf24l01p_crc_t crc) +{ + char conf; + + nrf24l01p_read_reg(dev, REG_CONFIG, &conf); + + switch (crc) { + case NRF24L01P_CRC_1BYTE: + conf &= ~(CRCO); + break; + + case NRF24L01P_CRC_2BYTE: + conf |= CRCO; + break; + + default: + return -1; + } + + return nrf24l01p_write_reg(dev, REG_CONFIG, (conf | EN_CRC)); +} + +int nrf24l01p_setup_auto_ack(nrf24l01p_t *dev, nrf24l01p_rx_pipe_t pipe, nrf24l01p_retransmit_delay_t delay_retrans, char count_retrans) +{ + char en_aa; + int status; + nrf24l01p_read_reg(dev, REG_EN_AA, &en_aa); + + switch (pipe) { + case NRF24L01P_PIPE0: + en_aa |= (1 << 0); + break; + + case NRF24L01P_PIPE1: + en_aa |= (1 << 1); + break; + + case NRF24L01P_PIPE2: + en_aa |= (1 << 2); + break; + + case NRF24L01P_PIPE3: + en_aa |= (1 << 3); + break; + + case NRF24L01P_PIPE4: + en_aa |= (1 << 4); + break; + + case NRF24L01P_PIPE5: + en_aa |= (1 << 5); + break; + + default: + return -1; + } + + /* Enable Auto Ack */ + status = nrf24l01p_write_reg(dev, REG_EN_AA, en_aa); + + if (status < 0) { + return status; + } + + count_retrans = (count_retrans < 16) ? count_retrans : 15; + + /* setup auto retransmit delay and count */ + return nrf24l01p_write_reg(dev, REG_SETUP_RETR, ((delay_retrans << 4) | count_retrans)); +} + +int nrf24l01p_disable_all_auto_ack(nrf24l01p_t *dev) +{ + return nrf24l01p_write_reg(dev, REG_EN_AA, 0x00); +} + + +int nrf24l01p_flush_tx_fifo(nrf24l01p_t *dev) +{ + int status; + char reg_content; + + gpio_clear(dev->cs); + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + status = spi_transfer_byte(dev->spi, CMD_FLUSH_TX, ®_content); + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + gpio_set(dev->cs); + + hwtimer_spin(DELAY_AFTER_FUNC_TICKS); + + return status; +} + +int nrf24l01p_flush_rx_fifo(nrf24l01p_t *dev) +{ + int status; + char reg_content; + + gpio_clear(dev->cs); + + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + status = spi_transfer_byte(dev->spi, CMD_FLUSH_RX, ®_content); + hwtimer_spin(DELAY_CS_TOGGLE_TICKS); + gpio_set(dev->cs); + + hwtimer_spin(DELAY_AFTER_FUNC_TICKS); + + return status; +} + +void nrf24l01p_rx_cb(void *arg) +{ + DEBUG("In HW cb\n"); + + nrf24l01p_t *dev = (nrf24l01p_t *)arg; + + /* clear interrupt */ + nrf24l01p_reset_all_interrupts(dev); + + /* informs thread about available rx data*/ + if (dev->listener != KERNEL_PID_UNDEF) { + msg_t m; + m.type = RCV_PKT_NRF24L01P; + m.content.ptr = (char *)dev; + /* transmit more things here ? */ + msg_send_int(&m, dev->listener); + } +} diff --git a/tests/driver_nrf24l01p_lowlevel/Makefile b/tests/driver_nrf24l01p_lowlevel/Makefile new file mode 100644 index 0000000000..0fd7fe3d0f --- /dev/null +++ b/tests/driver_nrf24l01p_lowlevel/Makefile @@ -0,0 +1,30 @@ +APPLICATION = driver_nrf24l01p_lowlevel +include ../Makefile.tests_common + +USEMODULE += shell +USEMODULE += shell_commands +USEMODULE += ps +USEMODULE += vtimer +USEMODULE += nrf24l01p + +FEATURES_REQUIRED = periph_spi + +SPI_PORT ?= SPI_0 +CE_PIN ?= GPIO_8 +CS_PIN ?= GPIO_7 +IRQ_PIN ?= GPIO_6 + +include $(RIOTBASE)/Makefile.include + +ifneq (,$(SPI_PORT)) +export CFLAGS += -DSPI_PORT=$(SPI_PORT) +endif +ifneq (,$(CE_PIN)) +export CFLAGS += -DCE_PIN=$(CE_PIN) +endif +ifneq (,$(CS_PIN)) +export CFLAGS += -DCS_PIN=$(CS_PIN) +endif +ifneq (,$(IRQ_PIN)) +export CFLAGS += -DIRQ_PIN=$(IRQ_PIN) +endif diff --git a/tests/driver_nrf24l01p_lowlevel/README.md b/tests/driver_nrf24l01p_lowlevel/README.md new file mode 100644 index 0000000000..c1a31dc9e2 --- /dev/null +++ b/tests/driver_nrf24l01p_lowlevel/README.md @@ -0,0 +1,94 @@ +# Test for nrf24l01p lowlevel functions + +## About +This is a small test application to see how the lowlevel-driver functions of the proprietary nrf24l01p-transceiver work. These functions consist of general SPI and GPIO commands, which abstract the driver-functions from the used board. In order to build this application, you need to add the board to the Makefile's `WHITELIST` first and define a pin mapping. + +## Predefined pin mapping +Please compare the `tests/driver_nrf24l01p_lowlevel/Makefile` for predefined pin-mappings on different boards. (In addition, you also need to connect to 3V and GND) + +## Usage +You should be presented with the RIOT shell, providing you with commands to initialize the transceiver (command: `it`), sending one packet (command: `send`) or read out and print all registers of the transceiver as binary values (command: `prgs`). + +### Procedure +* take two boards and connect a transceiver to each + (it should be also possible to use one board with different SPI-ports) +* depending on your board, you'll maybe also need to connect a UART/tty converter +* build and flash the test-program to each +* open a terminal (e.g. pyterm) for each +* if possible, reset the board by using the reset-button. You'll see "_Welcome to RIOT_" etc. +* type `help` to see the description of the commands +* initialize both with `it` +* with one board, send a packet by typing `send` +* in the next step you can also use `send` to send data in the other direction +* now you can use send on both boards/transceivers to send messages between them + + +## Expected Results +After you did all steps described above, you should see that a 32 Byte sequence (numbers from 32...1) has been transferred from one device to the other. This sequence is printed out from the receiver after the receive interrupt occurred and the receive-procedure has been made. + +After initialization (`it`) you should see the following output: + +``` + > it + +Init Transceiver + +Registering nrf24l01p_rx_handler thread... +################## Print Registers ################### +REG_CONFIG: +0x0 returned: 00111111 + +REG_EN_AA: +0x1 returned: 00000001 + +REG_EN_RXADDR: +0x2 returned: 00000011 + +REG_SETUP_AW: +0x3 returned: 00000011 + +REG_SETUP_RETR: +0x4 returned: 00101111 + +REG_RF_CH: +0x5 returned: 00000101 + +REG_RF_SETUP: +0x6 returned: 00100111 + +REG_STATUS: +0x7 returned: 00001110 + +REG_OBSERVE_TX: +0x8 returned: 00000000 + +REG_RPD: +0x9 returned: 00000000 + +REG_RX_ADDR_P0: +0xa returned: e7 e7 e7 e7 e7 + +REG_TX_ADDR: +0x10 returned: e7 e7 e7 e7 e7 + +REG_RX_PW_P0: +0x11 returned: 00100000 + +REG_FIFO_STATUS: +0x17 returned: 00010001 + +REG_DYNPD: +0x1c returned: 00000000 + +REG_FEATURE: +0x1d returned: 00000000 + +``` + +After the data has been sent (`send`), you should see the following output on the receiver terminal: +``` +In HW cb +nrf24l01p_rx_handler got a message: Received packet. +32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 + +``` \ No newline at end of file diff --git a/tests/driver_nrf24l01p_lowlevel/main.c b/tests/driver_nrf24l01p_lowlevel/main.c new file mode 100644 index 0000000000..54c4ae1461 --- /dev/null +++ b/tests/driver_nrf24l01p_lowlevel/main.c @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2014 Hamburg University of Applied Sciences + * + * 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 nrf24l01p lowlevel functions + * + * @author Peter Kietzmann + * + * @} + */ + +#ifndef SPI_PORT +#error "SPI_PORT not defined" +#endif +#ifndef CE_PIN +#error "CE_PIN not defined" +#endif +#ifndef CS_PIN +#error "CS_PIN not defined" +#endif +#ifndef IRQ_PIN +#error "IRQ_PIN not defined" +#endif + + +#include +#include +#include +#include +#include + +#include "nrf24l01p.h" +#include "nrf24l01p_settings.h" +#include "periph/spi.h" +#include "periph/gpio.h" +#include "vtimer.h" +#include "hwtimer.h" +#include "shell.h" +#include "shell_commands.h" +#include "thread.h" +#include "msg.h" + +#define TEST_RX_MSG 1 + +#define SHELL_BUFFER_SIZE 128 + +static int shell_read(void); +static void shell_write(int); +static void cmd_send(int argc, char **argv); +static void cmd_print_regs(int argc, char **argv); +static void cmd_its(int argc, char **argv); + + +void printbin(unsigned byte); +void print_register(char reg, int num_bytes); + + +static nrf24l01p_t nrf24l01p_0; + +/** + * define some additional shell commands + */ +static const shell_command_t shell_commands[] = { + { "prgs", "print registers", cmd_print_regs }, + { "it", "init transceiver", cmd_its }, + { "send", "send 32 bytes data", cmd_send }, + { NULL, NULL, NULL } +}; + +void prtbin(unsigned byte) +{ + for (char i = 0; i < 8; i++) { + printf("%u", (byte >> (7 - i)) & 0x0001); + } + + puts("\n"); +} + +/** + * @print register + */ +void print_register(char reg, int num_bytes) +{ + + vtimer_init(); + + char buf_return[num_bytes]; + int ret; + + + gpio_clear(CS_PIN); + vtimer_usleep(1); + ret = spi_transfer_regs(SPI_PORT, (CMD_R_REGISTER | (REGISTER_MASK & reg)), 0, buf_return, num_bytes); + gpio_set(CS_PIN); + + if (ret < 0) { + printf("Error in read access\n"); + } + else { + if (num_bytes < 2) { + printf("0x%x returned: ", reg); + + for (int i = 0; i < num_bytes; i++) { + prtbin(buf_return[i]); + } + } + else { + printf("0x%x returned: ", reg); + + for (int i = 0; i < num_bytes; i++) { + printf("%x ", buf_return[i]); + } + + printf("\n\n"); + } + } +} + +char rx_handler_stack[KERNEL_CONF_STACKSIZE_MAIN]; + +/* RX handler that waits for a message from the ISR */ +void *nrf24l01p_rx_handler(void *arg) +{ + msg_t msg_q[1]; + msg_init_queue(msg_q, 1); + unsigned int pid = thread_getpid(); + char rx_buf[NRF24L01P_MAX_DATA_LENGTH]; + + puts("Registering nrf24l01p_rx_handler thread..."); + nrf24l01p_register(&nrf24l01p_0, &pid); + + msg_t m; + + while (msg_receive(&m)) { + printf("nrf24l01p_rx_handler got a message: "); + + switch (m.type) { + case RCV_PKT_NRF24L01P: + puts("Received packet."); + + /* CE low */ + nrf24l01p_stop((nrf24l01p_t *)m.content.ptr); + + /* read payload */ + nrf24l01p_read_payload((nrf24l01p_t *)m.content.ptr, rx_buf, NRF24L01P_MAX_DATA_LENGTH); + + /* flush rx fifo */ + nrf24l01p_flush_rx_fifo((nrf24l01p_t *)m.content.ptr); + + /* CE high */ + nrf24l01p_start((nrf24l01p_t *)m.content.ptr); + + /* print rx buffer */ + for (int i = 0; i < NRF24L01P_MAX_DATA_LENGTH; i++) { + printf("%i ", rx_buf[i]); + } + + puts(""); + + break; + + default: + puts("stray message."); + break; + } + } + + puts("nrf24l01p_rx_handler: this should not have happened!"); + + return NULL; +} + +/** + * @init transceiver + */ +void cmd_its(int argc, char **argv) +{ + (void) argc; + (void) argv; + + puts("Init Transceiver\n"); + + nrf24l01p_init(&nrf24l01p_0, SPI_PORT, CE_PIN, CS_PIN, IRQ_PIN); + + /* create thread that gets msg when data arrives */ + thread_create( + rx_handler_stack, sizeof(rx_handler_stack), PRIORITY_MAIN - 1, 0, + nrf24l01p_rx_handler, 0, "nrf24l01p_rx_handler"); + + /* setup device as receiver */ + nrf24l01p_set_rxmode(&nrf24l01p_0); + + cmd_print_regs(0, 0); +} + +/** + * @set TX mode + */ +void cmd_send(int argc, char **argv) +{ + (void) argc; + (void) argv; + + puts("Send"); + + int status = 0; + char tx_buf[NRF24L01P_MAX_DATA_LENGTH]; + + /* fill TX buffer with numbers 32..1 */ + for (int i = 0; i < sizeof(tx_buf); i++) { + tx_buf[i] = NRF24L01P_MAX_DATA_LENGTH - i; + } + /* power on the device */ + nrf24l01p_on(&nrf24l01p_0); + /* setup device as transmitter */ + nrf24l01p_set_txmode(&nrf24l01p_0); + /* load data to transmit into device */ + nrf24l01p_preload(&nrf24l01p_0, tx_buf, NRF24L01P_MAX_DATA_LENGTH); + /* trigger transmitting */ + nrf24l01p_transmit(&nrf24l01p_0); + /* wait while data is pysically transmitted */ + hwtimer_wait(DELAY_DATA_ON_AIR); + + status = nrf24l01p_get_status(&nrf24l01p_0); + if (status & TX_DS) { + puts("Sent Packet"); + } + /* setup device as receiver */ + nrf24l01p_set_rxmode(&nrf24l01p_0); +} + +/** + * @print registers + */ +void cmd_print_regs(int argc, char **argv) +{ + (void) argc; + (void) argv; + + printf("################## Print Registers ###################\n"); + + + puts("REG_CONFIG: "); + print_register(REG_CONFIG, 1); + + puts("REG_EN_AA: "); + print_register(REG_EN_AA, 1); + + puts("REG_EN_RXADDR: "); + print_register(REG_EN_RXADDR, 1); + + puts("REG_SETUP_AW: "); + print_register(REG_SETUP_AW, 1); + + puts("REG_SETUP_RETR: "); + print_register(REG_SETUP_RETR, 1); + + puts("REG_RF_CH: "); + print_register(REG_RF_CH, 1); + + puts("REG_RF_SETUP: "); + print_register(REG_RF_SETUP, 1); + + puts("REG_STATUS: "); + print_register(REG_STATUS, 1); + + puts("REG_OBSERVE_TX: "); + print_register(REG_OBSERVE_TX, 1); + + puts("REG_RPD: "); + print_register(REG_RPD, 1); + + puts("REG_RX_ADDR_P0: "); + print_register(REG_RX_ADDR_P0, INITIAL_ADDRESS_WIDTH); + + puts("REG_TX_ADDR: "); + print_register(REG_TX_ADDR, INITIAL_ADDRESS_WIDTH); + + puts("REG_RX_PW_P0: "); + print_register(REG_RX_PW_P0, 1); + + puts("REG_FIFO_STATUS: "); + print_register(REG_FIFO_STATUS, 1); + + puts("REG_DYNPD: "); + print_register(REG_DYNPD, 1); + + puts("REG_FEATURE: "); + print_register(REG_FEATURE, 1); +} + + +/** + * @brief proxy for reading a char from std-in and passing it to the shell + */ +int shell_read(void) +{ + return (int) getchar(); +} + +/** + * @brief proxy for taking a character from the shell and writing it to std-out + */ +void shell_write(int c) +{ + putchar((char)c); +} + +int main(void) +{ + shell_t shell; + + puts("Welcome to RIOT!"); + + puts("Initializing shell..."); + shell_init(&shell, shell_commands, SHELL_BUFFER_SIZE, shell_read, + shell_write); + + puts("Starting shell..."); + shell_run(&shell); + return 0; +}