ทำไม AVR ของฉันรีเซ็ตเมื่อฉันโทร wdt_disable () เพื่อลองปิดตัวจับเวลาจ้องจับผิด?


34

ฉันมีปัญหาในการใช้งานตัวควบคุม watchdog ใน AVR ATtiny84A ที่กำลังรีเซ็ตชิปแม้ว่าตัวจับเวลาควรมีเวลาเหลืออีกมาก สิ่งนี้เกิดขึ้นอย่างไม่สอดคล้องกันและเมื่อใช้รหัสเดียวกันในหลายส่วนทางกายภาพ บางรีเซ็ตทุกครั้งบางรีเซ็ตบางครั้งและบางคนไม่เคย

เพื่อแสดงให้เห็นถึงปัญหาฉันได้เขียนโปรแกรมง่าย ๆ ที่ ...

  1. เปิดใช้งานการจ้องจับผิดด้วยการหมดเวลา 1 วินาที
  2. รีเซ็ตสุนัขเฝ้าบ้าน
  3. กะพริบไฟ LED สีขาวเป็นเวลา 0.1 วินาที
  4. กะพริบไฟ LED สีขาวเป็นเวลา 0.1 วินาที
  5. ปิดใช้งานการจ้องจับผิด

เวลารวมระหว่างการเปิดใช้งานและปิดใช้งานการเฝ้าระวังน้อยกว่า 0.3 วินาที แต่บางครั้งการเฝ้าระวังการตั้งค่าจะเกิดขึ้นเมื่อดำเนินการตามลำดับการปิดใช้งาน

นี่คือรหัส:

#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.

    }
}

เมื่อเริ่มต้นโปรแกรมจะตรวจสอบว่าการรีเซ็ตก่อนหน้านี้เกิดจากการหมดเวลาของ watchdog หรือไม่และถ้าเป็นเช่นนั้นไฟ LED สีแดงจะสว่างขึ้น ฉันเชื่อว่ารหัสนี้ไม่ควรดำเนินการและไฟ LED สีแดงไม่ควรเกิดขึ้น แต่บ่อยครั้งที่มันทำ

เกิดขึ้นที่นี่คืออะไร?


7
หากคุณตัดสินใจที่จะเขียนคำถามและคำตอบของคุณเองที่นี่เกี่ยวกับปัญหานี้ฉันสามารถจินตนาการถึงความเจ็บปวดและความทุกข์ทรมานที่จำเป็นต้องค้นพบ
Vladimir Cravero

3
พนันได้เลย! 12 ชั่วโมงในข้อผิดพลาดนี้ ในขณะที่ข้อผิดพลาดจะเกิดขึ้นนอกเว็บไซต์เท่านั้น ถ้าฉันนำบอร์ดไปที่เดสก์ท็อปของฉันแล้วข้อผิดพลาดจะหายไปน่าจะเกิดจากผลกระทบของอุณหภูมิ (สถานที่ของฉันเย็นซึ่งทำให้ oscillator จ้องจับผิดทำงานช้าลงเล็กน้อยเมื่อเทียบกับนาฬิการะบบ) ต้องใช้การทดลองมากกว่า 30 ครั้งเพื่อสร้างมันขึ้นมาใหม่และจับมันในวิดีโอ
bigjosh

ฉันเกือบจะรู้สึกถึงความเจ็บปวด ฉันไม่ใช่ EE ที่เก่าและนำทาง แต่บางครั้งฉันก็พบว่าตัวเองอยู่ในสถานการณ์เช่นนี้ จับได้ดีดื่มเบียร์และแก้ปัญหาต่อไป;)
Vladimir Cravero

คำตอบ:


41

มีข้อบกพร่องในรูทีนไลบรารี wdt_reset ()

นี่คือรหัส ...

__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" \
)

บรรทัดที่สี่ขยายออกเป็น ...

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

