Некорректная отработка интервала

pasv_19
Offline
Зарегистрирован: 23.06.2020

Добрый день!

Есть такой код

#define led_pin 3

int regim = 1;
int flag = 0;
int f1 = 0;
int f = 0;
int count;

unsigned long previousMillis = 0;
unsigned long previousMillis1 = 0;
unsigned long interval = 1000;
unsigned long interval_1 = 60000;

void setup()
{
  Serial.begin(9600);
  pinMode(led_pin, OUTPUT);
  pinMode(2, INPUT_PULLUP);
}

void loop()
{
 if (digitalRead(2) == LOW && flag == 0) {
    regim++;
    flag = 1;
    if (regim > 3) regim = 1;
  }
  if (digitalRead(2) == HIGH && flag == 1) flag = 0;

  if (regim == 1)
  {
    count = 0;
    f1 = 0;
    f = 0;
  }

  if (regim == 2)
  {
    if (f == 0) {
      Serial.print(millis());
      Serial.println(" Start");
    } 
    f = 1;
    unsigned long currentMillis = millis();
    if (count < 255) {

      digitalWrite(led_pin, HIGH);
      if (currentMillis - previousMillis >= interval) {
        previousMillis = currentMillis;
        count = count + 5;
      }
    }

    if (currentMillis - previousMillis1 >= interval_1) {
      previousMillis1 = currentMillis;
      regim = 3;
    }

    analogWrite(led_pin, count);

  }

  if (regim == 3)
  {
    if (f1 == 0) {
      Serial.print(millis());
      Serial.println(" Stop");
      Serial.println("");
    } 
    f1 = 1;
    digitalWrite(led_pin, LOW);
  }

}

При нажатии на кнопку включается нарастающий ШИМ (regim==2), при повторном нажатии этой же кнопки ШИМ выключается (regim==3). Если при включенном ШИМ в течении периода заданном interval_1 кнопка не была нажата - ШИМ отключается самостоятельно. Так вот этот интервал не всегда соответствует заданному. Если бы отличался на пару секунд я бы и не обращал на это внимания. Но разница доходит порой до 20-30 секунд. Сделал печать в порт меток времени включения-выключения. Например вот некоторые результаты вывода в порт для интервала в 60 секунд

15:41:34.059 -> 43354 Start
15:41:50.720 -> 60000 Stop
15:41:50.720 -> 
15:42:12.607 -> 81895 Start
15:42:50.702 -> 120000 Stop
15:42:50.702 -> 
15:43:02.246 -> 131574 Start
15:43:50.668 -> 180000 Stop
15:43:50.668 -> 
15:44:25.316 -> 214640 Start
15:44:50.637 -> 240000 Stop
15:44:50.637 -> 
15:45:10.902 -> 260271 Start
15:45:50.620 -> 300000 Stop
15:45:50.620 -> 
15:58:50.233 -> 1079889 Start
15:59:47.610 -> 1137242 Stop
15:59:47.610 -> 
16:05:05.152 -> 1454894 Start
16:05:44.558 -> 1494330 Stop
16:05:44.558 -> 

Как видно только в одном случае разница около 3 секунд. В остальных случаях разница намного больше от заданных 60 секунд. В чем ошибка?

 

 

b707
Offline
Зарегистрирован: 26.05.2017

обратите внимание, что у вас отметка "Stop" почти всегда выводится при "круглых" значениях миллис. Задуматесь, почему так...

Подсказка - обратите внимание, в какой момент вы пускаете секундомер для большего интервала.

pasv_19
Offline
Зарегистрирован: 23.06.2020

b707 пишет:

Подсказка - обратите внимание, в какой момент вы пускаете секундомер для большего интервала.

Ну, я думал что пуск секундомеров что для одного, что для второго интервала происходит в 44 строке. но судя по вашему ответу понимаю что я ошибаюсь? Добавил после 34 строки

previousMillis1=millis();

вроде бы заработало как нужно. Или и это неправильно? Тогда можно еще одну подсказку?

