Skip to main content
Associate
July 9, 2026
Question

ADC stuck at constant code for while ISR shows no error — looking for root cause hints

  • July 9, 2026
  • 13 replies
  • 153 views

Setup

  - MCU: STM32U595VITxQ (Cortex-M33)
  - ADC: ADC4, 57Mhz,12-bit, 7 channels, 6400 Hz sample rate, triggered by TIM1
  - Data path: GPDMA1 in linked-list circular mode, double-buffered with half/complete callbacks (128 samples per half-buffer). DMA writes directly into a static INT16U s_adc4_result[256][7] buffer in normal SRAM.
  - Signal chain: external current/voltage sensors → signal conditioning board (op-amps, anti-alias filter, DC bias) → ADC4 input pins
  - Firmware: FreeRTOS, half-callback copies 128 samples per channel into a ring buffer (PF_TOTAL_WAVE_NUM = 40 slots × 128 samples), pf_task consumes from queue

  Symptom

  Most of time, everything works fine. But some times something wrong happened,in a log file we captured, channels simultaneously read byte-identical constant values.

Questions:

  1. Has anyone seen an STM32U5 ADC report "healthy" (no OVR, no error, ADSTART set) while consistently delivering a constant code per channel? Are there silicon errata I should check?
  2. Are there known ADC4 / GPDMA1 quirks on STM32U595 (linked-list circular mode, TIM-triggered regular conversions) that could produce a "freeze the last good sample and keep DMA-cycling it" behavior?
  3. Could a VREF+/VDDA glitch cause the ADC to digitize a fixed code without setting any error flag? c
  4. Any other diagnostic register / signal I should capture next time it happens to definitively separate "ADC digitizing a clamped analog input" from "ADC/DMA digital block misbehaving"? I can add logging on the next firmware build.

 

 

13 replies

Ozone
Principal
July 9, 2026

> Most of time, everything works fine. But some times something wrong happened,in a log file we captured, channels simultaneously read byte-identical constant values.

This sounds like a race condition is involved.
Either another interrupt, or a task change in FreeRTOS.
The ADC is probably not the cause, I would look at the DMA, involved routines/handlers, and neighboring memory (map file).

You could try to set a write access breakpoint in the debugger, assuming a DMA access would not trigger it.
Or perhaps a stack overflow.

 

Edward_kAuthor
Associate
July 9, 2026

Thanks, i will check this situation carefully. But i think this is not the only case, some people meet this issue in other chips either.
 

 

Ozone
Principal
July 9, 2026

I have no specific experience with STM32U devices, but used ADC+DMA frequently on some F, C and L devices.

Another option would be that the DMA configuration gets corrupted at runtime, i.e. overwriting the ADCn.DR as source register. Although this seems not very likely.

Devices that have dual (or triple) modes have special configuration and result registers in the ADC register interface, perhaps this plays a role here.


On a loosely related note, a common “race condition” bug with RTOSes is stack allocation, at least in RTOSes that implement RT functionality in cyclically called sequential functions, not endless loops.
Pointers to local (stack) variables become invalid when once a cycle finishes.
If you access this pointer in subsequent cycles, you destroy other variables.

Edward_kAuthor
Associate
July 10, 2026

Thank you. I have already checked all the possibilities mentioned above and found no issues. Moreover, I am unable to reproduce this problem in the lab, but it does occur at the actual field site. I suspect it might be due to ESD (electrostatic discharge) interference. see this. 

 

Ozone
Principal
July 10, 2026

> I am unable to reproduce this problem in the lab, but it does occur at the actual field site. I suspect it might be due to ESD (electrostatic discharge) interference. see this. 

Possibly, but AFAIK such kind of behavior is normal and allowed for certain ESD tests, requiring a power cycle afterwards to get the device under test back to normal operation.

You could review the schematics and improve EMI protection of relevent inputs if this turns out to be the case.

Since there is no code shared, some general thoughts and ideas …

First, how many channels are affected, and is the error pattern consistent ?

Second, have you checked the time budget of your sampling chain ?
By that I mean, are the conversions definitely finished and transferred by DMA when the timer triggers the next sequence ?

For reproducing it in the lab, you could increase the load caused by other channels and tasks, especially those which involve interrupts and / or DMA.
Perhaps instrumenting the code to visualize events and tasks, create a (scope) trigger for the error event, and store/output relevant status information.

Edward_kAuthor
Associate
July 13, 2026

We’ve got 7 sampling channels, and I can confirm the first 3 are definitely affected.

 

ADC1: 2channels, trigger every 1us from TM6;

ADC2: 1channel, trigger every 0.5us from TIM15;

ADC4: 7channels, trigger every 156.25us from TIM1;

 

