cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F4 DMA and ADC

pedro
Associate II
Posted on March 20, 2015 at 00:24

Hi,

I want to configure the DMA to double buffer mode to work with triple adc in interleaved mode. My idea is to process data while getting new samples from the ADCs.

From the examples I've seen I will have no problem configuring the DMA to double buffer. Although my problem is with the processing I have to do when switching the DMA buffers, or in other words, what to do in the TC interrupt.

From the RM0090:

''If the conversion sequence is interrupted (for instance when DMA end of transfer occurs),

the multi-ADC sequencer must be reset by configuring it in independent mode first (bits

DUAL[4:0] = 00000) before reprogramming the interleaved mode.''

After reading the text above from the datasheet it is not clear to me the procedure. Should I reprogram all the ADC registers? Or should I only change the values of DUAL? Or should I disable the ADC and set the SWSTART flag again while changing DUAL values?

This question is kind of general to what should be the algorithm to implement after a DMA TC.

At last, I think it is useful to say that I'm kind of lost because this is my first time working with the DMA.

Thanks a lot and all help is appreciated 🙂

9 REPLIES 9
pedro
Associate II
Posted on March 20, 2015 at 21:00

Update:

I started experimenting with DMA and ADC in triple interleaved mode. I based myself on the examples provided by ST. 

Using the code below, I ran into a problem where the printed numbers don´t change even when I change the PC2 Pin from GND to 3V (Not even if I reset the uprocessor).

The configuration is almost exactly the same as the example from ST. Only changed the DMA Stream/Channel to my board ADC1 Request at Stream0 channel0. 

(Using STM32f407)

Code:

__IO uint32_t ADCTripleConvertedValue[3];

void ADC_Config(){

GPIO_InitTypeDef GPIO_InitStructure;

DMA_InitTypeDef DMA_InitStructure;

ADC_InitTypeDef ADC_InitStructure;

ADC_CommonInitTypeDef ADC_CommonInitStructure;

RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOC , ENABLE);

RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_DMA2 , ENABLE);

RCC_APB2PeriphClockCmd( RCC_APB2Periph_ADC1 , ENABLE);

RCC_APB2PeriphClockCmd( RCC_APB2Periph_ADC2 , ENABLE);

RCC_APB2PeriphClockCmd( RCC_APB2Periph_ADC3 , ENABLE);  

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;

GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;

GPIO_Init(GPIOC, &GPIO_InitStructure);

/*Channel 0 Stream 0 DMA2*/

DMA_InitStructure.DMA_Channel = DMA_Channel_0; 

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC->CDR;

DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&ADCTripleConvertedValue;

  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;

  DMA_InitStructure.DMA_BufferSize = 3;

  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;

  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; //Not Used Since FIFO Disabled

  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;

  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;

  DMA_Init(DMA2_Stream0, &DMA_InitStructure);

  /* DMA2_Stream0 enable */

  DMA_Cmd(DMA2_Stream0, ENABLE);

/* ADC Common configuration *************************************************/

// ADC clock 21Mhz

  ADC_CommonInitStructure.ADC_Mode = ADC_TripleMode_Interl;

  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;

  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_2;  

  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; 

  ADC_CommonInit(&ADC_CommonInitStructure);

/* ADC1 regular channel 12 configuration ************************************/

// External trigger for regular channel done by software

  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_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;

  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;

  ADC_InitStructure.ADC_NbrOfConversion = 1;

  ADC_Init(ADC1, &ADC_InitStructure);

ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 1, ADC_SampleTime_3Cycles);

/* Enable ADC1 DMA request.  */

  ADC_DMACmd(ADC1, ENABLE);

/* ADC2 regular channel 12 configuration ************************************/

  ADC_Init(ADC2, &ADC_InitStructure);

  /* ADC2 regular channel12 configuration */ 

  ADC_RegularChannelConfig(ADC2, ADC_Channel_12, 1, ADC_SampleTime_3Cycles);

  /* ADC3 regular channel 12 configuration ************************************/

  ADC_Init(ADC3, &ADC_InitStructure); 

  /* ADC3 regular channel12 configuration *************************************/

  ADC_RegularChannelConfig(ADC3, ADC_Channel_12, 1, ADC_SampleTime_3Cycles);

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

//Still make requests after last transfer done. Do DMA Requests as long as there is data to be converted

  ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);

