Peningkatan Membaca Encoder Rotary Arduino
Rotary encoders adalah perangkat input yang hebat untuk proyek elektronik - mudah-mudahan Instructable ini akan menginspirasi dan membantu Anda menggunakannya dalam proyek Anda berikutnya.
Mengapa menulis kode rotary encoder?
Saya ingin menggunakan rotary encoder berbiaya rendah sebagai mekanisme input untuk salah satu proyek saya yang akan datang dan pada awalnya bingung dengan pilihan kode yang tersedia untuk mengambil bacaan dari rotary encoder dan menentukan berapa banyak "deten" atau siklus yang telah diklik oleh encoder lalu dan ke arah mana. Saya pikir sketsa utama saya perlu menggunakan sebagian besar memori Arduino saya sehingga saya menghindari berbagai perpustakaan encoder yang tersedia, yang tampaknya sulit untuk dibuat bekerja ketika saya mencoba beberapa dari mereka. Mereka juga tampaknya menggunakan jauh lebih banyak anggaran kode daripada pendekatan kode berbasis sketsa yang dibahas sejak saat ini.
Jika Anda hanya ingin melewati pemikiran di balik pendekatan saya dan langsung masuk ke Instructable, jangan ragu untuk langsung beralih ke Langkah 1!
Pendekatan Lain
Beberapa pendekatan berbasis sketsa utama (yaitu mereka tidak menggunakan perpustakaan) dibahas dalam posting blog rt di mana mereka menulis kode rotary encoder yang membuat encoders termurah dapat digunakan sebagai input Arduino. Mereka juga memiliki contoh yang baik dari sinyal logika mereka yang dihasilkan oleh encoder. rt menemukan bahwa sistem interupsi timer berfungsi paling baik untuk mereka, tetapi saya khawatir frekuensi polling akan mengurangi kecepatan pembaruan layar di loop utama sketsa proyek saya. Mengingat bahwa rotary encoder akan bergerak untuk sebagian kecil dari waktu saya ingin layar diperbarui, ini tampaknya cocok untuk aplikasi saya.
Saya memilih untuk mulai menggunakan kode Steve Spence di sini, yang baik-baik saja tetapi tampaknya benar-benar melambat ketika saya memasukkan sisa kode sketsa saya (yang melibatkan penulisan pembaruan tampilan ke layar TFT kecil). Awalnya saya bertanya-tanya apakah itu bisa karena loop utama berisi pernyataan tolak.
Saya kemudian membaca artikel rotary encoder Oleg pada versi rutin layanan interupsi dari posting sebelumnya, saya juga berpikir itu mungkin ide yang baik untuk menggunakan manipulasi port langsung untuk membaca kedua pin secara bersamaan dan segera setelah interupsi kebakaran. Kode-nya dapat digunakan pada pin input apa pun jika kode manipulasi port ditulis ulang. Sebaliknya, saya memutuskan untuk hanya menggunakan interupsi perangkat keras pada pin digital 2 dan 3, sehingga kita dapat mengatur interupsi untuk hanya menyalakan tepi tegangan pin yang meningkat, bukan pada perubahan voltase pin, yang mencakup tepi jatuh. Ini mengurangi berapa kali ISR dipanggil, mengganggu dari loop utama.
Kode Oleg menggunakan tabel pencarian untuk mengurangi ukuran kode yang dikompilasi menjadi ukuran yang sangat kecil tapi saya tidak bisa mendapatkan hasil yang dapat diandalkan yang akan menangkap rotasi yang sangat lambat serta rotasi yang cukup cepat. Ingatlah bahwa pembongkaran perangkat keras (lihat Langkah 2) dapat banyak membantu dengan keandalan pembacaan, tetapi saya mencari solusi perangkat lunak untuk menyederhanakan pembuatan perangkat keras dan menjadi portabel untuk aplikasi perangkat keras lainnya.
Ini menyimpulkan pengenalan tantangan dan pertimbangan saya. Pada Langkah 2 kita akan melihat perangkat keras encoder, terminologi dan beberapa pertimbangan praktis ketika Anda ingin mengintegrasikan rotary encoder ke proyek Anda.
Langkah 1: Sedikit Tentang Rotary Encoder


