Блинк с delay и с кнопкой

Часто приходится слышать вопросы “как избавиться от 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);
}

// Всё. А разговоров-то было!
6 лайков

как просто оказывается паразитировать на millis() )))

А теперь в yield() засунем delay(). У нас delay() как гуталину на фабрике .у дяди кота Матроскина.

1 лайк

И можно приглашать деДивана, шоб он на АСМе, виртуозно стек разгребал.

А мне вот 38-я строка не нравится.
Я бы сначала инкрементировал, а потом - по модулю numLeds. Ну так - для методического единства. И, кстати, эту numLeds можно статическим полем класса - инкрементимровать в конструкторе.

Во как! Лучшее - враг хорошего.

Ну, первое - дело вкуса, а вот второе - методически не верно. Эта переменная не имеет отношения к классу - она относится к агрегату (массиву), т.е. к следующему уровню данных. Вот если бы мы массив в класс оборачивали, то в этом классе она была бы уместна.

То есть экземпляр класса не имеет права знать, сколько еще таких экземпляров создано?

ЕвгенийП, всё это красиво, но ведь и без всяких там классов и плюсов, это всё делается элементарно, не так ли? По крайней мере в этой задаче. Или, а вот сейчас, вы будете программировать для ПИК, а там нету всех этих плюсовых фишек, и чо? Пиши по новой? Как то не аккуратненько. Или нет?

У пиков и нет delay() как в ардуино ИДЕ, Хотя пикдуино много слышал, но ни разу не смотрел что это и что за ИДЕ…

Поразительно! А что мешает его заиметь? Ужос.(

Пикдуино? Без надобности (пока, по крайней мере).

Так ведь и у АВР, зачастую, всё это излишне. А чем то ПИК хуже?

Ну и не везде millis имеется, ну да ладно, это можно пристроить, так ведь и yield же ж нужно как то организовавать… Заради чего?

Чтобы показать новичкам, которые не хотят стрелять нормально, как пальнуть по мишени, не вытаскивая ствола из карманов брюк.

Да, супер. Впечатляет.)
Зачастую, просто понты. Всё решаемо и безо всяких миллис.

Имеет, но это методологически неверно. Класс должен инкапсулировать всё, что связано с ним самим и только. Всё что шире - это не его забота.

1 лайк

Я пишу так, как мне комфортно. Как говаривал Булат (в крещении Иоанн) Шалвович Окуджава:

«Каждый пишет, как он слышит.
Каждый слышит, как он дышит.
Как он дышит, так и пишет»

В ардуино для avr имеется, а «за всю Одессу» я никогда ничего не делаю.

Как? В ардуино для avr он и без нас организован, а, повторюсь, «за всю Одессу» я никогда ничего не делаю.

Пока не прочувствовал(
Но лично у меня вызывает чувство неудовлетворенности использование (и переопределение внутри класса) переменной, не являющейся полем класса.
И второй источник неудовлетворенности - нам что, для одной и той же кнопки с одним и тем же функционалом писать разные классы в зависимости от того, сколько именно светодиодов мы собираемся использовать у себя в программе?

Ну, я имел ввиду что yield организован в Ардуино ведь. А не использую я Ардуино. По ряду причин. И что, нужно изобретать millis, yield? Просто завязка на Ардуино фреймворк получается. А не хотелось бы. Хотелось бы попроще, как у людей.)

Вы не поверите, но здесь форум по “Ардуино”!

Знаете, я вот не понимаю смысла таких постов. Я делаю то, что хочу, так, как хочу и потому, что я этого хочу.

Если Вы желаете, чтобы я делал, то чего «хотелось бы» Вам, это, конечно, возможно, но … Вы ведь взрослый человек и понимаете как организуются такие вещи, правда?