I am trying to drive my SG90 servo using the timers on the Atmega329P using phase correct PWM. I have set up my motor well enough that it rotates, but I cannot get it to stop – the servo motor just continues rotating.
Here's my current code:
servo.c
#define F_CPU 16000000L
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include "servocontrol.h"
#define TOP 500 // 1000*(64/16000000)
void servo_delay() {
_delay_ms(1500);
}
void setup_servos() {
DDRD = (1 << DDD5) | (1 << DDD6);
TCCR0A |= (1 << COM0A1) | (1 << COM0A0) | (1 << WGM00) | (1 << COM0B1);
TCCR0B |= (1 << CS00) | (1 << CS01);
OCR0A = 0x00;
}
void move_servos() {
for (int i = 100; i < TOP; i++) {
OCR0A = i;
servo_delay();
if (OCR0A == 300) {
PORTD &= ~ (1 << PD5);
}
}
}
servocontrol.h
#ifndef SERVOCONTROL_H
#define SERVOCONTROL_H
void servo_delay();
void setup_servos();
void move_servos();
#endif
main.c
#define F_CPU 16000000L
#include <avr/io.h>
#include "servocontrol.h"
int main() {
setup_servos();
move_servos();
}
I'm trying to use timer0
in phase correct non-inverting mode, but I exprecienced the same problem when I used timer1
.
How do I get the servo to stop at the location I want? I feel like there is a part of the PWM process that I am currently missing, but I cannot seem to figure it out. I will greatly appreciate any help.
Best Answer
At 16 Mhz and with 1/64 clock source you have 250 kHz TC frequency. You set TOP to FF (WGM00), so your PWM period is 2 ms. Normal hobby servos need period about 20 ms (50 Hz).
You can try 1/1024 clock for 32 ms period (at TOP = FF). Then OCR0A between 8 and 16 will give you 1 to 2 ms pulse width, for 8 positions of servo resolution.
You can use Fast PWM mode for 16 ms period. Then OCR0A between 16 and 32 will give you same 1 to 2 ms pulse width, for 16 positions of servo resolution.
Or you can use Timer1 with OCR1A as TOP for precise period control and OCR1B as duty cycle with much better resolution.