cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F401 ADC not getting all the values

SBeno.1
Associate II

Hi,

First time poster here, I have looked around for answers and didn't find anything that could help me. I also tried to reach out to ST but they doesn't want to help and they are throwing me to local suppliers instead.

So here's the thing, the ADC isn't able to go through all the values, I can reach all the range ( 0 to 4095 ) but sometimes it's skiping few steps. According to the datasheet, it's normal to have a +/- 3 step error and I understand that. But I am seeing bigger steps here.

The configuration :

ADC in scan mode, scanning 11 channels at 112 cycles per samples.

ADC transferring data through DMA when a conversion is done

An external trigger from Timer2 starts the scan every 20ms

The clocks :

HSE of 16MHz

HCLK of 84, so APB1 is at 84MHz

ADC prescaler at 4, so 21 MHz

Before going through the code, I have to mention that I have tried few things to get rid of this behavior:

  • Using HSI
  • Using ADC Interrupts instead of DMA
  • Scanning only 1 channel
  • Dividing APB2 by 2
  • Tried every sampling rate available

Some of those things listed above seemed to fix the problem, but in fact it only moved it to another value. At first the problem was between 1991 and 2048 ( I had the ADC running at 42MHz which isn't in the spec ) but after fixing this, the problem was around 1024.

0693W000001pdSZQAY.png0693W000001pdS5QAI.png

This is a zoomed in graph of the output values, even with the noise the value between 1023 and 1036 can't be reached.

Now, you want to inspect the code ? Here it is :

void SystemClock_Config(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct;
    RCC_ClkInitTypeDef RCC_ClkInitStruct;
 
    /**Configure the main internal regulator output voltage 
    */
    __HAL_RCC_PWR_CLK_ENABLE();
 
    __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);
 
    /**Initializes the CPU, AHB and APB busses clocks 
    */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLM = 8;
    RCC_OscInitStruct.PLL.PLLN = 168;
    RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
    RCC_OscInitStruct.PLL.PLLQ = 7;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {
        _Error_Handler(__FILE__, __LINE__);
    }
 
    /**Initializes the CPU, AHB and APB busses clocks 
    */
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
                                | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
 
    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
    {
        _Error_Handler(__FILE__, __LINE__);
    }
 
    /**Configure the Systick interrupt time 
    */
    HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / 1000);
 
    /**Configure the Systick 
    */
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
 
    /* SysTick_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(SysTick_IRQn, 15, 0);
}
 
void ADC_peripheral_init(void)
{
    ADC_ChannelConfTypeDef sConfig;
 
    /**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) 
    */
    adcADC_HANDLE.Instance                      = ADC1;
    adcADC_HANDLE.Init.ClockPrescaler           = ADC_CLOCK_SYNC_PCLK_DIV6;
    adcADC_HANDLE.Init.Resolution               = ADC_RESOLUTION_12B;
    adcADC_HANDLE.Init.ScanConvMode             = ENABLE;
    adcADC_HANDLE.Init.ContinuousConvMode       = DISABLE;
    adcADC_HANDLE.Init.DiscontinuousConvMode    = DISABLE;
    adcADC_HANDLE.Init.NbrOfDiscConversion      = 0;
    adcADC_HANDLE.Init.ExternalTrigConvEdge     = ADC_EXTERNALTRIGCONVEDGE_RISING;
    adcADC_HANDLE.Init.ExternalTrigConv         = ADC_EXTERNALTRIGCONV_T2_TRGO;
    adcADC_HANDLE.Init.DataAlign                = ADC_DATAALIGN_RIGHT;
    adcADC_HANDLE.Init.NbrOfConversion          = NB_OF_ADC_INPUT_ENTRY;
    adcADC_HANDLE.Init.DMAContinuousRequests    = ENABLE;
    adcADC_HANDLE.Init.EOCSelection             = ADC_EOC_SEQ_CONV;
 
    if (HAL_ADC_Init(&adcADC_HANDLE) != HAL_OK)
    {
        _Error_Handler(__FILE__, __LINE__);
    }
 
    sConfig.SamplingTime = ADC_SAMPLETIME_112CYCLES;
 
    for (size_t i = 0; i < NB_OF_ADC_INPUT_ENTRY; i++)
    {
        sConfig.Channel = analog_inputs[i].channel;
        sConfig.Rank = i + 1;
        if (HAL_ADC_ConfigChannel(&adcADC_HANDLE, &sConfig) != HAL_OK)
        {
            _Error_Handler(__FILE__, __LINE__);
        }
    }
}
 
