cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F4 ADCs Can Achieve 2.4MSPS?

h239955
Associate II
Posted on October 02, 2014 at 15:36

Hi,

I�m running a single ADC in continuous mode with f

ADC

= 21MHz and expect it to provide 1.4MSPS. I seem to be getting only 0.55MSPS. Can anybody explain why this might be?

According to the STM32F407xx Datasheet (D022152, Rev 4), each of the three ADCs on the STM32F4 Discovery board should be able to take 12-bit samples at a rate of 2.4MSPS when 

f

ADC

is at its maximum value of 36MHz. This figure ties up with 15 ADC cycles being used for each sample (sample time = 3 cycles + 12 for the 12-bit conversion).

I�m using the same sampling strategy, just with a lower fADC clock speed so I can �t see how the chip is supposed to be driven to provide the quoted conversion rates. I know that I could use triple-interleaved mode to get the speed up however I�m fairly new to this area of software and would just like to understand what�s going on. Can anybody help?

Clock Setup

: I�m using the 8MHz external crystal on the Discovery board and have attached �system_stm32f4xx.c� for reference. In �stm32f4xx.h� I have changed HSE_VALUE to 8000000. I have confirmed that the period of PCLK1 is as expected (42MHz) as I am using a timer to trigger DAC output and have confirmed with a 'scope the time between updates.

ADC samples seen:

I�m using DMA to write the samples into a 32 element circular buffer, then examining the contents with the Crossworks debugger (allowing it to read the contents and then stopping it). The ADC input is a 50kHz sinewave. At 1.2MSPS I should see 28 samples for each period of the sinewave but I see only 11.

ADC Setup Code:

&sharpdefine ADC_BUFFER_SIZE   32

/* You can monitor the converted value by adding the variable ''ADC3ConvertedValue''

   to the debugger watch window */

__IO uint16_t ADC1Buffer1[ADC_BUFFER_SIZE];

/**

  * @brief  ADC1 channel10 with DMA configuration

  * @param  None

  * @retval None

  */

void ADC1_CH10_DMA_Config(void)

{

  ADC_InitTypeDef       ADC_InitStructure;

  ADC_CommonInitTypeDef ADC_CommonInitStructure;

  DMA_InitTypeDef       DMA_InitStructure;

  GPIO_InitTypeDef      GPIO_InitStructure;

  /* Enable ADC1, DMA2 and GPIO clocks ****************************************/

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOC, ENABLE);

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

  /* DMA2 Stream0 channel0 configuration **************************************/

  DMA_InitStructure.DMA_Channel = DMA_Channel_0; 

  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC1_DR_ADDRESS;

  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&ADC1Buffer1;

  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;

  DMA_InitStructure.DMA_BufferSize = ADC_BUFFER_SIZE;

  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;

  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;

  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;

  DMA_InitStructure.DMA_Priority = DMA_Priority_High;

  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;        

  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;

  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;

  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;

  DMA_Init(DMA2_Stream0, &DMA_InitStructure);

  DMA_Cmd(DMA2_Stream0, ENABLE);

  /* Configure ADC1 Channel10 pin as analog input ******************************/

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;

  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;

  GPIO_Init(GPIOC, &GPIO_InitStructure);

  /* ADC Common Init **********************************************************/

  ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;

  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;  // Min value = Div2

  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;

  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;  // Min value = 5Cycles

  ADC_CommonInit(&ADC_CommonInitStructure);

  /* ADC1 Init ****************************************************************/

  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;

  ADC_InitStructure.ADC_ScanConvMode = DISABLE;

  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;

  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;

  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;

  ADC_InitStructure.ADC_NbrOfConversion = 1;

  ADC_Init(ADC1, &ADC_InitStructure);

  /* ADC1 regular channel10 configuration *************************************/

  ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_3Cycles);

 /* Enable DMA request after last transfer (Single-ADC mode) */

  ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);

  /* Enable ADC1 DMA */

  ADC_DMACmd(ADC1, ENABLE);

  /* Enable ADC1 */

  ADC_Cmd(ADC1, ENABLE);

}

#adc #discovery #board
6 REPLIES 6
stm322399
Senior
Posted on October 02, 2014 at 16:23

If the PCLK is 42MHz and prescaler is 4, I won't say that fADC is 21MHz.

With fADC at 10.5MHz, you must get 14 samples per 50KHz period, which is closer to the 11 you observed. There is still a small difference though.

I find useful to toggle an I/O every time the DMA wraps, so the sample rate can be estimated accurately.

h239955
Associate II
Posted on October 02, 2014 at 16:38

Hi Laurent,

I think my prescaler is ok for fADC = 21MHz. The clock period that I measured at 42MHz is PCLK1, however fADC is based on PCLK2 which should be running twice as fast with my clock setup.

That's a great idea using GPIO to see when the DMA wraps. I'll see if I can figure out how to do that.

Thanks for your help,

Harvey

Posted on October 02, 2014 at 18:13

Yes APB2 should be 84 MHz, so 21 MHz / 15 = 1.4 Msps

To get to 2.4 Msps you'd run the processor at 144 MHz, to get a 36 MHz ADC Clock.

I demonstrated a similar scenario on an STM32F3-DISCO the other day.

https://my.st.com/public/STe2ecommunities/mcu/Lists/STM32Discovery/Flat.aspx?RootFolder=https://my.st.com/public/STe2ecommunities/mcu/Lists/STM32Discovery/STM32F3Discovery%20ADC_DMA%20project&FolderCTID=0x01200200770978C69A1141439FE559EB459D75800084C20D8867EAD444A5987D47BE638E0F&currentviews=40

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
Posted on October 02, 2014 at 18:17

Remember also that the ADC and DMA act autonomously from the processor, and will not stop because you have halted the CPU in a debugger.

Best was is to toggle a GPIO, which will have half the interrupt rate, and also divided by the sample buffer size.

With 1000 samples we get from 1.4 MHz to 1.4 KHz and then a pin that can be scoped at 700 Hz 
Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
h239955
Associate II
Posted on October 03, 2014 at 10:32

That link with the example code is very helpful. I was using the debugger to take a snapshot of the ADC data. I guess this approach is the chief suspect as to where I’m going wrong. I will try with toggling the IO pin and see how I get on.

Many thanks,

Harvey

h239955
Associate II
Posted on October 03, 2014 at 13:28

SOLVED. 

Toggling the IO pin confirms that the ADC is converting at 1.4MSPS as expected. Using the debugger to take a snapshot of a buffer being updated at that rate was in hindsight unrealistic. This experiment proves that the 32 buffer values shown by the debugger when I tell it to stop updating the Watch values are not a contiguous set of samples.

Many thanks to Clive and Laurent who both suggested the use of the IO pin to monitor the DMA Transfer Completion event.