з/ы. Сколько не пытаюсь разобраться с этим millis всегда у меня проблемы с правильным пуском. Никак не могу понять когда нужно запускать секундомер.

b707
Offline
Зарегистрирован: 26.05.2017

pasv_19 пишет:

Ну, я думал что пуск секундомеров что для одного, что для второго интервала происходит в 44 строке

строка 44 к пуску секундомеров вообще никакого отношения не имеет, ни к первому, ни ко второму

Цитата:
Тогда можно еще одну подсказку?

з/ы. Сколько не пытаюсь разобраться с этим millis всегда у меня проблемы с правильным пуском. Никак не могу понять когда нужно запускать секундомер.

если честно, я не очень разбирался в логике вашего кода. Но из самых общих соображений, если сообщение "Start" означает начало интервала - то где-то рядом с этой строкой и надо пускать секундомер.

А у вас как-то странно все - "Старт" в одном месте, запуск секундомера в другом, его окончание - в третьем... по логике, для периодического процесса  все эти три вещи должны происходить в одном блоке кода.

pasv_19
Offline
Зарегистрирован: 23.06.2020

b707 пишет:

строка 44 к пуску секундомеров вообще никакого отношения не имеет, ни к первому, ни ко второму

Как тогда должен выглядеть пуск секундомера?

Цитата:

А у вас как-то странно все - "Старт" в одном месте, запуск секундомера в другом, его окончание - в третьем... по логике, для периодического процесса  все эти три вещи должны происходить в одном блоке кода.

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

b707
Offline
Зарегистрирован: 26.05.2017

pasv_19 пишет:

b707 пишет:

строка 44 к пуску секундомеров вообще никакого отношения не имеет, ни к первому, ни ко второму

Как тогда должен выглядеть пуск секундомера?

пуск секундомеров у вас происходит в строке 49 для меньшего интервала и в строке 53 - для большего. А строка 44 - это только запрос текущего времени

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

У Вас достаточно плохо комментирована программа (от слова - никак). Что Вы предполагали, делая те или иные действия приходится догадываться. Потратьте пол-часа, поставьте комментарии, и сами поймете и остальные обойдутся без хрустальных шаров.

pasv_19
Offline
Зарегистрирован: 23.06.2020

b707 пишет:

пуск секундомеров у вас происходит в строке 49 для меньшего интервала и в строке 53 - для большего.

Что-то я окончательно запутался. Может в 48 и 54 или 49 и 55?

pasv_19
Offline
Зарегистрирован: 23.06.2020

mykaida пишет:

У Вас достаточно плохо комментирована программа (от слова - никак). Что Вы предполагали, делая те или иные действия приходится догадываться. Потратьте пол-часа, поставьте комментарии, и сами поймете и остальные обойдутся без хрустальных шаров.

Проставил, получилось как-то так...

#define led_pin 3

int regim = 1;
int flag = 0; // flag кнопки 
int f1 = 0; // флаги для 
int f = 0; // однократного вывода в порт
int count;

unsigned long previousMillis = 0;
unsigned long previousMillis1 = 0;
unsigned long interval = 1000;
unsigned long interval_1 = 60000;

void setup()
{
  Serial.begin(9600);
  pinMode(led_pin, OUTPUT);
  pinMode(2, INPUT_PULLUP);
}

