Merge pull request #6797 from aabadie/driver_sx127x

drivers/sx127x: rework of implementations from #6645 and #6002
This commit is contained in:
Martine Lenders 2017-06-30 20:19:19 +02:00 committed by GitHub
commit d61be01596
20 changed files with 5119 additions and 39 deletions

View File

@ -230,6 +230,8 @@ void cc2538_set_state(cc2538_rf_t *dev, netopt_state_t state)
RFCORE_WAIT_UNTIL(RFCORE->XREG_FSMSTAT0bits.FSM_FFCTRL_STATE > FSM_STATE_RX_CALIBRATION);
dev->state = NETOPT_STATE_IDLE;
break;
default:
break;
}
}

View File

@ -201,6 +201,13 @@ ifneq (,$(filter srf08,$(USEMODULE)))
USEMODULE += xtimer
endif
ifneq (,$(filter sx127%,$(USEMODULE)))
FEATURES_REQUIRED += periph_gpio
FEATURES_REQUIRED += periph_spi
USEMODULE += xtimer
USEMODULE += sx127x
endif
ifneq (,$(filter veml6070,$(USEMODULE)))
FEATURES_REQUIRED += periph_i2c
endif

View File

@ -130,3 +130,6 @@ endif
ifneq (,$(filter adcxx1c,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/adcxx1c/include
endif
ifneq (,$(filter sx127%,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/sx127x/include
endif

View File

@ -212,6 +212,7 @@ enum {
NETDEV_TYPE_ETHERNET,
NETDEV_TYPE_IEEE802154,
NETDEV_TYPE_CC110X,
NETDEV_TYPE_LORA,
NETDEV_TYPE_NRFMIN
};
@ -230,6 +231,11 @@ typedef enum {
NETDEV_EVENT_TX_MEDIUM_BUSY, /**< couldn't transfer packet */
NETDEV_EVENT_LINK_UP, /**< link established */
NETDEV_EVENT_LINK_DOWN, /**< link gone */
NETDEV_EVENT_TX_TIMEOUT, /**< timeout when sending */
NETDEV_EVENT_RX_TIMEOUT, /**< timeout when receiving */
NETDEV_EVENT_CRC_ERROR, /**< wrong CRC */
NETDEV_EVENT_FHSS_CHANGE_CHANNEL, /**< channel changed */
NETDEV_EVENT_CAD_DONE, /**< channel activity detection done */
/* expand this list if needed */
} netdev_event_t;

711
drivers/include/sx127x.h Normal file
View File

@ -0,0 +1,711 @@
/*
* Copyright (C) 2016 Unwired Devices <info@unwds.com>
* 2017 Inria
*
* 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_sx127x SX127X
* @ingroup drivers_netdev
* @brief Semtech SX127X driver (SX1272 and SX1276)
*
* This module contains the driver for radio devices of the Semtech SX127x
* series (SX1272 and SX1276).
* Only LoRa long range modem is supported at the moment.
*
* SX127x modules are designed to be used in the ISM RF band. This RF band
* depends on different regional regulatory worldwide.
* Be careful to configure the device to use a RF frequency allowed in your
* region.
* sHere is the list of allowed frequencies for your region (see
* [LoRaWAN regional parameters document available online]
* (https://www.lora-alliance.org/Contact/RequestSpecificationForm.aspx))
* - Europe has 2 allowed bands (ETSI):
* - EU863-870
* - EU433 (from 433.175MHZ to 434.665MHZ exactly)
* - US is US902-928
* - China has 2 allowed bands:
* - CN779-787 (from 779.5MHz to 786.5MHz exactly)
* - CN470-510 (from 470.3MHz to 509.7MHz exactly)
* - Australia is AU915-928
* - South asia, AS923:
* - Bruneï [923-925 MHz]
* - Cambodia [923-925 MHz]
* - Hong Kong [920-925 MHz]
* - Indonesia [923-925 MHz]
* - Japan [920-928 MHz]
* - Laos [923-925 MHz]
* - New Zealand [915-928 MHz]
* - Singapore [920-925 MHz]
* - Taiwan [922-928 MHz]
* - Thailand [920-925 MHz]
* - Vietnam [920-925 MHz]
* - South Korea: KR920-923 (from 920.9MHz to 923.3MHz exactly)
*
* For more information on Semtech SX1272 and SX1276 modules see:
* - [SX1272/73 datasheet](http://www.semtech.com/images/datasheet/sx1272.pdf)
* - [SX1276/77/78/79 datasheet](http://www.semtech.com/images/datasheet/sx1276_77_78_79.pdf)
*
* @{
*
* @file
* @brief Public interface for SX127X driver
* @author Eugene P. <ep@unwds.com>
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
*/
#ifndef SX127X_H
#define SX127X_H
#include "xtimer.h"
#include "net/netdev.h"
#include "periph/gpio.h"
#include "periph/spi.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name SX127X device default configuration
* @{
*/
#define SX127X_MODEM_DEFAULT (SX127X_MODEM_LORA) /**< Use LoRa as default modem */
#define SX127X_CHANNEL_DEFAULT (868300000UL) /**< Default channel frequency, 868.3MHz (Europe) */
#define SX127X_HF_CHANNEL_DEFAULT (868000000UL) /**< Use to calibrate RX chain for LF and HF bands */
#define SX127X_RF_MID_BAND_THRESH (525000000UL) /**< Mid-band threshold */
#define SX127X_FREQUENCY_RESOLUTION (61.03515625) /**< Frequency resolution in Hz */
#define SX127X_XTAL_FREQ (32000000UL) /**< Internal oscillator frequency, 32MHz */
#define SX127X_RADIO_WAKEUP_TIME (1000U) /**< In microseconds [us] */
#define SX127X_PREAMBLE_LENGTH (8U) /**< Preamble length, same for Tx and Rx */
#define SX127X_SYMBOL_TIMEOUT (10U) /**< Symbols timeout (s) */
#define SX127X_BW_DEFAULT (SX127X_BW_125_KHZ) /**< Set default bandwidth to 125kHz */
#define SX127X_SF_DEFAULT (SX127X_SF12) /**< Set default spreading factor to 12 */
#define SX127X_CR_DEFAULT (SX127X_CR_4_8) /**< Set default coding rate to 8 */
#define SX127X_FIX_LENGTH_PAYLOAD_ON (false) /**< Set fixed payload length on */
#define SX127X_IQ_INVERSION (false) /**< Set inverted IQ on */
#define SX127X_FREQUENCY_HOPPING (false) /**< Frequency hopping on */
#define SX127X_FREQUENCY_HOPPING_PERIOD (0U) /**< Frequency hopping period */
#define SX127X_FIXED_HEADER_LEN_MODE (false) /**< Set fixed header length mode (implicit header) */
#define SX127X_PAYLOAD_CRC_ON (true) /**< Enable payload CRC, optional */
#define SX127X_PAYLOAD_LENGTH (0U) /**< Set payload length, unused with implicit header */
#define SX127X_TX_TIMEOUT_DEFAULT (1000U * 1000U * 30UL) /**< TX timeout, 30s */
#define SX127X_RX_SINGLE (false) /**< Single byte receive mode => continuous by default */
#define SX127X_RX_BUFFER_SIZE (256) /**< RX buffer size */
#define SX127X_RADIO_TX_POWER (14U) /**< Radio power in dBm */
#ifndef SX1272_DEFAULT_PASELECT
/** @brief Default PA selection config (1: RFO, 0: PABOOST)
*
* This depends on the module configuration.
*/
#define SX1272_DEFAULT_PASELECT (1U)
#endif
#define SX127X_EVENT_HANDLER_STACK_SIZE (2048U) /**< Stack size event handler */
#define SX127X_IRQ_DIO0 (1<<0) /**< DIO0 IRQ */
#define SX127X_IRQ_DIO1 (1<<1) /**< DIO1 IRQ */
#define SX127X_IRQ_DIO2 (1<<2) /**< DIO2 IRQ */
#define SX127X_IRQ_DIO3 (1<<3) /**< DIO3 IRQ */
#define SX127X_IRQ_DIO4 (1<<4) /**< DIO4 IRQ */
#define SX127X_IRQ_DIO5 (1<<5) /**< DIO5 IRQ */
/** @} */
/**
* @brief SX127X initialization result.
*/
enum {
SX127X_INIT_OK = 0, /**< Initialization was successful */
SX127X_ERR_SPI, /**< Failed to initialize SPI bus or CS line */
SX127X_ERR_TEST_FAILED, /**< SX127X testing failed during initialization (check chip) */
SX127X_ERR_THREAD /**< Unable to create DIO handling thread (check amount of free memory) */
};
/**
* @brief Radio driver supported modems.
*/
enum {
SX127X_MODEM_FSK = 0, /**< FSK modem driver */
SX127X_MODEM_LORA, /**< LoRa modem driver */
};
/**
* @brief LoRa signal bandwidth.
*/
enum {
SX127X_BW_125_KHZ = 0, /**< 125 kHz bandwidth */
SX127X_BW_250_KHZ, /**< 250 kHz bandwidth */
SX127X_BW_500_KHZ /**< 500 kHz bandwidth */
};
/**
* @brief LoRa spreading factor rate
*/
enum {
SX127X_SF6 = 6, /**< spreading factor 6 */
SX127X_SF7, /**< spreading factor 7 */
SX127X_SF8, /**< spreading factor 8 */
SX127X_SF9, /**< spreading factor 9 */
SX127X_SF10, /**< spreading factor 10 */
SX127X_SF11, /**< spreading factor 11 */
SX127X_SF12 /**< spreading factor 12 */
};
/**
* @brief LoRa error coding rate.
*/
enum {
SX127X_CR_4_5 = 1, /**< coding rate 4/5 */
SX127X_CR_4_6, /**< coding rate 4/6 */
SX127X_CR_4_7, /**< coding rate 4/7 */
SX127X_CR_4_8 /**< coding rate 4/8 */
};
/**
* @brief Radio driver internal state machine states definition.
*/
enum {
SX127X_RF_IDLE = 0, /**< Idle state */
SX127X_RF_RX_RUNNING, /**< Sending state */
SX127X_RF_TX_RUNNING, /**< Receiving state */
SX127X_RF_CAD, /**< Channel activity detection state */
};
/**
* @brief Event types.
*/
enum {
SX127X_RX_DONE = 0, /**< Receiving complete */
SX127X_TX_DONE, /**< Sending complete*/
SX127X_RX_TIMEOUT, /**< Receiving timeout */
SX127X_TX_TIMEOUT, /**< Sending timeout */
SX127X_RX_ERROR_CRC, /**< Receiving CRC error */
SX127X_FHSS_CHANGE_CHANNEL, /**< Channel change */
SX127X_CAD_DONE, /**< Channel activity detection complete */
};
/**
* @name SX127X device descriptor boolean flags
* @{
*/
#define SX127X_LOW_DATARATE_OPTIMIZE_FLAG (1 << 0)
#define SX127X_ENABLE_FIXED_HEADER_LENGTH_FLAG (1 << 1)
#define SX127X_ENABLE_CRC_FLAG (1 << 2)
#define SX127X_CHANNEL_HOPPING_FLAG (1 << 3)
#define SX127X_IQ_INVERTED_FLAG (1 << 4)
#define SX127X_RX_CONTINUOUS_FLAG (1 << 5)
/** @} */
/**
* @brief LoRa configuration structure.
*/
typedef struct {
uint16_t preamble_len; /**< Length of preamble header */
uint8_t power; /**< Signal power */
uint8_t bandwidth; /**< Signal bandwidth */
uint8_t datarate; /**< Spreading factor rate, e.g datarate */
uint8_t coderate; /**< Error coding rate */
uint8_t freq_hop_period; /**< Frequency hop period */
uint8_t flags; /**< Boolean flags */
uint32_t rx_timeout; /**< RX timeout in symbols */
uint32_t tx_timeout; /**< TX timeout in symbols */
} sx127x_lora_settings_t;
/**
* @brief Radio settings.
*/
typedef struct {
uint32_t channel; /**< Radio channel */
uint32_t window_timeout; /**< Timeout window */
uint8_t state; /**< Radio state */
uint8_t modem; /**< Driver model (FSK or LoRa) */
sx127x_lora_settings_t lora; /**< LoRa settings */
} sx127x_radio_settings_t;
/**
* @brief SX127X internal data.
*/
typedef struct {
/* Data that will be passed to events handler in application */
xtimer_t tx_timeout_timer; /**< TX operation timeout timer */
xtimer_t rx_timeout_timer; /**< RX operation timeout timer */
uint32_t last_channel; /**< Last channel in frequency hopping sequence */
bool is_last_cad_success; /**< Sign of success of last CAD operation (activity detected) */
} sx127x_internal_t;
/**
* @brief SX127X hardware and global parameters.
*/
typedef struct {
spi_t spi; /**< SPI device */
gpio_t nss_pin; /**< SPI NSS pin */
gpio_t reset_pin; /**< Reset pin */
gpio_t dio0_pin; /**< Interrupt line DIO0 (Tx done) */
gpio_t dio1_pin; /**< Interrupt line DIO1 (Rx timeout) */
gpio_t dio2_pin; /**< Interrupt line DIO2 (FHSS channel change) */
gpio_t dio3_pin; /**< Interrupt line DIO3 (CAD done) */
gpio_t dio4_pin; /**< Interrupt line DIO4 (not used) */
gpio_t dio5_pin; /**< Interrupt line DIO5 (not used) */
} sx127x_params_t;
/**
* @brief SX127X IRQ flags.
*/
typedef uint8_t sx127x_flags_t;
/**
* @brief SX127X device.
* @extends netdev_t
*/
typedef struct sx127x_s {
netdev_t netdev; /**< Netdev parent struct */
sx127x_radio_settings_t settings; /**< Radio settings */
sx127x_params_t params; /**< Device driver parameters */
sx127x_internal_t _internal; /**< Internal sx127x data used within the driver */
sx127x_flags_t irq; /**< Device IRQ flags */
} sx127x_t;
/**
* @brief Hardware IO IRQ callback function definition.
*/
typedef void (sx127x_dio_irq_handler_t)(sx127x_t *dev);
/**
* @brief Setup the SX127X
*
* @param[in] dev Device descriptor
* @param[in] params Parameters for device initialization
*/
void sx127x_setup(sx127x_t *dev, const sx127x_params_t *params);
/**
* @brief Resets the SX127X
*
* @param[in] dev The sx127x device descriptor
*/
void sx127x_reset(const sx127x_t *dev);
/**
* @brief Initializes the transceiver.
*
* @param[in] dev The sx127x device descriptor
*
* @return result of initialization
*/
int sx127x_init(sx127x_t *dev);
/**
* @brief Initialize radio settings with default values
*
* @param[in] dev The sx127x device pointer
*/
void sx127x_init_radio_settings(sx127x_t *dev);
/**
* @brief Generates 32 bits random value based on the RSSI readings
*
* @attention This function sets the radio in LoRa mode and disables all
* interrupts from it. After calling this function either
* sx127x_set_rx_config or sx127x_set_tx_config functions must
* be called.
*
* @param[in] dev The sx127x device structure pointer
*
* @return random 32 bits value
*/
uint32_t sx127x_random(sx127x_t *dev);
/**
* @brief sx127x DIO0 IRQ handler.
*
* @param[in] arg An sx127x device instance
*/
void sx127x_on_dio0(void *arg);
/**
* @brief sx127x DIO1 IRQ handler.
*
* @param[in] arg An sx127x device instance
*/
void sx127x_on_dio1(void *arg);
/**
* @brief sx127x DIO2 IRQ handler.
*
* @param[in] arg An sx127x device instance
*/
void sx127x_on_dio2(void *arg);
/**
* @brief sx127x DIO3 IRQ handler.
*
* @param[in] arg An sx127x device instance
*/
void sx127x_on_dio3(void *arg);
/**
* @brief Start a channel activity detection.
*
* @param[in] dev The sx127x device descriptor
*/
void sx127x_start_cad(sx127x_t *dev);
/**
* @brief Gets current state of transceiver.
*
* @param[in] dev The sx127x device descriptor
*
* @return radio state [RF_IDLE, RF_RX_RUNNING, RF_TX_RUNNING]
*/
uint8_t sx127x_get_state(const sx127x_t *dev);
/**
* @brief Sets current state of transceiver.
*
* @param[in] dev The sx127x device descriptor
* @param[in] state The new radio state
*
* @return radio state [RF_IDLE, RF_RX_RUNNING, RF_TX_RUNNING]
*/
void sx127x_set_state(sx127x_t *dev, uint8_t state);
/**
* @brief Configures the radio with the given modem.
*
* @param[in] dev The sx127x device descriptor
* @param[in] modem Modem to be used [0: FSK, 1: LoRa]
*/
void sx127x_set_modem(sx127x_t *dev, uint8_t modem);
/**
* @brief Gets the synchronization word.
*
* @param[in] dev The sx127x device descriptor
*
* @return The synchronization word
*/
uint8_t sx127x_get_syncword(const sx127x_t *dev);
/**
* @brief Sets the synchronization word.
*
* @param[in] dev The sx127x device descriptor
* @param[in] syncword The synchronization word
*/
void sx127x_set_syncword(sx127x_t *dev, uint8_t syncword);
/**
* @brief Gets the channel RF frequency.
*
* @param[in] dev The sx127x device descriptor
*
* @return The channel frequency
*/
uint32_t sx127x_get_channel(const sx127x_t *dev);
/**
* @brief Sets the channel RF frequency.
*
* @param[in] dev The sx127x device descriptor
* @param[in] freq Channel RF frequency
*/
void sx127x_set_channel(sx127x_t *dev, uint32_t freq);
/**
* @brief Computes the packet time on air in milliseconds.
*
* @pre Can only be called once sx127x_init_radio_settings have already
* been called.
*
* @param[in] dev The sx127x device descriptor
* @param[in] pkt_len The received packet payload length
*
* @return computed air time (ms) for the given packet payload length
*/
uint32_t sx127x_get_time_on_air(const sx127x_t *dev, uint8_t pkt_len);
/**
* @brief Sets the radio in sleep mode
*
* @param[in] dev The sx127x device descriptor
*/
void sx127x_set_sleep(sx127x_t *dev);
/**
* @brief Sets the radio in stand-by mode
*
* @param[in] dev The sx127x device descriptor
*/
void sx127x_set_standby(sx127x_t *dev);
/**
* @brief Sets the radio in reception mode.
*
* @param[in] dev The sx127x device descriptor
*/
void sx127x_set_rx(sx127x_t *dev);
/**
* @brief Sets the radio in transmission mode.
*
* @param[in] dev The sx127x device descriptor
*/
void sx127x_set_tx(sx127x_t *dev);
/**
* @brief Gets the maximum payload length.
*
* @param[in] dev The sx127x device descriptor
*
* @return The maximum payload length
*/
uint8_t sx127x_get_max_payload_len(const sx127x_t *dev);
/**
* @brief Sets the maximum payload length.
*
* @param[in] dev The sx127x device descriptor
* @param[in] maxlen Maximum payload length in bytes
*/
void sx127x_set_max_payload_len(const sx127x_t *dev, uint8_t maxlen);
/**
* @brief Gets the SX127X operating mode
*
* @param[in] dev The sx127x device descriptor
*
* @return The actual operating mode
*/
uint8_t sx127x_get_op_mode(const sx127x_t *dev);
/**
* @brief Sets the SX127X operating mode
*
* @param[in] dev The sx127x device descriptor
* @param[in] op_mode The new operating mode
*/
void sx127x_set_op_mode(const sx127x_t *dev, uint8_t op_mode);
/**
* @brief Gets the SX127X bandwidth
*
* @param[in] dev The sx127x device descriptor
*
* @return the bandwidth
*/
uint8_t sx127x_get_bandwidth(const sx127x_t *dev);
/**
* @brief Sets the SX127X bandwidth
*
* @param[in] dev The sx127x device descriptor
* @param[in] bandwidth The new bandwidth
*/
void sx127x_set_bandwidth(sx127x_t *dev, uint8_t bandwidth);
/**
* @brief Gets the SX127X LoRa spreading factor
*
* @param[in] dev The sx127x device descriptor
*
* @return the spreading factor
*/
uint8_t sx127x_get_spreading_factor(const sx127x_t *dev);
/**
* @brief Sets the SX127X LoRa spreading factor
*
* @param[in] dev The sx127x device descriptor
* @param[in] sf The spreading factor
*/
void sx127x_set_spreading_factor(sx127x_t *dev, uint8_t sf);
/**
* @brief Gets the SX127X LoRa coding rate
*
* @param[in] dev The sx127x device descriptor
*
* @return the current LoRa coding rate
*/
uint8_t sx127x_get_coding_rate(const sx127x_t *dev);
/**
* @brief Sets the SX127X LoRa coding rate
*
* @param[in] dev The sx127x device descriptor
* @param[in] coderate The LoRa coding rate
*/
void sx127x_set_coding_rate(sx127x_t *dev, uint8_t coderate);
/**
* @brief Checks if the SX127X LoRa RX single mode is enabled/disabled
*
* @param[in] dev The sx127x device descriptor
*
* @return the LoRa single mode
*/
bool sx127x_get_rx_single(const sx127x_t *dev);
/**
* @brief Enable/disable the SX127X LoRa RX single mode
*
* @param[in] dev The sx127x device descriptor
* @param[in] single The LoRa RX single mode
*/
void sx127x_set_rx_single(sx127x_t *dev, bool single);
/**
* @brief Checks if the SX127X CRC verification mode is enabled
*
* @param[in] dev The sx127x device descriptor
*
* @return the LoRa single mode
*/
bool sx127x_get_crc(const sx127x_t *dev);
/**
* @brief Enable/Disable the SX127X CRC verification mode
*
* @param[in] dev The sx127x device descriptor
* @param[in] crc The CRC check mode
*/
void sx127x_set_crc(sx127x_t *dev, bool crc);
/**
* @brief Gets the SX127X frequency hopping period
*
* @param[in] dev The sx127x device descriptor
*
* @return the frequency hopping period
*/
uint8_t sx127x_get_hop_period(const sx127x_t *dev);
/**
* @brief Sets the SX127X frequency hopping period
*
* @param[in] dev The sx127x device descriptor
* @param[in] hop_period The frequency hopping period
*/
void sx127x_set_hop_period(sx127x_t *dev, uint8_t hop_period);
/**
* @brief Gets the SX127X LoRa fixed header length mode
*
* @param[in] dev The sx127x device descriptor
*
* @return the LoRa implicit mode
*/
bool sx127x_get_fixed_header_len_mode(const sx127x_t *dev);
/**
* @brief Sets the SX127X to fixed header length mode (explicit mode)
*
* @param[in] dev The sx127x device descriptor
* @param[in] mode The header mode
*/
void sx127x_set_fixed_header_len_mode(sx127x_t *dev, bool mode);
/**
* @brief Gets the SX127X payload length
*
* @param[in] dev The sx127x device descriptor
*
* @return the payload length
*/
uint8_t sx127x_get_payload_length(const sx127x_t *dev);
/**
* @brief Sets the SX127X payload length
*
* @param[in] dev The sx127x device descriptor
* @param[in] len The payload len
*/
void sx127x_set_payload_length(sx127x_t *dev, uint8_t len);
/**
* @brief Gets the SX127X TX radio power
*
* @param[in] dev The sx127x device descriptor
*
* @return the radio power
*/
uint8_t sx127x_get_tx_power(const sx127x_t *dev);
/**
* @brief Sets the SX127X transmission power
*
* @param[in] dev The sx127x device descriptor
* @param[in] power The TX power
*/
void sx127x_set_tx_power(sx127x_t *dev, uint8_t power);
/**
* @brief Gets the SX127X preamble length
*
* @param[in] dev The sx127x device descriptor
*
* @return the preamble length
*/
uint16_t sx127x_get_preamble_length(const sx127x_t *dev);
/**
* @brief Sets the SX127X LoRa preamble length
*
* @param[in] dev The sx127x device descriptor
* @param[in] preamble The LoRa preamble length
*/
void sx127x_set_preamble_length(sx127x_t *dev, uint16_t preamble);
/**
* @brief Sets the SX127X LoRa symbol timeout
*
* @param[in] dev The sx127x device descriptor
* @param[in] timeout The LoRa symbol timeout
*/
void sx127x_set_symbol_timeout(sx127x_t *dev, uint16_t timeout);
/**
* @brief Sets the SX127X RX timeout
*
* @param[in] dev The sx127x device descriptor
* @param[in] timeout The RX timeout
*/
void sx127x_set_rx_timeout(sx127x_t *dev, uint32_t timeout);
/**
* @brief Sets the SX127X TX timeout
*
* @param[in] dev The sx127x device descriptor
* @param[in] timeout The TX timeout
*/
void sx127x_set_tx_timeout(sx127x_t *dev, uint32_t timeout);
/**
* @brief Sets the SX127X LoRa IQ inverted mode
*
* @param[in] dev The sx127x device descriptor
* @param[in] iq_invert The LoRa IQ inverted mode
*/
void sx127x_set_iq_invert(sx127x_t *dev, bool iq_invert);
/**
* @brief Sets the SX127X LoRa frequency hopping mode
*
* @param[in] dev The sx127x device descriptor
* @param[in] freq_hop_on The LoRa frequency hopping mode
*/
void sx127x_set_freq_hop(sx127x_t *dev, bool freq_hop_on);
#ifdef __cplusplus
}
#endif
#endif /* SX127X_H */
/** @} */

1
drivers/sx127x/Makefile Normal file
View File

@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,134 @@
/*
* Copyright (C) 2016 Unwired Devices <info@unwds.com>
* 2017 Inria Chile
* 2017 Inria
*
* 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_sx127x
* @{
* @file
* @brief Semtech SX127X internal functions
*
* @author Eugene P. <ep@unwds.com>
* @author José Ignacio Alamos <jose.alamos@inria.cl>
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
*/
#ifndef SX127X_INTERNAL_H
#define SX127X_INTERNAL_H
#include <inttypes.h>
#include "sx127x.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name Constant values used to compute RSSI
* @{
*/
#if defined(MODULE_SX1272)
#define SX127X_RSSI_OFFSET (-139)
#else /* MODULE_SX1276 */
#define SX127X_RSSI_OFFSET_LF (-164)
#define SX127X_RSSI_OFFSET_HF (-157)
#endif
/** @} */
/**
* @brief Tests the transceiver version type.
*
* @param[in] dev The sx127x device descriptor
* @return true if test passed, false otherwise
*/
bool sx127x_test(const sx127x_t *dev);
/**
* @brief Writes the radio register at specified address.
*
* @param[in] dev The sx127x device structure pointer
* @param[in] addr Register address
* @param[in] data New register value
*/
void sx127x_reg_write(const sx127x_t *dev, uint8_t addr, uint8_t data);
/**
* @brief Reads the radio register at specified address.
*
* @param[in] dev The sx127x device structure pointer
* @param[in] addr Register address
*
* @return Register value
*/
uint8_t sx127x_reg_read(const sx127x_t *dev, uint8_t addr);
/**
* @brief Writes multiple radio registers starting at address (burst-mode).
*
* @param[in] dev The sx127x device structure pointer
* @param[in] addr First radio register address
* @param[in] buffer Buffer containing the new register's values
* @param[in] size Number of registers to be written
*/
void sx127x_reg_write_burst(const sx127x_t *dev, uint8_t addr, uint8_t *buffer,
uint8_t size);
/**
* @brief Reads multiple radio registers starting at address.
*
* @param[in] dev The sx127x device structure pointer
* @param[in] addr First radio register address
* @param[in] size Number of registers to be read
* @param[out] buffer Buffer where to copy registers data
*/
void sx127x_reg_read_burst(const sx127x_t *dev, uint8_t addr, uint8_t *buffer,
uint8_t size);
/**
* @brief Writes the buffer contents to the SX1276 FIFO
*
* @param[in] dev The sx127x device structure pointer
* @param[in] buffer Buffer Buffer containing data to be put on the FIFO.
* @param[in] size Size Number of bytes to be written to the FIFO
*/
void sx127x_write_fifo(const sx127x_t *dev, uint8_t *buffer, uint8_t size);
/**
* @brief Reads the contents of the SX1276 FIFO
*
* @param[in] dev The sx127x device structure pointer
* @param[in] size Size Number of bytes to be read from the FIFO
* @param[out] buffer Buffer Buffer where to copy the FIFO read data.
*/
void sx127x_read_fifo(const sx127x_t *dev, uint8_t *buffer, uint8_t size);
/**
* @brief Performs the Rx chain calibration for LF and HF bands
*
* Must be called just after the reset so all registers are at their
* default values
*
* @param[in] dev The sx127x device structure pointer
*/
void sx127x_rx_chain_calibration(sx127x_t *dev);
/**
* @brief Reads the current RSSI value.
*
* @param[in] dev The sx127x device descriptor
*
* @return current value of RSSI in [dBm]
*/
int16_t sx127x_read_rssi(const sx127x_t *dev);
#ifdef __cplusplus
}
#endif
#endif /* SX127X_INTERNAL_H */

View File

@ -0,0 +1,48 @@
/*
* Copyright (C) 2017 Inria
* 2017 Inria Chile
*
* 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_sx127x
* @{
* @file
* @brief Netdev driver definitions for SX127X driver
*
* @author José Ignacio Alamos <jose.alamos@inria.cl>
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
*/
#ifndef SX127X_NETDEV_H
#define SX127X_NETDEV_H
#include "net/netdev.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Reference to the netdev device driver struct
*/
extern const netdev_driver_t sx127x_driver;
/**
* @brief Received LoRa packet status information
*/
typedef struct netdev_radio_lora_packet_info {
uint8_t rssi; /**< RSSI of a received packet */
uint8_t lqi; /**< LQI of a received packet */
int8_t snr; /**< S/N ratio */
uint32_t time_on_air; /**< Time on air of a received packet (ms) */
} netdev_sx127x_lora_packet_info_t;
#ifdef __cplusplus
}
#endif
#endif /* SX127X_NETDEV_H */

View File

@ -0,0 +1,97 @@
/*
* Copyright (C) 2017 Inria
* 2017 Inria Chile
*
* 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_sx127x
* @{
* @file
* @brief Default configuration for SX127X driver
*
* @author José Ignacio Alamos <jose.alamos@inria.cl>
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
*/
#ifndef SX127X_PARAMS_H
#define SX127X_PARAMS_H
#include "board.h"
#include "sx127x.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name Set default configuration parameters for the SX127X driver
* Pins are adapted to ST Nucleo boards.
* @{
*/
#ifndef SX127X_PARAM_SPI
#define SX127X_PARAM_SPI (SPI_DEV(0))
#endif
#ifndef SX127X_PARAM_SPI_SPEED
#define SX127X_PARAM_SPI_SPEED (SPI_CLK_1MHZ)
#endif
#ifndef SX127X_PARAM_SPI_MODE
#define SX127X_PARAM_SPI_MODE (SPI_MODE_0)
#endif
#ifndef SX127X_PARAM_SPI_NSS
#define SX127X_PARAM_SPI_NSS GPIO_PIN(1, 6) /* D10 */
#endif
#ifndef SX127X_PARAM_RESET
#define SX127X_PARAM_RESET GPIO_PIN(0, 0) /* A0 */
#endif
#ifndef SX127X_PARAM_DIO0
#define SX127X_PARAM_DIO0 GPIO_PIN(0, 10) /* D2 */
#endif
#ifndef SX127X_PARAM_DIO1
#define SX127X_PARAM_DIO1 GPIO_PIN(1, 3) /* D3 */
#endif
#ifndef SX127X_PARAM_DIO2
#define SX127X_PARAM_DIO2 GPIO_PIN(1, 5) /* D4 */
#endif
#ifndef SX127X_PARAM_DIO3
#define SX127X_PARAM_DIO3 GPIO_PIN(1, 4) /* D5 */
#endif
#define SX127X_PARAMS_DEFAULT { .spi = SX127X_PARAM_SPI, \
.nss_pin = SX127X_PARAM_SPI_NSS, \
.reset_pin = SX127X_PARAM_RESET, \
.dio0_pin = SX127X_PARAM_DIO0, \
.dio1_pin = SX127X_PARAM_DIO1, \
.dio2_pin = SX127X_PARAM_DIO2, \
.dio3_pin = SX127X_PARAM_DIO3 }
/**@}*/
/**
* @brief SX127X configuration
*/
static const sx127x_params_t sx127x_params[] =
{
#ifdef SX127X_PARAMS_BOARD
SX127X_PARAMS_BOARD,
#else
SX127X_PARAMS_DEFAULT,
#endif
};
#ifdef __cplusplus
}
#endif
#endif /* SX127X_PARAMS_H */
/** @} */

File diff suppressed because it is too large Load Diff

422
drivers/sx127x/sx127x.c Normal file
View File

@ -0,0 +1,422 @@
/*
* Copyright (C) 2016 Unwired Devices <info@unwds.com>
* 2017 Inria Chile
* 2017 Inria
*
* 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_sx127x
* @{
* @file
* @brief Basic functionality of sx127x driver
*
* @author Eugene P. <ep@unwds.com>
* @author José Ignacio Alamos <jose.alamos@inria.cl>
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
* @}
*/
#include <stdbool.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "xtimer.h"
#include "thread.h"
#include "periph/gpio.h"
#include "periph/spi.h"
#include "sx127x.h"
#include "sx127x_internal.h"
#include "sx127x_registers.h"
#include "sx127x_netdev.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
/* Internal functions */
static void _init_isrs(sx127x_t *dev);
static void _init_timers(sx127x_t *dev);
static int _init_peripherals(sx127x_t *dev);
static void _on_tx_timeout(void *arg);
static void _on_rx_timeout(void *arg);
/* SX127X DIO interrupt handlers initialization */
static void sx127x_on_dio0_isr(void *arg);
static void sx127x_on_dio1_isr(void *arg);
static void sx127x_on_dio2_isr(void *arg);
static void sx127x_on_dio3_isr(void *arg);
void sx127x_setup(sx127x_t *dev, const sx127x_params_t *params)
{
netdev_t *netdev = (netdev_t*) dev;
netdev->driver = &sx127x_driver;
memcpy(&dev->params, params, sizeof(sx127x_params_t));
}
void sx127x_reset(const sx127x_t *dev)
{
/*
* This reset scheme complies with 7.2 chapter of the SX1272/1276 datasheet
* See http://www.semtech.com/images/datasheet/sx1276.pdf for SX1276
* See http://www.semtech.com/images/datasheet/sx1272.pdf for SX1272
*
* 1. Set NReset pin to LOW for at least 100 us
* 2. Set NReset in Hi-Z state
* 3. Wait at least 5 milliseconds
*/
gpio_init(dev->params.reset_pin, GPIO_OUT);
/* Set reset pin to 0 */
gpio_clear(dev->params.reset_pin);
/* Wait 1 ms */
xtimer_usleep(1000);
/* Put reset pin in High-Z */
gpio_init(dev->params.reset_pin, GPIO_IN);
/* Wait 10 ms */
xtimer_usleep(1000 * 10);
}
int sx127x_init(sx127x_t *dev)
{
/* Do internal initialization routines */
if (!_init_peripherals(dev)) {
return -SX127X_ERR_SPI;
}
/* Check presence of SX127X */
if (!sx127x_test(dev)) {
DEBUG("[Error] init : sx127x test failed\n");
return -SX127X_ERR_TEST_FAILED;
}
_init_timers(dev);
xtimer_usleep(1000); /* wait 1 millisecond */
sx127x_reset(dev);
sx127x_rx_chain_calibration(dev);
sx127x_set_op_mode(dev, SX127X_RF_OPMODE_SLEEP);
_init_isrs(dev);
return SX127X_INIT_OK;
}
void sx127x_init_radio_settings(sx127x_t *dev)
{
sx127x_set_freq_hop(dev, SX127X_FREQUENCY_HOPPING);
sx127x_set_iq_invert(dev, SX127X_IQ_INVERSION);
sx127x_set_rx_single(dev, SX127X_RX_SINGLE);
sx127x_set_tx_timeout(dev, SX127X_TX_TIMEOUT_DEFAULT);
sx127x_set_modem(dev, SX127X_MODEM_DEFAULT);
sx127x_set_channel(dev, SX127X_CHANNEL_DEFAULT);
sx127x_set_bandwidth(dev, SX127X_BW_DEFAULT);
sx127x_set_spreading_factor(dev, SX127X_SF_DEFAULT);
sx127x_set_coding_rate(dev, SX127X_CR_DEFAULT);
sx127x_set_fixed_header_len_mode(dev, SX127X_FIXED_HEADER_LEN_MODE);
sx127x_set_crc(dev, SX127X_PAYLOAD_CRC_ON);
sx127x_set_symbol_timeout(dev, SX127X_SYMBOL_TIMEOUT);
sx127x_set_preamble_length(dev, SX127X_PREAMBLE_LENGTH);
sx127x_set_payload_length(dev, SX127X_PAYLOAD_LENGTH);
sx127x_set_hop_period(dev, SX127X_FREQUENCY_HOPPING_PERIOD);
sx127x_set_tx_power(dev, SX127X_RADIO_TX_POWER);
}
uint32_t sx127x_random(sx127x_t *dev)
{
uint32_t rnd = 0;
sx127x_set_modem(dev, SX127X_MODEM_LORA); /* Set LoRa modem ON */
/* Disable LoRa modem interrupts */
sx127x_reg_write(dev, SX127X_REG_LR_IRQFLAGSMASK, SX127X_RF_LORA_IRQFLAGS_RXTIMEOUT |
SX127X_RF_LORA_IRQFLAGS_RXDONE |
SX127X_RF_LORA_IRQFLAGS_PAYLOADCRCERROR |
SX127X_RF_LORA_IRQFLAGS_VALIDHEADER |
SX127X_RF_LORA_IRQFLAGS_TXDONE |
SX127X_RF_LORA_IRQFLAGS_CADDONE |
SX127X_RF_LORA_IRQFLAGS_FHSSCHANGEDCHANNEL |
SX127X_RF_LORA_IRQFLAGS_CADDETECTED);
/* Set radio in continuous reception */
sx127x_set_op_mode(dev, SX127X_RF_OPMODE_RECEIVER);
for (unsigned i = 0; i < 32; i++) {
xtimer_usleep(1000); /* wait for the chaos */
/* Non-filtered RSSI value reading. Only takes the LSB value */
rnd |= ((uint32_t) sx127x_reg_read(dev, SX127X_REG_LR_RSSIWIDEBAND) & 0x01) << i;
}
sx127x_set_sleep(dev);
return rnd;
}
/**
* IRQ handlers
*/
void sx127x_isr(netdev_t *dev)
{
if (dev->event_callback) {
dev->event_callback(dev, NETDEV_EVENT_ISR);
}
}
static void sx127x_on_dio_isr(sx127x_t *dev, sx127x_flags_t flag)
{
dev->irq |= flag;
sx127x_isr((netdev_t *)dev);
}
static void sx127x_on_dio0_isr(void *arg)
{
sx127x_on_dio_isr((sx127x_t*) arg, SX127X_IRQ_DIO0);
}
static void sx127x_on_dio1_isr(void *arg)
{
sx127x_on_dio_isr((sx127x_t*) arg, SX127X_IRQ_DIO1);
}
static void sx127x_on_dio2_isr(void *arg)
{
sx127x_on_dio_isr((sx127x_t*) arg, SX127X_IRQ_DIO2);
}
static void sx127x_on_dio3_isr(void *arg)
{
sx127x_on_dio_isr((sx127x_t*) arg, SX127X_IRQ_DIO3);
}
/* Internal event handlers */
void sx127x_on_dio0(void *arg)
{
sx127x_t *dev = (sx127x_t *) arg;
netdev_t *netdev = (netdev_t*) &dev->netdev;
switch (dev->settings.state) {
case SX127X_RF_RX_RUNNING:
netdev->event_callback(netdev, NETDEV_EVENT_RX_COMPLETE);
break;
case SX127X_RF_TX_RUNNING:
xtimer_remove(&dev->_internal.tx_timeout_timer);
switch (dev->settings.modem) {
case SX127X_MODEM_LORA:
/* Clear IRQ */
sx127x_reg_write(dev, SX127X_REG_LR_IRQFLAGS,
SX127X_RF_LORA_IRQFLAGS_TXDONE);
/* Intentional fall-through */
case SX127X_MODEM_FSK:
default:
sx127x_set_state(dev, SX127X_RF_IDLE);
netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE);
break;
}
break;
case SX127X_RF_IDLE:
printf("sx127x_on_dio0: IDLE state\n");
break;
default:
printf("sx127x_on_dio0: Unknown state [%d]\n", dev->settings.state);
break;
}
}
void sx127x_on_dio1(void *arg)
{
/* Get interrupt context */
sx127x_t *dev = (sx127x_t *) arg;
netdev_t *netdev = (netdev_t*) &dev->netdev;
switch (dev->settings.state) {
case SX127X_RF_RX_RUNNING:
switch (dev->settings.modem) {
case SX127X_MODEM_FSK:
/* todo */
break;
case SX127X_MODEM_LORA:
xtimer_remove(&dev->_internal.rx_timeout_timer);
/* Clear Irq */
sx127x_reg_write(dev, SX127X_REG_LR_IRQFLAGS, SX127X_RF_LORA_IRQFLAGS_RXTIMEOUT);
sx127x_set_state(dev, SX127X_RF_IDLE);
netdev->event_callback(netdev, NETDEV_EVENT_RX_TIMEOUT);
break;
default:
break;
}
break;
case SX127X_RF_TX_RUNNING:
switch (dev->settings.modem) {
case SX127X_MODEM_FSK:
/* todo */
break;
case SX127X_MODEM_LORA:
break;
default:
break;
}
break;
default:
puts("sx127x_on_dio1: Unknown state");
break;
}
}
void sx127x_on_dio2(void *arg)
{
/* Get interrupt context */
sx127x_t *dev = (sx127x_t *) arg;
netdev_t *netdev = (netdev_t*) dev;
switch (dev->settings.state) {
case SX127X_RF_RX_RUNNING:
switch (dev->settings.modem) {
case SX127X_MODEM_FSK:
/* todo */
break;
case SX127X_MODEM_LORA:
if (dev->settings.lora.flags & SX127X_CHANNEL_HOPPING_FLAG) {
/* Clear IRQ */
sx127x_reg_write(dev, SX127X_REG_LR_IRQFLAGS,
SX127X_RF_LORA_IRQFLAGS_FHSSCHANGEDCHANNEL);
dev->_internal.last_channel = (sx127x_reg_read(dev, SX127X_REG_LR_HOPCHANNEL) &
SX127X_RF_LORA_HOPCHANNEL_CHANNEL_MASK);
netdev->event_callback(netdev, NETDEV_EVENT_FHSS_CHANGE_CHANNEL);
}
break;
default:
break;
}
break;
case SX127X_RF_TX_RUNNING:
switch (dev->settings.modem) {
case SX127X_MODEM_FSK:
break;
case SX127X_MODEM_LORA:
if (dev->settings.lora.flags & SX127X_CHANNEL_HOPPING_FLAG) {
/* Clear IRQ */
sx127x_reg_write(dev, SX127X_REG_LR_IRQFLAGS,
SX127X_RF_LORA_IRQFLAGS_FHSSCHANGEDCHANNEL);
dev->_internal.last_channel = (sx127x_reg_read(dev, SX127X_REG_LR_HOPCHANNEL) &
SX127X_RF_LORA_HOPCHANNEL_CHANNEL_MASK);
netdev->event_callback(netdev, NETDEV_EVENT_FHSS_CHANGE_CHANNEL);
}
break;
default:
break;
}
break;
default:
puts("sx127x_on_dio2: Unknown state");
break;
}
}
void sx127x_on_dio3(void *arg)
{
/* Get interrupt context */
sx127x_t *dev = (sx127x_t *) arg;
netdev_t *netdev = (netdev_t *) dev;
switch (dev->settings.modem) {
case SX127X_MODEM_FSK:
break;
case SX127X_MODEM_LORA:
/* Clear IRQ */
sx127x_reg_write(dev, SX127X_REG_LR_IRQFLAGS,
SX127X_RF_LORA_IRQFLAGS_CADDETECTED |
SX127X_RF_LORA_IRQFLAGS_CADDONE);
/* Send event message */
dev->_internal.is_last_cad_success = (sx127x_reg_read(dev, SX127X_REG_LR_IRQFLAGS) &
SX127X_RF_LORA_IRQFLAGS_CADDETECTED) == SX127X_RF_LORA_IRQFLAGS_CADDETECTED;
netdev->event_callback(netdev, NETDEV_EVENT_CAD_DONE);
break;
default:
puts("sx127x_on_dio3: Unknown modem");
break;
}
}
static void _init_isrs(sx127x_t *dev)
{
if (gpio_init_int(dev->params.dio0_pin, GPIO_IN, GPIO_RISING, sx127x_on_dio0_isr, dev) < 0) {
DEBUG("Error: cannot initialize DIO0 pin\n");
}
if (gpio_init_int(dev->params.dio1_pin, GPIO_IN, GPIO_RISING, sx127x_on_dio1_isr, dev) < 0) {
DEBUG("Error: cannot initialize DIO1 pin\n");
}
if (gpio_init_int(dev->params.dio2_pin, GPIO_IN, GPIO_RISING, sx127x_on_dio2_isr, dev) < 0) {
DEBUG("Error: cannot initialize DIO2 pin\n");
}
if (gpio_init_int(dev->params.dio3_pin, GPIO_IN, GPIO_RISING, sx127x_on_dio3_isr, dev) < 0) {
DEBUG("Error: cannot initialize DIO3 pin\n");
}
}
static void _on_tx_timeout(void *arg)
{
netdev_t *dev = (netdev_t *) arg;
dev->event_callback(dev, NETDEV_EVENT_TX_TIMEOUT);
}
static void _on_rx_timeout(void *arg)
{
netdev_t *dev = (netdev_t *) arg;
dev->event_callback(dev, NETDEV_EVENT_RX_TIMEOUT);
}
static void _init_timers(sx127x_t *dev)
{
dev->_internal.tx_timeout_timer.arg = dev;
dev->_internal.tx_timeout_timer.callback = _on_tx_timeout;
dev->_internal.rx_timeout_timer.arg = dev;
dev->_internal.rx_timeout_timer.callback = _on_rx_timeout;
}
static int _init_peripherals(sx127x_t *dev)
{
int res;
/* Setup SPI for SX127X */
res = spi_init_cs(dev->params.spi, dev->params.nss_pin);
if (res != SPI_OK) {
DEBUG("sx127x: error initializing SPI_%i device (code %i)\n",
dev->params.spi, res);
return 0;
}
res = gpio_init(dev->params.nss_pin, GPIO_OUT);
if (res < 0) {
DEBUG("sx127x: error initializing GPIO_%ld as CS line (code %i)\n",
(long)dev->params.nss_pin, res);
return 0;
}
gpio_set(dev->params.nss_pin);
DEBUG("sx127x: peripherals initialized with success\n");
return 1;
}

View File

@ -0,0 +1,850 @@
/*
* Copyright (C) 2016 Unwired Devices <info@unwds.com>
* 2017 Inria Chile
* 2017 Inria
*
* 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_sx127x
* @{
* @file
* @brief Implementation of get and set functions for SX127X
*
* @author Eugene P. <ep@unwds.com>
* @author José Ignacio Alamos <jose.alamos@inria.cl>
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
* @}
*/
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <inttypes.h>
#include "sx127x.h"
#include "sx127x_registers.h"
#include "sx127x_internal.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
uint8_t sx127x_get_state(const sx127x_t *dev)
{
return dev->settings.state;
}
void sx127x_set_state(sx127x_t *dev, uint8_t state)
{
#if ENABLE_DEBUG
switch (state) {
case SX127X_RF_IDLE:
DEBUG("[DEBUG] Change state: IDLE\n");
break;
case SX127X_RF_RX_RUNNING:
DEBUG("[DEBUG] Change state: RX\n");
break;
case SX127X_RF_TX_RUNNING:
DEBUG("[DEBUG] Change state: TX\n");
break;
default:
DEBUG("[DEBUG] Change state: UNKNOWN\n");
break;
}
#endif
dev->settings.state = state;
}
void sx127x_set_modem(sx127x_t *dev, uint8_t modem)
{
DEBUG("[DEBUG] set modem: %d\n", modem);
dev->settings.modem = modem;
switch (dev->settings.modem) {
case SX127X_MODEM_FSK:
/* Todo */
break;
case SX127X_MODEM_LORA:
sx127x_set_op_mode(dev, SX127X_RF_OPMODE_SLEEP);
sx127x_reg_write(dev, SX127X_REG_OPMODE,
(sx127x_reg_read(dev, SX127X_REG_OPMODE) &
SX127X_RF_LORA_OPMODE_LONGRANGEMODE_MASK) |
SX127X_RF_LORA_OPMODE_LONGRANGEMODE_ON);
sx127x_reg_write(dev, SX127X_REG_DIOMAPPING1, 0x00);
sx127x_reg_write(dev, SX127X_REG_DIOMAPPING2, 0x00);
break;
default:
break;
}
}
uint8_t sx127x_get_syncword(const sx127x_t *dev)
{
return sx127x_reg_read(dev, SX127X_REG_LR_SYNCWORD);
}
void sx127x_set_syncword(sx127x_t *dev, uint8_t syncword)
{
DEBUG("[DEBUG] Set syncword: %d\n", syncword);
sx127x_reg_write(dev, SX127X_REG_LR_SYNCWORD, syncword);
}
uint32_t sx127x_get_channel(const sx127x_t *dev)
{
return (((uint32_t)sx127x_reg_read(dev, SX127X_REG_FRFMSB) << 16) |
(sx127x_reg_read(dev, SX127X_REG_FRFMID) << 8) |
(sx127x_reg_read(dev, SX127X_REG_FRFLSB))) * SX127X_FREQUENCY_RESOLUTION;
}
void sx127x_set_channel(sx127x_t *dev, uint32_t channel)
{
DEBUG("[DEBUG] Set channel: %lu\n", channel);
/* Save current operating mode */
dev->settings.channel = channel;
channel = (uint32_t)((double) channel / (double) SX127X_FREQUENCY_RESOLUTION);
/* Write frequency settings into chip */
sx127x_reg_write(dev, SX127X_REG_FRFMSB, (uint8_t)((channel >> 16) & 0xFF));
sx127x_reg_write(dev, SX127X_REG_FRFMID, (uint8_t)((channel >> 8) & 0xFF));
sx127x_reg_write(dev, SX127X_REG_FRFLSB, (uint8_t)(channel & 0xFF));
}
uint32_t sx127x_get_time_on_air(const sx127x_t *dev, uint8_t pkt_len)
{
uint32_t air_time = 0;
switch (dev->settings.modem) {
case SX127X_MODEM_FSK:
/* todo */
break;
case SX127X_MODEM_LORA:
{
double bw = 0.0;
/* Note: When using LoRa modem only bandwidths 125, 250 and 500 kHz are supported. */
switch (dev->settings.lora.bandwidth) {
case SX127X_BW_125_KHZ:
bw = 125e3;
break;
case SX127X_BW_250_KHZ:
bw = 250e3;
break;
case SX127X_BW_500_KHZ:
bw = 500e3;
break;
default:
DEBUG("Invalid bandwith: %d\n", dev->settings.lora.bandwidth);
break;
}
/* Symbol rate : time for one symbol [secs] */
double rs = bw / (1 << dev->settings.lora.datarate);
double ts = 1 / rs;
/* time of preamble */
double t_preamble = (dev->settings.lora.preamble_len + 4.25) * ts;
/* Symbol length of payload and time */
double tmp =
ceil(
(8 * pkt_len - 4 * dev->settings.lora.datarate + 28
+ 16 * (dev->settings.lora.flags & SX127X_ENABLE_CRC_FLAG)
- (!(dev->settings.lora.flags & SX127X_ENABLE_FIXED_HEADER_LENGTH_FLAG) ? 20 : 0))
/ (double) (4 * dev->settings.lora.datarate
- (((dev->settings.lora.flags & SX127X_LOW_DATARATE_OPTIMIZE_FLAG)
> 0) ? 2 : 0)))
* (dev->settings.lora.coderate + 4);
double n_payload = 8 + ((tmp > 0) ? tmp : 0);
double t_payload = n_payload * ts;
/* Time on air */
double t_on_air = t_preamble + t_payload;
/* return milli seconds */
air_time = floor(t_on_air * 1e3 + 0.999);
}
break;
}
return air_time;
}
void sx127x_set_sleep(sx127x_t *dev)
{
DEBUG("[DEBUG] Set sleep\n");
/* Disable running timers */
xtimer_remove(&dev->_internal.tx_timeout_timer);
xtimer_remove(&dev->_internal.rx_timeout_timer);
/* Put chip into sleep */
sx127x_set_op_mode(dev, SX127X_RF_OPMODE_SLEEP);
sx127x_set_state(dev, SX127X_RF_IDLE);
}
void sx127x_set_standby(sx127x_t *dev)
{
DEBUG("[DEBUG] Set standby\n");
/* Disable running timers */
xtimer_remove(&dev->_internal.tx_timeout_timer);
xtimer_remove(&dev->_internal.rx_timeout_timer);
sx127x_set_op_mode(dev, SX127X_RF_OPMODE_STANDBY);
sx127x_set_state(dev, SX127X_RF_IDLE);
}
void sx127x_set_rx(sx127x_t *dev)
{
DEBUG("[DEBUG] Set RX\n");
switch (dev->settings.modem) {
case SX127X_MODEM_FSK:
/* todo */
break;
case SX127X_MODEM_LORA:
{
sx127x_reg_write(dev, SX127X_REG_LR_INVERTIQ2,
((dev->settings.lora.flags & SX127X_IQ_INVERTED_FLAG) ? SX127X_RF_LORA_INVERTIQ2_ON : SX127X_RF_LORA_INVERTIQ2_OFF));
#if defined(MODULE_SX1276)
/* ERRATA 2.3 - Receiver Spurious Reception of a LoRa Signal */
if (dev->settings.lora.bandwidth < 9) {
sx127x_reg_write(dev, SX127X_REG_LR_DETECTOPTIMIZE,
sx127x_reg_read(dev, SX127X_REG_LR_DETECTOPTIMIZE) & 0x7F);
sx127x_reg_write(dev, SX127X_REG_LR_TEST30, 0x00);
switch (dev->settings.lora.bandwidth) {
case SX127X_BW_125_KHZ: /* 125 kHz */
sx127x_reg_write(dev, SX127X_REG_LR_TEST2F, 0x40);
break;
case SX127X_BW_250_KHZ: /* 250 kHz */
sx127x_reg_write(dev, SX127X_REG_LR_TEST2F, 0x40);
break;
default:
break;
}
}
else {
sx127x_reg_write(dev, SX127X_REG_LR_DETECTOPTIMIZE,
sx127x_reg_read(dev, SX127X_REG_LR_DETECTOPTIMIZE) | 0x80);
}
#endif
/* Setup interrupts */
if (dev->settings.lora.flags & SX127X_CHANNEL_HOPPING_FLAG) {
sx127x_reg_write(dev, SX127X_REG_LR_IRQFLAGSMASK,
/* SX127X_RF_LORA_IRQFLAGS_RXTIMEOUT |
SX127X_RF_LORA_IRQFLAGS_RXDONE |
SX127X_RF_LORA_IRQFLAGS_PAYLOADCRCERROR | */
SX127X_RF_LORA_IRQFLAGS_VALIDHEADER |
SX127X_RF_LORA_IRQFLAGS_TXDONE |
SX127X_RF_LORA_IRQFLAGS_CADDONE |
/* SX127X_RF_LORA_IRQFLAGS_FHSSCHANGEDCHANNEL | */
SX127X_RF_LORA_IRQFLAGS_CADDETECTED);
/* DIO0=RxDone, DIO2=FhssChangeChannel */
sx127x_reg_write(dev, SX127X_REG_DIOMAPPING1,
(sx127x_reg_read(dev, SX127X_REG_DIOMAPPING1) &
SX127X_RF_LORA_DIOMAPPING1_DIO0_MASK &
SX127X_RF_LORA_DIOMAPPING1_DIO2_MASK) |
SX127X_RF_LORA_DIOMAPPING1_DIO0_00 |
SX127X_RF_LORA_DIOMAPPING1_DIO2_00);
}
else {
sx127x_reg_write(dev, SX127X_REG_LR_IRQFLAGSMASK,
/* SX127X_RF_LORA_IRQFLAGS_RXTIMEOUT |
SX127X_RF_LORA_IRQFLAGS_RXDONE |
SX127X_RF_LORA_IRQFLAGS_PAYLOADCRCERROR | */
SX127X_RF_LORA_IRQFLAGS_VALIDHEADER |
SX127X_RF_LORA_IRQFLAGS_TXDONE |
SX127X_RF_LORA_IRQFLAGS_CADDONE |
SX127X_RF_LORA_IRQFLAGS_FHSSCHANGEDCHANNEL |
SX127X_RF_LORA_IRQFLAGS_CADDETECTED);
/* DIO0=RxDone */
sx127x_reg_write(dev, SX127X_REG_DIOMAPPING1,
(sx127x_reg_read(dev, SX127X_REG_DIOMAPPING1) &
SX127X_RF_LORA_DIOMAPPING1_DIO0_MASK) |
SX127X_RF_LORA_DIOMAPPING1_DIO0_00);
}
sx127x_reg_write(dev, SX127X_REG_LR_FIFORXBASEADDR, 0);
sx127x_reg_write(dev, SX127X_REG_LR_FIFOADDRPTR, 0);
}
break;
}
sx127x_set_state(dev, SX127X_RF_RX_RUNNING);
if (dev->settings.window_timeout != 0) {
xtimer_set(&(dev->_internal.rx_timeout_timer),
dev->settings.window_timeout);
}
if (dev->settings.lora.flags & SX127X_RX_CONTINUOUS_FLAG) {
sx127x_set_op_mode(dev, SX127X_RF_LORA_OPMODE_RECEIVER);
}
else {
sx127x_set_op_mode(dev, SX127X_RF_LORA_OPMODE_RECEIVER_SINGLE);
}
}
void sx127x_set_tx(sx127x_t *dev)
{
switch (dev->settings.modem) {
case SX127X_MODEM_FSK:
/* todo */
break;
case SX127X_MODEM_LORA:
{
if (dev->settings.lora.flags & SX127X_CHANNEL_HOPPING_FLAG) {
sx127x_reg_write(dev, SX127X_REG_LR_IRQFLAGSMASK,
SX127X_RF_LORA_IRQFLAGS_RXTIMEOUT |
SX127X_RF_LORA_IRQFLAGS_RXDONE |
SX127X_RF_LORA_IRQFLAGS_PAYLOADCRCERROR |
SX127X_RF_LORA_IRQFLAGS_VALIDHEADER |
/* RFLR_IRQFLAGS_TXDONE | */
SX127X_RF_LORA_IRQFLAGS_CADDONE |
/* RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL | */
SX127X_RF_LORA_IRQFLAGS_CADDETECTED);
/* DIO0=TxDone, DIO2=FhssChangeChannel */
sx127x_reg_write(dev, SX127X_REG_DIOMAPPING1,
(sx127x_reg_read(dev, SX127X_REG_DIOMAPPING1 ) &
SX127X_RF_LORA_DIOMAPPING1_DIO0_MASK &
SX127X_RF_LORA_DIOMAPPING1_DIO2_MASK) |
SX127X_RF_LORA_DIOMAPPING1_DIO0_01 |
SX127X_RF_LORA_DIOMAPPING1_DIO2_00);
}
else
{
sx127x_reg_write(dev, SX127X_REG_LR_IRQFLAGSMASK,
SX127X_RF_LORA_IRQFLAGS_RXTIMEOUT |
SX127X_RF_LORA_IRQFLAGS_RXDONE |
SX127X_RF_LORA_IRQFLAGS_PAYLOADCRCERROR |
SX127X_RF_LORA_IRQFLAGS_VALIDHEADER |
/* RFLR_IRQFLAGS_TXDONE | */
SX127X_RF_LORA_IRQFLAGS_CADDONE |
SX127X_RF_LORA_IRQFLAGS_FHSSCHANGEDCHANNEL |
SX127X_RF_LORA_IRQFLAGS_CADDETECTED);
/* DIO0=TxDone */
sx127x_reg_write(dev, SX127X_REG_DIOMAPPING1,
(sx127x_reg_read(dev, SX127X_REG_DIOMAPPING1) &
SX127X_RF_LORA_DIOMAPPING1_DIO0_MASK) |
SX127X_RF_LORA_DIOMAPPING1_DIO0_01);
}
}
break;
}
sx127x_set_state(dev, SX127X_RF_RX_RUNNING);
if (dev->settings.window_timeout != 0) {
xtimer_set(&(dev->_internal.tx_timeout_timer),
dev->settings.window_timeout);
}
sx127x_set_op_mode(dev, SX127X_RF_OPMODE_TRANSMITTER );
}
uint8_t sx127x_get_max_payload_len(const sx127x_t *dev)
{
switch (dev->settings.modem) {
case SX127X_MODEM_FSK:
return sx127x_reg_read(dev, SX127X_REG_PAYLOADLENGTH);
case SX127X_MODEM_LORA:
return sx127x_reg_read(dev, SX127X_REG_LR_PAYLOADMAXLENGTH);
}
/* should never be reached */
return 0;
}
void sx127x_set_max_payload_len(const sx127x_t *dev, uint8_t maxlen)
{
DEBUG("[DEBUG] Set max payload len: %d\n", maxlen);
switch (dev->settings.modem) {
case SX127X_MODEM_FSK:
sx127x_reg_write(dev, SX127X_REG_PAYLOADLENGTH, maxlen);
break;
case SX127X_MODEM_LORA:
sx127x_reg_write(dev, SX127X_REG_LR_PAYLOADMAXLENGTH, maxlen);
break;
}
}
uint8_t sx127x_get_op_mode(const sx127x_t *dev)
{
return sx127x_reg_read(dev, SX127X_REG_OPMODE) & ~SX127X_RF_OPMODE_MASK;
}
void sx127x_set_op_mode(const sx127x_t *dev, uint8_t op_mode)
{
#if ENABLE_DEBUG
switch(op_mode) {
case SX127X_RF_OPMODE_SLEEP:
DEBUG("[DEBUG] Set op mode: SLEEP\n");
break;
case SX127X_RF_OPMODE_STANDBY:
DEBUG("[DEBUG] Set op mode: STANDBY\n");
break;
case SX127X_RF_OPMODE_RECEIVER:
DEBUG("[DEBUG] Set op mode: RECEIVER\n");
break;
case SX127X_RF_OPMODE_TRANSMITTER:
DEBUG("[DEBUG] Set op mode: TRANSMITTER\n");
break;
default:
DEBUG("[DEBUG] Set op mode: UNKNOWN\n");
break;
}
#endif
/* Replace previous mode value and setup new mode value */
sx127x_reg_write(dev, SX127X_REG_OPMODE,
(sx127x_reg_read(dev, SX127X_REG_OPMODE) & SX127X_RF_OPMODE_MASK) | op_mode);
}
uint8_t sx127x_get_bandwidth(const sx127x_t *dev)
{
return dev->settings.lora.bandwidth;
}
inline void _low_datarate_optimize(sx127x_t *dev)
{
if ( ((dev->settings.lora.bandwidth == SX127X_BW_125_KHZ) &&
((dev->settings.lora.datarate == SX127X_SF11) ||
(dev->settings.lora.datarate == SX127X_SF12))) ||
((dev->settings.lora.bandwidth == SX127X_BW_250_KHZ) &&
(dev->settings.lora.datarate == SX127X_SF12))) {
dev->settings.lora.flags |= SX127X_LOW_DATARATE_OPTIMIZE_FLAG;
} else {
dev->settings.lora.flags &= ~SX127X_LOW_DATARATE_OPTIMIZE_FLAG;
}
#if defined(MODULE_SX1272)
sx127x_reg_write(dev, SX127X_REG_LR_MODEMCONFIG1,
(sx127x_reg_read(dev, SX127X_REG_LR_MODEMCONFIG1) &
SX127X_RF_LORA_MODEMCONFIG1_LOWDATARATEOPTIMIZE_MASK) |
((dev->settings.lora.flags & SX127X_LOW_DATARATE_OPTIMIZE_FLAG)));
#else /* MODULE_SX1276 */
sx127x_reg_write(dev, SX127X_REG_LR_MODEMCONFIG3,
(sx127x_reg_read(dev, SX127X_REG_LR_MODEMCONFIG3) &
SX127X_RF_LORA_MODEMCONFIG3_LOWDATARATEOPTIMIZE_MASK) |
((dev->settings.lora.flags & SX127X_LOW_DATARATE_OPTIMIZE_FLAG) << 3));
#endif
}
inline void _update_bandwidth(const sx127x_t *dev)
{
uint8_t config1_reg = sx127x_reg_read(dev, SX127X_REG_LR_MODEMCONFIG1);
#if defined(MODULE_SX1272)
config1_reg &= SX1272_RF_LORA_MODEMCONFIG1_BW_MASK;
switch (dev->settings.lora.bandwidth) {
case SX127X_BW_125_KHZ:
config1_reg |= SX1272_RF_LORA_MODEMCONFIG1_BW_125_KHZ;
break;
case SX127X_BW_250_KHZ:
config1_reg |= SX1272_RF_LORA_MODEMCONFIG1_BW_250_KHZ;
break;
case SX127X_BW_500_KHZ:
config1_reg |= SX1272_RF_LORA_MODEMCONFIG1_BW_500_KHZ;
break;
default:
DEBUG("Unsupported bandwidth, %d", dev->settings.lora.bandwidth);
break;
}
#else /* MODULE_SX1276 */
config1_reg &= SX1276_RF_LORA_MODEMCONFIG1_BW_MASK;
switch (dev->settings.lora.bandwidth) {
case SX127X_BW_125_KHZ:
config1_reg |= SX1276_RF_LORA_MODEMCONFIG1_BW_125_KHZ;
break;
case SX127X_BW_250_KHZ:
config1_reg |= SX1276_RF_LORA_MODEMCONFIG1_BW_250_KHZ;
break;
case SX127X_BW_500_KHZ:
config1_reg |= SX1276_RF_LORA_MODEMCONFIG1_BW_500_KHZ;
break;
default:
DEBUG("Unsupported bandwidth, %d", dev->settings.lora.bandwidth);
break;
}
#endif
sx127x_reg_write(dev, SX127X_REG_LR_MODEMCONFIG1, config1_reg);
}
void sx127x_set_bandwidth(sx127x_t *dev, uint8_t bandwidth)
{
DEBUG("[DEBUG] Set bandwidth: %d\n", bandwidth);
dev->settings.lora.bandwidth = bandwidth;
_update_bandwidth((const sx127x_t *)dev);
_low_datarate_optimize(dev);
/* ERRATA sensitivity tweaks */
if ((dev->settings.lora.bandwidth == SX127X_BW_500_KHZ) &&
(dev->settings.channel > SX127X_RF_MID_BAND_THRESH)) {
/* ERRATA 2.1 - Sensitivity Optimization with a 500 kHz Bandwidth */
sx127x_reg_write(dev, SX127X_REG_LR_TEST36, 0x02);
sx127x_reg_write(dev, SX127X_REG_LR_TEST3A, 0x64);
}
else if (dev->settings.lora.bandwidth == SX127X_BW_500_KHZ) {
/* ERRATA 2.1 - Sensitivity Optimization with a 500 kHz Bandwidth */
sx127x_reg_write(dev, SX127X_REG_LR_TEST36, 0x02);
sx127x_reg_write(dev, SX127X_REG_LR_TEST3A, 0x7F);
}
else {
/* ERRATA 2.1 - Sensitivity Optimization with another Bandwidth */
sx127x_reg_write(dev, SX127X_REG_LR_TEST36, 0x03);
}
}
uint8_t sx127x_get_spreading_factor(const sx127x_t *dev)
{
return dev->settings.lora.datarate;
}
void sx127x_set_spreading_factor(sx127x_t *dev, uint8_t datarate)
{
DEBUG("[DEBUG] Set spreading factor: %d\n", datarate);
if (datarate == SX127X_SF6 &&
!(dev->settings.lora.flags & SX127X_ENABLE_FIXED_HEADER_LENGTH_FLAG)) {
/* SF 6 is only valid when using explicit header mode */
DEBUG("Spreading Factor 6 can only be used when explicit header "
"mode is set, this mode is not supported by this driver."
"Ignoring.\n");
return;
}
dev->settings.lora.datarate = datarate;
uint8_t config2_reg = sx127x_reg_read(dev, SX127X_REG_LR_MODEMCONFIG2);
config2_reg &= SX127X_RF_LORA_MODEMCONFIG2_SF_MASK;
config2_reg |= datarate << 4;
sx127x_reg_write(dev, SX127X_REG_LR_MODEMCONFIG2, config2_reg);
_low_datarate_optimize(dev);
switch(dev->settings.lora.datarate) {
case SX127X_SF6:
sx127x_reg_write(dev, SX127X_REG_LR_DETECTOPTIMIZE,
SX127X_RF_LORA_DETECTIONOPTIMIZE_SF6);
sx127x_reg_write(dev, SX127X_REG_LR_DETECTIONTHRESHOLD,
SX127X_RF_LORA_DETECTIONTHRESH_SF6);
break;
default:
sx127x_reg_write(dev, SX127X_REG_LR_DETECTOPTIMIZE,
SX127X_RF_LORA_DETECTIONOPTIMIZE_SF7_TO_SF12);
sx127x_reg_write(dev, SX127X_REG_LR_DETECTIONTHRESHOLD,
SX127X_RF_LORA_DETECTIONTHRESH_SF7_TO_SF12);
break;
}
}
uint8_t sx127x_get_coding_rate(const sx127x_t *dev)
{
return dev->settings.lora.coderate;
}
void sx127x_set_coding_rate(sx127x_t *dev, uint8_t coderate)
{
DEBUG("[DEBUG] Set coding rate: %d\n", coderate);
dev->settings.lora.coderate = coderate;
uint8_t config1_reg = sx127x_reg_read(dev, SX127X_REG_LR_MODEMCONFIG1);
#if defined(MODULE_SX1272)
config1_reg &= SX1272_RF_LORA_MODEMCONFIG1_CODINGRATE_MASK;
config1_reg |= coderate << 3;
#else /* MODULE_SX1276 */
config1_reg &= SX1276_RF_LORA_MODEMCONFIG1_CODINGRATE_MASK;
config1_reg |= coderate << 1;
#endif
sx127x_reg_write(dev, SX127X_REG_LR_MODEMCONFIG1, config1_reg);
}
static inline void _set_flag(sx127x_t *dev, uint8_t flag, bool value)
{
if (value) {
dev->settings.lora.flags |= flag;
}
else {
dev->settings.lora.flags &= ~flag;
}
}
bool sx127x_get_rx_single(const sx127x_t *dev)
{
return !(dev->settings.lora.flags & SX127X_RX_CONTINUOUS_FLAG);
}
void sx127x_set_rx_single(sx127x_t *dev, bool single)
{
DEBUG("[DEBUG] Set RX single: %d\n", single);
_set_flag(dev, SX127X_RX_CONTINUOUS_FLAG, !single);
}
bool sx127x_get_crc(const sx127x_t *dev)
{
#if defined(MODULE_SX1272)
return (sx127x_reg_read(dev, SX127X_REG_LR_MODEMCONFIG1) &
SX1272_RF_LORA_MODEMCONFIG1_RXPAYLOADCRC_MASK);
#else /* MODULE_SX1276 */
return (sx127x_reg_read(dev, SX127X_REG_LR_MODEMCONFIG2) &
SX1276_RF_LORA_MODEMCONFIG2_RXPAYLOADCRC_MASK);
#endif
}
void sx127x_set_crc(sx127x_t *dev, bool crc)
{
DEBUG("[DEBUG] Set CRC: %d\n", crc);
_set_flag(dev, SX127X_ENABLE_CRC_FLAG, crc);
#if defined(MODULE_SX1272)
uint8_t config2_reg = sx127x_reg_read(dev, SX127X_REG_LR_MODEMCONFIG1);
config2_reg &= SX1272_RF_LORA_MODEMCONFIG1_RXPAYLOADCRC_MASK;
config2_reg |= crc << 1;
sx127x_reg_write(dev, SX127X_REG_LR_MODEMCONFIG1, config2_reg);
#else /* MODULE_SX1276 */
uint8_t config2_reg = sx127x_reg_read(dev, SX127X_REG_LR_MODEMCONFIG2);
config2_reg &= SX1276_RF_LORA_MODEMCONFIG2_RXPAYLOADCRC_MASK;
config2_reg |= crc << 2;
sx127x_reg_write(dev, SX127X_REG_LR_MODEMCONFIG2, config2_reg);
#endif
}
uint8_t sx127x_get_hop_period(const sx127x_t *dev)
{
return sx127x_reg_read(dev, SX127X_REG_LR_HOPPERIOD);
}
void sx127x_set_hop_period(sx127x_t *dev, uint8_t hop_period)
{
DEBUG("[DEBUG] Set Hop period: %d\n", hop_period);
dev->settings.lora.freq_hop_period = hop_period;
uint8_t tmp = sx127x_reg_read(dev, SX127X_REG_LR_PLLHOP);
if (dev->settings.lora.flags & SX127X_CHANNEL_HOPPING_FLAG) {
tmp |= SX127X_RF_LORA_PLLHOP_FASTHOP_ON;
sx127x_reg_write(dev, SX127X_REG_LR_PLLHOP, tmp);
sx127x_reg_write(dev, SX127X_REG_LR_HOPPERIOD, hop_period);
}
}
bool sx127x_get_fixed_header_len_mode(const sx127x_t *dev)
{
return dev->settings.lora.flags & SX127X_ENABLE_FIXED_HEADER_LENGTH_FLAG;
}
void sx127x_set_fixed_header_len_mode(sx127x_t *dev, bool fixed_len)
{
DEBUG("[DEBUG] Set fixed header length: %d\n", fixed_len);
_set_flag(dev, SX127X_ENABLE_FIXED_HEADER_LENGTH_FLAG, fixed_len);
uint8_t config1_reg = sx127x_reg_read(dev, SX127X_REG_LR_MODEMCONFIG1);
#if defined(MODULE_SX1272)
config1_reg &= SX1272_RF_LORA_MODEMCONFIG1_IMPLICITHEADER_MASK;
config1_reg |= fixed_len << 2;
#else /* MODULE_SX1276 */
config1_reg &= SX1276_RF_LORA_MODEMCONFIG1_IMPLICITHEADER_MASK;
config1_reg |= fixed_len;
#endif
sx127x_reg_write(dev, SX127X_REG_LR_MODEMCONFIG1, config1_reg);
}
uint8_t sx127x_get_payload_length(const sx127x_t *dev)
{
return sx127x_reg_read(dev, SX127X_REG_LR_PAYLOADLENGTH);;
}
void sx127x_set_payload_length(sx127x_t *dev, uint8_t len)
{
DEBUG("[DEBUG] Set payload len: %d\n", len);
sx127x_reg_write(dev, SX127X_REG_LR_PAYLOADLENGTH, len);
}
static inline uint8_t sx127x_get_pa_select(uint32_t channel)
{
#if defined(MODULE_SX1272)
(void) channel;
#if SX1272_DEFAULT_PASELECT
return SX127X_RF_PACONFIG_PASELECT_RFO;
#else
return SX127X_RF_PACONFIG_PASELECT_PABOOST;
#endif
#else /* MODULE_SX1276 */
if (channel < SX127X_RF_MID_BAND_THRESH) {
return SX127X_RF_PACONFIG_PASELECT_PABOOST;
}
else {
return SX127X_RF_PACONFIG_PASELECT_RFO;
}
#endif
}
uint8_t sx127x_get_tx_power(const sx127x_t *dev)
{
return dev->settings.lora.power;
}
void sx127x_set_tx_power(sx127x_t *dev, uint8_t power)
{
DEBUG("[DEBUG] Set power: %d\n", power);
dev->settings.lora.power = power;
uint8_t pa_config = sx127x_reg_read(dev, SX127X_REG_PACONFIG);
#if defined(MODULE_SX1272)
uint8_t pa_dac = sx127x_reg_read(dev, SX1272_REG_PADAC);
#else /* MODULE_SX1276 */
uint8_t pa_dac = sx127x_reg_read(dev, SX1276_REG_PADAC);
#endif
pa_config = ((pa_config & SX127X_RF_PACONFIG_PASELECT_MASK) |
sx127x_get_pa_select(dev->settings.channel));
#if defined(MODULE_SX1276)
/* max power is 14dBm */
pa_config = (pa_config & SX127X_RF_PACONFIG_MAX_POWER_MASK) | 0x70;
#endif
sx127x_reg_write(dev, SX127X_REG_PARAMP, SX127X_RF_PARAMP_0050_US);
if ((pa_config & SX127X_RF_PACONFIG_PASELECT_PABOOST)
== SX127X_RF_PACONFIG_PASELECT_PABOOST) {
if (power > 17) {
pa_dac = ((pa_dac & SX127X_RF_PADAC_20DBM_MASK) |
SX127X_RF_PADAC_20DBM_ON);
} else {
pa_dac = ((pa_dac & SX127X_RF_PADAC_20DBM_MASK) |
SX127X_RF_PADAC_20DBM_OFF);
}
if ((pa_dac & SX127X_RF_PADAC_20DBM_ON) == SX127X_RF_PADAC_20DBM_ON) {
if (power < 5) {
power = 5;
}
if (power > 20) {
power = 20;
}
pa_config = ((pa_config & SX127X_RF_PACONFIG_OUTPUTPOWER_MASK) |
(uint8_t)((uint16_t)(power - 5) & 0x0F));
} else {
if (power < 2) {
power = 2;
}
if (power > 17) {
power = 17;
}
pa_config = ((pa_config & SX127X_RF_PACONFIG_OUTPUTPOWER_MASK) |
(uint8_t)((uint16_t)(power - 2) & 0x0F));
}
} else {
if (power < -1) {
power = -1;
}
if (power > 14) {
power = 14;
}
pa_config = ((pa_config & SX127X_RF_PACONFIG_OUTPUTPOWER_MASK) |
(uint8_t)((uint16_t)(power + 1) & 0x0F));
}
sx127x_reg_write(dev, SX127X_REG_PACONFIG, pa_config);
#if defined(MODULE_SX1272)
sx127x_reg_write(dev, SX1272_REG_PADAC, pa_dac);
#else /* MODULE_SX1276 */
sx127x_reg_write(dev, SX1276_REG_PADAC, pa_dac);
#endif
}
uint16_t sx127x_get_preamble_length(const sx127x_t *dev)
{
return dev->settings.lora.preamble_len;
}
void sx127x_set_preamble_length(sx127x_t *dev, uint16_t preamble)
{
DEBUG("[DEBUG] Set preamble length: %d\n", preamble);
dev->settings.lora.preamble_len = preamble;
sx127x_reg_write(dev, SX127X_REG_LR_PREAMBLEMSB,
(preamble >> 8) & 0xFF);
sx127x_reg_write(dev, SX127X_REG_LR_PREAMBLELSB,
preamble & 0xFF);
}
void sx127x_set_rx_timeout(sx127x_t *dev, uint32_t timeout)
{
DEBUG("[DEBUG] Set RX timeout: %lu\n", timeout);
dev->settings.lora.rx_timeout = timeout;
}
void sx127x_set_tx_timeout(sx127x_t *dev, uint32_t timeout)
{
DEBUG("[DEBUG] Set TX timeout: %lu\n", timeout);
dev->settings.lora.tx_timeout = timeout;
}
void sx127x_set_symbol_timeout(sx127x_t *dev, uint16_t timeout)
{
DEBUG("[DEBUG] Set symbol timeout: %d\n", timeout);
dev->settings.lora.rx_timeout = timeout;
uint8_t config2_reg = sx127x_reg_read(dev, SX127X_REG_LR_MODEMCONFIG2);
config2_reg &= SX127X_RF_LORA_MODEMCONFIG2_SYMBTIMEOUTMSB_MASK;
config2_reg |= (timeout >> 8) & ~SX127X_RF_LORA_MODEMCONFIG2_SYMBTIMEOUTMSB_MASK;
sx127x_reg_write(dev, SX127X_REG_LR_MODEMCONFIG2, config2_reg);
sx127x_reg_write(dev, SX127X_REG_LR_SYMBTIMEOUTLSB,timeout & 0xFF);
}
void sx127x_set_iq_invert(sx127x_t *dev, bool iq_invert)
{
DEBUG("[DEBUG] Set IQ invert: %d\n", iq_invert);
_set_flag(dev, SX127X_IQ_INVERTED_FLAG, iq_invert);
sx127x_reg_write(dev, SX127X_REG_LR_INVERTIQ,
(sx127x_reg_read(dev, SX127X_REG_LR_INVERTIQ) &
SX127X_RF_LORA_INVERTIQ_RX_MASK &
SX127X_RF_LORA_INVERTIQ_TX_MASK) |
SX127X_RF_LORA_INVERTIQ_RX_OFF |
(iq_invert ? SX127X_RF_LORA_INVERTIQ_TX_ON : SX127X_RF_LORA_INVERTIQ_TX_OFF));
sx127x_reg_write(dev, SX127X_REG_LR_INVERTIQ2,
(iq_invert ? SX127X_RF_LORA_INVERTIQ2_ON : SX127X_RF_LORA_INVERTIQ2_OFF));
}
void sx127x_set_freq_hop(sx127x_t *dev, bool freq_hop_on)
{
DEBUG("[DEBUG] Set freq hop: %d\n", freq_hop_on);
_set_flag(dev, SX127X_CHANNEL_HOPPING_FLAG, freq_hop_on);
}

View File

@ -0,0 +1,220 @@
/*
* Copyright (c) 2016 Unwired Devices <info@unwds.com>
* 2017 Inria Chile
* 2017 Inria
*
* 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_sx127x
* @{
* @file
* @brief implementation of internal functions for sx127x
*
* @author Eugene P. <ep@unwds.com>
* @author José Ignacio Alamos <jose.alamos@inria.cl>
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
* @}
*/
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <inttypes.h>
#include "irq.h"
#include "sx127x.h"
#include "sx127x_registers.h"
#include "sx127x_internal.h"
#include "sx127x_params.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
bool sx127x_test(const sx127x_t *dev)
{
/* Read version number and compare with sx127x assigned revision */
uint8_t version = sx127x_reg_read(dev, SX127X_REG_VERSION);
#if defined(MODULE_SX1272)
if (version != VERSION_SX1272) {
DEBUG("[Error] sx1272 test failed, invalid version number: %d\n",
version);
return false;
}
DEBUG("SX1272 transceiver detected.\n");
#else /* MODULE_SX1276) */
if (version != VERSION_SX1276) {
DEBUG("[Error] sx1276 test failed, invalid version number: %d\n",
version);
return false;
}
DEBUG("SX1276 transceiver detected.\n");
#endif
return true;
}
void sx127x_reg_write(const sx127x_t *dev, uint8_t addr, uint8_t data)
{
sx127x_reg_write_burst(dev, addr, &data, 1);
}
uint8_t sx127x_reg_read(const sx127x_t *dev, uint8_t addr)
{
uint8_t data;
sx127x_reg_read_burst(dev, addr, &data, 1);
return data;
}
void sx127x_reg_write_burst(const sx127x_t *dev, uint8_t addr, uint8_t *buffer,
uint8_t size)
{
unsigned int cpsr;
spi_acquire(dev->params.spi, SPI_CS_UNDEF, SX127X_PARAM_SPI_MODE, SX127X_PARAM_SPI_SPEED);
cpsr = irq_disable();
gpio_clear(dev->params.nss_pin);
spi_transfer_regs(dev->params.spi, SPI_CS_UNDEF, addr | 0x80, (char *) buffer, NULL, size);
gpio_set(dev->params.nss_pin);
irq_restore(cpsr);
spi_release(dev->params.spi);
}
void sx127x_reg_read_burst(const sx127x_t *dev, uint8_t addr, uint8_t *buffer,
uint8_t size)
{
unsigned int cpsr;
cpsr = irq_disable();
spi_acquire(dev->params.spi, SPI_CS_UNDEF, SX127X_PARAM_SPI_MODE, SX127X_PARAM_SPI_SPEED);
gpio_clear(dev->params.nss_pin);
spi_transfer_regs(dev->params.spi, SPI_CS_UNDEF, addr & 0x7F, NULL, (char *) buffer, size);
gpio_set(dev->params.nss_pin);
spi_release(dev->params.spi);
irq_restore(cpsr);
}
void sx127x_write_fifo(const sx127x_t *dev, uint8_t *buffer, uint8_t size)
{
sx127x_reg_write_burst(dev, 0, buffer, size);
}
void sx127x_read_fifo(const sx127x_t *dev, uint8_t *buffer, uint8_t size)
{
sx127x_reg_read_burst(dev, 0, buffer, size);
}
void sx127x_rx_chain_calibration(sx127x_t *dev)
{
uint8_t reg_pa_config_init_val;
uint32_t initial_freq;
/* Save context */
reg_pa_config_init_val = sx127x_reg_read(dev, SX127X_REG_PACONFIG);
initial_freq = (double) (((uint32_t) sx127x_reg_read(dev, SX127X_REG_FRFMSB) << 16)
| ((uint32_t) sx127x_reg_read(dev, SX127X_REG_FRFMID) << 8)
| ((uint32_t) sx127x_reg_read(dev, SX127X_REG_FRFLSB))) * (double) SX127X_FREQUENCY_RESOLUTION;
/* Cut the PA just in case, RFO output, power = -1 dBm */
sx127x_reg_write(dev, SX127X_REG_PACONFIG, 0x00);
/* Launch Rx chain calibration for LF band */
sx127x_reg_write(dev,
SX127X_REG_IMAGECAL,
(sx127x_reg_read(dev, SX127X_REG_IMAGECAL) & SX127X_RF_IMAGECAL_IMAGECAL_MASK)
| SX127X_RF_IMAGECAL_IMAGECAL_START);
while ((sx127x_reg_read(dev, SX127X_REG_IMAGECAL) & SX127X_RF_IMAGECAL_IMAGECAL_RUNNING)
== SX127X_RF_IMAGECAL_IMAGECAL_RUNNING) {
}
/* Set a frequency in HF band */
sx127x_set_channel(dev, SX127X_HF_CHANNEL_DEFAULT);
/* Launch Rx chain calibration for HF band */
sx127x_reg_write(dev,
SX127X_REG_IMAGECAL,
(sx127x_reg_read(dev, SX127X_REG_IMAGECAL) & SX127X_RF_IMAGECAL_IMAGECAL_MASK)
| SX127X_RF_IMAGECAL_IMAGECAL_START);
while ((sx127x_reg_read(dev, SX127X_REG_IMAGECAL) & SX127X_RF_IMAGECAL_IMAGECAL_RUNNING)
== SX127X_RF_IMAGECAL_IMAGECAL_RUNNING) {
}
/* Restore context */
sx127x_reg_write(dev, SX127X_REG_PACONFIG, reg_pa_config_init_val);
sx127x_set_channel(dev, initial_freq);
}
int16_t sx127x_read_rssi(const sx127x_t *dev)
{
int16_t rssi = 0;
switch (dev->settings.modem) {
case SX127X_MODEM_FSK:
rssi = -(sx127x_reg_read(dev, SX127X_REG_RSSIVALUE) >> 1);
break;
case SX127X_MODEM_LORA:
#if defined(MODULE_SX1272)
rssi = SX127X_RSSI_OFFSET + sx127x_reg_read(dev, SX127X_REG_LR_RSSIVALUE);
#else /* MODULE_SX1276 */
if (dev->settings.channel > SX127X_RF_MID_BAND_THRESH) {
rssi = SX127X_RSSI_OFFSET_HF + sx127x_reg_read(dev, SX127X_REG_LR_RSSIVALUE);
}
else {
rssi = SX127X_RSSI_OFFSET_LF + sx127x_reg_read(dev, SX127X_REG_LR_RSSIVALUE);
}
#endif
break;
default:
rssi = -1;
break;
}
return rssi;
}
void sx127x_start_cad(sx127x_t *dev)
{
switch (dev->settings.modem) {
case SX127X_MODEM_FSK:
break;
case SX127X_MODEM_LORA:
/* Disable all interrupts except CAD-related */
sx127x_reg_write(dev, SX127X_REG_LR_IRQFLAGSMASK,
SX127X_RF_LORA_IRQFLAGS_RXTIMEOUT |
SX127X_RF_LORA_IRQFLAGS_RXDONE |
SX127X_RF_LORA_IRQFLAGS_PAYLOADCRCERROR |
SX127X_RF_LORA_IRQFLAGS_VALIDHEADER |
SX127X_RF_LORA_IRQFLAGS_TXDONE |
/*SX127X_RF_LORA_IRQFLAGS_CADDONE |*/
SX127X_RF_LORA_IRQFLAGS_FHSSCHANGEDCHANNEL
/* | SX127X_RF_LORA_IRQFLAGS_CADDETECTED*/
);
/* DIO3 = CADDone */
sx127x_reg_write(dev, SX127X_REG_DIOMAPPING1,
(sx127x_reg_read(dev, SX127X_REG_DIOMAPPING1) &
SX127X_RF_LORA_DIOMAPPING1_DIO3_MASK) |
SX127X_RF_LORA_DIOMAPPING1_DIO3_00);
sx127x_set_state(dev, SX127X_RF_CAD);
sx127x_set_op_mode(dev, SX127X_RF_LORA_OPMODE_CAD);
break;
default:
break;
}
}

View File

@ -0,0 +1,540 @@
/*
* Copyright (C) 2016 Fundación Inria Chile
*
* 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_sx127x
* @{
* @file
* @brief Netdev adaptation for the sx127x driver
*
* @author Eugene P. <ep@unwds.com>
* @author José Ignacio Alamos <jose.alamos@inria.cl>
* @}
*/
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include "net/netopt.h"
#include "net/netdev.h"
#include "sx127x_registers.h"
#include "sx127x_internal.h"
#include "sx127x_netdev.h"
#include "sx127x.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
/* Internal helper functions */
static uint8_t _get_tx_len(const struct iovec *vector, unsigned count);
static int _set_state(sx127x_t *dev, netopt_state_t state);
static int _get_state(sx127x_t *dev, void *val);
/* Netdev driver api functions */
static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count);
static int _recv(netdev_t *netdev, void *buf, size_t len, void *info);
static int _init(netdev_t *netdev);
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, void *val, size_t len);
const netdev_driver_t sx127x_driver = {
.send = _send,
.recv = _recv,
.init = _init,
.isr = _isr,
.get = _get,
.set = _set,
};
static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count)
{
sx127x_t *dev = (sx127x_t*) netdev;
if (sx127x_get_state(dev) == SX127X_RF_TX_RUNNING) {
DEBUG("[WARNING] Cannot send packet: radio alredy in transmitting "
"state.\n");
return -ENOTSUP;
}
uint8_t size;
size = _get_tx_len(vector, count);
switch (dev->settings.modem) {
case SX127X_MODEM_FSK:
/* todo */
break;
case SX127X_MODEM_LORA:
/* Initializes the payload size */
sx127x_set_payload_length(dev, size);
/* Full buffer used for Tx */
sx127x_reg_write(dev, SX127X_REG_LR_FIFOTXBASEADDR, 0x00);
sx127x_reg_write(dev, SX127X_REG_LR_FIFOADDRPTR, 0x00);
/* FIFO operations can not take place in Sleep mode
* So wake up the chip */
if (sx127x_get_op_mode(dev) == SX127X_RF_OPMODE_SLEEP) {
sx127x_set_standby(dev);
xtimer_usleep(SX127X_RADIO_WAKEUP_TIME); /* wait for chip wake up */
}
/* Write payload buffer */
for (size_t i = 0;i < count ; i++) {
sx127x_write_fifo(dev, vector[i].iov_base, vector[i].iov_len);
}
break;
default:
puts("sx127x_netdev, Unsupported modem");
break;
}
/* Enable TXDONE interrupt */
sx127x_reg_write(dev, SX127X_REG_LR_IRQFLAGSMASK,
SX127X_RF_LORA_IRQFLAGS_RXTIMEOUT |
SX127X_RF_LORA_IRQFLAGS_RXDONE |
SX127X_RF_LORA_IRQFLAGS_PAYLOADCRCERROR |
SX127X_RF_LORA_IRQFLAGS_VALIDHEADER |
/* SX127X_RF_LORA_IRQFLAGS_TXDONE | */
SX127X_RF_LORA_IRQFLAGS_CADDONE |
SX127X_RF_LORA_IRQFLAGS_FHSSCHANGEDCHANNEL |
SX127X_RF_LORA_IRQFLAGS_CADDETECTED);
/* Set TXDONE interrupt to the DIO0 line */
sx127x_reg_write(dev, SX127X_REG_DIOMAPPING1,
(sx127x_reg_read(dev, SX127X_REG_DIOMAPPING1) &
SX127X_RF_LORA_DIOMAPPING1_DIO0_MASK) |
SX127X_RF_LORA_DIOMAPPING1_DIO0_01);
/* Start TX timeout timer */
xtimer_set(&dev->_internal.tx_timeout_timer, dev->settings.lora.tx_timeout);
/* Put chip into transfer mode */
sx127x_set_state(dev, SX127X_RF_TX_RUNNING);
sx127x_set_op_mode(dev, SX127X_RF_OPMODE_TRANSMITTER);
return 0;
}
static int _recv(netdev_t *netdev, void *buf, size_t len, void *info)
{
sx127x_t *dev = (sx127x_t*) netdev;
volatile uint8_t irq_flags = 0;
uint8_t size = 0;
switch (dev->settings.modem) {
case SX127X_MODEM_FSK:
/* todo */
break;
case SX127X_MODEM_LORA:
/* Clear IRQ */
sx127x_reg_write(dev, SX127X_REG_LR_IRQFLAGS, SX127X_RF_LORA_IRQFLAGS_RXDONE);
irq_flags = sx127x_reg_read(dev, SX127X_REG_LR_IRQFLAGS);
if ( (irq_flags & SX127X_RF_LORA_IRQFLAGS_PAYLOADCRCERROR_MASK) ==
SX127X_RF_LORA_IRQFLAGS_PAYLOADCRCERROR) {
/* Clear IRQ */
sx127x_reg_write(dev, SX127X_REG_LR_IRQFLAGS,
SX127X_RF_LORA_IRQFLAGS_PAYLOADCRCERROR);
if (!(dev->settings.lora.flags & SX127X_RX_CONTINUOUS_FLAG)) {
sx127x_set_state(dev, SX127X_RF_IDLE);
}
xtimer_remove(&dev->_internal.rx_timeout_timer);
netdev->event_callback(netdev, NETDEV_EVENT_CRC_ERROR);
return -EBADMSG;
}
netdev_sx127x_lora_packet_info_t *packet_info = info;
if (packet_info) {
/* there is no LQI for LoRa */
packet_info->lqi = 0;
uint8_t snr_value = sx127x_reg_read(dev, SX127X_REG_LR_PKTSNRVALUE);
if (snr_value & 0x80) { /* The SNR is negative */
/* Invert and divide by 4 */
packet_info->snr = -1 * ((~snr_value + 1) & 0xFF) >> 2;
}
else {
/* Divide by 4 */
packet_info->snr = (snr_value & 0xFF) >> 2;
}
int16_t rssi = sx127x_reg_read(dev, SX127X_REG_LR_PKTRSSIVALUE);
if (packet_info->snr < 0) {
#if defined(MODULE_SX1272)
packet_info->rssi = SX127X_RSSI_OFFSET + rssi + (rssi >> 4) + packet_info->snr;
#else /* MODULE_SX1276 */
if (dev->settings.channel > SX127X_RF_MID_BAND_THRESH) {
packet_info->rssi = SX127X_RSSI_OFFSET_HF + rssi + (rssi >> 4) + packet_info->snr;
}
else {
packet_info->rssi = SX127X_RSSI_OFFSET_LF + rssi + (rssi >> 4) + packet_info->snr;
}
#endif
}
else {
#if defined(MODULE_SX1272)
packet_info->rssi = SX127X_RSSI_OFFSET + rssi + (rssi >> 4);
#else /* MODULE_SX1276 */
if (dev->settings.channel > SX127X_RF_MID_BAND_THRESH) {
packet_info->rssi = SX127X_RSSI_OFFSET_HF + rssi + (rssi >> 4);
}
else {
packet_info->rssi = SX127X_RSSI_OFFSET_LF + rssi + (rssi >> 4);
}
#endif
}
packet_info->time_on_air = sx127x_get_time_on_air(dev, len);
}
size = sx127x_reg_read(dev, SX127X_REG_LR_RXNBBYTES);
if (buf == NULL) {
return size;
}
if (size > len) {
return -ENOBUFS;
}
if (!(dev->settings.lora.flags & SX127X_RX_CONTINUOUS_FLAG)) {
sx127x_set_state(dev, SX127X_RF_IDLE);
}
xtimer_remove(&dev->_internal.rx_timeout_timer);
/* Read the last packet from FIFO */
uint8_t last_rx_addr = sx127x_reg_read(dev, SX127X_REG_LR_FIFORXCURRENTADDR);
sx127x_reg_write(dev, SX127X_REG_LR_FIFOADDRPTR, last_rx_addr);
sx127x_read_fifo(dev, (uint8_t*)buf, size);
break;
default:
break;
}
return size;
}
static int _init(netdev_t *netdev)
{
sx127x_t *sx127x = (sx127x_t*) netdev;
sx127x->irq = 0;
sx127x_radio_settings_t settings;
settings.channel = SX127X_CHANNEL_DEFAULT;
settings.modem = SX127X_MODEM_DEFAULT;
settings.state = SX127X_RF_IDLE;
sx127x->settings = settings;
/* Launch initialization of driver and device */
DEBUG("init_radio: initializing driver...\n");
sx127x_init(sx127x);
sx127x_init_radio_settings(sx127x);
/* Put chip into sleep */
sx127x_set_sleep(sx127x);
DEBUG("init_radio: sx127x initialization done\n");
return 0;
}
static void _isr(netdev_t *netdev)
{
sx127x_t *dev = (sx127x_t *) netdev;
uint8_t irq = dev->irq;
dev->irq = 0;
switch (irq) {
case SX127X_IRQ_DIO0:
sx127x_on_dio0(dev);
break;
case SX127X_IRQ_DIO1:
sx127x_on_dio1(dev);
break;
case SX127X_IRQ_DIO2:
sx127x_on_dio2(dev);
break;
case SX127X_IRQ_DIO3:
sx127x_on_dio3(dev);
break;
default:
break;
}
}
static int _get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len)
{
sx127x_t *dev = (sx127x_t*) netdev;
if (dev == NULL) {
return -ENODEV;
}
switch(opt) {
case NETOPT_STATE:
assert(max_len >= sizeof(netopt_state_t));
return _get_state(dev, val);
case NETOPT_DEVICE_MODE:
assert(max_len >= sizeof(uint8_t));
*((uint8_t*) val) = dev->settings.modem;
return sizeof(uint8_t);
case NETOPT_CHANNEL:
assert(max_len >= sizeof(uint32_t));
*((uint32_t*) val) = sx127x_get_channel(dev);
return sizeof(uint32_t);
case NETOPT_BANDWIDTH:
assert(max_len >= sizeof(uint8_t));
*((uint8_t*) val) = sx127x_get_bandwidth(dev);
return sizeof(uint8_t);
case NETOPT_SPREADING_FACTOR:
assert(max_len >= sizeof(uint8_t));
*((uint8_t*) val) = sx127x_get_spreading_factor(dev);
return sizeof(uint8_t);
case NETOPT_CODING_RATE:
assert(max_len >= sizeof(uint8_t));
*((uint8_t*) val) = sx127x_get_coding_rate(dev);
return sizeof(uint8_t);
case NETOPT_MAX_PACKET_SIZE:
assert(max_len >= sizeof(uint8_t));
*((uint8_t*) val) = sx127x_get_max_payload_len(dev);
return sizeof(uint8_t);
case NETOPT_INTEGRITY_CHECK:
assert(max_len >= sizeof(netopt_enable_t));
*((netopt_enable_t*) val) = sx127x_get_crc(dev) ? NETOPT_ENABLE : NETOPT_DISABLE;
break;
case NETOPT_CHANNEL_HOP:
assert(max_len >= sizeof(netopt_enable_t));
*((netopt_enable_t*) val) = (dev->settings.lora.flags & SX127X_CHANNEL_HOPPING_FLAG) ? NETOPT_ENABLE : NETOPT_DISABLE;
break;
case NETOPT_CHANNEL_HOP_PERIOD:
assert(max_len >= sizeof(uint8_t));
*((uint8_t*) val) = sx127x_get_hop_period(dev);
return sizeof(uint8_t);
case NETOPT_SINGLE_RECEIVE:
assert(max_len >= sizeof(uint8_t));
*((netopt_enable_t*) val) = sx127x_get_rx_single(dev) ? NETOPT_ENABLE : NETOPT_DISABLE;
break;
default:
break;
}
return 0;
}
static int _set(netdev_t *netdev, netopt_t opt, void *val, size_t len)
{
sx127x_t *dev = (sx127x_t*) netdev;
int res = -ENOTSUP;
if (dev == NULL) {
return -ENODEV;
}
switch(opt) {
case NETOPT_STATE:
assert(len <= sizeof(netopt_state_t));
return _set_state(dev, *((netopt_state_t*) val));
case NETOPT_DEVICE_MODE:
assert(len <= sizeof(uint8_t));
sx127x_set_modem(dev, *((uint8_t*) val));
return sizeof(netopt_enable_t);
case NETOPT_CHANNEL:
assert(len <= sizeof(uint32_t));
sx127x_set_channel(dev, *((uint32_t*) val));
return sizeof(uint32_t);
case NETOPT_BANDWIDTH:
assert(len <= sizeof(uint8_t));
uint8_t bw = *((uint8_t *)val);
if (bw < SX127X_BW_125_KHZ ||
bw > SX127X_BW_500_KHZ) {
res = -EINVAL;
break;
}
sx127x_set_bandwidth(dev, bw);
return sizeof(uint8_t);
case NETOPT_SPREADING_FACTOR:
assert(len <= sizeof(uint8_t));
uint8_t sf = *((uint8_t *)val);
if (sf < SX127X_SF6 ||
sf > SX127X_SF12) {
res = -EINVAL;
break;
}
sx127x_set_spreading_factor(dev, sf);
return sizeof(uint8_t);
case NETOPT_CODING_RATE:
assert(len <= sizeof(uint8_t));
uint8_t cr = *((uint8_t *)val);
if (cr < SX127X_CR_4_5 ||
cr > SX127X_CR_4_8) {
res = -EINVAL;
break;
}
sx127x_set_coding_rate(dev, cr);
return sizeof(uint8_t);
case NETOPT_MAX_PACKET_SIZE:
assert(len <= sizeof(uint8_t));
sx127x_set_max_payload_len(dev, *((uint8_t*) val));
return sizeof(uint8_t);
case NETOPT_INTEGRITY_CHECK:
assert(len <= sizeof(netopt_enable_t));
sx127x_set_crc(dev, *((netopt_enable_t*) val) ? true : false);
return sizeof(netopt_enable_t);
case NETOPT_CHANNEL_HOP:
assert(len <= sizeof(netopt_enable_t));
sx127x_set_freq_hop(dev, *((netopt_enable_t*) val) ? true : false);
return sizeof(netopt_enable_t);
case NETOPT_CHANNEL_HOP_PERIOD:
assert(len <= sizeof(uint8_t));
sx127x_set_hop_period(dev, *((uint8_t*) val));
return sizeof(uint8_t);
case NETOPT_SINGLE_RECEIVE:
assert(len <= sizeof(uint8_t));
sx127x_set_rx_single(dev, *((netopt_enable_t*) val) ? true : false);
return sizeof(netopt_enable_t);
case NETOPT_RX_TIMEOUT:
assert(len <= sizeof(uint32_t));
sx127x_set_rx_timeout(dev, *((uint32_t*) val));
return sizeof(uint32_t);
case NETOPT_TX_TIMEOUT:
assert(len <= sizeof(uint32_t));
sx127x_set_tx_timeout(dev, *((uint32_t*) val));
return sizeof(uint32_t);
case NETOPT_TX_POWER:
assert(len <= sizeof(uint8_t));
sx127x_set_tx_power(dev, *((uint8_t*) val));
return sizeof(uint16_t);
case NETOPT_FIXED_HEADER:
assert(len <= sizeof(netopt_enable_t));
sx127x_set_fixed_header_len_mode(dev, *((netopt_enable_t*) val) ? true : false);
return sizeof(netopt_enable_t);
case NETOPT_PREAMBLE_LENGTH:
assert(len <= sizeof(uint16_t));
sx127x_set_preamble_length(dev, *((uint16_t*) val));
return sizeof(uint16_t);
case NETOPT_IQ_INVERT:
assert(len <= sizeof(netopt_enable_t));
sx127x_set_iq_invert(dev, *((netopt_enable_t*) val) ? true : false);
return sizeof(bool);
default:
break;
}
return res;
}
static uint8_t _get_tx_len(const struct iovec *vector, unsigned count)
{
uint8_t len = 0;
for (int i=0 ; i < count ; i++) {
len += vector[i].iov_len;
}
return len;
}
static int _set_state(sx127x_t *dev, netopt_state_t state)
{
switch (state) {
case NETOPT_STATE_SLEEP:
sx127x_set_sleep(dev);
break;
case NETOPT_STATE_STANDBY:
sx127x_set_standby(dev);
break;
case NETOPT_STATE_IDLE:
dev->settings.window_timeout = 0;
/* set permanent listening */
sx127x_set_rx(dev);
break;
case NETOPT_STATE_RX:
sx127x_set_rx(dev);
break;
case NETOPT_STATE_TX:
sx127x_set_tx(dev);
break;
case NETOPT_STATE_RESET:
sx127x_reset(dev);
break;
default:
return -ENOTSUP;
}
return sizeof(netopt_state_t);
}
static int _get_state(sx127x_t *dev, void *val)
{
uint8_t op_mode;
op_mode = sx127x_get_op_mode(dev);
netopt_state_t state;
switch(op_mode) {
case SX127X_RF_OPMODE_SLEEP:
state = NETOPT_STATE_SLEEP;
break;
case SX127X_RF_OPMODE_STANDBY:
state = NETOPT_STATE_STANDBY;
break;
case SX127X_RF_OPMODE_TRANSMITTER:
state = NETOPT_STATE_TX;
break;
case SX127X_RF_OPMODE_RECEIVER:
case SX127X_RF_LORA_OPMODE_RECEIVER_SINGLE:
state = NETOPT_STATE_IDLE;
break;
default:
break;
}
memcpy(val, &state, sizeof(netopt_state_t));
return sizeof(netopt_state_t);
}

