AVR. Учебный курс. Архитектура Программ. Часть 3

Приоритетный диспетчер.
Одной из проблем простого диспетчера является то, что все задачи имеют равный приоритет. С одной стороны, это просто и удобно. С другой — какое-либо важное событие можно прошляпить, пока там конвейер перещёлкает все задачи…

Проблему решает введение приоритетов.
В простейшем случае, можно ввести два приоритета — высокий и низкий. Разница между ними будет лишь в том с какой стороны очереди они будут засовываться на конвейер. Высокоприоритетные пихаются сразу в начало, низкоприоритетные с конца.
Разумеется, тут надо следить за тем, чтобы высокоприоритетные задачи не забивали конвейер, блокируя низкоприоритетные. Никакой защиты от этого нет, только думать головой.

Если нужна высокоуровневая система приоритетов, то можно очередь задач превратить в двумерный массив, где вторым этажом будет идти приоритет задачи. Правда при этом увеличиться время обработки конвейера — ведь надо будет сперва прочесать всю очередь в поисках наибольшего элемента. Но тут можно напридумывать кучу оптимизаций. Например, сортировать очередь при постановке задачи на конвейер, либо завести TOP list приоритетов, занося туда значения приоритетов. Тогда диспетчер, обрабатывая очередь, сразу будет искать нужный элемент, ориентируясь по TOP листу. Но вот так, на вскидку, я не берусь сказать какой из приемов будет эффективней/компактней/быстрей.

Кооперативная RTOS

На самом деле, называть RTOS такую систему нельзя. Она не является чисто реалтаймовой. Но название устоялось.

Это еще более сложная структура разделения кода. Отличается тем, что теперь у нас задания это не цепочки процедурок объединенных конвейером/флаг-автоматом, а полноценные законченные задачи.

Вот, например, программа мигания двумя светодиодами с разной частотой, на диспетчере (здесь и далее псевдокод), только функциональная часть:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Led1_OFF(void)
{
SetTimerTask(Led1_ON,1000);
led1_off();
}
 
Led1_On(void)
{
SetTimerTask(Led1_OFF,1000);
led1_on();
}
 
Led2_OFF(void)
{
SetTimerTask(Led2_ON,1000);
led2_off();
}
 
Led2_On(void)
{
SetTimerTask(Led2_OFF,1000);
led2_on();
}

Как видишь, у нас тут нет функционально законченных блоков. Все наши задачи представляют собой набор простейших действий залинкованных в цепочки друг на друга. Либо напрямую, либо через таймер. В очень больших программах, даже при такой абстракции запутаться уже несложно.

На кооперативной RTOS все будет выглядеть куда прозрачней:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Blink1_Task(void)
{
while(1)
	{
	Led1_on();
	Dispatch(delay,1000);
	led1_off();
	Dispatch(delay,1000);
	}
}
 
Blink2_Task(void)
{
while(1)
	{
	Led2_on();
	Dispatch(delay,1000);
	led2_off();
	Dispatch(delay,1000);
	}
}

Как видишь, программа двух действий разбилась на две независимые части. Мигание диодом 1 и мигание диодом 2.

Вырви из кода один блок, замени Dispatch(delay,1000) на delay_ms(1000); и задачу можно заливать без какой либо организации прям в контроллер и она будет работать как простейшая программа!
Т.е. теперь у нас уже получаются полноценные программы внутри основной программы. А что же такое

1
Dispatch(delay,1000);

А это команда API ядра RTOS. В нашем псеводокоде, допустим, ей мы приказываем передать управление диспетчеру (чтобы он занялся другими задачами) и вернуться сюда же когда таймерная служба натикает нам 1000мс.

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

Таким образом, у нас получается что задача может быть активной — получит управление сразу же как это будет доступно, ожидающей чего-либо, например, того же таймера и неактивной — просто висящей в памяти программ. Сюда же могут быть добавлены приоритеты, а также чайный клуб с шахматами и поэтессами.

Задача может ожидать события от чего угодно — таймера,установки/сброса бита, сообщения от другой задачи. Также она может посылать события другим задачам.

Подробней расписывать не буду, о работе корпоративной RTOS очень хорошо расписано в Salvo RTOS User Manual (перевод Андрея Шлеенкова).
А несколько позже, наверное, дам подробный раскур какой-нибудь кооперативной RTOS (пока еще даже не решил какой. Salvo, SCM или AVRX — сам еще ни одну из них толком не щупал).

