Skip to main content
jdcowpland
Associate III
September 10, 2013
Question

Triggering DMA on a Timer

  • September 10, 2013
  • 8 replies
  • 2082 views
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.
    This topic has been closed for replies.

    8 replies

    Tesla DeLorean
    Guru
    September 10, 2013
    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 (See Profile) Up vote any posts that you find helpful, it shows what's working..
    jdcowpland
    Associate III
    September 10, 2013
    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?

    Tesla DeLorean
    Guru
    September 10, 2013
    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 (See Profile) Up vote any posts that you find helpful, it shows what's working..
    pedro
    Associate III
    May 23, 2015
    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 III
    May 23, 2015
    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

    Tesla DeLorean
    Guru
    May 23, 2015
    pedro
    Associate III
    May 23, 2015
    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 III
    May 23, 2015
    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 !!!!
    }