# Junebug Servo C18 code.

Status
Not open for further replies.

#### Pommie

##### Well-Known Member
On another thread someone asked about C18 servo code and I wrote a simple version. It occurred to me that it wouldn't be too difficult to make this work on the Junebug and so here it is. This code reads the potentiometer on AN1 (VR1) and sets the position of a servo on RB3 to match it.

Switches 1,2,3 & 8 on.
Code:
#include <p18f1320.h>
#pragma config WDT = OFF, LVP = OFF, OSC = INTIO2
#define ServoPin LATBbits.LATB3
#define ServoTris TRISBbits.TRISB3

void main(void){
int ServoPos;
OSCCON=0x70;                //Osc=8MHz
ADCON0=0b00000101;          //A2D on and select AN1
ServoTris=0;                //make servo pin output
ServoPin=0;                 //Servo output off
CCP1CON=0b00001011;         //Special event trigger
T1CON=0b10010001;           //Timer 1 on with Pre=2
ServoPos=1500;              //set servo to mid position
CCPR1=ServoPos;             //set CCP initial value
while(1){
while(!PIR1bits.CCP1IF);    //wait for CCP interrupt bit
ServoPin=0;                 //end pulse
CCPR1=20000-ServoPos;       //Off time = 20mS - Servo Time
PIR1bits.CCP1IF=0;          //clear int flag
while(ADCON0bits.GO);       //Wait for it to complete
ServoPos=ADRES+1000;        //Pos will be 1mS to 2.023mS
while(!PIR1bits.CCP1IF);    //wait for int flag
ServoPin=1;                 //start pulse
CCPR1=ServoPos;             //Servo time in uS
PIR1bits.CCP1IF=0;          //clear int flag
}
}

Have fun.

Mike.
Project file attached.

Last edited:

#### blueroomelectronics

##### Well-Known Member
Your code looks great, I'm starting to be able to read and understand C18.

I've noticed many C18 programs use
#include <p18Cxxxx.h>

Last edited by a moderator:

#### Nigel Goodwin

##### Super Moderator
blueroomelectronics said:
I've noticed many C18 programs use
#include <p18Cxxxx.h>

I don't do C, but assembler programs use an include file for the same reason.

#### August Treubig

##### Member
What are C header files for?

The inclusion of 18cxxxx is done to get the proper definitions for your target pic (given that it is an 18).

This is the first few lines of 18cxxxx.h

Code:
#ifndef _P18CXXX_H
#define _P18CXXX_H

#if defined(__18C242)
#include <p18c242.h>
#elif defined(__18C252)
#include <p18c252.h>
#elif defined(__18C442)
#include <p18c442.h>
#elif defined(__18C452)
#include <p18c452.h>

So, by picking your target as say and 18F1320, then p18f1320.h will get included. Why don't we just include p18f1320.h directly.
Well suppose you change targets to "say" a 18F4620, then you have to edit the include line. This is just the way it is done for proper coding.

So what is in p18f1320.h

It will hold the definitions for all the memory locations (TRISA, LATA, etc) that you will be using in the program. With the header files, the compiler doesn't have to have "hard coded" things built in. Microchip can build a new pic with things in slightly different places and they just create a new header file and the compiler can find where they are.

I highly recommend that you go view the header files. Mine are in c:\MCC18\h

Last edited by a moderator:

#### Odin

##### Member
I had some trouble with my MPlab when I tested the code. The project wouldn't build due to some linker script error. Gonna try on another laptop. I've just built a servo tester with a 16F684, and asm code from a Myke Predko book. Not much more than a pot and the pic. I'm in the process of building a R/C helicopter, and needed something to center the servoes, in the absence of a radio system. A switch to make the circuit output exactly 1,5 ms for center position would be nice. A two position switch, to choose between center lock and normal operation. Would also be good if the pic outputs 1,5 ms until the pot reaches center position. That would avoid wrecking the servo gears if the switch is moved when the pot is at either end. (if the servo stalls because of mechanical limitis in the model)

Another use is for sending signals to an electronic speed controller (ESC). I've done that with an ESC for a brushless motor. Most ESCs woun't arm before you send them a zero throttle signal, 1,0 ms. Wouldn't harm if the servo tester just sent 1,0 ms until the pot is at zero throttle after boot up. And maybe a blinking LED if the pot is at > zero throttle.
My sport R/C helicopter turned into a attack helicopter the other day. Spinning up at full speed on top of a table inside my garage. Falling down, chopping a big chunk of the table plate, while I was running for my life...

