Skocz do zawartości

Używanie funkcji millis() do odmierzania czasu


Diabloo24

Pomocna odpowiedź

W projekcie z ESP8266 potrzebuje odczytywać dane z czujników co określony czas np. 30 min. Do programowania ESP8266 wykorzystuje środowisko Arduino i znalazłem w dokumentacji funkcję millis(), która odlicza czas od uruchomienia programu/urządzenia. Przy wykorzystaniu tej funkcji można stworzyć proste odmierzanie czasu i wykonywanie pomiaru w określonych przeze mnie odstępach np. co 30min. Funkcja ta byłaby świetnym rozwiązaniem, ale doczytałem, że licznik przepełnia się po 50 dniach i resetuje, a także, że wykorzystanie tej funkcji (gdy już licznik jest w dużej mierze napełniony) może ona wprowadzać błędy w innych operacjach arytmetycznych wykonywanych w tym programie. Niestety martwi mnie to, że mogą się pojawić błędy w innych obliczeniach, ponieważ oprócz samego odmierzania czasu i dokonywaniu pomiarów, wykonuję również trochę operacji matematycznych, a później wysyłam dane na serwer. Tutaj moje pytanie czy stosowanie takiego rozwiązania do odmierzania będzie w ogóle poprawne skoro może ono generować błędy? Czy ta funkcja może działać lepiej w esp8266 skoro ma więcej pamięci niż Arduino? W jaki inny sposób można rozwiązać odmierzanie czasu i cykliczne pomiary przy projektach, w których wykonuje się więcej operacji i obliczeń (odliczanie 30 - 60 min)?

Link do komentarza
Share on other sites

Nic się nie resetuje - licznik się przepełnia (to różnica), ale urok operacji na liczbach bez znaku polega na tym, że nie wpływa to na obliczenia (przynajmniej w zakresie odliczania czasów nie przekraczających połowy pojemności licznika, czyli w naszym przypadku 25 dni). Poza tym nie jest tu ważna ilość pamięci a długość samego licznika , która w obu przypadkach wynosi 4 bajty.

Zapoznaj się z kursem Arduino, konkretniej https://forbot.pl/blog/kurs-arduino-ii-wielozadaniowosc-opoznienia-z-millis-id18418. Nie jest to dokładnie Twój problem, ale tak przy okazji jest on wyjaśniony.

 

  • Lubię! 1
Link do komentarza
Share on other sites

52 minuty temu, ethanak napisał:

Nic się nie resetuje - licznik się przepełnia (to różnica),

Tak właśnie to miałem na myśli, lecz w pośpiechu użyłem nie odpowiedniego słowa.

54 minuty temu, ethanak napisał:

Zapoznaj się z kursem Arduino, konkretniej https://forbot.pl/blog/kurs-arduino-ii-wielozadaniowosc-opoznienia-z-millis-id18418. Nie jest to dokładnie Twój problem, ale tak przy okazji jest on wyjaśniony.

Tak, czytałem. W zasadzie użyłem tego w podobny sposób. Głównie zastanawia mnie wpływ takiego odliczania na poprawność innych operacji. Przykładowo czy przy 45 dniu odliczania czasu w programie nie zaczną występować błędy a urządzenie przestanie pracować stabilnie?

 

Martwię się o to, że w początkowych dniach pracy urządzenia gdy licznik (w którym jest zapisany czas pracy) nie będzie zapełniony nawet do połowy to wszystkie inne operacje, które wykonuje w programie będą działać prawidłowo itp. Natomiast, gdy będzie już blisko przepełnienia licznika to będzie on miał wpływ na inne operacje w programie i będą się pojawiać błędy przez co urządzenie będzie pracować niestabilnie..?

Link do komentarza
Share on other sites

Licznik się nie "zapełnia" tylko przyjmuje jakąś tam określoną wartość (od 0 do 0xffffffff), która nie ma nic wspólnego z "jakością" (czymkolwiek by ona nie była) pracy, a szczególnie nie ma wpływu na "jakość" (czymkolwiek by ona nie była) innych operacji.

Wszyscy na całym świecie to stosują, większość ma obiekcje które nikną po dokładnym zapoznaniu się z zasadami arytmetyki liczb o stałej długości bez znaku.

Tak przy okazji - ESP8266 ma zegar czasu rzeczywistego który działa również w trybie uśpienia...

  • Lubię! 1
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Jeśli ESP ma coś robić tylko co 30min to można go po prostu usypiać na ten czas (powerdown, potem robi reset i zaczyna program od nowa - trochę dziwne, ale dane można przechować w pamięci RTC), jeśli ma działać cały czas wystarczy wynik różnicy rzutować na UL i będzie działać dopóki nie zrobisz resetu lub nie braknie zasilania, na podstawie przykładu BlinkWithoutDelay:

const int ledPin =  LED_BUILTIN;// the number of the LED pin

 
int ledState = LOW;             // ledState used to set the LED
 
uint32_t previousMillis = 0;        // will store last time LED was updated
 
const uint32_t interval = 1000UL;           // interval at which to blink (milliseconds)

void setup() {
 
  pinMode(ledPin, OUTPUT);
}

void loop() {
  
  uint32_t currentMillis = millis();

  if ((uint32_t)(currentMillis - previousMillis) >= interval) {
 
    previousMillis = currentMillis;
 
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
      
    }
 
    digitalWrite(ledPin, ledState);
    
  }
}

Jak chcesz sprawdzić jak działa przepełnienie można wykorzystać mniejsze zmienne ze znakiem i bez:

Po edycji:

//Pretend "counter" is "millis"
unsigned char counter;
 unsigned char roznica;
unsigned char previousCounter = 0;
unsigned char interval=30;
const char interval2=30;
void setup() {
   Serial.begin(115200);
   delay(2000);  // give enough time to open the serial monitor after uploading
   Serial.println("Starting...");
}
void loop() {
   for (int x=0; x<1000; x++) { // run through uchar a few times
// ******* simulate millis()
       counter++; // simulate millis()
       Serial.println(counter);
// ******
       unsigned char currentCounter = counter;
     //  roznica=(currentCounter - previousCounter);
       if ((unsigned char)(currentCounter - previousCounter) >= interval) { // check for rollover
         Serial.println("Trigger Event!");
         previousCounter = currentCounter;
      }
   }
Serial.println("...stopping!");
Serial.println(roznica);
while(1) delay(1); // Stop the Serial monitor output
}

 

Edytowano przez kaczakat
Korekta dla ESP8266
Link do komentarza
Share on other sites

Faktycznie przykład 2 testowałem na AVR, działa OK też z ESP32, ale WDT ESP8266 wkurza się w while(1), tu WDT jest automatycznie dołączony do kodu, wystarczy zmienić tę linijkę jak napisał @ethanak dodając delay(1) lub yield(): while(1) delay(1);. Dodałem we wcześniejszym kodzie, jakby ktoś tu jeszcze zajrzał.

Link do komentarza
Share on other sites

1 godzinę temu, Diabloo24 napisał:

Okej, ale wracając do mojej wątpliwości, rozumiem, że bez obaw mogę korzystać z funkcji millis() i powszechnie jest to też stosowane

Dobrze rozumiesz.

@kaczakat- może być nawet delay(0) - to to samo co yield().

  • Lubię! 1
Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.