Skip to main content
toddberk
Associate II
November 10, 2010
Question

Timer One Pulse Mode issues

  • November 10, 2010
  • 11 replies
  • 2480 views
Posted on November 10, 2010 at 09:58

Timer One Pulse Mode issues

    This topic has been closed for replies.

    11 replies

    lowpowermcu
    Associate III
    May 17, 2011
    Posted on May 17, 2011 at 14:14

    Hi TMecha,

    What is the pulse width you want to have ?

    Herzlich,

    MCU Lüfter

    I don't understand what is going with stm32 forum ??!!!!!!

    lowpowermcu
    Associate III
    May 17, 2011
    Posted on May 17, 2011 at 14:14

    Hi TMecha,

    I can send an example of such use case.

    Herzlich,

    MCU Lüfter

    John F.
    Associate III
    May 17, 2011
    Posted on May 17, 2011 at 14:14

    Set up (for example) timer 1 and load the pulse width you want.

    TIM1->ARR = PulseWidth;

    but don't enable the timer yet.

    Set up another timer for a 20ms periodic interrupt.

    In that interrupt, start your pulse and enable timer 1.

        GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET); //pulse output high

        TIM_Cmd(TIM1, ENABLE);      //start timer 1

    The timer 1 interrupt will interrupt after PulseWidth period.

    In the timer 1 interrupt, disable and reload the timer and clear the pulse output low.

    void TIM1_UP_IRQHandler(void)

    {

      //Clear TIM1 update interrupt

      TIM_ClearITPendingBit(TIM1, TIM_IT_Update);

       

      //handle the pulse using timer 1

      TIM1->CR1 &= ((uint16_t)0x03FE);  //disable the TIM Counter

      TIM1->CNT  =  (uint16_t) 0;       //clear the counter

      TIM1->ARR = PulseWidth;

      GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET); //pulse output low

    }

    You can change the PulseWidth on the fly if you want for different widths every 20ms.

    John F.
    Associate III
    May 17, 2011
    Posted on May 17, 2011 at 14:14

    If you understood my suggestion, you should find it works well for you for one servo. (I wrote it to drive a servo with software controlled pulse width.)

    If you mean to drive

    multiple servos with different pulse widths

    then you may have to do more processing in a faster interrupt.

    toddberk
    toddberkAuthor
    Associate II
    May 17, 2011
    Posted on May 17, 2011 at 14:14

    MCU Lüfter,

    Sorry I should have specified that - the application is to drive numerous RC Servos without much CPU overhead or wait loops. Therefore I would like to vary the Pulse signal from approximately 1ms to 2ms.

    An example would be greatly appreciated. I am new to STM32 and so far this community has been great!

    -Todd

    Tesla DeLorean
    Guru
    May 17, 2011
    Posted on May 17, 2011 at 14:14

    If you are sending a single pulse every 20 ms, pretty soon you are just generating a frequency with multiple pulses?

    If that is the case why not just have the hardware to generate the frequency, and control the pulse width?

    On an STM32 running at 32 MHz, here is a 50 Hz signal (20 ms) with 1 ms pulse width.

    int TIM4_Configuration(void)

    {

        u16 period; // Period, interrupt periodicity in timer ticks

        u16 scale; // Prescale another clock divider (1-65536)

      u16 width; // Pulse width

        int TIM4_Frequency;

        int TIM4_Width;

        RCC_ClocksTypeDef RCC_ClockFreq;

        TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

        TIM_OCInitTypeDef  TIM_OCInitStructure;

        GPIO_InitTypeDef GPIO_InitStructure;

      // 32 MHz Master Clock

        scale = 3200; // 32 MHz / 3200 = 10 KHz

      period = 200; // 10 KHz / 200 = 50 Hz (20 ms)

        width = 10;     // 10 KHz = 100 us * 10 = 1000 us (1 ms)

      // For 72 MHz use scale = 7200, at 36 MHz use scale = 3600

      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

      RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);

      /* Time base configuration */

      TIM_TimeBaseStructure.TIM_Period = (period - 1); // 1-65536

      TIM_TimeBaseStructure.TIM_Prescaler = (scale - 1); // 1-65536, Master Clock Divisor

      TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 1,2 or 4 for tDTS

      TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

      TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);

       /* PWM1 Mode configuration: Channel1 on PB.06 */

      TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;

      TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

      TIM_OCInitStructure.TIM_Pulse = width; // Pulse Width

      TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

      TIM_OC1Init(TIM4, &TIM_OCInitStructure);

      TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);

      TIM_ARRPreloadConfig(TIM4, ENABLE);

      /* TIM4 enable counter */

      TIM_Cmd(TIM4, ENABLE);

      /* Configure TIM4_CH1 as alternate function push-pull */

      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;  // PB.06

      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

      GPIO_Init(GPIOB, &GPIO_InitStructure);

        /* Compute based on chip settings */

      RCC_GetClocksFreq(&RCC_ClockFreq);

      if (RCC_ClockFreq.HCLK_Frequency == RCC_ClockFreq.PCLK1_Frequency)

            TIM4_Frequency = RCC_ClockFreq.PCLK1_Frequency;

      else

            TIM4_Frequency = RCC_ClockFreq.PCLK1_Frequency * 2;

    #ifdef DBG

      printf(''TIM4CLOCK %d Hz\n'',TIM4_Frequency);

    #endif

      TIM4_Width            = (1000000 * width) / (TIM4_Frequency / scale);

      TIM4_Frequency    = (TIM4_Frequency / scale) / period; // APB1 (TIM4)

    #ifdef DBG

      printf(''TIM4 rate %d Hz, width %s us\n'',TIM4_Frequency, TIM4_Width);

    #endif

        return(TIM4_Frequency);

    }

    Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..
    lowpowermcu
    Associate III
    May 17, 2011
    Posted on May 17, 2011 at 14:14

    Hi TMecha,

    There is an example in ST library for one pulse mode which helped me to understand

    the one pulse mode for timers

    Let's we use TIM2 and TIM4 timers: TIM2 will trigger TIM4 every 20 ms.

    TIM2 must be configured in such way to generate update event every 20 ms

    (APB frequency/ARR register = 50 Hz, I think you must use timer prescaler)

    and select update event of TIM2 to trigger TIM4 using

    TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update)

    Now TIM4 must be configured in one pulse mode then select the input trigger

    from TIM2 using TIM_SelectInputTrigger() (you must refer to your product reference

    manual to know if you should select ITR0 , ITR1 or ITR2...)

    Finally, select slave mode with trigger for TIM4 using TIM_SelectSlaveMode()

    Now enable both timers TIM_Cmd(TIMx, ENABLE)

    In this configuration, you don't need processing interrupts.

    Do not hesitate if that stills not clear otherwise tell me how I can send you my source.

    Herzlich,

    MCU Lüfter

    toddberk
    toddberkAuthor
    Associate II
    May 17, 2011
    Posted on May 17, 2011 at 14:14

    Thanks clive1,

    I think this would probably work, thanks for your example. I will try it. I know I didn't specify the pulse should change in my original post, but in order to maximize resolution for adjusting the pulse width I believe I should adjust the scale so that period is close to 65536. Please confirm my calculations here:

    SYSClock = 72Mhz

    AHB prescalar = 1

    APB1 prescalar = 2

    PCLK1 = 36 Mhz

    Scale = (36000000*.02)/65536 = 11 (3.272 Mhz)

    Period = (36000000/11)*.02 = 65455

    Width(1ms) = 3272

    Width(2ms) = 6546

    This effectively gives me a resolution of about 3273 between 1ms and 2ms, correct?

    Thanks!

    -Todd

    lowpowermcu
    Associate III
    May 17, 2011
    Posted on May 17, 2011 at 14:14

    Hi,

    I have tailored John F. suggestion.

    In TIM1 I have added TIM_SelectOutputTrigger() and a new function Timer2_Configuration()

    It needs GPIO configuration for timer 2 pins (PA0....)

    I hope that this helps.

    #include ''stm32f10x.h''

    void RCC_Configuration(void);

    void GPIO_Configuration(void);

    void Timer1_Configuration(void);

    //note with SYSCLK_FREQ_72MHz defined, PCLK1 = 36MHz and PCLK2 = 72MHz

    uint16_t CCR1_Val = 1000; //1500 = 1.5ms (1000 = 1ms, 2000 = 2ms);

    int main(void)

    {

      RCC_Configuration();

      GPIO_Configuration();

      Timer1_Configuration();

      Timer2_Configuration();

     

      //TIM1 enable counter

      TIM_Cmd(TIM1, ENABLE);

      //TIM2 enable counter

      TIM_Cmd(TIM2, ENABLE);

     

      while(1)

      {

        ;

      }

    }

    void RCC_Configuration(void)

    {

      //Setup the microcontroller system. Initialize the Embedded Flash Interface,

      //initialize the PLL and update the SystemFrequency variable.

      SystemInit();

     

      // Enable GPIOB, AFIO and Timer1 clocks

      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO | RCC_APB2Periph_TIM1, ENABLE);

    }

    void GPIO_Configuration(void)

    {

      GPIO_InitTypeDef GPIO_InitStructure;

      //partial remap of Timer1

      GPIO_PinRemapConfig(GPIO_PartialRemap_TIM1, ENABLE);

      //GPIOB Configuration: TIM1 channel 2N as alternate function push-pull

      GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_0;

      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

      GPIO_Init(GPIOB, &GPIO_InitStructure);

    }

    /* TIM1 must be configured in such way to generate update event every 20 ms */

    void Timer1_Configuration(void)

    {

      //Timer1 is clocked by PCLK2 = 72MHz

      TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

      TIM_OCInitTypeDef  TIM_OCInitStructure;

      TIM_DeInit(TIM1);

      //Time base configuration

      TIM_TimeBaseStructure.TIM_Prescaler = 71; //clocked at 72MHz / 72 = 1MHz

      TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

      TIM_TimeBaseStructure.TIM_Period = 19999; //period = (72MHz / 72) / 20000 = 50Hz (20ms period)

      TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;

      TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

      /* Channel 1 Configuration in PWM mode */

      TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;

      TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

      TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;

      TIM_OCInitStructure.TIM_Pulse = CCR1_Val;

      TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low;

      TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;

      TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;

      TIM_OC2Init(TIM1, &TIM_OCInitStructure);

      TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);

      /* Select update event as trigger output ''TRGO'' */

      TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update)

     

      TIM_CtrlPWMOutputs(TIM1, ENABLE);

    }

    void Timer2_Configuration(void)

    { TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

      TIM_OCInitTypeDef  TIM_OCInitStructure;

     

     

      /* enable TIM2 APB clock */

      RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

      /* Add GPIO configuration for TIM2 channels: PA0... */

      /***************************************/

      /***************************************/

      /***************************************/

     

      TIM_TimeBaseStructure.TIM_Period = 100;

      TIM_TimeBaseStructure.TIM_Prescaler = 0;

      TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

      TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;

      TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

     

      /* Channel 1 Configuration in PWM mode */

      TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;

      TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

      TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;

      TIM_OCInitStructure.TIM_Pulse = 10;

      TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low;

      TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;

      TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;

      TIM_OC2Init(TIM2, &TIM_OCInitStructure);

       

      /* Select one pulse mode */

      TIM_SelectOnePulseMode(TIM2, TIM_OPMode_Single);

      /* connect TIM1 TRGO (update event in our case) to TIM2 trigger */

      TIM_SelectInputTrigger(TIM2, TIM_TS_ITR0);/* refer to TIMx Internal trigger connection in RM008 */

      /* select slave mode */

      TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Trigger);

    }

    Tesla DeLorean
    Guru
    May 17, 2011
    Posted on May 17, 2011 at 14:14

    SYSClock = 72Mhz

    AHB prescalar = 1

    APB1 prescalar = 2

    PCLK1 = 36 Mhz

    Look at the clock tree again, I'm pretty sure that the timer clock will still be 72 MHz

    I picked values to get the 20 ms exact. But yes you just reduce the scale to give you finer granularity in the width control, while keeping the numbers below 65536. You should be able to get the counter to generate an interrupt, and modulate the width there.

    Fix to my code

    printf(''TIM4 rate %d Hz, width %d us\n'',TIM4_Frequency, TIM4_Width);

    You could use floats to provide decimals places.

    Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..