Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

pwm basics

Status
Not open for further replies.

HakBot

New Member
I'm trying to understand the innerworkings of generating a pwm signal from a pic18f4550.

Here are the specs fo the pic/board:

Internal clock of the pic18f4550 = 48MHz
External clock (crystal) = 20MHz

I need the pwm period to be 20ms and the duty cycle of the servo to be 1-2ms to turn the servo left or right. Therefore the following calculation should give me the period.

20,000,000/(255+1)x16x4 = 1220.70Hz for the pwm frequency. This doesnt seem correct though. Can someone help me determine what my Timer2 prescale, Timer2 postscale, and PR2 variables should be?

Thanks!
Phil
 
Hi Phil,

A 20-msec PWM period is 50-Hz. Much too slow for the PWM module.

I've not done anything with the '2550 and '4550 samples on the Lab' bench here. Can you tell me what the core instruction speed is? Surely it's not 48-MHz, is it?

Regards, Mike
 
Hi again Phil,

I've not played with Servos before but I suspect setting up the PWM peripheral could provide good resolution at reduced processor overhead compared to a pure software solution. Unfortunately, as I mentioned before, I'm not sure you can setup the CCP module for a 20-msec PWM period. And if you could, you wouldn't have very fine control of that pulse width. Perhaps only 50 or so 20-usec steps between 1.0-msecs and 2.0-msecs using a 10-bit value for CCPR1L.

I wonder if something like this might work (drawing below)?

Break up the 20-msec overall Servo period into twenty periods by setting up the TMR2/CCP module for a 1-msec PWM period (4-usec TMR2 'ticks' and PR2=250). Enable TMR2 interrupts and preload the CCPR1L duty cycle register each interrupt with a value of 250 for period PWM00 (100% duty cycle), with a value of N (000..250) for period PWM01 (variable duty cycle), and with a value of 000 for periods PWM02 through PWM19 (000% duty cycle). The end result would be a 1.0-msec to 2.0-msec pulse with 0.004-msec resolution in 250 steps within a 20-msec period. And as you can see in the code example below, using very little processor 'overhead'.

The interrupt software could look something like this example for 12F683 where your main program outside of the ISR would simply stuff the Pulse variable with some value between 000 and 250;

Code:
;******************************************************************
;
ISR     movwf   W_ISR           ; save W-reg                      |B?
        swapf   STATUS,W        ; doesn't change STATUS bits      |B?
        movwf   S_ISR           ; save STATUS reg                 |B?
        clrf    STATUS          ; force bank 0                    |B0
        bcf     PIR1,TMR2IF     ; clear TMR2 interrupt flag       |B0
;
;  PWM00 1-msec period = 100% duty cycle
;  PWM01 1-msec period = variable (000..250 4-usec steps)
;  PWM02..PWM19 period = 000% duty cycle
;
ISR_Servo
        movlw   d'000'          ; assume next cycle PWM02-PWM19   |B0
        movwf   CCPR1L          ; set 000% duty cycle             |B0
ISR_Counter
        incf    PWMCTR,f        ; inc our 00..19 counter          |B0
        movf    PWMCTR,W        ;                                 |B0
        xorlw   d'20'           ; count within 00..19?            |B0
        bnz     ISR_PWM01       ; yes, branch, else               |B0
ISR_PWM00
        movwf   PWMCTR          ; reset counter to 00 (PWM00)     |B0
        movlw   d'250'          ; get value for 100% duty cycle   |B0
        movwf   CCPR1L          ; set 100% duty cycle             |B0
ISR_PWM01
        movf    PWMCTR,W        ;                                 |B0
        xorlw   d'01'           ; PWM01 next cycle?               |B0
        bnz     ISR_XIT         ; no, branch, else                |B0
        movf    Pulse,W         ; get Pulse width variable        |B0
        movwf   CCPR1L          ; set variable duty cycle         |B0
;
ISR_XIT swapf   S_ISR,W         ;                                 |B0
        movwf   STATUS          ; restore STATUS                  |B?
        swapf   W_ISR,f         ; don't screw up STATUS           |B?
        swapf   W_ISR,W         ; restore W-reg                   |B?
        retfie                  ; return from interrupt           |B?
;
;******************************************************************
Hey fellows. How do you experts do it?

Regards, Mike
 

Attachments

  • Servo PWM Concept.JPG
    Servo PWM Concept.JPG
    36.6 KB · Views: 523