Last edited by a moderator:

#### blueroomelectronics

##### Well-Known Member
Watch out with powering the Servo with your USB port. Using a powered hub will help as they can deliver the current a servo motor will need.

#### Mike - K8LH

##### Well-Known Member
Mike,

May I suggest an interrupt version of that code for newcomers to study? The ISR vector setup isn't quite as intuitive as it is in some other languages.

Regards...

#### Pommie

##### Well-Known Member
Mike said:
Mike,

May I suggest an interrupt version of that code for newcomers to study? The ISR vector setup isn't quite as intuitive as it is in some other languages.

Regards...

Might have a go at that tomorrow. Good to see you back, been anywhere nice?

Mike.

#### hosh

##### New Member
Pommie,

Thanks for the code. I have ordered a junebug and one of my future projects will need this exact code.

Corse, I still need to go through the tuts and learn C! But it still lets me see actual code and I tend to learn better from examples that I can dissect.

#### Pommie

##### Well-Known Member
Odin said:
I'm in the process of building a R/C helicopter, and needed something to center the servoes, in the absence of a radio system. A switch to make the circuit output exactly 1,5 ms for center position would be nice. A two position switch, to choose between center lock and normal operation. Would also be good if the pic outputs 1,5 ms until the pot reaches center position. That would avoid wrecking the servo gears if the switch is moved when the pot is at either end. (if the servo stalls because of mechanical limitis in the model)

Here is a version that will go fully left if button 1 pressed, center if button 2 pressed and fully right if button 3 pressed. If you press all 3 buttons together it will take up the position defined by the pot.

I also changed it to use interrupts because, as Mike said, it isn't as intuitive as one would imagine.

Mike.
Code:
#include <p18f1320.h>
#pragma config WDT = OFF, LVP = OFF, OSC = INTIO2

#define ServoPin LATBbits.LATB3
#define ServoTris TRISBbits.TRISB3

void ccp1_isr();

volatile int ServoPos;          //used to hold the servo position

#pragma code low_vector=0x18    //setup the ISR vector
void low_interrupt (){
}
#pragma code

#pragma interruptlow ccp1_isr   //the ISR
void ccp1_isr(){
if(ServoPin==1){            //will be 1 if we are at end of pulse
ServoPin=0;             //turn off servo output
CCPR1=20000-ServoPos;   //Off time = 20mS - Servo Time
}
else{
ServoPin=1;             //turn on servo output
CCPR1=ServoPos;         //On time
}
PIR1bits.CCP1IF=0;          //clear int flag
}
#pragma code

void main(){
OSCCON=0x70;                //Osc=8MHz
ADCON0=0b00000101;          //A2D on and select AN1
ServoTris=0;                //make bit 0 output
ServoPin=0;                 //Servo output off
CCP1CON=0b00001011;         //Special event trigger
T1CON=0b10010001;           //Timer 1 on with Pre=2
ServoPos=1500;              //set servo to mid position
CCPR1=ServoPos;             //set CCP initial value
PIE1bits.CCP1IE=1;          //enable CCP1 interrupt
INTCONbits.PEIE=1;          //enable peripheral interrupts
INTCONbits.GIE=1;           //enable glogal interrupts
INTCON2bits.RBPU=0;         //enable port b week pullups
while(1){                   //loop forever
static char Mode=0;             //0 = use ADC, 1=fixed pos
static char Keys;               //holds previous key values
char OldKeys,Edges;             //local variables
while(!ServoPin);           //Wait for start of servo pulse
while(ServoPin);            //wait for end - makes for good debounce
OldKeys=Keys;               //make a copy of keys
Keys=PORTB&0b00100101;      //get switch state
Keys^=0b00100101;           //make pressed keys = 1
if(Keys==0b00100101)        //all 3 pressed?
Mode=0;                 //yes, set to use ADC input
Edges=(Keys^OldKeys);       //keep only keys that have changed
Edges&=Keys;                //keep only new key presses - not key releases
if(Mode==0){                //are we using the pot?
while(ADCON0bits.GO);   //Wait for it to complete
ServoPos=ADRES+1000;    //Pos will be 1mS to 2.023mS
}
if(Edges!=0)                //any key pressed
Mode=1;                 //switch to fixed mode
if(Edges==0b00000001)       //Key 1 pressed
ServoPos=1000;          //set servo fully left
if(Edges==0b00000100)       //key 2?
ServoPos=1500;          //set center
if(Edges==0b00100000)       //key 3?
ServoPos=2000;          //set fully right
}
}

