Незалежне керування кількома сервоприводами. Таймери
Серия-статей: Програмування Ардуіно з нуля #5
Серия-статей: Ардуіно, використання сервоприводів #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); // Робимо паузу перед наступним переміщенням
}
Але в такому разі сервоприводи зможуть рухатися лише з однаковою швидкістю, що підходить далеко не завжди. Нам же потрібний універсальний варіант, з повністю незалежним управлінням. Тому замість використання затримки є сенс скористатися таймерами.
{{div|additionalright|Можна помітити, що для відліку часу ми використовуємо змінну тпа unsigned long
. Це пов'язано з тим, що тип int може зберігати числа від -32768 до 32767. Оскільки ми оперуємо мілісекундами, то через 32,67 секунди з початку роботи програми ми вже не зможемо використовувати int для зберігання часу відліку. Інакше поведінка програми стане непередбачуваною. А ось тип long
може зберігати число від -2 147 483 648 до 2 147 483 647, а без урахування знака unsigned long
– від 0 до 4 294 967 295. Т .е максимальне значення таймера - 4294967 секунд або майже 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(); // Оновлюємо лічильник часу, щоб
// Наступний відлік часу розпочався з цього моменту
}
Тепер ми досягли того, щоб сервоприводи здійснювали всі дії незалежно. Причому паралельно з рухом можна продовжувати виконання програми та здійснювати будь-які інші дії