Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Многозадачность на авр
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
Dimmix
Подскажите однако как лучше запустить многозадачность на авр-ах
V_G
Работа по прерываниям - почти та же многозадачность. Плюс в xmega-x - интенсивное использование DMA и механизмов событий. А реализация многозадачности как во "взрослых" процессорах скорее породит многоглюковость и тугодумие.
Если процессор плохо приспособлен для вашей задачи (многозадачности) - стоит сменить процессор.

Лучше один день потерять, потом за час долететь! ©
Artem_Petrik
Цитата(Dimmix @ Sep 5 2010, 15:06) *
Подскажите однако как лучше запустить многозадачность на авр-ах

Все зависит от задачи. Если не нужно задачи динамически создавать/удалять, то, ИМХО, scmRTOS - лучший выбор. Доку от нее в любом случае почитать стоит.
_Pasha
Цитата(Dimmix @ Sep 5 2010, 16:06) *
Подскажите однако как лучше запустить многозадачность на авр-ах

Элементарно - берете и запускаете. Если так формулировать свои мысли, ответа Вы не дождетесь
slanted
Цитата(Dimmix @ Sep 5 2010, 16:06) *
Подскажите однако как лучше запустить многозадачность на авр-ах

Ничего сложного. Можно взять scmRTOS или freeRTOS, можно самому написать в расслабленном режиме за неделю.
Всего-то нужно уметь сохранять и восстанавливать контекст (за вдохновением см. реализацию setjmp и longjmp из avr-libc), ну и при запуске задачи придется вручную расписать нужную область памяти так, как это сделала бы функция save_context. Ну. и потом просто восстановиться туда - все, мы уже летаем.
На самом деле, занятие это достаточно грустное - скажем, в mega168 с её килобайтом памяти влезают всего две-три задачи, так что для таких развлечений лучше подобрать кристалл, где памяти хотя-бы два-четыре килобайта минимум.
sensor_ua
Цитата
...нужно уметь сохранять и восстанавливать контекст...
Самое простое, ИМХО, это посмотреть реализацию TinyTimber. Вообще-то Dimmix спросил
Цитата
как лучше
. Я предпочитаю кооператив а-ля Protothreads - как на AVR, так и на других МК. Считаю, что в большинстве случаев (кроме узкозаточенных решений) критичная ко времени реагирования часть кода должна быть минимизирована и умещаться в обработчиках прерываний, а остальное спокойно работать в фоне. Расход памяти при кооперативе значительно меньше, но приходится платить рулением статическими переменными и решать несколько больше вопросов синхронизации.
777777
Цитата(Dimmix @ Sep 5 2010, 16:06) *
Подскажите однако как лучше запустить многозадачность на авр-ах

А как ее можно не запускать? Вряд ли хоть один проект может обойтись без прерываний, а обычно 90% работы выполняется в них. А что это, если не многозадачность?
MrYuran
Цитата(slanted @ Sep 7 2010, 01:49) *
На самом деле, занятие это достаточно грустное - скажем, в mega168 с её килобайтом памяти влезают всего две-три задачи, так что для таких развлечений лучше подобрать кристалл, где памяти хотя-бы два-четыре килобайта минимум.

Кроме вытесняющей, бывает ещё и кооперативная многозадачность, которая дополнительной памяти практически не требует.
slanted
Цитата(MrYuran @ Sep 7 2010, 12:49) *
Кроме вытесняющей, бывает ещё и кооперативная многозадачность, которая дополнительной памяти практически не требует.

Это как? Что там, что там - сколько задач, столько и контекстов, стеков и struct task'ов.

Цитата(777777 @ Sep 7 2010, 12:19) *
А как ее можно не запускать? Вряд ли хоть один проект может обойтись без прерываний, а обычно 90% работы выполняется в них. А что это, если не многозадачность?

Это не многозадачность, это асинхронная стейт-машина :-)

Цитата(sensor_ua @ Sep 7 2010, 07:19) *
Я предпочитаю кооператив а-ля Protothreads - как на AVR, так и на других МК.

Кооперативность или вытесняемость - вещь, в общем-то перпендикулярная к методу организации передачи управления.

