Servo "Pod" serial input

for Continuous Rotation Servos

Updated 3/13/16

Key Search Words: PIC Microcontroller, CCS C Compiler, PIC Projects

 Here is my CCS-C based update on our servo driver chips, which is used to control a continuous rotation servo. For many years we have used this exact technique in our smaller robots, such as the PICbot series with great success. The basic concept is this - A modified continuous servo works by rotating one way at 1ms, the other way with 2ms and somewhere in between around 1.5ms pulse input the servo completely stops. But this must be a very accurate pulse - Even 5-10us off and the motor will slowly rotate. Thus the need for accuracy precludes using timers in the PIC even with a 10MHz crystal which did not roll over fast enough (100us) and even presetting them was not accurate enough. Here is how I accomplished it.

Operation:

A serial byte is sent from the main processor, in this case a PIC16F887 microcontroller which sets the speed of each motor in 5 steps. Small home robots do not need an infinite variation on their speeds, only Fast Forward, Half speed Forward, Stop, Idle and two reverse speeds. The faster speeds allow them to travel toward a distant goal rapidly and then they can slow to half to approach their goal. Stop holds the motors still and Idle turns off all pulses to the motors so they dont draw large amounts of current when the robot is at rest.

The Program:

We first generate the blanking period of approximately 20ms by using the "TIMEOUT" parameter in the 232 setup. This does two things, first it looks for the serial input signal for the entire blanking period which is 85% of the pulse cycle. Then it times out and ends the low period. The pulse is generated accurately with the delay_us( ) function, standard in CCS. This is VERY accurate and allows us to stop the motor at the required pulse width.

Since there is no UART in this inexpensive device, we use a programming trick to always get the data in - I send the data from the main processor twice, separated by 10ms. This absolutely guarantees one of the two will land in the blanking interval. So 85% of the time it gets received on the first send, then 15% of the time is for sure. This time difference is so fast, it has no effect on the movements of the robot.

Photo if the robots PCB with the two servo driver chips on board, labeled RPOD and LPOD for right and left. They are the same program and are driven by two separate serial lines from the main controller.
The current robot, TDR uses continuous rotation servos. It will be used for research into tactile feedback navigation using insect type feelers and mammal "mouse" whiskers.
Schematic for the servo chip.
Sending serial data to Serial port:

#use rs232(baud=9600, xmit=Pin_B4, bits=8, parity=N,STREAM=SERVO1) //LEFT
#use rs232(baud=9600, xmit=Pin_B5, bits=8, parity=N,STREAM=SERVO2) //RIGHT

//Motor speed function table - Left Motor only, right is reversed:
// 0 - No Data in - turn on=low output, after that stays previous setting.
// 1 - STOP (1528uS) This will vary per motor!
// 2 - HALF FWD (1580)
// 3 - FULL FWD (2000us)
// 4 - HALF REV (1476)
// 5 - FULL REV (1000us)
// 6-255 - STOP (1528uS)

//Main Program 

delay_ms(2000);
while (true)   {
STOP();
delay_ms(1000);
HFWD();
delay_ms(2000);
STOP();
delay_ms(1000);
//FFWD();
//delay_ms(2000);
//STOP();
//delay_ms(2000);
HREV();
delay_ms(2000);
//FREV();
//delay_ms(2000);
//RROT();
//delay_ms(2000);
//STOP();
//delay_ms(2000);
//LROT();
//delay_ms(2000);
        
    }  
  
}
//********* Functions which have prototypes at top of program ****************
void IDLE(void) {
   SR=0;
   SL=0;
   CSEND();  
}
void STOP(void) {
   SR=1;
   SL=1;
   CSEND();
}

void HFWD(void) {
   SR=4;
   SL=2;
   CSEND();
}

void FFWD(void) {
   SR=5;
   SL=3;
   CSEND();
}

void HREV(void) {
   SR=2;
   SL=4;
   CSEND();
}

void FREV(void) {
   SR=3;
   SL=5;
   CSEND();
}
void RROT(void)  {
   SR=2;
   SL=2;
   CSEND();
}
void LROT(void)  {
   SR=4;
   SL=4;
   CSEND();
}
void CSEND(void)  {     //This sends data out serially to the motor chip.
   for (n=0;n<=1;n++) {   //send several times
     fputc(SR,SERVO1);    //send data to servo 1
     fputc(SL,SERVO2);    //send data to servo 2
     delay_ms(10);   }   //space between bursts of data
        }

Code Listing - PIC12F629:
//****************************************************************************
//Chris Schur
//(Program Name):  12F629 - Robot Serial Servo Pod chip
//Date:  3/3/16
//****************************************************************************

//I/O Designations ---------------------------------------------------
// A0 (GPIO0):  X
// A1 (GPIO1):  SERVO OUTPUT
// A2 (GPIO2):  OUTPUT STATUS LED
// A3 (GPIO3):  SERIAL DATA INPUT (can ONLY be input)
// A4 (GPIO4):  XTAL OUTPUT
// A5 (GPIO5):  XTAL INPUT

//--------------------------------------------------------------------
//Include Files:
#include <12F629.h>  //Normally chip, math, etc.  used is here.
//Directives and Defines:
#fuses NOPROTECT,NOMCLR
#use delay(crystal=10MHz)
#use fast_io(A)
#use rs232(baud=9600, rcv=Pin_A3, bits=8, parity=N,TIMEOUT=15)
//Note: the "disable_ints is critical, turns off constant barrage of interrupts
//during the aquisition of data in the get command so timing is not affected.
#define LED Pin_A2  //status LED
#define SERVO Pin_A1  //Servo Motor PWM

//*******Global Variables (put before main and Interrupts to make ***********
//available everywhere):*****
int16 PW = 0;
int8 S = 0;     
//************* Functions/Subroutines, Prototypes:*************************
                           //(none)
//*********** ----- Main Program     ****************************************
void main(void) {
   // Set TRIS I/O directions, define analog inputs, compartors:
        //(analog inputs digital by default)  
      
      set_tris_A(0b101000);
   //Initialize variables and Outputs:  --------------------------------------
      //variables used ONLY within main (local):
      
 
//---Initial Values---------------------------------------------------
   
output_low(SERVO);
output_low(LED);

//MAIN LOOP: >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
while (true)   {
//Motor speed function table:
// 0 - No Data in
// 1 - STOP (1528uS) This will vary per motor!
// 2 - HALF FWD (1580)
// 3 - FULL FWD (2000us)
// 4 - HALF REV (1476)
// 5 - FULL REV (1000us)
// 6-255 - STOP (1528uS)

switch(S) {
case 0:  //NO DATA - dont change previous value!
         break;
         
case 1:  //STOP
         PW = 1528;
         break;
case 2:   //HALF FWD (200-16)
         PW = 1580;
         break;
         
case 3:  //FULL FWD  (200-20)
         PW = 2000;
         break;
         
case 4:  //HALF REV
         PW = 1476;
         break;
         
case 5:  //FULL REV
         PW = 1000;
         break;
         
default: //STOP - no power
         break;
         
      }  //hctiws 
      
//Now generate pulse - 20ms low first:
S = getc();  //next generate 20ms delay using time out on RS232

if (S < 6)  {
//generate pulse if S is 0 - 5 only
if (PW < 500) 
   continue;
output_high(SERVO);
output_high(LED);
delay_us(PW);
output_low(SERVO);
output_low(LED);
}
   }  //elihw
} //niam

  
  HOME