Swordfish Module - Servo.bas

servo_large

DiskIcon Download the module: Servo.bas

 

Full credit is given to MichealM, whose method of controlling servos - posted here - forms the basis of this module.

 

Overview

This module controls between 1 and 8 hobby servos connected to PortB.  The module will only affect the pins which have servos connected - if less than 8 servos are used then the remaining pins on PortB can be used for other I/O.

The module is interrupt driven and uses the Timer1 and the CCP1 modules of the PIC. Although the interrupt routine is pretty lightweight (the assembler is less than 40 instructions long), users should be aware that their main program will have its execution paused every now and then to service the servos. In most cases I haven't found this to be a problem. On an 8MHz clock, I've used the module to control 8 servos whilst sending continuous UART data and reading an analogue voltage without any problems. If you do have timing-sensitive code then there are ways to mitigate problems caused by the interrupt - see below.

Please Note: The module will only work correctly if the PIC is clocked at 4, 8, 16, or 32MHz

 

Using the Module

Options

The module's options and their default values are:

Options
#option Servo_NumberOfServos = 8        'Number of servos (1 - 8)
#option Servo_Priority = ipHigh         'Servo interrupt priority

Servo_NumberOfServos sets the number of servos which will be controlled. Valid values are 1 to 8.

Servo_Priority sets whether the module's interrupt will be run as high or low priority. For best results, this should be left as high priority.

 

Connecting servos

Servo 0 should be connected to PortB.0; Servo 1 connected to PortB.1; and so on.

 

Setting Servo Positions

Servos are controlled by sending them a pulse of between 600uS and 2400uS duration every 20mS. The length of the pulse determines the servo's position, with 1500uS being approximately halfway.

The module stores the position of each servo in the word array ServoPosition. To set a particular servo's position, load the length of the desired pulse (in uS) into the array. i.e. to set Servo 3 to the maximum position, use ServoPosition(3) = 2400

 

Subroutines

Servo.On

Calling this routine will enable the interrupt and start sending pulses to the servos.

Servo.Off

Calling this routine stops the interrupt from firing and stops sending pulses to the servos. Note: If they are not receiving control pulses then servos will only hold their current position if no force is acting on them. However, turning off the interrupt may be useful if you need to execute a piece of code which must not be interrupted.

 

Example Program

This example program controls 3 servos.  Servo 0 is controlled by an analog input on AN0 while Servos 1 & 2 constantly move through their full range of motion.

Example Program
Device = 18F2520
Clock = 4
 
#option Servo_NumberOfServos = 3
 
Include "Servo.bas"
 
Dim Counter As Word
 
Function GetVoltage() As Byte
'Returns byte value representing voltage on AN0 pin (0 = 0V, 255 = 5V)    
 
    ADCON0.0 = 1                'Turn on A/D module        
    ADCON0.5 = 0                '}  
    ADCON0.4 = 0                '}Set A/D channel to AN0
    ADCON0.3 = 0                '}  
    ADCON0.2 = 0                '}
 
    ADCON0.1 = 1                'Start conversion    
 
    While ADCON0.1 = 1          '}Wait for conversion to finish        
    Wend                        '}    
    Result = ADRESH             'Store result (8 MSBs only)
 
    ADCON0.0 = 0                'Turn off A/D module   
 
End Function
 
 
'Main Program
'---Set-up analog to digital converter 
ADCON1.5 = 0                    '}Set A/D Vref+ and Vref- to Vdd and Vss
ADCON1.4 = 0                    '}   
ADCON1.3 = 1                    '}
ADCON1.2 = 1                    '}AN0 set to analog input 
ADCON1.1 = 1                    '}
ADCON1.0 = 0                    '}   
ADCON2.7 = 0                    'A/D result left justified
 
ADCON2.5 = 1                    '} 
ADCON2.4 = 0                    '}Set A/D Aquisition time to 12 Tad
ADCON2.3 = 1                    '}   
ADCON2.2 = 0                    '}Set A/D Converstion Clock to Fosc / 8
ADCON2.1 = 0                    '}This is the minimum possible with a 
ADCON2.0 = 1                    '}clock speed of 4MHz (Tad = 2uS)
 
Input(PORTA.0)
 
Servo.On                        'Start sending servo control pulses
While True 
    For Counter = 600 To 2400
        Servo.ServoPosition(0) = GetVoltage * 7 + 600
        Servo.ServoPosition(1) = Counter
        Servo.ServoPosition(2) = 3000 - Counter
        DelayMS(1)
    Next
    For Counter = 2400 To 600 Step -1
        Servo.ServoPosition(0) = GetVoltage * 7 + 600 
        Servo.ServoPosition(1) = Counter 
        Servo.ServoPosition(2) = 3000 - Counter  
        DelayMS(1)    
    Next
Wend

ServoInterruptComplete

This Boolean variable is set to "True" when the interrupt has finished executing. The interrupt is only called at the end of each servo pulse. Therefore, if you have a piece of code which must not be interrupted then you can do something like this:

ServoInterruptComplete
Servo.ServoInterruptComplete = False
 
Repeat                                      '}Wait for the interrupt to complete
Until Servo.ServoInterruptComplete = True   '}
 
'Do important bit of code

Once the flag is set to True, you know you have at least 600us (minimum servo pulse duration) to execute code before the interrupt will trigger again.

Forum Activity

Member Access