Skip to content

Commit 56621fa

Browse files
mrodgers-witekiogautierg-st
authored andcommitted
drivers: adc: stm32: add support for differential mode
Differential mode support consists of: - If differential mode is supported by the underlying hardware AND at least one differential channel is enabled in the devicetree for this ADC instance, then perform a differential mode calibration in addition to the usual single ended calibration during initialisation. - Set channels to the appropriate differential or single ended mode during channel setup. Currently the N6 series is not supported even though the underlying hardware supports differential mode, due to complications in the calibration procedure. Signed-off-by: Matt Rodgers <[email protected]> Co-authored-by: Guillaume Gautier <[email protected]>
1 parent 7ed3c4f commit 56621fa

File tree

1 file changed

+74
-7
lines changed

1 file changed

+74
-7
lines changed

drivers/adc/adc_stm32.c

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,17 @@ LOG_MODULE_REGISTER(adc_stm32);
109109
st_adc_oversampler,\
110110
value) 0)
111111

112+
#define ANY_CHILD_NODE_IS_DIFFERENTIAL(inst) \
113+
(DT_INST_FOREACH_CHILD_VARGS(inst, IS_EQ_NODE_PROP_OR, \
114+
zephyr_differential, \
115+
0, 1) 0)
116+
112117
#define IS_EQ_PROP_OR(inst, prop, default_value, compare_value) \
113118
IS_EQ(DT_INST_PROP_OR(inst, prop, default_value), compare_value) ||
114119

120+
#define IS_EQ_NODE_PROP_OR(node, prop, default_value, compare_value) \
121+
IS_EQ(DT_PROP_OR(node, prop, default_value), compare_value) ||
122+
115123
#define IS_EQ_STRING_PROP(inst, prop, compare_value) \
116124
IS_EQ(DT_INST_STRING_UPPER_TOKEN(inst, prop), compare_value) ||
117125