/* Enable ADC1 **************************************************************/

  ADC_Cmd(ADC1, ENABLE);

  /* Enable ADC2 **************************************************************/

  ADC_Cmd(ADC2, ENABLE);

  /* Enable ADC3 **************************************************************/

  ADC_Cmd(ADC3, ENABLE);

}

int main(){

unsigned int i;

USART2_Init(); // Inicialized USART2 for printf

//INIT_TIM2(); // Inicialize Timer2

ADC_Config();

ADC_SoftwareStartConv(ADC1);

for(i=0; i < 5000000;i++);

printf(''1-%d 2-%d 3-%d'',ADCTripleConvertedValue[0],ADCTripleConvertedValue[1],ADCTripleConvertedValue[2]);

while (1);

}

Posted on March 20, 2015 at 21:22

Really want an address, not content

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC->CDR;

https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Problem%20with%20ADC%20DMA%20and%20timer&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&currentviews=2...

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
pedro
Associate II
Posted on March 20, 2015 at 22:31

Thanks a lot clive!

You have no idea how much time I lost searching for an example like that....

Couple of question if you can answer:

- I want the conversion to be continuous in the triple interleaved mode. The best way to achieve that is by putting a timer as external trigger for the ADC1 and the frequency to be conversion time of this ADC (15 clock cycles)?

-The End of Transmission and Half Transmission Interrupts don´t stop the conversion sequence meaning that I should reset the multi-ADC sequencer (not sure how to do that)?

From Datasheet: ''If the conversion sequence is interrupted (for instance when DMA end of transfer occurs),

the multi-ADC sequencer must be reset by configuring it in independent mode first (bits

DUAL[4:0] = 00000) before reprogramming the interleaved mode.''

I'm not sure how the DMA processes the new requests when it's ''inside'' of an interrupt...

Also I will be using that ''double buffer'' strategy instead of really making DMA work in the double buffer mode.

Thanks again 🙂

Posted on March 21, 2015 at 00:00

The conversion doesn't stop, I'm not sure you need to do anything with the ADC/DMA. You want a big enough buffer you can work on the inactive half while the other fills.

You can trigger externally, or if you just want back-to-back or saturate the ADC you can just put it in continuous conversion mode. I tend to prefer timer driven conversion as it's less dependent on the specific hardware and clocks.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
pedro
Associate II
Posted on March 28, 2015 at 16:53

Hi Clive, I'm using the example you provided to get the ADC1 working at a Sample Frequency defined by me.

I'm getting a problem where the DMA Interrupt is only called when the TC and HT are both set by hardware. With this problem at hands, I dont have a ping pong strategy and my program crashes after some time. I really have no ideia why the hell this is happening since the DMA_BufferSize is always the double of the actual buffer. I would really appreciate some help because I'm lost here. Thanks a lot.