The ADC clock is 57MHz, over the 55MHz spec. Do you think this could be causing the problem?
 

Shown below is the code related to ADC TIMER, DMA, and interrupt handling.
 

Timer initialize

 

/* TIM1 init function */

void MX_TIM1_Init(void)

{

    TIM_ClockConfigTypeDef sClockSourceConfig = {0};

    TIM_MasterConfigTypeDef sMasterConfig = {0};

 

    htim1.Instance = TIM1;

    htim1.Init.Prescaler = 9;

    htim1.Init.CounterMode = TIM_COUNTERMODE_UP;

    htim1.Init.Period = 2499;

    htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

    htim1.Init.RepetitionCounter = 0;

    htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;

    if (HAL_TIM_Base_Init(&htim1) != HAL_OK) {

        Error_Handler();

    }

    sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;

    if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK) {

        Error_Handler();

    }

    sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;

    sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_UPDATE;

    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;

    if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK) {

        Error_Handler();

    }

}

 

/* TIM6 init function */

void MX_TIM6_Init(void)

{

    TIM_ClockConfigTypeDef sClockSourceConfig = {0};

    TIM_MasterConfigTypeDef sMasterConfig = {0};

 

    htim6.Instance = TIM6;

    htim6.Init.Prescaler = 0;

    htim6.Init.CounterMode = TIM_COUNTERMODE_UP;

    htim6.Init.Period = 159;

    htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;

    if (HAL_TIM_Base_Init(&htim6) != HAL_OK) {

        Error_Handler();

    }

    sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;

    if (HAL_TIM_ConfigClockSource(&htim6, &sClockSourceConfig) != HAL_OK) {

        Error_Handler();

    }

    sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;

    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;

    if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK) {

        Error_Handler();

    }

}

 

/* TIM15 init function */

void MX_TIM15_Init(void)

{

    TIM_ClockConfigTypeDef sClockSourceConfig = {0};

    TIM_MasterConfigTypeDef sMasterConfig = {0};

 

    htim15.Instance = TIM15;

    htim15.Init.Prescaler = 0;

    htim15.Init.CounterMode = TIM_COUNTERMODE_UP;

    htim15.Init.Period = 79;

    htim15.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

    htim15.Init.RepetitionCounter = 0;

    htim15.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;

    if (HAL_TIM_Base_Init(&htim15) != HAL_OK) {

        Error_Handler();

    }

    sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;

    if (HAL_TIM_ConfigClockSource(&htim15, &sClockSourceConfig) != HAL_OK) {

        Error_Handler();

    }

    sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;

    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;

    if (HAL_TIMEx_MasterConfigSynchronization(&htim15, &sMasterConfig) != HAL_OK) {

        Error_Handler();

    }

}

 

ADC initialize

/* ADC1 init function */

void MX_ADC1_Init(void)

{

    ADC_ChannelConfTypeDef sConfig = {0};

 

    /* Common config */

    hadc1.Instance = ADC1;

    hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;

    hadc1.Init.Resolution = ADC_RESOLUTION_14B;

    hadc1.Init.GainCompensation = 0;

    hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;

    hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;

    hadc1.Init.LowPowerAutoWait = DISABLE;

    hadc1.Init.ContinuousConvMode = DISABLE;

    hadc1.Init.DiscontinuousConvMode = DISABLE;

    hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;

    hadc1.Init.NbrOfConversion = 2;

    hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T6_TRGO;

    hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;

    hadc1.Init.DMAContinuousRequests = ENABLE;

    hadc1.Init.TriggerFrequencyMode = ADC_TRIGGER_FREQ_HIGH;

    hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;

    hadc1.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;

    hadc1.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR;

    hadc1.Init.OversamplingMode = DISABLE;

    if (HAL_ADC_Init(&hadc1) != HAL_OK) {

        Error_Handler();

    }

 

    /* Configure Regular Channel 9 */

    sConfig.Channel = ADC_CHANNEL_9;

    sConfig.Rank = ADC_REGULAR_RANK_1;

    sConfig.SamplingTime = ADC_SAMPLETIME_6CYCLES;

    sConfig.SingleDiff = ADC_SINGLE_ENDED;

    sConfig.OffsetNumber = ADC_OFFSET_NONE;

    sConfig.Offset = 0;

    if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) {

        Error_Handler();

    }

   

    /* Configure Regular Channel 16 */

    sConfig.Channel = ADC_CHANNEL_16;

    sConfig.Rank = ADC_REGULAR_RANK_2;

    sConfig.SamplingTime = ADC_SAMPLETIME_12CYCLES;

    if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) {

        Error_Handler();

    }

}

 

/* ADC2 init function */

