From c1073024a6b857359d0d70f75b03aac728109ef9 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Wed, 10 May 2023 09:11:16 +0200 Subject: [PATCH 1/9] cpu/stm32/periph_adc: support STM32L496AG --- cpu/stm32/include/periph/l4/periph_cpu.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpu/stm32/include/periph/l4/periph_cpu.h b/cpu/stm32/include/periph/l4/periph_cpu.h index 58479d5b7098..01eef9f17a30 100644 --- a/cpu/stm32/include/periph/l4/periph_cpu.h +++ b/cpu/stm32/include/periph/l4/periph_cpu.h @@ -37,7 +37,8 @@ extern "C" { #if defined(CPU_MODEL_STM32L476RG) || defined(CPU_MODEL_STM32L475VG) || \ defined(CPU_MODEL_STM32L452RE) || defined(CPU_MODEL_STM32L432KC) || \ - defined(CPU_MODEL_STM32L496ZG) || defined(CPU_MODEL_STM32L4R5ZI) + defined(CPU_MODEL_STM32L496ZG) || defined(CPU_MODEL_STM32L4R5ZI) || \ + defined(CPU_MODEL_STM32L496AG) /** * @brief ADC voltage regulator start-up time [us] */ From e4ca7b818271d424b909de22c46c0f675f0baf1e Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Wed, 10 May 2023 09:18:41 +0200 Subject: [PATCH 2/9] cpu/stm32/periph_adc: determine number of ADC from CMSIS header Instead of defining the number of ADC devices for each MCU model, the number of ADC devices is determined from ADCx definitions in CMSIS header. --- cpu/stm32/include/periph/l4/periph_cpu.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cpu/stm32/include/periph/l4/periph_cpu.h b/cpu/stm32/include/periph/l4/periph_cpu.h index 01eef9f17a30..1897a9f622cf 100644 --- a/cpu/stm32/include/periph/l4/periph_cpu.h +++ b/cpu/stm32/include/periph/l4/periph_cpu.h @@ -27,12 +27,14 @@ extern "C" { /** * @brief Available number of ADC devices */ -#if defined(CPU_MODEL_STM32L476RG) || defined(CPU_MODEL_STM32L475VG) || \ - defined(CPU_MODEL_STM32L496ZG) +#if defined(ADC3) #define ADC_DEVS (3U) -#elif defined(CPU_MODEL_STM32L452RE) || defined(CPU_MODEL_STM32L432KC) || \ - defined(CPU_MODEL_STM32L4R5ZI) +#elif defined(ADC2) +#define ADC_DEVS (2U) +#elif defined(ADC1) #define ADC_DEVS (1U) +#else +#error "Can't determine the number of ADC devices" #endif #if defined(CPU_MODEL_STM32L476RG) || defined(CPU_MODEL_STM32L475VG) || \ From c4d09083fa17d4c4c96c391458642bcc3f16ef9a Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Wed, 10 May 2023 08:56:14 +0200 Subject: [PATCH 3/9] cpu/stm32/periph_adc: defines are valid for all L4 MCUs --- cpu/stm32/periph/adc_l4.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/cpu/stm32/periph/adc_l4.c b/cpu/stm32/periph/adc_l4.c index a6c3bba8fa69..9e3a5e7f9913 100644 --- a/cpu/stm32/periph/adc_l4.c +++ b/cpu/stm32/periph/adc_l4.c @@ -40,33 +40,29 @@ #endif /** - * @brief map CPU specific register/value names + * @brief map CPU specific register/value names valid for all STM32L4 MCUs */ -#if defined(CPU_MODEL_STM32L476RG) || defined(CPU_MODEL_STM32L4R5ZI) || \ - defined(CPU_MODEL_STM32L496ZG) #define ADC_CR_REG CR #define ADC_ISR_REG ISR #define ADC_PERIPH_CLK AHB2 -/* on stm32-l476rg all ADC clocks are are enabled by this bit +/* on STM32L4xx MCUs all ADC clocks are are enabled by this bit further clock config is possible over CKMODE[1:0] bits in ADC_CCR reg */ #define ADC_CLK_EN_MASK (RCC_AHB2ENR_ADCEN) /* referring to Datasheet Section 6.3.18 (ADC characteristics) the minimum achievable sampling rate is 4.21 Msps (12 Bit resolution on slow channel) we use that worst case for configuring the sampling time to be sure it works on all channels. - TCONV = Sampling time + 12.5 ADC clock cycles. + TCONV = Sampling time + 12.5 ADC clock cycles (RM section 18.4.12) At 80MHz this means we need to set SMP to 001 (6.5 ADC clock cycles) to - stay within specs. (80000000/(6.5+12.5)) = 4210526 */ + stay within specs. (80000000/(6.5+12.5)) = 4210526 */ #define ADC_SMP_MIN_VAL (0x1) -/* The sampling time can be specified for each channel over SMPR1 and SMPR2. - This specifies the first channel that goes to SMPR2 instead of SMPR1. */ +/* The sampling time width is 3 bit */ #define ADC_SMP_BIT_WIDTH (3) /* The sampling time can be specified for each channel over SMPR1 and SMPR2. This specifies the first channel that goes to SMPR2 instead of SMPR1. */ #define ADC_SMPR2_FIRST_CHAN (10) -#endif /** * @brief Default VBAT undefined value From d8bd2d9f43541ddaefd70a964c4288671ce95ae4 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Wed, 10 May 2023 08:48:20 +0200 Subject: [PATCH 4/9] cpu/stm32/periph_adc: fix ADC clock disable for L4 The ADC clock disable is fixed using a counter. The counter is incremented in `prep` and decremented in `done`. The ADC clock is disabled if the counter becomes 0. --- cpu/stm32/periph/adc_l4.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/cpu/stm32/periph/adc_l4.c b/cpu/stm32/periph/adc_l4.c index 9e3a5e7f9913..6ffbb5c5e719 100644 --- a/cpu/stm32/periph/adc_l4.c +++ b/cpu/stm32/periph/adc_l4.c @@ -76,6 +76,9 @@ */ static mutex_t locks[ADC_DEVS]; +/* count the periph_clk_en calls to know when to disable the clock in done() */ +static uint8_t _clk_en_ctr = 0; + static inline ADC_TypeDef *dev(adc_t line) { return (ADC_TypeDef *)(ADC1_BASE + (adc_config[line].dev << 8)); @@ -85,16 +88,16 @@ static inline void prep(adc_t line) { mutex_lock(&locks[adc_config[line].dev]); periph_clk_en(ADC_PERIPH_CLK, ADC_CLK_EN_MASK); + _clk_en_ctr++; } static inline void done(adc_t line) { -/* on STM32L476RG (TODO: maybe true for other L4's? - haven't checked yet) - all adc devices are controlled by this one bit. - So don't disable the clock as other devices may still use it */ -#if !defined(CPU_MODEL_STM32L476RG) - periph_clk_dis(ADC_PERIPH_CLK, ADC_CLK_EN_MASK); -#endif + /* All ADC devices are controlled by this one bit. + * So don't disable the clock if other devices may still use it */ + if (_clk_en_ctr && --_clk_en_ctr == 0) { + periph_clk_dis(ADC_PERIPH_CLK, ADC_CLK_EN_MASK); + } mutex_unlock(&locks[adc_config[line].dev]); } From ccba70ff617deee9244a07a067bf18e85e02ee57 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Wed, 10 May 2023 08:50:49 +0200 Subject: [PATCH 5/9] cpu/stm32/periph_adc: support for internal V_REF+ For boards that have not connected the V_REF+ pin to an external reference voltage, the VREFBUF peripheral can be used as V_REF+ if supported by setting `VREFBUF_ENABLE=1`. --- cpu/stm32/periph/adc_l4.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cpu/stm32/periph/adc_l4.c b/cpu/stm32/periph/adc_l4.c index 6ffbb5c5e719..8bd946a4afc7 100644 --- a/cpu/stm32/periph/adc_l4.c +++ b/cpu/stm32/periph/adc_l4.c @@ -124,6 +124,16 @@ int adc_init(adc_t line) return -1; } +#if VREFBUF_ENABLE && defined(VREFBUF_CSR_ENVR) + /* enable VREFBUF if needed and available (for example if the board doesn't + * have an external reference voltage connected to V_REF+), wait until + * it is ready */ + RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; + VREFBUF->CSR &= ~VREFBUF_CSR_HIZ; + VREFBUF->CSR |= VREFBUF_CSR_ENVR; + while (!(VREFBUF->CSR & VREFBUF_CSR_VRR)) { } +#endif + /* lock device and enable its peripheral clock */ prep(line); From 5483235c611a256db0bfa957d449e38e947346e2 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Wed, 10 May 2023 09:04:12 +0200 Subject: [PATCH 6/9] cpu/stm32/periph_adc: use L4 lines instead of L4 models The ASCR register is available and has to be set for all STM32L471xx, STM32L475xx, STM32L476xx, STM32L485xx and STM32L486xx MCUs. Instead of using the CPU model for conditional compilation, the CPU line is used to support all MCU of that lines. --- cpu/stm32/periph/adc_l4.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cpu/stm32/periph/adc_l4.c b/cpu/stm32/periph/adc_l4.c index 8bd946a4afc7..b98ac96ef54c 100644 --- a/cpu/stm32/periph/adc_l4.c +++ b/cpu/stm32/periph/adc_l4.c @@ -154,8 +154,10 @@ int adc_init(adc_t line) 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 +#if defined(CPU_LINE_STM32L486xx) || defined(CPU_LINE_STM32L485xx) || \ + defined(CPU_LINE_STM32L476xx) || defined(CPU_LINE_STM32L475xx) || \ + defined(CPU_LINE_STM32L471xx) + /* On STM32L47xx/48xx devices, before any conversion of an input channel coming from GPIO pads, it is necessary to configure the corresponding GPIOx_ASCR register in the GPIO, in addition to the I/O configuration in analog mode. */ _port(adc_config[line].pin)->ASCR |= (1 << _pin_num(adc_config[line].pin)); From af8a87ad99dc711a529a33f5dc42af74b6506770 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Wed, 10 May 2023 09:23:37 +0200 Subject: [PATCH 7/9] cpu/stm32/periph_adc: fix SQR1 setting for L4 The setting of SQR1 is fixed. Setting the SQR1 did only work before because the ADC_SRQ_L is set to 0 for a sequence length of 1. --- cpu/stm32/periph/adc_l4.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cpu/stm32/periph/adc_l4.c b/cpu/stm32/periph/adc_l4.c index b98ac96ef54c..727a08e809b2 100644 --- a/cpu/stm32/periph/adc_l4.c +++ b/cpu/stm32/periph/adc_l4.c @@ -181,7 +181,7 @@ int adc_init(adc_t line) /* configure calibration for single ended input */ dev(line)->ADC_CR_REG &= ~(ADC_CR_ADCALDIF); - /* ´start automatic calibration and wait for it to complete */ + /* start automatic calibration and wait for it to complete */ dev(line)->ADC_CR_REG |= ADC_CR_ADCAL; while (dev(line)->ADC_CR_REG & ADC_CR_ADCAL) {} @@ -192,8 +192,8 @@ int adc_init(adc_t line) dev(line)->ADC_CR_REG |= (ADC_CR_ADEN); while ((dev(line)->ADC_ISR_REG & ADC_ISR_ADRDY) == 0) {} - /* set sequence length to 1 conversion */ - dev(line)->SQR1 |= (0 & ADC_SQR1_L); + /* set sequence length to 1 conversion, set ADC_SQR1_L to 0 */ + dev(line)->SQR1 &= ~ADC_SQR1_L_Msk; } /* configure sampling time for the given channel */ @@ -236,7 +236,8 @@ int32_t adc_sample(adc_t line, adc_res_t res) dev(line)->CFGR |= res; /* specify channel for regular conversion */ - dev(line)->SQR1 = (adc_config[line].chan << ADC_SQR1_SQ1_Pos); + dev(line)->SQR1 &= ~ADC_SQR1_SQ1_Msk; + dev(line)->SQR1 |= (adc_config[line].chan << ADC_SQR1_SQ1_Pos); /* start conversion and wait for it to complete */ dev(line)->ADC_CR_REG |= ADC_CR_ADSTART; From 6247b2aea4b3ab20b29f7f6da903f57bb84f4ffa Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Mon, 15 May 2023 14:23:48 +0200 Subject: [PATCH 8/9] cpu/stm32/periph_adc: fix CKMODE setting for L4 Setting the `ADC_CCR_CKMODE` did only work for the reset state. It is now cleared before it is set. Instead of using the `ADC_CCR_CKMODE_x` bits to set the mode, the mode defines are used. --- cpu/stm32/periph/adc_l4.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/cpu/stm32/periph/adc_l4.c b/cpu/stm32/periph/adc_l4.c index 727a08e809b2..63c64dc18cc7 100644 --- a/cpu/stm32/periph/adc_l4.c +++ b/cpu/stm32/periph/adc_l4.c @@ -64,6 +64,9 @@ This specifies the first channel that goes to SMPR2 instead of SMPR1. */ #define ADC_SMPR2_FIRST_CHAN (10) +#define ADC_CCR_CKMODE_HCLK_1 (ADC_CCR_CKMODE_0) +#define ADC_CCR_CKMODE_HCLK_2 (ADC_CCR_CKMODE_1) + /** * @brief Default VBAT undefined value */ @@ -140,14 +143,14 @@ int adc_init(adc_t line) /* set prescaler to 0 to let the ADC run with maximum speed */ ADC->CCR &= ~(ADC_CCR_PRESC); - /* Setting ADC clock to HCLK/1 is only allowed if AHB clock prescaler is 1*/ + ADC->CCR &= ~(ADC_CCR_CKMODE); if (!(RCC->CFGR & RCC_CFGR_HPRE_3)) { - /* set ADC clock to HCLK/1 */ - ADC->CCR |= (ADC_CCR_CKMODE_0); + /* set ADC clock to HCLK/1, only allowed if AHB clock prescaler is 1 */ + ADC->CCR |= ADC_CCR_CKMODE_HCLK_1 << ADC_CCR_CKMODE_Pos; } else { /* set ADC clock to HCLK/2 otherwise */ - ADC->CCR |= (ADC_CCR_CKMODE_1); + ADC->CCR |= ADC_CCR_CKMODE_HCLK_2 << ADC_CCR_CKMODE_Pos; } /* configure the pin */ From caa50a57e6ebee777852061b6354a3ebeca47b70 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Wed, 10 May 2023 09:31:00 +0200 Subject: [PATCH 9/9] cpu/stm32/periph_adc: support V_REFINT as ADC channel on L4 --- cpu/stm32/periph/adc_l4.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cpu/stm32/periph/adc_l4.c b/cpu/stm32/periph/adc_l4.c index 63c64dc18cc7..195bbefb911b 100644 --- a/cpu/stm32/periph/adc_l4.c +++ b/cpu/stm32/periph/adc_l4.c @@ -231,6 +231,11 @@ int32_t adc_sample(adc_t line, adc_res_t res) if (IS_USED(MODULE_PERIPH_VBAT) && line == VBAT_ADC) { vbat_enable(); } +#ifdef VREFINT_ADC + if (line == VREFINT_ADC) { + ADC->CCR |= ADC_CCR_VREFEN; + } +#endif /* first clear resolution */ dev(line)->CFGR &= ~(ADC_CFGR_RES); @@ -253,6 +258,11 @@ int32_t adc_sample(adc_t line, adc_res_t res) if (IS_USED(MODULE_PERIPH_VBAT) && line == VBAT_ADC) { vbat_disable(); } +#ifdef VREFINT_ADC + if (line == VREFINT_ADC) { + ADC->CCR &= ~ADC_CCR_VREFEN; + } +#endif /* free the device again */ done(line);