Change PWM pulse length

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Change PWM pulse length

1,682 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by emimad on Mon Oct 12 15:59:43 MST 2015
Dear all,
I'm having problems when try to change pulse length (not duty length).

In my aplication I need to change PWM signal period (duty will always be 50%) many times on execution so I'm doing next:

void UpdatePulseLength(void){
Chip_TIMER_Disable(LPC_TIMER32_1);

Chip_TIMER_PWMWrite(LPC_TIMER32_1, (1<<3));//enable PWM MAT3
Chip_TIMER_ResetOnMatchEnable(LPC_TIMER32_1, 0);//  MR0
Chip_TIMER_PrescaleSet(LPC_TIMER32_1, 72-1);

PWM_SPEED_us = 600000 / SpeedValue;                                // SpeedValue changes many times
Chip_TIMER_SetMatch(LPC_TIMER32_1, 0, PWM_SPEED_us);//length in µs PERIOD
Chip_TIMER_SetMatch(LPC_TIMER32_1, 3, PWM_SPEED_us / 2);//low in µsMR3 = DUTY 50%

Chip_TIMER_Enable(LPC_TIMER32_1);
}


It's works fine for a time... but after some times, PWM stops working  :~ .
0 Kudos
Reply
5 Replies

1,635 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by mathseng on Wed Oct 14 02:23:36 MST 2015
Hi,
Here is another way of looking at the problem.
I have just completed a simple project using the technique below on the LPC1347, and this gives seamless transitions from one half cycle at the old frequency, going to one half cycle at the new frequency, allowing glitch-free ramping in both directions. The clock output changes either this half-cycle or next, depending on the current value of TC. (This may be an issue for some applications, eg transitioning from 1Hz to 1MHz).

If the output should be a true square wave, this can best be obtained by using hardware to do the clocking without intervention by software interrupts. We do this by using the timer to output a square wave using a count length of 0.5 * desired full-cycle period.
Required:
- the PR register is constant, and is not changed for period changes.
- whenever the timer is enabled, a valid count must be in the period MR registers, and TC must be reset to 0, before enabling the counter.

Technique:
Instead of toggling the match pin at some varying period count, toggle the match pin output when TC == 0. (This seems counter-intuitive, but works like an overflow counter).
Reset the TC on match to either of the other 2 MR registers, one of which contains the new 0.5*period count - this allows changing frequency up or down seamlessly.
Result is TC is ALWAYS less than at least one of these 2 MR registers, so there is no possibility of invalid TC counts.
For this code,
- MR3 is associated with the output pin giving the clock output signal
- MR0, MR1 are the count registers, used to reset TC on match (MR1 is the main period counter, MR0 is the reserve period counter)
- MR0, MR1 are not associated with any output/hardware pin.

My initialization code contains:  (Note tmrCalcPR always equals 1 for my project)
LPC_CT32B0->PR  = tmrCalcPR - 1;// PR contains the 'granule' - number of counts per step
LPC_CT32B0->MR3 = 0;// MR3 toggles when TC == MR3 == 0
LPC_CT32B0->MR0 = LPC_CT32B0->MR1 = tmrCalcMR - 1;// Set period to (number -1) of granules at desired frequency
LPC_CT32B0->EMR  = EMR_TOGGLE << EMR_EMC3;// set EMR register to toggle on MAT3 (which is ALWAYS 0)
LPC_CT32B0->MCR = (MR0R | MR1R);// Set MRx/y as period register, resetting on TC==MRx || TC==MRy


In the code and comments below, the double test/assignment may seem redundant, but it is required to ensure correct ramping up in frequency. Note that the second test/assignment is against a changed MR1.
  - This is simple to prove with a paper timeline graph, which reveals that the 2 MR registers often contain 2 period counts (the old and the new), and that they do re-align correctly .

uint32_t tmrCalcMR = ((float)periphClock) / (freq*tmrCalcPR) + 0.5;       //  TmrCalcPR = 1, freq is how I calculate the 1/2 period count

// DO NOT handle case where PR changes
LPC_CT32B0->PR  = tmrCalcPR - 1;// prescale: calculate size of period being counted

// Aim: to create glitchless transition of clock from one frequency to next.
// Output MAT3 - set to ZERO, and match on TC==0, toggling on match
// Reset TC on TC==MR0 or TC==MR1
// This allows us to have varying frequencies, and to change from one frequency to next without glitching.
// There are 2 cases on attempt to change period
// a. TC < period(MR1)
// b. TC > period(MR1) - this happens when a previous update has not yet cycled, and we cannot update MR0 yet.
// What we want to happen is for the previous cycle to complete correctly.
// However, if the previous cycle is complete, then we want to align the 'long' period register.
// This is done by attempting to align both before and after the period change.
//
// Before changing period, if possible, align clock edges match registers.
// Change period register
// If TC < period, then align clock edges match register
if (LPC_CT32B0->TC < LPC_CT32B0->MR1)
LPC_CT32B0->MR0 = LPC_CT32B0->MR1;// safe, because TC resets on TC==MR3, or will reset on TC==MR0
LPC_CT32B0->MR1 = tmrCalcMR - 1;// Set period to (number -1) of granules at desired frequency
if (LPC_CT32B0->TC < LPC_CT32B0->MR1)
LPC_CT32B0->MR0 = LPC_CT32B0->MR1;// safe, because TC resets on TC==MR3

0 Kudos
Reply

1,635 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by emimad on Tue Oct 13 14:07:10 MST 2015
Solved!!!   8-)
just add TC reset and the problem dissapeared.
Thank you very much guys!  

void UpdatePulseLength(void){

Chip_TIMER_Disable(LPC_TIMER32_1);

        LPC_TIMER32_1 -> TC = 0;                         // this line solves the problem
PWM_SPEED_us = 600000 / SpeedValue;
Chip_TIMER_SetMatch(LPC_TIMER32_1, 0, PWM_SPEED_us);//length in µs 
Chip_TIMER_SetMatch(LPC_TIMER32_1, 3, PWM_SPEED_us / 2);//low in µs MR3 = DUTY 50%

Chip_TIMER_Enable(LPC_TIMER32_1);
}
0 Kudos
Reply

1,635 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by 8473 on Tue Oct 13 08:09:37 MST 2015
One more thought:

It appears as if you would adjust the frequency "out of sync" with the PWM-phase itself (otherwise there's limited probability of setting MATCH < COUNTER, which is --as I think-- the reason for your PWM 'stopping').

Although resetting the counter-register will prevent from "stopping" PWM, you still will experience "broken" PWM-phases -- as you likely "break" an ongoing phase when "randomly" resetting the counter.

You might consider doing the adjustment from the match-interrupt:
Instead of changing the hardware-registers from 'UpdatePulseLength', that function might write the new value(s) to some variable(s) visible to the irq-handler and enable the match-interrupt. On next match, the irq-handler could adjust the frequency (from variable) without any break in an ongoing PWM-phase and disable itself.

0 Kudos
Reply

1,635 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by R2D2 on Tue Oct 13 04:06:43 MST 2015

Quote: emimad
PWM stops working  :~ .



Then it's time to use an update function instead an init function copy  :~

Just stop the timer, reset the counter register, set match registers and start the timer again...

It's not necessary to set all registers (like prescaler) every time  :O
0 Kudos
Reply

1,635 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by 8473 on Tue Oct 13 03:31:35 MST 2015
Hello,

when "shortening" the period, make sure the timer-counter isn't actually larger than the new match-value.
Otherwise the timer would count up to 2^32, which might take a while -- which looks like PWM having stopped working..


0 Kudos
Reply