Manufaktur industri
Industri Internet of Things | bahan industri | Pemeliharaan dan Perbaikan Peralatan | Pemrograman industri |
home  MfgRobots >> Manufaktur industri >  >> Manufacturing Technology >> Proses manufaktur

Pembaca PWM Arduino Efisien & Dekoder Penerima RC dengan Fail-Safe Terintegrasi

Komponen dan perlengkapan

Pembaca PWM Arduino Efisien & Dekoder Penerima RC dengan Fail-Safe Terintegrasi
Arduino UNO
×1
Pembaca PWM Arduino Efisien & Dekoder Penerima RC dengan Fail-Safe Terintegrasi
Motor servo mikro SG90
×2

Tentang proyek ini

Proyek ini berisi kode generik namun efisien yang dapat digunakan untuk hanya membaca penerima RC (atau sinyal PWM lainnya) pada pin input Arduino apa pun, dan juga menerapkan fail-safe dalam hal hilangnya sinyal transmisi.

Di bawah ini adalah video yang menunjukkan Arduino uno bertindak sebagai pem campur servo menggunakan kode PWMread_RCfailsafe.ino yang tersedia di bawah halaman ini.

Dengan bantuan, fungsi dalam PWMread_RCfailsafe.ino mengurus register interrupt untuk Anda, dan dapat dipindahkan dengan mudah antara proyek-proyek yang berbeda yang menggunakan pin yang berbeda.

Dalam contoh video di bawah ini:

  • Fail-safe aktif saat penerima tidak memiliki sinyal dari transmisi. Channel elevator diatur ke penuh ke atas, dan channel aileron diatur ke netral
  • Arah servo biru ditentukan oleh posisi throttle
  • Rentang gerakan (rates) servo biru ditentukan oleh slider di sisi transmisi
  • Pengcampuran dinyalakan dan dimatikan menggunakan tombol gear pada transmisi

Isi

  • Bagaimana servo dikontrol oleh PWM
  • Contoh penggunaan Arduino dalam model RC / robot
  • Ringkasan Kode: Dekode PWM dari penerima RC dengan fail-safe
  • Cara menggunakan PWMread_RCfailsafe.ino
  • Tampilkan frekuensi dan rata-rata frame penerima
  • Contoh pem campuran servo
  • Alasan di balik pendekatan yang diambil
  • Keterbatasan

Bagaimana servo dan pengontrol kecepatan dikontrol oleh PWM?

Dianggap di seluruh proyek ini bahwa Anda memiliki pemahaman tentang sinyal PWM yang digunakan untuk mengontrol servo dan pengontrol kecepatan. Berikut adalah video bagus yang menjelaskan bagaimana sinyal modulasi lebar pulsa (PWM) ini bekerja.

Anda juga harus memiliki pengetahuan kerja tentang:

  • Arduino IDE
  • Variabel Float, boolean, dan int
  • If loops
  • For loops
  • Arrays
  • Servo library

Contoh penggunaan Arduino dalam model RC / robot

Anda hanya akan terbatas oleh imajinasi Anda:

  • Terapkan servo mixing, nyalakan/matikan lampu, kontrol pompa/klepa, atur urutan khusus...
  • Buat pengontrol (misalnya stabilisasi penerbangan / autopilot, pemegang arah, pemegang ketinggian / kedalaman, auto-leveler, deteksi dan hindari, pulang ke rumah...)
  • Buat model RC Anda merespons hilangnya sinyal atau tegangan baterai rendah...
  • Gunakan transmisi yang sama untuk banyak model / proyek tanpa harus mengubah pengaturan apa pun atau menggunakan fitur memori model.

Ringkasan kode: Dekode PWM dari penerima RC dengan fail-safe

Kode ini mengukur sinyal PWM (Pulse Width Modulation) menggunakan pin change interrupts. Fungsi yang digunakan mengotomatisasi penyiapan interrupt dan ekstraksi data dari pin digital atau analog apa pun (kecuali A6 dan A7), pada Arduino Uno, Nano atau Pro Mini. Ini membuat kode mudah digunakan bahkan untuk pemula.

Tujuan utama proyek ini adalah untuk membuat "modul" penerima RC generik dengan fail-safe yang dapat dengan mudah dipindahkan antara proyek. Seperti itu contoh kode yang ditampilkan di bagian "cara menggunakan" hanya dapat digunakan sebagai sarana untuk tujuan.

Catatan: kode ini tidak akan bekerja dengan software serial atau library lain yang menggunakan pin change interrupts.

Bagi yang tertarik bagaimana kode bekerja:

  • Pin input diidentifikasi dalam array. Array ini dapat berukuran berapa pun.
  • Fungsi setup mengaktifkan pin change interrupts dengan mengatur register yang sesuai untuk setiap pin yang tercantum dalam array pin.
  • Perubahan tegangan pada salah satu pin yang dipilih akan memicu salah satu dari tiga Interrupt Service Routes (ISR) tergantung pada register port mana pin tersebut berada ISR(PCINT0_vect) -> Port B, ISR(PCINT1_vect) -> Port C atau ISR(PCINT2_vect) -> Port D.
  • Dalam setiap ISR, FOR loop dan IF statements digunakan untuk menentukan pin mana yang berubah, dan channel RC mana yang dimilpinya. Waktu interrupt dicatat melalui penggunaan micros() sebelum kembali ke loop utama().
  • Interval waktu antara perubahan pin digunakan untuk menghitung lebar pulsa dan periode pengulangan.
  • Flag diatur dalam setiap ISR untuk menunjukkan ketika pulsa baru telah diterima
  • Flag kemudian digunakan oleh fungsi yang tersisa untuk mengekstrak dan memproses data yang dikumpulkan oleh ISRs

Video YouTube berikutnya yang dibuat olehJoop Brokking membicarakan proyek yang berbeda yang menggunakan metode yang sama untuk menghubungkan penerima RC ke arduino. Durante 8 menit pertama Joop menjelaskan dengan jelas cara use pin change interrupts untuk mengukur sinyal PWM dari penerima RC.

Semua detail ini diurus oleh PWMread_RCfailsafe.ino yang dapat diunduh di bawah halaman ini.

