|
|
  |
State machine, Приведите примеры реализации |
|
|
|
Mar 5 2009, 17:50
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Цитата(TMX @ Mar 5 2009, 20:09)  там Martin Gomez вручную забивает таблицу переходов no comment  Цитата в виде номера, функции и указателя на функцию. по номеру выбирается ссылка на функцию состояний и вызывается сама функция. Реализация требует и память и стек.
Писать программу без картинки - если получается, тогда зачем картинка? 1. А что? Дорого? Зато может способствовать уменьшению количества функций 2.Например, для повышения уровня языка реализации до проблемно-ориентированного http://www.citforum.ru/internet/xml/graphml/
|
|
|
|
|
Mar 6 2009, 07:35
|
Частый гость
 
Группа: Свой
Сообщений: 100
Регистрация: 19-01-05
Из: Москва
Пользователь №: 2 064

|
Цитата(_Pasha @ Mar 5 2009, 20:50)  no comment  Возможно, я не совсем понятно выразился - Gomez забивает не всю таблицу переходов автомата, а только одномерный массив jump table, указателей на функции состояний. Там в самом начале листинга можно ее увидеть. В любом случае это не дает преимуществ по сравнению с прямым присвоением: Код void(*state_pointer)(void) = RED_state; void main (void) { while(1) { state_pointer(); } }
void RED_state (void) { if (timer_flag) { output_control(RED_LAMP, OFF); output_control(GREEN_LAMP, ON); start_timer (GREEN_DELAY); state_pointer = GREEN_state; return; } if (button_flag) ...................... } Преимущества: работает быстро, фактически как вычисляемое GOTO Недостатки: требует стек для вызова функций, при реализации в прерываниях вызова по ссылке компилятор обычно сохраняет весь контекст, это долго и требует много стека. По поводу объема: в 8-битных процессорах если указатель занимает 2 байта, то каждое присвоение требует 2 команды ассемблера. Соответственно для автомата на 50 состояний и 150 переходов потребуется 300 команд в ПЗУ (600Б для AVR), для switch с переменной состояния типа char потребуется 300Б ПЗУ на переходы, для Gomez-технологии потребуется 300Б на переходы и 100Б на jump_table, итого 400Б. Для 16-ти и 32-х битных объем switch и присвоения указателей практически одинаков, а для Gomeza все равно дополнительно требуется место для jump table. Цитата 1. А что? Дорого? Зато может способствовать уменьшению количества функций 2.Например, для повышения уровня языка реализации до проблемно-ориентированного http://www.citforum.ru/internet/xml/graphml/1. За счет чего? C использованием switch вообще одна функция. 2.Обычно проблему снаала описывают, а потом реализуют. если еще есть интерес, могу продолжить про реализации...
|
|
|
|
|
Mar 6 2009, 14:35
|
Частый гость
 
Группа: Свой
Сообщений: 100
Регистрация: 19-01-05
Из: Москва
Пользователь №: 2 064

|
Цитата(Diz @ Mar 6 2009, 16:20)  Наверное, jumptable может быть полезен для сохранения текущего состояния в eeprom и восстановления после сбоя. Пишем лишь индекс, а не потенциально опасный указатель. сомнительно. все равно потом по указателю функция вызывается, для таких случаев обычно пишут лог переходов в том или ином виде, ну и список разрешенных переходов (собственно, он у Гомеса есть). реализовать кооперативную многозадачность с помощью конечных автоматов - собственно, этим я хотел закончить про реализации.
|
|
|
|
|
Mar 10 2009, 13:42
|
Частый гость
 
Группа: Свой
Сообщений: 100
Регистрация: 19-01-05
Из: Москва
Пользователь №: 2 064

|
Недостатки прямого присваивания значения переменной состояния, типа Код state = NEW_state в том, что при большом количестве ветвлений можно случайно пропустить присваивание. Одним из методов контроля на этапе компиляции является следующий: функция состояния возвращает значение следующего состояния Код void main (void) { static char state = STATE_X; while(1) { state = state_function(state); } }
char state_function (char state) { switch(state) { case STATE_X: if (event) { return STATE_Y; } else return STATE_X; case STATE_Y: ... case STATE_Z: ... } } кроме некоторого контроля за присваиванием, такой подход не дает никаких премуществ. Просто на практике не всегда бывает возможно обеспечить полноту тестов.
|
|
|
|
|
Mar 10 2009, 16:14
|
Гуру
     
Группа: Свой
Сообщений: 2 702
Регистрация: 14-07-06
Пользователь №: 18 823

|
Цитата(TMX @ Mar 6 2009, 18:35)  сомнительно. все равно потом по указателю функция вызывается, Индекс проще проверит на валидность. Всего лишь на максимум. Индекс удобно использовать при генерации события по смене состояния. Индекс удобно использовать для возврата в старое состояние. Кароч, я использую индексы и массивы функций. Иногда двумерный массив functions[state][event] в каком-нить менюшном сервисе.
--------------------
Уходя, оставьте свет...
|
|
|
|
|
Mar 10 2009, 16:57
|
Частый гость
 
Группа: Свой
Сообщений: 100
Регистрация: 19-01-05
Из: Москва
Пользователь №: 2 064

