2020-05-28 06:03 AM
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:
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.
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 !!:grinning_face_with_sweat:
Solved! Go to Solution.
2020-05-29 05:03 AM
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
2020-05-28 08:41 AM
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
2020-05-28 11:35 AM
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 :
2020-05-28 12:32 PM
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
Thanks a lot.
2020-05-28 01:20 PM
> 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
2020-05-28 07:59 PM
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.
2020-05-29 04:38 AM
2020-05-29 04:41 AM
See the picture above ! Missclicked the reply button
Vref is the output of a NCP51460, it has no decoupling at its output though.
2020-05-29 05:03 AM
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
2020-05-31 05:10 PM
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 :
Almost 100mV drop when sampling
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 !