diff --git a/cpu/stm32/Makefile.dep b/cpu/stm32/Makefile.dep index 431f89a060..505b1289a2 100644 --- a/cpu/stm32/Makefile.dep +++ b/cpu/stm32/Makefile.dep @@ -49,5 +49,8 @@ ifneq (,$(filter periph_adc,$(FEATURES_USED))) endif endif +ifneq (,$(filter periph_vbat,$(USEMODULE))) + FEATURES_REQUIRED += periph_adc +endif include $(RIOTCPU)/cortexm_common/Makefile.dep diff --git a/cpu/stm32/Makefile.features b/cpu/stm32/Makefile.features index ddaf3312aa..3ebfa16edb 100644 --- a/cpu/stm32/Makefile.features +++ b/cpu/stm32/Makefile.features @@ -34,6 +34,25 @@ ifneq (,$(filter $(STM32_WITH_BKPRAM),$(CPU_MODEL))) FEATURES_PROVIDED += backup_ram endif +STM32_WITH_VBAT = stm32f031% stm32f038% stm32f042% stm32f048% \ + stm32f051% stm32f058% stm32f071% stm32f072% \ + stm32f078% stm32f091% stm32f098% \ + stm32f2% \ + stm32f3% \ + stm32f4% \ + stm32f7% \ + stm32g0% \ + stm32g4% stm32gbk1cb \ + stm32l4% \ + stm32l5% \ + stm32u5% \ + stm32wb% \ + stm32wl% + +ifneq (,$(filter $(STM32_WITH_VBAT),$(CPU_MODEL))) + FEATURES_PROVIDED += periph_vbat +endif + # The f2, f4 and f7 do not support the pagewise api ifneq (,$(filter $(CPU_FAM),f2 f4 f7)) FEATURES_PROVIDED += periph_flashpage diff --git a/cpu/stm32/include/periph/cpu_vbat.h b/cpu/stm32/include/periph/cpu_vbat.h new file mode 100644 index 0000000000..082d6a14b4 --- /dev/null +++ b/cpu/stm32/include/periph/cpu_vbat.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 Otto-von-Guericke-Universität Magdeburg + * + * 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 + * @{ + * + * @file + * @brief CPU internal VBAT interface and definitions of the STM32 family + * + * @author Fabian Hüßler + */ + +#ifndef PERIPH_CPU_VBAT_H +#define PERIPH_CPU_VBAT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Enable the VBAT sampling channel + */ +void vbat_enable(void); + +/** + * @brief Disable the VBAT sampling channel + */ +void vbat_disable(void); + +#ifdef __cplusplus +} +#endif + +#endif /* PERIPH_CPU_VBAT_H */ +/** @} */ diff --git a/cpu/stm32/include/periph/f0/periph_cpu.h b/cpu/stm32/include/periph/f0/periph_cpu.h index 5b0e21a9e7..f8202c1798 100644 --- a/cpu/stm32/include/periph/f0/periph_cpu.h +++ b/cpu/stm32/include/periph/f0/periph_cpu.h @@ -61,6 +61,15 @@ typedef enum { ADC_RES_16BIT = (0xff) /**< not applicable */ } adc_res_t; /** @} */ + +/** + * @name Constants for internal VBAT ADC line + * @{ + */ +#define VBAT_ADC_RES ADC_RES_12BIT +#define VBAT_ADC_MAX 4095 +/** @} */ + #endif /* ndef DOXYGEN */ #ifdef __cplusplus diff --git a/cpu/stm32/include/periph/f2/periph_cpu.h b/cpu/stm32/include/periph/f2/periph_cpu.h index 5e82d3d254..598dfbc3b5 100644 --- a/cpu/stm32/include/periph/f2/periph_cpu.h +++ b/cpu/stm32/include/periph/f2/periph_cpu.h @@ -58,6 +58,15 @@ typedef enum { ADC_RES_16BIT = 2 /**< ADC resolution: 16 bit (not supported)*/ } adc_res_t; /** @} */ + +/** + * @name Constants for internal VBAT ADC line + * @{ + */ +#define VBAT_ADC_RES ADC_RES_12BIT +#define VBAT_ADC_MAX 4095 +/** @} */ + #endif /* ndef DOXYGEN */ #ifdef __cplusplus diff --git a/cpu/stm32/include/periph/f3/periph_cpu.h b/cpu/stm32/include/periph/f3/periph_cpu.h index 69df093792..01ccf69573 100644 --- a/cpu/stm32/include/periph/f3/periph_cpu.h +++ b/cpu/stm32/include/periph/f3/periph_cpu.h @@ -69,6 +69,15 @@ typedef enum { ADC_RES_16BIT = (0x2) /**< not applicable */ } adc_res_t; /** @} */ + +/** + * @name Constants for internal VBAT ADC line + * @{ + */ +#define VBAT_ADC_RES ADC_RES_12BIT +#define VBAT_ADC_MAX 4095 +/** @} */ + #endif /* ndef DOXYGEN */ #ifdef __cplusplus diff --git a/cpu/stm32/include/periph/f4/periph_cpu.h b/cpu/stm32/include/periph/f4/periph_cpu.h index 9318082c13..adba5e27be 100644 --- a/cpu/stm32/include/periph/f4/periph_cpu.h +++ b/cpu/stm32/include/periph/f4/periph_cpu.h @@ -69,6 +69,15 @@ typedef enum { ADC_RES_16BIT = 2 /**< ADC resolution: 16 bit (not supported)*/ } adc_res_t; /** @} */ + +/** + * @name Constants for internal VBAT ADC line + * @{ + */ +#define VBAT_ADC_RES ADC_RES_12BIT +#define VBAT_ADC_MAX 4095 +/** @} */ + #endif /* ndef DOXYGEN */ #ifdef __cplusplus diff --git a/cpu/stm32/include/periph/f7/periph_cpu.h b/cpu/stm32/include/periph/f7/periph_cpu.h index a7c1d5ddd2..742c3bceb7 100644 --- a/cpu/stm32/include/periph/f7/periph_cpu.h +++ b/cpu/stm32/include/periph/f7/periph_cpu.h @@ -52,6 +52,15 @@ typedef enum { ADC_RES_16BIT = 2 /**< ADC resolution: 16 bit (not supported)*/ } adc_res_t; /** @} */ + +/** + * @name Constants for internal VBAT ADC line + * @{ + */ +#define VBAT_ADC_RES ADC_RES_12BIT +#define VBAT_ADC_MAX 4095 +/** @} */ + #endif /* ndef DOXYGEN */ #ifdef __cplusplus diff --git a/cpu/stm32/include/periph/g0/periph_cpu.h b/cpu/stm32/include/periph/g0/periph_cpu.h index 7425c30d61..c325e36f90 100644 --- a/cpu/stm32/include/periph/g0/periph_cpu.h +++ b/cpu/stm32/include/periph/g0/periph_cpu.h @@ -32,6 +32,14 @@ extern "C" { */ #define STM32_BOOTLOADER_ADDR (0x1FFF0000) +/** + * @name Constants for internal VBAT ADC line + * @{ + */ +#define VBAT_ADC_RES ADC_RES_12BIT +#define VBAT_ADC_MAX 4095 +/** @} */ + #endif /* ndef DOXYGEN */ /** diff --git a/cpu/stm32/include/periph/g4/periph_cpu.h b/cpu/stm32/include/periph/g4/periph_cpu.h index bc694f76b2..ad86b3ae7f 100644 --- a/cpu/stm32/include/periph/g4/periph_cpu.h +++ b/cpu/stm32/include/periph/g4/periph_cpu.h @@ -32,6 +32,14 @@ extern "C" { */ #define STM32_BOOTLOADER_ADDR (0x1FFF0000) +/** + * @name Constants for internal VBAT ADC line + * @{ + */ +#define VBAT_ADC_RES ADC_RES_12BIT +#define VBAT_ADC_MAX 4095 +/** @} */ + #endif /* ndef DOXYGEN */ #ifdef __cplusplus diff --git a/cpu/stm32/include/periph/l4/periph_cpu.h b/cpu/stm32/include/periph/l4/periph_cpu.h index 78075b11b0..daa0d78f8e 100644 --- a/cpu/stm32/include/periph/l4/periph_cpu.h +++ b/cpu/stm32/include/periph/l4/periph_cpu.h @@ -63,6 +63,15 @@ typedef enum { ADC_RES_16BIT = (0x2) /**< not applicable */ } adc_res_t; /** @} */ + +/** + * @name Constants for internal VBAT ADC line + * @{ + */ +#define VBAT_ADC_RES ADC_RES_12BIT +#define VBAT_ADC_MAX 4095 +/** @} */ + #endif /* ndef DOXYGEN */ #ifdef __cplusplus diff --git a/cpu/stm32/include/periph/l5/periph_cpu.h b/cpu/stm32/include/periph/l5/periph_cpu.h index 0c3a214a1a..3d47d6a667 100644 --- a/cpu/stm32/include/periph/l5/periph_cpu.h +++ b/cpu/stm32/include/periph/l5/periph_cpu.h @@ -32,6 +32,14 @@ extern "C" { */ #define STM32_BOOTLOADER_ADDR (0x0BF90000) +/** + * @name Constants for internal VBAT ADC line + * @{ + */ +#define VBAT_ADC_RES ADC_RES_12BIT +#define VBAT_ADC_MAX 4095 +/** @} */ + #endif /* ndef DOXYGEN */ #ifdef __cplusplus diff --git a/cpu/stm32/include/periph/u5/periph_cpu.h b/cpu/stm32/include/periph/u5/periph_cpu.h index a5327d4c6e..bdc4c279b6 100644 --- a/cpu/stm32/include/periph/u5/periph_cpu.h +++ b/cpu/stm32/include/periph/u5/periph_cpu.h @@ -32,6 +32,14 @@ extern "C" { */ #define STM32_BOOTLOADER_ADDR (0x0BF90000) +/** + * @name Constants for internal VBAT ADC line + * @{ + */ +#define VBAT_ADC_RES ADC_RES_14BIT +#define VBAT_ADC_MAX 16383 +/** @} */ + #endif /* ndef DOXYGEN */ #ifdef __cplusplus diff --git a/cpu/stm32/include/periph/wb/periph_cpu.h b/cpu/stm32/include/periph/wb/periph_cpu.h index 0928df59e3..f03075e936 100644 --- a/cpu/stm32/include/periph/wb/periph_cpu.h +++ b/cpu/stm32/include/periph/wb/periph_cpu.h @@ -32,6 +32,14 @@ extern "C" { */ #define STM32_BOOTLOADER_ADDR (0x1FFF0000) +/** + * @name Constants for internal VBAT ADC line + * @{ + */ +#define VBAT_ADC_RES ADC_RES_12BIT +#define VBAT_ADC_MAX 4095 +/** @} */ + #endif /* ndef DOXYGEN */ #ifdef __cplusplus diff --git a/cpu/stm32/include/periph/wl/periph_cpu.h b/cpu/stm32/include/periph/wl/periph_cpu.h index 9d676cdd76..8edf765fbb 100644 --- a/cpu/stm32/include/periph/wl/periph_cpu.h +++ b/cpu/stm32/include/periph/wl/periph_cpu.h @@ -57,6 +57,14 @@ typedef enum { */ #define STM32_BOOTLOADER_ADDR (0x1FFF0000) +/** + * @name Constants for internal VBAT ADC line + * @{ + */ +#define VBAT_ADC_RES ADC_RES_12BIT +#define VBAT_ADC_MAX 4095 +/** @} */ + #endif /* ndef DOXYGEN */ /** diff --git a/cpu/stm32/include/periph_cpu.h b/cpu/stm32/include/periph_cpu.h index ac5f80c625..e862625bf6 100644 --- a/cpu/stm32/include/periph_cpu.h +++ b/cpu/stm32/include/periph_cpu.h @@ -72,6 +72,7 @@ #include "periph/cpu_timer.h" #include "periph/cpu_uart.h" #include "periph/cpu_usbdev.h" +#include "periph/cpu_vbat.h" #include "periph/cpu_wdt.h" #ifdef MODULE_PERIPH_CAN diff --git a/cpu/stm32/kconfigs/f0/Kconfig.lines b/cpu/stm32/kconfigs/f0/Kconfig.lines index 6a9d49432c..e52ba029c1 100644 --- a/cpu/stm32/kconfigs/f0/Kconfig.lines +++ b/cpu/stm32/kconfigs/f0/Kconfig.lines @@ -27,33 +27,39 @@ config CPU_LINE_STM32F031X6 bool select CPU_FAM_F0 select HAS_PERIPH_RTC_MEM + select HAS_PERIPH_VBAT config CPU_LINE_STM32F038XX bool select CPU_FAM_F0 select HAS_PERIPH_RTC_MEM + select HAS_PERIPH_VBAT config CPU_LINE_STM32F042X6 bool select CPU_FAM_F0 select HAS_PERIPH_RTC_MEM + select HAS_PERIPH_VBAT config CPU_LINE_STM32F048XX bool select CPU_FAM_F0 select HAS_PERIPH_RTC_MEM + select HAS_PERIPH_VBAT config CPU_LINE_STM32F051X8 bool select CPU_FAM_F0 select CLOCK_HAS_NO_MCO_PRE select HAS_PERIPH_RTC_MEM + select HAS_PERIPH_VBAT config CPU_LINE_STM32F058XX bool select CPU_FAM_F0 select CLOCK_HAS_NO_MCO_PRE select HAS_PERIPH_RTC_MEM + select HAS_PERIPH_VBAT config CPU_LINE_STM32F070X6 bool @@ -67,23 +73,28 @@ config CPU_LINE_STM32F071XB bool select CPU_FAM_F0 select HAS_PERIPH_RTC_MEM + select HAS_PERIPH_VBAT config CPU_LINE_STM32F072XB bool select CPU_FAM_F0 select HAS_PERIPH_RTC_MEM + select HAS_PERIPH_VBAT config CPU_LINE_STM32F078XX bool select CPU_FAM_F0 select HAS_PERIPH_RTC_MEM + select HAS_PERIPH_VBAT config CPU_LINE_STM32F091XC bool select CPU_FAM_F0 select HAS_PERIPH_RTC_MEM + select HAS_PERIPH_VBAT config CPU_LINE_STM32F098XX bool select CPU_FAM_F0 select HAS_PERIPH_RTC_MEM + select HAS_PERIPH_VBAT diff --git a/cpu/stm32/kconfigs/f2/Kconfig b/cpu/stm32/kconfigs/f2/Kconfig index d43df8d421..00a08c8fd0 100644 --- a/cpu/stm32/kconfigs/f2/Kconfig +++ b/cpu/stm32/kconfigs/f2/Kconfig @@ -15,6 +15,7 @@ config CPU_FAM_F2 select HAS_PERIPH_FLASHPAGE select HAS_PERIPH_HWRNG select HAS_PERIPH_RTC_MEM + select HAS_PERIPH_VBAT select HAS_PERIPH_WDT select HAS_BOOTLOADER_STM32 diff --git a/cpu/stm32/kconfigs/f3/Kconfig b/cpu/stm32/kconfigs/f3/Kconfig index 93651422b5..2cd5983079 100644 --- a/cpu/stm32/kconfigs/f3/Kconfig +++ b/cpu/stm32/kconfigs/f3/Kconfig @@ -14,6 +14,7 @@ config CPU_FAM_F3 select HAS_PERIPH_FLASHPAGE_PAGEWISE select HAS_PERIPH_FLASHPAGE_RAW select HAS_PERIPH_RTC_MEM + select HAS_PERIPH_VBAT select HAS_PERIPH_WDT select HAS_BOOTLOADER_STM32 diff --git a/cpu/stm32/kconfigs/f4/Kconfig b/cpu/stm32/kconfigs/f4/Kconfig index 0d510fc4be..889bebdb62 100644 --- a/cpu/stm32/kconfigs/f4/Kconfig +++ b/cpu/stm32/kconfigs/f4/Kconfig @@ -13,6 +13,7 @@ config CPU_FAM_F4 select HAS_CORTEXM_MPU select HAS_PERIPH_FLASHPAGE select HAS_PERIPH_RTC_MEM + select HAS_PERIPH_VBAT select HAS_PERIPH_WDT select HAS_BOOTLOADER_STM32 diff --git a/cpu/stm32/kconfigs/f7/Kconfig b/cpu/stm32/kconfigs/f7/Kconfig index 9647a952c8..4e3e0187d0 100644 --- a/cpu/stm32/kconfigs/f7/Kconfig +++ b/cpu/stm32/kconfigs/f7/Kconfig @@ -15,6 +15,7 @@ config CPU_FAM_F7 select HAS_PERIPH_FLASHPAGE select HAS_PERIPH_HWRNG select HAS_PERIPH_RTC_MEM + select HAS_PERIPH_VBAT select HAS_PERIPH_WDT select HAS_BOOTLOADER_STM32 diff --git a/cpu/stm32/kconfigs/g0/Kconfig b/cpu/stm32/kconfigs/g0/Kconfig index b1157ce533..891b429c8b 100644 --- a/cpu/stm32/kconfigs/g0/Kconfig +++ b/cpu/stm32/kconfigs/g0/Kconfig @@ -13,6 +13,7 @@ config CPU_FAM_G0 select HAS_PERIPH_FLASHPAGE select HAS_PERIPH_FLASHPAGE_PAGEWISE select HAS_PERIPH_FLASHPAGE_RAW + select HAS_PERIPH_VBAT select HAS_PERIPH_WDT select HAS_BOOTLOADER_STM32 diff --git a/cpu/stm32/kconfigs/g4/Kconfig b/cpu/stm32/kconfigs/g4/Kconfig index f80a6f9746..89e85d8c94 100644 --- a/cpu/stm32/kconfigs/g4/Kconfig +++ b/cpu/stm32/kconfigs/g4/Kconfig @@ -14,6 +14,7 @@ config CPU_FAM_G4 select HAS_PERIPH_FLASHPAGE select HAS_PERIPH_FLASHPAGE_PAGEWISE select HAS_PERIPH_HWRNG + select HAS_PERIPH_VBAT select HAS_PERIPH_WDT select HAS_BOOTLOADER_STM32 diff --git a/cpu/stm32/kconfigs/l4/Kconfig b/cpu/stm32/kconfigs/l4/Kconfig index 6ccd9df6af..d4fa612f40 100644 --- a/cpu/stm32/kconfigs/l4/Kconfig +++ b/cpu/stm32/kconfigs/l4/Kconfig @@ -15,6 +15,7 @@ config CPU_FAM_L4 select HAS_PERIPH_FLASHPAGE_PAGEWISE select HAS_PERIPH_HWRNG select HAS_PERIPH_RTC_MEM + select HAS_PERIPH_VBAT select HAS_PERIPH_WDT select HAS_BOOTLOADER_STM32 diff --git a/cpu/stm32/kconfigs/l5/Kconfig b/cpu/stm32/kconfigs/l5/Kconfig index 46d5f24857..5a227b62ac 100644 --- a/cpu/stm32/kconfigs/l5/Kconfig +++ b/cpu/stm32/kconfigs/l5/Kconfig @@ -14,6 +14,7 @@ config CPU_FAM_L5 select HAS_PERIPH_FLASHPAGE_PAGEWISE select HAS_PERIPH_HWRNG select HAS_PERIPH_RTC_MEM + select HAS_PERIPH_VBAT select HAS_PERIPH_WDT select HAS_BOOTLOADER_STM32 diff --git a/cpu/stm32/kconfigs/u5/Kconfig b/cpu/stm32/kconfigs/u5/Kconfig index 6129b2ce08..7304394a98 100644 --- a/cpu/stm32/kconfigs/u5/Kconfig +++ b/cpu/stm32/kconfigs/u5/Kconfig @@ -15,6 +15,7 @@ config CPU_FAM_U5 select HAS_PERIPH_FLASHPAGE_PAGEWISE select HAS_PERIPH_HWRNG select HAS_PERIPH_RTC_MEM + select HAS_PERIPH_VBAT select HAS_PERIPH_WDT select HAS_BOOTLOADER_STM32 diff --git a/cpu/stm32/kconfigs/wb/Kconfig b/cpu/stm32/kconfigs/wb/Kconfig index 61f2f7444e..c49cc9b9dc 100644 --- a/cpu/stm32/kconfigs/wb/Kconfig +++ b/cpu/stm32/kconfigs/wb/Kconfig @@ -14,6 +14,7 @@ config CPU_FAM_WB select HAS_PERIPH_FLASHPAGE_PAGEWISE select HAS_PERIPH_HWRNG select HAS_PERIPH_RTC_MEM + select HAS_PERIPH_VBAT select HAS_PERIPH_WDT select HAS_BOOTLOADER_STM32 diff --git a/cpu/stm32/kconfigs/wl/Kconfig b/cpu/stm32/kconfigs/wl/Kconfig index 7df178c438..a887221b6a 100644 --- a/cpu/stm32/kconfigs/wl/Kconfig +++ b/cpu/stm32/kconfigs/wl/Kconfig @@ -14,6 +14,7 @@ config CPU_FAM_WL select HAS_PERIPH_FLASHPAGE select HAS_PERIPH_FLASHPAGE_PAGEWISE select HAS_PERIPH_RTC_MEM + select HAS_PERIPH_VBAT select HAS_PERIPH_WDT select HAS_BOOTLOADER_STM32 diff --git a/cpu/stm32/periph/adc_f0_g0.c b/cpu/stm32/periph/adc_f0_g0.c index 50f68fffd9..2c1933784c 100644 --- a/cpu/stm32/periph/adc_f0_g0.c +++ b/cpu/stm32/periph/adc_f0_g0.c @@ -22,6 +22,14 @@ #include "cpu.h" #include "mutex.h" #include "periph/adc.h" +#include "periph/vbat.h" + +/** + * @brief Default VBAT undefined value + */ +#ifndef VBAT_ADC +#define VBAT_ADC ADC_UNDEF +#endif /** * @brief Allocate lock for the ADC device @@ -62,7 +70,9 @@ int adc_init(adc_t line) /* lock and power on the device */ prep(); /* configure the pin */ - gpio_init_analog(adc_config[line].pin); + if (adc_config[line].pin != GPIO_UNDEF) { + gpio_init_analog(adc_config[line].pin); + } /* reset configuration */ ADC1->CFGR2 = 0; /* enable device */ @@ -86,7 +96,10 @@ int32_t adc_sample(adc_t line, adc_res_t res) /* lock and power on the ADC device */ prep(); - + /* check if this is the VBAT line */ + if (IS_USED(MODULE_PERIPH_VBAT) && line == VBAT_ADC) { + vbat_enable(); + } /* set resolution and channel */ ADC1->CFGR1 = res; ADC1->CHSELR = (1 << adc_config[line].chan); @@ -95,7 +108,10 @@ int32_t adc_sample(adc_t line, adc_res_t res) while (!(ADC1->ISR & ADC_ISR_EOC)) {} /* read result */ sample = (int)ADC1->DR; - + /* check if this is the VBAT line */ + if (IS_USED(MODULE_PERIPH_VBAT) && line == VBAT_ADC) { + vbat_disable(); + } /* unlock and power off device again */ done(); diff --git a/cpu/stm32/periph/adc_f2.c b/cpu/stm32/periph/adc_f2.c index 3136e5ee6e..b6c7acaf96 100644 --- a/cpu/stm32/periph/adc_f2.c +++ b/cpu/stm32/periph/adc_f2.c @@ -24,12 +24,20 @@ #include "mutex.h" #include "periph/adc.h" #include "periph_conf.h" +#include "periph/vbat.h" /** * @brief Maximum allowed ADC clock speed */ #define MAX_ADC_SPEED (12000000U) +/** + * @brief Default VBAT undefined value + */ +#ifndef VBAT_ADC +#define VBAT_ADC ADC_UNDEF +#endif + /** * @brief Allocate locks for all three available ADC devices */ @@ -73,7 +81,9 @@ int adc_init(adc_t line) prep(line); /* configure the pin */ - gpio_init_analog(adc_config[line].pin); + if (adc_config[line].pin != GPIO_UNDEF) { + gpio_init_analog(adc_config[line].pin); + } /* set clock prescaler to get the maximal possible ADC clock value */ for (clk_div = 2; clk_div < 8; clk_div += 2) { if ((CLOCK_CORECLOCK / clk_div) <= MAX_ADC_SPEED) { @@ -85,17 +95,6 @@ int adc_init(adc_t line) /* enable the ADC module */ dev(line)->CR2 = ADC_CR2_ADON; - /* check if this channel is an internal ADC channel, if so - * enable the internal temperature and Vref */ - if (adc_config[line].chan == 16 || adc_config[line].chan == 17) { - /* check if the internal channels are configured to use ADC1 */ - if (dev(line) != ADC1) { - return -3; - } - - ADC->CCR |= ADC_CCR_TSVREFE; - } - /* free the device again */ done(line); return 0; @@ -112,7 +111,15 @@ int32_t adc_sample(adc_t line, adc_res_t res) /* lock and power on the ADC device */ prep(line); - + /* check if this is the VBAT line or another internal ADC channel */ + if (IS_USED(MODULE_PERIPH_VBAT) && line == VBAT_ADC) { + vbat_enable(); + } + else if (dev(line) == ADC1) { + if (adc_config[line].chan == 16 || adc_config[line].chan == 17) { + ADC->CCR |= ADC_CCR_TSVREFE; + } + } /* set resolution and conversion channel */ dev(line)->CR1 = res; dev(line)->SQR3 = adc_config[line].chan; @@ -121,7 +128,15 @@ int32_t adc_sample(adc_t line, adc_res_t res) while (!(dev(line)->SR & ADC_SR_EOC)) {} /* finally read sample and reset the STRT bit in the status register */ sample = (int)dev(line)->DR; - + /* check if this is the VBAT line or another internal ADC channel */ + if (IS_USED(MODULE_PERIPH_VBAT) && line == VBAT_ADC) { + vbat_disable(); + } + else if (dev(line) == ADC1) { + if (adc_config[line].chan == 16 || adc_config[line].chan == 17) { + ADC->CCR &= ~ADC_CCR_TSVREFE; + } + } /* power off and unlock device again */ done(line); diff --git a/cpu/stm32/periph/adc_f3.c b/cpu/stm32/periph/adc_f3.c index 2f0603719c..dc022524ba 100644 --- a/cpu/stm32/periph/adc_f3.c +++ b/cpu/stm32/periph/adc_f3.c @@ -24,6 +24,7 @@ #include "periph/adc.h" #include "periph_conf.h" #include "ztimer.h" +#include "periph/vbat.h" #define SMP_MIN (0x2) /*< Sampling time for slow channels (0x2 = 4.5 ADC clock cycles) */ @@ -33,6 +34,13 @@ #define ADC_INSTANCE ADC12_COMMON #endif +/** + * @brief Default VBAT undefined value + */ +#ifndef VBAT_ADC +#define VBAT_ADC ADC_UNDEF +#endif + /** * @brief Allocate locks for all available ADC devices */ @@ -128,8 +136,9 @@ int adc_init(adc_t line) } /* Configure the pin */ - gpio_init_analog(adc_config[line].pin); - + if (adc_config[line].pin != GPIO_UNDEF) { + gpio_init_analog(adc_config[line].pin); + } /* Init ADC line only if it wasn't already initialized */ if (!(dev(line)->CR & ADC_CR_ADEN)) { /* Enable ADC internal voltage regulator and wait for startup period */ @@ -192,6 +201,11 @@ int32_t adc_sample(adc_t line, adc_res_t res) /* Lock and power on the ADC device */ prep(line); + /* check if this is the VBAT line */ + if (IS_USED(MODULE_PERIPH_VBAT) && line == VBAT_ADC) { + vbat_enable(); + } + /* Set resolution */ dev(line)->CFGR &= ~ADC_CFGR_RES; dev(line)->CFGR |= res; @@ -206,6 +220,11 @@ int32_t adc_sample(adc_t line, adc_res_t res) /* Read the sample */ sample = (int)dev(line)->DR; + /* check if this is the VBAT line */ + if (IS_USED(MODULE_PERIPH_VBAT) && line == VBAT_ADC) { + vbat_disable(); + } + /* Power off and unlock device again */ done(line); diff --git a/cpu/stm32/periph/adc_f4_f7.c b/cpu/stm32/periph/adc_f4_f7.c index 378cb26144..64c489a6b1 100644 --- a/cpu/stm32/periph/adc_f4_f7.c +++ b/cpu/stm32/periph/adc_f4_f7.c @@ -23,12 +23,20 @@ #include "mutex.h" #include "periph/adc.h" #include "periph_conf.h" +#include "periph/vbat.h" /** * @brief Maximum allowed ADC clock speed */ #define MAX_ADC_SPEED (12000000U) +/** + * @brief Default VBAT undefined value + */ +#ifndef VBAT_ADC +#define VBAT_ADC ADC_UNDEF +#endif + /** * @brief Allocate locks for all three available ADC devices */ @@ -72,7 +80,9 @@ int adc_init(adc_t line) prep(line); /* configure the pin */ - gpio_init_analog(adc_config[line].pin); + if (adc_config[line].pin != GPIO_UNDEF) { + gpio_init_analog(adc_config[line].pin); + } /* set sequence length to 1 conversion and enable the ADC device */ dev(line)->SQR1 = 0; dev(line)->CR2 = ADC_CR2_ADON; @@ -100,7 +110,10 @@ int32_t adc_sample(adc_t line, adc_res_t res) /* lock and power on the ADC device */ prep(line); - + /* check if this channel is an internal ADC channel */ + if (IS_USED(MODULE_PERIPH_VBAT) && line == VBAT_ADC) { + vbat_enable(); + } /* set resolution and conversion channel */ dev(line)->CR1 = res; dev(line)->SQR3 = adc_config[line].chan; @@ -109,7 +122,10 @@ int32_t adc_sample(adc_t line, adc_res_t res) while (!(dev(line)->SR & ADC_SR_EOC)) {} /* finally read sample and reset the STRT bit in the status register */ sample = (int)dev(line)->DR; - + /* check if this channel was an internal ADC channel */ + if (IS_USED(MODULE_PERIPH_VBAT) && line == VBAT_ADC) { + vbat_disable(); + } /* power off and unlock device again */ done(line); diff --git a/cpu/stm32/periph/adc_l4.c b/cpu/stm32/periph/adc_l4.c index 6dc7df9bf8..7c2c7e13bd 100644 --- a/cpu/stm32/periph/adc_l4.c +++ b/cpu/stm32/periph/adc_l4.c @@ -25,6 +25,7 @@ #include "mutex.h" #include "periph/adc.h" #include "periph_conf.h" +#include "periph/vbat.h" #include "ztimer.h" /** @@ -55,6 +56,13 @@ #define ADC_SMPR2_FIRST_CHAN (10) #endif +/** + * @brief Default VBAT undefined value + */ +#ifndef VBAT_ADC +#define VBAT_ADC ADC_UNDEF +#endif + /** * @brief Allocate locks for all three available ADC devices */ @@ -122,8 +130,9 @@ int adc_init(adc_t line) } /* configure the pin */ - gpio_init_analog(adc_config[line].pin); - + if (adc_config[line].pin != GPIO_UNDEF) { + gpio_init_analog(adc_config[line].pin); + } #if defined(CPU_MODEL_STM32L476RG) || defined(CPU_MODEL_STM32L475VG) /* On STM32L475xx/476xx/486xx devices, before any conversion of an input channel coming from GPIO pads, it is necessary to configure the corresponding GPIOx_ASCR register in @@ -192,6 +201,11 @@ int32_t adc_sample(adc_t line, adc_res_t res) /* lock and power on the ADC device */ prep(line); + /* check if this is the VBAT line */ + if (IS_USED(MODULE_PERIPH_VBAT) && line == VBAT_ADC) { + vbat_enable(); + } + /* first clear resolution */ dev(line)->CFGR &= ~(ADC_CFGR_RES); @@ -208,6 +222,11 @@ int32_t adc_sample(adc_t line, adc_res_t res) /* read the sample */ sample = (int)dev(line)->DR; + /* check if this is the VBAT line */ + if (IS_USED(MODULE_PERIPH_VBAT) && line == VBAT_ADC) { + vbat_disable(); + } + /* free the device again */ done(line); diff --git a/cpu/stm32/periph/adc_wl.c b/cpu/stm32/periph/adc_wl.c index f3ccb2b7b4..7a895d613e 100644 --- a/cpu/stm32/periph/adc_wl.c +++ b/cpu/stm32/periph/adc_wl.c @@ -27,8 +27,16 @@ #include "mutex.h" #include "periph/adc.h" #include "periph_conf.h" +#include "periph/vbat.h" #include "ztimer.h" +/** + * @brief Default VBAT undefined value + */ +#ifndef VBAT_ADC +#define VBAT_ADC ADC_UNDEF +#endif + /** * @brief Allocate lock for the ADC device * @@ -59,7 +67,9 @@ int adc_init(adc_t line) prep(); /* configure the pin */ - gpio_init_analog(adc_config[line].pin); + if (adc_config[line].pin != GPIO_UNDEF) { + gpio_init_analog(adc_config[line].pin); + } /* init ADC line only if it wasn't already initialized */ if (!(ADC->CR & (ADC_CR_ADEN))) { @@ -116,6 +126,11 @@ int32_t adc_sample(adc_t line, adc_res_t res) /* lock and power on the ADC device */ prep(); + /* check if this is the VBAT line */ + if (IS_USED(MODULE_PERIPH_VBAT) && line == VBAT_ADC) { + vbat_enable(); + } + /* first clear resolution */ ADC->CFGR1 &= ~ADC_CFGR1_RES; @@ -132,6 +147,11 @@ int32_t adc_sample(adc_t line, adc_res_t res) /* read the sample */ sample = (int)ADC->DR; + /* check if this is the VBAT line */ + if (IS_USED(MODULE_PERIPH_VBAT) && line == VBAT_ADC) { + vbat_disable(); + } + /* free the device again */ done(); diff --git a/cpu/stm32/periph/vbat.c b/cpu/stm32/periph/vbat.c new file mode 100644 index 0000000000..71028727e7 --- /dev/null +++ b/cpu/stm32/periph/vbat.c @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2022 Otto-von-Guericke-Universität Magdeburg + * + * 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_vbat + * @{ + * + * @file + * @brief Implementation of STM32 backup battery monitoring + * + * @author Fabian Hüßler + * @} + */ + +#include "board.h" +#include "periph_conf.h" +#include "periph/adc.h" +#include "periph/vbat.h" + +/** + * @name Constants depending on CPU line + * @{ + * + * @ref VBAT_ADC_SCALE is a scale factor to calculate the right voltage value + * as documented in the data sheet. + * @ref VBAT_ADC_MIN_MV <= VBAT[mV] is the smallest voltage level required + * to power the backup domain. + */ +/* f0 */ +#if defined(CPU_LINE_STM32F031x6) || defined(CPU_LINE_STM32F038xx) || \ + defined(CPU_LINE_STM32F042x6) || defined(CPU_LINE_STM32F048xx) || \ + defined(CPU_LINE_STM32F051x8) || defined(CPU_LINE_STM32F058xx) || \ + defined(CPU_LINE_STM32F071xB) || defined(CPU_LINE_STM32F072xB) || \ + defined(CPU_LINE_STM32F078xx) || defined(CPU_LINE_STM32F091xC) || \ + defined(CPU_LINE_STM32F098xx) +#define VBAT_ADC_SCALE 2 +#define VBAT_ADC_MIN_MV 1650 +/* f2 */ +#elif defined(CPU_LINE_STM32F205xx) || defined(CPU_LINE_STM32F207xx) || \ + defined(CPU_LINE_STM32F215xx) || defined(CPU_LINE_STM32F217xx) +#define VBAT_ADC_SCALE 2 +#define VBAT_ADC_MIN_MV 1800 +/* f3 */ +#elif defined(CPU_LINE_STM32F301x8) || defined(CPU_LINE_STM32F302x8) || \ + defined(CPU_LINE_STM32F302xC) || defined(CPU_LINE_STM32F302xE) || \ + defined(CPU_LINE_STM32F303x8) || defined(CPU_LINE_STM32F303xC) || \ + defined(CPU_LINE_STM32F303xE) || defined(CPU_LINE_STM32F318x8) || \ + defined(CPU_LINE_STM32F318xx) || defined(CPU_LINE_STM32F328xx) || \ + defined(CPU_LINE_STM32F334x8) || defined(CPU_LINE_STM32F358xx) || \ + defined(CPU_LINE_STM32F373xC) || defined(CPU_LINE_STM32F378xx) || \ + defined(CPU_LINE_STM32F398xx) +#define VBAT_ADC_SCALE 2 +#define VBAT_ADC_MIN_MV 1650 +/* f4 */ +#elif defined(CPU_LINE_STM32F401xC) || defined(CPU_LINE_STM32F401xE) || \ + defined(CPU_LINE_STM32F410Cx) || defined(CPU_LINE_STM32F410Rx) || \ + defined(CPU_LINE_STM32F410Tx) || defined(CPU_LINE_STM32F411xE) || \ + defined(CPU_LINE_STM32F412Cx) || defined(CPU_LINE_STM32F412Rx) || \ + defined(CPU_LINE_STM32F412Vx) || defined(CPU_LINE_STM32F412Zx) || \ + defined(CPU_LINE_STM32F413xx) || defined(CPU_LINE_STM32F423xx) || \ + defined(CPU_LINE_STM32F427xx) || defined(CPU_LINE_STM32F429xx) || \ + defined(CPU_LINE_STM32F437xx) || defined(CPU_LINE_STM32F439xx) || \ + defined(CPU_LINE_STM32F446xx) || defined(CPU_LINE_STM32F469xx) || \ + defined(CPU_LINE_STM32F479xx) +#define VBAT_ADC_SCALE 4 +#define VBAT_ADC_MIN_MV 1650 +#elif defined(CPU_LINE_STM32F405xx) || defined(CPU_LINE_STM32F407xx) || \ + defined(CPU_LINE_STM32F415xx) || defined(CPU_LINE_STM32F417xx) +#define VBAT_ADC_SCALE 2 +#define VBAT_ADC_MIN_MV 1650 +/* f7 */ +#elif defined(CPU_LINE_STM32F722xx) || defined(CPU_LINE_STM32F723xx) || \ + defined(CPU_LINE_STM32F730xx) || defined(CPU_LINE_STM32F732xx) || \ + defined(CPU_LINE_STM32F733xx) || defined(CPU_LINE_STM32F745xx) || \ + defined(CPU_LINE_STM32F746xx) || defined(CPU_LINE_STM32F750xx) || \ + defined(CPU_LINE_STM32F756xx) || defined(CPU_LINE_STM32F765xx) || \ + defined(CPU_LINE_STM32F767xx) || defined(CPU_LINE_STM32F769xx) || \ + defined(CPU_LINE_STM32F777xx) || defined(CPU_LINE_STM32F779xx) +#define VBAT_ADC_SCALE 4 +#define VBAT_ADC_MIN_MV 1650 +/* g0 */ +#elif defined(CPU_LINE_STM32G030xx) || defined(CPU_LINE_STM32G031xx) || \ + defined(CPU_LINE_STM32G041xx) || defined(CPU_LINE_STM32G050xx) || \ + defined(CPU_LINE_STM32G051xx) || defined(CPU_LINE_STM32G061xx) || \ + defined(CPU_LINE_STM32G070xx) || defined(CPU_LINE_STM32G071xx) || \ + defined(CPU_LINE_STM32G081xx) || defined(CPU_LINE_STM32G0B0xx) || \ + defined(CPU_LINE_STM32G0B1xx) || defined(CPU_LINE_STM32G0C1xx) +#define VBAT_ADC_SCALE 3 +#define VBAT_ADC_MIN_MV 1550 +/* g4 */ +#elif defined(CPU_LINE_STM32G431xx) || defined(CPU_LINE_STM32G441xx) || \ + defined(CPU_LINE_STM32G471xx) || defined(CPU_LINE_STM32G473xx) || \ + defined(CPU_LINE_STM32G474xx) || defined(CPU_LINE_STM32G483xx) || \ + defined(CPU_LINE_STM32G484xx) || defined(CPU_LINE_STM32G491xx) || \ + defined(CPU_LINE_STM32G4A1xx) || defined(CPU_LINE_STM32G441xx) || \ + defined(CPU_LINE_STM32GBK1CB) +#define VBAT_ADC_SCALE 3 +#define VBAT_ADC_MIN_MV 1550 +/* l4 */ +#elif defined(CPU_LINE_STM32L412xx) || defined(CPU_LINE_STM32L422xx) || \ + defined(CPU_LINE_STM32L431xx) || defined(CPU_LINE_STM32L432xx) || \ + defined(CPU_LINE_STM32L433xx) || defined(CPU_LINE_STM32L442xx) || \ + defined(CPU_LINE_STM32L443xx) || defined(CPU_LINE_STM32L451xx) || \ + defined(CPU_LINE_STM32L452xx) || defined(CPU_LINE_STM32L462xx) || \ + defined(CPU_LINE_STM32L471xx) || defined(CPU_LINE_STM32L475xx) || \ + defined(CPU_LINE_STM32L476xx) || defined(CPU_LINE_STM32L485xx) || \ + defined(CPU_LINE_STM32L486xx) || defined(CPU_LINE_STM32L496xx) || \ + defined(CPU_LINE_STM32L4A6xx) || defined(CPU_LINE_STM32L4P5xx) || \ + defined(CPU_LINE_STM32L4Q5xx) || defined(CPU_LINE_STM32L4R5xx) || \ + defined(CPU_LINE_STM32L4R7xx) || defined(CPU_LINE_STM32L4R9xx) || \ + defined(CPU_LINE_STM32L4S5xx) || defined(CPU_LINE_STM32L4S7xx) || \ + defined(CPU_LINE_STM32L4S9xx) +#define VBAT_ADC_SCALE 3 +#define VBAT_ADC_MIN_MV 1550 +/* l5 */ +#elif defined(CPU_LINE_STM32L552xx) || defined(CPU_LINE_STM32L562xx) +#define VBAT_ADC_SCALE 3 +#define VBAT_ADC_MIN_MV 1550 +/* u5 */ +#elif defined(CPU_LINE_STM32U575xx) || defined(CPU_LINE_STM32U585xx) +#define VBAT_ADC_SCALE 4 +#define VBAT_ADC_MIN_MV 1650 +/* wb */ +#elif defined(CPU_LINE_STM32WB10xx) || defined(CPU_LINE_STM32WB15xx) || \ + defined(CPU_LINE_STM32WB30xx) || defined(CPU_LINE_STM32WB35xx) || \ + defined(CPU_LINE_STM32WB50xx) || defined(CPU_LINE_STM32WB55xx) || \ + defined(CPU_LINE_STM32WB5Mxx) +#define VBAT_ADC_SCALE 3 +#define VBAT_ADC_MIN_MV 1550 +/* wl */ +#elif defined(CPU_LINE_STM32WL54xx) || defined(CPU_LINE_STM32WL55xx) || \ + defined(CPU_LINE_STM32WLE4xx) || defined(CPU_LINE_STM32WLE5xx) +#define VBAT_ADC_SCALE 3 +#define VBAT_ADC_MIN_MV 1550 +#else +#error "VBAT: CPU line is not supported so far." +#endif +/** @} */ + +/** + * @name VBAT enable register + * @{ + */ +#if defined(CPU_LINE_STM32F373xC) || defined(CPU_LINE_STM32F378xx) +#define ADC_CCR_REG (SYSCFG->CFGR1) /* ADCx_COMMON is also defined */ +#elif defined(ADC_COMMON) +#define ADC_CCR_REG (ADC_COMMON->CCR) +#elif defined(ADC1_COMMON) +#define ADC_CCR_REG (ADC1_COMMON->CCR) +#elif defined(ADC12_COMMON) +#define ADC_CCR_REG (ADC12_COMMON->CCR) +#elif defined(ADC123_COMMON) +#define ADC_CCR_REG (ADC123_COMMON->CCR) +#elif defined(ADC12_COMMON_NS) +#define ADC_CCR_REG (ADC12_COMMON_NS->CCR) +#elif defined(ADC) +#define ADC_CCR_REG (ADC->CCR) +#else +#error "VBAT: CPU line is not supported so far." +#endif +/** @} */ + +/** + * @name VBAT enable flag + * @{ + */ +#if defined(ADC_CCR_VBATEN) +#define VBAT_ENABLE ADC_CCR_VBATEN; +#elif defined(ADC_CCR_VBATE) +#define VBAT_ENABLE ADC_CCR_VBATE; +#elif defined(ADC_CCR_VBATSEL) +#define VBAT_ENABLE ADC_CCR_VBATSEL; +#elif defined(SYSCFG_CFGR1_VBAT) +#define VBAT_ENABLE SYSCFG_CFGR1_VBAT; +#else +#error "VBAT: CPU line is not supported so far." +#endif +/** @} */ + +#ifndef CONFIG_VBAT_ADC_VREF_MV +#define CONFIG_VBAT_ADC_VREF_MV 3300 /**< ADC reference voltage */ +#endif + +/** + * @brief Override this function if you know how to retrieve the accurate + * ADC supply voltage in mV for your board. The default behaviour is + * to return @ref CONFIG_VBAT_ADC_VREF_MV. + * Once there is a driver to sample VREFINT, this function is likely + * to be changed. + */ +int32_t __attribute__((weak)) vref_mv(void) { + return CONFIG_VBAT_ADC_VREF_MV; +} + +#ifndef VBAT_ADC +#error "VBAT: Add internal VBAT ADC line to adc_config[] and #define VBAT_ADC." +#endif + +int vbat_init(void) +{ + return adc_init(VBAT_ADC); +} + +void vbat_enable(void) +{ + ADC_CCR_REG |= VBAT_ENABLE; +} + +void vbat_disable(void) +{ + ADC_CCR_REG &= ~VBAT_ENABLE; +} + +int32_t vbat_sample_mv(void) +{ + int32_t mv = adc_sample(VBAT_ADC, VBAT_ADC_RES); + mv = (mv * VBAT_ADC_SCALE * vref_mv()) / VBAT_ADC_MAX; + return mv; +} + +bool vbat_is_empty(void) +{ + return VBAT_ADC_MIN_MV > vbat_sample_mv(); +} diff --git a/drivers/periph_common/Kconfig b/drivers/periph_common/Kconfig index 2947f5c5b0..14219ec373 100644 --- a/drivers/periph_common/Kconfig +++ b/drivers/periph_common/Kconfig @@ -170,4 +170,5 @@ config HAVE_SHARED_PERIPH_RTT_PERIPH_RTC only one can be selected. rsource "Kconfig.timer" +rsource "Kconfig.vbat" rsource "Kconfig.wdt" diff --git a/drivers/periph_common/Kconfig.vbat b/drivers/periph_common/Kconfig.vbat new file mode 100644 index 0000000000..0df040285a --- /dev/null +++ b/drivers/periph_common/Kconfig.vbat @@ -0,0 +1,39 @@ +# Copyright (c) 2022 Otto-von-Guericke-Universität Magdeburg +# +# 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. +# + +menuconfig MODULE_PERIPH_VBAT + bool "Backup Battery monitoring driver" + depends on HAS_PERIPH_VBAT + depends on HAS_PERIPH_ADC + depends on TEST_KCONFIG + select MODULE_PERIPH_ADC + +if MODULE_PERIPH_VBAT + +config MODULE_PERIPH_INIT_VBAT + bool "Auto initialize VBAT ADC line" + default y if MODULE_PERIPH_INIT + +config VBAT_ADC_VREF_MV + int "ADC reference voltage in mV" + default 3300 + +endif # MODULE_PERIPH_VBAT + +menuconfig KCONFIG_USEMODULE_PERIPH_VBAT + bool "Configure backup battery monitoring peripheral driver" + depends on USEMODULE_PERIPH_VBAT + help + Configure backup battery monitoring peripheral driver using Kconfig. + +config VBAT_ADC_VREF_MV + int "ADC reference voltage in mV" + default 3300 + depends on KCONFIG_USEMODULE_PERIPH_VBAT + help + This is the reference voltage (VREF) of the ADC. + Often VREF is connected with VDDA, which is equal to VDD. diff --git a/kconfigs/Kconfig.features b/kconfigs/Kconfig.features index 5469b2420b..c178989a29 100644 --- a/kconfigs/Kconfig.features +++ b/kconfigs/Kconfig.features @@ -369,6 +369,11 @@ config HAS_PERIPH_USBDEV help Indicates that an USBDEV peripheral is present. +config HAS_PERIPH_VBAT + bool + help + Indicates that backup battery monitoring is supported + config HAS_PERIPH_WDT bool help