cancel
Showing results for 
Search instead for 
Did you mean: 

DMA and ADC on the STM32F2xx

jerry239955_st
Associate II
Posted on October 18, 2013 at 22:41

Trying to stuff multiple channels of ADC3 into a DMA, but am unsure of how the DMA address gets incremented. Is this done automatically behind the scenes and I don't have to worry, or am I missing a setup?  I didn't see an explanation of how DMA_InitStructure.DMA_MemoryInc works.  Any help here is appreciated.

I will also be double buffering, what is the best way to trigger the DMA_ISR after the last conversion?

I expect the

ADC_Zone_ADC buffer to have 8 Half words stuffed into it.

My setup follows  

void ADC_DMA_Config(void)

{

  DMA_InitTypeDef DMA_InitStructure;

  DMA_InitStructure.DMA_Channel = DMA_Channel_0; 

  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&ADC_Zone_ADC;

  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC2->DR;

  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;

  DMA_InitStructure.DMA_BufferSize = 8;

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

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

void ADC_Setup_Zone_ADC(void)

{

  GPIO_InitTypeDef      GPIO_InitStructure;

  ADC_InitTypeDef       ADC_InitStructure;

  ADC_CommonInitTypeDef ADC_CommonInitStructure;

  

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE);

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);  

 

  GPIO_InitStructure.GPIO_Pin= GPIO_Pin_3|GPIO_Pin_4|

GPIO_Pin_5

GPIO_Pin_6

|

GPIO_Pin_7|

GPIO_Pin_8|

GPIO_Pin_9|

GPIO_Pin_10

;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;

  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;

  GPIO_Init(ADC_ZONE_MON_PORT, &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_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;

  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;

  ADC_InitStructure.ADC_NbrOfConversion = 1;

  ADC_Init(ADC3, &ADC_InitStructure);

  ADC_RegularChannelConfig(ADC3,ADC_Channel_9, 1, ADC_SampleTime_15Cycles);

  ADC_RegularChannelConfig(ADC3,ADC_Channel_5, 2, ADC_SampleTime_15Cycles);

  ADC_RegularChannelConfig(ADC3,ADC_Channel_14, 3, ADC_SampleTime_15Cycles);

  ADC_RegularChannelConfig(ADC3,ADC_Channel_6, 4, ADC_SampleTime_15Cycles);

  ADC_RegularChannelConfig(ADC3,ADC_Channel_15, 5, ADC_SampleTime_15Cycles);

  ADC_RegularChannelConfig(ADC3,ADC_Channel_7, 6, ADC_SampleTime_15Cycles);

  ADC_RegularChannelConfig(ADC3,ADC_Channel_4, 7, ADC_SampleTime_15Cycles);

  ADC_RegularChannelConfig(ADC3,ADC_Channel_8, 8, ADC_SampleTime_15Cycles);

  

  ADC_Cmd(ADC3, ENABLE);

  ADC_SoftwareStartConv(ADC3);

}

5 REPLIES 5
Posted on October 18, 2013 at 22:52

The DataSize determines the MemoryInc amount, the BufferSize is of units DataSize, ie not sizeof(). It's a DMA controller, it manages such stuff.

ADC_InitStructure.ADC_NbrOfConversion = 1;

That would need to be 8, seeing as your setting up 8 channels.

Make the buffer bigger, use HT and TC interrupts to ping-pong between halves of the buffer.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
jerry239955_st
Associate II
Posted on October 18, 2013 at 23:14

Thanks Clive.

Instead of using HT and TC, couldn't I just interrupt on the TC, stop conversion, copy the buffer to a secondary buffer, then re-enable the conversion?

From what I understand from your response, the DMA-ADC binding will control when the DMA buffer address in incremented....that is, when the ADC finishes one channel conversion & gets ready for the next, it writes to the DMA and then increments the buffer address.  I understand this all happens in the background, I just want to make sure it happens. 🙂
Posted on October 19, 2013 at 01:25

The DMA + ADC interaction is not as tightly coupled as you seem to be suggesting. The DMA basically services the ADC EOC for each conversion, it's pretty agnostic to what else is going on in the peripherals. The ADC cycles through the channels, you'd need to manage the DMA so it's buffer is a multiple of the sample count, otherwise keeping track of the channel/position will become a night mare.

You mentioned double-buffering, there are ways to feed the DMA different buffers and basically chain through a list. It gets complicated quickly, so unless you're familiar with such techniques I'd avoid. I've built my share, but simple/elegant is the way to go when possible, as is working with data in-place.

Circular DMA, with HT and TC do this cleanly, and doesn't require moving data. It's good for audio type apps. Again you'd probably want a buffer bigger than 2x 8, a bigger buffer allows the interrupt load to be decimated, and processing to work on multiple samples. This may, or may not be appropriate for your application.

Some people would just use an 8 sample buffer and just let the ADC keep rolling, and just pluck the values out as needed. Others might want to trigger the burst of 8 samples from a timer trigger, and catch the TC as the EOC for all samples.

If jamming the ADC at full bore you'll want to make sure you don't overwhelm your processing/handling code.

The options are quite extensive, and flexible. I don't work with ADC's a lot, but generally I'm more concerned with placement of samples in the time domain. Nevertheless I seem to be building lots of examples.
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 19, 2013 at 01:34

Stopping/Starting is fraught with a lot of initialization code, and potential to lose synchronization. If you have a periodic requirement it would be better just to leave everything set up in a circular mode, not have the ADC run in continuous mode, but rather triggered manually or with a timer, at the periodicity desired.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
jerry239955_st
Associate II
Posted on October 21, 2013 at 15:17

I like the timer method suggestion.  Thanks for the help.  I will try and post the code when I am done.