@@ -187,6 +195,7 @@ struct adc_stm32_cfg {
187195
size_t pclk_len;
188196
uint32_t clk_prescaler;
189197
const struct pinctrl_dev_config *pcfg;
198+
bool differential_channels_used;
190199
const uint16_t sampling_time_table[STM32_NB_SAMPLING_TIME];
191200
int8_t num_sampling_time_common_channels;
192201
int8_t sequencer_type;
@@ -484,11 +493,16 @@ static void adc_stm32_calibration_measure(ADC_TypeDef *adc, uint32_t *calibratio
484493
}
485494
#endif
486495

487-
static void adc_stm32_calibration_start(const struct device *dev)
496+
static void adc_stm32_calibration_start(const struct device *dev, bool single_ended)
488497
{
489498
const struct adc_stm32_cfg *config =
490499
(const struct adc_stm32_cfg *)dev->config;
491500
ADC_TypeDef *adc = config->base;
501+
#ifdef LL_ADC_SINGLE_ENDED
502+
uint32_t calib_type = single_ended ? LL_ADC_SINGLE_ENDED : LL_ADC_DIFFERENTIAL_ENDED;
503+
#else
504+
ARG_UNUSED(single_ended);
505+
#endif
492506

493507
#if defined(STM32F3XX_ADC) || \
494508
defined(CONFIG_SOC_SERIES_STM32L4X) || \
@@ -497,7 +511,7 @@ static void adc_stm32_calibration_start(const struct device *dev)
497511
defined(CONFIG_SOC_SERIES_STM32H7RSX) || \
498512
defined(CONFIG_SOC_SERIES_STM32WBX) || \
499513
defined(CONFIG_SOC_SERIES_STM32G4X)
500-
LL_ADC_StartCalibration(adc, LL_ADC_SINGLE_ENDED);
514+
LL_ADC_StartCalibration(adc, calib_type);
501515
#elif defined(CONFIG_SOC_SERIES_STM32C0X) || \
502516
defined(CONFIG_SOC_SERIES_STM32F0X) || \
503517
DT_HAS_COMPAT_STATUS_OKAY(st_stm32f1_adc) || \
@@ -509,6 +523,7 @@ static void adc_stm32_calibration_start(const struct device *dev)
509523

510524
LL_ADC_StartCalibration(adc);
511525
#elif defined(CONFIG_SOC_SERIES_STM32U5X)
526+
ARG_UNUSED(calib_type);
512527
if (adc != ADC4) {
513528
uint32_t dev_id = LL_DBGMCU_GetDeviceID();
514529
uint32_t rev_id = LL_DBGMCU_GetRevisionID();
@@ -531,9 +546,11 @@ static void adc_stm32_calibration_start(const struct device *dev)
531546
}
532547
LL_ADC_StartCalibration(adc, LL_ADC_CALIB_OFFSET);
533548
#elif defined(CONFIG_SOC_SERIES_STM32H7X)
534-
LL_ADC_StartCalibration(adc, LL_ADC_CALIB_OFFSET, LL_ADC_SINGLE_ENDED);
549+
LL_ADC_StartCalibration(adc, LL_ADC_CALIB_OFFSET, calib_type);
535550
#elif defined(CONFIG_SOC_SERIES_STM32N6X)
536551
uint32_t calibration_factor;
552+
553+
ARG_UNUSED(calib_type);
537554
/* Start ADC calibration */
538555
LL_ADC_StartCalibration(adc, LL_ADC_SINGLE_ENDED);
539556
/* Disable additional offset before calibration start */
@@ -580,7 +597,10 @@ static int adc_stm32_calibrate(const struct device *dev)
580597
#if !DT_HAS_COMPAT_STATUS_OKAY(st_stm32f1_adc) && \
581598
!defined(CONFIG_SOC_SERIES_STM32N6X)
582599
adc_stm32_disable(adc);
583-
adc_stm32_calibration_start(dev);
600+
adc_stm32_calibration_start(dev, true);
601+
if (config->differential_channels_used) {
602+
adc_stm32_calibration_start(dev, false);
603+
}
584604
adc_stm32_calibration_delay(dev);
585605
#endif /* !DT_HAS_COMPAT_STATUS_OKAY(st_stm32f1_adc) */
586606

@@ -592,7 +612,7 @@ static int adc_stm32_calibrate(const struct device *dev)
592612
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32f1_adc) || \
593613
defined(CONFIG_SOC_SERIES_STM32N6X)
594614
adc_stm32_calibration_delay(dev);
595-
adc_stm32_calibration_start(dev);
615+
adc_stm32_calibration_start(dev, true);
596616
#endif /* DT_HAS_COMPAT_STATUS_OKAY(st_stm32f1_adc) */
597617

598618
#if defined(CONFIG_SOC_SERIES_STM32H7X) && \
@@ -1287,18 +1307,64 @@ static int adc_stm32_sampling_time_setup(const struct device *dev, uint8_t id,
12871307
return 0;
12881308
}
12891309

1310+
#if defined(STM32F3XX_ADC) || \
1311+
defined(CONFIG_SOC_SERIES_STM32G4X) || \
1312+
defined(CONFIG_SOC_SERIES_STM32H5X) || \
1313+
defined(CONFIG_SOC_SERIES_STM32H7X) || \
1314+
defined(CONFIG_SOC_SERIES_STM32H7RSX) || \
1315+
defined(CONFIG_SOC_SERIES_STM32L4X) || \
1316+
defined(CONFIG_SOC_SERIES_STM32L5X) || \
1317+
defined(CONFIG_SOC_SERIES_STM32U5X) || \
1318+
defined(CONFIG_SOC_SERIES_STM32WBX)
1319+
#define DIFFERENTIAL_MODE_SUPPORTED 1
1320+
#else
1321+
#define DIFFERENTIAL_MODE_SUPPORTED 0
1322+
#endif
1323+
1324+
#if DIFFERENTIAL_MODE_SUPPORTED
1325+
static void set_channel_differential_mode(ADC_TypeDef *adc, uint8_t channel_id, bool differential)
1326+
{
1327+
const uint32_t mode = differential ? LL_ADC_DIFFERENTIAL_ENDED : LL_ADC_SINGLE_ENDED;
1328+
const uint32_t channel = __LL_ADC_DECIMAL_NB_TO_CHANNEL(channel_id);
1329+
1330+
/* The ADC must be disabled to change the single ended / differential mode setting. The
1331+
* disable / re-enable cycle can take some time, so avoid doing this if the channel is
1332+
* already set to the correct mode.
1333+
*/
1334+
if (LL_ADC_GetChannelSingleDiff(adc, channel) == mode) {
1335+
return;
1336+
}
1337+
1338+
adc_stm32_disable(adc);
1339+
LL_ADC_SetChannelSingleDiff(adc, channel, mode);
1340+
adc_stm32_enable(adc);
1341+
}
1342+
#endif
1343+
12901344
static int adc_stm32_channel_setup(const struct device *dev,
12911345
const struct adc_channel_cfg *channel_cfg)
12921346
{
1293-
#ifdef CONFIG_SOC_SERIES_STM32H5X
1347+
#if defined(CONFIG_SOC_SERIES_STM32H5X) || DIFFERENTIAL_MODE_SUPPORTED
12941348
const struct adc_stm32_cfg *config = (const struct adc_stm32_cfg *)dev->config;
12951349
ADC_TypeDef *adc = config->base;
12961350
#endif
12971351

1352+
#if !DIFFERENTIAL_MODE_SUPPORTED
12981353
if (channel_cfg->differential) {
1299-
LOG_ERR("Differential channels are not supported");
1354+
LOG_ERR("Differential channels not supported on this SOC series");
13001355
return -EINVAL;
13011356
}
1357+
#else
1358+
if (channel_cfg->differential && !config->differential_channels_used) {
1359+
/* At least one channel must be set to differential mode in the devicetree
1360+
* to cause a differential calibration to be performed during init.
1361+
*/
1362+
LOG_ERR("Differential calibration not done, cannot use differential mode");
1363+
return -EINVAL;
1364+
}
1365+
1366+
set_channel_differential_mode(adc, channel_cfg->channel_id, channel_cfg->differential);
1367+
#endif
13021368

13031369
if (channel_cfg->gain != ADC_GAIN_1) {
13041370
LOG_ERR("Invalid channel gain");
@@ -1918,6 +1984,7 @@ static const struct adc_stm32_cfg adc_stm32_cfg_##index = { \
19181984
.pclk_len = DT_INST_NUM_CLOCKS(index), \
19191985
.clk_prescaler = ADC_STM32_DT_PRESC(index), \
19201986
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(index), \
1987+
.differential_channels_used = (ANY_CHILD_NODE_IS_DIFFERENTIAL(index) > 0), \
19211988
.sequencer_type = DT_INST_STRING_UPPER_TOKEN(index, st_adc_sequencer), \
19221989
.oversampler_type = DT_INST_STRING_UPPER_TOKEN(index, st_adc_oversampler), \
19231990
.sampling_time_table = DT_INST_PROP(index, sampling_times), \

0 commit comments

Comments
 (0)