Society of Robots - Robot Forum

Electronics => Electronics => Topic started by: BuddingContraptionist on October 10, 2008, 05:23:11 PM

Title: "servo" macro, position
Post by: BuddingContraptionist on October 10, 2008, 05:23:11 PM
I have an axon and this servo (http://www.robotshop.ca/home/products/robot-parts/motors/servo-motors/hitec-servo-en/hitec-hs-322hd-servo.html).  I've played around with spinning the servo around, but I'm unclear as to how to get it to do what I want.  I get that i need a square wave at 50 hz... but am I specifying an angle to rotate to with the duration of the high signal, or the speed with which to rotate in a particular direction?  and how many milliseconds is a cycle on the axon?
Title: Re: "servo" macro, position
Post by: pomprocker on October 10, 2008, 10:58:07 PM
you can search the forum for the answer to this question.  ;)
Title: Re: "servo" macro, position
Post by: izua on October 11, 2008, 03:00:27 AM
The angle is determined by the pulse width (the on pulse). The frequency remains the same. 1ms is usually left extreme, 2ms is right extreme.

If you would simply give a speed to the servo, it won't be the closed-feedback we are used to. This is what makes the servo go back when you move it with your hand and apply torque to a load. So, again, the pulse sets the angle. It must be sent out continously, too - if you won't do it, the servo's circuitry will shut down (and thus, the servo won't apply any torque).

One instruction takes 1/CPU speed. In axon's case it is 1 000 000 000 (ns) / 20 000 000 (MHz) = 50ns period.
Check this out for a graphical example: http://www.izua.ro/tools/servo_calculator
Title: Re: "servo" macro, position
Post by: BuddingContraptionist on October 11, 2008, 12:25:28 PM
that helps a lot in explanation, but i am still confused.

The spec for my servo says:

"All Hitec servos require 3-5V peak to peak square wave pulse. Pulse duration is from
0.9mS to 2.1mS with 1.5mS as center. The pulse refreshes at 50Hz (20mS)."

I modified the "servo" macro to use delay_us, to delay in microseconds.  So my main exec loop is this:

   while(1)
   {
      servo(PORTC, 0, 1500);
      delay_us(20000 - 1500);
   }

I thought that this would make the servo return to the 0 degree, centered rotation, and hold steadily.  Instead, it jitters back and forth at an odd angle.
Title: Re: "servo" macro, position
Post by: pomprocker on October 12, 2008, 11:17:37 AM
I'm not sure, but I bet if you used a more stable clock source it would jitter less?
Title: Re: "servo" macro, position
Post by: izua on October 12, 2008, 11:38:50 AM
just use something like
Code: [Select]
while(1)
{
PORTC |= (1 << PC0);
_delay_us(1500);
PORTC &= ~(1 << PC0);
_delay_us(18500);
}

You need to set F_CPU for delay to work right.

Title: Re: "servo" macro, position
Post by: BuddingContraptionist on October 14, 2008, 09:45:12 PM
dude i cannot get it to work right.  F_CPU is already defined in globals.h as 16000000, I even got the beta Axon code and tried using a timer interrupt function.  I got fairly close to good timing (i printed out a message on serial every 1000ms and it was only slightly faster than a second), but when I apply the logic of 1.5ms high every 20ms, it just jitters at a steady rate.  And with your code izua, it does it the same if i on-delay1500us-off-delay18500us or if i on-delay1000us-off-delay19000us (or the code i posted, which seems like it should do the same thing)

I have, through trial and error with the timer interrupt, found a range that allows me to rotate it to the left and right extents, so i know the servo works, but i can't get the timing right there either and it isn't a steady rotation.  I really want to see the math for what i need to do to be able to control it properly.
what am i doing wrong?
Title: Re: "servo" macro, position
Post by: BuddingContraptionist on October 15, 2008, 10:12:10 AM
i take it from the dearth of replies that it looks like it should work?  my math is correct?
Title: Re: "servo" macro, position
Post by: airman00 on October 15, 2008, 06:07:37 PM
you dont really want to use time as a measurement for controlling servos , you wanna use servos

Use this servo calculator to calculate cycles from milliseconds or angle:  http://narobo.com/software/servo_calculator/servo_calculator.html

Then use the the servo subroutine thats included in SoR_Utils.h

You use the subroutine as follows:

Code: [Select]
while(1)   // do an infinite loop
{
servo(PORTA,7,700);  // give the servo connected to PORTA.7 one pulse of 700 cycles which is the center point of the servo
delay_ms(20);  // delay 20 milliseconds so as not to overload the servo
}
Title: Re: "servo" macro, position
Post by: BuddingContraptionist on October 15, 2008, 08:44:23 PM
Code: [Select]
#include "SoR_Utils.h" //includes all the technical stuff

#define CLAMPMS(x)  (((x) > (2.5)) ? (2.5) : (((x) < (0.5)) ? (0.5) : (x)))
#define CLAMPDG(x)  (((x) > (180)) ? (180) : (((x) < (0)) ? (0) : (x)))

volatile float amt=0;
volatile float count=0;
volatile float ms=1.5;