Last edited:

#### blueroomelectronics

##### Well-Known Member
Thanks Pommie, I'm starting to C the light.

Now I've got to learn what pragma do, I originally thought it was for CONFIG now it also seems to be like ORG

#### Mike - K8LH

##### Well-Known Member
Very nice Mike (Pommie). Again, excellent comments for newcomer and veteran alike.

The 18F1320 Data Sheet seems to indicate that the CCP/ECCP module "special event trigger" mode automatically starts an ADC acquisition and conversion by setting the ADCON0bits.GO bit for you if the ADC module is turned on. Does that mean you could eliminate setting the ADCON0bits.GO bit? Would that require moving the ADC conversion into the ISR (to avoid timing problems)? Would there be any advantage doing it that way (in the ISR)?

Code:
void ccp1_isr()
{ if(ServoPin = !ServoPin)    // if toggled ServoPin = 1
CCPR1 = ServoPos;         // setup "on" time match value
else                        // else
CCPR1 = 20000 - ServoPos  // setup "off" time match value
PIR1bits.CCP1IF=0;          // clear CCP1 interrupt flag bit
}

Last edited:

#### Pommie

##### Well-Known Member
I hadn't realised that the ADC would automatically be triggered. On the 16 series this only happens on CCP2 (when 2 CCP modules are available) and I had assumed it would be the same for the 18 series.

I just commented out the setting of the Go bit and it still works. The "special event trigger" does start the conversion.

Mike.

#### blueroomelectronics

##### Well-Known Member
The 18 series also supports an automatic acquisition delay. Such nice PICs.

#### Pommie

##### Well-Known Member
I know, I set it to 16 Tad.

Mike.

#### futz

##### Active Member
Is there really that few people experimenting with the Junebug that nobody found this post interesting?
Work has been hectic lately and I haven't had much time to play. Saw the post, but couldn't do anything with it till today.

Here's an asm port of Pommie's first posted program:
Code:
	list	p=18F1320
include	<p18F1320.inc>
CONFIG	OSC=INTIO2,WDT=OFF,MCLRE=ON,LVP=OFF

cblock	0x00
ServoPosH,ServoPosL,src1,src2,dst1,dst2
endc

org	0x0000
init	movlw	0x70
movwf	OSCCON			;Osc=8MHz
movlw	b'00000101'		;A2D on and select AN1
movlw	b'01111101'		;A1 = analog
movlw	b'10110101'		;Right justify - Fosc/16
bcf	TRISB,3			;make servo pin output
bcf	LATB,3			;Servo output off
movlw	b'00001011'		;Special event trigger
movwf	CCP1CON
movlw	b'10010001'		;Timer 1 on with Pre=2
movwf	T1CON
movlw	0x05			;set servo to mid position
movwf	ServoPosH
movwf	CCPR1H			;and set CCP initial value
movlw	0xdc
movwf	ServoPosL
movwf	CCPR1L

main	btfss	PIR1,CCP1IF		;wait for CCP interrupt bit
goto	main
bcf	LATB,3			;end pulse
movlw	0x4e			;Off time = 20ms - Servo Time
movwf	src1
movlw	0x20
movwf	src2
movff	ServoPosH,dst1
movff	ServoPosL,dst2
call	sub16
movff	dst1,CCPR1H
movff	dst2,CCPR1L
bcf	PIR1,CCP1IF		;clear int flag
conv	btfsc	ADCON0,DONE		;Wait for it to complete
goto	conv
movff	ADRESH,src1		;Pos will be 1ms to 2.023ms
movlw	0x03
movwf	dst1
movlw	0xe8
movwf	dst2
movff	dst1,ServoPosH
movff	dst2,ServoPosL
wait	btfss	PIR1,CCP1IF		;wait for int flag
goto	wait
bsf	LATB,3			;start pulse
movff	ServoPosH,CCPR1H	;Servo time in uS
movff	ServoPosL,CCPR1L
bcf	PIR1,CCP1IF		;clear int flag
goto	main

sub16	movf	src2,W
subwf	dst2,F
movf	src1,W
btfss	STATUS,C
incf	src1,W
subwf	dst1,F
return

