Простейший пример, с помощью которого знакомятся или проверяют работу микроконтроллера, это мигание светодиодом. Обычно в таком примере используют функцию задержки delay():
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
digitalWrite(LED_BUILTIN, LOW);
delay(1000); // Приостанавливаем выполнение программы на 1 секунду
digitalWrite(LED_BUILTIN, HIGH);
delay(2000); // Приостанавливаем выполнение программы на 2 секунды
}
Функция delay()
приостанавливает выполнение программы на заданное количество миллисекунд. И если такое можно делать в тестовом примере, то в реальной ситуации такое не рациональное использование микроконтроллера не совсем разумно. Если к микроконтроллеру подсоединены другие устройства, можно было вместо вынужденного простоя считать с них информацию или выполнить другие действия. Рассмотрим схему, в которой к NodeMCU присоединены светодиод и кнопка.
При нажатии на кнопку светодиод должен начинать мигать. При повторном нажатии мигание прекращается. Светодиод подсоединён через сопротивление 220Ом к пину D0 (GPIO16). Кнопка замыкает выход 3.3v с пином D8 (GPIO14). Мигание светодиода в нашем примере не равномерное. Светится он 1 секунду, выключен – 2 секунды.
Программа работает без задержек delay()
. В процедуре void loop() мы опрашиваем состояние кнопки по команде btnPush = digitalRead(BTN_pin)
;
Если кнопка была нажата, то повторное нажатие не считывается в течение BTN_time
милисекунд, это сделано, чтобы избежать дребезга контактов кнопки при нажатии. Дальнейшее зависит от переменной LED_on
. При значении true
светодиод мигает, пока не будет повторно нажата кнопка. При значении false
светодиод не мигает и не светится.
// Включаем/выключаем мигание светодиодом при на кнопку по millis()
// Кнопка подсоединена на 3v и D8
#define LED_pin D0
#define BTN_pin D8
#define TIME_on 1000
#define TIME_off 2000
#define BTN_time 200 // время для борьбы с дребезгом кнопки
bool LED_on = false;
uint32_t btnTimer = 0; // время, после нажатия на кнопку чтобы избежать дребезга
void setup() {
Serial.begin(9600);
pinMode(LED_pin, OUTPUT);
pinMode(BTN_pin, INPUT);
}
void loop() {
bool btnPush = !digitalRead(BTN_pin);
if (LED_on) {
led_blink();
}
if (btnPush && ((millis() - btnTimer) > BTN_time)) { // кнопка нажата
btnTimer = millis();
LED_on = !LED_on;
if (LED_on) {
Serial.println("Включаем мигание светодиода");
} else {
Serial.println("ВЫключаем мигание светодиода");
digitalWrite(LED_pin, LOW);
}
}
}
void led_blink() {
static uint32_t tmr; // статическая переменная сохраняется вне процедуры
static bool on_off;
uint32_t tmr_blink;
if (on_off) {
tmr_blink = TIME_on;
}
else {
tmr_blink = TIME_off;
}
if ((millis() - tmr >= tmr_blink)) { // в зависимости от состояния пропускаем период 1 или 2 секунды
tmr = millis();
digitalWrite(LED_pin, !digitalRead(LED_pin)); // переключаем светодиод на противоположное текущему состояние
on_off = !on_off;
}
}
Переключение состояния светодиода происходит в процедуре void led_blink()
. Если значение функции millis(
) стало больше интервала мигания, то состояние светодиода пора менять. Такая организация программы позволяет дополнять процедуру void loop()
командами, например проверкой состояния каких-нибудь датчиков, не останавливая мигания светодиодов и продолжая периодически считывать состояние кнопки.