ARDUINO переполнение Millis() и Micros()

knik777
Offline
Зарегистрирован: 26.09.2015

Добрый день! Наткнулся на идею, хочу обсудить.

Millis() и Micros() обнуляются. Поэтому хоть и не критично, но возможен глюк работы ардуино при переполнении счетчика во время выполнения цикла в моей программе:

#define INTERVAL2  5*60*1000UL 
<...>
if (digitalRead(SENSORPIN)==1) // если сработал датчик движения
  {
    timeoff=millis(); // засекаем время когда он сработал
    ledstate=1; // свет включить
  }
  else
  {
    if (millis()-timeoff>INTERVAL2)  // если с момента включения по датчику движения прошло более 20 секунд
    {
      ledstate=0; // свет выключить
    }

Пришла идея, как красиво устранить этот глюк.

Для этого нужно условие

millis()-timeoff

заменить на значение по модулю:

abs(millis()-timeoff)

Таким образом, если обнуления millis() не произошло, то все в порядке - число и так положительное.

А если обнуление произошло, то Millis()-timeoff будет огромным отрицательным числом.

Как вам такая реализация?

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

Если сделать вычитание двух беззнаковых чисел, отрицательное значение не может получиться (разумеется если не привести принудительно). Посмотрите, что возвращает millis().

Поищите на форуме, эта тема уже обсуждалась неоднократно.

 

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

unsigned long вам в руки :)

knik777
Offline
Зарегистрирован: 26.09.2015

kisoft пишет:

Если сделать вычитание двух беззнаковых чисел, отрицательное значение не может получиться (разумеется если не привести принудительно). Посмотрите, что возвращает millis().

Поищите на форуме, эта тема уже обсуждалась неоднократно.

Поясню, откуда берется по моему мнению отрицательное число и собственно сам глюк:

Рассмотрим последовательность работы программы на 49 день от сотворения Мира :)

1) MILLIS близко к переполнению. оно равно, скажем 4294967000

2) присваиваем timeoff=4294967000

3) при значении 4294967295 происходит переполнение MILLIS и оно обнуляется.

4) если из небольшого значения MILLIS вычесть большое значение timeoff, образуется отрицательное число, которое и глючит программу, так как условие не выполняется никогда!

 

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

Unsigned long variables are extended size variables for number storage, and store 32 bits (4 bytes). Unlike standard longs unsigned longs won't store negative numbers, making their range from 0 to 4,294,967,295 (2^32 - 1).

knik777
Offline
Зарегистрирован: 26.09.2015

Valera19701 пишет:

Unsigned long variables are extended size variables for number storage, and store 32 bits (4 bytes). Unlike standard longs unsigned longs won't store negative numbers, making their range from 0 to 4,294,967,295 (2^32 - 1).

Валера, что Вы хотите сказать этим?

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

сделайте unsigned long timeoff = 0;

конец millis чему равен, и конец uint32_t (unsigned long) поэтому никогда не будет при вычитании отрицательного значения

knik777
Offline
Зарегистрирован: 26.09.2015

timeoff и так unsigned long (обратите внимание на буквы UL в описании #DEFINE)

вопрос в другом.

когда Unsigned long заканчивается, то есть переполняется, то MILLIS() обнуляется и начинает бежать заново. и нужно в коде предусмотреть, чтобы это обнуление не приводило к сбою.

прочитайте логику выполнения (я ее расписал под цифрами последовательностью).

на мой взгляд, отрицательные числа действительно должны образоваться при обнулении MILLIS. Разве нет?

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011
knik777
Offline
Зарегистрирован: 26.09.2015

 

проверил. отрицательных чисел действительно нет.

верно ли я понимаю, что даже при обнулении на 50й день значения MILLIS() ошибки в условии timeoff-MILIS()>interval не будет?

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

на micros проверьте, там 70 минут ждать

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

Давно всё проверено.
Читайте посты дальше.
Но только когда именно так:

  millis()  - prevTime  >=    interval

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

knik777 пишет:

возможен глюк работы ардуино при переполнении счетчика во время выполнения цикла в моей программе:

Невозможен.
 
Только пишите всегда, как у Вас:
 
if (millis()-timeoff>INTERVAL2)  
 
и никогда не пишите вот так
 
if (millis() > timeoff+INTERVAL2)  // ЭТО БЯКА!!!!!
 
и всем будет хорошо.
knik777
Offline
Зарегистрирован: 26.09.2015

спасибо! Я понял! :)

Клапауций 322
Offline
Зарегистрирован: 31.12.2015

knik777 пишет:

спасибо! Я понял! :)

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


// тест переполнения unsigned long millis() в коде class_BUTTON.h(класс титановый велосипед для тактовой кнопки).
// проверка корректности условия if (nm - m > 10) когда m считано до переполнения millis(), nm - после переполнения.

// результат проверки - корректно переползаем переполнение из "до" в "после":

// 4294967294 - 4294967284 = 10
// 4294967295 - 4294967285 = 10
// 0          - 4294967286 = 10
// 1          - 4294967287 = 10
// 2          - 4294967288 = 10
// 3          - 4294967289 = 10
// 4          - 4294967290 = 10
// 5          - 4294967291 = 10
// 6          - 4294967292 = 10
// 7          - 4294967293 = 10
// 8          - 4294967294 = 10
// 9          - 4294967295 = 10
// 10         - 0          = 10
// 11         - 1          = 10


#include <DigiUSB.h>

// unsigned long 0 - 4294967295

unsigned long m  = 4294967283; // 10 миллисекунд до переполнения.
unsigned long nm = 4294967293; // 1 миллисекунд до переполнения.

void setup() {

DigiUSB.begin();

int n = 0;

while(n < 14){

if (DigiUSB.tx_remaining() > 40) { // проверка места в буфере USB.
++n;
++m;
++nm;

DigiUSB.print(nm);
DigiUSB.print(" - ");
DigiUSB.print(m);
DigiUSB.print(" = ");
DigiUSB.println(nm - m); // здесь предполагаем получить 10, если условие отрабатывается корректно.

DigiUSB.delay(1);

}

DigiUSB.delay(1);

}

}

void loop() {DigiUSB.delay(1);}