|
Цитата(Dog Pawlowa @ Mar 10 2009, 19:14)  Индекс проще проверит на валидность. Всего лишь на максимум. Индекс удобно использовать при генерации события по смене состояния. Индекс удобно использовать для возврата в старое состояние.
Кароч, я использую индексы и массивы функций. Иногда двумерный массив functions[state][event] в каком-нить менюшном сервисе. с первым согласен. Только какой смысл в такой проверке? Проверка в рантайме - от какого бага она спасает? Если на этапе отладки - использование только поименованных констант не позволит вызвать неописанное состояние. С другой стороны, где гарантия, что индексы идут подряд. со вторым и третьим - не могу ничего сказать. Не могли бы вы написать пример реализации поподробнее? Для одномерного и двумерного массива? Особенно, для functions[state][event], что является индексом массива? я считаю, что удобство понятие не только субъективное. Удобно - когда логическая ошибка проявляется как можно раньше. Например, на этапе компиляции (стоимость обнаружения) Удобно - когда при введении нового состояния его надо прописывать как можно в меньшем количестве списков (связность). И компилятор следит за правильностью (стоимость). Неудобно - когда в нескольких списках описание состояния должно быть с одинаковым номером (связность). Цитата(-=TRO=- @ Mar 10 2009, 19:47)  Внутри программируемой логики собирают микропроцесоры и пишут для них программы. На микроконтроллерах пишут программы эмулирующие логику(конечные автоматы). Походу использовать компоненты по назначению сегодня не модно. насколько я знаю, в 70-х годах было мнение, что наиболее перспективная парадигма в программировании - автоматный подход. "Лучше забивать молотком шурупы, чем закручивать отверткой гвозди"
|
|
|
|
|
Mar 10 2009, 17:36
|
Гуру
     
Группа: Свой
Сообщений: 2 702
Регистрация: 14-07-06
Пользователь №: 18 823

|
Цитата(TMX @ Mar 10 2009, 20:57)  с первым согласен. Только какой смысл в такой проверке? В некоторых случаях смысл есть. Например, после ресета можно вернуться в нужную точку. Делал прибор, который, зараза, был чувствителен к ESD. Вот и пришлось решать проблему программно. Цитата(TMX @ Mar 10 2009, 20:57)  Не могли бы вы написать пример реализации поподробнее? Для одномерного и двумерного массива? Да все тупо... CODE // Определения состояний и функций
#define stRestart 0 #define f0 fRestart
#define stSelfTest 1 #define f1 fSelfTest
#define stWaiting 2 #define f2 fWaiting
// массив функций const VECTORS function[stQty] = { f0, f1, f2, f3,..}
//основной цикл процесса
event=GetTimerEvent(); if (!event) event=GetOtherEvent(); // тут собыий много может быть ... if (!event) if (state-old_state) event=evNew; function[state](); event=0;
// пример функции
void fRestart(void) { switch (event) { case evNew: .... GetDateAndTime(); SetLcdPos(1,2); printf("%s",rtc_date); Old(); break;
case ev500ms: switch (restart_counter) { ... сase 4: if (!LowPowerStart) state= stSelfTest; else {SetLcdPos(1,2); printf(msgStartLowVoltage[lang]); } } break; if (restart_counter<4) restart_counter++; break;
case evUpDn: state=stStartUserMenu; break; } }
Причина редактирования: Уменьшение видимого размера цитаты исходника.
--------------------
Уходя, оставьте свет...
|
|
|
|
|
Mar 11 2009, 07:18
|
Гуру
     
Группа: Свой
Сообщений: 2 702
Регистрация: 14-07-06
Пользователь №: 18 823

|
Цитата(ReAl @ Mar 10 2009, 22:51)  Тада, и я это предложение даже проиллюстрировал уже... LOL : чуть дальше "Но реально не применяю :-), как-то не жмёт пока обычный switch()... " Вот и меня не жмет. А switch или таблица - это вопрос качества монитора и разрешения Если все case не помещаются на два-три экрана, то лучше бить на таблицу. Если помещаются, то лучше помолиться за IAR, который при оптимизации ту же таблицу сам нарисует.
--------------------
Уходя, оставьте свет...
|
|
|
|
|
Mar 11 2009, 08:24
|
Частый гость
 
Группа: Участник
Сообщений: 84
Регистрация: 1-08-06
Пользователь №: 19 250

|
Цитата(TMX @ Mar 6 2009, 17:35)  сомнительно. все равно потом по указателю функция вызывается, для таких случаев обычно пишут лог переходов в том или ином виде, ну и список разрешенных переходов (собственно, он у Гомеса есть). Пример взят из реальной задачи - нужно было при каждой смене состояния сохранять его во внешней eeprom, чтобы восстановить при пропадании питания. Лог переходов писать смысла нет, так как не важно, каким путем мы попали в данное состояние - важен сам факт попадания. Индекс - один байт, пишется атомарно. При загрузке легко его проверить на попадание в диапазон разрешенных состояний. Если будет восстановлено неверное состояние, неприятно, но не так разрушительно как вызов функции по неверному указателю. Еще одна особенность - прошивка могла быть обновлена, и обработчики могли бы лежать на других адресах.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|