October 8, 2018

Сторожевой пес для CX8090

Сторожевой таймер (или watchdog, или вотчдог для краткости в дальнейшем) нужен в критических ситуациях. В щите сбора данных — может быть, а вот в станке, я ставлю его необходимость под сомнение. Тем не менее, вопросы типа "боюсь, что программа зависнет" или более самоуверенные — "что, если зависнет контроллер", задают постоянно. В CX8090 такая функция есть.

И была. Изначально. А вот библиотека для работы с ним, появилась не сразу.
Попробуйте не использовать сторожевой таймер в своих проектах. Неправильное применение сторожевого таймера может привести к бесконечной перезагрузке ПЛК, что в результате приведет к выходу из строя контроллера. Это печально само по себе, и вдвойне грустно, потому что случай не гарантийный.

Все испытания проводились на новом ПЛК CX8090, произведенном 2 августа 2018 года.

История библиотеки


Я беру с полки CD-диск c официальным дистрибутивом TwinCAT 2 за 2013 год и открываю исходный код библиотеки TcSystemCX80xx.lib, предназначенной специально для CX80xx. Функции вотчдога в ней еще нет.

Диск за 2014 год — функции все еще нет. Версия библиотеки 1.0.2. Да собственно чего тянуть, функция была добавлена только в версии 1.0.3, но на диск попасть не успела или просто тестировалась какое-то время. Вот комментарий разработчика:

2014/02/17 | 1.0.3 | V2.11.0 (Build 2239) | ICH | adding F_CX80xxSetWatchdog


Мне, к сожалению, не удалось найти старые библиотеки на сайте Бекхофф, хотя я бы с удовольствием покопался в истории промышленного софтостроения. Так что, конкретно указать на версию TwinCAT я не могу. Начиная с версии TwinCAT 2.11 (build 2249) внезапно появляется версия 1.0.6, которая до сих пор лежит на сайте Бекхофф. И это не самая последняя версия, так как с последними дистрибутивами TwinCAT 2 распространяется версия библиотеки 1.0.7.

За все это время функциональный блок вотчдога не изменялся ни разу.


Как спустить сторожевого пса


Библиотека содержит несколько полезных функций, работающих только на CX80xx. По сути, все эти функции оперируют внутренними адресами железа, битовыми масками, байтами и другими низкоуровневыми штуками процессора и памяти контроллера. Нас такие подробности должны интересовать разве что из любопытства, поэтому о нюансах читайте чуть позже. А сейчас, непосредственно функция сторожевого таймера:

FUNCTION F_CX80xxSetWatchdog : BYTE
VAR_INPUT
    tTimeOut : TIME; (* Watchdog TimeOut Time *)
    bEnable  : BOOL; (* Enable / Disable Watchdog *)
END_VAR

Это функция. Она не требует инстанциирования как функциональный блок. Просто регулярно вызывайте ее с (bEnable = TRUE), когда нужна функция сторожа; или вызовите блок единственный раз с (bEnable = FALSE), если нужно отключить таймер. Другими словами, блок требует регулярного вызова каждый цикл или по крайней мере не реже, чем это задано в параметре tTimeOut.
По идее функции должны быть сосредоточены сами в себе и ни коим образом не влиять на глобальное окружение. В данном же случае, такое применение уместно, так как: (а) функция всегда существует в единственном экземпляре и всегда требует ввода двух обязательных параметров; (б) другие программные единицы TwinCAT 2 так не умеют. В TwinCAT 3 мы могли бы попробовать шаблон Singleton.
На входе функции всего два параметра: (1) время реакции tTimeOut типа TIME, который может принимать значения в диапазоне от 500 миллисекунд (не меньше) до 127 секунд (не больше); (2) и параметр активациия/деактивации таймера bEnable типа BOOL. Куда уж проще.

После вызова функции, с целью активации сторожевого таймера (bEnable = TRUE), сразу же, в этом же цикле, начиная со следующей строчки после вызова функции, начинается контроль выполнения программы. Теперь, если программа зависнет и цикл не завершится в отведенное ему время, или если за цикл произойдет критическая ошибка, или сработает точка останова(!), если вы просто забудете регулярно вызывать функцию вотчдога — таймер начнет свой отсчет...

...и через заданное время tTimeOut — ПЛК перезагрузится.

После перезагрузки, ПЛК сделает вид, что ничего не произошло. Он будет считать, что загрузился штатным способом и запустится так, как было задано в его текущей системной конфигурации. Если у вас активирован загрузочный проект (boot project), то ПЛК-программа стартует автоматически, запустит вотчдог и, если ваша программа продолжит зависать, то контроллеру наступит бесконечный уроборос: зависание, перезагрузка, автозагрузка программы, зависание...

