drivers: add implementation for sx126x lora radio

This commit is contained in:
Alexandre Abadie 2021-03-11 17:28:33 +01:00
parent 4fc3112995
commit 0b5b9bd80f
No known key found for this signature in database
GPG Key ID: 1C919A403CAE1405
12 changed files with 1127 additions and 0 deletions

View File

@ -16,6 +16,7 @@ rsource "ncv7356/Kconfig"
rsource "pn532/Kconfig" rsource "pn532/Kconfig"
rsource "rn2xx3/Kconfig" rsource "rn2xx3/Kconfig"
rsource "slipdev/Kconfig" rsource "slipdev/Kconfig"
rsource "sx126x/Kconfig"
rsource "sx127x/Kconfig" rsource "sx127x/Kconfig"
rsource "tja1042/Kconfig" rsource "tja1042/Kconfig"
source "$(RIOTCPU)/nrf52/radio/nrf802154/Kconfig" source "$(RIOTCPU)/nrf52/radio/nrf802154/Kconfig"

View File

@ -68,6 +68,10 @@ ifneq (,$(filter lis2dh12%,$(USEMODULE)))
USEMODULE += lis2dh12 USEMODULE += lis2dh12
endif endif
ifneq (,$(filter llcc68,$(USEMODULE)))
USEMODULE += sx126x
endif
ifneq (,$(filter lps331ap lps2%hb,$(USEMODULE))) ifneq (,$(filter lps331ap lps2%hb,$(USEMODULE)))
USEMODULE += lpsxxx USEMODULE += lpsxxx
endif endif
@ -147,6 +151,10 @@ ifneq (,$(filter slipdev_%,$(USEMODULE)))
USEMODULE += slipdev USEMODULE += slipdev
endif endif
ifneq (,$(filter sx126%,$(USEMODULE)))
USEMODULE += sx126x
endif
ifneq (,$(filter sx127%,$(USEMODULE))) ifneq (,$(filter sx127%,$(USEMODULE)))
USEMODULE += sx127x USEMODULE += sx127x
endif endif

View File

@ -318,6 +318,7 @@ typedef enum {
NETDEV_ESP_NOW, NETDEV_ESP_NOW,
NETDEV_NRF24L01P_NG, NETDEV_NRF24L01P_NG,
NETDEV_SOCKET_ZEP, NETDEV_SOCKET_ZEP,
NETDEV_SX126X,
/* add more if needed */ /* add more if needed */
} netdev_type_t; } netdev_type_t;
/** @} */ /** @} */

236
drivers/include/sx126x.h Normal file
View File