void MX_ADC2_Init(void)

{

    ADC_ChannelConfTypeDef sConfig = {0};

 

    /* Common config */

    hadc2.Instance = ADC2;

    hadc2.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;

    hadc2.Init.Resolution = ADC_RESOLUTION_14B;

    hadc2.Init.GainCompensation = 0;

    hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;

    hadc2.Init.ScanConvMode = ADC_SCAN_DISABLE;

    hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV;

    hadc2.Init.LowPowerAutoWait = DISABLE;

    hadc2.Init.ContinuousConvMode = DISABLE;

    hadc2.Init.NbrOfConversion = 1;

    hadc2.Init.DiscontinuousConvMode = DISABLE;

    hadc2.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T15_TRGO;

    hadc2.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;

    hadc2.Init.DMAContinuousRequests = ENABLE;

    hadc2.Init.TriggerFrequencyMode = ADC_TRIGGER_FREQ_HIGH;

    hadc2.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;

    hadc2.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;

    hadc2.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR;

    hadc2.Init.OversamplingMode = DISABLE;

    if (HAL_ADC_Init(&hadc2) != HAL_OK) {

        Error_Handler();

    }

 

    /* Configure Regular Channel */

    sConfig.Channel = ADC_CHANNEL_7;

    sConfig.Rank = ADC_REGULAR_RANK_1;

    sConfig.SamplingTime = ADC_SAMPLETIME_6CYCLES;

    sConfig.SingleDiff = ADC_SINGLE_ENDED;

    sConfig.OffsetNumber = ADC_OFFSET_NONE;

    sConfig.Offset = 0;

    if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK) {

        Error_Handler();

    }

}

 

/* ADC4 init function */

void MX_ADC4_Init(void)

{

    ADC_ChannelConfTypeDef sConfig = {0};

 

    /* Common config */

    hadc4.Instance = ADC4;

    hadc4.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;

    hadc4.Init.Resolution = ADC_RESOLUTION_12B;

    hadc4.Init.DataAlign = ADC_DATAALIGN_RIGHT;

    hadc4.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;

    hadc4.Init.EOCSelection = ADC_EOC_SINGLE_CONV;

    hadc4.Init.LowPowerAutoPowerOff = ADC_LOW_POWER_NONE;

    hadc4.Init.LowPowerAutoWait = DISABLE;

    hadc4.Init.ContinuousConvMode = DISABLE;

    hadc4.Init.NbrOfConversion = 1;

    hadc4.Init.DiscontinuousConvMode = DISABLE;

    hadc4.Init.ExternalTrigConv = ADC4_EXTERNALTRIG_T1_TRGO2;

    hadc4.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;

    hadc4.Init.DMAContinuousRequests = ENABLE;

    hadc4.Init.TriggerFrequencyMode = ADC_TRIGGER_FREQ_LOW;

    hadc4.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;

    hadc4.Init.SamplingTimeCommon1 = ADC4_SAMPLETIME_39CYCLES_5;

    hadc4.Init.SamplingTimeCommon2 = ADC4_SAMPLETIME_39CYCLES_5;

    hadc4.Init.OversamplingMode = ENABLE;

    hadc4.Init.Oversampling.Ratio = ADC_OVERSAMPLING_RATIO_16;

    hadc4.Init.Oversampling.RightBitShift = ADC_RIGHTBITSHIFT_2;

    hadc4.Init.Oversampling.TriggeredMode = ADC_TRIGGEREDMODE_SINGLE_TRIGGER;

    if (HAL_ADC_Init(&hadc4) != HAL_OK) {

        Error_Handler();

    }

 

    /* Configure Regular Channel */

    sConfig.Channel = ADC_CHANNEL_2;

    sConfig.Rank = ADC4_RANK_CHANNEL_NUMBER;

    sConfig.SamplingTime = ADC4_SAMPLINGTIME_COMMON_1;

    sConfig.OffsetNumber = ADC_OFFSET_NONE;

    sConfig.Offset = 0;

    if (HAL_ADC_ConfigChannel(&hadc4, &sConfig) != HAL_OK) {

        Error_Handler();

    }

    /* Configure Regular Channel */

    sConfig.Channel = ADC_CHANNEL_3;

    if (HAL_ADC_ConfigChannel(&hadc4, &sConfig) != HAL_OK) {

        Error_Handler();

    }

 

    /* Configure Regular Channel */

    sConfig.Channel = ADC_CHANNEL_4;

    if (HAL_ADC_ConfigChannel(&hadc4, &sConfig) != HAL_OK) {

        Error_Handler();

    }

 

    /* Configure Regular Channel */

    sConfig.Channel = ADC_CHANNEL_11;

    if (HAL_ADC_ConfigChannel(&hadc4, &sConfig) != HAL_OK) {

        Error_Handler();

    }

 

    /* Configure Regular Channel */

    sConfig.Channel = ADC_CHANNEL_18;

    if (HAL_ADC_ConfigChannel(&hadc4, &sConfig) != HAL_OK) {

        Error_Handler();

    }

 

    /* Configure Regular Channel */

    sConfig.Channel = ADC_CHANNEL_20;

    if (HAL_ADC_ConfigChannel(&hadc4, &sConfig) != HAL_OK) {

        Error_Handler();

    }

}

 

