2019-05-16 01:21 PM
Hello,
I have an application that reads a couple of ADC channels. each time through the main loop. There's no It's a blocking ADC read so nothing else is going on when I'm reading the 2 channels.
For testing purposes I have a 3V battery attached to both inputs so the input signal is clean and stable. When I take multiple samples over time the two channels are within a couple of ADC counts of each other ( 3783 ADC ****) and range of the ADC acquisitions are typically +- 6 or 7 counts. An outlier might be 10 or 12 counts out. That's OK for what we're doing.
The problem occurs when I change from using the default Nucleo 8Mhz clock and use an external 20Mhz clock. I've modified the SystemClock_Config to use the external clock and have divided it down so the RCC/PLL is the same 4Mhz that the onboard clock was generating. Everything works correctly with the external clock - serial baud rates, timers, PWM generation, etc.
What doesn't work correctly is the ADC! Using the same stable battery source into the 2 ADC channels I get noise all over the place! Sometimes the 2 channels are close within a couple of ADC counts of each other. The next time they might be 100 ADC counts different! What used to be 3783 +- 7 is now 3780 +- 75 or 80! That's not even good enough for gov't work!
Below is my ADC config. The ADC clock is set to derived from AHB clock divided by a prescaler of 4. Would the AHB clock be different if the PLL is running the same as in the default case?
Any thoughts appreciated.
static void ADC_Config(void)
{
ADC_ChannelConfTypeDef sConfig;
// ADC_AnalogWDGConfTypeDef AnalogWDGConfig;
/* Configuration of ADCx init structure: ADC parameters and regular group */
AdcHandle.Instance = ADCx;
AdcHandle.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
AdcHandle.Init.Resolution = ADC_RESOLUTION_12B;
AdcHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
AdcHandle.Init.ScanConvMode = DISABLE; /* Sequencer disabled (ADC conversion on only 1 channel: channel set on rank 1) */
AdcHandle.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
AdcHandle.Init.LowPowerAutoWait = DISABLE;
AdcHandle.Init.NbrOfConversion = 1; /* Parameter discarded because sequencer is disabled */
AdcHandle.Init.DiscontinuousConvMode = DISABLE; /* Parameter discarded because sequencer is disabled */
AdcHandle.Init.NbrOfDiscConversion = 0; /* Parameter discarded because sequencer is disabled */
AdcHandle.Init.NbrOfConversion = 2; /* Parameter discarded because sequencer is disabled */
AdcHandle.Init.ScanConvMode = ENABLE; /* Sequencer disabled (ADC conversion on only 1 channel: channel set on rank 1) */
AdcHandle.Init.EOCSelection = DISABLE;
AdcHandle.Init.ContinuousConvMode = ENABLE; /* Continuous mode disabled to have only 1 conversion at each conversion trig */
AdcHandle.Init.ExternalTrigConv = ADC_SOFTWARE_START; /* Trig of conversion start done by external event */
AdcHandle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
AdcHandle.Init.DMAContinuousRequests = DISABLE;
AdcHandle.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
if (HAL_ADC_Init(&AdcHandle) != HAL_OK)
{
/* ADC initialization error */
Error_Handler(__FILE__, __LINE__);
}
/* Configuration of channel on ADCx regular group on sequencer rank 1 */
/* Note: Considering IT occurring after each ADC conversion if ADC */
/* conversion is out of the analog watchdog widow selected (ADC IT */
/* enabled), select sampling time and ADC clock with sufficient */
/* duration to not create an overhead situation in IRQHandler. */
sConfig.Channel = ADCx_CHANNELa;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_601CYCLES_5;
sConfig.SingleDiff = ADC_MODE;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
if (HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK)
{
/* Channel Configuration Error */
Error_Handler(__FILE__, __LINE__);
}
sConfig.Channel = ADCx_CHANNELb;
sConfig.Rank = ADC_REGULAR_RANK_2;
sConfig.SamplingTime = ADC_SAMPLETIME_601CYCLES_5;
sConfig.SingleDiff = ADC_MODE;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
if (HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK)
{
/* Channel Configuration Error */
Error_Handler(__FILE__, __LINE__);
}
}
2019-05-17 11:15 AM
I still don't understand why the ADC is more noisy with the external clock but by changing the ADC clock source from the AHB clock to the PLL, I get a little more stable ADC reading. It's now just a little more noisy than the original on board Nucleo clock. With a bit of averaging I can use the ADC readings for my purposes. (The signal I'm measuring doesn't change rapidly.) Below is the change to ADC_Config.
#if EXTERNAL_CLOCK
AdcHandle.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1; // use PLL clock
#else
AdcHandle.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; // use AHB clock
#endif