cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L1 ADC Overrun with DMA

Posted on July 02, 2014 at 17:31

Hello there,

I've been having trouble with the STM32L1 ADC when using it with the DMA with high sample rates.

Generally speaking, if I trigger the ADC conversion externally using a timer, I can achieve a sampling rate of about 500kHz without getting into trouble. If I configure the timer to trigger the ADC at higher rates, I'll get overrun errors. I can imagine this making sense as the manual states:

 

The interval between trigger events must be longer than the sequence for regular conversions.

So, to achieve the maximum stated sampling rate of 1MHz I tried to configure the ADC to convert continuously by setting the CONT bit. But as soon as the first conversion completes, an overrun error is reported. The same happens in SCAN mode which I plan to use for conversion of a limited amount of samples.

Does anyone have any idea as to what could be wrong? As far as I see, the HSI is running and none of the Buses have any prescaler configured. I'm sure I'm overlooking something trivial.

Here are the relevant portions of the code:

(note that I use IAR without the stdPeripheralLibrary, so the registers are addressed with this REGNAME_bit.BITNAME syntax)

startConversion()

{

  if (!ADC_SR_bit.ADONS)

  {

    /* ADC not running; power up the adc module */

    ADC_CR2_bit.ADON = 1;

 

    while (!ADC_SR_bit.ADONS)

    {

      /* wait until the adc is ready */

    }

  }

  while (ADC_SR_bit.RCNR);

  {

    /* Wait for regular channel ready */

  }

  IO_SET_OUTPUT_F(IO_SWT_PULL_UP, 1);

  ADC_CR2_bit.SWSTART = 1;

}

  /*********************************************************/

  /* DMA Config */

  /*********************************************************/

initDma()

{

  RCC_AHBENR_bit.DMA1EN = 1;

  /* map DMA to ADC Data Register */

  DMA_CPAR1 = (uint32_t) &ADC_DR;

  /* map memory to DMA */

  DMA_CMAR1 = (uint32_t) (m_aSampleBuffer);

  /* set number of data to actually sampled number of data */

  DMA_CNDTR1 = m_blockSize;

 

  /* Set DMA channel priority (0b00 == low, 0b11 == very high) */

  DMA_CCR1_bit.PL = 0x2;

  /* Set Memory size (0b00 = 8, 0b01 = 16, 0b10 = 32) */

  DMA_CCR1_bit.MSIZE = (sizeof(m_aSampleBuffer[0]) >> 1);

  /* Set Peripheral size (ADC_DR is 16bit) */

  DMA_CCR1_bit.PSIZE = 0x1;

  /* source is always at same address (ADC_DR) */

  DMA_CCR1_bit.PINC = 0;

  /* enable memory increment mode; writes next transfer to next address*/

  DMA_CCR1_bit.MINC = 1;

  /* enable circular mode */

  DMA_CCR1_bit.CIRC = 1;

  /* direction = read from peripheral */

  DMA_CCR1_bit.DIR = 0;

  /* Enable Transfer Error Interrupt */

  DMA_CCR1_bit.TEIE = 1;

  /* Enable full transfer interrupt */

  DMA_CCR1_bit.TCIE = 1;

  /* No Memory-to-memory mode */

  DMA_CCR1_bit.MEM2MEM = 0;

  /* Enable DMA Channel 1 */

  DMA_CCR1_bit.EN = 1;

  /* Enable DMA Ch.1 interrupt in NVIC */

  SETENA0_bit.DMA1_CHANNEL1 = 1;

}

 

  /*********************************************************/

  /* ADC Config */

  /*********************************************************/

initAdc()

{  

  startHsi();

  /* enable peripheral clock */

  RCC_APB2ENR_bit.ADC1EN = 1;

  /* prescaler for ADCCLK (0) */

  ADC_CCR_bit.ADCPRE = ADC_PRESCALE_1;

 

  /* set ADC Resolution (0) */

  ADC_CR1_bit.RES = ADC_RESOLUTION_12BIT;

 

  /* right alignment of result */

  ADC_CR2_bit.ALIGN = 0;

  /* set conversion time via cycles register (0) */

  configureConversionTime(ADC_CYCLES_4);

  /* delay after conversion => no delay */

  ADC_CR2_bit.DELS = 0;

  /* Configures the SQRx registers */

  configureSequencer(

m_blockSize

);

  /* scan mode */

  ADC_CR1_bit.SCAN = 1;

  /* overrun interrupt enable */

  ADC_CR1_bit.OVRIE = 1;

  SETENA0_bit.ADC1 = 1;

}

