|
Управление контекстом БЕЗ RTOS |
|
|
|
Nov 21 2014, 08:04
|
Местный
  
Группа: Свой
Сообщений: 321
Регистрация: 23-12-11
Из: Уфа
Пользователь №: 69 041

|
Я понимаю, что сейчас речь пойдет об изобретении велосипеда. Но мне это важно для понимания. В ОСРВах переключение между задачами осуществляется за счет переключения контекста. Мне интересно каким образом можно организовать такое переключения самостоятельно. Например, есть две функции ledBlinking1() и ledBlinking2(): Код void ledBlinging1 (vodi) { led1On(); delayms(1000); led1Off(); delayms(1000); }
void ledBlinging2 (vodi) { led2On(); delayms(1000); led2Off(); delayms(1000); } При размещении этих функций внутри задач rtos они будут моргать не влияя друг на друга. А если я озадачусь сделать тоже моргание без rtos, так чтобы они моргали независимо, то мне придется накидать витиеватый алгоритм запоминания предыдущего состояния и следить, сколько времени прошло, не пора бы потушить или зажечь... Но если я реализую функцию загрузки и выгрузки контекста функции, то такой алгоритм городить не придется. Пусть такая функция есть и называется она downloadContext (), а функция приостановки и сохранения контекста yeld(). Тогда, то же моргание без РТОС будет выглядеть следующим образом: Код void ledBlinging1 (void) { led1On(); timer1.setMs(1000); while (timer1.expired() == FALSE) yeld(); led1Off(); timer1.setMs(1000); while (timer1.expired() == FALSE) yeld();
}
void ledBlinging2 (void) { led1On(); timer1.setMs(1000); while (timer2.expired() == FALSE) yeld(); led2Off(); timer2.setMs(1000); while (timer2.expired() == FALSE) yeld(); }
int main (void) { while (1) { downloadContext (ledBlinging1); downloadContext (ledBlinging2); } } Может я чего неправильно сказал, но надеюсь смысл понятен. Вопрос: как сделать такое сохранение и загрузку контекста? Где про это можно почитать, посмотреть пример и т.п. Спасибо!
|
|
|
|
|
 |
Ответов
|
Nov 27 2014, 14:04
|
Местный
  
Группа: Свой
Сообщений: 271
Регистрация: 6-12-11
Из: Taganrog
Пользователь №: 68 701

|
Я лично после долгого знакомства с ОС2000 без отладчика и попытками обуздать там 100 нитей очень негативно отношусь ко всякой шизофрении, когда непонятно, "кто шил костюм", а память портится, причём нестабильно. Хотя периодически сильно хочется почитать научный обзор страниц так на 100, что же там придумано на текущий момент в этой области. Но по-русски и без наездов. Лучше всего ИМХО 1-нитевые "шедулеры", которые по очереди исполняют функции, которые были добавлены в список до того, и каждая функция добавляет новые адреса исполнения в список, плюс её возвращаемое значение -- адрес функции, которая должна выполниться после этой, ну или 0, если эта "нитка" завершена. Задача бъётся на кучу функций, которые имеют глобальные данные (возможно, в отдельных namespace, или там там сидят указатели на них в куче), и никакого сохранения контекста и переключения десятков регистров не нужно, стек у всех один ! Бродить в переключателе контекста на asm-е не надо, чтобы понять, куда меня перешедуливают, само переключение -- выход из одной функции и вызов другой, данные глобальны. Шедулер тупо вызывает по кругу свой массив адресов функций, они "как бы крутятся", возвращая вместо себя "наследника" в тот же адрес массива на то же место, и что-то кусочно исполняют. Также можно завести отдельный массив "ожидателей" -- системного таймера или не 0 по какому-то адресу, который будет и "семафором", "мьютексом", и чем угодно -- не нужны никакие критические секции, никто у тебя за спиной бяку никогда не сделает в непонятной нити с высоким приоритетом, лишь бы был отладчик хоть какой-то в IDE. Прерывания тоже можно использовать, но только в виде исключения -- если уж точно кто-то чего-то требует за 100 тактов, а не за 1000 (в порядке очереди). Ибо от прерываний основные проблемы нестабильности в ртосах всяких, когда и надо что-то бы сделать "где-то", а "там" в этот же момент тоже вдруг кто-то ковыряется "под стрелой"... Такие коды получаются абсолютно переносимыми (ну, кроме таймера), без-ассемблерными. И все контексты сидят "в домиках" возле каждой функции, их использующей, локальные автоматические переменные действуют недолго и не требуют сохранения между вызовами. Понятно, что ожидания и большие объёмы обработки там недопустимы, надо бить процессы на непрерывные куски. Случилось нечто -- запускается "процесс" его обработки, т.е. подшедуливается функция, где она расписана. Единственное плохо -- все варианты событий должны быть известны заранее, хотя во встроенных небольших системах так обычно и есть. Можно и параметры передавать в шедулер, с которыми планируется вызов функции, но это уже непрозрачней гораздо. Код ТС получается типа такого: Код typedef (func*)(void); func led1On() {/*make*/ addDelayed(led1Off, 1000); return NULL; } func led1Off() {/*make*/ addDelayed(led1On, 1000); return NULL; } func led2On() {/*make*/ addDelayed(led2Off, 1000); return NULL; } func led2Off() {/*make*/ addDelayed(led2On, 1000); return NULL; }
int main() { addDelayed(led1On, 1000); addDelayed(led1Off, 1000); main_loop(); } Вдруг на самом деле что такое уже есть в мировой практике -- подскажите.
|
|
|
|
|
Nov 28 2014, 07:04
|

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