@ -0,0 +1,236 @@
/*
* Copyright (C) 2021 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_sx126x SX1261/2/8 and LLCC68 LoRa radio driver
* @ingroup drivers_netdev
* @brief Driver for the SX1261/2/8 and LLCC68 LoRa radio device
*
* @{
*
* @file
*
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
*/
#ifndef SX126X_H
#define SX126X_H
#include "sx126x_driver.h"
#include "net/netdev.h"
#include "periph/gpio.h"
#include "periph/spi.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Device initialization parameters
*/
typedef struct {
spi_t spi; /**< SPI device */
gpio_t nss_pin; /**< SPI NSS pin */
gpio_t reset_pin; /**< Reset pin */
gpio_t busy_pin; /**< Busy pin */
gpio_t dio1_pin; /**< Dio1 pin */
sx126x_reg_mod_t regulator; /**< Power regulator mode */
} sx126x_params_t;
/**
* @brief Device descriptor for the driver
*/
typedef struct {
netdev_t netdev; /**< Netdev parent struct */
sx126x_params_t *params; /**< Initialization parameters */
sx126x_pkt_params_lora_t pkt_params; /**< Lora packet parameters */
sx126x_mod_params_lora_t mod_params; /**< Lora modulation parameters */
uint32_t channel; /**< Current channel frequency (in Hz) */
uint32_t rx_timeout; /**< RX timeout in ms */
} sx126x_t;
/**
* @brief Setup the radio device
*
* @param[in] dev Device descriptor
* @param[in] params Parameters for device initialization
* @param[in] index Index of @p params in a global parameter struct array.
* If initialized manually, pass a unique identifier instead.
*/
void sx126x_setup(sx126x_t *dev, const sx126x_params_t *params, uint8_t index);
/**
* @brief Initialize the given device
*
* @param[inout] dev Device descriptor of the driver
*
* @return 0 on success
*/
int sx126x_init(sx126x_t *dev);
/**
* @brief Gets the channel RF frequency.
*
* @param[in] dev Device descriptor of the driver
*
* @return The channel frequency
*/
uint32_t sx126x_get_channel(const sx126x_t *dev);
/**
* @brief Sets the channel RF frequency.
*
* @param[in] dev Device descriptor of the driver
* @param[in] freq Channel RF frequency
*/
void sx126x_set_channel(sx126x_t *dev, uint32_t freq);
/**
* @brief Gets the LoRa bandwidth
*
* @param[in] dev Device descriptor of the driver
*
* @return the bandwidth
*/
uint8_t sx126x_get_bandwidth(const sx126x_t *dev);
/**
* @brief Sets the LoRa bandwidth
*
* @param[in] dev Device descriptor of the driver
* @param[in] bandwidth The new bandwidth
*/
void sx126x_set_bandwidth(sx126x_t *dev, uint8_t bandwidth);
/**
* @brief Gets the LoRa spreading factor
*
* @param[in] dev Device descriptor of the driver
*
* @return the spreading factor
*/
uint8_t sx126x_get_spreading_factor(const sx126x_t *dev);
/**
* @brief Sets the LoRa spreading factor
*
* @param[in] dev Device descriptor of the driver
* @param[in] sf The spreading factor
*/
void sx126x_set_spreading_factor(sx126x_t *dev, uint8_t sf);
/**
* @brief Gets the LoRa coding rate
*
* @param[in] dev Device descriptor of the driver
*
* @return the current LoRa coding rate
*/
uint8_t sx126x_get_coding_rate(const sx126x_t *dev);
/**
* @brief Sets the LoRa coding rate
*
* @param[in] dev Device descriptor of the driver
* @param[in] cr The LoRa coding rate
*/
void sx126x_set_coding_rate(sx126x_t *dev, uint8_t cr);
/**
* @brief Gets the payload length
*
* @param[in] dev Device descriptor of the driver
*
* @return the payload length
*/
uint8_t sx126x_get_lora_payload_length(const sx126x_t *dev);
/**
* @brief Sets the payload length
*
* @param[in] dev Device descriptor of the driver
* @param[in] len The payload len
*/
void sx126x_set_lora_payload_length(sx126x_t *dev, uint8_t len);
/**
* @brief Checks if CRC verification mode is enabled
*
* @param[in] dev Device descriptor of the driver
*
* @return the LoRa single mode
*/
bool sx126x_get_lora_crc(const sx126x_t *dev);
/**
* @brief Enable/Disable CRC verification mode
*
* @param[in] dev Device descriptor of the driver
* @param[in] crc The CRC check mode
*/
void sx126x_set_lora_crc(sx126x_t *dev, bool crc);
/**
* @brief Gets the LoRa implicit header mode
*
* @param[in] dev Device descriptor of the driver
*
* @return the LoRa implicit mode
*/
bool sx126x_get_lora_implicit_header(const sx126x_t *dev);
/**
* @brief Sets LoRa implicit header mode
*
* @param[in] dev Device descriptor of the driver
* @param[in] mode The header mode
*/
void sx126x_set_lora_implicit_header(sx126x_t *dev, bool mode);
/**
* @brief Gets the LoRa preamble length
*
* @param[in] dev Device descriptor of the driver
*
* @return the preamble length
*/
uint16_t sx126x_get_lora_preamble_length(const sx126x_t *dev);
/**
* @brief Sets the LoRa preamble length
*
* @param[in] dev Device descriptor of the driver
* @param[in] preamble The LoRa preamble length
*/
void sx126x_set_lora_preamble_length(sx126x_t *dev, uint16_t preamble);
/**
* @brief Checks if the LoRa inverted IQ mode is enabled/disabled
*
* @param[in] dev Device descriptor of the driver
*
* @return the LoRa IQ inverted mode
*/
bool sx126x_get_lora_iq_invert(const sx126x_t *dev);
/**
* @brief Enable/disable the LoRa IQ inverted mode
*
* @param[in] dev Device descriptor of the driver
* @param[in] iq_invert The LoRa IQ inverted mode
*/
void sx126x_set_lora_iq_invert(sx126x_t *dev, bool iq_invert);
#ifdef __cplusplus
}
#endif
#endif /* SX126X_H */
/** @} */

14
drivers/sx126x/Kconfig Normal file
View File

@ -0,0 +1,14 @@
# Copyright (c) 2021 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.
#
config MODULE_SX126X
bool "SX126X LoRa Sub-GHz radio"
depends on HAS_PERIPH_GPIO_IRQ
depends on PACKAGE_DRIVER_SX126X
depends on TEST_KCONFIG
select MODULE_PERIPH_GPIO_IRQ
select MODULE_IOLIST

1
drivers/sx126x/Makefile Normal file
View File

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

View File

@ -0,0 +1,3 @@
USEMODULE += iolist
USEPKG += driver_sx126x
FEATURES_REQUIRED += periph_gpio_irq