Last edited:
Mike,

I checked the specs of the pic **broken link removed**
and it says: 48 MHz performance (12 MIPS).

You are correct. The period needed to drive a servo pulse is 20ms with a 1-2ms duty cycle (5-10%). I plan to start off with delay loops that will set the line to high and then wait the desired time. I will learn how to use the pwm functions on my chip later on.

I found this code and it works:
Code:
    if (PIR1bits.TMR2IF) 			// if the timer interrupt bit is set...
	{
		{
    		LATCbits.LATC0 = 1; 	// output high voltage to pin
			Delay100TCYx( delay ); 	// wait the desired pulse width
    		LATCbits.LATC0 = 0; 	// output 0 voltage to pin

        	count = 4; 				// reset the pulse counter

			// oscillate between max and min angular displacement of servo
       		delay += incr;
       		if (delay > 254) 
        		incr = -incr;
       		if (delay < 70) 
        		incr = -incr;

       PIR1bits.TMR2IF = 0; 		// reset the interrupt bit
    }

Since the chips internal clock is 48MHz it runs 48million cycles per second. On the pic18f4550 1 instruction is executed every 4 clock cycles so a Nop(); will take 1/12ns. Delay100TCYx(120); for instance will produce a delay of 1ms.

The following is what I do not understand.

Code:
	TRISCbits.TRISC0 = 0; //output pin c0

	// Timer2 prescale = 16
	T2CONbits.T2CKPS0 = 0;
	T2CONbits.T2CKPS1 = 1;
	
	// Timer2 postscale = 16
	T2CONbits.T2OUTPS0 = 1;
	T2CONbits.T2OUTPS1 = 1;
	T2CONbits.T2OUTPS2 = 1;
	T2CONbits.T2OUTPS3 = 1;

	// Timer period = maximum
	PR2 = 0xFF; 

	// Timer2 is on
	T2CONbits.TMR2ON = 1;

    // Enable Timer2 interrupt
    PIE1bits.TMR2IE = 1;

I need to make the timer interrupt every 20ms. Since I have a 20MHz external clock and a 48MHz internal, how do I set the bits above to do this?
 
Phil,

Timer 2 uses clocks of Tcyc (Tosc*4) for the clock source. That's approximately 83.3-nsecs for your 48-MHz system clock.

Section 13.1 in the '2550/'4550 Data Sheet shows that two bits in T2CON allow prescaling clocks to Timer 2 by 1:1, 1:4, or 1:16 and four bits in T2CON allow postscaling clocks coming from a Timer 2 / PR2 period match by 1:1 through 1:16 before setting the Timer 2 interrupt flag.

The maximum period we can set up on Timer 2 with your 48-MHz clock is 5.46-msecs (83.3-nsecs * prescale 16 * postscale 16 * PR2 maximum value 256). A wee bit short of the 20-msecs you're lookin' for.

Again, you might want to consider a shorter interrupt and a simple counter mechanism in your interrupt handler code to signal the beginning/end of the 20-msec period. Or, a slower clock?

I wouldn't want to tie up my micro project for 1 to 2-msecs every 20-msecs in a delay routine where I couldn't perform any other processing but if your main code can get by in the remaining 18 to 19-msecs each period, that's fine.

Good luck. Kind regards, Mike
 
Last edited:
HakBot said:
Mike,

I checked the specs of the pic **broken link removed**
and it says: 48 MHz performance (12 MIPS).

Since I have a 20MHz external clock and a 48MHz internal, how do I set the bits above to do this?

You don't have a clock like that!.

If you have a 20MHz crystal it's running at 20MHz, divided by four internally, to give a 200nS instruction cycle (5MIPS). The 48MHz comes in because you can have a 12MHz crystal and enable an internal 4x PLL to give effectively 48MHz but again divided internally by four to give 12MIPS.
 
Nigel,

Table 2-3 in the '2550/'4550 Data Sheet clearly shows how to derive an Fosc 48-MHz clock from a different crystal frequency to drive the processor core at an Fcyc of 12-MHz (12-MIPS). Very cool.

Mike
 
Last edited:
Thanks guys for the great responses but I'm afraid that I'm a bit of a noob here.

Nigel,

Sorry, I was a little confused on how the clock worked. Thanks for correcting me. From this new data I can calculate that the max period I can set will be
13.1-msecs (200-nsecs * prescale 16 * postscale 16 * PR2 maximum value 256). Is this correct?

Mike,

Again, you might want to consider a shorter interrupt and a simple counter mechanism in your interrupt handler code to signal the beginning/end of the 20-msec period. Or, a slower clock?

I guess I gave you some bogus clock speed data. Using the formula you gave me the max period I can set for timer2 would be 13.1ms. The original author of the software initially set a counter that would only send the pwm pulse every 4 timer interrupts. If I set the timer data to:
9.9-msecs (200-nsecs * prescale 16 * postscale 16 * PR2 195). it will give me a period of 10ms. If I set a counter to only send the pulse ever 2 interrupts this should work, correct? Is something like this what you are referring to?

Thanks!
 
Phil,

The Data Sheet shows that a 48-MHz Fosc from a 20-MHz crystal is possible by setting the PLL prescaler divider to 5 to produce the 4-MHz signal that the 96-MHz PLL requires. Then using a PLL postscaler divider value of 2 on the 96-MHz PLL signal do drive the core at 48-MHz.

So what core frequency are you running exactly? It seems you may not know for sure? And I suspect Nigel may not be quite up-to-date on the oscillator chain in this device.

As for your interrupt code. If it's setup to generate the 1 to 2-msec pulse every fourth interrupt then the interrupt period must be 5-msecs. If you set it up to generate the pulse every other interrupt then the interrupt period would be 10-msecs, as you said.

Mike
 
Last edited:
Mike said:
Nigel,

Table 2-3 in the '2550/'4550 Data Sheet clearly shows how to derive an Fosc 48-MHz clock from a different crystal frequency to drive the processor core at an Fcyc of 12-MHz (12-MIPS). Very cool.

Hi Mike,

Thanks, I'll have to have a read of the datasheet, the older 18F's didn't allow that!.
 
Phil,

It seems an 8-MHz Fosc would allow a 20-msec Timer 2 period. Tcyc would be 500-nsecs. Setting Timer 2 prescaler to 1:16 would provide 8-usec 'ticks' to the TMR2 register. Setting PR2=250 would provide 2-msec 'ticks' to the postscaler on each TMR2/PR2 match. Finally, setting the postscaler to 1:10 would provide a 20-msec interrupt.

Those same prescaler, postscaler, and PR2 settings would provide a 10-msec interrupt using a 16-MHz clock or a 5-msec interrupt using a 32-MHz clock.

Food for thought. Regards, Mike
 
Last edited:
Mike,

I’m a bit of a noob but what does Fosc stand for? The external crystal? Are you saying that the only way I can get the 20ms interrupt is to use a slower crystal?

You say that an 8MHz crystal will produce a 500ns cycle time. How are you calculating this time based on the MHz of the crystal?


If you set it up to generate the pulse every other interrupt then the interrupt period would be 10-msecs, as you said.

Can't I just set the following data to obtain a 10ms period and then set it so it only sends the pulse every other interrupt? (200-nsecs * prescale 16 * postscale 16 * PR2 195) = 9.9ms * 2(every other interrupt) = 20ms
This would allow me to use my existing 20MHz crystal.

Here is the board I'm using: **broken link removed**
I do not really know what frequency I'm running at. I was hoping you guys could help me determine that. The crystal is running at 20MHz and I was just going along with what Nigel said (200ns cycle time)

Im using the example code found here:
**broken link removed**
Direct link to firmware-> **broken link removed**

Currently Im using this code to oscilate the servo, its very similar to the link above: **broken link removed**

The author of this code has the timer set to: (200-nsecs * prescale 16 * postscale 16 * PR2 256) which comes out to 13.1-msecs. The code also only allows the 1-2ms pulse to be sent every 4 interrupts. How is he getting away with sending a pulse every 52.5ms instead of 20ms?
 
Last edited:
HakBot said:
Mike,

I’m a bit of a noob but what does Fosc stand for? The external crystal?
Fosc = core oscillator frequency.
Are you saying that the only way I can get the 20ms interrupt is to use a slower crystal?
No, a slower Fosc. With this PIC you can divide the the crystal frequency by 5 to provide the 96-MHz PLL with the required 4-MHz input and then divide the 96-MHz PLL output to some other frequency for the core.
You say that an 8MHz crystal will produce a 500ns cycle time. How are you calculating this time based on the MHz of the crystal?
Tosc (oscillator period) is the riciprical of Fosc (1/8000000)=125-nsecs. Tcyc (instruction cycle period) is Tosc*4=500-nsecs.
Can't I just set the following data to obtain a 10ms period and then set it so it only sends the pulse every other interrupt? (200-nsecs * prescale 16 * postscale 16 * PR2 195) = 9.9ms * 2(every other interrupt) = 20ms
This would allow me to use my existing 20MHz crystal.
Yes, this would provide something close to a 20-msec overall period, provided you're really using a 20-MHz core frequency.
Here is the board I'm using: **broken link removed**
I do not really know what frequency I'm running at. I was hoping you guys could help me determine that. The crystal is running at 20MHz and I was just going along with what Nigel said (200ns cycle time)

Im using the example code found here:
**broken link removed**
Direct link to firmware-> **broken link removed**

The author of this code has the timer set to: (200-nsecs * prescale 16 * postscale 16 * PR2 256) which comes out to 13.1-msecs. The code also only allows the 1-2ms pulse to be sent every 4 interrupts. How is he getting away with sending a pulse every 52.5ms instead of 20ms?
Let me check out these references. See you soon.

Regards, Mike
 
I am just a newbie hobbyist, about one project past blinking LEDs, but I might be able to add something to the discussion of how the RC servos work. I assume you are using a standard RC servo such as a Hitec HS300. That servo is reported to center at about 1.5 ms with a frame rate of 50 Hz (20 ms). That condition calculates to a duty cycle of 7.5%. However, I believe the servo position is not determined by the duty cycle, but rather by the actual pulse width. To test this, I used a 555 timer to generate a pulse width of 1.5 ms and triggered the 555 with a variable frequency source to create different “frame rates.” The servo did not move when the frame rate varied from 40 Hz (25 ms) to 400 Hz (2.5 ms; i.e., the duty cycle varied from 6% to 60%) and could be easily controlled by varying its pulse width. At frame rates over 200 Hz, however, the servo would buzz when displaced and returned to center. My current project is to control such a servo with the PWM from a 16F628A operated at 2.4 MHz and with which I should be able to obtain easily a “frame rate” of less than 200 Hz. I'm saving interrupts for my next project.

Best regards, John
 
John,

Thanks for the very interesting info' and welcome to the Forum.

May I ask what kind of resolution you need for that pulse? How many steps between 1.0-msecs and 2.0-msecs are ideal? Or does that depend on the type of servo and/or application?

Good luck on your upcoming '628A servo project.

Kind regards, Mike
 
Mike,
The HS300 rotates about 50 degrees each way with a +/- 0.5 ms change in pulse width. I'm using it just as a latch and should have plenty of resolution at a frame rate of 150 Hz. I won't know for sure until my PICStart Plus upgrade arrives tomorrow from Microchip so I can burn the '628A.
Regards, John
 
Mike,

I'm gettting confused. Can you clear up a few things for me? First what is the difference between the external crystal and the Fosc? Also, how can I determine what my Fosc is on the pic18f4550? I've searched thru the example code in the links I posted and I dont see any bits set to determine it. Thats why I just assumed it was running at 48MHz. The crystal is 20MHz, this I know for sure.

Are you saying that the only way I can get the 20ms interrupt is to use a slower crystal?
No, a slower Fosc. With this PIC you can divide the the crystal frequency by 5 to provide the 96-MHz PLL with the required 4-MHz input and then divide the 96-MHz PLL output to some other frequency for the core.

Isnt the Fosc determined by the speed of the crystal? So wont a slower crystal effect the fosc?
 
HakBot said:
Mike,

I'm gettting confused. Can you clear up a few things for me? First what is the difference between the external crystal and the Fosc? Also, how can I determine what my Fosc is on the pic18f4550? I've searched thru the example code in the links I posted and I dont see any bits set to determine it. Thats why I just assumed it was running at 48MHz. The crystal is 20MHz, this I know for sure.
I've been referring to Fosc as the oscillator frequency available to the core.

I just got back from running some errands so let me take a look at that project and I'll let you know what I find. Hang in there.

Isnt the Fosc determined by the speed of the crystal? So wont a slower crystal effect the fosc?
Normally that's the case. But with the diverse set of oscillator options on the '4550 it seems you can use just about any standard crystal and twiddle options in the oscillator chain to come up with the same frequency (grin) or a different frequency.
 
Whoa, this C18 stuff seems way over my head. Gads, where do you look to find the code that sets the configuration bytes?
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top