cancel
Showing results for 
Search instead for 
Did you mean: 

Triggering DMA on a Timer

jdcowpland
Associate II
Posted on September 10, 2013 at 12:08

Hi guys,

I'm having a few issues setting of a DMA transfer based on a timer and hoping someone might be able to point out where I'm going wrong. I have a timer (timer 2) which is set to 50 Hz and as far as I can tell, this timer is associated with DMA1 Ch2 if I want to use the Timer2_Update? (Chip is STM32F103ZG). The timer seems to be successfully triggering the DMA, but not at the 50 Hz rate I'm looking for. My timer code looks like this:

void Timer2_config(void){

GPIO_Digital_Output(GPIOF_BASE, _GPIO_PINMASK_ALL);  // Enable digital output on PORTE

GPIOF_ODR = 0;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);

  GPIO_StructInit(&GPIO_InitStructure);

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;

  GPIO_Init(GPIOA, &GPIO_InitStructure);

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

  RCC_APB1ENR.TIM2EN = 1;

  TIM2_CR1.CEN = 0;

  TIM2_PSC = 72;

  TIM2_ARR = 10000;

 

//TIM_OCInitTypeDef TIM_OCInitStructure;

TIM_OCStructInit(&TIM_OCInitStructure);

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;

TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

TIM_OC2Init(TIM2, &TIM_OCInitStructure);

//NVIC_IntEnable(IVT_INT_TIM2); // Enable timer interrupt

TIM_DMACmd(TIM2, TIM_DMA_CC2, ENABLE);

//TIM2_CR2.CCDS=1;

TIM2_CR1.CEN = 1;

}

Whilst my DMA is configured like this:

void DMA1Config(void){

  /* Configure clock for DMA Controller */

  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);

  /* Deintialize DMA Channels */

  DMA_DeInit(DMA1_Channel2);

  DMA1_CCR2.EN = 0;

  DMA1_CCR2.DIR_ = 0;                       //READ FROM PERIPHERAL

  DMA1_CCR2.CIRC = 0;                       //CIRCULAR MODE DISABLED (NORMAL MODE)

  DMA1_CCR2.PINC = 0;                       //PERIPHERAL INCREMENT MODE DISABLED

  DMA1_CCR2.MINC = 0;                       //MEMORY INCREMENT MODE DISABLED

  DMA1_CCR2.PSIZE0 = 1;                     //SET PERIPHERAL SIZE TO 16 BITS

  DMA1_CCR2.PSIZE1 = 0;                     //SET PERIPHERAL SIZE TO 16 BITS

  DMA1_CCR2.MSIZE0 = 1;                     //SET MEMORY SIZE TO 16 BITS

  DMA1_CCR2.MSIZE1 = 0;                     //SET MEMORY SIZE TO 16 BITS

  DMA1_CCR2.PL0 = 0;                        //SET CHANNEL PRIORITY HIGH

  DMA1_CCR2.PL1 = 1;                        //SET CHANNEL PRORITY HIGH

  DMA1_CCR2.MEM2MEM = 1;                    //ENABLE MEM2MEM MODE

                        

  DMA1_CNDTR2 = 2;

  DMA1_CPAR2 = (uint32_t)DummyDisplayBuffer;

  NVIC_IntEnable(IVT_INT_DMA1_Channel2);

  DMA1_CCR2.TCIE = 1;

  DMA1_CCR2.EN = 1;

}

The DMA transfer complete interrupt simply toggles an LED so I can see the speed it's transferring at.
8 REPLIES 8
Posted on September 10, 2013 at 16:42

