Независимое управление несколькими сервоприводами. Таймеры

Серия-статей: Программирование Arduino с нуля #5

Серия-статей: Arduino, использование сервоприводов #2

Мы научились менять положение сервопривода, но такое управление обладает несколькими недостатками. Попробуем подключить параллельно два сервопривода и добиться их синхронного перемещения.

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

#include // Подключаем библиотеку для работы с сервоприводом

Servo servo1; // Создаем объект типа «сервопривод»

Servo servo2; // Создаем еще один объект типа «сервопривод»

void setup()

int angle1 = 0; // Переменная, в которой хранится положение первого сервопривода

int angle2 = 0; // Переменная, в которой хранится положение второго сервопривода

{

servo1.attach(2); // Объясняем контроллеру, что управляющий провод первого сервопривода подключен к пину 2

servo2.attach(3); // Объясняем контроллеру, что управляющий провод первого сервопривода подключен к пину 3

}

void loop()

{

for (angle1 = 0; angle1 <= 180; angle1 += 1) // Начинаем движение с 0 до 180 градусов

{

servo1.write(angle1); // Даем команду первому сервоприводу принять новое положение

delay(10); // Делаем паузу перед следующим перемещением

}

for (angle2 = 0; angle2 <= 180; angle2 += 1) // Начинаем движение с 0 до 180 градусов

{

servo1.write(angle2); // Даем команду второму сервоприводу принять новое положение

delay(10); // Делаем паузу перед следующим перемещением

}

}

Результат действия программы несколько отдичатеся от необходимого нам. Сервоприводы движутся не синхронно, второй начинает движение только после первого. Дело в том, что команда delay() останавливает выполнение всех действий на заданное время. И второй цикл не начнет выполняться до тех пор, пока не будет завершен первый. Самое, на первый взгляд, простое решение – давать команду обеим сервоприводам в одном цикле:

for (angle = 0; angle <= 180; angle += 1) // Начинаем движение с 0 до 180 градусов

{

servo.write(angle1); // Даем команду первому сервоприводу принять новое положение

servo.write(angle1); // Даем команду первому сервоприводу принять новое положение

delay(10); // Делаем паузу перед следующим перемещением

}

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

Можно заметить, что для отсчета времени мы используем переменную тпа unsigned long. Это связано с тем, что тип int может хранить числа от -32 768 до 32 767. Поскольку мы оперируем миллисекундами, то через 32,67 секунды с начала работы программы мы уже не сможем использовать int для хранения времени отсчета. Иначе поведение программы станет непредсказуемым. А вот тип long может хранить уже число от -2 147 483 648 до 2 147 483 647, а без учета знака unsigned long – от 0 до 4 294 967 295. Т.е максимальное значение таймера - 4 294 967 секунд или почти 49 суток. Не думаю, что наша программа будет работать столько без перерыва
Самый простой способ – воспользоваться функцией millis(). Эта функция возвращает количество времени, прошедшее с запуска программы, с точностью до одной тысячной секунды. Добавим в нашу функцию loop() опрос текущего времени. Будем выполнять нужное действие – поворот сервопривода – только если с предыдущего опроса прошло определенное время

#include // Подключаем библиотеку для работы с сервоприводом

Servo servo1; // Создаем один объект типа «сервопривод»

int angle = 0; // Переменная,в которой хранится положение сервопривода

int interval = 10; // Интервал времени, через который будет производиться поворот сервопривода на один градус

unsigned long lastTimeCheck; // Время последнего движения сервопривода, в мс от начала работы программы

void setup()

{

servo1.attach(2); // Объясняем контроллеру, что управляющий провод сервопривода подключен к пину 2

lastTimeCheck=millis(); // Отчсет времени для движений сервопривода будет начинаться вместе с началом работы программы

}

void loop()

{

If (angle < 180) // Пока не достигнем крайнего положения

{

If ((millis()-lastTimeCheck)>= interval) // проверяем, сколько прошло времени с последнего

//действия и сравниваем с нужным нам интервалом.

//Если времени прошло больше – совершаем следующее действие

{

angle++; // Изменяем угол на один градус

servo1.write(angle); // Посылаем сервоприводу команду с новым углом поворота

lastTimeCheck= millis(); // Обновляем счетчик времени, для того чтобы следующий отсчет времени начался с этого момента

}

}

}