|
Цитата(WitFed @ Nov 27 2014, 16:04)  Лучше всего ИМХО 1-нитевые "шедулеры", которые по очереди исполняют функции, которые были добавлены в список до того, и каждая функция добавляет новые адреса исполнения в список, плюс её возвращаемое значение ... Извините, но ваш пост воспринимается как малопонятный набор слов. А приведенный код попахивает рекурсией. А рекурсия это самое вредное что можно придумать во встраиваемой системе. Убьете стек и не заметите. Может вы хотели сказать о машинах состояний управляемых шаблонами (цепочками состояний)? На более высоком уровне это называется интерперетаторы. Приведу пример простейшей машины состояний по шаблону отрабатывающей управление массивом соленоидов. Соленоиды в автоматике довольно хитро управляются. Во первых их нельзя включать одновременно, во вторых их модулируют ШИМ-ом, в третьих к ним применяют специальные форсированные импульсы, но так чтобы не перегреть. Вообщем требуют таких же сложных шаблонов как и информационные светодиоды. Этот автомат применяется мною и с RTOS и без. Сервисы RTOS здесь мало применимы, потому что им тут нечего делать и поскольку момент коммутации соленоидов строго привязан к окончанию выборки АЦП, так чтобы они не засоряли сигнал АЦП. CODE // Управляющая структура машины состояний управляемой шаблоном typedef struct { INT32U init_state; INT32U counter; INT32U *pattern_start_ptr; // Указатель на массив констант int являющийся цепочкой состояний (шаблоном) // Если значение в массиве = 0xFFFF, то процесс обработки завершается // Если значение в массиве = 0x0000, то вернуть указатель на начало цепочки INT32U *pttn_ptr; // Текущая позиция в цепочке состояний
} T_solnd_ptrn;
// Шаблоны работы соленоидов // ---------------------------------------------------------------------- // Шаблон состоит из массива пар слов. // Первое слово - значение напряжения (в процентах от максимального) на текущем интервале времени // Второе - длительность интервала времени в мс // интервал равный 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 }; // ----------------------------------------------------------------------
// Массив управляющих структур соленоидов . T_solnd_ptrn solnd_cbl[2];
/*------------------------------------------------------------------------------------------------------------- Инициализация машины состояний заданным шаблоном *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++; } }
/*------------------------------------------------------------------------------ Вызывается из процедуры прерывания АЦП с периодичностью ADC_PERIOD ------------------------------------------------------------------------------*/ void Solenoid_automat(void) { static INT32U solnd_presc = 0; INT32U duration; INT32U voltage; INT32U n;
solnd_presc++;
if ( solnd_presc >= (1000 / ADC_PERIOD) ) // Обработка ведется с периодом повторения - 1 мс { solnd_presc = 0; for (n=0; n<2; n++) { // Управление состоянием сигнала соленоида if ( solnd_cbl[n].counter ) { solnd_cbl[n].counter--; if ( solnd_cbl[n].counter == 0 ) // Меняем состояние сигнала при обнулении счетчика { if ( solnd_cbl[n].pattern_start_ptr != 0 ) { voltage = *solnd_cbl[n].pttn_ptr; solnd_cbl[n].pttn_ptr++; duration = *solnd_cbl[n].pttn_ptr; solnd_cbl[n].pttn_ptr++; if ( duration != 0xFFFF ) { if ( duration == 0 ) { solnd_cbl[n].pttn_ptr = solnd_cbl[n].pattern_start_ptr; voltage = *solnd_cbl[n].pttn_ptr; solnd_cbl[n].pttn_ptr++; solnd_cbl[n].counter = *solnd_cbl[n].pttn_ptr; solnd_cbl[n].pttn_ptr++; Set_solenoids_voltage(voltage, n); } else { solnd_cbl[n].counter = duration; Set_solenoids_voltage(voltage, n); } } else { // Обнуляем счетчик и таким образом выключаем обработку шаблона Set_solenoids_voltage(voltage,n); solnd_cbl[n].counter = 0; } } else { Set_solenoids_voltage(0,n); } } } }
} // if ((prediv & 3)==0) }
Сообщение отредактировал IgorKossak - Nov 28 2014, 15:58
Причина редактирования: [codebox] для длинного кода, [code] - для короткого!!!
|
|
|
|
|
Nov 28 2014, 08:12
|
Знающий
   
