Глюки функции Millis()

kast2k
Offline
Зарегистрирован: 16.01.2015

Добрый день!

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

Суть скетча в упрощенном варианте:

1. зажали концевик,

2. выполнили некую работу,

3. отпустили концевик,

4. подождали 1,5 секунды,

5. вывели результат.

И вроде все по коду замечательно, но временами функция millis() возвращает некорректные значения.

Скетч:

const int tohnichi=8;
const int koncevik=7;
const int resetkey=6;
const int greenlight=5;
const int redlight=4;

const int MaxWaitingTime=1500; //in milliseconds
const int MaxTorquesNumber=5; // интересующее количество затяжек

// начальное состояние системы
int koncevikFlag=0; 
int resetkeyFlag=0;
int tohnichiFlag=0;

int processStart=0; // работа запрещена

int tohnichiCount=0; // затяжек нет

unsigned long      previousMillis = 0;    // will store last time
unsigned long      previousMillisToh = 0;    // will store last time

void setup() 
{
  Serial.begin(9600);

  for (int i=6;i<9;i++) {
    pinMode(i,INPUT_PULLUP);
  }
  
  pinMode(redlight, OUTPUT);
  pinMode(greenlight, OUTPUT);
  digitalWrite(greenlight,LOW); // гасим зеленый свет
  digitalWrite(redlight,LOW);  // гасим красный свет

}

void ProcessStarted() {
  
  unsigned long currentMillis = millis();
  
  if(digitalRead(koncevik)==LOW&&koncevikFlag==0)//если пришел сигнал от концевика и переменная koncevikFlag равна 0 и красный сигнал не горит, то ... 
     { // начинаем работу
      koncevikFlag=1; // указали, что концевик зажат
      Serial.println("Koncevik zaghat");       
      processStart=1; // разрешаем работу
      Serial.println("processStart=1");       
      tohnichiCount=0;  // обнуляем счетчик затяжек

      digitalWrite(greenlight,LOW); // гасим зеленый и красный свет
      digitalWrite(redlight,LOW);
       
      if (previousMillis==0) {
        previousMillis=millis();
       }
     } 
       
  if(digitalRead(koncevik)==HIGH&&koncevikFlag==1)//если нет сигнала от концевика и переменная koncevikFlag равна 1 ,то ... 
     {  // отжат концевик
       if((currentMillis - previousMillis) >= MaxWaitingTime) 
         {
          Serial.println(String(currentMillis)+"-"+String(previousMillis)); //ошибка наблюдается здесь
            
          koncevikFlag=0;//обнуляем переменную koncevikFlag        
  
          Serial.println(String(tohnichiCount));
          tohnichiCount=0; // обнуляем счетчик затяжек
          processStart=0; // запретили работу
          previousMillis=0;        
        
            if  (tohnichiCount==MaxTorquesNumber) { // если количество затяжек равно заданному             
              digitalWrite(greenlight,HIGH); // зажигаем зеленый свет
              Serial.println("GREEN on");
              digitalWrite(redlight,LOW); // гасим красный свет
              Serial.println("RED off");
            }
          else // если количество затяжек не совпало
            {
              digitalWrite(greenlight,LOW); // гасим зеленый свет
              Serial.println("GREEN off");
              digitalWrite(redlight,HIGH); // зажигаем красный свет
              Serial.println("RED on");      
            }
        }    
     }
}

