Menü
ücretsiz
Kayıt
Ev  /  Kurulum ve kurulum/ Arduino, insertInterrupt ile kesintiye uğrar. Bir Arduino Programında Paralel Görevler (İplikler) Nasıl Çalıştırılır

Arduino, insertInterrupt ile kesintiye uğrar. Bir Arduino Programında Paralel Görevler (İplikler) Nasıl Çalıştırılır


Donanım kesintileri

Bu ders için komik bir resim bulamadım, sadece programlama üzerine bir ders buldum ve bu dersin en başında bize neyin ne olduğunu mükemmel bir şekilde açıklıyor. yarıda kesmek. Arduino'daki bir kesinti tam olarak aynı şekilde tanımlanabilir: mikrodenetleyici "her şeyi bırakır", kesme işleyicisinde bir işlev bloğu yürütmeye geçer, bunları yürütür ve ardından tam olarak ana kodda durduğu yere geri döner.

Kesintiler farklıdır, yani kesintilerin kendileri değil, nedenleri: bir kesinti analogdan dijitale dönüştürücüye, zamanlayıcı sayacına veya kelimenin tam anlamıyla bir mikrodenetleyici pinine neden olabilir. Bu tür kesintilere harici denir. donanım ve bugün bundan bahsediyoruz.

Harici donanım kesintisi- Bu, mikrodenetleyicinin pimindeki voltaj değişikliğinden kaynaklanan bir kesintidir. Ana nokta, mikrodenetleyicinin (bilgi işlem çekirdeği) pimi yoklamaz Ve bununla zaman kaybetme, başka bir "demir parçası" pimleme ile meşgul. Pimdeki voltaj değişir değişmez (yani dijital sinyal, +5 uygulandı / +5 kaldırıldı) - mikrodenetleyici bir sinyal alır, her şeyden çıkar, kesmeyi işler ve çalışmaya geri döner. Bu neden gerekli? Çoğu zaman, kesintiler, ana kodu yüklemeden kısa olayları - darbeleri ve hatta sayılarını saymak için algılamak için kullanılır. Bir donanım kesintisi, karmaşık uzun hesaplamalar veya koddaki gecikmeler sırasında bir düğmeye kısa bir süre basmayı veya bir sensör tetiklemesini yakalayabilir, örn. kabaca konuşursak - pim yoklanır ana koda paralel. Ayrıca, kesintiler, neredeyse tüm çevre birimleri kapatıldığında mikrodenetleyiciyi güç tasarrufu modlarından uyandırabilir. Arduino IDE'de donanım kesintileriyle nasıl çalışılacağını görelim.

Arduino'da Kesintiler

Tüm pimlerin "kesemeyeceği" gerçeğiyle başlayalım. evet var böyle bir şey pinChangeInterrupts, ancak ileri düzey derslerde bunun hakkında konuşacağız. Şimdi, donanım kesintilerinin yalnızca belirli pinler oluşturabileceğini anlamamız gerekiyor:

MK / kesme numarası INT 0 İÇ 1 İÇ 2 INT 3 İÇ 4 İÇ 5
ATmega 328/168 (Nano, UNO, Mini) D2 D3
ATmega 32U4 (Leonardo, Mikro) D3 D2 D0 D1 D7
ATmega 2560 (Mega) D2 D3 D21 D20 D19 D18

Tablodan da anlayacağınız gibi kesmelerin pin numarasından farklı olarak kendi numaraları vardır. Bu arada kullanışlı özellik digitalPinToInterrupt(pin), bir pin numarası alır ve kesme numarasını döndürür. Bu fonksiyonu Arduino nano üzerinde 3 rakamı ile besleyerek 1 elde ederiz. Yukarıdaki tabloya göre her şey tembeller için bir fonksiyondur.

İşlev kullanılarak bir kesme bağlanır insertInterrupt(pin, işleyici, mod):

  • toplu iğne- kesme numarası
  • işleyici- kesme işleyici işlevinin adı (bunu kendiniz oluşturmanız gerekir)
  • mod– çalışma "modunu" kesme:
    • DÜŞÜK(düşük) - bir sinyal tarafından tetiklenir DÜŞÜK pim üzerinde
    • YÜKSELEN(büyüme) - pimdeki sinyal değiştiğinde tetiklenir DÜŞÜK Açık YÜKSEK
    • DÜŞME(düşme) - pimdeki sinyal değiştiğinde tetiklenir YÜKSEK Açık DÜŞÜK
    • DEĞİŞTİRMEK(değiştir) - sinyal değiştiğinde tetiklenir (ile DÜŞÜK Açık YÜKSEK ve tersi)

Kesme işlevi kullanılarak da devre dışı bırakılabilir. detachInterrupt(sabitleme), pimin tekrar olduğu yer kesme numarası.

Ayrıca, işlevle kesmeleri genel olarak devre dışı bırakabilirsiniz. kesinti yok() ve bunları tekrar çöz kesintiler (). Onlara karşı dikkatli olun! kesinti yok() aynı zamanda zamanlayıcı kesintilerini durduracak ve tüm zaman fonksiyonları ve PWM üretimi sizin için “kesilecektir”.

Kesmede düğme basmalarının sayıldığı ve ana döngüde 1 saniyelik bir gecikmeyle çıktı verildiği bir örneğe bakalım. düğme ile çalışma normal mod, böyle kaba bir çıktıyı bir gecikmeyle birleştirmek imkansızdır:

