Skip to main content
harry_rostovtsev
Associate II
August 27, 2012
Question

Stop PWM output after N steps

  • August 27, 2012
  • 11 replies
  • 3799 views
Posted on August 27, 2012 at 20:01

Is there a way to tell a TIMer on STM32 chips to do some number of steps and automatically stop after finished (and give an interrupt)?  I can do it now with a counter in the ISR but a few pulses slip out before I can shutdown the PWM output or the timer.

    This topic has been closed for replies.

    11 replies

    harry_rostovtsev
    Associate II
    August 27, 2012
    Posted on August 27, 2012 at 21:58

    Here's my current code:

    uint16_t number_of_steps;
    int main(void) {
    NVIC_InitTypeDef nvic_init;
    NVIC_ClearPendingIRQ(TIM8_UP_TIM13_IRQn);
    nvic_init.NVIC_IRQChannel = TIM8_UP_TIM13_IRQn;
    nvic_init.NVIC_IRQChannelPreemptionPriority = 0;
    nvic_init.NVIC_IRQChannelSubPriority = 0;
    nvic_init.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&nvic_init);/* enables the device and sets interrupt priority */
    NVIC_ClearPendingIRQ(TIM8_CC_IRQn);
    nvic_init.NVIC_IRQChannel = TIM8_CC_IRQn;
    nvic_init.NVIC_IRQChannelPreemptionPriority = 1;
    nvic_init.NVIC_IRQChannelSubPriority = 1;
    nvic_init.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&nvic_init);/* enables the device and sets interrupt priority */
    /* set priorities of all interrupts in the system... */
    NVIC_SetPriority(SysTick_IRQn, 1);
    NVIC_SetPriority(TIM8_UP_TIM13_IRQn, 2);
    NVIC_SetPriority(TIM8_CC_IRQn, 3);
    GPIO_InitTypeDef GPIO_InitStructure;
    /* ENABLE GPIOC clock enable */
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
    /* GPIOC Configuration:TIM8 Channel3 in Output */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    /* Connect TIM8 pins to AF */
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource8, GPIO_AF_TIM8);
    /* Compute the value to be set in ARR register to generate signal frequency at 57 Khz */
    TimerPeriod = (SystemCoreClock / 2000 ) - 1;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;
    /* TIM8 clock enable */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);
    /* how many steps are we going */
    number_of_steps = 20;
    /* Time base configuration */
    uint16_t Period = 1000000 / 2000; // 2 KHz for 1MHz prescaled
    TIM_TimeBaseStructure.TIM_Period = Period - 1; // 1 MHz down to 1 KHz (1 ms)
    TIM_TimeBaseStructure.TIM_Prescaler = 120 - 1; // 120 MHz Clock down to 1 MHz (adjust per your clock)
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure);
    /* PWM Mode configuration: Channel3 */
    TIM_OCInitStructure.TIM_Pulse = Period / 2;
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    /* Output Compare PWM1 Mode configuration: Channelc Pc.08 */
    TIM_OC3Init(TIM8, &TIM_OCInitStructure);
    TIM_OC3PreloadConfig(TIM8, TIM_OCPreload_Enable);
    /* TIM8 enable counter */
    TIM_Cmd(TIM8, ENABLE);
    TIM_ITConfig(TIM8,TIM_IT_Update, ENABLE);
    TIM_CtrlPWMOutputs(TIM8, ENABLE);
    while (1)
    {
    }
    }
    void TIM8_UP_TIM13_IRQHandler(void) {
    if (number_of_steps == current_step) {
    TIM8->CCR3 = 0;
    TIM8->CR1 &= (uint16_t)~TIM_CR1_CEN; // this is same as TIM_Cmd(TIM8, DISABLE);
    TIM_ITConfig(TIM8,TIM_IT_Update, DISABLE);
    current_step = 1;
    }
    current_step++;
    // If this is uncommented, I have a few extra pulses that sneak out
    //printf(''TIM8 update int
    ''); 
    // If this is commented out, I only get 1 or 2 pulses, as if my ISR goes by to quickly
    if (TIM_GetITStatus(TIM8, TIM_IT_Update) != RESET) {
    TIM_ClearITPendingBit(TIM8, TIM_IT_Update);
    }
    }

    If you look in the interrupt handler, you can see my comment specify the problems. Basically, I don't think I'm using the right kind of interrupt (though I got similar behavior when I used the CCR interrupt).
    rod_gilchrist
    Associate
    January 29, 2013
    Posted on January 29, 2013 at 15:59

    Yes. (I am reading your question as that you wish to emit a specified number of PWM pulses.)

    The STM32 documentation implies that a timer is either  in Master mode (wherein it can output a signal on an internal trigger line) or Slave mode (wherein it can be triggered from things like an internal trigger line). The truth is that it can be both a Master and a Slave at the same time. 

    I use this  understanding to emit a specified number of pulses by connecting two timers 'back to back' and I use the repetition counter on an advanced counter to specify the number of pulses.

    Specifically, I put Timer 1 (which has a repetition counter) in PWM mode configured to generate pulses with the timing I want. I then configure its slave controller to be gated by Timer 4 on internal trigger 3 (any of timers 2, 3 or 4 would work, but timer 5 won't due to internal trigger connectivity). I also set Timer 1's master controller to emit an update event on its trigger output.

    I then configure Timer 4 in one pulse mode with a wide pulse (longer than 256 Timer 1 pulses) with its master controller outputting a compare output channel on 'trigger out' to gate Timer 1 and its slave controller set to reset Timer 4 based on the signal on internal trigger 0 (i.e. the update event from Timer 1, which only occurs after 'n' PWM pulses, depending on the value set in the Timer 1 repetition counter (which can be 1 to 256)).

    I haven't tried it, but I imagine the Timer 1 update would generate an interrupt in the normal way if desired, but with this configuration the pulse count is not dependent on interrupt speed. 

    Hope this helps.

    rod_gilchrist
    Associate
    January 30, 2013
    Posted on January 30, 2013 at 15:52

    Stop PWM output after N steps

    Is there a way to tell a TIMer on STM32 chips to do some number of steps and automatically stop after finished (and give an interrupt)?  I can do it now with a counter in the ISR but a few pulses slip out before I can shutdown the PWM output or the timer.

    My previous post describes how to do this.

    One point that I didn't make is that you do have to trigger an update event (i.e. set the update event bit in the Timer 1 EGR register) after you set the pulse count in the repetition register. The update event is required to load the repetition count into the actual down counter.

    I do this just before setting the enable bits on the two timers.

    Amel NASRI
    Technical Moderator
    January 31, 2013
    Posted on January 31, 2013 at 14:18

    I guess the break input feature will be helpfull in your case. Did you tried it?

    ST.MCU

    To give better visibility on the answered topics, please click on "Best Answer" on the reply which solved your issue or answered your question.
    barry
    Associate II
    January 27, 2015
    Posted on January 27, 2015 at 16:26

    Rod,

    Have you actually gotten the code to work?

    If so, could you post the code.  Is timer 8 completely identical to timer 1.

    Thanks

    Barry

    Tesla DeLorean
    Guru
    January 27, 2015
    Posted on January 27, 2015 at 16:57

    Is timer 8 completely identical to timer 1.

    They are both ''advanced'' timers, so you'd need to initialize the repetition count in the timebase, the N output settings in the channels, and enable the PWM output(input)
    Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..
    barry
    Associate II
    January 28, 2015
    Posted on January 28, 2015 at 19:51

    Thanks Clive,

    Another question 

    I am currently using timer 3(to generate aninterrupt once every  4 seconds)  to control timer 4 (I have hardwareconnected to PE12), but without a master slave connection.  This is not workingvery well to to generate an exact number  (200) of pulses.  It isclose but over a long time frame the stepper motor will drift off the correctstarting point.

    Do you think I can use timer 8 and its repetitioncounter as the master to control timer 4 PWM pulses so I get an exact number ofpulses for a stepper motor?

    The example in this thread seems to use theadvanced timer to generate the PWM pulses.

    Are there any examples in the various STdistributions?

    If this can't work, can I use timer 4 in a onepulse mode (and handle 200 interrupts)? The exact timing of the stepper motorpulses is not critical.

    Thanks

    Barry

    Tesla DeLorean
    Guru
    January 28, 2015
    Posted on January 28, 2015 at 21:58

    // STM32 FIVE 20 KHz PULSES at 50 Hz (20ms Intervals) (TIM1 CH1 PA.08) STM32F4 Discovery - sourcer32@gmail.com
    #include ''stm32f4_discovery.h''
    /**************************************************************************************/
    void RCC_Configuration(void)
    {
    /* --------------------------- System Clocks Configuration -----------------*/
    /* TIM1 clock enable */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
    /* TIM3 clock enable */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    /* GPIOA clock enable */
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    }
    /**************************************************************************************/
    void GPIO_Configuration(void)
    {
    GPIO_InitTypeDef GPIO_InitStructure;
    /*-------------------------- GPIO Configuration ----------------------------*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    /* Connect TIM1 pins to AF */
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_TIM1);
    }
    /**************************************************************************************/
    void TIM1_Configuration(void)
    {
    TIM_OCInitTypeDef TIM_OCInitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    uint16_t Period;
    Period = 1000000 / 20000; // 20 KHz from 1MHz prescaled
    /* Time base configuration */
    TIM_TimeBaseStructure.TIM_Prescaler = (SystemCoreClock / 1000000) - 1; // Get clock to 1 MHz on STM32F2/F4
    TIM_TimeBaseStructure.TIM_Period = Period - 1;
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 5 - 1; // Five Pulses per trigger
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
    /* Enable TIM1 Preload register on ARR */
    TIM_ARRPreloadConfig(TIM1, ENABLE);
    /* TIM PWM1 Mode configuration */
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = Period / 2; // 50%
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    /* Output Compare PWM1 Mode configuration: Channel1 PA.08 */
    TIM_OC1Init(TIM1, &TIM_OCInitStructure);
    TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
    /* One Pulse Mode selection */
    TIM_SelectOnePulseMode(TIM1, TIM_OPMode_Single);
    /* Input Trigger selection */
    TIM_SelectInputTrigger(TIM1, TIM_TS_ITR2); // Slave TIM1, Trigger TIM3 (TS=2)
    /* Slave Mode selection: Trigger Mode */
    TIM_SelectSlaveMode(TIM1, TIM_SlaveMode_Trigger);
    /* TIM1 Main Output Enable */
    TIM_CtrlPWMOutputs(TIM1, ENABLE);
    /* TIM1 enable counter */
    TIM_Cmd(TIM1, ENABLE);
    }
    /**************************************************************************************/
    void TIM3_Configuration(void)
    {
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    uint16_t Period;
    Period = 1000000 / 50; // 50 Hz from 1MHz prescaled
    /* Time base configuration */
    TIM_TimeBaseStructure.TIM_Prescaler = ((SystemCoreClock / 2) / 1000000) - 1; // Get clock to 1 MHz on STM32F2/F4
    TIM_TimeBaseStructure.TIM_Period = Period - 1;
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
    /* Trigger of TIM3 Update into TIM1 Slave */
    TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);
    /* TIM3 enable counter */
    TIM_Cmd(TIM3, ENABLE);
    }
    /**************************************************************************************/
    int main(void)
    {
    RCC_Configuration();
    GPIO_Configuration();
    TIM1_Configuration();
    TIM3_Configuration();
    while(1); // Don't want to exit
    }
    /**************************************************************************************/
    #ifdef USE_FULL_ASSERT
    /**
    * @brief Reports the name of the source file and the source line number
    * where the assert_param error has occurred.
    * @param file: pointer to the source file name
    * @param line: assert_param error line source number
    * @retval None
    */
    void assert_failed(uint8_t* file, uint32_t line)
    {
    /* User can add his own implementation to report the file name and line number,
    ex: printf(''Wrong parameters value: file %s on line %d
    
    '', file, line) */
    /* Infinite loop */
    while (1)
    {
    }
    }
    #endif

    Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..
    barry
    Associate II
    January 28, 2015
    Posted on January 28, 2015 at 23:22

    Clive,

    I'll give it a try.

    Thanks

    Barry

    barry
    Associate II
    February 8, 2015
    Posted on February 08, 2015 at 17:46

    Thanks again Clive.

    One question,  I can't figure out what one instruction will start the pulse sequence.

    I don't want to start the pulses immediately at initialization  time and I want to repeat the sequence over and over.

    The code shown below works, but I think I could clean it up if I understood the timers as well as you.

    I am using the update interrupt to get additional cycles of 200 pulses

     /* Enable the TIM8 gloabal Interrupt */

      NVIC_InitStructure.NVIC_IRQChannel = TIM8_UP_TIM13_IRQn;

      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

      NVIC_Init(&NVIC_InitStructure);

     /* TIM Interrupts enable */

      TIM_ITConfig(TIM8, TIM_IT_Update , ENABLE);

      

    ................

    //Interrupt Handler

    void   TIM8_UP_TIM13_IRQHandler(void)

    {

      if (TIM_GetITStatus(TIM8, TIM_IT_Update) != RESET)

      {

        TIM_ClearITPendingBit(TIM8, TIM_IT_Update);

        tim8_counter--;

        if ( tim8_counter != 0)

        {

             TIM8_Restart();

        }

      }

    }

     

      

     

    void TIM8_Restart(void)  // This seems to work

    {

      /* TIM8 Main Output Enable */

      TIM_CtrlPWMOutputs(TIM8, ENABLE);

       /* TIM8 enable counter */

      TIM_Cmd(TIM8, ENABLE);

    }

     

    //This is what I do the first time and when I want to send another series of 200 pulses

    // Some of these instructions were removed from the init code you posted

         tim8_counter = TIM8_INTERRUPTS;

        TIM_CtrlPWMOutputs(TIM8, ENABLE);

        TIM_Cmd(TIM8, ENABLE);

        TIM_Cmd(TIM3, ENABLE);

        TIM8_Restart();

    Any ideas on what is actually turn the pulses off and on?

    Thanks

    Barry