Целью этой статьи является обзор того, как работает сервопривод и как управлять им с помощью микроконтроллеров AVR.

Сервопривод нашел широкое применение в радиоуправляемых моделях, робототехнике и автоматике. Он управляется по одному сигнальному проводу с помощью импульсов переменной ширины частота которых равна 50Гц. Основными параметрами этих импульсов являются минимальная ширина, максимальная ширина и частота. Сейчас на рынке представлено огромное количество моделей приводов у которых сигналы управления могут отличаться, но в основном ширина импульса 1 мс соответствует углу поворота в 0 градусов, а ширина в 2 мс соответствует углу в 180 градусов.

 

Нейтральное положение определяется как положение, в котором сервопривод обладает одинаковым потенциалом вращения в обоих направлениях. Важно отметить, что различные сервоприводы обладают разными ограничениями в своем вращении, но они все имеют нейтральное положение, и это положение всегда находится в районе ширины импульса в 1,5 мс.

Когда сервопривод получает команду на перемещение, он перемещается в это положение и удерживает его. Если внешняя сила действует на сервопривод, когда он удерживает заданное положение, сервопривод будет сопротивляться перемещению из этого положения. Максимальная величина силы, которую может выдерживать сервопривод, характеризует вращающий момент сервопривода. Однако, сервопривод не навсегда удерживает свое положение, импульсы позиционирования должны повторяться, информируя сервопривод о сохранении положения.

Схема подключения сервопривода

Эта схема служит для тестирования сервомашинок в ручном или авто режиме. Здесь я использовал микроконтроллер ATmega328, который тактируется от внутреннего тактового генератора частотой 8МГц. Кнопка S1 "Режим" переключает режимы работы устройства, ручной и авто режим. При нажатии кнопки S2 "Центр" привод перемещается в центральное положение в ручном режиме при этом загорается светодиод D1. Потенциометр R2 предназначен для регулировки угла поворота привода в ручном режиме или скорости движения в авто режиме. Большинство сервомашинок имеет трехпроводное подключение, плюс питания(красный), минус питания(черный или коричневый) и сигнал управления(оранжевый, желтый или белый).

Получить сигнал с изменяемой шириной и частотой 50 Гц(периодом 20мс) достаточно легко, для этого используется 16 битный таймер/счетчик в режиме FastPWM. Частота выходного сигнала в режиме FastPWM вычисляется по формуле:

f = fclk/(N * (1 + ICR1))

где fclk - частота микроконтроллера;

N - предделитель таймера;

ICR - предел счета.

В нашем случае f = 8000000/(8*(1 + 20000)) = 50Гц(или 20мс)

Подготовим регистры таймера TCCR1A/B: выход таймера OC1A настроим на сброс при совпадении и установку на вершине счета, выбираем режим FastPWM с пределом счета ICR1, так как частота тактирования контроллера равна 8МГц включим предделитель на 8, чтобы получить частоту таймера 1МГц.

Для генерации ШИМ-сигнала на выходе OC1A используется регистр OCR1A. Ширина импульса сигнала изменяется путем записи определенных значений в регистр OCR1A, например при значении 1500 ширина импульса будет равна 1,5мс.

Ниже представлен исходный код программы с подробными комментариями:

// Управление сервоприводом с помощью микроконтроллеров AVR
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define MANUAL_MODE   0
#define AUTO_MODE    1

#define MAX_t     2000
#define MIDDLE_t  1500
#define MIN_t     1000

unsigned int impulse; 
volatile unsigned char mode = MANUAL_MODE;
unsigned char dir;

// Прерывание по совпадению T0
ISR(TIMER0_COMPA_vect)
{ 
  if(mode == AUTO_MODE) // Авто режим
  {
    ADCSRA |= (1 << ADSC);        // Старт одиночного преобразования        
    while (ADCSRA & (1 << ADSC)); // Ждем окончания преобразования      
    OCR0A = ADCH; // Записываем в регистр сравнения значение АЦП
	
	if(dir)
    {
    impulse++; // Увеличиваем ширину импульса
    // Если ширина импульса превысила макс. значение
	if(impulse > MAX_t) dir = 0; // Меняем направление
    }
    else
    {
    impulse--; // Уменьшаем ширину импульса
    // Если ширина импульса меньше мин. значения    
	if(impulse < MIN_t) dir = 1; // Меняем направление
    }
  }
}

int main(void)
{  
// Настройка портов ввода/вывода
DDRB |= (1 << PB1)|(1 << PB0);
DDRD = 0x00;
PORTD = 0xFF;

// Настройка Т0
TCCR0A |= (1 << WGM01);              // Режим СТС
TCCR0B |= (1 << CS01)|(1 << CS00);   // Предделитель на 64
TIMSK0 |= (1 << OCIE0A);             // Разрешаем прерывание по совпадению Т0

// Настройка Т1
TCCR1A = (1 << COM1A1)|(1 << WGM11); // Сброс вывода OC1A при совпадении, усановка на вершине счета  
TCCR1B = (1 << WGM13)|(1 << WGM12)   // Режим Fast PWM, с пределом счета ICR1
        |(1 << CS11);                // Предделитель на 8

ICR1 = 20000;     // Устанавливаем период сигнала 20мс 
OCR1A = MIDDLE_t; // Устанавливаем ширину импульса 1.5мс

// Настройка АЦП    
ADMUX |= (1 << ADLAR); // Выравнивание результата влево, читаем только ADCH
ADCSRA |= (1 << ADEN) // Разрешаем АЦП
        |(1 << ADPS2)|(1 << ADPS1); // Предделитель на 64, 8MHz/64 = 125kHz

sei(); // Глобально разрешаем прерывания

// Главный цикл
while(1)
{
OCR1A = impulse;
 
if(mode == MANUAL_MODE)
{
  // Если нажата кнопка переводим привод в среднее положение
  if(!(PIND & (1 << PD1)))
  {
  impulse = MIDDLE_t; // 1.5mS
  PORTB |= (1 << PB0); // Включаем светодиод
  }
  // Иначе ручной режим
  else
  {
  ADCSRA |= (1 << ADSC);        // Старт одиночного преобразования        
  while (ADCSRA & (1 << ADSC)); // Ждем окончания преобразования
  impulse = MIN_t + ((MAX_t - MIN_t)/255.0)*ADCH;
  OCR0A = 0xFF;
  // Если попали в центральное положение +-10мкс.
  if(impulse <= MIDDLE_t + 10 && impulse >= MIDDLE_t - 10) 
  PORTB |= (1 << PB0); // Включаем светодиод
  else PORTB &= ~(1 << PB0); // Иначе выключаем светодиод
  _delay_ms(1);  
  }
}

if(!(PIND & (1 << PD0))) // Переключаем режимы работы
{
while(!(PIND & (1 << PD0))){} // Ждем отпускания кнопки
_delay_ms(100);
if(mode == MANUAL_MODE)  // Если до этого был ручной режим
mode = AUTO_MODE;       // Выбираем авто режим
else mode = MANUAL_MODE; // Иначе ручной режим
}

}
}