題:
我可以使delayMicroseconds更準確嗎?
bwoogie
2017-04-22 07:17:59 UTC
view on stackexchange narkive permalink

我試圖對DMX數據進行位衝擊,這需要4us脈衝。我正在檢查結果是否運氣不佳,以查看Arduino在延遲方面的表現如何……似乎很糟糕。

  unsigned long ptime; void setup(){Serial.begin(9600);} void loop(){ptime = micros(); delayMicroseconds(4); Serial.println(micros()-ptime);延遲(1000); //只是為了防止串行監視器發瘋}  

結果如下:

8 4 8 4 4 4 4 4 8 8 8 8 4 8 8 4 4 8 4 4 4 8 8 8 4 8 4

我對它的精確度感到震驚。這是我想延遲的時間的兩倍,但甚至與我只能除以2的位置也不相符!

我可以做些什麼來獲得正確,一致的結果嗎?

為什麼為什麼要位衝擊而不是直接訪問UART?
所以我可以有多個輸出。
Mega具有四個UART。即使您永久持有一個用於編程的程序,仍然可以得到三個Universe。
我正在測試大型設備,因為這是我目前可用的設備,最終項目將具有ATMEGA328
ATmega328PB具有兩個UART。
此外,比一個AVR還要麻煩的是對多個串行端口(特別是250kbps)進行比特衝擊。
[`micros()`](https://www.arduino.cc/zh/reference/micros)的分辨率為4微秒。
這裡的問題是如何衡量。
五 答案:
Edgar Bonet
2017-04-22 12:14:57 UTC
view on stackexchange narkive permalink

如前面的答案所述,您的實際問題不是 delayMicroseconds()的準確性,而是 micros()的分辨率。

但是,要回答您的實際問題,還有 delayMicroseconds()的更準確的替代:函數 _delay_us() AVR-libc中的code>是周期精確的,例如

  _delay_us(1.125);  

完全按照說的去做。主要警告是該參數必須是編譯時常量。您必須 #include <util / delay.h> 才能訪問此功能。

還請注意,如果需要任何類型的準確延遲,則必須阻止中斷。

編輯:例如,如果要在PD2(Mega上的引腳19)上產生一個4 µs脈衝,我將按以下步驟進行操作。首先,請注意以下代碼

  noInterrupts(); PORTD | = _BV(PD2); // PD2 HIGHPORTD & =〜_BV(PD2); // PD2 LOWinterrupts();  

發出一個0.125 µs的長脈衝(2個CPU週期),因為這是執行設置端口 LOW code的指令所花費的時間>。然後,只需延遲增加丟失的時間即可:

  noInterrupts(); PORTD | = _BV(PD2); // PD2 HIGH_delay_us(3.875); PORTD & =〜_BV(PD2); // PD2 LOWinterrupts();  

,您將獲得精確到週期的脈衝寬度。值得注意的是, digitalWrite()無法實現此功能,因為對該函數的調用大約需要5 µs。

James Waldby - jwpat7
2017-04-22 07:38:34 UTC
view on stackexchange narkive permalink

您的測試結果具有誤導性。實際上, delayMicroseconds()的延遲相當精確(延遲超過2或3微秒)。您可以在文件/usr/share/arduino/hardware/arduino/cores/arduino/wiring.c中檢查其源代碼(在Linux系統上;或在其他系統上的類似路徑上)。

但是, micros()的分辨率為4微秒。 (例如,參見有關 micros()的garretlab頁面。)因此,您將永遠不會看到在 4微秒和8微秒之間的讀數。實際的延遲可能只是4微秒內的幾個週期,但您的代碼會將其報告為8。

嘗試連續執行10或20個 delayMicroseconds(4); (通過複製代碼,而不是使用循環),然後報告 micros()的結果。

10個延遲為4時,我得到的是32和28的混合...但是4 * 10 = 40。
例如,10的5延遲會得到什麼? :)還請注意,為了進行比特猛擊,您可能需要使用相當直接的端口訪問,即不是`digitalWrite()`,這需要花費幾微秒的時間才能執行。
40&44 ...但不應該舍入嗎?我在這裡想念什麼?
“四捨五入”是指`delayMicroseconds()`嗎?我認為這比四捨五入更好。 ¶關於錯誤的根源,如果例程被內聯,則時間取決於周圍的代碼。您可以閱讀彙編或反彙編清單進行查看。 (請參閱[我的回答](https://arduino.stackexchange.com/a/33042/3917)中的“製作裝配體列表”部分)[與Arduino Mega 2560中的PORTB等效](https://arduino.stackexchange。 com / q / 33033),但這可能與您的bitbanging項目有關
Nick Gammon
2017-04-22 11:19:05 UTC
view on stackexchange narkive permalink

我正在檢查Arduino在延遲方面的表現如何……似乎很糟糕。

micros()記錄明確的分辨率為4 µs。

您可以通過更改計時器0的預分頻器來提高分辨率(當然會丟掉數字,但是您可以補償

或者使用定時器1或定時器2的預分頻器為1,這將為您提供62.5 ns的分辨率。


 串行.begin(9600);  

反正這會很慢。


8 4 8 4 4 4 4 4 8 8 8 8 4 8 8 4 4 8 4 4 4 8 8 8 4 8 4

您的輸出與 micros()的4 µs分辨率完全一致再加上有時您會得到兩個“滴答聲”,有時是一個滴答聲,具體取決於您何時開始計時。


您的代碼是一個有趣的測量誤差示例。 delayMicroseconds(4); 將延遲接近4 µs。但是,您嘗試測量它的方法是錯誤的。

此外,如果發生中斷,則中斷間隔會稍微延長。如果要精確延遲,則需要關閉中斷。

sigi
2017-07-01 05:20:40 UTC
view on stackexchange narkive permalink

用示波器測量時,我發現:

delayMicroseconds(0) = delayMicroseconds(1) =4μs實際延遲。

因此,如果要延遲35微秒,則需要:

  delayMicroseconds(31);  
dannyf
2017-07-01 15:22:50 UTC
view on stackexchange narkive permalink

我能做些什麼來獲得正確,一致的結果嗎?

Arduino的實現是非常通用的,因此在某些應用程序中可能效果不佳。短暫延遲有幾種方法,每種都有其不足之處。

  1. 使用nop。每個指令只有一個指令,大約等於我們的16分。

  2. 直接使用tcnt0。每個都是4us,因為預分頻器設置為64。您可以更改prescaker以獲得16th us分辨率。

  3. 使用刻度,可以實現systick的克隆並將其用作延遲的依據。它提供了更高的分辨率和準確性。

  4. ol>

    編輯:

    我使用以下代碼塊來計時各種方法的時間:

      time0 = TCNT0; delay4us(); // 65 // t0delayus(4 * 16); // 77 // _ delay_us(4); // 65 // delayMicroseconds(4); // 45time1 = TCNT0-time0; // //預期為64位 

    ,在此之前,我已將timer0預分頻器重置為1:1,因此每個TCNT0滴答都是1微秒的16分之一。

    delay4us()由NOP()建立;它產生了65個滴答聲的延遲,或剛好超過4us;
  5. t0delayus()是由timer0延遲建立的。它產生了77個滴答聲的延遲;比delay4us()
  6. _delay_us()稍差的是gcc-avr函數。性能與delay4us()相當;
  7. delayMicroseconds()產生了45個滴答聲。除非在具有其他中斷的環境中使用,否則arduino實現其計時功能的方式往往會計數不足。
  8. ol>

    希望它會有所幫助。

請注意,預期結果是65個滴答聲,而不是64個滴答聲,因為讀取`TCNT0`需要1個CPU週期。


該問答將自動從英語翻譯而來。原始內容可在stackexchange上找到,我們感謝它分發的cc by-sa 3.0許可。
Loading...