I've obtained a PICDEM 2 Plus demo board. It provides some standard hardware interfaced with a PIC18F542 and an in circuit programmer and debugger. The hardware includes an lcd screen, an I2C thermometer, a variable resistor wired to an analog-to-digital converter, a serial port and some buttons. There is an on board 4Mhz crystal for clocking the processor and a voltage regulator for a clean power supply.
This board saves me trying to understand the hardware and the software at the same time which is great. I'll write a basic version of the software I want and then build some hardware for it to run on.
The first task I set myself was to move a servo to a hard coded position and then maintain it. This didn't prove too difficult. I then found the limits of the servos movements using a multimeter to measure current. When the servo is at rest it consumes about 9ma. If the servo is not moving and it is consuming more than 9ma then it is up against the stop and I have sent it a pulse that is either too long or too short for its range. Once I found the limits I found the pulses required to move the servo through 180°.
A pulse of 1.5ms is the defacto standard for the center position. 1ms is 0° and 2ms is 90°. My servo, Futaba FP-S28, can move through about 190-200°. Using the method described above I found the absolute limits were 0.25ms to 2.4ms however the min and max for 180° movement were 0.4ms to 2.3ms.
The below code uses Timer0 to send a pulse of the desired length and then wait 18ms before sending the same pulse again. the servo signal wire is connected to RB1.
list p=18f452
INCLUDE "P18F452.inc"
;code protect disabled
CONFIG CP0=OFF
;Oscillator switch enabled, RC oscillator with OSC2 as I/O pin.
CONFIG OSCS=ON, OSC=HS
;Brown-OutReset enabled, BOR Voltage is 2.5v
CONFIG BOR=ON, BORV=25
;Watch Dog Timer disabled
CONFIG WDT=OFF
ORG 0x00
GOTO MAIN
ORG 0x08 ;INTERRUPT VECTOR
GOTO ISR ;INTERRUPT SERVICE ROUTINE
;================================================
ISR
;clear the interrupt
bcf INTCON, TMR0IF
;is the output already on?
btfsc PORTB, RB1
;no, it is not
goto turnServoOff
;yes it is
turnServoOn
;turn on the servo control bit
bsf PORTB, RB1
;wait 0.4ms
;F7 is the high byte of the timer offset
movlw 0xF7
;write to the high byte first because the actual write occurs when the low byte is written
movwf TMR0H
;00 is the low byte of the timer offset
movlw 0x00
;The high and low bytes are now written
movwf TMR0L
;we're done, get back to what we were doing before the interrupt
goto ENDISR
turnServoOff
;turn on the servo control bit
bcf PORTB, RB1
;wait 18ms
;B9 is the high byte of the timer offset
movlw 0xB9
;write to the high byte first because the actual write occurs when the low byte is written
movwf TMR0H
;B1 is the low byte of the timer offset
movlw 0xB1
;The high and low bytes are now written
movwf TMR0L
ENDISR
RETFIE
;================================================
MAIN
;init port b
;zero everything (trun it all off)
clrf PORTB
;clear bit 1 to make RB1 output
bcf TRISB , RB1
;setup tmr0
;disable the timer until we have set it up
bcf T0CON, TMR0ON
;turn the prescalar off
bsf T0CON, PSA
;internal clock source (use the program counter clock rather than an external pin)
bcf T0CON, T0CS
;set to 16 bit mode
bcf T0CON, T08BIT
;B9 is the high byte of the timer offset
movlw 0xB9
;write to the high byte first because the actual write occurs when the low byte is written
movwf TMR0H
;B1 is the low byte of the timer offset
movlw 0xB1
;The high and low bytes are now written
movwf TMR0L
;enable tmr0 interrupt
bsf INTCON, TMR0IE
;enable all interrupts
bsf INTCON, GIE
;enable timer, we're good to go
bsf T0CON, TMR0ON
;do nothing until interrupted
goto $
;use TMR0 with no prescalar
;18ms is 18000 timer ticks so 65536-18000=47537 which is B9B1
;so B9B1 is the value to preload tmr0 with
;0.5ms is 50 timer ticks so 65536-500=65036 which is FE0C
;1ms is 1000 timer ticks so 65536-1000=64536 which is FC18
;2ms is 2000 timer ticks so 65536-2000=63536 which is F830
;2.5ms is 2500 timer ticks so 65536-2500=63036 which is F63C
END
Changing the values in the turnServoOn routine will cause the servo to move to another position. The limit positions are laid out in the table at the end of the code.
Next step is to manage more than 1 servo, probably 3 since thats how many I have to play with.