Вот пример работы сего скетча :(

Koncevik zaghat
processStart=1
9149-7538
0
GREEN off
RED on
resetkey
resetkey pressed
Koncevik zaghat
processStart=1
14483-12069
0
GREEN off
RED on
resetkey
resetkey pressed
Koncevik zaghat
processStart=1
16824-16825  -- вот функция millis() вернула значение 16824
0
GREEN off
RED on
Koncevik zaghat
processStart=1
22082-16862
0
GREEN off
RED on

 

vde69
Offline
Зарегистрирован: 10.01.2016
//*************************************************************************************************
// процедура сравнивает два времени и возвращает разницу в виде числа, учитывает переход времени через 0
//   start_time - начальное время
//   end_time - конечное время
//
// !!!! процедура чуствительна к разрядности исполняемого кода !!!!
// !!!! процедура может работать неправильно при двойном переходе времени через 0 !!!!
//*************************************************************************************************
unsigned long getDelayTime(unsigned long start_time, unsigned long end_time)
{
  unsigned long result;
  if (start_time <= end_time) {
    result = end_time - start_time;
  }
  else {
    result = 4294967295 - end_time + start_time;
  }
  return result;
}

 

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Автора не смущает тот факт, что previousMillis время от времени обнуляется в скетче (строка 68), затем - получается currentMillis (строка 39), затем - заполняется previousMillis (строка 53)? При таком подходе вполне вероятен сценарий, когда previousMillis будет больше currentMillis. Это так - что заметил навскидку.

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

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Ну почему же неправильно, всё правильно, давай проанализируем код:

1. Строка 39, фиксируем текущий millis в currentMillis, это "0", точка отсчета, дальше время будет увеличиваться. Запомним это.

2. Строка 53, фиксируем текущий millis в previousMillis, при этом миллис уже укатился вперед, тем более там два вывода в Сериал, короче миллис стал чуть больше, чем был в 39 строке.

3. Строка 61, закономерный результат, currentMillis меньше, чем previousMillis.

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

Для начала, попробуй в 53 строке присваивать не millis, а currentMillis, ситуация немного измениться, во всяком случае такого уже не будет, но будет что-нибуль другое. Но это совсем другая история.

 

kast2k
Offline
Зарегистрирован: 16.01.2015

vde69,

Спасибо за процедуру.

DIYMan,

меня не смущает обнуление previousMillis.

Смущение вызвало то, что

1. в currentMillis  записалось значение меньшее чем в previousMillis

2. контроллер пропустил сравнение if((currentMillis - previousMillis) >= MaxWaitingTime).

Т.е. (currentMillis (16824)- previousMillis(16825)) >=1500 позволило напечатать это в лог.

kast2k
Offline
Зарегистрирован: 16.01.2015

kisoft,

Да, я исправил previousMillis=millis(); на previousMillis=currentMillis; и проблема пока не возвращается

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

vde69 пишет:

//*************************************************************************************************
// процедура сравнивает два времени и возвращает разницу в виде числа, учитывает переход времени через 0
//   start_time - начальное время
//   end_time - конечное время
//
// !!!! процедура чуствительна к разрядности исполняемого кода !!!!
// !!!! процедура может работать неправильно при двойном переходе времени через 0 !!!!
//*************************************************************************************************
unsigned long getDelayTime(unsigned long start_time, unsigned long end_time)
{
  unsigned long result;
  if (start_time <= end_time) {
    result = end_time - start_time;
  }
  else {
    result = 4294967295 - end_time + start_time;
  }
  return result;
}

 

не надо учитавать переход ( переполнение ) миллис() ! давно уже обсуждено.....

kast2k
Offline
Зарегистрирован: 16.01.2015

Коллеги,

всем спасибо. Разобрался. Тему можно закрывать

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

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

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

kast2k пишет:

vde69,

Спасибо за процедуру.

DIYMan,

меня не смущает обнуление previousMillis.

Смущение вызвало то, что

1. в currentMillis  записалось значение меньшее чем в previousMillis

2. контроллер пропустил сравнение if((currentMillis - previousMillis) >= MaxWaitingTime).

Т.е. (currentMillis (16824)- previousMillis(16825)) >=1500 позволило напечатать это в лог.

Про беззнаковую арифметику рассказать?

 unsigned long m1 = 16824;
 unsigned long m2 = 16825;
 unsigned long m3 = m1 - m2;
 Serial.println(String(m3));

Для интереса - проверь ;)

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

ещё бы и результат увидеть.....

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

DIYMan ,   для интересса - какое у тибя должно быть значение m3 ?
какое предполагаешь ?

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Там будут все единицы, т.е. число много больше, чем 1500.

UPD: единицы - в смысле все биты будут равны 1. Т.е. это максимальное число, которое можно вписать в unsigned long.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

SU-27-16 пишет:

DIYMan ,   для интересса - какое у тибя должно быть значение m3 ?
какое предполагаешь ?

Да всё просто - все биты в единичку, мы же беззнаковыми числами оперируем ;)