Beberapa informasi berguna tentang manipulasi port juga dapat ditemukan di sini:https://tronixstuff.com/2011/10/22/tutorial-arduino-port-manipulation/

Selain penanganan pin change interrupt, fungsi khusus RC_decode() telah ditulis untuk mengonversi lebar pulsa (1000-2000uS) menjadi sinyal kontrol +-100% dari transmisi. Fail-safe memeriksa sinyal transmisi yang valid menggunakan toleransi sinyal 10-330Hz, dan 500-2500uS. Jika sinyal hilang maka RC_decode() mengembalikan nilai fail-safe yang ditentukan sebelumnya.

Nilai kalibrasi untuk transmisi tertentu, dan posisi fail-safe dapat diatur untuk setiap channel dalam PWMread_RCfailsafe.ino

Cara menggunakan PWMread_RCfailsafe.ino

Langkah 1: Contoh perangkat keras pemasangan dengan Arduino Uno

Jika mengikuti pemasangan contoh dengan Arduino Uno hubungkan penerima Anda sebagai berikut: (jika tidak jika Anda menggunakan proyek Anda sendiri lompat langsung ke langkah 2)

  • Dayakan penerima menggunakan pin 5v dan GND
  • Sambungkan pin sinyal dari penerima ke pin 2 hingga 7 pada Arduino menggunakan kabel jumper betina ke paka. (Jika Anda memiliki penerima 2 channel hubungkan hanya ke pin 2 dan 3)
  • Untuk contoh pem campuran servo sambungkan kabel sinyal satu servo ke pin 9 dan yang lain ke pin 10.
Pembaca PWM Arduino Efisien & Dekoder Penerima RC dengan Fail-Safe Terintegrasi

Langkah 2: Salin PWMread_RCfailsafe.ino ke folder sketsa

Contoh sketsa RC_Read_Example telah disertakan untuk diunduh di bawah halaman. Anda dapat menggunakan ini sebagai sketsa utama Anda saat mengikuti langkah di bawah ini.

Salin dan tempel file PWMread_RCfailsafe.ino ke folder yang berisi sketsa utama Anda. Saat Anda membuka sketsa di IDE, tab kedua akan muncul yang berisi kode dalam PWMread_RCfailsafe.ino.

Pembaca PWM Arduino Efisien & Dekoder Penerima RC dengan Fail-Safe Terintegrasi

Langkah 3: Tentukan pin input

Buka atau buka ulang sketsa utama di Arduino IDE.

Klik tab PWMread_RCfailsafe, gulir ke bawah ke judul "USER DEFINED VARIABLES" dan masukkan pin input dalam array pwmPIN[].

Catatan: Berapa pun pin yang dapat digunakan, dan dalam urutan apa pun. Hanya perlu waspada bahwa semakin banyak input yang Anda miliki semakin banyak waktu yang akan dihabiskan kode untuk mengatasi rutinitas interrupt. Catat A6 dan A7 adalah pin analog saja dan tidak dapat digunakan.

Arduino MEGA saat ini tidak didukung, namun ini dapat dengan mudah diperbaiki jika ada minat untuk itu.

Catatan: elemen pertama dalam pwmPIN[] adalah channel 1, elemen kedua channel 2, dst... jika Anda menggunakan semua channel dari penerima akan baik untuk memastikan channel penerima 1 sesuai dengan channel 1 dalam pwmPIN[]...

Pembaca PWM Arduino Efisien & Dekoder Penerima RC dengan Fail-Safe Terintegrasi

Langkah 4: Tinjau fungsi yang tersedia dalam PWMread_RCfailsafe.ino

Pembaca PWM Arduino Efisien & Dekoder Penerima RC dengan Fail-Safe Terintegrasi

Langkah 5: Cetak data lebar pulsa ke serial

Unggah kode RC_Read_Example, nyalakan transmisi Anda dan cetak data lebar pulsa mentah ke serial.

Fungsi RC_avail() harus digunakan untuk memeriksa kapan data baru telah diterima pada semua channel, lalu gunakan print_RCpwm() untuk mengirim data lebar pulsa ke serial.

Pembaca PWM Arduino Efisien & Dekoder Penerima RC dengan Fail-Safe Terintegrasi

Langkah 6: Kalibrasi transmisi

Menggunakan data lebar pulsa yang dicetak ke serial melalui print_RCpwm() untuk memodifikasi nilai secara manual dalam array RC_min[], RC_mid[], dan RC_max[] untuk mengkalibrasi setiap channel ke rentang +-100%.

Pembaca PWM Arduino Efisien & Dekoder Penerima RC dengan Fail-Safe Terintegrasi

Langkah 7: Cetak channel yang telah dikalibrasi ke serial

Comment out fungsi print_RCpwm()

Gunakan fungsi RC_decode(channel) untuk mengkalibrasi setiap channel ke rentang +-1.

Lalu cetak setiap channel yang telah dikalibrasi ke serial menggunakan fungsi decimal2percentage() diikuti oleh Serial.println("")

Pembaca PWM Arduino Efisien & Dekoder Penerima RC dengan Fail-Safe Terintegrasi

Langkah 8: Atur fail-safe

Sesuaikan posisi fail-safe dalam array RC_failsafe[] untuk setiap channel (dalam rentang +-1).

Nyalakan dan matikan transmisi untuk memeriksa apakah fail-safe beroperasi sebagaimana diinginkan.

Input RC sekarang dapat digunakan dalam sketsa Anda.

Catatan: Anda mungkin perlu menonaktifkan fitur fail-safe dalam penerima, jika tidak arduino tidak akan dapat merespons hilangnya sinyal transmisi.

Pembaca PWM Arduino Efisien & Dekoder Penerima RC dengan Fail-Safe Terintegrasi

Tampilkan frekuensi dan rata-rata frame penerima

Periode pengulangan pulsa penerima dan frekuensi dapat dicetak ke serial. Periksa apakah data baru tersedia pada channel yang dipilih dengan menggunakan fungsi PWM_read(nomor channel), sebelum menggunakan PWM_period() dan PWM_freq() untuk mengekstrak data untuk dicetak. Kode contoh tersedia dalam RC_FrameRate.ino.