movf	src1,W
btfsc	STATUS,C
incf	src1,W
return

end

Last edited:

#### Pommie

##### Well-Known Member
Nicely done Futz. Are you going to do the interrupt version as well?

One little suggestion to make it more readable. Use word variables with Lo and Hi on the end instead of 1 & 2, use the . with decimal numbers and make use of the low and high directives.
Code:
;so this
movlw	0x03
movwf	dst1
movlw	0xe8
movwf	dst2
;becomes
movlw	low(.1000)
movwf	dstLo
movlw	high(.1000)
movwf	dstHi
call	add16

Mike.

#### futz

##### Active Member
Pommie said:
Nicely done Futz. Are you going to do the interrupt version as well?
The port is done, but the buttons aren't working yet. I have to go through it (maybe tomorrow) and see what I did wrong. Most likely I got one of the bit tests backward and it's branching the wrong way. The potentiometer/servo part works fine though.

One little suggestion to make it more readable. Use word variables with Lo and Hi on the end instead of 1 & 2,
Those 1 & 2 things came from Chuck McMannis, the guy I stole those 16-bit add/subtract code snips from. I didn't change a thing. I prefer to use H and L suffixes. I'll change that in the second port.

use the . with decimal numbers and make use of the low and high directives.
Mike.
I used a decimal number? If so, I sure didn't mean to. I usually think in hex.
EDIT: Ah, I see what you're talking about.

Good stuff. I'll put those suggestions into future programs. Thanks!

Last edited:

#### futz

##### Active Member
Pommie said:
Are you going to do the interrupt version as well?
Finally found the problem. Here's the asm port of Pommie's second program:
Code:
	list	p=18F1320
include	<p18F1320.inc>
CONFIG	OSC=INTIO2,WDT=OFF,MCLRE=ON,LVP=OFF

cblock	0x00
Mode,Keys,OldKeys,Edges,ServoPosH,ServoPosL
srcH,srcL,dstH,dstL
endc

org	0x00
goto	init

org	0x18
isr	btfss	PORTB,3			;the ISR
goto	else1
bcf	LATB,3			;turn off servo output
movlw	high(.20000)		;Off time = 20ms - Servo Time
movwf	srcH
movlw	low(.20000)
movwf	srcL
movff	ServoPosH,dstH
movff	ServoPosL,dstL
call	sub16
movff	dstH,CCPR1H
movff	dstL,CCPR1L
goto	isr_done
else1	bsf	LATB,3			;turn on servo output
movff	ServoPosH,CCPR1H	;On time
movff	ServoPosL,CCPR1L
isr_done
bcf	PIR1,CCP1IF		;clear int flag
retfie	FAST

init	movlw	0x70
movwf	OSCCON			;Osc=8MHz
movlw	b'00000101'		;A2D on and select AN1
movlw	b'01111101'		;A1 = analog
movlw	b'10110101'		;Right justify - Fosc/16
bcf	TRISB,3			;make servo pin output
bcf	LATB,3			;Servo output off
movlw	b'00001011'		;Special event trigger
movwf	CCP1CON
movlw	b'10010001'		;Timer 1 on with Pre=2
movwf	T1CON
movlw	high(.1500)		;set servo to mid position
movwf	ServoPosH
movwf	CCPR1H			;and set CCP initial value
movlw	low(.1500)
movwf	ServoPosL
movwf	CCPR1L
bsf	PIE1,CCP1IE		;enable CCP1 interrupt
bsf	INTCON,PEIE		;enable peripheral interrupts
bsf	INTCON,GIE		;enable global interrupts
bcf	INTCON2,RBPU		;enable PORTB weak pullups

