Inne odpowiedzi są bardzo dobre, ale chcę rozwinąć sposób micros()
działania. To zawsze odczytuje bieżący timera sprzętowego (ewentualnie TCNT0
), który jest stale aktualizowany przez sprzęt (w rzeczywistości, co 4 ms powodu preskalera 64). Następnie dodaje licznik przepełnienia timera 0, który jest aktualizowany przez przerwanie przepełnienia timera (pomnożone przez 256).
Dlatego nawet w ISR możesz polegać na micros()
aktualizacji. Jeśli jednak zaczekasz zbyt długo , przegapisz aktualizację przepełnienia, a wtedy zwrócony wynik spadnie (tj. Otrzymasz 253, 254, 255, 0, 1, 2, 3 itd.)
Jest to micros()
- nieco uproszczone, aby usunąć definicje dla innych procesorów:
unsigned long micros() {
unsigned long m;
uint8_t oldSREG = SREG, t;
cli();
m = timer0_overflow_count;
t = TCNT0;
if ((TIFR0 & _BV(TOV0)) && (t < 255))
m++;
SREG = oldSREG;
return ((m << 8) + t) * (64 / clockCyclesPerMicrosecond());
}
Powyższy kod dopuszcza przepełnienie (sprawdza bit TOV0), aby mógł poradzić sobie z przepełnieniem, gdy przerwania są wyłączone, ale tylko raz - nie ma możliwości obsługi dwóch przepełnień.
TLDR;
- Nie rób opóźnień w ISR
- Jeśli musisz to zrobić, możesz czas,
micros()
ale nie millis()
. Również delayMicroseconds()
jest taka możliwość.
- Nie opóźniaj więcej niż 500 µs, bo przegapisz przepełnienie timera.
- Nawet krótkie opóźnienia mogą spowodować, że przegapisz przychodzące dane szeregowe (przy 115200 bodów dostaniesz nowy znak co 87 µs).
micros()
nie jest ISR. Jest to normalna funkcja. Flaga TOV0 pozwala przetestować sprzęt, aby sprawdzić, czy nastąpiło przepełnienie timera (ale jeszcze nie zostało przetworzone).Nie jest niewłaściwe używanie
millis()
lubmicros()
wykonywanie procedury przerwania.To jest źle wykorzystać je nieprawidłowo.
Najważniejsze jest to, że podczas rutynowego przerywania „zegar nie tyka”.
millis()
imicros()
nie zmieni się (no cóż,micros()
początkowo będzie, ale kiedy minie ten magiczny milisekundowy punkt, w którym wymagany jest milisekundowy haczyk, wszystko się rozpada).Możesz więc z pewnością zadzwonić
millis()
lubmicros()
sprawdzić aktualny czas w swoim ISR, ale nie oczekuj, że ten czas się zmieni.To właśnie ten brak zmiany w czasie jest ostrzegany w cytowanym przez ciebie cytacie.
delay()
polega namillis()
zmianie, aby wiedzieć, ile czasu minęło. Ponieważ to się nie zmienia,delay()
nigdy się nie skończy.Zasadniczo
millis()
imicros()
poda czas, w którym został wywołany Twój ISR, bez względu na to, kiedy go używasz.źródło
micros()
aktualizacje. Zawsze odczytuje rejestr timera sprzętowego.Cytowane wyrażenie nie jest ostrzeżeniem, jest jedynie stwierdzeniem o tym, jak rzeczy działają.
Nie ma nic z natury niewłaściwego w użyciu
millis()
lubmicros()
w ramach prawidłowo napisanej procedury przerwania.Z drugiej strony, robienie czegokolwiek w niewłaściwie napisanej procedurze przerwań jest z definicji błędne.
Procedura przerwania, której wykonanie zajmuje więcej niż kilka mikrosekund, jest prawdopodobnie napisana nieprawidłowo.
W skrócie: Prawidłowo napisana procedura przerwania nie spowoduje ani nie napotka problemów z
millis()
lubmicros()
.Edycja: Jeśli chodzi o „dlaczego micros ()„ zaczyna zachowywać się nieprawidłowo ””, jak wyjaśniono na stronie internetowej „ badanie funkcji mikr Arduino ”,
micros()
kod na zwykłym Uno jest funkcjonalnie równoważny zZwraca to czterobajtowy niepodpisany długi składający się z trzech najniższych bajtów z
timer0_overflow_count
i jednego bajtu z rejestru licznika timera-0.Jest
timer0_overflow_count
on zwiększany mniej więcej raz na milisekundę przez modułTIMER0_OVF_vect
obsługi przerwań, jak wyjaśniono w badaniu strony internetowej funkcji arduino millis .Przed rozpoczęciem obsługi przerwań sprzęt AVR wyłącza przerwania. Jeśli (na przykład) moduł obsługi przerwań miałby działać przez pięć milisekund z przerwaniami wciąż wyłączonymi, co najmniej cztery przepełnienia timera 0 zostałyby pominięte. [Przerwania napisane w kodzie C w systemie Arduino nie są ponownie wysyłane (zdolne do prawidłowej obsługi wielu nakładających się wykonań w ramach tego samego modułu obsługi), ale można napisać moduł obsługi języka asemblera ponownego wprowadzania, który ponownie włącza przerwania, zanim rozpocznie się czasochłonny proces.]
Innymi słowy, przepełnienia timera nie „gromadzą się”; za każdym razem, gdy nastąpi przepełnienie, zanim zostanie obsłużone przerwanie z poprzedniego przepełnienia,
millis()
licznik traci milisekundę, a rozbieżnośćtimer0_overflow_count
z kolei równieżmicros()
znika przez milisekundę.Jeśli chodzi o „krótszy niż 500 μs” jako górny limit czasowy przetwarzania przerwań, „aby zapobiec zbyt długiemu blokowaniu przerwania timera”, możesz wzrosnąć do nieco poniżej 1024 μs (np. 1020 μs) i
millis()
nadal działałby, większość czas. Uważam jednak, że program obsługi przerwań, który przyjmuje więcej niż 5 μs jako opieszałość, więcej niż 10 μs jako lenistwo, ponad 20 μs jak ślimak.źródło
micros()
„zacząć zachowywać się nieregularnie”? A co rozumiesz przez „poprawnie napisaną procedurę przerwania”? Zakładam, że oznacza to „krótszy niż 500us” (aby zapobiec zbyt długiemu blokowaniu przerwania timera), „używanie zmiennych komunikacyjnych do komunikacji” i „niewywoływanie kodu biblioteki” w jak największym stopniu, czy jest coś jeszcze?