cancel
Showing results for 
Search instead for 
Did you mean: 

STM32G0: Stoppping/Starting ADC with DMA for re-calibration

TBuls
Associate II

To avoid the best accuracy when using the ADC it is advised to re-calibrate the ADC when environment temperature changes "too much"

The ADC calibration can only be done when the ADC is disabled, so to re-calibrate the ADC has to be stopped, disabled, calibrated, then enabled and started again.

I'm using the ADC with DMA in circular buffer mode, using only the LL API.

I can see however that stopping and then starting the ADC this way results is strange intermittent inaccuracies in the ADC values read (offsets). This happens also when i'm not re-calibrating, so its not that im reading the calibration factor or something like that.

What is the advised way of stopping and then restarting the ADC when using DMA/circular mode?

1 ACCEPTED SOLUTION

Accepted Solutions
Piranha
Chief II
  1. Disable the DMA.
  2. Disable the ADC.
  3. Do the ADC calibration.
  4. Enable the ADC.
  5. Reinitialize the NDTR and enable the DMA.

View solution in original post

3 REPLIES 3
Piranha
Chief II
  1. Disable the DMA.
  2. Disable the ADC.
  3. Do the ADC calibration.
  4. Enable the ADC.
  5. Reinitialize the NDTR and enable the DMA.

Piranha,

Thanks for your response, it triggered me to check and check again my sequence because it was actually pretty much the same as you propose, except for the fact that i disabled the ADC before disabling the DMA. (Im assuming with stopping DMA you mean disabling it?).

That however doesn't seem to make much difference because...

It seems to be working now .... but i don't for the life of me know what was wrong before.

In any case, thanx for your support, maybe if i find time i will backtrace what i did wrong...

For whomever this may be of interest, the i code i have now is pretty much this (warning LL code ahead):

// Disable DMA
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
while (LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1))
  ;
 
// Stop ADC
if (LL_ADC_REG_IsConversionOngoing(ADC1) && !LL_ADC_REG_IsStopConversionOngoing(ADC1)) {
  LL_ADC_REG_StopConversion(ADC1);
}
while (LL_ADC_REG_IsStopConversionOngoing(ADC1))
  ;
 
// Disable ADC
if (LL_ADC_IsEnabled(ADC1) && !LL_ADC_IsDisableOngoing(ADC1)) {
  LL_ADC_Disable(ADC1);
}
while (LL_ADC_IsEnabled(ADC1))
  ;
LL_ADC_REG_SetDMATransfer(ADC1, LL_ADC_REG_DMA_TRANSFER_NONE);
 
// Calibrate
LL_ADC_StartCalibration(ADC1);
while (LL_ADC_IsCalibrationOnGoing(ADC1) or (!LL_ADC_IsActiveFlag_EOCAL(ADC1)))
  ;
 
DelayMs(2);
 
// Enable ADC
LL_ADC_REG_SetDMATransfer(ADC1, LL_ADC_REG_DMA_TRANSFER_UNLIMITED);
 
if (!LL_ADC_IsEnabled(ADC1)) {
  LL_ADC_ClearFlag_ADRDY(ADC1);
  LL_ADC_Enable(ADC1);
}
while ((!LL_ADC_IsEnabled(ADC1)) or (!LL_ADC_IsActiveFlag_ADRDY(ADC1)))
  ;
 
// Start the ADC
LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_1, LL_ADC_DMA_GetRegAddr(ADC1, LL_ADC_DMA_REG_REGULAR_DATA), reinterpret_cast<uint32_t>(m_channels), LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, ADC_CHANNEL_COUNT);
 
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
while (!LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1))
  ;
 
LL_ADC_REG_StartConversion(ADC1);

Piranha,

Because the outcome of this endeavor was a bit dissapointing ("it works now but i don't know why") i have been bisecting the root cause of the error.

It seems in my non-working version i let the ball drop on your bullet 5, re-initializing NTDR. That is, i initialized it once on startup because circular, but when stopping and starting in between it naturally needs re-initializing, so in my non-working version the two lines:

  LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_1, LL_ADC_DMA_GetRegAddr(ADC1, LL_ADC_DMA_REG_REGULAR_DATA), reinterpret_cast<uint32_t>(m_channels), LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
  LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, ADC_CHANNEL_COUNT);

aren't there (The second line being the only one relevant to NTDR)

Thanks again for your time.