Skip to main content
carlesls2
Associate III
June 16, 2025
Question

HAL_ADC_ConvCpltCallback optimized away

  • June 16, 2025
  • 5 replies
  • 957 views

Post edited to follow the community rules: Please review how to share a code in this post.

Using the stm32g030k8 and the Version: 1.18.1 STM32CubeIDE

 

I just recently applied an optimization in order to get more space for my application firmware. And then I realize that the HAL_ADC_ConvCpltCallback has been optimized away. I use the -Og (debug optimization) to use the less agressive and more debug friendly optimization. 

 

 
__attribute__((used)) void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc1)
{
ADC_Average_Buff[CHANNEL_VREF] = ADC_DMA_Buff[CHANNEL_VREF];

ADC_Average_Buff[CHANNEL_VBAT] = ADC_DMA_Buff[CHANNEL_VBAT];

ADC_Average_Buff[CHANNEL_CURRENT] = ADC_DMA_Buff[CHANNEL_CURRENT];

ADC_Average_Buff[CHANNEL_TEMPERATURE] = ADC_DMA_Buff[CHANNEL_TEMPERATURE];

ADC_Average_Buff[CHANNEL_VREF] += ADC_DMA_Buff[CHANNEL_VREF];

ADC_Average_Buff[CHANNEL_VBAT] += ADC_DMA_Buff[CHANNEL_VBAT];

ADC_Average_Buff[CHANNEL_CURRENT] += ADC_DMA_Buff[CHANNEL_CURRENT];

ADC_Average_Buff[CHANNEL_TEMPERATURE] += ADC_DMA_Buff[CHANNEL_TEMPERATURE];

//-------------------------------------------------------------

if (ADC_Samples >= ADC_SAMPLES - 1)

{

//------------------------------

// Voltage Reference calculation

if (ADC_Average_Buff[CHANNEL_VREF] > 0)

{

VoltageReference_Dig = (uint16_t) ADC_Average_Buff[CHANNEL_VREF] / ADC_SAMPLES;

VoltageReference = (uint16_t) __LL_ADC_CALC_VREFANALOG_VOLTAGE( VoltageReference_Dig, LL_ADC_RESOLUTION_12B);

}

else

{

VoltageReference = VDDA_APPLI;

// WARNING: Measure error, action: pending.

}

//------------------------------

// Voltage Battery calculation

if (ADC_Average_Buff[CHANNEL_VBAT] > 0)

{

VoltageBattery_Dig = (uint16_t) ADC_Average_Buff[CHANNEL_VBAT]/ ADC_SAMPLES;

VoltageBattery_Pin = (uint16_t) __LL_ADC_CALC_DATA_TO_VOLTAGE( VoltageReference, VoltageBattery_Dig,LL_ADC_RESOLUTION_12B);

ConversionFloat_Auxiliar = (float) (VoltageBattery_Pin);

if (ConversionFloat_Auxiliar > 0)

{

ConversionFloat_Auxiliar = ((((ConversionFloat_Auxiliar / 1000) / ADC_VBAT_R2) * (ADC_VBAT_R2 + ADC_VBAT_R1)) * 1000)+ ADC_VBAT_OFFSET;

//ConversionFloat_Auxiliar /= 1000;

}

else

{

ConversionFloat_Auxiliar = 0;

}

VoltageBattery = (uint16_t)ConversionFloat_Auxiliar;

}

else

{

// WARNING: Measure error, action: pending.

}

//------------------------------

// Current Motor calculation

if (ADC_Average_Buff[CHANNEL_CURRENT] > 0) // index == 1

{

VoltageMotorShunt_Dig = (uint16_t) ADC_Average_Buff[CHANNEL_CURRENT] / ADC_SAMPLES;

VoltageMotorShunt_Pin = (uint16_t) __LL_ADC_CALC_DATA_TO_VOLTAGE( VoltageReference, VoltageMotorShunt_Dig,

LL_ADC_RESOLUTION_12B);

ConversionFloat_Auxiliar = (float) (VoltageMotorShunt_Pin);

if (ConversionFloat_Auxiliar > 0)

{

ConversionFloat_Auxiliar = (((ConversionFloat_Auxiliar / 1000)/ ADC_SHUNT) * 1000) + ADC_MOTOR_SHUNT_OFFSET;

}

else

{

ConversionFloat_Auxiliar = 0;

}

CurrentMotor = ConversionFloat_Auxiliar;

}

else

{

// WARNING: Measure error, action: pending.

}

//------------------------------

// Temperature calculation

if (ADC_Average_Buff[CHANNEL_TEMPERATURE] > 0)

{

Temperature_Dig = (uint16_t) ADC_Average_Buff[CHANNEL_TEMPERATURE] / ADC_SAMPLES;

Temperature = 100*(int16_t) __LL_ADC_CALC_TEMPERATURE(VoltageReference, Temperature_Dig, LL_ADC_RESOLUTION_12B);

}

else

{

// WARNING: Measure error, action: pending.

}

//------------------------------

// Initialization buffers

ADC_Samples = 0;

ADC_Average_Buff[CHANNEL_VREF] = 0;

ADC_Average_Buff[CHANNEL_VBAT] = 0;

ADC_Average_Buff[CHANNEL_CURRENT] = 0;

ADC_Average_Buff[CHANNEL_TEMPERATURE] = 0;

//------------------------------



//------------------------------

}

else

{

ADC_Samples++;

}

} // End HAL_ADC_ConvCpltCallback