void HAL_ADC_MspInit(ADC_HandleTypeDef *adcHandle)

{

    GPIO_InitTypeDef GPIO_InitStruct = {0};

    RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

    if (adcHandle->Instance == ADC1) {

        /* Initializes the peripherals clock */

        PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADCDAC;

        PeriphClkInit.AdcDacClockSelection = RCC_ADCDACCLKSOURCE_PLL2;

        PeriphClkInit.PLL2.PLL2Source = RCC_PLLSOURCE_HSE;

        PeriphClkInit.PLL2.PLL2M = 2;

        PeriphClkInit.PLL2.PLL2N = 57;

        PeriphClkInit.PLL2.PLL2P = 2;

        PeriphClkInit.PLL2.PLL2Q = 2;

        PeriphClkInit.PLL2.PLL2R = 8;

        PeriphClkInit.PLL2.PLL2RGE = RCC_PLLVCIRANGE_1;

        PeriphClkInit.PLL2.PLL2FRACN = 0;

        PeriphClkInit.PLL2.PLL2ClockOut = RCC_PLL2_DIVR;

        if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) {

            Error_Handler();

        }

 

        /* ADC12 clock enable */

        __HAL_RCC_ADC12_CLK_ENABLE();

 

        /** ADC1 GPIO Configuration

            PA4     ------> ADC1_IN9

            PA5     ------> ADC1_IN10

            PB1     ------> ADC1_IN16

            PB2     ------> ADC1_IN17

        */

        GPIO_InitStruct.Pin = ADC1_IN09_PIN | ADC1_IN10_PIN;

        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;

        GPIO_InitStruct.Pull = GPIO_NOPULL;

        HAL_GPIO_Init(ADC1_IN0910_PORT, &GPIO_InitStruct);

 

        GPIO_InitStruct.Pin = ADC1_IN16_PIN | ADC1_IN17_PIN;

        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;

        GPIO_InitStruct.Pull = GPIO_NOPULL;

        HAL_GPIO_Init(ADC1_IN1617_PORT, &GPIO_InitStruct);

 

        handle_GPDMA1_Channel[ADC1_DMA_DMACH].Instance = ADC1_DMA_INST;

        handle_GPDMA1_Channel[ADC1_DMA_DMACH].InitLinkedList.Priority = DMA_HIGH_PRIORITY;

        handle_GPDMA1_Channel[ADC1_DMA_DMACH].InitLinkedList.LinkStepMode = DMA_LSM_FULL_EXECUTION;

        handle_GPDMA1_Channel[ADC1_DMA_DMACH].InitLinkedList.LinkAllocatedPort = DMA_LINK_ALLOCATED_PORT0;

        handle_GPDMA1_Channel[ADC1_DMA_DMACH].InitLinkedList.TransferEventMode = DMA_TCEM_LAST_LL_ITEM_TRANSFER;

        handle_GPDMA1_Channel[ADC1_DMA_DMACH].InitLinkedList.LinkedListMode = DMA_LINKEDLIST_CIRCULAR;

        if (HAL_DMAEx_List_Init(&handle_GPDMA1_Channel[ADC1_DMA_DMACH]) != HAL_OK) {

            Error_Handler();

        }

        if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel[ADC1_DMA_DMACH], DMA_CHANNEL_NPRIV) != HAL_OK) {

            Error_Handler();

        }

    } else if (adcHandle->Instance == ADC2) {

        /** ADC2 GPIO Configuration

            PA2     ------> ADC2_IN07

            PA3     ------> ADC2_IN08

        */

        GPIO_InitStruct.Pin = ADC2_IN07_PIN | ADC2_IN08_PIN;

        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;

        GPIO_InitStruct.Pull = GPIO_NOPULL;

        HAL_GPIO_Init(ADC2_IN0708_PORT, &GPIO_InitStruct);

 

        handle_GPDMA1_Channel[ADC2_DMA_DMACH].Instance = ADC2_DMA_INST;

        handle_GPDMA1_Channel[ADC2_DMA_DMACH].InitLinkedList.Priority = DMA_HIGH_PRIORITY;

        handle_GPDMA1_Channel[ADC2_DMA_DMACH].InitLinkedList.LinkStepMode = DMA_LSM_FULL_EXECUTION;

        handle_GPDMA1_Channel[ADC2_DMA_DMACH].InitLinkedList.LinkAllocatedPort = DMA_LINK_ALLOCATED_PORT0;

        handle_GPDMA1_Channel[ADC2_DMA_DMACH].InitLinkedList.TransferEventMode = DMA_TCEM_LAST_LL_ITEM_TRANSFER;

        handle_GPDMA1_Channel[ADC2_DMA_DMACH].InitLinkedList.LinkedListMode = DMA_LINKEDLIST_CIRCULAR;

        if (HAL_DMAEx_List_Init(&handle_GPDMA1_Channel[ADC2_DMA_DMACH]) != HAL_OK) {

            Error_Handler();

        }

        if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel[ADC2_DMA_DMACH], DMA_CHANNEL_NPRIV) != HAL_OK) {

            Error_Handler();

        }

    } else if (adcHandle->Instance == ADC4) {

        /* ADC4 clock enable */

        __HAL_RCC_ADC4_CLK_ENABLE();

 

        /** ADC4 GPIO Configuration

            PC1     ------> ADC4_IN2

            PC2     ------> ADC4_IN3

            PC3     ------> ADC4_IN4

            PA6     ------> ADC4_IN11

            PB0     ------> ADC4_IN18

            PA7     ------> ADC4_IN20

        */

        GPIO_InitStruct.Pin = ADC4_IN02_PIN;

        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;

        GPIO_InitStruct.Pull = GPIO_NOPULL;

        HAL_GPIO_Init(ADC4_IN02_PORT, &GPIO_InitStruct);

 

        GPIO_InitStruct.Pin = ADC4_IN03_PIN;

        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;

        GPIO_InitStruct.Pull = GPIO_NOPULL;

        HAL_GPIO_Init(ADC4_IN03_PORT, &GPIO_InitStruct);

 

        GPIO_InitStruct.Pin = ADC4_IN04_PIN;

        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;

        GPIO_InitStruct.Pull = GPIO_NOPULL;

        HAL_GPIO_Init(ADC4_IN04_PORT, &GPIO_InitStruct);

 

        GPIO_InitStruct.Pin = ADC4_IN11_PIN;

        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;

        GPIO_InitStruct.Pull = GPIO_NOPULL;

        HAL_GPIO_Init(ADC4_IN11_PORT, &GPIO_InitStruct);

 

        GPIO_InitStruct.Pin = ADC4_IN18_PIN;

        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;

        GPIO_InitStruct.Pull = GPIO_NOPULL;

        HAL_GPIO_Init(ADC4_IN18_PORT, &GPIO_InitStruct);

 

        GPIO_InitStruct.Pin = ADC4_IN20_PIN;

        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;

        GPIO_InitStruct.Pull = GPIO_NOPULL;

        HAL_GPIO_Init(ADC4_IN20_PORT, &GPIO_InitStruct);

 

        handle_GPDMA1_Channel[ADC4_DMA_DMACH].Instance = ADC4_DMA_INST;

        handle_GPDMA1_Channel[ADC4_DMA_DMACH].InitLinkedList.Priority = DMA_LOW_PRIORITY_HIGH_WEIGHT;

        handle_GPDMA1_Channel[ADC4_DMA_DMACH].InitLinkedList.LinkStepMode = DMA_LSM_FULL_EXECUTION;

        handle_GPDMA1_Channel[ADC4_DMA_DMACH].InitLinkedList.LinkAllocatedPort = DMA_LINK_ALLOCATED_PORT0;

        handle_GPDMA1_Channel[ADC4_DMA_DMACH].InitLinkedList.TransferEventMode = DMA_TCEM_LAST_LL_ITEM_TRANSFER;

        handle_GPDMA1_Channel[ADC4_DMA_DMACH].InitLinkedList.LinkedListMode = DMA_LINKEDLIST_CIRCULAR;

        if (HAL_DMAEx_List_Init(&handle_GPDMA1_Channel[ADC4_DMA_DMACH]) != HAL_OK) {

            Error_Handler();

        }

        if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel[ADC4_DMA_DMACH], DMA_CHANNEL_NPRIV) != HAL_OK) {

            Error_Handler();

        }

    }

}

 

