Mikrodenetleyici Uyku Yarışı Durumu


11

Aşağıdaki kodu çalıştıran bir mikro denetleyici verildi:

volatile bool has_flag = false;

void interrupt(void) //called when an interrupt is received
{
    clear_interrupt_flag(); //clear interrupt flag
    has_flag = true; //signal that we have an interrupt to process
}

int main()
{
    while(1)
    {
        if(has_flag) //if we had an interrupt
        {
            has_flag = false; //clear the interrupt flag
            process(); //process the interrupt
        }
        else
            sleep(); //place the micro to sleep
    }
}

Durumun yanlış olarakif(has_flag) değerlendirildiğini ve uyku talimatını uygulamak üzere olduğumuzu varsayalım . Uyku talimatını uygulamadan hemen önce bir kesinti alırız. Kesmeden ayrıldıktan sonra uyku talimatını uygularız.

Bu yürütme sırası istenmez, çünkü:

  • Mikrodenetleyici uyanmak ve çağırmak yerine uykuya daldı process().
  • Bundan sonra herhangi bir kesinti alınmazsa mikrodenetleyici asla uyanamayabilir.
  • İçin yapılan çağrı process(), bir sonraki kesintiye kadar ertelenir.

Bu yarış koşulunun oluşmasını önlemek için kod nasıl yazılabilir?

Düzenle

ATMega gibi bazı mikrodenetleyiciler, bu durumun ortaya çıkmasını önleyen bir Uyku etkin bitine sahiptir (bunu işaret ettiğiniz için Kvegaoro'ya teşekkür ederiz). JRoberts bu davranışı örnekleyen bir örnek uygulama sunmaktadır.

PIC18'lerde olduğu gibi diğer mikrolarda bu bit yoktur ve sorun hala devam eder. Bununla birlikte, bu mikrolar, küresel kesme etkinleştirme bitinin ayarlanıp ayarlanmadığına bakılmaksızın kesmelerin yine de çekirdeği uyandıracak şekilde tasarlanmıştır (bunu işaret ettiğiniz için teşekkür ederim supercat). Bu tür mimariler için çözüm, uyumadan hemen önce küresel kesintileri devre dışı bırakmaktır. Bir kesme uyku talimatını uygulamadan hemen önce tetiklenirse, kesme işleyicisi yürütülmez, çekirdek uyanır ve genel kesmeler yeniden etkinleştirildikten sonra kesme işleyicisi yürütülür. Sahte kodda, uygulama şöyle görünecektir:

int main()
{
    while(1)
    {
        //clear global interrupt enable bit.
        //if the flag tested below is not set, then we enter
        //sleep with the global interrupt bit cleared, which is
        //the intended behavior.
        disable_global_interrupts();

        if(has_flag) //if we had an interrupt
        {
            has_flag = false; //clear the interrupt flag
            enable_global_interrupts();  //set global interrupt enable bit.

            process(); //process the interrupt
        }
        else
            sleep(); //place the micro to sleep
    }
}

Bu pratik veya teorik bir soru mu?
AndrejaKo

teoride her ms (bir kabul edilebilir değer girin) ms bir kez uyanır ve sonra hiçbir şey yapması gerekmiyorsa uykuya geri döner bir zamanlayıcı kullanın.
Grady Player

1
Ben interrupt_flagolarak yapacağım intve her kesinti olduğunda bunu arttıracağım. Sonra değiştirmek if(has_flag)için while (interrupts_count)ve daha sonra uyku. Bununla birlikte, while döngüsünden çıktıktan sonra kesme meydana gelebilir. Bu bir sorunsa, kesmenin kendisindeki işlem mi yapılır?
angelatlarge

1
Peki hangi mikro çalıştırdığınıza bağlıdır .. Eğer bir ATmega328 olsaydı, kesinti sırasında uyku modunu devre dışı bırakabilirsiniz, bu yüzden tarif ettiğiniz yarış durumu gerçekleşirse uyku fonksiyonu geçersiz kılınır, tekrar döngüye girer ve işlersiniz küçük bir gecikme ile kesinti. Ancak, maksimum gecikmenize eşit veya daha az bir aralıkta uyanmak için bir zamanlayıcı kullanmak da harika bir çözüm olacaktır
Kvegaoro

1
@TRISAbits: PIC 18x'te cevabımda tarif ettiğim yaklaşım gayet iyi çalışıyor (bu kısmı kullanırken normal tasarımım).
supercat

Yanıtlar:


9

Bu durumda genellikle bir tür donanım desteği vardır. Örneğin, AVR'lerin seikesintileri etkinleştirme talimatı, aşağıdaki talimat tamamlanıncaya kadar etkinleştirme hatalarını ortadan kaldırır . Bununla birlikte şunları yapabilirsiniz:

forever,
   interrupts off;
   if has_flag,
      interrupts on;
      process interrupt;
   else,
      interrupts-on-and-sleep;    # won't be interrupted
   end
end

Örnekte gözden kaçırılacak kesinti bu durumda işlemci uyku sırasını tamamlayana kadar kapalı kalır.


Mükemmel cevap! Sağladığınız algoritma bir AVR'de gerçekten iyi çalışıyor. Önerin için teşekkürler.
TRISAbits 21:13

3

Birçok mikro denetleyicide, belirli kesme nedenlerini etkinleştirmeye veya devre dışı bırakmaya ek olarak (genellikle bir kesme denetleyici modülü içinde), CPU çekirdeğinde kesme isteklerinin kabul edilip edilmeyeceğini belirleyen bir ana bayrak bulunur. Bir kesme isteği çekirdeğe ulaşırsa, çekirdeğin gerçekten kabul etmek isteyip istemediğine bakılmaksızın, birçok mikrodenetleyici uyku modundan çıkar.

Böyle bir tasarımda, güvenilir uyku davranışı elde etmek için basit bir yaklaşım, ana döngünün bir bayrağı temizlemesini ve ardından işlemcinin uyanık olması için herhangi bir neden olup olmadığını kontrol etmesini sağlamaktır. Bu süre içinde, bu nedenlerden herhangi birini etkileyebilecek herhangi bir kesinti bayrağı ayarlamalıdır. Ana döngü uyanık kalmak için herhangi bir neden bulamadıysa ve bayrak ayarlanmadıysa, ana döngü kesintileri devre dışı bırakmalı ve bayrağı tekrar kontrol etmelidir [belki birkaç NOP talimatından sonra bir kesintinin beklemede olması mümkünse bir devre dışı bırakma-kesme talimatı sırasında, aşağıdaki talimatla ilişkili işlenen getirme işlemi gerçekleştirildikten sonra işlenebilir]. Bayrak hala ayarlanmamışsa, uyu.

Bu senaryoda, ana döngü kesintileri devre dışı bırakmadan önce gerçekleşen bir kesme, son testten önce bayrağı ayarlar. Uyku talimatı verilmeden önce servise gönderilmek için çok geç bekleyen bir kesinti, işlemcinin uykuya geçmesini önleyecektir. Her iki durum da gayet iyi.

Çıkışta uyku bazen kullanmak için iyi bir modeldir, ancak tüm uygulamalar gerçekten "uymaz". Örneğin, enerjiyi verimli kullanan bir LCD'ye sahip bir cihaz en kolay aşağıdaki gibi kodla programlanabilir:

void select_view_user(int default_user)
{
  int current_user;
  int ch;
  current_user = default_user;
  do
  {
    lcd_printf(0, "User %d");
    lcd_printf(1, ...whatever... );
    get_key();
    if (key_hit(KEY_UP)) {current_user = (current_user + 1) % MAX_USERS};
    if (key_hit(KEY_DOWN)) {current_user = (current_user + MAX_USERS-1) % MAX_USERS};
    if (key_hit(KEY_ENTER)) view_user(current_user);
  } while(!key_hit(KEY_EXIT | KEY_TIMEOUT));
}

Hiçbir düğmeye basılmazsa ve başka hiçbir şey olmuyorsa, get_keyyöntemin yürütülmesi sırasında sistemin uyumaması için hiçbir neden yoktur . Anahtarların bir kesmeyi tetiklemesi ve durum makinesi aracılığıyla tüm kullanıcı arabirimi etkileşimini yönetmesi mümkün olsa da, yukarıdaki gibi kod genellikle küçük mikro denetleyicilere özgü yüksek modal kullanıcı arabirimi akışlarını işlemenin en mantıklı yoludur.


Süper cevap için teşekkürler supercat. Kesintileri devre dışı bırakmak ve sonra uykuya dalmak, çekirdek, global kesme bitinin ayarlanmış / açık olup olmadığına bakılmaksızın herhangi bir kesme kaynağından uyanması koşuluyla harika bir çözümdür. PIC18 interrupt donanım şemasına bir göz attım ve bu çözüm işe yarayacaktı.
TRISAbits 21:13

1

Mikro kesintiye uyanmak için programlayın.

Belirli ayrıntılar kullandığınız mikroma bağlı olarak değişir.

Ardından main () rutini değiştirin:

int main()
{
    while(1)
    {
        sleep();
        process(); //process the interrupt
    }
}

1
Soruda kesme sırasında uyanma mimarisi olduğu varsayılmaktadır. Cevabınızın soru / sorunu çözdüğünü sanmıyorum.
Mart'ta

@angelatlarge Puanı kabul edildi. Ben yardımcı olduğunu düşünüyorum bir kod örneği ekledim.
20:13

@ jwygralak67: Öneri için teşekkürler, ancak sağladığınız kod sorunu yalnızca process () yordamına taşır, bu da artık process () gövdesini çalıştırmadan önce kesintinin olup olmadığını test etmek zorundadır.
TRISAbits 20:13

2
Kesinti gerçekleşmemişse, neden uyanıkız?
JRobert

1
@JRobert: Önceki bir kesintiden uyanabilir, işlem () rutinini tamamlayabiliriz ve if (has_flag) testini bitirdiğimizde ve uykudan hemen önce başka bir kesinti elde edebiliriz, bu da açıkladığım soruna neden olur. soru.
TRISAbits
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.