Arduino UNO зависает

Тема в разделе "Arduino & Shields", создана пользователем Artem88, 2 окт 2015.

  1. Artem88

    Artem88 Нерд

    Помогите разобраться, почему arduino зависает на этой строчке (где бы она не стояла):
    attachInterrupt(0, alarmFunction, FALLING);

    Код (C++):

    #include <Wire.h>
    #include <DS3231.h>
    #include <avr/sleep.h>
    #include <LiquidCrystal_I2C.h>


    DS3231 clock;
    RTCDateTime dt;
    boolean isAlarm = false;
    int alarmLED = 4;
    int count = 0;
    int wakePin = 2;
    LiquidCrystal_I2C lcd(0x27, 16, 2); // set the LCD address to 0x27 for a 16 chars and 2 line display



    void sleepNow()         // Функция увода ардуины в спячку.
    {
      lcd.clear();
      lcd.noBacklight();
      digitalWrite(alarmLED, LOW);             // Выключаем светодиод
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // Здесь устанавливается режим сна
      sleep_enable();                        // Включаем sleep-бит в регистре mcucr. Теперь возможен слип
      count = 0;                             // Обнуляем счетчик прошедших секунд
      sleep_mode();                          // Здесь устройство перейдет в режим сна!!!
    }


    void alarmFunction()      // Функция просыпания
    {
      sleep_disable();
      lcd.backlight();
      lcd.clear();
      lcd.print("*** INT 0 ***");
      lcd.setCursor(0, 1);
      lcd.print("WakeUp Alarm");
      isAlarm = true;
      digitalWrite(alarmLED, isAlarm);                // Включаем светодиод
    }


    void setup()
    {
      attachInterrupt(0, alarmFunction, FALLING);     //  Эта строка вызывает зависание ардуино
      lcd.init();
      lcd.backlight();
      lcd.clear();
      // Initialize DS3231
      lcd.print("Init DS3231");
      delay(2000);

      clock.begin();
      clock.enableOutput(false);
     
      clock.armAlarm1(false);
      clock.armAlarm2(false);
      clock.clearAlarm1();
      clock.clearAlarm2();
     
      // Automatik (Year, Month, Day, Hour, Minute, Second)
      clock.setDateTime(__DATE__, __TIME__);
     
      // Set Alarm1 - Every 20s in each minute
      // setAlarm1(Date or Day, Hour, Minute, Second, Mode, Armed = true)
      clock.setAlarm1(0, 0, 0, 25, DS3231_MATCH_S);
     
      // Setup LED Pin
      pinMode(alarmLED, OUTPUT);
      pinMode(wakePin, INPUT);
      digitalWrite(wakePin, HIGH);              
      digitalWrite(alarmLED, 1);
    }

    void loop()
    {
      lcd.clear();

      dt = clock.getDateTime();

      lcd.print(dt.hour);   lcd.print(":");
      lcd.print(dt.minute); lcd.print(":");
      lcd.print(dt.second); lcd.print("");
      count++;
      delay(1000);                           // Ждем секунду
      // Проверяем - если прошло 10 секунд - засыпаем
      if (count >= 10)
      {
        lcd.clear();
        lcd.print("Ent Sleep mode");
        delay(2000);
        count = 0;
        isAlarm = false;
        clock.clearAlarm1();
        digitalWrite(alarmLED, isAlarm);
        sleepNow();     // Вызов функции sleep() для засыпания
      }

    }
     

    Вложения:

  2. AlexU

    AlexU Гуру

    А можно подробнее по поводу "где она стояла"?
    Мои предположения:
    После вызова функции 'attachInterrupt' сработает прерывание -- вызовется его обработчик 'alarmFunction'. И в приведенном Вами примере может случиться так, что функции lcd.* вызовутся до того как будет вызвана функция lcd.init() -- это первое предположение зависания, второе -- перед вызовом обработчика прерывания все прерывания отключаются, и если функции lcd.* зависят от прерываний, то это то же может привести к зависанию.
    Вообще по "правилам хорошего тона" обработчик прерываний должен выполнять минимум работы, а у Вас он занимается выводом информации на дисплей -- это не есть гут.

    PS: все сказанное выше актуально для Arduino UNO, за другие контроллеры не уверен.
     
  3. Megakoteyka

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

    Скорее всего тут и сидит засада. Функции для работы с дисплеем наверняка используют delay, а delay в прерываниях использовать вообще нельзя, т.к. внутри прерывания заблокированы другие прерывания, в т.ч. прерывание таймера, в котором обновляется счетчик, за которым следит delay.
    Только не для ардуино, а для AVR. В других контроллерах/процессорах ситуация аналогична с поправкой на возможное наличие приоритетов прерываний.
     
  4. Artem88

    Artem88 Нерд

    Сделал так:
    Код (C++):
    void alarmFunction()      // Функция просыпания
    {
      sleep_disable();
     
      digitalWrite(alarmLED, isAlarm);                // Включаем светодиод
    }
    ардуина просыпается по прерыванию, засыпает, и больше не просыпается.
     
  5. Megakoteyka

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

    Из sleep_disable убрали обращения к lcd?
     
  6. AlexU

    AlexU Гуру

    Вы используете прерывание "по спаду":
    Код (C++):
    attachInterrupt(0, alarmFunction, FALLING);
    А в докуметации к ATmega328 (раздел '9.1 Sleep Modes') указано следующее:
    Попробуйте:
    Код (C++):
    attachInterrupt(0, alarmFunction, CHANGE);
    Ну и соответственно логику программы нужно будет пересмотреть.

    PS: Для более точной информации все-таки следует покапаться в документации.
     
  7. Artem88

    Artem88 Нерд

    В процессе экспериментов со скетчем у меня потерялась строчка в конце "void loop()"
    Код (C++):
    clock.clearAlarm1();
    вернул её на место и все заработало, просыпается каждую минуту на 25ой секунде и через десять секунд засыпает.

    Сейчас скетч выглядит так:
    Код (C++):
    #include <Wire.h>
    #include <DS3231.h>
    #include <avr/sleep.h>
    #include <LiquidCrystal_I2C.h>


    DS3231 clock;
    RTCDateTime dt;
    boolean isAlarm = false;
    int alarmLED = 4;
    int count = 0;
    int wakePin = 2;
    LiquidCrystal_I2C lcd(0x27, 16, 2); // set the LCD address to 0x27 for a 16 chars and 2 line display



    void sleepNow()         // Функция увода ардуины в спячку.
    {
      lcd.clear();
      lcd.noBacklight();
      digitalWrite(alarmLED, LOW);             // Выключаем светодиод
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // Здесь устанавливается режим сна
      sleep_enable();                        // Включаем sleep-бит в регистре mcucr. Теперь возможен слип
      count = 0;                             // Обнуляем счетчик прошедших секунд
      sleep_mode();                          // Здесь устройство перейдет в режим сна!!!
    }


    void alarmFunction()      // Функция просыпания
    {
      sleep_disable();
      isAlarm = true;
      digitalWrite(alarmLED, isAlarm);                // Включаем светодиод
    }


    void setup()
    {
      attachInterrupt(0, alarmFunction, FALLING);     //  Эта строка вызывает зависание ардуино
      lcd.init();
      lcd.backlight();
      lcd.clear();
      // Initialize DS3231
      lcd.print("Init DS3231");
      delay(2000);

      clock.begin();
      clock.enableOutput(false);
      clock.armAlarm1(false);
      clock.armAlarm2(false);
      clock.clearAlarm1();
      clock.clearAlarm2();
      // Automatik (Year, Month, Day, Hour, Minute, Second)
      clock.setDateTime(__DATE__, __TIME__);
      // Set Alarm1 - Every 20s in each minute
      // setAlarm1(Date or Day, Hour, Minute, Second, Mode, Armed = true)
      clock.setAlarm1(0, 0, 0, 25, DS3231_MATCH_S);
      // Setup LED Pin
      pinMode(alarmLED, OUTPUT);
      pinMode(wakePin, INPUT);
      digitalWrite(wakePin, HIGH);            
      digitalWrite(alarmLED, 1);
    }

    void loop()
    {
      lcd.backlight();
      lcd.clear();

      dt = clock.getDateTime();

      lcd.print(dt.hour);   lcd.print(":");
      lcd.print(dt.minute); lcd.print(":");
      lcd.print(dt.second); lcd.print("");
      count++;
      delay(1000);                           // Ждем секунду
      // Проверяем - если прошло 10 секунд - засыпаем
      if (count >= 10)
      {
        lcd.clear();
        lcd.print("Ent Sleep mode");
        delay(2000);
        count = 0;
        isAlarm = false;
        clock.clearAlarm1();
        digitalWrite(alarmLED, isAlarm);
        sleepNow();     // Вызов функции sleep() для засыпания
      }

    }

    Большое спасибо за помощь!
     
    Megakoteyka нравится это.
  8. DrProg

    DrProg Вечный нерд

    То есть RTC дергает ногу 0 ардуины через 25 сек, что вызывает пробуждение?
     
    ИгорьК нравится это.
  9. Artem88

    Artem88 Нерд

    Дергает один раз в минуту, при наступлении 25ой секунды. В дальнейшем планируется сделать чтобы ардуина просыпалась в одно и тоже время каждый день, опрашивала датчик, записывала показания на sd-карту и опять засыпала.
     
  10. DrProg

    DrProg Вечный нерд

    Ну так остался один шаг, если научились усыплять и пробуждать алярмом. Кстати, опыт весьма полезный, наверняка пригодится со временем.
     
    ИгорьК нравится это.
  11. Rustam

    Rustam Нерд

    работает ли это от зависания?
    rduino watchdog или автоматический RESET в случае зависания
    https://geektimes.ru/post/255800/
     
  12. lerik2703

    lerik2703 Гик

    а почему это не должно работать?
     
  13. Rustam

    Rustam Нерд

    это работает, но у меня программа засыпает на 5 минут и на эти 5 мин надо отключать watchdog, и ардуинка может зависнуть пока будет спать
     
  14. IvanT

    IvanT Нуб

    Доброй ночи!
    Помогите новичку разобраться с одним вопросом.

    Имеется плата Arduino nano с подключенными к ней 3 сервомоторчиками. Возможно ли, считать данные с сервомоторчиков управляемых платой Arduino nano и передать их скажем на Arduino UNO с мотор шилдом для управления шаговыми двигателями (не вмешиваясь в работу Arduino nano).
     
  15. Onkel

    Onkel Гуру

    Возможно. Любой технический вопрос сводится к финансовому. На валы сервомашинок надо поставить энкодеры нужного разрешения, а это (пром. энкодеры) от 5 тыр (условная Корея, на самом деле хрен знает кто), сейчас может и больше - я привел сентябрьско- октябрьскую цену.
     
  16. IvanT

    IvanT Нуб

  17. Onkel

    Onkel Гуру

    а еще лучше прямо с управляющего провода сервомашинки. Это же проще?