DMA queue config

/**

  * @brief  DMA Linked-list ADC1Queue configuration

  * @param  None

  * @retval None

  */

HAL_StatusTypeDef MX_ADC1Queue_Config(void)

{

    HAL_StatusTypeDef ret = HAL_OK;

    /* DMA node configuration declaration */

    DMA_NodeConfTypeDef pNodeConfig;

 

    /* Set node configuration ################################################*/

    pNodeConfig.NodeType = DMA_GPDMA_LINEAR_NODE;

    pNodeConfig.Init.Request = GPDMA1_REQUEST_ADC1;

    pNodeConfig.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;

    pNodeConfig.Init.Direction = DMA_PERIPH_TO_MEMORY;

    pNodeConfig.Init.SrcInc = DMA_SINC_FIXED;

    pNodeConfig.Init.DestInc = DMA_DINC_INCREMENTED;

    pNodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_HALFWORD;

    pNodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_HALFWORD;

    pNodeConfig.Init.SrcBurstLength = 1;

    pNodeConfig.Init.DestBurstLength = 1;

    pNodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;

    pNodeConfig.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;

    pNodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;

    pNodeConfig.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE;

    pNodeConfig.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;

    pNodeConfig.SrcAddress = 0;

    pNodeConfig.DstAddress = 0;

    pNodeConfig.DataSize = 0;

 

    /* Build ADC1Node Node */

    ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &ADC1Node);

 

    /* Insert ADC1Node to Queue */

    ret |= HAL_DMAEx_List_InsertNode_Tail(&ADC1Queue, &ADC1Node);

 

    ret |= HAL_DMAEx_List_SetCircularMode(&ADC1Queue);

 

    return ret;

}

 

