Halo, Habr! Saya hadir untuk Anda terjemahan artikel "Timer interrupt" oleh E.
Kata Pengantar
Papan Arduino memungkinkan Anda untuk dengan cepat dan minimal menyelesaikan berbagai masalah. Tetapi di mana interval waktu sewenang-wenang diperlukan (polling sensor secara berkala, sinyal PWM presisi tinggi, pulsa berdurasi panjang) fungsi penundaan perpustakaan standar tidak nyaman. Selama durasi aksi mereka, sketsa ditangguhkan dan menjadi tidak mungkin untuk mengelolanya.
Dalam situasi serupa, lebih baik menggunakan penghitung waktu AVR bawaan. Bagaimana melakukan ini dan tidak tersesat di belantara teknis lembar data, kata sebuah artikel yang sukses , terjemahan yang dibawa ke perhatian Anda.

Artikel ini membahas timer AVR dan Arduino dan cara menggunakannya dalam proyek Arduino dan sirkuit pengguna.
Apa itu timer?
Seperti dalam kehidupan sehari-hari dalam mikrokontroler, timer adalah beberapa hal yang dapat memberi sinyal di masa depan, pada saat yang Anda atur. Ketika momen ini datang, mikrokontroler terputus, mengingatkannya untuk melakukan sesuatu, misalnya, untuk mengeksekusi sepotong kode tertentu.
Pengatur waktu, seperti interupsi eksternal, bekerja secara independen dari program utama. Alih-alih mengulang atau mengulangi panggilan tunda milis () , Anda dapat menetapkan timer untuk melakukan tugasnya sementara kode Anda melakukan hal-hal lain.
Jadi, misalkan ada perangkat yang perlu melakukan sesuatu, misalnya, berkedip LED setiap 5 detik. Jika Anda tidak menggunakan penghitung waktu, tetapi menulis kode normal, Anda perlu mengatur variabel pada saat LED dinyalakan dan terus-menerus memeriksa apakah saat peralihannya telah tiba. Dengan interupsi timer, Anda hanya perlu mengkonfigurasi interupsi, dan kemudian memulai timer. LED akan berkedip tepat waktu, terlepas dari tindakan program utama.
Bagaimana cara kerja timer?
Kerjanya dengan menambah variabel yang disebut register hitung . Register penghitungan dapat menghitung ke nilai tertentu, tergantung pada ukurannya. Timer menambah penghitungnya berulang-ulang sampai mencapai nilai maksimumnya, pada titik ini penghitung akan meluap dan mengatur ulang ke nol. Timer biasanya menetapkan bit bendera untuk memberi tahu Anda bahwa terjadi overflow.
Anda dapat memeriksa flag ini secara manual atau Anda dapat membuat pengatur waktu - menyebabkan interupsi secara otomatis ketika flag diatur. Seperti interupsi lainnya, Anda dapat menetapkan Interrupt Service Routine ( ISR ) untuk mengeksekusi kode yang ditentukan ketika timer meluap. ISR sendiri akan menghapus flag overflow, jadi menggunakan interupsi biasanya merupakan pilihan terbaik karena kesederhanaan dan kecepatannya.
Untuk meningkatkan nilai penghitung pada interval waktu yang tepat, timer harus terhubung ke sumber jam. Sumber jam menghasilkan sinyal yang terus berulang. Setiap kali timer mendeteksi sinyal ini, timer akan menambah nilai penghitungnya. Karena timer beroperasi pada sumber jam, satuan waktu terkecil yang dapat diukur adalah periode siklus. Jika Anda menghubungkan sinyal clock 1 MHz, maka resolusi timer (atau periode timer) adalah:
T = 1 / f (f adalah frekuensi clock)
T = 1/1 MHz = 1/10 ^ 6 Hz
T = (1 β 10 ^ -6) s
Dengan demikian, resolusi timer adalah sepersejuta detik. Meskipun Anda dapat menggunakan sumber clock eksternal untuk timer, dalam kebanyakan kasus sumber internal chip itu sendiri digunakan.
Jenis Timer
Di papan Arduino standar pada chip AVR 8-bit, ada beberapa timer sekaligus. Chip Atmega168 dan Atmega328 memiliki tiga timer Timer0, Timer1, dan Timer2. Mereka juga memiliki pengawas waktu yang dapat digunakan untuk melindungi dari kegagalan atau sebagai mekanisme reset perangkat lunak. Berikut adalah beberapa fitur dari setiap timer.
Timer0:
Timer0 adalah timer 8-bit, yang berarti register penghitungnya dapat menyimpan angka hingga 255 (mis., Byte yang tidak ditandatangani). Timer0 digunakan oleh fungsi sementara Arduino standar seperti delay () dan millis () , jadi sebaiknya jangan bingung jika Anda peduli dengan konsekuensinya.
Timer1:
Timer1 adalah timer 16-bit dengan nilai hitungan maksimum 65535 (integer unsigned). Penghitung waktu ini menggunakan pustaka Arduino Servo, ingatlah ini jika Anda menggunakannya di proyek Anda.
Timer2:
Timer2 adalah 8 bit dan sangat mirip dengan Timer0. Ini digunakan dalam fungsi nada Arduino () .
Timer3, Timer4, Timer5:
Chip ATmega1280 dan ATmega2560 (diinstal dalam varian Arduino Mega) memiliki tiga timer tambahan. Semuanya 16 bit dan bekerja mirip dengan Timer1.
Daftarkan Konfigurasi
Untuk menggunakan penghitung waktu ini, AVR memiliki register pengaturan. Pengatur waktu berisi banyak register semacam itu. Dua di antaranya - register kontrol penghitung waktu / penghitung berisi variabel pengaturan dan disebut TCCRxA dan TCCRxB, di mana x adalah jumlah penghitung waktu (TCCR1A dan TCCR1B, dll.). Setiap register berisi 8 bit dan setiap bit menyimpan variabel konfigurasi. Berikut ini rincian dari lembar data Atmega328:
Yang paling penting adalah tiga bit terakhir di TCCR1B: CS12, CS11 dan CS10. Mereka menentukan frekuensi jam dari timer. Memilih mereka dalam kombinasi yang berbeda, Anda dapat memesan timer untuk bertindak pada kecepatan yang berbeda. Berikut adalah tabel lembar data yang menjelaskan efek dari bit terpilih:
Secara default, semua bit ini diatur ke nol.
Misalkan Anda ingin Timer1 dijalankan pada frekuensi jam dengan satu sampel per periode. Saat meluap, Anda ingin memanggil rutin interupsi, yang mengalihkan LED yang terhubung ke leg 13 ke status hidup atau mati. Untuk contoh ini, kita akan menulis kode Arduino, tetapi kita akan menggunakan prosedur dan fungsi pustaka avr-libc setiap kali ini tidak membuat hal-hal terlalu rumit. Pendukung AVR murni dapat mengadaptasi kode sesuai keinginan mereka.
Pertama, inisialisasi timer:
Register TIMSK1 adalah register masker interupsi timer / counter1. Ini mengontrol interupsi yang dapat disebabkan oleh timer. Mengatur bit TOIE1 memberitahu timer untuk menyela ketika timer meluap. Lebih lanjut tentang ini nanti.
Ketika Anda mengatur bit CS10, timer mulai menghitung dan, segera setelah terjadi overflow interupsi, ISR (TIMER1_OVF_vect) dipanggil. Ini selalu terjadi ketika timer meluap.
Selanjutnya kita mendefinisikan fungsi interupsi ISR:
ISR(TIMER1_OVF_vect) { digitalWrite(LEDPIN, !digitalRead(LEDPIN)); }
Sekarang kita dapat mendefinisikan siklus () siklus dan mengganti LED terlepas dari apa yang terjadi di program utama. Untuk mematikan timer, atur TCCR1B = 0 kapan saja.
Seberapa sering LED berkedip?
Timer1 diatur ke overflow interrupt dan mari kita asumsikan bahwa Anda menggunakan Atmega328 dengan frekuensi clock 16 MHz. Karena timer adalah 16-bit, itu dapat dihitung dengan nilai maksimum (2 ^ 16 - 1), atau 65535. Pada 16 MHz, siklus berjalan 1 / (16 β 10 ^ 6) detik atau 6,25e-8 s. Ini berarti bahwa 65535 sampel akan muncul dalam (65535 β 6.25e-8 detik) dan ISR akan dipanggil setelah sekitar 0,0041 detik. Maka dari waktu ke waktu, setiap empat ribu detik. Terlalu cepat untuk melihat kedipan.
Jika kita menerapkan sinyal PWM yang sangat cepat dengan cakupan 50% ke LED, maka cahaya akan tampak terus menerus, tetapi kurang terang dari biasanya. Eksperimen semacam itu menunjukkan kekuatan mikrokontroler yang luar biasa - bahkan chip 8-bit yang murah dapat memproses informasi lebih cepat daripada yang dapat kita deteksi.
Pembatas waktu dan mode CTC
Untuk mengontrol periode, Anda dapat menggunakan pembagi yang memungkinkan Anda untuk membagi sinyal jam menjadi dua tingkat yang berbeda dan menambah periode timer. Misalnya, Anda ingin LED berkedip pada interval satu detik. Ada tiga bit CS dalam register TCCR1B yang mengatur resolusi yang paling tepat. Jika Anda mengatur bit CS10 dan CS12 menggunakan:
TCCR1B |= (1 << CS10); TCCR1B |= (1 << CS12);
maka frekuensi sumber jam akan dibagi dengan 1024. Ini memberikan resolusi timer 1 / (16 β 10 ^ 6/1024) atau 6.4e-5 s. Sekarang timer akan meluap setiap (65535 β 6.4e-5s) atau untuk 4.194s. Itu terlalu panjang.
Tetapi ada mode timer AVR lain. Ini disebut reset timer coincident atau CTC. Alih-alih menghitung untuk meluap, timer membandingkan penghitungnya dengan variabel yang sebelumnya disimpan dalam register. Ketika hitungan cocok dengan variabel ini, penghitung waktu dapat menetapkan bendera atau menyebabkan interupsi, seperti dalam kasus overflow.
Untuk menggunakan mode CTC, Anda perlu memahami berapa banyak siklus yang Anda butuhkan untuk mendapatkan interval satu detik. Misalkan rasio divisi masih 1024.
Perhitungannya adalah sebagai berikut:
(target time) = (timer resolution) * (# timer counts + 1) (# timer counts + 1) = (target time) / (timer resolution) (# timer counts + 1) = (1 s) / (6.4e-5 s) (# timer counts + 1) = 15625 (# timer counts) = 15625 - 1 = 15624
Anda harus menambahkan unit tambahan ke jumlah sampel karena dalam mode CTC, ketika penghitung cocok dengan nilai yang ditetapkan, itu akan mengatur ulang sendiri ke nol. Reset membutuhkan satu periode jam, yang harus diperhitungkan dalam perhitungan. Dalam banyak kasus, kesalahan dalam satu periode tidak terlalu signifikan, tetapi dalam tugas-tugas presisi tinggi itu bisa menjadi kritis.
Fungsi setup () akan seperti ini:
void setup() { pinMode(LEDPIN, OUTPUT);
Anda juga perlu mengganti interupsi melimpah dengan interupsi kebetulan:
ISR(TIMER1_COMPA_vect) { digitalWrite(LEDPIN, !digitalRead(LEDPIN)); }
Sekarang LED akan hidup dan mati selama tepat satu detik. Dan Anda bisa melakukan apa saja di loop () loop. Sampai Anda mengubah pengaturan timer, program tidak ada hubungannya dengan interupsi. Anda tidak memiliki batasan dalam menggunakan timer dengan berbagai mode dan pengaturan pembagi.
Berikut ini adalah contoh awal lengkap yang dapat Anda gunakan sebagai dasar untuk proyek Anda sendiri:
Ingatlah bahwa Anda dapat menggunakan fungsi ISR ββbawaan untuk memperluas fungsi timer. Misalnya, Anda perlu menyurvei sensor setiap 10 detik. Tetapi tidak ada pengaturan pengatur waktu yang menyediakan penghitungan lama tanpa meluap. Namun, Anda dapat menggunakan ISR untuk menambah variabel penghitungan satu kali per detik dan kemudian menyurvei sensor ketika variabel mencapai 10. Menggunakan mode STS dari contoh sebelumnya, interruptnya bisa seperti ini:
ISR(TIMER1_COMPA_vect) { seconds++; if(seconds == 10) { seconds = 0; readSensor(); } }
Karena variabel akan dimodifikasi di dalam ISR, itu harus dinyatakan sebagai volatile . Karenanya, saat menjelaskan variabel di awal program, Anda perlu menulis:
volatile byte seconds;
Kata penutup penerjemah
Pada suatu waktu, artikel ini menyelamatkan saya banyak waktu ketika mengembangkan generator pengukur prototipe. Saya berharap ini akan bermanfaat bagi pembaca lain.