Skocz do zawartości

_delay_ms() vs delay()


Belferek

Pomocna odpowiedź

Wiadomo, że wszelkie blokujące delay-e to nie najlepszy wybór. Eksperymentując okazuje się, że programując Arduino Uno w dedykowanym IDE możemy oprócz standardowego delay() użyć _delay_ms(). Różnica jest od razu widoczna w wielkości kodu wynikowego. Kod z _delay_ms() jest wyraźnie mniejszy. Wiem, że to różne funkcje i zastanawiam się dlaczego we wszystkich źródłach dot. programowania w Arduino IDE preferuje się delay(), które generuje bardziej obszerny kod wynikowy. Pytanie więc do doświadczonych kolegów kiedy (a kiedy nie) możemy użyć _delay_ms()?

Edytowano przez Belferek
Link do komentarza
Share on other sites

Szukając przez chwilę u wujka Google, _delay_ms() odnosi mnie do funkcji z bibliotek AVR. Natomiast delay() to funkcja z API (myślę że można nazwać to API) które stworzyło Arduino (lub Wiring jeśli tam było pierwsze). 

23 minuty temu, Belferek napisał:

Wiem, że to różne funkcje i zastanawiam się dlaczego we wszystkich źródłach dot. programowania w Arduino IDE preferuje się delay(), które generuje bardziej obszerny kod wynikowy.

We wszystkim spod parasola Arduino będzie używane właśnie delay(), dlatego że nieważne jaką płytkę weźmiesz, delay() i kod napisany za pomocą API Arduino, będzie działać funkcjonalnie (przynajmniej powinien) tak samo. A postarać się muszą o to twórcy konkretnej implementacji. I teraz jeśli _delay_ms() jest funkcją z biblioteki AVR, to niekoniecznie użyjemy ją dla płytek na procesorach ARM itp.

23 minuty temu, Belferek napisał:

Pytanie więc do doświadczonych kolegów kiedy (a kiedy nie) możemy użyć _delay_ms()?

Jak piszesz kod do konkretnego zadania i będzie tam tylko wspomniana 328P (lub inna z rodziny, dla której działa _delay_ms()), to używaj zoptymalizowanych funkcji. Natomiast jeśli piszesz "multiplatformowy" kod to używaj funkcji i metod z API Arduino.

Edytowano przez Matthew11
  • Lubię! 1
  • Pomogłeś! 1
Link do komentarza
Share on other sites

Jeszcze jedno: delay() to funkcja, która zawiesza działanie programu na określony czas. W tym czasie możliwe jest wykonywanie przez procesor innych czynności (taki sztandarowy przykład to obsługa WiFi w ESP8266), przy czym do czasu zawieszenia nie dolicza się czas spędzony na obsłudze przerwań. Na odmianę _delay_ms() polega na odliczeniu iluś tam cykli procesora (przeliczonych z milisekund), realizowana jest po prostu przez pętlę wykonującą n obrotów. Procesor jest cały czas zajęty, a czas spędzony w przerwaniach będzie doliczony do czasu zawieszenia działania (w końcu pętla nie wie, że coś ją przerwało i wznowiło). Przy okazji - __delay_ms() nie potrzebuje żadnych timerów.

Obie funkcje są niedokładne i nie powinny być stosowane do odmierzania czasu.

A objętość kodu nie ma nic wspólnego z zastosowaniem danej funkcji. Z dokładnością do stwierdzenia, że w pewnych implementacjach kod delay() może być baaaaaardzo krótki - coś w stylu:
 


void delay(uint32_t parametr)
{
	extern volatile uint32_t millis_counter; 
  	uint32_t t=millis_counter;
	while (millis_counter - t > parametr);
}

gdzie millis_counter jest po prostu zwiększany w przerwaniu co milisekundę.

 

 

  • Lubię! 1
  • Pomogłeś! 1
Link do komentarza
Share on other sites

1 minutę temu, ethanak napisał:

A objętość kodu nie ma nic wspólnego z zastosowaniem danej funkcji

Powiem jak Ferdek -" jak nie ma jak ma":

Skompilowany programik z 3 wywołaniami delay():

Szkic używa 2990 bajtów (9%) pamięci programu. Maksimum to 32256 bajtów.

