|
|
  |
Управление контекстом БЕЗ RTOS |
|
|
|
Nov 28 2014, 09:25
|
Знающий
   
Группа: Свой
Сообщений: 526
Регистрация: 5-08-05
Пользователь №: 7 390

|
Цитата(AlexandrY @ Nov 28 2014, 12:09)  Ни как ОНО узнает об этом без обратной связи то? откуда вообще взялся светодиод в энтом абстрактном примере?  Придираетесь, профессор? Цитата(AlexandrY @ Nov 28 2014, 12:09)  ERROR лишнее состояние, да и IDLE тоже. да, да  назовем IDLE "константа 65535" и захардкодим, чтобы джедаи не догадались
|
|
|
|
|
Nov 28 2014, 09:57
|

Ally
     
Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050

|
Цитата(psL @ Nov 28 2014, 11:25)  откуда вообще взялся светодиод в энтом абстрактном примере?  Придираетесь, профессор? Т.е. нынче принято обсуждать что угодно, но только не то, что спросил TC? Цитата(psL @ Nov 28 2014, 11:25)  да, да  назовем IDLE "константа 65535" и захардкодим, чтобы джедаи не догадались  Боюсь вы термину IDLE придумали некое свое семантическое значение. Если оно вам помогает , ну и хорошо. Только автоматов разных может быть много в программе и не упасётесь разными вариациями: IDLE1, IDLE2, IDLE3 ...
|
|
|
|
|
Nov 28 2014, 10:55
|
Знающий
   
Группа: Свой
Сообщений: 526
Регистрация: 5-08-05
Пользователь №: 7 390

|
Цитата(AlexandrY @ Nov 28 2014, 12:57)  Т.е. нынче принято обсуждать что угодно, но только не то, что спросил TC? ну ТС хотелось "контекста". В общем случае "контекст" может выглядеть так: Код struct job { void* data; // "контекст" void (*proc)(void*); };
struct job jobs[] = { {.data=..., .proc=... }, ... };
void job_proc(struct job* job){ job->proc(job->data); }
void main(){ for(i=0; i<sizeof(jobs)/sizeof(struct job);i++) job_proc(&jobs[i]); } В качестве data и *proc м.б. struct fsm и fsm_proc из предыдущего примера с небольшой доработкой. Или принципиально затачивать каждый пример для светодиода?
|
|
|
|
|
Nov 28 2014, 11:06
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Golikov A. @ Nov 28 2014, 13:49)  потому что при предложенном подходе каждая функция изначально критическая секция, с атомарным доступом ко всем используемым переменным до окончания своей работы. Функции не прерываются в середине, а только в определенных местах, и они должны правильно отрабатывать смену параметров на лету. Это верно только в одном частном случае - когда границы критической секции не выходят за пределы таких коммутируемых функций. А если выходят - как быть? Также тут предполагается, что моменты переключения задач всегда на границах таких функций. Это корпоративная многозадачность. А если длительность таких функций очень (на порядки различается)? А если нужно выполнять функции, реагирующие на реалтайм-события, критичные к времени реакции. И в то же время среди функций есть тяжёлые (длительные) вычислительные функции, к тому же если время их выполнения заранее не известно. Как в этом случае быть?
|
|
|
|
|
Nov 28 2014, 11:28
|

Ally
     
Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050

