Урок 10. Прерывание по таймеру в Ардуино. Библиотека MsTimer2. Параллельные процессы.

Arduino UNO R3

Узнаем, как работать с прерываниями по таймеру. Напишем простую программу с параллельными процессами.

Предыдущий урок     Список уроков     Следующий урок

В реальной программе надо одновременно совершать много действий. Во введении я приводил пример программы контроллера холодильника на модуле Пельтье. Перечислю, какие действия она совершает:

Операция

Время цикла
Опрашивает 3 кнопки, обрабатывает сигналы с них для устранения дребезга 2 мс
Регенерирует данные семисегментных светодиодных индикаторов 2 мс
Вырабатывает сигналы управления для 2 датчиков температуры DS18B20 и считывает данные с них. Датчики имеют последовательный интерфейс  1-wire. 100 мкс для каждого бита,
1 сек общий цикл чтения
Чтение аналоговых значений тока и напряжения на элементе Пельтье, напряжения питания 100 мкс
Цифровая фильтрация аналоговых значений тока и напряжения 10 мс
Вычисление мощности на элементе Пельтье 10 мс
ПИД (пропорционально интегрально дифференциальный) регулятор стабилизации тока и напряжения 100 мкс
Регулятор мощности 10 мс
Регулятор температуры 1 сек
Защитные функции, контроль целостности данных 1 сек
Управление, общая логика работы системы 10 мс

Все эти операции выполняются циклически, у всех разные периоды циклов. Ни какую из них нельзя приостановить. Любое, даже кратковременное, изменение времени периода операции приведет к неприятностям: значительной погрешности измерения, неправильной работе стабилизаторов, мерцанию индикаторов, неустойчивой реакции нажатий на кнопки и т.п.

 

В программе контроллера холодильника существует несколько параллельных процессов, которые и совершают все эти действия, каждое в цикле со своим временем периода. Параллельные процессы - это процессы, действия которых выполняются одновременно.

В предыдущих уроках мы создали класс для объекта кнопка. Мы сказали, что это класс для обработки сигнала в параллельном процессе. Что для его нормальной работы необходимо вызывать функцию (метод) обработки сигнала в цикле с регулярным периодом (мы выбрали время 2 мс). И тогда в любом месте программы доступны признаки, показывающие текущее  состояние кнопки или сигнала.

В одном цикле мы поместили код обработки состояния кнопок и управление светодиодами. А в конце цикла поставили функцию задержки delay(2).  Но, время на выполнение программы в цикле меняет общее время цикла. И период цикла явно не равен 2 мс. К тому же, во время выполнения функции delay() программа зависает и не может производить других действий. На сложной программе получится полный хаос.

Выход  - вызывать функцию обработки состояния кнопки по прерыванию от аппаратного таймера. Каждые 2 мс основной цикл программы должен прерываться, происходить обработка сигнала кнопки и управление возвращаться в основной цикл на код, где он был прерван. Короткое время на обработку сигнала кнопки не будет значительно влиять на выполнение основного цикла. Т.е. обработка кнопки будет происходить параллельно, незаметно для основной программы.

Аппаратное прерывание от таймера.

Аппаратное прерывание это сигнал, сообщающий о каком-то событии. По его приходу выполнение программы приостанавливается, и управление переходит на обработчик прерываний. После обработки управление возвращается в прерванный код программы.

С точки зрения программы прерывание это вызов функции по внешнему, не связанному напрямую с программным кодом, событию.

Сигнал прерывания от таймера вырабатывается циклически, с заданным временем периода. Формирует его аппаратный таймер – счетчик с логикой, сбрасывающий его код при достижении определенного значения. Программно установив код для логики сброса, мы можем задать время периода прерывания от таймера.

Установка режима и времени периода таймера Ардуино производится через аппаратные регистры микроконтроллера. При желании можете разобраться, как это делается. Но я  предлагаю более простой вариант – использование библиотеки MsTimer2. Тем более, что установка режима таймера происходит редко, а значит, использование библиотечных функций не приведет к замедлению работы программы.

 

Библиотека MsTimer2.

