Bisakah suatu fungsi dipanggil secara otomatis ketika suatu input berubah?

21

Saat ini, sketsa saya sedang memeriksa pin input setiap kali sepanjang loop utama. Jika mendeteksi perubahan, ia memanggil fungsi khusus untuk meresponsnya. Berikut kodenya (dipangkas hingga yang penting):

int pinValue = LOW;

void pinChanged()
{
    //...
}

void setup()
{
    pinMode(2, INPUT);
}

void loop()
{
    // Read current input
    int newValue = digitalRead(2);

    // Has the input changed?
    if (newValue != pinValue) {
        pinValue = newValue;
        pinChanged();
    }
}

Sayangnya, ini tidak selalu berfungsi dengan baik untuk perubahan yang sangat singkat pada input (mis. Pulsa singkat), terutama jika loop()berjalan agak lambat.

Apakah ada cara untuk membuat Arduino mendeteksi perubahan input dan memanggil fungsi saya secara otomatis?

Peter Bloomfield
sumber
1
Apa yang Anda cari adalah Interrupt Eksternal
Butzke

Jawaban:

26

Anda dapat melakukan ini menggunakan interupsi eksternal. Sebagian besar Arduino hanya mendukung ini pada jumlah pin yang terbatas. Untuk detail lengkap, lihat dokumentasi di attachInterrupt().

Dengan asumsi Anda menggunakan Uno, Anda bisa melakukannya seperti ini:

void pinChanged()
{
    //...
}

void setup()
{
    pinMode(2, INPUT);
    attachInterrupt(0, pinChanged, CHANGE);
}

void loop()
{
}

Ini akan memanggil pinChanged()setiap kali perubahan terdeteksi pada interupsi eksternal 0. Pada Uno, yang sesuai dengan pin GPIO 2. Penomoran interupsi eksternal berbeda pada papan lain, jadi penting untuk memeriksa dokumentasi yang relevan.

Namun ada beberapa keterbatasan dalam pendekatan ini. Fungsi kustom pinChanged()sedang digunakan sebagai Interrupt Service Routine (ISR). Itu berarti sisa kode (semua yang ada di loop()) dihentikan sementara saat panggilan sedang dijalankan. Untuk mencegah gangguan pada waktu penting, Anda harus berusaha membuat ISR secepat mungkin.

Penting juga untuk dicatat bahwa tidak ada interupsi lain yang berjalan selama ISR Anda. Itu berarti apa pun yang mengandalkan interupsi (seperti inti delay()dan millis()fungsi) mungkin tidak berfungsi dengan baik di dalamnya.

Terakhir, jika ISR Anda perlu mengubah variabel global apa pun dalam sketsa, mereka biasanya harus dinyatakan sebagai volatile, misalnya:

volatile int someNumber;

Itu penting karena memberitahu kompiler bahwa nilainya dapat berubah secara tiba-tiba, jadi harus berhati-hati untuk tidak menggunakan salinan / cache yang ketinggalan zaman.

Peter Bloomfield
sumber
tentang "pulsa singkat" yang disebutkan dalam pertanyaan, apakah ada waktu minimum pin harus dalam keadaan untuk memicu interupsi? (jelas itu akan jauh lebih sedikit daripada pemungutan suara, yang tergantung pada apa lagi yang terjadi dalam lingkaran)
sachleen
1
@sachleen Itu akan berfungsi selama itu tidak terjadi selama pelaksanaan fungsi ISR ​​(seperti yang dijelaskan dalam jawaban); itu sebabnya pinChanged()harus sesingkat mungkin. Maka biasanya waktu minimum adalah waktu untuk menjalankan pinChanged()fungsi itu sendiri.
jfpoilpret
2
Beri +1 untuk jawaban yang sangat terperinci ini yang mencakup semua hal penting yang harus dipedulikan seseorang ketika menggunakan interupsi!
jfpoilpret
3
Selain mendeklarasikan global yang dibagikan volatile, jika variabel global lebih lebar dari 1 byte, seperti angka berapa pun, Anda harus melindungi terhadap interupsi pin-perubahan yang terjadi antara akses byte oleh program. Pernyataan seperti someNumber +=5;melibatkan menambahkan byte rendah dan menambahkan byte tinggi dengan carry yang disertakan. Kedua (lebih banyak, untuk variabel yang lebih luas) tidak boleh dibagi dengan interupsi. Mematikan interupsi dan mengembalikannya sebelum dan sesudah operasi (masing-masing) sudah cukup.
JRobert
@sachleen - mengenai ukuran pulsa minimum. Sulit untuk menemukan jawaban yang pasti dalam lembar data, tetapi jika dilihat dari waktu untuk pin-change interupsi, mereka terkunci dalam setengah siklus clock. Setelah interupsi "diingat", ia tetap diingat sampai ISR ​​menendang dan mengatasinya.
Nick Gammon
5

Keadaan perubahan apa pun pada pin yang dikonfigurasi sebagai input digital dapat membuat interupsi. Berbeda dengan vektor unik untuk penyebab interupsi oleh INT1 atau INT2, fitur PinChangeInt menggunakan vektor umum dan kemudian Interrupt Service Routine (alias ISR) untuk vektor ini perlu menentukan pin yang diubah.

Untungnya PinChangeInt Library memudahkan ini.

PCintPort::attachInterrupt(PIN, burpcount,RISING); // attach a PinChange Interrupt to our pin on the rising edge
// (RISING, FALLING and CHANGE all work with this library)
// and execute the function burpcount when that pin changes
mpflaga
sumber
0

Jika Anda ingin mendeteksi tegangan yang melewati ambang batas , alih-alih menjadi TINGGI atau RENDAH, Anda dapat menggunakan komparator analog. Contoh sketsa:

volatile boolean triggered;

ISR (ANALOG_COMP_vect)
  {
  triggered = true;
  }

void setup ()
  {
  Serial.begin (115200);
  Serial.println ("Started.");
  ADCSRB = 0;           // (Disable) ACME: Analog Comparator Multiplexer Enable
  ACSR =  bit (ACI)     // (Clear) Analog Comparator Interrupt Flag
        | bit (ACIE)    // Analog Comparator Interrupt Enable
        | bit (ACIS1);  // ACIS1, ACIS0: Analog Comparator Interrupt Mode Select (trigger on falling edge)
   }  // end of setup

void loop ()
  {
  if (triggered)
    {
    Serial.println ("Triggered!"); 
    triggered = false;
    }

  }  // end of loop

Ini bisa berguna untuk hal-hal seperti detektor cahaya, di mana Anda mungkin perlu mendeteksi perubahan dari (katakanlah) 1V ke 2V pada input.

Contoh rangkaian:

masukkan deskripsi gambar di sini

Anda juga dapat menggunakan Unit Pengambilan Input pada prosesor, yang akan mengingat waktu yang tepat dari input tertentu, dengan menyimpan hitungan Timer / Penghitung saat ini. Ini memungkinkan Anda menyimpan momen yang tepat (yah, hampir persis) saat peristiwa minat terjadi, daripada memperkenalkan penundaan (mungkin beberapa mikrodetik) sebelum ISR dapat digunakan untuk menangkap waktu saat ini.

Untuk aplikasi kritis waktu, ini dapat memberikan akurasi yang sedikit meningkat.

Contoh aplikasi: Ubah Arduino Anda menjadi kapasitor tester

Nick Gammon
sumber