View File

@ -0,0 +1,2 @@
USEMODULE_INCLUDES_sx126x := $(LAST_MAKEFILEDIR)/include
USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_sx126x)

View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2021 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_sx126x
* @{
* @file
* @brief Netdev driver definitions for SX1261/2/8 and LLCC68 driver
*
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
*/
#ifndef SX126X_NETDEV_H
#define SX126X_NETDEV_H
#include "net/netdev.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Reference to the netdev device driver struct
*/
extern const netdev_driver_t sx126x_driver;
#ifdef __cplusplus
}
#endif
#endif /* SX126X_NETDEV_H */
/** @} */

View File

@ -0,0 +1,81 @@
/*
* Copyright (C) 2021 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_sx126x
*
* @{
* @file
* @brief Default configuration
*
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
*/
#ifndef SX126X_PARAMS_H
#define SX126X_PARAMS_H
#include "board.h"
#include "sx126x.h"
#include "sx126x_driver.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name Set default configuration parameters
*
* Default values are adapted for mbed shield used with to nucleo64 boards
* @{
*/
#ifndef SX126X_PARAM_SPI
#define SX126X_PARAM_SPI SPI_DEV(0)
#endif
#ifndef SX126X_PARAM_SPI_NSS
#define SX126X_PARAM_SPI_NSS GPIO_PIN(0, 8) /* D7 */
#endif
#ifndef SX126X_PARAM_RESET
#define SX126X_PARAM_RESET GPIO_PIN(0, 0) /* A0 */
#endif
#ifndef SX126X_PARAM_BUSY
#define SX126X_PARAM_BUSY GPIO_PIN(1, 3) /* D3 */
#endif
#ifndef SX126X_PARAM_DIO1
#define SX126X_PARAM_DIO1 GPIO_PIN(1, 4) /* D5 */
#endif
#ifndef SX126X_PARAM_REGULATOR
#define SX126X_PARAM_REGULATOR SX126X_REG_MODE_DCDC
#endif
#define SX126X_PARAMS { .spi = SX126X_PARAM_SPI, \
.nss_pin = SX126X_PARAM_SPI_NSS, \
.reset_pin = SX126X_PARAM_RESET, \
.busy_pin = SX126X_PARAM_BUSY, \
.dio1_pin = SX126X_PARAM_DIO1, \
.regulator = SX126X_PARAM_REGULATOR }
/**@}*/
/**
* @brief Configuration struct
*/
static const sx126x_params_t sx126x_params[] =
{
SX126X_PARAMS
};
#ifdef __cplusplus
}
#endif
#endif /* SX126X_PARAMS_H */
/** @} */

307
drivers/sx126x/sx126x.c Normal file
View File