Библиотека предназначена для конфигурирования аппаратного прерывания от Таймера 2 микроконтроллера. Она имеет всего три функции:

  • MsTimer2::set(unsigned long ms, void (*f)())

Эта функция устанавливает время периода прерывания в мс. С таким периодом будет вызываться обработчик прерывания f. Он должен быть объявлен как void (не возвращает ничего) и не иметь аргументов. * f – это указатель на функцию. Вместо него надо написать имя функции.

  • MsTimer2::start()

Функция разрешает прерывания от таймера.

  • MsTimer2::stop()

Функция запрещает прерывания от таймера.

Перед именем функций надо писать MsTimer2::, т.к. библиотека написана с использованием директивы пространства имен namespace.

Для установки библиотеки скопируйте каталог MsTimer2 в папку libraries в рабочей папке Arduino IDE. За тем запустите программу Arduino IDE, откройте Скетч -> Подключить библиотеку и посмотрите, что в списке библиотек присутствует библиотека MsTimer2.

Загрузить библиотеку MsTimer2 в zip-архиве можно здесь. Для установки его надо распаковать.

Простая программа с параллельной обработкой сигнала кнопки.

Теперь напишем простую программу с одной кнопкой и светодиодом из урока 6. К плате Ардуино подключена одна кнопка по схеме:Схема подключения

Выглядит это так:

Подключение к плате

На каждое нажатие кнопки светодиод на плате Ардуино меняет свое состояние. Необходимо чтобы были установлены библиотеки Button и MsTimer2.

// sketch_10_1 урока 10
// Нажатие на кнопку меняет состояние светодиода

#include <MsTimer2.h>
#include <Button.h>

#define LED_1_PIN 13     // светодиод подключен к выводу 13
#define BUTTON_1_PIN 12  // кнопка подключена к выводу 12

Button button1(BUTTON_1_PIN, 15);  // создание объекта - кнопка

void setup() {
  pinMode(LED_1_PIN, OUTPUT);      // определяем вывод светодиода как выход
  MsTimer2::set(2, timerInterupt); // задаем период прерывания по таймеру 2 мс
  MsTimer2::start();              // разрешаем прерывание по таймеру
}

void loop() {

// управление светодиодом
  if ( button1.flagClick == true ) {
    // был клик кнопки
    button1.flagClick= false;         // сброс признака
    digitalWrite(LED_1_PIN, ! digitalRead(LED_1_PIN));  // инверсия состояния светодиода
   
  }   
}

// обработчик прерывания
void  timerInterupt() {
  button1.scanState();  // вызов метода ожидания стабильного состояния для кнопки 
}

В функции setup() задаем время цикла прерывания по таймеру 2 мс и указываем имя обработчика прерывания timerInterrupt. Функция обработки сигнала кнопки button1.scanState() вызывается в обработчике прерывания таймера каждые 2 мс.

Таким образом, состояние кнопки мы обрабатываем параллельным процессом. А в основном цикле программы проверяем признак клика кнопки и меняем состояние светодиода.

 

Квалификатор volatile.

Давайте изменим цикл loop() в предыдущей программе.

void loop() {

  while(true) {
    if ( button1.flagClick == true ) break;   
  }

    // был клик кнопки
    button1.flagClick= false;         // сброс признака
    digitalWrite(LED_1_PIN, ! digitalRead(LED_1_PIN));  // инверсия светодиода       
}

Логически ничего не поменялось.

  • В первом варианте программа проходила цикл loop до конца и в нем анализировала флаг button1.flagClick.
  • Во втором варианте программа анализирует флаг  button1.flagClick в бесконечном цикле while. Когда флаг становится активным, то выходит из цикла while по break и инвертирует состояние светодиода.

Разница только в том, в каком цикле крутится программа в loop или в while.

Но если мы запустим последний вариант программы, то увидим, что светодиод не реагирует на нажатие кнопки. Давайте уберем класс, упростим программу.

#include <MsTimer2.h>
#define LED_1_PIN 13     // светодиод подключен к выводу 13
int count=0;