// uses the following logic to count its iteration rate:
// (16000000 / 8) / 256 = 7812.5 cycles per 1000 ms
// by this i deduce that 156.25 cycles is 20 ms
// and in turn have a formula to plug in 1.5 ms to...
void timerInterruptFunc(void)
{
count+=1.0;
if(count>=156.25)
{
count=0;
amt = (float)((float)(7812.5 * ms)/1000.0);
PORTC |= (1 << PC0);
}
if(count>=amt)
{
PORTC &= ~(1 << PC0);
}
}

int main(void)
{
//declare variables here
int i=0;//useless variable
int j=0;//useless variable

char inp[6];
int inpon=0;
int t;
char degrees=0;

/****************INITIALIZATIONS*******************/
//other stuff Im experimenting with for SoR
uartInit();  // initialize the UART (serial port)
uartSetBaudRate(0, 38400); // set UARTE speed, for Bluetooth
uartSetBaudRate(1, 115200); // set UARTD speed, for USB connection
uartSetBaudRate(2, 38400); // set UARTH speed
uartSetBaudRate(3, 38400); // set UARTJ speed, for Blackfin
//G=Ground, T=Tx (connect to external Rx), R=Rx (connect to external Tx)

rprintfInit(uart1SendByte);// initialize rprintf system and configure uart1 (USB) for rprintf

timer0Init(); // initialize the timer system

configure_ports(); // configure which ports are analog, digital, etc.

a2dInit(); // initialize analog to digital converter (ADC)
a2dSetPrescaler(ADC_PRESCALE_DIV32); // configure ADC scaling
a2dSetReference(ADC_REFERENCE_AVCC); // configure ADC reference voltage

LED_on();

rprintf("\r\nSystem Warming Up");

//let system stabelize for X time
for(i=0;i<=16;i++)
{
delay_cycles(5000);
rprintf(".");
}

delay_cycles(6000);

//read each ADC once to get it working accurately
for(i=0;i<16;i++)
j=a2dConvert8bit(i);

LED_off();

rprintf("Initialization Complete \r\n");

timerAttach(TIMER0OVERFLOW_INT, timerInterruptFunc);

reset_timer_0();

while(1)
{
t=uart1GetByte();
if(t!=-1)
{
if(t==13) // enter
{
rprintf("\n");
inp[inpon] = '\0';
inpon=0;
if(degrees==1)
ms = atoi(inp);
else
ms = atof(inp);

if(degrees==1)
{
ms = CLAMPDG(ms);
rprintf("using: %d", (int)ms);
rprintf("\n");
ms = (((ms/180.0)*1.8) + 0.6);
}
else
{
ms = CLAMPMS(ms);
rprintf("using: ");
rprintfFloat(3, (double)ms);
rprintf("\n");
}
}
else if((t>=48 && t<=57) || t == 46)  // numeral or period
{
inp[inpon++] = (char)t;
rprintf("%c", t);
}
else if(t==68 || t==100) //  'D'  'd'
{
rprintf("\ndegrees.\n");
degrees = 1;
}
else if(t==77 || t==109) //  'M'  'm'
{
rprintf("\nmilliseconds.\n");
degrees = 0;
}
else if(t==8) // backspace
{
inp[--inpon]='\0';
rprintf("\n");
for(i=0; i<6; i++)
{
if(inp[i]!='\0')
rprintf("%c", inp[i]);
else
break;
}
}
}
}

return 0;
}

it is essential that you set TIMER0PRESCALE to TIMER_CLK_DIV8 in timer640.h, and #define RPRINTF_FLOAT in rprintf.h
For some reason, however, the left/right extents are 0.6 and 2.4, not 1.9 and 2.1 like the spec says... 1.5 is dead center though
Title: Re: "servo" macro, position
Post by: airman00 on October 15, 2008, 09:27:45 PM
looks good

can you edit it a bit and make it one easy to use macro

like you would call it by saying servo(PORTA,7,1.5)

I personally would still use cycles since I'm used to it , but I think the others on the forum would benefit from this macro
Title: Re: "servo" macro, position
Post by: BuddingContraptionist on October 15, 2008, 10:38:08 PM
well its interrupt driven, all you do is set ms to 1.5 or whatever in the while(1) main exec loop...

but suddenly my servo doesn't work anymore  :-[

daaaaaammiiiiiit
Title: Re: "servo" macro, position
Post by: airman00 on October 15, 2008, 10:39:33 PM
well its interrupt driven, all you do is set ms to 1.5 or whatever in the while(1) main exec loop...

but suddenly my servo doesn't work anymore  :-[

daaaaaammiiiiiit

LOL

check your power , check your wiring

what pulse are you giving it now? Still 1.5 or something higher or lower?
Title: Re: "servo" macro, position
Post by: BuddingContraptionist on October 15, 2008, 11:55:11 PM
wow it was a combination of file corruption (somehow my timer640.h got rolled back, I had changed the timer0 prescaler to be divide by 8 which is essential for the interrupt timing)
and low power, i waited a while and tried and it jittered a bit.  I tried my other battery (and the code fix) and it snaps to attention once more.  Odd that the microcontroller still runs and interfaces with the hyperterminal prompt, but there isnt enough power to move the servo.

Anyway I modified it a little so you can hit 'd' to input values in degrees (90 being center, 180 and 0 the extremes) and 'm' to input in milliseconds (1.5, etc)
Title: Re: "servo" macro, position
Post by: Admin on October 16, 2008, 10:00:57 PM
Quote
In axon's case it is 1 000 000 000 (ns) / 20 000 000 (MHz)
Its 16MHz ;D