2025-11-19 6:49 AM
We're using the following circuit to measure a battery voltage @ 20 kHz for use in motor control.
We are having some issues though, where the ADC suddenly starts measuring 0 V (0 in the data register) and gets locked in this state. In this state, we also measured 0 V at the MCU pin on the board, and a resistance < 20 Ohm between the pin and GND, both using a multimeter. The issue is not fixed by a reset (pulse on the NRST pin), only removing the battery and power cycling the MCU.
How and why can/does this happen?
We initially thought it was overvoltage and tried connecting a PSU directly to an ADC pin on the STM32G474RE NUCLEO board. We only started seeing abnormalities at 6 V and destroyed the ADC at around 9 V, so this does not seem to be the reason.
The ADC is clocked 170/4 = 42.5 MHz and configured as follows (from IOC file):
ADC1.CommonPathInternal=null|null|null|null
ADC1.DMAContinuousRequests=DISABLE
ADC1.EOCSelection=ADC_EOC_SEQ_CONV
ADC1.EnableAnalogWatchDog1=false
ADC1.EnableInjectedConversion=ENABLE
ADC1.EnableRegularConversion=DISABLE
ADC1.ExternalTrigInjecConv=ADC_EXTERNALTRIGINJEC_T1_TRGO
ADC1.IPParameters=EOCSelection,EnableRegularConversion,EnableInjectedConversion,EnableAnalogWatchDog1,DMAContinuousRequests,master,InjectedRank-10\#ChannelInjectedConversion,InjectedChannel-10\#ChannelInjectedConversion,Rank1_Channel,InjectedSamplingTime-10\#ChannelInjectedConversion,InjectedOffsetNumber-10\#ChannelInjectedConversion,Rank2_Channel,InjNumberOfConversion,ExternalTrigInjecConv,Overrun,InjectedRank-13\#ChannelInjectedConversion,InjectedChannel-13\#ChannelInjectedConversion,InjectedSamplingTime-13\#ChannelInjectedConversion,InjectedOffsetNumber-13\#ChannelInjectedConversion,CommonPathInternal
ADC1.InjNumberOfConversion=2
ADC1.InjectedChannel-10\#ChannelInjectedConversion=ADC_CHANNEL_3
ADC1.InjectedChannel-13\#ChannelInjectedConversion=ADC_CHANNEL_2
ADC1.InjectedOffsetNumber-10\#ChannelInjectedConversion=ADC_OFFSET_NONE
ADC1.InjectedOffsetNumber-13\#ChannelInjectedConversion=ADC_OFFSET_NONE
ADC1.InjectedRank-10\#ChannelInjectedConversion=1
ADC1.InjectedRank-13\#ChannelInjectedConversion=2
ADC1.InjectedSamplingTime-10\#ChannelInjectedConversion=ADC_SAMPLETIME_2CYCLES_5
ADC1.InjectedSamplingTime-13\#ChannelInjectedConversion=ADC_SAMPLETIME_2CYCLES_5
ADC1.Overrun=ADC_OVR_DATA_OVERWRITTEN
ADC1.Rank1_Channel=ADC_CHANNEL_3
ADC1.Rank2_Channel=ADC_CHANNEL_2
ADC1.master=1
PA2.Mode=IN3-Single-Ended
PA2.Signal=ADC1_IN3And the following code (generated mostly by CubeMX) is used for initialisation
/* ADC1 init function */
void MX_ADC1_Init(void)
{
/* USER CODE BEGIN ADC1_Init 0 */
/* USER CODE END ADC1_Init 0 */
ADC_MultiModeTypeDef multimode = {0};
ADC_InjectionConfTypeDef sConfigInjected = {0};
/* USER CODE BEGIN ADC1_Init 1 */
/* USER CODE END ADC1_Init 1 */
/** Common config
*/
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.GainCompensation = 0;
hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
hadc1.Init.OversamplingMode = DISABLE;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure the ADC multi-mode
*/
multimode.Mode = ADC_MODE_INDEPENDENT;
if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
{
Error_Handler();
}
/** Configure Injected Channel
*/
sConfigInjected.InjectedChannel = ADC_CHANNEL_3;
sConfigInjected.InjectedRank = ADC_INJECTED_RANK_1;
sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_2CYCLES_5;
sConfigInjected.InjectedSingleDiff = ADC_SINGLE_ENDED;
sConfigInjected.InjectedOffsetNumber = ADC_OFFSET_NONE;
sConfigInjected.InjectedOffset = 0;
sConfigInjected.InjectedNbrOfConversion = 2;
sConfigInjected.InjectedDiscontinuousConvMode = DISABLE;
sConfigInjected.AutoInjectedConv = DISABLE;
sConfigInjected.QueueInjectedContext = DISABLE;
sConfigInjected.ExternalTrigInjecConv = ADC_EXTERNALTRIGINJEC_T1_TRGO;
sConfigInjected.ExternalTrigInjecConvEdge = ADC_EXTERNALTRIGINJECCONV_EDGE_RISING;
sConfigInjected.InjecOversamplingMode = DISABLE;
if (HAL_ADCEx_InjectedConfigChannel(&hadc1, &sConfigInjected) != HAL_OK)
{
Error_Handler();
}
/** Configure Injected Channel
*/
sConfigInjected.InjectedChannel = ADC_CHANNEL_2;
sConfigInjected.InjectedRank = ADC_INJECTED_RANK_2;
if (HAL_ADCEx_InjectedConfigChannel(&hadc1, &sConfigInjected) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC1_Init 2 */
HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
HAL_Delay(1);
ADC1->IER |= ADC_IER_JEOSIE; // Enable end of injected sequence of conversion interrupt
HAL_ADC_Start(&hadc1);
ADC1->CR |= ADC_CR_JADSTART; // Enable injected conversions on the selected trigger
/* USER CODE END ADC1_Init 2 */
}
2025-11-19 6:56 AM
This sounds like latch-up behavior.
Prevention would depend on where this is occurring. If this is a motor application, strong diodes preventing negative voltages and overvoltages where they shouldn't be can help. Nothing wrong with the circuit shown.