main	btfss	PORTB,3			;Wait for start of servo pulse
goto	main
wait2	btfsc	PORTB,3			;wait for end - makes for good debounce
goto	wait2
movff	Keys,OldKeys		;make a copy of keys
movf	PORTB,W			;get switch state
andlw	b'00100101'
movwf	Keys
movlw	b'00100101'		;make pressed keys = 1
xorwf	Keys,F
movlw	b'00100101'		;all 3 pressed?
cpfseq	Keys
goto	not3
clrf	Mode			;yes, set to use ADC input
not3	movf	Keys,W			;keep only keys that have changed
xorwf	OldKeys,W
movwf	Edges
andwf	Keys,W			;keep only new key presses - not key releases
movwf	Edges
tstfsz	Mode			;are we using the pot?
goto	nomode
waitcv	btfsc	ADCON0,DONE		;wait for it to complete
goto	waitcv
movff	ADRESH,srcH		;Pos will be 1ms to 2.023ms
movlw	high(.1000)
movwf	dstH
movlw	low(.1000)
movwf	dstL
movff	dstH,ServoPosH
movff	dstL,ServoPosL
nomode	tstfsz	Edges			;any key pressed
goto	mode1			;go switch to fixed mode
goto	mode0
mode1	movlw	1			;switch to fixed mode
movwf	Mode
mode0	movlw	1
subwf	Edges,W			;key 1 pressed
btfss	STATUS,Z
goto	key2
movlw	high(.1000)		;set servo fully left
movwf	ServoPosH
movlw	low(.1000)
movwf	ServoPosL
goto	again
key2	movlw	0x04			;key 2?
subwf	Edges,W
btfss	STATUS,Z
goto	key3
movlw	high(.1500)		;set center
movwf	ServoPosH
movlw	low(.1500)
movwf	ServoPosL
goto	again
key3	movlw	0x20			;key 3?
subwf	Edges,W
btfss	STATUS,Z
goto	again
movlw	high(.2000)		;set fully right
movwf	ServoPosH
movlw	low(.2000)
movwf	ServoPosL
again	goto	main

sub16	movf	srcL,W
subwf	dstL,F
movf	srcH,W
btfss	STATUS,C
incf	srcH,W
subwf	dstH,F
return

movf	srcH,W
btfsc	STATUS,C
incf	srcH,W
return

end

and here's the first one again, modified with the suggestions from a few posts ago:
Code:
	list	p=18F1320
include	<p18F1320.inc>
CONFIG	OSC=INTIO2,WDT=OFF,MCLRE=ON,LVP=OFF

cblock	0x00
ServoPosH,ServoPosL,srcH,srcL,dstH,dstL
endc

org		0x0000
init	movlw	0x70
movwf	OSCCON			;Osc=8MHz
movlw	b'00000101'		;A2D on and select AN1
movlw	b'01111101'		;A1 = analog
movlw	b'10110101'		;Right justify - Fosc/16
bcf	TRISB,3			;make servo pin output
bcf	LATB,3			;Servo output off
movlw	b'00001011'		;Special event trigger
movwf	CCP1CON
movlw	b'10010001'		;Timer 1 on with Pre=2
movwf	T1CON
movlw	high(.1500)		;set servo to mid position
movwf	ServoPosH
movwf	CCPR1H			;and set CCP initial value
movlw	low(.1500)
movwf	ServoPosL
movwf	CCPR1L

main	btfss	PIR1,CCP1IF		;wait for CCP interrupt bit
goto	main
bcf	LATB,3			;end pulse
movlw	high(.20000)		;Off time = 20ms - Servo Time
movwf	srcH
movlw	low(.20000)
movwf	srcL
movff	ServoPosH,dstH
movff	ServoPosL,dstL
call	sub16
movff	dstH,CCPR1H
movff	dstL,CCPR1L
bcf	PIR1,CCP1IF		;clear int flag
conv	btfsc	ADCON0,DONE		;Wait for it to complete
goto	conv
movff	ADRESH,srcH		;Pos will be 1ms to 2.023ms
movlw	high(.1000)
movwf	dstH
movlw	low(.1000)
movwf	dstL
movff	dstH,ServoPosH
movff	dstL,ServoPosL
wait	btfss	PIR1,CCP1IF		;wait for int flag
goto	wait
bsf	LATB,3			;start pulse
movff	ServoPosH,CCPR1H	;Servo time in uS
movff	ServoPosL,CCPR1L
bcf	PIR1,CCP1IF		;clear int flag
goto	main

sub16	movf	srcL,W
subwf	dstL,F
movf	srcH,W
btfss	STATUS,C
incf	srcH,W
subwf	dstH,F
return

movf	srcH,W
btfsc	STATUS,C
incf	srcH,W
return

end

Last edited:

#### Odin

##### Member
blueroomelectronics said:
Watch out with powering the Servo with your USB port.

Found out the hard way... No damage though. I'm using a separate 5 V powersupply for the servo now.

Status
Not open for further replies.

Replies
4
Views
2K
Replies
15
Views
3K
Replies
0
Views
1K
Replies
4
Views
1K
Replies
141
Views
16K