Цитата
Считаю, что в большинстве случаев (кроме узкозаточенных решений) критичная ко времени реагирования часть кода должна быть минимизирована и умещаться в обработчиках прерываний, а остальное спокойно работать в фоне. Расход памяти при кооперативе значительно меньше, но приходится платить рулением статическими переменными и решать несколько больше вопросов синхронизации.

Все это так, но прототреды и прочие завуалированные стейт-машины дико неудобны тем, что _весь_ код должен поддерживать асинхронность сверху донизу. В какой-то момент придется выбирать: либо память и честное переключение контекста, либо повеситься без мыла на прерываниях.
MrYuran
Цитата(slanted @ Sep 7 2010, 18:34) *
Это как? Что там, что там - сколько задач, столько и контекстов, стеков и struct task'ов.
Кооперативность или вытесняемость - вещь, в общем-то перпендикулярная к методу организации передачи управления.

А вот и нет. В кооперативной задача сама "знает", когда отдаст управление, и соответственно сохраняет свой контекст. Не все регистры подряд, а только нужную инфу. Например, в static переменных. В отличие от вытесняющей, где ход выполнения задачи может прерваться вообще посреди оператора или функции.
slanted
Цитата(MrYuran @ Sep 7 2010, 18:42) *
А вот и нет. В кооперативной задача сама "знает", когда отдаст управление, и соответственно сохраняет свой контекст. Не все регистры подряд, а только нужную инфу. Например, в static переменных.

Те регистры что не "все подряд" сохранит компилятор при вызове функции переключения задач ;-)
А то что вы описываете - это прототреды.
777777
Цитата(MrYuran @ Sep 7 2010, 18:42) *
А вот и нет. В кооперативной задача сама "знает", когда отдаст управление, и соответственно сохраняет свой контекст. Не все регистры подряд, а только нужную инфу. Например, в static переменных. В отличие от вытесняющей, где ход выполнения задачи может прерваться вообще посреди оператора или функции.

Ага, я вот тут набросал многозадачную операционку с кооперативной многозадачностью:

Код
for(;;)
    {
    task1();
    task2();
    task3();
    // ...
    }
sensor_ua
Цитата
Все это так, но прототреды и прочие завуалированные стейт-машины дико неудобны тем, что _весь_ код должен поддерживать асинхронность сверху донизу. В какой-то момент придется выбирать: либо память и честное переключение контекста, либо повеситься без мыла на прерываниях.

Похоже, у Вас просто нет опыта использования подобных технологий. Холивар разводить не хочу, да и пока не о чем спорить. Потому как вижу только Ваши эмоции, но ничего относящегося к предмету.
slanted
Цитата(sensor_ua @ Sep 7 2010, 20:19) *
Похоже, у Вас просто нет опыта использования подобных технологий. Холивар разводить не хочу, да и пока не о чем спорить. Потому как вижу только Ваши эмоции, но ничего относящегося к предмету.

Покажите код штоле ;-)
sensor_ua
Цитата
Покажите код штоле ;-)

Вот выдрал простой кусочек из реального старинного проекта "выходного дня" - всего-лишь блинк, который пришлось вставлять в программку вместо обычного ожидающего
Код
void BlinkLed(unsigned short Blink_Count, unsigned short LED_on_ms, unsigned short LED_off_ms)
дабы добавить к имевшейся задаче ещё парочку параллельно выполняющихся. Всё это фунциклируетsmile.gif без жесткого риалтайма.
Код
unsigned short clock(void){
  unsigned short tms;
  __enter_critical();          
  tms = xtimer_ms;
  __exit_critical();
  return (tms);        

}

unsigned short clock_Elapsed(unsigned short tstart){
  return ((unsigned short)( clock() - tstart));
}

static unsigned short BCount;
static unsigned short Bon_ms;
static unsigned short Boff_ms;
static unsigned short Bts_ms;

void xBlinkLed_Start(unsigned short Blink_Count, unsigned short LED_on_ms, unsigned short LED_off_ms){
  BCount = Blink_Count;
  Bon_ms = LED_on_ms;
  Boff_ms = LED_off_ms;
  Bts_ms = clock();
  LED_OFF();
}