Uçucu int sayacı = 0; // sayaç değişkeni geçersiz kurulum() ( Serial.begin(9600); // iletişim için port açıldı // D2 ve GND üzerindeki bağlı düğme pinMode(2, INPUT_PULLUP); \ // D2 kesinti 0 // işleyici - işlev düğmesiTick // FALLING - butona tıklandığında sinyal 0 olur ve onu yakalarız pluginInterrupt(0, buttonTick, FALLING); ) void buttonTick() ( counter++; // + basarak ) void loop() ( Serial.println (sayaç); // çıkış gecikmesi(1000); // bekle )

Bu nedenle, kodumuz gecikme sırasında bile tıklamaları sayar! Harika. Ama ne uçucu? Global bir değişken ilan ettik. tezgah, düğmedeki tıklama sayısını saklayacak. Değişkenin değeri kesmede değişecekse, belirticiyi kullanarak mikrodenetleyiciyi bu konuda bilgilendirmeniz gerekir. uçucu değişkenin veri tipini belirtmeden önce yazılan , aksi halde çalışma hatalı olacaktır. Sadece şunu hatırlamanız gerekiyor: bir kesmede bir değişken değişirse, yapın uçucu.

Birkaç önemli nokta daha:

  • Bir kesmede değiştirilen değişkenler şu şekilde bildirilmelidir: uçucu
  • Kesintilerde aşağıdaki gibi gecikmeler olmaz gecikme()
  • Bir kesmede değerini değiştirmez milis() Ve mikro ()
  • Kesintide, bağlantı noktasına giden çıktı düzgün çalışmıyor ( Serial.print()), ayrıca orada kullanmayın - çekirdeği yükler
  • Kesinti halinde, mümkün olduğunca az hesaplama yapmaya çalışmanız gerekir ve genel olarak "uzun" eylemler - bu, sık kesintilerle MC'nin çalışmasını yavaşlatır! Ne yapalım? Alttarafı oku.

Kesme, hemen işlenmesi gerekmeyen bir olayı yakalarsa, aşağıdaki kesme işleme algoritmasını kullanmak daha iyidir:

  • Kesme işleyicisinde, sadece bayrağı kaldırın
  • Programın ana döngüsünde bayrağı kontrol ediyoruz, yükselirse sıfırlıyor ve gerekli işlemleri yapıyoruz.
geçici boolean intFlag = false; // flag void setup() ( Serial.begin(9600); // iletişim için port açıldı // D2 ve GND pinMode(2, INPUT_PULLUP); // D2 kesintiye uğradı 0 // işleyici - buttonTick işlevi // FALLING - butona basıldığında sinyal 0 olur ve onu yakalarız pluginInterrupt(0, buttonTick, FALLING); ) void buttonTick() ( intFlag = true; // interrupt bayrağını kaldırdı ) void loop() ( if (intFlag) ( intFlag = false; // reset // birşeyler yapın Serial.println("Interrupt!"); ) )

Bu temelde kesintiler hakkında bilmeniz gereken her şey, daha fazlası özel durumlar gelişmiş eğitimlerde ele alacağız.

Video

Ve Arduino fonksiyonunun kullanımına bir örnek vereceğiz. iliştirmeKesme().

Kesinti, işlemciye acil dikkat gerektiren bazı olayların meydana geldiği hakkında bilgi veren bir sinyaldir. İşlemci, mevcut talimatların yürütülmesini keserek ve kontrolü kesme işleyicisine (ISR, Interrupt Service Routine) devrederek bu sinyale yanıt vermelidir. Bir işleyici, kendimize yazdığımız ve olaya yanıt vermesi gereken kodu oraya koyduğumuz normal bir işlevdir.

ISR kesintisine hizmet verdikten sonra, işlev işini sonlandırır ve işlemci mutlu bir şekilde kesintiye uğrayan etkinliklere geri döner - kodu durduğu yerden yürütmeye devam eder. Tüm bunlar otomatik olarak gerçekleşir, bu nedenle görevimiz yalnızca hiçbir şeyi bozmadan ve işlemciyi çok sık dikkatimizi dağıtmaya zorlamadan bir kesme işleyicisi yazmaktır. Devreyi, bağlı cihazların çalışma ilkelerini ve bir kesintinin ne sıklıkla çağrılabileceği, oluşumunun özellikleri nelerdir hakkında bir anlayışa ihtiyacınız olacak. Bütün bunlar, kesintilerle çalışmanın ana zorluğudur.

Donanım ve yazılım kesintileri

Arduino'daki kesintiler birkaç türe ayrılabilir:

  • Donanım kesintileri. Mikroişlemci mimarisi düzeyinde kesinti. Olayın kendisi üretken bir anda gerçekleşebilir. harici cihaz– örneğin, klavyede bir düğmeye basmak, hareket ettirmek Bilgisayar faresi ve benzeri.
  • Yazılım kesintileri. ile programın içinde çalıştırın Özel talimat. Bir kesme işleyicisini çağırmak için kullanılır.
  • Dahili (senkronize) kesmeler. Programın yürütülmesindeki bir değişiklik veya ihlalin bir sonucu olarak (örneğin, geçersiz bir adrese, geçersiz işlem koduna vb. erişirken) dahili bir kesinti meydana gelir.

Neden donanım kesintilerine ihtiyacımız var?

Donanım kesintileri, harici bir olaya yanıt olarak meydana gelir ve harici bir donanım aygıtından gelir. Arduino'da 4 tip donanım kesintisi vardır. Hepsi kesme pimindeki sinyalde farklılık gösterir:

  • Kontak yere çekilir. Kesme işleyicisi, kesme pimi DÜŞÜK olduğu sürece yürütülür.
  • Bir kontaktaki sinyali değiştirme. Bu durumda Arduino, kesme pininde bir sinyal değişikliği meydana geldiğinde bir kesme işleyicisi yürütür.
  • Bir pimde sinyali DÜŞÜK'ten YÜKSEK'e değiştirmek - düşükten yükseğe geçerken, bir kesme işleyicisi yürütülür.
  • Bir pimdeki sinyali YÜKSEK'ten DÜŞÜK'e değiştirme - sinyal yüksekten alçağa değiştiğinde, bir kesme işleyicisi yürütülür.