Mengapa rotary encoders sangat keren?
- Tidak seperti resistor variabel / potensiometer, mereka memiliki perjalanan tak terbatas ke segala arah dan karena mereka menghasilkan "kode Gray" digital, Anda dapat mengatur skala bacaan mereka ke rentang apa pun yang Anda suka.
- Arah ganda membuatnya berguna untuk menambah atau mengurangi nilai dalam suatu variabel atau menavigasi menu.
- Akhirnya, banyak dari enkoder putar ini datang dengan tombol tekan tengah, yang dapat digunakan untuk memilih item menu, mengatur ulang penghitung atau melakukan apa pun yang dapat Anda pikirkan yang mungkin sesuai dengan tombol tekan sesaat.
Ketentuan
- PPR: pulsa per putaran - biasanya 12, 20 atau 24. Anda mungkin juga melihat spesifikasi untuk rotasi maksimum dalam rpm, dll. Ini mungkin ditentukan oleh kecenderungan pembuat enkode untuk "memantulkan" kontak - lihat di bawah ini.
- Detent: klik kecil dari aksi saat muncul ke titik istirahat alami di antara pulsa. Mungkin ada satu detent per pulsa / siklus (tidak sama dengan rotasi poros) atau dua.
- Bouncing: kontak mekanis di dalam enkoder benar-benar terpental untuk melompat dan kembali pada kontak saat berputar, berpotensi menyebabkan terlalu banyak pembacaan yang dikaitkan dengan fase perjalanan antar deten.
- Debounce: Ini bisa dilakukan dalam perangkat keras, mungkin dengan kapasitor keramik bernilai rendah antara masing-masing pin dan Ground, atau dalam perangkat lunak, mungkin dengan penundaan. Dalam kedua kasus tersebut, tujuannya adalah untuk menciptakan sistem yang mengabaikan kontak yang terpental.
Kiat
- Carilah bagian berulir di dekat pangkal poros dan mur yang cocok jika Anda ingin memasang encoder di panel atau selungkup.
- Banyak tombol tersedia untuk rotary encoders, dengan yang paling mudah tersedia dalam poros diameter 6mm.
- Perhatikan apakah poros enkoder Anda menggunakan permukaan datar atau splines untuk mencapai kesesuaian yang tepat dengan kenop.
- Tubuh rotary encoder juga dapat hadir dengan pin / stub yang dinaikkan, dimaksudkan untuk kawin dengan indentasi / lubang kecil di panel Anda (mungkin disembunyikan oleh kenop Anda) dan mencegah encoder Anda berputar ketika Anda memutar kenop. Anda mungkin ingin menghapus ini jika Anda dapat membuat gesekan yang cukup untuk mencegah rotasi badan pembuat enkode menggunakan baut pemasangan untuk mengencangkan pembuat enkode pada panel atau penutup.
- Pastikan Anda mengetahui di mana status detent untuk encoder Anda dan sesuaikan kode Anda. Contoh saya menggunakan encoder yang pin keduanya terputus dari ground dan ditarik tinggi oleh masing-masing resistor pullup input. Ini mendorong saya memilih interupsi RISING. Jika kedua pin terhubung ke ground ketika di detent, mereka akan membutuhkan kode yang mencari tegangan pin FALLING.
Langkah 2: Sirkuit