@ -0,0 +1,307 @@
/*
* Copyright (C) 2021 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_sx126x
* @{
*
* @file
* @brief Device driver implementation for the SX1261/2/8 and LLCC68 LoRa radio driver
*
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
*
* @}
*/
#include <errno.h>
#include "sx126x_netdev.h"
#include "net/lora.h"
#include "periph/spi.h"
#include "sx126x_driver.h"
#include "sx126x.h"
#include "sx126x_params.h"
#define ENABLE_DEBUG 0
#include "debug.h"
#ifndef CONFIG_SX126X_PKT_TYPE_DEFAULT
#define CONFIG_SX126X_PKT_TYPE_DEFAULT (SX126X_PKT_TYPE_LORA)
#endif
#ifndef CONFIG_SX126X_CHANNEL_DEFAULT
#define CONFIG_SX126X_CHANNEL_DEFAULT (868300000UL) /* in Hz */
#endif
#ifndef CONFIG_SX126X_TX_POWER_DEFAULT
#define CONFIG_SX126X_TX_POWER_DEFAULT (14U) /* in dBm */
#endif
#ifndef CONFIG_SX126X_RAMP_TIME_DEFAULT
#define CONFIG_SX126X_RAMP_TIME_DEFAULT (SX126X_RAMP_10_US)
#endif
void sx126x_setup(sx126x_t *dev, const sx126x_params_t *params, uint8_t index)
{
netdev_t *netdev = (netdev_t *)dev;
netdev->driver = &sx126x_driver;
dev->params = (sx126x_params_t *)params;
netdev_register(&dev->netdev, NETDEV_SX126X, index);
}
static const uint16_t _bw_khz[3] = {
[LORA_BW_125_KHZ] = 125,
[LORA_BW_250_KHZ] = 250,
[LORA_BW_500_KHZ] = 500,
};
static uint8_t _compute_ldro(sx126x_t *dev)
{
uint32_t symbol_len = (uint32_t)(1 << dev->mod_params.sf) / _bw_khz[dev->mod_params.bw - SX126X_LORA_BW_125];
if (symbol_len >= 16) {
return 0x01;
}
return 0x00;
}
static void sx126x_init_default_config(sx126x_t *dev)
{
/* packet type must be set first */
sx126x_set_pkt_type(dev, SX126X_PKT_TYPE_LORA);
sx126x_set_channel(dev, CONFIG_SX126X_CHANNEL_DEFAULT);
/* Configure PA optimal settings for maximum output power
* Values used here comes from the datasheet, section 13.1.14 SetPaConfig
* and are optimal for a TX output power of 14dBm.
*/
if (IS_USED(MODULE_LLCC68) || IS_USED(MODULE_SX1262)) {
sx126x_pa_cfg_params_t pa_cfg = {
.pa_duty_cycle = 0x02,
.hp_max = 0x02,
.device_sel = 0x00,
.pa_lut = 0x01
};
sx126x_set_pa_cfg(dev, &pa_cfg);
}
else if (IS_USED(MODULE_SX1268)) {
sx126x_pa_cfg_params_t pa_cfg = {
.pa_duty_cycle = 0x04,
.hp_max = 0x06,
.device_sel = 0x00,
.pa_lut = 0x01
};
sx126x_set_pa_cfg(dev, &pa_cfg);
}
else { /* IS_USED(MODULE_SX1261) */
sx126x_pa_cfg_params_t pa_cfg = {
.pa_duty_cycle = 0x04,
.hp_max = 0x00,
.device_sel = 0x01,
.pa_lut = 0x01
};
sx126x_set_pa_cfg(dev, &pa_cfg);
}
sx126x_set_tx_params(dev, CONFIG_SX126X_TX_POWER_DEFAULT, CONFIG_SX126X_RAMP_TIME_DEFAULT);
dev->mod_params.bw = (sx126x_lora_bw_t)(CONFIG_LORA_BW_DEFAULT + SX126X_LORA_BW_125);
dev->mod_params.sf = (sx126x_lora_sf_t)CONFIG_LORA_SF_DEFAULT;
dev->mod_params.cr = (sx126x_lora_cr_t)CONFIG_LORA_CR_DEFAULT;
dev->mod_params.ldro = _compute_ldro(dev);
sx126x_set_lora_mod_params(dev, &dev->mod_params);
dev->pkt_params.pld_len_in_bytes = 0;
dev->pkt_params.crc_is_on = LORA_PAYLOAD_CRC_ON_DEFAULT;
dev->pkt_params.header_type = (
IS_ACTIVE(CONFIG_LORA_FIXED_HEADER_LEN_MODE_DEFAULT) ? true : false
);
dev->pkt_params.preamble_len_in_symb = CONFIG_LORA_PREAMBLE_LENGTH_DEFAULT;
dev->pkt_params.invert_iq_is_on = (
IS_ACTIVE(CONFIG_LORA_IQ_INVERTED_DEFAULT) ? true : false
);
sx126x_set_lora_pkt_params(dev, &dev->pkt_params);
}
static void _dio1_isr(void *arg)
{
netdev_trigger_event_isr((netdev_t *)arg);
}
int sx126x_init(sx126x_t *dev)
{
/* Setup SPI for SX126X */
int res = spi_init_cs(dev->params->spi, dev->params->nss_pin);
if (res != SPI_OK) {
DEBUG("[sx126x] error: failed to initialize SPI_%i device (code %i)\n",
dev->params->spi, res);
return -1;
}
DEBUG("[sx126x] init: SPI_%i initialized with success\n", dev->params->spi);
gpio_init(dev->params->reset_pin, GPIO_OUT);
gpio_init(dev->params->busy_pin, GPIO_IN_PD);
/* Initialize DIOs */
if (gpio_is_valid(dev->params->dio1_pin)) {
res = gpio_init_int(dev->params->dio1_pin, GPIO_IN, GPIO_RISING, _dio1_isr, dev);
if (res < 0) {
DEBUG("[sx126x] error: failed to initialize DIO1 pin\n");
return res;
}
}
else {
DEBUG("[sx126x] error: no DIO1 pin defined\n");
return -EIO;
}
/* Reset the device */
sx126x_reset(dev);
/* Configure the power regulator mode */
sx126x_set_reg_mode(dev, dev->params->regulator);
/* Initialize radio with the default parameters */
sx126x_init_default_config(dev);
/* Configure available IRQs */
const uint16_t irq_mask = (
SX126X_IRQ_TX_DONE |
SX126X_IRQ_RX_DONE |
SX126X_IRQ_PREAMBLE_DETECTED |
SX126X_IRQ_HEADER_VALID |
SX126X_IRQ_HEADER_ERROR |
SX126X_IRQ_CRC_ERROR |
SX126X_IRQ_CAD_DONE |
SX126X_IRQ_CAD_DETECTED |
SX126X_IRQ_TIMEOUT
);
sx126x_set_dio_irq_params(dev, irq_mask, irq_mask, 0, 0);
if (IS_ACTIVE(ENABLE_DEBUG)) {
sx126x_pkt_type_t pkt_type;
sx126x_get_pkt_type(dev, &pkt_type);
DEBUG("[sx126x] init radio: pkt type: %d\n", pkt_type);
sx126x_chip_status_t radio_status;
sx126x_get_status(dev, &radio_status);
DEBUG("[sx126x] init: chip mode %d\n", radio_status.chip_mode);
DEBUG("[sx126x] init: cmd status %d\n", radio_status.cmd_status);
}
return res;
}
uint32_t sx126x_get_channel(const sx126x_t *dev)
{
return dev->channel;
}
void sx126x_set_channel(sx126x_t *dev, uint32_t freq)
{
dev->channel = freq;
sx126x_set_rf_freq(dev, dev->channel);
}
uint8_t sx126x_get_bandwidth(const sx126x_t *dev)
{
return dev->mod_params.bw - SX126X_LORA_BW_125;
}
void sx126x_set_bandwidth(sx126x_t *dev, uint8_t bandwidth)
{
dev->mod_params.bw = bandwidth + SX126X_LORA_BW_125;
dev->mod_params.ldro = _compute_ldro(dev);
sx126x_set_lora_mod_params(dev, &dev->mod_params);
}
uint8_t sx126x_get_spreading_factor(const sx126x_t *dev)
{
return dev->mod_params.sf;
}
void sx126x_set_spreading_factor(sx126x_t *dev, uint8_t sf)
{
dev->mod_params.sf = (sx126x_lora_sf_t)sf;
dev->mod_params.ldro = _compute_ldro(dev);
sx126x_set_lora_mod_params(dev, &dev->mod_params);
}
uint8_t sx126x_get_coding_rate(const sx126x_t *dev)
{
return dev->mod_params.cr;
}
void sx126x_set_coding_rate(sx126x_t *dev, uint8_t cr)
{
dev->mod_params.cr = (sx126x_lora_cr_t)cr;
sx126x_set_lora_mod_params(dev, &dev->mod_params);
}
uint8_t sx126x_get_lora_payload_length(const sx126x_t *dev)
{
sx126x_rx_buffer_status_t rx_buffer_status;
sx126x_get_rx_buffer_status(dev, &rx_buffer_status);
return rx_buffer_status.pld_len_in_bytes;
}
void sx126x_set_lora_payload_length(sx126x_t *dev, uint8_t len)
{
dev->pkt_params.pld_len_in_bytes = len;
sx126x_set_lora_pkt_params(dev, &dev->pkt_params);
}
bool sx126x_get_lora_crc(const sx126x_t *dev)
{
return dev->pkt_params.crc_is_on;
}
void sx126x_set_lora_crc(sx126x_t *dev, bool crc)
{
dev->pkt_params.crc_is_on = crc;
sx126x_set_lora_pkt_params(dev, &dev->pkt_params);
}
bool sx126x_get_lora_implicit_header(const sx126x_t *dev)
{
return dev->pkt_params.header_type == SX126X_LORA_PKT_IMPLICIT;
}
void sx126x_set_lora_implicit_header(sx126x_t *dev, bool mode)
{
dev->pkt_params.header_type = (mode ? SX126X_LORA_PKT_IMPLICIT : SX126X_LORA_PKT_EXPLICIT);
sx126x_set_lora_pkt_params(dev, &dev->pkt_params);
}
uint16_t sx126x_get_lora_preamble_length(const sx126x_t *dev)
{
return dev->pkt_params.preamble_len_in_symb;
}
void sx126x_set_lora_preamble_length(sx126x_t *dev, uint16_t preamble)
{
dev->pkt_params.preamble_len_in_symb = preamble;
sx126x_set_lora_pkt_params(dev, &dev->pkt_params);
}
bool sx126x_get_lora_iq_invert(const sx126x_t *dev)
{
return dev->pkt_params.invert_iq_is_on;
}
void sx126x_set_lora_iq_invert(sx126x_t *dev, bool iq_invert)
{
dev->pkt_params.invert_iq_is_on = iq_invert;
sx126x_set_lora_pkt_params(dev, &dev->pkt_params);
}

