## ATmega328P controlling a SG90 Micro Servo

6 posts / 0 new
Author
Message

I've been trying to control an SG90 Micro servo motor using an ATmega328P on an Arduino Uno (a clone, unfortunately). The problem I encounter is that servo motor does not turn to +90 degrees and -90 degrees using the expected duty cycle.

I started with determining the value of ICR1 for a PWM period of 20 ms, according to the SG90 datasheet. Fast PWM is used to generate the pulses. I assume the clock frequency to be 10 MHz: I determined this frequency by trying to let a LED blink once per second, this resulted in a frequency of somewhere around 10 MHz. This yields the ICR1 to be 199999 and using a pre-scaler of 8, this value becomes 24999.

Using the non-inverting Fast PWM mode, to generate a 2 ms (for +90 degrees) and 1 ms (for -90 degrees) pulse, the values for OCR1A should be around 2499 and 1249 respectively. By trial and error, I obtain the values 4899 (for +90 degrees) and 1199 (for -90 degrees). As you see, this differs quite significantly from the calculated values.

What I also noticed is that the value for ICR1 can be changed quite a bit, while the servo motor still rotates to both extremes. For example, ICR1 = 9999 or ICR1 = 39999 also works.

Below is the used code,

```#include <avr/io.h>
#include <util/delay.h>

int main(void) {
DDRB |= 1 << PINB1; // Set pin 9 on arduino to output

/* 1. Set Fast PWM mode 14: set WGM11, WGM12, WGM13 to 1 */
/* 3. Set pre-scaler of 8 */
/* 4. Set Fast PWM non-inverting mode */
TCCR1A |= (1 << WGM11) | (1 << COM1A1);
TCCR1B |= (1 << WGM12) | (1 << WGM13) | (1 << CS11);

/* 2. Set ICR1 register: PWM period */
ICR1 = 24999;

/* 5. Set duty cycle */
while(1) {
OCR1A = 4899;

_delay_ms(5000);

OCR1A = 1199;

_delay_ms(5000);
}

return 0;
}```

This topic has a solution.
Last Edited: Fri. Feb 10, 2017 - 01:14 PM

Arduinos run with 16mhz clock not 10mhz, atleast the ones I have seen.

Assuming a 16 MHz clock frequency, ICR1 = 39999 to obtain a 20 ms period. The servo still rotates to +90 and -90 degrees at OCR1A = 1199 and OCR1A = 4899, respectively. This is not a 1 ms and 2 ms pulse. By the way, I noticed that on the crystal on the Arduino board it says '12.000', I assume this means 12 MHz. Again, this results in the same OCR1A values. Strange!

clau wrote:

Assuming a 16 MHz clock frequency, ICR1 = 39999 to obtain a 20 ms period. The servo still rotates to +90 and -90 degrees at OCR1A = 1199 and OCR1A = 4899, respectively. This is not a 1 ms and 2 ms pulse. By the way, I noticed that on the crystal on the Arduino board it says '12.000', I assume this means 12 MHz. Again, this results in the same OCR1A values. Strange!

Yes, 39999 ICR1 should be correct when using 16mhz.

that 12mhz might for the USB-uart ic those clones use, i have arduino with 12mhz and 16mhz crystals on my hands right as i write this, if its indeed only the 12mhz crystal on the board maybe hmmn, havent seen arduino like that yet.

If you have access to an oscilloscope that would confirm whether or not your waveform is correct, which it might well be and you could simply have a faulty servo.

You could also use this to measure the oscillation of the crystal too as this would confirm the frequency of the device, however as Jonis has already said the crystal marked '12.00' may not actually be used by your micro.

Could be worth posting a link to your Arduino clone too, so we know what you're working with...

This reply has been marked as the solution.

I found the solution. I was wrong by saying the crystal is 12 MHz, it is indeed 16 MHz (labelled '16.000'). I was looking at the wrong crystal!

Using a clock frequency of 16 MHz: ICR1 = 39999, OCR1A  = 3999 for +90 degrees rotation, and OCR1A = 1999 for -90 degrees rotation. These values for OCR1A do not give a full +/- 90 degrees rotation, however using an offset of 800, the new OCR1A values become 3999 + 800 = 4799 and 1999 - 800 = 1199 and this yields a full +/- 90 degrees rotation. And these new values are close to the values obtained by trial and error.

The new code becomes,

```#include <avr/io.h>
#include <util/delay.h>

int main(void) {
DDRB |= 1 << PINB1; // Set pin 9 on arduino to output

/* 1. Set Fast PWM mode 14: set WGM11, WGM12, WGM13 to 1 */
/* 3. Set pre-scaler of 8 */
/* 4. Set Fast PWM non-inverting mode */
TCCR1A |= (1 << WGM11) | (1 << COM1A1);
TCCR1B |= (1 << WGM12) | (1 << WGM13) | (1 << CS11);

/* 2. Set ICR1 register: PWM period */
ICR1 = 39999;

/* Offset for correction */
int offset = 800;

/* 5. Set duty cycle */
while(1) {
OCR1A = 3999 + offset;

_delay_ms(5000);

OCR1A = 1999 - offset;

_delay_ms(5000);
}

return 0;
}```

Thank you guys for the help!