Использование прерываний

Тема в разделе "Arduino & Shields", создана пользователем Yah, 27 июн 2015.

  1. Yah

    Yah Нерд

    Как мне недавно стало известно в arduino имеется очень удобная библиотека TimerOne для обработки прерываний по времени. Как я понял при помощи этой библиотеки можно создать лишь одно прерывание по времени
    Код (Text):
    #include <TimerOne.h>

    void setup()
    {
      Serial.begin(9600);
     
      Timer1.initialize();// 1 секунда по умолчанию
      Timer1.attachInterrupt(Interview);

    }

    void Interview()
    {

        Serial.println("GPSInterview");
    }

    void loop()
    {

    }
    а как быть если необходимо использовать 2 прерывания по времени, и возможно ли это аппаратно на Arduino Uno?
     
  2. geher

    geher Гуру

    А зачем больше? Задаем минимально необходимый интервал и считаем срабатывания.
    например, надо прерывания через две секунды и через три.
    Делаем прерывание раз в секунду, увеличиваем счетчик и каждое второе срабатывание (count%2==0) делаем то, что надо раз в две секунды, а каждое третье (count%3==0) то, что надо раз в три секунды.
     
  3. Alex19

    Alex19 Гуру

    Да у Arduino UNO (ATmega328) есть 3 таймера.

    Данная библиотека работает только с 1 таймером.

    Можно работать, как сказал geher, но в Вашем случае таймер лишний в принципе, достаточно сделать счетчик при помощи millis().
     
  4. Yah

    Yah Нерд

    ок, но если мне нужно одну операцию делать раз в 750мс а вторую раз в 1сек
     
  5. Alex19

    Alex19 Гуру

    Все зависит от того, что Вам нужно, если просто отправить данные в Serial, то точность (именно 750, а не 751 к примеру) Вам не нужна и можно сделать как в примерах со светодиодом - Blink without Delay.

    Если же Вам нужна супер точность, то тогда решение от geher.

    UPD. Для чего Вам нужен второй таймер?
    Чтобы понять, нужно ли там использование прерывания или нет.
     
    Последнее редактирование: 27 июн 2015
  6. Tomasina

    Tomasina Сушитель лампочек Модератор

    Одного таймера хватит - делаем прерывание каждые 250 мс и в нем смотрим: если прошло 750 мс, вызываем функцию 1, если прошло 1000 мс, вызываем функцию 2.
     
    Yah нравится это.
  7. Alex19

    Alex19 Гуру

    Вот как на millis, без прерываний
    Код (Text):
    unsigned long previousT0Millis = 0;    
    unsigned long previousT1Millis = 0;

    const long intervalT0 = 1000;    
    const long intervalT1 = 750;    


    void setup() {
      Serial.begin(115200);  
    }

    void loop()
    {
      unsigned long currentMillis = millis();
      if(currentMillis - previousT0Millis >= intervalT0)
      {
        previousT0Millis = currentMillis;

        Serial.println("1 sec.");
      }

      currentMillis = millis();
      if(currentMillis - previousT1Millis >= intervalT1)
      {
        previousT1Millis = currentMillis;

        Serial.println("0,75 sec.");
      }
    }
     
    Иногда таймер это единственный выход, иногда лучше обходится без него. Все от задачи.
     
  8. geher

    geher Гуру

    Таймер 0 задействован для millis(). Его лучше не трогать.
    Остается еще только два, которые бывают нужны еще много для чего: ШИМ (каждый из двух таймеров обслуживает свои пины), некоторые библиотеки (например, IRrecv, servo).
    Но если очень нужно, то можно залезть в исходники того же TimerOne и самому по аналогии задействовать второй таймер.
     
    Alex19 нравится это.
  9. Alex19

    Alex19 Гуру

    Да, для простой отправки сериала использовать таймер, мне кажется расточительным. Лучше это сделать, через millis().
     
  10. Yah

    Yah Нерд

    В догонку о прерываниях.
    Код (Text):
    #include <TimerOne.h>

    volatile int LoopNum = 0;
    volatile unsigned long int PackageNum = 0;

    void setup()
    {
      Serial.begin(115200);
      Timer1.initialize(50000);// 50мс
      Timer1.attachInterrupt(Interview);
      Serial.println("START");
    }

    void Interview()
    {
      if ((LoopNum % 2) == 0)
      {
        Serial.println("AX");
        Serial.println("MG");
      }
      if ((LoopNum % 15) == 0)
      {
        Serial.println("SN");
      }
      if ((LoopNum % 20) == 0)
      {
        //опрашиваем GPS и отправляем в сериал порт строку GPS
        String GPS = "GPS:"; GPS += PackageNum;
        GPS += "; LAT="; GPS += 0.000000;
        GPS += "; LON="; GPS += 0.000000;
        GPS += "; DATE="; GPS += 30062015;
        GPS += "; TIME="; GPS += 2;
        GPS += "; VALID="; GPS += 0;
        GPS += ";";

        Serial.println(GPS);
      }

      if (LoopNum >= 20)
        LoopNum = 0;
      else
        LoopNum++;
    }

    void loop()
    {

    }
     
    Не как не могу понять в чем дело. Все работает хорошо, если не использовать код с 31 по 38 строку (где String GPS) то ничего не работает, в чем может быть дело?
     
  11. geher

    geher Гуру

    Возможно, слишком много действий для прерывания: создается достаточно длинная строка и проталкивается в последовательный порт. При этом много накладных расходов на перевыделение памяти и преобразование чисел в строку. А до следующего прерывания только 50 мс
     
  12. Yah

    Yah Нерд

    Тогда отсюда вытекает следующий вопрос: как посчитать количество символов которые можно передать за 50мс при скорости сериал порта равным 115200 бод?
    Для UART 1бод = 1бит/с, следовательно если каждый символ весит 8 бит = 1 байт, то за 50мс можно передать 115200/1000=115,2 * *50 = 5760 бит = 720 символов, так ли это?
     
  13. geher

    geher Гуру

    Несколько меньше, поскольку в потоке еще присутствуют стартовые и стоповые биты, защитные интервалы.
    А еще есть дополнительная вычислительная нагрузка по построению строки, которая тоже занимает какое-то время.
     
  14. Yah

    Yah Нерд

    Точно вычислить это время не реально?
     
  15. Megakoteyka

    Megakoteyka Оракул Модератор

    Это время можно легко измерить.
     
  16. Yah

    Yah Нерд

    Измерил необходимое мне время
    Код (Text):
    void loop()
    {
        unsigned long curr_time = millis();
     
      if ((LoopNum % 2) == 0)
      {
        String AX = "AX:"; AX += PackageNum;
        AX += "; X="; AX += random(-1000, 1000);
        AX += "; Y="; AX += random(-1000, 1000);
        AX += "; Z="; AX += random(-1000, 1000);
        AX += ";";
        Serial.println(AX);

        String MG = "MG:"; MG += PackageNum;
        MG += "; X="; MG += random(-1000, 1000);
        MG += "; Y="; MG += random(-1000, 1000);
        MG += "; Z="; MG += random(-1000, 1000);
        MG += ";";
        Serial.println(MG);
      }
     
      if ((LoopNum % 15) == 0)
      {
        String SN = "SN:"; SN += PackageNum;
        SN += "; TMP="; SN += random(0, 100);
        SN += "; PRS="; SN += random(0, 100000);
        SN += "; U="; SN += random(0, 10);
        SN += ";";
        Serial.println(SN);
      }
     
      if ((LoopNum % 20) == 0)
      {
        String GPS = "GPS:"; GPS += PackageNum;
        GPS += "; LAT="; GPS += 0.000000;
        GPS += "; LON="; GPS += 0.000000;
        GPS += "; DATE="; GPS += 30062015;
        GPS += "; TIME="; GPS += 2;
        GPS += "; VALID="; GPS += 0;
        Serial.println(GPS);
      }
       
       
        PackageNum++;
       
       
        Serial.print("Time=");
        Serial.print(millis() - curr_time);
        Serial.println(" ms");
    }
    Получилось в среднем 160мс, поставил период 200мс
    Код (Text):
      Timer1.initialize(200000);// 200мс
    ничего не работает... Попробовал увеличить до 1 сек, опять же ничего не заработало