cancel
Showing results for 
Search instead for 
Did you mean: 

9 pins of ADC with DMA2 and timer TIM2 for sampling 200kHz

Nabila Gina Nastiti
Associate II
Posted on June 08, 2018 at 14:40

Hello everyone,

I have 9 pins of ADC that each of them will received signal from function generator 20 kHz. I want to use ADC to store the data inside an array by using DMA2. ADC1 has 3 pins, ADC2 has 3 pins, ADC3 has 3 pins. Because the sampling for all 9 pins supposed to be done at the same time (as they are received input at the same time), therefore i use triple mode regular simultaneous. The idea is to read all 9 pins, convert them, store them in DMA2 bufer (size : 3*1000) when conversion finish, and start read from ADC1again and repeat this. It is because i want to store all the signals at the same time from different pins. The process stop when buffer is full.

I use TIM2 for sampling and make external trigger conversion ADC1 as master TRGO, while ADC2 and ADC3 are set to CC2. I disable DMA Access Mode as each stream of DMA2 is connected to one ADC, where stream0 for ADC1, stream2 for ADC2 and stream1 for ADC3. I set the NVIC for all DMA2 streams with preemption priority stream0 as 0, stream2 as 1, and stream1 as 2.

Could anyone help me ifit is do-ableor did i miss something ? I understand that it requires a very fast conversion, but i cannot use fast interleaved because i need all three ADCs. The result so far is not as expected as the conversion stored in array is not sinusoidal.

Please inform me if this problem description is not clear

Thank you

Best regards,

Nabila

#timer #dma #stm32f429i-disc1 #adc

Note: this post was migrated and contained many threaded conversations, some content may be missing.
9 REPLIES 9
AvaTar
Lead
Posted on June 11, 2018 at 09:52

Do you mean 9 different input channels, or 3 inputs sampled with 3 ADCs ?

I don't know which MCU you are using, but 200kHz can be easily achieved with one ADC and 9 channels.

Because the sampling for all 9 pins supposed to be done at the same time (as they are received input at the same time), ...

In a strict sense, this is not possible. You would need 9 separate ADCs for that.

Using one ADC with 9 multiplexed channels, you get a fixed, constant offset between channels. With a sufficient sample rate, this should not pose difficulties.

Posted on June 11, 2018 at 10:04

Hi , thank you for your reply

I use three ADCs, each ADC has 3 inputs, so in total there are 9 different inputs

I am using STM32F429I

I have question about the sampling time. Below is my configuration for one ADC :

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 Init ****************************************************************

ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;

ADC_InitStructure.ADC_ScanConvMode = ENABLE;

ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // triggered by TIM2

ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising; 

// addition ADC1 is master to ADC2 and ADC3

ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;

ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;

ADC_InitStructure.ADC_NbrOfConversion = 3;

ADC_Init(ADC1, &ADC_InitStructure);

// ADC regular channel configuration *************************************

ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 1, ADC_SampleTime_15Cycles);

ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 2, ADC_SampleTime_15Cycles);

ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 3, ADC_SampleTime_15Cycles); 

And here is the configuration for timer :

/* Enable TIM2 Peripheral clock */

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);

TIM_TimeBaseStructure.TIM_Prescaler = 0;//1;

TIM_TimeBaseStructure.TIM_Period = (84000000/ 200000) - 1;// 200 khz from 84 MHz TIM2CLK APB1 = HCLK/4, TIM2CLK = HCLK/2 //42000 1kHz Interrupt frequency

TIM_TimeBaseStructure.TIM_ClockDivision = 0;

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);

TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

TIM_Cmd(TIM2, ENABLE);

Is it correct way to do sampling 200khz ? Should i use reduce the sampling time to 3 cycle ? I've tried to calculate it correctly but i think i didnt understand it correctly

The DMA will poll the result of ADC after finish conversion (Transfer Complete) and store it in array.

Thank you for your attention

Regards,

Nabila G.

Posted on June 11, 2018 at 10:09

Oh and the ADC mode is triple mode regular simultaneous

Posted on June 11, 2018 at 11:09

Is it correct way to do sampling 200khz ? Should i use reduce the sampling time to 3 cycle ? I've tried to calculate it correctly but i think i didnt understand it correctly

The sampling time does not define the sampling rate (except in continuous mode, which is inappropriate in your case). It is the time the S&H capacitor is connected to the input, i.e. the voltage to sample.

In your case, the sampling rate is defined by TIM2, generating the trigger signal.

Just make sure that the sum of sampling time + conversion time + inter-sampling delay (ADC_TwoSamplingDelay) for all three channels is less than the trigger rate.

In other words, the next timer-generated trigger happens only after sampling + conversion + inter-sample delay for the previous is finished.

And make sure to copy the results (e.g. after the DMA TC interrupt) before the first conversion of the next sequence is finished and DMA starts to overwrite your buffer.

Posted on June 11, 2018 at 11:50

Hi,

Because my input is 20 kHz, and want to set timer for sampling 200 kHz, so samplingtime+converstiontime+intersamplingdelay < 5 us , right?

