Глюки функции Millis()
- Войдите на сайт для отправки комментариев
Пнд, 21/03/2016 - 14:33
Добрый день!
Коллеги, в казалось бы простом скетче где-то закралась странная ошибка.
Суть скетча в упрощенном варианте:
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
//************************************************************************************************* // процедура сравнивает два времени и возвращает разницу в виде числа, учитывает переход времени через 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; }Автора не смущает тот факт, что previousMillis время от времени обнуляется в скетче (строка 68), затем - получается currentMillis (строка 39), затем - заполняется previousMillis (строка 53)? При таком подходе вполне вероятен сценарий, когда previousMillis будет больше currentMillis. Это так - что заметил навскидку.
Зачем вообще обнулять previousMillis - не пойму. Эти испуги с переходом через ноль и якобы неправильным подсчётом промежутков связаны лишь с тем, что люди не знакомы с беззнаковой арифметикой. Те, кто знает - не парятся :)
Ну почему же неправильно, всё правильно, давай проанализируем код:
1. Строка 39, фиксируем текущий millis в currentMillis, это "0", точка отсчета, дальше время будет увеличиваться. Запомним это.
2. Строка 53, фиксируем текущий millis в previousMillis, при этом миллис уже укатился вперед, тем более там два вывода в Сериал, короче миллис стал чуть больше, чем был в 39 строке.
3. Строка 61, закономерный результат, currentMillis меньше, чем previousMillis.
Разумеется это происходит не каждый раз, потому что нужно чтобы карты сложились и это может происходить, например, когда идет дребезг концевика. Можешь сказать, что этого нет, потому что по монитору этого не видно, так тут везде еще есть вывод в Сериал, а это не мало по времени выполнения.
Для начала, попробуй в 53 строке присваивать не millis, а currentMillis, ситуация немного измениться, во всяком случае такого уже не будет, но будет что-нибуль другое. Но это совсем другая история.
vde69,
Спасибо за процедуру.
DIYMan,
меня не смущает обнуление previousMillis.
Смущение вызвало то, что
1. в
currentMillis записалось значение меньшее чем в previousMillis2. контроллер пропустил сравнениеif((currentMillis - previousMillis) >= MaxWaitingTime).Т.е.16824)-(currentMillis (16825)) >=1500 позволило напечатать это в лог.previousMillis(kisoft,
Да, я исправил
previousMillis=millis(); на previousMillis=currentMillis; и проблема пока не возвращается//************************************************************************************************* // процедура сравнивает два времени и возвращает разницу в виде числа, учитывает переход времени через 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; }не надо учитавать переход ( переполнение ) миллис() ! давно уже обсуждено.....
Коллеги,
всем спасибо. Разобрался. Тему можно закрывать
ты разобрался , молодец....
а тем , кто на такие грабельки наступит , оставь пояснения , код с комментами....
...как знаешь :)
vde69,
Спасибо за процедуру.
DIYMan,
меня не смущает обнуление previousMillis.
Смущение вызвало то, что
1. в
currentMillis записалось значение меньшее чем в previousMillis2. контроллер пропустил сравнениеif((currentMillis - previousMillis) >= MaxWaitingTime).Т.е.16824)-(currentMillis (16825)) >=1500 позволило напечатать это в лог.previousMillis(Про беззнаковую арифметику рассказать?
Для интереса - проверь ;)
ещё бы и результат увидеть.....
DIYMan , для интересса - какое у тибя должно быть значение m3 ?
какое предполагаешь ?
Там будут все единицы, т.е. число много больше, чем 1500.
UPD: единицы - в смысле все биты будут равны 1. Т.е. это максимальное число, которое можно вписать в unsigned long.
DIYMan , для интересса - какое у тибя должно быть значение m3 ?
какое предполагаешь ?
Да всё просто - все биты в единичку, мы же беззнаковыми числами оперируем ;)