View File

@ -0,0 +1,436 @@
/*
* Copyright (C) 2021 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_sx126x
* @{
* @file
* @brief Netdev adaptation for the SX1261/2/8 and LLCC68 driver
*
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
* @}
*/
#include <assert.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include "iolist.h"
#include "net/netopt.h"
#include "net/netdev.h"
#include "net/netdev/lora.h"
#include "net/lora.h"
#include "sx126x.h"
#include "sx126x_netdev.h"
#define ENABLE_DEBUG 0
#include "debug.h"
#if IS_USED(MODULE_LLCC68)
#define SX126X_MAX_SF LORA_SF11
#else
#define SX126X_MAX_SF LORA_SF12
#endif
static int _send(netdev_t *netdev, const iolist_t *iolist)
{
sx126x_t *dev = (sx126x_t *)netdev;
netopt_state_t state;
netdev->driver->get(netdev, NETOPT_STATE, &state, sizeof(uint8_t));
if (state == NETOPT_STATE_TX) {
DEBUG("[sx126x] netdev: cannot send packet, radio is already transmitting.\n");
return -ENOTSUP;
}
uint8_t size = iolist_size(iolist);
/* Ignore send if packet size is 0 */
if (!size) {
return 0;
}
DEBUG("[sx126x] netdev: sending packet now (size: %d).\n", size);
/* Write payload buffer */
for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) {
if (iol->iol_len > 0) {
sx126x_set_lora_payload_length(dev, iol->iol_len);
sx126x_write_buffer(dev, 0, iol->iol_base, iol->iol_len);
DEBUG("[sx126x] netdev: send: wrote data to payload buffer.\n");
}
}
state = NETOPT_STATE_TX;
netdev->driver->set(netdev, NETOPT_STATE, &state, sizeof(uint8_t));
DEBUG("[sx126x] netdev: send: transmission in progress.\n");
return 0;
}
static int _recv(netdev_t *netdev, void *buf, size_t len, void *info)
{
DEBUG("[sx126x] netdev: read received data.\n");
sx126x_t *dev = (sx126x_t *)netdev;
uint8_t size = 0;
netdev_lora_rx_info_t *packet_info = (netdev_lora_rx_info_t *)info;
if (packet_info) {
sx126x_pkt_status_lora_t pkt_status;
sx126x_get_lora_pkt_status(dev, &pkt_status);
packet_info->snr = pkt_status.snr_pkt_in_db;
packet_info->rssi = pkt_status.rssi_pkt_in_dbm;
}
sx126x_rx_buffer_status_t rx_buffer_status;
sx126x_get_rx_buffer_status(dev, &rx_buffer_status);
size = rx_buffer_status.pld_len_in_bytes;
if (buf == NULL) {
return size;
}
if (size > len) {
return -ENOBUFS;
}
sx126x_read_buffer(dev, rx_buffer_status.buffer_start_pointer, buf, size);
return 0;
}
static int _init(netdev_t *netdev)
{
sx126x_t *dev = (sx126x_t *)netdev;
/* Launch initialization of driver and device */
DEBUG("[sx126x] netdev: initializing driver...\n");
if (sx126x_init(dev) != 0) {
DEBUG("[sx126x] netdev: initialization failed\n");
return -1;
}
DEBUG("[sx126x] netdev: initialization successful\n");
return 0;
}
static void _isr(netdev_t *netdev)
{
sx126x_t *dev = (sx126x_t *)netdev;
sx126x_irq_mask_t irq_mask;
sx126x_get_and_clear_irq_status(dev, &irq_mask);
if (irq_mask & SX126X_IRQ_TX_DONE) {
DEBUG("[sx126x] netdev: SX126X_IRQ_TX_DONE\n");
netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE);
}
else if (irq_mask & SX126X_IRQ_RX_DONE) {
DEBUG("[sx126x] netdev: SX126X_IRQ_RX_DONE\n");
netdev->event_callback(netdev, NETDEV_EVENT_RX_COMPLETE);
}
else if (irq_mask & SX126X_IRQ_PREAMBLE_DETECTED) {
DEBUG("[sx126x] netdev: SX126X_IRQ_PREAMBLE_DETECTED\n");
}
else if (irq_mask & SX126X_IRQ_SYNC_WORD_VALID) {
DEBUG("[sx126x] netdev: SX126X_IRQ_SYNC_WORD_VALID\n");
}
else if (irq_mask & SX126X_IRQ_HEADER_VALID) {
DEBUG("[sx126x] netdev: SX126X_IRQ_HEADER_VALID\n");
netdev->event_callback(netdev, NETDEV_EVENT_RX_STARTED);
}
else if (irq_mask & SX126X_IRQ_HEADER_ERROR) {
DEBUG("[sx126x] netdev: SX126X_IRQ_HEADER_ERROR\n");
}
else if (irq_mask & SX126X_IRQ_CRC_ERROR) {
DEBUG("[sx126x] netdev: SX126X_IRQ_CRC_ERROR\n");
netdev->event_callback(netdev, NETDEV_EVENT_CRC_ERROR);
}
else if (irq_mask & SX126X_IRQ_CAD_DONE) {
DEBUG("[sx126x] netdev: SX126X_IRQ_CAD_DONE\n");
netdev->event_callback(netdev, NETDEV_EVENT_CAD_DONE);
}
else if (irq_mask & SX126X_IRQ_CAD_DETECTED) {
DEBUG("[sx126x] netdev: SX126X_IRQ_CAD_DETECTED\n");
}
else if (irq_mask & SX126X_IRQ_TIMEOUT) {
DEBUG("[sx126x] netdev: SX126X_IRQ_TIMEOUT\n");
netdev->event_callback(netdev, NETDEV_EVENT_RX_TIMEOUT);
}
else {
DEBUG("[sx126x] netdev: SX126X_IRQ_NONE\n");
}
}
static int _get_state(sx126x_t *dev, void *val)
{
sx126x_chip_status_t radio_status;
sx126x_get_status(dev, &radio_status);
netopt_state_t state = NETOPT_STATE_OFF;
switch (radio_status.chip_mode) {
case SX126X_CHIP_MODE_RFU:
case SX126X_CHIP_MODE_STBY_RC:
case SX126X_CHIP_MODE_STBY_XOSC:
state = NETOPT_STATE_STANDBY;
break;
case SX126X_CHIP_MODE_TX:
state = NETOPT_STATE_TX;
break;
case SX126X_CHIP_MODE_RX:
state = NETOPT_STATE_RX;
break;
default:
break;
}
memcpy(val, &state, sizeof(netopt_state_t));
return sizeof(netopt_state_t);
}
static int _get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len)
{
(void)max_len; /* unused when compiled without debug, assert empty */
sx126x_t *dev = (sx126x_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_TYPE:
assert(max_len >= sizeof(uint16_t));
sx126x_pkt_type_t pkt_type;
sx126x_get_pkt_type(dev, &pkt_type);
*((uint16_t *)val) =
(pkt_type == SX126X_PKT_TYPE_LORA) ? NETDEV_TYPE_LORA : NETDEV_TYPE_UNKNOWN;
return sizeof(uint16_t);
case NETOPT_CHANNEL_FREQUENCY:
assert(max_len >= sizeof(uint32_t));
*((uint32_t *)val) = sx126x_get_channel(dev);
return sizeof(uint32_t);
case NETOPT_BANDWIDTH:
assert(max_len >= sizeof(uint8_t));
*((uint8_t *)val) = sx126x_get_bandwidth(dev);
return sizeof(uint8_t);
case NETOPT_SPREADING_FACTOR:
assert(max_len >= sizeof(uint8_t));
*((uint8_t *)val) = sx126x_get_spreading_factor(dev);
return sizeof(uint8_t);
case NETOPT_CODING_RATE:
assert(max_len >= sizeof(uint8_t));
*((uint8_t *)val) = sx126x_get_coding_rate(dev);
return sizeof(uint8_t);
case NETOPT_PDU_SIZE:
assert(max_len >= sizeof(uint8_t));
*((uint8_t *)val) = sx126x_get_lora_payload_length(dev);
return sizeof(uint8_t);
case NETOPT_INTEGRITY_CHECK:
assert(max_len >= sizeof(netopt_enable_t));
*((netopt_enable_t *)val) = sx126x_get_lora_crc(dev) ? NETOPT_ENABLE : NETOPT_DISABLE;
return sizeof(netopt_enable_t);
case NETOPT_RANDOM:
assert(max_len >= sizeof(uint32_t));
sx126x_get_random_numbers(dev, val, 1);
return sizeof(uint32_t);
case NETOPT_IQ_INVERT:
assert(max_len >= sizeof(uint8_t));
*((netopt_enable_t *)val) = sx126x_get_lora_iq_invert(dev) ? NETOPT_ENABLE : NETOPT_DISABLE;
return sizeof(netopt_enable_t);
case NETOPT_RSSI:
assert(max_len >= sizeof(int8_t));
sx126x_get_rssi_inst(dev, ((int16_t *)val));
return sizeof(int8_t);
default:
break;
}
return -ENOTSUP;
}
static int _set_state(sx126x_t *dev, netopt_state_t state)
{
switch (state) {
case NETOPT_STATE_STANDBY:
DEBUG("[sx126x] netdev: set NETOPT_STATE_STANDBY state\n");
sx126x_set_standby(dev, SX126X_CHIP_MODE_STBY_XOSC);
break;
case NETOPT_STATE_IDLE:
case NETOPT_STATE_RX:
DEBUG("[sx126x] netdev: set NETOPT_STATE_RX state\n");
sx126x_cfg_rx_boosted(dev, true);
if (dev->rx_timeout != 0) {
sx126x_set_rx(dev, dev->rx_timeout);
}
else {
sx126x_set_rx(dev, SX126X_RX_SINGLE_MODE);
}
break;
case NETOPT_STATE_TX:
DEBUG("[sx126x] netdev: set NETOPT_STATE_TX state\n");
sx126x_set_tx(dev, 0);
break;
case NETOPT_STATE_RESET:
DEBUG("[sx126x] netdev: set NETOPT_STATE_RESET state\n");
sx126x_reset(dev);
break;
default:
return -ENOTSUP;
}
return sizeof(netopt_state_t);
}
static int _set(netdev_t *netdev, netopt_t opt, const void *val, size_t len)
{
(void)len; /* unused when compiled without debug, assert empty */
sx126x_t *dev = (sx126x_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, *((const netopt_state_t *)val));
case NETOPT_DEVICE_TYPE:
assert(len <= sizeof(uint16_t));
/* Only LoRa modem is supported for the moment */
if (*(const uint16_t *)val == NETDEV_TYPE_LORA) {
sx126x_set_pkt_type(dev, SX126X_PKT_TYPE_LORA);
return sizeof(uint16_t);
}
else {
return -EINVAL;
}
case NETOPT_CHANNEL_FREQUENCY:
assert(len <= sizeof(uint32_t));
sx126x_set_channel(dev, *((const uint32_t *)val));
return sizeof(uint32_t);
case NETOPT_BANDWIDTH:
assert(len <= sizeof(uint8_t));
uint8_t bw = *((const uint8_t *)val);
if (bw > LORA_BW_500_KHZ) {
res = -EINVAL;
break;
}
sx126x_set_bandwidth(dev, bw);
return sizeof(uint8_t);
case NETOPT_SPREADING_FACTOR:
assert(len <= sizeof(uint8_t));
uint8_t sf = *((const uint8_t *)val);
if ((sf < LORA_SF6) || (sf > SX126X_MAX_SF)) {
res = -EINVAL;
break;
}
sx126x_set_spreading_factor(dev, sf);
return sizeof(uint8_t);
case NETOPT_CODING_RATE:
assert(len <= sizeof(uint8_t));
uint8_t cr = *((const uint8_t *)val);
if ((cr < LORA_CR_4_5) || (cr > LORA_CR_4_8)) {
res = -EINVAL;
break;
}
sx126x_set_coding_rate(dev, cr);
return sizeof(uint8_t);
case NETOPT_PDU_SIZE:
assert(len <= sizeof(uint8_t));
sx126x_set_lora_payload_length(dev, *((const uint8_t *)val));
return sizeof(uint8_t);
case NETOPT_INTEGRITY_CHECK:
assert(len <= sizeof(netopt_enable_t));
sx126x_set_lora_crc(dev, *((const netopt_enable_t *)val) ? true : false);
return sizeof(netopt_enable_t);
case NETOPT_RX_TIMEOUT:
assert(len <= sizeof(uint32_t));
dev->rx_timeout = *(const uint32_t *)val;
return sizeof(uint32_t);
case NETOPT_TX_POWER:
assert(len <= sizeof(int16_t));
int16_t power = *((const int16_t *)val);
if ((power < INT8_MIN) || (power > INT8_MAX)) {
res = -EINVAL;
break;
}
sx126x_set_tx_params(dev, power, SX126X_RAMP_10_US);
return sizeof(int16_t);
case NETOPT_FIXED_HEADER:
assert(len <= sizeof(netopt_enable_t));
sx126x_set_lora_implicit_header(dev, *((const netopt_enable_t *)val) ? true : false);
return sizeof(netopt_enable_t);
case NETOPT_PREAMBLE_LENGTH:
assert(len <= sizeof(uint16_t));
sx126x_set_lora_preamble_length(dev, *((const uint16_t *)val));
return sizeof(uint16_t);
case NETOPT_SYNCWORD:
assert(len <= sizeof(uint8_t));
sx126x_set_lora_sync_word(dev, *((uint8_t *)val));
return sizeof(uint8_t);
case NETOPT_IQ_INVERT:
assert(len <= sizeof(netopt_enable_t));
sx126x_set_lora_iq_invert(dev, *((const netopt_enable_t *)val) ? true : false);
return sizeof(bool);
default:
break;
}
return res;
}
const netdev_driver_t sx126x_driver = {
.send = _send,
.recv = _recv,
.init = _init,
.isr = _isr,
.get = _get,
.set = _set,
};