This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 9e938882205 stm32/adc: add support for differential mode (DIFSEL) and
calibration
9e938882205 is described below
commit 9e938882205b32e14c7d35e397193f19772c4174
Author: Daniel P. Carvalho <[email protected]>
AuthorDate: Mon Sep 14 22:45:29 2026 -0300
stm32/adc: add support for differential mode (DIFSEL) and calibration
Add board-level configuration and driver support for differential input
channels and auto-calibration on STM32 ADC IPv2 and IPv2G4 (STM32F3,
STM32G4), following the STM32H5 architecture pattern requested in review:
- In hardware headers (stm32_adc_v2.h and stm32_adc_v2g4.h), fix register
comments and define ADC_DIFSEL register shift and masks.
- In stm32_adc_m3m4_v1v2.c:
- Add difsel field to struct stm32_dev_s initialized from
BOARD_ADCx_DIFSEL if defined, falling back to ADC_DIFSEL_DEFAULT (0).
- Configure DIFSEL before enabling the ADC in adc_configure().
- Fix and enable adc_calibrate() for HAVE_IP_ADC_V2, supporting both
single-ended calibration (ADCALDIF=0) and differential calibration
(ADCALDIF=1) based on the channel selection.
Verified on Nucleo-G431KB (single-ended and differential channel 1).
Assisted-by: Gemini:gemini-2.5-pro
Signed-off-by: Daniel P. Carvalho <[email protected]>
---
arch/arm/src/common/stm32/hardware/stm32_adc_v2.h | 7 +-
.../arm/src/common/stm32/hardware/stm32_adc_v2g4.h | 4 +-
arch/arm/src/common/stm32/stm32_adc_m3m4_v1v2.c | 84 ++++++++++++++++++----
3 files changed, 76 insertions(+), 19 deletions(-)
diff --git a/arch/arm/src/common/stm32/hardware/stm32_adc_v2.h
b/arch/arm/src/common/stm32/hardware/stm32_adc_v2.h
index e8629afef80..364035d7e6f 100644
--- a/arch/arm/src/common/stm32/hardware/stm32_adc_v2.h
+++ b/arch/arm/src/common/stm32/hardware/stm32_adc_v2.h
@@ -637,14 +637,13 @@
/* ADC analog watchdog 3 configuration register */
-#define ADC_AWD3CR_CH_SHIFT (1) /* Bits 1-18: Analog
watchdog 2 channel selection */
+#define ADC_AWD3CR_CH_SHIFT (1) /* Bits 1-18: Analog
watchdog 3 channel selection */
#define ADC_AWD3CR_CH_MASK (0x3ffff << ADC_AWD3CR_CH_SHIFT)
# define ADC_AWD3CR_CH(n) (1 << (n)) /* Channel n=1..18 */
-/* ADC differential mode selection register 2 */
-#define ADC_DIFSEL_
+/* ADC differential mode selection register */
-#define ADC_DIFSEL_CH_SHIFT (1) /* Bits 1-18: Analog
watchdog 2 channel selection */
+#define ADC_DIFSEL_CH_SHIFT (1) /* Bits 1-18: Differential
mode selection */
#define ADC_DIFSEL_CH_MASK (0x3ffff << ADC_DIFSEL_CH_SHIFT)
# define ADC_DIFSEL_CH(n) (1 << (n)) /* Channel n=1..18 */
diff --git a/arch/arm/src/common/stm32/hardware/stm32_adc_v2g4.h
b/arch/arm/src/common/stm32/hardware/stm32_adc_v2g4.h
index 314244086db..96c8d7f7951 100644
--- a/arch/arm/src/common/stm32/hardware/stm32_adc_v2g4.h
+++ b/arch/arm/src/common/stm32/hardware/stm32_adc_v2g4.h
@@ -766,13 +766,13 @@
/* ADC analog watchdog 3 configuration register (AWD3CR) */
-#define ADC_AWD3CR_CH_SHIFT (0) /* Bits
0-18: Analog watchdog 2 channel selection */
+#define ADC_AWD3CR_CH_SHIFT (0) /* Bits
0-18: Analog watchdog 3 channel selection */
#define ADC_AWD3CR_CH_MASK (0x7ffff << ADC_AWD3CR_CH_SHIFT)
# define ADC_AWD3CR_CH(n) (1 << (n)) /*
Channel n=0..18 */
/* ADC differential mode selection register (DIFSEL) */
-#define ADC_DIFSEL_CH_SHIFT (0) /* Bits
0-18: Analog watchdog 2 channel selection */
+#define ADC_DIFSEL_CH_SHIFT (0) /* Bits
0-18: Differential mode selection */
#define ADC_DIFSEL_CH_MASK (0x7ffff << ADC_DIFSEL_CH_SHIFT)
# define ADC_DIFSEL_CH(n) (1 << (n)) /*
Channel n=0..18 */
diff --git a/arch/arm/src/common/stm32/stm32_adc_m3m4_v1v2.c
b/arch/arm/src/common/stm32/stm32_adc_m3m4_v1v2.c
index b53e2b6a071..f65aa02f9ae 100644
--- a/arch/arm/src/common/stm32/stm32_adc_m3m4_v1v2.c
+++ b/arch/arm/src/common/stm32/stm32_adc_m3m4_v1v2.c
@@ -371,6 +371,14 @@
#define ANIOC_TRIGGER_REGULAR (1 << 0)
#define ANIOC_TRIGGER_INJECTED (1 << 1)
+/* ADC differential mode selection configuration */
+
+#ifdef HAVE_IP_ADC_V2
+# define ADC_DIFSEL_DEFAULT 0
+# define ADC_DIFSEL_ALL_SINGLE 0x0
+# define ADC_DIFSEL_ALL_DIFF 0x7ffff
+#endif
+
/****************************************************************************
* Private Types
****************************************************************************/
@@ -447,6 +455,9 @@ struct stm32_dev_s
#ifdef ADC_HAVE_JEXTCFG
uint32_t jextcfg; /* External event configuration for injected
group */
#endif
+#ifdef HAVE_IP_ADC_V2
+ uint32_t difsel; /* ADCx_DIFSEL (Differential Mode) value */
+#endif
#ifdef ADC_HAVE_TIMER
uint32_t tbase; /* Base address of timer used by this ADC block */
uint32_t pclck; /* The PCLK frequency that drives this timer */
@@ -596,7 +607,6 @@ static int adc_extcfg_set(struct stm32_dev_s *priv,
uint32_t extcfg);
#ifdef ADC_HAVE_JEXTCFG
static int adc_jextcfg_set(struct stm32_dev_s *priv, uint32_t jextcfg);
#endif
-
static void adc_dumpregs(struct stm32_dev_s *priv);
#ifdef CONFIG_STM32_ADC_LL_OPS
@@ -787,6 +797,13 @@ static struct stm32_dev_s g_adcpriv1 =
#ifdef ADC1_HAVE_JEXTCFG
.jextcfg = ADC1_JEXTCFG_VALUE,
#endif
+#ifdef HAVE_IP_ADC_V2
+# ifdef BOARD_ADC1_DIFSEL
+ .difsel = BOARD_ADC1_DIFSEL,
+# else
+ .difsel = ADC_DIFSEL_DEFAULT,
+# endif
+#endif
#ifdef ADC1_HAVE_TIMER
.trigger = CONFIG_STM32_ADC1_TIMTRIG,
.tbase = ADC1_TIMER_BASE,
@@ -855,6 +872,13 @@ static struct stm32_dev_s g_adcpriv2 =
#ifdef ADC2_HAVE_JEXTCFG
.jextcfg = ADC2_JEXTCFG_VALUE,
#endif
+#ifdef HAVE_IP_ADC_V2
+# ifdef BOARD_ADC2_DIFSEL
+ .difsel = BOARD_ADC2_DIFSEL,
+# else
+ .difsel = ADC_DIFSEL_DEFAULT,
+# endif
+#endif
#ifdef ADC2_HAVE_TIMER
.trigger = CONFIG_STM32_ADC2_TIMTRIG,
.tbase = ADC2_TIMER_BASE,
@@ -923,6 +947,13 @@ static struct stm32_dev_s g_adcpriv3 =
#ifdef ADC3_HAVE_JEXTCFG
.jextcfg = ADC3_JEXTCFG_VALUE,
#endif
+#ifdef HAVE_IP_ADC_V2
+# ifdef BOARD_ADC3_DIFSEL
+ .difsel = BOARD_ADC3_DIFSEL,
+# else
+ .difsel = ADC_DIFSEL_DEFAULT,
+# endif
+#endif
#ifdef ADC3_HAVE_TIMER
.trigger = CONFIG_STM32_ADC3_TIMTRIG,
.tbase = ADC3_TIMER_BASE,
@@ -984,6 +1015,13 @@ static struct stm32_dev_s g_adcpriv4 =
#ifdef ADC4_HAVE_JEXTCFG
.jextcfg = ADC4_JEXTCFG_VALUE,
#endif
+#ifdef HAVE_IP_ADC_V2
+# ifdef BOARD_ADC4_DIFSEL
+ .difsel = BOARD_ADC4_DIFSEL,
+# else
+ .difsel = ADC_DIFSEL_DEFAULT,
+# endif
+#endif
#ifdef ADC4_HAVE_TIMER
.trigger = CONFIG_STM32_ADC4_TIMTRIG,
.tbase = ADC4_TIMER_BASE,
@@ -1255,6 +1293,7 @@ static void tim_modifyreg32(struct stm32_dev_s *priv, int
offset,
uint32_t clrbits, uint32_t setbits)
{
uint32_t addr = priv->tbase + offset;
+
putreg32((getreg32(addr) & ~clrbits) | setbits, addr);
}
#endif
@@ -2387,18 +2426,29 @@ static void adc_watchdog_cfg(struct stm32_dev_s *priv)
#if defined(HAVE_IP_ADC_V2)
static void adc_calibrate(struct stm32_dev_s *priv)
{
-#if 0 /* Doesn't work */
- /* Calibrate the ADC */
+ /* Calibrate single-ended channels if necessary */
- adc_modifyreg(priv, STM32_ADC_CR_OFFSET, ADC_CR_ADCALDIF, AD_CR_ADCAL);
+ if (priv->difsel != ADC_DIFSEL_ALL_DIFF)
+ {
+ adc_modifyreg(priv, STM32_ADC_CR_OFFSET, ADC_CR_ADCALDIF, 0);
+ adc_modifyreg(priv, STM32_ADC_CR_OFFSET, 0, ADC_CR_ADCAL);
- /* Wait for the calibration to complete */
+ /* Wait for the single-ended calibration to complete */
- while ((adc_getreg(priv, STM32_ADC_CR_OFFSET) & ADC_CR_ADCAL) != 0);
+ while ((adc_getreg(priv, STM32_ADC_CR_OFFSET) & ADC_CR_ADCAL) != 0);
+ }
-#else
- UNUSED(priv);
-#endif
+ /* Calibrate differential channels if necessary */
+
+ if (priv->difsel != ADC_DIFSEL_ALL_SINGLE)
+ {
+ adc_modifyreg(priv, STM32_ADC_CR_OFFSET, 0, ADC_CR_ADCALDIF);
+ adc_modifyreg(priv, STM32_ADC_CR_OFFSET, 0, ADC_CR_ADCAL);
+
+ /* Wait for the differential calibration to complete */
+
+ while ((adc_getreg(priv, STM32_ADC_CR_OFFSET) & ADC_CR_ADCAL) != 0);
+ }
}
#elif defined(HAVE_IP_ADC_V1) && defined(HAVE_BASIC_ADC)
static void adc_calibrate(struct stm32_dev_s *priv)
@@ -2746,7 +2796,7 @@ static void adc_configure(struct adc_dev_s *dev)
adc_voltreg_cfg(priv);
- /* Calibrate ADC - doesn't work for now */
+ /* Calibrate ADC */
adc_calibrate(priv);
@@ -2809,6 +2859,12 @@ static void adc_configure(struct adc_dev_s *dev)
adc_extcfg_set(priv, priv->extcfg);
#endif
+#ifdef HAVE_IP_ADC_V2
+ /* Configure differential mode */
+
+ adc_putreg(priv, STM32_ADC_DIFSEL_OFFSET, priv->difsel);
+#endif
+
/* Enable ADC */
adc_enable(priv, true);
@@ -2922,8 +2978,8 @@ out:
#ifdef HAVE_HSI_CONTROL
static void adc_reset_hsi_disable(struct adc_dev_s *dev)
{
- adc_reset(dev);
- adc_shutdown(dev);
+ adc_reset(dev);
+ adc_shutdown(dev);
}
#endif
@@ -4656,8 +4712,8 @@ static void adc_llops_dumpregs(struct stm32_adc_dev_s
*dev)
*
****************************************************************************/
-static int adc_llops_multicfg(struct stm32_adc_dev_s *dev, uint8_t mode)
#if defined(HAVE_IP_ADC_V2)
+static int adc_llops_multicfg(struct stm32_adc_dev_s *dev, uint8_t mode)
{
struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
int ret = OK;
@@ -4710,6 +4766,7 @@ errout:
return ret;
}
#elif defined(HAVE_IP_ADC_V1) && !defined(HAVE_BASIC_ADC)
+static int adc_llops_multicfg(struct stm32_adc_dev_s *dev, uint8_t mode)
{
struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
int ret = OK;
@@ -4784,6 +4841,7 @@ errout:
return ret;
}
#else /* ADV IPv1 BASIC */
+static int adc_llops_multicfg(struct stm32_adc_dev_s *dev, uint8_t mode)
{
if (mode != ADC_MULTIMODE_INDEP)
{