/**

  * @brief  DMA Linked-list ADC2Queue configuration

  * @param  None

  * @retval None

  */

HAL_StatusTypeDef MX_ADC2Queue_Config(void)

{

    HAL_StatusTypeDef ret = HAL_OK;

    /* DMA node configuration declaration */

    DMA_NodeConfTypeDef pNodeConfig;

 

    /* Set node configuration ################################################*/

    pNodeConfig.NodeType = DMA_GPDMA_LINEAR_NODE;

    pNodeConfig.Init.Request = GPDMA1_REQUEST_ADC2;

    pNodeConfig.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;

    pNodeConfig.Init.Direction = DMA_PERIPH_TO_MEMORY;

    pNodeConfig.Init.SrcInc = DMA_SINC_FIXED;

    pNodeConfig.Init.DestInc = DMA_DINC_INCREMENTED;

    pNodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_HALFWORD;

    pNodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_HALFWORD;

    pNodeConfig.Init.SrcBurstLength = 1;

    pNodeConfig.Init.DestBurstLength = 1;

    pNodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;

    pNodeConfig.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;

    pNodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;

    pNodeConfig.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE;

    pNodeConfig.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;

    pNodeConfig.SrcAddress = 0;

    pNodeConfig.DstAddress = 0;

    pNodeConfig.DataSize = 0;

 

    /* Build ADC2Node Node */

    ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &ADC2Node);

 

    /* Insert ADC2Node to Queue */

    ret |= HAL_DMAEx_List_InsertNode_Tail(&ADC2Queue, &ADC2Node);

 

    ret |= HAL_DMAEx_List_SetCircularMode(&ADC2Queue);

 

    return ret;

}

 

/**

  * @brief  DMA Linked-list ADC4Queue configuration

  * @param  None

  * @retval None

  */

HAL_StatusTypeDef MX_ADC4Queue_Config(void)

{

    HAL_StatusTypeDef ret = HAL_OK;

    /* DMA node configuration declaration */

    DMA_NodeConfTypeDef pNodeConfig;

 

    /* Set node configuration ################################################*/

    pNodeConfig.NodeType = DMA_GPDMA_LINEAR_NODE;

    pNodeConfig.Init.Request = GPDMA1_REQUEST_ADC4;

    pNodeConfig.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;

    pNodeConfig.Init.Direction = DMA_PERIPH_TO_MEMORY;

    pNodeConfig.Init.SrcInc = DMA_SINC_FIXED;

    pNodeConfig.Init.DestInc = DMA_DINC_INCREMENTED;

    pNodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_HALFWORD;

    pNodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_HALFWORD;

    pNodeConfig.Init.SrcBurstLength = 1;

    pNodeConfig.Init.DestBurstLength = 1;

    pNodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;

    pNodeConfig.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;

    pNodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;

    pNodeConfig.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE;

    pNodeConfig.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;

    pNodeConfig.SrcAddress = 0;

    pNodeConfig.DstAddress = 0;

    pNodeConfig.DataSize = 0;

 

    /* Build ADC4Node Node */

    ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &ADC4Node);

 

    /* Insert ADC4Node to Queue */

    ret |= HAL_DMAEx_List_InsertNode_Tail(&ADC4Queue, &ADC4Node);

 

    ret |= HAL_DMAEx_List_SetCircularMode(&ADC4Queue);

 

    return ret;

}

 

