Is it possible to invert PWM output on LPC1114?

Started by bobryanmm August 17, 2013
I am trying to drive a servo using the following function and I've messed with the EMR register but I think the values are basically ignored because I've got PWM output enabled for MR0/MR1. The datasheet says we need to read a set of rules that apply when PWM mode is used. I know I can get around this by triggering interrupts with the timers/compare modules then toggling the IO pins in the interrupt functions but I'd be surprised if there wasn't a simpler way to do this that might not require any processing. Right now my waveform is inverted of a valid 50Hz servo pulse (1-2ms high then 18-19ms low). I've found some example code online but not for these processors and the LPC17xx seems to have a different PWM peripheral (requires manual latching of the data, makes use of other bits/registers not described in the LPC1114 datasheet).

void PWM_init(void)
{
LPC_TMR16B0->TCR = 0; //Disable Timer0
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<7);
LPC_TMR16B0->EMR = (1<<0)|(1<<1)|(1<<3)|(1<<5)|(1<<10);
LPC_TMR16B0->PR = 11; //set prescaler to 12 for ~20ms period

LPC_IOCON->PIO0_8 &= ~0x07;
LPC_IOCON->PIO0_9 &= ~0x07;
LPC_IOCON->PIO0_8 |= 0x02;
LPC_IOCON->PIO0_9 |= 0x02;

LPC_TMR16B0->MR3 = 20000-1;
LPC_TMR16B0->MR0 = 1500;
LPC_TMR16B0->MR1 = 1500;
LPC_TMR16B0->MCR = 0x400;
LPC_TMR16B0->PWMC = 0x0B;

NVIC_EnableIRQ(TIMER_16_0_IRQn); // Enable the TIMER1 Interrupt
LPC_TMR16B0->TCR = 1; // Enable Timer16
}

Any help is quite welcome. Thanks -Bob

An Engineer's Guide to the LPC2100 Series

Hi Bob,

are your PWM signal working fine when you set a another duty-cycle? If
yes you probably are inverting your signal on your hardware (probably on
signal isolation using a opto or using a transistor). You can change
this two ways:
- Adding a transistor on your hardware to invert your driver signal;
- invert you signal on software. To invert in software, the easier way
is taking your full duty-cycle (100%) value and subtract your calculated
duty cycle (for example: for 2ms high and 18ms low you are using a 10%
duty cycle and to invert the waveform you need to use 90% of duty cycle).
I use LPC2136/38 to control servo motors using PWM and build my own
driver for it (low power), using hardware invertion.

Atenciosamente,

Mauro L. Lenz
Fone: (55) 8115-6944
Skype: mauro_eafs

"Ajude a combater o Spam. Ao encaminhar uma mensagem, apague o e-mail do remetente e utilize o campo CCo quando enviar para mais de um destinatio."

Em 17/08/2013 12:20, bobryanmm escreveu:
> void PWM_init(void)

Mauro,

Thanks for the response. I am definitely not inverting the signal in hardware. The duty cycle is as expected though the signal is inverted so the actual duty cycle is 100%-desired% (in my case a 80-90%). The EMR register seems to dictate if the EM0 output should be set or cleared on compare match but there is a note that if PWM is enabled in the PWMC register that EMCn has no affect.

"If the match outputs are configured as PWM output in the PWMCON registers (Section 18.7.12), the function of the external match registers is determined by the PWM rules (Section 18.7.13 "Rules for single edge controlled PWM outputs" on page 340)."

If we look up the rules on page 340 we get:
"
18.7.13 Rules for single edge controlled PWM outputs
1. All single edge controlled PWM outputs go LOW at the beginning of each PWM cycle (timer is set to zero) unless their match value is equal to zero.
2. Each PWM output will go HIGH when its match value is reached. If no match occurs (i.e. the match value is greater than the PWM cycle length), the PWM output remains continuously LOW.
3. If a match value larger than the PWM cycle length is written to the match register, and the PWM signal is HIGH already, then the PWM signal will be cleared on the next start of the next PWM cycle.
4. If a match register contains the same value as the timer reset value (the PWM cycle length), then the PWM output will be reset to LOW on the next clock tick. Therefore, the PWM output will always consist of a one clock tick wide positive pulse with a period determined by the PWM cycle length (i.e. the timer reload value).
5. If a match register is set to zero, then the PWM output will go to HIGH the first time the timer goes back to zero and will stay HIGH continuously.

Note: When the match outputs are selected to serve as PWM outputs, the timer reset (MRnR) and timer stop (MRnS) bits in the Match Control Register MCR must be set to 0 except for the match register setting the PWM cycle length. For this register, set the MRnR bit to 1 to enable the timer reset when the timer value matches the value of the corresponding match register.
"

