Продолжим тему сторожевых таймеров в микроконтроллерах STM32.
В уроке 138 мы уже подробно познакомились с назначением сторожевого таймера (Watchdog Timer или WDT), для чего он применяется.
Также мы узнали, как работает в контроллере STM32 независимый сторожевой таймер (Independent watchdog или IWDG), а также поработали с ним и на практике.
А ещё мы узнали, что сторожевых таймеров у контроллера STM32 два, и кроме независимого существует ещё и оконный сторожевой таймер (Window watchdog или WWDG).
Но мы с ним познакомились очень кратко, а теперь пришло время узнать поподробнее, как он работает, а также проверить его в работе на живой схеме.
Для изучения работы оконного сторожевого таймера (Window watchdog) мы возьмём тот же контроллер STM32F103.
WWDG в данном контроллере — это оконный сторожевой таймер, счётчик которого считает сверху вниз, начиная со значения, которое мы вносим в определённый регистр, но не до нуля а до 63 (0x3F). Только кроме значения, с которого считает WWDG, и значения 0x63, до которого считает данный сторожевой таймер, существует ещё одно промежуточное значение, находящееся между данными величинами. Оно также является задаваемой величиной и, таким образом, образуется окно между данной величиной и значением 0x3F, поэтому данный сторожевой таймер и является оконным. Подобно IWDG, сторожевой таймер WWDG также, досчитав до 0x3F, даст команду на перезагрузку системы, если же мы, конечно, не перезагрузим данный оконный сторожевой таймер своевременно. Только перезагрузить мы должны его не только не позже, чем его счётчик досчитает вниз до 0x3F, но ещё и не раньше, чем он досчитает до промежуточной величины, которая также заносится в определённый регистр. Данная величина поэтому должна находиться в интервале от числа 0x3F до заданного значения счётчика, с которого он начинает обратный отсчёт. Таким образом, мы получаем возможность проверки определённых временных интервалов хода нашей программы.
Откроем в Reference Manual на данный контроллер раздел WWDG и посмотрим блок-схему оконного сторожевого таймера
Здесь мы можем уже наглядно наблюдать, как происходит работа WWDG.
Тактовые сигналы счётчика идут через первый предделитель с постоянным коэффициентом деления 4096 с шины PCLK1 на второй, регулируемый предделитель, значение коэффициента деления которого мы можем устанавливать с помощью битов регистра WDGTB. После второго предделителя тактовые сигналы попадают на счётчик обратного отсчёта.
Число, которое находится в битах T6:T0 регистра WWDG_CR после старта оконного таймера начинает инкрементироваться как только оно достигнет значения 0x3F, то есть как только бит T6 очистится, на один из мультиплексоров пойдёт сигнал о перезагрузке системы. Но на то он и мультиплексор, что сигнал о перезагрузке он может получить не только от очистки бита T6. Аналогичный сигнал он может получить от компаратора, в котором сравниваются значения битов T6:T0 регистра WWDG_CR со значениями битов W6:W0 регистра WWDG_CFR. И, если первое число больше второго, то есть если счётчик обратного отсчёта ещё не досчитал до значения первой границы, а в это время мы пытались перезагрузить оконный сторожевой таймер, то мультиплексор и получит данный сигнал, который также заставит систему перезагрузиться.
Также в технической документации процесс работы оконного сторожевого таймера показан и на графике
Также объясняется, как можно рассчитать время работы WWDG от старта до окончания счёта и перезагрузки
В отличие от независимого сторожевого таймера IWDG, WWDG уже может работать и с прерываниями. Правда, в рамках данного урока мы практически с ними работать не будем, так как я пока не придумал, как их использовать. Возможно, впоследствии мы ещё с ними поработаем. Тем не менее как они устроены, мы узнаем непременно. Судя по технической документации на контроллер, прерывания от оконного сторожевого таймера срабатывают тогда, когда счётчик досчитает до 0x40, то есть за 1 тик до перезагрузки системы. Скорее всего, данные прерывания нужны для того, чтобы предупредить о том, что счётчик практически уже досчитал до конца и у нас произошло зависание. Тем самым можно это использовать для отладки программ, чтобы в этот момент всё же перезагрузить таймер и не перезагружать систему, но также и узнать, что у нас что-то пошло не так. Либо всё же систему перезагрузить, но на какую-то ножку порта выдать сигнал, гласящий о том, что у нас произошло зависание.
Теперь давайте познакомимся поближе с регистрами таймера WWDG.
Первый регистр — управляющий регистр
Рассмотрим назначение всех битов данного регистра.
WDGA (Activation bit): бит активации. Данный бит устанавливается программно для того, чтобы запустить оконный сторожевой таймер. Сбрасывается аппаратно после перезагрузки системы
0 — WWDG отключен,
1 — WWDG включен.
T[6:0] (7-bit counter): данные биты содержат число, с которого WWDG начинает обратный отсчёт. Мы заносим данное число, когда запускаем таймер, а также мы заносим сюда число и в момент перезагрузки таймера.
Следующий регистр — регистр конфигурации
Назначение битов данного регистра:
EWI (Early wakeup interrupt): бит разрешения прерывания раннего пробуждения. Прерывания срабатывают, когда счётчик досчитает до 0x40, то есть за 1 тик до перезагрузки системы. Данный бит мы можем только установить, чтобы разрешить прерывания от WWDG. Сбрасывается он аппаратно после перезагрузки системы.
WDGTB[1:0] (Timer base): биты установки коэффициента деления предделителя оконного сторожевого таймера.
Предделитель в зависимости от установок данных битов будет следующий (с учётом коэффициента деления первого предделителя)
W[6:0] (7-bit window value): биты установки значения верхней границы таймера для образования окна между данной границей и числом 0x3F. Когда счёт будет происходить в данном окне, мы и должны перезагрузить оконный сторожевой таймер для предотвращения перезагрузки системы.
Следующий регистр — регистр состояния оконного сторожевого таймера
В данном регистре присутствует только один бит
EWIF (Early wakeup interrupt flag): флаг прерывания раннего пробуждения. Устанавливается аппаратно, когда счётчик достигнет числа 0x40, если включены прерывания. Сбрасывается аппаратно, то есть записать мы в него можем только 0. Также данный бит установлен, если прерывания не включены.
Теперь настало время наши знания, которые мы получили в теоретической части, подтвердить практикой, так как теория без практики не способствует усвоению материала.
Схема занятия та же самая, что и в уроке 138
А проект нашего занятия мы также сделаем из проекта урока 138 с именем IWDG01 и назовём его, соответственно, WWDG01.
Откроем проект в Cube MX и отключим сначала таймер IWDG
Включим оконный сторожевой таймер
Перейдём в Clock Configuration и, чтобы максимально увеличить время счёта WWDG, уменьшим частоту тактирования
Для рассчёта времени счёта оконного сторожевого таймера мы будем использовать частоту 4 мегагерца (PCLK1).
Перейдём в Configuration и для начала уменьшим время срабатывания таймера TIM2, так как WWDG на медленные интервалы не рассчитан, не забывая при этом также и о том, что мы уменьшили частоту тактирования нашего контроллера
Теперь таймер 2 будет отчитывать интервалы времени примерно по 400 милисекунд.
Настроим WWDG
Мы включили также и прерывания для оконного сторожевого таймера. Поэтому включим и глобальные прерывания, которые, скорее всего, даже включатся сами
Сохраним проект, сгенерируем проект для System Workbench, откроем его там, зайдём в настройки, установим уровень оптимизации в 1 и удалим отладочные настройки при их наличии.
Попробуем собрать проект. Если всё нормально собралось, то продолжим.
Посмотрим, как у нас инициализируется наш оконный сторожевой таймер.
Откроем файл main.c и в теле функции main() зайдём в вызываемую функцию инициализации WWDG MX_WWDG_Init.
Там инициализируются поля структуры и затем вызывается уже библиотечная функция HAL_WWDG_Init в которую и передаётся адрес данной структуры.
Поэтому зайдём внутрь этой функции и посмотрим, что здесь происходит.
Вызывается сначала функция HAL_WWDG_MspInit, которая почти пустая, там только деинициализация.
Затем записываются соответствующие биты в регистр WWDG_CR. В данном регистре устанавливается бит запуска счётчика WWDG_CR_WDGA (7-й бит), а в младшие шесть бит — значение счётчика обратного отсчёта.
Затем записываются биты регистра WWDG_CFR, которые состоят из бита разрешения прерываний EWI (9-й бит), битов предделителя (биты 7 и 8), а также шесть бит верхней границы счёта.
Вот, собственно, и вся инициализация оконного сторожевого таймера.
Добавим глобальную переменную, которая будет служить флагом первого цикла мигания наших светодиодов
uint8_t tim2_count=0, fl_no_tirst = 0;
Также добавим функцию для перезагрузки сторожевого таймера
1 2 3 4 5 6 7 8 9 |
/* USER CODE BEGIN 0 */ void WWDG_Refresh(void) { if(HAL_WWDG_Refresh(&hwwdg) != HAL_OK) { Error_Handler(); } } /* USER CODE END 0 */ |
В процедуре обработки прерываний от таймера 2 HAL_TIM_PeriodElapsedCallback нам предстоит также немного исправить код.
Первым делом удалим перезагрузку независимого таймера из двух кейсов
//HAL_IWDG_Refresh(&hiwdg);
IWDG->KR = 0x0000AAAAU;
Подключим нашу плату, соберём код и прошьём контроллер. Понятно, что с первого раза он, скорее всего не прошьётся, но как с этим бороться мы уже знаем из урока 138.
После прошивки контроллера мы увидим, что светодиоды будут мигать только два, и то второй недолго
Я думаю, вы уже догадались, почему так происходит.
В следующей части урока мы продолжим знакомство с оконным сторожевым таймером (WWDT), но уже на практике.
Предыдущий урок Программирование МК STM32 Следующая часть
Отладочную плату STM32F103C8T6 можно приобрести здесь STM32F103C8T6
Программатор недорогой можно купить здесь ST-Link V2
Смотреть ВИДЕОУРОК (нажмите на картинку)
Добавить комментарий