ความตั้งใจของสายนี้คือการเขียน 1 ถึง WD_CHANGE_BIT ซึ่งจะทำให้บรรทัดต่อไปนี้เขียน 0 ลงใน bit watchdog enable (WDE) จากแผ่นข้อมูล:

ในการปิดใช้งานตัวจับเวลา Watchdog ที่เปิดใช้งานต้องทำตามขั้นตอนต่อไปนี้: 1. ในการดำเนินการเดียวกันให้เขียนตรรกะหนึ่งลงใน WDCE และ WDE ต้องเขียนตรรกะหนึ่งไปยัง WDE โดยไม่คำนึงถึงค่าก่อนหน้าของบิต WDE 2. ภายในสี่รอบนาฬิกาถัดไปในการดำเนินการเดียวกันให้เขียนบิต WDE และ WDP ตามต้องการ แต่ด้วยการล้างบิต WDCE

น่าเสียดายที่การมอบหมายนี้มีผลข้างเคียงของการตั้งค่า 3 บิตด้านล่างของWatchdog Control Register (WDCE) เป็น 0 นี่เป็นการตั้งค่าพรีสเกลเลอร์ให้เป็นค่าที่สั้นที่สุดทันที หากพรีสเกลเลอร์ตัวใหม่ถูกไล่ออกไปแล้วในขณะที่คำสั่งนี้ดำเนินการตัวประมวลผลจะถูกรีเซ็ต

เนื่องจากตัวจับเวลาจ้องจับผิดทำงานออกจากออสซิลเลเตอร์ 128 kHz ที่เป็นอิสระทางร่างกายจึงยากที่จะคาดการณ์ว่าสถานะของพรีสเกลเลอร์ใหม่จะสัมพันธ์กับโปรแกรมที่กำลังทำงานอยู่ บัญชีนี้สำหรับพฤติกรรมที่สังเกตได้ในวงกว้างซึ่งข้อผิดพลาดสามารถสัมพันธ์กับแรงดันไฟฟ้าอุณหภูมิและแบทช์การผลิตเนื่องจากสิ่งเหล่านี้สามารถส่งผลกระทบต่อความเร็วของจ้องจับผิดสุนัขเฝ้าบ้านและนาฬิการะบบไม่สมมาตร นี่เป็นข้อผิดพลาดที่หายากมาก!

นี่คือรหัสปรับปรุงที่หลีกเลี่ยงปัญหานี้ ...

__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" \
)

wdrคำแนะนำพิเศษจะรีเซ็ตตัวจับเวลาจ้องจับผิดดังนั้นเมื่อบรรทัดต่อไปนี้อาจเปลี่ยนไปเป็นพรีสเกลเลอร์ที่แตกต่างกันจะรับประกันว่ายังไม่หมดเวลา

สิ่งนี้สามารถแก้ไขได้โดยการวางบิต WD_CHANGE_BIT และ WDE ลงใน WD_CONTROL_REGISTER ตามที่แนะนำในเอกสารข้อมูลทางเทคนิค ...

; 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

... แต่สิ่งนี้ต้องการรหัสเพิ่มเติมและการลงทะเบียนเริ่มต้นพิเศษ เนื่องจากตัวนับจ้องจับผิดถูกรีเซ็ตเมื่อปิดใช้งานอยู่การรีเซ็ตพิเศษจึงไม่ปิดบังอะไรเลยและไม่มีผลข้างเคียงโดยไม่ได้ตั้งใจ


7
ฉันอยากจะให้อุปกรณ์ประกอบฉากแก่คุณเพราะเมื่อฉันไปตรวจสอบรายการปัญหา avr-libc ดูเหมือนว่าคุณ (น่าจะเป็นคุณ) ที่ส่งมาที่นั่นแล้วsavannah.nongnu.org/bugs/?44140
vicatcu

1
ps "josh.com" เป็นจริง ... ประทับใจ
vicatcu
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.