Из-за постоянной круговерти с загрузкой-переагрузкой, ПЛК рано или поздно может выйти из строя. Почему? Например, потому что скидывает на флэшку PERSISTENT данные или остаются незакрытыми файловые операции операционной системы, или любая другая причина. Не используйте вотчдог, пишите программы аккуратно.

После того как функция отработает, мы получаем на выходе битовую маску типа BYTE, а по сути всего лишь три значимых бита:

(* return enable state *)
nRetVal.0           := bEnabled;
nRetVal.1           := bMinWDTimeAct;
nRetVal.2           := bMaxWDTimeAct;
F_CX80xxSetWatchdog := nRetVal;

Далее номера битов в байте выхода:
  • бит 0 — установлен, когда таймер запущен.
  • бит 1 — установлен, когда время таймаута меньше или равно 500 миллисекундам.
  • бит 2 — установлен, когда время таймаута больше или равно 127 секунд 500 мс.


Что внутри будильника?


Заглянем в недра тундры. Вотчдог хранит время таймаута в 500 миллисекундных интервалах. 127 секунд и 500 миллисекунд или 127 500 — это как раз 255 раз по 500. Затем это значение сдвигается на восемь бит "влево" в старший байт, а в младшем байте выставляются биты разрешения или запрещения.

Выбор таких временных интервалов в 500 мс возможно связан с "медленным" и неточным таймером вотчдога в архитектуре ARM9, который непосредственно встроен в железо. Медленный он в целях энергосбережения и потому что работает сам по себе, обходится без "точных кварцев" и других дорогостоящих электронных компонентов. Это очень топорное и упрощенное объяснение, но его вполне достаточно для текущего уровня поста.

После формирования слова управления, оно записывается в память через указатель на специальный неизменяемый адрес памяти. Именно по этой причине функция активируется сразу же, не дожидаясь окончания программного цикла: она влияет непосредственно на железо архитектуры.

Интересно, что вотчдог отключается не каким-то там специальным битом (его установкой или сбросом), а установкой максимально возможного значения временного интервала = 127с 500мс. Именно это происходит, когда мы вызываем функцию со сброшенным флагом разрешения (bEnable = FALSE).


Практика


Практиковаться будем на трех примерах ниже, каждый из которых так или иначе останавливает исполнение программы. Конкретнее, это точка останова, деление на ноль и бесконечный цикл. Набрасываете требуемый номер шага в переменную state и через 3 секунды получаете реакцию вотчдога на соответствующую критическую ситуацию:

PROGRAM MAIN
VAR
    state, cnt : INT;
    z : int := -1;
END_VAR

(*[...]*)

F_CX80xxSetWatchdog(tTimeOut := T#3s, bEnable := TRUE);

CASE state OF
0:
    F_CX8090_LED_WD(eMode := eLED_RED_OFF);
    F_CX8090_LED_ERR(eMode := eLED_RED_OFF);

    z := z + 1;
    state := 10;
    
10: (* breakpoint *)
    ;
    
20: (* zero division *)
    z := state / z;
    
30: (* inf. loop *)
    F_CX8090_LED_WD(eMode := eLED_RED_FLASHING_200ms);
    WHILE TRUE DO
        ;
    END_WHILE
    F_CX8090_LED_WD(eMode := eLED_RED_OFF);
END_CASE

cnt := cnt + 1;


Помигаем светодиодом на прощанье


На корпусе CX8090 есть индикатор WD (=Watchdog), который в документации заявлен как не используемый в прошивке для CX8080 / CX809x. Тем не менее у нас есть механизм для управления этими лампами: функция F_CX8090_LED_WD управляет светодиодом WD, F_CX8090_LED_ERR — управляет светодиодом ERR (для ошибок). Доступны несколько режимов свечения и мигания с различной скважностью: покопайтесь в типах данных библиотеки.

Касательно отключения индикаторов, то оба индикатора двухцветные. Поэтому не важно какой цвет индикатора выключать: красный (eLED_RED_OFF) или зеленый (eLED_GREEN_OFF), отключается сразу весь светодиод, то есть оба цвета свечения пропадают одновременно.

Вызвать функцию достаточно один раз. Отработает она в том же цикле и даже раньше его завершения. В дальнейшем, работой индикаторов управляют какие-то внутренние цепи "железа", совершенно отвязанные от TwinCAT, поэтому блоки не требуют вызова каждый цикл. А вообще, можно сделать так:

F_CX8090_LED_WD(eMode := eLED_RED_FLASHING_200ms);

WHILE itHaveToContinueThen DO
    ; (* что-то делаем в цикле *)
END_WHILE

F_CX8090_LED_WD(eMode := eLED_RED_OFF);


И еще раз, постарайтесь не использовать сторожевой таймер.

No comments

Post a Comment

Note: Only a member of this blog may post a comment.