;******************************************************************
;
; // 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?
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.
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
;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
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]
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.ClydeCrashKop said:I couldn’t use CCP because timer1 is busy being a very stable frequency counter for a dual tachometer.
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 did find some really trick mechanisms that control RPM by varying the prop pitch instead of throttle.
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?