|
Цитата(psL @ Nov 28 2014, 12:55)  ну ТС хотелось "контекста". В общем случае "контекст" может выглядеть так: Код struct job { void* data; // "контекст" void (*proc)(void*); };
struct job jobs[] = { {.data=..., .proc=... }, ... };
void job_proc(struct job* job){ job->proc(job->data); }
void main(){ for(i=0; i<sizeof(jobs)/sizeof(struct job);i++) job_proc(&jobs[i]); } В качестве data и *proc м.б. struct fsm и fsm_proc из предыдущего примера с небольшой доработкой. Или принципиально затачивать каждый пример для светодиода? А где здесь сохранение контекста? Здесь изображен просто синхронный вызов функций с неким указателем неизвестно на что. А если брать struct fsm как контекст задачи в применении к светодиоду, то это будет точно то же что я и продемонстрировал если вам дописать недостающий код, но будет более раздуто, так как состояниям будет выделяться дополнительная величина в управляющем шаблоне.
|
|
|
|
|
Nov 28 2014, 11:48
|
Знающий
   
Группа: Свой
Сообщений: 526
Регистрация: 5-08-05
Пользователь №: 7 390

|
Цитата(AlexandrY @ Nov 28 2014, 14:28)  А где здесь сохранение контекста? Указатель на "контекст" хранится в *data, не нужно ТС никаких downloadContext. Т.е. Код struct job jobs[] = { {.data=(void*)myfsm, .proc=fsm_proc }, ... };
void fsm_proc(void* p){ struct fsm* myfsm = (struct fsm*)p; ... } да, будет почти тоже самое. В этих примерах все абстрактно. Но, пмсм, так проще для понимания. Насколько вообще понимаю суть вопроса, ТС просит обьяснить, как писать реентерабельные функции.
|
|
|
|
|
Nov 28 2014, 11:58
|

Ally
     
Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050

|
Цитата(psL @ Nov 28 2014, 13:48)  Указатель на "контекст" хранится в *data, не нужно ТС никаких downloadContext. Т.е. Вопрос был как сохранить контекст, а не где его хранить! И ваш ответ по сути таков: превращать любую задачу в автомат состояний и состояние хранить в структуре, которую не надо сохранять потому как она у каждой задачи своя. Мой пример тоже основан на этом. Но! Только для светодиодов, соленоидов и прочей односигнальной мелочи. Для более сложных задач нужно применять минимум кооперативную многозадачность.
|
|
|
|
|
Nov 28 2014, 14:07
|
Гуру
     
Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454

|
Цитата Это верно только в одном частном случае - когда границы критической секции не выходят за пределы таких коммутируемых функций. А если выходят - как быть? не могут выходить, функции так создаются Цитата Также тут предполагается, что моменты переключения задач всегда на границах таких функций. Это корпоративная многозадачность. А если длительность таких функций очень (на порядки различается)? опять же функции создаются так чтобы такого не было и переключение всегда на границе функций. К примеру: есть функция обработки большого массива, функция при каждом вызове обрабатывает 1 элемент и выходит с кодом 1, а когда обработает последний вернет 0. В основной программе функция вызывается пока не вернет 0. Цитата А если нужно выполнять функции, реагирующие на реалтайм-события, критичные к времени реакции. И в то же время среди функций есть тяжёлые (длительные) вычислительные функции, к тому же если время их выполнения заранее не известно. Как в этом случае быть? wink.gif ну собственно вы сейчас называете все недостатки такого подхода и почему появились операционки. Отсюда ответ на ваш вопрос использовать операционку!
|
|
|
|
|
Nov 28 2014, 14:38
|
Местный
  
Группа: Участник
Сообщений: 202
Регистрация: 10-04-05
Из: Санкт-Петербург
Пользователь №: 4 011

|
Цитата(AlexandrY @ Nov 28 2014, 10:04)  Этот автомат применяется мною и с RTOS и без. Извините, но это жесть что влезаю из топика "С vs C++", но этот код можно сделать еще лучше. Код // Управляющая структура машины состояний управляемой шаблоном typedef struct { INT32U init_state; INT32U counter; INT32U *pattern_start_ptr; // Указатель на массив констант int являющийся цепочкой состояний (шаблоном) // Если значение в массиве = 0xFFFF, то процесс обработки завершается // Если значение в массиве = 0x0000, то вернуть указатель на начало цепочки INT32U *pttn_ptr; // Текущая позиция в цепочке состояний
} T_solnd_ptrn; pattern_start_ptr и pttn_ptr нет ну никакого смысла делать указателями, только лишние разименовывания. По смыслу это индекс позиции. Дать им имена pos и startPos или index и startIndex. Чистая вкусовщина - меня коробит от сочетания в именах больших букв и подчеркиваний ну и typedef struct туда-же. Выделять типы данных префиксом "T_" нет смысла - современный редактор цветом покажет, что данный символ - тип данных. Т.е. меняем на Код struct SolenoidPattern { INT32U init_state; INT32U counter; INT32U startPos ; // Указатель на массив констант int являющийся цепочкой состояний (шаблоном) // Если значение в массиве = 0xFFFF, то процесс обработки завершается // Если значение в массиве = 0x0000, то вернуть указатель на начало цепочки INT32U pos; // Текущая позиция в цепочке состояний }; Код // Шаблоны работы соленоидов // ---------------------------------------------------------------------- // Шаблон состоит из массива пар слов. // Первое слово - значение напряжения (в процентах от максимального) на текущем интервале времени // Второе - длительность интервала времени в мс // интервал равный 0 - означает возврат в начало шаблона // интервал равный 65535 - означает застывание состояния
const INT32U SOLENOID1_ON[10] = { 100, 300, 25, 300, 100, 300, 25, 300, 25, 0xFFFF }; const INT32U SOLENOID2_ON[10] = { 25, 300, 100, 300, 25, 300, 100, 300, 25, 0xFFFF }; const INT32U SOLENOID_OFF[4] = { 0, 2000, 0, 0xFFFF }; Здорово бы комментарии поддержать кодом, введя тип данных Код struct Point { uint32_t voltage; uint32_t time; }; const Point SOLENOID1_ON[5] = { {100, 300}, {25, 300}, {100, 300}, {25, 300}, {25, 0xFFFF} }; Тогда такая комбинация Код voltage = *solnd_cbl[n].pttn_ptr; solnd_cbl[n].pttn_ptr++; duration = *solnd_cbl[n].pttn_ptr; solnd_cbl[n].pttn_ptr++; сокращается до Код voltage = solnd_cbl[n].at(pos).voltage; duration = solnd_cbl[n].at(pos).time; solnd_cbl[n].pos++; Тут мне не все понятно, вроде бы эта функция вроде конструктора SolenoidPattern. Не хватает определения функции Set_solenoids_voltage. Код /*------------------------------------------------------------------------------------------------------------- Инициализация машины состояний заданным шаблоном *pttn - указатель на начало шаблона n - индекс соленоида (0..1) -------------------------------------------------------------------------------------------------------------*/ void Set_Solenoid_pattern(const INT32U *pttn, INT32U n) { if ( pttn != 0 ) { solnd_cbl[n].pattern_start_ptr = (INT32U *)pttn; solnd_cbl[n].pttn_ptr = (INT32U *)pttn; Set_solenoids_voltage(*solnd_cbl[n].pttn_ptr, n); solnd_cbl[n].pttn_ptr++; solnd_cbl[n].counter = *solnd_cbl[n].pttn_ptr; solnd_cbl[n].pttn_ptr++; } } Ну и может быть отказаться от конструкции Код for (int i = 0; i < n; ++i) {} в пользу Код for (const Point & point : pointsArray) {} тогда из цикла пропадает индекс, но так сделать не всегда можно, иногда он нужен.
|
|
|
|
|
Nov 28 2014, 15:34
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Golikov A. @ Nov 28 2014, 20:07)  К примеру: есть функция обработки большого массива, функция при каждом вызове обрабатывает 1 элемент и выходит с кодом 1, а когда обработает последний вернет 0. В основной программе функция вызывается пока не вернет 0. А если таких элементов миллион и обработка - одна простая операция? Будет чудовищный оверхед - 10% времени будет выполняться полезная работа и 90% - входы/выходы в функцию и разные проверки. Вы конечно скажете - "в таком случае нужно не по 1-му, а по 100 элементов за раз обрабатывать". Но тогда получается программа привязывается к среде выполнения и кроме решения задачи каждый раз нужно учитывать ещё кучу условий. А если длительность обработки каждого элемента заранее неизвестна? А если вызываете функции некоей сторонней библиотеки, где тоже время выполнения заранее неизвестно? Цитата(Golikov A. @ Nov 28 2014, 20:07)  ну собственно вы сейчас называете все недостатки такого подхода и почему появились операционки. Отсюда ответ на ваш вопрос использовать операционку! Конечно. Всё уместно на своём месте. И для суперлупа есть свой узкий круг задач, которые для него оптимальны, и своеобразное построение алгоритма. Я тоже его частенько использую (совместно с ОС), для экономии памяти (под стек). Но ТС пишет о таком подходе как о панацее для всего. Что в корне не верно. Традиционная вытесняющая ОС гораздо более универсальна. А для суперцикла есть только своя ниша. Цитата(Golikov A. @ Nov 28 2014, 21:04)  чего то я видать безнадежно устарел Код for (const Point & point : pointsArray) вот это что за на? Вся эта си-плюс-плюсная объектно-инкапсулированная хрень хороша только для тех, кто не заглядывает в файлы листинга компилятора. А если Вы задумываетесь об оптимальности не исходников (как здесь), а результирующего кода (скорости выполнения и размера), то выбирайте наиболее стандартные конструкции, типа for (int i = 0; i < n; ++i). Оптимизаторы компиляторов на них наиболее "натасканы" и код будет оптимальным. Я, после опыта оптимизации по скорости DSP-кода, взял это себе за правило - если хочется чтобы код был наиболее оптимален после компилятора, конструкции в исходнном коде должны быть наиболее простыми. На входе у меня был вот такой вот весь правильный С++ код, со всеми конструкторами/деструкторами и т.п. и при этом он безбожно тормозил и алгоритм не успевал обработать поток данных. После убиения всей этой плюсовой красоты и полного переписывания на простой си-код, скорость выполнения того-же самого алгоритма увеличилась в несколько сотен раз! Потому что оптимизатор простой код сумел многократно распараллелить.
|
|
|
|
|
  |
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
|
|
|