Paling baik untuk menggunakan channel pertama karena ini akan menjadi pulsa pertama yang dikirim dalam setiap frame penerima. PWM_read() menggunakan flag yang sama dengan RC_decode(CH) jadi pastikan PWM_read() dipanggil terlebih dahulu.

Lihat tangkapan layar di bawah ini:

Pembaca PWM Arduino Efisien & Dekoder Penerima RC dengan Fail-Safe Terintegrasi

Periode penerima berguna untuk diketahui karena memberi tahu Anda berapa banyak waktu yang tersisa sebelum data berikutnya tiba. Jika RC_avail() tidak mendeteksi data RC baru setelah waktu yang ditentukan misalnya 21ms maka jalankan RC_decode() untuk memicu fail-safe dan / atau untuk terus menjalankan program (yang bisa menjadi kontrol PID) pada frekuensi yang stabil.

Ini dicapai dalam RC_Read_Example.ino oleh pernyataan if berikut.

now = millis(); if(RC_avail() || now - rc_update > 21) rc_update = now; // update RC input data using RC_decode() // run a PID controller // apply servo mixing // position the servos } 

Contoh pem campuran servo

Saya telah menyertakan RC_ServoMixer_Example.ino untuk menunjukkan bagaimana Anda bisa mencampur dua channel penerima (dalam hal ini channel 2 dan 3, elevator dan aileron). Sketsa ini juga menunjukkan metode untuk mengatur arah servo, rate, dan sub trim. Library servo digunakan untuk mengontrol servo melalui pin 9 dan 10.

Di bawah ini adalah tangkapan layar bagian pem campuran servo dari kode:

Pembaca PWM Arduino Efisien & Dekoder Penerima RC dengan Fail-Safe Terintegrasi

Campuran dicapai dengan cara menambahkan dan mengurangi dua channel bersama-bagi, dan membatasi output ke rentang -1 hingga +1. Saat menerapkan elevator dan aileron mixing Anda membuat dua output satu untuk setiap servo.

mix1 = channel 2 - channel3 (elv - ail)

mix2 = channel 2 + channel3 (elv - ail)

Sebelum memosisikan servo Anda perlu mengonversi sinyal +-100% (+-1) ke lebar pulsa setara dalam mikrodetik untuk servo. Dalam RC_ServoMixer_Example.ino saya menggunakan fungsi calc_uS() untuk melakukannya. Fungsi ini ditempatkan di bawah sketsa dan ditampilkan dalam tangkapan layar di bawah ini.

Pembaca PWM Arduino Efisien & Dekoder Penerima RC dengan Fail-Safe Terintegrasi

Arah, rate, dan sub trim yang ditentukan untuk setiap servo digunakan untuk menghitung lebar pulsa yang sesuai untuk servo.

Pulsa netral standar adalah 1500uS, dan rentang normal baik dari netral adalah +-500uS. Ini memberikan lebar pulsa minimum 1000uS (-100%) dan maksimal 2000uS (+100%). Pulsa dengan rate, arah dan sub trim yang diterapkan dapat dengan demikian dihitung sebagai berikut.

pulsa, uS = 1500 + (servo_position_% * rates * direction + sub trim) * 500

Arah servo, rate dan sub trim dapat statis atau dimodifikasi secara dinamis oleh sketsa sebagai respons terhadap input dari channel penerima lain, atau dengan beberapa cara lain.

Alasan di balik pendekatan yang diambil

Mungkin untuk membaca penerima RC menggunakan fungsi pulseIn(PIN, HIGH), namun pulseIn() memblokir kode di loop() sementara menunggu pulsa dimulai dan kemudian selesai, membuang waktu pemrosesan yang berharga. Jika ada lebih dari satu input data juga bisa hilang.

Untuk kecepatan paling baik untuk menggunakan fitur pin change interrupt Arduino bersama manipulasi port langsung untuk memungkinkan kode di loop() berjalan dengan penundaan minimum. Namun ini lebih rumit dan memakan waktu daripada sekadar memanggil pulseIn(PIN, HIGH).

Oleh karena itu saya ingin mendapatkan keuntungan dari kedua dunia dengan menulis beberapa kode generik yang bisa saya pindahkan antara proyek. Yang diperlukan hanyalah menyalin dan menempelkan file .ino (yang berisi fungsi dan rutinitas interrupt) ke folder sketsa utama, tentukan pin input, dan lalu gunakan fungsi dalam sketsa.

Keterbatasan

Fungsi micros()

Waktu mikrodetik pada arduino dilakukan menggunakan fungsi micros(). Fungsi ini menghitung dalam langkah 4uS. Ini berarti kita memiliki tingkat presisi 4 mikrodetik saat kita mengukur pulsa 1000-2000uS. Dari sudut pandang praktis ini lebih dari cukup.

Jika diinginkan mungkin untuk meningkatkan resolusi ini menjadi 0,5uS dengan menggunakan timer interrupts. lihat tautan di bawah ini:

https://www.instructables.com/id/How-to-get-an-Arduino-micros-function-with-05us-pr/

Efisiensi PWMread_RCfailsafe.ino

Jika Anda menggunakan PWMread_RCfailsafe.ino untuk membaca penerima 6 atau 9 channel 1.4-2.0% dari waktu pemrosesan dihabiskan untuk menjalankan rutinitas pin change interrupt, yang saya anggap lebih dari dapat diterima.

Namun selalu baik untuk memahami keterbatasan kode, dan bagaimana itu dapat dipercepat jika diperlukan.

Di bawah ini adalah daftar waktu yang dibutuhkan untuk menjalankan setiap ISR tergantung pada jumlah channel input yang dipilih.

1 channel < 8uS

2 channels < 12uS

3 channels < 16uS

4 channels < 20uS

5 channels < 20uS

6 channels < 24uS

Catatan: semakin banyak channel yang digunakan semakin lama waktu yang dibutuhkan setiap ISR untuk berjalan. Ini karena for loop berjalan melalui setiap channel setiap kali ISR dipanggil.

Waktu tambahan (inefisiensi) ini tidak signifikan saat mengukur sinyal RC frekuensi rendah (misalnya 50hz).

Di atas di atas itu memakan waktu ~4uS untuk masuk dan keluar dari ISR. Untuk satu pulsa ISR berjalan dua kali, sekali di awal pulsa (LOW ke HIGH) dan kemudian lagi di akhir (HIGH ke LOW).

Waktu yang dibutuhkan untuk mengukur 1 pulsa saat menggunakan 6 input RC adalah

2 * (4us untuk masuk ISR + 24uS untuk menjalankan ISR) = 2 * 28 = 48uS.

Catatan: ini adalah lebar pulsa minimum yang dapat diukur.

Waktu yang dibutuhkan untuk membaca semua 6 channel adalah 288uS (6 * 48uS)

Asumsi periode pengulangan penerima adalah 20 milidetik, maka interrupt akan berjalan selama 1.44% (0.000288/0.02) dari waktu. Ini jauh lebih baik daripada menggunakan fungsi pulseIn(). pulseIn() akan memblokir kode selama hingga 20 milidetik untuk setiap pin.

FYI: jika arduino hanya memiliki 2 input RC maka ISR akan berjalan hanya 0.16% dari waktu (0.000032/0.02)

Frekuensi praktis maksimum (Hz)

Jika menggunakan kode ini untuk tujuan lain saya menyarankan bahwa frekuensi praktis maksimum adalah 2.5kHz. Ini memberikan 100 langkah resolusi dari fungsi micros() (+- 0.025kHz).

Jika menggunakan satu pin input pada frekuensi ini 3% dari waktu dihabiskan dalam interrupt, yang berarti duty minimum yang dapat diukur adalah 0.03. Ini setara dengan lebar pulsa minimum 12uS.

Untuk frekuensi yang lebih tinggi tulis ulang ISR untuk aplikasi Anda.

Kode

  • PWMread_RCfailsafe
  • RC_Read_Example
  • RC_FrameRate
  • RC_ServoMixer_Example
