diff --git a/boards/lora-e5-dev/Kconfig b/boards/lora-e5-dev/Kconfig index 6bc1fbc2d2..f462d14cbd 100644 --- a/boards/lora-e5-dev/Kconfig +++ b/boards/lora-e5-dev/Kconfig @@ -14,6 +14,7 @@ config BOARD_LORA_E5_DEV select CPU_MODEL_STM32WLE5JC # Put defined MCU peripherals here (in alphabetical order) + select HAS_PERIPH_ADC select HAS_PERIPH_I2C select HAS_PERIPH_LPUART select HAS_PERIPH_RTT diff --git a/boards/lora-e5-dev/Makefile.features b/boards/lora-e5-dev/Makefile.features index 8137cf9f1b..2ce3144305 100644 --- a/boards/lora-e5-dev/Makefile.features +++ b/boards/lora-e5-dev/Makefile.features @@ -2,6 +2,7 @@ CPU = stm32 CPU_MODEL = stm32wle5jc # Put defined MCU peripherals here (in alphabetical order) +FEATURES_PROVIDED += periph_adc FEATURES_PROVIDED += periph_i2c FEATURES_PROVIDED += periph_lpuart FEATURES_PROVIDED += periph_rtt diff --git a/boards/lora-e5-dev/include/periph_conf.h b/boards/lora-e5-dev/include/periph_conf.h index ef555a4c4d..35fd68360b 100644 --- a/boards/lora-e5-dev/include/periph_conf.h +++ b/boards/lora-e5-dev/include/periph_conf.h @@ -152,6 +152,20 @@ static const i2c_conf_t i2c_config[] = { #define I2C_NUMOF ARRAY_SIZE(i2c_config) /** @} */ + +/** + * @name ADC configuration + * @{ + */ +static const adc_conf_t adc_config[] = { + { GPIO_PIN(PORT_B, 3), 2 }, + { GPIO_PIN(PORT_B, 4), 3 }, +}; + +#define ADC_NUMOF ARRAY_SIZE(adc_config) +/** @} */ + + #ifdef __cplusplus } #endif diff --git a/cpu/stm32/Makefile.dep b/cpu/stm32/Makefile.dep index 4fc2539328..a3f0cefe4b 100644 --- a/cpu/stm32/Makefile.dep +++ b/cpu/stm32/Makefile.dep @@ -41,4 +41,12 @@ ifneq (,$(filter periph_rtc_mem,$(USEMODULE))) FEATURES_REQUIRED += periph_rtc endif +ifneq (,$(filter periph_adc,$(FEATURES_USED))) + ifneq (,$(filter $(CPU_FAM),wl)) + USEMODULE += ztimer + USEMODULE += ztimer_msec + endif +endif + + include $(RIOTCPU)/cortexm_common/Makefile.dep diff --git a/cpu/stm32/include/periph/wl/periph_cpu.h b/cpu/stm32/include/periph/wl/periph_cpu.h index 9b5e66fdd9..9d676cdd76 100644 --- a/cpu/stm32/include/periph/wl/periph_cpu.h +++ b/cpu/stm32/include/periph/wl/periph_cpu.h @@ -26,6 +26,31 @@ extern "C" { #ifndef DOXYGEN +/** + * @brief ADC voltage regulator start-up time [us] + */ +#define ADC_T_ADCVREG_STUP_US (20) + +/** + * @brief Available number of ADC devices + */ +#define ADC_DEVS (1U) + +/** + * @brief Override ADC resolution values + * @{ + */ +#define HAVE_ADC_RES_T +typedef enum { + ADC_RES_6BIT = (ADC_CFGR1_RES), /**< ADC resolution: 6 bit */ + ADC_RES_8BIT = (ADC_CFGR1_RES_1), /**< ADC resolution: 8 bit */ + ADC_RES_10BIT = (ADC_CFGR1_RES_0), /**< ADC resolution: 10 bit */ + ADC_RES_12BIT = (0x0), /**< ADC resolution: 12 bit */ + ADC_RES_14BIT = (0x1), /**< not applicable */ + ADC_RES_16BIT = (0x2) /**< not applicable */ +} adc_res_t; +/** @} */ + /** * @brief Starting address of the ROM bootloader * see application note AN2606 ( Table 143 : System memory) diff --git a/cpu/stm32/include/periph_cpu.h b/cpu/stm32/include/periph_cpu.h index 88b2fcbea1..f84be15b77 100644 --- a/cpu/stm32/include/periph_cpu.h +++ b/cpu/stm32/include/periph_cpu.h @@ -491,7 +491,7 @@ typedef enum { typedef struct { gpio_t pin; /**< pin connected to the channel */ #if !defined(CPU_FAM_STM32F0) && !defined(CPU_FAM_STM32L0) && \ - !defined(CPU_FAM_STM32L1) + !defined(CPU_FAM_STM32L1) && !defined(CPU_FAM_STM32WL) uint8_t dev; /**< ADCx - 1 device used for the channel */ #endif uint8_t chan; /**< CPU ADC channel connected to the pin */ diff --git a/cpu/stm32/periph/Kconfig b/cpu/stm32/periph/Kconfig index 832410fd79..0770a36680 100644 --- a/cpu/stm32/periph/Kconfig +++ b/cpu/stm32/periph/Kconfig @@ -27,3 +27,10 @@ config MODULE_PERIPH_CAN select MODULE_PERIPH_I2C help STM32 CAN peripheral controller. + +config MODULE_PERIPH_ADC + bool "ADC peripheral driver" + depends on HAS_PERIPH_ADC + select MODULE_ZTIMER if HAS_CPU_STM32WL + select MODULE_ZTIMER_MSEC if HAS_CPU_STM32WL + select MODULE_PERIPH_COMMON diff --git a/cpu/stm32/periph/adc_wl.c b/cpu/stm32/periph/adc_wl.c new file mode 100644 index 0000000000..f3ccb2b7b4 --- /dev/null +++ b/cpu/stm32/periph/adc_wl.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2014-2016 Freie Universität Berlin + * Copyright (C) 2018 HAW-Hamburg + * Copyright (C) 2021 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup cpu_stm32 + * @ingroup drivers_periph_adc + * @{ + * + * @file + * @brief Low-level ADC driver implementation + * + * @author Hauke Petersen + * @author Michel Rottleuthner + * @author Francisco Molina + * + * @} + */ + +#include "cpu.h" +#include "mutex.h" +#include "periph/adc.h" +#include "periph_conf.h" +#include "ztimer.h" + +/** + * @brief Allocate lock for the ADC device + * + * All STM32WL CPUs we support so far only come with a single ADC device. + */ +static mutex_t lock = MUTEX_INIT; + +static inline void prep(void) +{ + mutex_lock(&lock); + periph_clk_en(APB2, RCC_APB2ENR_ADCEN); +} + +static inline void done(void) +{ + periph_clk_dis(APB2, RCC_APB2ENR_ADCEN); + mutex_unlock(&lock); +} + +int adc_init(adc_t line) +{ + /* check if the line is valid */ + if (line >= ADC_NUMOF) { + return -1; + } + + /* lock device and enable its peripheral clock */ + prep(); + + /* configure the pin */ + gpio_init_analog(adc_config[line].pin); + + /* init ADC line only if it wasn't already initialized */ + if (!(ADC->CR & (ADC_CR_ADEN))) { + + /* set prescaler to 0 to let the ADC run with maximum speed */ + ADC_COMMON->CCR &= ~(ADC_CCR_PRESC); + + /* set ADC clock to PCLK/2 otherwise */ + ADC->CFGR2 &= ~(ADC_CFGR2_CKMODE_0 | ADC_CFGR2_CKMODE_1); + ADC->CFGR2 |= ADC_CFGR2_CKMODE_0; + + /* enable ADC internal voltage regulator and wait for startup period */ + ADC->CR |= (ADC_CR_ADVREGEN); +#if IS_USED(MODULE_ZTIMER_USEC) + ztimer_sleep(ZTIMER_USEC, ADC_T_ADCVREG_STUP_US); +#else + /* to avoid using ZTIMER_USEC unless already included round up the + internal voltage regulator start up to 1ms */ + ztimer_sleep(ZTIMER_MSEC, 1); +#endif + + /* ´start automatic calibration and wait for it to complete */ + ADC->CR |= ADC_CR_ADCAL; + while (ADC->CR & ADC_CR_ADCAL) {} + + /* clear ADRDY by writing it*/ + ADC->ISR |= (ADC_ISR_ADRDY); + + /* enable ADC and wait for it to be ready */ + ADC->CR |= (ADC_CR_ADEN); + while ((ADC->ISR & ADC_ISR_ADRDY) == 0) {} + + /* set sequence length to 1 conversion */ + ADC->CFGR1 &= ~ADC_CFGR1_CONT; + + /* Sampling time of 3.5 ADC clocks for all channels*/ + ADC->SMPR = 0x0101; + } + + /* free the device again */ + done(); + return 0; +} + +int32_t adc_sample(adc_t line, adc_res_t res) +{ + int sample; + + /* check if resolution is applicable */ + if (res & 0x3) { + return -1; + } + + /* lock and power on the ADC device */ + prep(); + + /* first clear resolution */ + ADC->CFGR1 &= ~ADC_CFGR1_RES; + + /* then set resolution to the required value*/ + ADC->CFGR1 |= res; + + /* specify channel for regular conversion */ + ADC->CHSELR = (1 << adc_config[line].chan); + + /* start conversion and wait for it to complete */ + ADC->CR |= ADC_CR_ADSTART; + while (!(ADC->ISR & ADC_ISR_EOC)) {} + + /* read the sample */ + sample = (int)ADC->DR; + + /* free the device again */ + done(); + + return sample; +} diff --git a/dist/tools/doccheck/exclude_patterns b/dist/tools/doccheck/exclude_patterns index ad255e0edc..2311fb74ba 100644 --- a/dist/tools/doccheck/exclude_patterns +++ b/dist/tools/doccheck/exclude_patterns @@ -14567,3 +14567,5 @@ boards/stm32g0316\-disco/include/periph_conf\.h:[0-9]+: warning: Member uart_con boards/lora\-e5\-dev/include/board\.h:[0-9]+: warning: Member SX126X_PARAM_TX_PA_MODE \(macro definition\) of file board\.h is not documented\. drivers/sx126x/include/sx126x_params\.h:[0-9]+: warning: Member SX126X_PARAM_TX_PA_MODE \(macro definition\) of file sx126x_params\.h is not documented\. drivers/sx126x/include/sx126x_params\.h:[0-9]+: warning: Member SX126X_TX_PA_MODE \(macro definition\) of file sx126x_params\.h is not documented\. +boards/lora\-e5\-dev/include/periph_conf\.h:[0-9]+: warning: Member ADC_NUMOF \(macro definition\) of file periph_conf\.h is not documented\. +boards/lora\-e5\-dev/include/periph_conf\.h:[0-9]+: warning: Member adc_config\[\] \(variable\) of file periph_conf\.h is not documented\.