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.

60 Hz PWM for RC Servo using postscaler?

Status
Not open for further replies.

ClydeCrashKop

Well-Known Member
Most Helpful Member
I would really like to use the 16F628A hardware PWM to run an RC servo at 60 Hz. My program is busy getting input and can’t be interrupted 60 times per second to send output. I could use a second PIC for output but would rather not. This note is in the Mid-Range MCU Family Reference Manual.

Note: The Timer2 postscaler is not used in the determination of the PWM frequency. The postscaler could be used to have a servo update rate at a different frequency than the PWM output.

Googling found a lot of complaints quoting that note and saying that the postscaler has no effect.

I am still looking but has anyone had success with this or is it misinformation?
 
The Timer 2 postscaler is not used for determining the period when running the CCP module in PWM mode.

One method for overcoming low frequency period limitations is to break up the PWM period into smaller/faster PWM "frames" and use a small bit of interrupt code to set the duty cycle for each "frame". The algorithm and code are relatively simple, have reasonably low processor overhead, and can be used to generate a 0% to 100% duty cycle output on the CCP1 pin for very long periods;

Code:
;******************************************************************
;
;  //  setup 250-usec PWM frames using PR2 = 249 and prescaler
;  //  1 (4 MHz) or 4 (16 MHz) for 1 usec pulse width resolution
;
;  int servo = 1500;            // duty cycle in 1 usec ticks
;  int pulse = 0;               // ISR work variable
;  int frame = 1;               // frame number, 0..80
;
;  void ISR()
;  { PIR1bits.TMR2IF = 0;       // clear Timer 2 interrupt flag
;    if (--frame == 0)          // if end of 20 msec period
;    { frame = 80;              // 80 frames = 20 msec period
;      pulse = servo;           // setup work variable
;    }
;    if(pulse > 250)            // if pulse > 250-usecs
;    { CCPR1L = 250;            // do a 100% duty cycle frame
;      pulse -= 250;            // subtract 250-usecs
;    }
;    else                       // do a variable or 0% frame
;    { CCPR1L = (pulse);        // 0..250
;      pulse = 0;               // remaining frames are %0
;    }
;  }
;
isrproc
;
;  save main program context
;
        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 Timer 2 interrupt flag    |B0
;
;  check for start of new 20 msec servo period
;
        decfsz  frame,F         ; end of 20 msec period?          |B0
        goto    doframe         ; no, branch, else                |B0
        movlw   d'80'           ; reset pwm frame number          |B0
        movwf   frame           ;                                 |B0
        movf    servo,W         ; setup 'pulse' work variable     |B0
        movwf   pulse           ; for new 20 msec servo period    |B0
        movf    servo+1,W       ;                                 |B0
        movwf   pulse+1         ;                                 |B0
;
;  setup CCP1 PWM duty cycle for next 250 usec PWM "frame"
;
doframe
        movlw   d'250'          ; pulse = pulse - 250             |B0
        subwf   pulse,F         ;                                 |B0
        bc      setduty         ;                                 |B0
        movlw   1               ;                                 |B0
        subwf   pulse+1,F       ;                                 |B0
        movlw   d'250'          ; assume 100% duty cycle frame    |B0
        bc      setduty         ; borrow? no, branch, else        |B0
        addwf   pulse,W         ; WREG = last portion of pulse    |B0
        clrf    pulse           ; force 0% duty cycle for the     |B0
        clrf    pulse+1         ; remaining pwm frames            |B0
setduty
        movwf   CCPR1L          ; set duty cycle for next frame   |B0
;
;  restore main program context
;
        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?
 
Another method for generating a servo output uses the CCP module "compare" mode. This method would require just two interrupts per 20 msec cycle for a single Servo channel output and so might be better tolerated by your timing sensitive program.

Regards, Mike
 
I can think of two methods of using the PWM module to control a RC servo. With this I am assuming you have 10 bits of available resolution.

The first has the PWM on all the time and only a small fraction of the period is available to control the pulse width of the servo because 90% is needed to maintain the space between pulses (18-19mS at 50Hz refresh rate). Even if you refresh at 100Hz you still spend 80% on space and 10% on mark regardless of the pulse value. This may be adequate if you only need to control one or two servos in the background and don't want to deal with (more) interrupts.

