Development Board: Dedicated 8 Channel Servo Controller


Download the Swordfish program and Gerber files here: Dedicated Servo Controller Source Files

This project offers a dedicated solution to control servo motors, which can be controlled via USART data packets. This approach has it advantages, though Andy has made an excellent feature enriched Servo Library which can be used in pretty much any program.

Servo Motor's are one of the many hobby electronic marvels that make life a lot easier when working with motion/kinetic applications. They are somewhat precision instruments that offer their full range of motion with pulse widths usually between 1mS and 2mS. That's not much play, and further more, the positional information must be transmitted to the servo approximately fifty times a second (50Hz).

Take a moment to consider the resolution required to get any real degree of granularity with a servo; 0.001 second pulse for minimum deflection, and 0.002 second pulse for maximum deflection. Also take into account that a PIC usually operates at around 10MIPS (10 Million Instructions Per Second), each program cycle will take 100nS.

If you wanted 0.000001 second resolution (the capability to change a pulse width in 1uS increments), then the PIC would only have 10 program cycles to accommodate pulse width maintenance. Considering most normal PIC applications are doing more then one thing at a time, dedicating such hefty resources for the servo does not leave much leeway for other jobs.


Controlling 8 Servo's With Ease

The on-board CCP events are very useful for such an application, and allow very accurate and quick interrupts on exact intervals. This program makes use of both the CCP1 special event and CCP2 normal event to control 8 servos in real time. Further more, the PIC will continually monitor the UART for new servo position data.


Why Dedicated/Standalone?

Given the demand placed on the PIC to accommodate 8 servos, there's not much resource scope for other tasks. The use of UART definitely made good use of what resources are left over - keep in mind that I am using the hardware UART module which has little overheads to consider. The protocol employed is very lean, extremely tolerant to noisy conditions and detecting transmission errors.

Here's what the program looks like in operation;

Figure 1 - Dedicated Servo Controller Simulation.
For clarity, the above schematic does not show the voltage regulator or crystal oscillator circuits.

Dedicated Servo Controller Code

Program Notes

Note 1: Servo positional information is received via UART at 38400 baud in the following manner:

$FFFF Servo(0) Servo(1) Servo(2) Servo(3) Servo(4) Servo(5) Servo(6) Servo(7) CRC Checksum
Header Servo Positions Validation

Note 2: All data types are 16-Bit WORDS. This means that the header is of type WORD, so to is each Servo position and the CRC Checksum.
Note 3: CRC is calculated by XOR'ing all Servo positions together, for example:

Servo(0) = 1000
Servo(1) = 1100
Servo(2) = 1200
Servo(3) = 1300
Servo(4) = 1400
Servo(5) = 1500
Servo(6) = 1600
Servo(7) = 1700


CRC = 1000 XOR 1100 XOR 1200 XOR 1300 XOR 1400 XOR 1500 XOR 1600 XOR 1700
CRC = 1600

For example, to make every servo pulse equal 1.5mS, the following command would be used in SF:


Note 4: Program is designed to work with an 8Mhz External Oscillator.
Note 5: Ensure common ground connection between all circuits.

Thanks to XOR from the SF forum for the servo code, I've simply turned it into a dedicated controller with an UART interface.

Dedicated Servo Controller
Dedicated Servo Controller
By Graham (Servo controller code developed by XOR)
Servo positional information is received in the following manner;
Note 1: Both Servo(x) and CRC are WORD type variables.
Note 2: CRC is calculated by XOR'ing all Servo positions together, for example;
Servo(0) = 1000
Servo(1) = 1100
Servo(2) = 1200
Servo(3) = 1300
Servo(4) = 1400
Servo(5) = 1500
Servo(6) = 1600
Servo(7) = 1700
     CRC = 1600
Note 3: Program is designed to work with an 16Mhz External Oscillator
Note 4: Ensure common ground connection between all circuits
Device = 18F2620
Clock = 16
Config MCLRE = OFF
Include "USART.bas"
// define valid servo range
Const Servo_Min = 600
Const Servo_Max = 2400
// default servo starting position
Const DefaultPos = 1500
Dim tmpServo(8) As Word   
Dim ServoPeriodValue As CCPR1L.asWord
Dim ServoCompareValue As CCPR2L.asword
Dim ServoOnIntEnable As PIE1.bits(2)
Dim ServoOnFlag As PIR1.bits(2)
Dim ServoOffIntEnable As PIE2.bits(0)
Dim ServoOffFlag As PIR2.bits(0)
Dim Servo(8) As Word
Dim Servo_Port As Byte
Dim n As Byte
// interrupt to service 8 servo channels
Interrupt ServoManager()
    Save(FSR0, FSR1)                 // context save
    If ServoOnFlag = 1 Then          // CCP1 Special Compare Event Interrupt
        LATB = Servo_Port            // Turn On Next Servo
        ServoCompareValue = Servo(n) // Active Servo Value loaded in CCPR2   
            rlncf Servo_Port, F, 0
        End ASM
        n = n And 7       
        ServoOnFlag = 0   
    Else                             // CCP2 Normal Compare Event Interrupt
        LATB = 0                     // Turn Off Active Servo
        ServoOffFlag = 0
    Restore                          // context restore
