Tại sao thiết lập lại AVR của tôi khi tôi gọi wdt_disable () để thử tắt bộ đếm thời gian theo dõi?


34

Tôi đang gặp vấn đề khi thực hiện trình tự theo dõi vô hiệu hóa trên AVR ATtiny84A thực sự đặt lại chip mặc dù bộ định thời nên còn nhiều thời gian trên đó. Điều này xảy ra không nhất quán và khi chạy cùng một mã trên nhiều phần vật lý; một số thiết lập lại mỗi lần, một số đôi khi thiết lập lại, và một số không bao giờ.

Để giải thích vấn đề, tôi đã viết một chương trình đơn giản ...

  1. Cho phép cơ quan giám sát với thời gian chờ 1 giây
  2. Đặt lại cơ quan giám sát
  3. Bật đèn LED trắng trong 0,1 giây
  4. Tắt đèn LED trắng trong 0,1 giây
  5. Vô hiệu hóa cơ quan giám sát

Tổng thời gian giữa kích hoạt và vô hiệu hóa watchdog nhỏ hơn 0,3 giây, nhưng đôi khi thiết lập lại watchdog xảy ra khi chuỗi vô hiệu hóa được thực thi.

Đây là mã:

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

    }
}

Khi khởi động, chương trình sẽ kiểm tra xem liệu thiết lập lại trước đó có phải do thời gian chờ của watchdog gây ra hay không và nếu vậy nó sẽ sáng đèn LED màu đỏ và xóa cờ thiết lập lại watchdog để cho biết rằng thiết lập lại watchdog đã xảy ra. Tôi tin rằng mã này sẽ không bao giờ được thực thi và đèn LED màu đỏ sẽ không bao giờ được bật, nhưng nó thường như vậy.

Chuyện gì đang xảy ra ở đây?


7
Nếu bạn quyết định viết lên câu hỏi và trả lời của riêng bạn ở đây về vấn đề này, tôi có thể tưởng tượng nỗi đau và sự đau khổ cần thiết để khám phá nó.
Vladimir Cravero

3
Bạn đặt cược! 12 giờ về lỗi này. Trong một thời gian, lỗi sẽ CHỈ xảy ra ngoài trang web. Nếu tôi mang các bảng đến máy tính để bàn của mình thì lỗi sẽ biến mất, rất có thể là do hiệu ứng nhiệt độ (nơi tôi lạnh khiến bộ dao động của watchdog chạy chậm hơn một chút so với đồng hồ hệ thống). Phải mất hơn 30 thử nghiệm để tái tạo nó và bắt nó trong hành động trên video.
bigjosh

Tôi gần như có thể cảm thấy đau. Tôi không phải là một EE cũ và điều hướng nhưng đôi khi tôi thấy mình trong những tình huống như vậy. Bắt tuyệt vời, uống bia và tiếp tục giải quyết vấn đề;)
Vladimir Cravero

Câu trả lời:


41

Có một lỗi trong thói quen thư viện wdt_reset ().

Đây là mã ...

__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òng thứ tư mở rộng ra ...

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

Mục đích của dòng này là ghi 1 vào WD_CHANGE_BIT, điều này sẽ cho phép dòng sau ghi 0 vào bit kích hoạt watchdog (WDE). Từ biểu dữ liệu:

Để vô hiệu hóa Bộ định thời Watchdog được bật, phải tuân theo quy trình sau: 1. Trong cùng một thao tác, hãy viết một logic cho WDCE và WDE. Một logic phải được ghi vào WDE bất kể giá trị trước đó của bit WDE. 2. Trong bốn chu kỳ xung nhịp tiếp theo, trong cùng một thao tác, hãy viết các bit WDE và WDP như mong muốn, nhưng với bit WDCE bị xóa.

Thật không may, nhiệm vụ này có tác dụng phụ là cũng thiết lập 3 bit dưới cùng của Thanh ghi điều khiển Watchdog (WDCE) thành 0. Điều này ngay lập tức đặt bộ đếm gộp đến giá trị ngắn nhất của nó. Nếu bộ đếm gộp mới đã được kích hoạt tại thời điểm lệnh này được thực thi, bộ xử lý sẽ được đặt lại.

Vì bộ định thời watchdog chạy bộ dao động 128 kHz độc lập vật lý, thật khó để dự đoán trạng thái của bộ đếm gộp mới sẽ liên quan đến chương trình đang chạy. Điều này cho phạm vi rộng của các hành vi được quan sát trong đó lỗi có thể tương quan với điện áp cung cấp, nhiệt độ và lô sản xuất vì tất cả những điều này có thể ảnh hưởng đến tốc độ của bộ dao động theo dõi và đồng hồ hệ thống không đối xứng. Đây là một lỗi rất khó tìm!

Đây là mã cập nhật để tránh vấn đề này ...

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

Thêm wdr đặt lại bộ đếm thời gian theo dõi, vì vậy khi dòng sau có khả năng chuyển sang bộ đếm gộp khác, nó được đảm bảo chưa hết thời gian.

Điều này cũng có thể được khắc phục bằng cách OR các bit WD_CHANGE_BIT và WDE vào WD_CONTROL_REGISTER như được đề xuất trong biểu dữ liệu ...

; 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

... nhưng điều này đòi hỏi nhiều mã hơn và một thanh ghi đầu thêm. Vì bộ đếm watchdog được thiết lập lại khi nó bị vô hiệu hóa, nên thiết lập lại bổ sung không ghi đè bất cứ thứ gì và không có tác dụng phụ không chủ ý.


7
Tôi cũng muốn tặng bạn đạo cụ vì khi tôi đi kiểm tra danh sách vấn đề avr-libc, có vẻ như bạn (có lẽ là bạn) đã gửi nó ở đó đã savannah.nongnu.org/bugs/?44140
Abbeyatcu

1
ps "josh.com" là có thật ... ấn tượng
Abbeyatcu
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.