ADC application

void adc_app_init(void)

{

    // ADC1 Config

    if (HAL_ADCEx_Calibration_Start(&hadc1, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED) != HAL_OK) {

        /* Calibration Error */

        Error_Handler();

    }

    // calib ADC channel to reduce offset error

    HAL_ADCEx_Calibration_SetValue(

        &hadc1, ADC_SINGLE_ENDED, HAL_ADCEx_Calibration_GetValue(&hadc1, ADC_SINGLE_ENDED));

    MX_ADC1Queue_Config();

    __HAL_LINKDMA(&hadc1, DMA_Handle, handle_GPDMA1_Channel[ADC1_DMA_DMACH]);

    if (HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel[ADC1_DMA_DMACH], &ADC1Queue) != HAL_OK) {

        Error_Handler();

    }

    if (DATA_VAL(INT08U, SYS_HWID) == HWID_Pro) {

        if (HAL_ADC_Start_DMA(&hadc1, (uint32_t *)s_adc1_result, (2 * ADC1_BUF_SIZE * ADC1_CH_NUM)) != HAL_OK) {

            Error_Handler();

        }

    }

 

    // ADC2 Config

    if (HAL_ADCEx_Calibration_Start(&hadc2, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED) != HAL_OK) {

        /* Calibration Error */

        Error_Handler();

    }

    // calib ADC channel to reduce offset error

    HAL_ADCEx_Calibration_SetValue(

        &hadc2, ADC_SINGLE_ENDED, HAL_ADCEx_Calibration_GetValue(&hadc2, ADC_SINGLE_ENDED));

    MX_ADC2Queue_Config();

    __HAL_LINKDMA(&hadc2, DMA_Handle, handle_GPDMA1_Channel[ADC2_DMA_DMACH]);

    if (HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel[ADC2_DMA_DMACH], &ADC2Queue) != HAL_OK) {

        Error_Handler();

    }

    if (HAL_ADC_Start_DMA(&hadc2, (uint32_t *)s_adc2_result, (2 * ADC2_BUF_SIZE * ADC2_CH_NUM)) != HAL_OK) {

        Error_Handler();

    }

 

    // ADC4 Config

    if (HAL_ADCEx_Calibration_Start(&hadc4, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED) != HAL_OK) {

        /* Calibration Error */

        Error_Handler();

    }

    // calib ADC channel to reduce offset error

    HAL_ADCEx_Calibration_SetValue(&hadc4, ADC_SINGLE_ENDED, HAL_ADCEx_Calibration_GetValue(&hadc4, ADC_SINGLE_ENDED));

    MX_ADC4Queue_Config();

    __HAL_LINKDMA(&hadc4, DMA_Handle, handle_GPDMA1_Channel[ADC4_DMA_DMACH]);

    if (HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel[ADC4_DMA_DMACH], &ADC4Queue) != HAL_OK) {

        Error_Handler();

    }

    if (HAL_ADC_Start_DMA(&hadc4, (uint32_t *)s_adc4_result, (2 * ADC4_BUF_SIZE * ADC4_CH_NUM)) != HAL_OK) {

        Error_Handler();

    }

 

    // start timer1 to trigger ADC4 conversion

    if (HAL_TIM_Base_Start(&htim1) != HAL_OK) {

        Error_Handler();

    }

    // start timer6 to trigger ADC1 conversion

    if (HAL_TIM_Base_Start(&htim6) != HAL_OK) {

        Error_Handler();

    }

    delay_200ns();

 

    // start timer15 to trigger ADC2 conversion

    if (HAL_TIM_Base_Start(&htim15) != HAL_OK) {

        Error_Handler();

    }

}

 

DMA interrupt call back

/**

  * @brief  Conversion half-transfer callback in non-blocking mode

  * @param  hadc: ADC handle

  * @retval None

  */

void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)

