Skip to main content
John Hite
Associate III
March 24, 2017
Question

Timer with DMA

  • March 24, 2017
  • 3 replies
  • 724 views
Posted on March 24, 2017 at 19:00

STM32F437

I am using TIM4 channel 1 and would like to have the timer output handled by DMA instead of my timer ISR but my DMA attempt is not working. So I am fishing for timer/dma example code hopefully that uses DMA interrupts as well.

Thanks,

JH

    This topic has been closed for replies.

    3 replies

    S.Ma
    Principal
    March 25, 2017
    Posted on March 25, 2017 at 17:11

    Have you browsed through cube nucleo/discovery/HAL/LL examples related to Timer+DMA?

    John Hite
    John HiteAuthor
    Associate III
    March 25, 2017
    Posted on March 25, 2017 at 18:03

    Thanks for posting. I have not looked there. If there are non-HAL examples I would be interested. Could you provide a URL?

    Thanks,

    jh

    S.Ma
    Principal
    March 25, 2017
    Posted on March 25, 2017 at 18:50

    I guess it's related to download STM32 Cube package.

    Otherwise, did a fun project where the datasheet of the STM32F437 was C coded... consider this as pseudo code to get clues on the DMA vs Timer. In this code, it's mostly for Output compare extract. 

    //====================== Capture Compare Initialization functions ================================ 8><8 =======

    u32 EnableTimerCC_Interrupt(u32 u, u32 n, FunctionalState Enable) { // n = [1..4]

    Timer_t* Timer = (Timer_t*) u;

    if(n==0) while(1); // not allowed

    if(n>=TIMER_MAX_CC) while(1); // not available CCn

    if(Timer->CCR[n]==0) while(1); // not available

    // Clear any pending flags first

    Timer->TIM->SR &= TimerCCFlags[n];

    if(Enable) Timer->TIM->DIER |= TimerCCFlags[n];

    else Timer->TIM->DIER &= ~TimerCCFlags[n];

    return 0;

    }

    u32 SetTimerInputCC(u32 u, u32 n, IO_Pin_t* Pin, FunctionalState Enable) {

    Timer_t* Timer = (Timer_t*) u;

    if(n==0) while(1); // not allowed

    if(n>=TIMER_MAX_CC) while(1); // not available CCn

    if(Timer->CCR[n]==0) while(1); // not available

    // Enable is not used yet....

    TIM_ICInitTypeDef IC;

    TIM_ICStructInit(&IC);

    // For now... should be passed as parameter later on. Rising edge, no prescaler, no filtering

    IC.TIM_Channel = TimerCC_Channels[n];

    IC.TIM_ICPolarity = TIM_ICPolarity_Rising;

    IC.TIM_ICSelection = TIM_ICSelection_DirectTI;

    IC.TIM_ICPrescaler = TIM_ICPSC_DIV1;

    IC.TIM_ICFilter = 0;

    TIM_ICInit(Timer->TIM, &IC);

    if(Pin) {

    Timer->PinCC[n] = Pin;

    IO_PinClockEnable(Pin);

    IO_PinSetHigh(Pin);

    IO_PinSetInput(Pin);

    IO_PinEnablePullUpDown(Pin, ENABLE, DISABLE);

    IO_PinEnableHighDrive(Pin, ENABLE);

    IO_PinConfiguredAs(Pin, GetPinAF(Pin->Name,(u32)Timer->TIM));

    };

    // EnableTimerCC_Interrupt(u, n, Enable);

    return 0;

    }

    u32 HookTimerCC(u32 u, u32 n, u32 fn, u32 ct) {

    Timer_t* Timer = (Timer_t*) u;

    if(n==0) while(1); // not allowed

    if(n>=TIMER_MAX_CC) while(1); // not available CCn

    if(Timer->CCR[n]==0) while(1); // not available

    Timer->ctCC[n] = ct;

    Timer->fnCC[n] = fn;

    return 0;

    }

    void TIM_DMA_Activate(Timer_t* Timer) {

    u32 n;

    for(n=1;n<4;n++) {

    if(Timer->EdgesSize[n]<=1) continue;

    // This CC is hooked up to a DMA, let's enable them

    DMA_StreamChannelInfo_t* DSCI = Get_TimerDMA_InfoByPPP_n((u32)Timer->TIM, n, DMA_DIR_PeripheralToMemory | DMA_DIR_MemoryToPeripheral);

    DMA_Cmd(DSCI->Stream/*DMA1_Stream4*/, ENABLE); // DMA enable

    BookDMA_Stream(DSCI->Stream);

    TIM_DMACmd(Timer->TIM, TIM_DMA_n[n], ENABLE); // TIM2 Update DMA Request enable

    };

    }

    // In this scheme, the timer is reset, wait for Ch2 rising edge, then starts one pulse mode

    // For example, the output compares can generate pulses or edge which can trigger time shifted ADC, DAC, Analog Switch behaviours

    // Using external triggers is easier to probe, hence easier debug, faster move forward job

    void EnableOneShotTimerCC_Triggered(Timer_t* Timer, u32 n) {

    TIM_DMA_Activate(Timer); // Will sweep through all DMA possible interaction

    TIM_SelectOnePulseMode(Timer->TIM, TIM_OPMode_Single); // One Pulse Mode selection

    if(n==1)

    TIM_SelectInputTrigger(Timer->TIM, TIM_TS_TI1FP1); // Input Timer Trigged on CH1

    else

    if(n==2) TIM_SelectInputTrigger(Timer->TIM, TIM_TS_TI2FP2); // Input Timer Trigged on CH2

    else

    while(1);

    // Slave Mode selection: Trigger Mode (the trigger will set the Timer enable bit)

    TIM_SelectSlaveMode(Timer->TIM, TIM_SlaveMode_Trigger);

    }

    static void TimerOutputCC_SetDMA(Timer_t* Timer, u32 n) {

    DMA_InitTypeDef DMAI;

    u32 DataSize;

    if(Timer->Max > 0x0000FFFF)

    DataSize = DMA_PeripheralDataSize_Word;

    else

    DataSize = DMA_PeripheralDataSize_HalfWord;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1 , ENABLE);

    DMA_StreamChannelInfo_t* DSCI = Get_TimerDMA_InfoByPPP_n((u32)Timer->TIM, n, DMA_DIR_PeripheralToMemory | DMA_DIR_MemoryToPeripheral);

    DMA_DeInit(DSCI->Stream);//(DMA1_Stream4);

    DMAI.DMA_Channel = DSCI->Channel;//DMA_Channel_5;

    DMAI.DMA_PeripheralBaseAddr = (u32)Timer->CCR[n];

    DMAI.DMA_Memory0BaseAddr = (u32)Timer->EdgesTableAdr[n];

    DMAI.DMA_DIR = DMA_DIR_MemoryToPeripheral;

    DMAI.DMA_BufferSize = Timer->EdgesSize[n];

    DMAI.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

    DMAI.DMA_MemoryInc = DMA_MemoryInc_Enable;

    DMAI.DMA_PeripheralDataSize = DataSize;

    DMAI.DMA_MemoryDataSize = DataSize;

    DMAI.DMA_Mode = DMA_Mode_Circular;

    DMAI.DMA_Priority = DMA_Priority_High;

    DMAI.DMA_FIFOMode = DMA_FIFOMode_Disable;

    DMAI.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;

    DMAI.DMA_MemoryBurst = DMA_MemoryBurst_Single;

    DMAI.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;

    DMA_Init(DSCI->Stream/*DMA1_Stream4*/, &DMAI);

    }

    void SetTimerOutputCC_SingleEdge(Timer_t* Timer, u32 n, u32 Value_lsb) {

    Timer->EdgesSize[n] = 0; // Non zero triggers the DMA mode for multiple pulses

    Timer->EdgesTableAdr[n] = 0;

    TIM_OCInitTypeDef OC;

    TIM_OCStructInit(&OC);

    OC.TIM_OCMode = TIM_OCMode_PWM2;

    OC.TIM_OutputState = TIM_OutputState_Enable;

    OC.TIM_Pulse = Value_lsb;

    OC.TIM_OCPolarity = TIM_OCPolarity_Low;

    TIM_OCnInit(Timer->TIM, n, &OC);

    }

    void SetTimerOutputCC_MultiEdges(Timer_t* Timer, u32 n, u32 Adr, u32 Size) {

    Timer->EdgesSize[n] = Size; // Non zero triggers the DMA mode for multiple pulses

    Timer->EdgesTableAdr[n] = Adr;

    TIM_OCInitTypeDef OC;

    TIM_OCStructInit(&OC);

    OC.TIM_OCMode = TIM_OCMode_Toggle;

    OC.TIM_OutputState = TIM_OutputState_Enable;

    OC.TIM_Pulse = 1;

    OC.TIM_OCPolarity = TIM_OCPolarity_Low;

    TIM_OCnInit(Timer->TIM, n, &OC);

    //CC1 only for now

    TimerOutputCC_SetDMA(Timer, n);

    TIM_OCnPreloadConfig(Timer->TIM, n, TIM_OCPreload_Disable); // Enable preload feature * // no shadow registers please

    }

    void EnableFreeRunTimer(Timer_t* Timer) {

    //u32 n;

    // Simnple timer mode

    if(Timer->EdgesSize[1]<2) { // DMA needed here

    TIM_Cmd(Timer->TIM, ENABLE);

    return;

    }

    //=======================================================

    // DMA transfer is required here (CC1 hard code for now

    TIM_DMA_Activate(Timer);

    TIM_Cmd(Timer->TIM, ENABLE); // let's run the Timer at last

    }