Когда мы говорили о таймерах, то упоминали так называемый «сторожевой» таймер (англ. watchdog). Для чего он нужен? Допустим, вы попытались обратиться в запрещённую или не существующую область памяти (при условии что MemManage_Fault()
вы сами не разрешали) — сработает внутреннее прерывание и начнётся выполнение функции HardFault_Handler()
, в которой по-умолчанию — вечный цикл. Другими словами устройство превратиться в кирпич и перестанет реагировать. Вы наверняка встречались с такой проблемой ранее, скажем, в телефоне. К тому же, вы сами могли допустить ошибку и попасть в вечный цикл случайно.
xxxxxxxxxx
for (uint8_t i = 0; i < 1000; i++) { // i = [0 ... 255]
// your code here
}
Единственный способ вывести устройство из такого состояния — отключить питание и включить его снова.
Have you tried turning it off and on again? // The IT Crowd
Для решения подобных ситуаций служит сторожевой таймер. Причём он может быть двух видов.
Если устройство было перезагружено из-за сторожевого таймера (например, из-за IWDG), то в специальном регистре RCC появится флаг.
xxxxxxxxxx
// cmsis
if ((RCC->CSR & RCC_CSR_IWDGRSTF) == RCC_CSR_IWDGRSTF) {
RCC->CSR &= ~RCC_CSR_IWDGRSTF;
}
// std periph
if (RCC_GetFlagStatus(RCC_FLAG_IWDGRST) != RESET) {
// do something useful and then clean the flag
RCC_ClearFlag(RCC_FLAG_IWDGRST);
}
Таймер IWDG тактируется от собственного источника — низкоскоростной RC-цепочки (LSI). Потому-то он и называется независимым — его работа никак не зависит от основной программы (тактирующего сигнала ядра). Однако, данный источник очень не стабилен по частоте (может плавать в пределах ±9% в зависимости от температуры), да и к тому же при заявленной частоте 40 кГц может иметь реальную в переделах от 30 до 60 кГц. Таймер WWDG берёт тактовый сигнал от шины APB1 с некоторым делителем. Мы рассмотрим работу только с независимым сторожевым таймером, применяя стандартную библиотеку периферии.
Таймер IWDG имеет 12 разрядов, т.е. максимальное значение счётчика — 212 - 1 = 0xFFF
. Так же мы можем выставить предделитель для LSI от 4 до 256. Допустим что нам требуется перезагрузить устройство, если в течении 1,5 секунды оно не сбросила счётчик к первоначальному значению. Возьмём за частоту LSI — 40 кГц. Тогда при предделителе равном 256 частота работы таймера будет примерно 156 Гц. Значит, что бы подождать 1.5 секунды нужно 156 * 1,5 = 234 такта. Настроим IWDG.
xxxxxxxxxx
void iwdg_init(void) {
// включаем LSI
RCC_LSICmd(ENABLE);
while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);
// разрешается доступ к регистрам IWDG
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
// устанавливаем предделитель
IWDG_SetPrescaler(IWDG_Prescaler_256);
// значение для перезагрузки
IWDG_SetReload(234);
// перезагрузим значение
IWDG_ReloadCounter();
// LSI должен быть включен
IWDG_Enable();
}
Пример использования
xxxxxxxxxx
void main(void) {
iwdg_init();
while(1) {
IWDG_ReloadCounter();
}
}
Код можно найти на GitHub: CMSIS, SPL.
Назад | Оглавление | Дальше
KickTheDog()
— он возмутился и сказал что не позволит пинать собак в его компании. Пришлось дать функции другое название -- PetTheDog()
, т.е. «гладить собаку».↩↩