cancel
Showing results for 
Search instead for 
Did you mean: 

Stm32F4: Timer2 trigger ADC, load data into DMA, WRONG frequency!

cyqdtc
Associate II
Posted on January 12, 2015 at 10:59

Hi all,

I am trying to trigger ADC2 with a timer at around 100kHz. The data will be loaded into DMA. However, the data obtained through DMA has a much higher frequency than the timer. For the ADC1, I plan to use randomly usingADC_SoftwareStartConv(ADC1). Could you please give me a hand?

int main(void)
{ 
TIM2_Config();
Config_ADC();
Config_DMA();
TIM_Cmd(TIM2, ENABLE);
while (1);
}
void TIM2_Config(void) 
{ 
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
/* TIM2 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/* Time base configuration */
//AHB1 runs at 42MHz, 
//timer clock = 84MHz =AHB1 CLK * 2 , since AHB1 PRESC = 4 (!1)
TIM_Cmd(TIM2, DISABLE);
TIM_DeInit(TIM2);
TIM_TimeBaseStructure.TIM_Prescaler = 16; // 84MHz/16 = 5.25MHz
TIM_TimeBaseStructure.TIM_Period = 42 - 1; // 5.25MHz/42 = 125kHz(adjust according to analog BP)
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //no division
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* TIM IT enable */
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
/* TIM2 TRGO selection */
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); // Timer2 will trigger ADC2: ADC_ExternalTrigConv_T2_TRGO
} 
void DMA2_Stream2_IRQHandler(void)
{ 
//For double buffer mode: every time this interrupt is entered, only one array is updated
//Check if the transfer complete interrupt flag has been set
if(DMA_GetITStatus(DMA2_Stream2, DMA_IT_TCIF2) == SET)
{
DMA_ClearITPendingBit(DMA2_Stream2, DMA_IT_TCIF2);
}
}
void Config_ADC(void)
{
ADC_InitTypeDef ADC_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable ADC1, ADC2 and GPIO clocks ****************************************/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2, ENABLE);
/* Configure ADC1 Channel10 pin as analog input ******************************/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_2; //DC -> PC0; 2k -> PC2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_Init(GPIOC, &GPIO_InitStructure);
ADC_DeInit();
/* ADC Common Init **********************************************************/
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);
/* ADC1 Init ****************************************************************/
ADC_StructInit(&ADC_InitStructure);
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//we will convert one time
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;//select no external triggering
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 1; //single channel conversion
ADC_Init(ADC1, &ADC_InitStructure); //INIT ADC1
//Triggered by a timer
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO; //triggered by timer 2
ADC_Init(ADC2, &ADC_InitStructure); //INIT ADC2
/* ADC2 regular channel configuration *************************************/
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_3Cycles); //DC channel
ADC_RegularChannelConfig(ADC2, ADC_Channel_12, 1, ADC_SampleTime_3Cycles); //2kHz channel
/* Enable DMA request after last transfer (Single-ADC mode) */
ADC_DMARequestAfterLastTransferCmd(ADC2, ENABLE);
/* Enable ADC1 and ADC2 */
//ADC_Cmd(ADC1, ENABLE);
ADC_Cmd(ADC2, ENABLE);
//Enable ADC DMA command
ADC_DMACmd(ADC2, ENABLE);
}
void Config_DMA(void)
{
//DMA2 Channel 1, stream 2, will be used for ADC 2
NVIC_InitTypeDef NVIC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); //Init DMA clock
DMA_Cmd(DMA2_Stream2, DISABLE); 
while (DMA2_Stream2->CR & DMA_SxCR_EN);
DMA_DeInit(DMA2_Stream2);
//Double buffer mode
DMA_StructInit(&DMA_InitStructure);
DMA_InitStructure.DMA_Channel = DMA_Channel_1;//ADC2
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC2 -> DR); //data register address of ADC2 
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&ADCDataRec1[0]; //Set the memory1 location
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; //Sending data from memory to the peripheral's Tx register
DMA_InitStructure.DMA_BufferSize = 960; //Define the number of bytes to send
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //Don't increment the peripheral 'memory'
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //Increment the memory location
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//HalfWord size memory transfers
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord; //HalfWord size memory transfers
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //circular mode
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_Priority = DMA_Priority_High; //Priority is high to avoid saturating the FIFO since we are in direct mode
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable; //FIFO enabled
DMA_InitStructure.DMA_MemoryBurst =DMA_MemoryBurst_INC8;
DMA_InitStructure.DMA_PeripheralBurst =DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream2, &DMA_InitStructure); 
//Enable the transfer complete interrupt for DMA2 Stream2
DMA_ITConfig(DMA2_Stream2, DMA_IT_TC, ENABLE); //Enable the Transfer Complete interrupt 
//Enable Double buffer mode
DMA_DoubleBufferModeConfig(DMA2_Stream2, (uint32_t)&ADCDataRec2[0], DMA_Memory_0);
DMA_DoubleBufferModeCmd(DMA2_Stream2, ENABLE);
//Enable the DMA2 Stream2 (SPI1_RX) Interrupt
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//Enable DMA
DMA_Cmd(DMA2_Stream2, ENABLE); 
}
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}

5 REPLIES 5
Posted on January 12, 2015 at 12:53

What does ''much higher'' mean? 117647 Hz, be specific, it might help understand what's going on.

The Prescaler and Period are set as N-1 numbers.

Interrupting at 100 KHz is getting a bit high, DMA 10 or 100 samples to decimate the interrupt loading, and toggle a GPIO, and measure it on a scope.

Try without double buffering.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
cyqdtc
Associate II
Posted on January 14, 2015 at 05:21

Thanks for your reply Clive! I will try to describe the problem clearly. 

Much higher means:

1. An update interrupt handler for timer 2 has been implemented.

2. If the trigger frequency for ADC is the timer update frequency, every time the timer is updated (at 110kHz), only one ADC reading should be read.

3. I use debug mode and set a break point inside the interrupt handler of timer 2.

4. However, every time the timer interrupt handler is entered, it can be observed on the DMA destination memory that hundreds of ADC readings(instead of one) have been updated already.

5. Therefore, I suspect that the ADC trigger frequency is much higher than the timer update frequency. Or could it because the timer update TGO cannot be stopped by the break point? 

Thanks a lot!

Posted on January 14, 2015 at 05:53

The debugger stops the processor, the TIM+ADC+DMA do not (technically there's a DBGMCU setting to stall the timer if suitably configured).

When I calibrate these things I have the DMA HT/TC interrupts toggle a GPIO, and I verify the speed on a scope. The DMA is the last thing in the chain, so all the pieces are functioning if the DMA completes. Suggest you transfer enough samples so you're in the 1-10 KHz range. Having the DMA do 100's of samples will exacerbate any timing errors with the TIM configuration and make them more apparent on the scope.

I would not recommend interrupting on the TIM, or ADC. If you want to confirm a TIM rate have one of the channels configured in toggle mode (hardware) and scope the pin.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
shoaib
Associate II
Posted on March 07, 2016 at 16:49

The original post was too long to process during our migration. Please click on the provided URL to read the original post. https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006I6jW&d=%2Fa%2F0X0000000buC%2FtjAP8.KVnTCIzBqdlhv5uEkW5_tPKbgIvzbK8JZx2O0&asPdf=false
Posted on March 07, 2016 at 16:59

Please review other threads with working examples before posting broken code to multiple threads. Thanks.

[DEAD LINK /public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/ADC%20trigger%20on%20timer%20update&currentviews=3606]https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/ADC%20trigger%20on%20timer%20update¤tviews=3606

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