lpc11u24 and pwmout

28 Feb 2012

I am trying to figure out how PwmOut works on the LPC11U24

#include "mbed.h"

PwmOut hr(p6);
int prd = 1000; // 1000 milli seconds or 1 second
int pw = 500;   // 500 milli seconds or .5 seconds or 50% duty cycle
int main() {
        hr.period_ms(prd);

    while (1) {
    hr.pulsewidth_ms(pw);

    }
}

I am assuming that I would see a 1 second period with a .5 second pulse.

What I see on my oscilloscope is a waveform that has a period of 31.6ms and a pulse width of 10ms.

Also if i run the code below I get no waveform only a 2.7 volt DC


#include "mbed.h"

PwmOut hr(p6);
int prd = 1000; // 1000 milli seconds or 1 second
int pw = 500;   // 500 milli seconds or .5 seconds or 50% duty cycle
int main() {
        hr.period_ms(prd);
        hr.pulsewidth_ms(pw);
    while (1) {


    }
}

What am I doing wrong.

Thanks Bill

01 Mar 2012

I have same problem. And if you change the PwmOut pin, you will see different behaviours.

However, same code on M3 mbed works fine.

Looks to be a bug in mbed M0 firmware?

02 Mar 2012

OK I think I've got it.

The problem in the mbed firmware is that, when 16bit timer is used, the period always overflow 65535, since the clock is 48Mhz. That means, any period that is larger than 1ms will not function properly - with 16bit timer, i.e., p5/6/34/36/20/14. However, the default period is always set to 20ms, which is way bigger than 1ms. That fails all 16bit-timer bases PWM pins.

This can be fixed by setting not only proper period/pulsewidth, but also the prescale register.

mbed-team, pls correct me if I'm wrong.

02 Mar 2012

Mataxa Thank you for all of you analysis on this problem. I had a feeling this is a firmware or API issue I have been digging into the timer registers to produce my own functions to work around this problem.

I hope to have it figured out soon.

02 Mar 2012

This example shows how to use prescale.

#include "mbed.h"

PwmOut servo(p5); /* p5 : LPC_CT16B0, MR1 */
AnalogIn ain(p20);
Serial pc(USBTX, USBRX);
DigitalOut mled(LED1);

int main() {
    LPC_CT16B0->TCR = 2;
    LPC_CT16B0->PR = 19;
    LPC_CT16B0->MR3 = 48000; // 1ms
    LPC_CT16B0->TCR = 1;
    servo.write(0.5f);
    pc.printf("%d %d %d %d, %d\n",  LPC_CT16B0->MR0, LPC_CT16B0->MR1, LPC_CT16B0->MR2, LPC_CT16B0->MR3, LPC_CT16B0->PR);
    while (1) {
        wait(0.25);
        mled = ain.read();
    }
}

The connection between pin and timer is as below. Note that the LPC11u24 section of page http://mbed.org/handbook/PwmOut is not only in-complete, but also wrong.

/*
 * Timer/Match Register : Pinout Options
 * CT16B0/MR1           : p5
 * CT16B0/MR0           : p6
 * CT16B0/MR2           : p34
 * CT16B1/MR0           : p36
 * CT16B1/MR1           : p20 and p14
 * CT32B0/MR0           : p25
 * CT32B0/MR1           : p26 and USBTX 
 * CT32B0/MR2           : p10
 * CT32B1/MR0		: p17
 * CT32B1/MR1		: p18
 * CT32B1/MR2		: p30
 */

Original mbed firmware works fine with CT32Bx timers. But for CT16Bx timers, you have to manually set MR3 to 48000, which stands for 1ms, and then set PR to 19, which increases the tick every 1+19=20 ms. Besides, you can NOT set pulsewidth directly, coz it does not take PR into consideration. The workaround is to use PwmOut.write which sets MRx according to value of MR3.

03 Mar 2012

CT32B1/MR0 appears to be

 * CT32B1/MR0		: p16
03 Mar 2012

I haven't tested CT32B1 coz this timer is occupied by Ticker and wait.

03 Mar 2012

Finally got it with help for mataxa

This will produce a waveform that has a 1 second period with a .9 second pulseWidth.

#include "mbed.h"