To me this is pretty clear that rules 1 and 2 say the output will be set low upon timer reset and set high on compare match. I've worked with lots of micros that allow these outputs to be inverted rather simply but that doesn't seem to be the case with this chip. I know there are ways to work around it but I'm just wondering if anyone might have insight on this limitation or if I'm misreading the datasheet etc.

Thanks again -Bob

--- In l..., Mauro Lenz wrote:
>
> Hi Bob,
>
> are your PWM signal working fine when you set a another duty-cycle? If
> yes you probably are inverting your signal on your hardware (probably on
> signal isolation using a opto or using a transistor). You can change
> this two ways:
> - Adding a transistor on your hardware to invert your driver signal;
> - invert you signal on software. To invert in software, the easier way
> is taking your full duty-cycle (100%) value and subtract your calculated
> duty cycle (for example: for 2ms high and 18ms low you are using a 10%
> duty cycle and to invert the waveform you need to use 90% of duty cycle).
> I use LPC2136/38 to control servo motors using PWM and build my own
> driver for it (low power), using hardware invertion.
>
> Atenciosamente,
>
> Mauro L. Lenz
> Fone: (55) 8115-6944
> Skype: mauro_eafs
>
> "Ajude a combater o Spam. Ao encaminhar uma mensagem, apague o e-mail do remetente e utilize o campo CCo quando enviar para mais de um destinatio."
>
> Em 17/08/2013 12:20, bobryanmm escreveu:
> > void PWM_init(void)
>

Handle the inversion is software. For example, if you want a two millisecond inverted pulse, set up the PWM for 18 milliseconds.

Jeff

--- In l..., "bobryanmm" wrote:
>
> I am trying to drive a servo using the following function and I've messed with the EMR register but I think the values are basically ignored because I've got PWM output enabled for MR0/MR1. The datasheet says we need to read a set of rules that apply when PWM mode is used. I know I can get around this by triggering interrupts with the timers/compare modules then toggling the IO pins in the interrupt functions but I'd be surprised if there wasn't a simpler way to do this that might not require any processing. Right now my waveform is inverted of a valid 50Hz servo pulse (1-2ms high then 18-19ms low). I've found some example code online but not for these processors and the LPC17xx seems to have a different PWM peripheral (requires manual latching of the data, makes use of other bits/registers not described in the LPC1114 datasheet).
>
> void PWM_init(void)
> {
> LPC_TMR16B0->TCR = 0; //Disable Timer0
> LPC_SYSCON->SYSAHBCLKCTRL |= (1<<7);
> LPC_TMR16B0->EMR = (1<<0)|(1<<1)|(1<<3)|(1<<5)|(1<<10);
> LPC_TMR16B0->PR = 11; //set prescaler to 12 for ~20ms period
>
> LPC_IOCON->PIO0_8 &= ~0x07;
> LPC_IOCON->PIO0_9 &= ~0x07;
> LPC_IOCON->PIO0_8 |= 0x02;
> LPC_IOCON->PIO0_9 |= 0x02;
>
> LPC_TMR16B0->MR3 = 20000-1;
> LPC_TMR16B0->MR0 = 1500;
> LPC_TMR16B0->MR1 = 1500;
> LPC_TMR16B0->MCR = 0x400;
> LPC_TMR16B0->PWMC = 0x0B;
>
> NVIC_EnableIRQ(TIMER_16_0_IRQn); // Enable the TIMER1 Interrupt
> LPC_TMR16B0->TCR = 1; // Enable Timer16
> }
>
> Any help is quite welcome. Thanks -Bob
>

In this case that will work but in certain circumstances it wouldn't because we'd have the high pulse at the end of the cycle. I went ahead and disabled PWM on the outputs and use EMC0 to clear the EM0 bit on compare match then trigger and interrupt on EM3 compare match and inside that interrupt routine I'm setting EM0 high again.. This does work fine but I would prefer doing this without use of interrupts or additional processing. After reading the datasheet a number of times I don't think this is possible. Thanks -Bob

