cancel
Showing results for 
Search instead for 
Did you mean: 

ADC sampling rate

dkilshtein9
Associate II
Posted on January 04, 2013 at 21:21

I need to sample 2 ADC channels at a rate of 200KHz I also need to sample another ADC channel at a rate of kHz.

Currently I use the same ADC (ADC3) for all 3 channels.

During the initialization, I supply the DMA with a buffer (of 9000 longs) and I set ADC_NbrOfConversion=3.

Everything looks fine (the whole buffer is filled)

The problem is that all channels are sampled with the same rate. Is there a way to sample the first two with a high rate and the other channel with lower rate? 

Is there a way of using different buffers (one buffer for the fast channels 1,2 and another buffer for the slow channel 3)?

When I look at the buffer I see:

Buff[0] = adc1_val

Buff[1] = adc2_val

Buff[2] = adc3_val

Buff[3] = adc1_val

Buff[4] = adc2_val

Buff[5] = adc3_val

I would like to see a buffer like that: 

Buff[0] = adc1_val

Buff[1] = adc2_val

Buff[2] = adc1_val

Buff[3] = adc2_val

Buff[4] = adc1_val

Buff[5] = adc2_val

Buff[6] = adc3_val

Buff[7] = adc1_val

Buff[8] = adc2_val

Buff[9] = adc1_val

Buff[10] = adc2_val

Buff[11] = adc1_val

Buff[12] = adc2_val

Buff[13] = adc3_val

Or two different buffers.

Here is the code i use to initialize the ADC:

void ADC_HW_Init(void)

{

  ADC_InitTypeDef       ADC_InitStructure;

  ADC_CommonInitTypeDef ADC_CommonInitStructure;

  DMA_InitTypeDef       DMA_InitStructure;

  GPIO_InitTypeDef      GPIO_InitStructure;

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_GPIOA, ENABLE);

  

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE);

  

  DMA_InitStructure.DMA_Channel = DMA_Channel_2;  

  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC3_DR_ADDRESS;

  DMA_InitStructure.DMA_Memory0BaseAddr =  (uint32_t)&val[0];

  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;

  DMA_InitStructure.DMA_BufferSize = 9000

  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_Word;

  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);

 

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;

  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;

  GPIO_Init(GPIOF, &GPIO_InitStructure);

  

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_2;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;

  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;

  GPIO_Init(GPIOA, &GPIO_InitStructure);

  ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;

  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;

  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;

  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;

  ADC_CommonInit(&ADC_CommonInitStructure);

  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;

  ADC_InitStructure.ADC_ScanConvMode = ENABLE;

  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;;

  

  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;

  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;

  ADC_InitStructure.ADC_NbrOfConversion = 3;

  ADC_InitStructure.ADC_ExternalTrigConv = 0;

  ADC_Init(ADC3, &ADC_InitStructure);

  ADC_RegularChannelConfig(ADC3, ADC_Channel_7, 1, ADC_SampleTime_56Cycles);

  ADC_RegularChannelConfig(ADC3, ADC_Channel_1, 2, ADC_SampleTime_56Cycles);

  ADC_RegularChannelConfig(ADC3, ADC_Channel_2, 3, ADC_SampleTime_56Cycles);

  ADC_DMARequestAfterLastTransferCmd(ADC3, ENABLE);

 

#stm32-adc #stm32-adc #stm32-adc #stm32-adc #stm32-adc #atm32-adc
16 REPLIES 16
Posted on January 04, 2013 at 21:40

May be you should use a different ADCx unit, or inject slower requests periodically, though I think that will complicate the buffer management.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
ken239955
Associate II
Posted on January 05, 2013 at 06:11

I cannot see how you can achieve this using DMA.

One solution would be to use interrupts and manage the data storage in the ISR.

frankmeyer9
Associate II
Posted on January 05, 2013 at 14:11

Or, for simplicity, you could just drop some results of the third channel, using every second, third or fourth.

The sample rate for ch3 could than only be a fraction of that for ch1+2, though.

dkilshtein9
Associate II
Posted on January 06, 2013 at 11:02

Thanks for the answers. Since I brought it up, I got some new requirements:

1. It is OK to use 2 ADCs (one for the fast channel and another for the other two slow channels)

2. All data has 8bit resolution and not 12 as I previously configured.

My system clock is 120MHz and my biggest issue now is to configure the ADC to a sample rate of 200 KHz.

I configured these parameters:

Div = 8

ADC_SampleTime_15Cycles = 15

ADC_delay_between_2_sampling_phases = 5 

Are these correct? Will the code bellow do the job?

void ADC_HW_Init(void)

