ใช้ AVR Watchdog เหมือน ISR ปกติ


17

ฉันพยายามล้อมหัวนาฬิกาจับเวลาในซีรีย์ ATTinyX5 ดังนั้นสิ่งที่ฉันได้อ่านทำให้ดูเหมือนว่าคุณสามารถใช้มันเพื่อทำให้โปรแกรมทำสิ่งที่เฉพาะเจาะจงตลอดเวลา N วินาที แต่ไม่เคยแสดงให้เห็นว่าจริงๆ คนอื่นทำให้ดูเหมือนว่ามันจะรีเซ็ตชิปเท่านั้นเว้นแต่บางสิ่งบางอย่างในการรีเซ็ตรหัสจะถูกนับในเวลาเดียวกัน (ซึ่งดูเหมือนจะเป็นการใช้ "ปกติ")

มีวิธีการใช้ WDT เหมือนที่คุณต้องการ TIMER1_COMPA_vect หรือคล้ายกัน ฉันสังเกตเห็นว่ามันมีโหมดไทม์เอาต์ 1 วินาทีและฉันชอบที่จะใช้มันเพื่อทำให้บางสิ่งเกิดขึ้นทุก ๆ 1 วินาทีในโค้ดของฉัน (และควรนอนในระหว่างนั้น)

คิด?

* ปรับปรุง: *เนื่องจากมันถูกถามว่าสิ่งที่ผมหมายถึงคือส่วน 8.4 ของATTinyX5 แผ่นข้อมูล ไม่ใช่ว่าฉันเข้าใจอย่างถ่องแท้ซึ่งเป็นปัญหาของฉัน ...


1
+1 สำหรับการคิดนอกกรอบ ยกนิ้วขึ้นหากคุณเพิ่มลิงค์ไปยังแผ่นข้อมูลสำหรับ AVR
jippie

คำตอบ:


23

แน่นอนที่สุดคุณสามารถ ตามแผ่นข้อมูลตัวจับเวลาจ้องจับผิดสามารถตั้งค่าเพื่อรีเซ็ต MCU หรือทำให้เกิดการขัดจังหวะเมื่อมันทริกเกอร์ ดูเหมือนว่าคุณมีความสนใจในความเป็นไปได้ที่จะขัดจังหวะ

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

ฉันจะดูตัวอย่างแผ่นข้อมูลสองสามรายการรวมถึงรหัสบางส่วนที่ฉันใช้ (ใน C)

ไฟล์และคำจำกัดความที่รวมอยู่

ในการเริ่มต้นคุณอาจต้องการรวมไฟล์ส่วนหัวสองไฟล์ต่อไปนี้เพื่อให้สามารถใช้งานได้:

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

นอกจากนี้ฉันใช้ Macro <_BV (BIT)> ซึ่งกำหนดไว้ในหนึ่งในส่วนหัว AVR มาตรฐานดังต่อไปนี้ (ซึ่งอาจเป็นที่นิยมมากขึ้นสำหรับคุณ):

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

จุดเริ่มต้นของรหัส

เมื่อเริ่มต้น MCU โดยทั่วไปคุณจะต้องเริ่มต้น I / O ตั้งค่าตัวนับและอื่น ๆ ที่นี่เป็นช่วงเวลาที่ดีเพื่อให้แน่ใจว่า WDT ไม่ได้ทำให้เกิดการรีเซ็ตเนื่องจากมันสามารถทำอีกครั้งทำให้โปรแกรมของคุณอยู่ใน การวนซ้ำไม่เสถียร

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

จากนั้นหลังจากที่คุณตั้งค่าชิปที่เหลือแล้วให้ทำซ้ำ WDT การตั้งค่า WDT ต้องการ "ลำดับเวลา" แต่ง่ายในการทำ ...

// 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

แน่นอนการขัดจังหวะของคุณควรถูกปิดใช้งานในระหว่างรหัสนี้ ให้แน่ใจว่าได้เปิดใช้งานพวกเขาในภายหลัง!

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

WDT Interrupt Service Routine สิ่งต่อไปที่คุณต้องกังวลคือการจัดการ WDT ISR สิ่งนี้ทำเช่นนี้:

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 สลีป

