Mikrodenetleyicilerde kesinti kullanımı ve FSM örneği


9

İlk Soru

Mikrodenetleyicilerdeki kesintilerin ele alınması hakkında genel bir sorum var. MSP430 kullanıyorum, ancak sorunun diğer UC'lere genişletilebileceğini düşünüyorum. Kod boyunca sık sık kesintileri etkinleştirmek / devre dışı bırakmak için iyi bir uygulama olup olmadığını bilmek istiyorum. Yani, kesintilere karşı hassas olmayacak bir kod bölümüm varsa (veya daha da kötüsü, herhangi bir nedenle kesintileri dinlememelidir), daha iyi mi:

  • Kesmeleri önce devre dışı bırakın ve ardından kritik bölümden sonra yeniden etkinleştirin.
  • İlgili ISR'nin içine bir bayrak yerleştirin ve (kesmeyi devre dışı bırakmak yerine), kritik bölümden önce bayrağı false olarak ayarlayın ve hemen ardından sıfırlayın. ISR kodunun yürütülmesini önlemek için.
  • Ne ikisi de, bu yüzden önerilerinizi bekliyoruz!

Güncelleme: Kesmeler ve Durum Grafikleri

Belirli bir durum sunacağım. 4 bloktan oluşan bir durum grafiği uygulamak istediğimizi varsayalım:

  1. Geçişler / Etkisi.
  2. Çıkış koşulları.
  3. Giriş etkinliği.
  4. Etkinlik yapın.

Bir profesörün bize üniversitede öğrettiği budur. Muhtemelen, bunu yapmanın en iyi yolu bu şemayı takip etmektir:

while(true) {

  /* Transitions/Effects */
  //----------------------------------------------------------------------
  next_state = current_state;

  switch (current_state)
  {
    case STATE_A:
      if(EVENT1) {next_state = STATE_C}
      if(d == THRESHOLD) {next_state = STATE_D; a++}
      break;
    case STATE_B:
      // transitions and effects
      break;
    (...)
  }

  /* Exit activity -> only performed if I leave the state for a new one */
  //----------------------------------------------------------------------
  if (next_state != current_state)
  {
    switch(current_state)
    {
      case STATE_A:
        // Exit activity of STATE_A
        break;
      case STATE_B:
        // Exit activity of STATE_B
        break;
        (...)
    }
  }

  /* Entry activity -> only performed the 1st time I enter a state */
  //----------------------------------------------------------------------
  if (next_state != current_state)
  {
    switch(next_state)
    {
      case STATE_A:
        // Entry activity of STATE_A
        break;
      case STATE_B:
        // Entry activity of STATE_B
        break;
      (...)
    }
  }

  current_state = next_state;

  /* Do activity */
  //----------------------------------------------------------------------
  switch (current_state)
  {
    case STATE_A:
      // Do activity of STATE_A
      break;
    case STATE_B:
      // Do activity of STATE_B
      break;
    (...)
  }
}

Diyelim ki STATE_A, bir dizi düğmeden gelen kesintiye karşı duyarlı olmak istediğimi varsayalım (debouce sistemi vb. İle). Birisi bu düğmelerden birine bastığında, bir kesme oluşturulur ve giriş bağlantı noktasıyla ilgili bayrak bir değişkene kopyalanır buttonPressed. Geri dönme bir şekilde 200 ms'ye ayarlanmışsa (bekçi sayacı, zamanlayıcı, sayaç, ...), buttonPressed200 ms'den önce yeni bir değerle güncellenemeyeceğinden eminiz . Sana sorduğum şey bu (ve kendimi :) tabii ki)

STATE_AAyrılmadan önce DO etkinliğinde kesintiyi etkinleştirmem ve devre dışı bırakmam gerekir mi?

/* Do activity */
//-------------------------------------
switch (current_state)
{
  case STATE_A:
    // Do activity of STATE_A
    Enable_ButtonsInterrupt(); // and clear flags before it
    // Do fancy stuff and ...
    // ... wait until a button is pressed (e.g. LPM3 of the MSP430)
    // Here I have my buttonPressed flag ready!
    Disable_ButtonsInterrupt();
    break;
  case STATE_B:
    // Do activity of STATE_B
    break;
  (...)
}