void Configure_ADCStruct(ADC_ParametersTypeDef * ADCParam){
last_y = 0;
SamplingCycle = RESET;
SamplingRunDone = RESET;
AdcCurrentParameters = *ADCParam;
printf(''SamFreq - %d, BuffSize - %d, CurrentMode - %d,FilterON - %d'',AdcCurrentParameters.SampleFrequency,AdcCurrentParameters.BufferSize,AdcCurrentParameters.CurrentMode,AdcCurrentParameters.FilterON);
RCC_Configuration();
GPIO_Configuration();
NVIC_Configuration();
TIM2_Configuration(AdcCurrentParameters.SampleFrequency);
DMA_Configuration();
ADC_Configuration();
}
void RCC_Configuration(void)
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
}
/**************************************************************************************/
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* ADC Channel 11 -> PC1
*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
/**************************************************************************************/
void ADC_Configuration(void)
{
ADC_CommonInitTypeDef ADC_CommonInitStructure;
ADC_InitTypeDef ADC_InitStructure;
/* ADC Common Init */
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;
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 = DISABLE; // 1 Channel
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // Conversions Triggered
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 1;
ADC_Init(ADC1, &ADC_InitStructure);
/* ADC1 regular channel 11 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 1, ADC_SampleTime_3Cycles); // PC1
/* 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);
}
/**************************************************************************************/
#define BUFFERSIZE 5000 // 200KHz x2 HT/TC at 1KHz
__IO uint16_t ADCConvertedValues[BUFFERSIZE]={100};
void DMA_Configuration()
{
DMA_InitTypeDef DMA_InitStructure;
DMA_DeInit(DMA2_Stream0);
DMA_InitStructure.DMA_Channel = DMA_Channel_0;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&ADCConvertedValues[0];
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = AdcCurrentParameters.BufferSize*2; // Count of 16-bit words
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);
/* Enable DMA Stream Half / Transfer Complete interrupt */
DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, ENABLE);
DMA_ITConfig(DMA2_Stream0, DMA_IT_HT, ENABLE);
/* DMA2_Stream0 enable */
DMA_Cmd(DMA2_Stream0, ENABLE);
}
/**************************************************************************************/
void TIM2_Configuration(unsigned int SampleFrequency)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
/* Time base configuration */
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Period = (84000000/SampleFrequency)-1;//(84000000 / 200000) - 1; // 200 KHz, from 84 MHz TIM2CLK (ie APB1 = HCLK/4, TIM2CLK = HCLK/2)
TIM_TimeBaseStructure.TIM_Prescaler = (0);
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* TIM2 TRGO selection */
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); // ADC_ExternalTrigConv_T2_TRGO
/* TIM2 enable counter */
TIM_Cmd(TIM2, ENABLE);
}
/**************************************************************************************/
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the DMA Stream IRQ Channel */
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/**************************************************************************************/
void DMA2_Stream0_IRQHandler(void){ // Called at 1 KHz for 200 KHz sample rate
unsigned int VectorSizex2 = AdcCurrentParameters.BufferSize*2,i,j,AdcValue;
float y;
/* Test on DMA Stream Half Transfer interrupt */
if(DMA_GetITStatus(DMA2_Stream0, DMA_IT_HTIF0))
{
SendChar('H');
/* Clear DMA Stream Half Transfer interrupt pending bit */
DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_HTIF0);
// Add code here to process first half of buffer (ping)
if(AdcCurrentParameters.CurrentMode == SAMPLING_RUN){
DMA_Cmd(DMA2_Stream0,DISABLE);
ADC_Cmd(ADC1,DISABLE);
SamplingRunDone = DONE;
ActiveBuff = (unsigned short int *)ADCConvertedValues;
}
if(AdcCurrentParameters.CurrentMode == SAMPLING_CYCLE && SamplingCycle == STOP){
DMA_Cmd(DMA2_Stream0,DISABLE);
ADC_Cmd(ADC1,DISABLE);
ActiveBuff = (unsigned short int *)ADCConvertedValues;
SamplingCycle = DONE;
}
}
/* Test on DMA Stream Transfer Complete interrupt */
if(DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0))
{
SendChar('T');
/* Clear DMA Stream Transfer Complete interrupt pending bit*/
DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);
if(AdcCurrentParameters.CurrentMode == SAMPLING_CYCLE && SamplingCycle == STOP){
DMA_Cmd(DMA2_Stream0,DISABLE);
ADC_Cmd(ADC1,DISABLE);
ActiveBuff = (unsigned short int *)&ADCConvertedValues[AdcCurrentParameters.BufferSize];
SamplingCycle = DONE;
}
}
}

Posted on March 28, 2015 at 23:32

But what is the size being used, and how are you observing TC and HT?

If you're looking at them in a debugger, they are probably streaming faster than the reaction time in the debugger, the TIM+ADC+DMA don't stop when the processor stops.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
pedro
Associate II
Posted on March 29, 2015 at 15:58

Im using a Frequency of 1 hz and a buffersize of 1.

Meaning that even on debug i should see a difference between HT and TC.

I also have those SendChar(H) SendChar(T) going from usart to my terminal and they come as a pair and not individually and 1s separated.

EDIT: Each two seconds I see HT HT HT on my terminal and not H T H T separated by 1 second.

pedro
Associate II
Posted on March 29, 2015 at 19:45

I believe its solved. DMA FIFO was enabled.

Thanks clive

Edit:

Why use 84000000 is TIM2 is on APB1 with 42Mhz on the bus?

Thanks
Posted on March 29, 2015 at 23:21

You'll want to review the Clock Tree from the Reference Manual. The TIM clocks typically derive from one stage earlier in the divide chain for the APB bus, except the DIV1 case. So the TIM clocks are usually 2X the APB clocks. In the F4 case this is usually 168 MHz for APB2, and 84 MHz for APB1, based timers, assuming the DIV2 and DIV4 settings for the APB. Or 180/90 for the F427/9 parts.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..