แทนที่จะให้ MCU เข้าสู่โหมดสลีปภายใน WDT ISR ฉันขอแนะนำให้เปิดใช้งานโหมดสลีปที่ส่วนท้ายของ ISR จากนั้นให้โปรแกรม MAIN ทำให้ MCU เข้าสู่โหมดสลีป ด้วยวิธีนี้โปรแกรมจะออกจาก ISR ก่อนที่จะเข้าสู่โหมดสลีปและมันจะตื่นขึ้นมาและกลับไปที่ WDT ISR โดยตรง

// 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   
  ****************************/

   }
 }

ว้าว ... มีรายละเอียดมาก ขอบคุณ! ฉันสับสนเล็กน้อยในส่วนสลีปที่คุณแสดงลูปหลัก (ถ้า (MCUCR & _BV (SE)) {// ถ้าเปิดใช้งานการสลีป ... ฯลฯ ) ฉันสับสนว่าทำไมในการมองหลัก ปิดการใช้งานและเปิดใช้งานการขัดจังหวะอย่างต่อเนื่อง และส่วนที่ด้านบนของส่วนนั้น (set_sleep_mode (SLEEP_MODE_PWR_DOWN);) ที่ควรจะทำงานที่ไหน
Adam Haile

ตกลงส่วน "set_sleep_mode (MODE)" ควรอยู่ใน main ก่อนที่จะวนลูปหลักในรหัสเริ่มต้นอื่น ๆ ที่คุณตั้งค่าพอร์ต I / O, ตัวจับเวลาและอื่น ๆ คุณไม่จำเป็นต้อง enable_sleep (); ณ จุดนั้นเนื่องจากจะทำหลังจากทริกเกอร์ WDT แรกของคุณ ข้างในของลูปหลักนั้นรหัสสลีปจะทำงานเฉพาะเมื่อเปิดใช้งานโหมดสลีปเท่านั้นและไม่จำเป็นต้องปิดการใช้งาน / เปิดใช้งานอินเตอร์รัปต์ที่นั่นอีกต่อไปเฉพาะในกรณีที่คุณทำ sleep_bod_disable () คำสั่ง IF ทั้งหมดนั้นอาจอยู่ที่ด้านล่าง (แต่ยังอยู่ด้านใน) ของการวนซ้ำ MAIN หลังจากรหัสอื่น ๆ ที่คุณเรียกใช้อยู่ที่นั่น
Kurt E. Clothier

ตกลง ... ที่เหมาะสมมากขึ้นในขณะนี้ เพียง แต่สิ่งสุดท้ายที่ฉันเลือนคือสิ่งนี้ "ลำดับหมดเวลา" คือ ...
อดัมเซ

หมายเหตุด้านข้าง: ฉันรู้ว่าทำไมคุณถึงอยากเข้านอน แต่ฉันคิดว่าคุณไม่จำเป็นต้องใช้ WDT?
Adam Haile

ดูที่ส่วนเล็ก ๆ ของแผ่นข้อมูล: 8.4.1 โดยทั่วไปในการเปลี่ยนการลงทะเบียน WDT คุณจะต้องตั้งค่าบิตการเปลี่ยนแปลงแล้วตั้งค่าบิต WDTCR ที่เหมาะสมภายในวงจรนาฬิกาจำนวนมาก รหัสที่ฉันให้ไว้ในส่วนการตั้งค่า WDT ทำได้โดยการเปิดใช้งานบิตเปลี่ยน WD ก่อน และไม่ต้องคุณไม่ต้องใช้ฟังก์ชั่นการนอนหลับเลยด้วย WDT อาจเป็นแหล่งขัดจังหวะตามกำหนดเวลามาตรฐานไม่ว่าด้วยเหตุผลใดก็ตามที่คุณต้องการ
Kurt E. Clothier

1

ตามแผ่นข้อมูลเป็นไปได้ คุณสามารถเปิดใช้งานทั้งขัดจังหวะและการรีเซ็ต หากเปิดใช้งานทั้งคู่การหมดเวลาจ้องจับผิดครั้งแรกจะทริกเกอร์การขัดจังหวะซึ่งทำให้บิตการเปิดใช้งานการขัดจังหวะถูกเปิดใช้งาน การหมดเวลาครั้งต่อไปจะรีเซ็ต CPU ของคุณ หากคุณเปิดใช้งานอินเตอร์รัปต์โดยตรงหลังจากถูกเรียกใช้งานการหมดเวลาครั้งต่อไปจะ (อีกครั้ง) ทริกเกอร์การขัดจังหวะเท่านั้น

คุณยังสามารถเปิดใช้งานการขัดจังหวะและไม่สามารถเปิดใช้งานการรีเซ็ตได้เลย คุณจะต้องตั้งค่าบิต WDIE ทุกครั้งที่มีการขัดจังหวะ


อืม .... ฉันคิดว่าเหมาะสมแล้ว ฉันจะให้มันยิง ฉันมักจะเหมือนกับการใช้สิ่งที่สิ่งที่พวกเขาไม่ได้มีเจตนา :)
อดัมเซ

จริงๆแล้วฉันคิดว่ามันเป็นการออกแบบที่ฉลาด ประหยัดเวลาในขณะที่รักษาการทำงานของสุนัขเฝ้าบ้าน
Tom L.

2
การหมดเวลาเริ่มต้นของ WDT และการขัดจังหวะภายหลังสามารถใช้เพื่อประโยชน์สำหรับแอปพลิเคชันบางอย่างที่เปิดใช้งาน WDT สำหรับการกู้คืน Hangup จริง หนึ่งสามารถดูที่อยู่ส่งคืนแบบซ้อนใน WDT ISR เพื่ออนุมานว่ารหัสกำลังพยายามทำอะไรเมื่อเกิด "หมดเวลาที่ไม่คาดคิด"
Michael Karas

1

นี่ง่ายกว่าที่แนะนำข้างต้นและที่อื่น ๆ

ตราบใดที่WDTONฟิวส์ไม่ได้ตั้งโปรแกรมไว้ (ไม่ใช่โปรแกรมที่ตั้งไว้ตามค่าเริ่มต้น) คุณต้องใช้ ...

  1. ตั้งค่าการเปิดใช้งานบิตขัดจังหวะการเฝ้าดูและการหมดเวลาในการลงทะเบียนตัวควบคุม watchdog
  2. เปิดใช้งานการขัดจังหวะ

นี่คือตัวอย่างโค้ดที่จะเรียกใช้ ISR หนึ่งครั้งต่อ 16ms ...

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);
}