bool xBlinkLed_Done(bool forceoff){
  static unsigned char st = 0;
  if(forceoff){
    BCount = 0;
    st = 0;
    LED_OFF();
  }
  if(BCount){
    switch(st){
    case 0:
            if(clock_Elapsed(Bts_ms) < Boff_ms)break;
            LED_ON();  Bts_ms = clock();      
            st = 1;

    case 1: if(clock_Elapsed(Bts_ms) < Bon_ms)break;
            LED_OFF(); Bts_ms = clock();
            BCount -= 1;                
            st = 0;
            break;

    default:
            st = 0;
            break;
    }
  }
  if(BCount == 0){ return true;}
  return false;
}

Тут в явном виде хоть и не представлены именно прототриды, но зато неплохо выражены способы применения и видны накладные
slanted
Цитата(sensor_ua @ Sep 8 2010, 00:49) *
Вот выдрал простой кусочек из реального старинного проекта "выходного дня" - всего-лишь блинк, который пришлось вставлять в программку вместо обычного ожидающего

Жуть :-)

На тредах это было бы просто
Код
while ( timeout--)
{
    timeout & 1 ? LED_ON() : LED_OFF();
    kernel_sleep(delay);
}


Но речь не столько об этом, сколько о том, что написание и отладка более-менее сложной логики превращается в ад - представьте что у вас там пара десятков состояний, причем переключаемых не по "switch ( g_state )", а по битовым флажкам, условиям нулевости некоторых указателей и так далее. Нет, адекватно перенумеровать их все можно далеко не всегда, да и что толку с того, что если мне надо будет постоянно помнить, что в state1 у меня p_queue должен быть NULL, а в state2 взведен флаг F_NEW_MESSAGE и сброшен F_RESYNC - не проще ли сразу их и проверять? Это раз.

