PSV100 писал(а):
Да хоть любой протокол, к примеру.
Протокол -- другое дело.
Если бы вы привели в пример "TCP стек", то, разумеется, на Д-схемах там делать нечего.
Вернее, может и можно что-то сделать, но целиковый TCP стек на Д-схемах это, скорее, из разряда "дичь".
PSV100 писал(а):
В рамках затронутой здесь SWITCH-технологии был хороший примерчик:
http://www.kit-e.ru/articles/circuit/2007_08_170.phpЭто не ахти какой плохой пример.
И вот почему:
1) В этой статье единственным преимуществом SWITCH-подхода предъявляется то, что
SWITCH-статья писал(а):
Одно из них уже было приведено: при работе драйвера клавиатуры основная программа не испытывает существенных задержек, связанных с работой антидребезгового алгоритма.
Но это преимущество не имеет никакого отношения к SWITCH подходу. Вообще никакого.
Там приведён пример, где "обычный подход" приводит к задержкам основной программы? Не приведён. И вовсе непонятно почему такие задержки будут появляться. В качестве примера приводится ссылка на реализацию на ассемблере.
Это же вообще несравнимые вещи.
2) В этой статье код на C предлагается писать вручную. Занавес. Это и есть код на switch-case, в котором легко ошибиться и который тяжело поддерживать. "key_state=3". О, да, эта строка говорит о многом.
3) На "рис. 3" приведена временная схема работы автомата. К ней вопрос: а что, если кнопка *всегда* дребезжит?
Как её должен расценивать автомат? Ну, это всё к слову о том, что от того, как нужно реагировать на дребезг существенно зависит алгоритм обработки.
Ну да ладно. Давайте возьмём и напишем алгоритм как он есть? Жалко что-ли?
Вот он. И этот код гораздо компактнее SWITCH-кода!
Код:
WHILE TRUE LOOP
(* Ждём нажатия клавиши *)
WHILE key_code=0 LOOP
ПАУЗА;
END_WHILE;
_key_code := key_code; (* запоминаем что было нажато *)
ПАУЗА debounce;
IF key_code <> _key_code THEN
(* после антидребезга нажата другая -- ну его нафиг, начинаем сначала *)
CONTINUE;
END_IF;
SEND_MESSAGE(оба-на-клавиша-нажата);
(* Вот здесь по задаче нужно сделать задержку длительностью first_delay,
но при отпускании клавиши нужно начать сначала.
Можно сохранить текущее время и сверять его, но в МЭК61131 есть готовые блоки.
В данном случае, достаточно TOF: http://plc24.ru/tajmery-v-codesys/ *)
delayTOF(IN := FALSE); (* сбрасываем таймер *)
REPEAT
ПАУЗА;
delayTOF(IN := key_code=_key_code, PT := first_delay);
UNTIL delayTOF.Q
END_REPEAT;
IF key_code <> _key_code THEN (* технически, этот IF можно поместить и внутрь предыдущего WHILE, но не суть *)
(* Если клавиша поменялась -- всё по новой *)
CONTINUE;
END_IF;
(* А теперь нужно шарашить импульсы и интервалом auto_repeat *)
WHILE key_code=_key_code LOOP
(* Здесь, кстати, в исходной постановке задачи непонятно что должно быть, если first_delay меньше auto_repeat *)
SEND_MESSAGE(оба-на-клавиша-нажата);
(* Тут можно было бы сделать и TOF (или вообще BLINK), но сделаем вариант в лоб *)
следующий_повтор := TIME() + auto_repeat;
WHILE key_code=_key_code AND TIME()<следующий повтор THEN
ПАУЗА;
END_WHILE;
END_WHILE;
END_WHILE;
Разумеется, такое гораздо проще и понятнее, чем исходный вариант на switch-case.
Тут некая многословность из-за всяких END_WHILE, но оно несильно портит картину.
Но, повторюсь, по сути, это 1-в-1 повторение "хвалёного SWITCH-кода" с той поправкой, что вместо "case 2: ... key_state=3" используется одна строка "ПАУЗА;"
Вот и вся разница. Но читать код на паузах гораздо проще (понятно что в каком порядке выполняется) и писать его гораздо проще (можно не бояться накосячить в индексах).
Это же просто последовательные действия. Если что-то становится "слишком сложным", то можно взять и вычленить фрагмент в отдельную процедуру. Вообще никаких проблем.
PSV100 писал(а):
Рисовать Д-схему на "паузах" в таких случаях у меня нет желания (да и в целом Д-схему).
Мой вариант на ST наверняка можно 1-в-1 переложить на Д-схемы и там тоже всё нормально будет.
В целом, оно будет более-менее похоже и на "рис. 2".