Використовуйте AVR Watchdog як звичайний ISR


17

Я намагаюся обернути голову навколо сторожового таймера серії ATTinyX5. Тож я читав, що я читав, здавалося, що ти можеш використовувати це для того, щоб змусити програму робити щось певне протягом N секунд, але ніколи насправді не показав, як це зробити. Іншим здавалося, що він буде скинутий лише чіп, якщо щось тим часом не буде скинуто (як правило, це "звичайне" використання).

Чи є спосіб використання WDT, як ти TIMER1_COMPA_vect чи подібний. Я помітив, що у нього є режим тайм-ауту на 1 секунду, і мені дуже хотілося б використовувати це, щоб щось кожні 1 секунду траплялося в моєму коді (і краще спати між ними).

Думки?

* Оновлення: * Оскільки я задав запит, я маю на увазі розділ 8.4 аркуша ATTinyX5 . Не те, щоб я цілком це розумів, яка моя проблема ...


1
+1 для роздумів поза межами. Додаткові великі пальці вгору, якщо ви додасте посилання на таблицю даних для AVR.
jippie

Відповіді:


23

Ти, звичайно, можеш. Відповідно до таблиці, таймер сторожової собаки може бути налаштований для скидання MCU або викликати переривання при його спрацюванні. Здається, вас більше цікавить можливість переривання.

Налаштування WDT насправді простіше, ніж звичайний таймер, з тієї ж причини, що менш корисна: менше варіантів. Він працює на внутрішньо каліброваному тактовою частотою 128 кГц, тобто його терміни не впливають на основну тактову частоту MCU. Він також може продовжувати працювати під час найглибших режимів сну, щоб забезпечити джерело пробудження.

Я перегляну кілька прикладів даних, а також якийсь код, який я використав (на С).

Файли та визначення

Для початку ви, ймовірно, захочете включити наступні два файли заголовка для роботи.

#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 вперше запущений, ви зазвичай ініціалізуєте введення-виведення, встановіть таймери тощо. Десь тут хороший час, щоб переконатися, що 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 Наступне, про що слід потурбуватися - це обробка 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 Sleep

Замість того, щоб увімкнути 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   
  ****************************/

   }
 }

WOW ... дуже докладно. Спасибі! Мене трохи бентежить розділ сну, де ви показуєте головну петлю (якщо (MCUCR & _BV (SE)) {// Якщо сон увімкнено ... і т. Д.) Мене бентежить, чому в головному вигляді ви б постійно відключати та включати переривання. І частина вгорі цього розділу (set_sleep_mode (SLEEP_MODE_PWR_DOWN);) Де це потрібно запускати?
Адам Хейле

Гаразд, частина "set_sleep_mode (MODE)" повинна бути в основному ДО головного циклу, в ідеалі - в іншому ініціалізаційному коді, де ви встановлюєте порти вводу / виводу, таймери тощо. Насправді вам не потрібно активувати_заспівати (); в цей момент, оскільки це буде зроблено після першого пуску WDT. ВНУТРЕННЯ основного циклу, що код сну виконує лише IF, сон увімкнено, і не зовсім потрібно вимикати / відновлювати перерви там, лише якщо ви робите sleep_bod_disable (); Весь цей оператор IF може знаходитися внизу (але все ще всередині) циклу MAIN після будь-якого іншого коду, який ви там виконуєте.
Курт Е. Клотьє

Ок ... це має більше сенсу зараз. Єдине останнє, на чому я нечіткий - це ця "приурочена послідовність" ...
Адам Хейл

Бічна примітка: Я знаю, чому ви хотіли б спати, але я припускаю, що цього не потрібно робити під час використання WDT?
Адам Хейле

Погляньте на цей невеликий розділ аркуша: 8.4.1. В основному, щоб змінити регістр WDT, вам потрібно встановити біт змін, а потім встановити належні біти WDTCR протягом стількох тактових циклів. Код, який я надав у розділі налаштування WDT, робить це, спочатку ввімкнувши біт зміни WD. І ні, вам не потрібно взагалі використовувати функцію сну з WDT. Це може бути стандартним джерелом призупиненого переривання з будь-якої причини.
Курт Е. Клотьє

1

За інформаційним аркушем це можливо. Ви навіть можете ввімкнути як переривання, так і скидання. Якщо обидва ввімкнено, перший тайм-аут сторожової собаки спричинить переривання, через що біт Enable Enable може бути знятий (переривання вимкнено). Наступний час очікування буде скинутий ваш процесор. Якщо ви ввімкнете переривання безпосередньо після його виконання, наступний тайм-аут (знову) спричинить лише переривання.

Ви також можете просто включити переривання і взагалі не активувати скидання. Вам доведеться встановлювати біт WDIE кожного разу, коли спрацьовує перерва.


Хммм .... я думаю, що це має сенс. Я дам йому постріл. Мені завжди подобається використовувати речі для того, що вони не призначалися :)
Адам Хейле

Насправді я думаю, що це розумний дизайн. Економить таймер, зберігаючи функціональність сторожового собаки.
Том Л.

2
Цей початковий тайм-аут WDT і подальше переривання також можуть бути використані для переваги для деяких додатків, які дозволяють WDT для фактичного відновлення затримки. Можна подивитися на складену зворотну адресу в WDT ISR, щоб зробити висновок про те, що код намагався робити, коли стався "несподіваний тайм-аут".
Майкл Карась

1

Це набагато простіше, ніж запропоновано вище та в інших місцях.

Поки WDTONзапобіжник не запрограмований (він не запрограмований за замовчуванням), тоді вам потрібно лише ...

  1. Встановіть біт увімкнення переривання сторожового дому та тайм-аут у реєстрі контролю сторожових дог
  2. Увімкнути переривання.

Ось приклад коду, який виконуватиме ISR один раз на 16 мс ...

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 секунду, ви можете використовувати ці значення тут, щоб встановити відповідні біти в WDTCR...

введіть тут опис зображення

Зауважте, що вам потрібно виконати часову послідовність, щоб змінити час очікування. Ось код, який встановлює час очікування на 1 секунду ...

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

Невиконання приуроченої послідовності під час установки зберігає один рядок коду - операцію читання-зміна-запис. Початкова послідовність рекомендується у таблиці "Якщо сторожовий дог випадково ввімкнено, наприклад, утеченим покажчиком або умовою коричневого виходу", а решта мого коду специфічна для використання WDT у поєднанні з режимом сну, як цього вимагає ОП. Ваше рішення не простіше, ви просто нехтували рекомендованим / необхідним кодом пластини котла.
Курт Е. Клотьє

@ KurtE.Clothier Вибачте, просто намагаюся навести найпростіший робочий приклад!
bigjosh
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.