С первого взгляда ничего не изменилось, сервопривод точно также плавно меняет свое положение за 1,8 секунды. Теперь подключим два сервопривода

Servo servo1; // Создаем один объект типа «сервопривод»

int angle1 = 0; // Переменная,в которой хранится положение первого сервопривода

int interval1 = 10; // Интервал времени, через который будет производиться поворот первого сервопривода на один градус

unsigned long lastTimeCheck1; // Время последнего движения первого сервопривода, в мс от начала работы программы

Servo servo2; // Создаем еще один объект типа «сервопривод»

int angle2 = 0; // Переменная,в которой хранится положение второго сервопривода

int interval2 = 20; // Интервал времени, через который будет производиться поворот второго сервопривода на один градус

unsigned long lastTimeCheck2; // Время последнего движения второго сервопривода, в мс от начала работы программы

void setup()

{

servo1.attach(2); // Объясняем контроллеру, что управляющий провод сервопривода подключен к пину 2

servo1.attach(3); // Объясняем контроллеру, что управляющий провод второго сервопривода подключен к пину 3

lastTimeCheck=millis(); // Отчсет времени для движений сервопривода будет начинаться вместе с началом работы программы

lastTimeCheck2=millis(); // Отчсет времени для движений сервопривода будет начинаться вместе с началом работы программы

}

void loop()

{

if (angle1 < 180) // Пока не достигнем крайнего положения

{

If ((millis()-lastTimeCheck1)>= interval1)// проверяем, сколько прошло времени с последнего

//действия и сравниваем с нужным нам интервалом.

//Если времени прошло больше – совершаем следующее действие

{

angle1++; // Изменяем угол на один градус

servo1.write(angle1); // Посылаем первому сервоприводу команду с новым углом поворота

lastTimeCheck1= millis(); // Обновляем счетчик времени, для того чтобы следующий отсчет времени начался с этого момента

}

}

if (angle2 < 180) // Пока не достигнем крайнего положения

{

if ((millis()-lastTimeCheck2)>= interval2) // проверяем, сколько прошло времени с последнего

//действия и сравниваем с нужным нам интервалом.

//Если времени прошло больше – совершаем следующее действие

{

angle2++; // Изменяем угол на один градус

servo1.write(angle2); // Посылаем второму сервоприводу команду с новым углом поворота

lastTimeCheck2= millis(); // Обновляем счетчик времени, для того чтобы следующий отсчет времени начался с этого момента

}

}

}

Единственное, нужно учитывать, что при малых значениях интервала и большом количестве действий, выполняемых в loop() частота вызова этой функции может оказаться больше, чем заданный интервал. Соответсвенно действие будет совершаться не через указанное время, а только при следующем вызове loop() Такая ситуация может возникнуть, например, при непрерывном выводе текста через Serial.print(). Не рекомендуется помещать эту функцию в loop(). А если нам необходимо непрерывно выводить какую-то информацию, то можно ограничить частоту таким же образом, как и для движения сервоприводов:

if ((millis()-lastMessageTime)>= interval) // проверяем, сколько прошло времени с последнего вывода сообщения

// и сравниваем с нужным нам интервалом. Если времени прошло больше – шлем следующее сообщение

{

Serial.print(“time=”);

Serial.println(lastMessageTime);

lastMessageTime = millis(); // Обновляем счетчик времени, для того чтобы

// следующий отсчет времени начался с этого момента

}

Теперь мы добились того, чтобы сервоприводы совершали все действия независимо. Причем параллельно с движением можно продолжать выполнение программы и совершать любые другие действия

Еще:

Программирование контроллеров – с чего начать (Программирование Arduino с нуля #1)
Подключение периферии, платы расширения (Программирование Arduino с нуля #2)
Организация питания для Arduino (Программирование Arduino с нуля #3)
Программное ограничение перемещения сервопривода (Arduino, использование сервоприводов #4)
Подключение шагового двигателя. Контроллер L298 (Программирование Arduino с нуля #8)
Подключаем сервопривод к Arduino (Программирование Arduino с нуля #4)
Создаем класс для управления сервоприводом (Программирование Arduino с нуля #6)