Rangkaiannya sangat sederhana. Anda akan perlu:
• Arduino berbasis ATMEGA328P, seperti Uno, Pro Mini atau Nano.
• Encoder putar kuadratur mekanis (berlawanan dengan optik) - ini adalah jenis yang paling umum jadi jangan khawatir terlalu banyak jika tidak ditentukan. Daftar eBay dan Aliexpress akan sering menyebut Arduino dalam deskripsi dan ini adalah indikator yang baik bahwa seseorang cocok.
• Kawat kait / jumper mengarah.
• Opsional: papan tempat memotong roti prototipe.
Pertama-tama, cari koleksi tiga pin di satu sisi encoder. Ini adalah tiga untuk mengukur rotasi dengan kode kita. Jika ada dua pin bersamaan di sisi lain, ini kemungkinan untuk tombol push tengah. Kami akan mengabaikan ini untuk saat ini.
Dari ketiga pin bersamaan, pin ground enkoder tersambung ke pin ground Arduino. Salah satu dari dua pin lainnya terhubung ke pin digital 2 dan sisanya pada terhubung ke pin digital 3. Jika arah rotasi Anda tidak seperti yang Anda inginkan, cukup tukar dua pin non-ground.
Pin 2 dan 3 penting karena pada Arduino berbasis ATMEGA328P mereka adalah satu-satunya pin yang memiliki kemampuan untuk mendeteksi interupsi pin RISING dan FALLING. Papan MEGA 2560 dll memiliki pin interupsi perangkat keras lain yang dapat melakukan ini.
Catatan: Dalam diagram pin ground adalah salah satu pin ujung. Pada kenyataannya, pin ground sering kali merupakan pin tengah tetapi ini tidak selalu demikian, jadi baca lembar data atau uji pembuat enkode Anda untuk mengetahui pin mana yang di-ground.
Catatan lain: ArneTR membuat komentar yang bagus tentang tidak memiliki koneksi kabel yang terpisah ke tegangan positif logika (misalnya 5V atau 3.3V) untuk rangkaian rotary encoder yang ditunjukkan. Arduino tidak dapat membaca rotary encoder tanpa kedua sinyal ground (yang kami hubungkan dengan kawat) dan tegangan logika (kadang-kadang dijelaskan sebagai Vcc atau Vdd), jadi bagaimana Arduino dapat membaca logika dari encoder ini tanpa positif kawat tegangan? Jawabannya adalah bahwa chip ATMEGA328P di Arduino memiliki mode khusus yang dapat Anda atur pada pin digital (yang kami gunakan) di mana pin secara otomatis ditarik "tinggi" ke tegangan logika oleh resistor internal. Lihat kode "pinMode (pinX, INPUT_PULLUP)" untuk melihat kami memberi tahu Arduino bahwa kami ingin memanfaatkan mode ini. Setelah diatur, kita hanya perlu menyediakan encoder dengan kabel ground karena kabel sensing dari pin digital sudah menyediakan tegangan logika.
SATU HAL LEBIH BANYAK ... Githyuk menemukan bahwa pembuat enkode bermerek tertentu tidak berfungsi dengan cara melakukan hal-hal ini (yaitu kode di bawah). Silakan lihat bagian komentar untuk detail tetapi secara umum, mencoba encoder yang berbeda akan menjadi langkah debugging yang baik ketika Anda telah kehabisan langkah yang lebih mudah / lebih cepat / lebih murah.
Langkah 3: Kode
Jika Anda tidak terbiasa dengan pemrograman Arduino, silakan dapatkan sendiri sumber daya ini dari Arduino.
Kode ini gratis untuk Anda gunakan (karena tanpa biaya dan harus dimodifikasi sesuka Anda), mohon cantumkan di mana Anda seharusnya.
/ ******* Sketsa Rotary Encoder Berbasis Interupsi *******
oleh Simon Merrett, berdasarkan wawasan dari Oleg Mazurov, Nick Gammon, rt, Steve Spence
* /
pin int statis = 2; // Pin interupsi perangkat keras pertama kami adalah pin digital 2
pin int statisB = 3; // Pin interupsi perangkat keras kedua kami adalah pin digital 3
byte volatile aFlag = 0; // beri tahu kami saat kami mengharapkan peningkatan pada pinA untuk memberi sinyal bahwa pembuat kode telah tiba di detent
byte volatile bFlag = 0; // beri tahu kami saat kami mengharapkan kenaikan pada pinB untuk memberi sinyal bahwa encoder telah tiba di detent (arah yang berlawanan dengan saat aFlag diatur)
encoderPo volatile byte = 0; // variabel ini menyimpan nilai posisi encoder kami saat ini. Ubah ke int atau uin16_t alih-alih byte jika Anda ingin merekam rentang yang lebih besar dari 0-255
byte volatile oldEncPos = 0; // menyimpan nilai posisi encoder terakhir sehingga kami dapat membandingkan dengan pembacaan saat ini dan melihat apakah telah berubah (jadi kami tahu kapan harus mencetak ke monitor serial)
pembacaan byte volatil = 0; // di suatu tempat untuk menyimpan nilai langsung yang kita baca dari pin interupsi sebelum memeriksa untuk melihat apakah kita telah memindahkan seluruh penahanan
pengaturan batal () {
pinMode (pinA, INPUT_PULLUP); // atur pinA sebagai input, tarik TINGGI ke tegangan logika (5V atau 3.3V untuk kebanyakan kasus)
pinMode (pinB, INPUT_PULLUP); // atur pinB sebagai input, tarik TINGGI ke tegangan logika (5V atau 3.3V untuk kebanyakan kasus)
attachInterrupt (0, PinA, RISING); // atur interupsi pada PinA, cari sinyal edge edge dan jalankan Rutin Layanan Interupsi "PinA" (di bawah)
attachInterrupt (1, PinB, RISING); // atur interupsi pada PinB, cari sinyal edge edge dan jalankan Rutin Layanan Interupsi "PinB" (di bawah)
Serial.begin (115200); // mulai tautan monitor serial
}
membatalkan PinA () {
cli (); // hentikan interupsi yang terjadi sebelum kita membaca nilai pin
membaca = PIND & 0xC; // baca semua delapan nilai pin lalu hapus semua kecuali nilai pinA dan pinB
if (membaca == B00001100 && aFlag) {// periksa bahwa kami memiliki kedua pin di detent (HIGH) dan bahwa kami mengharapkan detent pada naiknya pin ini
encoderPos -; // kurangi jumlah posisi pembuat kode
bFlag = 0; // setel ulang bendera untuk belokan berikutnya
aFlag = 0; // setel ulang bendera untuk belokan berikutnya
}
lain jika (membaca == B00000100) bFlag = 1; // sinyal bahwa kami mengharapkan pinB untuk memberi sinyal transisi agar tidak berputar bebas
sei (); // restart interupsi
}
membatalkan PinB () {
cli (); // hentikan interupsi yang terjadi sebelum kita membaca nilai pin
membaca = PIND & 0xC; // baca semua delapan nilai pin lalu hapus semua kecuali nilai pinA dan pinB
if (membaca == B00001100 && bFlag) {// periksa bahwa kami memiliki kedua pin di detent (HIGH) dan bahwa kami mengharapkan detent pada naiknya pin ini
encoderPos ++; // peningkatan jumlah posisi pembuat kode
bFlag = 0; // setel ulang bendera untuk belokan berikutnya
aFlag = 0; // setel ulang bendera untuk belokan berikutnya
}
lain jika (membaca == B00001000) aFlag = 1; // sinyal bahwa kami mengharapkan pinA untuk memberi sinyal transisi agar tidak berputar bebas
sei (); // restart interupsi
}
void loop () {
if (oldEncPos! = encoderPos) {
Serial.println (encoderPos);
oldEncPos = encoderPos;
}
}
Itu dia!
Lampiran
Unduh rotaryEncoder.ino
Langkah 4: Kesimpulan