The other one involves and interrupt just before the PWM period resets. At that time load a (new) value in the PWM duty register which will take care of the pulse itself. This will allow half of the available resolution for the actual value. Then immediately after the PWM begins it's new cycle, clear the register and wait for the next interrupt in another 20mS. Once the cycle is complete the PWM will maintain a low output until a non-zero value is loaded again.
 
Here is some real sloppy code for the 628A that runs a Hitec servo. Note, it uses about 90Hz, not 60 Hz as that was pretty much the limit of the prescaler. The servo code is in the first section. The sections having to do with LED's are for other controls. Bascially, the servo is used as a latch. There are two positons, high and low. You could define whatever positon(s) you want. I have had no problems running analog servos up to about 140 Hz.

Is there an easy way to provide just snippets of code with annotations? I have seen it done, but am not sure how to do it. John
 

Attachments

  • Latch Control 16Ff628Atempo.asm
    5.6 KB · Views: 328
Thanks John
That looks interesting. I have kind of been thinking along those lines. I am glad to hear that I have more tolerance on the frequency.
 
ClydeCrashKop said:
Thanks John
That looks interesting. I have kind of been thinking along those lines. I am glad to hear that I have more tolerance on the frequency.

You have lot's of tolerance on the frequency, it's basically the pulse width that matters, not the repetition rate - the repetition rate is mainly responsible for how fast it responds.
 
jpanhalt said:
Is there an easy way to provide just snippets of code with annotations? I have seen it done, but am not sure how to do it. John

You enclose the code in code tags, that is you put [ code] at the beginning of the section and [ /code] at the end. Note, I had to insert spaces into the tags to stop them working.

Mike.
 
I couldn’t use CCP because timer1 is busy being a very stable frequency counter for a dual tachometer. (2 engines) I will pull out the stops on the servo for continuous rotation. It will have a tiny model boat propeller on it, mounted inside a gauge to indicate synchronization. It will rotate at varying speeds to indicate which engine is slower and by how much. When the prop stops, it is in sync.
You guys got me thinking in the right direction. Thanks a lot.
If you see any way to make the interrupts shorter or faster, please let me know. Any other refinements are welcome.
Code:
;This program uses Timer0 interrupts and prescalers to send RC servo compatible PWM at 55Hz 
;to 57Hz depending on the length of your high pulse. At 4 mhz and 1:64 prescale, timer0 will
;roll over and interrupt at about 61 Hz for the low part of the pulse. The high pulse uses a
; 1:8 prescale which would roll over every 2048us. You want a range from 1000us to 2000us so
;preload timer1 with a value that will let it roll over sooner to get the pulse width you want.
;2000 / 8 =250 counts for the longest pulse. 256-250=6 so if you preload timer1 with 6, it
; will roll over after 250 counts. 1000 / 8 = 125 counts for the shortest pulse. 256-125=131
; so if you  preload timer1 with 131, it will roll over after 125 counts.
; Center is approx. 68 so if you put 68 +/- up to 62 in the variable “Position”, you should
; get a full range of motion or speed. This is low resolution. You only get 125 positions or
;62 speeds left and right but it only interrupts your program about 20us, 120 times per minute.
;Thanks to Nigel Goodwin and his tutorials for delays and initializing Timer0 and interrupts.
;Clyde Crashkop
	LIST	p=16F628A		;tell assembler what chip we are using
	include "P16F628A.inc"		;include the defaults for the chip
	ERRORLEVEL	0,	-302	;suppress bank selection messages
	__config 0x3D18			;sets the configuration settings (oscillator type etc.)

		cblock	0x20		;start of general purpose registers
			count			;used in looping routines
			count1			;used in delay routine
			counta			;used in delay routine
			countb			;used in delay routine
			toggle			;flag to alternate between PWM high & low
			Position		;Center is 68 +/- 62 for extremes
			W_TEMP
			STATUS_TEMP
		endc

		org	0x0000
		goto	Start