#define sysClock                        48000000
#define preScale                        65535
#define CT16B0                          0x80    //Table 23
#define PIO0_8_FUNC_MASK                0x7     //Table 69
#define PIO0_8_FUNC_CT16B0_MAT0         0x2     
#define CT16B0MCR_MR3_RESET_ENABLED     0x400   //Table 285
#define CT16B0EMR_EMC0_LOW              0x10    //Table 289
#define CT16B0EMR_EMC0_HIGH             0x20
#define CT16B0EMR_EMC0_TOGGLE           0x30
#define CT16B0EMR_EM0                   0x1
#define CT16B0PWMC_PWM0_ENABLED         0x1     //PWM mode is enabled for CT16B0_MAT0
#define CT16B0PWMC_PWM0_DISABLED        0x0     //Table 292
#define CT16B0PWMC_PWM3_ENABLED         0x8
#define CT16B0PWMC_PWM3_DISABLED        0x0
#define CT16B0TCR_COUNTERENABLE_ENABLED 0x1     //Table 278

int main() {
//enable the counter for the PWM
LPC_SYSCON->SYSAHBCLKCTRL |= CT16B0;	//enables clock for the 16-bit counter/timer 0 Table 23

LPC_CT16B0->PR = preScale;		        //PreScaler to 65536

//connect mbed pin 6 (lpc11u24 pin8) to PWM
LPC_IOCON->PIO0_8 &= ~PIO0_8_FUNC_MASK;		        //Seems this is standard to clear FUNC Bits before setting it
LPC_IOCON->PIO0_8 |= PIO0_8_FUNC_CT16B0_MAT0;       //configure Timer0_16 to pin 8 CT16B0_MAT0 0x2 Table 69

//set match registers
LPC_CT16B0->MR0	= 73;	                            //Match Register MR0 for counter CT16B0 Table 278
LPC_CT16B0->MR3	= 732;	                            //Match Register MR3 which is the period of the PWM

//reset MR3
LPC_CT16B0->MCR = CT16B0MCR_MR3_RESET_ENABLED;     //Reset MR3 on match with Timer Count TC Table 285

//set the pins behaviour to the matches of MR0 and MR3
LPC_CT16B0->EMR = CT16B0EMR_EMC0_TOGGLE | CT16B0EMR_EM0;			     //0x30 set pin EMC0 Table 289
						                                                 //0x01 set pin HIGH EM0
LPC_CT16B0->PWMC = CT16B0PWMC_PWM0_ENABLED | CT16B0PWMC_PWM3_ENABLED;    //Table 292

LPC_CT16B0->TCR = CT16B0TCR_COUNTERENABLE_ENABLED;                      //turn on timer0 Table 278						                
NVIC_EnableIRQ(TIMER_16_0_IRQn);		                                //enable timer0 interupt

    while (1) {

    }
}
04 Mar 2012

Hi William,

Nice job.

I don't have a oscilloscope. Could u do me a favour to check by oscilloscope the wave for the same code, but without setting EMR? That is, comment this line:

LPC_CT16B0->EMR = CT16B0EMR_EMC0_TOGGLE | CT16B0EMR_EM0;

According to the user manual, it should not produce a square wave. But somehow ... it seems to me it is not necessary to set it, coz I can see the LED up and down even w/o setting EMR. Quite confused.

thanks

04 Mar 2012

mataxa Yes the whole thing is quite confusing I was using different values for the MR0 and it didn't seem to do anything it is the same square wave as before with the line commented out. /media/uploads/wdooley62/waveform.jpg

04 Mar 2012

Thanks.

I noticed that EMR is set automatically somehow, somewhere.

Could you print the value of LPC_CT16B0->EMR in different lines to see when the value is changed from 0 to 5?

User manual section 15.7.13 may have the answer. However, I don't quite understand it. =(

04 Mar 2012

Another issue When you change the period LPC_CT16B0->MR3 Match Register the LPC_CT16B0->TC depending on where TC is in the count and if MR3 is changed to a value less than what TC is currently at then TC will count to 65535.

A work around is to set TC = 0 prior to changing MR3. The only problem with this is the sometimes the last period before the changed period is a bit shorter than it should be.