Второе, даже если я успешно построил свою машину с двумя десятками состояний, я не смогу написать код в виде:
Код
void serial_console_thread(void) {
    for(;;) {
        print("What's your name?");
        readline(buf);
        print("Hello, "); print(buf);
}

void gui_thread(void)
{
    for(;;) {
        key = readkey();
        if ( key == KEY_RIGHT )
            menu_move_right();
        ....
        else
             beep(); // wrong key pressed
        display_update();
    }
}

как это позволила бы сделать система с передачей управления через сохранение контекста (неважно, с вытеснением или без оного - я же могу просто в ключевых местах yield() расставить).

Я буду вынужден _весь_ код, с самого нижнего до самого верхнего уровня порезать кровавыми ножницами на мелкие кусочки в асинхронном стиле и переключать их по тем же флагам, т.е. что-то вроде:
Код
while ( (msg=get_message())!=0 ) {
  switch ( msg )
  {
  case SERIAL_TX_COMPLETE:
  case SERIAL_RX_COMPLETE:
      console_update(&console, msg);
      break;
  case DISPLAY_UPDATE_COMPLETE:
      ...
      break;
  case KEY_PRESSED:
      ...
      break;
  }
}

void console_update(struct console *pc, int msg)
{
   switch ( pc->state )
   {
   case START:
        serial_send_async("What's your name?");
        pc->state = QUESTION_SENT;
        break;
   case QUESTION_SENT:
        switch ( msg )
        {
        case SERIAL_TX_COMPLETE:
                serial_receive_async(buf);
                break;
        case SERIAL_RX_COMPLETE:
                pc->state = GREETING_SENT;
                struct vector v[] = { "Hello, ", buf, "!"};
                serial_send_async_vector(v, 3);
                break;
        }
        break;
   case GREETING_SENT:
        if ( msg == SERIAL_TX_COMPLETE ) pc->state = START;
        break;
   }
}


Теперь попробуйте вообразить в таком стиле не "Hello, Vasya!", а что-то вроде SMTP, или, скажем, веб-панель управления... а светодиодами помигать - это не показатель.
MrYuran
Цитата(slanted @ Sep 8 2010, 15:40) *
Я буду вынужден _весь_ код, с самого нижнего до самого верхнего уровня порезать кровавыми ножницами на мелкие кусочки в асинхронном стиле и переключать их по тем же флагам

Да, есть такое...
Но тут, как говорится, за всё надо платить.
И за удобство, и за свободные ресурсы.
Сомневаюсь я что-то, что кому-нибудь придёт в голову поднимать SMTP или WEB на меге с 1к ОЗУ
sensor_ua
Цитата
я же могу просто в ключевых местах yield() расставить

Вот тут и начинается "кровосмешение" - у мну в коде именно yield() и ставится. Примерно так
Код
void serial_console_thread(void) {
  __TICK_BEGIN__
  while( !print("What's your name?"){ TICK_YIELD(); }
  readline(buf);
  while(!buf){ TICK_YIELD(); readline(buf); }
  while( !print("Hello, "){ TICK_YIELD(); }
  while( !print(buf){ TICK_YIELD(); }
  __TICK_END__
}
void gui_thread(void)
{
   __TICK_BEGIN__    
  while( NO_KEY == key = readkey() ){ TICK_YIELD();}
        
  if ( key == KEY_RIGHT )
    menu_move_right();
    ....
  else
    beep(); // wrong key pressed
  display_update();
  __TICK_END__
}

Метки в TICK_YIELD() создаются из макроса __LINE__.
Но где надо я могу себе позволить тупо перерисовать из документации машину состояний с теми же именами состояний (например машина состояний ModBus).
slanted
Цитата(sensor_ua @ Sep 8 2010, 16:15) *
Вот тут и начинается "кровосмешение" - у мну в коде именно yield() и ставится. Примерно так

Если это реализация на основе duff's device, то вы не сможете сделать yield изнутри функции. В результате stdio отдыхает, библиотеки, если они не специально спроектированы под async, отдыхают...
sensor_ua
Цитата
вы не сможете сделать yield изнутри функции
Простите, я неясно написал? TICK_YIELD() это реализация yield(). Вываливаемся в шедулер или вызывающую функцию - если второе, то организация вываливания в шедулер просто чуть-чуть дольше, чем в первом случае. Как ни странно, но последовательный набор действий с ожиданиями (ввод-вывод разной природы) используется довольно часто и второй случай без срочного вываливания в шедулер вполне востребован. Насчёт stdio частично правы - одним ретаргетингом ожидающий ввод-вывод на неожидающий не поправишь, но как и с библиотеками проблема полноценного использования stdio более теоретического плана, чем практического. Асинхронный ввод-вывод же (да, его нужно делать самому - в ANSI он не есть таковой) организовывается как неожидающий и при этом легко превращается в ожидающий, но не наоборот. Оборачивание же стандартных sprintf и sscanf вполне несложная работенка.
slanted
Цитата(sensor_ua @ Sep 8 2010, 19:07) *
Простите, я неясно написал? TICK_YIELD() это реализация yield(). Вываливаемся в шедулер или вызывающую функцию - если второе, то организация вываливания в шедулер просто чуть-чуть дольше, чем в первом случае.

Насколько дольше? Как по мне - придется в каждую функцию во всей цепочке вызовов вставить индикатор, показывающий, просто так мы возвращаемся или с отдачей управления.

Цитата
Как ни странно, но последовательный набор действий с ожиданиями (ввод-вывод разной природы) используется довольно часто и второй случай без срочного вываливания в шедулер вполне востребован.

Для ввода-вывода с некоторой минимальной обработкой это вполне применимо, кто бы спорил. Вот сложная логика, вкупе с отсутствием локальных переменных - лучше ну его нафиг.

Повторюсь - подобные подходы плохи не тем, что они "усложняют" код, они плохи тем, что требуют асинхронности по _всему_ коду, снизу доверху.
sensor_ua
Цитата
Насколько дольше? Как по мне - придется в каждую функцию во всей цепочке вызовов вставить индикатор, показывающий, просто так мы возвращаемся или с отдачей управления.
smile.gif Булевый код возврата (или NULL/!NULL) практически ничего не стОит. А анализ кода возврата при небольшой вложенности вполне сравним по времени с сохранением/загрузкой контекста (тут нужно цифры рассматривать, но, ИМХО, при глубине вложенности до 3 чаще вытеснение проиграет, а если глубина больше 4, то это уже скорее будет говнокодwink.gif).
Цитата
Вот сложная логика, вкупе с отсутствием локальных переменных - лучше ну его нафиг.
Ну не знаю - мне удобно для всякой логики (может то, что Вы называете сложной, не попадалось) - важно только понимать, где могут быть ощутимо долгоиграющие участки кода и там отдавать управление вручную. Последнее скорее может показаться непривычно (я бы сказал, что оно просто несколько не похоже на примеры из учебников), чем неудобно.
Цитата
они плохи тем, что требуют асинхронности по _всему_ коду, снизу доверху
опятьwink.gif Имею другое мнение. Считаю, что оперирование асинхронно изменяющимися данных и наличие буферов для них есть само по себе основным источником асинхронности в системах на МК. А если семь бед - один ресет(С), то зачем быть святее Папы Римского?
slanted
Цитата(sensor_ua @ Sep 8 2010, 22:04) *
smile.gif Булевый код возврата (или NULL/!NULL) практически ничего не стОит. А анализ кода возврата при небольшой вложенности вполне сравним по времени с сохранением/загрузкой контекста (тут нужно цифры рассматривать, но, ИМХО, при глубине вложенности до 3 чаще вытеснение проиграет, а если глубина больше 4, то это уже скорее будет говнокодwink.gif).

Это процессору он ничего не стоит. А вот возвращать осмысленный результат придется через глобальное состояние - код возврата-то уже занят. Конечно, все можно завернуть в макросы, но в один прекрасный момент мы обнаружим, что пишем не на C, а черт-те на чём :-)


Цитата(sensor_ua @ Sep 8 2010, 22:04) *
Считаю, что оперирование асинхронно изменяющимися данных и наличие буферов для них есть само по себе основным источником асинхронности в системах на МК. А если семь бед - один ресет(С), то зачем быть святее Папы Римского?

Если уж на то пошло, то весь мир вокруг асинхронный ;-)

И, опять же, если абстрагироваться по максимуму - само наличие разницы между синхронным и асинхронным кодом есть следствие линейной модели стека и отсутствия поддержки coroutines. Такшта спор о том, какие костыли неудобнее :-) Хотя и практически важный в некоторых частных случаях :-)
DRUID3
Цитата(Dimmix @ Sep 5 2010, 15:06) *
Подскажите однако как лучше запустить многозадачность на авр-ах