Kesintiler, zamanlama problemlerini çözmeye yardımcı oldukları için Arduino programlarında kullanışlıdır. Örneğin, UART ile çalışırken kesintiler, her karakterin gelişini takip etmemenizi sağlar. Harici donanım aygıtı bir kesme sinyali verir, işlemci hemen karakteri zamanında yakalayan kesme işleyicisini çağırır. Bu, kesinti olmadan UART'ın durumunu kontrol etmek için harcanacak olan CPU zamanından tasarruf sağlar, bunun yerine, ana programı etkilemeden tüm gerekli eylemler kesme işleyicisi tarafından gerçekleştirilir. Donanım aygıtından herhangi bir özel yetenek gerekmez.

Kesinti çağırmanın ana nedenleri şunlardır:

  • Çıkışın durum değişikliğinin belirlenmesi;
  • Zamanlayıcı kesintisi;
  • SPI, I2C, USART aracılığıyla veri kesintileri;
  • Analogdan dijitale dönüştürme;
  • EEPROM, flash bellek kullanma isteği.

Arduino'da kesintiler nasıl uygulanır?

Bir kesme sinyali alındığında, işlem askıya alınır. Kesinti anında yürütüleceği bildirilen işlevin yürütülmesi başlar. Bildirilen bir işlev, giriş değerlerini kabul edemez ve tamamlandığında değerleri döndüremez. Kesme, ana program döngüsündeki kodun kendisini etkilemez. Arduino'da kesintilerle çalışmak için şunu kullanın: standart fonksiyon iliştirmeKesme().

Farklı Arduino kartlarında kesintilerin uygulanması arasındaki fark

Donanım uygulamasına bağlı olarak belirli model Mikrodenetleyicinin birkaç kesmesi vardır. Arduino Uno kartının 2 ve 3 numaralı pinlerinde 2 kesinti vardır, ancak ikiden fazla çıkış gerekirse kart destekler özel mod pin değiştirme Bu mod, tüm pinler için girişi değiştirerek çalışır. Giriş değiştirme kesme modundaki fark, kesmelerin sekiz pimden herhangi biri üzerinde üretilebilmesidir. Bu durumda işleme daha karmaşık ve daha uzun olacaktır, çünkü her bir kişinin son durumunu takip etmeniz gerekecektir.

Diğer kartlarda kesinti sayısı daha fazladır. Örneğin, kartın harici kesintileri kaldırabilen 6 pini vardır. Tüm Arduino kartları için, insertInterrupt (interrupt, function, mode) fonksiyonu ile çalışırken, Inerrupt 0 argümanı dijital pin 2 ile ilişkilendirilir.

Arduino Dilinde Kesintiler

Şimdi uygulamaya geçelim ve kesmeleri projelerinizde nasıl kullanacağınız hakkında konuşalım.

InsertInterrupt() Sözdizimi

InsertInterrupt işlevi, kesintilerle çalışmak için kullanılır. Bir işleyiciye harici bir kesinti bağlamaya yarar.

Çağrı sözdizimi: insertInterrupt(interrupt, function, mode)

İşlev bağımsız değişkenleri:

  • kesme - çağrılan kesme sayısı (standart 0 - 2. pin için, Arduino Uno kartı için 1 - 3. pin için),
  • işlev - kesintiye uğradığında çağrılan işlevin adı (önemli - işlev herhangi bir değeri kabul etmemeli veya döndürmemelidir),
  • mod, kesmeyi tetiklemenin koşuludur.

Aşağıdaki tetikleme koşulları ayarlanabilir:

  • DÜŞÜK - kontak sıfır değerine sahip olduğunda, düşük bir sinyal seviyesinde gerçekleştirilir. Kesme döngüsel olarak tekrarlanabilir - örneğin, bir düğmeye basıldığında.
  • DEĞİŞİM - ön tarafta, sinyal yüksekten alçağa değiştiğinde veya tam tersi olduğunda kesme gerçekleşir. Herhangi bir sinyal değişiminde bir kez yürütülür.
  • YÜKSELEN - Sinyal DÜŞÜK'ten YÜKSEK'e değiştiğinde bir kesme gerçekleştirin.
  • FALLING - Sinyal YÜKSEK'ten DÜŞÜK'e değiştiğinde bir kez kesme gerçekleştirin.4

Önemli notlar