The conversion time is (samplingtime+resolution)=15+12=27 cycle

                                    Intersamplingdelay            = 5 cycles

                                    sampling time  -> i set single ADC with buffer size 3 , so 200 kHz/3 = 66.6 kHz

Is this correct ?

Here is the configuration for one of DMA2. DMA2 with stream 0 :

DMA_Cmd(DMA2_Stream0, DISABLE);

DMA_InitStructure.DMA_Channel =DMA_Channel_0;

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC1_DR_ADDRESS; // read from ADC1 register

DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&ADCConvertedValue1; // send data to RAM buffer

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;

DMA_InitStructure.DMA_BufferSize = 3;//*1000;

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; // half word = 8 bit

DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //circular

DMA_InitStructure.DMA_Priority = DMA_Priority_High;

DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; // dis

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

// Clear update interrupt bit

DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);

// interrupt DMA when transfer is complete

DMA_ITConfig(DMA2_Stream0, DMA_IT_TC , ENABLE);// | DMA_IT_HT

DMA_Cmd(DMA2_Stream0, ENABLE);

Here is one of the DMA interrupt :

void DMA2_Stream0_IRQHandler(void){ 

if(DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0)){

//add code here to process second half of buffer

channel = 0;

sig.mic_sig[0].mdata[test_cnt_up1]= (ADCConvertedValue1[0]*div_val/0xFFF)/dec_val;

sig.mic_sig[1].mdata[test_cnt_up1]= (ADCConvertedValue1[1]*div_val/0xFFF)/dec_val;

sig.mic_sig[2].mdata[test_cnt_up1]= (ADCConvertedValue1[2]*div_val/0xFFF)/dec_val;

test_cnt_up1++;

//add_two1+=2;

if(test_cnt_up1 == 1000){

DMA_Cmd(DMA2_Stream0, DISABLE);

DMA_ITConfig(DMA2_Stream0, DMA_IT_TC , DISABLE);

}

DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);

}

}

Thank you for your help

Posted on June 11, 2018 at 12:07

Because my input is 20 kHz, and want to set timer for sampling 200 kHz, so samplingtime+converstiontime+intersamplingdelay < 5 us , right?

The conversion time is (samplingtime+resolution)=15+12=27 cycle

                                    Intersamplingdelay            = 5 cycles

                                    sampling time  -> i set single ADC with buffer size 3 , so 200 kHz/3 = 66.6 kHz

This calculation relates to the ADC clock, especially the sampling and delay cycles.

For a 14MHz clock (max. value on many of ST's ADCs), this would give about 71ns per cycle, so about 2us.

If you don't need to increase the sampling rate, I would probably increase the sampling time a bit. Shorter sampling times complicates the design of the input stages.

sig.mic_sig[0].mdata[test_cnt_up1]= (ADCConvertedValue1[0]*div_val/0xFFF)/dec_val;

This code might get you into rounding/saturation/truncation trouble, considering C's sometimes surprising type promotion. Validate the possible ADC values with all possible

div_val

and

dec_val

values.
Posted on June 11, 2018 at 14:03

Hi,

I tried increasing the sampling time from 15 to 28 cycles but the result is still not what i want. The input is sinusoid and i expect the ADC output will look like sinusoid also as the sampling is very fast. The output suppose to be in range between 0-2V , but the results inside the array show only 2V or something 1.9V , and the sampling seem like not consistent, so i suspect that i made wrong sampling.

For div_val and dec_val, those are float type and ADCConvertedValue1 is uint16_ t type . I dont think they make problem. I have change 0xFFF to 4095, but the result is still the same.

Do you know where else should i check ?

Thank you for your help

Posted on June 11, 2018 at 15:46

I would start with verifying that the ADC input end up in the channels they are supposed to.

For instance, put a constant, but different voltage on each input. Even with some noise, the values should be clearly distinguishable. Some voltage divider would do the job, for instance.

I tried increasing the sampling time from 15 to 28 cycles but the result is still not what i want. The input is sinusoid and i expect the ADC output will look like sinusoid also as the sampling is very fast. The output suppose to be in range between 0-2V , but the results inside the array show only 2V or something 1.9V , and the sampling seem like not consistent, so i suspect that i made wrong sampling.

I never tried triple mode, so I can't judge immediately if the code is correct. Have you pulled the code from a working triple-mode ADC example ?

I would have started with one ADC, and 9 channels.

Do you have declared all buffers touched by DMA/interrupt as volatile ?

For div_val and dec_val, those are float type and ADCConvertedValue1 is uint16_ t type . I dont think they make problem. I have change 0xFFF to 4095, but the result is still the same.

Then make sure the FPU is enabled and you use it. Otherwise, the resulting runtime might be surprising.

And, I would reduce the expression div_val/0xFFF)/dec_val to one pre-calculated single value, to reduce the runtime.

Posted on June 11, 2018 at 16:14

Hi,

I've tried with constant voltage and it works fine, with the configuration both for triple mode and independent mode.

I haven't try to do triple mode for one ADC and 9 channels. I will check it out.

I have declared all buffers as volatile.

I am not sure how to change the expression of that calculation, but i will try.

Thanks for your help