--- In l..., "ksdoubleshooter" wrote:
>
> Handle the inversion is software. For example, if you want a two millisecond inverted pulse, set up the PWM for 18 milliseconds.
>
> Jeff
>
> --- In l..., "bobryanmm" wrote:
> >
> > I am trying to drive a servo using the following function and I've messed with the EMR register but I think the values are basically ignored because I've got PWM output enabled for MR0/MR1. The datasheet says we need to read a set of rules that apply when PWM mode is used. I know I can get around this by triggering interrupts with the timers/compare modules then toggling the IO pins in the interrupt functions but I'd be surprised if there wasn't a simpler way to do this that might not require any processing. Right now my waveform is inverted of a valid 50Hz servo pulse (1-2ms high then 18-19ms low). I've found some example code online but not for these processors and the LPC17xx seems to have a different PWM peripheral (requires manual latching of the data, makes use of other bits/registers not described in the LPC1114 datasheet).
> >
> > void PWM_init(void)
> > {
> > LPC_TMR16B0->TCR = 0; //Disable Timer0
> > LPC_SYSCON->SYSAHBCLKCTRL |= (1<<7);
> > LPC_TMR16B0->EMR = (1<<0)|(1<<1)|(1<<3)|(1<<5)|(1<<10);
> > LPC_TMR16B0->PR = 11; //set prescaler to 12 for ~20ms period
> >
> > LPC_IOCON->PIO0_8 &= ~0x07;
> > LPC_IOCON->PIO0_9 &= ~0x07;
> > LPC_IOCON->PIO0_8 |= 0x02;
> > LPC_IOCON->PIO0_9 |= 0x02;
> >
> > LPC_TMR16B0->MR3 = 20000-1;
> > LPC_TMR16B0->MR0 = 1500;
> > LPC_TMR16B0->MR1 = 1500;
> > LPC_TMR16B0->MCR = 0x400;
> > LPC_TMR16B0->PWMC = 0x0B;
> >
> > NVIC_EnableIRQ(TIMER_16_0_IRQn); // Enable the TIMER1 Interrupt
> > LPC_TMR16B0->TCR = 1; // Enable Timer16
> > }
> >
> > Any help is quite welcome. Thanks -Bob
>

--- In l..., "bobryanmm" wrote:
>
> In this case that will work but in certain circumstances it wouldn't because we'd have the high pulse at the end of the cycle.

Cycles come and go but where they start is up to you. I maintain that the cycle starts whenever the pulse goes high. I can look at the off time as being ahead of the leading edge or after the trailing edge - my rules.

Richard

The PWM mode for the LPC1100 timers has some issues, the polarity is the opposite of what most might expect and setting up a match register with the same value as the terminal count cause a glitch on the output.

The following code will produce a exactly what you originally were trying to accomplish -- polarity inversion and de-glitching by changing the pin to GPIO in the case where a value of 0 is passed for the duty cycle.

#include
#include
#include "pwm.h"
#include "gpio.h"

void pwm_init(void)
{
SYSAHBCLKCTRL |= (1 << 8); //power up timer 16b1
IOCON_PIO1_9 = 0 | (0 << 3) | (3 << 6);
IOCON_PIO1_10 = 0 | (0 << 3) | (3 << 6);
GPIO1DIR |= ((1 << 9) | (1 << 10));
GPIO1_MASKED_RW(((1 << 9) | (1 << 10))) = 0;
TMR16B1TCR = 2; //reset timer
TMR16B1PR = 2; //prescaler
TMR16B1MR3 = 1000; //match register 3 -- sets pwm frequency
TMR16B1MCR = (2 << 9); //reset on match 3
TMR16B1PWMC = 0x03; //enable pwm on mat0 and mat1
TMR16B1TCR = 1; //start timer
}

