RC Servo decoding/encoding using 12F683
This code decodes 5 channels from an RC receiver, and ouputs 4 servo channels on GPIO pins 0,1,4 and 5. It uses hardware capture on the CCP1 pin, and Darrel Taylor interrupts to measure the pulse widths, and also uses DT_INTS to build the pulse widths out. The Main: is where you could perform more math on the pulse widths before they are pulsed out.
For getting the 5 channels in, the signal wire on channels 1, 3 and 5 must be connected to diodes (I used 1n914's) and the diodes must all be connected to the CCP1 pin. Channels 2 and 4 are measured using end and start of channels 1, 3 and 5.
Currently 313 words long.
To do list:
1. code does not always start with RC Ch1, have an idea for this, but need to add it
2. code does not like more than 100% travel, like many transmitters can be set to. Pulse out is wrong when it exceeds limits.
3. need to add normal and reverse - easy just need to add
4. filtering - outputs are very solid, but this could make a dumb receiver into a berg
or FMA Co-pilot like receiver (ignore pulse measurement if it is way off previous, etc...
I will put updates here as I make them: http://www.scalerobotics.com/stamps/...ssthrough.html
Code:
; Walter Dunckel [email protected]
; http://www.scalerobotics.com/PWMpassthrough.html
@ __config _INTRC_OSC_NOCLKOUT & _WDT_OFF & _MCLRE_ON & _CP_OFF
DEFINE OSC 8
INCLUDE "DT_INTS-14.bas";interrupt routines
INTCON = %10101000 '
OSCCON = %01110000 'set for 8mhz internal
CMCON0 = 7 'TURN COMPARITORS OFF
ANSEL = %00000000 'Set A/D OFF
ADCON0 = %00000000 'Analog converter OFF
TRISIO = %000100 'Set GSIO 4 and 2 to INPUT, others to OUTPUT
OPTION_REG = %11000010
T1CON = %00110001
T2CON = %01001110
CCP1CON = %00000101
Servo1 VAR GPIO.1
Servo2 var GPIO.0
Servo3 var GPIO.4
Servo4 var GPIO.5
GPIO = 0
TMR0 = 0
pulsebit var byte bank0
bittest VAR pulsebit.0 'tells us if first/2nd cycle of pulseout interrupt
pulsebit = 0
pulsebit.1 = 1 ' pulsebit.1=pulse1 measurement, .2 =2 and .3 =3
pulsebit.4 = 1 ' pulsebit.4 through 7 is channel output counter
rise1 var word bank0
rise1_l var rise1.byte0
rise1_h var rise1.byte1
rise2 var word bank0
rise2_l var rise2.byte0
rise2_h var rise2.byte1
rise3 var word bank0
rise3_l var rise3.byte0
rise3_h var rise3.byte1
fall1 var word bank0
fall1_l var fall1.byte0
fall1_h var fall1.byte1
fall2 var word bank0
fall2_l var fall2.byte0
fall2_h var fall2.byte1
fall3 var word bank0
fall3_l var fall3.byte0
fall3_h var fall3.byte1
RC1 var byte bank0
RC2 VAR byte bank0
RC3 var byte bank0
RC4 var byte bank0
RC5 var byte bank0
adj var byte bank1 ;adjustment centers values for pulse out byte
adj = 484
ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag
INT_Handler CCP1_INT, PWMeasure, ASM, yes
INT_Handler TMR0_INT, PulseOut, ASM, yes
endm
INT_CREATE
INT_ENABLE TMR0_INT
INT_ENABLE CCP1_INT
ENDASM
Main:
RC1 = 255 - (fall1 - rise1 - adj) ;RC channel 1
RC2 = 255 - (rise2 - fall1 - adj)
RC3 = 255 - (fall2 - rise2 - adj)
RC4 = 255 - (rise3 - fall2 - adj)
RC5 = 255 - (fall3 - rise3 - adj)
GOTO Main
'---[TMR0_INT - interrupt handler]---
ASM
PulseOut
pulse1
btfss _pulsebit,4
goto pulse2
btfsc _bittest ;test are we at end of 1ms, or end of 2nd half
goto endpulse1 ;if high skip 7 lines
bcf INTCON,5 ;stop timer
movf _RC1,w ;load TMR0 with value of pulse variable
movwf TMR0
bsf _bittest ;set bittest, indicating we are on second half
bsf INTCON,5 ;start timer again for second interrupt
goto exitpulse ;skip to return from interrupt
endpulse1
bcf INTCON,5 ;stop timer
bcf _Servo1 ;bring ServoOut pin low for end of pulse width
bcf _bittest
movlw d'17' ;set timer0 for next 1ms time base
movwf TMR0
bcf _pulsebit,4 ;done with ch1
bsf _pulsebit,5 ;set for ch2
bsf INTCON,5 ;enable timer0 interrupt
bsf _Servo2 ;set Servo2 pin high (begin pulse width)
goto exitpulse
pulse2
btfss _pulsebit,5
goto pulse3
btfsc _bittest
goto endpulse2
bcf INTCON,5
movf _RC2,w
movwf TMR0
bsf _bittest
bsf INTCON,5
goto exitpulse
endpulse2
bcf INTCON,5
bcf _Servo2
bcf _bittest
movlw d'17'
movwf TMR0
bcf _pulsebit,5
bsf _pulsebit,6
bsf INTCON,5
bsf _Servo3
goto exitpulse
pulse3
btfss _pulsebit,6
goto pulse4
btfsc _bittest
goto endpulse3
bcf INTCON,5
movf _RC3,w
movwf TMR0
bsf _bittest
bsf INTCON,5
goto exitpulse
endpulse3
bcf INTCON,5
bcf _Servo3
bcf _bittest
movlw d'17'
movwf TMR0
bcf _pulsebit,6
bsf _pulsebit,7
bsf INTCON,5
bsf _Servo4
goto exitpulse
pulse4
btfsc _bittest
goto endpulse4
bcf INTCON,5
movf _RC4,w
movwf TMR0
bsf _bittest
bsf INTCON,5
goto exitpulse
endpulse4
bcf INTCON,5
bcf _Servo4
bcf _bittest
movlw d'17'
movwf TMR0
bcf _pulsebit,7
bsf _pulsebit,4
goto exitpulse
exitpulse
INT_RETURN
ENDASM
'---[CCP1_INT - Pulse Capture interrupt handler]--
ASM
PWMeasure
BTFSS CCP1CON, CCP1M0 ; Check for falling edge watch
GOTO FALL_EDGE ; Go pick up the falling edge
btfss _pulsebit,1 ; Is it time to capture leading edge of pulse1
goto $+6 ; No, skip 6 lines, try next
MOVF CCPR1L,W ; else store leading edge of pulse1
MOVWF _rise1_l ; into word rise1
MOVF CCPR1H,W
MOVWF _rise1_h
goto nextone ; pulse1 rise values done
btfss _pulsebit,2 ; Is it time to capture leading edge of pulse2
goto $+6 ; No, try next
MOVF CCPR1L,W ; else store leading edge of pulse2
MOVWF _rise2_l ; into word rise2
MOVF CCPR1H,W
MOVWF _rise2_h
goto nextone ; pulse2 rise values done
btfss _pulsebit,3 ; Is it time to capture leading edge of pulse3
goto $+5 ; Shouldn't really get here, but if we do
MOVF CCPR1L,W ; else store leading edge of pulse3
MOVWF _rise3_l ; into word rise3
MOVF CCPR1H,W
MOVWF _rise3_h ; pulse3 rise values done
nextone
BCF CCP1CON, CCP1M0 ; Now set capture for trailing edge
GOTO nextone2 ; Exit the interrupt service routine
FALL_EDGE:
BSF CCP1CON, CCP1M0 ; Re-set for trailing edge capture
btfss _pulsebit,1 ; Is it time to capture trailing edge of pulse1
goto $+8 ; No, skip 8 lines and try next
MOVF CCPR1L,W ; else store trailing edge of pulse1
MOVWF _fall1_l ; into word fall1
MOVF CCPR1H,W
MOVWF _fall1_h
bcf _pulsebit,1 ; got values for pulse1,now set for pulse2
bsf _pulsebit,2
goto nextone2 ; Done with pulse1
btfss _pulsebit,2 ; Is it time to capture trailing edge of pulse2
goto $+8 ; No, skip 8 lines to next test
MOVF CCPR1L,W ; else store store trailing edge of pulse2
MOVWF _fall2_l ; into word fall2
MOVF CCPR1H,W
MOVWF _fall2_h
bcf _pulsebit,2 ; got values for pulse2,now set for pulse3
bsf _pulsebit,3
goto nextone2 ; Done with pulse2
btfss _pulsebit,3 ; Is it time to capture trailing edge of pulse3
goto nextone2 ; No, end trailing edge check
MOVF CCPR1L,W ; else store trailing edge of pulse3
MOVWF _fall3_l ; into word fall3
MOVF CCPR1H,W
MOVWF _fall3_h
bcf _pulsebit,3 ; got values for pulse3,now set for pulse1
bsf _pulsebit,1
;**Setup pulseout for Servo1 (at end of capture start pulsing out)
bcf INTCON,2 ;clear timer flag if set
movlw d'17' ;set timer0 first 1ms time base
movwf TMR0
bsf INTCON,5 ;enable timer0 interrupt
bsf _Servo1 ;set Servo1 pin high (begin pulse width)
nextone2
INT_RETURN
ENDASM
Where to go from here .... imagine, imagine, imagine...
Quote:
Originally Posted by
rmteo
like what the multi-rotor guys would want.
Yes! Add multi rotor motor conrol to the list, (as well as single rotor). Yes, for the last three links, you might have to upgrade up to a PIC16, or maybe even a PIC18. But I thought I would get an example out using one of the smallest chips supported by PBP to get peoples imaginations going. If the 12f683 can breeze through this, think what a 40mhz chip ( and a little of your imagination and a little of your code) can do. Something very similar was done 8 years ago, see link two. He added a lot of neat features. Too bad I didn't see it 8 years ago, I could have used that......
Only, with this code, you can put your own PBP code into it.
Here are a couple examples of things which perform servo encoding and decoding, with a "little" math in between, ok, and in some cases a "little" extra hardware. There is a lot more than this out there though....
http://www.servocity.com/html/servo_mixer.html Mixer for RC
http://homepages.paradise.net.nz/bhabbott/decoder.html Giving smarts to a dumb receiver
http://rcpilot.sourceforge.net/modules/rcap/index.php A rudder controlling autopilot using a PIC16F876
http://www.u-nav.com/circuitboards/alt3.html An altitude hold device
http://www.fmadirect.com/products.htm?cat=75&nid=6 A very smart receiver stablilizing device .. and more.
http://www.sparkfun.com/commerce/pro...oducts_id=9038 UAV autopilot based on a dsPIC30F4011 (ok a "little" faster than a 12F)
http://www.sparkfun.com/commerce/pro...oducts_id=8785 UAV autopilot based on Arduino chip