Первое, с чем сталкивается осваивающий Arduino новичок, это неприятное свойство функции delay() — блокирование выполнения программы. Множество примеров в интернете используют эту функцию, но практическое применение как-то намекает, что лучше без неё обойтись.
Как и положено начинающему, яизобрёл велосипед сделал свою реализацию неблокирующей задержки. Задача стояла так:
Подсмотрев, что большинство ардуинских библиотек сделаны с применением ООП, я тоже решил не выделываться и написал класс SmartDelay, который можно получить с гитхаба как zip для добавления в Arduino IDE или сделать git clone в ~/Arduino/libraries/
В результате получилось вот такое.
Метод Now() возвращает true, если интервал прошёл. В этом случае отсчёт начинается снова на тот же интервал. То есть, Now() каждый раз «перезаряжается» автоматически.
Классическое мигание светодиодом можно сразу усложнить до мигания двумя. Например, лампочки подключены к ножкам 12 и 11, должны мигать с интервалом в 1с и 777мс соответственно.
В цикле можно выполнять ещё что-то, мигание светодиодов не будет блокировать выполнение этого кода.
Понятно, что это не полная замена delay(), который останавливает по��ок на заданное время, надо писать программу всегда как МКА (механизм конечных автоматов). То есть, хранить состояние и в зависимости от него переходить к нужному месту кода.
Старый вариант:
Новый вариант:
Метод Set(интервал) устанавливает новый интервал и возвращает старый. Просто посмотреть на интервал можно методом Get();
Stop() останавливает обработку и Now() всегда возвращает false.
Start() возобновляет работу и Now() начинает работать как обычно.
Если надо притормозить подсчёт времени, но не останавливать совсем, то есть метод Wait(). Например, если мигает светодиод 12, а при нажатии кнопки не мигает, достаточно добавить вот такой код в loop() в примере с двумя диодами выше:
Так, при высоком уровне сигнала на 9 ноге диод на 12 мигать не будет и продолжит, когда там появится 0.
Когда по такому «таймеру» отрисовывается экран, например, и параллельно обрабатываются кнопки, то бывает нужно перерисовать экран или часть сразу после нажатия на кнопку, а не ждать окончания интервала. Для этого служит метод Reset(), после которого следующий вызов Now() вернёт true. Например:
Из багов я вижу только, что не учитывается переполнение счётчика микросекунд, а в остальном да, надо почистить код. Мне не нравится, как сделан Reset(), пока думаю.
Объектный подход мне понравился, позволяет спрятать весь код в библиотеку, в которую можно потом уже никогда не заглядывать. Теперь эта маленькая библиотечка живёт во всем моих проектах :)
→ Проект на GitHub
Как и положено начинающему, я
- Обеспечить псевдомногозадачность, чтобы разные события происходили в своё время, со своими интервалами и не блокировали друг-друга.
- Было удобно этим пользоваться.
- Можно было оформить как библиотеку и легко включать в другие проекты без копипастов.
Подсмотрев, что большинство ардуинских библиотек сделаны с применением ООП, я тоже решил не выделываться и написал класс 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
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Имело смысл это делать?
41.95%Полезно.125
25.5%Хорошая попытка.76
3.69%Фигня.11
10.74%Бессмысленный говнокод.32
1.01%Я сделал лучше!3
11.07%Всё придумано до нас!33
6.04%Что такое delay()?18
Проголосовали 298 пользователей. Воздержались 94 пользователя.