void pwm_output(uint32_t chn, uint32_t duty_cycle)
{
switch (chn)
{
case 0:
if (duty_cycle == 0)
{
IOCON_PIO1_9 &= ~7; //mat0 back to io
GPIO1_MASKED_RW(1 << 9) = 0; //and set low
}
else
{
if (duty_cycle > 1000)
duty_cycle = 1000;
TMR16B1MR0 = 1000 - duty_cycle;
IOCON_PIO1_9 |= 1; //p1.9 set to t16b1 mat0
}
break;
case 1:
if (duty_cycle == 0)
{
IOCON_PIO1_10 &= ~7; //mat1 back to io
GPIO1_MASKED_RW(1 << 10) = 0; //and set low
}
else
{
if (duty_cycle > 1000)
duty_cycle = 1000;
TMR16B1MR1 = 1000 - duty_cycle;
IOCON_PIO1_10 |= 2; //p1.10 set to t16b1 mat1
}
break;
}
}
--- In l..., "bobryanmm" wrote:
>
> In this case that will work but in certain circumstances it wouldn't because we'd have the high pulse at the end of the cycle. I went ahead and disabled PWM on the outputs and use EMC0 to clear the EM0 bit on compare match then trigger and interrupt on EM3 compare match and inside that interrupt routine I'm setting EM0 high again.. This does work fine but I would prefer doing this without use of interrupts or additional processing. After reading the datasheet a number of times I don't think this is possible. Thanks -Bob
>
> --- In l..., "ksdoubleshooter" wrote:
> >
> > Handle the inversion is software. For example, if you want a two millisecond inverted pulse, set up the PWM for 18 milliseconds.
> >
> > Jeff
> >
> > --- In l..., "bobryanmm" wrote:
> > >
> > > I am trying to drive a servo using the following function and I've messed with the EMR register but I think the values are basically ignored because I've got PWM output enabled for MR0/MR1. The datasheet says we need to read a set of rules that apply when PWM mode is used. I know I can get around this by triggering interrupts with the timers/compare modules then toggling the IO pins in the interrupt functions but I'd be surprised if there wasn't a simpler way to do this that might not require any processing. Right now my waveform is inverted of a valid 50Hz servo pulse (1-2ms high then 18-19ms low). I've found some example code online but not for these processors and the LPC17xx seems to have a different PWM peripheral (requires manual latching of the data, makes use of other bits/registers not described in the LPC1114 datasheet).
> > >
> > > void PWM_init(void)
> > > {
> > > LPC_TMR16B0->TCR = 0; //Disable Timer0
> > > LPC_SYSCON->SYSAHBCLKCTRL |= (1<<7);
> > > LPC_TMR16B0->EMR = (1<<0)|(1<<1)|(1<<3)|(1<<5)|(1<<10);
> > > LPC_TMR16B0->PR = 11; //set prescaler to 12 for ~20ms period
> > >
> > > LPC_IOCON->PIO0_8 &= ~0x07;
> > > LPC_IOCON->PIO0_9 &= ~0x07;
> > > LPC_IOCON->PIO0_8 |= 0x02;
> > > LPC_IOCON->PIO0_9 |= 0x02;
> > >
> > > LPC_TMR16B0->MR3 = 20000-1;
> > > LPC_TMR16B0->MR0 = 1500;
> > > LPC_TMR16B0->MR1 = 1500;
> > > LPC_TMR16B0->MCR = 0x400;
> > > LPC_TMR16B0->PWMC = 0x0B;
> > >
> > > NVIC_EnableIRQ(TIMER_16_0_IRQn); // Enable the TIMER1 Interrupt
> > > LPC_TMR16B0->TCR = 1; // Enable Timer16
> > > }
> > >
> > > Any help is quite welcome. Thanks -Bob
> > >
>

Using a match interrupt and diddling with stuff in the ISR will result in duty cycle jitter.

Jeff

--- In l..., "bobryanmm" wrote:
>
> In this case that will work but in certain circumstances it wouldn't because we'd have the high pulse at the end of the cycle. I went ahead and disabled PWM on the outputs and use EMC0 to clear the EM0 bit on compare match then trigger and interrupt on EM3 compare match and inside that interrupt routine I'm setting EM0 high again.. This does work fine but I would prefer doing this without use of interrupts or additional processing. After reading the datasheet a number of times I don't think this is possible. Thanks -Bob
>
> --- In l..., "ksdoubleshooter" wrote:
> >
> > Handle the inversion is software. For example, if you want a two millisecond inverted pulse, set up the PWM for 18 milliseconds.
> >
> > Jeff
> >
> > --- In l..., "bobryanmm" wrote:
> > >
> > > I am trying to drive a servo using the following function and I've messed with the EMR register but I think the values are basically ignored because I've got PWM output enabled for MR0/MR1. The datasheet says we need to read a set of rules that apply when PWM mode is used. I know I can get around this by triggering interrupts with the timers/compare modules then toggling the IO pins in the interrupt functions but I'd be surprised if there wasn't a simpler way to do this that might not require any processing. Right now my waveform is inverted of a valid 50Hz servo pulse (1-2ms high then 18-19ms low). I've found some example code online but not for these processors and the LPC17xx seems to have a different PWM peripheral (requires manual latching of the data, makes use of other bits/registers not described in the LPC1114 datasheet).
> > >
> > > void PWM_init(void)
> > > {
> > > LPC_TMR16B0->TCR = 0; //Disable Timer0
> > > LPC_SYSCON->SYSAHBCLKCTRL |= (1<<7);
> > > LPC_TMR16B0->EMR = (1<<0)|(1<<1)|(1<<3)|(1<<5)|(1<<10);
> > > LPC_TMR16B0->PR = 11; //set prescaler to 12 for ~20ms period
> > >
> > > LPC_IOCON->PIO0_8 &= ~0x07;
> > > LPC_IOCON->PIO0_9 &= ~0x07;
> > > LPC_IOCON->PIO0_8 |= 0x02;
> > > LPC_IOCON->PIO0_9 |= 0x02;
> > >
> > > LPC_TMR16B0->MR3 = 20000-1;
> > > LPC_TMR16B0->MR0 = 1500;
> > > LPC_TMR16B0->MR1 = 1500;
> > > LPC_TMR16B0->MCR = 0x400;
> > > LPC_TMR16B0->PWMC = 0x0B;
> > >
> > > NVIC_EnableIRQ(TIMER_16_0_IRQn); // Enable the TIMER1 Interrupt
> > > LPC_TMR16B0->TCR = 1; // Enable Timer16
> > > }
> > >
> > > Any help is quite welcome. Thanks -Bob
> > >
>

