Заміна delay() для неблокирующих затримок у Arduino IDE

Перше, з чим стикається освоює Arduino новачок, це неприємна властивість функції delay() — блокування виконання програми. Безліч прикладів в інтернеті використовують цю функцію, але практичне застосування як натякає, що краще без неї обійтися.

Як і належить початківцю, я винайшов велосипед зробив свою реалізацію неблокірующіх затримки. Завдання стояло так:

  • Забезпечити псевдомногозадачность, щоб різні події відбувалися в свій час, зі своїми інтервалами і не блокували один одного.
  • Було зручно користуватися цим.
  • Можна було оформити як бібліотеку і легко включати в інші проекти без копипастов.


Підглянувши, що більшість ардуинских бібліотек зроблені із застосуванням ООП, я теж вирішив не вироблятися і написав клас SmartDelay, який можна отримати з гитхаба як zip для додавання в Arduino IDE або зробити git clone в ~/Arduino/libraries/

У результаті вийшло ось таке.

#include <SmartDelay.h>

SmartDelay foo(1000000UL); // у мікросекундах

void loop () {
if (foo.Now()) {
// Код тут виконується кожен інтервал в мікросекундах, зазначений у конструкторі вище.
}
//Інший код
}


Метод Now() повертає true, якщо інтервал пройшов. У цьому разі відлік починається знову на той самий інтервал. Тобто, Now() кожен раз «перезаряджається» автоматично.

Класичне миготіння світлодіодом можна відразу ускладнити до миготіння двома. Наприклад, лампочки підключені до ніжок 12 і 11, повинні мигати з інтервалом в 1с і 777мс відповідно.

#include <SmartDelay.h>

SmartDelay led12(1000000UL); 
SmartDelay led11(777000UL);

setup () {
pinMode(12,OUTPUT);
pinMode(11,OUTPUT);
}

byte led12state=0;
byte led11state=0;

void loop () {
if (led12.Now()) {
digitalWrite(12,led12state);
led12state=!led12state;
}
if (led11.Now()) {
digitalWrite(11,led11state);
led11state=!led11state;
}
}


У циклі можна виконувати ще щось, миготіння світлодіодів не буде блокувати виконання цього коду.

Зрозуміло, що це не повна заміна delay(), який зупиняє потік на заданий час, треба писати програму завжди як МКА (механізм кінцевих автоматів). Тобто, зберігати стан і залежно від нього переходити до потрібного місця коду.

Старий варіант:

...
action1();
delay(1000);
action2();
delay(500);
action3();
...


Новий варіант:

byte state=0;
SmartDelay d();
...
switch (state) {
case 0: 
action1(); 
d.Set(1000000UL);
state=1;
break;
case 1:
if (d.Now) {
action2();
d.Set(500000UL);
state=2;
}
break;
case 2:
if (d.Now) {
action3();
d.Stop();
state=0;
}
break;
}
...


Метод Set(інтервал) встановлює новий інтервал і повертає старий. Просто подивитися на інтервал можна методом Get();

Stop() зупиняє обробку та Now() завжди повертає false.

Start() відновлює роботу та Now() починає працювати як звичайно.

Якщо треба пригальмувати підрахунок часу, але не зупиняти зовсім, тобто метод Wait(). Наприклад, якщо блимає світлодіод 12, а при натисканні кнопки не блимає, досить додати ось такий код у loop() в прикладі з двома діодами вище:

...
if (digitalRead(9)) led12.Wait();
...


Так, при високому рівні сигналу на 9 нозі діод на 12 блимати не буде і надалі, коли там з'явиться 0.

Коли по такій «таймер» малюється екран, наприклад, і паралельно обробляються кнопки, то буває потрібно перемалювати екран або частину відразу після натискання на кнопку, а не чекати закінчення інтервалу. Для цього служить метод Reset(), після якого наступний виклик Now() поверне true. Наприклад:

SmartDelay display(1000000UL);

void loop() {
if (btClick()) display.Reset(); // тицьнув у кнопку, треба промалювати екранчик.
if (display.Now()) screenRedraw(); // вивід екранчика.
}


З багів я бачу тільки, що не враховується переповнення лічильника мікросекунд, а в іншому так, треба почистити код. Мені не подобається, як зроблений Reset(), поки думаю.

Об'єктний підхід мені сподобався, дозволяє приховати увесь код в бібліотеку, в яку можна потім вже ніколи не заглядати. Тепер ця маленька бібліотечка живе у всіх моїх проектах. :)

Проект на гітхабі от: github.com/nw-wind/SmartDelay



Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.