Problems moving a servo with analogWrite or the Library

Hello guys!
I have a little tricky problem, were I already spent hours… So I hope you can help me :wink:

I want to move with my Arduino Uno a Servo in it’s whole range continuously going left-right-left-right…

A first easy solution: analogWrite()

boolean countUp = true;

int Start = 0;
int Ende = 255;
int Position = Start;

void loop() {

analogWrite(3, Position);

if(Position==Ende){
countUp=false;
}
if(Position==Start){
countUp=true;
}

if(countUp==true){
Position++;
}
else{
Position–;
}

delay(30);

}

At first appeareance it works.
BUT: There are random problems. Sometimes the servo stops, or moves back. Then goes on.
What did I do wrong? (also have to say, that I have a external power supply for the servo and the board’s power I connected via USB)

Second solution: using the library

#include <Servo.h>

Servo myservo;
int var = 0;

void setup() {
myservo.attach(3);

}

void loop() {

myservo.write(var);
delay(100);
var=var+3;
if(var>180){
var=0;
delay(300);
}

}

Now it seams to work without the random-errors. At first: why this?
AND: I have a realy small range now the servo is moving! This are maybe like 100°. And if I move the servo by hand I see, that it has like 200°.
How can I expand the range? Is normally the whole range usabel I see moving it by hand?

I´m really exited what you say. Maybe someone of you know what the problems are…
Thanks a lot,
Marcus

The analogWrite() function is NOT how to control a servo. Don't even try.

The Servo library IS the proper way.

AND: I have a realy small range now the servo is moving! This are maybe like 100°

A servo is intended for things like steering and controlling flaps and rudders. 100 degrees is a huge amount for those purposes.

How can I expand the range?

Buy the right kind of servo.

Is normally the whole range usabel I see moving it by hand?

If you have moved the servo AT ALL "by hand" is is likely that you have damaged it.

Thanks for your fast reply, but your answer doesn't help. I need to understand what I do. 1.) I know the servo works fine because I tried it and a nother one as well 2.) I know that I have more range because I bought some with a bigger range than 180° 3.) You cant't use the analogWrite. Ok. Still need to know what I am doing. It's a pwm-signal, so why not?

So, I still need to expand the range. Maybe changing the source code of the library? Or is there a nother one? Or how can I maybe make the analogWrite secure?

Realy hoping for some way because there must be one!

It’s a pwm-signal, so why not?

It isn’t. Servos are moved using PPM (pulse position modulation), not PWM (pulse width modulation).

So, I still need to expand the range.

You can’t make a servo do what it can’t do.

Maybe changing the source code of the library?

No.

I know that I have more range because I bought some with a bigger range than 180°

Which ones?

Ooh, seems to be that the seller told me the wrong thing :frowning:
I went to a nother one.
Your answer is a bad answer, but also the right one.
So thank you very much! :slight_smile:

I youst sought it must be posibil because the interesting thing is: The servo has a nearly 80° bigger range (normal range 120°) when you give him a direct signal via the analogWrite(pin, value between 0-255:) Only I have this shitti random-errors. But do you know how fast I damage the servo with this? And the clou is: when the servo moves that big range with the wrong PWM signal: coudent it possibil move it whis a changed library in the normal way over the ppn???

In theory servo is controlled by pulses from 1ms to 2ms wide, with the centre position selected by a 1.5ms pulse width. These pulses are sent at a frequency of 50Hz.

Arduino PWM using ‘analogWrite()’ is at 490Hz or 980Hz for pins 5 and 6 on an UNO.

In practice, often the pulses that actually need to be sent for full swing of a servo will be between <1ms to >2ms.
The servo library defaults to pulses between 544us and 2400us, for ‘write(0)’ and ‘write(180)’ respectively.
This behaviour can be changed by specifying a maximum and minimum in ‘attach()’.
The function prototype is ‘uint8_t attach(int pin, int min, int max);’

Instead of ‘write()’, you can also use ‘writeMicroseconds()’ too. Have a read of the servo library’s *.h and *.cpp files for more info.

Or you could just use code snippet below for the Uno, that provides you with two hardware PWM signals that are more accurate then either analogWrite(), or the Servo library.

The following code controls two servos with 50Hz PWM signals, at 14-bit resolution on D9 and D10:

