1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-01-17 05:12:57 +01:00

Merge pull request #16579 from akshaim/pr/wl55jc_lorawan_final

drivers/sx126x: Add support for Nucleo -WL55JC
This commit is contained in:
José Alamos 2021-07-09 14:48:11 +02:00 committed by GitHub
commit 934c875aba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 578 additions and 36 deletions

View File

@ -1,4 +1,10 @@
ifneq (,$(filter stdio_uart,$(USEMODULE)))
FEATURES_REQUIRED += periph_lpuart
endif
ifneq (,$(filter netdev_default,$(USEMODULE)))
USEMODULE += sx126x_stm32wl
endif
ifneq (,$(filter sx126x_stm32wl,$(USEMODULE)))
USEMODULE += sx126x_rf_switch
endif
include $(RIOTBOARD)/common/nucleo/Makefile.dep

View File

@ -0,0 +1,73 @@
/*
* Copyright (C) 2021 Freie Universität Berlin
*
* 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 boards_nucleo-wl55jc
* @{
*
* @file board.c
* @brief Board specific implementations for the Nucleo-wl55jc board
*
*
* @author Akshai M <akshai.m@fu-berlin.de>
*
* @}
*/
#include <stdio.h>
#include "board.h"
#include "periph/gpio.h"
void board_init(void)
{
/* initialize the CPU */
board_common_nucleo_init();
if (IS_USED(MODULE_SX126X_STM32WL)) {
/* Initialize the GPIO control for RF 3-port switch (SP3T) */
gpio_init(FE_CTRL1, GPIO_OUT);
gpio_init(FE_CTRL2, GPIO_OUT);
gpio_init(FE_CTRL3, GPIO_OUT);
}
}
/**
* @brief Callback to set RF switch mode
*
* This function sets the GPIO's wired to the SP3T RF Switch. Nucleo-WL55JC
* supports three modes of operation.
*/
#if IS_USED(MODULE_SX126X_STM32WL)
void nucleo_wl55jc_sx126x_set_rf_mode(sx126x_t *dev, sx126x_rf_mode_t rf_mode)
{
(void)dev;
switch (rf_mode) {
case SX126X_RF_MODE_RX:
gpio_set(FE_CTRL1);
gpio_clear(FE_CTRL2);
gpio_set(FE_CTRL3);
break;
case SX126X_RF_MODE_TX_LPA:
gpio_set(FE_CTRL1);
gpio_set(FE_CTRL2);
gpio_set(FE_CTRL3);
break;
case SX126X_RF_MODE_TX_HPA:
gpio_clear(FE_CTRL1);
gpio_set(FE_CTRL2);
gpio_set(FE_CTRL3);
break;
default:
break;
}
}
#endif

View File