End Interrupt
// initialize servos and interrupt settings
Sub InitializeServos()
    Dim i As Byte
    LATB = 0                     // servo port
    TRISB = 0
    T1CON = %00100000            // prescaler = 1:4 = 1us ticks @16Mhz ; timer1 off
    TMR1L = 0                    // Timer1 starts at 0
    TMR1H = 0
    CCP1CON = %00001011          // CCP1 triggers special compare event on Timer1 match
    CCP2CON = %00001010          // CCP2 normal compare event on Timer1 match
    ServoPeriodValue = 2500      // special event interrupt every 2500us; jumps to ServoON ISR
    Servo_Port = 1               // servo outputs start at PORTB.0, then shift and rotate
    n = 0                        // start with servo(0)
    ServoOnFlag = 0
    ServoOffFlag = 0
    ServoOnIntEnable = 1
    ServoOffIntEnable = 1
    INTCON = %11000000            // enable GIE and PEIE
    For i = 0 To 7
        Servo(i) = DefaultPos
    T1CON.0 = 1                   // start Timer1
End Sub
// sub that waits for the header ($FF,$FF) to be received
Sub WaitForHeader()
    Until USART.ReadByte = 255
    Until USART.ReadByte = 255
End Sub
// collect each servo position, and return false if non-valid data is received
Function GetServoPos() As Boolean
    Dim i As Byte
    Result = True
    For i = 0 To 7
        tmpServo(i).byte0 = USART.ReadByte
        tmpServo(i).byte1 = USART.ReadByte
        If tmpServo(i) < Servo_Min Or tmpServo(i) > Servo_Max Then
            Result = False
End Function
// Receive the CRC, and validate data. Return false if non-valid.
Function CheckCRC() As Boolean
    Dim i As Byte
    Dim CRC As Word
    Dim tmpCRC As Word
    Result = True
    tmpCRC = USART.ReadWord
    CRC = 0
    For i = 0 To 7
        CRC = CRC Xor tmpServo(i)
    If tmpCRC <> CRC Then
        Result = False
End Function
// update all 8 servo positions
Sub UpdateServos()
    Dim i As Byte
    For i = 0 To 7
        Servo(i) = tmpServo(i)
End Sub
// initialize program
// main program loop
While True    
    WaitForHeader                             // wait for valid data header ($FF,$FF)
    If GetServoPos Then                       // attempt to receive servo positions
        If CheckCRC Then                      // attempt to validate CRC
            UpdateServos                      // if all good, then update servo positions
            USART.Write(1)                    // send complete response (1)
            USART.Write(0)                    // send error response (0)


Example Transmit Program

The following program is an example of how to transmit the required data to the dedicated servo controller (the previous simulation animation is with this program in use);

Example Transmit Program
Device = 18F2620
Clock = 40
Config OSC = HSPLL,
       MCLRE = OFF
Include "USART.bas"
Dim Servo(8) As Word,
    i As Byte,
    tmpPos As Word
Sub SendServoPositions()
    Dim CRC As Word
    Dim i As Byte
    // send header
    // send each servo position
    For i = 0 To 7
    // calculate and send CRC
    CRC = 0
    For i = 0 To 7
        CRC = CRC Xor Servo(i)
End Sub
// start of main program
While True 
    // rotate all servos in one direction
    For tmpPos = 1000 To 2000
        For i = 0 To 7
            Servo(i) = tmpPos
    // rotate all servos in the other direction    
    For tmpPos = 2000 To 1000 Step -1
        For i = 0 To 7
            Servo(i) = tmpPos

Note that the above program has the following hardware requirements:

  • 10Mhz Crystal Oscillator
  • MCLRE disabled

You can simply edit either the program clock or config settings to change the above hardware requirements. This was just an example how to send packets of data to the dedicated controller.


Servo Development Board

As always, to make life even easier I prefer to have a purpose built development board. The desired features were;

  • Interface for 8 Servo Motors
  • Direct PICKit 2 programming
  • PICKit 2 UART interface
  • Onboard Voltage Regulator (capable of driving all 8 servos)
  • Small footprint

The end result managed to include all of the above. Here's the PCB layout for the board;


The above design was to suit my requirements as a quick and easy development board. The end result could differ depending on what end users require. All downloads can be found at the top of the page.


Thoughts comments and feedback are all welcome!

Posted: 5 years 3 months ago by MMcLaren #14356
MMcLaren's Avatar
The link to the Servo Library in the article seems to be broken...
Posted: 5 years 3 months ago by Jon Chandler #14352
Jon Chandler's Avatar
This is a Graham issue and he's not been around much lately.

To find missing files, use DOCman under Site Tools on the menu bar.

Servo Module

Forum Activity

Member Access