View File

@ -76,5 +76,9 @@ PSEUDOMODULES += adc081c
PSEUDOMODULES += adc101c
PSEUDOMODULES += adc121c
# include variants of SX127X drivers as pseudo modules
PSEUDOMODULES += sx1272
PSEUDOMODULES += sx1276
# add all pseudo random number generator variants as pseudomodules
PSEUDOMODULES += prng_%

View File

@ -269,10 +269,81 @@ typedef enum {
*/
NETOPT_LAST_ED_LEVEL,
/**
* @brief Get/Set preamble length as uint16_t in host byte order.
*/
NETOPT_PREAMBLE_LENGTH,
/**
* @brief Enable/disable integrity check (e.g CRC).
*/
NETOPT_INTEGRITY_CHECK,
/**
* @brief Enable/disable channel hopping.
*/
NETOPT_CHANNEL_HOP,
/**
* @brief Get/Set channel hopping period as uint8_t.
*/
NETOPT_CHANNEL_HOP_PERIOD,
/**
* @brief Enable/disable single packet reception.
*
* If enabled, RX is turned off upon reception of a packet
*/
NETOPT_SINGLE_RECEIVE,
/**
* @brief Get/Set the reception timeout of a packet.
*
* Values are retrieved/passed as uint32_t in host byte order.
*/
NETOPT_RX_TIMEOUT,
/**
* @brief Get/Set the transmission timeout of a packet.
*
* Values are retrieved/passed as uint32_t in host byte order.
*/
NETOPT_TX_TIMEOUT,
/**
* @brief Get/Set the radio modem type as uint8_t.
*/
NETOPT_DEVICE_MODE,
/**
* @brief Get/Set the radio modulation bandwidth as uint8_t.
*/
NETOPT_BANDWIDTH,
/**
* @brief Get/Set the radio spreading factor as uint8_t.
*/
NETOPT_SPREADING_FACTOR,
/**
* @brief Get/Set the radio coding rate as uint8_t.
*/
NETOPT_CODING_RATE,
/**
* @brief Enable/disable fixed header mode.
*/
NETOPT_FIXED_HEADER,
/**
* @brief Enable/disable IQ inverted.
*/
NETOPT_IQ_INVERT,
/* add more options if needed */
/**
* @brief maximum number of options defined here
* @brief maximum number of options defined here.
*
* @note Interfaces are not meant to respond to that.
*/
@ -306,6 +377,8 @@ typedef enum {
* transmitting a packet */
NETOPT_STATE_RESET, /**< triggers a hardware reset. The resulting
* state of the network device is @ref NETOPT_STATE_IDLE */
NETOPT_STATE_STANDBY, /**< standby mode. The devices is awake but
* not listening to packets. */
/* add other states if needed */
} netopt_state_t;

View File

@ -24,44 +24,57 @@ extern "C" {
#endif
static const char *_netopt_strmap[] = {
[NETOPT_CHANNEL] = "NETOPT_CHANNEL",
[NETOPT_IS_CHANNEL_CLR] = "NETOPT_IS_CHANNEL_CLR",
[NETOPT_ADDRESS] = "NETOPT_ADDRESS",
[NETOPT_ADDRESS_LONG] = "NETOPT_ADDRESS_LONG",
[NETOPT_ADDR_LEN] = "NETOPT_ADDR_LEN",
[NETOPT_SRC_LEN] = "NETOPT_SRC_LEN",
[NETOPT_NID] = "NETOPT_NID",
[NETOPT_IPV6_IID] = "NETOPT_IPV6_IID",
[NETOPT_TX_POWER] = "NETOPT_TX_POWER",
[NETOPT_MAX_PACKET_SIZE] = "NETOPT_MAX_PACKET_SIZE",
[NETOPT_PRELOADING] = "NETOPT_PRELOADING",
[NETOPT_PROMISCUOUSMODE] = "NETOPT_PROMISCUOUSMODE",
[NETOPT_AUTOACK] = "NETOPT_AUTOACK",
[NETOPT_ACK_REQ] = "NETOPT_ACK_REQ",
[NETOPT_RETRANS] = "NETOPT_RETRANS",
[NETOPT_PROTO] = "NETOPT_PROTO",
[NETOPT_STATE] = "NETOPT_STATE",
[NETOPT_RAWMODE] = "NETOPT_RAWMODE",
[NETOPT_RX_START_IRQ] = "NETOPT_RX_START_IRQ",
[NETOPT_RX_END_IRQ] = "NETOPT_RX_END_IRQ",
[NETOPT_TX_START_IRQ] = "NETOPT_TX_START_IRQ",
[NETOPT_TX_END_IRQ] = "NETOPT_TX_END_IRQ",
[NETOPT_AUTOCCA] = "NETOPT_AUTOCCA",
[NETOPT_CSMA] = "NETOPT_CSMA",
[NETOPT_CSMA_RETRIES] = "NETOPT_CSMA_RETRIES",
[NETOPT_IS_WIRED] = "NETOPT_IS_WIRED",
[NETOPT_DEVICE_TYPE] = "NETOPT_DEVICE_TYPE",
[NETOPT_CHANNEL_PAGE] = "NETOPT_CHANNEL_PAGE",
[NETOPT_CCA_THRESHOLD] = "NETOPT_CCA_THRESHOLD",
[NETOPT_CCA_MODE] = "NETOPT_CCA_MODE",
[NETOPT_STATS] = "NETOPT_STATS",
[NETOPT_ENCRYPTION] = "NETOPT_ENCRYPTION",
[NETOPT_ENCRYPTION_KEY] = "NETOPT_ENCRYPTION_KEY",
[NETOPT_RF_TESTMODE] = "NETOPT_RF_TESTMODE",
[NETOPT_L2FILTER] = "NETOPT_L2FILTER",
[NETOPT_L2FILTER_RM] = "NETOPT_L2FILTER_RM",
[NETOPT_LAST_ED_LEVEL] = "NETOPT_LAST_ED_LEVEL",
[NETOPT_NUMOF] = "NETOPT_NUMOF",
[NETOPT_CHANNEL] = "NETOPT_CHANNEL",
[NETOPT_IS_CHANNEL_CLR] = "NETOPT_IS_CHANNEL_CLR",
[NETOPT_ADDRESS] = "NETOPT_ADDRESS",
[NETOPT_ADDRESS_LONG] = "NETOPT_ADDRESS_LONG",
[NETOPT_ADDR_LEN] = "NETOPT_ADDR_LEN",
[NETOPT_SRC_LEN] = "NETOPT_SRC_LEN",
[NETOPT_NID] = "NETOPT_NID",
[NETOPT_IPV6_IID] = "NETOPT_IPV6_IID",
[NETOPT_TX_POWER] = "NETOPT_TX_POWER",
[NETOPT_MAX_PACKET_SIZE] = "NETOPT_MAX_PACKET_SIZE",
[NETOPT_PRELOADING] = "NETOPT_PRELOADING",
[NETOPT_PROMISCUOUSMODE] = "NETOPT_PROMISCUOUSMODE",
[NETOPT_AUTOACK] = "NETOPT_AUTOACK",
[NETOPT_ACK_REQ] = "NETOPT_ACK_REQ",
[NETOPT_RETRANS] = "NETOPT_RETRANS",
[NETOPT_PROTO] = "NETOPT_PROTO",
[NETOPT_STATE] = "NETOPT_STATE",
[NETOPT_RAWMODE] = "NETOPT_RAWMODE",
[NETOPT_RX_START_IRQ] = "NETOPT_RX_START_IRQ",
[NETOPT_RX_END_IRQ] = "NETOPT_RX_END_IRQ",
[NETOPT_TX_START_IRQ] = "NETOPT_TX_START_IRQ",
[NETOPT_TX_END_IRQ] = "NETOPT_TX_END_IRQ",
[NETOPT_AUTOCCA] = "NETOPT_AUTOCCA",
[NETOPT_CSMA] = "NETOPT_CSMA",
[NETOPT_CSMA_RETRIES] = "NETOPT_CSMA_RETRIES",
[NETOPT_IS_WIRED] = "NETOPT_IS_WIRED",
[NETOPT_DEVICE_TYPE] = "NETOPT_DEVICE_TYPE",
[NETOPT_CHANNEL_PAGE] = "NETOPT_CHANNEL_PAGE",
[NETOPT_CCA_THRESHOLD] = "NETOPT_CCA_THRESHOLD",
[NETOPT_CCA_MODE] = "NETOPT_CCA_MODE",
[NETOPT_STATS] = "NETOPT_STATS",
[NETOPT_ENCRYPTION] = "NETOPT_ENCRYPTION",
[NETOPT_ENCRYPTION_KEY] = "NETOPT_ENCRYPTION_KEY",
[NETOPT_RF_TESTMODE] = "NETOPT_RF_TESTMODE",
[NETOPT_L2FILTER] = "NETOPT_L2FILTER",
[NETOPT_L2FILTER_RM] = "NETOPT_L2FILTER_RM",
[NETOPT_LAST_ED_LEVEL] = "NETOPT_LAST_ED_LEVEL",
[NETOPT_DEVICE_MODE] = "NETOPT_DEVICE_MODE",
[NETOPT_BANDWIDTH] = "NETOPT_BANDWIDTH",
[NETOPT_SPREADING_FACTOR] = "NETOPT_SPREADING_FACTOR",
[NETOPT_CODING_RATE] = "NETOPT_CODING_RATE",
[NETOPT_SINGLE_RECEIVE] = "NETOPT_SINGLE_RECEIVE",
[NETOPT_RX_TIMEOUT] = "NETOPT_RX_TIMEOUT",
[NETOPT_TX_TIMEOUT] = "NETOPT_TX_TIMEOUT",
[NETOPT_PREAMBLE_LENGTH] = "NETOPT_PREAMBLE_LENGTH",
[NETOPT_INTEGRITY_CHECK] = "NETOPT_INTEGRITY_CHECK",
[NETOPT_CHANNEL_HOP] = "NETOPT_CHANNEL_HOP",
[NETOPT_CHANNEL_HOP_PERIOD] = "NETOPT_CHANNEL_HOP_PERIOD",
[NETOPT_FIXED_HEADER] = "NETOPT_FIXED_HEADER",
[NETOPT_IQ_INVERT] = "NETOPT_IQ_INVERT",
[NETOPT_NUMOF] = "NETOPT_NUMOF",
};
const char *netopt2str(netopt_t opt)

View File

@ -0,0 +1,24 @@
APPLICATION = driver_sx127x
include ../Makefile.tests_common
BOARD_INSUFFICIENT_MEMORY := nucleo32-f031
BOARD ?= nucleo-l1
USEMODULE += od
USEMODULE += shell
USEMODULE += shell_commands
USEMODULE += ps
USEMODULE += xtimer
DRIVER ?= sx1276
# use SX1276 by default
USEMODULE += ${DRIVER}
FEATURES_REQUIRED ?= periph_spi
FEATURES_REQUIRED ?= periph_gpio
CFLAGS += -DDEVELHELP
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,134 @@
## About
This is a manual test application for the SX127X radio driver.
This test application uses the default pin configuration provided by the
driver implementation and that matches the ST Nucleo 64 pins layout.
It is best to use [SX1272](https://developer.mbed.org/components/SX1272MB2xAS/)
or [SX1276](https://developer.mbed.org/components/SX1276MB1xAS/) mbed modules
with nucleo boards or the all-in-one
[ST P-NUCLEO-LRWAN1 LoRa kit](http://www.st.com/en/evaluation-tools/p-nucleo-lrwan1.html).
If you have other hardware (boards, Semtech based LoRa module), you can adapt
the configuration to your needs by copying an adapted version of
`drivers/sx127x/include/sx127x_params.h` file to your application directory.
By default the application builds the SX1276 version of the driver. If you
want to use this application with a SX1272 module, set the variable `DRIVER` in
the application [Makefile](Makefile):
```
DRIVER = sx1272
```
instead of
```
DRIVER = sx1276
```
You can also pass `DRIVER` when building the application:
```
$ make BOARD=nucleo-l073 DRIVER=sx1272 -C tests/drivers_sx127x flash term
```
## Usage
This test application provides low level shell commands to interact with the
SX1272/SX1276 modules.
Once the board is flashed and you are connected via serial to the shell, use the `help`
command to display the available commands:
```
> help
help
Command Description
---------------------------------------
setup Initialize LoRa modulation settings
random Get random number from sx127x
channel Get/Set channel frequency (in Hz)
register Get/Set value(s) of registers of sx127x
send Send raw payload string
listen Start raw payload listener
reboot Reboot the node
ps Prints information about running threads.
```
Once the board is booted, use `setup` to configure the basic LoRa settings:
* Bandwidth: 125kHz, 250kHz or 500kHz
* Spreading factor: between 7 and 12
* Code rate: between 5 and 8
Example:
```
> setup 125 12 5
setup: setting 125KHz bandwidth
[Info] setup: configuration set with success
```
All values are supported by both SX1272 and SX1276.
The `random ` command use the Semtech to generate a random integer value.
Example:
```
> random
random: number from sx127x: 2339536315
> random
random: number from sx127x: 863363442
```
The `channel` command allows to change/query the RF frequency channel.
The default is 868MHz for Europe, change to 915MHz for America. The frequency
is given/returned in Hz.
Example:
```
> channel set 868000000
New channel set
> channel get
Channel: 868000000
```
The `register` command allows to get/set the content of the module registers.
Example:
```
> register get all
- listing all registers -
Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F
0x00 00 80 1A 0B 00 52 D9 00 00 0F 08 2B 00 4D 80 00
0x10 00 FF 00 00 00 00 00 00 00 00 00 00 00 72 C4 0A
0x20 00 08 01 FF 00 00 08 00 00 00 00 00 00 50 14 40
0x30 00 03 05 67 1C 0A 00 0A 42 12 64 19 01 A1 00 00
0x40 00 10 22 13 0E 5B DB 24 0E 81 3A 2E 00 03 00 00
0x50 00 00 04 23 01 24 3F B0 09 05 84 0B D0 0B D0 32
0x60 2B 14 00 00 10 00 00 00 0F E0 00 0C F6 10 1D 07
0x70 00 5C 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- done -
```
Use the `send` and `receive` commands in order to exchange messages between several modules.
You need first to ensure that all modules are configured the same: use `setup` and
`channel` commands to configure them correctly.
Assuming you have 2 modules, one listening and one sending messages, do the following:
* On listening module:
```
> setup 125 12 5
setup: setting 125KHz bandwidth
[Info] setup: configuration set with success
> channel set 868000000
New channel set
> listen
Listen mode set
```
* On sending module:
```
> setup 125 12 5
setup: setting 125KHz bandwidth
[Info] setup: configuration set with success
> channel set 868000000
New channel set
> send This\ is\ RIOT!
```
On the listening module, the message is captured:
```
{Payload: "This is RIOT!" (13 bytes), RSSI: 103, SNR: 240}
```

365
tests/driver_sx127x/main.c Normal file
View File

@ -0,0 +1,365 @@
/*
* Copyright (C) 2016 Unwired Devices <info@unwds.com>
* 2017 Inria Chile
* 2017 Inria
*
* 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 SX127X modem driver
*
* @author Eugene P. <ep@unwds.com>
* @author José Ignacio Alamos <jose.alamos@inria.cl>
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
* @}
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "thread.h"
#include "xtimer.h"
#include "shell.h"
#include "shell_commands.h"
#include "net/gnrc/netdev.h"
#include "net/netdev.h"
#include "board.h"
#include "periph/rtc.h"
#include "sx127x_internal.h"
#include "sx127x_params.h"
#include "sx127x_netdev.h"
#define SX127X_LORA_MSG_QUEUE (16U)
#define SX127X_STACKSIZE (THREAD_STACKSIZE_DEFAULT)
#define MSG_TYPE_ISR (0x3456)
static char stack[SX127X_STACKSIZE];
static kernel_pid_t _recv_pid;
static char message[32];
static sx127x_t sx127x;
static netdev_t *netdev;
int lora_setup_cmd(int argc, char **argv) {
if (argc < 4) {
puts("usage: setup "
"<bandwidth (125, 250, 500)> "
"<spreading factor (7..12)> "
"<code rate (5..8)>");
return -1;
}
/* Check bandwidth value */
int bw = atoi(argv[1]);
uint8_t lora_bw;
switch (bw) {
case 125:
puts("setup: setting 125KHz bandwidth");
lora_bw = SX127X_BW_125_KHZ;
break;
case 250:
puts("setup: setting 250KHz bandwidth");
lora_bw = SX127X_BW_250_KHZ;
break;
case 500:
puts("setup: setting 500KHz bandwidth");
lora_bw = SX127X_BW_500_KHZ;
break;
default:
puts("[Error] setup: invalid bandwidth value given, "
"only 125, 250 or 500 allowed.");
return -1;
}
/* Check spreading factor value */
uint8_t lora_sf = atoi(argv[2]);
if (lora_sf < 7 || lora_sf > 12) {
puts("[Error] setup: invalid spreading factor value given");
return -1;
}
/* Check coding rate value */
int cr = atoi(argv[3]);;
if (cr < 5 || cr > 8) {
puts("[Error ]setup: invalid coding rate value given");
return -1;
}
uint8_t lora_cr = (uint8_t)(cr - 4);
/* Configure radio device */
netdev_t *netdev = (netdev_t*) &sx127x;
netdev->driver->set(netdev, NETOPT_BANDWIDTH,
&lora_bw, sizeof(uint8_t));
netdev->driver->set(netdev, NETOPT_SPREADING_FACTOR,
&lora_sf, 1);
netdev->driver->set(netdev, NETOPT_CODING_RATE,
&lora_cr, sizeof(uint8_t));
puts("[Info] setup: configuration set with success");
return 0;
}
int random_cmd(int argc, char **argv)
{
printf("random: number from sx127x: %u\n",
(unsigned int) sx127x_random((sx127x_t*) netdev));
/* reinit the transceiver to default values */
sx127x_init_radio_settings((sx127x_t*) netdev);
return 0;
}
int register_cmd(int argc, char **argv)
{
if (argc < 2) {
puts("usage: register <get | set>");
return -1;
}
if (strstr(argv[1], "get") != NULL) {
if (argc < 3) {
puts("usage: register get <all | allinline | regnum>");
return -1;
}
if (strcmp(argv[2], "all") == 0) {
puts("- listing all registers -");
uint8_t reg = 0, data = 0;
/* Listing registers map */
puts("Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F");
for (unsigned i = 0; i <= 7; i++) {
printf("0x%02X ", i << 4);
for (unsigned j = 0; j <= 15; j++, reg++) {
data = sx127x_reg_read(&sx127x, reg);
printf("%02X ", data);
}
puts("");
}
puts("-done-");
return 0;
} else if (strcmp(argv[2], "allinline") == 0) {
puts("- listing all registers in one line -");
/* Listing registers map */
for (uint16_t reg = 0; reg < 256; reg++) {
printf("%02X ", sx127x_reg_read(&sx127x, (uint8_t) reg));
}
puts("- done -");
return 0;
} else {
long int num = 0;
/* Register number in hex */
if (strstr(argv[2], "0x") != NULL) {
num = strtol(argv[2], NULL, 16);
} else {
num = atoi(argv[2]);
}
if (num >= 0 && num <= 255) {
printf("[regs] 0x%02X = 0x%02X\n",
(uint8_t) num,
sx127x_reg_read(&sx127x, (uint8_t) num));
} else {
puts("regs: invalid register number specified");
return -1;
}
}
} else if (strstr(argv[1], "set") != NULL) {
if (argc < 4) {
puts("usage: register set <regnum> <value>");
return -1;
}
long num, val;
/* Register number in hex */
if (strstr(argv[2], "0x") != NULL) {
num = strtol(argv[2], NULL, 16);
} else {
num = atoi(argv[2]);
}
/* Register value in hex */
if (strstr(argv[3], "0x") != NULL) {
val = strtol(argv[3], NULL, 16);
} else {
val = atoi(argv[3]);
}
sx127x_reg_write(&sx127x, (uint8_t) num, (uint8_t) val);
}
else {
puts("usage: register get <all | allinline | regnum>");
return -1;
}
return 0;
}
int send_cmd(int argc, char **argv)
{
if (argc <= 1) {
puts("usage: send <payload>");
return -1;
}
printf("sending \"%s\" payload (%d bytes)\n",
argv[1], strlen(argv[1]) + 1);
struct iovec vec[1];
vec[0].iov_base = argv[1];
vec[0].iov_len = strlen(argv[1]) + 1;
if (netdev->driver->send(netdev, vec, 1) == -ENOTSUP) {
puts("Cannot send: radio is still transmitting");
}
return 0;
}
int listen_cmd(int argc, char **argv)
{
/* Switch to continuous listen mode */
netdev->driver->set(netdev, NETOPT_SINGLE_RECEIVE, false, sizeof(uint8_t));
sx127x_set_rx(&sx127x);
printf("Listen mode set\n");
return 0;
}
int channel_cmd(int argc, char **argv)
{
if(argc < 2) {
puts("usage: channel <get|set>");
return -1;
}
uint32_t chan;
if (strstr(argv[1], "get") != NULL) {
netdev->driver->get(netdev, NETOPT_CHANNEL, &chan, sizeof(uint32_t));
printf("Channel: %i\n", (int) chan);
return 0;
}
if (strstr(argv[1], "set") != NULL) {
if(argc < 3) {
puts("usage: channel set <channel>");
return -1;
}
chan = atoi(argv[2]);
netdev->driver->set(netdev, NETOPT_CHANNEL, &chan, sizeof(uint32_t));
printf("New channel set\n");
}
else {
puts("usage: channel <get|set>");
return -1;
}
return 0;
}
static const shell_command_t shell_commands[] = {
{ "setup", "Initialize LoRa modulation settings", lora_setup_cmd},
{ "random", "Get random number from sx127x", random_cmd },
{ "channel", "Get/Set channel frequency (in Hz)", channel_cmd },
{ "register", "Get/Set value(s) of registers of sx127x", register_cmd },
{ "send", "Send raw payload string", send_cmd },
{ "listen", "Start raw payload listener", listen_cmd },
{ NULL, NULL, NULL }
};
static void _event_cb(netdev_t *dev, netdev_event_t event)
{
if (event == NETDEV_EVENT_ISR) {
msg_t msg;
msg.type = MSG_TYPE_ISR;
msg.content.ptr = dev;
if (msg_send(&msg, _recv_pid) <= 0) {
puts("gnrc_netdev: possibly lost interrupt.");
}
}
else {
size_t len;
netdev_sx127x_lora_packet_info_t packet_info;
switch (event) {
case NETDEV_EVENT_RX_COMPLETE:
len = dev->driver->recv(dev, NULL, 0, 0);
dev->driver->recv(dev, message, len, &packet_info);
printf("{Payload: \"%s\" (%d bytes), RSSI: %i, SNR: %i, TOA: %i}\n",
message, (int)len,
packet_info.rssi, (int)packet_info.snr,
(int)packet_info.time_on_air);
break;
case NETDEV_EVENT_TX_COMPLETE:
puts("Transmission completed");
break;
case NETDEV_EVENT_CAD_DONE:
break;
case NETDEV_EVENT_TX_TIMEOUT:
break;
default:
printf("Unexpected netdev event received: %d\n", event);
break;
}
}
}
void *_recv_thread(void *arg)
{
static msg_t _msg_q[SX127X_LORA_MSG_QUEUE];
msg_init_queue(_msg_q, SX127X_LORA_MSG_QUEUE);
while (1) {
msg_t msg;
msg_receive(&msg);
if (msg.type == MSG_TYPE_ISR) {
netdev_t *dev = msg.content.ptr;
dev->driver->isr(dev);
}
else {
puts("Unexpected msg type");
}
}
}
int main(void)
{
memcpy(&sx127x.params, sx127x_params, sizeof(sx127x_params));
netdev = (netdev_t*) &sx127x;
netdev->driver = &sx127x_driver;
netdev->driver->init(netdev);
netdev->event_callback = _event_cb;
_recv_pid = thread_create(stack, sizeof(stack), THREAD_PRIORITY_MAIN - 1,
THREAD_CREATE_STACKTEST, _recv_thread, NULL,
"recv_thread");
if (_recv_pid <= KERNEL_PID_UNDEF) {
puts("Creation of receiver thread failed");
return 1;
}
/* start the shell */
puts("Initialization successful - starting the shell now");
char line_buf[SHELL_DEFAULT_BUFSIZE];
shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);
return 0;
}