Пьезоэлемент, как избавиться от delay();

Тема в разделе "Arduino & Shields", создана пользователем Miheeei, 14 июн 2017.

  1. Miheeei

    Miheeei Нерд

    Здравствуйте, подскажите пожалуйста как избавиться от delay();, при работе с пьезоэлементом.
    Должны звучать поочередно разные частоты, при этом МК не должен тормозить.

    С начала написала так:
    Код (C++):
    #define Sound 5
    void setup() {}
    void loop()
    {
      tone(Sound, 1500, 150);
      delay(100);
      tone(Sound, 2500, 150);
      delay(100);
      tone(Sound, 3500, 150);
      delay(100);
    }
    Затем решила делать так:
    Код (C++):
    #define Sound 5
    long previousMillis = 0;
    long interval = 100;
    void setup() {}
    void loop()
    {
      tone(Sound, 1500, 150);
      unsigned long currentMillis = millis();
      if(currentMillis - previousMillis > interval)
      {
        tone(Sound, 2500, 150);
        previousMillis = currentMillis;
      }
      if(currentMillis - previousMillis > interval)
      {
        tone(Sound, 3500, 150);
      }
    }
    В итоге звучит только первый сигнал с частотой 1500 Гц.
    Что я делаю не так?
     
  2. Andrey12

    Andrey12 Гик

    Вы указываете длительность 150 а delay только 100

    Попробуйте следующий код.
    Код (C++):
    #define Sound 5
    void setup() {}
    void loop()
    {
      tone(Sound, 1500, 150);
      delay(200);
      tone(Sound, 2500, 150);
      delay(200);
      tone(Sound, 3500, 150);
      delay(200);
    }
     
  3. mcureenab

    mcureenab Гуру

    Тут практически все не так. Нужно конечный автомат делать.
    Состояние автомата S (0 - ничего не делаем совсем, и 1,2,3 - три паузы, 4 - запуск ), это проверка времени. Если время 100 мс истекло, переход к следующему состоянию.
    Переход состояния сопровождается переключением звука - tone.

    Код (C++):
    static char S = 0;

    void setup()
    {
        S = 4; // Запускаем автомат.
    }

    void loop()
    {
       unsiged long ms = millis();
       static unsiged long pause = 0;
       switch(S)
       {
          case 4: // Тут только переход к состоянию 1
             tone(Sound, 1500, 150);
             pause = ms + 100; // В состоянии 1 проведем 100 мс
             S = 1; //
             break;
          case 1: // Тут программируем паузу и переход.
             if ( ms < pause ) break; // В паузе возвращаем управление
             tone(Sound, 2500, 150);
             pause += 100; // В состоянии 2 проведем еще 100 мс
             S = 2; //
             break;
          case 2: // Тут программируем паузу и переход.
             if ( ms < pause ) break; // В паузе возвращаем управление
             tone(Sound, 3500, 150);
             pause += 100; // В состоянии 3 проведем еще 100 мс
             S = 3; //
             break;
          case 3: // Тут программируем паузу и переход на начало.
             if ( ms < pause ) break; // В паузе возвращаем управление
             noTone(Sound); // От себя добавил
             S = 4; // Мелодия циклически повторяется
             break;
       }
    }
     
    arkadyf и Miheeei нравится это.
  4. Miheeei

    Miheeei Нерд

    На сомом деле длительность сигнала и длительность паузы друг другу не мешаю.
    Даже если я сделаю:
    Код (C++):
    tone(Sound, 1500, 1000);
    delay(200);
    Сигнал пьезо будет 1000 мс., после чего будет пауза в 100 мс.
    Я же написала что при помощи delay все работает но, тормозит МК. А это не есть хорошо.
     
    Andrey12 нравится это.
  5. Miheeei

    Miheeei Нерд

    Честно говоря тоже думала о создать переключатель триггер.
    Единственное я это буду делать отдельной функцией. Я это вижу так:
    Код (C++):
    void AllarmBeep()
    {
       unsiged long ms = millis();
       static unsiged long pause = 0;
       switch(S)
       {
          case 4: // Тут только переход к состоянию 1
             tone(Sound, 1500, 150);
             pause = ms + 100; // В состоянии 1 проведем 100 мс
             S = 1; //
             break;
          case 1: // Тут программируем паузу и переход.
             if ( ms < pause ) break; // В паузе возвращаем управление
             tone(Sound, 2500, 150);
             pause += 100; // В состоянии 2 проведем еще 100 мс
             S = 2; //
             break;
          case 2: // Тут программируем паузу и переход.
             if ( ms < pause ) break; // В паузе возвращаем управление
             tone(Sound, 3500, 150);
             pause += 100; // В состоянии 3 проведем еще 100 мс
             S = 3; //
             break;
          case 3: // Тут программируем паузу и переход на начало.
             if ( ms < pause ) break; // В паузе возвращаем управление
             noTone(Sound); // От себя добавил
             S = 4; // Мелодия циклически повторяется
             break;
       }
    }
    А далее буду вызывать AllarmBeep(); когда она нужна будет.
     
  6. Andrey12

    Andrey12 Гик

    Понял, просто совсем не знаком с tone(). То есть выполнение tone(Sound, 1500, 1000); равносильно delay(1000); дальше выполнение кода не идет пока не истечет задержка указанная в tone?
     
  7. Miheeei

    Miheeei Нерд

    Ну не совсем, пока 1000 мс. звучит пьезо МК при этом может выполнять другие операции. А вот когда срабатывает delay тут уже МК в паузе.
    Получается что это просто время звучания данной частоты, можно конечно и не указывать время звучания, но тогда пьезо будет пищать без остановки, остановить можно командой noTone(Sound);
     
    Andrey12 нравится это.
  8. mcureenab

    mcureenab Гуру

    Тогда нужно

    Код (C++):
             S = 4; // Мелодия циклически повторяется
    заменить на

    Код (C++):
             S = 0; // Мелодия закончилась
    Вызывать функцию AllarmBeep из loop нужно постоянно, пока мелодия в принципе может звучать. Или какую то более сложную логику делать.

    Чтобы запустить мелодию делаем:

    Код (C++):
            S = 4;
    когда

    Код (C++):
            0 == S;
    значит мелодия закончилась.

    И еще стоит первым делом сделать проверку 0 == S, чтобы весь switch не гонять, когда автомат остановлен.
     
    arkadyf нравится это.
  9. Miheeei

    Miheeei Нерд

    Спасибо большое, буду вечером пробовать, после чего отпешусь
     
  10. Miheeei

    Miheeei Нерд

     
  11. mcureenab

    mcureenab Гуру

    Объявите глобальную переменую S. Она ведь в разных местах программы может понадобиться.

    В моём примере S объявлена - static char S = 0;
     
  12. Miheeei

    Miheeei Нерд

    Извиняюсь, не увидела объявление "S".
    Но не чего не изменилось, ошибок нет, и при этом пьезо молчит
    Вот полностью функция:
    Код (C++):
    static char S = 0;
    bool FlagOnBeep = false;

    void setup(){}

    void OnBeep()
    {
     
    if(FlagOnBeep == false)
      {
        unsigned long ms = millis();
        static unsigned long pause = 0;
        switch(S)
        {
          case 3:
          tone(PiezzoPin, 1000, 50);
          pause = ms + 100;
          S = 1;
          break;
        case 1:
          if ( ms < pause ) break;
          tone(PiezzoPin, 2000, 50);
          pause += 100;
          S= 2; //
          break;
        case 2:
          if ( ms < pause ) break;
          tone(PiezzoPin, 3000, 100);
          pause += 100;
          S = 3;
          break;
        }
        FlagOnBeep = true;
      }
    }

    void loop()
    {
      OnBeep();
    }
    При этом в СОМ порту, не могу отследить значение "S"
    Serial.println(S); Не чего нет...
     
  13. mcureenab

    mcureenab Гуру

    FlagOnBeep всегда false. И S всегда 0.

    FlagOnBeep не нужен. Есть же состояние S==0. Проверяйте его.

    Где то должно быть

    S = 3;
     
  14. Cynep

    Cynep Нерд

    "S" в начале равно 0 и потом измениться не может, так как в переключателе нет "case 0:"
    Поставте S = номер, с которого надо начать.
     
  15. Cynep

    Cynep Нерд

    Далее, функция onBeep вызывается постоянно, но выполняется только один раз, потому что флаг FlagOnBeep после первого раза становится true и больше не меняется.

    Чтобы оно работало надо в цикле loop флаг FlagOnBeep вернуть в исходное состояние в каком-то месте.

    В коде ниже исправил флаг, но переменную "S" не трогал
    Код (C++):

    void loop()
    {

      if(FlagOnBeep == false)
      {
        OnBeep();
        FlagOnBeep = true; //блокируем вызов onBeep
      }
    }

    //
    void OnBeep()
    {
        unsigned long ms = millis();
        static unsigned long pause = 0;
        switch(S)
        {
          case 3:
          tone(PiezzoPin, 1000, 50);
          pause = ms + 100;
          S = 1;
          break;
        case 1:
          if ( ms < pause ) break;
          tone(PiezzoPin, 2000, 50);
          pause += 100;
          S= 2; //
          break;
        case 2:
          if ( ms < pause ) break;
          tone(PiezzoPin, 3000, 100);
          pause += 100;
          S = 3;
          break;
        }
        FlagOnBeep = false; //обнуляем флаг, чтобы функция опять была вызвана
    }
     
     
  16. Cynep

    Cynep Нерд

    Не знаком с работой этой штуковиной. Этот код должен проиграть мелодию?
     
  17. Cynep

    Cynep Нерд

    Вы хотите выводить звук не используя задержку?
    Попробуйте так:).
    Код (C++):

    unsigned long timeToPlay;
    unsigned long ms;
    int id = 1;
    int pause = 100;


    void setup(){
    timeToPlay = millis();
    }
    void loop()
    {
      ms =   millis();


      if(ms >= timeToplay)
      {
        //сбрасываем номер звука
        if(id>3)
          id = 1;

        //выбираем звук
        switch(id)
        {
          case 1:
          tone(PiezzoPin, 1000, 50);
          break;
          case 2:
          tone(PiezzoPin, 2000, 50);
          break;
          case 3:
          tone(PiezzoPin, 3000, 50);
          break;
        }
        //ставим время следующего звука
        timeToPlay += pause;
        //увеличиваем номер звука
        id++;
      }
    }
     
    по идее, должно сработать.
     
    Последнее редактирование: 17 июн 2017
  18. Miheeei

    Miheeei Нерд

    FlagOnBeep вообще убрала, это был влаг раньше когда не было свича
    Во общем заработало когда сделала "unsigned ctatic char S = 3;".
    Но почему то очень медленно переключает, если раньше пауза была 100 мс., то сейчас как будто все 700.
    Где можно уменьшить время паузы?
     
  19. Cynep

    Cynep Нерд

    Код выше пробовали?

    Покажите свой скетч.
     
  20. mcureenab

    mcureenab Гуру

    за долю секунды пролетают 3 вспышки, потом пауза 5 сек.
    про время до переключения ноты в комментариях написал.

    после состояния 3 идет состояние 0. в состоянии 0 время не обслуживается. При выходе из состояния 3 нет смысла устанавливать время pause.
    Однако программа увидит, что автомат остановлен ( 0 == S) и может запустить его снова не дождавшись, когда тон завершится.
    Т.е. после состояния 3 нужно переходить не в 0, а на состояние 4.

    Код (C++):
        case 4:
          if ( ms < pause ) break;
          S = 0; // Остановили автомат
          break;
     
    Сосбственно скетч:

    Код (C++):
    #define PiezzoPin LED_BUILTIN

    static char S = 0;

    void setup(){}

    void OnBeep()
    {
        unsigned long ms = millis();
        static unsigned long pause = 0;
        switch(S)
        {
        case 1:
          tone(PiezzoPin, 1000, 50);
          pause = ms + 100; // 100 мс - время до следующей ноты
          S = 2;
          break;
        case 2:
          if ( ms < pause ) break;
          tone(PiezzoPin, 2000, 50);
          pause += 100; // 100 мс - время до следующей ноты
          S = 3;
          break;
        case 3:
          if ( ms < pause ) break;
          tone(PiezzoPin, 3000, 100);
          pause += 100; // ??? ни для чего
          S = 0; // Остановили автомат
          break;
        }
    }

    void loop()
    {
      if( S ) OnBeep(); // Автомат включен - выполняем.
      else{  // Автомат остановлен.
        delay(5000);  // побездельничаем 5 сек
        S = 1;  // Запускаем автомат.
      }
    }