{

  

  ADC_InitTypeDef       ADC_InitStructure;

  ADC_CommonInitTypeDef ADC_CommonInitStructure;

  DMA_InitTypeDef       DMA_InitStructure;

  GPIO_InitTypeDef      GPIO_InitStructure;

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOF, ENABLE);

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE);

  

  DMA_InitStructure.DMA_Channel = DMA_Channel_2;  

  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC3_DR_ADDRESS;

  DMA_InitStructure.DMA_Memory0BaseAddr =  (uint32_t)&val[0];

  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;

  DMA_InitStructure.DMA_BufferSize = BUFF_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_Word;

  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);

 

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;

  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;

  GPIO_Init(GPIOF, &GPIO_InitStructure);

  

  

  ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;

  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div8;

  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;

  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;

  ADC_CommonInit(&ADC_CommonInitStructure);

  ADC_InitStructure.ADC_Resolution = ADC_Resolution_8b;

  ADC_InitStructure.ADC_ScanConvMode = ENABLE;

  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_InitStructure.ADC_ExternalTrigConv = 0;

  ADC_Init(ADC3, &ADC_InitStructure);

  ADC_RegularChannelConfig(ADC3, ADC_Channel_7, 1, ADC_SampleTime_15Cycles);

  ADC_DMARequestAfterLastTransferCmd(ADC3, ENABLE);

 

}

frankmeyer9
Associate II
Posted on January 07, 2013 at 10:06

Not sure which derivate you use, but 120MHz suggests  F2xx or F4xx.

My system clock is 120MHz and my biggest issue now is to configure the ADC to a sample rate of 200 KHz.

 

The best solution is to use a timer to trigger the ADC. You will need to set the ADC_ExternalTrigxxx entries correctly for the initialisation, and setup the timer.

I don't have an appropriate F2 example at hand, but there should be some in the FW lib.

dkilshtein9
Associate II
Posted on January 07, 2013 at 17:39

Thanks for the reply.

You are right I forgot to mention I use the F2xx on the STM3220G-EVAL.

I know I can use timer as an external trigger but power consumption is a critical issue in this project and to save as much power as I can I prefer not using hardware blocks that I can do without.

Are you saying there is no way to configure the ADC to work at 200KHz at all?

What are these parameters for?

 

ADC_Prescaler_Divx

ADC_SampleTime_xCycles

ADC_delay_between_2_sampling_phases 

frankmeyer9
Associate II
Posted on January 07, 2013 at 18:07

Are you saying there is no way to configure the ADC to work at 200KHz at all?

 

At least I think it is not the intended way, and IMHO an inconvenient and ponderous method.

The trigger timing itself is not part of the ADC functionality, instead you have options for external triggers, such as timers.

I often used a SW trigger from the Systick handler, which is not really a recommended option for a 200kHz rate.

And I'm sure one timer unit will not make much difference for the power consumption. Scaling down the system clock to 100/80/60 MHz would save much more.

ADC_Prescaler_Divx

 

ADC_SampleTime_xCycles

 

ADC_delay_between_2_sampling_phases 

 

The meaning is documented in the reference manual, but citing from memory:

ADC_Prescaler_Divx sets the prescaler for the ADC clock generation, basically the speed of the SA sequencer.

ADC_SampleTime_xCycles is the time (number of cycles) the S&H stage is sensing the input voltage.

ADC_delay_between_2_sampling_phases is an additional, configurable delay between two samplings. I came across that only in dual/triple ADC modes yet.

Posted on January 07, 2013 at 20:12

I'd look to pace the ADC from one of the TIM units. If you prescale down to 1MHz (say), the bulk of the high frequency consumption would be in the prescaler and not the whole TIM unit. Consider also a timer on APB1, and keep the speed of it down.

In terms of power consumption you want to watch the pin drivers, especially ones outputting clocks or periodic signals. Use the lowest frequency slew rate settings.

As fm suggests look at cutting the clocking down, figure out how much work there is to do, and the clock required to meet dead-lines. Look at the power envelope, and use WFI went idling.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
dkilshtein9
Associate II
Posted on January 08, 2013 at 12:40

Thanks,

First of all I use the __WFI() in the vApplicationIdleHook() function of FreeRTOS.

Now, about the ADC + timer, what would be better to create a timer interrupt and call ADC_SoftwareStartConv(ADCx) from the timer ISR (may not be as accurate due to a clock shift in a multi tasking environment) or to set the ADC to work with an external trigger (probably more accurate but harder to verify)?

Does anyone have an example of how to connect a timer to an ADC (sorry, but I need to provide a working solution by the end of this week)?