; Timer0 interrupt. 
            	ORG     0x0004
            	MOVWF W_TEMP
            	SWAPF STATUS,W
            	MOVWF STATUS_TEMP

            	BCF     INTCON,		T0IF
            	MOVF	toggle,	f	;Move it back to its self for zero flag
            	SKPNZ
            	GOTO	LowOut
            	
				clrf	toggle			;To toggle for next interrupt.
				MOVF	Position,	w	;Get Position for length of high pulse
				MOVWF	TMR0			;Center is 68 +/- 60 for extremes
				bsf 	STATUS,		RP0	;select bank 1
				MOVLW   b'00000010'		;prescaler = 1:8 for high time
		       	MOVWF   OPTION_REG
				bcf 	STATUS,		RP0	;select bank 0
            	BSF		PORTB,	3	;Physically farthest from B6 T1CKI frequency input.
            	SWAPF STATUS_TEMP,W 
            	MOVWF STATUS 
            	SWAPF W_TEMP,F 
            	SWAPF W_TEMP,W 
               	RETFIE
	LowOut
				BSF		toggle,		0	;To toggle for next interrupt.
				bsf 	STATUS,		RP0	;select bank 1
				MOVLW   b'00000101'		;prescaler = 1:64 for low time
		       	MOVWF   OPTION_REG
				bcf 	STATUS,		RP0	;select bank 0
            	BCF		PORTB,	3	;Physically farthest from B6 T1CKI frequency input.
            	SWAPF STATUS_TEMP,W 
            	MOVWF STATUS 
            	SWAPF W_TEMP,F 
            	SWAPF W_TEMP,W             	
            	RETFIE

Start	movlw	0x07
		movwf	CMCON			;turn comparators off (make it like a 16F84)
Initialise
		clrf	count
		clrf	PORTA
		clrf	PORTB
SetPorts
		bsf 	STATUS,		RP0	;select bank 1
		movlw	0x00			;make all pins outputs
		movwf	TRISA
		movlw	0x00			;make all pins outputs
		movwf	TRISB
		MOVLW   b'00000101'		;prescaler = 1:64 for low time
       	MOVWF   OPTION_REG
		bcf 	STATUS,		RP0	;select bank 0
		CLRF	INTCON
        BSF     INTCON    , T0IE	;enable timer interrupts
        clrf    TMR0
        bcf     INTCON, 	T0IF	;start timer
        bsf     INTCON, 	GIE	;start interrupts

Main	;Everything from here on is just to exercise the servo 
		MOVLW	d'68'	;Center
		MOVWF	Position
		CALL	Delay255
		CALL	Delay255
		CALL	Delay255
		CALL	Delay255

		MOVLW	d'6'	;Long pulse
		MOVWF	Position
		CALL	Delay255
		CALL	Delay255
		CALL	Delay255
		CALL	Delay255
		
		MOVLW	d'125'	;Short pulse
		MOVWF	Position
		CALL	Delay255
		CALL	Delay255
		CALL	Delay255
		CALL	Delay255
		
		goto	Main			;loop for ever

;Delay routines

Delay255	movlw	0xff		;delay 255 mS
		goto	d0
Delay100	movlw	d'100'		;delay 100mS
		goto	d0
Delay50		movlw	d'50'		;delay 50mS
		goto	d0
Delay20		movlw	d'20'		;delay 20mS
		goto	d0
Delay5		movlw	0x05		;delay 5.000 ms (4 MHz clock)
d0		movwf	count1
d1		movlw	0xC7			;delay 1mS
		movwf	counta
		movlw	0x01
		movwf	countb
Delay_0
		decfsz	counta, f
		goto	$+2
		decfsz	countb, f
		goto	Delay_0

		decfsz	count1	,f
		goto	d1
		retlw	0x00

;end of Delay routines		

		end
 
That is hard to read so here is asm file.
 

Attachments

  • TMR0 servo.asm
    4.5 KB · Views: 303
ClydeCrashKop said:
I couldn’t use CCP because timer1 is busy being a very stable frequency counter for a dual tachometer. (2 engines) I will pull out the stops on the servo for continuous rotation. It will have a tiny model boat propeller on it, mounted inside a gauge to indicate synchronization. It will rotate at varying speeds to indicate which engine is slower and by how much. When the prop stops, it is in sync.
[/CODE]

It's called a "synchroscope" in full-scale, twin-engine aircraft. Please let us know how it works. John
 
