2025-06-15 11:39 PM - last edited on 2025-06-16 2:22 AM by Andrew Neil
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.
2025-06-17 1:10 AM
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.
2025-06-17 1:45 AM
The call back is called in ADC_DMAConvCplt and in HAL_ADC_IRQHandler. I apply the attribute used and I also apply optimize("O0"), but no luck. They are never called.
2025-06-17 3:35 AM
@Ozone wrote: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.
I will redo the entire project commit by commit and check when the callback goes away... and then tell you, is it ok?
2025-06-17 4:10 AM
The map file shows it doesn't go away, isn't that right ?
And you never really convinced us that it is optimised out in the first place.
I suspect it is still called, but only the buffer accesses are optimized out because you failed to declare it as "volatile".
Why can't you fire up the debugger, and look what really happens ?
2025-06-17 4:19 AM
I debugged the code... never enters inside the call back or its parents...
Every single variable inside callback is volatile. Could it be that since the callback and interruptions must be short the optimization do not tolerate many even volatile varibles inside the callback?
Thanks for all.
Regards, Carles.
2025-06-17 4:43 AM
@carlesls2 wrote:never enters inside the call back or its parents...
So it never enters the hardware interrupt handler ?
That means your interrupts are not happening at the hardware level.
Check that interrupts are enabled - both in the peripheral itself, and in the NVIC.
@carlesls2 wrote:Could it be that since the callback and interruptions must be short the optimization do not tolerate many even volatile varibles inside the callback?.
No. The optimiser knows nothing about interrupts
2025-06-17 4:58 AM
> I debugged the code... never enters inside the call back or its parents...
The "parent" of this callback would be the ADC interrupt itself.
If the code does not come across this interrupt vector, there is probably a problem with your ADC configuration.
> Could it be that since the callback and interruptions must be short the optimization do not tolerate many even volatile varibles inside the callback?
No, not per se.
In theory, your interrupt routine can take as long as it wants/needs, as long as it does not interfere with other interrupts, and eventually itself (a.k.a. overflow). The latter often happens e.g. when you call debug printf functionality from interrupt context.
The "volatile" keyword is not so special. It only tells the compiler that a data item is referenced from elsewhere, outside it's scope. The C language itself comes from a high-level OS background, and has no concept of interrupts - functions which are never called explicitly. "Volatile" forces an explicit read or write with every access.
2025-06-17 5:57 AM
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?
2025-06-17 6:41 AM
> 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.