void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{
 
    GPIO_InitTypeDef GPIO_InitStruct;
    if (adcHandle->Instance == ADC1)
    {
        /* ADC1 clock enable */
        __HAL_RCC_ADC1_CLK_ENABLE();
  
        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
 
        for (size_t i = 0; i < NB_OF_ADC_INPUT_ENTRY; i++)
        {
            GPIO_InitStruct.Pin = analog_inputs[i].pin;
            HAL_GPIO_Init(analog_inputs[i].port, &GPIO_InitStruct);
        }
 
        /* ADC1 DMA Init */
        /* ADC1 Init */
        hdma_adc1.Instance                  = DMA2_Stream0;
        hdma_adc1.Init.Channel              = DMA_CHANNEL_0;
        hdma_adc1.Init.Direction            = DMA_PERIPH_TO_MEMORY;
        hdma_adc1.Init.PeriphInc            = DMA_PINC_DISABLE;
        hdma_adc1.Init.MemInc               = DMA_MINC_ENABLE;
        hdma_adc1.Init.PeriphDataAlignment  = DMA_PDATAALIGN_WORD;
        hdma_adc1.Init.MemDataAlignment     = DMA_MDATAALIGN_WORD;
        hdma_adc1.Init.Mode                 = DMA_CIRCULAR;
        hdma_adc1.Init.Priority             = DMA_PRIORITY_LOW;
        hdma_adc1.Init.FIFOMode             = DMA_FIFOMODE_DISABLE;
 
        if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
        {
            _Error_Handler(__FILE__, __LINE__);
        }
 
        __HAL_LINKDMA(adcHandle, DMA_Handle, hdma_adc1);
 
        /* ADC1 interrupt Init */
        HAL_NVIC_SetPriority(ADC_IRQn, 5, 0);
        HAL_NVIC_EnableIRQ(ADC_IRQn);
    }
}

Thank you all for taking the time to read this and to try to help me !!😅

1 ACCEPTED SOLUTION

Accepted Solutions

Schematics looks good. The devil may be in details, i.e. layout. By measuring directly on the pins, exclude layout/PCB errors and bad solder joints.

I'd recommend to connect VBAT to VDD (as is recommended in DS), but I don't believe that's the root of your problem.

> Vref is the output of a NCP51460, it has no decoupling at its output though.

As an experient, add some, as close to the VREF+/VSSA pins as possible.

JW

View solution in original post

10 REPLIES 10

What hardware is this, a "known good" board like Disco or Nucleo, or your own board?

Can't this be a damaged mcu, can you try on another hardware?

Can you try some more standard input signal, e.g. a sawtooth?

 Can you try other ADC, i.e. ADC2/ADC3?

JW

I can reproduce the behavior on our boards on 2 different chip ( STM32F401VC and STM32F401VE )

I will try it today on a nucleo board, but as I recall it has been seen on a dev platform before. I will double check by the end of the day .

One good detail is that the input stage is unbuffered :

0693W000001pfADQAY.png

SBeno.1
Associate II

Hey, thank you for your answer, you had me doubt my self and I copied the code to make it work on my nucleo ... looks like ADC is reading all the values.

So, the verdict would be

  • Running a few hours at 42MHz has damaged the ADC and it can't recover from it
  • Our input stage is creating weird thing inside the chip. I will change the IC and try again.

Thanks a lot.

> Running a few hours at 42MHz has damaged the ADC and it can't recover from it

I don't think so.

I would suspect the board design - are *all* VSS and VDD pins sonnected, including VDDA/VSSA? Is VSSA/VDDA somehow filtered? How exactly is VREF+ connected/filtered? How are all these pins decoupled?

> I will change the IC and try again.

Please report back with the results.

JW

TDK
Guru

SAR ADCs can have missing step problems, but in this case where you're missing 8+ consecutive codes, seems like something else might be the issue. I'd try out a different board before spending too much time on it.

Your values are skipping from 0b1111111111 to 0b10000001100, which suggests there might be an issue resolving the 4th lowest bit, but then the 3rd is also high so it doesn't really make sense.

FWIW, I've used the STM32F4 ADC extensively and never experienced missing values like this.

If you feel a post has answered your question, please click "Accept as Solution".

0693W000001phytQAA.png

See the picture above ! Missclicked the reply button

Vref is the output of a NCP51460, it has no decoupling at its output though.

Schematics looks good. The devil may be in details, i.e. layout. By measuring directly on the pins, exclude layout/PCB errors and bad solder joints.

I'd recommend to connect VBAT to VDD (as is recommended in DS), but I don't believe that's the root of your problem.

> Vref is the output of a NCP51460, it has no decoupling at its output though.

As an experient, add some, as close to the VREF+/VSSA pins as possible.

JW

SBeno.1
Associate II

Mystery has been solved ...

We added a 100nF on the VREF pin and it solved the problem.

Since we wanted to inspect the behavior ( for educationnal purpose 🙂 ) we removed it and scoped the Vref pin and look what we have found :

0693W000001pldzQAA.png

Almost 100mV drop when sampling

0693W000001ple4QAA.png

The Vref uses an ncp51460 to get its reference. In the ref design there's not decoupling cap but ... seems that it need some to sustain the sampling load.

It has been a great learning experience, thanks everybode for all your input !