Skip to main content
tergu
Associate II
December 26, 2018
Question

Timer 2 output compare mode generating DMA requests without CC match on F411

  • December 26, 2018
  • 5 replies
  • 1736 views

Hey everyone,

I am outputting a waveform via TIM2 CCR1 stored in an array and transferred via circular DMA access.

The timer is configured for output compare so that whenever a match occurs, a dma request is generated and the next value is fetched from the array. If I make CCR1 larger than ARR no match should occur and no new value should be loaded. The output should keep it's last state. For whatever reason the next values are still loaded into CCR1. I have looked through the datasheet but could not find anything. The code I use to test this is below.

If anyone has an idea on why this is happening please get back to me.

Cheers,

Christian.

 #define array_size 4
 unsigned int array[array_size] = {200, 400, 1000, 600};
 
 //enable clock for Port A
 RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
 //set PA0 to AF output
 GPIOA->MODER |= GPIO_MODER_MODER0_1;
 //set AF1
 GPIOA->AFR[0] |= 0x01;
 //set to high speed
 GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR0_0 | GPIO_OSPEEDER_OSPEEDR0_1;
 
 //enable clock for tim2
 RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
 //set prescaler
 TIM2->PSC = 0;
 //set ARR Value
 TIM2->ARR = 999;
 //set initial CC value
 TIM2->CCR1 = TIM2->ARR >> 1;
 //set CC mode
 TIM2->CCMR1 = TIM_CCMR1_OC1M_0 | TIM_CCMR1_OC1M_1;
 //enable CC Channel
 TIM2->CCER = TIM_CCER_CC1E;
 //enable DMA for CC1
 TIM2->DIER |= TIM_DIER_CC1DE;
 //start timer
 TIM2->CR1 = TIM_CR1_CEN;
 
 //enable clock for DMA1
 RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
 //set peripheral register address
 DMA1_Stream5->PAR = &(TIM2->CCR1);
 //Set memory address
 DMA1_Stream5->M0AR = &(array[0]);
 //set number of items to be transferred
 DMA1_Stream5->NDTR = array_size;
 
 //set channel to 3 (TIM2)
 DMA1_Stream5->CR |= DMA_SxCR_CHSEL_0 | DMA_SxCR_CHSEL_1;
 //Set priority
 DMA1_Stream5->CR |= DMA_SxCR_PL_0 | DMA_SxCR_PL_1;
 //set memory size
 DMA1_Stream5->CR |= DMA_SxCR_MSIZE_1;	//32 bit
 //set peripheral size
 DMA1_Stream5->CR |= DMA_SxCR_PSIZE_1;	//32 bit
 //increment memory pointer
 DMA1_Stream5->CR |= DMA_SxCR_MINC;
 //circular mode
 DMA1_Stream5->CR |= DMA_SxCR_CIRC;
 //direction
 DMA1_Stream5->CR |= DMA_SxCR_DIR_0;
 //clear flags
 DMA1->HIFCR = 0xFFFFFFFF;
 DMA1->LIFCR = 0xFFFFFFFF;
 
 //enable dma controller
 DMA1_Stream5->CR |= DMA_SxCR_EN;

    This topic has been closed for replies.

    5 replies

    S.Ma
    Principal
    December 26, 2018

    Have done something like this in the past, using Standard library. It's a generic Timer code which enables DMA multi-pulse generation. Maybe it will ring a bell to make your code operate:

    // 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
    }

    tergu
    terguAuthor
    Associate II
    December 26, 2018

    Thanks, but I don't hear any bells ringing.

    The code works, and as long as the CC values are below ARR behaves like I would expect it to. After each CC event a dma request is generated and the next value loaded. What I don't get is how can a dma request be generated it I write a value larger than ARR into CC. It seems like an extra dma request is generated at overflow if no CC match occurs within one timer period?

    waclawek.jan
    Super User
    December 27, 2018

    This is feature of the timer, find this at the description of TIMx_SR.CC1IF:

    When the contents of TIMx_CCR1 are greater than the contents of TIMx_ARR, the CC1IF bit

    goes high on the counter overflow (in upcounting and up/down-counting modes) or underflow

    (in downcounting mode)

    JW

    tergu
    terguAuthor
    Associate II
    December 27, 2018

    Thanks for your reply, I think you are right and this is what is happening.

    I actually read over that paragraph but I thought that the DMA and Interrupt are independent as they are configured independently too. I looked through the TIM2 to TIM5 section and also quickly through the DMA section to find something that links these two together but could not find anything. They should add a paragraph somewhere saying "whenever a IF is set a DMA request is generated too" and add "CC1IF bit goes high and a dma request is initiated" to the section you mentioned.

    waclawek.jan
    Super User
    December 27, 2018

    I agree that this could be made clearer; but then the whole timers chapter (together with the concept of several timer chapters) is a complete disaster too.

    I've been bitten by exactly this "feature" so early in my encounter with the STM32 that I percieve it now just as a minor imperfection in the documentation. Much more and much nastier were to come...

    JW