PWMread_RCfailsafeArduino
File .ino ini berisi fungsi dan rutinitas pin change interrupt (ISR) yang digunakan untuk mendekode Penerima RC dan menerapkan fail-safe jika sinyal transmisi hilang. Salin dan tempel file ini ke folder yang sama dengan sketsa utama (saat Anda membuka sketsa kode ini akan muncul sebagai tab kedua di Arduino IDE). Lalu ikuti instruksi dalam file.
/* Kelvin Nelson 24/07/2019 * * Pulse Width Modulation (PWM) decoding of RC Receiver with failsafe * * This code contains easy to use functions to measure square wave signals on any arduiuno pro mini, nano or uno pins, excluding A6 and A7. * The code is intended to be used with RC receivers, but could also be used in most other PWM measurement applications as a direct replacement for pulseIn(PIN, HIGH). * (to date it hasn't been tested at a frequency greater than 1khz or on an arduino mega) * * An RC signal pulse can be converted from a pulse width duration (1000-2000uS) at each input pin into an -+100% (-+1.0) output for use in a sketch. * The calibration for this conversion plus a failsafe setting can be set for each channel. (fail safe tolerances 10-330Hz and 500-2500uS). * * The raw data for each pin can also be extracted, i.e. time of pulse, pulse width, frame length, duty and frequency. * * Set-up is quick, the organisation of this file is as follows: * * - Overview of the code * - List of functions * - How to use, including example sketches * - User defined variables -> specify input pins, transmitter calibration, and failsafe. * - Global variables and functions * * OVERVIEW OF THE CODE: * * The code enables pin change interrupts on the selected pins by setting the appropriate registers. * * A voltage change on any of the selected pins will trigger one of three Interrupt Service Routines depending on which register the pin belongs to. * - ISR(PCINT0_vect), ISR(PCINT1_vect) or ISR(PCINT2_vect) * * Within each ISR the code determines which pin has changed, and makes a note of the time before returning back to the main loop(). * * The time intervals between pin changes are used to calculate pulse width and frame length. * * Flags are set by the ISR to indicate when new pulses are received. * * The Flags are then used to extract and process the data collected by each ISR. * * Although it's not exactly the same, this code follows similar principles to those explained in this video: https://youtu.be/bENjl1KQbvo * */ // LIST OF FUNCTIONS: // OUTPUT TYPE NAME OF FUNCTION NOTES // void setup_pwmRead() initialise the PWM measurement using pin change interrupts // RC RECEIVER DECODING // boolean RC_avail() returns a HIGH when new RC data is available // float RC_decode(channel number) decodes the selected RC channel into the range +-100%, and applies a failsafe. // void print_RCpwm() Prints the RC channel raw data to serial port (used for calibration). // GENERIC PWM MEASUREMENTS // boolean PWM_read(channel number) returns a HIGH when a new pulse has been detected on a particular channel. // The function saves the pulse data to variables outside the interrupt routines // and must be called just before using the rest of PWM functions. // unsigned long PWM_time() returns the time at the start of pulse // float PWM() returns the pulse width // float PWM_period() returns the time between pulses // float PWM_freq() calculates the frequency // float PWM_duty() calculates the duty // NOTE: PWM_read(CH) and RC_decode(CH) use the same flags to detect when new data is available, meaning data could be lost if both are used on the same channel at the same time. // SUGESTION: if you want to use PWM_read(CH) to find the frame rate of an RC channel call it before RC_decode(CH). The output from RC_decode(CH) will then default to the failsafe. // HOW TO USE, including example sketches // under the "USER DEFINED VARIABLES" title in the code below: // // Step 1: enter the input pins into the array pwmPIN[] = {}. // // - Any number of pins can be entered into pwmPIN[] (pins available 0 - 13 and A0 - A5) // - The pins do not need to be in numerical order, for example pwmPIN[] = {A0,5,6,10,8} for 5 channels, or pwmPIN[] = {A0,5} for 2 channels // - The first element in the array is the pin number for "channel 1", and the second is the pin number for "channel 2"... etc. // - All pins connected to the RC receiver need to be at the start of the array. i.e. the first 2 channels could be RC inputs and the 3rd channel could be connected to another device like the echo pin of an ultrasonic sensor. // // Step 2: if an RC receiver is connected to all of the inputs then set RC_inputs to 0, if not specify the number of channels connected to the receiver i.e. RC_inputs = 2; // // Step 3: calibrate your transmitter by uploading a simple sketch with this .ino file included in the sketch folder, and print the raw PWM values to serial (alternatively copy and paste the functions needed into the sketch). // Using the info from the serial monitor manually update the values in arrays RC_min[], RC_mid[], RC_max[] to suit your transmitter (use full rates to get the best resolution). // an example sketch for printing the RC channel PWM data to serial. /* void setup() { setup_pwmRead(); Serial.begin(9600); } void loop() { if(RC_avail()) print_RCpwm(); } */ // Step 4: Choose a failsafe position for each channel, in the range -1.0 to +1.0, and enter it into the array RC_failsafe[] = {} // Note: if you would like the arduino to respond to the loss of transmitter signal you may need to disable the failsafe feature on your receiver (if it has one). // an example sketch to check the operation of the failsafe, and for printing the calibrated channels to serial: /* unsigned long now; // timing variables to update data at a regular interval unsigned long rc_update; const int channels = 6; // specify the number of receiver channels float RC_in[channels]; // an array to store the calibrated input from receiver void setup() { setup_pwmRead(); Serial.begin(9600); } void loop() { now = millis(); if(RC_avail() || now - rc_update > 25){ // if RC data is available or 25ms has passed since last update (adjust to suit frame rate of receiver) rc_update = now; //print_RCpwm(); // uncommment to print raw data from receiver to serial for (int i = 0; i<channels; i++){ // run through each RC channel int CH = i+1; RC_in[i] = RC_decode(CH); // decode receiver channel and apply failsafe print_decimal2percentage(RC_in[i]); // uncomment to print calibrated receiver input (+-100%) to serial } Serial.println(); // uncomment when printing calibrated receiver input to serial. } } */ // EXAMPLE USE OF GENERIC PWM FUNCTIONS: /* // Print the pulse width of channel 1 to the serial monitor. // This is equivelant to the using standard arduino pulseIn(pin, HIGH) function, but without blocking the code. if (PWM_read(1)){ // if a new pulse is detected on channel 1, print the pulse width to serial monitor. Serial.println(PWM()); } */ // Or /* // Print RC receiver frame length and frame rate if (PWM_read(1)){ // if a new pulse is detected on channel 1 Serial.print(PWM_period(),0);Serial.print("uS "); Serial.print(PWM_freq());Serial.println("Hz"); } */ /* * USER DEFINED VARIABLES (MODIFY TO SUIT YOUR APPLICATION) */ // PWM input pins, any of the following pins can be used: digital 0 - 13 or analog A0 - A5 const int pwmPIN[]={2,3,4,5,6,7}; // an array to identify the PWM input pins (the array can be any length) // first pin is channel 1, second is channel 2...etc int RC_inputs = 0; // The number of pins in pwmPIN that are connected to an RC receiver. Addition pins not connected to an RC receiver could be used for any other purpose i.e. detecting the echo pulse on an HC-SR04 ultrasonic distance sensor // When 0, it will automatically update to the number of pins specified in pwmPIN[] after calling setup_pwmRead(). // Calibration of each RC channel: // The arrays below are used to calibrate each RC channel into the range -1 to +1 so that servo direction, mixing, rates, sub trims...etc can be applied in sketch depending on application. // The arrays should be modified in order to calibrate the min, middle and max pulse durations to suit your transmitter (use max rates to get the best resolution). // FYI: the function print_PWM() will print the raw pulse width data for all the RC channels to the serial port. // if the RC_min[], RC_mid[], RC_max[] are empty or have missing data the calibration will default to min 1000us, mid 1500us and max 2000us. //SANWA 6CH 40MHz with corona RP6D1 // THR RUD PIT BAL SWITCH SLIDER int RC_min[6] = { 988, 1060, 976, 960, 1056, 1116}; int RC_mid[6] = { 1472, 1446, 1424, 1398, 1374, 1460}; int RC_max[6] = { 1800, 1816, 1796, 1764, 1876, 1796}; // fail safe positions float RC_failsafe[] = {0.00, 0.00, 1, 0.00, -0.25, 0.00}; // enter a failsafe position (in the range of -+1) for each RC channel in case radio signal is lost // if the array is the incorrect length for the number of RC channels, the failsafe will default to neutral i.e. 0. // The failsafe tolerances are: 10-330Hz & 500-2500us /* * GLOBAL PWM DECODE VARIABLES */ const int num_ch = sizeof(pwmPIN)/sizeof(int); // calculate the number of input pins (or channels) volatile int PW[num_ch]; // an array to store pulsewidth measurements volatile boolean prev_pinState[num_ch]; // an array used to determine whether a pin has gone low-high or high-low volatile unsigned long pciTime; // the time of the current pin change interrupt volatile unsigned long pwmTimer[num_ch]; // an array to store the start time of each PWM pulse volatile boolean pwmFlag[num_ch]; // flag whenever new data is available on each pin volatile boolean RC_data_rdy; // flag when all RC receiver channels have received a new pulse unsigned long pwmPeriod[num_ch]; // period, mirco sec, between two pulses on each pin byte pwmPIN_reg[num_ch]; // each of the input pins expressed as a position on it's associated port register byte pwmPIN_port[num_ch]; // identify which port each input pin belongs to (0 = PORTB, 1 = PORTC, 2 = PORTD) const int size_RC_min = sizeof(RC_min) / sizeof(int); // measure the size of the calibration and failsafe arrays const int size_RC_mid = sizeof(RC_mid) / sizeof(int); const int size_RC_max = sizeof(RC_max) / sizeof(int); const int size_RC_failsafe = sizeof(RC_failsafe) / sizeof(float); // FUNCTION USED TO TURN ON THE INTERRUPTS ON THE RELEVANT PINS // code from https://playground.arduino.cc/Main/PinChangeInterrupt void pciSetup(byte pin){ *digitalPinToPCMSK(pin) |= bit (digitalPinToPCMSKbit(pin)); // enable pin PCIFR |= bit (digitalPinToPCICRbit(pin)); // clear any outstanding interrupt PCICR |= bit (digitalPinToPCICRbit(pin)); // enable interrupt for the group } // FUNCTION USED TO FIND THE PIN POSITION ON EACH PORT REGISTER: helps the interrupt service routines, ISR, run faster void pwmPIN_to_port(){ for (int i = 0; i < num_ch; i++){ // determine which port and therefore ISR (PCINT0_vect, PCINT1_vect or PCINT2_vect) each pwmPIN belongs to. pwmPIN_port[i] = 1; // pin belongs to PCINT1_vect (PORT C) if (pwmPIN[i] >= 0 && pwmPIN[i] <= 7) pwmPIN_port[i] = 2; // pin belongs to PCINT2_vect (PORT D) else if (pwmPIN[i] >= 8 && pwmPIN[i] <= 13) pwmPIN_port[i] = 0; // pin belongs to PCINT0_vect (PORT B) // covert the pin number (i.e. pin 11 or pin A0) to the pin position in the port register. There is most likely a better way of doing this using a macro... // (Reading the pin state directly from the port registers speeds up the code in the ISR) if(pwmPIN[i] == 0 || pwmPIN[i] == A0 || pwmPIN[i] == 8) pwmPIN_reg[i] = 0b00000001; else if(pwmPIN[i] == 1 || pwmPIN[i] == A1 || pwmPIN[i] == 9) pwmPIN_reg[i] = 0b00000010; else if(pwmPIN[i] == 2 || pwmPIN[i] == A2 || pwmPIN[i] == 10) pwmPIN_reg[i] = 0b00000100; else if(pwmPIN[i] == 3 || pwmPIN[i] == A3 || pwmPIN[i] == 11) pwmPIN_reg[i] = 0b00001000; else if(pwmPIN[i] == 4 || pwmPIN[i] == A4 || pwmPIN[i] == 12) pwmPIN_reg[i] = 0b00010000; else if(pwmPIN[i] == 5 || pwmPIN[i] == A5 || pwmPIN[i] == 13) pwmPIN_reg[i] = 0b00100000; else if(pwmPIN[i] == 6) pwmPIN_reg[i] = 0b01000000; else if(pwmPIN[i] == 7) pwmPIN_reg[i] = 0b10000000; } } // SETUP OF PIN CHANGE INTERRUPTS void setup_pwmRead(){ for(int i = 0; i < num_ch; i++){ // run through each input pin pciSetup(pwmPIN[i]); // enable pinchange interrupt for pin } pwmPIN_to_port(); // determines the port for each input pin // pwmPIN_to_port() also coverts the pin number in pwmPIN[] (i.e. pin 11 or pin A0) to the pin position in the port register (i.e. 0b00000001) for use in the ISR. if(RC_inputs == 0 || RC_inputs > num_ch) RC_inputs = num_ch; // define the number of pins connected to an RC receiver. } // INTERRUPT SERVICE ROUTINES (ISR) USED TO READ PWM INPUT // the PCINT0_vect (B port register) reacts to any changes on pins D8-13. // the PCINT1_vect (C port register) "" "" A0-A5. // the PCINT2_vect (D port register) "" "" D0-7. // port registers are used to speed up if statements in ISR code: // https://www.arduino.cc/en/Reference/PortManipulation https://tronixstuff.com/2011/10/22/tutorial-arduino-port-manipulation/ // https://harperjiangnew.blogspot.co.uk/2013/05/arduino-port-manipulation-on-mega-2560.html // READ INTERRUPTS ON PINS D8-D13: ISR routine detects which pin has changed, and returns PWM pulse width, and pulse repetition period. ISR(PCINT0_vect){ // this function will run if a pin change is detected on portB pciTime = micros(); // Record the time of the PIN change in microseconds for (int i = 0; i < num_ch; i++){ // run through each of the channels if (pwmPIN_port[i] == 0){ // if the current channel belongs to portB if(prev_pinState[i] == 0 && PINB & pwmPIN_reg[i]){ // and the pin state has changed from LOW to HIGH (start of pulse) prev_pinState[i] = 1; // record pin state pwmPeriod[i] = pciTime - pwmTimer[i]; // calculate the time period, micro sec, between the current and previous pulse pwmTimer[i] = pciTime; // record the start time of the current pulse } else if (prev_pinState[i] == 1 && !(PINB & pwmPIN_reg[i])){ // or the pin state has changed from HIGH to LOW (end of pulse) prev_pinState[i] = 0; // record pin state PW[i] = pciTime - pwmTimer[i]; // calculate the duration of the current pulse pwmFlag[i] = HIGH; // flag that new data is available if(i+1 == RC_inputs) RC_data_rdy = HIGH; } } } } // READ INTERRUPTS ON PINS A0-A5: ISR routine detects which pin has changed, and returns PWM pulse width, and pulse repetition period. ISR(PCINT1_vect){ // this function will run if a pin change is detected on portC pciTime = micros(); // Record the time of the PIN change in microseconds for (int i = 0; i < num_ch; i++){ // run through each of the channels if (pwmPIN_port[i] == 1){ // if the current channel belongs to portC if(prev_pinState[i] == 0 && PINC & pwmPIN_reg[i]){ // and the pin state has changed from LOW to HIGH (start of pulse) prev_pinState[i] = 1; // record pin state pwmPeriod[i] = pciTime - pwmTimer[i]; // calculate the time period, micro sec, between the current and previous pulse pwmTimer[i] = pciTime; // record the start time of the current pulse } else if (prev_pinState[i] == 1 && !(PINC & pwmPIN_reg[i])){ // or the pin state has changed from HIGH to LOW (end of pulse) prev_pinState[i] = 0; // record pin state PW[i] = pciTime - pwmTimer[i]; // calculate the duration of the current pulse pwmFlag[i] = HIGH; // flag that new data is available if(i+1 == RC_inputs) RC_data_rdy = HIGH; } } } } // READ INTERRUPTS ON PINS D0-7: ISR routine detects which pin has changed, and returns PWM pulse width, and pulse repetition period. ISR(PCINT2_vect){ // this function will run if a pin change is detected on portD pciTime = micros(); // Record the time of the PIN change in microseconds for (int i = 0; i < num_ch; i++){ // run through each of the channels if (pwmPIN_port[i] == 2){ // if the current channel belongs to portD if(prev_pinState[i] == 0 && PIND & pwmPIN_reg[i]){ // and the pin state has changed from LOW to HIGH (start of pulse) prev_pinState[i] = 1; // record pin state pwmPeriod[i] = pciTime - pwmTimer[i]; // calculate the time period, micro sec, between the current and previous pulse pwmTimer[i] = pciTime; // record the start time of the current pulse } else if (prev_pinState[i] == 1 && !(PIND & pwmPIN_reg[i])){ // or the pin state has changed from HIGH to LOW (end of pulse) prev_pinState[i] = 0; // record pin state PW[i] = pciTime - pwmTimer[i]; // calculate the duration of the current pulse pwmFlag[i] = HIGH; // flag that new data is available if(i+1 == RC_inputs) RC_data_rdy = HIGH; } } } } /* * RC OUTPUT FUNCTIONS */ boolean RC_avail(){ boolean avail = RC_data_rdy; RC_data_rdy = LOW; // reset the flag return avail; } float RC_decode(int CH){ if(CH < 1 || CH > RC_inputs) return 0; // if channel number is out of bounds return zero. int i = CH - 1; // determine the pulse width calibration for the RC channel. The default is 1000, 1500 and 2000us. int Min; if(CH <= size_RC_min) Min = RC_min[CH-1]; else Min = 1000; int Mid; if(CH <= size_RC_mid) Mid = RC_mid[CH-1]; else Mid = 1500; int Max; if(CH <= size_RC_max) Max = RC_max[CH-1]; else Max = 2000; float CH_output; if(FAILSAFE(CH) == HIGH){ // If the RC channel is outside of failsafe tolerances (10-330hz and 500-2500uS) if(CH > size_RC_failsafe) CH_output = 0; // and if no failsafe position has been defined, set output to neutral else CH_output = RC_failsafe[i]; // or if defined set the failsafe position } else{ // If the RC signal is valid CH_output = calibrate(PW[i],Min,Mid,Max); // calibrate the pulse width to the range -1 to 1. } return CH_output; // The signal is mapped from a pulsewidth into the range of -1 to +1, using the user defined calibrate() function in this code. // 0 represents neutral or center stick on the transmitter // 1 is full displacement of a control input is one direction (i.e full left rudder) // -1 is full displacement of the control input in the other direction (i.e. full right rudder) } /* * Receiver Calibration */ // NEED TO SPEED UP float calibrate(float Rx, int Min, int Mid, int Max){ float calibrated; if (Rx >= Mid) { calibrated = map(Rx, Mid, Max, 0, 1000); // map from 0% to 100% in one direction } else if (Rx == 0) { calibrated = 0; // neutral } else { calibrated = map(Rx, Min, Mid, -1000, 0); // map from 0% to -100% in the other direction } return calibrated * 0.001; } // Basic Receiver FAIL SAFE // check for 500-2500us and 10-330Hz (same limits as pololu) boolean FAILSAFE(int CH){ int i = CH-1; boolean failsafe_flag = LOW; if(pwmFlag[i] == 1) // if a new pulse has been measured. { pwmFlag[i] = 0; // set flag to zero if(pwmPeriod[i] > 100000) // if time between pulses indicates a pulse rate of less than 10Hz { failsafe_flag = HIGH; } else if(pwmPeriod[i] < 3000) // or if time between pulses indicates a pulse rate greater than 330Hz { failsafe_flag = HIGH; } if(PW[i] < 500 || PW[i] > 2500) // if pulswidth is outside of the range 500-2500ms { failsafe_flag = HIGH; } } else if (micros() - pwmTimer[i] > 100000) // if there is no new pulswidth measurement within 100ms (10hz) { failsafe_flag = HIGH; } return failsafe_flag; } /* * Quick print function of Rx channel input */ void print_RCpwm(){ // display the raw RC Channel PWM Inputs for (int i = 0; i < RC_inputs; i++){ //Serial.print(" ch");Serial.print(i+1); Serial.print(" "); if(PW[i] < 1000) Serial.print(" "); Serial.print(PW[i]); } Serial.println(""); } void print_decimal2percentage(float dec){ int pc = dec*100; // the number and text will take up 6 charactors i.e ___3%_ or -100%_ if (pc >= 0) Serial.print(" "); if (abs(pc) < 100) Serial.print(" "); if (abs(pc) < 10) Serial.print(" "); Serial.print(" ");Serial.print(pc);Serial.print("% "); } /* * GENERIC PWM FUNCTIONS */ unsigned long pin_time; float pin_pwm; float pin_period; boolean PWM_read(int CH){ if(CH < 1 && CH > num_ch) return false; int i = CH-1; boolean avail = pwmFlag[i]; if (avail == HIGH){ pwmFlag[i] = LOW; noInterrupts(); pin_time = pwmTimer[i]; pin_pwm = PW[i]; pin_period = pwmPeriod[i]; interrupts(); } return avail; } unsigned long PWM_time(){return pin_time;} float PWM_period(){return pin_period;} float PWM(){return pin_pwm;} float PWM_freq(){ float freq; return freq = 1000000 / pin_period; // frequency Hz } float PWM_duty(){ float duty; duty = pin_pwm/pin_period; return duty; } 
RC_Read_ExampleArduino
Contoh sketsa yang digunakan untuk menampilkan data mentah untuk mengkalibrasi penerima RC Anda dan mengatur fail-safe Anda. File PWMread_RCfailsafe.ino harus disalin ke folder yang sama agar fungsi tersedia.
unsigned long now; // timing variables to update data at a regular interval unsigned long rc_update; const int channels = 6; // specify the number of receiver channels float RC_in[channels]; // an array to store the calibrated input from receiver void setup() { setup_pwmRead(); Serial.begin(9600); } void loop() { now = millis(); if(RC_avail() || now - rc_update > 25){ // if RC data is available or 25ms has passed since last update (adjust to be equal or greater than the frame rate of receiver) rc_update = now; print_RCpwm(); // uncommment to print raw data from receiver to serial for (int i = 0; i<channels; i++){ // run through each RC channel int CH = i+1; RC_in[i] = RC_decode(CH); // decode receiver channel and apply failsafe //print_decimal2percentage(RC_in[i]); // uncomment to print calibrated receiver input (+-100%) to serial } //Serial.println(); // uncomment when printing calibrated receiver input to serial. } } 
RC_FrameRateArduino
Contoh sketsa yang mencetak rata-rata frame dan frekuensi Penerima RC. File PWMread_RCfailsafe.ino harus disalin ke folder yang sama agar fungsi tersedia.
void setup() { setup_pwmRead(); Serial.begin(9600); } void loop() { // Print RC receiver frame length and frame rate if (PWM_read(1)){ // if a new pulse is detected on channel 1 Serial.print(PWM_period(),0);Serial.print("uS "); Serial.print(PWM_freq());Serial.println("Hz"); } } 
RC_ServoMixer_ExampleArduino
Contoh pem campuran servo. Dua channel dari penerima 6 channel dicampur dan dikirim ke dua servo yang dikontrol menggunakan library servo. File PWMread_RCfailsafe.ino harus disalin ke folder yang sama agar fungsi tersedia.
// servo variables #include <Servo.h> // include the servo library to control the servos Servo servo1; // name each servo output for use with the servo library Servo servo2; // Each servo must be attached to a pin that has a PWM output // on the arduino uno, nano and pro mini these pins are 3, 5, 6, 9, 10 and 11 const int servo1_pin = 9; // identify the pins that each servo signal wire is connected to const int servo2_pin = 10; // Select Servo Direction, Rates and Sub-trim (the size of each array must match the number of servos) boolean servo_dir[] = {0,1}; // Direction: 0 is normal, 1 is reverse float servo_rates[] = {1,0.5}; // Rates: range 0 to 2 (1 = +-500us (NORMAL), 2 = +-1000us (MAX)): The amount of servo deflection in both directions float servo_subtrim[] = {0.0,0.0}; // Subtrimrange -1 to +1 (-1 = 1000us, 0 = 1500us, 1 = 2000us): The neutral position of the servo boolean servo_mix_on = true; unsigned long now; // timing variables to update data at a regular interval unsigned long rc_update; // Receiver variables const int channels = 6; // specify the number of receiver channels float RC_in[channels]; // an array to store the calibrated input from receiver void setup() { servo1.attach(servo1_pin, 500, 2500); // attach the servo library to each servo pin, and define min and max uS values servo2.attach(servo2_pin, 500, 2500); setup_pwmRead(); Serial.begin(9600); } void loop() { now = millis(); if(RC_avail() || now - rc_update > 25){ // if RC data is available or 25ms has passed since last update (adjust to > frame rate of receiver) rc_update = now; print_RCpwm(); // uncommment to print raw data from receiver to serial for (int i = 0; i<channels; i++){ // run through each RC channel int CH = i+1; RC_in[i] = RC_decode(CH); // decode receiver channel and apply failsafe //print_decimal2percentage(RC_in[i]); // uncomment to print calibrated receiver input (+-100%) to serial } //Serial.println(); // uncomment when printing calibrated receiver input to serial. int servo1_uS; // variables to store the pulse widths to be sent to the servo int servo2_uS; if (servo_mix_on == true){ // MIXING ON float mix1 = RC_in[1] - RC_in[2]; // Channel 2 (ELV) - Channel 3 (AIL) float mix2 = RC_in[1] + RC_in[2]; // Channel 2 (ELV) + Channel 3 (AIL) if(mix1 > 1) mix1 = 1; // limit mixer output to +-1 else if(mix1 < -1) mix1 = -1; if(mix2 > 1) mix2 = 1; // limit mixer output to +-1 else if(mix2 < -1) mix2 = -1; // Calculate the pulse widths for the servos servo1_uS = calc_uS(mix1, 1); // apply the servo rates, direction and sub_trim for servo 1, and convert to a RC pulsewidth (microseconds, uS) servo2_uS = calc_uS(mix2, 2); // apply the servo rates, direction and sub_trim for servo 2, and convert to a RC pulsewidth (microseconds, uS) } else{ // MIXING OFF servo1_uS = calc_uS(RC_in[1],1); // apply the servo rates, direction and sub_trim for servo 1, and convert to a RC pulsewidth (microseconds, uS) servo2_uS = calc_uS(RC_in[2],2); // apply the servo rates, direction and sub_trim for servo 1, and convert to a RC pulsewidth (microseconds, uS) } servo1.writeMicroseconds(servo1_uS); // write the pulsewidth to the servo. servo2.writeMicroseconds(servo2_uS); // write the pulsewidth to the servo. } } int calc_uS(float cmd, int servo){ // cmd = commanded position +-100% // servo = servo num (to apply correct direction, rates and trim) int i = servo-1; float dir; if(servo_dir[i] == 0) dir = -1; else dir = 1; // set the direction of servo travel cmd = 1500 + (cmd*servo_rates[i]*dir + servo_subtrim[i])*500; // apply servo rates and sub trim, then convert to a uS value if(cmd > 2500) cmd = 2500; // limit pulsewidth to the range 500 to 2500us else if(cmd < 500) cmd = 500; return cmd; } 

Skematik

Penerima RC ini di dayakan oleh 5v dan ground dari pin ICSP dengan 6 output sinyal terhubung ke pin 2-7
Servo mikro 1 di dayakan oleh pin 5v dan ground, dengan kabel sinyal terhubung ke pin 9
Servo mikro 2 di dayakan oleh pin 3.3v dan ground, dengan kabel sinyal terhubung ke pin 10Pembaca PWM Arduino Efisien & Dekoder Penerima RC dengan Fail-Safe Terintegrasi

Proses manufaktur

  1. Menguasai Input dan Output dalam Pemrograman C
  2. Buat Animasi LCD dan Game Menarik dengan Arduino UNO
  3. Buat Voltmeter Cerdas dengan Arduino & Smartphone – Proyek DIY Mudah
  4. Pencatat Suhu & Kelembaban Arduino dengan DHT11 dan Ethernet Shield
  5. Membaca Suhu dan Kelembaban dengan DHT11 pada Blynk – Tutorial Arduino Langkah demi Langkah
  6. Python 3 ke Arduino UNO: Kontrol Perintah Mudah dan Demo LED
  7. Bangun Automata Seluler Real-Time di Arduino dengan Layar OLED 128x64
  8. Membuat Radio FM Kompak dengan Arduino Nano dan RDA8057M
  9. Buat Penerima Radio FM Canggih dengan Arduino dan Modul TEA5767
  10. Bangun Rangkaian Input Analog Terisolasi untuk Arduino