Сторожевой таймер (WatchDog, WDT) в микроконтроллерах ARM AT91SAM7X Печать
Добавил(а) microsin   

Микроконтроллеры AT91SAM7X128, AT91SAM7X256, AT91SAM7X512 снабжены таймером WDTC, который может выполнить сброс ядра микроконтроллера и периферии при зависании (непредусмотренном зацикливании) программы. В этой статье приводится только минимальный набор сведений, достаточных для начала использования WDTC, подробности см. в даташите.

Тактируется WDTC от внутреннего генератора с частотой 32768/128 = 256 Гц, и позволяет установить время срабатывания WDTC от 0 до 16 секунд с точностью 12 бит (4096 значений). Счетчик WDTC декрементный, и по достижению нуля происходит сброс. Чтобы сброс процессора не происходил, WDTC необходимо периодически сбрасывать записью специального значения в регистр команд WDTC_WDCR.

По умолчанию (после сброса или включения питания) работа WDTC разрешена, и устанавливается на выдержку 16 секунд, поэтому если нужно работать без WDTC, то сразу после сброса WDTC надо отключить. Обычно это делается (так сделано во всех примерах Atmel для IAR) в модуле board_lowlevel.c (в функции LowLevelInit) оператором:

AT91C_BASE_WDTC -> WDTC_WDMR = AT91C_WDTC_WDDIS;

Пользовательский интерфейс WDTC состоит из 3-х регистров:

WDTC_WDCR   регистр команд (только запись). Предназначен только для сброса WDT, что делается только так, и никак иначе:

AT91C_BASE_WDTC -> WDTC_WDCR = (0xA5 << 24) | AT91C_WDTC_WDRSTT;

Ключ 0xA5 нужен для дополнительной защиты регистра от нежелательной записи, а бит WDRSTT, равный 1, задает сброс WDT.

WDTC_WDMR   регистр режима (чтение, однократная запись). Этот регистр устроен таким образом, что записать туда можно что-то только один раз. В этом регистре хранится задержка времени (выдержка) WDTC, разрешение сброса от WDTC процессора и периферии, запрет/разрешение сторожевого таймера и другие настройки. Как WDTC отключается, я уже показывал. А вот пример, как настроить работу WDTC на выдержку 5 секунд (это делается в том же месте проекта board_lowlevel.c -> LowLevelInit):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
/* Инициализация Watchdog
 *************************/
    //AT91C_BASE_WDTC->WDTC_WDMR = AT91C_WDTC_WDDIS;
// WDIDLEHLT = 1 в нерабочем режиме WDT не работает
// WDDBGHLT = 1 при отладке WDT не работает
// WDD = WDT_TIME * 256 5 секунд
// WDDIS = 0 WDT разрешен
// WDRPROC = 0 полный сброс - и процессора и периферии
// WDRSTEN = 1 разрешение сброса от WDT
// WDFIEN = 0 запрет прерывания от WDT
// WDV = WDT_TIME * 256 5 секунд
    AT91C_BASE_WDTC->WDTC_WDMR =  AT91C_WDTC_WDIDLEHLT
                                | AT91C_WDTC_WDDBGHLT
                                | ((WDT_TIME*256) << 16)
                                | AT91C_WDTC_WDRSTEN
                                | (WDT_TIME*256);

WDTC_WDSR   регистр состояния (только чтение). Там есть только два бита - WDERR и WDUNF. Для работы с WDTC чтение регистра WDTC_WDSR не нужно.

[Обработчик прерывания для таймера WatchDog AT91SAM7X]

Прерывание от таймера WatchDog (WDTC) может быть в двух случаях - прерывание по ошибке (флаг WDERR в регистре WDSR) и прерывание по переполнению (условие срабатывания защиты WatchDog, флаг WDUNF в регистре WDSR). Событие ошибки - это когда WDTC попытались неправильно сбросить, а переполнение - когда никто WDTC не сбросил - это и означает, что система зависла, и обычно по такому событию настраивается перезагрузка. Но иногда нужно, чтобы вместо перезагрузки по событию переполнения срабатывало прерывание. Итак, чтобы начало работать прерывание от WDTC, нужно сделать следующие два простых действия.

1. Настроить WDTC на срабатывание прерывания. Обычно код настройки встраивается код процедуры LowLevelInit файла at91lib\board\board_lowlevel.c. Вот пример такой настройки (показан только кусок настройки WDTC):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
void LowLevelInit ( void )
//Выполняется низкоуровневая инициализация чипа.
{
    ...
    /* Инициализация ватчдога. */
// WDIDLEHLT = 1 в нерабочем режиме WDT не работает
// WDDBGHLT = 1 при отладке WDT не работает
// WDD = WDT_TIME * 256 5 секунд
// WDDIS = 0 WDT разрешен
// WDRPROC = 0 полный сброс - и процессора и периферии
// WDRSTEN = 0 запрет сброса от WDT
// WDFIEN = 1 разрешение прерывания от WDT
// WDV = WDT_TIME * 256 5 секунд
    AT91C_BASE_WDTC->WDTC_WDMR =  AT91C_WDTC_WDIDLEHLT
                                | AT91C_WDTC_WDDBGHLT
                                | ((WDT_TIME*256) << 16)
                                | AT91C_WDTC_WDFIEN
                                | (WDT_TIME*256);
    ...
}

2. Написать код обработки прерывания WatchDog. Вешается этот код на обработчик прерывания PIT или DBGU (System Interrupt). Вот пример такого обработчика:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
void ISR_Pit(void)
// System Interrupt Handler
{
    if (AT91C_BASE_WDTC -> WDTC_WDSR & AT91C_WDTC_WDUNF)
    {
        //сработало переполнение WDTC - система зависла
        printf ("Watchdog Underflow%s", CRLF);
        // далее действия по обработке зависания, и обычно перезагрузка системы
    }
    if (AT91C_BASE_WDTC -> WDTC_WDSR & AT91C_WDTC_WDERR)
    {
        //произошла ошибка при работе с WDTC (подробности - см. даташит)
        printf ("Watchdog Error%s", CRLF);
        // далее действия по обработке ошибки
    }
 
    //Далее идет стандартный код обработки DBGU и PIT (если он нужен)
    ...
}

[Ссылки]

1. Сторожевой таймер AT91SAM7X.