Skocz do zawartości

Dlaczego "millis jest złe" ;)


SOYER

Pomocna odpowiedź

Przepełnienie po 4`294`967`296ms co daje niecałe 50 dni.

Ponadto millis nie zawiera pewnych wartości, co zdaje się 125ms licznik przeskakuje o ileś tam (chyba 8 - przeanalizuj źródła).

Operując na millis (porównując czas) operujesz na zmiennej 32 bit. W ARM to nawet dobrze, bo 32-biot wykonuje się szybciej niż 8-bit, w AVR same wady: zamiast 1 bajt dla czasów do 255ms, 2 bajtów do 65sekund zawsze zużywasz 4 bajty. Porównanie 4 bajtów trwa dłużej niż 1 czy 2.

Gdy pracowałem na AVR, nie używałem mechanizmu czasu systemowego 32-bit do porównania, tylko tworzyłem wirtualne timery liczące do 0, "szyte na miarę", czyli 1, 2 czy 4 bajtowe. Długie odcinki czasu, jeśli nie zależy na dokładności ms, liczyłem timerami 10 lub 100ms a nawet 1s. Tak można zaoszczędzić RAM, a porównanie jest szybsze.

Wirtualne timery w Arduino można sobie zrobić ale...twórcy nie przewidzieli wektorów w które można "wpiąć" swoje funkcje. Trzeba uruchomić przerwania od porównania (sa dwa dostępne) i tam stworzyć timery ale nie będą liczyć dokładnie 1m, więc trzeba co jakiś czas zrobić "przeskok".

Niestety, w Arduino, czego się nie tknąć, to zawsze trzeba stosować protezy 😞

Link do komentarza
Share on other sites

Ahhh Ty 😄 Ok, najprościej jak się da skoro już nowy do tego temat założyłeś. Otóż ta funkcja używa zmiennych UL co zazwyczaj (choć niezawsze) nie jest konieczne bo dłuższe odcinki czasu można sobie łączyć z mniejszych operując już na zmiennych lokalnych, dodatkowo zmienna jest static volatile co dodatkowo zwiększa narzut (czas wykonywania, zużyta pamięć przy każdym wywołaniu) z używaniem jej związany, dodatkowo blokuje ona Timer0, który można wykorzystać do choćby tego samego celu tylko w sposób bardziej oszczędny czy dopasowany do własnych potrzeb i co najważniejsze uważam, że millis jest złe bo tak.

Faktycznie jest sporym ułatwieniem i nie kwestionuję tego ale lepiej jest korzystać z timera bezpośrednio na własnych zmiennych i odwoływać się do nich przez przepisanie a nie za pośrednictwem funkcji. wystarczą do tego 2 makra żeby wygodnie odczytywać upływający czas a przy większej aplikacji oszczędności sięgają setek bajtów. No i należy pamiętać, że kwarc atmegi to nie rtc i odmierzanie w ten sposób czasu nie jest zbyt dokładne.

Czy ta odpowiedź jest zadowalająca?

  • Tak.
  • Raczej tak.
  • Nie.
  • Raczej nie.
  • Nie wiem.

 

Link do komentarza
Share on other sites

Raczej nie. 

28 minut temu, atMegaTona napisał:

lepiej jest korzystać z timera bezpośrednio na własnych zmiennych i

Jak? Przykład dla zielonego, lub link do douczenia(dla greenhorna). 

Myślałem, że millis to odwoływanie się do procesu który i tak się wykonuje i "podbieranie" mu "czasu" dla własnych celów. Domyślam się, że sama funkcja millis() jest "zasobożerna", o to chodzi? 

Skoro nie należy korzystać z millis(), to jak zrobić proste miganie dwoma ledami z różnymi interwałami?? 

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

35 minut temu, atMegaTona napisał:

Otóż ta funkcja używa zmiennych UL

A  jakie byś zaproponował?

35 minut temu, atMegaTona napisał:

dłuższe odcinki czasu można sobie łączyć z mniejszych operując już na zmiennych lokalnych

I to dopiero jest marnowanie zasobów.

36 minut temu, atMegaTona napisał:

blokuje ona Timer0

Nie "blokuje" a "ogranicza możliwość jego używania". Jakoś pwm sobie z tym radzi...

39 minut temu, atMegaTona napisał:

No i należy pamiętać, że kwarc atmegi to nie rtc i odmierzanie w ten sposób czasu nie jest zbyt dokładne

Jeśli komuś jest potrzebny dokładny czas to nie używa kwarca atmegi, prawda? Jaka jest tolerancja dla kwarca w Arduino? Serio pytam bo nie wiem.

5 minut temu, SOYER napisał:

Domyślam się, że sama funkcja millis() jest "zasobożerna"

Bardzo. Obejrzyj sobie w arduino-1.x.x/hardware/arduino/avr/cores/arduino/wiring.c to zobaczysz jak strasznie zasobożerna.

 

14 minut temu, SOYER napisał:

Skoro nie należy korzystać z millis(), to jak zrobić proste miganie dwoma ledami z różnymi interwałami

Na AVR się najprawdopodobniej nie da, trzeba użyć STM32.

( @SOYER dobrze, że dotychczas o tym nie wiedzieliśmy 😉 )

 

 

Link do komentarza
Share on other sites

1 minutę temu, ethanak napisał:

Na AVR się najprawdopodobniej nie da, trzeba użyć STM32.

Ot i cała odpowiedź na moje pytanie, tylko dlaczego w temacie dotyczącym Arduino IDE, @atMegaTonapisze, że millis jest złe. 

 

3 minuty temu, ethanak napisał:

dobrze, że dotychczas o tym nie wiedzieliśmy 😉 )

 

o czym? 

Link do komentarza
Share on other sites

Kolejny temat nad wyższością forda nad volkswagenem.. Rzecz w tym, że kiedy używa się przerwania bezpośrednio można sobie wybrać dowolny interwał to raz, można też podczepić się przy okazji np. pod pwm dodatkowo można też wykorzystać do zliczania millisów, millisówX10 czy jak komu pasuje najlepiej, rejestry sprzętowe GPIOR lub inne nieużywane np. od eeprom i zazwyczaj wystarcza do tego 16 bit i finalnie odczytu aktualnego stanu czy różnicy można dokonać za pomocą prostego makra które nawet można na upartego podstawić zamiast tej złej funkcji millis w arduino.

Być może w krótkim programie do mrugania diodami różnica w użyciu pamięci będzie niewielka ale kiedy pisze się coś większego gdzie mega32 puchnie od kodu to warto tego typu rozwiązania alternatywne wziąć jednak pod uwagę zamiast z góry zakładać, że się nie zmieści i trzeba kupić płytkę z większą atmegą.

Ktoś ostatnio pisał, że mu 2 bajty zbrakło, czasami braknie 200 a tym czasem w ten sposób da się oszczędzić całkiem niemało flasha więc chyba warto mieć to na uwadze i właśnie na to chciałem uwagę zwrócić jak widać skutecznie.

Funkcja micros pod tym względem jest jeszcze gorsza. Będzie trzeba chyba otworzyć nowy temat pt. "Dlaczego micros jest jeszcze gorsza" 😉

Link do komentarza
Share on other sites

1 godzinę temu, SOYER napisał:

Domyślam się, że sama funkcja millis() jest "zasobożerna", o to chodzi? 

sama funkcja nie bardzo ale wiele odwołań do niej tak z samej natury tej funkcji wynika duża ilość wywołań a do tego niektórzy korzystają jeszcze z micros.

Analizując linijka po linijce wszystkie 6 linijek tej funkcji 😉 można by mieć wrażenie, że to przepisanie do zmiennej 'm' nie ma sensu a jednak ma bo chodzi tu właśnie o ilość odwołań do zmiennej volatile unsigned long i jak najszybsze pobranie danych kiedy przerwania są zablokowane a to też nie zawsze nie szkodzi bo czasem szkodzi i właśnie na to również chciałem zwrócić uwagę.

Dziękuję za uwagę.

Edytowano przez Anonim
okropne błędy ortograficzne
Link do komentarza
Share on other sites

15 godzin temu, atMegaTona napisał:

można by mieć wrażenie, że to przepisanie do zmiennej 'm' nie ma sensu a jednak ma bo chodzi tu właśnie o ilość odwołań do zmiennej volatile unsigned long. 

Nie chodzi o ilość odwołań, tylko o to, ze dostęp musi być atomowy. W arduino w mało elegancko zrealizowanmo to "na piechotę"

	uint8_t oldSREG = SREG; // Zapamietaj SREG w którym jest flaga przerwań "I"

	cli(); // Blokuj przerwania
	....
	SREG = oldSREG; // Przywróć SREG co za tym idzie flagę przerwań

a jest do tego makro ATOMIC_BLOCK https://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html

Można więc było millis napisać tak

unsigned long millis() {
	unsigned long m;
	ATOMIC_BLOCK( ATOMIC_RESTORESTATE ) {
		m = timer0_millis;
		}
	
	return m;
}

Kod jest czytelniejszy. To samo w ARM wyglądało by po prostu tak

unsigned long millis() {
	return timer0_millis;
}

 

Link do komentarza
Share on other sites

1 godzinę temu, atMegaTona napisał:

Ktoś ostatnio pisał, że mu 2 bajty zbrakło

Nawet wiem kto. Tylko to nie było o Arduino (chociaż faktycznie używałem Arduino IDE do kompilacji i uploadu na ATtiny85, z lenistwa), a akurat w moim programie funkcja millis występuje (nawet podobnie wygląda) bo ją lubię, w dodatku tego samego timera dodatkowo używam do sterowania dźwiękiem. Tak że kulą w płot, panie kolego 🙂

Tak przy okazji, przepisanie do zmiennej m nie ma nic wspólnego z ilością odwołań, po prostu trzeba pobrać wartość zmiennej volatile kiedy przerwania są zablokowane, odblokować przerwania jeśli trzeba i dopiero zwrócić wartość. Inaczej jak przepisując gdzieś po prostu się nie da...

A co do rozwiązań alternatywnych...

Co ma wspólnego ilość wywołań funkcji z jej wielkością/małością? Ilość kodu będzie dokładnie taka sama.

I jeszcze jedno:

Arduino jest dla początkujących. Jeśli ktoś przestaje być początkujący, to albo szuka innych rozwiązań, albo te które Arduino core oferuje uważa za wystarczające i - jeśli faktycznie są wystarczające - próby udowadniania takiemu komuś że powinien robić inaczej brzmią po prostu śmiesznie.

Aha, i jeszcze jedno:

Istnienie funkcji millis to wymóg pewnego standardu, który całkiem nieźle został zaimplementowany w Arduino. Podobnie kilka innych funkcji, na których tu się psy wiesza. Może warto zanim się następnego psa powiesi sprawdzić, co to jest za standard?

Link do komentarza
Share on other sites

36 minut temu, ethanak napisał:

Tak przy okazji, przepisanie do zmiennej m nie ma nic wspólnego z ilością odwołań, po prostu trzeba pobrać wartość zmiennej volatile kiedy przerwania są zablokowane, odblokować przerwania jeśli trzeba i dopiero zwrócić wartość. Inaczej jak przepisując gdzieś po prostu się nie da...

Cóż, otóż się da, wystarczyłoby zwrócić zmienną timer0_millis przez return tylko, że wtedy możliwy byłby błąd w sytuacji kiedy ta zmienna byłaby przekazywana po kawałku (8 bitowym) i pomiędzy kawałkami wystąpiłoby przerwanie i o to tu głównie chodziło, na ilość odwołań celowo zwróciłem uwagę nieco na wyrost bo jest to właśnie meritum tego o co mi tu chodzi :

36 minut temu, ethanak napisał:

Co ma wspólnego ilość wywołań funkcji z jej wielkością/małością? Ilość kodu będzie dokładnie taka sama.

Otóż ma, ponieważ odwołanie się do zmiennej volatile wymaga większego nakładu kodu asm niż do zmiennej lokalnej i dlatego w swoich funkcjach pisanych na arduino kiedy muszę skorzystać z globalnej volatile lub static lub jakiejkolwiek globalnej to przepisuje ją do lokalnej i na niej wykonuję działania po czym wynikiem na powrót modyfikuję tę globalną aby ograniczyć maksymalnie ilość odwołań do niej. Polecam sprawdzić samemu jaka będzie różnica, w niektórych przypadkach nawet połowę pamięci programu zajmują takie właśnie globalne odwołania i wykonują się dużo dłużej. To się tyczy oczywiście nie tylko pojedynczych zmiennych ale i całych obiektów struktur/klas, warto wtedy przepisywać interesujące składniki nie całe obiekty. 

36 minut temu, ethanak napisał:

Arduino jest dla początkujących. Jeśli ktoś przestaje być początkujący, to albo szuka innych rozwiązań, albo te które Arduino core oferuje uważa za wystarczające i - jeśli faktycznie są wystarczające - próby udowadniania takiemu komuś że powinien robić inaczej brzmią po prostu śmiesznie.

Cóż, w swoim czasie twierdzenie, że Ziemia jest okrągła też brzmiało śmiesznie a niektórych nawet spalono za to na stosie, mimo tego w żaden sposób nie przeforsowało to realnej płaskości Ziemi.

36 minut temu, ethanak napisał:

Istnienie funkcji millis to wymóg pewnego standardu, który całkiem nieźle został zaimplementowany w Arduino. Podobnie kilka innych funkcji, na których tu się psy wiesza. Może warto zanim się następnego psa powiesi sprawdzić, co to jest za standard?

Jestem zdecydowanym przeciwnikiem znęcania się nad zwierzętami i żadnych psów wieszać nie chcę, po prostu uważam, że skoro można coś poprawić to warto to zrobić mimo, że akurat funkcja millis nie jest najlepszym tego przykładem ale za to micros już tak i łatwo to zrobić nawet początkującemu z jako-takim obyciem w programowaniu do czego przecież nikogo nie zmuszam.

Swoją drogą byli nawet tacy co próbowali cały framework na asme przerobić ale to zupełnie inny temat.

Link do komentarza
Share on other sites

4 godziny temu, atMegaTona napisał:

Cóż, otóż się da, wystarczyłoby zwrócić zmienną timer0_millis przez return tylko, że wtedy możliwy byłby błąd

Czyli w trzech linijkach treści ująłeś to co ja w trzech słowach - się nie da.

 

4 godziny temu, atMegaTona napisał:
4 godziny temu, ethanak napisał:

Co ma wspólnego ilość wywołań funkcji z jej wielkością/małością? Ilość kodu będzie dokładnie taka sama.

Otóż ma, ponieważ odwołanie się do zmiennej volatile wymaga większego nakładu kodu asm niż do zmiennej lokalnej

Chyba mnie nie zrozumiałeś. Wywołanie funkcji bez parametrów to jedna instrukcja w asemblerze (trzy bajty pewnie, nie znam asm na AVR-y ale coś w stylu "JSR adres") i nie ma żadnej różnicy, czy funkcja ma dziesięć bajtów czy dziesięć kilobajtów oraz czy ma zmienne volatile. Nie mówię tu o samej funkcji.

 

4 godziny temu, atMegaTona napisał:
5 godzin temu, ethanak napisał:

Jeśli ktoś przestaje być początkujący, to albo szuka innych rozwiązań, albo te które Arduino core oferuje uważa za wystarczające i - jeśli faktycznie są wystarczające - próby udowadniania takiemu komuś że powinien robić inaczej brzmią po prostu śmiesznie.

Cóż, w swoim czasie twierdzenie, że Ziemia jest okrągła też brzmiało śmiesznie

Skąd taki dziwny argument? Co ma piernik do wiatraka? Chciałbym zaznaczyć, że już dwa rządy w naszym kochanym kraju chciały (jeden nadal chce) mi udowodnić że wiedzą lepiej co jest dla mnie dobre, mam tego dość i nie musisz się do tego dorzucać. Powtarzam: jeśli do danego zadania to co oferuje Arduinowy wiring jest wystarczające - nie będę szukał lepszych rozwiązań bo nie istnieje żadna potrzeba. Program, który ma np. zmierzyć co minutę temperaturę przy pomocy dwóch DS18B20 i wyświetlić wyniki owych pomiarów na jakimś LCD-ku nie będzie tego robić ani szybciej, ani lepiej jeśli zastosujemy tu superszybkie operacje na rejestrach a minutę będziemy odmierzać przy pomocy dwóch makr, uwalniając przy okazji timer zero hgw why.

4 godziny temu, atMegaTona napisał:

skoro można coś poprawić to warto to zrobić

Następny z cyklu "weźmy się i zróbcie"...

Nie wiem jak u Was, w świecie w którym razem z kolegą es2 żyjecie - ale u nas w świecie GPL-u i podobnych jak się widzi że coś można poprawić, a do tego jeszcze się to umie zrobić, to się nie trąbi na cały świat że coś jest złe a ja bym zrobił lepiej tylko najlepiej nie za darmo - ale się siada i robi.

 

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

Ja tylko nieskromnie dorzucę, że Arduino to nie tylko AVR, są też ARMy, gdzie najczęściej twórcy core stosują SysTick. Czy takie rozwiązanie jest też złe (może jest złe tylko dlatego że nie jest to STM). Jako przykład, fragment z Teensy Core: https://github.com/PaulStoffregen/cores/blob/f606ad9efb149e5559f37899c94784ca0bba9463/teensy3/core_pins.h#L2003 

Edytowano przez Matthew11
Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • 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.