From a post here on Oct 3 2006
My best customer wants an indicator to help manually synchronize the RPMs on his 2 boat engines. He has an airplane with a sync gauge that has a propeller that spins one way or the other and stops when the engines are in sync. He really likes that and I thought I could put it on his boat. I can’t seem to find it or any others online.
The Yanmar diesels put out a hall effect tach signal at about 200 pulses per rev from the flywheel ring gear.
That is the main reason I got started with PICs. While just trying to make a whirly gig, I now have a perfect dual digital tach on a 4 X 40 LCD with the difference in RPM, port or stbd. I almost had it working with a gear reduced motor, a DPDT relay and PWM at about 2 kHz but the output was messing up the tach input. This was just before they took the boats to the Bahamas for 2 months. I did manage make and install a 10 LED bar graph before they left. It serves the purpose for now and I told him the prop version was in progress. I learned a whole lot in the process and there is no way I could charge him for all the time spent so I told him “No charge. Merry Christmas”. Besides, a little Karma goes a long way. Now I am on vacation for 2 months. (hehehe)
 
ClydeCrashKop said:
I couldn’t use CCP because timer1 is busy being a very stable frequency counter for a dual tachometer.
Well, Timer 0 works extremely well as a frequency counter too. Actually, it works better because there's an anomoly when Timer 1 is used as a counter that throws away the first count. Anyway, that would free up Timer 1.

I can't quite figure out what you're doing from your brief description and code but I suspect you could reallocate PIC resources to come up with a simpler cleaner tighter solution than what you have now.

Good luck with your project.

Mike
 
Tell us how you're implementing the Timer 1 frequency counter? How long and how often are you counting the hall effect signal on each motor? How to you switch the T1CKI counter input pin between motors?
 
Last edited:
There is synchroscope and synchrophase as applied to aircraft. The former is to help the pilot ensure the props are at the same speed. The latter is to help get them in phase. It makes a big difference in vibration and sound levels when the props go into phase. Larger transport aircraft, like the DC6 and B29, had phasing for their four engines. Small light twins usually do not.

Anyway, based on what you are trying to do, maybe there is a different approach that might be easier. Starting with a Hall signal from each motor, why not simply compare phase? Say, the position of the falling edge from each. You could also pull an RPM measurement from the same signal, but that would not be what would drive your propellor indicator. John
 
That is what I wanted to do in the first place but couldn’t find enough info. A PLL almost looked like it would work but I’m not an analog guy and it wouldn’t come together for me.
I have a friend that repairs avionics. He helped some. I did find some really trick mechanisms that control RPM by varying the prop pitch instead of throttle.
 
Last edited:
The frequencies range from approx. 1400 Hz at 700 RPM to 8000 Hz at 4000 RPM so if I count pulses for half a second I get and actual RPM number but that takes a 16 bit counter. I started using timer0, incrementing the high byte in an interrupt from Nigel’s tutorials but it fluctuated by up to 15 on every reading. Timer1 working in the background, without interruptions was perfectly steady and all I had to is calibrate the read time with delays. If timer1 will keep counting during the PWM interrupts, it will be perfect and I will just recalibrate.
To switch inputs, I used a 74LS03 open collector NAND gate controlled by RB7 as a data selector as per diagram below. With open collector, I could tie two outputs together and either gate can pull the output low without fighting each other. I will build another one using a 74LS157 data selector from that batch of chips Philba gave me.
Will timer1 keep counting during the PWM interrupts ?
 

Attachments

  • Dual tach input.jpg
    Dual tach input.jpg
    30.6 KB · Views: 335
ClydeCrashKop said:
A PLL almost looked like it would work but I’m not an analog guy and it wouldn’t come together for me.

I was only analog until last year. Maybe that's showing my age. I used the LM1496, which gives a signal proportional to phase (product of the two input voltages). There are possibly many other chips for the same purpose. I have also used the XR-2211 and CD4046.

I did find some really trick mechanisms that control RPM by varying the prop pitch instead of throttle.

That raises an interesting issue. Knowing the props are out of sync/phase is usually not a problem when you have only two props. Keeping them in sync, particularly in rough weather is another issue. I assume you just want the indication, not the control. And by indication, I mean something that would show which prop was turning faster or slower. Right?

As for the code example I gave, in that project, I was faced with a problem of having variable delays that could affect the servo pulse width and position. I considered interrupts, but being new to digital, I wanted to avoid them. I solved the problem by carefully flow charting the logic and adding NOP instructions to make each loop the same regardless of path taken. That may be caveman, but it worked. John
 
Just the same RPM is good for a boat. Keeps the vibration down and sounds better. My problem with phase was that I could see detecting out of phase at the same frequency but not if they were 100 RPM different. It just wouldn’t click in my head.
LOL, I usually try the caveman approach first, then when all else fails, I try reading the instructions.:D
 
Last edited:
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top