Группа: Свой
Сообщений: 526
Регистрация: 5-08-05
Пользователь №: 7 390

|
Цитата(AlexandrY @ Nov 28 2014, 10:04)  Этот автомат применяется мною и с RTOS и без... сложный пример. Можно проще: CODE enum fsm_state { IDLE, PROCESS, ERROR };
struct fsm { enum fsm_state state; // data... };
static struct fsm myfsm = { .state = IDLE, // data startup init... };
void fsm_init(struct fsm* fsm){ fsm->state = IDLE; // data (re)init... }
void fsm_proc(struct fsm* fsm){ switch(fsm->state){ case IDLE: // do startup init ... fsm->state = PROCESS; break; case PROCESS: // do process ... fsm->state = IDLE; // or fsm->state = ERROR; break; default: fsm->state = ERROR; case ERROR: // error handler break; } } Вообще без ОСи никакого контекста не будет, потому что в этом случае процесс(задача)разветвленная, но одна. В противном случае что такое контекст? Состояние регистров и стека при выполнении подпрограммы или прерывания?
Сообщение отредактировал IgorKossak - Nov 28 2014, 15:59
Причина редактирования: [codebox] для длинного кода, [code] - для короткого!!!
|
|
|
|
|
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: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, 22:41
|
Знающий
   
Группа: Свой
Сообщений: 526
Регистрация: 5-08-05
Пользователь №: 7 390

|
Цитата(AlexandrY @ Nov 28 2014, 14:58)  Вопрос был как сохранить контекст, а не где его хранить! Для более сложных задач нужно применять минимум кооперативную многозадачность. Разве как и где не взаимосвязаны? Очевидно, что данные сохраняются по указателю в структуре данных задачи. И это разжевывать не нужно. Для "минимум коопертивной многозадачности" задача как раз таки вырождается в машину состояния. Для примера можно, например, посмотреть выход с препроцессора для protothreads Цитата(Slash @ Nov 28 2014, 17:38)  Извините, но это жесть что влезаю из топика "С vs C++", но этот код можно сделать еще лучше. Непонятен смысл рефакторинга указателей. Мало того, что массив превращается в контейнер, так еще указатели превращаются в объекты с перегруженным operator []. Ради чего это сделано? Кто и кому будет кидать исключение для .at() на микроконтроллере? Или все это ради for на наборе? Как-то избыточно.
|
|
|
|
Сообщений в этой теме
yanvasiij Управление контекстом БЕЗ RTOS Nov 21 2014, 08:04 1113 ваш main() это и есть диспетчер задач. таким образ... Nov 21 2014, 08:09 yanvasiij Цитата(1113 @ Nov 21 2014, 13:09) ваш mai... Nov 21 2014, 08:12 1113 в СИ такого нет. вам надо реализовать надстройку н... Nov 21 2014, 08:23 demiurg_spb Цитата(1113 @ Nov 21 2014, 12:23) в СИ та... Nov 21 2014, 08:36 yanvasiij Ну я вот думал, что мне предложат ассемблерный сох... Nov 21 2014, 08:24 1113 всё описано, даже на русском. а "сохраняльщик... Nov 21 2014, 08:25 scifi Цитата(yanvasiij @ Nov 21 2014, 11:04) ви... Nov 21 2014, 08:25 SSerge Цитата(scifi @ Nov 21 2014, 15:25) Всё пр... Nov 21 2014, 12:21 yanvasiij Цитата(scifi @ Nov 21 2014, 13:25) Всё пр... Nov 21 2014, 08:41 scifi Цитата(yanvasiij @ Nov 21 2014, 11:39) Из... Nov 21 2014, 08:42 1113 Цитата(yanvasiij @ Nov 21 2014, 11:41) Из... Nov 21 2014, 08:53 demiurg_spb Цитата(yanvasiij @ Nov 21 2014, 12:41) Эт... Nov 21 2014, 09:36 yanvasiij Цитата(scifi @ Nov 21 2014, 13:42) Хороше... Nov 21 2014, 08:51 ViKo Цитата(yanvasiij @ Nov 21 2014, 11:51) РТ... Nov 21 2014, 09:08  1113 Цитата(ViKo @ Nov 21 2014, 12:08) RTOS-ин... Nov 21 2014, 09:34 yanvasiij Цитата(1113 @ Nov 21 2014, 13:53) для так... Nov 21 2014, 08:59 Golikov A. прерывание в этом не поможет? Собственно как в РТ... Nov 21 2014, 09:14 AlexandrY Цитата(yanvasiij @ Nov 21 2014, 10:04) Мо... Nov 21 2014, 10:08 MrYuran А если немного подумать, может и не нужна никакая ... Nov 21 2014, 12:46 AlexandrY Цитата(MrYuran @ Nov 21 2014, 14:46) А ес... Nov 21 2014, 13:06  MrYuran Цитата(AlexandrY @ Nov 21 2014, 17:06) Не... Nov 21 2014, 13:23 scifi Цитата(MrYuran @ Nov 21 2014, 15:46) А ес... Nov 21 2014, 15:46 jcxz Цитата(WitFed @ Nov 27 2014, 20:04) Также... Nov 28 2014, 04:02  Slash Цитата(AlexandrY @ Nov 28 2014, 10:04) Эт... Nov 28 2014, 14:38 Golikov A. ага супер луп на конечных автоматах называется... ... Nov 27 2014, 15:01 Golikov A. ЦитатаНепонятно - почему не нужны критические секц... Nov 28 2014, 07:49 jcxz Цитата(Golikov A. @ Nov 28 2014, 13:49) п... Nov 28 2014, 11:06 Golikov A. fsm->state можно и в прерывании сменить на ERRO... Nov 28 2014, 09:11 AlexandrY Цитата(Golikov A. @ Nov 28 2014, 11:11) f... Nov 28 2014, 09:18 Golikov A. ЦитатаЭто верно только в одном частном случае - ко... Nov 28 2014, 14:07 jcxz Цитата(Golikov A. @ Nov 28 2014, 20:07) К... Nov 28 2014, 15:34 Golikov A. чего то я видать безнадежно устарел
Кодfor (co... Nov 28 2014, 15:04 Slash Сколько не читал битв "C vs C++", почему... Nov 28 2014, 16:07  jcxz Цитата(Slash @ Nov 28 2014, 22:07) Очень ... Nov 28 2014, 16:26 Golikov A. вопрос в другом.
Разве в С++ есть for без ;; ?
for... Nov 28 2014, 15:49 Golikov A. ЦитатаСтоит отметить, что хоть range-based for и я... Nov 28 2014, 16:15 yanvasiij Чтобы внести конкретики. Зачем мне это надо и поче... Dec 1 2014, 07:29 scifi Цитата(yanvasiij @ Dec 1 2014, 10:29) Зач... Dec 1 2014, 07:55 yanvasiij Цитата(scifi @ Dec 1 2014, 12:55) Кстати,... Dec 1 2014, 08:12 scifi Цитата(yanvasiij @ Dec 1 2014, 11:12) На ... Dec 1 2014, 09:01
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|