Skip to main content
adamajames2
Associate II
August 29, 2008
Question

Variable PWM generation

  • August 29, 2008
  • 11 replies
  • 2080 views
Posted on August 29, 2008 at 16:42

Variable PWM generation

    This topic has been closed for replies.

    11 replies

    adamajames2
    Associate II
    May 17, 2011
    Posted on May 17, 2011 at 12:42

    Is it possible using the TIMx PWM Output Mode to produce a wave with a controllable Duty cycle (as in j1850 PWM)?

    Example:

    Bit 0 represents a wave with a 25% duty cycle |^|____

    Bit 1 resresents a wave with a 75% duty cycle. |^^^|_

    So if I send 0100101

    I would see a wave with a variable duty cycle that changes on each bit I transmit.

    |^|____|^^^|_|^|____|^|____|^^^|_|^|____|^^^|_

    Your help will be very much appreciated

    lanchon
    Associate III
    May 17, 2011
    Posted on May 17, 2011 at 12:42

    of course, the compare registers are optionally shadowed. you get an interrupt at the start of a cycle and you've got full cycle to decide what the duty of the next cycle will be; you just have to program the value before this cycle ends.

    lanchon
    Associate III
    May 17, 2011
    Posted on May 17, 2011 at 12:42

    you should use the time base periodic int, not the CC3 int. and enable the interrupt before enabling the timer.

    > the very first pulse is 1us longer than it should be

    the PWM output is probably active before you start the time-base

    1) make the first PWM cycle have zero width:

    PWM_TIM_OCInitStructure.TIM_Pulse = 0; // this goes through to the CC reg since preloading is disabled; it'll be active now and during the first PWM cycle.

    PWM_TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

    TIM_OCInit(TIM2, &PWM_TIM_OCInitStructure);

    TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);

    TIM_ARRPreloadConfig(TIM2, ENABLE);

    TIM2->CCR3 = 432; // now this sits in the preload and will rule cycle 2.

    /* TIM enable counter */

    TIM_Cmd(TIM2, ENABLE); // note that if this immediately causes a time-base int (I don't think so) then the above statement should be removed (the ISR would set width of the 2nd cycle).

    }

    2) if the 1 cycle delay at the beginning is a problem and you need an accurate first pulse, you need to preset the time base counter with a value near the upper limit so that the first PWM cycle is reduced to one or two time-base clocks.

    adamajames2
    Associate II
    May 17, 2011
    Posted on May 17, 2011 at 12:42

    Thank-you lanchon - your response made me persist. I feel I should post the working code for completeness

    void SetUpTimer2(void)

    {

    /* Time base configuration */

    PWM_TIM_TimeBaseStructure.TIM_Period = 1728; /* 1728 at 72MHz gives us 24us bit time */

    PWM_TIM_TimeBaseStructure.TIM_Prescaler = 0;

    PWM_TIM_TimeBaseStructure.TIM_ClockDivision = 0;

    PWM_TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

    TIM_TimeBaseInit(TIM2, &PWM_TIM_TimeBaseStructure);

    /* PWM1 Mode configuration: Channel1 */

    PWM_TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;

    PWM_TIM_OCInitStructure.TIM_Channel = TIM_Channel_3;

    PWM_TIM_OCInitStructure.TIM_Pulse = 432;

    PWM_TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

    TIM_OCInit(TIM2, &PWM_TIM_OCInitStructure);

    TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);

    TIM_ARRPreloadConfig(TIM2, ENABLE);

    /* TIM enable counter */

    TIM_Cmd(TIM2, ENABLE);

    /* Enable the CC3 Interrupt Request */

    TIM_ITConfig(TIM2, TIM_IT_CC3, ENABLE);

    }

    Then the interrupt is simply this:

    void TIM2_IRQHandler(void)

    {

    if ( TIM_GetITStatus(TIM2, TIM_IT_CC3))

    { /* rising edge detected */

    /* Clear TIM2 Capture compare interrupt pending bit */

    TIM_ClearITPendingBit(TIM2, TIM_IT_CC3);

    if (Pulse == 432)

    Pulse = 1296; /*75% Pulse*/

    else

    Pulse = 432; /*25% Pulse*/

    TIM2->CCR3 = Pulse;

    }

    }

    The only slight thing it is doing wrong now is the very first pulse is 1us longer than it should be(the 25% high pulse is 7us rather than 6us). I have no idea why.

    Thanks again

    Adam

    zackguy
    Visitor II
    May 17, 2011
    Posted on May 17, 2011 at 12:42

    I have also face this problem now, could you please give some detailed information? I managed to generate PWM from TIM3_CH3 and the result is good seen on an Oscilloscope, but, i couldn't capture the TIM3 interrupt, code as following:

    /* -----------------------------------------------------------------------

        TIM3 Configuration: generate PWM signal with duty cycle:

        TIM3 CLK = 36 MHz, Prescaler = 0x0, TIM3 counter clock = 36 MHz

        TIM3 ARR Register = 719 => TIM3 Frequency = TIM3 counter clock/(ARR + 1)

        TIM3 Frequency = 100 KHz.

        TIM3 Channel1 duty cycle = (TIM3_CCR1/ TIM3_ARR)* 100 = 50%     CRRx_Val = 360

        TIM3 Channel2 duty cycle = (TIM3_CCR2/ TIM3_ARR)* 100 = 37.5%

        TIM3 Channel3 duty cycle = (TIM3_CCR3/ TIM3_ARR)* 100 = 25%     CRRx_Val = 180

        TIM3 Channel4 duty cycle = (TIM3_CCR4/ TIM3_ARR)* 100 = 12.5%

      ----------------------------------------------------------------------- */

      /* Time base configuration */

      TIM_TimeBaseStructure.TIM_Period = 719;

      TIM_TimeBaseStructure.TIM_Prescaler = 0;

      TIM_TimeBaseStructure.TIM_ClockDivision = 0;

      TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

      TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

      

      

      // Clear TIM2 update pending flag

      TIM_ClearFlag(TIM3, TIM_FLAG_Update);

      

      // Enabel interrupt

      //TIM_ITConfig(TIM3, TIM_IT_CC3, ENABLE);

      TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);

      

      

      //TIM_UpdateRequestConfig(TIM3,TIM_UpdateSource_Regular);

      

      

       /* PWM1 Mode configuration: Channel3 */

      TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;

      TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

      TIM_OCInitStructure.TIM_Pulse = CCR_Val;

      TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

      TIM_OC3Init(TIM3, &TIM_OCInitStructure);

      TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);

      //TIM_ARRPreloadConfig(TIM3,DISABLE);

      TIM_ARRPreloadConfig(TIM3, ENABLE);

      /* TIM3 enable counter */

      TIM_Cmd(TIM3, ENABLE);

    void TIM3_IRQHandler(void)

    {

        if (TIM_GetITStatus(TIM3, TIM_IT_CC3)!= RESET)

        {

            

            GPIO_WriteBit(GPIO_

    LED

    , GPIO_LED0_PIN, (BitAction)((1-GPIO_ReadOutputDataBit(GPIO_LED, GPIO_LED0_PIN)))); 

            //IC3value = TIM_GetCapture3(TIM3);

       

            TIM_ClearITPendingBit(TIM3, TIM_IT_CC3);

        }

    }

    Why doesn't The LED toggle with the TIM interrupt? How can I ''you get an interrupt at the start of a cycle and you've got full cycle to decide what the duty of the next cycle will be''

    Thank you advanced.

    lanchon
    Associate III
    May 17, 2011
    Posted on May 17, 2011 at 12:42

    glad to hear it worked.

    > Thanks again lanchon - I hope ST pay you for this service.

    ha! in fact ST turned down my most recent sample request...

    adamajames2
    Associate II
    May 17, 2011
    Posted on May 17, 2011 at 12:42

    Thanks again lanchon - I hope ST pay you for this service.

    I did as you said and stopped the long first pulse problem by setting Pulse to 0 initially.

    I have now successfully transmitted a J1850PWM message this way to a vehicle ECU and received the expected response (seen on a scope).

    Adam

    zackguy
    Visitor II
    May 17, 2011
    Posted on May 17, 2011 at 12:43

    I did use the ''TIM_ITConfig(TIM3, TIM_IT_CC3, ENABLE)'' instead of '' TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE)'' and they act as no differece, so that could not be the reason. I just wondering should i take another timer to set as input to cature the interrupt.

    lowpowermcu
    Associate III
    May 17, 2011
    Posted on May 17, 2011 at 12:43

    Hi liu.zack,

    Perhaps you have to uncomment the following line in your code:

     //TIM_ITConfig(TIM3, TIM_IT_CC3, ENABLE);

    Regards,

    be low power, be happy

    smart2
    Visitor II
    May 17, 2011
    Posted on May 17, 2011 at 12:43

    based on your code, looks like you used interrupt for TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);

    and in Interrupt handler you are checking for  if (TIM_GetITStatus(TIM3, TIM_IT_CC3)!= RESET)

    both should be same either you use CC3 or IT_Update