Bir sonraki yinelemede blok 1'i (geçiş / efektler) bir daha yürüttüğümden emin olduğum bir şekilde, geçişler boyunca kontrol edilen koşulların, önceki değerinin üzerine yazılan bir sonraki kesmeden gelmediğinden buttonPressedeminim. (250 ms geçmesi gerektiğinden bunun olması imkansız olsa da) gerekir.


3
Durumunuz hakkında daha fazla bilgi sahibi olmadan bir tavsiye yapmak zor. Ancak bazen gömülü sistemlerde kesintileri devre dışı bırakmak gerekir. Kesmelerin kaçırılmaması için kesilmelerin yalnızca kısa sürelerle devre dışı bırakılması tercih edilir. Tüm kesmeler yerine yalnızca belirli kesmeleri devre dışı bırakmak mümkün olabilir. Açıkladığınız gibi ISR'nin içindeki bayrağı kullandığımı hatırlamıyorum, bu yüzden en iyi çözümünüz olup olmadığına kuşkuluyum.
kkrambo

Yanıtlar:


17

İlk taktik, genel bellenimi tasarlamak ve böylece kesintilerin herhangi bir zamanda gerçekleşmesini sağlamaktır. Ön plan kodunun bir atomik diziyi yürütebilmesi için kesmeleri kapatmak zorunda kalmadan dikkatli bir şekilde yapılmalıdır. Etrafında genellikle mimari bir yol vardır.

Ancak, makine size hizmet etmek için orada, tersi değil. Genel kurallar sadece kötü programcıların gerçekten kötü kod yazmasını engellemek içindir. Makinenin nasıl çalıştığını tam olarak anlamak ve daha sonra istenen görevi yerine getirmek için bu yetenekleri kullanmak için iyi bir yol tasarlamak daha iyidir.

Döngüler veya bellek konumlarında gerçekten sıkı olmadıkça (kesinlikle olabilir), aksi takdirde kodun netliği ve sürdürülebilirliği için optimize etmek istediğinizi unutmayın. Örneğin, bir saat işareti kesilmesinde 32 bitlik bir sayacı güncelleyen 16 bitlik bir makineniz varsa, ön plan kodu sayacı okuduğunda sayacın iki yarısının tutarlı olduğundan emin olmanız gerekir. Bunun bir yolu kesintileri kapatmak, iki kelimeyi okumak ve daha sonra kesintileri tekrar açmaktır. Kesme gecikmesi kritik değilse, bu tamamen kabul edilebilir.

Düşük kesme gecikmesi olması durumunda, örneğin, yüksek kelimeyi okuyabilir, düşük kelimeyi okuyabilir, yüksek kelimeyi tekrar okuyabilir ve değiştiyse tekrarlayabilirsiniz. Bu, ön plan kodunu biraz yavaşlatır, ancak kesinti gecikmesine neden olmaz. Çeşitli küçük numaralar var. Bir diğeri, kesme yordamında sayacın artırılması gerektiğini belirten bir bayrak ayarlamak, ardından bunu ön plan kodundaki ana olay döngüsünde yapmak olabilir. Sayaç kesme oranı yeterince yavaşsa, olay döngüsü bayrak yeniden ayarlanmadan önce artışı yapacak şekilde çalışır.

Veya bayrak yerine tek sözcüklük bir sayaç kullanın. Ön plan kodu, sistemi güncellediği son sayacı içeren ayrı bir değişkeni tutar. Bir seferde kaç keneyi işlemesi gerektiğini belirlemek için canlı sayacın imzasız bir çıkarımını yapar. Bu, ön plan kodunun bir seferde en fazla 2 N -1 olayını kaçırmasına izin verir; burada N, ALU'nun atomik olarak işleyebileceği yerel bir kelimedeki bit sayısıdır.

Her yöntemin kendine özgü avantaj ve dezavantajları vardır. Tek bir doğru cevap yok. Yine, makinenin nasıl çalıştığını anlayın , o zaman başparmak kurallarına ihtiyacınız olmayacak.


7

Kritik bir bölüme ihtiyacınız varsa, kritik bölümünüzü koruyan işlemin atomik olduğundan ve kesilemeyeceğinden emin olmalısınız.

