Servo Motor Interfacing with AVR
CODE:
/* Quick interactive demo running servo with Timer 1 */
// ------- Preamble -------- //
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include "pinDefines.h"
#include "USART.h"
#define PULSE_MIN 1000 /* experiment with these values */
#define PULSE_MAX 2000 /* to match your own servo */
#define PULSE_MID 1500
static inline uint16_t getNumber16(void);
static inline void initTimer1Servo(void) {
/* Set up Timer1 (16bit) to give a pulse every 20ms */
/* Use Fast PWM mode, counter max in ICR1 */
TCCR1A |= (1 << WGM11);
TCCR1B |= (1 << WGM12) | (1 << WGM13);
TCCR1B |= (1 << CS10); /* /1 prescaling -- counting in microseconds */
ICR1 = 20000; /* TOP value = 20ms */
TCCR1A |= (1 << COM1A1); /* Direct output on PB1 / OC1A */
DDRB |= (1 << PB1); /* set pin for output */
}
static inline void showOff(void) {
printString("Center\r\n");
OCR1A = PULSE_MID;
_delay_ms(1500);
printString("Clockwise Max\r\n");
OCR1A = PULSE_MIN;
_delay_ms(1500);
printString("Counterclockwise Max\r\n");
OCR1A = PULSE_MAX;
_delay_ms(1500);
printString("Center\r\n");
OCR1A = PULSE_MID;
_delay_ms(1500);
}
int main(void) {
// -------- Inits --------- //
uint16_t servoPulseLength;
OCR1A = PULSE_MID; /* set it to middle position initially */
initTimer1Servo();
initUSART();
printString("\r\nWelcome to the Servo Demo\r\n");
showOff();
// ------ Event loop ------ //
while (1) {
printString("\r\nEnter a four-digit pulse length:\r\n");
servoPulseLength = getNumber16();
printString("On my way....\r\n");
OCR1A = servoPulseLength;
DDRB |= (1 << PB1); /* re-enable output pin */
_delay_ms(1000);
printString("Releasing...\r\n");
while (TCNT1 < 3000) {;
} /* delay until pulse part of cycle done */
DDRB &= ~(1 << PB1); /* disable output pin */
} /* End event loop */
return (0); /* This line is never reached */
}
static inline uint16_t getNumber16(void) {
// Gets a PWM value from the serial port.
// Reads in characters, turns them into a number
char thousands = '0';
char hundreds = '0';
char tens = '0';
char ones = '0';
char thisChar = '0';
do {
thousands = hundreds; /* shift numbers over */
hundreds = tens;
tens = ones;
ones = thisChar;
thisChar = receiveByte(); /* get a new character */
transmitByte(thisChar); /* echo */
} while (thisChar != '\r');
transmitByte('\n'); /* newline */
return (1000 * (thousands - '0') + 100 * (hundreds - '0') +
10 * (tens - '0') + ones - '0');
}
The other main difference is the addition of a separate ICR1 register, which can be
used to set the PWM frequency. The details involve how the AVR, an 8-bit machine,
deals with 16-bit numbers, but the upshot is that we can use ICR1 to set the frequency
for most PWM modes when we’re not changing the frequency often, and
use OCR1A to set the frequency if we are changing it a lot.
Servos are positioning motors with a built-in feedback circuit, and it’s this internal circuitry that makes them so simple to use. As positioning motors, they don’t spin
around and around like traditional motors. Instead, they only rotate through 180
degrees or so, and they can move to the desired position to roughly the nearest
degree on command. This makes them ideal for pulling up the landing gear on a
You send position controls to a servo with a signal pulse that ranges nominally
from 1 ms to 2 ms. When you send a 1 ms pulse, the motor turns to its mostcounterclockwise
position, and when you send a 2 ms pulse, it turns the shaft to
its most clockwise. If you send a pulse of 1.5 ms, the motor turns the shaft to the
center, and so on. These are “nominal” values—you’ll find that they vary a little bit
from servo to servo, but assuming that 1.5 milliseconds is somewhere in the middle
is a good starting point.
The circuitry inside the servo is expecting a pulse like this every 10–20 ms, and as
long as it receives a pulse, it tries to turn the motor shaft to the right position. When
no pulse is seen for a while, the servo disengages and can be turned more or less
freely. From our perspective, this means that we need to be able to send a short
voltage pulse signal of fairly precise duration that controls the position of the servo,
and to send these pulses every 20 milliseconds or so.
/* Quick interactive demo running servo with Timer 1 */
// ------- Preamble -------- //
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include "pinDefines.h"
#include "USART.h"
#define PULSE_MIN 1000 /* experiment with these values */
#define PULSE_MAX 2000 /* to match your own servo */
#define PULSE_MID 1500
static inline uint16_t getNumber16(void);
static inline void initTimer1Servo(void) {
/* Set up Timer1 (16bit) to give a pulse every 20ms */
/* Use Fast PWM mode, counter max in ICR1 */
TCCR1A |= (1 << WGM11);
TCCR1B |= (1 << WGM12) | (1 << WGM13);
TCCR1B |= (1 << CS10); /* /1 prescaling -- counting in microseconds */
ICR1 = 20000; /* TOP value = 20ms */
TCCR1A |= (1 << COM1A1); /* Direct output on PB1 / OC1A */
DDRB |= (1 << PB1); /* set pin for output */
}
static inline void showOff(void) {
printString("Center\r\n");
OCR1A = PULSE_MID;
_delay_ms(1500);
printString("Clockwise Max\r\n");
OCR1A = PULSE_MIN;
_delay_ms(1500);
printString("Counterclockwise Max\r\n");
OCR1A = PULSE_MAX;
_delay_ms(1500);
printString("Center\r\n");
OCR1A = PULSE_MID;
_delay_ms(1500);
}
int main(void) {
// -------- Inits --------- //
uint16_t servoPulseLength;
OCR1A = PULSE_MID; /* set it to middle position initially */
initTimer1Servo();
initUSART();
printString("\r\nWelcome to the Servo Demo\r\n");
showOff();
// ------ Event loop ------ //
while (1) {
printString("\r\nEnter a four-digit pulse length:\r\n");
servoPulseLength = getNumber16();
printString("On my way....\r\n");
OCR1A = servoPulseLength;
DDRB |= (1 << PB1); /* re-enable output pin */
_delay_ms(1000);
printString("Releasing...\r\n");
while (TCNT1 < 3000) {;
} /* delay until pulse part of cycle done */
DDRB &= ~(1 << PB1); /* disable output pin */
} /* End event loop */
return (0); /* This line is never reached */
}
static inline uint16_t getNumber16(void) {
// Gets a PWM value from the serial port.
// Reads in characters, turns them into a number
char thousands = '0';
char hundreds = '0';
char tens = '0';
char ones = '0';
char thisChar = '0';
do {
thousands = hundreds; /* shift numbers over */
hundreds = tens;
tens = ones;
ones = thisChar;
thisChar = receiveByte(); /* get a new character */
transmitByte(thisChar); /* echo */
} while (thisChar != '\r');
transmitByte('\n'); /* newline */
return (1000 * (thousands - '0') + 100 * (hundreds - '0') +
10 * (tens - '0') + ones - '0');
}
The other main difference is the addition of a separate ICR1 register, which can be
used to set the PWM frequency. The details involve how the AVR, an 8-bit machine,
deals with 16-bit numbers, but the upshot is that we can use ICR1 to set the frequency
for most PWM modes when we’re not changing the frequency often, and
use OCR1A to set the frequency if we are changing it a lot.
No comments:
Post a Comment