cancel
Showing results for 
Search instead for 
Did you mean: 

Variable PWM generation

adamajames2
Associate II
Posted on August 29, 2008 at 16:42

Variable PWM generation

11 REPLIES 11
lanchon
Associate III
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.

adamajames2
Associate II
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

adamajames2
Associate II
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

lanchon
Associate III
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
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

lanchon
Associate III
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...

zackguy
Associate II
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.

lowpowermcu
Associate II
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

zackguy
Associate II
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.