// STM32 TIM2 DMA Mem-to-Mem pacing VLDiscovery - sourcer32@gmail.com
#include ''STM32vldiscovery.h''
#define OUTLENGTH 5
volatile u16 InBuffer[OUTLENGTH], OutBuffer[OUTLENGTH];
/**************************************************************************************/
void RCC_Configuration(void)
{
// clock for DMA
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
// clock for TIM2
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
}
/**************************************************************************************/
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/******************************************************************************/
void DMA1_Channel2_IRQHandler(void) // Called at 10 Hz
{
if (DMA_GetITStatus(DMA1_IT_TC2))
{
DMA_ClearITPendingBit(DMA1_IT_TC2);
STM32vldiscovery_LEDToggle(LED3); // Toggle 5 Hz
STM32vldiscovery_LEDToggle(LED4);
// Do something
}
}
/**************************************************************************************/
void DMA_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure;
DMA_DeInit(DMA1_Channel2);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&InBuffer[0]; // Source
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&OutBuffer[0]; // Destination
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = OUTLENGTH;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
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; // Repetitive, for my convenience
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // So we can pace via trigger
DMA_Init(DMA1_Channel2, &DMA_InitStructure);
/* Enable DMA1 Channel2 Transfer Complete interrupt */
DMA_ITConfig(DMA1_Channel2, DMA_IT_TC, ENABLE);
// turning DMA on (DMA1 Channel2 -> TIM2_UP)
DMA_Cmd(DMA1_Channel2, ENABLE);
}
/**************************************************************************************/
void TIM2_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Prescaler = (SystemCoreClock / 1000000) - 1; // 1 MHz
TIM_TimeBaseStructure.TIM_Period = 20000-1; // 1MHz/20000 = 50 Hz
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 1;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
// ''connecting'' DMA and TIM2
TIM_DMACmd(TIM2, TIM_DMA_Update, ENABLE);
// turning on TIM2
TIM_Cmd(TIM2, ENABLE);
}
/**************************************************************************************/
int main(void)
{
/* Initialise LEDs LD3 & LD4 */
STM32vldiscovery_LEDInit(LED3);
STM32vldiscovery_LEDInit(LED4);
STM32vldiscovery_LEDOn(LED3);
STM32vldiscovery_LEDOff(LED4);
RCC_Configuration();
NVIC_Configuration();
DMA_Configuration();
TIM2_Configuration();
while(1);
}

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

Great, thanks Clive! Only major difference I'm noticing is that your code seems to indicate that Mem2Mem isn't possible when trying to trigger DMA with a timer. Is that the case?

Posted on September 10, 2013 at 17:34

If you use DMA_M2M_Enable it will try to do so as rapidly as possible, as it ignores all peripheral side inputs.

From RM0008

''Memory-to-memory mode

 

The DMA channels can also work without being triggered by a request from a peripheral.

 

This mode is called Memory to Memory mode.

 

If the MEM2MEM bit in the DMA_CCRx register is set, then the channel initiates transfers as soon as it is enabled by software by setting the Enable bit (EN) in the DMA_CCRx register.''

 

 

That, and I program the timers with N-1 values to get the timing correct, and use the time base update rather than a channel.
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 May 23, 2015 at 20:04

Sorry to revive this post but I'm trying to trigger DMA with a timer as the owner post.

My board is a STM32F4 The problem i get is that when I enable the TIM3, the timer which enables a dma transfer I get a Tranfer Errror Interrupt Flag right away. Code:

void DMA_Config(void){
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
__IO uint32_t Timeout = 50000;
int i;
/* Enable DMA clock & TIM3 Clock*/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3 ,ENABLE);
/* 
Time base configuration 
Frequency of 18Mhz
*/
TIM_TimeBaseStructure.TIM_Period = 20 - 1; 
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
//TIM_Cmd(TIM3, ENABLE); /* TIM enable counter */ //MUDAR POSICAO
TIM_DMACmd(TIM3, TIM_DMA_Update, ENABLE ); /* Enable TIM3_Updates DMA Requests */
//SET COUNTER TO 1
/* Reset DMA Stream registers (for debug purpose) */
DMA_DeInit(DMA1_Stream2);
/* Configure DMA Stream */
DMA_InitStructure.DMA_Channel = DMA_Channel_5; 
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(GPIOB->ODR); //dest
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)fb; //source
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA_InitStructure.DMA_BufferSize = (uint32_t)VTOTAL;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; 
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA1_Stream2, &DMA_InitStructure);
/* Enable DMA Stream Transfer Complete interrupt */
DMA_ITConfig(DMA1_Stream2, DMA_IT_TC, ENABLE);
DMA_Cmd(DMA1_Stream2,ENABLE);
Timeout = 50000;
while ((DMA_GetCmdStatus(DMA1_Stream2) != ENABLE) && (Timeout-- > 0))
{
}
/* Check if a timeout condition occurred */
if (Timeout == 0)
{
/* Manage the error: to simplify the code enter an infinite loop */
while (1)
{
}
}
/* Enable the DMA Stream IRQ Channel */
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM3,ENABLE);
}

