Делаем несколько дел одновременно-периодически

Arduino
Автор темы, Сержант
Сержант
Аватара
Arduino
Автор темы, Сержант
Сержант
Сообщения: 64
Зарегистрирован: 19 апреля 2017
С нами: 6 лет 11 месяцев

#1 Arduino » 4 декабря 2017, 19:41

Сразу оговорюсь, в рамках 8-ми битных контроллеров настоящей одновременности добится невозможно (только если какое-то действие будет выполняется независимым аппаратным блоком, например - генерация PWM). Но для подавляющего большинства задач - хватает "псевдо-одновременности". Когда контроллер по очереди "хватается" то за одно дело, то за другое. Если на каждом "деле" он не задерживается слишком на долго, то с точки зрения человека это выглядит как "одновременно".
Типичный пример чем может одновременно заниматься контроллер:

мигать диодом (периодически переключать его состояние)
читать датчик температуры
слушать приходящие команды из Serial
сообщать о текущем состоянии переменных в Serial

Так как каждое из этих действий выполняется контроллером за микросекунды (миллисекунды в худшем случае), то пробегая раз за разом по этому списку - мы создадим иллюзию одновременности. Главное - не задерживаться на одном месте не долго. Скажем при мигании диодом - не использовать функцию delay() (она полностью останавливает выполнение скетча на какое-то время).

Заводим для каждого действия свою переменную previousTime (естественно имя чуть-чуть меняем) и оборачиваем действий в if(millis()-previousTime)

Два дела сразу

Скажем мы хотим мигать диодом раз в пол-секунды и, при этом выводить, раз в секунду, в Serial текущие значение millis() (что-бы даже не видя ардуины физически иметь уверенность что скетч не завис).

Код: Выделить всё

#define LED_PIN  13      // номер выхода,подключенного к светодиоду
#define  BLINK_INTERVAL  5000UL  // интервал между включение/выключением светодиода (5 секунд)
#define PRINT_INTERVAL 1000UL  // периодичность вывода времени в Serial (1 cсекунда)
#define SERIAL_SPEED 9600 // скорость работы Serial
  
void setup
() {
  // задаем режим выхода для порта, подключенного к светодиоду
  pinMode(LED_PIN, OUTPUT);      
   
  
// задаем скорость работы ком-порта
  Serial.begin(SERIAL_SPEED);
   
   
}
  
void loop
()
{
  // мигаем диодом   (периодически переключаем его состояние)
  static unsigned long prevBlinkTime = 0; // время когда последний раз переключали диод
  if(millis() - prevBlinkTime > BLINK_INTERVAL) {
    prevBlinkTime = millis();  // 
    digitalWrite(LED_PIN,!digitalRead(LED_PIN)); 
  
}
   
  
// периодически выводим millis() в Serial
  static unsigned long prevPrintTime=0;
  if(millis()-prevPrintTime>PRINT_INTERVAL){
    prevPrintTime=millis();
     
    Serial
.print("Current time:");
    Serial.println(millis());
 
  
}
   
}

Причесываем код

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

Код: Выделить всё

#define LED_PIN  13      // номер выхода,подключенного к светодиоду
#define  BLINK_INTERVAL  5000UL  // интервал между включение/выключением светодиода (5 секунд)
#define PRINT_INTERVAL 1000UL  // периодичность вывода времени в Serial (1 cекунда)
#define SERIAL_SPEED 9600 // скорость работы Serial
  
void setup
() {
  // задаем режим выхода для порта, подключенного к светодиоду
  pinMode(LED_PIN, OUTPUT);      
   
  
// задаем скорость работы ком-порта
  Serial.begin(SERIAL_SPEED);
   
   
}
  
void loop
()
{
  blinkLed(BLINK_INTERVAL);  // мигаем
  printTime(PRINT_INTERVAL); // выводим время
}
 
// мигает диодом с периодичностью interval
void blinkLed(unsigned long interval ){
  static unsigned long prevTime = 0; // время когда последний раз переключали диод
  if(millis() - prevTime > interval) {
    prevTime = millis();  // 
    digitalWrite(LED_PIN,!digitalRead(LED_PIN)); 
  
}
}
 
// выводит в Serial время с периодичностью interval
void printTime(unsigned long interval){
  static unsigned long prevTime=0;
  if(millis()-prevTime>interval){
    prevTime=millis();
     
    Serial
.print("Current time:");
    Serial.println(millis());
 
  
}
}

Шаблон/Заготовка

Как видите, благодаря тому что мы, с помощью ключевого слово static, "спрятали" объявление prevTime внутрь функции (но при этом сохранили ее способность сохранять значение при выходе из функции подобно глобальной переменной) мы смогли внутри обеих функций (blinkLed и printTime) использовать одно и тоже имя переменной prevTime. Нам теперь не нужно хмурить мозг что-бы каждый раз придумывать новое уникальное имя переменное. Более того, у нас получилась как-бы "универсальная заготовка периодической функции".

Код: Выделить всё

// "заготовка/шаблон функции" которая периодически выполняет КАКОЕ-ТО-ДЕЙСТВИЕ
void somePeriodical(unsigned long interval){
  static unsigned long prevTime=0;
  if(millis()-prevTime>interval){
    prevTime=millis();
     
    КАКОЕ
-ТО-ДЕЙСТВИЕ;
 
  
}
}

Теперь, когда нам понадобится заняться "еще чем-нибудь" время от времени. Мы возьмем этот шаблон. Вместо имени somePeriodical дадим более осмысленное имя, впишем действие которое нам нужно, и вызовем эту функцию из loop()

А зачем нужен параметр функции?

Так же, если вы заметили, этот шаблон не содержит жестко прописанного интервала времени. Мы его "подаем снаружи" с помощью параметра функции. Это дает нам возможность, примеру, сам интервал мигания положить в какую-то переменную и менять его по мере надобности (ну скажем в зависимости от температуры).
Или, мигать с разной частотой в зависимости от того нажата кнопка или нет. Примерно так:

Код: Выделить всё

void loop(){
   
  if
(digitalRead(PIN_BUTTON)) blinkLed(BLINK_INTERVAL)
                        else blinkLed(BLINK_INTERVAL*2); // в два раза медленее
  .....
}

Тут мы мигаем либо с нормальной, либо с половинной частотой в зависимости от того HIGH или LOW уровень сейчас на пине PIN_BUTTON (нажата или нет кнопка)


Вернуться в «Программирование»

Кто сейчас на форуме (по активности за 5 минут)

Сейчас этот раздел просматривают: 6 гостей