void setup() {
  pinMode(LED_1_PIN, OUTPUT);      // определяем вывод светодиода как выход
  MsTimer2::set(500, timerInterupt); // задаем период прерывания по таймеру 500 мс
  MsTimer2::start();              // разрешаем прерывание по таймеру
}

void loop() {

  while (true) {
    if ( count != 0 ) break;
  }

  count= 0; 
  digitalWrite(LED_1_PIN, ! digitalRead(LED_1_PIN));  // инверсия состояния светодиода       
}

// обработчик прерывания
void  timerInterupt() {
  count++; 
}

В этой программе счетчик count увеличивается на 1 в обработчике прерывания каждые 500 мс. В цикле while он анализируется, по break выходим из цикла и инвертируем состояние светодиода. Проще программы не придумаешь, но она тоже не работает.

Дело в том, что компилятор языка C++ по мере своего интеллекта оптимизирует программу. Иногда это не идет на пользу. Компилятор видит, что в цикле while никакие операции с переменной count не производятся. Поэтому он считает, что достаточно проверить состояние count только один раз. Зачем в цикле проверять, то, что никогда не может измениться. Компилятор  корректирует код, оптимизируя его по времени исполнения. Проще говоря убирает из цикла код проверки переменной.  Понять, что переменная count меняет свое состояние в обработчике прерывания, компилятор не может. В результате мы зависаем в цикле while.

В вариантах программы с выполнением цикла loop до конца компилятор считает, что все переменные могут измениться и оставляет код проверки. Если в цикл while вставить вызов любой системной функции, то компилятор также решит, что переменные могут измениться.

Если, например, добавить в цикл while вызов функции delay(), то программа заработает.

  while (true) {
    if ( count != 0 ) break;
    delay(1);
  }

Хороший стиль – разрабатывать программы, в которых цикл loop выполняется до конца и программа нигде не подвисает. В следующем уроке будет единственный код с анализом флагов в бесконечных циклах while. Дальше я планирую во всех программах выполнять loop до конца.

Иногда это сделать непросто или не так эффективно. Тогда надо использовать квалификатор volatile. Он указывается при объявлении переменной и сообщает компилятору, что не надо пытаться оптимизировать ее использование. Он запрещает компилятору делать предположения по поводу значения переменной, так как переменная может быть изменена в другом программном блоке, например, в параллельном процессе. Также компилятор размещает переменную в ОЗУ, а не в регистрах общего назначения.

Достаточно в программе при объявлении count написать

volatile int count=0;

и все варианты будут работать.

Для программы с управлением кнопкой надо объявить, что свойства экземпляра класса Button могут измениться.

volatile Button button1(BUTTON_1_PIN, 15);  // создание объекта - кнопка

По моим наблюдениям применение квалификатора  volatile никак не увеличивает длину кода программы.

 

Сравнение метода обработки сигнала кнопки с библиотекой Bounce.

Существует готовая библиотека для устранения дребезга кнопок Bounce. Проверка состояния кнопки происходит при вызове функции update(). В этой функции:

  • считывается сигнал кнопки;
  • сравнивается с состоянием во время предыдущего вызова update();
  • проверяется, сколько прошло времени с предыдущего вызова с помощью функции millis();
  • принимается решение о том, изменилось ли состояние кнопки.

Далее надо еще считать состояние кнопки функцией read().

  • Но это не параллельная обработка сигнала. Функцию update() обычно вызывают в основном, асинхронном цикле программы. Если ее не вызывать дольше определенного времени, то информация о сигнале кнопки будет потеряна. Нерегулярные вызовы приводят к неправильной работе алгоритма.
  • Сама функция имеет достаточно большой код и выполняется намного дольше функций библиотеки Button (уроки 7, 8, 9).
  • Цифровой фильтрации сигналов по среднему значению там вообще нет.

В сложных программах эту библиотеку лучше не использовать.

 

В следующем уроке напишем более сложную программу с параллельными процессами. Узнаем, как реализовывать выполнение блоков программы в циклах с различными временными интервалами от одного прерывания по таймеру.

Предыдущий урок     Список уроков     Следующий урок

