Hur fungerar funktionen delayMicroseconds (). Från vad jag förstod är prescaler för timer0 inställd på 64. För en 16MHz klocka ger 4.0uS per räkning. Jag är lite förvirrad i matematiken för att komma till 1uS-intervallet?
Kommentarer
Svar
Källkoden för den här funktionen är ganska väldokumenterad och finns i / usr / share / arduino / hardware /arduino/cores/arduino/wiring.c på Linux-system. Windows-system har en liknande sökväg till filen wiring.c. Anstränga dig för att hitta filen och bläddra igenom den. För tillfället fokuserar du bara på den här enskilda funktionen, den litar inte på några andra funktioner.
Genom att inspektera koden kommer du att märka att det inte handlar om timers, det handlar om instruktionscykler. Koden är starkt beroende av att kompilatoroptimering är exakt densamma för dig som för utvecklaren av biblioteket. Det antar författaren! Antalet CPU-cykler som ”bränns” av varje instruktion är väl dokumenterad i Atmel AVR instruktionsuppsättningsdokument .
Först är fördröjningsvärdet kontrolleras för att vara lika med 1, i det fallet bara att återvända från rutinen som redan spenderats under en mikrosekund av CPU-tid.
Därefter multipliceras fördröjningsvärdet med fyra (<<=2
). __asm__
-slingan kompileras till en 4 CPU-cykelslinga. 4 cykler × 4 = 16 cykler. 16MHz / (4 × 4) = 1MHz, vilket tar 1 us cykeltid, den upplösning som vi är ute efter.
De sista -2 mikrosekunderna (innan slingan startas) är återigen en korrigering på kompilatorn infört overhead. Att ringa __asm__
-kod från C kräver några extra instruktioner för att spara CPU-register.
För en vanlig Arduino @ 16MHz kommer endast följande kod att sammanställas:
/* 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 ); }
BTW: Den sammanställda koden är ganska korrekt, men tänk på följande: På Arduino finns tidsinställda avbrott konfigurerade som de flesta inte är medvetna om. När ett avbrott tas emot under körningen av delayMicroseconds()
blir tidpunkten för delayMicroseconds()
fel. Du kan naturligtvis stoppa avbrott innan du ringer till delayMicroseconds()
och aktivera dem efteråt, men det påverkar återigen tidsnoggrannheten med den sammanställda kodens varaktighet för att aktivera / inaktivera. Kommentarer
- Eller om du inte har Arduino IDE installerad finns den här filen på github.com/arduino / Arduino / blob / master / hardware / arduino / cores / …
- Det ' s flyttade hit: github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/…
micros()
säger " På 16 MHz Arduino-kort (t.ex. Duemilanove och Nano) har denna funktion en upplösning på fyra mikrosekunder (dvs. det returnerade värdet är alltid en multipel av fyra). "