Jak działa funkcja delayMicroseconds (). Z tego, co zrozumiałem, prescaler timera0 jest ustawiony na 64. Dla zegara 16MHz daje 4,0uS na liczbę. Jestem trochę zdezorientowany w matematyce, aby dostać się do interwału 1uS?
Komentarze
Odpowiedź
Kod źródłowy tej funkcji jest dość dobrze udokumentowany i można go znaleźć w / usr / share / arduino / hardware /arduino/cores/arduino/wiring.c w systemach Linux. Systemy Windows będą miały podobną ścieżkę do pliku wire.c. Spróbuj znaleźć plik i przejrzeć go. Na razie skup się tylko na tej pojedynczej funkcji, nie polega ona na żadnych innych funkcjach.
Po sprawdzeniu kodu zauważysz, że nie chodzi o timery, tylko o cykle instrukcji. Kod w dużej mierze opiera się na optymalizacji kompilatora, która jest dokładnie taka sama dla Ciebie, jak dla programisty biblioteki. To założenie autora! Liczba cykli procesora „spalonych” przez każdą instrukcję jest dobrze udokumentowana w dokumencie zestawu instrukcji Atmel AVR .
Najpierw wartość opóźnienia to sprawdzane, czy jest równe 1, w tym przypadku po prostu wracając z procedury już spędzonej przez mikrosekundę czasu procesora.
Następnie wartość opóźnienia jest mnożona przez cztery (<<=2
). __asm__
-loop kompiluje się w pętlę 4 cykli procesora. 4 cykle × 4 = 16 cykli. 16 MHz / (4 × 4) = 1 MHz, co zajmuje 1 us czasu cyklu, rozdzielczość, której oczekujemy.
Ostatnie -2 mikrosekundy (przed rozpoczęciem pętli) to znowu korekta kompilatora wprowadzony narzut. Wywołanie __asm__
-kodu z C wymaga dodatkowych instrukcji, aby zapisać rejestry procesora.
Dla normalnego Arduino @ 16 MHz zostanie skompilowany tylko następujący kod:
/* Delay for the given number of microseconds. Assumes a 8 or 16 MHz clock. */ void delayMicroseconds(unsigned int us) { // calling avrlib"s delay_us() function with low values (e.g. 1 or // 2 microseconds) gives delays longer than desired. //delay_us(us); // for the 16 MHz clock on most Arduino boards // for a one-microsecond delay, simply return. the overhead // of the function call yields a delay of approximately 1 1/8 us. if (--us == 0) return; // the following loop takes a quarter of a microsecond (4 cycles) // per iteration, so execute it four times for each microsecond of // delay requested. us <<= 2; // account for the time taken in the preceeding commands. us -= 2; // busy wait __asm__ __volatile__ ( "1: sbiw %0,1" "\n\t" // 2 cycles "brne 1b" : "=w" (us) : "0" (us) // 2 cycles ); }
Przy okazji: skompilowany kod jest dość dokładny, ale należy pamiętać o następujących kwestiach: W Arduino są skonfigurowane przerwania czasowe, których większość nie jest świadoma. Kiedy podczas wykonywania operacji delayMicroseconds()
zostanie odebrane przerwanie, czas delayMicroseconds()
będzie nieprawidłowy. Możesz oczywiście zatrzymać przerwania przed wywołaniem delayMicroseconds()
i włączyć je później, ale to znowu wpływa na dokładność taktowania przez czas trwania skompilowanego kodu do włączania / wyłączania.
Komentarze
- Jeśli nie masz zainstalowanego środowiska Arduino IDE, ten plik jest dostępny pod adresem github.com/arduino / Arduino / blob / master / hardware / arduino / cores / …
- It ' s przeniesiono tutaj: github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/…
micros()
mówi " Na płytach Arduino 16 MHz (np. Duemilanove i Nano) ta funkcja ma rozdzielczość czterech mikrosekund (tj. zwracana wartość jest zawsze wielokrotnością czterech). "