void loop()
{
   if (digitalRead(2) == HIGH && flag == 0) {  // перключаем режимы
    regim++;                                // с каждым
    flag = 1;                               // нажатием
    if (regim > 3) regim = 1;               // кнопки          
  }
  
if (digitalRead(2) == LOW && flag == 1) flag = 0;

  if (regim == 1) // начальный режим
  {
    count = 0;
    f1 = 0;
    f = 0;
  }

  if (regim == 2) // режим включения ШИМ
  {
    
     if (f == 0) {                               //  вспомогательная 
      Serial.print(millis());                   //  часть 
      Serial.println(" ШИМ включен");           //  для 
    }                                           //  вывода 
    f = 1;                                      //  меток
  
    
    unsigned long currentMillis = millis(); //запоминаем текущее значение millis
    
    if (count < 255) {                                      // проверяем текущее значение ШИМ, если оно не максимальное
        if (currentMillis - previousMillis >= interval) {  // увеличиваем его 
        previousMillis = currentMillis;                    // с определенным интервалом
        count = count + 5;                                // и определенным шагом
      }
    }

    if (currentMillis - previousMillis1 >= interval_1) { // проверяем не прошла ли одна секунда с момента перехода в режим 2. 
      previousMillis1 = currentMillis;                   //Если да -
      regim = 3;                                        //переходим в режим 3
    }

    analogWrite(led_pin, count);

  }

  if (regim == 3) // режим выключения ШИМ
  {
    if (f1 == 0) {
      Serial.print(millis());
      Serial.println(" ШИМ выключен");
      Serial.println("");
    } 
    f1 = 1;
    digitalWrite(led_pin, LOW);
  }

}

Start Stop заменил на ШИМ включен - выключен

vosara
vosara аватар
Offline
Зарегистрирован: 08.02.2014

Попробуйте переместить 58_ю строчку (previousMillis1 = currentMillis;) под 52_ю

sadman41
Offline
Зарегистрирован: 19.10.2016

Не надо "запускать секундомер", millis() и так тикает без посторонней помощи. Следует запомнить его показания на момент старта.

pasv_19
Offline
Зарегистрирован: 23.06.2020

В общем, если я правильно понял, мне нужно сохранять previousMillis1=millis() как можно чаще перед тем как проверять условие.

Как я уже говорил поставил эту строку после 34 строки и вроде бы начал интервал отрабатывать нормально.

p/s Что интересно - меня подвел Proteus. Я сначала в нем проверял работу и там первоначальный код все красиво отрабатывал, а вот в железе вылез баг.

 

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

previousMillis1=millis сохранять в момент начала режима, т е отсчёт вести с момента включения шим

Гриша
Offline
Зарегистрирован: 27.04.2014

andycat пишет:
previousMillis1=millis сохранять в момент начала режима, т е отсчёт вести с момента включения шим

поддерживаю. previousMillis1=millis - это точка отсчета, относительно которой по превышении значения заданного интервала произойдет событие. Короче говоря, засекаем время в момент когда побежали и постоянно проверяем когда выйдет запланированное побегать время. Как только это произошло, можно идти в душ и смывать трудовую испарину :) 

pasv_19 пишет:

В общем, если я правильно понял, мне нужно сохранять previousMillis1=millis() как можно чаще перед тем как проверять условие.

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

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

pasv_19
Offline
Зарегистрирован: 23.06.2020

Гриша пишет:

pasv_19 пишет:

В общем, если я правильно понял, мне нужно сохранять previousMillis1=millis() как можно чаще перед тем как проверять условие.

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

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

Ну на данный момент я остановился на том что previousMillis1=millis() запоминаю при каждом нажатии кнопки. Пока вроде бы работает нормально. Если и это неправильно ткните уже мне пожалуйста пальцем куда его вставить.

 

 

Гриша
Offline
Зарегистрирован: 27.04.2014

pasv_19 пишет:

Ну на данный момент я остановился на том что previousMillis1=millis() запоминаю при каждом нажатии кнопки. Пока вроде бы работает нормально. Если и это неправильно ткните уже мне пожалуйста пальцем куда его вставить.

я всего лишь прокомментировал ваше сообщение

pasv_19 пишет:

В общем, если я правильно понял, мне нужно сохранять previousMillis1=millis() как можно чаще перед тем как проверять условие

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

обратите внимание на строку 10 и сравните у себя в посте 8

 