{

    if (hadc == &hadc1) {

        // ADC1 call back

        NVIC_SetPendingIRQ(EXTI12_IRQn);

    } else if (hadc == &hadc2) {

        // ADC2 call back

        NVIC_SetPendingIRQ(EXTI14_IRQn);

    } else if (hadc == &hadc4) {

        // ADC4 call back

        if (s_pf_adc_data_cb) {

            for (INT16U i = 0; i < ADC4_BUF_SIZE; i ++) {

                s_adc4_pf_data[0][i] = s_adc4_result[i][0 + ADC4_RX_CH_NUM];

                s_adc4_pf_data[1][i] = s_adc4_result[i][1 + ADC4_RX_CH_NUM];

                s_adc4_pf_data[2][i] = s_adc4_result[i][2 + ADC4_RX_CH_NUM];

            }

            s_pf_adc_data_cb((INT16U *)s_adc4_pf_data, BD_1PPS_CNT);

        }

        if (s_rx_adc_data_cb) {

            static INT16U adc_camp_count = 0;

            if (++adc_camp_count > 199) {

                adc_camp_count = 0;

                for (INT16U i = 0; i < ADC4_BUF_SIZE; i++)

                    for (INT16U j = 0; j < ADC4_RX_CH_NUM; j ++)

                        s_adc4_rx_data[j][i] = s_adc4_result[i][j];

                s_rx_adc_data_cb((INT16U *)s_adc4_rx_data);

            }

        }

        if (__HAL_ADC_GET_FLAG(&hadc4, ADC_FLAG_OVR)) {

            // printf_adc_val();

            __HAL_ADC_CLEAR_FLAG(&hadc4, ADC_FLAG_OVR);

        }

    }

}

 

/**

  * @brief  Conversion DMA complete callback in non-blocking mode

  * @param  hadc: ADC handle

  * @retval None

  */

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)

{

    if (hadc == &hadc1) {

        // ADC1call back

        NVIC_SetPendingIRQ(EXTI13_IRQn);

    } else if (hadc == &hadc2) {

        // ADC2 call back

        NVIC_SetPendingIRQ(EXTI15_IRQn);

    } else if (hadc == &hadc4) {

        // ADC4 call back

        if (s_pf_adc_data_cb) {

            for (INT16U i = 0; i < ADC4_BUF_SIZE; i ++) {

                s_adc4_pf_data[0][i] = s_adc4_result[i + ADC4_BUF_SIZE][0 + ADC4_RX_CH_NUM];

                s_adc4_pf_data[1][i] = s_adc4_result[i + ADC4_BUF_SIZE][1 + ADC4_RX_CH_NUM];

                s_adc4_pf_data[2][i] = s_adc4_result[i + ADC4_BUF_SIZE][2 + ADC4_RX_CH_NUM];

            }

            s_pf_adc_data_cb((INT16U *)s_adc4_pf_data, BD_1PPS_CNT);

        }

    }

}

Philippe Cherbonnel
ST Employee
July 10, 2026

Hello,

As mentioned in users comments, root cause is uncertain: ADC ? DMA ? System IRQ overhead ?

To debug ADC activity:

Can place this code somewhere in your code, run periodically:

  if (LL_ADC_IsActiveFlag_EOS(ADC4) == 1UL)
  {
    LL_ADC_ClearFlag_EOS(ADC4);
    adc_eos_count++;
  }  
  //(or LL_ADC_IsActiveFlag_EOC(ADC4) but take care: EOC is read-clear, therefore access during debug will impact flag state)

  conv_data = LL_ADC_REG_ReadConversionData32(ADC4);

If flag EOS and conv_data are updated periodically, it means ADC is alive.

To debug DMA transfers:

Can you check periodically:

LL_ADC_IsActiveFlag_OVR(ADC4);

Try to disable DMA transfer:

LL_ADC_REG_SetDMATransfer(ADC4, LL_ADC_REG_DMA_TRANSFER_NONE_ADC4);

To debug system IRQ overhead:

Try to disable all ADC interrupts:

ADC4->IER = 0UL;

and DMA interrupts

  LL_DMA_DisableIT_TO(GPDMA1, LL_DMA_CHANNEL_1);
  LL_DMA_DisableIT_USE(GPDMA1, LL_DMA_CHANNEL_1);
  LL_DMA_DisableIT_ULE(GPDMA1, LL_DMA_CHANNEL_1);
  LL_DMA_DisableIT_DTE(GPDMA1, LL_DMA_CHANNEL_1);
  LL_DMA_DisableIT_HT(GPDMA1, LL_DMA_CHANNEL_1);
  LL_DMA_DisableIT_TC(GPDMA1, LL_DMA_CHANNEL_1);
 

Edward_kAuthor
Associate
July 13, 2026

“As you can see in the picture, the ISR register shows the ADC module hasn’t stopped, and the OVR hasn’t been triggered.”
 

 

waclawek.jan
Super User
July 10, 2026

> in a log file we captured, channels simultaneously read byte-identical constant values.

Infinitely?

And where is the adc_dr value from, in that log? Reading out ADC_DR by processor, while it’s being handled by DMA, is a bad idea.

JW

Edward_kAuthor
Associate
July 13, 2026

We only read data when an exception occurs

waclawek.jan
Super User
July 13, 2026

> in a log file we captured, channels simultaneously read byte-identical constant values.

Infinitely?

 

>  We only read data when an exception occurs

what exception?

 

JW