Saturday, June 12, 2010

Servo Position With State Machine (Hexapod Part3)


The full source code can be found here
The interesting part is the Interrupt Service Routine (ISR). In the previous example I wasn't using a prescalar but in this one I am. I'm using a 1:8 prescalar which means the timer is increased at an eighth the rate it used to be. this allows the full movement range of the servo to be expressed in one byte.

The ISR implements a 4 state state machine. The states are
  • initialDelay - this state turns on the servo and pauses for 0.25ms. This is a absolute shorted pulse that will ever be needed
  • servoMinimumDelay - this state pauses for the minimum position of the servo. The SERVO1MIN variable is set to 13 for the Futaba servo.  In my last post I discovered that the position I want to be the 0 position requires a pulse of 0.4ms. We have already delayed by 0.25ms in the previous state so we now need to delay by 0.15 ms to complete a 0.4ms delay. Delaying for 0x13 (19) timer ticks, with the prescalar set to 1:8 means we will delay for 19 * (0.01us * 8) = 0.152ms
  • servoPositionDelay - the delay based on the required position of the servo. The position is subtracted from 0xFF because the timer triggers when it overflows (reaches 0xFF+1 or 256) . I could miss this line if I made position 0 the max position but having the subtraction means the minimum position is also the minimum delay. The only gotcha in this state is that if we are in position 0 we want to jump to the next state without waiting for the timer to expire
  • operationalDelay - this state turns the pulse to the servo off and then sets the timer to delay for 18ms.
You'll also see the  the line decf    SERVO1POS, f which will make the servo move one position per ISR call so it takes a few seconds to move across its full range. The position then underflows which puts it back to it maximum position.

So far I haven't taken into account the time it takes to execute the ISR code. When the ISR is longer I'll probably have to modify the timers by a step or two to avoid jitter on the pulse but we'll see how it goes


...
...
ISR
    ;clear the interrupt
    bcf     INTCON, TMR0IF
    bcf     T0CON, TMR0ON    ;disable timer
  
    ;which state?
    movf    CONTROL_STATE, w
    addwf    PCL, f
    nop
    nop
    ;delay for 0.25ms as the minimum position of any servo
    goto     initialDelay  
    ;delay for the minimum for a selected servo
    goto     servoMinimumDelay
    ;delay for the position of the servo
    goto     servoPositionDelay
    ;delay for 18ms so we can do other stuff  
    goto    operationalDelay   

initialDelay
    movlw    0xFF
    movwf    TMR0H
    movlw    0xE1            ;256-(250us/8) = 256-31 - 225 = 0xE1
    movwf    TMR0L

    bsf        PORTB, RB1        ;turn on the servo control bit

    goto    ENDISR

servoMinimumDelay
    movlw    0xFF
    movwf    TMR0H            ;load high timer
  
    movf    SERVO1MIN , w    ;load min position
    sublw    0xFF            ;prepare low byte for timer

    movwf    TMR0L            ;load min delay into timer

    goto     ENDISR
  
servoPositionDelay
    movf    SERVO1POS,w
    bz        operationalDelay    ;if we're at pos=0 don't delay
    movf    SERVO1POS,w
    sublw    0xFF
    movwf    TMR0L
    goto     ENDISR  

operationalDelay            ;delay of 18ms that lets other things happen
    bcf        PORTB, RB1        ;turn off the servo control bit
    ;wait 18
    movlw     0xF7            ;F7 is the high byte of the timer offset
    movwf    TMR0H            ;write to the high byte first because the actual write occurs when the low byte is written
    movlw    0x2E            ;36S is the low byte of the timer offset
    movwf     TMR0L            ;The high and low bytes are now written

    movlw    0x00            ;reset state machine (0x00 because it will be incremented at the end
    movwf    CONTROL_STATE    ;
  
    decf    SERVO1POS, f    ;move the servo one step (for testing only)

ENDISR
    incf CONTROL_STATE, f   
    incf CONTROL_STATE, f   
    incf CONTROL_STATE, f   
    incf CONTROL_STATE, f    ;move to the next state
    bsf     T0CON, TMR0ON    ;enable timer, we're good to go
    RETFIE

No comments:

Post a Comment