|
|
  |
Управление контекстом БЕЗ RTOS |
|
|
|
Nov 21 2014, 10:08
|

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

|
Цитата(yanvasiij @ Nov 21 2014, 10:04)  Может я чего неправильно сказал, но надеюсь смысл понятен. Вопрос: как сделать такое сохранение и загрузку контекста? Где про это можно почитать, посмотреть пример и т.п. Спасибо! Это всегда называлось кооперативной многозадачностью. Переключение будет выглядеть вот так: Код /* * Performs a context switch between two threads. */ PUBLIC _port_switch _port_switch: push {r4, r5, r6, r7, lr} mov r4, r8 mov r5, r9 mov r6, r10 mov r7, r11 push {r4, r5, r6, r7} mov r3, sp str r3, [r1, #CONTEXT_OFFSET] ldr r3, [r0, #CONTEXT_OFFSET] mov sp, r3 pop {r4, r5, r6, r7} mov r8, r4 mov r9, r5 mov r10, r6 mov r11, r7 pop {r4, r5, r6, r7, pc} А смотреть надо стандарт ARM EABI по поводу того какие регистры сохранять, а какие не надо сохранять.
|
|
|
|
|
Nov 21 2014, 12:21
|
Профессионал
    
Группа: Свой
Сообщений: 1 719
Регистрация: 13-09-05
Из: Novosibirsk
Пользователь №: 8 528

|
Цитата(scifi @ Nov 21 2014, 15:25)  Всё придумано до нас: protothreads. Кстати, о protothreads. С использованием возможностей С++ становится ещё удобнее. http://blog.brush.co.nz/2008/07/protothreads/
--------------------
Russia est omnis divisa in partes octo.
|
|
|
|
|
Nov 21 2014, 13:06
|

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

|
Цитата(MrYuran @ Nov 21 2014, 14:46)  А если немного подумать, может и не нужна никакая многозадачность и вполне достаточно event-driven state machine В случае с двумя светодиодами - однозначно Я к qp давно приглядываюсь, а он за это время становится все лучше и лучше Для state machine лучше IAR visualSTATE трудно что либо найти. Неутомимая Xenia у нас тут с этим помогает.
|
|
|
|
|
Nov 21 2014, 15:46
|
Гуру
     
Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136

|
Цитата(MrYuran @ Nov 21 2014, 15:46)  А если немного подумать, может и не нужна никакая многозадачность и вполне достаточно event-driven state machine В случае с двумя светодиодами - однозначно Как сказать. Если программа описывает процесс, линейно текущий во времени, с ветвлениями и переходами, то она естественным образом ложится на сишный код, и мне такое представление кажется интуитивно более понятным. И protothreads тут весьма кстати. Те же две лампочки вписываются в эту схему. Да, всё можно свести к конечному автомату, но не всегда стоит это делать, так как читаемость кода может страдать.
|
|
|
|
|
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, 04:02
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(WitFed @ Nov 27 2014, 20:04)  Также можно завести отдельный массив "ожидателей" -- системного таймера или не 0 по какому-то адресу, который будет и "семафором", "мьютексом", и чем угодно -- не нужны никакие критические секции, никто у тебя за спиной бяку никогда не сделает в непонятной нити с высоким приоритетом, лишь бы был отладчик хоть какой-то в IDE. Непонятно - почему не нужны критические секции? Как без них если нужна атомарность доступа к объекту? Случай (работа под ОС): семафор проверяется внутри критической секции. При его занятости, ОС переводит таск в сост. "ожидает данный семафор" и переключает контекст. А как Вы своим велосипедом такой случай разрулите?  А если внутри двух критических секций? А как реализуете функцию типа WaitForMultipleObjects? Все плюсы и минусы построения диспетчера с переключением контекста (ОС) и без (все вариации суперцикла) думаю давно известны. Суперцикл хорош, только если не нужен блокирующий доступ к ресурсам (защищённым семафорами и мьютексами).
|
|
|
|
|
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, 07:49
|
Гуру
     
Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454

|
Цитата Непонятно - почему не нужны критические секции? Как без них если нужна атомарность доступа к объекту? потому что при предложенном подходе каждая функция изначально критическая секция, с атомарным доступом ко всем используемым переменным до окончания своей работы. Функции не прерываются в середине, а только в определенных местах, и они должны правильно отрабатывать смену параметров на лету. такие суперлупы неплохое и надежное решение, но все же это предыдущий шаг перед операционкой. Цитата Извините, но ваш пост воспринимается как малопонятный набор слов. труднопонятный Цитата А приведенный код попахивает рекурсией. есть такое... может с именами функций что напутано... WitFed приоткройте завесу, обрисуйте чуть подробнее задумку мастера?
|
|
|
|
|
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] - для короткого!!!
|
|
|
|
|
  |
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
|
|
|