As the reader can see, I have added the __attribute__((used)) but it is still being optimized away. I also check if the ADC_DMAConvCplt or HAL_ADC_IRQHandler or DMA1_Channel1_IRQHandler are being called, but they are not being called.  

what should I do next?

Thanks for all. 

5 replies

Ozone
Principal
June 16, 2025

First, there is a "insert code sample" menu entry, which makes code snippets like yours much more readable.

Ozone_0-1750058081255.png

Second, declare your ADC_Average_Buff array volatile.
It is highly recommended (read: required) for variables changed in interrupt context anyway.

> __attribute__((used)) void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc1)

It would be new to me that "__attribute__((used))" has any effect on functions.

 

Technical Moderator
June 16, 2025

Hello @carlesls2 

Did you try to disable optimization for the callback function? 

#pragma GCC push_options
#pragma GCC optimize ("O0") // Disable optimization for this function
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) {
 // Your callback implementation
}
#pragma GCC pop_options
"To give better visibility on the answered topics, please click on ""Accept as Solution"" on the reply which solved your issue or answered your question.Saket_Om"
carlesls2
carlesls2Author
Associate III
June 16, 2025

what is push_options and pop_options?

 

#pragma GCC push_options

 

#pragma GCC pop_options

 Thank you.

Technical Moderator
June 16, 2025

Hello @carlesls2 

Please check the GCC manual

Saket_Om_0-1750065481181.png

 

"To give better visibility on the answered topics, please click on ""Accept as Solution"" on the reply which solved your issue or answered your question.Saket_Om"
Andrew Neil
Super User
June 16, 2025

@carlesls2 wrote:

the HAL_ADC_ConvCpltCallback has been optimized away.


How are you sure that the function has been optimised away?

As @Ozone suggests, if you haven't qualified things which get modified in the callback as volatile, then accesses to those variables may get optimised-out.

 


@carlesls2 wrote:

I also check if the ADC_DMAConvCplt or HAL_ADC_IRQHandler or DMA1_Channel1_IRQHandler are being called, but they are not being called.  


Sounds like the hardware interrupts are not happening...

A complex system that works is invariably found to have evolved from a simple system that worked.A complex system designed from scratch never works and cannot be patched up to make it work.
carlesls2
carlesls2Author
Associate III
June 16, 2025

carlesls2_0-1750068065763.png

I does not seem optimized away... it exists in the .map file. I guess this is not discarted... it seems not optimized away but it is never called. Yet this occurs when the optimization is applied. 

Ozone
Principal
June 16, 2025

These callbacks are called from within the interrupt handler - hence their name.
I don't use Cube/HAL code, so you would need to check the respective ADC interrupt handler implementation. But they are supposedly called from there.

Perhaps some conditional code, or some preprocessor conditions (#if <..>) applying.
You could set a debug breakpoint in the handler, and step through.
Only avoid opening a interrupt status register view.

TDK
Super User
June 16, 2025

> And then I realize that the HAL_ADC_ConvCpltCallback has been optimized away.

Why do you think it's optimized away? Convince us.

 

Ensure ADC_Average_Buff is set to volatile.

"If you feel a post has answered your question, please click ""Accept as Solution""."
carlesls2
carlesls2Author
Associate III
June 17, 2025

I am not sure but whenever the optimization is applied (when I apply the -Og optimization) the HAL_ADC_ConvCpltCallback is never triggered. The It may not be optimized away but this occurs because of the optimization. 

I created a new smaller project to see if the HAL_ADC_ConvCpltCallback is being optimized away and it is not.

Ozone
Principal
June 17, 2025

To be more explicit - apply your "-Og" optimisation settings, and set a breakpoint in the ADC interrupt handler before the branch to the callback(s).

Unless a simple code review reveals the reason.

Karl Yamashita
Principal
June 17, 2025

That's a lot of stuff going on inside of the callback. That's bad practice. You should only save the data, set a flag, and do your calculations in the main loop

bool adc_rdy = false;

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc1) {
	ADC_Average_Buff[CHANNEL_VREF] = ADC_DMA_Buff[CHANNEL_VREF];
	ADC_Average_Buff[CHANNEL_VBAT] = ADC_DMA_Buff[CHANNEL_VBAT];
	ADC_Average_Buff[CHANNEL_CURRENT] = ADC_DMA_Buff[CHANNEL_CURRENT];
	ADC_Average_Buff[CHANNEL_TEMPERATURE] = ADC_DMA_Buff[CHANNEL_TEMPERATURE];
	ADC_Average_Buff[CHANNEL_VREF] += ADC_DMA_Buff[CHANNEL_VREF];
	ADC_Average_Buff[CHANNEL_VBAT] += ADC_DMA_Buff[CHANNEL_VBAT];
	ADC_Average_Buff[CHANNEL_CURRENT] += ADC_DMA_Buff[CHANNEL_CURRENT];
	ADC_Average_Buff[CHANNEL_TEMPERATURE] += ADC_DMA_Buff[CHANNEL_TEMPERATURE];
	adc_rdy = true;
} // End HAL_ADC_ConvCpltCallback


void ADC_Calculate(void) {
	if (adc_rdy) {
		adc_rdy = false;
		//-------------------------------------------------------------
		if (ADC_Samples >= ADC_SAMPLES - 1)
		{
			//------------------------------
			// Voltage Reference calculation
			if (ADC_Average_Buff[CHANNEL_VREF] > 0)
			{
				VoltageReference_Dig = (uint16_t) ADC_Average_Buff[CHANNEL_VREF]
						/ ADC_SAMPLES;
				VoltageReference = (uint16_t) __LL_ADC_CALC_VREFANALOG_VOLTAGE(
						VoltageReference_Dig, LL_ADC_RESOLUTION_12B);
			}
			else
			{
				VoltageReference = VDDA_APPLI;
				// WARNING: Measure error, action: pending.
			}
			//------------------------------
			// Voltage Battery calculation
			if (ADC_Average_Buff[CHANNEL_VBAT] > 0)
			{
				VoltageBattery_Dig = (uint16_t) ADC_Average_Buff[CHANNEL_VBAT]
						/ ADC_SAMPLES;
				VoltageBattery_Pin = (uint16_t) __LL_ADC_CALC_DATA_TO_VOLTAGE(
						VoltageReference, VoltageBattery_Dig,
						LL_ADC_RESOLUTION_12B);
				ConversionFloat_Auxiliar = (float) (VoltageBattery_Pin);
				if (ConversionFloat_Auxiliar > 0)
				{
					ConversionFloat_Auxiliar =
							((((ConversionFloat_Auxiliar / 1000) / ADC_VBAT_R2)
									* (ADC_VBAT_R2 + ADC_VBAT_R1)) * 1000)
									+ ADC_VBAT_OFFSET;
					//ConversionFloat_Auxiliar /= 1000;
				}
				else
				{
					ConversionFloat_Auxiliar = 0;
				}
				VoltageBattery = (uint16_t) ConversionFloat_Auxiliar;
			}
			else
			{
				// WARNING: Measure error, action: pending.
			}
			//------------------------------
			// Current Motor calculation
			if (ADC_Average_Buff[CHANNEL_CURRENT] > 0) // index == 1
					{
				VoltageMotorShunt_Dig =
						(uint16_t) ADC_Average_Buff[CHANNEL_CURRENT]
								/ ADC_SAMPLES;
				VoltageMotorShunt_Pin =
						(uint16_t) __LL_ADC_CALC_DATA_TO_VOLTAGE(
								VoltageReference, VoltageMotorShunt_Dig,
								LL_ADC_RESOLUTION_12B);
				ConversionFloat_Auxiliar = (float) (VoltageMotorShunt_Pin);
				if (ConversionFloat_Auxiliar > 0)
				{
					ConversionFloat_Auxiliar = (((ConversionFloat_Auxiliar
							/ 1000) / ADC_SHUNT) * 1000)
							+ ADC_MOTOR_SHUNT_OFFSET;
				}
				else
				{
					ConversionFloat_Auxiliar = 0;
				}
				CurrentMotor = ConversionFloat_Auxiliar;
			}
			else
			{
				// WARNING: Measure error, action: pending.
			}
			//------------------------------
			// Temperature calculation
			if (ADC_Average_Buff[CHANNEL_TEMPERATURE] > 0)
			{
				Temperature_Dig =
						(uint16_t) ADC_Average_Buff[CHANNEL_TEMPERATURE]
								/ ADC_SAMPLES;
				Temperature = 100
						* (int16_t) __LL_ADC_CALC_TEMPERATURE(VoltageReference,
								Temperature_Dig, LL_ADC_RESOLUTION_12B);
			}
			else
			{
				// WARNING: Measure error, action: pending.
			}
			//------------------------------
			// Initialization buffers
			ADC_Samples = 0;
			ADC_Average_Buff[CHANNEL_VREF] = 0;
			ADC_Average_Buff[CHANNEL_VBAT] = 0;
			ADC_Average_Buff[CHANNEL_CURRENT] = 0;
			ADC_Average_Buff[CHANNEL_TEMPERATURE] = 0;
			//------------------------------
			//------------------------------
		}
		else
		{
			ADC_Samples++;
		}
	}
}

int main(void) {
	// rest of ST's code here

	while (1) {
		ADC_Calculate();
	}
}

 

As others have mentioned, what makes you think the callback is being optimized out?

Have you tried to debug, place a break point in the callback to see if it does interrupt?

Have you called HAL_ADCEx_Calibration_Start prior to starting the ADC conversion?

Are you using a timer to periodically start the ADC conversion? Using DMA in circular mode?

If a reply has proven helpful, click on Accept as Solution so that it'll show at top of the post.CAN Jammer an open source CAN bus hacking toolCANableV3 Open Source
Ozone
Principal
June 17, 2025

> That's a lot of stuff going on inside of the callback. That's bad practice. You should only save the data, set a flag, and do your calculations in the main loop ...

Now that you say it ...
I thought this callback consisted mostly of the copy operation, i.e. the first lines.
I didn't realize all the other code after that is part of this callback as well ... 
To my defence, there is no visible indention, and it's easy to overlook.

Another point not yet mentioned - this handler code is using floating point operations.
This involves a costly emulation on M0+ MCUs like this, and stack issues on M4/M7 MCUs.
While it is easier on an abstract C level, there is no accuracy benefit for float values here.