It's really strange since from reference manual TIM3_UP -> DMA1 Stream2 Channel 5 Thanks a lot in advance Edit: GPIOB clock is enabled
pedro
Associate II
Posted on May 23, 2015 at 21:33

I think I found the problem to be in:

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(GPIOB->ODR);

I tried a diferent address based on 0x400000xx and the dma wrote with sucess.

I still don´t know why the dma does not write to GPIOB ODR or how to solve it :(.

Edit:

From RM0090 I found that DMA1 does not has access to AHB1 Peripherals. I think I have to change to DM2 to solve the problem

pedro
Associate II
Posted on May 24, 2015 at 00:21

Hi clive,

Thanks, i already saw that post on month ago but I did not remember it.

Anyway, I need to do the oposite thing and write to the GPIO. I also need to do it at 18Mhz speed. Is this achivable with stm32f407 at 144Mhz?

I'm trying but when I try to get my timer to high speed I see a FIFO error, and I believe it's because the DMA can not transfer everything.

Thanks 
pedro
Associate II
Posted on May 24, 2015 at 00:58

Also the DMA is disabling itself after the first entry on the iterrupt without giving an fifo error in this case:

void DMA_Config(void){
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
GPIO_InitTypeDef GPIO_InitStrucuture;
__IO uint32_t Timeout = 50000;
int i;
/* Enable DMA clock & TIM3 Clock*/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8 ,ENABLE);
//RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
/*Enable GPIOB Pins as digital output*/
/* 
Time base configuration 
Frequency of 18Mhz
*/
TIM_TimeBaseStructure.TIM_Period = 5000-1; 
TIM_TimeBaseStructure.TIM_Prescaler = 0; //Not sure if 144Mhz timer
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure);
TIM_DMACmd(TIM8, TIM_DMA_Update, ENABLE ); /* Enable TIM3_Updates DMA Requests */
//SET COUNTER
/* Reset DMA Stream registers (for debug purpose) */
DMA_DeInit(DMA2_Stream2);
/* Configure DMA Stream */
DMA_InitStructure.DMA_Channel = DMA_Channel_7; 
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(GPIOB->ODR); //;0x40000010 &(GPIOB->ODR)//dest
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)fb; //source
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA_InitStructure.DMA_BufferSize = (uint32_t)VTOTAL;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; 
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream1, &DMA_InitStructure);
/* Enable DMA Stream Transfer Complete interrupt */
DMA_ITConfig(DMA2_Stream1, DMA_IT_TC, ENABLE);
DMA_Cmd(DMA2_Stream1,ENABLE);
/* Enable DAC Channel2 */
Timeout = 50000;
while ((DMA_GetCmdStatus(DMA2_Stream1) != ENABLE) && (Timeout-- > 0))
{
}
/* Check if a timeout condition occurred */
if (Timeout == 0)
{
/* Manage the error: to simplify the code enter an infinite loop */
while (1)
{
}
}
/* Enable the DMA Stream IRQ Channel */
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM8,ENABLE);
}
void DMA2_Stream1_IRQHandler(void)
{ 
TIM8->CR1 &= ~0x1; //Disable Timer Stop trigger
DMA2->LIFCR = 0xC00; //Clear IT HT Flag from stream 1 DMA 2
DMA2_Stream1->NDTR = VTOTAL - 1; //Reload Bytes to send !!!!! not sure about -1 !!!!
}