Ни для кого не секрет, что Arduino и подобные платы – отличный старт, для желающих поближе познакомиться с микроконтроллерами, программированием и разработкой устройств.
Среда Arduino предоставляет действтительно интуитивно понятные средства для того, чтобы сосредоточиться на главном.
Где же уловка ? Она есть и не одна.
Сегодня мы попробуем развенчать функцию “delay”, столь любимую новичками.
Рассмотрим простой код, прямо из примера, поставляемого со средой Arduino.
// the setup routine runs once when you press reset: void setup() { // initialize the digital pin as an output. pinMode(led, OUTPUT); } // the loop routine runs over and over again forever: void loop() { digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level) delay(1000); // wait for a second digitalWrite(led, LOW); // turn the LED off by making the voltage LOW delay(1000); // wait for a second }
Что с ним не так ?
Вроде всё хорошо.
- Зажигаем лампочку
- Ждем
- Гасим
- Ждем
Намылить-смыть-повторить...
А теперь представьте, что микроконтроллер не просто “подмигивает”, но ещё и выполняет какую-то полезную нагрузку – например, общается с внешним миром – ждет ввода данных или замыкания контакта.
Так вот есть плохая новость – в то время, когда программа выполняет оператор “delay”, ваша схема совершенно глуха к внешним раздражителям (в общем случае, хотя всегда есть прерывания по таймеру, например).
И что же делать ?
Избавляться от delay. Для этого в Arduino-мире есть довольно простой и достаточно элегантный ход.
Существует оператор millis(), который возвращает количество миллисекунд, прошедших после старта программы. Возвращаемый тип – unsigned long. То есть переполнение случится где-то дней через 50 после старта микропрограммы.
Теперь рассмотрим немного измененный код. К счастью, он тоже включен в примеры и называется “BlinkWithoutDelay”
int ledState = LOW; // ledState used to set the LED long previousMillis = 0; // will store last time LED was updated // the follow variables is a long because the time, measured in miliseconds, // will quickly become a bigger number than can be stored in an int. long interval = 1000; // interval at which to blink (milliseconds) void setup() { // set the digital pin as output: pinMode(ledPin, OUTPUT); } void loop() { // here is where you'd put code that needs to be running all the time. // check to see if it's time to blink the LED; that is, if the // difference between the current time and last time you blinked // the LED is bigger than the interval at which you want to // blink the LED. unsigned long currentMillis = millis(); if(currentMillis - previousMillis > interval) { // save the last time you blinked the LED previousMillis = currentMillis; // if the LED is off turn it on and vice-versa: if (ledState == LOW) ledState = HIGH; else ledState = LOW; // set the LED with the ledState of the variable: digitalWrite(ledPin, ledState); } }
Что происходит здесь ?
- Делаем “зарубку”
- Проверяем, не прошла ли одна секунда от зарубки
- Если да, меняем состояние светодиода и делаем новую зарубку
- Намылить-смыть-повторить
Несмотря на то, что код изрядно “поправился” работает он более корректно.
К сожалению, мир не идеален и масса готовых компонент всё также использует этот самый delay. Но, теперь мы предупреждены и знаем, что можно посмотреть на предмет внезапно появившихся “глюков”.
Кстати, данный текст навеян историей из реальной жизни – при разработке front-end для управления интернет радио “весь цинизм” при чтении данных из UART и случился – и потеря названия трека было наименьшей из проблем.
И вот ведь как получается – даже в таких далеких от микроконтроллеров вещей, как JavaScript. Там тоже всё исполняется в один поток, но есть приёмы, как это обойти – например, механизм callback'ов.
Итак, delay – обращаться с большой осторожностью и не злоупотреблять без обоснованной необходимости. Удачных проектов!