#dma #stm32 #adc #dma-performance #adc-noise-variance #overrun
19 REPLIES 19
stm322399
Senior
Posted on July 02, 2014 at 18:11

IMHO, that should work better when

ADC_CR2_bit.DMA = 1;

However, it does not explain why you do not have trouble under 500kHz (unless you do not use DMA at that speed).

Posted on July 02, 2014 at 18:18

Woah... now I feel stupid. This must have been lost during one of my clean-up actions. Of course without that bit it doesn't work at slower sampling rates either.

Thank you very much!

Posted on July 02, 2014 at 18:26

Well... an overrun still happens; but now only after the first value seems to have been transferred by the DMA...

stm322399
Senior
Posted on July 02, 2014 at 18:33

DDS bit of CR2 might help as well.

Posted on July 02, 2014 at 18:44

Thanks, Laurent.

Concerning the DDS, the manual says : This bit is set and cleared by software.

 

0: No new DMA request is issued after the last transfer (as configured in the DMA controller)

 

1: DMA requests are issued as long as data are converted and DMA=1As the overrun happens after only one sample has been converted (of m_blocksize samples, in my case currently 24), from my understanding setting or clearing the DDS bit does not have an influence of the outcome as the DMA should transfer 23 further samples.

If anything I suppose should leave it cleared (which it is, according to the register content displayed by the debugger).

Anyway. I verified that the DDS bit does not have any influence on the described behaviour.
stm322399
Senior
Posted on July 02, 2014 at 19:06

bit after bit, we will eventually succeed to configure that ADC : CONT in CR2 is required for the ADC not to stop after the first conversion !

Posted on July 02, 2014 at 19:26

CONT

Here as well, from my understanding of the manual, chapter 12.3.8 Scan mode, the CONT bit should only have an influence after the configured sequence has been completely converted and I have indeed configured the ADC in SCAN mode.

Indeed I have verified this. Setting the CONT bit does not change the outcome.

DELS

What does change the outcome is setting the DELS bit. But I don't want to have a delay between conversions if not necessary. All the examples I have seen so far do not set a delay either (except the ADC1_Freeze example in the StdPeripheralLibrary where setting the delay is exactly the point of the example), so I'm slightly confused as to why it doesn't work if a delay is not configured.

The manual does state:

If the ADC is the only peripheral that needs to transfer data, then a minimum delay should be configured:

 

15 APB clock cycles if fAPB < fADCCLK/2 or else 7 APB clock cycles if fAPB < fADCCLK, otherwise no delay is needed.

 

I understand that APB must be at the SYSCLK speed as no prescaler is defined in RCC_CFGR. Maybe the problem is that other peripherals are active and for some reason ''polluting'' the bus? (this ADC part is only part of a bigger system)

The following peripherals are currently active as well:

on APB2:

- TIM9, TIM11, ADC

on AHB:

- GPIOA, GPIOB, GPIOC, GPIOH, FLITF, DMA1

stm322399
Senior
Posted on July 02, 2014 at 20:23

OK. It's now time to consider that the

configureSequencer

function is a relevant part of the code. If something is wrong with SQR's, this can explain the problem.

os_kopernika
Associate II
Posted on July 02, 2014 at 21:01

I did configure that, STM32L-Discovery, ADC1, 12-bit, with DMA1.

I requested exactly 1Msps and exactly that amount of samples was DMA delivered (TIM3 TRGO triggers ADC1 and simultaneously ticks TIM9 that counts sample requests). Both values were equal, no overrun IRQs, no missing data, what requested was delivered on time. After that my sample code was counting variance on ADC1 data to qualify the embedded ADC performance (you can view the results

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=1166198#1166198

).

Most of the code was running from SRAM (except of STM library that was in Flash), -O1 but even then - no overruns.