Вытесняющая RTOS
Все не относящееся к вытесняющим ОС назвать полноценной операционной системой реального времени нельзя. Дело в том, что понятие реального времени означает, что чтобы ни случилось — время обработки и создание реакции на критического событие не превысит заданного порога.

Т.е. если заявлено что при ядерной ялярме стержни в реактор упадут в течении 50мс, то должно быть так.

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

Этого недостатка лишена вытесняющая RTOS. Она является по настоящему реалтаймовой.

И задачи там могут быть вида:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Blink1_Task(void)
{
while(1)
	{
	Led1_on();
	delay_ms(1000);
	led1_off();
	delay_ms(1000);
	}
}
 
Blink2_Task(void)
{
while(1)
	{
	Led2_on();
	delay_ms(1000);
	led2_off();
	delay_ms(1000);
	}
}
 
Да хоть 
 
DumbLoop(void)
{
m1: goto m1
}

Тут это не важно. Диспетчер тут обладает полномочиями прерывать выполнение любой задачи когда угодно, между двумя любыми командами процессора — делается это посредством прерывания на котором сидит диспетчер. В результате, каждая задача выполняется рывками, кусочками заданного интервала. Скажем, по 10мс на задачу. В этом случае, даже если одна будет наглухо висеть, то вся система не повиснет, т.к. диспетчер будет у ней забирать управление и делать более важные вещи. Круто, да?

Но за крутизну приходится платить. Т.к. у нас происходит отбирание управления у одной задачи и передача другой, то надо сохранять состояние остановленной задачи. И одним сохранением регистров (как это было в прерывании) тут не обойдешься. Ведь у каждой задачи будет еще и собственный стек, и локальные переменные и регистры.
В результате, на каждую задачу выделяется свое адресное пространство с запасом и не дай боже одной задаче потереть контекст другой — все рухнет как карточный домик.

На восьмиразрядных микроконтроллерах вытесняющие RTOS существуют (правда название не вспомню, но видел), однако худо бедно этот монстр может запуститься разве что на ATmega128. Да и то большая часть ресурсов, в первую очередь оперативная память, МК будет занята проворачиванием диспетчера, так что кооперативки тут предпочтительней. А вот на ARM ситуация с этим делом уже намного лучше.