I to samo kiedy zamieniam delay() na _delay_ms()

Szkic używa 2828 bajtów (8%) pamięci programu. Maksimum to 32256 bajtów.

Ja tu widzę różnicę 162 bajtów, a to już trochę jest.

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

Z zastosowaniem - czyli z tym, do czego służy dana funkcja, a nie z tym że sobie ją wywołasz w programie 3 razy.

Rozumiem, że kompilując te testowe programy wyłączyłeś optymalizację?

 

Edytowano przez ethanak
Link do komentarza
Share on other sites

(edytowany)
8 minut temu, ethanak napisał:

Rozumiem, że kompilując te testowe programy wyłączyłeś optymalizację?

Nie, kompiluję z domyślnymi ustawieniami świeżo zainstalowanego IDE 1.8.11

Gdzie w Arduino IDE mogę włączyć (wyłączyć) optymalizację, o której piszesz?

Edytowano przez Belferek
Link do komentarza
Share on other sites

Przed chwilą, Belferek napisał:

Nie, kompiluję z domyślnymi ustawieniami świeżo zainstalowanego IDE 1.8.11

No to testy możesz sobie obić o kant warsztatu, bo sprawdziłeś właśnie zdolność kompilatora do optymalizacji kodu obu funkcji.

6 minut temu, Belferek napisał:

Gdzie w Arduino IDE mogę włączyć optymalizację, o której piszesz?

W pliku platforms.txt - nie wiem gdzie u Ciebie siedzi, u mnie w ~/arduino-1.8.10/hardware/arduino/avr/platform.txt

 

  • Pomogłeś! 1
Link do komentarza
Share on other sites

Dzięki za wskazówki. Daleki jestem od "testowania kompilatora". Chodziło mi najzwyczajniej o jakby nie było ponad 100 bajtów oszczędności bez wnikania w gąszcz opcji konfiguracyjnych kompilatora.

Link do komentarza
Share on other sites

Anonim

Wracając do meritum. Różnica w ilości zużytej pamięci w porównaniu pomiędzy obiema funkcjami zmniejsza się wraz z ilością wywołań. Każde wywołanie _delay_ms() generuje kod asm - im więcej wywołań tym więcej pamięci zużywa. Polecam zapoznać się z implementacją tej funkcji bo została ona napisana w sposób dość nietypowy. Natomiast funkcja delay() z arduino odwołuje się do globalnych zmiennych volatile co samo w sobie stanowi narzut na wykorzystanie pamięci. Z doświadczenia wiem, że bardziej optymalnym jest przeliczenie sobie instrukcji asm względem ustawionego taktowania i wielokrotne wywoływanie pętli opóźniającej, niż stosowanie _delay_ms() czy tym bardziej delay() arduino. Niestety taka pętla nie bierze pod uwagę czasu spędzonego w obsłudze przerwań na co zwrócił uwagę @ethanak. Można co prawda dopisać procedurę uwzględniającą wpływ przerwań ale w rezultacie nie będzie to znacząco lepsze rozwiązanie od funkcji delay(). Najlepszym i najdokładniejszym sposobem na precyzyjne odmierzanie odcinków czasu w avr jest zastosowanie RTC z timera 2 w trybie asynchronicznym co z kolei uniemożliwia taktowanie rdzenia szybciej niż pozwala na to wbudowany oscylator czyli 8MHz. Reasumując, jeśli się nie chce komplikować sobie życia najlepszym i uniwersalnym sposobem jest trzymanie się wstępnie wybranej konwencji, jeśli arduino to arduino jeśli AVR lib c to lib c.  Nadmienię jeszcze tylko, że najbardziej optymalna metoda to avr lib c + wstawki asm gdzie różnica z frameworkiem arduino pod względem prędkości działania i użycia pamięci jest tak znaczna w wielu przypadkach, że wykorzystanie arduino wręcz uniemożliwia realizację założonych celów, jest to niestety żmudne i czasochłonne. Na szczęście takich przypadków nie ma zbyt wiele a samo arduino też korzysta z avr lib c.

Link do komentarza
Share on other sites

Anonim
Ten temat został zamknięty.
×
×
  • 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.