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

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

Серия-статей: Ардуіно, використання сервоприводів #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); // Робимо паузу перед наступним переміщенням

}

Але в такому разі сервоприводи зможуть рухатися лише з однаковою швидкістю, що підходить далеко не завжди. Нам же потрібний універсальний варіант, з повністю незалежним управлінням. Тому замість використання затримки є сенс скористатися таймерами.

{{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(); // Оновлюємо лічильник часу, щоб

// Наступний відлік часу розпочався з цього моменту

}

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

Еще:

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