AVR Watchdog'u normal ISR gibi kullanın


17

Başımı ATTinyX5 serisindeki bekçi zamanlayıcısının etrafına sarmaya çalışıyorum. Bu yüzden okuduğum şeyler programın N saniye belirli bir şey yapmasını sağlamak için kullanabileceğiniz gibi görünüyordu ama gerçekten nasıl olduğunu asla göstermedi. Diğerleri ise, kod sıfırlamasında bir şey bu arada sayılmazsa (sadece "normal" kullanım gibi görünüyor) çipi sıfırlayacak gibi görünüyordu.

WDT'yi TIMER1_COMPA_vect veya benzeri gibi kullanmanın herhangi bir yolu var mı? Ben 1 saniyelik bir zaman aşımı modu olduğunu fark ettim ve gerçekten benim kod her 1 saniyede bir şey yapmak için kullanmak isterdim (ve tercihen arasında uyku).

Düşünceler?

* Güncelleme: * Sorulduğu için, atıfta bulunduğum şey ATTinyX5 Veri Sayfası'nın 8.4 . Tam olarak anladığımdan değil, bu benim sorunum ...


1
Kutunun dışında düşünmek için +1. AVR için veri sayfasına bir bağlantı eklerseniz ekstra başparmak yukarıya.
jippie

Yanıtlar:


23

Kesinlikle yapabilirsiniz. Veri sayfasına göre, gözlemci zamanlayıcısı, MCU'yu sıfırlamak veya tetiklendiğinde kesintiye neden olmak için ayarlanabilir. Kesinti olasılığıyla daha fazla ilgileniyorsunuz gibi görünüyor.

WDT'nin kurulumu normal bir Zamanlayıcıdan daha kolaydır, aynı nedenden dolayı daha az kullanışlıdır: daha az seçenek. Dahili olarak kalibre edilmiş 128kHz saatte çalışır, yani zamanlaması MCU'nun ana saat hızından etkilenmez. Ayrıca bir uyanma kaynağı sağlamak için en derin uyku modlarında çalışmaya devam edebilir.

Birkaç veri sayfası örnekleri yanı sıra (C) kullandığım bazı kod üzerinden gidecek.

Dahil Edilen Dosyalar ve Tanımlar

Başlamak için, muhtemelen işlerin çalışması için aşağıdaki iki başlık dosyasını dahil etmek isteyeceksiniz:

#include <avr/wdt.h>        // Supplied Watch Dog Timer Macros 
#include <avr/sleep.h>      // Supplied AVR Sleep Macros

Ayrıca, aşağıdaki gibi standart AVR başlıklarından birinde tanımlanan Makro <_BV (BIT)> kullanıyorum (size daha tanıdık gelebilir):

#define _BV(BIT)   (1<<BIT)

Kod Başlangıcı

MCU ilk kez başlatıldığında, tipik olarak G / Ç, ayar zamanlayıcıları vb. kararsız bir döngü.

if(MCUSR & _BV(WDRF)){            // If a reset was caused by the Watchdog Timer...
    MCUSR &= ~_BV(WDRF);                 // Clear the WDT reset flag
    WDTCSR |= (_BV(WDCE) | _BV(WDE));   // Enable the WD Change Bit
    WDTCSR = 0x00;                      // Disable the WDT
}

WDT Kurulumu

Ardından, çipin geri kalanını ayarladıktan sonra WDT'yi tekrar yapın. WDT'yi kurmak için "zamanlanmış bir dizi" gerekir, ancak bunu yapmak gerçekten çok kolaydır ...

// Set up Watch Dog Timer for Inactivity
WDTCSR |= (_BV(WDCE) | _BV(WDE));   // Enable the WD Change Bit
WDTCSR =   _BV(WDIE) |              // Enable WDT Interrupt
           _BV(WDP2) | _BV(WDP1);   // Set Timeout to ~1 seconds

Elbette, bu kod sırasında kesintileriniz devre dışı bırakılmalıdır. Daha sonra bunları yeniden etkinleştirdiğinizden emin olun!

cli();    // Disable the Interrupts
sei();    // Enable the Interrupts

WDT Kesinti Hizmeti Rutini Endişelenmeniz gereken bir sonraki şey WDT ISR'yi kullanmaktır. Bu şu şekilde yapılır:

ISR(WDT_vect)
{
  sleep_disable();          // Disable Sleep on Wakeup
  // Your code goes here...
  // Whatever needs to happen every 1 second
  sleep_enable();           // Enable Sleep Mode
}

MCU Uyku

MCU'yu WDT ISR'nin içine uyku moduna geçirmek yerine, ISR'nin sonunda uyku modunu etkinleştirmenizi ve ardından ANA programın MCU'yu uyku moduna geçirmesini öneririm. Bu şekilde, program aslında uyumadan önce ISR'yi terk eder ve uyanır ve doğrudan WDT ISR'ye geri döner.

// Enable Sleep Mode for Power Down
set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // Set Sleep Mode: Power Down
sleep_enable();                     // Enable Sleep Mode  
sei();                              // Enable Interrupts 

/****************************
 *  Enter Main Program Loop  *
 ****************************/
 for(;;)
 {
   if (MCUCR & _BV(SE)){    // If Sleep is Enabled...
     cli();                 // Disable Interrupts
     sleep_bod_disable();   // Disable BOD
     sei();                 // Enable Interrupts
     sleep_cpu();           // Go to Sleep

 /****************************
  *   Sleep Until WDT Times Out  
  *   -> Go to WDT ISR   
  ****************************/

   }
 }

WOW ... çok detaylı. Teşekkürler! Ana döngüyü gösterdiğiniz uyku bölümü ile biraz kafam karıştı (eğer (MCUCR & _BV (SE)) {// Uyku Etkinse ... vb.) Ana görünümde neden kesintileri sürekli olarak devre dışı bırakır ve etkinleştirir. Ve bu bölümün en üstündeki kısım (set_sleep_mode (SLEEP_MODE_PWR_DOWN);) Bunun nerede çalışması gerekiyor?
Adam Haile

Tamam, "set_sleep_mode (MODE)" bölümü ana döngüden önce, ideal olarak G / Ç bağlantı noktalarını, zamanlayıcıları vb. Ayarladığınız diğer başlatma kodunda olmalıdır. Gerçekten enable_sleep (); bu noktada, çünkü ilk WDT tetikleyicinizden sonra yapılacaktır. Ana döngünün İÇİNDE, bu uyku kodu yalnızca uyku etkinse EĞER yürütür ve sadece sleep_bod_disable () yapıyorsanız, kesintileri devre dışı bırakmak / yeniden etkinleştirmek tamamen gerekli değildir. Bu IF ifadesinin tamamı, orada yürüttüğünüz diğer kodlardan sonra MAIN döngüsünün altında (ancak yine de içinde) olabilir.
Kurt E. Clothier

Tamam ... şimdi daha mantıklı. Bulanık olduğum tek şey, bu "zamanlanmış dizi" nin ne
olduğudur

Yan not: uyu neden isteyeyim biliyorum, ama yok farz olması WDT kullanırken nasıl?
Adam Haile

Veri sayfasının bu küçük bölümüne bir bakın: 8.4.1. Temel olarak, WDT kaydını değiştirmek için, değişiklik bitini ayarlamanız ve ardından çok sayıda saat döngüsü içinde uygun WDTCR bitlerini ayarlamanız gerekir. WDT Kurulumu bölümünde sağladığım kod, önce WD değiştirme bitini etkinleştirerek bunu yapar. Ve hayır, WDT ile uyku işlevini hiç kullanmak zorunda değilsiniz. İstediğiniz herhangi bir nedenden dolayı standart zamanlanmış bir kesme kaynağı olabilir.
Kurt E. Clothier

1

Veri sayfasına göre mümkündür. Bir kesmeyi ve sıfırlamayı da etkinleştirebilirsiniz. Her ikisi de etkinse, ilk bekçi zaman aşımı, Kesme Etkinleştirme bitinin ayarlanmamasına (kesme devre dışı) neden olan kesmeyi tetikler. Bir sonraki zaman aşımı CPU'nuzu sıfırlar. Kesmeyi yürütüldükten hemen sonra etkinleştirirseniz, bir sonraki zaman aşımı (yeniden) yalnızca bir kesmeyi tetikler.

Ayrıca kesmeyi etkinleştirebilir ve sıfırlamayı hiç etkinleştiremezsiniz. Kesinme her tetiklendiğinde WDIE bitini ayarlamanız gerekir.


Hmmm .... Bence bu mantıklı. Ben bir şans vereceğim. Her zaman amaçlanmayan şeyleri kullanmaktan hoşlanırım :)
Adam Haile

Aslında akıllı bir tasarım olduğunu düşünüyorum. Watchdog işlevselliğini korurken bir zamanlayıcı tasarrufu sağlar.
Tom L.

2
WDT'nin bu ilk zaman aşımı ve sonraki kesinti, WDT'nin gerçek takma kurtarma için kullanılmasını sağlayan bazı uygulamalarda avantaj sağlamak için de kullanılabilir. "Beklenmedik zaman aşımı" gerçekleştiğinde kodun ne yapmaya çalıştığını belirlemek için WDT ISR'deki yığılmış dönüş adresine bakılabilir.
Michael Karas

1

Bu, yukarıda ve başka yerlerde önerilenden çok daha kolay.

WDTONSigorta programlanmadığı sürece (varsayılan olarak programlanmamıştır), o zaman sadece ...

  1. Watchdog kesme etkinleştirme bitini ve watchdog kontrol kaydındaki zaman aşımını ayarlayın.
  2. Kesmeleri etkinleştir.

İşte 16 ms'de bir kez bir ISR yürütecek bir kod örneği ...

ISR(WDT_vect) {
   // Any code here will get called each time the watchdog expires
}

void main(void) {
   WDTCR =  _BV(WDIE);    // Enable WDT interrupt, leave existing timeout (default 16ms) 
   sei();                                           // Turn on global interrupts
   // Put any code you want after here.
   // You can also go into deep sleep here and as long as 
   // global interrupts are eneabled, you will get woken 
   // up when the watchdog timer expires
   while (1);
}

Gerçekten bu kadar. Bekçi köpeği sıfırlamasını hiçbir zaman etkinleştirmediğimizden, devre dışı bırakmak için asla zamanlanmış dizilerle uğraşmak zorunda değiliz. ISR çağrıldığında gözlemci kesme bayrağı otomatik olarak silinir.

Her 1 saniyeden farklı bir süre istiyorsanız, bu değerleri burada uygun bitleri ayarlamak için kullanabilirsiniz WDTCR...

resim açıklamasını buraya girin

Zaman aşımını değiştirmek için zamanlanmış diziyi yürütmeniz gerektiğini unutmayın. İşte zaman aşımını 1 saniyeye ayarlayan kod ...

   WDTCR = _BV(WDCE) | _BV(WDE);                   // Enable changes
   WDTCR = _BV(WDIE) | _BV( WDP2) | _BV( WDP1);    // Enable WDT interrupt, change timeout to 1 second

Kurulum sırasında zamanlanmış dizinin gerçekleştirilmemesi bir kod satırı kaydeder - bir okuma-değiştirme-yazma işlemi. Başlangıç ​​dizisi "Watchdog yanlışlıkla etkinleştirilmişse, örneğin bir kaçak işaretçi veya kahverengileşme koşulu tarafından" veri sayfasında önerilir ve kodumun geri kalanı WDT'nin uyku modu ile kombinasyon halinde kullanılmasına özeldir. OP. Çözümünüz daha basit değil, önerilen / gerekli kazan plakası kodunu ihmal ettiniz.
Kurt E. Clothier

@ KurtE.Clothier Üzgünüm, en basit çalışma örneğini vermeye çalışıyoruz!
bigjosh
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.