The 18F2431 makes it slightly easier to perform servo PWM because it's PCPWM module can go down to 19 htz while maintaining fast OSC rates, unlike other PIC chips HPWM. Here is an example of using the PCPWM to output 3 servo signals. This specific example senses the pulses (up to 5 channels) by using the CCP1 capture pin. To get 5 channels into one pin for PW capture, channels 1, 3 and 5 are input using small signal diodes. Then, since this drops the signal down to under 3 volts, I used two 2n3904 transistors. One to boost up the voltage, and the other one to invert the signal back to normal. (It was all I had lying around). Somehow, you will need to boost your signal after the diodes for the CCP1 capture to sense the incoming pulses.
Note: Because the PCPWM was used, this will not work on any chip outside of the 18F2431 family.
Code:
;This only works on a 18F2431 family device with Power Control PWM (PCPWM)
;By Scale Robotics Inc.
; The input signal should look something like this:-
;
; |<------------------ 1 frame (~20mS) -------------->|
; |<1~2mS>
; ______ ______ ______ ______
; _______| |______| |______| |________________| |____
; gap ch1 ch2 ch3 ch4 ch5 sync gap ch1 etc
define OSC 40
asm
__CONFIG _CONFIG1H, _OSC_HSPLL_1H
__CONFIG _CONFIG2H, _WDTEN_OFF_2H & _WDPS_512_2H
__CONFIG _CONFIG4L, _LVP_OFF_4L
endasm
clear
ADCON0 = %00000000
ADCON1 = %00000000
portb=0
trisb = %11000000
trisc = %00000110
trisa = %00000000
DTCON = %00000101 'dead time for complimentary ouputs
PTCON0 = %00001101 '1:1 postscale, Fosc/4 1:64 prescale, Sincle Shot mode
PTPERL = 255 '
PTPERH = 251
PWMCON0 =%01010000 'PWM[5:0] ouputs enabled
PWMCON1 = 1 'updates enabled, overrides sync w/timebase
PTCON1 = %10000000 'PWM timebase is on, counts up
FLTCONFIG = %00000010 'disable fault A, cycle by cycle mode
CCP1CON = %00000101 'Capture mode; every rising edge
duty1 var word 'width of outgoing pulse1
duty2 var word 'duty values 625 to 1250 = 1 to 2 ms pulse (Center at 625)
duty3 var word
risetime1 var word 'Rise Time for incoming pulse1
risetime2 var word
risetime3 var word
falltime1 var word 'falltime for incoming pulse1
falltime2 var word
falltime3 var word
pulsewidth1 var word 'pulse width for incoming pulse1
pulsewidth2 var word
pulsewidth3 var word
pulsewidth4 var word
pulsewidth5 var word
pulseNumber var byte
pulseNumber = 1 'tells which pulse we are reading
CCP1CON.0 = 1
timerone var word
timerone = 60315 ;
INCLUDE "DT_INTS-18.bas" ; Base Interrupt System
ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler TMR5_INT, _PulseOut, ASM, yes
INT_Handler CCP1_INT, _PulseMeasure, ASM, yes
INT_Handler TMR0_INT, _TimeOut, ASM, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM
T1CON = %00110001 ;Timer1 used by CCP1 capture
T5CON = $01 ;used as pulseout timer
T0CON = %11000111 ; Prescaler = 8, TMR1ON
@ INT_ENABLE TMR5_INT ; Enable Timer 1 Interrupts
@ INT_ENABLE CCP1_INT ; Enable Capture Compare for pulse width measurement
@ INT_ENABLE TMR0_INT ; deadtime (sync gap) indicator for lull between pulses
Main:
pause 10
'do something to these values if you want to filter, center, etc
'Below we are just passing values through
duty1 = pulsewidth1 >>1 'send channel 1 out PCPWM on PortB.1
duty2 = pulsewidth3 >>1 'send channel 3 out PCPWM on PortB.3
duty3 = pulsewidth5 >>1 'send channel 5 out PCPWM on PortB.4
GOTO Main
'---[TMR5_INT - interrupt handler]------------------------------------------
PulseOut: 'set up pulse width values for pulseout and reset single shot bit
TMR5L = timerone.byte0
TMR5H = timerone.byte1
PDC0L = duty1.lowbyte
PDC0H = duty1.highbyte
PDC1L = duty2.lowbyte
PDC1H = duty2.highbyte
PDC2L = duty3.lowbyte
PDC2H = duty3.highbyte
PTCON1.7=1 'resets single shot PTEN bit
@ INT_RETURN
'---[CCP1_INT - interrupt handler]------------------------------------------
PulseMeasure:
if CCP1CON.0 = 1 then ; Check CCP1M0 for rising edge watch
select case pulseNumber
case 1
TMR0L = 0 'reset timeout timer0
TMR0H = 0
risetime1.lowbyte = CCPR1L
risetime1.highbyte = CCPR1H
case 2
TMR0L = 0 'reset timeout timer0
TMR0H = 0
risetime2.lowbyte = CCPR1L
risetime2.highbyte = CCPR1H
case 3
TMR0L = 0 'reset timeout timer0
TMR0H = 0
risetime3.lowbyte = CCPR1L
risetime3.highbyte = CCPR1H
End select
@ BCF CCP1CON, CCP1M0 ; Now capture the trailing edge
Else 'check for falling edge time
@ BSF CCP1CON, CCP1M0 ; Re-set for trailing edge capture
select case pulsenumber
case 1
falltime1.lowbyte = CCPR1L
falltime1.highbyte = CCPR1H
pulsewidth1 = falltime1 - risetime1
case 2
falltime2.lowbyte = CCPR1L
falltime2.highbyte = CCPR1H
pulsewidth2 = risetime2 - falltime1
pulsewidth3 = falltime2 - risetime2
case 3
falltime3.lowbyte = CCPR1L
falltime3.highbyte = CCPR1H
pulsewidth4 = risetime3 - falltime2
pulsewidth5 = falltime3 - risetime3
end select
pulsenumber = pulsenumber + 1 'get ready for next channel
endif
@ INT_RETURN
'---[TMR0_INT - interrupt handler]------------------------------------------
TimeOut:
pulsenumber = 1 'if pause between pulse in exceeds about 7 ms, get ready
'to receive pulse 1 (senses dead time between pulse bursts)
@ INT_RETURN
To see a similar project, but using SPWM, look here: http://www.picbasic.co.uk/forum/showthread.php?t=12657
Bookmarks