jcxz 184 1 февраля, 2017 Опубликовано 1 февраля, 2017 · Жалоба Если честно, немного извращенно. Прототреды и то проще. Еще проще написать функции линейно, и вызывать их по указателю. Состояние - ссылка на нужную функцию. И никаких трат на сохранение-восстановление контекста. Понятно когда таких состояний мало - я тоже не использую такой метод. Я же сразу говорил - это для сложных автоматов оправдано. Когда состояний много, когда переходы между состояниями по разным условиям. А особенно - если представьте: Нужно чтобы после состояния S1 выполнялось несколько состояний S10-S11-S12 с последующим возвратом в основной поток к S2. И такая-же последовательность нужна после скажем S3. С классическим подходом (функциями или switch/case) придётся городить огород с сохранением переменной состояния автомата в отдельной переменной и восстановлением её потом. Здесь же всё просто - просто пишем функцию. И всё. То же самое - циклы с циклическим изменением состояний S1-S2-S3-...S1-.... Опять при таком подходе получается всё много нагляднее и проще чем классически. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
demiurg1978 1 1 февраля, 2017 Опубликовано 1 февраля, 2017 (изменено) · Жалоба Я единственный раз использовал дополнительную переменную-состояние. Next_State. И только из-за того, что получались одинаковые куски кода. Как-то зашел разговор о культуре программирования. Я решил перелопатить код в соответствии с перечисленными участником форума правилами. В итоге перематерился. По сути я не программой занимался, а окультуриванием кода. Гламурность наводил. В особенности меня порой убивает запрет на GOTO. Хотя и понимаю, что GOTO порой опасно использовать. Если где-то возникают трудности, значит нужно пересматривать подход в целом. Это правило меня никогда не подводило. Честно сказать, я не программист в чистом виде. Увлекался электроникой, в определенный момент решил взяться за изучение МК. Так получилось, что моим выбором стали AVR-ки. Начинал с асма. На си переполз 3-4 года назад. Изменено 1 февраля, 2017 пользователем demiurg1978 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
DogPawlowa 0 2 февраля, 2017 Опубликовано 2 февраля, 2017 · Жалоба Получили линейный, хорошо читаемый код. Немного спорно. А при наличии нескольких задач-автоматов SwitchStack() перестает быть симметричной. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 184 2 февраля, 2017 Опубликовано 2 февраля, 2017 · Жалоба А при наличии нескольких задач-автоматов SwitchStack() перестает быть симметричной. В смысле? Если нужно несколько независимых автоматов - для каждого свой стек и всё то же самое. В SwitchStack() просто добавится аргумент - указатель на рабочую переменную/стек. Ну или своя SwitchStack() для каждого автомата. Я единственный раз использовал дополнительную переменную-состояние. Next_State. И только из-за того, что получались одинаковые куски кода. Как-то зашел разговор о культуре программирования. Я решил перелопатить код в соответствии с перечисленными участником форума правилами. В итоге перематерился. Это же тема - изначально про случаи сложных автоматов. Для простых достаточно switch/case. А вот в случае сложных автоматов (а тем более если в них есть повторяющиеся или циклические участки) мой метод значительно упрощает код, он становится легче для анализа и отладки. Значит и матюкаться при его редактировании/отладке приходится меньше. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
demiurg1978 1 2 февраля, 2017 Опубликовано 2 февраля, 2017 (изменено) · Жалоба А вот в случае сложных автоматов (а тем более если в них есть повторяющиеся или циклические участки) мой метод значительно упрощает код, он становится легче для анализа и отладки. Значит и матюкаться при его редактировании/отладке приходится меньше. Хотелось бы посмотреть реальный пример, который не жалко выложить на обозрение и который показывает причину, почему реализовано именно такое решение. Пока не вижу смысла так заморачиваться. Тем более - с накладными расходами. То бишь сохранение-восстановление контекста. Изменено 2 февраля, 2017 пользователем demiurg1978 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 184 2 февраля, 2017 Опубликовано 2 февраля, 2017 · Жалоба Хотелось бы посмотреть реальный пример, который не жалко выложить на обозрение и который показывает причину, почему реализовано именно такое решение. Причины у всех свои. Кто захочет - тот применит. Я тут никого не заставляю. Всё уже более чем разжёвано. И почему такое решение не может быть реализовано?? Вариант с switch/case при кол-ве состояний >=20 много хуже получается и это, имхо, очевидно. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
demiurg1978 1 2 февраля, 2017 Опубликовано 2 февраля, 2017 · Жалоба Вариант с switch/case при кол-ве состояний >=20 много хуже получается и это, имхо, очевидно. В этом случае никто не спорит. И я сам об этом сказал. Что при большом кол-ве состоянии switch-case нечитабелен. Использую функции и индексный переход. В вашем же случае я хотел посмотреть причину применения вашего подхода. Эти переходы по состояниям. Так сказать, наглядный реальный пример. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 184 2 февраля, 2017 Опубликовано 2 февраля, 2017 · Жалоба В этом случае никто не спорит. И я сам об этом сказал. Что при большом кол-ве состоянии switch-case нечитабелен. Использую функции и индексный переход. В вашем же случае я хотел посмотреть причину применения вашего подхода. Эти переходы по состояниям. Так сказать, наглядный реальный пример. Ну например - использовал такой метод в ISR осуществляющем приоритетный арбитраж доступа к нескольким микросхемам (FLASH, FRAM) висящим на шине SPI, и управляющем транзакциями (старт/стоп DMA-пересылок) записи, чтения к этим микросхемам по запросам от нескольких пользовательских каналов. Каждая транзакция состояла из нескольких шагов (подать команду разрешения записи, стартовать передачу блока, завершить обмен, опросить статус чипа и т.п.), которые разбивались на ещё более мелкие шаги. Эти транзакции собирались в ещё более крупные пакеты операций. Каждый такой уровень - это отдельный автомат со своей переменной состояния. Эти автоматы реализовал switch/case-ом. В этом-же ISR работал процесс осциллографирования, который в реальном времени перегонял данные из кольцевого буфера во FRAM, во FLASH формируя осциллограммы в определённом формате, осуществляя синхронизацию, передискретизацию потока и многое другое. Вот именно его и реализовал вышеописанным способом. Реализовать в виде задачи ОС это было неоптимально, из-за требований к скорости работы и других требований. Этот процесс даже описанный указанным способом занимает около 24кБ в исходнике, а в виде классического switch/case - страшно даже подумать. И нереально отладить. Выкладывать сюда эту реализацию не буду, так как во-первых - коммерческий проект; во-вторых - исходник этот - почти сотня КБ - смысла нет, всё равно никто не станет разбираться в таком объёме, если даже сможет ;) Так же использовал такой метод при опросе через nRF24L01+ через короткие окна (неск. мсек) удалённого устройства, где тоже весь протокол обмена был описан в виде многоуровневого автомата, и протокол довольно объёмный, так что в виде классического автомата реализовывать - слишком громоздко, а в виде задачи ОС - по требованиям скорости не проходило, так как было нужно много мелких переключений задач. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
DogPawlowa 0 2 февраля, 2017 Опубликовано 2 февраля, 2017 · Жалоба занимает около 24кБ в исходнике, а в виде классического switch/case - страшно даже подумать. Оптимизацию включать пробовали? ;) Еще лет 10 назад тот ИАР для того же AVR при оптимизации заменял switch-case на массив функций. Теперь сравниваем вызов функции по индексу, который используется автором, с Вашей системой. Выход из функции и вызов ее опять (или вызов следующей при смене состояния) = восстановить регистры, а затем их сохранить. Ваш вариант = сохранить регистры, а потом восстановить их опять. Разница - да, она есть, и заключается вот в чем - в одном случае человек в поезде лицом в направлении движения, а в другом - спиной в направлении движения. А поезд тот же, и везет точно так же. Нету разницы! Экономии программной памяти в огромном количестве - нет! Увеличения быстродействия - нет! (потому что восстановление/сохранение контекста не оптимизировано). Может вызов функции по индексу и требует пары лишних байт, зато позволяет легко организовать иерархию автоматов. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
demiurg1978 1 2 февраля, 2017 Опубликовано 2 февраля, 2017 · Жалоба Что-то мне это баловство со стеком не нравится. Хорошо, сохранили контекст. Убежали в другой обработчик. А тут бац. Ситуация в корне поменялась. И тут мы прибегаем в обработчик, где в сохраненных регистрах старые данные. Да такая система, что обезьяна с гранатой. Никогда не знаешь, что произойдет и куда прилетит... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 184 3 февраля, 2017 Опубликовано 3 февраля, 2017 · Жалоба И тут мы прибегаем в обработчик, где в сохраненных регистрах старые данные. Да такая система, что обезьяна с гранатой. Никогда не знаешь, что произойдет и куда прилетит... Какие старые данные в каких регистрах??? Вы что такое "переключение контекста" понимаете? Как например это используется в ОС понимаете? Советую подучить теорию. Оптимизацию включать пробовали? ;) Еще лет 10 назад тот ИАР для того же AVR при оптимизации заменял switch-case на массив функций. А Вы читать, то что я написал - пробовали? Что такое "исходник" понимаете? Я вообще ничего не говорил о скомпилённом размере. Я говорил о сложности "объёмного исходного кода", который для классического автомата будет ещё большего размера и очень запутанным. И не знаю где там IAR заменяет что-то на массив функций, я по коду IAR для ARM вижу, что на месте switch() такого автомата IAR генерит огромную последовательность из десятков пар инструкций CMP/BEQ. Вне зависимости - включена оптимизация или нет. Так что даже по скорости вариант с переключением контекста будет быстрее. Разница - да, она есть, и заключается вот в чем - в одном случае человек в поезде лицом в направлении движения, а в другом - спиной в направлении движения. А поезд тот же, и везет точно так же. Нету разницы! Следуя Вашей аналогии: в Вашем случае человек добирается до цели кучей разных поездов, перепрыгивая из одного в другой; в моём - едет на одном поезде и какая разница как он там сидит - спиной или лицом - главное то, что ему удобно и он может в это время полезными делами заниматься, а не прыганьем из одного поезда в другой. :laughing: Экономии программной памяти в огромном количестве - нет! Увеличения быстродействия - нет! (потому что восстановление/сохранение контекста не оптимизировано). Может вызов функции по индексу и требует пары лишних байт, зато позволяет легко организовать иерархию автоматов. Ещё раз - попробуйте прочитать хотя-бы мои сообщения, на которые Вы пытаетесь отвечать! Цель не стояла - увеличение быстродействия или тем более - экономия пары байт программной памяти (что вообще полный бред в современных условиях). Цель: кардинальное улучшение читаемости и соответственно - удобства написания/отладки кода больших сложных автоматов. Но даже если заниматься ерундой и экономить байты памяти программ: Попробуйте когда-нить открыть листинг скомпилённой программы. Увидите, что вызов/возврат из функции занимает далеко не пару байт, даже думаю на AVR. Каждая такая функция это: её вызов (косвенный, который дороже прямого вызова + адресация в массиве указателей) + сохранение регистров на входе в неё + восстановление регистров на выходе + возврат. Сколько это байт будет на AVR? В моём случае в таком месте будет только: прямой вызов функции. Всё! Сколько это байт будет на AVR? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
DogPawlowa 0 3 февраля, 2017 Опубликовано 3 февраля, 2017 · Жалоба Цель: кардинальное улучшение читаемости и соответственно - удобства написания/отладки кода больших сложных автоматов. Банальный макрос #define SwitchStack(N) } void Step##N##() { делает то же самое "улучшение" читаемости и удобство. Все равно место вызова Вашей функции не берется с потолка. Но даже если заниматься ерундой и экономить байты памяти программ: ...Каждая такая функция это: её вызов (косвенный, который дороже прямого вызова + адресация в массиве указателей) + сохранение регистров на входе в неё + восстановление регистров на выходе + возврат. Сколько это байт будет на AVR? Немало, тут я полностью согласен. Но вызов функции по индексу - это один раз на всю программу, преамбула/постамбула тоже одни на всех. Ваш вызов функции против моего вызова функции. Ваше переключение контекста гарантированно дольше, так как должно покрыть все используемые всей функцией регистры, мое переключение (вызов-возврат) в среднем короче, потому что адаптировано к конкретному куску кода, выделенному в отдельную функцию. Еще что-то есть? ;) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Obam 30 3 февраля, 2017 Опубликовано 3 февраля, 2017 (изменено) · Жалоба Ваш вызов функции против моего вызова функции. Ваше переключение контекста гарантированно дольше Простите, что вставляю свои 5 коп, но приводя исходники и рассказывая идею, jcxz чётко заявлял об ARM: а в нём конвейерная организации. В AVR тоже есть конвейер? Контекст сохраняется одинаково? Так что, речь может идти только о удобочитаемости исходников. И что останавливает от проверки одного и того же кода для ARM и AVR. Кстати, нужен только симулятор (а они есть и недурные, хоть в том же IAR) ядер. Изменено 3 февраля, 2017 пользователем Obam Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 184 3 февраля, 2017 Опубликовано 3 февраля, 2017 · Жалоба Банальный макрос #define SwitchStack(N) } void Step##N##() { делает то же самое "улучшение" читаемости и удобство. Все равно место вызова Вашей функции не берется с потолка. Ну да, а изменение значения переменной-состояния автомата где? А если эта переменная меняется условно, в зависимости от результата выполнения предыдущего состояния автомата? В N-й раз уже говорю, но похоже не доходит :(( - Мой метод оптимален для сложных автоматов, с большим числом состояний, условными переходами между состояниями, циклическим выполнением каких-то шагов автомата, общих последовательностей шагов (которые моим методом оформляются в простые функции). Покажите как Вы с помощью этого макроса реализуете условное изменение состояния автомата? А цикл? Немало, тут я полностью согласен. Но вызов функции по индексу - это один раз на всю программу, преамбула/постамбула тоже одни на всех. Это почему это сохранение/восстановление контекста одно на всех? Разве в соглашениях вызова функций для AVR сохранение контекста делает вызывающий функцию, а не сама функция? В любом случае - разница даже в десятки байт - ни о чём, смысла нет обсуждать. Ваше переключение контекста гарантированно дольше, так как должно покрыть все используемые всей функцией регистры, мое переключение (вызов-возврат) в среднем короче, потому что адаптировано к конкретному куску кода, выделенному в отдельную функцию. Да ладно?! А переменные, которые Ваши состояния автомата используют их нет? Без переменных? В моём случае - они хранятся в регистрах и сохраняются/восстанавливаются одним и тем же кодом, а у Вас - для каждого состояния придётся их на входе в каждое состояние заново читать, а после - записывать. Если уж Вас так волнует количество байт кода, то как раз мой вариант в любом случае будет короче. Попробуйте на практике реализовать для автомата хотя бы с десятком состояний. Еще что-то есть? ;) Всё уже было. Если Вы не поняли - очевидно рано ещё, со временем может поймёте. А пока - никто Вас не заставляет изучать что-то новое, включать мозг, продолжайте и дальше жить в своём однобайтовом мирке... Так что, речь может идти только о удобочитаемости исходников. Именно об этом и шла речь в первую очередь. Но некоторые товарищи погрязли в однобайтном мирке, похоже никогда не видели исходников размером хотя-бы несколько сотен строк и похоже объяснять что-то бесполезно.... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
DogPawlowa 0 4 февраля, 2017 Опубликовано 4 февраля, 2017 · Жалоба >> Покажите как Вы с помощью этого макроса реализуете условное изменение состояния автомата? А цикл? А как Вы со своей функцией реализуете условное изменение состояния автомата? - Никак. Вы хотели линейности кода - вот оно. >> Ну да, а изменение значения переменной-состояния автомата где? >> А если эта переменная меняется условно, в зависимости от результата выполнения предыдущего состояния автомата? Так еще проще, чем у Вас - присвоил новое значение и вышел. Естественное структурирование автомата. А у Вас - if (..) goto ? >>В N-й раз уже говорю, но похоже не доходит :(( - Мой метод оптимален для сложных автоматов, Вы в автоподпись добавьте - крут, как яйца ;) >> с большим числом состояний "Большим" - это сколько? У меня сотни - без проблем. >> условными переходами между состояниями, циклическим выполнением каких-то шагов автомата 1. Бывают автоматы без условных переходов и циклического выполнения? 2. Вот не знал, что есть проблемы реализовать переходы (условные и безусловные) и циклы с массивами функций. >>общих последовательностей шагов (которые моим методом оформляются в простые функции). Вы наверное и сами функции изобрели? ;) Общие последовательности шагов реализуются добавлением одной переменной, которая указывает куда выйти после окончания последовательности, она есть и в Вашей реализации. >> Это почему это сохранение/восстановление контекста одно на всех? Разве в соглашениях вызова функций для AVR сохранение контекста делает вызывающий функцию, >>а не сама функция? В любом случае - разница даже в десятки байт - ни о чём, смысла нет обсуждать. Да, тут я погорячился, восстановление контекста одно на всех, а вот сохранение конечно раздельное. >> А переменные, которые Ваши состояния автомата используют их нет? Без переменных? В моём случае - они хранятся в регистрах и сохраняются/восстанавливаются одним и тем же кодом, >> а у Вас - для каждого состояния придётся их на входе в каждое состояние заново читать, а после - записывать. А у Вас этой записи нет?! А в стек и обратно Вы переменные не гоняете? ;) Мои переменные лежат там спокойно и работа с ними идет, когда нужно, а Вы туда-сюда, туда-сюда. Я же написал уже, что у меня запись и восстановление оптимизированы по месту, а у Вас всегда пишутся и читаются все регистры, которые задействованы во всей функции поддержки автомата. А Вы не поняли и недостаток пытаетесь выдать за достоинство. >> Если уж Вас так волнует количество байт кода, то как раз мой вариант в любом случае будет короче. >> Попробуйте на практике реализовать для автомата хотя бы с десятком состояний. Это называется "на слабо" :) Да, наверное есть случаи, когда Ваш метод имеет смысл. Спасибо, я попробую. >>Всё уже было. Если Вы не поняли - очевидно рано ещё, со временем может поймёте. >>Но некоторые товарищи погрязли в однобайтном мирке, похоже никогда не видели исходников размером хотя-бы несколько сотен строк и похоже объяснять что-то бесполезно.... Спасибо, что снизошли! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться