Bir giriş değiştiğinde bir fonksiyon otomatik olarak çağrılabilir mi?


21

Şu anda, çizimim ana döngü boyunca her seferinde bir giriş pini kontrol ediyor. Bir değişiklik tespit ederse, buna yanıt vermek için özel bir işlev çağırır. İşte kod (esaslara göre ayarlanmış):

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();
    }
}

Maalesef bu, giriş üzerinde yapılan kısa değişiklikler (örneğin kısa süreli darbeler) için, özellikle loop()biraz yavaş çalışıyorsa , her zaman düzgün çalışmaz .

Arduino'nun giriş değişikliklerini tespit etmesini ve fonksiyonumu otomatik olarak çağırmasını sağlamanın bir yolu var mı?


1
Aradığın şey Bir Dış Kesinti
Butzke

Yanıtlar:


26

Bunu harici kesmeleri kullanarak yapabilirsiniz. Çoğu Arduinos, bunu ancak sınırlı sayıda iğnede destekliyor. Tüm detaylar için belgelere bakınız attachInterrupt().

Uno kullandığınızı varsayarsak, şöyle yapabilirsiniz:

void pinChanged()
{
    //...
}

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

void loop()
{
}

Bu pinChanged(), harici kesme 0'da bir değişiklik tespit edildiğinde çağrılır. Uno'da, GPIO pin 2'ye karşılık gelir. Harici kesme numarası, diğer panolarda farklıdır, bu nedenle ilgili dokümantasyonun kontrol edilmesi önemlidir.

Yine de bu yaklaşımın kısıtlamaları var. Özel pinChanged()işlev, Bir Kesme Hizmeti Rutini (ISR) olarak kullanılıyor. Bu loop(), çağrı yürütülürken kodun geri kalanının (içindeki her şeyin ) geçici olarak durdurulduğu anlamına gelir . Önemli zamanlamaların aksamasını önlemek için, ISR'leri olabildiğince hızlı hale getirmeyi hedeflemelisiniz.

ISR'niz sırasında başka hiçbir kesinti olmayacağına dikkat etmek önemlidir. Bu, kesintilere dayanan herhangi bir şeyin (çekirdek delay()ve millis()işlevler gibi) içinde düzgün çalışmayabileceği anlamına gelir .

Son olarak, eğer ISR'niz çizimdeki herhangi bir global değişkeni değiştirmek zorundaysa, genellikle şöyle bildirilmelidir volatile:

volatile int someNumber;

Bu önemlidir, çünkü derleyiciye değerin beklenmedik şekilde değişebileceğini söyler, bu nedenle güncel olmayan kopyalarını / önbelleklerini kullanmamaya özen göstermelidir.


soruda belirtilen "kısa darbeler" ile ilgili olarak, pimin kesmeyi tetikleyebilmesi için bir durumda olması gereken asgari bir süre var mı? (Açıkçası yoklamadan çok daha az olacaktır, ki bu döngüde başka neler olduğuna bağlıdır)
sachleen

1
@sachleen Bir ISR işlevinin yürütülmesi sırasında gerçekleşmediği sürece çalışacaktır (cevabında açıklandığı gibi); bu yüzden pinChanged()mümkün olduğunca kısa olmalıdır. Bu nedenle tipik olarak asgari süre, pinChanged()işlevin kendisini yürütme zamanı olmalıdır .
jfpoilpret

2
Kesinti kullanırken dikkat edilmesi gereken tüm önemli şeyleri içeren bu çok ayrıntılı cevap için +1!
jfpoilpret

3
Genel volatiledeğişkenleri bildirmenin yanı sıra , global değişken 1 byte'dan daha genişse, someNumber'ın olduğu gibi, program tarafından bayt erişimi arasında gerçekleşen pin değiştirme kesintisine karşı korunmanız gerekir. Bunun gibi bir ifade someNumber +=5;, düşük baytların eklenmesini ve yüksek baytların carry ile birlikte eklenmesini içerir. Bu ikisi (daha geniş değişkenler için daha fazla) bir kesme ile bölünmemelidir. Kesintileri kapatmak ve operasyon öncesi ve sonrasında (sırasıyla) geri yüklemek yeterlidir.
JRobert

sachleen - minimum darbe büyüklüğü ile ilgili. Veri sayfasında kesin bir cevap bulmak zor, ancak pin değiştirme kesintilerinin zamanlamasına bakılırsa, yarım saat içinde kilitlenirler. Kesinti "hatırlandığında", ISR devreye girip onunla ilgilenene kadar hatırlanmaya devam eder.
Nick Gammon

5

Dijital giriş olarak yapılandırılmış herhangi bir pin üzerindeki herhangi bir değişiklik durumu, bir kesinti oluşturabilir. INT1 veya INT2 tarafından kesilen sebepler için benzersiz vektörlerin aksine, PinChangeInt özelliği ortak bir vektör kullanır ve daha sonra bu vektör için Kesinti Servis Rutini'nin (aka ISR) daha sonra hangi pimin değişeceğini belirlemesi gerekir.

Neyse ki PinChangeInt Kütüphanesi bunu kolaylaştırır.

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

0

Eşiği geçen bir voltajı yalnızca YÜKSEK veya DÜŞÜK olmak yerine tespit etmek istemeniz durumunda , analog karşılaştırıcıyı kullanabilirsiniz. Örnek çizim:

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

Bu, bir girişte 1V ile 2V arasında bir değişiklik saptamanız gerekebilecek ışık dedektörleri gibi şeyler için faydalı olabilir.

Örnek devre:

görüntü tanımını buraya girin

Geçerli Zamanlayıcı / Sayıcı 1 sayısını kaydederek, belirli girişlerin kesin zamanını hatırlayacak olan İşlemcideki Giriş Yakalama Birimini de kullanabilirsiniz. Bir ISR'nin şimdiki zamanı yakalamak için kullanılmasından önce gecikmeyi (muhtemelen birkaç mikrosaniyelik) tanıtmak yerine ilgi ortaya çıktı.

Zamanlama kritik uygulamaları için, bu biraz artan doğruluk verebilir.

Örnek uygulama: Arduino'nuzu kapasitör test cihazına çevirin

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.