PDA

View Full Version : RC Servo decoding/encoding using 12F683



ScaleRobotics
- 11th February 2010, 18:53
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/84-projects/pwm/71-pwm-passthrough.html



; 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

rmteo
- 11th February 2010, 19:18
What is the purpose/function of this?

malc-c
- 11th February 2010, 19:24
I'm a tad confused (easy I know :) )

I read this as using an RC transmitter sending signals to a receiver, you then connect the first five channels from the RX via diodes to a single pin on the PIC which then drives 4 servos driven from the PIC as it's decoded each channel ...

What's the use of the PIC, why not simply connect 4 servos direct to the RC receiver and get the same result ?

ScaleRobotics
- 11th February 2010, 19:35
What is the purpose/function of this?

Good question. Think of it as a platform. Most applications I can dream up for it are RC based.

Say you have a simple minded receiver and transmitter, but you want to do your own aileron/rudder/elevator mixing - go ahead, just write some code. Even do some IR leveling of your plane ... go ahead...

Say your receiver gives some bogus pulses out when it looses your transmitter signal for a second or two - you can filter these out, go to last good signal, or a default setting for that servo.

Say you want to build an autonomous vehicle, but still have the option of controlling it with your transmitter. Set autopilot on/off using channel 5.

Say you want to build an altitude hold circuit. This is a perfect platform. (Ok, maybe not quite perfect yet, but should be close once I sync to ch1)

Since all the pulses are done using interrupts (and no blocking loops like pause, pulsin, pulsout), there is a lot of time in between to do what ever you want it to do.


What's the use of the PIC, why not simply connect 4 servos direct to the RC receiver and get the same result ?

Because then you could not do any of the above.

rmteo
- 11th February 2010, 20:05
Ah, now I see what you are trying to do - like what the multi-rotor guys would want. I did a similar application using a PIC24FJ32GA104 which has 5 independent input capture modules and 5 independent output compare/PWM modules. Most everything done in hardware (except for the intermediate computations) and fully interrupt driven. Vectored interrupts sure came in handy in this situation.

ScaleRobotics
- 11th February 2010, 22:03
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/product_info.php?products_id=9038 UAV autopilot based on a dsPIC30F4011 (ok a "little" faster than a 12F)
http://www.sparkfun.com/commerce/product_info.php?products_id=8785 UAV autopilot based on Arduino chip

rmteo
- 11th February 2010, 22:43
I prefer to do everything in hardware where possible. Hardware is cheap (the same cannot be said for software - the abovementioned PIC24 is <$2) and will outperform anything that can be done in software. For example, on the PIC24FJ the built-in hardware multiplier will do a 17x17 multiply in a single instruction cycle - 62.5nS with a 32MHz clock. Compare this to a PIC12/16 (with a 4MHz clock) which needs about 250uS to do a 16x16 multiply operation - about 4,000 times slower.

Right now I'm working a flybarless controller which needs to take inputs from a receiver, inputs from 3 axis accelerometers, do the necessary math intensive (mostly word) computations, then output pulses to the CCPM servos. The pulses should be output concurrently and this is where the hardware PWM becomes really important.

ScaleRobotics
- 11th February 2010, 23:08
the abovementioned PIC24 is <$2) and will outperform anything that can be done in software. Agreed, that is a very fast chip, but I always get an error programing PIC24F's with PBP code. ;)

The PIC18 devices have a hardware multiplier, so that is ultimately where I am going with this. The pulse width is being sensed with a CCP1 capture, which is hardware. But the 12F needs a tiny bit of assembly to control that hardware. The PIC18F2431 has more hardware that lends itself better (than the 12F and 16F) to that sort of thing.



Right now I'm working a flybarless controller which needs to take inputs from a receiver, inputs from 3 axis accelerometers, do the necessary math intensive (mostly word) computations, then output pulses to the CCPM servos. The pulses should be output concurrently and this is where the hardware PWM becomes really important.

