Независимое управление несколькими сервоприводами. Таймеры
Серия-статей: Программирование Arduino с нуля #5
Серия-статей: Arduino, использование сервоприводов #2
#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(); // Обновляем счетчик времени, для того чтобы
// следующий отсчет времени начался с этого момента
}
Теперь мы добились того, чтобы сервоприводы совершали все действия независимо. Причем параллельно с движением можно продолжать выполнение программы и совершать любые другие действия