Wednesday, June 2, 2010

PIC18, timers and servos (Hexapod part 2)

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.

No comments:

Post a Comment