Arizona Chris
Joined: 20 Dec 2014 Posts: 69 Location: Arizona
|
Robot Serial Servo Driver for Continuous Rotation Servos |
Posted: Mon Mar 14, 2016 9:30 pm |
|
|
Hi all, this will be the first of several uploads on serial servo chips. This one uses the ultra cheap 12F629 and while it has no UART, we can still produce a very useable serial servo chip by using a few simple tricks....
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.
Before I post the code, here is my web page devoted to this project with schematics, photos of the devices and all code:
http://www.schursastrophotography.com/PICprojects/servopod1.html
Now to send a 9600 baud byte to the servo chip from the main processor, you first set up the serial out like this:
Code: | #use rs232(baud=9600, xmit=Pin_B4, bits=8, parity=N,STREAM=SERVO1) //LEFT |
To send the actual signal serially this is the command you can send,
using this table for speeds:
//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)
Sending the code:
Code: | SR=5;
SL=3;
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
|
Here is the PIC12F629 code:
Code: |
//****************************************************************************
//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
| [/code] |
|