/* * Copyright (c) 2018 Kokoon Technology Limited * Copyright (c) 2019 Song Qiang * Copyright (c) 2019 Endre Karlson * Copyright (c) 2020 Teslabs Engineering S.L. * Copyright (c) 2021 Marius Scholtz, RIC Electronics * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT st_stm32_adc #include #include #include #include #include #include #include #include #if defined(CONFIG_SOC_SERIES_STM32U5X) #include #endif /* CONFIG_SOC_SERIES_STM32U5X */ #define ADC_CONTEXT_USES_KERNEL_TIMER #include "adc_context.h" #define LOG_LEVEL CONFIG_ADC_LOG_LEVEL #include LOG_MODULE_REGISTER(adc_stm32); #include #if defined(CONFIG_SOC_SERIES_STM32F3X) #if defined(ADC1_V2_5) #define STM32F3X_ADC_V2_5 #elif defined(ADC5_V1_1) #define STM32F3X_ADC_V1_1 #endif #endif /* reference voltage for the ADC */ #define STM32_ADC_VREF_MV DT_INST_PROP(0, vref_mv) #if !defined(CONFIG_SOC_SERIES_STM32F0X) && \ !defined(CONFIG_SOC_SERIES_STM32G0X) && \ !defined(CONFIG_SOC_SERIES_STM32L0X) && \ !defined(CONFIG_SOC_SERIES_STM32WLX) #define RANK(n) LL_ADC_REG_RANK_##n static const uint32_t table_rank[] = { RANK(1), RANK(2), RANK(3), RANK(4), RANK(5), RANK(6), RANK(7), RANK(8), RANK(9), RANK(10), RANK(11), RANK(12), RANK(13), RANK(14), RANK(15), RANK(16), }; #define SEQ_LEN(n) LL_ADC_REG_SEQ_SCAN_ENABLE_##n##RANKS static const uint32_t table_seq_len[] = { LL_ADC_REG_SEQ_SCAN_DISABLE, SEQ_LEN(2), SEQ_LEN(3), SEQ_LEN(4), SEQ_LEN(5), SEQ_LEN(6), SEQ_LEN(7), SEQ_LEN(8), SEQ_LEN(9), SEQ_LEN(10), SEQ_LEN(11), SEQ_LEN(12), SEQ_LEN(13), SEQ_LEN(14), SEQ_LEN(15), SEQ_LEN(16), }; #endif #define RES(n) LL_ADC_RESOLUTION_##n##B static const uint32_t table_resolution[] = { #if defined(CONFIG_SOC_SERIES_STM32F1X) || \ defined(STM32F3X_ADC_V2_5) RES(12), #elif !defined(CONFIG_SOC_SERIES_STM32H7X) RES(6), RES(8), RES(10), RES(12), #if defined(CONFIG_SOC_SERIES_STM32U5X) RES(14), #endif /* CONFIG_SOC_SERIES_STM32U5X */ #else RES(8), RES(10), RES(12), RES(14), RES(16), #endif }; #define SMP_TIME(x, y) LL_ADC_SAMPLINGTIME_##x##CYCLE##y /* * Conversion time in ADC cycles. Many values should have been 0.5 less, * but the adc api system currently does not support describing 'half cycles'. * So all half cycles are counted as one. */ #if defined(CONFIG_SOC_SERIES_STM32F0X) || defined(CONFIG_SOC_SERIES_STM32F1X) static const uint16_t acq_time_tbl[8] = {2, 8, 14, 29, 42, 56, 72, 240}; static const uint32_t table_samp_time[] = { SMP_TIME(1, _5), SMP_TIME(7, S_5), SMP_TIME(13, S_5), SMP_TIME(28, S_5), SMP_TIME(41, S_5), SMP_TIME(55, S_5), SMP_TIME(71, S_5), SMP_TIME(239, S_5), }; #elif defined(CONFIG_SOC_SERIES_STM32F2X) || \ defined(CONFIG_SOC_SERIES_STM32F4X) || \ defined(CONFIG_SOC_SERIES_STM32F7X) static const uint16_t acq_time_tbl[8] = {3, 15, 28, 56, 84, 112, 144, 480}; static const uint32_t table_samp_time[] = { SMP_TIME(3, S), SMP_TIME(15, S), SMP_TIME(28, S), SMP_TIME(56, S), SMP_TIME(84, S), SMP_TIME(112, S), SMP_TIME(144, S), SMP_TIME(480, S), }; #elif defined(CONFIG_SOC_SERIES_STM32F3X) #ifdef ADC5_V1_1 static const uint16_t acq_time_tbl[8] = {2, 3, 5, 8, 20, 62, 182, 602}; static const uint32_t table_samp_time[] = { SMP_TIME(1, _5), SMP_TIME(2, S_5), SMP_TIME(4, S_5), SMP_TIME(7, S_5), SMP_TIME(19, S_5), SMP_TIME(61, S_5), SMP_TIME(181, S_5), SMP_TIME(601, S_5), }; #else static const uint16_t acq_time_tbl[8] = {2, 8, 14, 29, 42, 56, 72, 240}; static const uint32_t table_samp_time[] = { SMP_TIME(1, _5), SMP_TIME(7, S_5), SMP_TIME(13, S_5), SMP_TIME(28, S_5), SMP_TIME(41, S_5), SMP_TIME(55, S_5), SMP_TIME(71, S_5), SMP_TIME(239, S_5), }; #endif /* ADC5_V1_1 */ #elif defined(CONFIG_SOC_SERIES_STM32L0X) || \ defined(CONFIG_SOC_SERIES_STM32G0X) || \ defined(CONFIG_SOC_SERIES_STM32WLX) static const uint16_t acq_time_tbl[8] = {2, 4, 8, 13, 20, 40, 80, 161}; static const uint32_t table_samp_time[] = { SMP_TIME(1, _5), SMP_TIME(3, S_5), SMP_TIME(7, S_5), SMP_TIME(12, S_5), SMP_TIME(19, S_5), SMP_TIME(39, S_5), SMP_TIME(79, S_5), SMP_TIME(160, S_5), }; #elif defined(CONFIG_SOC_SERIES_STM32L4X) || \ defined(CONFIG_SOC_SERIES_STM32L5X) || \ defined(CONFIG_SOC_SERIES_STM32WBX) || \ defined(CONFIG_SOC_SERIES_STM32G4X) static const uint16_t acq_time_tbl[8] = {3, 7, 13, 25, 48, 93, 248, 641}; static const uint32_t table_samp_time[] = { SMP_TIME(2, S_5), SMP_TIME(6, S_5), SMP_TIME(12, S_5), SMP_TIME(24, S_5), SMP_TIME(47, S_5), SMP_TIME(92, S_5), SMP_TIME(247, S_5), SMP_TIME(640, S_5), }; #elif defined(CONFIG_SOC_SERIES_STM32L1X) static const uint16_t acq_time_tbl[8] = {5, 10, 17, 25, 49, 97, 193, 385}; static const uint32_t table_samp_time[] = { SMP_TIME(4, S), SMP_TIME(9, S), SMP_TIME(16, S), SMP_TIME(24, S), SMP_TIME(48, S), SMP_TIME(96, S), SMP_TIME(192, S), SMP_TIME(384, S), }; #elif defined(CONFIG_SOC_SERIES_STM32H7X) static const uint16_t acq_time_tbl[8] = {2, 3, 9, 17, 33, 65, 388, 811}; static const uint32_t table_samp_time[] = { SMP_TIME(1, _5), SMP_TIME(2, S_5), SMP_TIME(8, S_5), SMP_TIME(16, S_5), SMP_TIME(32, S_5), SMP_TIME(64, S_5), SMP_TIME(387, S_5), SMP_TIME(810, S_5), }; #elif defined(CONFIG_SOC_SERIES_STM32U5X) static const uint16_t acq_time_tbl[8] = {5, 6, 12, 20, 36, 68, 391, 814}; static const uint32_t table_samp_time[] = { SMP_TIME(5,), SMP_TIME(6, S), SMP_TIME(12, S), SMP_TIME(20, S), SMP_TIME(36, S), SMP_TIME(68, S), SMP_TIME(391, S_5), SMP_TIME(814, S), }; #endif /* External channels (maximum). */ #define STM32_CHANNEL_COUNT 20 struct adc_stm32_data { struct adc_context ctx; const struct device *dev; uint16_t *buffer; uint16_t *repeat_buffer; uint8_t resolution; uint8_t channel_count; #if defined(CONFIG_SOC_SERIES_STM32F0X) || \ defined(CONFIG_SOC_SERIES_STM32G0X) || \ defined(CONFIG_SOC_SERIES_STM32L0X) int8_t acq_time_index; #endif }; struct adc_stm32_cfg { ADC_TypeDef *base; void (*irq_cfg_func)(void); struct stm32_pclken pclken; const struct pinctrl_dev_config *pcfg; bool has_temp_channel; bool has_vref_channel; }; #ifdef CONFIG_ADC_STM32_SHARED_IRQS static bool init_irq = true; #endif static int check_buffer_size(const struct adc_sequence *sequence, uint8_t active_channels) { size_t needed_buffer_size; needed_buffer_size = active_channels * sizeof(uint16_t); if (sequence->options) { needed_buffer_size *= (1 + sequence->options->extra_samplings); } if (sequence->buffer_size < needed_buffer_size) { LOG_ERR("Provided buffer is too small (%u/%u)", sequence->buffer_size, needed_buffer_size); return -ENOMEM; } return 0; } static void adc_stm32_start_conversion(const struct device *dev) { const struct adc_stm32_cfg *config = dev->config; ADC_TypeDef *adc = (ADC_TypeDef *)config->base; LOG_DBG("Starting conversion"); #if defined(CONFIG_SOC_SERIES_STM32F0X) || \ defined(CONFIG_SOC_SERIES_STM32F3X) || \ defined(CONFIG_SOC_SERIES_STM32L0X) || \ defined(CONFIG_SOC_SERIES_STM32L4X) || \ defined(CONFIG_SOC_SERIES_STM32L5X) || \ defined(CONFIG_SOC_SERIES_STM32WBX) || \ defined(CONFIG_SOC_SERIES_STM32G0X) || \ defined(CONFIG_SOC_SERIES_STM32G4X) || \ defined(CONFIG_SOC_SERIES_STM32H7X) || \ defined(CONFIG_SOC_SERIES_STM32U5X) || \ defined(CONFIG_SOC_SERIES_STM32WLX) LL_ADC_REG_StartConversion(adc); #else LL_ADC_REG_StartConversionSWStart(adc); #endif } #if !defined(CONFIG_SOC_SERIES_STM32F2X) && \ !defined(CONFIG_SOC_SERIES_STM32F4X) && \ !defined(CONFIG_SOC_SERIES_STM32F7X) && \ !defined(CONFIG_SOC_SERIES_STM32F1X) && \ !defined(STM32F3X_ADC_V2_5) && \ !defined(CONFIG_SOC_SERIES_STM32L1X) static void adc_stm32_calib(const struct device *dev) { const struct adc_stm32_cfg *config = (const struct adc_stm32_cfg *)dev->config; ADC_TypeDef *adc = config->base; #if defined(STM32F3X_ADC_V1_1) || \ defined(CONFIG_SOC_SERIES_STM32L4X) || \ defined(CONFIG_SOC_SERIES_STM32L5X) || \ defined(CONFIG_SOC_SERIES_STM32WBX) || \ defined(CONFIG_SOC_SERIES_STM32G4X) LL_ADC_StartCalibration(adc, LL_ADC_SINGLE_ENDED); #elif defined(CONFIG_SOC_SERIES_STM32F0X) || \ defined(CONFIG_SOC_SERIES_STM32G0X) || \ defined(CONFIG_SOC_SERIES_STM32L0X) || \ defined(CONFIG_SOC_SERIES_STM32WLX) LL_ADC_StartCalibration(adc); #elif defined(CONFIG_SOC_SERIES_STM32U5X) LL_ADC_StartCalibration(adc, LL_ADC_CALIB_OFFSET); #elif defined(CONFIG_SOC_SERIES_STM32H7X) LL_ADC_StartCalibration(adc, LL_ADC_CALIB_OFFSET, LL_ADC_SINGLE_ENDED); #endif while (LL_ADC_IsCalibrationOnGoing(adc)) { } } #endif #if defined(CONFIG_SOC_SERIES_STM32G0X) || \ defined(CONFIG_SOC_SERIES_STM32G4X) || \ defined(CONFIG_SOC_SERIES_STM32H7X) || \ defined(CONFIG_SOC_SERIES_STM32L0X) || \ defined(CONFIG_SOC_SERIES_STM32L4X) || \ defined(CONFIG_SOC_SERIES_STM32L5X) || \ defined(CONFIG_SOC_SERIES_STM32U5X) || \ defined(CONFIG_SOC_SERIES_STM32WBX) || \ defined(CONFIG_SOC_SERIES_STM32WLX) #ifdef LL_ADC_OVS_RATIO_2 /* table for shifting oversampling mostly for ADC3 != ADC_VER_V5_V90 */ static const uint32_t stm32_adc_ratio_table[] = { 0, LL_ADC_OVS_RATIO_2, LL_ADC_OVS_RATIO_4, LL_ADC_OVS_RATIO_8, LL_ADC_OVS_RATIO_16, LL_ADC_OVS_RATIO_32, LL_ADC_OVS_RATIO_64, LL_ADC_OVS_RATIO_128, LL_ADC_OVS_RATIO_256, }; #endif /* ! ADC_VER_V5_V90 */ /* * Function to configure the oversampling ratio and shit using stm32 LL * ratio is directly the sequence->oversampling (a 2^n value) * shift is the corresponding LL_ADC_OVS_SHIFT_RIGHT_x constant */ static void adc_stm32_oversampling(ADC_TypeDef *adc, uint8_t ratio, uint32_t shift) { LL_ADC_SetOverSamplingScope(adc, LL_ADC_OVS_GRP_REGULAR_CONTINUED); #if defined(CONFIG_SOC_SERIES_STM32H7X) /* * Set bits manually to circumvent bug in LL Libraries * https://github.com/STMicroelectronics/STM32CubeH7/issues/177 */ #if defined(ADC_VER_V5_V90) if (adc == ADC3) { MODIFY_REG(adc->CFGR2, (ADC_CFGR2_OVSS | ADC3_CFGR2_OVSR), (shift | stm32_adc_ratio_table[ratio])); } else { MODIFY_REG(adc->CFGR2, (ADC_CFGR2_OVSS | ADC_CFGR2_OVSR), (shift | (((1UL << ratio) - 1) << ADC_CFGR2_OVSR_Pos))); } #endif /* ADC_VER_V5_V90*/ MODIFY_REG(adc->CFGR2, (ADC_CFGR2_OVSS | ADC_CFGR2_OVSR), (shift | (((1UL << ratio) - 1) << ADC_CFGR2_OVSR_Pos))); #elif defined(CONFIG_SOC_SERIES_STM32U5X) if (adc == ADC1) { /* the LL function expects a value from 1 to 1024 */ LL_ADC_ConfigOverSamplingRatioShift(adc, (1 << ratio), shift); } else { /* the LL function expects a value LL_ADC_OVS_RATIO_x */ LL_ADC_ConfigOverSamplingRatioShift(adc, stm32_adc_ratio_table[ratio], shift); } #else /* CONFIG_SOC_SERIES_STM32H7X */ LL_ADC_ConfigOverSamplingRatioShift(adc, stm32_adc_ratio_table[ratio], shift); #endif /* CONFIG_SOC_SERIES_STM32H7X */ } #endif /* CONFIG_SOC_SERIES_STM32xxx */ /* * Enable ADC peripheral, and wait until ready if required by SOC. */ static int adc_stm32_enable(ADC_TypeDef *adc) { #if defined(CONFIG_SOC_SERIES_STM32L4X) || \ defined(CONFIG_SOC_SERIES_STM32L5X) || \ defined(CONFIG_SOC_SERIES_STM32WBX) || \ defined(CONFIG_SOC_SERIES_STM32G0X) || \ defined(CONFIG_SOC_SERIES_STM32G4X) || \ defined(CONFIG_SOC_SERIES_STM32H7X) || \ defined(CONFIG_SOC_SERIES_STM32WLX) if (LL_ADC_IsEnabled(adc) == 1UL) { return 0; } LL_ADC_ClearFlag_ADRDY(adc); LL_ADC_Enable(adc); /* * Enabling ADC modules in L4, WB, G0 and G4 series may fail if they are * still not stabilized, this will wait for a short time to ensure ADC * modules are properly enabled. */ uint32_t count_timeout = 0; while (LL_ADC_IsActiveFlag_ADRDY(adc) == 0) { if (LL_ADC_IsEnabled(adc) == 0UL) { LL_ADC_Enable(adc); count_timeout++; if (count_timeout == 10) { return -ETIMEDOUT; } } } #else LL_ADC_Enable(adc); #endif return 0; } static int start_read(const struct device *dev, const struct adc_sequence *sequence) { const struct adc_stm32_cfg *config = dev->config; struct adc_stm32_data *data = dev->data; ADC_TypeDef *adc = (ADC_TypeDef *)config->base; uint8_t resolution; int err; switch (sequence->resolution) { #if defined(CONFIG_SOC_SERIES_STM32F1X) || \ defined(STM32F3X_ADC_V2_5) case 12: resolution = table_resolution[0]; break; #elif !defined(CONFIG_SOC_SERIES_STM32H7X) case 6: resolution = table_resolution[0]; break; case 8: resolution = table_resolution[1]; break; case 10: resolution = table_resolution[2]; break; case 12: resolution = table_resolution[3]; break; #if defined(CONFIG_SOC_SERIES_STM32U5X) case 14: resolution = table_resolution[4]; break; #endif /* CONFIG_SOC_SERIES_STM32U5X */ #else case 8: resolution = table_resolution[0]; break; case 10: resolution = table_resolution[1]; break; case 12: resolution = table_resolution[2]; break; case 14: resolution = table_resolution[3]; break; case 16: resolution = table_resolution[4]; break; #endif default: LOG_ERR("Invalid resolution"); return -EINVAL; } uint32_t channels = sequence->channels; uint8_t index = find_lsb_set(channels) - 1; if (channels > BIT(index)) { LOG_ERR("Only single channel supported"); return -ENOTSUP; } data->buffer = sequence->buffer; uint32_t channel = __LL_ADC_DECIMAL_NB_TO_CHANNEL(index); #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); #endif #if defined(CONFIG_SOC_SERIES_STM32F0X) || \ defined(CONFIG_SOC_SERIES_STM32L0X) LL_ADC_REG_SetSequencerChannels(adc, channel); #elif defined(CONFIG_SOC_SERIES_STM32G0X) || \ defined(CONFIG_SOC_SERIES_STM32WLX) /* STM32G0 in "not fully configurable" sequencer mode */ LL_ADC_REG_SetSequencerChannels(adc, channel); while (LL_ADC_IsActiveFlag_CCRDY(adc) == 0) { } #else LL_ADC_REG_SetSequencerRanks(adc, table_rank[0], channel); LL_ADC_REG_SetSequencerLength(adc, table_seq_len[0]); #endif data->channel_count = 1; err = check_buffer_size(sequence, data->channel_count); if (err) { return err; } #if defined(CONFIG_SOC_SERIES_STM32G0X) || \ defined(CONFIG_SOC_SERIES_STM32WLX) /* * Writing ADC_CFGR1 register while ADEN bit is set * resets RES[1:0] bitfield. We need to disable and enable adc. */ if (LL_ADC_IsEnabled(adc) == 1UL) { LL_ADC_Disable(adc); } while (LL_ADC_IsEnabled(adc) == 1UL) { } LL_ADC_SetResolution(adc, resolution); adc_stm32_enable(adc); #elif !defined(CONFIG_SOC_SERIES_STM32F1X) && \ !defined(STM32F3X_ADC_V2_5) LL_ADC_SetResolution(adc, resolution); #endif #if defined(CONFIG_SOC_SERIES_STM32L0X) || \ defined(CONFIG_SOC_SERIES_STM32WLX) /* * setting OVS bits is conditioned to ADC state: ADC must be disabled * or enabled without conversion on going : disable it, it will stop */ LL_ADC_Disable(adc); while (LL_ADC_IsEnabled(adc) == 1UL) { } #endif #if defined(CONFIG_SOC_SERIES_STM32G0X) || \ defined(CONFIG_SOC_SERIES_STM32G4X) || \ defined(CONFIG_SOC_SERIES_STM32H7X) || \ defined(CONFIG_SOC_SERIES_STM32L0X) || \ defined(CONFIG_SOC_SERIES_STM32L4X) || \ defined(CONFIG_SOC_SERIES_STM32L5X) || \ defined(CONFIG_SOC_SERIES_STM32U5X) || \ defined(CONFIG_SOC_SERIES_STM32WBX) || \ defined(CONFIG_SOC_SERIES_STM32WLX) switch (sequence->oversampling) { case 0: LL_ADC_SetOverSamplingScope(adc, LL_ADC_OVS_DISABLE); break; case 1: adc_stm32_oversampling(adc, 1, LL_ADC_OVS_SHIFT_RIGHT_1); break; case 2: adc_stm32_oversampling(adc, 2, LL_ADC_OVS_SHIFT_RIGHT_2); break; case 3: adc_stm32_oversampling(adc, 3, LL_ADC_OVS_SHIFT_RIGHT_3); break; case 4: adc_stm32_oversampling(adc, 4, LL_ADC_OVS_SHIFT_RIGHT_4); break; case 5: adc_stm32_oversampling(adc, 5, LL_ADC_OVS_SHIFT_RIGHT_5); break; case 6: adc_stm32_oversampling(adc, 6, LL_ADC_OVS_SHIFT_RIGHT_6); break; case 7: adc_stm32_oversampling(adc, 7, LL_ADC_OVS_SHIFT_RIGHT_7); break; case 8: adc_stm32_oversampling(adc, 8, LL_ADC_OVS_SHIFT_RIGHT_8); break; #if defined(CONFIG_SOC_SERIES_STM32H7X) || defined(CONFIG_SOC_SERIES_STM32U5X) /* stm32 U5, H7 ADC1 & 2 have oversampling ratio from 1..1024 */ case 9: adc_stm32_oversampling(adc, 9, LL_ADC_OVS_SHIFT_RIGHT_9); break; case 10: adc_stm32_oversampling(adc, 10, LL_ADC_OVS_SHIFT_RIGHT_10); break; #endif /* CONFIG_SOC_SERIES_STM32H7X */ default: LOG_ERR("Invalid oversampling"); adc_stm32_enable(adc); return -EINVAL; } /* re-enable ADC after changing the OVS */ adc_stm32_enable(adc); #else if (sequence->oversampling) { LOG_ERR("Oversampling not supported"); return -ENOTSUP; } #endif if (sequence->calibrate) { #if !defined(CONFIG_SOC_SERIES_STM32F2X) && \ !defined(CONFIG_SOC_SERIES_STM32F4X) && \ !defined(CONFIG_SOC_SERIES_STM32F7X) && \ !defined(CONFIG_SOC_SERIES_STM32F1X) && \ !defined(STM32F3X_ADC_V2_5) && \ !defined(CONFIG_SOC_SERIES_STM32L1X) /* we cannot calibrate the ADC while the ADC is enabled */ LL_ADC_Disable(adc); while (LL_ADC_IsEnabled(adc) == 1UL) { } adc_stm32_calib(dev); /* re-enable ADC after calibration */ adc_stm32_enable(adc); #else LOG_ERR("Calibration not supported"); return -ENOTSUP; #endif } #if defined(CONFIG_SOC_SERIES_STM32F0X) || \ defined(STM32F3X_ADC_V1_1) || \ defined(CONFIG_SOC_SERIES_STM32L0X) || \ defined(CONFIG_SOC_SERIES_STM32L4X) || \ defined(CONFIG_SOC_SERIES_STM32L5X) || \ defined(CONFIG_SOC_SERIES_STM32WBX) || \ defined(CONFIG_SOC_SERIES_STM32G0X) || \ defined(CONFIG_SOC_SERIES_STM32G4X) || \ defined(CONFIG_SOC_SERIES_STM32H7X) || \ defined(CONFIG_SOC_SERIES_STM32U5X) || \ defined(CONFIG_SOC_SERIES_STM32WLX) LL_ADC_EnableIT_EOC(adc); #elif defined(CONFIG_SOC_SERIES_STM32F1X) LL_ADC_EnableIT_EOS(adc); #elif defined(STM32F3X_ADC_V2_5) adc_stm32_enable(adc); LL_ADC_EnableIT_EOS(adc); #else LL_ADC_EnableIT_EOCS(adc); #endif adc_context_start_read(&data->ctx, sequence); return adc_context_wait_for_completion(&data->ctx); } static void adc_context_start_sampling(struct adc_context *ctx) { struct adc_stm32_data *data = CONTAINER_OF(ctx, struct adc_stm32_data, ctx); data->repeat_buffer = data->buffer; adc_stm32_start_conversion(data->dev); } static void adc_context_update_buffer_pointer(struct adc_context *ctx, bool repeat_sampling) { struct adc_stm32_data *data = CONTAINER_OF(ctx, struct adc_stm32_data, ctx); if (repeat_sampling) { data->buffer = data->repeat_buffer; } } static void adc_stm32_isr(const struct device *dev) { struct adc_stm32_data *data = dev->data; const struct adc_stm32_cfg *config = (const struct adc_stm32_cfg *)dev->config; ADC_TypeDef *adc = config->base; *data->buffer++ = LL_ADC_REG_ReadConversionData32(adc); adc_context_on_sampling_done(&data->ctx, dev); LOG_DBG("%s ISR triggered.", dev->name); } static int adc_stm32_read(const struct device *dev, const struct adc_sequence *sequence) { struct adc_stm32_data *data = dev->data; int error; adc_context_lock(&data->ctx, false, NULL); error = start_read(dev, sequence); adc_context_release(&data->ctx, error); return error; } #ifdef CONFIG_ADC_ASYNC static int adc_stm32_read_async(const struct device *dev, const struct adc_sequence *sequence, struct k_poll_signal *async) { struct adc_stm32_data *data = dev->data; int error; adc_context_lock(&data->ctx, true, async); error = start_read(dev, sequence); adc_context_release(&data->ctx, error); return error; } #endif static int adc_stm32_check_acq_time(uint16_t acq_time) { if (acq_time == ADC_ACQ_TIME_MAX) { return ARRAY_SIZE(acq_time_tbl) - 1; } for (int i = 0; i < 8; i++) { if (acq_time == ADC_ACQ_TIME(ADC_ACQ_TIME_TICKS, acq_time_tbl[i])) { return i; } } if (acq_time == ADC_ACQ_TIME_DEFAULT) { return 0; } LOG_ERR("Conversion time not supported."); return -EINVAL; } /* * Enable internal channel source */ static void adc_stm32_set_common_path(const struct device *dev, uint32_t PathInternal) { const struct adc_stm32_cfg *config = (const struct adc_stm32_cfg *)dev->config; ADC_TypeDef *adc = (ADC_TypeDef *)config->base; (void) adc; /* Avoid 'unused variable' warning for some families */ /* Do not remove existing paths */ PathInternal |= LL_ADC_GetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(adc)); LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(adc), PathInternal); } static void adc_stm32_setup_speed(const struct device *dev, uint8_t id, uint8_t acq_time_index) { const struct adc_stm32_cfg *config = (const struct adc_stm32_cfg *)dev->config; ADC_TypeDef *adc = config->base; #if defined(CONFIG_SOC_SERIES_STM32F0X) || defined(CONFIG_SOC_SERIES_STM32L0X) LL_ADC_SetSamplingTimeCommonChannels(adc, table_samp_time[acq_time_index]); #elif defined(CONFIG_SOC_SERIES_STM32G0X) LL_ADC_SetSamplingTimeCommonChannels(adc, LL_ADC_SAMPLINGTIME_COMMON_1, table_samp_time[acq_time_index]); #else LL_ADC_SetChannelSamplingTime(adc, __LL_ADC_DECIMAL_NB_TO_CHANNEL(id), table_samp_time[acq_time_index]); #endif } static void adc_stm32_setup_channels(const struct device *dev, uint8_t channel_id) { const struct adc_stm32_cfg *config = dev->config; #ifdef CONFIG_SOC_SERIES_STM32G4X ADC_TypeDef *adc = config->base; if (config->has_temp_channel) { #if DT_NODE_HAS_STATUS(DT_NODELABEL(adc1), okay) if ((__LL_ADC_CHANNEL_TO_DECIMAL_NB(LL_ADC_CHANNEL_TEMPSENSOR_ADC1) == channel_id) && (adc == ADC1)) { adc_stm32_set_common_path(dev, LL_ADC_PATH_INTERNAL_TEMPSENSOR); } #endif #if DT_NODE_HAS_STATUS(DT_NODELABEL(adc5), okay) if ((__LL_ADC_CHANNEL_TO_DECIMAL_NB(LL_ADC_CHANNEL_TEMPSENSOR_ADC5) == channel_id) && (adc == ADC5)) { adc_stm32_set_common_path(dev, LL_ADC_PATH_INTERNAL_TEMPSENSOR); } #endif } #else if (config->has_temp_channel && __LL_ADC_CHANNEL_TO_DECIMAL_NB(LL_ADC_CHANNEL_TEMPSENSOR) == channel_id) { adc_stm32_set_common_path(dev, LL_ADC_PATH_INTERNAL_TEMPSENSOR); } #endif /* CONFIG_SOC_SERIES_STM32G4X */ if (config->has_vref_channel && __LL_ADC_CHANNEL_TO_DECIMAL_NB(LL_ADC_CHANNEL_VREFINT) == channel_id) { adc_stm32_set_common_path(dev, LL_ADC_PATH_INTERNAL_VREFINT); } } static int adc_stm32_channel_setup(const struct device *dev, const struct adc_channel_cfg *channel_cfg) { #if defined(CONFIG_SOC_SERIES_STM32F0X) || \ defined(CONFIG_SOC_SERIES_STM32G0X) || \ defined(CONFIG_SOC_SERIES_STM32L0X) struct adc_stm32_data *data = dev->data; #endif int acq_time_index; if (channel_cfg->channel_id >= STM32_CHANNEL_COUNT) { LOG_ERR("Channel %d is not valid", channel_cfg->channel_id); return -EINVAL; } acq_time_index = adc_stm32_check_acq_time( channel_cfg->acquisition_time); if (acq_time_index < 0) { return acq_time_index; } #if defined(CONFIG_SOC_SERIES_STM32F0X) || \ defined(CONFIG_SOC_SERIES_STM32G0X) || \ defined(CONFIG_SOC_SERIES_STM32L0X) if (data->acq_time_index == -1) { data->acq_time_index = acq_time_index; } else { /* All channels of F0/L0 must have identical acquisition time.*/ if (acq_time_index != data->acq_time_index) { return -EINVAL; } } #endif if (channel_cfg->differential) { LOG_ERR("Differential channels are not supported"); return -EINVAL; } if (channel_cfg->gain != ADC_GAIN_1) { LOG_ERR("Invalid channel gain"); return -EINVAL; } if (channel_cfg->reference != ADC_REF_INTERNAL) { LOG_ERR("Invalid channel reference"); return -EINVAL; } adc_stm32_setup_channels(dev, channel_cfg->channel_id); adc_stm32_setup_speed(dev, channel_cfg->channel_id, acq_time_index); LOG_DBG("Channel setup succeeded!"); return 0; } static int adc_stm32_init(const struct device *dev) { struct adc_stm32_data *data = dev->data; const struct adc_stm32_cfg *config = dev->config; const struct device *clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE); ADC_TypeDef *adc = (ADC_TypeDef *)config->base; int err; LOG_DBG("Initializing...."); data->dev = dev; #if defined(CONFIG_SOC_SERIES_STM32F0X) || \ defined(CONFIG_SOC_SERIES_STM32G0X) || \ defined(CONFIG_SOC_SERIES_STM32L0X) /* * All conversion time for all channels on one ADC instance for F0 and * L0 series chips has to be the same. For STM32G0 currently only one * of the two available common channel conversion times is used. * This additional variable is for checking if the conversion time * selection of all channels on one ADC instance is the same. */ data->acq_time_index = -1; #endif if (clock_control_on(clk, (clock_control_subsys_t *) &config->pclken) != 0) { return -EIO; } /* Configure dt provided device signals when available */ err = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); if (err < 0) { LOG_ERR("ADC pinctrl setup failed (%d)", err); return err; } #if defined(CONFIG_SOC_SERIES_STM32U5X) /* Enable the independent analog supply */ LL_PWR_EnableVDDA(); #endif /* CONFIG_SOC_SERIES_STM32U5X */ #if defined(CONFIG_SOC_SERIES_STM32L4X) || \ defined(CONFIG_SOC_SERIES_STM32L5X) || \ defined(CONFIG_SOC_SERIES_STM32WBX) || \ defined(CONFIG_SOC_SERIES_STM32G4X) || \ defined(CONFIG_SOC_SERIES_STM32H7X) || \ defined(CONFIG_SOC_SERIES_STM32U5X) /* * L4, WB, G4, H7 and U5 series STM32 needs to be awaken from deep sleep * mode, and restore its calibration parameters if there are some * previously stored calibration parameters. */ LL_ADC_DisableDeepPowerDown(adc); #endif /* * F3, L4, WB, G0 and G4 ADC modules need some time * to be stabilized before performing any enable or calibration actions. */ #if defined(STM32F3X_ADC_V1_1) || \ defined(CONFIG_SOC_SERIES_STM32L4X) || \ defined(CONFIG_SOC_SERIES_STM32L5X) || \ defined(CONFIG_SOC_SERIES_STM32WBX) || \ defined(CONFIG_SOC_SERIES_STM32G0X) || \ defined(CONFIG_SOC_SERIES_STM32G4X) || \ defined(CONFIG_SOC_SERIES_STM32H7X) || \ defined(CONFIG_SOC_SERIES_STM32U5X) || \ defined(CONFIG_SOC_SERIES_STM32WLX) LL_ADC_EnableInternalRegulator(adc); k_busy_wait(LL_ADC_DELAY_INTERNAL_REGUL_STAB_US); #endif #if defined(CONFIG_SOC_SERIES_STM32F0X) || \ defined(CONFIG_SOC_SERIES_STM32L0X) || \ defined(CONFIG_SOC_SERIES_STM32WLX) LL_ADC_SetClock(adc, LL_ADC_CLOCK_SYNC_PCLK_DIV4); #elif defined(STM32F3X_ADC_V1_1) || \ defined(CONFIG_SOC_SERIES_STM32L4X) || \ defined(CONFIG_SOC_SERIES_STM32L5X) || \ defined(CONFIG_SOC_SERIES_STM32WBX) || \ defined(CONFIG_SOC_SERIES_STM32G0X) || \ defined(CONFIG_SOC_SERIES_STM32G4X) || \ defined(CONFIG_SOC_SERIES_STM32H7X) LL_ADC_SetCommonClock(__LL_ADC_COMMON_INSTANCE(adc), LL_ADC_CLOCK_SYNC_PCLK_DIV4); #elif defined(CONFIG_SOC_SERIES_STM32L1X) || \ defined(CONFIG_SOC_SERIES_STM32U5X) LL_ADC_SetCommonClock(__LL_ADC_COMMON_INSTANCE(adc), LL_ADC_CLOCK_ASYNC_DIV4); #endif #if !defined(CONFIG_SOC_SERIES_STM32F2X) && \ !defined(CONFIG_SOC_SERIES_STM32F4X) && \ !defined(CONFIG_SOC_SERIES_STM32F7X) && \ !defined(CONFIG_SOC_SERIES_STM32F1X) && \ !defined(STM32F3X_ADC_V2_5) && \ !defined(CONFIG_SOC_SERIES_STM32L1X) /* * Calibration of F1 and F3 (ADC1_V2_5) series has to be started * after ADC Module is enabled. */ adc_stm32_calib(dev); #endif #if defined(CONFIG_SOC_SERIES_STM32F0X) || \ defined(CONFIG_SOC_SERIES_STM32L0X) || \ defined(CONFIG_SOC_SERIES_STM32L4X) || \ defined(CONFIG_SOC_SERIES_STM32L5X) || \ defined(CONFIG_SOC_SERIES_STM32WBX) || \ defined(CONFIG_SOC_SERIES_STM32G0X) || \ defined(CONFIG_SOC_SERIES_STM32G4X) || \ defined(CONFIG_SOC_SERIES_STM32H7X) || \ defined(CONFIG_SOC_SERIES_STM32U5X) || \ defined(CONFIG_SOC_SERIES_STM32WLX) if (LL_ADC_IsActiveFlag_ADRDY(adc)) { LL_ADC_ClearFlag_ADRDY(adc); } /* * These STM32 series has one internal voltage reference source * to be enabled. */ LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(adc), LL_ADC_PATH_INTERNAL_VREFINT); #endif #if defined(CONFIG_SOC_SERIES_STM32F0X) || \ defined(STM32F3X_ADC_V1_1) || \ defined(CONFIG_SOC_SERIES_STM32L0X) || \ defined(CONFIG_SOC_SERIES_STM32L4X) || \ defined(CONFIG_SOC_SERIES_STM32L5X) || \ defined(CONFIG_SOC_SERIES_STM32WBX) || \ defined(CONFIG_SOC_SERIES_STM32G0X) || \ defined(CONFIG_SOC_SERIES_STM32G4X) || \ defined(CONFIG_SOC_SERIES_STM32H7X) || \ defined(CONFIG_SOC_SERIES_STM32U5X) || \ defined(CONFIG_SOC_SERIES_STM32WLX) /* * ADC modules on these series have to wait for some cycles to be * enabled. */ uint32_t adc_rate, wait_cycles; if (clock_control_get_rate(clk, (clock_control_subsys_t *) &config->pclken, &adc_rate) < 0) { LOG_ERR("ADC clock rate get error."); } wait_cycles = SystemCoreClock / adc_rate * LL_ADC_DELAY_CALIB_ENABLE_ADC_CYCLES; for (int i = wait_cycles; i >= 0; i--) { } #endif err = adc_stm32_enable(adc); if (err < 0) { return err; } config->irq_cfg_func(); #if defined(CONFIG_SOC_SERIES_STM32F1X) || \ defined(STM32F3X_ADC_V2_5) /* * Calibration of F1 and F3 (ADC1_V2_5) must starts after two cycles * after ADON is set. */ LL_ADC_StartCalibration(adc); LL_ADC_REG_SetTriggerSource(adc, LL_ADC_REG_TRIG_SOFTWARE); #endif #ifdef CONFIG_SOC_SERIES_STM32H7X /* * To ensure linearity the factory calibration values * should be loaded on initialization. */ uint32_t channel_offset = 0U; uint32_t linear_calib_buffer = 0U; if (adc == ADC1) { channel_offset = 0UL; } else if (adc == ADC2) { channel_offset = 8UL; } else /*Case ADC3*/ { channel_offset = 16UL; } /* Read factory calibration factors */ for (uint32_t count = 0UL; count < ADC_LINEAR_CALIB_REG_COUNT; count++) { linear_calib_buffer = *(uint32_t *)( ADC_LINEAR_CALIB_REG_1_ADDR + channel_offset + count ); LL_ADC_SetCalibrationLinearFactor( adc, LL_ADC_CALIB_LINEARITY_WORD1 << count, linear_calib_buffer ); } #endif adc_context_unlock_unconditionally(&data->ctx); return 0; } static const struct adc_driver_api api_stm32_driver_api = { .channel_setup = adc_stm32_channel_setup, .read = adc_stm32_read, #ifdef CONFIG_ADC_ASYNC .read_async = adc_stm32_read_async, #endif .ref_internal = STM32_ADC_VREF_MV, /* VREF is usually connected to VDD */ }; #ifdef CONFIG_ADC_STM32_SHARED_IRQS bool adc_stm32_is_irq_active(ADC_TypeDef *adc) { return LL_ADC_IsActiveFlag_EOCS(adc) || LL_ADC_IsActiveFlag_OVR(adc) || LL_ADC_IsActiveFlag_JEOS(adc) || LL_ADC_IsActiveFlag_AWD1(adc); } #define HANDLE_IRQS(index) \ static const struct device *dev_##index = DEVICE_DT_INST_GET(index); \ const struct adc_stm32_cfg *cfg_##index = dev_##index->config; \ ADC_TypeDef *adc_##index = (ADC_TypeDef *)(cfg_##index->base); \ \ if (adc_stm32_is_irq_active(adc_##index)) { \ adc_stm32_isr(dev_##index); \ } static void adc_stm32_shared_irq_handler(void) { DT_INST_FOREACH_STATUS_OKAY(HANDLE_IRQS); } static void adc_stm32_irq_init(void) { if (init_irq) { init_irq = false; IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), adc_stm32_shared_irq_handler, NULL, 0); irq_enable(DT_INST_IRQN(0)); } } #define ADC_STM32_CONFIG(index) \ static const struct adc_stm32_cfg adc_stm32_cfg_##index = { \ .base = (ADC_TypeDef *)DT_INST_REG_ADDR(index), \ .irq_cfg_func = adc_stm32_irq_init, \ .pclken = { \ .enr = DT_INST_CLOCKS_CELL(index, bits), \ .bus = DT_INST_CLOCKS_CELL(index, bus), \ }, \ .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(index), \ .has_temp_channel = DT_INST_PROP(index, has_temp_channel), \ .has_vref_channel = DT_INST_PROP(index, has_vref_channel), \ }; #else #define ADC_STM32_CONFIG(index) \ static void adc_stm32_cfg_func_##index(void) \ { \ IRQ_CONNECT(DT_INST_IRQN(index), \ DT_INST_IRQ(index, priority), \ adc_stm32_isr, DEVICE_DT_INST_GET(index), 0); \ irq_enable(DT_INST_IRQN(index)); \ } \ \ static const struct adc_stm32_cfg adc_stm32_cfg_##index = { \ .base = (ADC_TypeDef *)DT_INST_REG_ADDR(index), \ .irq_cfg_func = adc_stm32_cfg_func_##index, \ .pclken = { \ .enr = DT_INST_CLOCKS_CELL(index, bits), \ .bus = DT_INST_CLOCKS_CELL(index, bus), \ }, \ .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(index), \ .has_temp_channel = DT_INST_PROP(index, has_temp_channel), \ .has_vref_channel = DT_INST_PROP(index, has_vref_channel), \ }; #endif /* CONFIG_ADC_STM32_SHARED_IRQS */ #define STM32_ADC_INIT(index) \ \ PINCTRL_DT_INST_DEFINE(index); \ \ ADC_STM32_CONFIG(index) \ \ static struct adc_stm32_data adc_stm32_data_##index = { \ ADC_CONTEXT_INIT_TIMER(adc_stm32_data_##index, ctx), \ ADC_CONTEXT_INIT_LOCK(adc_stm32_data_##index, ctx), \ ADC_CONTEXT_INIT_SYNC(adc_stm32_data_##index, ctx), \ }; \ \ DEVICE_DT_INST_DEFINE(index, \ &adc_stm32_init, NULL, \ &adc_stm32_data_##index, &adc_stm32_cfg_##index, \ POST_KERNEL, CONFIG_ADC_INIT_PRIORITY, \ &api_stm32_driver_api); DT_INST_FOREACH_STATUS_OKAY(STM32_ADC_INIT)