Kesintilerle çalışırken, aşağıdaki önemli sınırlamalar dikkate alınmalıdır:

  • İşleyici işlevinin yürütülmesi çok uzun sürmemelidir. Mesele şu ki, Arduino aynı anda birden fazla kesintiyi kaldıramaz. İşleyici işleviniz yürütülürken, diğer tüm kesmeler yok sayılır ve önemli olayları kaçırabilirsiniz. Büyük bir şey yapmanız gerekiyorsa, olay işlemeyi ana döngü() döngüsünde iletmeniz yeterlidir. İşleyicide yalnızca olay bayrağını ayarlayabilirsiniz ve döngüde bayrağı kontrol edip işleyebilirsiniz.
  • Değişkenler konusunda çok dikkatli olmalısınız. Akıllı bir C++ derleyicisi, ihtiyaç duymadığı değişkenleri kaldırarak programınızı "yeniden optimize edebilir". Derleyici, bazı değişkenleri bir bölümde ayarlayıp diğerinde kullandığınızı görmeyecektir. Temel veri türleri söz konusu olduğunda bu olasılığı ortadan kaldırmak için şunları kullanabilirsiniz: anahtar kelime uçucu, şu şekilde: uçucu boole durumu = 0. Ancak bu yöntem karmaşık veri yapılarıyla çalışmaz. Bu yüzden her zaman tetikte olmalısınız.
  • Çok sayıda kesme kullanılması önerilmez (6-8'den fazla kesmemeye çalışın). Çok sayıda çeşitli olay, kodun ciddi şekilde karmaşıklaşmasını gerektirir ve bu nedenle hatalara yol açar. Ek olarak, sistemlerde yürütmenin zamansal doğruluğunun olmadığı da anlaşılmalıdır. büyük miktar konuşma kesintisi olamaz - sizin için önemli olan komut çağrıları arasındaki boşluğun ne olduğunu asla tam olarak anlayamayacaksınız.
  • İşleyicilerde Delay() kullanılmamalıdır. Gecikme aralığını belirleme mekanizması zamanlayıcıları kullanır ve ayrıca işleyicinizin engelleyeceği kesintiler üzerinde de çalışırlar. Sonuç olarak herkes herkesi bekleyecek ve program askıda kalacaktır. Aynı nedenle kesme tabanlı iletişim protokolleri (i2c gibi) kullanılamaz.

InsertInterrupt örnekleri

İşe koyulalım ve bakalım en basit örnek kesintileri kullanma. Örnekte, Arduino Uno'nun 2. pimindeki sinyal değiştiğinde, geleneksel olarak LED'i bağlayacağımız 13. pimin durumunu değiştirecek bir işleyici işlevi tanımlıyoruz.

#define PIN_LED 13 uçucu boolean actionState = LOW; void setup() ( pinMode(PIN_LED, OUTPUT); // Kesmeyi ayarlayın // myEventListener işlevi pin 2'de sinyal değiştiğinde çağrılır (kesme 0 pin 2'ye bağlanır) // sinyal değiştiğinde (hayır hangi yönde olursa olsun) insertInterrupt (0, myEventListener, CHANGE); ) void loop() ( // Tüm olay işleme kodu myEventListener işlevinde olacağından döngü işlevinde hiçbir şey yapmıyoruz) void myEventListener() ( actionState != actionState; // / / LED'i açmak veya kapatmak gibi başka şeyler yapın digitalWrite(PIN_LED, actionState); )

Zamanlayıcı ve düğmeler için daha karmaşık kesintilere ve işleyicilerine ilişkin bazı örneklere bakalım.

Sıçrama önleyici düğme kesintileri

Kesinti meydana geldiğinde - kontaklar düğmeye basıldığında sağlam temas kurmadan önce, salınarak birkaç işlem oluştururlar. Gevezelikle başa çıkmanın iki yolu vardır - donanım, yani düğmeye bir kondansatör lehimleyerek ve yazılım.

İşlevi kullanarak gevezelikten kurtulabilirsiniz - düğmenin ilk çalıştırılmasından itibaren geçen süreyi tespit etmenizi sağlar.

If(digitalRead(2)==HIGH) ( //düğmeye basıldığında //Bir önceki basıştan bu yana 100 milisaniyeden fazla zaman geçmişse if (millis() - öncekiMillis >= 100) ( //Giriş saatini hatırla ilk işlem öncekiMillis = millis(); if (led==oldled) ( // butonun durumunun değişmediğini kontrol eder led=!led; )

Bu kod, gevezeliği kaldırmanıza izin verir ve kesintilerde izin verilmeyen gecikme işlevinde olduğu gibi programın yürütülmesini engellemez.

Zamanlayıcı kesintileri

Bir zamanlayıcı, 16 MHz işlemciden elde edilen belirli bir frekansta sayan bir sayaçtır. Almak için frekans bölücüyü yapılandırabilirsiniz. istenen mod hesaplar. Sayacı, belirli bir değere ulaşıldığında kesintiler oluşturacak şekilde de yapılandırabilirsiniz.

Ve zamanlayıcı kesintisi, kesintiyi milisaniyede bir kez yürütmenize izin verir. Arduino'nun 3 zamanlayıcısı vardır - Timer0, Timer1 ve Timer2. Timer0, millis() işlevine iletilen sayacı güncelleyen milisaniyede bir kesinti oluşturmak için kullanılır. Bu zamanlayıcı sekiz bittir ve 0'dan 255'e kadar sayar. Değer 255 olduğunda bir kesme üretilir. Varsayılan olarak, 1kHz'e yakın bir frekans elde etmek için 65'e bir saat bölücü kullanılır.

Karşılaştırma kayıtları, zamanlayıcıdaki durumu ve saklanan verileri karşılaştırmak için kullanılır. Bu örnekte, sayaç 0xAF'ye ulaştığında kod bir kesme oluşturacaktır.

TIMSK0 |= _BV(OCIE0A);

Zamanlayıcı kesme vektörü için bir kesme işleyicisi tanımlamak gerekir. Kesme vektörü, kesme çağrıldığında yürütülecek talimatın konumuna işaretçidir. Birkaç kesinti vektörü, bir kesinti vektör tablosunda birleştirilir. Bu durumda zamanlayıcının adı TIMER0_COMPA_vect olacaktır. Bu işleyicide, döngü () ile aynı eylemler gerçekleştirilecektir.

SIGNAL(TIMER0_COMPA_vect) ( unsigned long currentMillis = millis(); wiper1.Update(currentMillis); if(digitalRead(2) == HIGH) ( wiper2.Update(currentMillis); led1.Update(currentMillis); ) led2.Update( currentMillis); led3.Update(currentMillis); ) // loop() fonksiyonu boş kalacaktır. boşluk döngüsü() ( )

Özetleme

Arduino'da kesinti oldukça karmaşık bir konudur çünkü bir kerede projenin tüm mimarisini düşünmeniz, kodun nasıl yürütüldüğünü, hangi olayların mümkün olduğunu, ana kod kesintiye uğradığında ne olacağını hayal etmeniz gerekir. Bu dil yapısıyla çalışmanın tüm özelliklerini ortaya çıkarmayı amaçlamadık, asıl amaç ana kullanım durumlarını tanıtmaktı. İlerleyen yazılarda kesintilerden daha detaylı bahsetmeye devam edeceğiz.

Genel olarak konuşursak, Arduino gerçek görev paralelleştirmesini veya çoklu okumayı desteklemez. Ancak döngünün her tekrarında mümkündür. döngü() mikrodenetleyiciye bazı ek arka plan görevlerini yürütme zamanının gelip gelmediğini kontrol etmesini söyleyin. Bu durumda, kullanıcıya birkaç görevin aynı anda gerçekleştirildiği anlaşılıyor.

Örneğin, bir LED'i belirli bir frekansta yanıp sönelim ve aynı anda bir piezo yayıcıdan siren gibi yükselen ve alçalan sesler çıkaralım. Hem LED'i hem de piezo yayıcıyı Arduino'ya birden fazla kez bağladık. Devreyi şekildeki gibi kuralım.

Bir LED'i "13" dışında bir dijital pine bağlıyorsanız, 220 ohm'luk bir akım sınırlama direncini unutmayın.

2 LED ve Piezo Buzzer Kontrolü delay() operatörünü kullanarak

Bu taslağı yazalım ve Arduino'ya yükleyelim.

Sabit int sesPin = 3; /* piezoelektrik elemanın bağlı olduğu pin numarası ile bir değişken tanımlayın */ const int ledPin = 13; // LED pin numarası ile bir değişken tanımlayın geçersiz kurulum()( pinMode(sesPin, ÇIKIŞ); // pin 3'ü çıkış olarak bildirin. pinMode(ledPin, ÇIKIŞ); // pin 13'ü çıkış olarak bildirin. } geçersiz döngü() (// Ses kontrolü: tone(soundPin, 700); // 700 Hz gecikme frekansında ses çıkar(200); ton(soundPin, 500); // 500 Hz'de gecikme(200); ton(soundPin, 300); // 300 Hz'de gecikme(200); ton(soundPin, 200); // 200 Hz'de gecikme(200); // LED kontrolü: digitalWrite(ledPin, HIGH); // yangın geciktirme(200); digitalWrite(ledPin, DÜŞÜK); // gecikmeyi söndür(200); }

Açtıktan sonra, taslağın tam olarak ihtiyacımız olan şekilde yürütülmediği görülebilir: siren tamamen çalışana kadar LED yanıp sönmeyecek ve LED'in yanıp sönmesini istiyoruz sırasında bir siren sesi. Buradaki sorun nedir?

Gerçek şu ki, bu sorun olağan şekilde çözülemez. Görevler, mikrodenetleyici tarafından kesinlikle sırayla gerçekleştirilir. Şebeke gecikme() programın çalışmasını belirli bir süre geciktirir ve bu süre dolana kadar programın aşağıdaki komutları yürütülmez. Bu nedenle, döngüdeki her görev için farklı bir yürütme süresi belirleyemeyiz. döngü() programlar. Bu nedenle, bir şekilde çoklu görevi simüle etmeniz gerekir.

3 Paralel Süreçler"delay()" operatörü olmadan

Arduino'nun görevleri sözde paralel olarak gerçekleştireceği seçenek, Arduino geliştiricileri tarafından önerilmiştir. Yöntemin özü, döngünün her tekrarında döngü() LED'i yanıp sönme (bir arka plan görevi gerçekleştirme) zamanının gelip gelmediğini kontrol ederiz. Ve öyleyse, o zaman LED'in durumunu tersine çeviriyoruz. Bu bir tür baypas operatörüdür. gecikme().

Sabit int sesPin = 3; // piezoelektrik elemanın pin numarası ile değişken const int ledPin = 13; // LED pin numaralı değişken const long ledInterval = 200; // LED yanıp sönme aralığı, msn. int ledState = DÜŞÜK; // başlangıç ​​haliİşaretsiz LED'ler uzun öncekiMillis = 0; // önceki LED ateşlemesinin zamanını sakla geçersiz kurulum()( pinMode(sesPin, ÇIKIŞ); // pin 3'ü çıkış olarak ayarlayın. pinMode(ledPin, ÇIKIŞ); // pin 13'ü çıkış olarak ayarlayın. } geçersiz döngü() (// Ses kontrolü: tone(soundPin, 700); gecikme(200); ton(soundPin, 500); gecikme(200); ton(soundPin, 300); gecikme(200); ton(soundPin, 200); gecikme(200); // Yanıp Sönen LED: // Arduino açıldığından beri geçen süre, ms: unsigned long currentMillis = millis(); // Eğer yanıp sönme zamanı geldiyse, if (currentMillis - priorMillis >= ledInterval) ( öncekiMillis = currentMillis; // o zaman şimdiki zamanı sakla if (ledState == LOW) ( // ve LED durumunu tersine çevir ledState = HIGH; ) else ( ledState = LOW; ) digitalWrite(ledPin, ledState); // LED durumunu değiştir ) }

Önemli bir dezavantaj Bu method LED kontrol bloğundan önceki kod bölümünün LED'in yanıp sönme zaman aralığından "ledInterval" daha hızlı yürütülmesi gerektiğidir. Aksi takdirde, yanıp sönme gereğinden daha az sıklıkta gerçekleşecek ve görevlerin paralel yürütülmesinin etkisini alamayacağız. Özellikle çizimimizde sirenin sesindeki değişim süresi 200+200+200+200 = 800 ms olup LED'in yanıp sönme aralığını 200 ms olarak ayarladık. Ancak LED, ayarladığımızın 4 katı olan 800 ms'lik bir sürede yanıp sönecektir.

Genel olarak, kod operatörü kullanıyorsa gecikme(), bu durumda sözde paralelliği simüle etmek zordur, bu nedenle bundan kaçınmak istenir.

Bu durumda siren ses kontrol ünitesinin de saatin gelip gelmediğini kontrol etmesi ve kullanmaması gerekir. gecikme(). Ancak bu, kod miktarını artıracak ve programın okunabilirliğini kötüleştirecektir.

4 ArduinoThread Kitaplığını Kullanma paralel iş parçacıkları oluşturmak için

Sorunu çözmek için harika bir kütüphane kullanacağız Arduino İpliği, kolayca sözde paralel süreçler oluşturmanıza olanak tanır. Benzer şekilde çalışır, ancak zamanı kontrol etmek için kod yazmanıza izin vermez - görevi bu döngüde tamamlayıp tamamlamamanız gerekir. Bu, kod miktarını azaltır ve taslağın okunabilirliğini artırır. Kütüphaneyi çalışırken kontrol edelim.


Her şeyden önce, kütüphane arşivini resmi web sitesinden indirin ve dizine açın. kütüphaneler/ Arduino IDE geliştirme ortamı. Ardından klasörü yeniden adlandırın Arduino Konu ustası V Arduino İpliği.

Bağlantı şeması aynı kalacaktır. Sadece program kodu değişecektir.

#katmak // ArduinoThread kitaplığının bağlanması const int soundPin = 3; // piezoelektrik elemanın pin numarası ile değişken const int ledPin = 13; // LED pin numaralı değişken Thread ledThread = Thread(); // LED'i kontrol etmek için bir dizi oluştur soundThread = Thread(); // siren için bir kontrol akışı oluştur geçersiz kurulum()( pinMode(sesPin, ÇIKIŞ); // pin 3'ü çıkış olarak bildirin. pinMode(ledPin, ÇIKIŞ); // pin 13'ü çıkış olarak bildirin. ledThread.onRun(ledBlink); // iş parçacığına bir görev atayın ledThread.setInterval(1000); // yanıt aralığını ayarlayın, ms soundThread.onRun(sound); // diziye bir görev atayın soundThread.setInterval(20); // yanıt aralığını ayarla, ms } geçersiz döngü() (// LED'i değiştirme zamanının gelip gelmediğini kontrol edin: if (ledThread.shouldRun()) ledThread.run(); // diziyi başlat // Sirenin tonunu değiştirme zamanının gelip gelmediğini kontrol edin: if (soundThread.shouldRun()) soundThread.run(); // iş parçacığını başlat } // LED akısı: geçersiz ledBlink() ( statik bool ledStatus = false; // LED durumu Açık/Kapalı ledStatus = !ledStatus; // durumu tersine çevir digitalWrite(ledPin, ledStatus); // led'i aç/kapat } // Siren akışı: geçersiz ses() ( statik int tonu = 100; // ses tonu, Hz tonu(soundPin, ton); // sireni "ton" Hz'de aç if (ton )

Programda iki iş parçacığı oluşturuyoruz - ledİplik Ve sesİpliği, her biri kendi işlemini gerçekleştirir: biri LED'i yanıp söner, ikincisi siren sesini kontrol eder. Her iş parçacığı için döngünün her yinelemesinde, yürütme zamanının gelip gelmediğini kontrol ederiz. Ulaşırsa, yöntem kullanılarak yürütülmek üzere başlatılır. koşmak(). Ana şey operatörü kullanmamak gecikme(). Daha ayrıntılı açıklamalar kodda verilmiştir.


Kodu Arduino hafızasına yükleyin, çalıştırın. Şimdi her şey tam olması gerektiği gibi çalışıyor!

Genel olarak konuşursak, Arduino gerçek görev paralelleştirmesini veya çoklu okumayı desteklemez. Ancak döngünün her tekrarında mümkündür. döngü() mikrodenetleyiciye bazı ek arka plan görevlerini yürütme zamanının gelip gelmediğini kontrol etmesini söyleyin. Bu durumda, kullanıcıya birkaç görevin aynı anda gerçekleştirildiği anlaşılıyor.

Örneğin, bir LED'i belirli bir frekansta yanıp sönelim ve aynı anda bir piezo yayıcıdan siren gibi yükselen ve alçalan sesler çıkaralım. Hem LED'i hem de piezo yayıcıyı Arduino'ya birden fazla kez bağladık. Devreyi şekildeki gibi kuralım.

Bir LED'i "13" dışında bir dijital pine bağlıyorsanız, 220 ohm'luk bir akım sınırlama direncini unutmayın.

2 LED ve Piezo Buzzer Kontrolü delay() operatörünü kullanarak

Bu taslağı yazalım ve Arduino'ya yükleyelim.

Sabit int sesPin = 3; /* piezoelektrik elemanın bağlı olduğu pin numarası ile bir değişken tanımlayın */ const int ledPin = 13; // LED pin numarası ile bir değişken tanımlayın geçersiz kurulum()( pinMode(sesPin, ÇIKIŞ); // pin 3'ü çıkış olarak bildirin. pinMode(ledPin, ÇIKIŞ); // pin 13'ü çıkış olarak bildirin. } geçersiz döngü() (// Ses kontrolü: tone(soundPin, 700); // 700 Hz gecikme frekansında ses çıkar(200); ton(soundPin, 500); // 500 Hz'de gecikme(200); ton(soundPin, 300); // 300 Hz'de gecikme(200); ton(soundPin, 200); // 200 Hz'de gecikme(200); // LED kontrolü: digitalWrite(ledPin, HIGH); // yangın geciktirme(200); digitalWrite(ledPin, DÜŞÜK); // gecikmeyi söndür(200); }

Açtıktan sonra, taslağın tam olarak ihtiyacımız olan şekilde yürütülmediği görülebilir: siren tamamen çalışana kadar LED yanıp sönmeyecek ve LED'in yanıp sönmesini istiyoruz sırasında bir siren sesi. Buradaki sorun nedir?

Gerçek şu ki, bu sorun olağan şekilde çözülemez. Görevler, mikrodenetleyici tarafından kesinlikle sırayla gerçekleştirilir. Şebeke gecikme() programın çalışmasını belirli bir süre geciktirir ve bu süre dolana kadar programın aşağıdaki komutları yürütülmez. Bu nedenle, döngüdeki her görev için farklı bir yürütme süresi belirleyemeyiz. döngü() programlar. Bu nedenle, bir şekilde çoklu görevi simüle etmeniz gerekir.

3 Paralel Süreçler"delay()" operatörü olmadan

Arduino'nun görevleri sözde paralel olarak gerçekleştireceği seçenek, Arduino geliştiricileri tarafından önerilmiştir. Yöntemin özü, döngünün her tekrarında döngü() LED'i yanıp sönme (bir arka plan görevi gerçekleştirme) zamanının gelip gelmediğini kontrol ederiz. Ve öyleyse, o zaman LED'in durumunu tersine çeviriyoruz. Bu bir tür baypas operatörüdür. gecikme().

Sabit int sesPin = 3; // piezoelektrik elemanın pin numarası ile değişken const int ledPin = 13; // LED pin numaralı değişken const long ledInterval = 200; // LED yanıp sönme aralığı, msn. int ledState = DÜŞÜK; // işaretsiz LED'in ilk durumu uzun öncekiMillis = 0; // önceki LED ateşlemesinin zamanını sakla geçersiz kurulum()( pinMode(sesPin, ÇIKIŞ); // pin 3'ü çıkış olarak ayarlayın. pinMode(ledPin, ÇIKIŞ); // pin 13'ü çıkış olarak ayarlayın. } geçersiz döngü() (// Ses kontrolü: tone(soundPin, 700); gecikme(200); ton(soundPin, 500); gecikme(200); ton(soundPin, 300); gecikme(200); ton(soundPin, 200); gecikme(200); // Yanıp Sönen LED: // Arduino açıldığından beri geçen süre, ms: unsigned long currentMillis = millis(); // Eğer yanıp sönme zamanı geldiyse, if (currentMillis - priorMillis >= ledInterval) ( öncekiMillis = currentMillis; // o zaman şimdiki zamanı sakla if (ledState == LOW) ( // ve LED durumunu tersine çevir ledState = HIGH; ) else ( ledState = LOW; ) digitalWrite(ledPin, ledState); // LED durumunu değiştir ) }

Bu yöntemin önemli bir dezavantajı, LED kontrol bloğundan önceki kod bölümünün "ledInterval" LED'inin yanıp sönme zaman aralığından daha hızlı yürütülmesi gerekmesidir. Aksi takdirde, yanıp sönme gereğinden daha az sıklıkta gerçekleşecek ve görevlerin paralel yürütülmesinin etkisini alamayacağız. Özellikle çizimimizde sirenin sesindeki değişim süresi 200+200+200+200 = 800 ms olup LED'in yanıp sönme aralığını 200 ms olarak ayarladık. Ancak LED, ayarladığımızın 4 katı olan 800 ms'lik bir sürede yanıp sönecektir.

Genel olarak, kod operatörü kullanıyorsa gecikme(), bu durumda sözde paralelliği simüle etmek zordur, bu nedenle bundan kaçınmak istenir.

Bu durumda siren ses kontrol ünitesinin de saatin gelip gelmediğini kontrol etmesi ve kullanmaması gerekir. gecikme(). Ancak bu, kod miktarını artıracak ve programın okunabilirliğini kötüleştirecektir.

4 ArduinoThread Kitaplığını Kullanma paralel iş parçacıkları oluşturmak için

Sorunu çözmek için harika bir kütüphane kullanacağız Arduino İpliği, kolayca sözde paralel süreçler oluşturmanıza olanak tanır. Benzer şekilde çalışır, ancak zamanı kontrol etmek için kod yazmanıza izin vermez - görevi bu döngüde tamamlayıp tamamlamamanız gerekir. Bu, kod miktarını azaltır ve taslağın okunabilirliğini artırır. Kütüphaneyi çalışırken kontrol edelim.


Her şeyden önce, kütüphane arşivini resmi web sitesinden indirin ve dizine açın. kütüphaneler/ Arduino IDE geliştirme ortamı. Ardından klasörü yeniden adlandırın Arduino Konu ustası V Arduino İpliği.

Bağlantı şeması aynı kalacaktır. Sadece program kodu değişecektir.

#katmak // ArduinoThread kitaplığının bağlanması const int soundPin = 3; // piezoelektrik elemanın pin numarası ile değişken const int ledPin = 13; // LED pin numaralı değişken Thread ledThread = Thread(); // LED'i kontrol etmek için bir dizi oluştur soundThread = Thread(); // siren için bir kontrol akışı oluştur geçersiz kurulum()( pinMode(sesPin, ÇIKIŞ); // pin 3'ü çıkış olarak bildirin. pinMode(ledPin, ÇIKIŞ); // pin 13'ü çıkış olarak bildirin. ledThread.onRun(ledBlink); // iş parçacığına bir görev atayın ledThread.setInterval(1000); // yanıt aralığını ayarlayın, ms soundThread.onRun(sound); // diziye bir görev atayın soundThread.setInterval(20); // yanıt aralığını ayarla, ms } geçersiz döngü() (// LED'i değiştirme zamanının gelip gelmediğini kontrol edin: if (ledThread.shouldRun()) ledThread.run(); // diziyi başlat // Sirenin tonunu değiştirme zamanının gelip gelmediğini kontrol edin: if (soundThread.shouldRun()) soundThread.run(); // iş parçacığını başlat } // LED akısı: geçersiz ledBlink() ( statik bool ledStatus = false; // LED durumu Açık/Kapalı ledStatus = !ledStatus; // durumu tersine çevir digitalWrite(ledPin, ledStatus); // led'i aç/kapat } // Siren akışı: geçersiz ses() ( statik int tonu = 100; // ses tonu, Hz tonu(soundPin, ton); // sireni "ton" Hz'de aç if (ton )

Programda iki iş parçacığı oluşturuyoruz - ledİplik Ve sesİpliği, her biri kendi işlemini gerçekleştirir: biri LED'i yanıp söner, ikincisi siren sesini kontrol eder. Her iş parçacığı için döngünün her yinelemesinde, yürütme zamanının gelip gelmediğini kontrol ederiz. Ulaşırsa, yöntem kullanılarak yürütülmek üzere başlatılır. koşmak(). Ana şey operatörü kullanmamak gecikme(). Daha ayrıntılı açıklamalar kodda verilmiştir.


Kodu Arduino hafızasına yükleyin, çalıştırın. Şimdi her şey tam olması gerektiği gibi çalışıyor!

Projenin uygulanması sırasında birkaç kesinti gerekebilir, ancak bunların her biri maksimum önceliğe sahipse, o zaman aslında hiçbir fonksiyon buna sahip olmayacaktır. Aynı nedenle, bir düzineden fazla kesinti kullanılması önerilmez.

İşleyiciler, yalnızca zaman aralıklarına maksimum duyarlılığa sahip olan işlemlere uygulanmalıdır. Unutmayın ki program interrupt işleyicisinde iken diğer tüm interruptlar devre dışıdır. Çok sayıda kesinti, yanıtlarında bir bozulmaya yol açar.

Bir kesmenin aktif olduğu ve diğerlerinin devre dışı bırakıldığı anda, iki kesme önemli nüanslar devre tasarımcısı tarafından dikkate alınacaktır. İlk olarak, kesinti süresi mümkün olduğunca kısa olmalıdır.

Bu, diğer tüm planlanmış kesintilerin kaçırılmamasını sağlayacaktır. İkincisi, bir kesintiyi işlerken programlama kodu diğer kesintilerden etkinlik gerektirmemelidir. Bu engellenmezse, program donacaktır.

Uzun işleme kullanmayın döngü() değişkeni volatile olarak ayarlanmış bir kesme işleyicisi için kod geliştirmek daha iyidir. Programa başka bir işleme gerek olmadığını söyleyecektir.

işlev çağrısı ise Güncelleme() gerekiyorsa, önce durum değişkenini kontrol etmeniz gerekir. Bu, daha fazla işleme gerekip gerekmediğini belirleyecektir.

Zamanlayıcıyı yapılandırmaya başlamadan önce kodu kontrol etmelisiniz. Anduino zamanlayıcıları sınırlı kaynaklara atfedilmelidir, çünkü bunlardan yalnızca üç tane vardır ve en çok performansı göstermek için kullanılırlar. farklı işlevler. Zamanlayıcıların kullanımıyla karıştırırsanız, bir dizi işlem çalışmayı durdurabilir.

Belirli bir zamanlayıcı hangi işlevlerde çalışır?

Arduino Uno mikrodenetleyici için, üç zamanlayıcının her birinin kendi işlemleri vardır.

Bu yüzden Zamanlayıcı0 Beşinci ve altıncı pimdeki PWM'den sorumludur, işlevler milis() , mikro () , gecikme() .

Başka bir zamanlayıcı - zamanlayıcı1, dokuzuncu ve onuncu pimde PWM ile kütüphanelerle birlikte kullanılır WaveHC ve Servo.

Zamanlayıcı2 PWM ile 11 ve 13 pinlerinde ve ayrıca ton.

Devre tasarımcısı, paylaşılan verilerin güvenli kullanımına özen göstermelidir. Sonuçta, bir kesme tüm işlemci işlemlerini bir milisaniye için durdurur ve aralarındaki veri alışverişi döngü() ve kesme işleyicileri kalıcı olmalıdır. Derleyici kendi amacına ulaşmak için bir durum ortaya çıkabilir. maksimum performans kodu optimize etmeye başlayacak.

Bu işlemin sonucu, ana kod değişkenlerinin bir kopyasının, bunlara maksimum erişim hızını sağlayacak şekilde kayıt defterine kaydedilmesi olacaktır.

Bu işlemin dezavantajı, gerçek değerlerin kayıtlı kopyalarla değiştirilmesi olabilir, bu da işlevsellik kaybına yol açabilir.

Bunun olmasını önlemek için bir değişken kullanmanız gerekir. değişken , bu da gereksiz optimizasyonların önlenmesine yardımcı olacaktır. Güncellemeler için döngü gerektiren büyük diziler kullanırken, bu güncellemeler sırasında kesintileri devre dışı bırakmanız gerekir.