drivers: adc: rework stm32 adc sequencer
Add sequencer support for all STM32 series. Signed-off-by: Guillaume Gautier <guillaume.gautier-ext@st.com>
This commit is contained in:
parent
6d6d7b5607
commit
c201d21964
1 changed files with 136 additions and 93 deletions
|
@ -68,18 +68,18 @@ LOG_MODULE_REGISTER(adc_stm32);
|
||||||
num_sampling_time_common_channels,\
|
num_sampling_time_common_channels,\
|
||||||
0, value) 0)
|
0, value) 0)
|
||||||
|
|
||||||
|
#define ANY_ADC_SEQUENCER_TYPE_IS(value) \
|
||||||
|
(DT_INST_FOREACH_STATUS_OKAY_VARGS(IS_EQ_PROP_OR, \
|
||||||
|
st_adc_sequencer,\
|
||||||
|
0, value) 0)
|
||||||
|
|
||||||
#define IS_EQ_PROP_OR(inst, prop, default_value, compare_value) \
|
#define IS_EQ_PROP_OR(inst, prop, default_value, compare_value) \
|
||||||
IS_EQ(DT_INST_PROP_OR(inst, prop, default_value), compare_value) ||
|
IS_EQ(DT_INST_PROP_OR(inst, prop, default_value), compare_value) ||
|
||||||
|
|
||||||
/* reference voltage for the ADC */
|
/* reference voltage for the ADC */
|
||||||
#define STM32_ADC_VREF_MV DT_INST_PROP(0, vref_mv)
|
#define STM32_ADC_VREF_MV DT_INST_PROP(0, vref_mv)
|
||||||
|
|
||||||
#if !defined(CONFIG_SOC_SERIES_STM32C0X) && \
|
#if ANY_ADC_SEQUENCER_TYPE_IS(FULLY_CONFIGURABLE)
|
||||||
!defined(CONFIG_SOC_SERIES_STM32F0X) && \
|
|
||||||
!defined(CONFIG_SOC_SERIES_STM32G0X) && \
|
|
||||||
!defined(CONFIG_SOC_SERIES_STM32L0X) && \
|
|
||||||
!defined(CONFIG_SOC_SERIES_STM32WBAX) && \
|
|
||||||
!defined(CONFIG_SOC_SERIES_STM32WLX)
|
|
||||||
#define RANK(n) LL_ADC_REG_RANK_##n
|
#define RANK(n) LL_ADC_REG_RANK_##n
|
||||||
static const uint32_t table_rank[] = {
|
static const uint32_t table_rank[] = {
|
||||||
RANK(1),
|
RANK(1),
|
||||||
|
@ -98,6 +98,22 @@ static const uint32_t table_rank[] = {
|
||||||
RANK(14),
|
RANK(14),
|
||||||
RANK(15),
|
RANK(15),
|
||||||
RANK(16),
|
RANK(16),
|
||||||
|
#if defined(LL_ADC_REG_RANK_17)
|
||||||
|
RANK(17),
|
||||||
|
RANK(18),
|
||||||
|
RANK(19),
|
||||||
|
RANK(20),
|
||||||
|
RANK(21),
|
||||||
|
RANK(22),
|
||||||
|
RANK(23),
|
||||||
|
RANK(24),
|
||||||
|
RANK(25),
|
||||||
|
RANK(26),
|
||||||
|
RANK(27),
|
||||||
|
#if defined(LL_ADC_REG_RANK_28)
|
||||||
|
RANK(28),
|
||||||
|
#endif /* LL_ADC_REG_RANK_28 */
|
||||||
|
#endif /* LL_ADC_REG_RANK_17 */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SEQ_LEN(n) LL_ADC_REG_SEQ_SCAN_ENABLE_##n##RANKS
|
#define SEQ_LEN(n) LL_ADC_REG_SEQ_SCAN_ENABLE_##n##RANKS
|
||||||
|
@ -119,8 +135,24 @@ static const uint32_t table_seq_len[] = {
|
||||||
SEQ_LEN(14),
|
SEQ_LEN(14),
|
||||||
SEQ_LEN(15),
|
SEQ_LEN(15),
|
||||||
SEQ_LEN(16),
|
SEQ_LEN(16),
|
||||||
|
#if defined(LL_ADC_REG_SEQ_SCAN_ENABLE_17RANKS)
|
||||||
|
SEQ_LEN(17),
|
||||||
|
SEQ_LEN(18),
|
||||||
|
SEQ_LEN(19),
|
||||||
|
SEQ_LEN(20),
|
||||||
|
SEQ_LEN(21),
|
||||||
|
SEQ_LEN(22),
|
||||||
|
SEQ_LEN(23),
|
||||||
|
SEQ_LEN(24),
|
||||||
|
SEQ_LEN(25),
|
||||||
|
SEQ_LEN(26),
|
||||||
|
SEQ_LEN(27),
|
||||||
|
#if defined(LL_ADC_REG_SEQ_SCAN_ENABLE_28RANKS)
|
||||||
|
SEQ_LEN(28),
|
||||||
|
#endif /* LL_ADC_REG_SEQ_SCAN_ENABLE_28RANKS */
|
||||||
|
#endif /* LL_ADC_REG_SEQ_SCAN_ENABLE_17RANKS */
|
||||||
};
|
};
|
||||||
#endif
|
#endif /* ANY_ADC_SEQUENCER_TYPE_IS(FULLY_CONFIGURABLE) */
|
||||||
|
|
||||||
/* External channels (maximum). */
|
/* External channels (maximum). */
|
||||||
#define STM32_CHANNEL_COUNT 20
|
#define STM32_CHANNEL_COUNT 20
|
||||||
|
@ -167,6 +199,7 @@ struct adc_stm32_cfg {
|
||||||
const struct pinctrl_dev_config *pcfg;
|
const struct pinctrl_dev_config *pcfg;
|
||||||
const uint16_t sampling_time_table[STM32_NB_SAMPLING_TIME];
|
const uint16_t sampling_time_table[STM32_NB_SAMPLING_TIME];
|
||||||
int8_t num_sampling_time_common_channels;
|
int8_t num_sampling_time_common_channels;
|
||||||
|
int8_t sequencer_type;
|
||||||
int8_t res_table_size;
|
int8_t res_table_size;
|
||||||
const uint32_t res_table[];
|
const uint32_t res_table[];
|
||||||
};
|
};
|
||||||
|
@ -724,6 +757,70 @@ static int set_resolution(const struct device *dev,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int set_sequencer(const struct device *dev)
|
||||||
|
{
|
||||||
|
const struct adc_stm32_cfg *config = dev->config;
|
||||||
|
struct adc_stm32_data *data = dev->data;
|
||||||
|
ADC_TypeDef *adc = (ADC_TypeDef *)config->base;
|
||||||
|
|
||||||
|
uint8_t channel_id;
|
||||||
|
uint8_t channel_index = 0;
|
||||||
|
uint32_t channels_mask = 0;
|
||||||
|
|
||||||
|
/* Iterate over selected channels in bitmask keeping track of:
|
||||||
|
* - channel_index: ranging from 0 -> ( data->channel_count - 1 )
|
||||||
|
* - channel_id: ordinal position of channel in data->channels bitmask
|
||||||
|
*/
|
||||||
|
for (uint32_t channels = data->channels; channels;
|
||||||
|
channels &= ~BIT(channel_id), channel_index++) {
|
||||||
|
channel_id = find_lsb_set(channels) - 1;
|
||||||
|
|
||||||
|
uint32_t channel = __LL_ADC_DECIMAL_NB_TO_CHANNEL(channel_id);
|
||||||
|
|
||||||
|
channels_mask |= channel;
|
||||||
|
|
||||||
|
#if ANY_ADC_SEQUENCER_TYPE_IS(FULLY_CONFIGURABLE)
|
||||||
|
if (config->sequencer_type == FULLY_CONFIGURABLE) {
|
||||||
|
#if defined(CONFIG_SOC_SERIES_STM32H7X) || defined(CONFIG_SOC_SERIES_STM32U5X)
|
||||||
|
/*
|
||||||
|
* Each channel in the sequence must be previously enabled in PCSEL.
|
||||||
|
* This register controls the analog switch integrated in the IO level.
|
||||||
|
*/
|
||||||
|
LL_ADC_SetChannelPreselection(adc, channel);
|
||||||
|
#endif /* CONFIG_SOC_SERIES_STM32H7X || CONFIG_SOC_SERIES_STM32U5X */
|
||||||
|
LL_ADC_REG_SetSequencerRanks(adc, table_rank[channel_index], channel);
|
||||||
|
LL_ADC_REG_SetSequencerLength(adc, table_seq_len[channel_index]);
|
||||||
|
}
|
||||||
|
#endif /* ANY_ADC_SEQUENCER_TYPE_IS(FULLY_CONFIGURABLE) */
|
||||||
|
}
|
||||||
|
|
||||||
|
#if ANY_ADC_SEQUENCER_TYPE_IS(NOT_FULLY_CONFIGURABLE)
|
||||||
|
if (config->sequencer_type == NOT_FULLY_CONFIGURABLE) {
|
||||||
|
LL_ADC_REG_SetSequencerChannels(adc, channels_mask);
|
||||||
|
|
||||||
|
#if !defined(CONFIG_SOC_SERIES_STM32F0X) && \
|
||||||
|
!defined(CONFIG_SOC_SERIES_STM32L0X) && \
|
||||||
|
!defined(CONFIG_SOC_SERIES_STM32U5X) && \
|
||||||
|
!defined(CONFIG_SOC_SERIES_STM32WBAX)
|
||||||
|
/*
|
||||||
|
* After modifying sequencer it is mandatory to wait for the
|
||||||
|
* assertion of CCRDY flag
|
||||||
|
*/
|
||||||
|
while (LL_ADC_IsActiveFlag_CCRDY(adc) == 0) {
|
||||||
|
}
|
||||||
|
LL_ADC_ClearFlag_CCRDY(adc);
|
||||||
|
#endif /* !CONFIG_SOC_SERIES_STM32F0X && !L0X && !U5X && !WBAX */
|
||||||
|
}
|
||||||
|
#endif /* ANY_ADC_SEQUENCER_TYPE_IS(NOT_FULLY_CONFIGURABLE) */
|
||||||
|
|
||||||
|
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32f1_adc) || \
|
||||||
|
DT_HAS_COMPAT_STATUS_OKAY(st_stm32f4_adc)
|
||||||
|
LL_ADC_SetSequencersScanMode(adc, LL_ADC_SEQ_SCAN_ENABLE);
|
||||||
|
#endif /* st_stm32f1_adc || st_stm32f4_adc */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int start_read(const struct device *dev,
|
static int start_read(const struct device *dev,
|
||||||
const struct adc_sequence *sequence)
|
const struct adc_sequence *sequence)
|
||||||
{
|
{
|
||||||
|
@ -747,22 +844,20 @@ static int start_read(const struct device *dev,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(CONFIG_SOC_SERIES_STM32C0X) && \
|
#if ANY_ADC_SEQUENCER_TYPE_IS(FULLY_CONFIGURABLE)
|
||||||
!defined(CONFIG_SOC_SERIES_STM32F0X) && \
|
|
||||||
!defined(CONFIG_SOC_SERIES_STM32G0X) && \
|
|
||||||
!defined(CONFIG_SOC_SERIES_STM32L0X) && \
|
|
||||||
!defined(CONFIG_SOC_SERIES_STM32WBAX) && \
|
|
||||||
!defined(CONFIG_SOC_SERIES_STM32WLX)
|
|
||||||
if (data->channel_count > ARRAY_SIZE(table_seq_len)) {
|
if (data->channel_count > ARRAY_SIZE(table_seq_len)) {
|
||||||
LOG_ERR("Too many channels for sequencer. Max: %d", ARRAY_SIZE(table_seq_len));
|
LOG_ERR("Too many channels for sequencer. Max: %d", ARRAY_SIZE(table_seq_len));
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
#else
|
#endif /* ANY_ADC_SEQUENCER_TYPE_IS(FULLY_CONFIGURABLE) */
|
||||||
|
|
||||||
|
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32f1_adc) && !defined(CONFIG_ADC_STM32_DMA)
|
||||||
|
/* Multiple samplings is only supported with DMA for F1 */
|
||||||
if (data->channel_count > 1) {
|
if (data->channel_count > 1) {
|
||||||
LOG_ERR("This device only supports single channel sampling");
|
LOG_ERR("Without DMA, this device only supports single channel sampling");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
#endif
|
#endif /* DT_HAS_COMPAT_STATUS_OKAY(st_stm32f1_adc) && !CONFIG_ADC_STM32_DMA */
|
||||||
|
|
||||||
/* Check and set the resolution */
|
/* Check and set the resolution */
|
||||||
err = set_resolution(dev, sequence);
|
err = set_resolution(dev, sequence);
|
||||||
|
@ -770,77 +865,10 @@ static int start_read(const struct device *dev,
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t channel_id;
|
/* Configure the sequencer */
|
||||||
uint8_t channel_index = 0;
|
err = set_sequencer(dev);
|
||||||
|
if (err < 0) {
|
||||||
/* Iterate over selected channels in bitmask keeping track of:
|
return err;
|
||||||
* - channel_index: ranging from 0 -> ( data->channel_count - 1 )
|
|
||||||
* - channel_id: ordinal position of channel in data->channels bitmask
|
|
||||||
*/
|
|
||||||
for (uint32_t channels = data->channels; channels;
|
|
||||||
channels &= ~BIT(channel_id), channel_index++) {
|
|
||||||
channel_id = find_lsb_set(channels) - 1;
|
|
||||||
|
|
||||||
uint32_t channel = __LL_ADC_DECIMAL_NB_TO_CHANNEL(channel_id);
|
|
||||||
|
|
||||||
#if defined(CONFIG_SOC_SERIES_STM32H7X)
|
|
||||||
/*
|
|
||||||
* Each channel in the sequence must be previously enabled in PCSEL.
|
|
||||||
* This register controls the analog switch integrated in the IO level.
|
|
||||||
*/
|
|
||||||
LL_ADC_SetChannelPreSelection(adc, channel);
|
|
||||||
#elif defined(CONFIG_SOC_SERIES_STM32U5X)
|
|
||||||
/*
|
|
||||||
* Each channel in the sequence must be previously enabled in PCSEL.
|
|
||||||
* This register controls the analog switch integrated in the IO level.
|
|
||||||
* Only for ADC1 instance (ADC4 has no Channel preselection capability).
|
|
||||||
*/
|
|
||||||
if (adc == ADC1) {
|
|
||||||
LL_ADC_SetChannelPreselection(adc, channel);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(CONFIG_SOC_SERIES_STM32F0X) || \
|
|
||||||
defined(CONFIG_SOC_SERIES_STM32L0X)
|
|
||||||
LL_ADC_REG_SetSequencerChannels(adc, channel);
|
|
||||||
#elif defined(CONFIG_SOC_SERIES_STM32WLX)
|
|
||||||
/* Init the the ADC group for REGULAR conversion*/
|
|
||||||
LL_ADC_REG_SetSequencerConfigurable(adc, LL_ADC_REG_SEQ_CONFIGURABLE);
|
|
||||||
LL_ADC_REG_SetTriggerSource(adc, LL_ADC_REG_TRIG_SOFTWARE);
|
|
||||||
LL_ADC_REG_SetSequencerLength(adc, LL_ADC_REG_SEQ_SCAN_DISABLE);
|
|
||||||
LL_ADC_REG_SetOverrun(adc, LL_ADC_REG_OVR_DATA_OVERWRITTEN);
|
|
||||||
LL_ADC_REG_SetSequencerRanks(adc, LL_ADC_REG_RANK_1, channel);
|
|
||||||
LL_ADC_REG_SetSequencerChannels(adc, channel);
|
|
||||||
/* Wait for config complete config is ready */
|
|
||||||
while (LL_ADC_IsActiveFlag_CCRDY(adc) == 0) {
|
|
||||||
}
|
|
||||||
LL_ADC_ClearFlag_CCRDY(adc);
|
|
||||||
#elif defined(CONFIG_SOC_SERIES_STM32C0X) || defined(CONFIG_SOC_SERIES_STM32G0X)
|
|
||||||
/* C0 and G0 in "not fully configurable" sequencer mode */
|
|
||||||
LL_ADC_REG_SetSequencerChannels(adc, channel);
|
|
||||||
LL_ADC_REG_SetSequencerConfigurable(adc, LL_ADC_REG_SEQ_FIXED);
|
|
||||||
while (LL_ADC_IsActiveFlag_CCRDY(adc) == 0) {
|
|
||||||
}
|
|
||||||
LL_ADC_ClearFlag_CCRDY(adc);
|
|
||||||
#elif defined(CONFIG_SOC_SERIES_STM32WBAX)
|
|
||||||
LL_ADC_REG_StopConversion(adc);
|
|
||||||
while (LL_ADC_REG_IsStopConversionOngoing(adc) != 0) {
|
|
||||||
}
|
|
||||||
LL_ADC_REG_SetSequencerChannels(adc, channel);
|
|
||||||
LL_ADC_REG_SetSequencerConfigurable(adc, LL_ADC_REG_SEQ_FIXED);
|
|
||||||
#elif defined(CONFIG_SOC_SERIES_STM32U5X)
|
|
||||||
if (adc != ADC4) {
|
|
||||||
LL_ADC_REG_SetSequencerRanks(adc, table_rank[channel_index], channel);
|
|
||||||
LL_ADC_REG_SetSequencerLength(adc, table_seq_len[channel_index]);
|
|
||||||
} else {
|
|
||||||
LL_ADC_REG_SetSequencerConfigurable(adc, LL_ADC_REG_SEQ_FIXED);
|
|
||||||
LL_ADC_REG_SetSequencerLength(adc,
|
|
||||||
BIT(__LL_ADC_CHANNEL_TO_DECIMAL_NB(channel)));
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
LL_ADC_REG_SetSequencerRanks(adc, table_rank[channel_index], channel);
|
|
||||||
LL_ADC_REG_SetSequencerLength(adc, table_seq_len[channel_index]);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = check_buffer(sequence, data->channel_count);
|
err = check_buffer(sequence, data->channel_count);
|
||||||
|
@ -886,6 +914,8 @@ static int start_read(const struct device *dev,
|
||||||
|
|
||||||
#if !defined(CONFIG_ADC_STM32_DMA)
|
#if !defined(CONFIG_ADC_STM32_DMA)
|
||||||
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32f4_adc)
|
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32f4_adc)
|
||||||
|
/* Trigger an ISR after each sampling (not just end of sequence) */
|
||||||
|
LL_ADC_REG_SetFlagEndOfConversion(adc, LL_ADC_REG_FLAG_EOC_UNITARY_CONV);
|
||||||
LL_ADC_EnableIT_EOCS(adc);
|
LL_ADC_EnableIT_EOCS(adc);
|
||||||
#elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32f1_adc)
|
#elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32f1_adc)
|
||||||
LL_ADC_EnableIT_EOS(adc);
|
LL_ADC_EnableIT_EOS(adc);
|
||||||
|
@ -939,12 +969,19 @@ static void adc_stm32_isr(const struct device *dev)
|
||||||
(const struct adc_stm32_cfg *)dev->config;
|
(const struct adc_stm32_cfg *)dev->config;
|
||||||
ADC_TypeDef *adc = config->base;
|
ADC_TypeDef *adc = config->base;
|
||||||
|
|
||||||
*data->buffer++ = LL_ADC_REG_ReadConversionData32(adc);
|
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32f1_adc)
|
||||||
|
if (LL_ADC_IsActiveFlag_EOS(adc) == 1) {
|
||||||
/* ISR is triggered after each conversion, and at the end-of-sequence. */
|
#elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32f4_adc)
|
||||||
if (++data->samples_count == data->channel_count) {
|
if (LL_ADC_IsActiveFlag_EOCS(adc) == 1) {
|
||||||
data->samples_count = 0;
|
#else
|
||||||
adc_context_on_sampling_done(&data->ctx, dev);
|
if (LL_ADC_IsActiveFlag_EOC(adc) == 1) {
|
||||||
|
#endif
|
||||||
|
*data->buffer++ = LL_ADC_REG_ReadConversionData32(adc);
|
||||||
|
/* ISR is triggered after each conversion, and at the end-of-sequence. */
|
||||||
|
if (++data->samples_count == data->channel_count) {
|
||||||
|
data->samples_count = 0;
|
||||||
|
adc_context_on_sampling_done(&data->ctx, dev);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DBG("%s ISR triggered.", dev->name);
|
LOG_DBG("%s ISR triggered.", dev->name);
|
||||||
|
@ -963,6 +1000,11 @@ static void adc_context_on_complete(struct adc_context *ctx, int status)
|
||||||
adc_stm32_disable(adc);
|
adc_stm32_disable(adc);
|
||||||
LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(adc),
|
LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(adc),
|
||||||
LL_ADC_PATH_INTERNAL_NONE);
|
LL_ADC_PATH_INTERNAL_NONE);
|
||||||
|
|
||||||
|
#if defined(CONFIG_SOC_SERIES_STM32H7X) || defined(CONFIG_SOC_SERIES_STM32U5X)
|
||||||
|
/* Reset channel preselection register */
|
||||||
|
LL_ADC_SetChannelPreselection(adc, 0);
|
||||||
|
#endif /* CONFIG_SOC_SERIES_STM32H7X || CONFIG_SOC_SERIES_STM32U5X */
|
||||||
}
|
}
|
||||||
|
|
||||||
static int adc_stm32_read(const struct device *dev,
|
static int adc_stm32_read(const struct device *dev,
|
||||||
|
@ -1450,6 +1492,7 @@ static const struct adc_stm32_cfg adc_stm32_cfg_##index = { \
|
||||||
.pclk_len = DT_INST_NUM_CLOCKS(index), \
|
.pclk_len = DT_INST_NUM_CLOCKS(index), \
|
||||||
.clk_prescaler = ADC_STM32_DT_PRESC(index), \
|
.clk_prescaler = ADC_STM32_DT_PRESC(index), \
|
||||||
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(index), \
|
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(index), \
|
||||||
|
.sequencer_type = DT_INST_PROP(index, st_adc_sequencer), \
|
||||||
.sampling_time_table = DT_INST_PROP(index, sampling_times), \
|
.sampling_time_table = DT_INST_PROP(index, sampling_times), \
|
||||||
.num_sampling_time_common_channels = \
|
.num_sampling_time_common_channels = \
|
||||||
DT_INST_PROP_OR(index, num_sampling_time_common_channels, 0),\
|
DT_INST_PROP_OR(index, num_sampling_time_common_channels, 0),\
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue