Skip to main content
Graduate II
June 16, 2025
Question

HAL_ADC_ConvCpltCallback optimized away

  • June 16, 2025
  • 5 replies
  • 954 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. 

    This topic has been closed for replies.

    5 replies

    Explorer
    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
    carlesls2Author
    Graduate II
    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

     

    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...

    carlesls2Author
    Graduate II
    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. 

    Explorer
    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.

    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.

    carlesls2Author
    Graduate II
    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.

    Explorer
    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.

    Graduate II
    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?

    Explorer
    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.