@ -27,6 +27,7 @@
| Programming Manual 2 | [Reference Manual - M0](https://www.st.com/resource/en/programming_manual/dm00104451-cortexm0-programming-manual-for-stm32l0-stm32g0-stm32wl-and-stm32wb-series-stmicroelectronics.pdf) |
| Board Manual | [Board Manual](https://www.st.com/resource/en/data_brief/nucleo-wl55jc.pdf) |
| Board Schematic | [Board Schematic](https://www.st.com/resource/en/user_manual/dm00622917-stm32wl-nucleo64-board-mb1389-stmicroelectronics.pdf) |
| Additional Reference | [Developing LoRaWAN Application](https://www.st.com/resource/en/application_note/dm00660451-how-to-build-a-lora-application-with-stm32cubewl-stmicroelectronics.pdf)
## Flashing the device

View File

@ -22,10 +22,27 @@
#include "board_nucleo.h"
/* Required for `nucleo_wl55jc_sx126x_set_rf_mode` */
#if IS_USED(MODULE_SX126X_STM32WL)
#include "sx126x.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name Sub-GHz radio (LoRa) configuration
* @{
*/
#define SX126X_PARAM_SPI (SPI_DEV(0))
#if IS_USED(MODULE_SX126X_STM32WL)
extern void nucleo_wl55jc_sx126x_set_rf_mode(sx126x_t *dev, sx126x_rf_mode_t rf_mode);
#define SX126X_PARAM_SET_RF_MODE_CB nucleo_wl55jc_sx126x_set_rf_mode
#endif
/** @} */
/**
* @name LED pin definitions and handlers
* @{
@ -52,7 +69,10 @@ extern "C" {
#define LED2_TOGGLE (LED0_PORT->ODR ^= LED2_MASK)
/** @} */
/* nucleo-wl55jc always use LED0, as there is no dual use of its pin */
/**
* @brief Nucleo-wl55jc always use LED0, as there is no dual use of its pin
* @{
*/
#ifndef AUTO_INIT_LED0
#define AUTO_INIT_LED0
#endif
@ -70,6 +90,17 @@ extern "C" {
#define BTN2_MODE GPIO_IN_PU
/** @} */
/**
* @name RF 3-port switch (SP3T) control
*
* Refer Section 6.6.3 RF Overview in User Manual (UM2592)
* @{
*/
#define FE_CTRL1 GPIO_PIN(PORT_C, 4)
#define FE_CTRL2 GPIO_PIN(PORT_C, 5)
#define FE_CTRL3 GPIO_PIN(PORT_C, 3)
/** @} */
#ifdef __cplusplus
}
#endif

View File

@ -84,6 +84,21 @@ static const uart_conf_t uart_config[] = {
*/
static const spi_conf_t spi_config[] = {
{
.dev = SUBGHZSPI, /* Internally connected to Sub-GHz radio Modem */
.mosi_pin = GPIO_UNDEF,
.miso_pin = GPIO_UNDEF,
.sclk_pin = GPIO_UNDEF,
.cs_pin = GPIO_UNDEF,
.mosi_af = GPIO_AF_UNDEF,
.miso_af = GPIO_AF_UNDEF,
.sclk_af = GPIO_AF_UNDEF,
.cs_af = GPIO_AF_UNDEF,
.rccmask = RCC_APB3ENR_SUBGHZSPIEN,
.apbbus = APB3,
}
/* SUBGHZ DEBUG PINS use the SPI1 pins */
#if !IS_ACTIVE(CONFIG_STM32_WL55JC_SUBGHZ_DEBUG)
,{
.dev = SPI1,
.mosi_pin = GPIO_PIN(PORT_A, 7),
.miso_pin = GPIO_PIN(PORT_A, 6),
@ -96,6 +111,7 @@ static const spi_conf_t spi_config[] = {
.rccmask = RCC_APB2ENR_SPI1EN,
.apbbus = APB2,
}
#endif
};
#define SPI_NUMOF ARRAY_SIZE(spi_config)

View File

@ -14,3 +14,9 @@
* sharing the same architecture, the implementation is split into several CPU
* specific parts and an architecture part (e.g. arm7_common and lpc23xx).
*/
/**
* @defgroup config_cpu CPU Configurations
* @ingroup config
* @brief Compile time configurations for different kinds of CPU.
*/

View File

@ -172,6 +172,11 @@ void periph_clk_dis(bus_t bus, uint32_t mask)
#endif
break;
#endif
#if defined(CPU_FAM_STM32WL)
case APB3:
RCC->APB3ENR &= ~(mask);
break;
#endif
#if defined(CPU_FAM_STM32L4) || defined(CPU_FAM_STM32WB) || \
defined(CPU_FAM_STM32G4) || defined(CPU_FAM_STM32L5) || \
defined(CPU_FAM_STM32WL)

View File

@ -37,6 +37,7 @@
#include "stmclk.h"
#include "periph_cpu.h"
#include "periph/init.h"
#include "periph/gpio.h"
#include "board.h"
#if defined (CPU_FAM_STM32L4) || defined (CPU_FAM_STM32G4) || \
@ -151,6 +152,52 @@ static void _gpio_init_ain(void)
}
#endif
/**
* @brief Initialize HW debug pins for Sub-GHz Radio
*/
void _wl55jc_init_subghz_debug_pins(void)
{
#if IS_ACTIVE(CONFIG_STM32_WL55JC_SUBGHZ_DEBUG)
/* SUBGHZSPI Debug */
gpio_init(CPU_STM32WL_SUBGHZSPI_DEBUG_MOSIOUT, GPIO_OUT);
gpio_init_af(CPU_STM32WL_SUBGHZSPI_DEBUG_MOSIOUT,
CPU_STM32WL_SUBGHZSPI_DEBUG_MOSIOUT_AF);
gpio_init(CPU_STM32WL_SUBGHZSPI_DEBUG_MISOOUT, GPIO_OUT);
gpio_init_af(CPU_STM32WL_SUBGHZSPI_DEBUG_MISOOUT,
CPU_STM32WL_SUBGHZSPI_DEBUG_MISOOUT_AF);
gpio_init(CPU_STM32WL_SUBGHZSPI_DEBUG_SCKOUT, GPIO_OUT);
gpio_init_af(CPU_STM32WL_SUBGHZSPI_DEBUG_SCKOUT,
CPU_STM32WL_SUBGHZSPI_DEBUG_SCKOUT_AF);
gpio_init(CPU_STM32WL_SUBGHZSPI_DEBUG_NSSOUT, GPIO_OUT);
gpio_init_af(CPU_STM32WL_SUBGHZSPI_DEBUG_NSSOUT,
CPU_STM32WL_SUBGHZSPI_DEBUG_NSSOUT_AF);
/* Sub-GHz Radio Debug */
gpio_init(CPU_STM32WL_SUBGHZ_RF_BUSY, GPIO_OUT);
gpio_init_af(CPU_STM32WL_SUBGHZ_RF_BUSY,
CPU_STM32WL_SUBGHZ_RF_BUSY_AF);
gpio_init(CPU_STM32WL_SUBGHZ_DEBUG_RF_NRESET, GPIO_OUT);
gpio_init_af(CPU_STM32WL_SUBGHZ_DEBUG_RF_NRESET,
CPU_STM32WL_SUBGHZ_DEBUG_RF_NRESET_AF);
gpio_init(CPU_STM32WL_SUBGHZ_DEBUG_RF_SMPSRDY, GPIO_OUT);
gpio_init_af(CPU_STM32WL_SUBGHZ_DEBUG_RF_SMPSRDY,
CPU_STM32WL_SUBGHZ_DEBUG_RF_SMPSRDY_AF);
gpio_init(CPU_STM32WL_SUBGHZ_DEBUG_RF_LDORDY, GPIO_OUT);
gpio_init_af(CPU_STM32WL_SUBGHZ_DEBUG_RF_LDORDY,
CPU_STM32WL_SUBGHZ_DEBUG_RF_LDORDY_AF);
gpio_init(CPU_STM32WL_SUBGHZ_DEBUG_RF_HSE32RDY, GPIO_OUT);
gpio_init_af(CPU_STM32WL_SUBGHZ_DEBUG_RF_HSE32RDY,
CPU_STM32WL_SUBGHZ_DEBUG_RF_HSE32RDY_AF);
#endif
}
void cpu_init(void)
{
/* initialize the Cortex-M core */
@ -184,4 +231,8 @@ void cpu_init(void)
/* trigger static peripheral initialization */
periph_init();
if (IS_ACTIVE(CONFIG_STM32_WL55JC_SUBGHZ_DEBUG)) {
_wl55jc_init_subghz_debug_pins();
}
}

View File

@ -34,6 +34,95 @@ extern "C" {
#endif /* ndef DOXYGEN */
/**
* @defgroup cpu_stm32_wl_debug STM32WL hardware debugging
* @ingroup cpu_stm32
* @{
*/
/**
* @defgroup cpu_stm32_wl_debug_subghz_spi STM32WL Sub-GHz SPI debug pins
* @ingroup cpu_stm32_wl_debug
* @{
*/
#define CPU_STM32WL_SUBGHZSPI_DEBUG_MOSIOUT GPIO_PIN(PORT_A, 7)
#define CPU_STM32WL_SUBGHZSPI_DEBUG_MOSIOUT_AF GPIO_AF13
#define CPU_STM32WL_SUBGHZSPI_DEBUG_MISOOUT GPIO_PIN(PORT_A, 6)
#define CPU_STM32WL_SUBGHZSPI_DEBUG_MISOOUT_AF GPIO_AF13
#define CPU_STM32WL_SUBGHZSPI_DEBUG_SCKOUT GPIO_PIN(PORT_A, 5)
#define CPU_STM32WL_SUBGHZSPI_DEBUG_SCKOUT_AF GPIO_AF13
#define CPU_STM32WL_SUBGHZSPI_DEBUG_NSSOUT GPIO_PIN(PORT_A, 4)
#define CPU_STM32WL_SUBGHZSPI_DEBUG_NSSOUT_AF GPIO_AF13
/** @} */
/** @} */
/**
* @defgroup cpu_stm32_wl_debug_subghz_radio STM32WL Sub-GHz Radio debug pins
* @ingroup cpu_stm32_wl_debug
* @{
*/
/*!
* RF BUSY debug pin definition
*/
#define CPU_STM32WL_SUBGHZ_RF_BUSY GPIO_PIN(PORT_A, 12)
/*!
* RF BUSY debug pin alternate function
*/
#define CPU_STM32WL_SUBGHZ_RF_BUSY_AF GPIO_AF6
/*!
* RF NRESET debug pin definition
*/
#define CPU_STM32WL_SUBGHZ_DEBUG_RF_NRESET GPIO_PIN(PORT_A, 11)
/*!
* RF NRESET debug pin alternate function
*/
#define CPU_STM32WL_SUBGHZ_DEBUG_RF_NRESET_AF GPIO_AF13
/*!
* RF SMPSRDY debug pin definition
*/
#define CPU_STM32WL_SUBGHZ_DEBUG_RF_SMPSRDY GPIO_PIN(PORT_B, 2)
/*!
* RF SMPSRDY debug pin alternate function
*/
#define CPU_STM32WL_SUBGHZ_DEBUG_RF_SMPSRDY_AF GPIO_AF13
/*!
* RF LDORDY debug pin definition
*/
#define CPU_STM32WL_SUBGHZ_DEBUG_RF_LDORDY GPIO_PIN(PORT_B, 4)
/*!
* RF LDORDY debug pin alternate function
*/
#define CPU_STM32WL_SUBGHZ_DEBUG_RF_LDORDY_AF GPIO_AF13
/*!
* RF HSE32RDY debug pin definition
*/
#define CPU_STM32WL_SUBGHZ_DEBUG_RF_HSE32RDY GPIO_PIN(PORT_A, 10)
/*!
* RF HSE32RDY debug pin alternate function
*/
#define CPU_STM32WL_SUBGHZ_DEBUG_RF_HSE32RDY_AF GPIO_AF13
/** @} */
/**
* @defgroup cpu_stm32_wl_config STM32WL compile time configuration
* @ingroup cpu_stm32_wl_debug
* @ingroup config_cpu
* @{
*/
/**
* @brief Set this to 1 to enable hardware debugging.
*/
#ifdef DOXYGEN
#define CONFIG_STM32_WL55JC_SUBGHZ_DEBUG
#endif
/** @} */
#ifdef __cplusplus
}
#endif

View File

@ -324,8 +324,10 @@ typedef enum {
GPIO_AF12, /**< use alternate function 12 */
GPIO_AF13, /**< use alternate function 13 */
GPIO_AF14, /**< use alternate function 14 */
GPIO_AF15 /**< use alternate function 15 */
GPIO_AF15, /**< use alternate function 15 */
#endif
GPIO_AF_UNDEF /** an UNDEF value definition, e.g. for register
based spi */
#endif
} gpio_af_t;

View File

@ -1,4 +1,5 @@
# Copyright (c) 2021 Inria
# Copyright (c) 2021 Freie Universitaet Berlin
#
# 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
@ -22,3 +23,11 @@ config HAS_CPU_STM32WL
bool
help
Indicates that the cpu being used belongs to the 'stm32wl' family.
config STM32_WL55JC_SUBGHZ_DEBUG
bool "STM32WL->Enable Hardware Debugging"
help
Enable Hardware debug pins. This would affect onboard peripherals such as SPI
as the pins are multiplexed. For more information check Alternate Functions
column in Table 19 : STM32WL55/54xx pin definition in STM32WL55/54xx
datasheet.

View File

@ -487,17 +487,18 @@ void stmclk_init_sysclk(void)
~100 kHz and the MSI clock to be based on MSISRANGE in RCC_CSR
(instead of MSIRANGE in the RCC_CR) */
RCC->CR = (RCC_CR_HSION);
/* Use VDDTCXO regulator, required by the radio and HSE */
if (IS_ACTIVE(CLOCK_ENABLE_HSE) || IS_USED(MODULE_SX126X_STM32WL)) {
#ifdef RCC_CR_HSEBYPPWR
RCC->CR |= (RCC_CR_HSEBYPPWR);
#endif
}
/* Enable the HSE clock only when it's provided by the board and required:
- Use HSE as system clock
- Use HSE as PLL input clock
*/
if (IS_ACTIVE(CLOCK_ENABLE_HSE)) {
/* Use VDDTCXO regulator */
#if defined(CPU_FAM_STM32WL)
RCC->CR |= (RCC_CR_HSEBYPPWR);
#endif
RCC->CR |= (RCC_CR_HSEON);
while (!(RCC->CR & RCC_CR_HSERDY)) {}
}

View File

@ -34,6 +34,20 @@
extern "C" {
#endif
/**
* * @note Forward declaration of the SX126x device descriptor
*/
typedef struct sx126x sx126x_t;
/**
* @brief RF switch states
*/
typedef enum {
SX126X_RF_MODE_RX,
SX126X_RF_MODE_TX_LPA,
SX126X_RF_MODE_TX_HPA,
} sx126x_rf_mode_t;
/**
* @brief Whether there's only one variant of this driver at compile time or
* not.
@ -43,8 +57,17 @@ extern "C" {
+ IS_USED(MODULE_SX1262) \
+ IS_USED(MODULE_SX1268) \
+ IS_USED(MODULE_LLCC68) \
+ IS_USED(MODULE_SX126X_STM32WL) \
) == 1)
/**
* @brief Used to identify if its a generic SPI module
*/
#if (IS_USED(MODULE_SX1261) || IS_USED(MODULE_SX1262) || \
IS_USED(MODULE_SX1268) || IS_USED(MODULE_LLCC68))
#define SX126X_SPI 1
#endif
/**
* @brief Variant of the SX126x driver.
*/
@ -53,6 +76,7 @@ typedef enum {
SX126X_TYPE_SX1262,
SX126X_TYPE_SX1268,
SX126X_TYPE_LLCC68,
SX126X_TYPE_STM32WL,
} sx126x_type_t;
/**
@ -66,19 +90,26 @@ typedef struct {
gpio_t dio1_pin; /**< Dio1 pin */
sx126x_reg_mod_t regulator; /**< Power regulator mode */
sx126x_type_t type; /**< Variant of sx126x */
#if IS_USED(MODULE_SX126X_RF_SWITCH)
/**
* @ brief Interface to set RF switch parameters
*/
void(*set_rf_mode)(sx126x_t *dev, sx126x_rf_mode_t rf_mode);
#endif
} sx126x_params_t;
/**
* @brief Device descriptor for the driver
*/
typedef struct {
struct sx126x {
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) */
uint8_t rx_timeout; /**< Rx Timeout in terms of symbols */
} sx126x_t;
bool radio_sleep; /**< Radio sleep status */
};
/**
* @brief Setup the radio device

View File

@ -39,6 +39,11 @@ config MODULE_LLCC68
select MODULE_PERIPH_GPIO
select MODULE_PERIPH_GPIO_IRQ
config MODULE_SX126X_STM32WL
bool "SX126X-STM32WL"
select MODULE_SX126X
select MODULE_PERIPH_GPIO
endif
endmenu

View File

@ -103,6 +103,25 @@ static inline bool sx126x_is_sx1268(sx126x_t *dev)
}
}
/**
* @brief Check whether the device model is stm32wl (onboard radio)
*
* @param[in] dev Device descriptor of the driver
*
* @retval true if the device is stm32wl
* @retval false otherwise
*/
static inline bool sx126x_is_stm32wl(sx126x_t *dev)
{
assert(dev);
if (SX126X_SINGLE) {
return IS_USED(MODULE_SX126X_STM32WL);
}
else {
return dev->params->type == SX126X_TYPE_STM32WL;
}
}
#ifdef __cplusplus
}
#endif

View File

@ -57,6 +57,10 @@ extern "C" {
#define SX126X_PARAM_REGULATOR SX126X_REG_MODE_DCDC
#endif
#ifndef SX126X_PARAM_SET_RF_MODE_CB
#define SX126X_PARAM_SET_RF_MODE_CB NULL
#endif
#ifndef SX126X_PARAM_TYPE
# if IS_USED(MODULE_SX1261)
# define SX126X_PARAM_TYPE SX126X_TYPE_SX1261
@ -66,18 +70,28 @@ extern "C" {
# define SX126X_PARAM_TYPE SX126X_TYPE_SX1268
# elif IS_USED(MODULE_LLCC68)
# define SX126X_PARAM_TYPE SX126X_TYPE_LLCC68
# elif IS_USED(MODULE_SX126X_STM32WL)
# define SX126X_PARAM_TYPE SX126X_TYPE_STM32WL
# else
# error "You should select at least one of the SX126x variants."
# endif
#endif
#if IS_USED(MODULE_SX126X_RF_SWITCH)
#define SX126X_SET_RF_MODE .set_rf_mode = SX126X_PARAM_SET_RF_MODE_CB
#else
#define SX126X_SET_RF_MODE
#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, \
.type = SX126X_PARAM_TYPE, \
.regulator = SX126X_PARAM_REGULATOR }
.regulator = SX126X_PARAM_REGULATOR, \
SX126X_SET_RF_MODE }
/**@}*/
/**

View File

@ -87,7 +87,9 @@ static const uint16_t _bw_khz[3] = {
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];
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;
}
@ -111,7 +113,7 @@ static void sx126x_init_default_config(sx126x_t *dev)
else if (sx126x_is_sx1268(dev)) {
sx126x_set_pa_cfg(dev, &sx1268_pa_cfg);
}
else { /* sx126x_is_sx1261(dev) */
else { /* sx126x_is_sx1261(dev) or sx126x_is_stm32wl(dev) */
sx126x_set_pa_cfg(dev, &sx1261_pa_cfg);
}
sx126x_set_tx_params(dev, CONFIG_SX126X_TX_POWER_DEFAULT, CONFIG_SX126X_RAMP_TIME_DEFAULT);
@ -134,10 +136,12 @@ static void sx126x_init_default_config(sx126x_t *dev)
sx126x_set_lora_pkt_params(dev, &dev->pkt_params);
}
#if IS_ACTIVE(SX126X_SPI)
static void _dio1_isr(void *arg)
{
netdev_trigger_event_isr(arg);
}
#endif
int sx126x_init(sx126x_t *dev)
{
@ -152,6 +156,7 @@ int sx126x_init(sx126x_t *dev)
DEBUG("[sx126x] init: SPI_%i initialized with success\n", dev->params->spi);
#if IS_ACTIVE(SX126X_SPI)
gpio_init(dev->params->reset_pin, GPIO_OUT);
gpio_init(dev->params->busy_pin, GPIO_IN_PD);
@ -167,6 +172,7 @@ int sx126x_init(sx126x_t *dev)
DEBUG("[sx126x] error: no DIO1 pin defined\n");
return -EIO;
}
#endif
/* Reset the device */
sx126x_reset(dev);
@ -182,6 +188,7 @@ int sx126x_init(sx126x_t *dev)
SX126X_IRQ_TX_DONE |
SX126X_IRQ_RX_DONE |
SX126X_IRQ_PREAMBLE_DETECTED |
SX126X_IRQ_SYNC_WORD_VALID |
SX126X_IRQ_HEADER_VALID |
SX126X_IRQ_HEADER_ERROR |
SX126X_IRQ_CRC_ERROR |
@ -211,22 +218,26 @@ int sx126x_init(sx126x_t *dev)
uint32_t sx126x_get_channel(const sx126x_t *dev)
{
DEBUG("[sx126x]: sx126x_get_radio_status \n");
return dev->channel;
}
void sx126x_set_channel(sx126x_t *dev, uint32_t freq)
{
DEBUG("[sx126x]: sx126x_set_channel %" PRIu32 "Hz \n", freq);
dev->channel = freq;
sx126x_set_rf_freq(dev, dev->channel);
}
uint8_t sx126x_get_bandwidth(const sx126x_t *dev)
{
DEBUG("[sx126x]: sx126x_get_bandwidth \n");
return dev->mod_params.bw - SX126X_LORA_BW_125;
}
void sx126x_set_bandwidth(sx126x_t *dev, uint8_t bandwidth)
{
DEBUG("[sx126x]: sx126x_set_bandwidth %02x\n", 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);
@ -234,11 +245,13 @@ void sx126x_set_bandwidth(sx126x_t *dev, uint8_t bandwidth)
uint8_t sx126x_get_spreading_factor(const sx126x_t *dev)
{
DEBUG("[sx126x]: sx126x_get_spreading_factor \n");
return dev->mod_params.sf;
}
void sx126x_set_spreading_factor(sx126x_t *dev, uint8_t sf)
{
DEBUG("[sx126x]: sx126x_set_spreading_factor : %02x\n", 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);
@ -246,17 +259,20 @@ void sx126x_set_spreading_factor(sx126x_t *dev, uint8_t sf)
uint8_t sx126x_get_coding_rate(const sx126x_t *dev)
{
DEBUG("[sx126x]: sx126x_get_coding_rate \n");
return dev->mod_params.cr;
}
void sx126x_set_coding_rate(sx126x_t *dev, uint8_t cr)
{
DEBUG("[sx126x]: sx126x_set_coding_rate %01x\n", 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)
{
DEBUG("[sx126x]: sx126x_get_lora_payload_length \n");
sx126x_rx_buffer_status_t rx_buffer_status;
sx126x_get_rx_buffer_status(dev, &rx_buffer_status);
@ -265,50 +281,59 @@ uint8_t sx126x_get_lora_payload_length(const sx126x_t *dev)
void sx126x_set_lora_payload_length(sx126x_t *dev, uint8_t len)
{
DEBUG("[sx126x]: sx126x_set_lora_payload_length %d\n", 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)
{
DEBUG("[sx126x]: sx126x_get_lora_crc \n");
return dev->pkt_params.crc_is_on;
}
void sx126x_set_lora_crc(sx126x_t *dev, bool crc)
{
DEBUG("[sx126x]: sx126x_set_lora_crc %d\n", 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)
{
DEBUG("[sx126x]: sx126x_get_lora_implicit_header \n");
return dev->pkt_params.header_type == SX126X_LORA_PKT_IMPLICIT;
}
void sx126x_set_lora_implicit_header(sx126x_t *dev, bool mode)
{
DEBUG("[sx126x]: sx126x_set_lora_implicit_header %d\n", 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)
{
DEBUG("[sx126x]: sx126x_get_lora_preamble_length \n");
return dev->pkt_params.preamble_len_in_symb;
}
void sx126x_set_lora_preamble_length(sx126x_t *dev, uint16_t preamble)
{
DEBUG("[sx126x]: sx126x_set_lora_preamble_length %" PRIu16 "\n", 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)
{
DEBUG("[sx126x]: sx126x_get_lora_iq_invert \n");
return dev->pkt_params.invert_iq_is_on;
}
void sx126x_set_lora_iq_invert(sx126x_t *dev, bool iq_invert)
{
DEBUG("[sx126x]: sx126x_set_lora_iq_invert %d\n", iq_invert);
dev->pkt_params.invert_iq_is_on = iq_invert;
sx126x_set_lora_pkt_params(dev, &dev->pkt_params);
}

View File

@ -37,6 +37,19 @@
const uint8_t llcc68_max_sf = LORA_SF11;
const uint8_t sx126x_max_sf = LORA_SF12;
#if IS_USED(MODULE_SX126X_STM32WL)
static netdev_t *_dev;
void isr_subghz_radio(void)
{
/* Disable NVIC to avoid ISR conflict in CPU. */
NVIC_DisableIRQ(SUBGHZ_Radio_IRQn);
NVIC_ClearPendingIRQ(SUBGHZ_Radio_IRQn);
netdev_trigger_event_isr(_dev);
cortexm_isr_end();
}
#endif
static int _send(netdev_t *netdev, const iolist_t *iolist)
{
sx126x_t *dev = container_of(netdev, sx126x_t, netdev);
@ -50,6 +63,7 @@ static int _send(netdev_t *netdev, const iolist_t *iolist)
}
size_t pos = 0;
/* Write payload buffer */
for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) {
if (iol->iol_len > 0) {
@ -113,6 +127,12 @@ static int _init(netdev_t *netdev)
{
sx126x_t *dev = container_of(netdev, sx126x_t, netdev);
if (sx126x_is_stm32wl(dev)) {
#if IS_USED(MODULE_SX126X_STM32WL)
_dev = netdev;
#endif
}
/* Launch initialization of driver and device */
DEBUG("[sx126x] netdev: initializing driver...\n");
if (sx126x_init(dev) != 0) {
@ -132,6 +152,12 @@ static void _isr(netdev_t *netdev)
sx126x_get_and_clear_irq_status(dev, &irq_mask);
if (sx126x_is_stm32wl(dev)) {
#if IS_USED(MODULE_SX126X_STM32WL)
NVIC_EnableIRQ(SUBGHZ_Radio_IRQn);
#endif
}
if (irq_mask & SX126X_IRQ_TX_DONE) {
DEBUG("[sx126x] netdev: SX126X_IRQ_TX_DONE\n");
netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE);
@ -287,6 +313,12 @@ static int _set_state(sx126x_t *dev, netopt_state_t state)
case NETOPT_STATE_IDLE:
case NETOPT_STATE_RX:
DEBUG("[sx126x] netdev: set NETOPT_STATE_RX state\n");
#if IS_USED(MODULE_SX126X_RF_SWITCH)
/* Refer Section 4.2 RF Switch in Application Note (AN5406) */
if (dev->params->set_rf_mode) {
dev->params->set_rf_mode(dev, SX126X_RF_MODE_RX);
}
#endif
sx126x_cfg_rx_boosted(dev, true);
int _timeout = (sx126x_symbol_to_msec(dev, dev->rx_timeout));
if (_timeout != 0) {
@ -299,6 +331,11 @@ static int _set_state(sx126x_t *dev, netopt_state_t state)
case NETOPT_STATE_TX:
DEBUG("[sx126x] netdev: set NETOPT_STATE_TX state\n");
#if IS_USED(MODULE_SX126X_RF_SWITCH)
if (dev->params->set_rf_mode) {
dev->params->set_rf_mode(dev, SX126X_RF_MODE_TX_LPA);
}
#endif
sx126x_set_tx(dev, 0);
break;

View File

@ -246,6 +246,10 @@ PSEUDOMODULES += sx1261
PSEUDOMODULES += sx1262
PSEUDOMODULES += sx1268
PSEUDOMODULES += llcc68
PSEUDOMODULES += sx126x_stm32wl
# include RF switch implemented in the board for use with sx126x
PSEUDOMODULES += sx126x_rf_switch
# include variants of SX127X drivers as pseudo modules
PSEUDOMODULES += sx1272

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2021 Inria
* 2021 Freie Universität Berlin
*
* 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
@ -14,6 +15,7 @@
* @brief HAL implementation for the SX1261/2 LoRa radio driver
*
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
* @author Akshai M <akshai.m@fu-berlin.de>
*
* @}
*/
@ -25,6 +27,7 @@
#include "sx126x.h"
#include "sx126x_hal.h"
#include "sx126x_internal.h"
#define ENABLE_DEBUG 0
#include "debug.h"
@ -32,6 +35,28 @@
#define SX126X_SPI_SPEED (SPI_CLK_1MHZ)
#define SX126X_SPI_MODE (SPI_MODE_0)
#ifndef SX126X_PERIPH_WAKEUP_TIME
#define SX126X_PERIPH_WAKEUP_TIME 10000U
#endif
#ifndef SX126X_SPI_WAKEUP_TIME
#define SX126X_SPI_WAKEUP_TIME 500U
#endif
#if IS_USED(MODULE_SX126X_STM32WL)
static uint8_t sx126x_radio_wait_until_ready(sx126x_t *dev)
{
if (dev->radio_sleep == true) {
DEBUG("[sx126x_radio] : Wakeup radio \n");
sx126x_hal_wakeup(dev);
}
/* Wait until Busy/ BusyMS signal goes low */
while (((PWR->SR2 & PWR_SR2_RFBUSYMS) && ((PWR->SR2 & PWR_SR2_RFBUSYS))) == 1) {}
return 0;
}
#endif
sx126x_hal_status_t sx126x_hal_write(const void *context,
const uint8_t *command, const uint16_t command_length,
const uint8_t *data, const uint16_t data_length)
@ -40,15 +65,45 @@ sx126x_hal_status_t sx126x_hal_write(const void *context,
(void)data_length;
sx126x_t *dev = (sx126x_t *)context;
/* wait for the device to not be busy anymore */
while (gpio_read(dev->params->busy_pin)) {}
if (sx126x_is_stm32wl(dev)) {
#if IS_USED(MODULE_SX126X_STM32WL)
sx126x_radio_wait_until_ready(dev);
spi_acquire(dev->params->spi, SPI_CS_UNDEF, SX126X_SPI_MODE, SX126X_SPI_SPEED);
spi_acquire(dev->params->spi, SPI_CS_UNDEF, SX126X_SPI_MODE, SX126X_SPI_SPEED);
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);
/* Check if radio is set to sleep or `RxDutyCycle` mode */
if (command[0] == 0x84 || command[0] == 0x94) {
dev->radio_sleep = true;
}
else {
dev->radio_sleep = false;
}
/* Pull NSS low */
PWR->SUBGHZSPICR &= ~PWR_SUBGHZSPICR_NSS;
spi_transfer_bytes(dev->params->spi, SPI_CS_UNDEF, data_length != 0, command,
NULL, command_length);
if (data_length) {
spi_transfer_bytes(dev->params->spi, SPI_CS_UNDEF, false, data, NULL,
data_length);
}
/* Pull NSS high */
PWR->SUBGHZSPICR |= PWR_SUBGHZSPICR_NSS;
#endif
}
else {
/* wait for the device to not be busy anymore */
while (gpio_read(dev->params->busy_pin)) {}
spi_acquire(dev->params->spi, SPI_CS_UNDEF, SX126X_SPI_MODE, SX126X_SPI_SPEED);
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);
return 0;
}
@ -59,26 +114,77 @@ sx126x_hal_status_t sx126x_hal_read(const void *context,
{
sx126x_t *dev = (sx126x_t *)context;
/* wait for the device to not be busy anymore */
while (gpio_read(dev->params->busy_pin)) {}
if (sx126x_is_stm32wl(dev)) {
#if IS_USED(MODULE_SX126X_STM32WL)
sx126x_radio_wait_until_ready(dev);
spi_acquire(dev->params->spi, SPI_CS_UNDEF, SX126X_SPI_MODE, SX126X_SPI_SPEED);
/* Pull NSS low */
PWR->SUBGHZSPICR &= ~PWR_SUBGHZSPICR_NSS;
spi_transfer_bytes(dev->params->spi, SPI_CS_UNDEF, true, command, NULL, \
command_length);
spi_transfer_bytes(dev->params->spi, SPI_CS_UNDEF, false, NULL, data, \
data_length);
/* Pull NSS high */
PWR->SUBGHZSPICR |= PWR_SUBGHZSPICR_NSS;
#endif
}
else {
/* wait for the device to not be busy anymore */
while (gpio_read(dev->params->busy_pin)) {}
spi_acquire(dev->params->spi, SPI_CS_UNDEF, SX126X_SPI_MODE, SX126X_SPI_SPEED);
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_acquire(dev->params->spi, SPI_CS_UNDEF, SX126X_SPI_MODE, SX126X_SPI_SPEED);
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 0;
}
sx126x_hal_status_t sx126x_hal_reset(const void *context)
{
DEBUG("[sx126x_hal] reset\n");
sx126x_t *dev = (sx126x_t *)context;
gpio_set(dev->params->reset_pin);
gpio_clear(dev->params->reset_pin);
/* it takes 100us for the radio to be ready after reset */
ztimer_sleep(ZTIMER_USEC, 100);
gpio_set(dev->params->reset_pin);
if (sx126x_is_stm32wl(dev)) {
#if IS_USED(MODULE_SX126X_STM32WL)
/* Reset Radio */
RCC->CSR |= RCC_CSR_RFRST;
/* Clear radio reset */
RCC->CSR &= ~RCC_CSR_RFRST;
ztimer_sleep(ZTIMER_USEC, 100);
/* Wait while reset is done */
while ((RCC->CSR & RCC_CSR_RFRSTF) != 0UL) {}
/* Asserts the reset signal of the Radio peripheral */
PWR->SUBGHZSPICR |= PWR_SUBGHZSPICR_NSS;
/* Enable EXTI 44 : Radio IRQ ITs for CPU1 */
EXTI->IMR2 |= EXTI_IMR2_IM44;
/* Set NVIC Priority and enable NVIC */
NVIC_SetPriority(SUBGHZ_Radio_IRQn, 0);
NVIC_EnableIRQ(SUBGHZ_Radio_IRQn);
/* Enable wakeup signal of the SUBGHZ Radio */
PWR->CR3 |= PWR_CR3_EWRFBUSY;
/* Clear Pending Flag */
PWR->SCR = PWR_SCR_CWRFBUSYF;
dev->radio_sleep = true;
#endif
}
else {
gpio_set(dev->params->reset_pin);
gpio_clear(dev->params->reset_pin);
/* it takes 100us for the radio to be ready after reset */
ztimer_sleep(ZTIMER_USEC, 100);
gpio_set(dev->params->reset_pin);
}
return 0;
}
@ -87,12 +193,23 @@ sx126x_hal_status_t sx126x_hal_wakeup(const void *context)
DEBUG("[sx126x_hal] wakeup\n");
sx126x_t *dev = (sx126x_t *)context;
spi_acquire(dev->params->spi, SPI_CS_UNDEF, SX126X_SPI_MODE, SX126X_SPI_SPEED);
gpio_clear(dev->params->nss_pin);
gpio_set(dev->params->nss_pin);
spi_release(dev->params->spi);
if (sx126x_is_stm32wl(dev)) {
#if IS_USED(MODULE_SX126X_STM32WL)
/* Pull NSS low */
PWR->SUBGHZSPICR &= ~PWR_SUBGHZSPICR_NSS;
ztimer_sleep(ZTIMER_USEC, 1000);
/* Pull NSS high */
PWR->SUBGHZSPICR |= PWR_SUBGHZSPICR_NSS;
ztimer_sleep(ZTIMER_USEC, SX126X_PERIPH_WAKEUP_TIME);
#endif
}
else {
spi_acquire(dev->params->spi, SPI_CS_UNDEF, SX126X_SPI_MODE, SX126X_SPI_SPEED);
gpio_clear(dev->params->nss_pin);
gpio_set(dev->params->nss_pin);
spi_release(dev->params->spi);
ztimer_sleep(ZTIMER_USEC, SX126X_SPI_WAKEUP_TIME);
}
/* it takes 500us for the radio device to be ready after waking up */
ztimer_sleep(ZTIMER_USEC, 500);
return 0;
}