Подключаем сервопривод к STM32

Те кто занимаются робототехникой наверняка прекрасно знают, что такое сервопривод и для чего он нужен. Ну а лично я встретился с этим механизмом впервые и в этой статейке хочу попытаться максимально просто объяснить как подключить сервопривод к STM32. Итак, что же представляет собой этот хитрый механизм: Сервопривод - это устройство позволяющее относительно точно задавать и сохранять положение своего вала. В отличии от обычных двигателей вал которых крутится постоянно, в сервоприводе он поворачивается на определённый угол. Причем если какое либо внешнее воздействие будет стараться изменить угол поворота, то хитроумная схема внутри будет заставлять сервопривод вернуть вал на место. Хотя если расковырять и присмотреться, то ничего шибко хитрого: Внутри небольшая платка, коллекторный двигатель, переменный резистор и редуктор. Электроника определяет текущее положение вала благодаря переменному резистору который вращается вместе с ним. Из сервопривода выходят три провода - питание и управляющий. Электроника сравнивает текущее положение вала с тем которое должно быть (исходя из управляющего сигнала) и если есть разница, то пытается эту разницу свести на нет за счет вращения двигателя в ту или другую сторону. В силу своего нищебродства я купил самый дешевый сервопривод Mystery SD-90 за 4 бакса, вот он:

servo logo
 
Хотя для экспериментов нам хватит и этого, ведь наша цель научиться управлять этой штукой без всякой нагрузки. Подключение к контроллеру дело не хитрое: нужно подсоединить всего лишь один провод к любому выводу контроллера. Но лучше всего подсоединять не к какому попало, а к тому откуда можно снимать ШИМ сигнал, ведь именно он понадобится нам для управления этой железкой. Я прицепил сигнальный провод сервы на вывод PA1. Кстати распиновка такая: Коричневый - земля, красный - питание, желтый - сигнал. Питать можно от пяти вольт без всяких стабилизаторов. В момент старта или если вал заклинить рукой, светодиод на платке дискавери начинает тускнеть, это говорит о неплохих аппетитах сервопривода. Однако в момент простоя она практически ничего не потребляет. Теперь о том как эти делом рулить. Вал моего сервопривода может поворачиваться на угол от 0 до 180 градусов. Для того чтоб сказать серве на какой угол нужно повернуть вал мы должны подавать на сигнальный провод импульсы. Чем длинней импульс - тем больше угол поворота. Если верить интернетам, то рекомендуемая частота подачи управляющих импульсов 50 гц. Однако у меня прекрасно работало и на 300 гц :) А вот если понижать частоту до очень низкой то она становится какой-то тормозной. Для того чтоб повернуть вал в ноль градусов я подавал импульсы длительностью 520 микросекунд

servo 0

Меня очень удивил тот факт, что нет ни какой защиты от импульсов не правильной длительности. То есть если чуть уменьшить длину импульса то начинается хруст пластмассовых шестерёнок. Та же самая ситуация когда мы пытаемся посылать слишком длинные импульсы. Не исключено, что в более дорогих моделях этого недостатка нет. Для установки вала сервопривода в 90 градусов, я подавал импульсы длительностью 1.44 миллисекунды:

servo 90

Кстати если прекратить подачу импульсов после того как сервопривод встал в нужное положение то он в нем и останется. Но при попытке сдвинуть вал - он без проблем провернется. То есть если нужно поддерживать положение то подача импульсов не должна прекращаться. И наконец еще одно крайнее положение  - 180 градусов. Длинна импульса 2.36 миллисекунды:

servo 180

Я заметил, что длительности импульсов немного расходятся с теми про которые обычно пишут в интернетах (для других серв), возможно я что-то поломал. Она у меня адски хрустела шестерёнками пока я не подобрал правильные длительности импульсов. Поэтому возможно что код который приведен ниже придётся немного подправить для другой модели  сервопривода. Понадобится всего лишь задать две константы:
#define SERVO_180 8200
#define SERVO_0 1800
Первая - число которое нужно записать в регистр сравнения для получения импульсов такой длинны чтоб вал сервопривода встал в положение 180°, ну а вторая соответственно тоже самое но для положения 0°. Для устновки положения вала используется функция set_pos в которую нужно передать желаемый угол поворота вала. В остальном этот код очень похож на тот который был написан в статье про ШИМ


#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#define SERVO_180 8200
#define SERVO_0 1800
// Функция устанавливает позицию вала (в градусах)
void set_pos(uint8_t pos) {
  uint32_t tmp=(SERVO_180 - SERVO_0) /180 ;
  TIM2->CCR2 = SERVO_0 + tmp * pos;
}
// Функция задержки
void delay(void) {
  volatile uint32_t i;
  for (i=1; i != 0xFFFF; i++)
    ;
 }
int main()
{
  //Включем порт А
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
  //Включаем Таймер 2
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
  GPIO_InitTypeDef PORT;
  // Настроим ногу (PA1) к которой подключен сервопривод
  PORT.GPIO_Pin = (GPIO_Pin_1);
  //Будем использовать альтернативный режим а не обычный GPIO
  PORT.GPIO_Mode = GPIO_Mode_AF_PP;
  PORT.GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_Init(GPIOA, &PORT);
  //Разрешаем таймеру использовать ногу PA1 для ШИМа
  TIM2->CCER |= (TIM_CCER_CC2E);
  TIM2->CCMR1|= (TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2);
  //Настраиваем предделитель чтоб частота ШИМа была в районе 50 Гц
  TIM2->PSC = 6;
  //Запускаем таймер
  TIM2->CR1 |= TIM_CR1_CEN;
  uint8_t i;
  //Начинаем крутить сервой от 0 до 180 градусов. 
  while(1)
  {
    for (i=0;i<=180;i++) {
      delay();
      set_pos(i);
    }
  }
}



Комментарии и вопросы как всегда приветствуются.