Saya mencoba menggigit data DMX dan itu membutuhkan pulsa 4us. Tidak memiliki banyak keberuntungan dengan hasil yang saya periksa untuk melihat seberapa baik Arduino menunda ... Tampaknya sangat mengerikan.
Inilah sedikit tes cepat yang saya lakukan:
unsigned long ptime;
void setup() {
Serial.begin(9600);
}
void loop() {
ptime = micros();
delayMicroseconds(4);
Serial.println(micros() - ptime);
delay(1000); //just to keep the serial monitor from going nuts
}
Dan hasilnya:
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
Saya agak terkejut betapa buruk akurasinya. Ini dua kali lipat waktu yang saya ingin tunda, tetapi itu bahkan tidak konsisten di mana saya bisa membagi dengan 2!
Adakah yang bisa saya lakukan untuk mendapatkan hasil yang benar dan konsisten?
arduino-mega
timers
time
bwoogie
sumber
sumber
Jawaban:
Seperti yang dijelaskan dalam jawaban sebelumnya, masalah Anda yang sebenarnya bukan keakuratannya
delayMicroseconds()
, melainkan resolusimicros()
.Namun, untuk menjawab Anda yang sebenarnya pertanyaan , ada alternatif yang lebih akurat untuk
delayMicroseconds()
: fungsi_delay_us()
dari AVR-libc adalah siklus-akurat dan, misalnyamelakukan persis apa yang dikatakannya. Peringatan utama adalah bahwa argumen harus berupa konstanta waktu kompilasi. Anda harus
#include <util/delay.h>
memiliki akses ke fungsi ini.Perhatikan juga bahwa Anda harus memblokir interupsi jika Anda ingin jenis penundaan yang akurat.
Sunting : Sebagai contoh, jika saya menghasilkan pulsa 4 μs pada PD2 (pin 19 pada Mega), saya akan melanjutkan sebagai berikut. Pertama, perhatikan kode berikut ini
membuat pulsa panjang 0,125 μs (2 siklus CPU), karena itulah waktu yang diperlukan untuk menjalankan instruksi yang mengatur port
LOW
. Kemudian, tambahkan saja waktu yang hilang dalam penundaan:dan Anda memiliki lebar pulsa yang akurat-siklus. Perlu dicatat bahwa ini tidak dapat dicapai dengan
digitalWrite()
, karena panggilan ke fungsi ini membutuhkan waktu sekitar 5 μs.sumber
Hasil tes Anda menyesatkan.
delayMicroseconds()
sebenarnya keterlambatan cukup tepat (untuk keterlambatan lebih dari 2 atau 3 mikrodetik). Anda dapat memeriksa kode sumbernya di file /usr/share/arduino/hardware/arduino/cores/arduino/wiring.c (di sistem Linux; atau di jalur serupa di sistem lain).Namun, resolusi
micros()
adalah empat mikrodetik. (Lihat, misalnya, halaman garretlab tentangmicros()
.) Oleh karena itu, Anda tidak akan pernah melihat pembacaan antara 4 mikrodetik dan 8 mikrodetik. Penundaan yang sebenarnya mungkin hanya beberapa siklus selama 4 mikrodetik, tetapi kode Anda akan melaporkannya sebagai 8.Coba lakukan 10 atau 20
delayMicroseconds(4);
panggilan secara berturut-turut (dengan menduplikasi kode, bukan dengan menggunakan loop) lalu laporkan hasilnyamicros()
.sumber
digitalWrite()
, yang membutuhkan beberapa mikrodetik untuk dijalankan.delayMicroseconds()
? Saya tidak melihat itu lebih baik daripada membulatkan tekad. ¶ Mengenai sumber ketidaktepatan, jika rutin mendapat inline maka waktunya tergantung pada kode di sekitarnya. Anda dapat membaca daftar perakitan atau pembongkaran untuk melihat. (Lihat bagian “Membuat daftar perakitan” dalam jawaban saya untuk pertanyaan Setara untuk PORTB di Arduino Mega 2560 , yang mungkin juga relevan dengan proyek bitbanging Andamicros()
memiliki resolusi yang terdokumentasi dengan baik dari 4 μs.Anda dapat meningkatkan resolusi dengan mengubah prescaler untuk Timer 0 (tentu saja yang membuang angka-angka, tetapi Anda dapat mengimbanginya).
Atau gunakan Timer 1 atau Timer 2 dengan prescaler 1, yang memberi Anda resolusi 62,5 ns.
Lagipula itu akan lambat.
Output Anda persis konsisten dengan resolusi 4 μs
micros()
ditambah dengan fakta bahwa kadang-kadang Anda akan mendapatkan dua "kutu" dan kadang-kadang satu, tergantung pada kapan Anda memulai pengaturan waktu.Kode Anda adalah contoh kesalahan pengukuran yang menarik.
delayMicroseconds(4);
akan menunda hampir 4 μs. Namun upaya Anda untuk mengukurnya salah.Juga, jika terjadi interupsi maka itu akan meregangkan interval sedikit. Anda perlu mematikan interupsi jika Anda ingin penundaan yang tepat.
sumber
Ketika diukur dengan osiloskop, saya menemukan bahwa:
delayMicroseconds(0)
=delayMicroseconds(1)
= 4 μs keterlambatan nyata.Jadi, jika Anda menginginkan penundaan 35 μs, Anda perlu:
sumber
Implementasi Arduino cukup umum sehingga mungkin tidak seefektif pada beberapa aplikasi. Ada beberapa cara untuk penundaan pendek, masing-masing dengan kekurangannya sendiri.
Gunakan nop. Masing-masing adalah satu instruksi jadi ke-16 kita.
Gunakan tcnt0 secara langsung. Masing-masing adalah 4us saat prescaler diatur ke 64. Anda dapat mengubah prescaker untuk mencapai resolusi ke-16 kita.
Gunakan tanda centang, Anda dapat menerapkan klon systick dan menggunakannya sebagai dasar penundaan. Ini menawarkan resolusi yang lebih baik plus akurasi.
edit:
Saya menggunakan blok berikut untuk mengatur waktu berbagai pendekatan:
sebelum itu, saya telah mengatur ulang timer0 prescaler ke 1: 1 sehingga setiap centang TCNT0 adalah 1/16 mikrodetik.
semoga membantu.
sumber
TCNT0
membutuhkan 1 siklus CPU.