From 8e86382e29e0f93ee599073b6cfa6449c98ad9bd Mon Sep 17 00:00:00 2001 From: Aymeric Brochier Date: Tue, 17 May 2022 15:30:01 +0200 Subject: [PATCH 1/4] sys/include/net/lora: add 2.4Ghz BW definitions --- sys/include/net/lora.h | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/sys/include/net/lora.h b/sys/include/net/lora.h index af41ac1046c2..bbe94b88c6fc 100644 --- a/sys/include/net/lora.h +++ b/sys/include/net/lora.h @@ -75,6 +75,14 @@ extern "C" { #define CONFIG_LORA_BW_DEFAULT (LORA_BW_250_KHZ) #elif IS_ACTIVE(CONFIG_LORA_BW_DEFAULT_500) #define CONFIG_LORA_BW_DEFAULT (LORA_BW_500_KHZ) +#elif IS_ACTIVE(CONFIG_LORA_BW_DEFAULT_200) +#define CONFIG_LORA_BW_DEFAULT (LORA_BW_200_KHZ) +#elif IS_ACTIVE(CONFIG_LORA_BW_DEFAULT_400) +#define CONFIG_LORA_BW_DEFAULT (LORA_BW_400_KHZ) +#elif IS_ACTIVE(CONFIG_LORA_BW_DEFAULT_800) +#define CONFIG_LORA_BW_DEFAULT (LORA_BW_800_KHZ) +#elif IS_ACTIVE(CONFIG_LORA_BW_DEFAULT_1600) +#define CONFIG_LORA_BW_DEFAULT (LORA_BW_1600_KHZ) #endif #ifndef CONFIG_LORA_BW_DEFAULT @@ -131,6 +139,12 @@ extern "C" { #define CONFIG_LORA_CR_DEFAULT (LORA_CR_4_7) #elif IS_ACTIVE(CONFIG_LORA_CR_DEFAULT_CR_4_8) #define CONFIG_LORA_CR_DEFAULT (LORA_CR_4_8) +#elif IS_ACTIVE(CONFIG_LORA_CR_DEFAULT_CR_LI_4_5) +#define CONFIG_LORA_CR_DEFAULT (LORA_CR_LI_4_5) +#elif IS_ACTIVE(CONFIG_LORA_CR_DEFAULT_CR_LI_4_6) +#define CONFIG_LORA_CR_DEFAULT (LORA_CR_LI_4_6) +#elif IS_ACTIVE(CONFIG_LORA_CR_DEFAULT_CR_LI_4_8) +#define CONFIG_LORA_CR_DEFAULT (LORA_CR_LI_4_8) #endif #ifndef CONFIG_LORA_CR_DEFAULT @@ -201,6 +215,18 @@ extern "C" { */ #define LORA_SYNCWORD_PUBLIC (0x34) /**< Syncword used for public networks */ #define LORA_SYNCWORD_PRIVATE (0x12) /**< Syncword used for private networks */ +/* see https://lora-developers.semtech.com/documentation/tech-papers-and-guides/physical-layer-proposal-2.4ghz */ +#define LORA_SYNCWORD_ISM2400_PUBLIC (0x21) /**< Syncword used for public networks on ISM2400 */ +/** @} */ + +/** + * @name LoRa 2.4Ghz band frequency definitions + * + * See https://lora-developers.semtech.com/documentation/tech-papers-and-guides/physical-layer-proposal-2.4ghz/ + * @{ + */ +#define LORA_ISM2400_FREQUENCY_LOW 2400000000 /**< lowest frequency ISM24000 band */ +#define LORA_ISM2400_FREQUENCY_HIGH 2500000000 /**< highest frequency ISM24000 band */ /** @} */ /** @@ -213,14 +239,19 @@ extern "C" { enum { LORA_BW_125_KHZ = 0, /**< 125 kHz bandwidth */ LORA_BW_250_KHZ, /**< 250 kHz bandwidth */ - LORA_BW_500_KHZ /**< 500 kHz bandwidth */ + LORA_BW_500_KHZ, /**< 500 kHz bandwidth */ + LORA_BW_200_KHZ, /**< 200 kHz bandwidth, only 2.4Ghz */ + LORA_BW_400_KHZ, /**< 400 kHz bandwidth, only 2.4Ghz */ + LORA_BW_800_KHZ, /**< 800 kHz bandwidth, only 2.4Ghz */ + LORA_BW_1600_KHZ, /**< 1600 kHz bandwidth, only 2.4Ghz */ }; /** * @brief LoRa modulation spreading factor rate */ enum { - LORA_SF6 = 6, /**< spreading factor 6 */ + LORA_SF5 = 5, /**< spreading factor 5, sx126x and sx1280 */ + LORA_SF6, /**< spreading factor 6 */ LORA_SF7, /**< spreading factor 7 */ LORA_SF8, /**< spreading factor 8 */ LORA_SF9, /**< spreading factor 9 */ @@ -236,7 +267,10 @@ enum { LORA_CR_4_5 = 1, /**< coding rate 4/5 */ LORA_CR_4_6, /**< coding rate 4/6 */ LORA_CR_4_7, /**< coding rate 4/7 */ - LORA_CR_4_8 /**< coding rate 4/8 */ + LORA_CR_4_8, /**< coding rate 4/8 */ + LORA_CR_LI_4_5, /**< coding rate long interleaving 4/5 */ + LORA_CR_LI_4_6, /**< coding rate long interleaving 4/6 */ + LORA_CR_LI_4_8 /**< coding rate long interleaving 4/8 */ }; /** @} */ From 1b5addd1fdf3c1ed04562f0caab3f18107d95df6 Mon Sep 17 00:00:00 2001 From: Aymeric Brochier Date: Tue, 17 May 2022 15:33:08 +0200 Subject: [PATCH 2/4] pkg/lorabasics: initial import --- pkg/Kconfig | 1 + pkg/lorabasics/Kconfig | 33 +++++ pkg/lorabasics/Makefile | 23 +++ pkg/lorabasics/Makefile.dep | 11 ++ pkg/lorabasics/Makefile.include | 11 ++ pkg/lorabasics/doc.txt | 13 ++ pkg/lorabasics/driver_sx1280_hal/Makefile | 3 + .../driver_sx1280_hal/driver_sx1280_hal.c | 136 ++++++++++++++++++ ...s-remove-const-from-radio_type-tcxo_.patch | 27 ++++ .../0002-treewide-fix-include-paths.patch | 68 +++++++++ 10 files changed, 326 insertions(+) create mode 100644 pkg/lorabasics/Kconfig create mode 100644 pkg/lorabasics/Makefile create mode 100644 pkg/lorabasics/Makefile.dep create mode 100644 pkg/lorabasics/Makefile.include create mode 100644 pkg/lorabasics/doc.txt create mode 100644 pkg/lorabasics/driver_sx1280_hal/Makefile create mode 100644 pkg/lorabasics/driver_sx1280_hal/driver_sx1280_hal.c create mode 100644 pkg/lorabasics/patches/0001-smtc_ral-ral_defs-remove-const-from-radio_type-tcxo_.patch create mode 100644 pkg/lorabasics/patches/0002-treewide-fix-include-paths.patch diff --git a/pkg/Kconfig b/pkg/Kconfig index 2de9bb54d2b8..8bc7e16e6fb1 100644 --- a/pkg/Kconfig +++ b/pkg/Kconfig @@ -38,6 +38,7 @@ rsource "libcose/Kconfig" rsource "libfixmath/Kconfig" rsource "libhydrogen/Kconfig" rsource "littlefs2/Kconfig" +rsource "lorabasics/Kconfig" rsource "lora-serialization/Kconfig" rsource "lua/Kconfig" rsource "lv_drivers/Kconfig" diff --git a/pkg/lorabasics/Kconfig b/pkg/lorabasics/Kconfig new file mode 100644 index 000000000000..aeeb28828d3d --- /dev/null +++ b/pkg/lorabasics/Kconfig @@ -0,0 +1,33 @@ +# Copyright (c) 2022 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 PACKAGE_LORABASICS + bool "LoRa Basics" + depends on TEST_KCONFIG + # depends on HAS_ARCH_32_BIT + select MODULE_LORABASICS_SMTC_RAL + +if PACKAGE_LORABASICS + +config MODULE_LORABASICS_SX1280_DRIVER + bool "LoRaBasics SX1280 driver code" + depends on HAS_PERIPH_SPI + depends on HAS_PERIPH_GPIO + depends on HAS_PERIPH_GPIO_IRQ + select MODULE_PERIPH_SPI + select MODULE_PERIPH_GPIO + select MODULE_PERIPH_GPIO_IRQ + select MODULE_ZTIMER + select MODULE_ZTIMER_MSEC + select MODULE_LORABASICS_DRIVER_SX1280_HAL + +config MODULE_LORABASICS_DRIVER_SX1280_HAL + bool "LoRaBasicsModem SX1280 driver hal" + +config MODULE_LORABASICS_SMTC_RAL + bool "LoRaBasicsModem Radio Abstraction Layer (RAL)" + +endif # PACKAGE_LORABASICS diff --git a/pkg/lorabasics/Makefile b/pkg/lorabasics/Makefile new file mode 100644 index 000000000000..282eeaa8b2d3 --- /dev/null +++ b/pkg/lorabasics/Makefile @@ -0,0 +1,23 @@ +PKG_NAME=lorabasicsmodem +PKG_URL=https://github.com/lorabasics/lorabasicsmodem.git +PKG_VERSION=04e415bcfbdc7f1f4bff918a4867fab53bc8bb8a +PKG_LICENSE=BSD + +include $(RIOTBASE)/pkg/pkg.mk + +CFLAGS += -Wno-error=unused-parameter +CFLAGS += -Wno-error=unused-function + +IGNORE_MODULES := lorabasics_driver_sx1280_hal + # + +LORABASICS_MODULES := $(filter-out $(IGNORE_MODULES),$(filter lorabasics%,$(USEMODULE))) + +all: $(LORABASICS_MODULES) + @true + +lorabasics_smtc_ral: + $(QQ)"$(MAKE)" -C $(PKG_SOURCE_DIR)/smtc_ral/src -f $(RIOTBASE)/Makefile.base MODULE=$@ SRC="ral.c ral_sx1280.c" + +lorabasics_sx1280_driver: + $(QQ)"$(MAKE)" -C $(PKG_SOURCE_DIR)/sx1280_driver/src -f $(RIOTBASE)/Makefile.base MODULE=$@ diff --git a/pkg/lorabasics/Makefile.dep b/pkg/lorabasics/Makefile.dep new file mode 100644 index 000000000000..08222d5ccd7c --- /dev/null +++ b/pkg/lorabasics/Makefile.dep @@ -0,0 +1,11 @@ +ifneq (,$(filter lorabasics_sx1280_driver,$(USEMODULE))) + FEATURES_REQUIRED += periph_gpio_irq + FEATURES_REQUIRED += periph_spi + + USEMODULE += ztimer_msec + + USEMODULE += lorabasics_driver_sx1280_hal +endif +# This package has assumptions that only work for 32-bit architectures +FEATURES_REQUIRED += arch_32bit +USEMODULE += lorabasics_smtc_ral diff --git a/pkg/lorabasics/Makefile.include b/pkg/lorabasics/Makefile.include new file mode 100644 index 000000000000..ff41b54cc785 --- /dev/null +++ b/pkg/lorabasics/Makefile.include @@ -0,0 +1,11 @@ +INCLUDES += -I$(PKGDIRBASE)/lorabasicsmodem \ + # + +ifneq (,$(filter lorabasics_driver_sx1280_hal,$(USEMODULE))) + DIRS += $(RIOTBASE)/pkg/lorabasics/driver_sx1280_hal + + # In the lorabasics package setting this define includes RAL code for the sx1280 radio + CFLAGS += -DSX1280 +endif + +PSEUDOMODULES += lorabasics diff --git a/pkg/lorabasics/doc.txt b/pkg/lorabasics/doc.txt new file mode 100644 index 000000000000..d9a8fe1d3aaf --- /dev/null +++ b/pkg/lorabasics/doc.txt @@ -0,0 +1,13 @@ +/** + * @defgroup pkg_lorabasicsmodem LoRa Basics + * @ingroup pkg + * @brief LoRa Basics Modem + * + * # HAL implementation for the SX1280 LoRa radio driver and vendor code for SX1280 + * + * # License + * + * Licensed under BSD. + * + * @see git@github.com:lorabasics/lorabasicsmodem.git + */ diff --git a/pkg/lorabasics/driver_sx1280_hal/Makefile b/pkg/lorabasics/driver_sx1280_hal/Makefile new file mode 100644 index 000000000000..e93476024ba6 --- /dev/null +++ b/pkg/lorabasics/driver_sx1280_hal/Makefile @@ -0,0 +1,3 @@ +MODULE = lorabasics_driver_sx1280_hal + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/lorabasics/driver_sx1280_hal/driver_sx1280_hal.c b/pkg/lorabasics/driver_sx1280_hal/driver_sx1280_hal.c new file mode 100644 index 000000000000..ecf0059495b3 --- /dev/null +++ b/pkg/lorabasics/driver_sx1280_hal/driver_sx1280_hal.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2022 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 pkg_lorabasicsmodem + * @{ + * + * @file + * @brief HAL implementation for the SX1280 LoRa radio driver + * + * @author Francisco Molina + * @author Nicolas Albarel + * @author Didier Donsez + * @author Olivier Alphand + * @author Aymeric Brochier + * + * @} + */ + +#include +#include +#include "irq.h" +#include "periph/spi.h" +#include "ztimer.h" + +#include "sx1280.h" +#include "sx1280_constants.h" +#include "sx1280_driver/src/sx1280_hal.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +/** + * @brief Wait until radio busy pin is reset to 0 + */ +static void sx1280_hal_wait_on_busy(const void *context) +{ + sx1280_t *dev = (sx1280_t *)context; + + while (gpio_read(dev->params->dio0_pin)) {} +} + +sx1280_hal_status_t sx1280_hal_write(const void *context, const uint8_t *command, + const uint16_t command_length, + const uint8_t *data, const uint16_t data_length) +{ + sx1280_t *dev = (sx1280_t *)context; + + sx1280_hal_wakeup(context); + + DEBUG("[sx1280_hal_write]: command_length=%d data_length=%d\n", command_length, data_length); + + spi_acquire(dev->params->spi, SPI_CS_UNDEF, dev->params->spi_mode, dev->params->spi_clk); + spi_transfer_bytes(dev->params->spi, dev->params->nss_pin, data_length != 0, + command, NULL, command_length); + if (data_length) { + spi_transfer_bytes(dev->params->spi, dev->params->nss_pin, false, data, + NULL, data_length); + } + spi_release(dev->params->spi); + + /* 0x84 - SX1280_SET_SLEEP opcode. In sleep mode the radio dio is stuck + to 1 => do not test it */ + if (command[0] != 0x84) { + sx1280_hal_wait_on_busy(context); + } + return SX1280_HAL_STATUS_OK; +} + +sx1280_hal_status_t sx1280_hal_read(const void *context, const uint8_t *command, + const uint16_t command_length, + uint8_t *data, const uint16_t data_length) +{ + sx1280_t *dev = (sx1280_t *)context; + + sx1280_hal_wakeup(context); + + spi_acquire(dev->params->spi, SPI_CS_UNDEF, dev->params->spi_mode, dev->params->spi_clk); + spi_transfer_bytes(dev->params->spi, dev->params->nss_pin, true, \ + command, NULL, command_length); + spi_transfer_bytes(dev->params->spi, dev->params->nss_pin, false, \ + NULL, data, data_length); + spi_release(dev->params->spi); + + return SX1280_HAL_STATUS_OK; +} + +void sx1280_hal_reset( const void *context ) +{ + sx1280_t *dev = (sx1280_t *)context; + + gpio_clear(dev->params->reset_pin); + ztimer_sleep(ZTIMER_MSEC, SX1280_RESET_MS); + gpio_set(dev->params->reset_pin); + ztimer_sleep(ZTIMER_MSEC, SX1280_WAKEUP_TIME_MS); +} + +sx1280_hal_status_t sx1280_hal_wakeup(const void *context) +{ + sx1280_t *dev = (sx1280_t *)context; + + if (dev->mode == SX1280_HAL_OP_MODE_SLEEP) { + /* Busy is HIGH in sleep mode, wake-up the device */ + gpio_clear(dev->params->nss_pin); + sx1280_hal_wait_on_busy(context); + gpio_set(dev->params->nss_pin); + + /* Radio is awake in STDBY_RC mode */ + dev->mode = SX1280_HAL_OP_MODE_STDBY_RC; + } + else { + /* if the radio is awake, just wait until busy pin get low */ + sx1280_hal_wait_on_busy(context); + } + + return SX1280_HAL_STATUS_OK; +} + +sx1280_hal_operating_mode_t sx1280_hal_get_operating_mode(const void *context) +{ + sx1280_t *dev = (sx1280_t *)context; + + return dev->mode; +} + +void sx1280_hal_set_operating_mode(const void *context, const sx1280_hal_operating_mode_t op_mode) +{ + sx1280_t *dev = (sx1280_t *)context; + + dev->mode = op_mode; +} diff --git a/pkg/lorabasics/patches/0001-smtc_ral-ral_defs-remove-const-from-radio_type-tcxo_.patch b/pkg/lorabasics/patches/0001-smtc_ral-ral_defs-remove-const-from-radio_type-tcxo_.patch new file mode 100644 index 000000000000..894847738da4 --- /dev/null +++ b/pkg/lorabasics/patches/0001-smtc_ral-ral_defs-remove-const-from-radio_type-tcxo_.patch @@ -0,0 +1,27 @@ +From 73580e23be59f585a0d551002f4fcb7cd5126561 Mon Sep 17 00:00:00 2001 +From: Francisco Molina +Date: Mon, 4 Apr 2022 08:26:40 +0200 +Subject: [PATCH] smtc_ral/ral_defs: remove const from radio_type/tcxo_cfg defs + +--- + smtc_ral/src/ral_defs.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/smtc_ral/src/ral_defs.h b/smtc_ral/src/ral_defs.h +index a7abc25..bf1ce6f 100644 +--- a/smtc_ral/src/ral_defs.h ++++ b/smtc_ral/src/ral_defs.h +@@ -97,8 +97,8 @@ typedef struct ral_tcxo_conf_s + typedef struct ral_s + { + const void* context; +- const ral_radio_types_t radio_type; +- const ral_tcxo_cfg_t tcxo_cfg; ++ ral_radio_types_t radio_type; ++ ral_tcxo_cfg_t tcxo_cfg; + } ral_t; + + /** +-- +2.32.0 + diff --git a/pkg/lorabasics/patches/0002-treewide-fix-include-paths.patch b/pkg/lorabasics/patches/0002-treewide-fix-include-paths.patch new file mode 100644 index 000000000000..ffdce570ae61 --- /dev/null +++ b/pkg/lorabasics/patches/0002-treewide-fix-include-paths.patch @@ -0,0 +1,68 @@ +From 272fb3de6159f79d8100821e4a6ed3fcd359173f Mon Sep 17 00:00:00 2001 +From: Francisco Molina +Date: Mon, 4 Apr 2022 08:47:50 +0200 +Subject: [PATCH 2/2] treewide: fix include paths + +--- + smtc_ral/src/ral.c | 6 +++--- + smtc_ral/src/ral_sx1280.c | 2 +- + sx1280_driver/src/sx1280.c | 6 +++--- + 3 files changed, 7 insertions(+), 7 deletions(-) + +diff --git a/smtc_ral/src/ral.c b/smtc_ral/src/ral.c +index 55fb127..0cba70c 100644 +--- a/smtc_ral/src/ral.c ++++ b/smtc_ral/src/ral.c +@@ -34,8 +34,8 @@ + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +-#include "ral.h" +-#include "ral_hal.h" ++#include "smtc_ral/src/ral.h" ++#include "smtc_ral/src/ral_hal.h" + + #if defined( SX126X ) + #include "ral_sx126x.h" +@@ -47,7 +47,7 @@ + #include "ral_sx1276.h" + #endif + #if defined( SX1280 ) +-#include "ral_sx1280.h" ++#include "smtc_ral/src/ral_sx1280.h" + #endif + #if !defined( SX126X ) && !defined( SX1272 ) && !defined( SX1276 ) && !defined( SX1280 ) + #error "Unknown radio selected..." +diff --git a/smtc_ral/src/ral_sx1280.c b/smtc_ral/src/ral_sx1280.c +index bd584ca..ecd8c03 100644 +--- a/smtc_ral/src/ral_sx1280.c ++++ b/smtc_ral/src/ral_sx1280.c +@@ -36,7 +36,7 @@ + + #include // memcpy + +-#include "sx1280.h" ++#include "sx1280_driver/src/sx1280.h" + #include "ral_sx1280.h" + + /* +diff --git a/sx1280_driver/src/sx1280.c b/sx1280_driver/src/sx1280.c +index d3e7d18..0b69d55 100644 +--- a/sx1280_driver/src/sx1280.c ++++ b/sx1280_driver/src/sx1280.c +@@ -35,9 +35,9 @@ + */ + + #include // memcpy +-#include "sx1280_hal.h" +-#include "sx1280_regs.h" +-#include "sx1280.h" ++#include "sx1280_driver/src/sx1280_hal.h" ++#include "sx1280_driver/src/sx1280_regs.h" ++#include "sx1280_driver/src/sx1280.h" + + /* + * ----------------------------------------------------------------------------- +-- +2.32.0 + From 09ca09adbfd6f332be4f5288f0aed50b784298b8 Mon Sep 17 00:00:00 2001 From: Aymeric Brochier Date: Tue, 17 May 2022 15:46:24 +0200 Subject: [PATCH 3/4] drivers/sx1280: initial import --- drivers/Kconfig.net | 1 + drivers/include/net/netdev.h | 1 + drivers/include/sx1280.h | 267 +++++++++++++ drivers/sx1280/Kconfig | 17 + drivers/sx1280/Makefile | 1 + drivers/sx1280/Makefile.dep | 2 + drivers/sx1280/Makefile.include | 7 + drivers/sx1280/include/sx1280_constants.h | 75 ++++ drivers/sx1280/include/sx1280_netdev.h | 40 ++ drivers/sx1280/include/sx1280_params.h | 113 ++++++ drivers/sx1280/sx1280.c | 406 +++++++++++++++++++ drivers/sx1280/sx1280_netdev.c | 458 ++++++++++++++++++++++ 12 files changed, 1388 insertions(+) create mode 100644 drivers/include/sx1280.h create mode 100644 drivers/sx1280/Kconfig create mode 100644 drivers/sx1280/Makefile create mode 100644 drivers/sx1280/Makefile.dep create mode 100644 drivers/sx1280/Makefile.include create mode 100644 drivers/sx1280/include/sx1280_constants.h create mode 100644 drivers/sx1280/include/sx1280_netdev.h create mode 100644 drivers/sx1280/include/sx1280_params.h create mode 100644 drivers/sx1280/sx1280.c create mode 100644 drivers/sx1280/sx1280_netdev.c diff --git a/drivers/Kconfig.net b/drivers/Kconfig.net index 063412226748..4f1f77439cbd 100644 --- a/drivers/Kconfig.net +++ b/drivers/Kconfig.net @@ -21,5 +21,6 @@ rsource "slipdev/Kconfig" rsource "$(RIOTCPU)/native/socket_zep/Kconfig" rsource "sx126x/Kconfig" rsource "sx127x/Kconfig" +rsource "sx1280/Kconfig" rsource "tja1042/Kconfig" endmenu # Network Device Drivers diff --git a/drivers/include/net/netdev.h b/drivers/include/net/netdev.h index 5195c24f2237..c4d37fcb268c 100644 --- a/drivers/include/net/netdev.h +++ b/drivers/include/net/netdev.h @@ -320,6 +320,7 @@ typedef enum { NETDEV_NRF24L01P_NG, NETDEV_SOCKET_ZEP, NETDEV_SX126X, + NETDEV_SX1280, NETDEV_CC2420, NETDEV_ETHOS, NETDEV_SLIPDEV, diff --git a/drivers/include/sx1280.h b/drivers/include/sx1280.h new file mode 100644 index 000000000000..ec17e58c128a --- /dev/null +++ b/drivers/include/sx1280.h @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2022 Inria + * Copyright (C) 2020-2022 Université Grenoble Alpes + * + * 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_sx1280 LoRa radio driver + * @ingroup drivers_netdev + * @brief Driver for the sx1280 LoRa radio device + * + * @{ + * + * @file + * + * @author Francisco Molina + * @author Aymeric Brochier + * + */ + +#ifndef SX1280_H +#define SX1280_H + +#include + +#include "sx1280_driver/src/sx1280_hal.h" +#include "sx1280_driver/src/sx1280.h" +#include "smtc_ral/src/ral.h" +#include "smtc_ral/src/ral_defs.h" + +#include "net/netdev.h" + +#include "periph/gpio.h" +#include "periph/spi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name SX1280 device default configuration + * @{ + */ +#define SX1280_CHANNEL_DEFAULT (2403000000UL) /**< Default channel frequency, 2403MHz */ +#define SX1280_RADIO_TX_POWER (SX1280_PWR_MAX) /**< Radio power in dBm */ +/** @} */ + +/** + * * @note Forward declaration of the sx1280 device descriptor + */ +typedef struct sx1280 sx1280_t; + +/** + * @brief Device initialization parameters + */ +typedef struct { + spi_t spi; /**< SPI device */ + spi_mode_t spi_mode; /**< SPI mode */ + spi_clk_t spi_clk; /**< SPI clk */ + gpio_t nss_pin; /**< SPI NSS pin */ + gpio_t reset_pin; /**< Reset pin */ + gpio_t busy_pin; /**< Busy pin */ + gpio_t dio0_pin; /**< Dio0 pin */ + gpio_t dio1_pin; /**< Dio1 pin */ + sx1280_reg_mod_t regulator; /**< Power regulator mode */ +} sx1280_params_t; + +/** + * @brief Device descriptor for the driver + */ +struct sx1280 { + netdev_t netdev; /**< Netdev parent struct */ + ral_t ral; /**< Radio abstraction */ + ral_params_lora_t ral_params_lora; /**< LoRa modulation parameters */ + sx1280_params_t *params; /**< Initialization parameters */ + sx1280_hal_operating_mode_t mode; /**< Operating Mode */ +}; + +/** + * @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 sx1280_setup(sx1280_t *dev, const sx1280_params_t *params, uint8_t index); + +/** + * @brief Initialize the given device + * + * @param[inout] dev Device descriptor of the driver + * + * @return 0 on success + */ +int sx1280_init(sx1280_t *dev); + +/** + * @brief Gets the channel RF frequency. + * + * @param[in] dev Device descriptor of the driver + * + * @return The channel frequency + */ +uint32_t sx1280_get_channel(const sx1280_t *dev); + +/** + * @brief Gets a random number + * + * @param[in] dev Device descriptor of the driver + * + * @return a random number + */ +uint32_t sx1280_random(const sx1280_t *dev); +/** + * @brief Sets the channel RF frequency. + * + * @param[in] dev Device descriptor of the driver + * @param[in] freq Channel RF frequency + */ +void sx1280_set_channel(sx1280_t *dev, uint32_t freq); + +/** + * @brief Gets the LoRa bandwidth + * + * @param[in] dev Device descriptor of the driver + * + * @return the bandwidth + */ +uint32_t sx1280_get_bandwidth(const sx1280_t *dev); + +/** + * @brief Sets the LoRa bandwidth + * + * @param[in] dev Device descriptor of the driver + * @param[in] bandwidth The new bandwidth + */ +void sx1280_set_bandwidth(sx1280_t *dev, uint16_t bandwidth); + +/** + * @brief Gets the LoRa spreading factor + * + * @param[in] dev Device descriptor of the driver + * + * @return the spreading factor + */ +uint8_t sx1280_get_spreading_factor(const sx1280_t *dev); + +/** + * @brief Sets the LoRa spreading factor + * + * @param[in] dev Device descriptor of the driver + * @param[in] sf The spreading factor + */ +void sx1280_set_spreading_factor(sx1280_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 sx1280_get_coding_rate(const sx1280_t *dev); + +/** + * @brief Sets the LoRa coding rate + * + * @param[in] dev Device descriptor of the driver + * @param[in] cr The LoRa coding rate + */ +void sx1280_set_coding_rate(sx1280_t *dev, uint8_t cr); + +/** + * @brief Gets the payload length + * + * @param[in] dev Device descriptor of the driver + * + * @return the payload length + */ +uint8_t sx1280_get_lora_payload_length(const sx1280_t *dev); + +/** + * @brief Sets the payload length + * + * @param[in] dev Device descriptor of the driver + * @param[in] len The payload len + */ +void sx1280_set_lora_payload_length(sx1280_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 sx1280_get_lora_crc(const sx1280_t *dev); + +/** + * @brief Enable/Disable CRC verification mode + * + * @param[in] dev Device descriptor of the driver + * @param[in] crc The CRC check mode + */ +void sx1280_set_lora_crc(sx1280_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 sx1280_get_lora_implicit_header(const sx1280_t *dev); + +/** + * @brief Sets LoRa implicit header mode + * + * @param[in] dev Device descriptor of the driver + * @param[in] mode The header mode + */ +void sx1280_set_lora_implicit_header(sx1280_t *dev, bool mode); + +/** + * @brief Gets the LoRa preamble length + * + * @param[in] dev Device descriptor of the driver + * + * @return the preamble length + */ +uint16_t sx1280_get_lora_preamble_length(const sx1280_t *dev); + +/** + * @brief Sets the LoRa preamble length + * + * @param[in] dev Device descriptor of the driver + * @param[in] preamble The LoRa preamble length + */ +void sx1280_set_lora_preamble_length(sx1280_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 sx1280_get_lora_iq_invert(const sx1280_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 sx1280_set_lora_iq_invert(sx1280_t *dev, bool iq_invert); + +#ifdef __cplusplus +} +#endif + +#endif /* SX1280_H */ +/** @} */ diff --git a/drivers/sx1280/Kconfig b/drivers/sx1280/Kconfig new file mode 100644 index 000000000000..c586ffb7a1fd --- /dev/null +++ b/drivers/sx1280/Kconfig @@ -0,0 +1,17 @@ +# Copyright (c) 2022 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_SX1280 + bool "LoRa 2.4Ghz SX1280 Driver" if !(HAVE_SX1280 && MODULE_NETDEV_DEFAULT) + default y if (HAVE_SX1280 && MODULE_NETDEV_DEFAULT) + depends on TEST_KCONFIG + select PACKAGE_LORABASICS + select MODULE_LORABASICS_SX1280_DRIVER + +config HAVE_SX1280 + bool + help + Indicates that an sx1280 2.4Ghz transceiver is present. diff --git a/drivers/sx1280/Makefile b/drivers/sx1280/Makefile new file mode 100644 index 000000000000..48422e909a47 --- /dev/null +++ b/drivers/sx1280/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/sx1280/Makefile.dep b/drivers/sx1280/Makefile.dep new file mode 100644 index 000000000000..f402c8e2daae --- /dev/null +++ b/drivers/sx1280/Makefile.dep @@ -0,0 +1,2 @@ +USEPKG += lorabasics +USEMODULE += lorabasics_sx1280_driver diff --git a/drivers/sx1280/Makefile.include b/drivers/sx1280/Makefile.include new file mode 100644 index 000000000000..3b1935b9615a --- /dev/null +++ b/drivers/sx1280/Makefile.include @@ -0,0 +1,7 @@ +USEMODULE_INCLUDES_sx1280 := $(LAST_MAKEFILEDIR)/include +USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_sx1280) + +# Overide defaults see See https://lora-developers.semtech.com/documentation/tech-papers-and-guides/physical-layer-proposal-2.4ghz/ +CFLAGS += -DCONFIG_LORA_BW_DEFAULT_800=1 +CFLAGS += -DCONFIG_LORA_SF_DEFAULT_SF12=1 +CFLAGS += -DCONFIG_LORA_CR_DEFAULT_CR_LI_4_8=1 diff --git a/drivers/sx1280/include/sx1280_constants.h b/drivers/sx1280/include/sx1280_constants.h new file mode 100644 index 000000000000..a8fbf20845cb --- /dev/null +++ b/drivers/sx1280/include/sx1280_constants.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2022 Inria + * Copyright (C) 2020-2022 Université Grenoble Alpes + * + * 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_sx1280 + * @{ + * + * @file + * @brief Internal addresses, registers and constants + * + * @author Aymeric Brochier + * + */ + +#ifndef SX1280_CONSTANTS_H +#define SX1280_CONSTANTS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief SX1280 Single RX mode. + * + * sx1280_set_rx constant to be in single mode + * + * There is a known issue about this in some circonstances described in section 16.1 of the datasheet: + * https://manualzz.com/doc/55353537/semtech-sx1280-2.4-ghz-transceiver-datasheet + * + * When subjected to a high co-channel traffic conditions + * (for example in BLE mode when the SX1280 receives more than 220 packets per second) + * and only when configured in continuous receiver + * The SX1280 busy line will remain high and the radio unresponsive. + * + * If the radio may be subject to high levels of BLE traffic, to allow the radio to remain in operation RX single mode must be used + */ +#define SX1280_RX_SINGLE_MODE 0x0000 + +/** + * @brief SX1280 Continuous RX mode. + * + * @note This addresses a known issue detailed in section 16.1 of the datasheet, see + * @ref SX1280_RX_SINGLE_MODE for more details. + * + * sx1280_set_rx require this constant to be in continuous mode + * + */ +#define SX1280_RX_CONTINUOUS_MODE 0xFFFF + +/** + * @brief SX1280 wakeup time in ms + * @note From the datasheet wakeup time is usually 1200us but rounded up to 2 to only use the ZTIMER_MSEC + * + */ +#define SX1280_WAKEUP_TIME_MS 2 + +/** + * @brief SX1280 reset time in ms + * hold NRST pin low for 1ms, nothing is specified in the datasheet, 1ms worked fine + * + */ +#define SX1280_RESET_MS 1 + +#ifdef __cplusplus +} +#endif + +#endif /* SX1280_CONSTANTS_H */ +/** @} */ diff --git a/drivers/sx1280/include/sx1280_netdev.h b/drivers/sx1280/include/sx1280_netdev.h new file mode 100644 index 000000000000..38cace0a7916 --- /dev/null +++ b/drivers/sx1280/include/sx1280_netdev.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 Inria + * Copyright (C) 2020-2022 Université Grenoble Alpes + * + * 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_sx1280 + * @{ + * @file + * @brief Netdev driver definitions for LoRa SX1280 Driver driver + * + * @author Francisco Molina + * @author Aymeric Brochier + * + */ + +#ifndef SX1280_NETDEV_H +#define SX1280_NETDEV_H + +#include "net/netdev.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Reference to the netdev device driver struct + */ +extern const netdev_driver_t sx1280_driver; + +#ifdef __cplusplus +} +#endif + +#endif /* SX1280_NETDEV_H */ +/** @} */ diff --git a/drivers/sx1280/include/sx1280_params.h b/drivers/sx1280/include/sx1280_params.h new file mode 100644 index 000000000000..c64f648a7ca2 --- /dev/null +++ b/drivers/sx1280/include/sx1280_params.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2022 Inria + * Copyright (C) 2020-2022 Université Grenoble Alpes + * + * 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_sx1280 + * @{ + * @file + * @brief Default configuration + * + * @author Francisco Molina + * @author Aymeric Brochier + * + */ + +#ifndef SX1280_PARAMS_H +#define SX1280_PARAMS_H + +#include "board.h" +#include "sx1280.h" +#include "sx1280_constants.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Set default configuration parameters + * + * Default values are adapted for mbed shield used with to nucleo64 boards + * @see https://github.com/donsez/RIOT/blob/pr/sx1280/drivers/sx1280/include/sx1280_hal.h + * @see 'board/commun/nucleo64/include/arduino_pinmap.h' + * + * ARDUINO_PIN_10 -> GPIO_PIN(PORT_B, 6) -> GPIO_PIN(1, 6) + * ARDUINO_PIN_7 -> GPIO_PIN(PORT_A, 8) -> GPIO_PIN(0, 8) + * ARDUINO_PIN_6 -> GPIO_PIN(PORT_B, 10) -> GPIO_PIN(1, 10) + * ARDUINO_PIN_9 -> GPIO_PIN(PORT_C, 7) -> GPIO_PIN(2, 7) + * + * @{ + */ + +#ifndef USE_RX_CONTINUOUS_MODE +#define USE_RX_CONTINUOUS_MODE /**< default RX MODE */ +#endif + +#ifdef USE_RX_CONTINUOUS_MODE +#define SX1280_RX_MODE SX1280_RX_CONTINUOUS_MODE /**< continuous RX MODE */ +#else +#define SX1280_RX_MODE SX1280_RX_SINGLE_MODE /**< single RX MODE */ +#endif + +#ifndef SX1280_PARAM_SPI +#define SX1280_PARAM_SPI SPI_DEV(0) /**< default SPI device */ +#endif + +#ifndef SX1280_PARAM_SPI_CLK +#define SX1280_PARAM_SPI_CLK SPI_CLK_5MHZ /**< default SPI speed */ +#endif + +#ifndef SX1280_PARAM_SPI_MODE +#define SX1280_PARAM_SPI_MODE SPI_MODE_0 /**< default SPI mode for sx1280 */ +#endif + +#ifndef SX1280_PARAM_SPI_NSS +#define SX1280_PARAM_SPI_NSS GPIO_PIN(1, 6) /**< SPI NSS pin */ +#endif + +#ifndef SX1280_PARAM_RESET +#define SX1280_PARAM_RESET GPIO_PIN(0, 8) /**< Reset pin */ +#endif + +#ifndef SX1280_PARAM_DIO0 +#define SX1280_PARAM_DIO0 GPIO_PIN(1, 10) /**< DIO0 */ +#endif + +#ifndef SX1280_PARAM_DIO1 +#define SX1280_PARAM_DIO1 GPIO_PIN(2, 7) /**< DIO1 */ +#endif + + +/** + * @brief Default sx1280 parameters + */ +#define SX1280_PARAMS { .spi = SX1280_PARAM_SPI, \ + .spi_mode = SX1280_PARAM_SPI_MODE, \ + .spi_clk = SX1280_PARAM_SPI_CLK, \ + .nss_pin = SX1280_PARAM_SPI_NSS, \ + .reset_pin = SX1280_PARAM_RESET, \ + .dio0_pin = SX1280_PARAM_DIO0, \ + .dio1_pin = SX1280_PARAM_DIO1, \ +} + +/**@}*/ + +/** + * @brief Configuration struct + */ +static const sx1280_params_t sx1280_params[] = +{ + SX1280_PARAMS +}; + +#ifdef __cplusplus +} +#endif + +#endif /* SX1280_PARAMS_H */ +/** @} */ diff --git a/drivers/sx1280/sx1280.c b/drivers/sx1280/sx1280.c new file mode 100644 index 000000000000..09325e0424dc --- /dev/null +++ b/drivers/sx1280/sx1280.c @@ -0,0 +1,406 @@ +/* + * Copyright (C) 2022 Inria + * Copyright (C) 2020-2022 Université Grenoble Alpes + * + * 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_sx1280 + * @{ + * @file + * @brief Device driver implementation for the LoRa SX1280 Driver + * + * @author Francisco Molina + * @author Aymeric Brochier + * + * @} + */ + +#include +#include "ztimer.h" + +#include "sx1280_driver/src/sx1280.h" +#include "sx1280_driver/src/sx1280_regs.h" +#include "smtc_ral/src/ral_sx1280.h" + +#include "sx1280.h" +#include "sx1280_constants.h" +#include "sx1280_params.h" +#include "sx1280_netdev.h" + +#include "net/lora.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +static ral_lora_bw_t _lora_bw_to_ral_bw(int bw) +{ + switch (bw) { + case LORA_BW_125_KHZ: + return RAL_LORA_BW_125_KHZ; + case LORA_BW_200_KHZ: + return RAL_LORA_BW_200_KHZ; + case LORA_BW_250_KHZ: + return RAL_LORA_BW_250_KHZ; + case LORA_BW_400_KHZ: + return RAL_LORA_BW_400_KHZ; + case LORA_BW_500_KHZ: + return RAL_LORA_BW_500_KHZ; + case LORA_BW_800_KHZ: + return RAL_LORA_BW_800_KHZ; + case LORA_BW_1600_KHZ: + return RAL_LORA_BW_1600_KHZ; + default: + return -1; + } +} + +static ral_lora_sf_t _lora_sf_to_ral_sf(int sf) +{ + return (ral_lora_sf_t)sf; +} + +static ral_lora_cr_t _lora_cr_to_ral_cr(int cr) +{ + switch (cr) { + case LORA_CR_4_5: + return RAL_LORA_CR_4_5; + case LORA_CR_4_6: + return RAL_LORA_CR_4_6; + case LORA_CR_4_7: + return RAL_LORA_CR_4_7; + case LORA_CR_4_8: + return RAL_LORA_CR_4_8; + case LORA_CR_LI_4_5: + return RAL_LORA_CR_LI_4_5; + case LORA_CR_LI_4_6: + return RAL_LORA_CR_LI_4_6; + case LORA_CR_LI_4_8: + return RAL_LORA_CR_LI_4_8; + default: + return -1; + } +} + +static int _ral_lora_cr_to_lora_cr(int cr) +{ + switch (cr) { + case RAL_LORA_CR_4_5: + return LORA_CR_4_5; + case RAL_LORA_CR_4_6: + return LORA_CR_4_6; + case RAL_LORA_CR_4_7: + return LORA_CR_4_7; + case RAL_LORA_CR_4_8: + return LORA_CR_4_8; + case RAL_LORA_CR_LI_4_5: + return LORA_CR_LI_4_5; + case RAL_LORA_CR_LI_4_6: + return LORA_CR_LI_4_6; + case RAL_LORA_CR_LI_4_8: + return LORA_CR_LI_4_8; + default: + return -1; + } +} + +static uint32_t _get_bw(ral_lora_bw_t lora_bw) +{ + switch (lora_bw) { + case RAL_LORA_BW_007_KHZ: + return (uint32_t)7000; + case RAL_LORA_BW_010_KHZ: + return (uint32_t)10000; + case RAL_LORA_BW_015_KHZ: + return (uint32_t)15000; + case RAL_LORA_BW_020_KHZ: + return (uint32_t)20000; + case RAL_LORA_BW_031_KHZ: + return (uint32_t)31000; + case RAL_LORA_BW_041_KHZ: + return (uint32_t)41000; + case RAL_LORA_BW_062_KHZ: + return (uint32_t)62000; + case RAL_LORA_BW_125_KHZ: + return (uint32_t)125000; + case RAL_LORA_BW_200_KHZ: + return (uint32_t)200000; + case RAL_LORA_BW_250_KHZ: + return (uint32_t)250000; + case RAL_LORA_BW_400_KHZ: + return (uint32_t)400000; + case RAL_LORA_BW_500_KHZ: + return (uint32_t)500000; + case RAL_LORA_BW_800_KHZ: + return (uint32_t)800000; + case RAL_LORA_BW_1600_KHZ: + return (uint32_t)1600000; + default: + return 0; + } +} + +static void _dio1_isr(void *arg) +{ + netdev_trigger_event_isr(arg); +} + +static int enable_irq(const sx1280_t *dev) +{ + const uint16_t irq_mask = ( + SX1280_IRQ_TX_DONE | + SX1280_IRQ_RX_DONE | + SX1280_IRQ_PBL_DET | + SX1280_IRQ_HEADER_VALID | + SX1280_IRQ_HEADER_ERROR | + SX1280_IRQ_CAD_DONE | + SX1280_IRQ_CAD_DET | + SX1280_IRQ_TIMEOUT + ); + + int res = sx1280_set_dio_irq_params(dev, irq_mask, irq_mask, SX1280_IRQ_NONE, SX1280_IRQ_NONE); + + return res; +} + +void sx1280_setup(sx1280_t *dev, const sx1280_params_t *params, uint8_t index) +{ + dev->ral.context = dev; + dev->ral.radio_type = RAL_RADIO_SX1280; + dev->ral.tcxo_cfg.tcxo_ctrl_mode = RAL_TCXO_NONE; + + netdev_t *netdev = &dev->netdev; + + netdev->driver = &sx1280_driver; + dev->params = (sx1280_params_t *)params; + netdev_register(&dev->netdev, NETDEV_SX1280, index); +} + +static uint32_t sx1280_init_default_config(sx1280_t *dev) + +{ + /* default parameters */ + const ral_params_lora_t params_default = { + .freq_in_hz = SX1280_CHANNEL_DEFAULT, + .sf = _lora_sf_to_ral_sf(CONFIG_LORA_SF_DEFAULT), + .bw = _lora_bw_to_ral_bw(CONFIG_LORA_BW_DEFAULT), + .cr = _lora_cr_to_ral_cr(CONFIG_LORA_CR_DEFAULT), + .pbl_len_in_symb = CONFIG_LORA_PREAMBLE_LENGTH_DEFAULT, + .sync_word = LORA_SYNCWORD_ISM2400_PUBLIC, + .pld_is_fix = false, + .crc_is_on = true, + .invert_iq_is_on = false, + .symb_nb_timeout = 0, + .pwr_in_dbm = SX1280_RADIO_TX_POWER, + }; + + /* set current ral_params */ + dev->ral_params_lora = params_default; + + ral_status_t res = ral_sx1280_setup_lora(&dev->ral, ¶ms_default); + + if (res != RAL_STATUS_OK) { + DEBUG("[sx1280] init: ERROR failed LoRa setup, %d\n", res); + return -EIO; + } + + return 0; +} + +int sx1280_init(sx1280_t *dev) +{ + /* Setup SPI for SX1280 */ + int res = spi_init_cs(dev->params->spi, dev->params->nss_pin); + + if (res != SPI_OK) { + DEBUG("[sx1280] error: failed to initialize SPI_%i device (code %i)\n", + dev->params->spi, res); + return -1; + } + + DEBUG("[sx1280] init: SPI_%i initialized with success\n", dev->params->spi); + /* Initialize Reset */ + if (gpio_init(dev->params->reset_pin, GPIO_OUT) < 0) { + DEBUG("[sx1280] error: failed to initialize RESET pin\n"); + return -EIO; + } + /* Initialize DIO0s */ + if (gpio_init(dev->params->dio0_pin, GPIO_IN) < 0) { + DEBUG("[sx1280] error: failed to initialize DIO1 pin\n"); + return -EIO; + } + /* Initialize DI01s */ + if (gpio_init_int(dev->params->dio1_pin, GPIO_IN, GPIO_RISING, _dio1_isr, dev)) { + DEBUG("[sx1280] error: failed to initialize DIO1 pin\n"); + return -EIO; + } + /* sx1280_reset() and sx1280_set_reg_mode() set in ral_sx1280_init */ + res = ral_sx1280_init(&dev->ral); + if (res == RAL_STATUS_OK) { + uint16_t fwid; + ral_sx1280_read_register(&dev->ral, SX1280_REG_FW_VERSION, (uint8_t *)&fwid, 2); + if (fwid == 0x5853) { + DEBUG("[sx1280]: firmware version = %x\n", fwid); + } + else { + DEBUG("[sx1280] error: firmware version = %x\n", fwid); + return -EIO; + } + } + + sx1280_init_default_config(dev); + + res = enable_irq(dev); + if (res != SX1280_STATUS_OK) { + DEBUG("[sx1280] init: ERROR failed irq setup, %d\n", res); + return -EIO; + } + + sx1280_set_lna_settings(dev, SX1280_LNA_HIGH_SENSITIVITY_MODE); + + return 0; +} + +uint32_t sx1280_get_channel(const sx1280_t *dev) +{ + DEBUG("[sx1280]: sx1280_get_channel\n"); + return dev->ral_params_lora.freq_in_hz; +} + +void sx1280_set_channel(sx1280_t *dev, uint32_t freq) +{ + DEBUG("[sx1280]: sx1280_set_channel %" PRIu32 "Hz\n", freq); + dev->ral_params_lora.freq_in_hz = freq; + sx1280_set_rf_freq(dev, dev->ral_params_lora.freq_in_hz); +} + +uint32_t sx1280_get_bandwidth(const sx1280_t *dev) +{ + return _get_bw(dev->ral_params_lora.bw); +} + +void sx1280_set_bandwidth(sx1280_t *dev, uint16_t bandwidth) +{ + DEBUG("[sx1280]: sx1280_set_bandwidth (KHz)\n"); + switch (bandwidth) { + case 200: + puts("setup: setting 200KHz bandwidth"); + dev->ral_params_lora.bw = RAL_LORA_BW_200_KHZ; + break; + case 400: + puts("setup: setting 400KHz bandwidth"); + dev->ral_params_lora.bw = RAL_LORA_BW_400_KHZ; + break; + case 800: + puts("setup: setting 800KHz bandwidth"); + dev->ral_params_lora.bw = RAL_LORA_BW_800_KHZ; + break; + case 1600: + puts("setup: setting 1600KHz bandwidth"); + dev->ral_params_lora.bw = RAL_LORA_BW_1600_KHZ; + break; + /* assume caller function check the validity of the value so should never go here */ + default: + puts("[Error] setup: invalid bandwidth value given, " + "only 200, 400, 800 or 1600 allowed."); + return; + } + ral_sx1280_setup_lora(&dev->ral, &dev->ral_params_lora); +} + +uint8_t sx1280_get_spreading_factor(const sx1280_t *dev) +{ + DEBUG("[sx1280]: sx1280_get_spreading_factor\n"); + /* no conversion needed */ + return dev->ral_params_lora.sf; +} + +void sx1280_set_spreading_factor(sx1280_t *dev, uint8_t sf) +{ + DEBUG("[sx1280]: sx1280_set_spreading_factor\n"); + dev->ral_params_lora.sf = sf; + ral_sx1280_setup_lora(&dev->ral, &dev->ral_params_lora); +} + +uint8_t sx1280_get_coding_rate(const sx1280_t *dev) +{ + DEBUG("[sx1280]: sx1280_get_coding_rate\n"); + return _ral_lora_cr_to_lora_cr(dev->ral_params_lora.cr); +} + +void sx1280_set_coding_rate(sx1280_t *dev, uint8_t cr) +{ + DEBUG("[sx1280]: sx1280_set_coding_rate\n"); + dev->ral_params_lora.cr = cr; + ral_sx1280_setup_lora(&dev->ral, &dev->ral_params_lora); +} + +uint8_t sx1280_get_lora_payload_length(const sx1280_t *dev) +{ + DEBUG("[sx1280]: sx1280_get_lora_payload_length\n"); + sx1280_rx_buffer_status_t rx_buffer_status; + + sx1280_get_rx_buffer_status(dev, &rx_buffer_status); + return rx_buffer_status.pld_len_in_bytes; +} +void sx1280_set_lora_payload_length(sx1280_t *dev, uint8_t len) +{ + DEBUG("[sx1280]: sx1280_set_lora_payload_length\n"); + dev->ral_params_lora.pld_len_in_bytes = len; + ral_sx1280_setup_lora(&dev->ral, &dev->ral_params_lora); +} + +bool sx1280_get_lora_crc(const sx1280_t *dev) +{ + DEBUG("[sx1280]: sx1280_get_lora_crc\n"); + return dev->ral_params_lora.crc_is_on; +} + +void sx1280_set_lora_crc(sx1280_t *dev, bool crc) +{ + DEBUG("[sx1280]: sx1280_set_lora_crc\n"); + dev->ral_params_lora.crc_is_on = crc; + ral_sx1280_setup_lora(&dev->ral, &dev->ral_params_lora); +} + +bool sx1280_get_lora_implicit_header(const sx1280_t *dev) +{ + (void)dev; + DEBUG("[sx1280]: sx1280_get_lora_implicit_header not implemented \n"); + return false; +} + +void sx1280_set_lora_implicit_header(sx1280_t *dev, bool mode) +{ + (void)dev; + DEBUG("[sx1280]: sx1280_set_lora_implicit_header not implemented %d\n", mode); +} + +uint16_t sx1280_get_lora_preamble_length(const sx1280_t *dev) +{ + DEBUG("[sx1280]: sx1280_get_lora_preamble_length\n"); + return dev->ral_params_lora.pbl_len_in_symb; +} + +void sx1280_set_lora_preamble_length(sx1280_t *dev, uint16_t preamble) +{ + DEBUG("[sx1280]: sx1280_set_lora_preamble_length\n"); + dev->ral_params_lora.pbl_len_in_symb = preamble; + ral_sx1280_setup_lora(&dev->ral, &dev->ral_params_lora); +} + +bool sx1280_get_lora_iq_invert(const sx1280_t *dev) +{ + DEBUG("[sx1280]: sx1280_get_lora_iq_invert\n"); + return dev->ral_params_lora.invert_iq_is_on; +} + +void sx1280_set_lora_iq_invert(sx1280_t *dev, bool iq_invert) +{ + DEBUG("[sx1280]: sx1280_set_lora_iq_invert\n"); + dev->ral_params_lora.invert_iq_is_on = iq_invert; + ral_sx1280_setup_lora(&dev->ral, &dev->ral_params_lora); +} diff --git a/drivers/sx1280/sx1280_netdev.c b/drivers/sx1280/sx1280_netdev.c new file mode 100644 index 000000000000..4c48fd6244ae --- /dev/null +++ b/drivers/sx1280/sx1280_netdev.c @@ -0,0 +1,458 @@ +/* + * Copyright (C) 2022 Inria + * Copyright (C) 2020-2022 Université Grenoble Alpes + * + * 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_sx1280 + * @{ + * @file + * @brief Netdev adaptation for the LoRa SX1280 Driver driver + * + * @author Francisco Molina + * @author Aymeric Brochier + * + * @} + */ + +#include +#include +#include +#include + +#include "iolist.h" +#include "net/netopt.h" +#include "net/netdev.h" +#include "net/netdev/lora.h" +#include "net/lora.h" + +#include "sx1280.h" +#include "sx1280_params.h" + +#include "sx1280_netdev.h" +#include "sx1280_driver/src/sx1280.h" + +#include "smtc_ral/src/ral_defs.h" +#include "smtc_ral/src/ral_sx1280.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +static int _send(netdev_t *netdev, const iolist_t *iolist) +{ + + sx1280_t *dev = (sx1280_t *)netdev; + netopt_state_t state; + + netdev->driver->get(netdev, NETOPT_STATE, &state, sizeof(netopt_state_t)); + if (state == NETOPT_STATE_TX) { + DEBUG("[sx1280] netdev: cannot send packet, radio is already transmitting.\n"); + return -ENOTSUP; + } + else { + DEBUG("[sx1280] netdev: can send packet\n"); + } + + size_t pos = 0; + + /* Write payload buffer */ + for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) { + if (iol->iol_len > 0) { + /* write data to payload buffer */ + sx1280_write_buffer(dev, pos, iol->iol_base, iol->iol_len); + DEBUG("[sx1280] netdev: send: wrote data to payload buffer.\n"); + pos += iol->iol_len; + } + } + /* Ignore send if packet size is 0 */ + if (!pos) { + return 0; + } + + DEBUG("[sx1280] netdev: sending packet now (size: %d).\n", pos); + sx1280_set_lora_payload_length(dev, pos); + + state = NETOPT_STATE_TX; + netdev->driver->set(netdev, NETOPT_STATE, &state, sizeof(state)); + DEBUG("[sx1280] netdev: send: transmission in progress.\n"); + + return 0; +} + +static int _recv(netdev_t *netdev, void *buf, size_t len, void *info) +{ + DEBUG("[sx1280] netdev: read received data.\n"); + + sx1280_t *dev = (sx1280_t *)netdev; + uint8_t size = 0; + + /* Get received packet info and size here */ + netdev_lora_rx_info_t *packet_info = info; + + if (packet_info) { + sx1280_pkt_status_lora_t pkt_status; + sx1280_get_lora_pkt_status(dev, &pkt_status); + packet_info->snr = pkt_status.snr; + packet_info->rssi = pkt_status.rssi; + } + + sx1280_rx_buffer_status_t rx_buffer_status; + + sx1280_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; + } + + /* Read the received packet content here and write it to buf */ + sx1280_read_buffer(dev, rx_buffer_status.buffer_start_pointer, buf, size); + + /* RX SINGLE MODE */ + if (sx1280_hal_get_operating_mode(&dev) == SX1280_HAL_OP_MODE_RX) { + DEBUG("[sx1280] netdev stop RX single mode" ); + netopt_state_t state = NETOPT_STATE_IDLE; + dev->netdev.driver->set(&dev->netdev, NETOPT_STATE, &state, sizeof(state)); + return 0; + /* RX CONTINUOUS MODE */ + } + else if (sx1280_hal_get_operating_mode(&dev) == SX1280_HAL_OP_MODE_RX_C) { + DEBUG("[sx1280] netdev keep RX continuous mode" ); + netopt_state_t state = NETOPT_STATE_RX; + dev->netdev.driver->set(&dev->netdev, NETOPT_STATE, &state, sizeof(state)); + return size; + } + else { + DEBUG("[sx1280] netdev NOT IMPLEMENTED"); + return 0; + } +} + +static int _init(netdev_t *netdev) +{ + sx1280_t *dev = (sx1280_t *)netdev; + + /* Launch initialization of driver and device */ + DEBUG("[sx1280] netdev: initializing driver...\n"); + if (sx1280_init(dev) != 0) { + DEBUG("[sx1280] netdev: initialization failed\n"); + return -1; + } + + DEBUG("[sx1280] netdev: initialization successful\n"); + return 0; +} + +static void _isr(netdev_t *netdev) +{ + sx1280_t *dev = (sx1280_t *)netdev; + sx1280_irq_mask_t irq_mask; + + ral_sx1280_get_and_clear_irq_status(&dev->ral, &irq_mask); + + if (irq_mask & RAL_IRQ_RX_DONE) { + if (irq_mask & RAL_IRQ_RX_CRC_ERROR) { + DEBUG("[sx1280] netdev: SX1280_IRQ_CRC_ERROR\n"); + netdev->event_callback(netdev, NETDEV_EVENT_CRC_ERROR); + } + else { + DEBUG("[sx1280] netdev: SX1280_IRQ_RX_DONE\n"); + netdev->event_callback(netdev, NETDEV_EVENT_RX_COMPLETE); + } + } + else if (irq_mask & RAL_IRQ_TX_DONE) { + DEBUG("[sx1280] netdev: SX1280_IRQ_TX_DONE\n"); + netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE); + } + else if (irq_mask & RAL_IRQ_RX_HDR_OK) { + DEBUG("[sx1280] netdev: SX1280_IRQ_HEADER_VALID\n"); + netdev->event_callback(netdev, NETDEV_EVENT_RX_STARTED); + } + else if (irq_mask & RAL_IRQ_RX_HDR_ERROR) { + DEBUG("[sx1280] netdev: SX1280_IRQ_HEADER_ERROR\n"); + } + else if (irq_mask & RAL_IRQ_RX_TIMEOUT) { + DEBUG("[sx1280x] netdev: SX1280_IRQ_RX_TIMEOUT\n"); + netdev->event_callback(netdev, NETDEV_EVENT_RX_TIMEOUT); + } +} + +static int _get_state(sx1280_t *dev, void *val) +{ + (void)dev; + netopt_state_t state = NETOPT_STATE_OFF; + sx1280_chip_status_t radio_status; + + sx1280_get_status(dev, &radio_status); + + switch (radio_status.chip_mode) { + + case SX1280_CHIP_MODE_STBY_RC: + /* Intentional fall-through */ + case SX1280_CHIP_MODE_STBY_XOSC: + state = NETOPT_STATE_STANDBY; + DEBUG("NETOPT_STATE_STANDBY "); + break; + + case SX1280_CHIP_MODE_RX: + state = NETOPT_STATE_RX; + DEBUG("NETOPT_STATE_RX "); + break; + case SX1280_CHIP_MODE_TX: + state = NETOPT_STATE_TX; + DEBUG("NETOPT_STATE_TX "); + break; + case SX1280_CHIP_MODE_FS: + DEBUG("SX1280_CHIP_MODE_FS"); + /* Intentional fall-through */ + default: + DEBUG("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 */ + sx1280_t *dev = (sx1280_t *)netdev; + + if (dev == NULL) { + return -ENODEV; + } + + switch (opt) { + case NETOPT_DEVICE_TYPE: + assert(max_len == sizeof(uint16_t)); + *((uint16_t *)val) = NETDEV_TYPE_LORA; + return sizeof(uint16_t); + case NETOPT_STATE: + assert(max_len >= sizeof(netopt_state_t)); + return _get_state(dev, val); + + case NETOPT_CHANNEL_FREQUENCY: + assert(max_len >= sizeof(uint32_t)); + *((uint32_t *)val) = sx1280_get_channel(dev); + return sizeof(uint32_t); + + case NETOPT_BANDWIDTH: + assert(max_len >= sizeof(uint32_t)); + *((uint32_t *)val) = sx1280_get_bandwidth(dev); + return sizeof(uint32_t); + + case NETOPT_SPREADING_FACTOR: + assert(max_len >= sizeof(uint8_t)); + *((uint8_t *)val) = sx1280_get_spreading_factor(dev); + return sizeof(uint8_t); + + case NETOPT_CODING_RATE: + assert(max_len >= sizeof(uint8_t)); + *((uint8_t *)val) = sx1280_get_coding_rate(dev); + return sizeof(uint8_t); + + case NETOPT_PDU_SIZE: + assert(max_len >= sizeof(uint8_t)); + *((uint8_t *)val) = sx1280_get_lora_payload_length(dev); + return sizeof(uint8_t); + + case NETOPT_INTEGRITY_CHECK: + assert(max_len >= sizeof(netopt_enable_t)); + *((netopt_enable_t *)val) = sx1280_get_lora_crc(dev) ? NETOPT_ENABLE : NETOPT_DISABLE; + return sizeof(netopt_enable_t); + + case NETOPT_IQ_INVERT: + assert(max_len >= sizeof(uint8_t)); + *((netopt_enable_t *)val) = sx1280_get_lora_iq_invert(dev) ? NETOPT_ENABLE : NETOPT_DISABLE; + return sizeof(netopt_enable_t); + + case NETOPT_RSSI: + assert(max_len >= sizeof(int16_t)); + sx1280_get_rssi_inst(dev, ((int16_t *)val)); + return sizeof(int16_t); + + default: + break; + } + + return -ENOTSUP; +} + +static int _set_state(sx1280_t *dev, netopt_state_t state) +{ + (void)dev; + switch (state) { + case NETOPT_STATE_STANDBY: + DEBUG("[sx1280] netdev: set NETOPT_STATE_STANDBY state\n"); + sx1280_set_standby(dev, SX1280_STANDBY_CFG_RC); + break; + + case NETOPT_STATE_IDLE: + /* intentional fall-throught */ + case NETOPT_STATE_RX: + DEBUG("[sx1280] netdev: set NETOPT_STATE_RX state\n"); + sx1280_set_rx(dev->ral.context, SX1280_TICK_SIZE_1000_US, SX1280_RX_MODE ); + break; + + case NETOPT_STATE_TX: + DEBUG("[sx1280] netdev: set NETOPT_STATE_TX state\n"); + sx1280_set_tx(dev->ral.context, SX1280_TICK_SIZE_1000_US, 0 ); + break; + + case NETOPT_STATE_RESET: + DEBUG("[sx1280] netdev: set NETOPT_STATE_RESET state\n"); + sx1280_reset(dev); + sx1280_init(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 */ + sx1280_t *dev = (sx1280_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_CHANNEL_FREQUENCY: + assert(len <= sizeof(uint32_t)); + uint32_t params_lora_bw = sx1280_get_bandwidth(dev); + + uint32_t freq = *((const uint32_t *)val); + uint32_t min_freq = (uint32_t)LORA_ISM2400_FREQUENCY_LOW + (params_lora_bw / 2); + uint32_t max_freq = (uint32_t)LORA_ISM2400_FREQUENCY_HIGH - (params_lora_bw / 2); + + if (freq < min_freq || freq > max_freq) { + res = -EINVAL; + break; + } + sx1280_set_channel(dev, *((const uint32_t *)val)); + return sizeof(uint32_t); + + case NETOPT_BANDWIDTH: + assert(len <= sizeof(uint16_t)); + uint16_t bw = *((const uint16_t *)val); + switch (bw) { + case 200: /* fall-through */ + case 400: /* fall-through */ + case 800: /* fall-through */ + case 1600: + sx1280_set_bandwidth(dev, bw); + return sizeof(uint16_t); + default: + res = -EINVAL; + puts("invalid bandwidth, use 200, 400 , 800, 1600"); + break; + } + + break; /* outer switch*/ + + case NETOPT_SPREADING_FACTOR: + assert(len <= sizeof(uint8_t)); + uint8_t sf = *((const uint8_t *)val); + if ((sf < RAL_LORA_SF5) || (sf > RAL_LORA_SF12)) { + res = -EINVAL; + break; + } + sx1280_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); + switch (cr) { + case LORA_CR_4_5: + cr = RAL_LORA_CR_4_5; + break; + case LORA_CR_4_6: + cr = RAL_LORA_CR_4_6; + break; + case LORA_CR_4_7: + cr = RAL_LORA_CR_4_7; + break; + case LORA_CR_4_8: + cr = RAL_LORA_CR_4_8; + break; + case LORA_CR_LI_4_5: + cr = RAL_LORA_CR_LI_4_5; + break; + case LORA_CR_LI_4_6: + cr = RAL_LORA_CR_LI_4_6; + break; + case LORA_CR_LI_4_8: + cr = RAL_LORA_CR_LI_4_8; + break; + default: + res = -EINVAL; + puts( + "invalid cr, use\n \ + LORA_CR_4_5 = 1\n \ + LORA_CR_4_6 = 2\n \ + LORA_CR_4_7 = 3\n \ + LORA_CR_4_8 = 4\n \ + LORA_CR_LI_4_5 = 5\n \ + LORA_CR_LI_4_6 = 6\n \ + LORA_CR_LI_4_8 = 7\n"); + return res; + + } + sx1280_set_coding_rate(dev, cr); + return sizeof(uint8_t); + + case NETOPT_PDU_SIZE: + assert(len <= sizeof(uint8_t)); + sx1280_set_lora_payload_length(dev, *((const uint8_t *)val)); + return sizeof(uint8_t); + + case NETOPT_INTEGRITY_CHECK: + assert(len <= sizeof(netopt_enable_t)); + sx1280_set_lora_crc(dev, *((const netopt_enable_t *)val) ? true : false); + return sizeof(netopt_enable_t); + + case NETOPT_PREAMBLE_LENGTH: + assert(len <= sizeof(uint16_t)); + sx1280_set_lora_preamble_length(dev, *((const uint16_t *)val)); + return sizeof(uint16_t); + + case NETOPT_IQ_INVERT: + assert(len <= sizeof(netopt_enable_t)); + sx1280_set_lora_iq_invert(dev, *((const netopt_enable_t *)val) ? true : false); + return sizeof(bool); + + default: + DEBUG(" OPT unrecognised (missing case) %i", opt); + break; + } + + return res; +} + +const netdev_driver_t sx1280_driver = { + .send = _send, + .recv = _recv, + .init = _init, + .isr = _isr, + .get = _get, + .set = _set, +}; From 779e249af6b6d6f60dfe9f8301b6becb7a02a1b8 Mon Sep 17 00:00:00 2001 From: Aymeric Brochier Date: Tue, 17 May 2022 15:47:56 +0200 Subject: [PATCH 4/4] tests/driver_sx1280: initial import --- tests/driver_sx1280/Makefile | 13 + tests/driver_sx1280/Makefile.ci | 5 + tests/driver_sx1280/README.md | 172 +++++++++++++ tests/driver_sx1280/app.config.test | 8 + tests/driver_sx1280/main.c | 379 ++++++++++++++++++++++++++++ 5 files changed, 577 insertions(+) create mode 100644 tests/driver_sx1280/Makefile create mode 100644 tests/driver_sx1280/Makefile.ci create mode 100644 tests/driver_sx1280/README.md create mode 100644 tests/driver_sx1280/app.config.test create mode 100644 tests/driver_sx1280/main.c diff --git a/tests/driver_sx1280/Makefile b/tests/driver_sx1280/Makefile new file mode 100644 index 000000000000..c7869c3c5e5d --- /dev/null +++ b/tests/driver_sx1280/Makefile @@ -0,0 +1,13 @@ +BOARD ?= nucleo-f103rb + +include ../Makefile.tests_common + +# required modules +USEMODULE += sx1280 + +USEMODULE += shell +USEMODULE += shell_commands + +USEMODULE += iolist + +include $(RIOTBASE)/Makefile.include diff --git a/tests/driver_sx1280/Makefile.ci b/tests/driver_sx1280/Makefile.ci new file mode 100644 index 000000000000..31931b03b6c0 --- /dev/null +++ b/tests/driver_sx1280/Makefile.ci @@ -0,0 +1,5 @@ +BOARD_INSUFFICIENT_MEMORY := \ + nucleo-l011k4 \ + stm32f030f4-demo \ + samd10-xmini \ + # diff --git a/tests/driver_sx1280/README.md b/tests/driver_sx1280/README.md new file mode 100644 index 000000000000..4cc4fb074e49 --- /dev/null +++ b/tests/driver_sx1280/README.md @@ -0,0 +1,172 @@ +## About + +This is a manual test application for the SX1280 radio driver. + +This manual test application was also tested with a custom board (esp32-wroom-32 + lambda80C-24S on Mikrobus_0) : +see https://github.com/thingsat/tinygs_2g4station + +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/sx1280/include/sx1280_params.h` file to your application directory. + +## Build and Flash + +```shell +cd tests/driver_sx1280 +make flash term +``` + +## Usage + +This test application provides low level shell commands to interact with the +SX1280 module. + +Once the board is flashed and you are connected via serial to the shell, use the `help` +command to display the available commands: + +``` +> help +2022-05-09 10:27:13,724 # help +2022-05-09 10:27:13,731 # Command Description +2022-05-09 10:27:13,731 # --------------------------------------- +2022-05-09 10:27:13,735 # sx1280 Control the SX1280 radio +2022-05-09 10:27:13,738 # reboot Reboot the node +2022-05-09 10:27:13,742 # version Prints current RIOT_VERSION +2022-05-09 10:27:13,747 # pm interact with layered PM subsystem +``` + +### Getter + +``` +2022-04-28 15:00:32,795 # main(): This is RIOT! (Version: 2021.10-devel-3491-g23b57-pr/sx1280_v2) +2022-04-28 15:00:32,806 # Initialization successful - starting the shell now +> sx1280 +2022-04-28 15:00:39,408 # sx1280 +2022-04-28 15:00:39,409 # Usage: sx1280 +> sx1280 get +2022-04-28 15:00:44,808 # sx1280 get +2022-04-28 15:00:44,809 # Usage: sx1280 get +> sx1280 get type +2022-04-28 15:00:48,064 # sx1280 get type +2022-04-28 15:00:48,065 # Device type: lora +> sx1280 get freq +2022-04-28 15:00:52,639 # sx1280 get freq +2022-04-28 15:00:52,639 # Frequency: 2403000000 Hz +> sx1280 get bw +2022-04-28 15:00:57,130 # sx1280 get bw +2022-04-28 15:00:57,131 # Bandwidth: 800 kHz +> sx1280 get sf +2022-04-28 15:01:01,476 # sx1280 get sf +2022-04-28 15:01:01,477 # Spreading factor: 12 +2022-04-28 15:01:10,159 # sx1280 get cr +2022-04-28 15:01:10,165 # Coding rate: 6 +2022-04-28 15:01:10,165 # Usage: use +2022-04-28 15:01:10,166 # LORA_CR_4_5 = 1 +2022-04-28 15:01:10,166 # LORA_CR_4_6 = 2 +2022-04-28 15:01:10,167 # LORA_CR_4_7 = 3 +2022-04-28 15:01:10,167 # LORA_CR_4_8 = 4 +2022-04-28 15:01:10,169 # LORA_CR_LI_4_5 = 5 +2022-04-28 15:01:10,170 # LORA_CR_LI_4_6 = 6 +2022-04-28 15:01:10,171 # LORA_CR_LI_4_8 = 7 +``` + +### Setter + +``` +2022-04-28 16:30:53,635 # sx1280 +2022-04-28 16:30:53,642 # Usage: sx1280 +> sx1280 set +2022-04-28 16:30:56,504 # sx1280 set +2022-04-28 16:30:56,505 # Usage: sx1280 set +> sx1280 freq +2022-04-28 16:31:02,802 # sx1280 freq +> sx1280 set freq +2022-04-28 16:31:12,282 # sx1280 set freq +2022-04-28 16:31:12,287 # Usage: use freq between 2400000000 + (bw/2) and 2500000000 - (bw/2) (Hz) ! +2022-04-28 16:31:12,290 # Usage: sx1280 set +> sx1280 set bw +2022-04-28 16:31:18,924 # sx1280 set bw +2022-04-28 16:31:18,925 # Usage: use 200, 400, 800, 1600 (kHz) +2022-04-28 16:31:18,929 # Usage: sx1280 set +> sx1280 set sf +2022-04-28 16:31:23,232 # sx1280 set sf +2022-04-28 16:31:23,233 # Usage: use SF between 5 and 12 +2022-04-28 16:31:23,236 # Usage: sx1280 set +> sx1280 set cr +2022-04-28 15:01:10,159 # sx1280 set cr +2022-04-28 15:01:10,165 # Usage: use +2022-04-28 15:01:10,166 # LORA_CR_4_5 = 1 +2022-04-28 15:01:10,166 # LORA_CR_4_6 = 2 +2022-04-28 15:01:10,167 # LORA_CR_4_7 = 3 +2022-04-28 15:01:10,167 # LORA_CR_4_8 = 4 +2022-04-28 15:01:10,169 # LORA_CR_LI_4_5 = 5 +2022-04-28 15:01:10,170 # LORA_CR_LI_4_6 = 6 +2022-04-28 15:01:10,171 # LORA_CR_LI_4_8 = 7 +> sx1280 set cr 6 +2022-04-28 15:01:12,359 # sx1280 set cr 6 +2022-04-28 15:01:12,362 # cr set + +2022-04-28 16:31:27,001 # Usage: sx1280 set +> sx1280 set freq 2455555555 +2022-04-28 16:32:20,235 # sx1280 set freq 2455555555 +2022-04-28 16:32:20,236 # freq set +> sx1280 set bw 200 +2022-04-28 16:32:29,468 # sx1280 set bw 200 +2022-04-28 16:32:29,468 # setup: setting 200KHz bandwidth +2022-04-28 16:32:29,469 # bw set +> sx1280 set sf 5 +2022-04-28 16:32:35,138 # sx1280 set sf 5 +2022-04-28 16:32:35,141 # sf set +> sx1280 set cr 4 +2022-04-28 16:32:40,970 # sx1280 set cr 4 +2022-04-28 16:32:40,973 # cr set + +sx1280 reset +2022-05-17 13:36:54,836 # sx1280 reset +2022-05-17 13:36:54,836 # resetting sx1280... +``` + +### TX/RX + + Once we have a shell on both card we can use the shell cmd : + +``` +2022-04-28 16:26:48,034 # main(): This is RIOT! (Version: 2021.10-devel-3491-g23b57-pr/sx1280_v2) +2022-04-28 16:26:48,046 # Initialization successful - starting the shell now +help +2022-04-28 16:27:23,060 # help +2022-04-28 16:27:23,061 # Command Description +2022-04-28 16:27:23,066 # --------------------------------------- +2022-04-28 16:27:23,067 # sx1280 Control the SX1280 radio +2022-04-28 16:27:23,072 # reboot Reboot the node +2022-04-28 16:27:23,077 # version Prints current RIOT_VERSION +2022-04-28 16:27:23,082 # pm interact with layered PM subsystem +2022-04-28 16:27:23,085 # ps Prints information about running threads. +> sx1280 +2022-04-28 16:27:27,574 # sx1280 +2022-04-28 16:27:27,575 # Usage: sx1280 +> sx1280 tx +2022-04-28 16:27:30,710 # sx1280 tx +2022-04-28 16:27:30,711 # Usage: sx1280 tx +> sx1280 tx test_default_params +2022-04-28 16:28:00,452 # sx1280 tx test_default_params +2022-04-28 16:28:00,455 # sending "test_default_params" payload (20 bytes) +> 2022-04-28 16:28:00,718 # Transmission completed +``` + +``` +2022-04-28 16:26:43,538 # Connect to serial port /dev/tty.usbserial-14620 +Welcome to pyterm! +Type '/exit' to exit. +sx1280 +2022-04-28 16:27:49,686 # sx1280 +2022-04-28 16:27:49,687 # Usage: sx1280 +> sx1280 rx +2022-04-28 16:27:53,657 # sx1280 rx +2022-04-28 16:27:53,657 # Usage: sx1280 rx +> sx1280 rx start +2022-04-28 16:27:56,596 # sx1280 rx start +2022-04-28 16:27:56,600 # Listen mode started +> 2022-04-28 16:28:00,562 # Data reception started +2022-04-28 16:28:00,726 # Received: "test_default_params" (20 bytes) - [RSSI: -43, SNR: 8] +``` diff --git a/tests/driver_sx1280/app.config.test b/tests/driver_sx1280/app.config.test new file mode 100644 index 000000000000..aecb9479c42e --- /dev/null +++ b/tests/driver_sx1280/app.config.test @@ -0,0 +1,8 @@ +# this file enables modules defined in Kconfig. Do not use this file for +# application configuration. This is only needed during migration. +CONFIG_MODULE_SX1280=y + +CONFIG_MODULE_IOLIST=y + +CONFIG_MODULE_SHELL=y +CONFIG_MODULE_SHELL_COMMANDS=y diff --git a/tests/driver_sx1280/main.c b/tests/driver_sx1280/main.c new file mode 100644 index 000000000000..e55266186b57 --- /dev/null +++ b/tests/driver_sx1280/main.c @@ -0,0 +1,379 @@ +/* + * Copyright (C) 2022 Inria + * Copyright (C) 2020-2022 Université Grenoble Alpes + * + * 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 SX1280 Driver + * + * @author Aymeric Brochier + * + * @} + */ + +#include +#include +#include +#include + +#include "msg.h" +#include "thread.h" +#include "shell.h" + +#include "net/lora.h" +#include "net/netdev.h" +#include "net/netdev/lora.h" + +#include "sx1280.h" +#include "sx1280_params.h" +#include "sx1280_netdev.h" + +#define SX1280_MSG_QUEUE (8U) +#define SX1280_STACKSIZE (THREAD_STACKSIZE_DEFAULT) +#define SX1280_MSG_TYPE_ISR (0x3456) +#define SX1280_MAX_PAYLOAD_LEN (128U) + +static char stack[SX1280_STACKSIZE]; +static kernel_pid_t _recv_pid; + +static char message[SX1280_MAX_PAYLOAD_LEN]; + +static sx1280_t sx1280; + +static void _event_cb(netdev_t *dev, netdev_event_t event) +{ + if (event == NETDEV_EVENT_ISR) { + msg_t msg; + msg.type = SX1280_MSG_TYPE_ISR; + if (msg_send(&msg, _recv_pid) <= 0) { + puts("sx1280_netdev: possibly lost interrupt."); + } + } + else { + switch (event) { + case NETDEV_EVENT_RX_STARTED: + puts("Data reception started"); + break; + + case NETDEV_EVENT_RX_COMPLETE: + { + size_t len = dev->driver->recv(dev, NULL, 0, 0); + netdev_lora_rx_info_t packet_info; + dev->driver->recv(dev, message, len, &packet_info); + printf( + "Received: \"%s\" (%d bytes) - [RSSI: %i, SNR: %i]\n", + message, (int)len, packet_info.rssi, (int)packet_info.snr); + } + break; + + case NETDEV_EVENT_TX_COMPLETE: + puts("Transmission completed"); + break; + + case NETDEV_EVENT_TX_TIMEOUT: + puts("Transmission timeout"); + break; + + default: + printf("Unexpected netdev event received: %d\n", event); + break; + } + } +} + +void *_recv_thread(void *arg) +{ + netdev_t *netdev = arg; + + static msg_t _msg_queue[SX1280_MSG_QUEUE]; + + msg_init_queue(_msg_queue, SX1280_MSG_QUEUE); + + while (1) { + msg_t msg; + msg_receive(&msg); + if (msg.type == SX1280_MSG_TYPE_ISR) { + netdev->driver->isr(netdev); + } + else { + puts("Unexpected msg type"); + } + } +} + +static void _get_usage(const char *cmd) +{ + printf("Usage: %s get \n", cmd); +} + + +static void _usage_freq(void) +{ + printf("Usage: use freq between 2400000000 + (bw/2) and 2500000000 - (bw/2) (Hz) !\n"); +} + +static void _usage_bw(void) +{ + printf("Usage: use 200, 400, 800, 1600 (kHz)\n"); +} + +static void _usage_sf(void) +{ + printf("Usage: use SF between 5 and 12\n"); +} + +static void _usage_cr(void) +{ + printf( + "Usage: use\n \ + LORA_CR_4_5 = 1\n \ + LORA_CR_4_6 = 2\n \ + LORA_CR_4_7 = 3\n \ + LORA_CR_4_8 = 4\n \ + LORA_CR_LI_4_5 = 5\n \ + LORA_CR_LI_4_6 = 6\n \ + LORA_CR_LI_4_8 = 7\n"); +} + + +static int sx1280_get_cmd(netdev_t *netdev, int argc, char **argv) +{ + if (argc == 2) { + _get_usage(argv[0]); + return -1; + } + + if (!strcmp("type", argv[2])) { + uint16_t type; + netdev->driver->get(netdev, NETOPT_DEVICE_TYPE, &type, sizeof(uint16_t)); + printf("Device type: %s\n", (type == NETDEV_TYPE_LORA) ? "lora" : "fsk"); + } + else if (!strcmp("freq", argv[2])) { + uint32_t freq; + netdev->driver->get(netdev, NETOPT_CHANNEL_FREQUENCY, &freq, sizeof(uint32_t)); + printf("Frequency: %" PRIu32 " Hz\n", freq); + } + else if (!strcmp("bw", argv[2])) { + uint32_t bw_val = 0; + netdev->driver->get(netdev, NETOPT_BANDWIDTH, &bw_val, sizeof(uint32_t)); + printf("Bandwidth: %" PRIu32 " kHz\n", bw_val / 1000); + } + else if (!strcmp("sf", argv[2])) { + uint8_t sf; + netdev->driver->get(netdev, NETOPT_SPREADING_FACTOR, &sf, sizeof(uint8_t)); + printf("Spreading factor: %d\n", sf); + } + else if (!strcmp("cr", argv[2])) { + uint8_t cr; + netdev->driver->get(netdev, NETOPT_CODING_RATE, &cr, sizeof(uint8_t)); + printf("Coding rate: %d\n", cr); + _usage_cr(); + } + else { + _get_usage(argv[0]); + return -1; + } + + return 0; +} + +static void _set_usage(const char *cmd) +{ + printf("Usage: %s set \n", cmd); +} + +static int sx1280_set_cmd(netdev_t *netdev, int argc, char **argv) +{ + if (argc == 3) { + if (!strcmp("freq", argv[2])) { + _usage_freq(); + } + if (!strcmp("bw", argv[2])) { + _usage_bw(); + } + if (!strcmp("sf", argv[2])) { + _usage_sf(); + } + if (!strcmp("cr", argv[2])) { + _usage_cr(); + } + } + if (argc != 4) { + _set_usage(argv[0]); + return -1; + } + + int ret = 0; + + if (!strcmp("freq", argv[2])) { + uint32_t freq = strtoul(argv[3], NULL, 10); + ret = netdev->driver->set(netdev, NETOPT_CHANNEL_FREQUENCY, &freq, sizeof(uint32_t)); + } + else if (!strcmp("bw", argv[2])) { + uint16_t bw = atoi(argv[3]); + ret = netdev->driver->set(netdev, NETOPT_BANDWIDTH, &bw, sizeof(uint16_t)); + } + else if (!strcmp("sf", argv[2])) { + uint8_t sf = atoi(argv[3]); + ret = netdev->driver->set(netdev, NETOPT_SPREADING_FACTOR, &sf, sizeof(uint8_t)); + } + else if (!strcmp("cr", argv[2])) { + uint8_t cr = atoi(argv[3]); + ret = netdev->driver->set(netdev, NETOPT_CODING_RATE, &cr, sizeof(uint8_t)); + } + else { + _set_usage(argv[0]); + return -1; + } + + if (ret < 0) { + printf("cannot set %s\n", argv[2]); + return ret; + } + + printf("%s set\n", argv[2]); + return 0; +} + +static void _rx_usage(const char *cmd) +{ + printf("Usage: %s rx \n", cmd); +} + +static int sx1280_rx_cmd(netdev_t *netdev, int argc, char **argv) +{ + if (argc == 2) { + _rx_usage(argv[0]); + return -1; + } + + if (!strcmp("start", argv[2])) { + /* Switch to RX (IDLE) state */ + netopt_state_t state = NETOPT_STATE_IDLE; + netdev->driver->set(netdev, NETOPT_STATE, &state, sizeof(state)); + printf("Listen mode started\n"); + } + else if (!strcmp("stop", argv[2])) { + /* Switch to STANDBY state */ + netopt_state_t state = NETOPT_STATE_STANDBY; + netdev->driver->set(netdev, NETOPT_STATE, &state, sizeof(state)); + printf("Listen mode stopped\n"); + } + else { + _rx_usage(argv[0]); + return -1; + } + + return 0; +} + +static int sx1280_tx_cmd(netdev_t *netdev, int argc, char **argv) +{ + if (argc == 2) { + printf("Usage: %s tx \n", argv[0]); + return -1; + } + + printf("sending \"%s\" payload (%u bytes)\n", + argv[2], (unsigned)strlen(argv[2]) + 1); + iolist_t iolist = { + .iol_base = argv[2], + .iol_len = (strlen(argv[2]) + 1) + }; + + if (netdev->driver->send(netdev, &iolist) == -ENOTSUP) { + puts("Cannot send: radio is still transmitting"); + return -1; + } + + return 0; +} + +static int sx1280_reset_cmd(netdev_t *netdev, int argc, char **argv) +{ + (void)argc; + (void)argv; + + puts("resetting sx1280..."); + netopt_state_t state = NETOPT_STATE_RESET; + + netdev->driver->set(netdev, NETOPT_STATE, &state, sizeof(netopt_state_t)); + return 0; + +} +int sx1280_cmd(int argc, char **argv) +{ + if (argc < 2) { + printf("Usage: %s \n", argv[0]); + return -1; + } + + netdev_t *netdev = &sx1280.netdev; + + if (!strcmp("get", argv[1])) { + return sx1280_get_cmd(netdev, argc, argv); + } + else if (!strcmp("set", argv[1])) { + return sx1280_set_cmd(netdev, argc, argv); + } + else if (!strcmp("rx", argv[1])) { + return sx1280_rx_cmd(netdev, argc, argv); + } + else if (!strcmp("tx", argv[1])) { + return sx1280_tx_cmd(netdev, argc, argv); + } + else if (!strcmp("reset", argv[1])) { + return sx1280_reset_cmd(netdev, argc, argv); + } + else { + printf("Unknown cmd %s\n", argv[1]); + printf("Usage: %s \n", argv[0]); + return -1; + } + + return 0; +} + +static const shell_command_t shell_commands[] = { + { "sx1280", "Control the SX1280 radio", sx1280_cmd }, + { NULL, NULL, NULL } +}; + +int main(void) +{ + sx1280_setup(&sx1280, &sx1280_params[0], 0); + + netdev_t *netdev = &sx1280.netdev; + + netdev->driver = &sx1280_driver; + + if (netdev->driver->init(netdev) < 0) { + puts("Failed to initialize SX1280 device, exiting"); + return 1; + } + + netdev->event_callback = _event_cb; + + _recv_pid = thread_create(stack, sizeof(stack), THREAD_PRIORITY_MAIN - 1, + THREAD_CREATE_STACKTEST, _recv_thread, netdev, + "recv_thread"); + + if (_recv_pid <= KERNEL_PID_UNDEF) { + puts("Creation of receiver thread failed"); + return 1; + } + + puts("Initialization successful - starting the shell now"); + char line_buf[SHELL_DEFAULT_BUFSIZE]; + + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + return 0; +}