void loop()
{
   if (digitalRead(2) == HIGH && flag == 0) 
    {  // перключаем режимы
      regim++;                                // с каждым
      flag = 1;                               // нажатием
      if (regim > 3) regim = 1;               // кнопки          
    }
  
  if (digitalRead(2) == LOW && flag == 1)  {flag = 0} ;   //// фигурные скобки пропустили !!!!!

  if (regim == 1) // начальный режим
    {
      count = 0;
      f1 = 0;
      f = 0;
    }

  if (regim == 2) // режим включения ШИМ
    {
    
        if (f == 0) 
	    {                               //  вспомогательная 
          Serial.print(millis());                   //  часть 
          Serial.println(" ШИМ включен");           //  для 
        }                                           //  вывода 
     f = 1;                                      //  меток
  
    
        unsigned long currentMillis = millis(); //запоминаем текущее значение millis
    
       if (count < 255)  
	    {                                      // проверяем текущее значение ШИМ, если оно не максимальное
         if (currentMillis - previousMillis >= interval) 
		    {  // увеличиваем его 
              previousMillis = currentMillis;                    // с определенным интервалом
             count = count + 5;                                // и определенным шагом
            }
        }

        if (currentMillis - previousMillis1 >= interval_1) 
	    { // проверяем не прошла ли одна секунда с момента перехода в режим 2. 
          previousMillis1 = currentMillis;                   //Если да -
          regim = 3;                                        //переходим в режим 3
        }

     analogWrite(led_pin, count);

    }

  if (regim == 3) // режим выключения ШИМ
    {
      if (f1 == 0) 
	    {
           Serial.print(millis());
          Serial.println(" ШИМ выключен");
          Serial.println("");
        } 
       f1 = 1;
       digitalWrite(led_pin, LOW);
    }

}

 

 

 

pasv_19
Offline
Зарегистрирован: 23.06.2020

Гриша пишет:

обратите внимание на строку 10 и сравните у себя в посте 8

 


  if (digitalRead(2) == LOW && flag == 1)  {flag = 0} ;   //// фигурные скобки пропустили !!!!!

Разве фигурные скобки нельзя пропускать если в условии выполняется только один оператор?

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

"работает - не трож" (с)
А вот разобраться в каждой строчке стоит однозначно.

vosara
vosara аватар
Offline
Зарегистрирован: 08.02.2014
#define led_pin 3

int regim = 1;
int flag = 0; // flag кнопки
int f1 = 0; // флаги для
int f = 0; // однократного вывода в порт
int count;

unsigned long previousMillis = 0;
unsigned long previousMillis1 = 0;
unsigned long interval = 1000;
unsigned long interval_1 = 60000;

void setup()
{
  Serial.begin(9600);
  pinMode(led_pin, OUTPUT);
  pinMode(2, INPUT_PULLUP);
}

void loop()
{
  unsigned long currentMillis = millis(); //запоминаем текущее значение millis
  if (digitalRead(2) == HIGH && flag == 0) {  // перключаем режимы
    regim++;                                // с каждым
    flag = 1;                               // нажатием
    if (regim > 3) regim = 1;               // кнопки
  }

  if (digitalRead(2) == LOW && flag == 1) flag = 0;

  if (regim == 1) // начальный режим
  {
    count = 0;
    f1 = 0;
    f = 0;
  }

  if (regim == 2) // режим включения ШИМ
  {

    if (f == 0) {                               //  вспомогательная
      Serial.print(millis());                   //  часть
      Serial.println(" ШИМ включен");           //  для
      previousMillis1 = currentMillis;
    }                                           //  вывода
    f = 1;                                      //  меток

    if (count < 255) {                                      // проверяем текущее значение ШИМ, если оно не максимальное
      if (currentMillis - previousMillis >= interval) {  // увеличиваем его
        previousMillis = currentMillis;                    // с определенным интервалом
        count = count + 5;                                // и определенным шагом
      }
    }

    if (currentMillis - previousMillis1 >= interval_1) { // проверяем не прошла ли одна секунда с момента перехода в режим 2.
      //Если да -
      regim = 3;                                        //переходим в режим 3
    }
    analogWrite(led_pin, count);
  }

  if (regim == 3) // режим выключения ШИМ
  {
    if (f1 == 0) {
      Serial.print(millis());
      Serial.println(" ШИМ выключен");
      Serial.println("");
    }
    f1 = 1;
    digitalWrite(led_pin, LOW);
  }
}