Помогите советом. Прерывание цикла.

Тема в разделе "Arduino & Shields", создана пользователем m3duat0p, 30 янв 2017.

  1. m3duat0p

    m3duat0p Нуб

    Доброго времени суток. Помогите, пожалуйста, советом по организации прерывания.
    Суть такая. Пытаюсь соорудить что-то типа LED контроллера.
    Схема отладки:
    1. UNO
    2. Тактовая кнопка, притянута к земле, подключена на второй pin.
    3. RGB светодиоды:
    redled = 3;
    greenled = 5;
    blueled = 6;

    Что хотелось бы увидеть. Диоды мигают по какому-то своему алгоритму, при нажатии кнопки происходит переключение программы "мигалки".
    Например. Мигает красный, нажал кнопку - замигал зеленый, еще раз нажал - замигал синий, еще раз нажал - стали мигать RGB по очереди.
    Программист я неопытный. Гуглил, читал, в итоге пришел к одному варианту, который больше всего напоминает то, что я хочу, но и с ним немного что-то не так.

    По порядку

    Код (C++):
    int switchPin = 2;
    int redled = 3;
    int greenled = 5;
    int blueled = 6;
    volatile int LEDstate = 0;

    char colors[3] = {redled, greenled, blueled}; //Засунул в массив для удобства обращения через циклы

    void setup()
    {
      pinMode(blueled, OUTPUT);
      pinMode(redled, OUTPUT);
      pinMode(greenled, OUTPUT);
      pinMode(switchPin, INPUT);
      attachInterrupt(0, LEDstateSwitch, LOW);
    }
    //Мигаем три раза цветом по выбору
    void anyblink(int x){
      for(int i=0;i<3;i++){
        digitalWrite(colors[x], HIGH);
        delay(1000);
        digitalWrite(colors[x], LOW);
        delay(1000);
      }
    }
    //Мигаем бесконечно цветом по выбору
    void anyblink(int x){
      int tmp_LEDstate = LEDstate;
      while(tmp_LEDstate == LEDstate){
        for(int i=0;i<3;i++){
          digitalWrite(colors[x], HIGH);
          delay(1000);
          digitalWrite(colors[x], LOW);
          delay(1000);
        }
      }
    }

    //Три красных, три зеленых, три синих
    void anyblink_all(){
      for(int i=0;i<3;i++){
        anyblink(i);
      }
    }
    //В лупе - код, который запускает определенную функцию, в зависимости от значения LEDstate
    //LEDstate принимает значения от [0-3], а каждое нажатие кнопки увеличивает значение на 1
    //Если значение LED state становится больше 3, то присваиваем значение "0"
    //Таким образом, переключаем программы мигалок по кругу
    void loop() {
      if (LEDstate > 3) //Проверяем, не превышено ли максимальное значение
      {
        LEDstate = 0;
      }
      else
      {
          if (LEDstate == 0) //Три красных свистка
          {
            anyblink(0);
          }
          else if (LEDstate == 1) //Три зеленых
          {
           anyblink(1);
          }
          else if (LEDstate == 2) //Три синих
          {
           anyblink(2);
           }
          else if (LEDstate == 3)//Три красных, три зеленых, три синих.
          {
            anyblink_all();
          }
       }
    }
    //Обработка прерывания с дебонсом и увеличением значения LEDstate на 1
    void LEDstateSwitch()
    {
      static unsigned long millis_prev;
      if(millis()-100 > millis_prev) LEDstate++;
      millis_prev = millis();
    }
    Получается на выходе следующее. Если берем функцию с тремя вспышками, то прерывается только та вспышка, с которой совпало нажатие кнопки, оставшиеся срабатывают, и только после этого происходит переключение программы. Если не совсем понятно, то на примере:
    Мигает красный, который должен мигнуть три раза по условиям функции. Если нажимаем во время первой вспышки из трех, прерывается только первая вспышка, диод моргает еще два раза красным и после этого включается зеленая мигалка.
    Если берем функцию anyblink_all(), которая, напоминаю, должна мигнуть три раза красным, три зеленым, и три синим, то попытавшись ее прервать на красном, увидим, как она домаргивает зеленым и синим и только потом начинается выполнение следующей функции.
    В случае с infiniteblink(), бесконечным циклом, переключение вообще не происходило, если не добавлять условие
    Код (C++):
    int tmp_LEDstate = LEDstate;
      while(tmp_LEDstate == LEDstate)
    Но это не помогает с остальными, к сожалению.

    Буду признателен за содействие и соучастие :)

    P.S. Почему-то, после добавления прерывания длительность всех задержек стала меньше. Диоды мигают не по секунде, а по 0,5 по ощущениям.
     
  2. Jedi

    Jedi Гик

    Так она и должна так работать. Прерывание прерыванием, но выход из anyblink() у Вас не по прерыванию, а по окончанию функции.
    Иными словами - вошли в функцию, начали мигать, выскочило прерывание по кнопке, изменили значение LEDstate, из прерывания вышли в функцию туда, откуда ушли, закончили начатую функцию, определились с новой функцией и т.д.
    delay() останавливает процессор, он спит в это время, прерывание по кнопке выбрасывает из delay()
     
    m3duat0p нравится это.
  3. rkit

    rkit Гуру

    delay годится только для самых простых случаев.
    Лучше воспользоваться функцией millis, и проверять время в цикле.. Это позволит выполнять другую работу, пока ожидаете, пока пройдет секунда.
     
  4. m3duat0p

    m3duat0p Нуб

    Спасибо. Сам немного покопался из изменил код функций таким образом:
    Код (C++):
    void infiniteblink(int x){
      int tmp_LEDstate = LEDstate;
      while(tmp_LEDstate == LEDstate){
        digitalWrite(colors[x], HIGH);
        delay(1000);
        digitalWrite(colors[x], LOW);
        delay(1000);
      }
    }

    //------------------------------------------

    void anyblink(int x){
      for(int i=0;i<3;i++){
        int tmp_LEDstate = LEDstate;
        digitalWrite(colors[x], HIGH);
        delay(1000);
        digitalWrite(colors[x], LOW);
        delay(1000);
        if(tmp_LEDstate != LEDstate) break;
      }
    }

    //------------------------------------------

    void anyblink_all(){
      int tmp_LEDstate = LEDstate;
      for(int i=0;i<3;i++){
        anyblink(i);
        if(tmp_LEDstate != LEDstate) break;
      }
    }
    Не идеально, но сойдет в данном случае. Не знаю, как будет дело обстоять, если алгоритмы буду более сложные. Если у Вас есть идея получше, буду признателен :)

    Да, читал про это тоже, но неприятных последствий, до этого случая, не испытывал. millis() пока толком не пользовался.
    Более громоздко получится наверное, да? Особенно, если алгоритмы мигалок придется писать более сложные.
    Что-то типа этого должно быть?
    Код (C++):
    void blink_millis() {
    static unsigned long millis_prev;
      if(millis()-1000 > millis_prev) digitalWrite(colors[x], HIGH);
      millis_prev = millis();
      if(millis()-1000 > millis_prev) digitalWrite(colors[x], LOW);
      millis_prev = millis();
    }
     
  5. rkit

    rkit Гуру

    Можно еще упростить.
    По громоздкости большой разницы нет.
    Для совсем простых циклов можно так.
     
    m3duat0p нравится это.
  6. m3duat0p

    m3duat0p Нуб

    Интересное решение!
     
  7. ostrov

    ostrov Гуру

    millis() используют не по причине уменьшения или увеличения громоздкости кода, а для "многозадачности" работы программы. Если процесс один и он линеен, то delay() вполне подойдет, если же потоков много, то нет. Многозадачность в кавычках написал потому, что это тоже не полноценная многозадачность, но для большинства задач ее хватит с запасом.