HAL_ADC_ConvCpltCallback never gets called

Post here first, or if you can't find a relevant section!
Post Reply
Aiyseri
Posts: 3
Joined: Fri Oct 27, 2023 7:16 pm

HAL_ADC_ConvCpltCallback never gets called

Post by Aiyseri »

Hi Everyone,
I'm working on a STM32F401CC using the STM32duino libraries (2.2.2). My board variant aliases as a Blackpill since its the same processor and clocks.
What I'd like to implement is something like this:
A timer that triggers ADC reads at fixed interval - Works (software configurable later)
An interrupt driven ADC callback that accumulates the read adc value and spits it out every N time it gets run (i.e., software integration of analog value).

I've understood that the Arduino style analogRead function goes through the ADC setup, ADC trigger, ADC read and cleanup each time, so that's not good for high sample rates.
I've found that analogRead calls to adc_read_value, and there's where everything happens I've taken the contents of adc_read_value and copied it into my own for customization.
I've attempted to separate the setup, trigger and read into separate functions, which looks OK without changes.

My setup looks like this (straight copy-paste from adc_read_value):

Code: Select all

void my_adc_setup(PinName pin, uint32_t resolution)
{
	ADC_ChannelConfTypeDef  AdcChannelConf = {};
	uint32_t samplingTime = ADC_SAMPLINGTIME;
	uint32_t channel = 0;
	uint32_t bank = 0;

	if ((pin & PADC_BASE) && (pin < ANA_START)) {
		#if defined(STM32H7xx) || defined(STM32MP1xx)
			#ifdef ADC3
				AdcHandle.Instance = ADC3;
			#else
				AdcHandle.Instance = ADC2;
			#endif
		#else
			AdcHandle.Instance = ADC1;
			#if defined(ADC5) && defined(ADC_CHANNEL_TEMPSENSOR_ADC5)
				if (pin == PADC_TEMP_ADC5) {
					AdcHandle.Instance = ADC5;
				}
			#endif
		#endif
		channel = get_adc_internal_channel(pin);
		samplingTime = ADC_SAMPLINGTIME_INTERNAL;
	} else {
		AdcHandle.Instance = (ADC_TypeDef *)pinmap_peripheral(pin, PinMap_ADC);
		channel = get_adc_channel(pin, &bank);
		#if defined(ADC_VER_V5_V90)
			if (AdcHandle.Instance == ADC3) {
				samplingTime = ADC3_SAMPLINGTIME;
			}
		#endif
		#if defined(ADC4_SAMPLINGTIME)
			if (AdcHandle.Instance == ADC4) {
				samplingTime = ADC4_SAMPLINGTIME;
			}
		#endif
	}

	if (AdcHandle.Instance == NP) {
		return;
	}

	#ifdef ADC_CLOCK_DIV
		AdcHandle.Init.ClockPrescaler        = ADC_CLOCK_DIV;                 /* (A)synchronous clock mode, input ADC clock divided */
	#endif
	#ifdef ADC_RESOLUTION_12B
		switch (resolution) {
			#ifdef ADC_RESOLUTION_6B
				case 6:
					AdcHandle.Init.Resolution          = ADC_RESOLUTION_6B;             /* resolution for converted data */
					break;
			#endif
			case 8:
				AdcHandle.Init.Resolution          = ADC_RESOLUTION_8B;             /* resolution for converted data */
				break;
			case 10:
				AdcHandle.Init.Resolution          = ADC_RESOLUTION_10B;            /* resolution for converted data */
				break;
			case 12:
				default:
				AdcHandle.Init.Resolution          = ADC_RESOLUTION_12B;            /* resolution for converted data */
			break;
			#ifdef ADC_RESOLUTION_14B
				case 14:
					AdcHandle.Init.Resolution          = ADC_RESOLUTION_14B;            /* resolution for converted data */
					break;
			#endif
			#ifdef ADC_RESOLUTION_16B
			case 16:
				AdcHandle.Init.Resolution          = ADC_RESOLUTION_16B;            /* resolution for converted data */
				break;
			#endif
		}
	#else
	  UNUSED(resolution);
	#endif
	#ifdef ADC_DATAALIGN_RIGHT
		AdcHandle.Init.DataAlign             = ADC_DATAALIGN_RIGHT;           /* Right-alignment for converted data */
	#endif
	#ifdef ADC_SCAN_SEQ_FIXED
		AdcHandle.Init.ScanConvMode          = ADC_SCAN_SEQ_FIXED;            /* Sequencer disabled (ADC conversion on only 1 channel: channel set on rank 1) */
	#else
		AdcHandle.Init.ScanConvMode          = DISABLE;                       /* Sequencer disabled (ADC conversion on only 1 channel: channel set on rank 1) */
	#endif
	#ifdef ADC_EOC_SINGLE_CONV
		AdcHandle.Init.EOCSelection          = ADC_EOC_SINGLE_CONV;           /* EOC flag picked-up to indicate conversion end */
	#endif
	#if !defined(STM32F1xx) && !defined(STM32F2xx) && !defined(STM32F4xx) && \
	    !defined(STM32F7xx) && !defined(ADC1_V2_5)
		AdcHandle.Init.LowPowerAutoWait      = DISABLE;                       /* Auto-delayed conversion feature disabled */
	#endif
	#if !defined(STM32F1xx) && !defined(STM32F2xx) && !defined(STM32F3xx) && \
		!defined(STM32F4xx) && !defined(STM32F7xx) && !defined(STM32G4xx) && \
		!defined(STM32H5xx) && !defined(STM32H7xx) && !defined(STM32L4xx) &&  \
		!defined(STM32L5xx) && !defined(STM32MP1xx) && !defined(STM32WBxx) || \
		defined(ADC_SUPPORT_2_5_MSPS)
		AdcHandle.Init.LowPowerAutoPowerOff  = DISABLE;                       /* ADC automatically powers-off after a conversion and automatically wakes-up when a new conversion is triggered */
	#endif
	#ifdef ADC_CHANNELS_BANK_B
		AdcHandle.Init.ChannelsBank          = bank;
	#elif defined(ADC_CHANNELS_BANK_A)
		AdcHandle.Init.ChannelsBank          = ADC_CHANNELS_BANK_A;
	#endif
		AdcHandle.Init.ContinuousConvMode    = DISABLE;                       /* Continuous mode disabled to have only 1 conversion at each conversion trig */
	#if !defined(STM32F0xx) && !defined(STM32L0xx)
		AdcHandle.Init.NbrOfConversion       = 1;                             /* Specifies the number of ranks that will be converted within the regular group sequencer. */
	#endif
	AdcHandle.Init.DiscontinuousConvMode = DISABLE;                       /* Parameter discarded because sequencer is disabled */
	#if !defined(STM32C0xx) && !defined(STM32F0xx) && !defined(STM32G0xx) && \
		!defined(STM32L0xx) && !defined(STM32WLxx) && !defined(ADC_SUPPORT_2_5_MSPS)
		AdcHandle.Init.NbrOfDiscConversion   = 0;                             /* Parameter discarded because sequencer is disabled */
	#endif
	AdcHandle.Init.ExternalTrigConv      = ADC_SOFTWARE_START;            /* Software start to trig the 1st conversion manually, without external event */
	#if !defined(STM32F1xx) && !defined(ADC1_V2_5)
		AdcHandle.Init.ExternalTrigConvEdge  = ADC_EXTERNALTRIGCONVEDGE_NONE; /* Parameter discarded because software trigger chosen */
	#endif
	#if !defined(STM32F1xx) && !defined(STM32H7xx) && !defined(STM32MP1xx) && \
	    !defined(ADC1_V2_5)
		AdcHandle.Init.DMAContinuousRequests = DISABLE;                       /* DMA one-shot mode selected (not applied to this example) */
	#endif
	#ifdef ADC_CONVERSIONDATA_DR
		AdcHandle.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DR;      /* Regular Conversion data stored in DR register only */
	#endif
	#ifdef ADC_OVR_DATA_OVERWRITTEN
		AdcHandle.Init.Overrun               = ADC_OVR_DATA_OVERWRITTEN;      /* DR register is overwritten with the last conversion result in case of overrun */
	#endif
	#ifdef ADC_LEFTBITSHIFT_NONE
		AdcHandle.Init.LeftBitShift          = ADC_LEFTBITSHIFT_NONE;         /* No bit shift left applied on the final ADC conversion data */
	#endif

	#if defined(STM32F0xx)
		AdcHandle.Init.SamplingTimeCommon    = samplingTime;
	#endif
	#if defined(STM32C0xx) || defined(STM32G0xx) || defined(STM32U5xx) || \
	    defined(STM32WLxx) || defined(ADC_SUPPORT_2_5_MSPS)
		AdcHandle.Init.SamplingTimeCommon1   = samplingTime;              /* Set sampling time common to a group of channels. */
		AdcHandle.Init.SamplingTimeCommon2   = samplingTime;              /* Set sampling time common to a group of channels, second common setting possible.*/
	#endif
	#if defined(STM32L0xx)
		AdcHandle.Init.LowPowerFrequencyMode = DISABLE;                       /* To be enabled only if ADC clock < 2.8 MHz */
		AdcHandle.Init.SamplingTime          = samplingTime;
	#endif
	#if !defined(STM32F0xx) && !defined(STM32F1xx) && !defined(STM32F2xx) && \
		!defined(STM32F3xx) && !defined(STM32F4xx) && !defined(STM32F7xx) && \
		!defined(STM32L1xx) && !defined(ADC_SUPPORT_2_5_MSPS)
		AdcHandle.Init.OversamplingMode      = DISABLE;
		/* AdcHandle.Init.Oversample ignore for STM32L0xx as oversampling disabled */
		/* AdcHandle.Init.Oversampling ignored for other as oversampling disabled */
	#endif
	#if defined(ADC_CFGR_DFSDMCFG) && defined(DFSDM1_Channel0)
		AdcHandle.Init.DFSDMConfig           = ADC_DFSDM_MODE_DISABLE;        /* ADC conversions are not transferred by DFSDM. */
	#endif
	#ifdef ADC_TRIGGER_FREQ_HIGH
		AdcHandle.Init.TriggerFrequencyMode  = ADC_TRIGGER_FREQ_HIGH;
	#endif
	#ifdef ADC_VREF_PPROT_NONE
		AdcHandle.Init.VrefProtection = ADC_VREF_PPROT_NONE;
	#endif

	  AdcHandle.State = HAL_ADC_STATE_RESET;
	  AdcHandle.DMA_Handle = NULL;
	  AdcHandle.Lock = HAL_UNLOCKED;
	/* Some other ADC_HandleTypeDef fields exists but not required */

	g_current_pin = pin; /* Needed for HAL_ADC_MspInit*/

	if (HAL_ADC_Init(&AdcHandle) != HAL_OK) {
		return;
	}

	AdcChannelConf.Channel      = channel;                          /* Specifies the channel to configure into ADC */

	#if defined(STM32G4xx) || defined(STM32H5xx) || defined(STM32L4xx) || \
		defined(STM32L5xx) || defined(STM32WBxx)
		if (!IS_ADC_CHANNEL(&AdcHandle, AdcChannelConf.Channel)) {
	#else
		if (!IS_ADC_CHANNEL(AdcChannelConf.Channel)) {
	#endif
		return;
	}
	#if defined(ADC_SCAN_SEQ_FIXED) && defined(ADC_RANK_CHANNEL_NUMBER)
		AdcChannelConf.Rank         = ADC_RANK_CHANNEL_NUMBER;          /* Enable the rank of the selected channels when not fully configurable */
	#else
		AdcChannelConf.Rank         = ADC_REGULAR_RANK_1;               /* Specifies the rank in the regular group sequencer */
	#endif
	#if !defined(STM32L0xx)
		#if !defined(STM32G0xx)
			AdcChannelConf.SamplingTime = samplingTime;                     /* Sampling time value to be set for the selected channel */
		#else
			AdcChannelConf.SamplingTime = ADC_SAMPLINGTIME_COMMON_1;        /* Sampling time value to be set for the selected channel */
		#endif
	#endif
	#if defined(ADC_DIFFERENTIAL_ENDED) && !defined(ADC1_V2_5)
		AdcChannelConf.SingleDiff   = ADC_SINGLE_ENDED;                 /* Single-ended input channel */
		AdcChannelConf.OffsetNumber = ADC_OFFSET_NONE;                  /* No offset subtraction */
	#endif
	#if !defined(STM32C0xx) && !defined(STM32F0xx) && !defined(STM32F1xx) && \
		!defined(STM32F2xx) && !defined(STM32G0xx) && !defined(STM32L0xx) && \
		!defined(STM32L1xx) && !defined(STM32WBxx) && !defined(STM32WLxx) && \
		!defined(ADC1_V2_5)
		AdcChannelConf.Offset = 0;                                      /* Parameter discarded because offset correction is disabled */
	#endif
	#if defined (STM32H7xx) || defined(STM32MP1xx)
		AdcChannelConf.OffsetRightShift = DISABLE;                      /* No Right Offset Shift */
		AdcChannelConf.OffsetSignedSaturation = DISABLE;                /* Signed saturation feature is not used */
	#endif

	/*##-2- Configure ADC regular channel ######################################*/
	if (HAL_ADC_ConfigChannel(&AdcHandle, &AdcChannelConf) != HAL_OK) {
		/* Channel Configuration Error */
		return;
	}

	#if defined(ADC_CR_ADCAL) || defined(ADC_CR2_RSTCAL)
		/*##-2.1- Calibrate ADC then Start the conversion process ####################*/
		#if defined(ADC_CALIB_OFFSET)
			if (HAL_ADCEx_Calibration_Start(&AdcHandle, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED) != HAL_OK)
		#elif defined(ADC_SINGLE_ENDED) && !defined(ADC1_V2_5)
			if (HAL_ADCEx_Calibration_Start(&AdcHandle, ADC_SINGLE_ENDED) !=  HAL_OK)
		#else
			if (HAL_ADCEx_Calibration_Start(&AdcHandle) !=  HAL_OK)
		#endif
		{
			/* ADC Calibration Error */
			return;
		}
	#endif
	
}
The function that the timer-trigger calls looks like this (Just a call to HAL_ADC_Start_IT):

Code: Select all

void my_adc_start(){
	uhADCxConvertedValue = 0;
	cnt_value = 0;
	adc_fresh = 1;
	HAL_ADC_Start_IT(&AdcHandle);
	//HAL_ADC_Start(&AdcHandle);
}
And as far as I understand, i should define my callback function like this:

Code: Select all

extern "C" void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef * handle){
	//Accumulate
	uhADCxConvertedValue += HAL_ADC_GetValue(handle);
	cnt_value += 1;
	
	if(cnt_value == 30){
		adc_fresh = 1;
		adc_value = uhADCxConvertedValue;
		uhADCxConvertedValue = 0;
		cnt_value = 0;
	}
}
But the callback function never gets called.

Anybody got any ideas what i'm missing?

Best regards,
Post Reply

Return to “General discussion”