50 комментариев на «Урок 10. Прерывание по таймеру в Ардуино. Библиотека MsTimer2. Параллельные процессы.»

    • Создать 3 объекта Button, для каждого вызывать метод scanState() в прерывании и сделать в цикле loop() 3 проверки для каждой кнопки и светодиода.

  1. Правильно ли прерывание по таймеру называть параллельным процессом?
    Ведь loop() во время выполнения timerInterupt() прерывается?

    • Наверное, зависит от времени реакции программы и от задачи. Вы нажимаете на кнопку и программа тут же что-то делает. Вы не замечаете, что она прервалась на другую задачу. Для вас как пользователя это процессы, которые выполняются одновременно.
      Если рассуждать с вашей точки зрения, то параллельное выполнение задач возможно только в мультипроцессорной системе.

  2. А как насчет использования функции millis(), что-то типа:
    TS=millis();
    if ((TS-TSo) >= 1000)
    {
    TSo=TS;

    }
    пробовал…, вроде бы работает…

    • Эту проверку надо все время вызывать и чем чаще тем лучше. Если программа задержится в какой-то задаче, то не вовремя отработает такое псевдо прерывание.

    • Если вы хотите использовать эту функцию как синхронизатор точных отрезков времени, то ее надо изменить на:

      TS=millis();
      if ((TS-TSo) >= 1000)
      {
      TSo=TSo+1000;

      }

  3. Такая ситуация…
    В Ардуину передаю строку (например дату-время) периодически (каждую секунду)…
    В случае аппаратного прерывания строка «порвется»?
    Если да, то как с этим бороться?

    • Что значит строка порвется?
      Если имеется в виду передача по последовательному порту, то это происходит так. Строка функцией класса Serial загружается в буфер. От туда по прерыванию аппаратного последовательного порта после передачи каждого байта загружается новый байт. На загрузку нового байта есть время передачи предыдущего байта. Даже если произойдет задержка, то ничего страшного не будет. Передача или прием это тоже параллельные процессы, реализованные встроенным классом Serial.

  4. Когда-то давно изучал бейсик и 386 комп был чудом техники, Теперь жизнь заставляет учить все с нуля. Спасибо очень помогают Ваши уроки, но никак не могу понять для чего нужны классы? Объем кода меньше не становиться.

  5. А если мне надо одновременно контролировать несколько кнопок, причем контролировать длительность из нажатия.
    К примеру, короткое нажатие- считываю соответствующее кнопке значение ПЗУ и выполняю какое-то действие, длинное- считываю усредненное значение АЦП, и очень длинное- какое-то другое значение, пусть будет какая-нибудь калибровка. Тут как быть? случай без dalay, т.е. с прерываниями. В прерывании опрашивать кнопки или считать тики? я тут не зря упоминаю усредненное значение АЦП. )))) Старый стал))) Не соображу))

    • Много вариантов решения. Например, в уроке 42 в программе контроллера элемента Пельтье при удержании кнопок «+» или «-» значение параметра меняется автоматически. Посмотрите как там реализовано.

  6. а если так:
    кнопки на пин1, пин2, пин3, пин4
    входы подтянуты к плюсу
    примем А=!пин1*8, В=!пин2*4, С=!пин3*2, D=!пин4,
    ПОРТ=A+B+C+D
    Это, типа порт с маской
    Запускаем «тикалку»

    Введем некую переменную ПортХ

    если ПортХ=ПОРТ && ПортХ !=0, то считаем тики ТИК++
    если ПортХ !=ПОРТ && ПортХ !=0, тогда

    смотрим, сколько у нас натикало
    если ТИК<10 считаем, что дребезжит и сбрасываем счетчик

    если ТИК <20, сбрасываем счетчик тикалки, считаем, что на кнопку наступили и делаем, что-то первое

    если ТИК<40, сбрасываем счетчик тикалки, считаем, что на кнопку наступили и делаем, что-то второе

    если ТИК<80, сбрасываем счетчик тикалки, считаем, что на кнопку конкретно наступили и делаем, что-то третье

    если натикало больше 80, считаем, что кот спит на клаве, сбрасываем счетчик тикалки и ничего не делаем, вернее, всё остальное по порядку

    В прерывании по тикалке читаем состояние ПОРТ

    Так пойдет? Тут получается, что мы можем отслеживать несколько длительностей (тут отслеживаем 5 состояний нажатий кнопок
    1- кнопки никому не нужны и давить их никто не собирался, ну, может, импульсы вокруг летают, или кот проскочил, наступив на клаву, до 10 тиков

    2- нормально наступили, ненадолго, от 10 до 20 тиков,

    3- конкретно надавили, чуток подержали и отпустили от 20 до 40 тиков

    4- давили от души, но вовремя одумались от 40 до 80 тиков

    5- больше 80- либо тарелку с салатом и клаву перепутали, либо кот-подлец нашел себе новую лёжку
    Вроде, должно работать
    Число тиков, конечно, условно

  7. чуть не забыл, помимо отслеживания времени нажатия, что-то делать можем по любому сочетанию кнопок

  8. Не совсем понимаю… А можно ли сделать так (кнопки не нужны мне), чтобы три управляющих выхода на плате мигали светодиодами с разной задержкой, но одновременно? То есть пин1 = 100 мс, пин2 = 500 мс, пин3 = 1000мс. А то они работают друг за другом…

  9. Написал программу по включению/выключению подсветки кнопкой (ИК датчик) и автоматическому её выключению через час (на случай, если ушел и забыл выключить либо ложное срабатывание датчика — чтобы свет не горел целый день).

    int ledPin = 13;
    int keyPin = 2; // кнопка
    bool keyState, oldKeyState; // состояние кнопки
    long on = 3600000; // время свечения, мс
    unsigned long ms, ms1 = 0;
    unsigned long timer = 0;

    void setup() {
    pinMode(ledPin, OUTPUT);
    }

    void loop() {
    // кнопка ВКЛ/ВЫКЛ и убираем дребезг контактов
    ms = millis();
    if ((digitalRead(keyPin)==HIGH)&&(ms-ms1>1000)) {
    ms1 = ms;
    if(keyState==oldKeyState) {
    keyState=!keyState;
    digitalWrite(ledPin, keyState);
    timer = millis(); // запоминаем момент времени, нужно для отсчета прошедшего времени
    }
    }
    else oldKeyState=keyState;

    // если светодиод включен и светится больше чем надо
    if ((keyState==HIGH)&&(millis()-timer >= on)) {
    keyState=LOW; //выключаем
    digitalWrite(ledPin,keyState); // реализуем новое состояние
    }
    }

    Сомневаюсь в правильности работы программы и не дает покоя момент переполнения счетчика millis().
    Нужен ли здесь сторожевой таймер?

      • Спасибо.
        1. Если вместо millis использовать библиотеку mstimer2, нужно ли будет в этом случае отслеживать переполнение счетчика?
        2. Можно ли как-то еще упростить программу?

        • Если для счетчика времени выбрать достаточную разрядность (на 1 час), то переполнение отслеживать не надо. По нажатию кнопки или по достижению счетчиком нужного времени он должен сбрасываться в 0.
          Мне кажется с mstimer программа будет проще.

  10. Наверное стоит упомянуть в тексте урока что указанная библиотека MsTimer2 не будет работать Mega2650. Я уж думал что начал косячить от бессоницы, полез искать оригинальные библиотеки — а там оказывается есть варианты. Для Atmega2560 или Teensy надо использовать FlexiTimer2. Скачать можно здесь: http://playground.arduino.cc/Main/FlexiTimer2 Но все равно уроки отличные.

    • Спасибо за подсказку… А то уже час не могу понять в чем дело… Готовый пример с таймером не работает… а на delay работает… Оказывается из-за Меги

  11. Здравствуйте, при компиляции первого примера при расположении в конце программы блока

    void timerInterupt()
    {
    button1.scanState(); // вызов метода ожидания стабильного состояния для кнопки
    }
    Arduino выдает сообщение:
    ‘timerInterupt’ was not declared in this scope
    при переносе блока перед void setup() компилируется нормально, но не работает. Cветодиод (на плате, не 13й) светит постоянно?

    • Возможно у вас скетч или библиотека находится в папке названной русскими символами.

  12. Со светодиодом разобрался, перепутал кнопку и светодиод, извините стормозил. А вот с остальным не понятно. Если оставляю void timerInterupt()
    {
    button1.scanState(); // вызов метода ожидания стабильного состояния для кнопки
    }
    то не компилируется, а если переношу перед void setup, все ок?

  13. Да, Вы правы нашел в пути одну русскую папку, спасибо. А порядок в этом случае получается не важен? Меняется что нибудь если переместить этот блок перед void loop()

    • в версиях IDE выше 1.6.12 функции надо объявлять до setup иначе компилируется с ошибкой о не объявленной функции

  14. У меня не совпадает интервал вызова обработчика прерывания с установленным мною.

    Указываю:

    MsTimer2::set(2000, timerInterupt);

    А в обработчике делаю Serial.println(«interupt»). По выводу сообщений вижу, что обработчик вызывается не каждые 2 секунду, а каждую секунду, то есть ровно в два раза чаще.

    Любое число, какое бы не задал — обработчик вызывается ровно в 2 раза чаще.

    Не могу этого понять.

    Arduino NANO 3 ATmega328

    • У меня все совпадает. Специально проверил на такой программе.

      #include
      void setup() {
      Serial.begin(9600);
      MsTimer2::set(2000, timerInterupt);
      MsTimer2::start();
      }

      void loop() {
      }

      // обработчик прерывания
      void timerInterupt() {
      Serial.println(«interupt»);
      }

      Проверьте у себя этот скетч. Библиотека устанавливает делитель таймера в зависимости от частоты тактирования микроконтроллера. Может у вас этот параметр неправильно определяется.

  15. У меня леонардо.Упорно не хочет переходить на программу обработки прерывания по вашей библиотеке.(MsTimer2.h) Перепробовал все примеры.И еще
    Компилятор не воспринимает
    volatile Button button1(BUTTON_1_PIN, 15);

    • Может та же проблема, что и у меня была с мегой? Сейчас эксперементирую с FlexiTimer2.h

    • Здравствуйте!
      Может быть У вас дважды определяется библиотека Button.
      Одну библиотеку надо удалить. У меня вылетала ошибка этой библиотеки при обновлении Arduino IDE. Дело в том, что появилась еще одна библиотека с тем же именем Button.h. Проверьте внимательно и лишнюю удалите.

      • Спасибо за совет. Похоже и правда две библиотеки.
        Большое спасибо за уроки.Обещаю дойти до элемента Пельтье. Началось с того ,что купил LCD wh1602b1 оказалось это индикатор с SPI -интерфейсом кое как нашел инфу. Но оказалось библиотек-то нет на него.Потому он такой дешевый 🙂 . Вот решил изучить предмет детально.

  16. У меня вообще заморочки!!! После дельфи не могу переключиться… то := пишу, то не могу понять, почему if не работает… (забываю, что == надо ставить) 🙂

  17. Все равно прерывания по таймеру не происходит. Может это только Леонардовский проц так себя ведет.Даже инверсия светодиода в обработке не работает в последнем скетче.

    • Может у вас та же проблема. Повторяю комментарий выше.

      Наверное стоит упомянуть в тексте урока что указанная библиотека MsTimer2 не будет работать Mega2650. Я уж думал что начал косячить от бессоницы, полез искать оригинальные библиотеки — а там оказывается есть варианты. Для Atmega2560 или Teensy надо использовать FlexiTimer2. Скачать можно здесь: http://playground.arduino.cc/Main/FlexiTimer2

  18. Здравствуйте, а не подскажите что может произойти, если в обработчике прерывания выполняется большое количество операций и затем происходит новое прерывание?
    Программа зациклится?

    • Здравствуйте!
      Я уже отвечал на этот вопрос на форуме сайта. Посмотрите по этой ссылке в конце форума http://mypractic-forum.ru/viewtopic.php?t=23

  19. Добрый день.

    По мотивам урока решил поправить свой проект. Сразу споткнулся на проблеме. Следующий код:

    #include «DHT.h»
    #include «MsTimer2.h»
    #define DHTPIN 9
    DHT dht(DHTPIN, DHT11);

    int currTemp;
    int currHum;

    void setup() {
    dht.begin();
    Serial.begin(9600);
    MsTimer2::set(1000, getCurrentSensorData);
    MsTimer2::start();
    }

    void loop() {
    Serial.print(«From Loop: T=»);
    Serial.print(currTemp);
    Serial.print(» H=»);
    Serial.println(currHum);
    delay(1000);

    }

    // Считывание показателей датчиков
    void getCurrentSensorData() {
    currTemp = dht.readTemperature();
    currHum = dht.readHumidity();

    Serial.print(«From Interrupt: T=»);
    Serial.print(currTemp);
    Serial.print(» H=»);
    Serial.println(currHum);
    }

    В порт постоянно выдаёт нули. Если вместо прерывания по таймеру вызвать в loop функцию getCurrentSensorData(), то всё работает. Подскажите, в чём может быть проблема.

    • Здравствуйте!
      Скорее всего, дело в том, что библиотека DHT.h использует функцию millis() для отработки временных интервалов. А вы вызываете функции dht.read в прерывании. При входе в обработчик прерывания прерывания запрещаются и millis() не работает.
      Вообще вы поступаете не корректно. В прерывании должны отрабатываться короткие процессы, а вы подвешиваете систему.

  20. Здравствуйте, Эдуард!
    Написал скетч для обработки сигналов 5 радиокнопок.
    Использовал библиотеку RCswitch.
    На принятую радиопосылку Нано включает звуковой сигнал на 2сек. и светодиод на 30 сек.Проблема в невозможности работы всех пяти каналов одновременно.Как должен выглядеть фрагмент кода в обработчике прерываний если нельзя использовать delay ? Спасибо.

    #include
    #include
    #include

    RCSwitch mySwitch = RCSwitch();

    volatile int relePin2 = 3;
    volatile int relePin3 = 4;
    volatile int relePin4 = 5;
    volatile int relePin5 = 6;
    volatile int relePin6 = 7;
    volatile int buzPin = 11; // пьезо

    void setup() {

    Serial.begin(9600);

    mySwitch.enableReceive(0); // приемник на pin 2 (DATA)

    pinMode(relePin2,OUTPUT);
    pinMode(relePin3,OUTPUT);
    pinMode(relePin4,OUTPUT);
    pinMode(relePin5,OUTPUT);
    pinMode(relePin6,OUTPUT);
    pinMode(buzPin,OUTPUT);

    digitalWrite(relePin2,HIGH);
    digitalWrite(relePin3,HIGH);
    digitalWrite(relePin4,HIGH);
    digitalWrite(relePin5,HIGH);
    digitalWrite(relePin6,HIGH);

    MsTimer2::set(2, timerInterupt); // задаем период прерывания по таймеру 2 мс
    MsTimer2::start(); // разрешаем прерывание по таймеру

    }

    void loop()

    {

    if (mySwitch.available())
    {
    volatile int value = mySwitch.getReceivedValue();
    Serial.print( mySwitch.getReceivedValue() );

    switch (value)
    {

    case 3328511:
    digitalWrite(relePin2,LOW);
    delay(500);
    digitalWrite(buzPin,HIGH);
    delay(2000);
    digitalWrite(buzPin,LOW);
    delay(20000);
    digitalWrite(relePin2,HIGH);
    break;

    case 1256703:
    digitalWrite(relePin3,LOW);
    delay(500);
    digitalWrite(buzPin,HIGH);
    delay(2000);
    digitalWrite(buzPin,LOW);
    delay(20000);
    digitalWrite(relePin3,HIGH);
    break;

    case 5369087:
    digitalWrite(relePin4,LOW);
    delay(500);
    digitalWrite(buzPin,HIGH);
    delay(2000);
    digitalWrite(buzPin,LOW);
    delay(20000);
    digitalWrite(relePin4,HIGH);
    break;

    case 11769343:
    digitalWrite(relePin5,LOW);
    delay(500);
    digitalWrite(buzPin,HIGH);
    delay(2000);
    digitalWrite(buzPin,LOW);
    delay(20000);
    digitalWrite(relePin5,HIGH);
    break;

    case 12305919:
    digitalWrite(relePin6,LOW);
    delay(500);
    digitalWrite(buzPin,HIGH);
    delay(2000);
    digitalWrite(buzPin,LOW);
    delay(20000);
    digitalWrite(relePin6,HIGH);
    break;

    }
    mySwitch.resetAvailable();
    }
    }
    // обработчик прерывания
    void timerInterupt() {

    ????????????????????????
    }

  21. Извините не пропечатались библиотеки:

    #include
    #include
    #include

    и в void setup()

    digitalWrite(buzPin,LOW);

  22. Здравствуйте!

    Написал скетч для приема сигнала от 5 радиокнопок и включения
    по сигналам пьезоэлемента и реле нагрузок на 22сек. Для радио передачи использовал библиотеку RCswitch.Требуется паралельная работа всех пяти каналов. Помогите пожалуйста с фрагментом кода, который надо писать в обработчике прерываний для обработки радиосигнала и выполнении команд процессора без delay.

    Спасибо.

    #include
    #include
    #include

    RCSwitch mySwitch = RCSwitch();

    int relePin2 = 3; // реле будет тут
    int relePin3 = 4; // реле будет тут
    int relePin4 = 5; // реле будет тут
    int relePin5 = 6; // реле будет тут
    int relePin6 = 7; // реле будет тут
    int buzPin = 11; // пьезо

    void setup() {

    Serial.begin(9600);

    //mySwitch.enableTransmit(10);
    mySwitch.enableReceive(0); // приемник на pin 2 (DATA)

    pinMode(relePin2,OUTPUT);
    pinMode(relePin3,OUTPUT);
    pinMode(relePin4,OUTPUT);
    pinMode(relePin5,OUTPUT);
    pinMode(relePin6,OUTPUT);
    pinMode(buzPin,OUTPUT);

    digitalWrite(relePin2,HIGH);
    digitalWrite(relePin3,HIGH);
    digitalWrite(relePin4,HIGH);
    digitalWrite(relePin5,HIGH);
    digitalWrite(relePin6,HIGH);
    digitalWrite(buzPin,LOW);

    MsTimer2::set(2, timerInterupt); // задаем период прерывания по таймеру 2 мс
    MsTimer2::start(); // разрешаем прерывание по таймеру

    }

    void loop()

    {

    if (mySwitch.available())
    {
    volatile int value = mySwitch.getReceivedValue();
    Serial.print( mySwitch.getReceivedValue() );

    switch (value)
    {

    case 3328511:
    digitalWrite(relePin2,LOW);
    delay(500);
    digitalWrite(buzPin,HIGH);
    delay(2000);
    digitalWrite(buzPin,LOW);
    delay(20000);
    digitalWrite(relePin2,HIGH);
    break;

    case 1256703:
    digitalWrite(relePin3,LOW);
    delay(500);
    digitalWrite(buzPin,HIGH);
    delay(2000);
    digitalWrite(buzPin,LOW);
    delay(20000);
    digitalWrite(relePin3,HIGH);
    break;

    case 5369087:
    digitalWrite(relePin4,LOW);
    delay(500);
    digitalWrite(buzPin,HIGH);
    delay(2000);
    digitalWrite(buzPin,LOW);
    delay(20000);
    digitalWrite(relePin4,HIGH);
    break;

    case 11769343:
    digitalWrite(relePin5,LOW);
    delay(500);
    digitalWrite(buzPin,HIGH);
    delay(2000);
    digitalWrite(buzPin,LOW);
    delay(20000);
    digitalWrite(relePin5,HIGH);
    break;

    case 12305919:
    digitalWrite(relePin6,LOW);
    delay(500);
    digitalWrite(buzPin,HIGH);
    delay(2000);
    digitalWrite(buzPin,LOW);
    delay(20000);
    digitalWrite(relePin6,HIGH);
    break;

    }
    mySwitch.resetAvailable();
    }
    }

    // обработчик прерывания
    void timerInterupt()

    {
    ??????????????????
    }

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *