Watchdog zamanlayıcısını kapatmayı denemek için wdt_disable () işlevini çağırdığımda AVR'm neden sıfırlanıyor?


34

AVR ATtiny84A'da devre dışı bırakılan bir bekçi uygulaması dizisinin yürütülmesi , zamanlayıcının üzerinde bolca zaman kalmasına rağmen çipi sıfırlıyor. Bu tutarsızca ve aynı kodu birçok fiziksel parçada çalıştırırken olur; bazıları her zaman sıfırlanır, bazıları bazen sıfırlanır, bazıları ise hiçbir zaman sıfırlanmaz.

Sorunu göstermek için, basit bir program yazdım ki ...

  1. Watchdog'u 1 saniye zaman aşımı ile etkinleştirir
  2. Bekçi köpeği sıfırlar
  3. Beyaz LED'i 0,1 saniye boyunca yanıp söner
  4. Beyaz LED'i 0,1 saniye süreyle yanıp söndü
  5. Bekçi köpeğini devre dışı bırakır

Bekçi uygulaması etkinleştirme ve devre dışı bırakma arasındaki toplam süre 0,3 saniyeden azdır, ancak bazen devre dışı bırakma sırası yürütüldüğünde bir bekçi uygulaması sıfırlaması gerçekleşir.

İşte kod:

#define F_CPU 1000000                   // Name used by delay.h. We are running 1Mhz (default fuses)

#include <avr/io.h>
#include <util/delay.h>
#include <avr/wdt.h>


// White LED connected to pin 8 - PA5

#define WHITE_LED_PORT PORTA
#define WHITE_LED_DDR DDRA
#define WHITE_LED_BIT 5


// Red LED connected to pin 7 - PA6

#define RED_LED_PORT PORTA
#define RED_LED_DDR DDRA
#define RED_LED_BIT 6


int main(void)
{
    // Set LED pins to output mode

    RED_LED_DDR |= _BV(RED_LED_BIT);
    WHITE_LED_DDR |= _BV(WHITE_LED_BIT);


    // Are we coming out of a watchdog reset?
    //        WDRF: Watchdog Reset Flag
    //        This bit is set if a watchdog reset occurs. The bit is reset by a Power-on Reset, or by writing a
    //        logic zero to the flag

    if (MCUSR & _BV(WDRF) ) {

        // We should never get here!


        // Light the RED led to show it happened
        RED_LED_PORT |= _BV(RED_LED_BIT);

        MCUCR = 0;        // Clear the flag for next time
    }

    while(1)
    {
        // Enable a 1 second watchdog
        wdt_enable( WDTO_1S );

        wdt_reset();          // Not necessary since the enable macro does it, but just to be 100% sure

        // Flash white LED for 0.1 second just so we know it is running
        WHITE_LED_PORT |= _BV(WHITE_LED_BIT);
        _delay_ms(100);
        WHITE_LED_PORT &= ~_BV(WHITE_LED_BIT);
        _delay_ms(100);

        // Ok, when we get here, it has only been about 0.2 seconds since we reset the watchdog.

        wdt_disable();        // Turn off the watchdog with plenty of time to spare.

    }
}

Başlangıçta, program önceki sıfırlamanın bir bekçi uygulaması zaman aşımından kaynaklanıp kaynaklanmadığını kontrol eder ve eğer öyleyse kırmızı LED'i yanar ve bir bekçi uygulaması sıfırlamanın gerçekleştiğini belirtmek için bekçi uygulaması sıfırlama bayrağını temizler. Bu kodun asla yürütülmemesi gerektiğine ve kırmızı LED'in hiçbir zaman yanmaması gerektiğine inanıyorum, ancak çoğu zaman böyle oluyor.

Burada neler oluyor?


7
Bu konuda kendi soru-cevaplarınızı yazmaya karar verirseniz, onu keşfetmek için gereken acıyı ve acıları hayal edebiliyorum.
Vladimir Cravero,

3
Emin ol! Bu böcek 12 saat. Bir süre, böcek SADECE site dışında olur. Tahtaları masaüstüme getirirsem, muhtemelen sıcaklık etkileri nedeniyle hata gider (yerim soğuktur; bu nedenle bekçi osilatörü sistem saatine göre biraz daha yavaş çalışır). Videoyu oynatmak ve kopyalamak için 30'dan fazla deneme sürdü.
bigjosh

Acıyı neredeyse hissedebiliyorum. Yaşlı ve yönlendirilmiş bir EE değilim ama bazen kendimi böyle durumlarda buldum. Harika av, bir bira içip problemleri çözmeye devam edin;)
Vladimir Cravero,

Yanıtlar:


41

Wdt_reset () kütüphanesindeki bir hata var.

İşte kod ...

__asm__ __volatile__ ( \
   "in __tmp_reg__, __SREG__" "\n\t" \
   "cli" "\n\t" \
   "out %0, %1" "\n\t" \
   "out %0, __zero_reg__" "\n\t" \
   "out __SREG__,__tmp_reg__" "\n\t" \
   : /* no outputs */ \
   : "I" (_SFR_IO_ADDR(_WD_CONTROL_REG)), \
   "r" ((uint8_t)(_BV(_WD_CHANGE_BIT) | _BV(WDE))) \
   : "r0" \
)

Dördüncü satır genişler ...

out _WD_CONTROL_REG, _BV(_WD_CHANGE_BIT) | _BV(WDE)

Bu satırın amacı WD_CHANGE_BIT'e 1 yazmaktır, bu da takip eden satırın watchdog etkinleştirme bitine (WDE) 0 yazmasını sağlayacaktır. Veri sayfasından:

Etkinleştirilmiş bir Watchdog Timer'ı devre dışı bırakmak için aşağıdaki prosedür izlenmelidir: 1. Aynı işlemde, WDCE ve WDE'ye bir mantık yazın. WDE bitinin önceki değerinden bağımsız olarak WDE'ye bir mantık yazılmalıdır. 2. Sonraki dört saat döngüsü içinde, aynı işlemde, WDE ve WDP bitlerini istediğiniz gibi yazın, ancak WDCE biti temizlendi.

Ne yazık ki, bu ödevin, Watchdog Control Register'ın (WDCE) alt 3 bitini 0'lara ayarlaması da yan etkiye sahiptir . Bu hemen ön ölçekleyiciyi en kısa değerine ayarlar. Yeni hazırlayıcı bu talimatın yürütüldüğü sırada halihazırda kovulmuşsa, işlemci sıfırlanır.

Bekçi uygulaması zamanlayıcısı fiziksel olarak bağımsız bir 128 kHz osilatörden çıktığından, yeni ön yükleyicinin durumunun çalışan programa göre ne olacağını tahmin etmek zordur. Bu, hatanın besleme gerilimi, sıcaklık ve üretim grubu ile ilişkilendirilebildiği çok çeşitli gözlemlenen davranışları hesaba katar, çünkü tüm bunlar gözlemci osilatörünün ve sistem saatinin asimetrik hızını etkileyebilir. Bu bulmak çok zor bir hataydı!

İşte bu sorunu önleyen güncellenmiş kod ...

__asm__ __volatile__ ( \
   "in __tmp_reg__, __SREG__" "\n\t" \
   "cli" "\n\t" \
   "wdr" "\n\t" \
   "out %0, %1" "\n\t" \
   "out %0, __zero_reg__" "\n\t" \
   "out __SREG__,__tmp_reg__" "\n\t" \
   : /* no outputs */ \
   : "I" (_SFR_IO_ADDR(_WD_CONTROL_REG)), \
   "r" ((uint8_t)(_BV(_WD_CHANGE_BIT) | _BV(WDE))) \
   : "r0" \
)

Ekstra wdrtalimat, bekçi uygulaması zamanlayıcısını sıfırlar, böylece aşağıdaki satır potansiyel olarak farklı bir ön ölçeklendiriciye geçtiğinde, henüz zaman aşımına uğramadığı garanti edilir.

Bu aynı zamanda, WD_CHANGE_BIT ve WDE bitlerinin veri sayfalarında önerildiği gibi WD_CONTROL_REGISTER içine ORING edilmesiyle de düzeltilebilir ...

; Write logical one to WDCE and WDE
; Keep old prescaler setting to prevent unintentional Watchdog Reset
in r16, WDTCR
ori r16, (1<<WDCE)|(1<<WDE)
out WDTCR, r16

... fakat bu daha fazla kod ve fazladan bir kayıt defteri gerektirir. Watchdog sayacı yine de devre dışı bırakıldığında sıfırlandığından, ekstra sıfırlama hiçbir şeyi engellemez ve istenmeyen bir yan etkisi olmaz.


7
Avr-libc sayı listesini kontrol etmeye gittiğimde, size (muhtemelen sizden) çoktan savannah.nongnu.org/bugs/?44140
vicatcu

1
ps "josh.com" gerçek ... etkileyici
vicatcu
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.