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
Posted on July 02, 2014 at 21:22

The configureSequencer function:

(I verified with the debugger that the contents of the SQR registers were indeed the correct channel id's).

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

#define ADC_SQR_BITS 5

#define ADC_SQR_MASK 31

configureSequencer(uint_fast8_t numSamples)

{

  uint32_t *reg = (uint32_t *) &ADC_SQR5;

 

  for (uint_fast8_t i = 0; i < numSamles; i++)

  {

      *reg &= ~(ADC_SQR_MASK << ((i % 6) * ADC_SQR_BITS));

      *reg |= (m_channel << (i % 6) * ADC_SQR_BITS));

      

      if (i % 6 == 5)

      {

          reg--;

      }

  }

  ADC_SQR1_bit.L = numSamples - 1;

}

stm322399
Senior
Posted on July 02, 2014 at 21:45

What says the debugger about SQR1 ?

Do you tried again the run at 500KHz with CR2.DMA/CONT/DDS bit set, and observed that everything is working fine ?

Also report RCC_CFGR value, so that we can check frequencies. When frequencies are just enough (ADCCLK=APB2CLK), I do not exclude that APB2 is saturated by accesses from CPU (e.g. with status register polling) so that the DMA cannot work. This case is easy to exclude anyway, do not poll on APB2, just wait end of DMA (or poll DMA SR if you like).

Posted on July 02, 2014 at 21:54

@Brutte:

Thank you. Your posting in the AVRFreaks could have brought me on the right track!

From what I gather, your SYSCLK is running at 32MHz while mine is derived from the HSI and thus only running at 16MHz. I assume this leads to the AHB and/or APB being too slow to handle the DMA transfers on time. As on my board I do not have an external clock source available I can not verify this (I also don't have a Discovery board available currently).

If you or anyone else has the time, it would be great if this could be verified. The clock switching could be done by setting the SW bit in RCC_CFGR to 0x1 after enabling the HSI oscillator.

Anyway. This already helped me quite a bit! Thanks a lot to both of you!

---------------------------------------------

EDIT: @Laurent: I do not currently have my board available and can only test it tomorrow, but by setting ADC_CR(1 or 2?).DELS to 2 or 3 it worked like a charm. So I assume it would also work when clocking it with 500kHz.

Also, I'm not actively polling anything on the bus, I'm only polling a flag set through the DMA IRQ. Is it actually possible to poll the DMA as you mention it? From what i gather, there is only a DMA Interrupt Status Register, but no regular status register. So I think I have to enable the DMA interrupt anyway?

stm322399
Senior
Posted on July 02, 2014 at 22:00

The PLL can x2 the HSI, so that your AHB/APB2 will be 32MHz.

Posted on July 02, 2014 at 22:11

Great, thanks! I did overlook that. Will try this tomorrow.

os_kopernika
Associate II
Posted on July 02, 2014 at 22:48

''your SYSCLK is running at 32MHz''

HSE

''I assume this leads to the AHB and/or APB being too slow to handle the DMA transfers on time.''

Yes but your throughput is only 0.5Msps with 16MHz SYSCLK which gives the same ratio as mine (1Msps with 32MHz clock). Also consider I execute that (SRAM intensive V(X) ) from SRAM while your is (most likely) from flash. So no, IMHO you should be able to provide ~32 of SRAM transactions in parallel with ~32 of Flash transactions in between conversions (ignoring stalls, collisions, folding, prefetch, etc).

Posted on July 03, 2014 at 09:10

''your throughput is only 0.5Msps with 16MHz SYSCLK which gives the same ratio as mine (1Msps with 32MHz clock).''

 

Are you refering to the throughput on the Bus? Where would this be specified? Or are you refering to the ADC? Because ADCCLK is always directly derived from the HSI, so that ratio would not be the same.

So no, IMHO you should be able to provide ~32 of SRAM transactions in parallel with ~32 of Flash transactions in between conversions (ignoring stalls, collisions, folding, prefetch, etc).

I'm not sure what you wanted to tell me with that. I should be able to execute 32 transactions on the bus between the end of one conversion and the beginning of the next one? At what sample rate?

Posted on July 03, 2014 at 09:34

Now I think I understand what you were trying to say: that it should work even with a 16MHz system clock?

Looks like someone configured the MSI to be used as system clock... with the HSI it seems to work just fine.

os_kopernika
Associate II
Posted on July 03, 2014 at 12:12

''that it should work even with a 16MHz system clock?''

Yes, that is how I understand that (not tested) because your throughput requires only one DMA transaction (possibly SRAM transactions) per 32 system clock ticks. The overrun would mean that DMA was not able to gain access to data bus during that 32 ticks.

So IMHO 500kHz DMA should work even with lower system clock (mind there is an additional synchronization time required in between different clock domains)
sinorown
Associate
Posted on July 07, 2014 at 01:45

@moktor:

I'm just wondering what could be the cause.

Have you found it yet?

If yes, please enlighten me.

Thanks.