Better ways of interfacing Servos

Hi folks,

i'm trying to implement servos in one of my projects - however pretty much
every bit of code I came up with uses the delay or delayMicroseconds function -
and thus stops execution of the main program for the time the signal is generated.

The other option i went after was using PWM with analogOut - however the only
usable PWM-frequency for driving servos would be 8ms (via CPU clock divider),
which leaves a mere 64 possible servo positions (8ms = 256 => 2ms (servo pulsewidth) = 256/4 = 64).

So my questions are:

-How can i set the frquency of the PWM output more accurately
(say 50hz for servo applications)?
-How can i set the resolution of the PWM to 10 or probably 12 bits?
-Are there any better ways/ ideas how to drive servos without interrupting
the execution of the main program for a longer period of time?

Thanks for your upcoming ideas :wink:

Moe

A method for setting the PWM frequency is definitely on the list of priorities (and getting higher all the time). In the meantime, doing this will require some low-level hacking. See the timer sections in the datasheet for the ATmega8 (or ATmega168).

Thanks for your hints, Mellis.

After some hours of diggin' in datasheets and webpages i finally came up with a solution that works:
(hope it's of any interest)

/* ServoPWM

*/

int val = 0;

void setup(void) {
pinMode(9,OUTPUT); // Servo Pin
Serial.begin(9600);

TCCR1A = 0x00; // sets timer control bits to PWM Phase and Frequency Correct mode
TCCR1B = 0x12; // sets timer control bits to Prescaler N = 8
ICR1 = 0x0FA0; // Upper Timer Limit = 4000 (in hex) equals 4ms => 1/5 of servo frequency

}

void loop(void) {
val = analogRead(0); // read potentiometer
val = (val * 1.76) + 600; // convert potentiometer reading to value in microseconds (my servo responds to signals in the 600us - 2400us range)
analogWrite(9,val); // pulse servo
Serial.println(val); // output servo value

}

what it does:

  • it initializes the PWM timer so its frequency is 4ms - 1/5 of the servo's frequency of 20ms
    (as all of the servo's signals fall in between the 0.5ms - 2.5ms range).
  • analogWrite now takes values from 0 to 4000 (for us = 0.001ms)

EDIT:

it currently only works on pins 9 and 10 as Timer1 only drives these two.

best regards,
Moe

This is very interesting.... my question follows on nicely....

I am using IR recievers and emitters. The recievers (vishay tsop 4838) accept a carrier frequency of 38khz. If there is an ability to change the frequency of PWM in Arduino I was hoping to pulse my IR emitters (leds) at 38khz using analogWrite.... does this sound sensible?.... if so could anyone give some ideas on how to change the PWM freq to 38khz? I am quite new to all this! :-[

If this doesnt sound sensible what is a better way of pulsing the leds? I have started a new topic for this question elsewhere...

Thanks! :slight_smile:

This is really great to know. I'm using the delay method to control three servos with no problems with accuracy or code stopping, at least for my robot, but this is a much better solution.

Is there something special about pin 11?

-Z-

Thanks for your hints, Mellis.

After some hours of diggin' in datasheets and webpages i finally came up with a solution that works:
(hope it's of any interest)

/* ServoPWM

*/

int val = 0;

void setup(void) {
pinMode(9,OUTPUT); // Servo Pin
Serial.begin(9600);

TCCR1A = 0x00; // sets timer control bits to PWM Phase and Frequency Correct mode
TCCR1B = 0x12; // sets timer control bits to Prescaler N = 8
ICR1 = 0x0FA0; // Upper Timer Limit = 4000 (in hex) equals 4ms => 1/5 of servo frequency

}

void loop(void) {
val = analogRead(0); // read potentiometer
val = (val * 1.76) + 600; // convert potentiometer reading to value in microseconds (my servo responds to signals in the 600us - 2400us range)
analogWrite(9,val); // pulse servo
Serial.println(val); // output servo value

}

what it does:

  • it initializes the PWM timer so its frequency is 4ms - 1/5 of the servo's frequency of 20ms
    (as all of the servo's signals fall in between the 0.5ms - 2.5ms range).
  • analogWrite now takes values from 0 to 4000 (for us = 0.001ms)

EDIT:

it currently only works on pins 9 and 10 as Timer1 only drives these two.

best regards,
Moe

Very interesting code. Thanks.

One question though. I thought that you were only supposed to give the servo pulse every 20 - 30ms. If i'm reading the code correctly, it will just give one pulse right after another.

I probably just need to go read up on timers...