61 thoughts on “AVR. Учебный курс. Архитектура Программ. Часть 3”

  1. Ура, я первый =))))

    Долго ждал эту статью, кстати, Вы будете выкладывать предыдущую? Или она останется на зеркале?

  2. Да, еще вопрос(теперь уже после прочтения):
    Будете рассказывать про ARM и заодно FPGA?
    Так как достаточно актуальная вещь=)

  3. Как обычно, попридираюсь =) Словосочетание «Кооперативная RTOS» повергает мозг в когнетивный диссонанс и полный ступор… В состоянии негодования я всё-таки заставил себя дочитать статью до конца и увидел, что ниже таки объясняется, кто кооперативность и RTOS — вещи несовместимые в принципе, но тем не менее, лучше исправить на что-нить типа «OS с кооперативной многозадачностью», или просто «Кооперативная ОС». Да, чуть ниже опечатка — «о работе корпоративной RTOS».
    В примерах с миганием светодиодом лучше написать
    Blink1_Task(void)
    {
    while(1){
    Led1_on();
    Dispatch(delay,1000);
    led1_off();
    Dispatch(delay,1000);
    }
    }
    иначе смысл теряется.

    Ну и по поводу вытесняющей многозадачности: недавно щупал Atomthreads — пока не понял, насколько она RTOS, но вытесняющая многозадачность там есть. Утверждается, что ядро и четыре задачи помещаются на шестнадцатой меге.

    1. Дак оно уже устоялось как выражение. Ту же сальву возьми, или скм. Кооперативки в чистом виде, а тудаже — RTOS :)

      Но ладно, добавил сразу же пояснение. =)

      1. [offtopic]
        Ну да, устоявшаяся терминология — страшная вещь =) Вот, например, взять слово «хакер» — меня просто бесит, когда его употребляют в контексте «взломщик, компьютерный злоумышленник и тд.», а что поделаешь — людям вдолбили в голову, что хакеры — это такие плохие дяди, которые спят и видят, как кому-нибудь напакостить… Или, вот, поближе пример — все говорят RTOS, RTOS, а на самом деле, зачастую, речь идёт лишь о real-time шедуллере, так как ОС, вообще-то, не из одного шедуллера состоит. =)
        [/offtopic]
        Кстати, если уж на то пошло, то и вытесняющая многозадачность сама по себе не делает операционку риал-таймовой — требуется ещё куча механизмов.

  4. Прикольно =) У меня тут после прочтения предыдущей части возникла идея замутить свою RTOS под PIC18, повкурил во все что нашел по этой теме, общая концепция стала ясна, но в некоторых деталях вылазят затупы. К примеру, есть N задач, одна из них имеет самый высокий приоритет, пусть это получение данных с АЦП. Нужен какой-то сервис, который будет проверять доступность ресурса (завершение преобразования), проверять флаг, и пока он не выставлен, отдавать процессорное время другой задаче, которая от АЦП не зависит (которой не требуются эти данные). И по выставлению флага сохранять контекст задачи и переходить к приоритетной задаче. Это единственное, что пришло в голову, но я не уверен, что иду верной дорогой =)
    Еще мельком увидел всякие фишки типа mailbox’ов (я так понимаю для обмена данными между задачами), в итоге в голове возникла какая-то каша =) Самая основная непонятка — как сделать ядро ОС такой, чтобы оно могло.. мм, скажем так, асбстрагироваться от задачи, то есть чтобы без модификации ядра можно было полноценно работать с i2c, uart и т.п. периферией, то есть чтобы ядро могло распознавать необходимые ресурсы для этих блоков и соот-но отдавать нужную задачу ЦПУ.

    1. Заколебёшься описывать зависимости от всех ресурсов. Проще отдать это на откуп задаче. Типа, пнул задачу, она быстренько проверила нужные её ресурс и, в случае недоступности, сразу вернула управление обратно. Конечно, получается изрядная трата ресурсов на лишнее переключение контекста, зато просто и надёжно. А если очень уж хочется заморочиться, то нужно реализовывать что-то типа кондишена — некая структура, которая описывает ожидаемый ресурс — какой параметр каким должен стать, что бы задача могла продолжить вычисление.
      Что касается обмена информацией между потоками, про mailbox`ы не понял. Обычно используются мьютексы, семафоры и shared memory (во взрослых осях ещё есть пайпы и куча прочего барахла).

      1. Угу, вот что-то такое пришло в голову — описание флагов для определенного типа перефирии, соот-но проверка на соответствие и принятие решение о продолжении/прерывании задачи.
        Mailbox, по-крайней мере в аппноте микрочиповской, описывается так «Mailboxes allow the designer to pass messages between tasks, and allows messages to be looked at when the task is ready, and to reply telling the sender that the message was received. One message can be sent to many tasks at the same time.» Мне не совсем понятно назначение, ведь имхо проще работать через ту же службу в ядре, которая получая сообщения о завершении или прерывании задачи, определит какая задача будет выполняться далее.
        Про почтовые ящики видел и в книге MicroCOS-II. Но видимо мне пока не стоит множить сущности без необходимости, и так пытаюсь откусить больше, чем могу съесть =))

        1. Тут, вероятно, проще абстрагироваться от переферии, а то замучаешься под каждую новую задачу подтачивать ядро — лучше задавать кандишен в стиле «проверь мне по такому вот адресу такой вот бит на такое вот значение». А если понаворачивать слегка, то можно сделать разные типы условий, например «N-байтное значение по адресу X равно/больше/меньше», туда же программное таймеры загнать, добавить очереди условий (типа, если прошло столько-то времени И освободился такой-то ресурс) и тд. Но не факт, что проверка таких условий не станет дороже простого переключения контекста =)

          1. Ещё один вариант — по каждому прерыванию добавлять задачу для его обработки в очередь. Такой код можно сделать в виде директивы препроцессора типа
            ISR_DPC(some_interrupt)
            {
            код обычной задачи(или потока для нормальной ОС)
            }
            Где ISR_DPC:
            void _DPC_some_interrupt();
            ISR(some_interrupt)
            {
            InsertTask(_DPC_some_interrupt);
            }

            void _DPC_some_interrupt() //здесь ; нет

            Или в случае, если нужно что-то сделать сразу после прихода прерывания, а потом обработать всё остальное:
            ISR(time_critical_interrupt)
            {
            делаем всё, что нужно сделать быстро
            InsertTask(interrupt_task,task_parameter)
            }

      2. Дак это. Семафоры для этого и придумали. Сказал диспетчеру, мол жду этот семафор и ушел гулять. Как свершилось — сразу же вызвали.

          1. Почитай мануал к сальве. Там это буквально на пальцах объясняется.

            В двух словах — это вроде флагового бита. Т.е. мы отдаем управление диспетчеру с такими словами Dispatcher(semafor,bit); и когда бит придет диспетчер нас сюда сам вернет. До этого времени задача остается в ждущем режиме.

  5. Да, кстати. Получается что в RTOS придется отказаться от прерываний? Например для UARTа..? Или завершения преобразования АЦП?

    1. Да, все аппаратное обеспечение отдается на откуп диспетчера ОС. Но ты можешь ему сказать — мол жду такой то бит в таком то регистре. Как будет — зови. И он вызовет задачу когда это случится.

      1. М… Расходуем ресурсы))Теперь мы должны тратить тики на то, чтобы посмотреть, что же там с «аля прерываниями».. А не получится так, что например мы не успеваем посмотреть в UART, таким образом мы профукаем бит данных, в итоге получим трудно ловимый баг..?

        1. А для этого должна быть буфферизация. Прерывания, естественно, никуда не деваются. Но при написании обработчиков очень четко следить чтобы оно не заклинило диспетчер.

          В таких системах, обычно, все аппаратное барахло завязано в ядро ос. Уже отлаженное и протестированное, а на откуп прикладного программера остается лишь развесистое АПИ, где он может сказать ОС что он и от какой железки хочет.

            1. Может быть зависит от ОС. У меня на FreeRTOS (вытесняющая) прерывания как раз очень даже используются.
              Пример: есть задача, которая обрабатывает данные, пришедщие с SPIslave/UART. Она в цикле «вытаскивает» данные из очереди. Если очередь пуста, то задача уходит в состояние блокировки.
              А в очередь данные «пишет» обработчик прерывания. Как только в очередь поступили данные, проверяется, нет ли задач, которые ждут появления данных на этой очереди. Далее, если приоритет ждущей задачи выше, чем «прерванной» задачи то происходит переключение. Если приоритет такой же, то процессорное время будет выделено позже.
              Таким образом время в прерывании небольшое, задержки минимальны и порцессор не загружен бесполезными опросами железа.

              Проблема вытесняющих ОС в том, что под каждую задачу — свой стек (по 80-120К). Памяти программ надо всего лишь 8K под ядро.

    2. В той же Atomthreads вполне себе можно использовать прерывания, только в начале и в конце надо вызвать спец. функции ядра. Ну и аккуратно с ними надо быть, что бы не сломать общую архитектуру программы.

  6. Использовал кто-то scmRTOS? Это система с вытесняющей многозадачностью, авторы заявляют про малое требование к оперативной памяти, минимум 512 кБ, сейчас пытаюсь с ней разобраться, интересует ваше мнение.

    1. Используем для AVR и ARM (Mega16, Mega32, AT91SAM7S64), отличная ОСь! глюков замечено небыло… обновления выходят, проект поддерживается, свободная, открытая, бесплатная — несомненные плюсы, по функционалу не uCOS, но и ресурсов надо на порядок меньше (вытесняющая по приоритетам + минимально необходимый набор сервисов типо таймеров, мьютексов, evnt-ов, очередей итд).

  7. Интересующимся стоит почитать раздел «Процессы» из книги Таненбаума «Операционные системы. Разработка и реализация». Там написано по теме побольше. Плюс раскрыта тема сисе^Wмежпроцессного взаимодействия, а это очень весело :)

    1. Да пока не сталкивался с тем, что это было бы проблемой. Завожу глобальные переменные через которые все это дело общается. Получается компактно и быстро. А взаиомодступ исключаю атомариванием.

  8. @Разумеется, тут надо следить за тем, чтобы высокоприоритетные задачи не забивали конвейер, блокируя низкоприоритетные. Никакой защиты от этого нет, только думать головой.@

    Кстати говоря решенная для ОС задача, в самом простом случае выбирается интервал времени по истечению которого система проворачивает все задачи без учета их приоритета(в усложненном варианте в форточках нечто подобное, ресурсоёмкие задачи с высоким приоритетом систему не вешают, хотя и отклик от «низкоприоритетных» возрастает в разы)

    1. Проблема в том, что у МК очень ограниченные ресурсы, и то, что реализовано в «больших» системах (а большие — это уже Z80 :)), может оказаться просто неприемлемо — либо занимает очень много места, либо требует много памяти, либо просто долго выполняется.

      А вообще, такие вопросы разбирались в книжке Танненбаума, кажется, ее тут уже советовали.

      1. понятное дело что сложное не накрутить, но реализовать конечное время отклика на первый взгляд не так и трудно, пусть хотя бы той же высокоприоритетной задачей или может быть вочдоком?

          1. всмысле рухнет? я имел ввиду по некоторому прерыванию — маскируем приоритеты и отрабатываем все процессы в очереди, размасскировываем и обнуляем таймер.

            или имеется ввиду что в этот момент может что-то важное прийти и не успеет провернуться? о_О

          2. Почему без сохранения контекста? Вроде, речь про это не шла.

            Есть другой вариант — на каждую очередь/задачу выделять определённое количество квантов времени, зависящее от приоритета очереди.

            1. А тогда почему сразу не сделать вытесняющую RTOS? Вот только ресурсов 8ми разрядного МК на это не хватит.

              1. Ой, сорри, что-то я спросоня не обратил внимание, что речь вообще про однозадачный шедулер =) Тов. queeni смутил рассуждениями про операционки. ИМХО для однозадачного шедулера предложенный им вариант весьма сомнителен…

  9. > Если нужна высокоуровневая система приоритетов, то можно очередь задач превратить в двумерный массив, где вторым этажом будет идти приоритет задачи. Правда при этом увеличиться время обработки конвейера — ведь надо будет сперва прочесать всю очередь в поисках наибольшего элемента. Но тут можно напридумывать кучу оптимизаций. Например, сортировать очередь при постановке задачи на конвейер, либо завести TOP list приоритетов, занося туда значения приоритетов. Тогда диспетчер, обрабатывая очередь, сразу будет искать нужный элемент, ориентируясь по TOP листу. Но вот так, на вскидку, я не берусь сказать какой из приемов будет эффективней/компактней/быстрей.

    Снова изобретаешь велосипед. Сложность просмотра массива с любой из оптимизаций — O(n), где N — количество задач. Двумерный массив может еще и отъесть кучу памяти при «сильно развитой» системе приоритетов. Сортировка очереди или TOP list выигрыша по времени могут и не дать, по памяти — скорее всего, да. Я бы попробовал реализовать приоритетную очередь, на blogspot’е я давал ссылку на книжку с описанием. Тоже не берусь сказать, будет ли это быстрее (все-таки при небольших n разница между линейной и логарифмической сложностью незаметна), но выигрыш по памяти почти гарантирую.

    1. Насчет таких алгоритмов стоит почитать если уж не Кнута или Кормена, то хотя бы Седжвика «Фундаментальные алгоритмы на С». Сейчас читаю, неплохо написана книга. Начиная с анализа алгоритмов, дальше структуры данных, поиск и сортировка.

      Кто бы мне эту книгу подсунул лет 5 назад… :)

    2. >Я бы попробовал реализовать приоритетную очередь, на blogspot’е я давал ссылку на книжку с описанием.
      Лучший вариант в случае если не динамического распределения памяти — по кольцевому буфер на каждый приоритет. При этом общий размер несильно превышает размер буфера без приоритетов(при правильном выборе размеров каждого из буферов), а выигрыш в скорости значителен по сравнению даже с отсортированным по приоритетом связным списком внутри массива — на каждый приоритет планировщиком производится всего по 2 операции чтения, 1 сравнения и один раз — удаление задачи. При добавлении задачи — 2 чтения, сравнение, запись и сохранение задачи. С этим могут разве что сравниться двусвязные списки на каждый приоритет в массиве, но в этом случае при добавлении задачи требуется поиск свободного места.

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

  10. Ковыряю AvrX. И это, как оказалось, на кооперативку нифига не похоже. А вполне себе rtos с вытесняющей многозадачностью, но с кооперативным выполнением задач с одинаковым приоритетом.

    Может кто чистую кооперативную ос посоветует?

    1. Мммм, у меня есть реализация вытесняющей RTOS, с приоритетами, правда, есть заморочка с задержками (пойдёт обычный _delay_ms, но с делителем, который надо получать от диспетчера (есть вариант макроса delay(int), который уже будет выполнять весь ширпотрёб))
      Если интересно, прошу в аську (указана в профайле), здесь, к сожалению, не всегда комментарии проверяю)

        1. *Надо бы здесь ввести возможность правки комментария*

          Ах да, вес прошивки — 700байт (готова к обслуживанию 32 потоков)))

          1. Комменты править раньше можно было, но уж больно адски оно грузило хостинг…

            Хм, а как сделано вытеснение? Что сохраняется под контекстом?

            1. Вытеснение — прерывание таймера

              Приоритет — время, которое таймер даст поработать потоку, прежде чем остановить его. У меня сейчас вот так (может, в будущем изменю):
              enum ThreadPrority
              {
              THREAD_PRIORITY_LOW = 0xFE,
              THREAD_PRIORITY_MEDIUM = 0xFD,
              THREAD_PRIORITY_HIGH = 0xFC
              };

              (В итоге, задача с высшим приоритетом получит в 3 раза больше процессорного времени, чем с минимальным)

              контекст — это SREG, стэк в 64 байта (регулируемо), стэк-поинтер

              естественно, я НИКОГДА не скажу, что это работает очень быстро и всё такое (на операции с стеком, сохранения текущей задачи и переходу к следующей уходит процессорное время, БЕССПОРНО), но мне, как, с рождения, программисту, позволительно понизить производительность конечного устройства ради повышения удобства работы с онным (к слову, ошибок логики тоже становица меньше, впринципе))

              1. Большая производительность и не нужна обычно. Разве что отработка критичных преырваний. В принципе нормально так. На тини не развернешься, но на меге от 64 уже можно жить.

                  1. Если будет более менее подробное описание + пример проги хотя бы с 3-4 задачами (суть задач не важна, хоть диодами мигать). То можно и на главную.

  11. Почему-то во второй части статьи мне уже не разрешает отвечать. Ладно, отвечу в третьей.
    1. > Из функции SetTimerTask можно выйти не восстановив прерывания. Это правильно?
    > Нет. Где там такое?
    А вот посмотрите случай когда нет свободный таймеров — вываливаемся БЕЗ восстановления прерываний. В сааааааамом конце этой функции. Видите?

    2. > TimerService. После шедевра: if(MainTimer[index].Time !=1){…} в .Time после отработки таймера останется величина 1.
    > Да остается 1
    Все же это не веривел, когда инициализирующее значение одно, а проверяется на другое.
    Остается изменить логику проверок…
    Да и надо бы тогда изменить сбивающий с толку комментарий чуть ниже: // Дощелкали до
    нуля?

    1. Он там раньше был и стоит даже коммент
      // тут можно сделать return c кодом ошибки — нет свободных таймеров

      Дело в том, что отсутствие свободных таймеров это сродни переполнению стека. Т.е. ахтунг капец ситуация, когда рушится вся логика. Т.е. таймеров должно хватать. И я не вижу адекватных способов разрулить ситуацию когда таймеров не хватает. Не блокировать же диспетчер и всю логику до тех пор, пока не освободится какой либо таймер. Это бред, можно сказать, что логика рушится сразу. Так что при проектировании их надо указывать с точной гарантией что хватит. А отстутсвие разрешения прерываний это максимизация ошибки. Т.е. если мы выйдем туда, то вся программа встанет колом, т.к. прерывания будут запрещены.

      2. Ну так там и написано, что проверяем на 1 или на ноль, на что именно я еще сам не решил. Стоит «To Do: посчитать по тактам» Так что я еще не уверен какую именно логику проверок там лучше сделать.

  12. Здравствуйте. Полезная статья.
    А несколько позже, наверное, дам подробный раскур какой-нибудь кооперативной RTOS (пока еще даже не решил какой. Salvo, SCM или AVRX — сам еще ни одну из них толком не щупал).
    Как я понял у вас была статья которую вы специально для этого написали ‘Работа с RTOS AVRX’ нашел в «Этеншн. Важное заявление :)», но в архиве ее не нашел. Подскажите может она по другому называется.

  13. DI HALT не подскажите на ваш взгляд самую оптимальную RTOS для МК Atmega(16, 32) в плане системных требований. Рассматривал бегло FREERTOS и Salvo. В FREERTOS в основном написано для Atmega323, он более мощный нежели Atmega16.

Добавить комментарий

Ваш e-mail не будет опубликован.

Перед отправкой формы:
Human test by Not Captcha