Часто приходится слышать вопросы “как избавиться от delay
, а то во время ожидания кнопки (или ещё чего-нибудь) не опрашиваются?”
На самом деле, если обработка не особо сложная, то не стоит прямо сразу кидаться во все тяжкие, прощаться с простым как валенок delay
и всё усложнять. Старина delay
ещё послужит. Он и не такие кнопки видал.
Просто нужно знать, что во время ожидания в delay
постоянно вызывается специальная функция yield
. Если Вы напишете функцию с таким именем, то она и будет вызываться. И опрашивайте в ней кнопки сколько угодно, а в основной программе останется честный, простоватый и скромный трудяга delay
.
Например, у нас имеется “блинк” на два светодиода (зелёный и красный). Сначала мигает зелёный светодиод, по нажатию кнопки он перестаёт мигать и начинает мигать красный. По следующему нажатию кнопки красный перестаёт мигать и начинает мигать снова зелёный и так далее.
Вот пример реализации. Обратите внимание на функцию loop - в ней самый обыкновенный “блинк с delay
” - никаких фокусов. Опрос кнопки и переключение светодиодов находится в функции yield
, которую мы сами никогда не вызываем, но которая постоянно вызывается во время ожидания по delay
.
Пример
//////////////////////////////////////////////////////////
//
// Определения пинов
//
constexpr uint8_t
pinButton = 3, // пин кнопки
pinLEDRed = 11, // пин красного светодиода
pinLEDGreen = 12; // пин зелёного светодиода
//////////////////////////////////////////////////////////
//
// Тип данных "Светодиод".
//
// Конструктор имеет параметром номер пина
// метод "погасить" - void off(void)
// метод "инвертировать" - void toggle(void)
//
class TheLED {
uint8_t pin;
public:
inline TheLED(const uint8_t _pin) : pin(_pin) { pinMode(pin, OUTPUT); }
inline void off(void) { digitalWrite(pin, LOW); }
inline void toggle(void) { digitalWrite(pin, !digitalRead(pin)); }
};
//////////////////////////////////////////////////////////
//
// Объявление массива из двух светодиодов
// переменной - текущий светодиод (индекс в массиве: 0 или 1)
// и функции переключить текущий светодиод (сначала гасит
// текущий, а потом переключает)
//
static TheLED leds[] = { TheLED(pinLEDGreen), TheLED(pinLEDRed) };
static int8_t currentLed = 0;
inline void toggleLeds(void) {
leds[currentLed].off();
currentLed = 1 - currentLed;
}
//////////////////////////////////////////////////////////
//
// Функция, которая возвращает true, если кнопка была нажата.
// Кнопка считается нажатой, если её текущее состояние "нажата",
// а предыдущее было - "отпущено". Пин должен быть INPUT_PULLUP
//
inline bool isButtonPressed(void) {
static bool oldButtonState = false;
const bool newButtonState = ! digitalRead(pinButton);
const bool result = newButtonState && ! oldButtonState;
oldButtonState = newButtonState;
return result;
}
//////////////////////////////////////////////////////////
//
// Функция, которая постоянно вызывается во время ожидания (delay)
// Если была нажата кнопка она переключает программу на "другой" светодиод
//
void yield(void) {
if (isButtonPressed()) toggleLeds();
}
//////////////////////////////////////////////////////////
//
// В setup нужно только проинициализировать пин кнопки
//
void setup(void) {
pinMode(pinButton, INPUT_PULLUP);
}
//////////////////////////////////////////////////////////
//
// Обычный блинк - инвертируем текущий светодиод и ждём в delay.
// Больше ничего делать не надо. Кнопка обработается в yield
//
void loop(void) {
leds[currentLed].toggle();
delay(100);
}
// Всё. А разговоров-то было!