I really don't know what flybarless is vs flybraless, but seriously, that sounds very cool! I would love to see your code for that, and I am sure others would as well.

But with RC working at 50 hertz, how quick do you REALLY need the chip to be? That is a lot of time to do some math, at least for most applications.

rmteo
- 11th February 2010, 23:57
The nice thing about working in hardware is that coding is pretty straightforward - and not terribly exciting to see. It is usually a case of setting up registers to do what you want. Most compilers usually do this pretty efficiently anyway.

Why work with a less capable device when the only advantage may a slightly lower cost (perhaps less than a $1 difference). The higher end devices are far easier to use, have greatly superior performance/features - what's not to like.

BTW, the PIC18 has an 8x8 hardware multiplier (the PIC24's is 17x17). Even with a 40MHz clock, the PIC18 requires 4.0uS to perform a signed 16x16 multiply - the PIC24 with a 32MHz clock is still 64 times faster needing only 62.5nS.

ScaleRobotics
- 12th February 2010, 00:32
rmteo,

I agree with you. The PIC24's are definitely better than PIC18's. I don't hold anything against them. I just enjoy PBP better than my CCS compiler. And I think I can perform the math functions I need, including atan, cos, sin and hypotenuse (using a cordic equation), plus all the multiplication and division I need in the 20ms between RC frames. Once I start performing equations that exceed 20ms, I will certainly have to switch compilers! But maybe by then, MeLabs will support PIC24 devices.

malc-c
- 12th February 2010, 08:30
Good question. Think of it as a platform. Most applications I can dream up for it are RC based.

Say you have a simple minded receiver and transmitter, but you want to do your own aileron/rudder/elevator mixing - go ahead, just write some code. Even do some IR leveling of your plane ... go ahead...

Say your receiver gives some bogus pulses out when it looses your transmitter signal for a second or two - you can filter these out, go to last good signal, or a default setting for that servo.

Say you want to build an autonomous vehicle, but still have the option of controlling it with your transmitter. Set autopilot on/off using channel 5.

Say you want to build an altitude hold circuit. This is a perfect platform. (Ok, maybe not quite perfect yet, but should be close once I sync to ch1)

Since all the pulses are done using interrupts (and no blocking loops like pause, pulsin, pulsout), there is a lot of time in between to do what ever you want it to do.



Because then you could not do any of the above.

Maybe if you had informed us of this in your first post it would of been a bit clearer of what your intentions are. ;)

ScaleRobotics
- 12th February 2010, 10:17
v1.1a Added a means to ensure the sync gap is in the right place, and read proper channels.

http://www.scalerobotics.com/stamps/84-projects/pwm/71-pwm-passthrough.html (http://www.scalerobotics.com/PWMpassthrough.html)

ScaleRobotics
- 20th February 2010, 18:55
Here is the schematic and picture of the project.

http://www.picbasic.co.uk/forum/attachment.php?attachmentid=4031&stc=1&d=1266688036

http://www.picbasic.co.uk/forum/attachment.php?attachmentid=4032&stc=1&d=1266688027

http://www.picbasic.co.uk/forum/attachment.php?attachmentid=4033&stc=1&d=1266690797

Above are the three input pulses in red, and servo4 output in yellow

Project page: http://www.scalerobotics.com/stamps/84-projects/pwm/71-pwm-passthrough.html

Squibcakes
- 14th September 2010, 01:49
Right now I'm working a flybarless controller which needs to take inputs from a receiver, inputs from 3 axis accelerometers, do the necessary math intensive (mostly word) computations, then output pulses to the CCPM servos. The pulses should be output concurrently and this is where the hardware PWM becomes really important.

Ambitious. How is it coming along?

I have a 4 blade scaley with no stabilisation and wouldn't mind adding a gyro on the pitch axis... but this would require some tricky eccpm mixing similar to what you describe above....