Bob,

Driving a servo should be easy and doesn't require any interrupts (unless I don't fully understand your question). You have to do quite a bit of setup in the timers to get it going, but once it's going the timer device generates the waveform for you.

I have some sample code for PWM on the LPC1114 up on Github at: https://github.com/engineergorman/lpc1114fn28 https://github.com/engineergorman/lpc1114fn28

I'm using the free-version of the Keil dev environment. Just grab the whole lpc1114fn28 folder, navigate to 'pwm' or 'pwmfan' samples, open and build them. I've been working on BLDC control lately and have been modifying the shared headers in core. So if it doesn't build properly just send me a note and I'll fix it.

Cheers,
Chris

---In l..., wrote:

Using a match interrupt and diddling with stuff in the ISR will result in duty cycle jitter.

Jeff

--- In l... mailto:l..., "bobryanmm" wrote:
>
> In this case that will work but in certain circumstances it wouldn't because we'd have the high pulse at the end of the cycle. I went ahead and disabled PWM on the outputs and use EMC0 to clear the EM0 bit on compare match then trigger and interrupt on EM3 compare match and inside that interrupt routine I'm setting EM0 high again.. This does work fine but I would prefer doing this without use of interrupts or additional processing. After reading the datasheet a number of times I don't think this is possible. Thanks -Bob
>
> --- In l... mailto:l..., "ksdoubleshooter" wrote:
> >
> > Handle the inversion is software. For example, if you want a two millisecond inverted pulse, set up the PWM for 18 milliseconds.
> >
> > Jeff
> >
> > --- In l... mailto:l..., "bobryanmm" wrote:
> > >
> > > I am trying to drive a servo using the following function and I've messed with the EMR register but I think the values are basically ignored because I've got PWM output enabled for MR0/MR1. The datasheet says we need to read a set of rules that apply when PWM mode is used. I know I can get around this by triggering interrupts with the timers/compare modules then toggling the IO pins in the interrupt functions but I'd be surprised if there wasn't a simpler way to do this that might not require any processing. Right now my waveform is inverted of a valid 50Hz servo pulse (1-2ms high then 18-19ms low). I've found some example code online but not for these processors and the LPC17xx seems to have a different PWM peripheral (requires manual latching of the data, makes use of other bits/registers not described in the LPC1114 datasheet).
> > >
> > > void PWM_init(void)
> > > {
> > > LPC_TMR16B0->TCR = 0; //Disable Timer0
> > > LPC_SYSCON->SYSAHBCLKCTRL |= (1<<7);
> > > LPC_TMR16B0->EMR = (1<<0)|(1<<1)|(1<<3)|(1<<5)|(1<<10);
> > > LPC_TMR16B0->PR = 11; //set prescaler to 12 for ~20ms period
> > >
> > > LPC_IOCON->PIO0_8 &= ~0x07;
> > > LPC_IOCON->PIO0_9 &= ~0x07;
> > > LPC_IOCON->PIO0_8 |= 0x02;
> > > LPC_IOCON->PIO0_9 |= 0x02;
> > >
> > > LPC_TMR16B0->MR3 = 20000-1;
> > > LPC_TMR16B0->MR0 = 1500;
> > > LPC_TMR16B0->MR1 = 1500;
> > > LPC_TMR16B0->MCR = 0x400;
> > > LPC_TMR16B0->PWMC = 0x0B;
> > >
> > > NVIC_EnableIRQ(TIMER_16_0_IRQn); // Enable the TIMER1 Interrupt
> > > LPC_TMR16B0->TCR = 1; // Enable Timer16
> > > }
> > >
> > > Any help is quite welcome. Thanks -Bob
> > >
> >
>