Contiki...

Цитата(MrYuran @ Sep 8 2010, 14:50) *
Сомневаюсь я что-то, что кому-нибудь придёт в голову поднимать SMTP или WEB на меге с 1к ОЗУ

Верно... но 4-х уже с головой. smile.gif

Пишу это скорее как прикол. Сам никогда не использовал эту чудо-ось, если есть у кого реальный опыт - рассказывайте.

Цитата
Веб-браузер (возможно самый маленький в мире)
... даже такое там есть...

P.S.: Гы... Сервак на 64k. - CPU: MOS-Technology 8bit 6510 - Clock Speed: 1Mhz. wacko.gif Однако... Зажрались мы. Может Хейц и прав был чо 512 k RAM должно хватать всем? smile.gif
Dimmix
вариантов мультизадачности много, видимо нужен спец компилятор который будет организовывать многозадачность в зависимости от типа треда, контики можно попробовать канечно...
т.е. Вы утверждаете что лучше поставить на атмегу ось и мышь
MrYuran
Цитата(Dimmix @ Sep 9 2010, 18:00) *
т.е. Вы утверждаете что лучше поставить на атмегу ось и мышь

Ось и мышь - понятия абсолютно перпендикулярные, никак друг от друга не зависят
Равно как USB, Ethernet и прочее другое.
Вам просто привели в пример разные подходы.
Самый простой - каруселька из стэйт-машин. Простой поначалу, до определённого уровня сложности проекта.
С определённого этапа проще становится встроенная ось.
Так что, всё зависит от задачи.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.