Bu nedenle, en çok tek bir işlemci komutuyla (ve bir derleyici içsel işlevi kullanılarak çağrılan) işlenen kesintileri devre dışı bırakmak, alabileceğiniz en güvenli bahislerden biridir.

Sisteminize bağlı olarak, bununla ilgili bazı sorunlar olabilir, örneğin bir kesinti kaçırılabilir. Bazı mikrodenetleyiciler, küresel kesme etkinliğinin durumuna bakılmaksızın bayrakları ayarlar ve kritik bölümden ayrıldıktan sonra kesmeler yürütülür ve gecikir. Ancak, yüksek bir hızda gerçekleşen bir kesintiniz varsa, kesintileri çok uzun bir süre bloke ederseniz, kesintinin ikinci bir kez daha kaçırılmasını sağlayabilirsiniz.

Kritik bölümünüz yalnızca bir kesmenin yürütülmemesini gerektirir, ancak diğerinin yürütülmesi gerekiyorsa, diğer yaklaşım uygulanabilir görünmektedir.

Kendimi kesme hizmeti rutinlerini mümkün olduğunca kısa programlarken buluyorum. Böylece normal bir program rutini sırasında kontrol edilen bir bayrak koydular. Ancak bunu yaparsanız, bu bayrağın kurulmasını beklerken yarış koşullarına dikkat edin.

Pek çok seçenek var ve bunun tek bir doğru yanıtı yok, bu dikkatli bir tasarım gerektiren ve diğer şeylerden biraz daha fazla düşünmeyi hak eden bir konudur.


5

Bir kod bölümünün kesintisiz çalışması gerektiğini belirlediyseniz, olağandışı durumlar dışında, kesintileri görevi tamamlamak için mümkün olan en düşük süre boyunca devre dışı bırakmanız ve daha sonra yeniden etkinleştirmeniz gerekir.

İlgili ISR'nin içine bir bayrak yerleştirin ve (kesmeyi devre dışı bırakmak yerine), kritik bölümden önce bayrağı false olarak ayarlayın ve hemen ardından sıfırlayın. ISR kodunun yürütülmesini önlemek için.

Bu yine de bir kesilmeye, bir kod sıçramasına, bir çeke, sonra bir dönüşe izin verir. Kodunuz bu kadar kesintiyi kaldırabiliyorsa, muhtemelen ISR'yi kontrolü gerçekleştirmek yerine bir bayrak ayarlayacak şekilde tasarlamanız gerekir - daha kısa olacaktır - ve normal kod rutininizdeki bayrağı işleyebilirsiniz. Birisi kesmeye çok fazla kod koyar gibi geliyor ve kesmeyi normal kodda yapılması gereken daha uzun eylemler gerçekleştirmek için kullanıyor.

Kesintilerin uzun olduğu kodla uğraşıyorsanız, önerdiğiniz gibi bir bayrak sorunu çözebilir, ancak kesmedeki aşırı kodu ortadan kaldırmak için kodu yeniden faktörlendiremiyorsanız kesmeleri devre dışı bırakmanız daha iyi olacaktır. .

Bayrak yolunu yapmanın ana sorunu, daha sonra yankıları olabilen kesinti hiç yürütmemenizdir. Çoğu mikrodenetleyici, kesmeler genel olarak devre dışı bırakılsa bile kesme bayraklarını izler ve kesmeleri yeniden etkinleştirdiğinizde kesmeyi yürütür:

  • Kritik bölüm sırasında herhangi bir kesinti meydana gelmezse, hiçbiri daha sonra yürütülmez.
  • Kritik bölüm sırasında bir kesinti meydana gelirse, bir bölüm daha sonra yürütülür.
  • Kritik bölüm sırasında birden fazla kesinti meydana gelirse, yalnızca bir bölüm sonra yürütülür.

Sisteminiz karmaşıksa ve kesintileri daha eksiksiz izlemek zorundaysa, kesintileri izlemek ve buna göre çalışmak için daha karmaşık bir sistem tasarlamanız gerekir.

Ancak, kesintilerinizi her zaman işlevlerini yerine getirmek için gereken minimum işi yapacak şekilde tasarlar ve diğer her şeyi düzenli işlemeye ertelerseniz, kesintiler diğer kodunuzu nadiren olumsuz etkiler. Kesinti gerekirse verileri yakalayın veya bırakın veya çıkışları vb. Gerektiği gibi ayarlayın / sıfırlayın, daha sonra ana kod yolunun kesintileri etkileyen bayraklara, arabelleklere ve değişkenlere dikkat etmesini sağlayın, böylece uzun işlem ana döngüde yapılabilir, kesinti yerine.

Bu, kesintisiz bir kod bölümüne ihtiyaç duyabileceğiniz çok, çok az durum hariç hepsini ortadan kaldırmalıdır.


Çalıştığım durumu daha iyi açıklamak için yazıyı güncelledim :)
Umberto D.

1
Örnek kodunuzda, gerekli olmadığında belirli düğme kesmesini devre dışı bırakmayı ve gerektiğinde etkinleştirmeyi düşünürüm. Bunu sık sık yapmak, tasarım gereği bir sorun değildir. Daha sonra gerekirse koda başka kesintiler ekleyebilmek için genel kesmeleri açık bırakın. Alternatif olarak, A durumuna gittiğinizde bayrağı sıfırlayın ve aksi takdirde yok sayın. Düğmeye basılır ve bayrak ayarlanırsa, kimin umrunda? A durumuna dönene kadar yok sayın
Adam Davis

Evet! Bu bir çözüm olabilir çünkü gerçek tasarımda sıklıkla global kesmeler etkinken LPM3'e (MSP430) giderim ve bir kesinti tespit edilir edilmez yürütmeyi sürdürerek LPM3'ten çıkarım. Yani bir çözüm, kodun ikinci bölümünde bildirildiğini düşündüğünüz bir çözümdür: ihtiyaç duyan bir durumun do aktivitesini gerçekleştirmeye başlar başlamaz kesmeleri etkinleştirin ve geçişler bloğuna gitmeden önce devre dışı bırakın. Başka bir olası çözüm, "Etkinlik bloğu yap" durumundan ayrılmadan hemen önce kesmeyi devre dışı bırakmak ve bir süre sonra (ne zaman?) Yeniden etkinleştirmek olabilir?
Umberto D.

1

Açıkladığınız gibi ISR'nin içine bir bayrak koymak, kesmeyi tetikleyen olayı göz ardı ettiğiniz için muhtemelen işe yaramaz. Kesintileri global olarak devre dışı bırakmak genellikle daha iyi bir seçimdir. Diğerlerinin söylediği gibi, bunu çok sık yapmak zorunda kalmamalısınız. Tek bir komutla yapılan herhangi bir okuma veya yazma işleminin, komut yürütüldüğünden veya yürütmediği için korunması gerekmediğini unutmayın.

Çoğu, ne tür kaynakları paylaşmaya çalıştığınıza bağlıdır. ISR'den ana programa veri besliyorsanız (veya tersi), FIFO tamponu gibi bir şey uygulayabilirsiniz. Tek atomik işlem, okuma ve yazma işaretleyicilerini güncellemek olacaktır, bu da kesintiler devre dışı bırakıldığında harcanan süreyi en aza indirir.


0

Dikkate almanız gereken ince bir fark var. Bir kesilmenin işlenmesini "ertelemeyi" veya "yok saymayı" ve kesmeyi seçebilirsiniz.

Genellikle kodda kesmeyi devre dışı bıraktığımızı söyleriz. Donanım işlevleri nedeniyle muhtemelen ne olacağı, kesintileri etkinleştirdiğimizde tetikleyeceğidir. Bu bir şekilde kesintiyi geciktiriyor. Sistemin güvenilir bir şekilde çalışması için bu kesintilerin ele alınmasını geciktirebileceğimiz maksimum süreyi bilmemiz gerekir. Ardından, kesintilerin devre dışı bırakıldığı tüm vakaların daha kısa sürede tamamlanacağından emin olun.

Bazen kesintileri göz ardı etmek isteriz. En iyi yol kesmeyi donanım düzeyinde engellemektir. Hangi girişlerin kesinti üretmesi gerektiğini söyleyebileceğimiz bir kesinti denetleyicisi veya benzeri genellikle vardır.

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.