นั่นมันจริงๆ เนื่องจากเราไม่เคยเปิดใช้งานการรีเซ็ตสุนัขเฝ้าบ้านเราจึงไม่ต้องยุ่งกับลำดับเวลาเพื่อปิดใช้งาน แฟล็กการขัดจังหวะการจ้องจับผิดจะถูกลบโดยอัตโนมัติเมื่อมีการเรียก ISR

หากคุณต้องการช่วงเวลาที่แตกต่างจากทุก ๆ 1 วินาทีคุณสามารถใช้ค่าเหล่านี้ที่นี่เพื่อตั้งค่าบิต apropriate ในWDTCR...

ป้อนคำอธิบายรูปภาพที่นี่

โปรดทราบว่าคุณจำเป็นต้องดำเนินการตามลำดับเวลาเพื่อเปลี่ยนการหมดเวลา นี่คือรหัสที่ตั้งค่าการหมดเวลาเป็น 1 วินาที ...

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

การไม่ดำเนินการตามลำดับเวลาในระหว่างการตั้งค่าจะบันทึกรหัสหนึ่งบรรทัด - การดำเนินการอ่าน - แก้ไข - เขียน ขอแนะนำให้ใช้ลำดับเริ่มต้นในแผ่นข้อมูล "หาก Watchdog เปิดใช้งานโดยไม่ตั้งใจเช่นตัวชี้จากการหลบหนีหรือสภาวะหมดแรง" และส่วนที่เหลือของรหัสของฉันมีเฉพาะสำหรับการใช้ WDT ร่วมกับโหมดสลีปตามที่ร้องขอโดย OP วิธีแก้ปัญหาของคุณนั้นไม่ง่ายคุณแค่ละเลยรหัสจานหม้อไอน้ำที่แนะนำ / จำเป็น
เคิร์ตอี. Clothier

@ KurtE.Clothier ขออภัยเพียงแค่พยายามยกตัวอย่างการทำงานที่ง่ายที่สุด!
bigjosh
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.