Saya harap Anda menemukan kode ini berguna untuk proyek Anda berikutnya yang menggunakan rotary encoder atau yang telah menginspirasi Anda untuk mempertimbangkan rotary encoder sebagai input untuk proyek Anda berikutnya.
Ringkasan Tujuan
Saya telah mencoba menulis beberapa kode yang mencapai keseimbangan baik:
- Portabilitas (kode manipulasi port adalah kompromi ketika pindah ke chip lain)
- Kecepatan (manipulasi port sangat membantu)
- Ukuran kode yang dikompilasi rendah (manipulasi port dan bitmath membantu)
- Andal mencatat rotasi manual lambat dan cepat
- Mengurangi panggilan rutin layanan interupsi nugatory (menggunakan interupsi RISING dan menonaktifkan interupsi sementara)
Peringatan dan Gagasan untuk Perbaikan
Kode ini tidak sempurna dengan cara apa pun dan Anda mungkin ingin mengubahnya untuk menggunakan pin lain. Saya menguji kode ini dengan sketsa yang menyebabkan keterlambatan dan pembacaan yang paling tidak dapat diandalkan dengan pendekatan lain yang dibahas - saya tentu saja belum membandingkannya dengan timer untuk melihat kode siapa yang menghasilkan lebih sedikit rutinitas layanan interupsi nugatory, membutuhkan waktu paling sedikit untuk mengeksekusi atau menyaring persentase terpental kontak tertinggi. Mungkin seseorang mungkin ingin melakukan tes benchmark terhadap pendekatan lain di luar sana.