void setup() {
  // Initialise timer 1 for phase and frequency correct PWM
  pinMode(9, OUTPUT);                         // Set digital pin 9 (D9) to an output
  pinMode(10, OUTPUT);                        // Set digital pin 10 (D10) to an output
  TCCR1A = _BV(COM1A1) | _BV(COM1B1);         // Enable the PWM outputs OC1A, and OC1B on digital pins 9, 10
  TCCR1B = _BV(WGM13) | _BV(CS11);            // Set phase and frequency correct PWM and prescaler of 8 on timer 1
  ICR1 = 20000;                               // Set the PWM frequency to 50Hz
  OCR1A = 1500;                               // Centre the servo on D9
  OCR1B = 1500;                               // Centre the servo on D10
}

void loop() {
  OCR1A = 1000;                               // Move the servo to min position on D9 
  OCR1B = 1000;                               // Move the servo to min position on D10
  delay(1000);                                // Wait for 1 second
  OCR1A = 2000;                               // Move the servo to max position on D9
  OCR1B = 2000;                               // Move the servo to max position on D10
  delay(1000);                                // Wait for 1 second
}

Just load the OCR1A or the OCR1B registers with the value in microseconds you’d like to send to each servo, for example to center the servo just load 1500, minimum 1000 (or less) and maximum 2000 (or more).

Wow! That seems nice ;-) cool.... Thank you. Yust can't try now because I'm not at home now. How can I learn this coding? Is there some list or tutorial?

I would like to know, how the commands work: TCCR1A _BV(COM1A1) ICR1

And what do you do here? TCCR1B = _BV(WGM13) | _BV(CS11); // Set phase and frequency correct PWM and prescaler of 8 on timer 1

Hi Mochilero,

This method uses the 16-bit timer 1 on the Uno. Timer 1 on the Uno has 2 output channels on pins 9 and 10. It's possible to set this timer to use "Phase and Frequency Correct" mode, which gives you control of both the phase (duty-cycle) and the frequency (period) of the PWM output.

The frequency of the PWM output in "Phase and Frequency Correct" mode is given by the formula:

PWM Frequency = CPU Frequency / (2 * N * TOP)

where: CPU Frequency = 16MHz (for the Uno) N = timer prescaler divider value TOP = maximum timer value

The line of code:

TCCR1B = _BV(WGM13) | _BV(CS11);            // Set phase and frequency correct PWM and prescaler of 8 on timer 1

Sets timer 1 to "Phase and Frequency Correct" mode, the 16-bit ICR1 register as the our TOP value and our timer prescaler divider (N) to 8.

Taking the formula:

PWM Frequency = CPU Frequency / (2 * N * ICR1)

...we can rewrite this as:

ICR1 = CPU Frequency / (2 * N * PWM Frequency)

So, if the PWM frequency is 50Hz, then:

ICR1 = 16MHz / (2 * 8 * 50) = 20000

In this "Phase and Frequency Correct" mode the timer counts up to the ICR1 (or TOP) value, then counts back down to 0 and so on.... up and down. (Hence the divide by 2 in the formula above).

The preceding line:

TCCR1A = _BV(COM1A1) | _BV(COM1B1);         // Enable the PWM outputs OC1A, and OC1B on digital pins 9, 10

Sets the the output on digital pins 9 and 10 to output a 1 (high) every time the Timer 1's counter matches the values in the 16-bit OCR1A (on D9) and OCR1B (on D10) registers, while counting up and sets the output to 0 (low) when counting down.

So, if you set the values in the OCR1x registers to 0, the PWM outputs on D9 and D10 will be 0V (or 0% duty cycle). If you set them equal to the ICR1 value (20000), then the PWM outputs 5V (or 100% duty cycle). Likewise of you set the OCR1x registers to 10000, you'll get a 50Hz square wave (50% duty cycle).

As OldSteve said, Servos are driven by a PWM signal between usually about 1ms and 2ms, or 1000us and 2000us. Using a timer prescaler of 8 is nice, because value in the OCR1x registers correspond to the waveform's pulse width in microseconds (us). So, if you enter 1500 in the OCR1x register, this will give you a 1500us pulse width at 50Hz and centre the servo.

If you need to use other frequencies you can load the ICR1 register with other values, for example 2500 will create a 400Hz signal. However increasing the frequency will result in a lower resolution.

Resolution is given by the formula:

Resolution = log(ICR1 + 1) / log(2)

At 50Hz, this will give you:

Resolution = log (20000 + 1) / log(2) = 14.28 = 14-bit resolution (more than enough for most servos)

All this information comes from the Atmega 328P datasheet, section 15: 16 Timer/Counter1 with PWM.

Hi MartinL,

I realy thank you for your nice explications! Now, I can solve my problems, so it works! xDDD Also its interesting what is posibil with my Arduino ;-)