uRTOS - событийная операционная система реального времени с вытесняющей многозадачностью для микроконтроллеров AVR

uRTOS

Для начала сразу же хочу сказать, что не обладаю образованием программиста-системотехника, поэтому буду объяснять всё своими словами. Прошу принимать всю нижеизложенную информацию как моё личное видение. Я старался объяснить всё как можно более понятно. Но о чём-то мог забыть или что-то объяснить не так. Не стесняйтесь спрашивать и уточнять. Ну, в путь…

О СИСТЕМЕ


uRTOS – событийная операционная система реального времени с вытесняющей многозадачностью, написанная на ассемблере и предназначенная для работы на всех микроконтроллерах AVR фирмы Atmel с объёмом оперативной памяти от 128 байт. ОС легко настраивается, масштабируется и обладает адаптивным потреблением памяти микроконтроллера.


Давайте разберёмся, что такое событийная операционная система с вытесняющей многозадачностью.
ОС uRTOS состоит из ядра и пользовательских процессов (ПП).

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

Процессы – это отдельные мини-программы, куски программного кода абсолютно независимые друг от друга, выполняющие различные задачи. К примеру, один процесс может обрабатывать информацию, поступающую от датчиков, подключенных к АЦП МК, второй – отвечает за общение МК через USART, третий – выводит служебную информацию на ЖКИ, четвёртый – опрашивает кнопки клавиатуры, пятый -… и т.д. То есть, присутствует полнейшее разделение труда. Когда один из процессов выполняется, говорят, что он получил управление. В этом случае выполнение команд крутится внутри кода этого процесса и не должно выходить за его пределы. Но об этом чуть позже.

В одно и то же время может выполняться только один процесс. Поэтому, понятие многозадачности применительно к однокристальным МК, по сути, мифическое. Однако в реальных условиях диспетчер передаёт управление от процесса к процессу настолько быстро, что складывается впечатление, будто все процессы выполняются одновременно. Это, конечно же, иллюзия, но она работает.

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

Отличительная особенность ОС с приоритетным вытеснением от других ОС – возможность достаточно точно предсказать время реакции системы на внешнее воздействие. Например, если процессу-обработчику нажатия кнопки назначить наивысший приоритет, то при нажатии на кнопку выполнение любого процесса будет прервано и управление будет передано этому процессу-обработчику. В этом случае, время между нажатием на кнопку и запуском процесса-обработчика можно очень точно предсказать. Если ОС обладает предсказуемым временем реакции на внешнее воздействие, то такая ОС называется ОС «реального времени».

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

uRTOS является ОС мягкого реального времени, т.к. внутри её диспетчера любые прерывания запрещены. А это несколько растягивает интервал выполнения задачи реального времени во время работы системы при больших нагрузках, хотя и не намного.

Теперь важный момент: отдельно взятый процесс не подозревает о существовании ядра с диспетчером и других процессов. Он живёт в сладком неведении, и его задача – выполняться. Работает он, предположим, с регистрами общего назначения, сохраняет там какие-то данные, а тут вдруг раз – вытеснение. Откуда-то появляется более важный процесс и отодвигает наш процесс от регистров общего назначения, мол «дай-ка я теперь с ними поработаю, а ты пока отдохни». Регистры общего назначения одни, а процессов много, и все хотят с ними поработать. Как же быть? Ответ очевиден – отдельно сохранять содержимое регистров общего назначения для каждого процесса. И где сделать это, как не в ОЗУ МК?! То есть у каждого процесса где-то в ОЗУ есть своя маленькая нычка, в которой лежат только его регистры общего назначения. Называется эта нычка – стек процесса. И сохраняются там не только регистры общего назначения. Но об этом чуть позже.

Отсюда вытекает основная проблема всех вытесняющих ОС – жёсткая зависимость от наличия и объёма оперативной памяти микроконтроллера. Поэтому при разработке этой системы я уделил особое внимание вопросу использования ресурсов МК.
А теперь обо всём поподробнее.

О ПАМЯТИ


Исходный код системы обладает модульной структурой. Если какие-то из объектов (таймеры, события, семафоры) не используются, их модуль не подключается, благодаря чему достигается экономия используемой памяти МК. На данный момент ОС uRTOS обладает следующими характеристиками потребления памяти МК:

  • чистая ОС (ядро + idle-процесс) – 756 байт Flash + 48 байт SRAM;
  • 7 ПП, 8 таймеров/событий/семафоров – 1312 байт Flash + 360 байт SRAM;
  • 31 ПП, 32 таймеров/событий/семафоров – 1466 байт Flash + 1596 байт SRAM.

Информация о состоянии процессов, таймеров, событий и семафоров хранится в ОЗУ МК в виде двоичных масок. Номер бита в маске соответствует порядковому номеру объекта. В связи с чем, объём Flash-памяти, потребляемый системой, также зависит и от количества используемых объектов (процессов, таймеров, событий, семафоров), поскольку многие процедуры в системе рассчитаны на многобайтную обработку. Для сохранения и обработки маски 8 объектов требуется 1 байт ОЗУ и 1 цикл обработки, для 32 объектов – 4 байта и 4 цикла.

В начале файла RTOS_kernel.asm объявляется псевдофункция:

#define MASK_LEN(X) ((X-1)/8+1)

которая вычисляет минимальное количество байт, необходимых для хранения маски из X бит. Например, для хранения маски 12 процессов будут нужны 2 байта, 28 процессов – 4 байта и т.д. Таким образом, программный код подстраивается под количество объектов при помощи директив препроцессора. Неиспользуемые команды отбрасываются при компиляции, неиспользуемая оперативная память не резервируется. Подобный подход применяется в системе глобально. Что делает исходный код максимально компактным, а потребление ОЗУ – минимальным.

О ПРОЦЕССАХ


ОС uRTOS поддерживает до 31 пользовательского процесса. Каждый процесс представляет собой обособленную программу, выполняющую определённый круг задач и имеющую свой стек для сохранения данных и адресов возврата из процедур. Процессы встраиваются в систему на этапе компиляции, и динамическая работа с процессами не поддерживается.

Приоритеты процессов

В ОС uRTOS каждому процессу присваивается свой уникальный приоритет, который выбирается разработчиком из следующих соображений:

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

Индексом приоритета процесса выступает порядковый номер процесса в системе. С увеличением порядкового номера процесса его приоритет уменьшается. Самый приоритетный процесс имеет порядковый номер 0. Самым низшим приоритетом обладает системный Idle-процесс, представляющий собой бесконечный цикл. Это т.н. «холостой» процесс, который не выполняет никаких полезных действий, кроме как занимает неиспользованное процессорное время ядра МК. Должно же оно что-то делать, пока все пользовательские процессы находятся в состоянии ожидания. Вот и прыгает программный счётчик внутри idle-процесса, пока не «проснётся» один из пользовательских процессов и не отберёт у него управление.

Приоритеты процессов определяются в таблице TaskPrio в основном файле проекта uRTOS.asm. В нём последовательно размещаются метки макросов INIT всех процессов: первая метка принадлежит самому приоритетному пользовательскому процессу, последняя – наименее приоритетному процессу. Метка idle-процесса находится ниже всех – в подключаемом файле RTOS_misc.asm. Поэтому, разработчику не надо самостоятельно её указывать.

Состояния процессов

Процессы могут находится в одном из трёх состояний:

  • RUN – процесс выполняется. И будет выполняться до тех пор, пока не будет прерван более приоритетным процессом, прерыванием или пока сам добровольно не передаст управление диспетчеру;
  • READY – процесс готов к выполнению и ждёт, пока выполнятся все процессы с более высоким приоритетом;
  • WAIT – процесс находится в ожидании таймера, события или семафора.

Состояния процессов (1) и (2) принудительно задаются диспетчером, состояние (3) – добровольно выбирается самим процессом.

Если Процесс-1 в состоянии RUN был прерван другим, более приоритетным Процессом-2, то он переводится диспетчером в состояние READY, чтобы продолжить своё выполнение позже, а прервавший его Процесс-2 – в состояние RUN. Если прервавший его Процесс-2 в свою очередь также прерывается более приоритетным Процессом-3, то и он тоже переводится диспетчером в состояние READY, а прервавший его Процесс-3 в состояние RUN. Как только самый приоритетный Процесс-3 выполнит необходимые действия и отдаст управление диспетчеру, он переводится в состояние WAIT, а прерванный им Процесс-2 – в состояние RUN. Выполнив необходимые действия, Процесс-2 переводится диспетчером в состояние WAIT, а прерванный им Процесс-1 – в состояние RUN. Так осуществляется приоритетное вытеснение процессов.

Номер выполняемого процесса хранится в переменной CurrTask. Маска процессов, находящихся в состоянии READY, т.е. готовых к выполнению, хранится в переменной ReadyTasks. Маски процессов в состоянии WAIT находятся во вспомогательных переменных, относящихся к модулям, чей макрос был использован процессом для перехода в это состояние (Timers, Events, MutexWaiters).

Структура процессов

Все процессы должны иметь следующую структуру:

1. Task0:	INIT 10
2.		clr 	r0
3.		clr	r1
4. Loop:	EventWait Event0
5.		ldi	r16,0xFF
6.		mov	r20,r16
7.		Wait 50
8.		dec	r18
9.		rjmp	Loop

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

.macro	INIT
	ldi	r16,@0
	rcall	INIT_TASK
.endm

В качестве аргумента макроса передаётся размер дополнительного места в стеке процесса, которое может быть использовано для сохранения данных или адресов возврата из процедур. Данный макрос также выполняет ещё одну важную функцию. После выполнения инструкции rcall INIT_TASK выполнение программы возвращается в секцию инициализации, а в стеке ядра остаётся адрес инструкции, следующей за макросом INIT, т.е. первой инструкции самого процесса. Этот адрес и записывается в самое начало стека процесса как адрес возврата в процесс.

(2) – первая инструкция процесса. К ней перейдёт управление при первом запуске процесса. Инструкции (2) и (3) будут выполнены только один раз – при первом запуске процесса (удобно применять для инициализации периферии). Инструкции (4) – (9) представляют собой бесконечный цикл, внутри которого будет происходить, собственно, выполнение процесса. Основным требованием к пользовательскому процессу является наличие внутри его цикла макросов, предусматривающих передачу управления диспетчеру. Это все системные макросы Event*, Mutex* и макрос Wait, которые будут там в любом случае, поскольку выполнение всех процессов в ОС uRTOS так или иначе зависит от событий, происходящих в системе. Требование это обусловлено тем, что, если выполнение цикла процесса будет происходить непрерывно (без передачи управления диспетчеру), процессы с более низким приоритетом никогда не получат управления.

Стеки процессов

Понятие стека – самое фундаментальное, если речь идёт об ОСРВ, потому как всё функционирование вытесняющей ОСРВ сводится к работе со стеком. Разберёмся теперь, что же такое стек процесса. Стек процесса – это область в оперативной памяти, в которой сохраняется вся информация, имеющая отношение конкретно к выполняемому процессу. А именно:

  • 32 байта регистров общего назначения;
  • 1 байт регистра SREG;
  • 2 байта адреса возврата в процесс;
  • а также любая другая информация, сохраняемая непосредственно процессом.

Минимальная длина стека процесса составляет 35 байт. Для МК, имеющих интерфейс для работы с внешней памятью, требуется также дополнительный байт для сохранения регистра RAMPZ.

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

Сразу же оговорим основные свойства стека:

  • стек увеличивается в сторону начала памяти, уменьшается – в сторону конца памяти;
  • стек всегда читается справа — налево, т.е. начало стека находится справа, конец стека – слева. Соответственно, последний байт стека всегда имеет меньший адрес, чем первый байт (исключение составляет пустой стек, в котором начало и конец стека находятся по одному адресу);
  • в указателе стека SPH:SPL всегда находится адрес первой свободной ячейки памяти после конца стека (т.е. предыдущей ячейки памяти, потому что со стеком всё «справа-налево»). К примеру, если последний сохранённый в стеке байт имеет адрес 0x0105, то в указателе стека SPH:SPL находится значение 0x0104. Именно такой смысл принимает в дальнейшем фраза «адрес конца стека процесса». Не путайте её с фразой «адрес последнего байта в стеке процесса».

Инициализация стеков процессов осуществляется сразу после старта системы и очистки ОЗУ МК. Происходит она следующим образом:

  1. В регистр Х выставляется начальный адрес таблицы адресов концов стеков процессов ProcStackTable. Потому как заполняться стеки процессов будут из конца в начало (слева — направо).
  2. В регистр Y выставляется начальный адрес таблицы стеков процессов ProcData.
  3. Счётчик порядкового номера процесса устанавливается в 0.
  4. Текущий номер процесса умножается на 2, чтобы получить смещение по таблице TaskPrio в основном файле проекта uRTOS.asm, в которой хранятся адреса макросов INIT всех процессов. Адреса эти имеют размерность 2 байта.
  5. В регистр Z выставляется адрес начала таблицы TaskPrio, к которому прибавляется смещение из п.4.
  6. Из ячейки таблицы TaskPrio, на которую указывает регистр Z, извлекается адрес макроса INIT для данного процесса и сохраняется в стек ядра для последующего перехода.
  7. Выполняется команда ret, после выполнения которой выполнение программы переходит к макросу INIT процесса (но мы пока всё ж таки внутри ядра). Код процесса фактически начинается со следующей после макроса INIT команды.
  8. В макросе INIT передаём размер дополнительного места в стеке процесса и возвращаемся к инициализации, сохраняя в стеке ядра адрес первой инструкции процесса (первой команды после макроса INIT).
  9. Прибавляем размер дополнительного места в стеке к адресу в регистре Y. На этом этапе мы запаслись для процесса местом, где он будет сохранять адреса возврата из процедур или другие данные. Далее идёт секция, в которой будут храниться регистры общего назначения. То есть, с этого адреса будет начинаться восстановление контекста процесса. Следовательно, этот адрес является адресом конца стека процесса.
  10. Сохраняем текущее значение регистра Y в таблицу адресов концов стеков процессов ProcStackTable в ячейку, соответствующую текущему номеру процесса.
  11. Пропускаем место, необходимое для сохранения регистров общего назначения и регистра SREG, прибавляя его длину к значению регистра Y.
  12. Вытаскиваем из стека ядра адрес первой инструкции процесса, который остался там после выполнение макроса INIT, и сохраняем его в оставшиеся две ячейки стека процесса по адресу, который находится в регистре Y.
  13. Переводим процесс в состояние READY, выставляя его бит в маске готовых к запуску процессов ReadyTasks.
  14. Увеличиваем значение текущего номера процесса на 1.
  15. Повторяем действия 4 – 14, пока не разметим стеки для всех процессов.

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

[ДМn-1, …, ДМ1, ДМ0,] R0, SREG, R1, …, R30, R31, [RAMPZ,] АВс, АВм

где
ДМn-1 – (n-1)-ый байт дополнительного места в стеке,
ДМ1 – 1-й байт дополнительного места в стеке,
ДМ0 – 0-й байт дополнительного места в стеке,
АВс – старший байт адреса возврата в процесс,
АВм – младший байт адреса возврата в процесс.

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

При сохранении процессом данных в своём стеке (использование команд push, call, rcall) последующее сохранение контекста этого процесса приведёт к смещению ячеек, хранящих регистры общего назначения, регистр SREG и адрес возврата, в сторону конца стека процесса. Например, если выполнить следующий код в каком-то процессе:

…
ldi	r30,0xAA
push	r30
dec	r30
push	r30
Wait	10
pop	r30

стек этого процесса после выполнения макроса Wait 10 примет следующий вид:

[ДМn-1, …, ДМ3, ДМ2,] R0, SREG, R1, …, R29, 0xA9, R31, [RAMPZ,] АВс, АВм, 0xA9, 0xAA

где
АВс – старший байт адреса инструкции pop r30,
АВм – младший байт адреса инструкции pop r30.

Как видно, конец стека процесса сместился на 2 байта влево (c адреса ячейки ДМ0 в адрес ячейки ДМ2), заняв первые 2 байта дополнительного места в стеке. Если бы дополнительного места в стеке процесса не было предусмотрено (в начале процесса стоял бы макрос INIT 0), то были бы затёрты два первых байта стека предыдущего процесса, что привело бы к нарушению логики работы системы и вызвало бы её неминуемое «падение». Так что, всегда выделяйте дополнительное место в стеке процесса, непосредственно работающего со своим стеком (использующего инструкции push, pop, rcall, call). Точно рассчитать это место можно, проанализировав исходный код процесса от начала до конца и посчитав количество вложенных вызовов процедур и инструкций push, используемых процессом. Каждый вызов процедуры занимает 2 байта в стеке процесса, а инструкция push – 1 байт. Если же процесс не работает напрямую со своим стеком, макрос инициализации для экономии ОЗУ МК может принять вид INIT 0.

Загрузка контекста процесса (макрос LOAD_TASK)

При загрузке контекста процесса первым делом в переменную KernelSP сохраняется текущее значение указателя стека SPH:SPL, поскольку оно указывает на конец стека ядра. Далее адрес конца стека запускаемого процесса из соответствующей ячейки таблицы ProcStackTable записывается в указатель стека SPH:SPL, после чего последовательным выполнением инструкций pop производится восстановление регистров общего назначения и регистра SREG. При этом значение указателя стека увеличивается, пока не достигнет ячеек, в которых хранится адрес возврата в процесс. Остаётся только выполнить команду reti, и вот были мы «в диспетчере», а стали – «в процессе».

Сохранение контекста процесса (макрос SAVE_TASK)

Выполнение любого процесса может быть прервано двумя способами:

  • если происходит прерывание;
  • если процесс добровольно отдаёт управление диспетчеру, переходя в ожидание события, таймера или семафора.

В обоих случаях адрес инструкции, выполнение которой было прервано, автоматически сохраняется в начале стека процесса. Следующим пунктом будет необходимо выполнить сохранение контекста процесса — выполнить макрос SAVE_TASK, в котором путём последовательных выполнений инструкции push регистры общего назначения и регистр SREG сохраняются в том же стеке процесса. При этом указатель стека уменьшается. Последним сохраняется в стек регистр R0. После чего текущее значение указателя стека SPH:SPL сохраняется в соответствующей ячейке таблицы ProcStackTable, поскольку оно является адресом конца стека процесса. А вместо него в указатель стека SPH:SPL возвращается значение конца стека ядра, которое было сохранено в переменной KernelSP при выполнении макроса LOAD_TASK. Итак, были «в процессе», а стали — «в обработчике прерывания» или «в диспетчере» (а по факту, и там, и там – это «в ядре»).

Добровольная передача управления от процесса к диспетчеру производится путём выполнения макросов перехода в ожидание (таймера, события, семафора). Рассмотрим макрос перехода в ожидание таймера — Wait X, где X – число тиков системного таймера:


	.macro	Wait
1.		cli
2.		push	r16
3.		outi	ProcVAR,pWait
4.		outi	ProcVAR+1,High(@0)
5.		outi	ProcVAR+2,Low(@0)
6.		pop	r16
7.		rcall	DOOR
	.endm

(1) – при передаче управления от процесса к диспетчеру производится передача служебных данных через 3-байтный массив ProcVAR. Операция эта носит атомарный характер и не может быть прервана. Поэтому, первой инструкцией макроса является команда запрета прерываний. Все макросы, выполняемые в процессах, начинаются с этой инструкции.
(2) – сохраняем в стек процесса значение регистра r16, потому что он будет использован при передаче информации.
(3) – загружаем в первый байт массива ProcVAR значение, соответствующее типу макроса.
(4) – во второй байт массива ProcVAR загружаем старший байт интервала таймера.
(5) — в третий байт массива ProcVAR загружаем младший байт интервала таймера.
(6) – восстанавливаем значение регистра r16 из стека.
(7) – выполняем переход в единую для всех процессов точку входа в диспетчер, сохраняя адрес инструкции процесса, следующей за макросом, в стек процесса.

После выполнения макроса оказались в такой же ситуации, как и при возникновении прерывания — адрес возврата в процесс уже сохранён в стеке, осталось только выполнить макрос SAVE_TASK, и мы окажемся «в диспетчере». Ну а дальше извлекается информация из массива ProcVAR и производится обработка данных макроса.

Таблица ProcStackTable

Теперь о том, как выбирается «соответствующая ячейка таблицы ProcStackTable».
Всякий процесс, находящийся в состоянии RUN, всегда характеризуется следующими утверждениями:

  • значение переменной CurrTask равно номеру этого процесса;
  • бит регистра ReadyTasks, с порядковым номером, соответствующим номеру этого процесса, равен нулю;
  • значение переменной CurrTaskSP равно адресу ячейки в таблице ProcStackTable, содержащей адрес конца стека этого процесса.

То есть, для того, чтобы перевести любой процесс из состояния READY в состояние RUN и передать ему управление, в диспетчере выполняются следующие действия:

  1. Снимается (обнуляется) бит запускаемого процесса в регистре ReadyTasks.
  2. Переменной CurrTask присваивается значение, равное номеру запускаемого процесса.
  3. Вычисляется адрес ячейки таблицы ProcStackTable, содержащей адрес конца стека запускаемого процесса, путём сложения начального адреса таблицы ProcStackTable с удвоенным номером запускаемого процесса. С удвоенным потому, что в ячейках таблицы находятся 2-байтные адреса.
  4. Переменной CurrTaskSP присваивается адрес ячейки из п.3.
  5. Выполняется макрос LOAD_TASK.
  6. Выполняется команда reti.

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

О ДИСПЕТЧЕРЕ


Диспетчер (или планировщик) – это одна из составных частей ядра ОС, которая отвечает за организацию выполнения процессов в соответствии с их приоритетами и осуществляет переключение контекстов этих процессов (выполняет их перепланировку). Исходный код диспетчера находится в файле RTOS_dispatch.asm.

В ОС uRTOS механизм переключения процессов базируется на событийном принципе: любой процесс, находящийся в состоянии WAIT, переводится в состояние READY только при возникновении события, которое этот процесс ожидает (сработал таймер, изменился уровень на одной из ног МК, отправился или принялся байт на одном из интерфейсов).

В случае возникновения такого события, информация о процессе, его ожидавшем, передаётся в область диспетчера, где производится арбитраж процессов. И, если ожидавший события процесс имеет приоритет выше, чем приоритет выполняющегося в данный момент процесса, этот процесс переводится в состояние RUN, а процесс, чьё выполнение было прервано – в состояние READY.

Если возникновение события инициировало перевод сразу нескольких процессов в состояние READY, маска этих процессов опять же передаётся в область диспетчера, где производится их арбитраж. И, если выясняется, что в этой маске содержится бит более приоритетного процесса, чем выполняемый в данный момент, этот процесс переводится в состояние RUN, а прерванный им – в состояние READY.

О СОБЫТИЯХ


В состав ядра ОС uRTOS входит модуль обработки до 32 пользовательских событий. Находится этот модуль в файле RTOS_events.asm. Этот модуль также можно назвать механизмом пользовательских прерываний, потому что действует он по тому же принципу, что и механизм прерываний МК – при возникновении какого либо системного события выполнение программы переходит по адресу обработчика этого события. Только в случае с пользовательскими событиями управление будет передано пользовательскому процессу, ожидавшему это событие.

Пользовательское событие может быть любым – изменение показаний какого-либо датчика, превышение значением какой-либо величины, появление новых данных и т.п.

Все действия с пользовательскими событиями производятся через системные макросы: EventWait, EventSet, EventSetISR, EventClean.

Макрос EventWait

Для того, чтобы процесс перешёл в состояние ожидания какого-либо пользовательского события, необходимо выполнить этот макрос, передав с его аргументом имя ожидаемого события. Например: EventWait MyEvent.

При этом происходит следующее:
  • текущий процесс передаёт управление диспетчеру. При этом адрес следующей за макросом EventWait инструкции процесса сохраняется в стеке процесса в качестве адреса возврата в процесс;
  • выполняется проверка переменной ReadyEvents на предмет установленного бита события MyEvent. Если выясняется, что бит события установлен, бит обнуляется, а управление возвращается к текущему процессу. Если выясняется, что бит не установлен, выполняется следующее:
  • текущий процесс переводится в состояние WAIT;
  • в соответствующей событию MyEvent ячейке массива Events выставляется бит текущего процесса;
  • из регистра ReadyTasks извлекается номер следующего по приоритету READY-процесса, который тут же переводится в состояние RUN и получает управление.

Макрос EventSet

При помощи этого макроса процесс может «просигналить» системе о возникновении пользовательского события. Например: EventSet MyEvent.

При этом выполняется следующее:
  • текущий процесс передаст управление диспетчеру. При этом адрес следующей за макросом EventSet инструкции процесса сохраняется в стеке процесса в качестве адреса возврата в процесс;
  • данные из соответствующей событию MyEvent ячейки массива Events проверяются на наличие в них битов ожидавших события процессов;
  • если выясняется, что события MyEvent ждёт один или несколько процессов, их биты выставляются в маске готовых к запуску процессов ReadyTasks, после чего производится арбитраж процессов. И, если среди них нет более приоритетного, чем текущий процесс, управление возвращается к текущему процессу. В противном случае, текущий процесс переводится в состояние READY, а более приоритетный (из ожидавших события MyEvent) – в состояние RUN и получает управление;
  • если выясняется, что события MyEvent никто не ждёт, в переменной ReadyEvents выставляется бит этого события, а управление возвращается к текущему процессу.

Макрос EventSetISR

Этот макрос выполняет те же действия, что и макрос EventSet, но применяется он только в «быстрых» обработчиках прерываний (о «быстрых» и «сложных» обработчиках прерываний читайте ниже).

Макрос EventClean

Иногда возникает необходимость продолжить выполнение процесса только после возникновения нового события, а не в случае, если событие уже произошло на момент проверки. Или просто нужно обнулить какое-либо событие, которое на момент своего повторного возникновения уже обрабатывается каким-либо процессом. Для этих целей применяется макрос EventClean, который снимает (обнуляет) бит события в переменной ReadyEvents. Например: EventClean MyEvent.

Минимальное время, необходимое для передачи управления более приоритетному процессу, ожидавшему событие, в случае его возникновения, составляет 37 мкс.

О ПРЕРЫВАНИЯХ


В ОС uRTOS не существует такого понятия как «код выполняется в прерывании». Прерывание всего лишь инициирует передачу управления от процесса к ядру, в котором уже выполняется код. Часть программного кода ядра, обрабатывающая событие возникновения прерывания, называется обработчиком прерывания. В ОС uRTOS возможно организовать два уровня обработчиков прерываний – быстрый и сложный.

«Быстрый» тип обработчика прерывания целесообразно применять, если обработчик не содержит большого числа команд. В этом случае обработчик будет являться частью ядра и выполняться будет на уровне ядра (не смотря на то, что может быть объявлен в файле с пользовательским процессом) и пользоваться будет стеком ядра. Вложенные «быстрые» прерывания в этом случае будут запрещены ядром. Для «быстрого» типа обработчиков прерываний хорошо подойдут обработчики прерываний аппаратных устройств (USART, TWI, АЦП), обработчики внешних прерываний. Одним словом, везде, где важна высокая скорость реакции на прерывание, целесообразнее будет применить «быстрый» тип обработчика прерывания.

«Быстрые» обработчики прерываний строятся по следующей схеме:

1. _ADCC:	
2.	SAVE_TASK
3.	…
4.	…
5.	rjmp	RET_TO_TASK

(1) – метка на код обработчика прерывания. Переход к этой метке прописывается в соответствующем месте в секции векторов прерываний МК, которая находится в файле RTOS_vectors.asm.
(2) – макрос сохранения контекста процесса, который был прерван возникшим прерыванием.
(3)–(4) – инструкции обработчика прерывания.
(5) – команда возврата в процесс, находящийся на момент выхода из обработчика прерывания в состоянии RUN. Я обращаю на это особое внимание, потому как, если в коде обработчика прерывания будет использован макрос EventSetISR, в результате его выполнения более приоритетный процесс (ожидавший событие) может получить управление, и возврат будет произведён уже в этот процесс, а не в тот, который был прерван возникшим прерыванием. Это важно. В этом случае переключение контекстов процессов произойдёт внутри обработчика прерывания, а конкретно – внутри макроса EventSetISR.

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

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

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

О ТАЙМЕРАХ


Для формирования временных задержек в выполнении кода процессов в ОС uRTOS применяется 16-битный системный таймер (использующий один из аппаратных таймеров МК), срабатывающий через определённые промежутки времени, называемыми тиками системного таймера.

Любой процесс, использующий по ходу своего выполнения временные задержки, должен иметь для себя зарезервированной одну из ячеек (3 байта) в таблице Timers. Эта ячейка называется таймер процесса. В ней содержится 16-битное значение тика системного таймера, при котором таймер процесса сработает. Также в этой ячейке хранится номер процесса, который ожидает срабатывания таймера. Всего системой поддерживается до 32 таймеров процессов.

При каждом тике системного таймера происходит переход к обработчику прерывания системного таймера, в котором 16-битное значение тика системного таймера, хранящееся в переменной TimerTick, инкрементируется, и производится проверка всех активных таймеров из переменной ActiveTimers на предмет совпадения их значений срабатывания с текущим значением тика системного таймера. Если значения совпадают, бит сработавшего таймера в переменной ActiveTimers снимается, а номер процесса, ожидавшего срабатывания этого таймера, передаётся в область диспетчера, где происходит арбитраж. И, если ожидавший срабатывания таймера процесс имеет более высокий приоритет, чем процесс, выполнение которого было прервано прерыванием системного таймера, этот процесс переводится в состояние RUN, а прерванный процесс – в состояние READY. Затем производится возврат в RUN-процесс. Таким образом, перепланировка процессов производится внутри обработчика прерывания системного таймера.

Максимальная длительность задержки, которую можно получить при помощи системного таймера – 65535 тиков. То есть, при периоде системного тика, равным 1 мс, любой процесс при помощи макроса Wait сможет приостановить своё выполнение на время до 65,535 с.

Макрос Wait

Процесс может приостановить своё выполнение на заданный промежуток времени, кратный длительности тика системного таймера, при помощи это макроса. Например, если системный таймер «тикает» каждую 1 мс, то процесс может заблокировать своё выполнение на 100 мс, выполнив макрос Wait 100.

При этом происходит следующее:
  • текущий процесс передаёт управление диспетчеру;
  • производится поиск свободного таймера процесса путём инвертирования битов в маске активных таймеров ActiveTimers и получения номера первого свободного бита;
  • в ячейке таблицы Timers, соответствующей выбранному свободному таймеру процесса, сохраняется текущее значение тика системного таймера, к которому прибавляется длительность требуемой задержки. Также в этой ячейке сохраняется номер текущего процесса. Таким образом текущий процесс оказывается переведённым в состояние WAIT;
  • из регистра ReadyTasks извлекается номер следующего по приоритету READY-процесса, который тут же переводится в состояние RUN и получает управление.

Минимальное время, необходимое для передачи управления менее приоритетному процессу, в случае перехода более приоритетного процесса в состояние ожидания таймера, составляет 42 мкс.
Минимальное время, необходимое для передачи управления более приоритетному процессу, ожидавшему таймер, в случае его срабатывания, составляет 49 мкс.

О СЕМАФОРАХ


В ОС uRTOS поддерживается до 32 семафоров взаимоисключения бинарного типа. Данный тип семафора позволяет производить атомарный доступ к какому-либо объекту. Например, если несколько процессов производят обмен данными через интерфейс TWI, то только один из них может передавать и принимать данные в отдельно взятый момент времени. Если какой-нибудь процесс начнёт обмен данными в то время, как другой процесс ещё не закончил свой сеанс обмена данными, данные будут потеряны, а в работе протокола возникнут ошибки. Чтобы предотвратить возникновение подобной ситуации, необходимо воспользоваться семафором взаимоисключения, при помощи которого можно заблокировать на время обмена данными доступ к шине TWI для других процессов. По завершении обмена семафор необходимо разблокировать (освободить), чтобы доступ к шине получили остальные процессы.

Макрос MutexLock

Для блокирования доступа к какому-либо объекту можно выполнить макрос MutexLock, передав в качестве аргумента имя семафора. Например: MutexLock LCDBuf.

При выполнении этого макроса происходит следующее:
  • текущий процесс передаёт управление диспетчеру;
  • производится проверка бита семафора LCDBuf в переменной LockedMutex на предмет заблокированности (бит установлен);
  • если семафор не заблокирован, он блокируется путём выставления бита семафора в переменной LockedMutex, а также записью номера текущего процесса в соответствующую семафору LCDBuf ячейку таблицы MutexLockers. Затем управление возвращается в текущий процесс.
  • если семафор LCDBuf уже заблокирован каким-то процессом, выполняется следующее:
    — бит текущего процесса выставляется в соответствующей семафору LCDBuf ячейке таблицы MutexWaiters. Таким образом текущий процесс переводится в состояние WAIT;
    — из регистра ReadyTasks извлекается номер следующего по приоритету READY-процесса, который тут же переводится в состояние RUN и получает управление.

Макрос MutexUnlock

Данный макрос разблокирует (освобождает) семафор, предварительно заблокированный макросом MutexLock. Например: MutexUnlock LCDBuf.

При выполнении этого макроса происходит следующее:
  • текущий процесс передаёт управление диспетчеру;
  • из соответствующей семафору LCDBuf ячейки таблицы MutexWaiters извлекаются данные и проверяется, ожидал ли какой-нибудь из процессов освобождения семафора LCDBuf;
  • если никто не ждал освобождения семафора LCDBuf, бит этого семафора в переменной LockedMutex снимается (обнуляется), а управление возвращается текущему процессу;
  • если выясняется, что в маске ожидавших освобождения семафора процессов (MutexWaiters) есть установленные биты, то бит самого приоритетного из них снимается, а его номер заносится в соответствующую семафору LCDBuf ячейку таблицы MutexLockers. Т.е. семафор переблокируется этим процессом;
  • далее номер этого же процесса передаётся в область диспетчера, где производится арбитраж. И, если выясняется, что его приоритет выше приоритета текущего процесса, он переводится в состояние RUN и получает управление, а текущий процесс переводится в состояние READY. В противном случае он переводится в состояние READY, а управление возвращается к текущему процессу.

Минимальное время, необходимое для передачи управления более приоритетному процессу, ожидавшему освобождения семафора, в случае его разблокирования, составляет 37 мкс.

НАСТРОЙКА СИСТЕМЫ


Большая часть настроек ОС находится в основном файле проекта – uRTOS.asm. В секции Config настраиваются следующие параметры системы:

  • CPU_FREQ – частота работы ядра МК в герцах;
  • SAVE_RAMPZ – если МК, на котором разворачивается ОС uRTOS, имеет в наличии интерфейс для подключения внешней памяти, и при работе устройства планируется её использование, необходимо установить значение этой константы в 1. Это позволит регистру RAMPZ сохраняться в стеках процессов;
  • ALL_TASKS – общее количество процессов в системе, т.е. количество пользовательских процессов + 1 системный idle-процесс;
  • ALL_TIMERS – количество используемых таймеров процессов или, попросту говоря, количество процессов, использующих системный таймер для организации своих задержек;
  • ALL_EVENTS – количество пользовательских событий в системе. Ниже производится присвоение каждому событию его имени, что позволит в дальнейшем обращаться к событию по его имени, а не по номеру. Номера должны идти по порядку, начиная с 0;
  • ALL_MUTEX – количество используемых в системе семафоров. Ниже производится присвоение каждому семафору его имени, что позволит в дальнейшем обращаться к семафору по его имени, а не по номеру. Номера должны идти по порядку, начиная с 0.

Далее идёт секция настройки индикации ошибок. Это простое, но очень полезное средство отладки программ, встроенное в ядро системы. Оно позволяет разместить макрос ERROR_ON в любом месте в коде процесса для индикации ошибки (подачи логической 1 на один из выходов МК), в случае, если выполнение команд перейдёт к этому месту. В качестве аргумента макроса передаётся код ошибки (от 1 до 255), который при выполнении макроса сохраняется в переменной ErrorID, откуда его можно извлечь любым удобным способом (выдать в порт, по шине TWI, через USART и т.д.). Основное применение этого средства отладки – отслеживание ситуаций, когда производится запись в уже заполненные массивы данных – стеки данных, буферы обмена и т.п.

В секции Tasks производится подключение файлов, содержащих исходные коды процессов, а также выполняется назначение приоритетов процессам.

Если значение параметра ALL_TIMERS отлично от нуля, необходимо настроить системный таймер, открыв для этого файл RTOS_timers.asm. По-умолчанию, период системного тика равен 1 мс как самый оптимальный, а для тактирования системного таймера предполагается использование одного из 8-битных аппаратных таймеров МК. Но это не принципиально, можно использовать в этих целях и 16-битный таймер, однако при этом придётся подправить макрос инициализации системного таймера INIT_SYS_TIMER.

Задача настройки таймера МК сводится к расчёту значения, которое должно быть записано в регистр совпадения этого таймера, чтобы прерывание по совпадению возникало раз в 1 мс. Также в настройках таймера МК должен быть активирован сброс значения регистра счёта таймера в ноль при достижении им значения, которое находится в регистре совпадения. Называется этот сброс — Clear Timer on Compare (CTC).

Расчёт значения для регистра совпадения производится в строке:

.equ TimerDivider = CPU_FREQ/SYS_TIMER_PRESC/1000

где
CPU_FREQ – тактовая частота работы ядра МК, указанная в файле uRTOS.asm;
SYS_TIMER_PRESC – значение предделителя аппаратного таймера МК.

Повторюсь, что данный расчёт справедлив для периода системного тика в 1 мс. Если используемый таймер МК 8-битный, то значение константы TimerDivider не должно превышать 255, но должно к нему стремиться, потому что, чем ближе получившееся значение этой константы будет к 255, тем точнее будет системный таймер отмерять интервалы. Остаётся только подобрать правильное, а главное – существующее, значение предделителя таймера МК, записав его в константу SYS_TIMER_PRESC.

В строке

.equ STimerCRValue = 0<<CS22|1<<CS21|1<<CS20

производится определение значения, которое будет записано в регистр управления таймера МК для его запуска. Обычно, оно содержит биты предделителя, но может содержать и другую информацию. В данном случае в константу STimerCRValue будет записано значение, при котором Таймер-2 МК ATmega168 будет работать с предделителем, равным 32.

Последним этапом настройки ОС является настройка вектора прерывания системного таймера. В файле RTOS_vectors.asm находятся векторы прерываний для МК, на котором запускается система (не забудьте перенести туда векторы прерываний для своего МК).

В векторе прерывания по совпадению для выбранного вами аппаратного таймера МК должна находиться инструкция перехода в обработчик прерывания системного таймера:

rjmp	SYS_TIMER

На этом настройка ОС uRTOS может считаться оконченной.

ПРИМЕР ИСПОЛЬЗОВАНИЯ


В архиве uRTOS.zip находится демо-проект (AVR Studio 4.18, Proteus 7.7) под управлением ОС uRTOS. Проект демонстрирует базовые принципы работы системы и взаимодействия процессов через события. В проекте продемонстрировано ШИМ-управление двигателем постоянного тока при помощи потенциометра (переменного сопротивления) с выводом текущего уровня напряжения на входе АЦП, к которому подключен потенциометр, через видео-буфер на буквенно-цифровой ЖКИ типа HD44780, а также обмен данными через USART.

В выполнении задачи задействовано 5 пользовательских процессов:
  • RunADC – запускает одиночное аналого-цифровое преобразование с частотой 100 Гц, и производит усреднение по 4-м последним результатам измерений;
  • RunUSART — обслуживает USART, принимая и выводя данные;
  • RunPWM – сохраняет 8-битное значение напряжения на выходе потенциометра в регистр совпадения одного из аппаратных таймеров МК, работающего в режиме быстрой ШИМ, меняя тем самым скважность импульсов;
  • LCD_Driver – обслуживает видео-буфер ЖКИ, выводя данные из этого буфера в ЖКИ;
  • InitLED – моргает светодиодом активности с частотой 1 Гц.

Алгоритм работы системы следующий:
  1. Процесс RunADC запускает одиночное АЦ-преобразование и уходит в ожидание своего таймера на 10 мс. За это время происходит прерывание по завершению АЦ-преобразования, обрабатываемое «быстрым» обработчиком _ADCC, который сохраняет полученное значение в определённой ячейке ОЗУ. После срабатывания таймера процесс извлекает новое значение из этой ячейки и проверяет, отличается ли предыдущее значение напряжения на выходе потенциометра от только что измеренного. Если не отличается, процесс запускает очередное преобразование и снова уходит в «спячку» на 10 мс. Если же значения отличаются, процесс выставляет флаг события ADC0Changed, после чего производит преобразование значения напряжения на выходе потенциометра из двоичного в двоично-десятичное, блокирует доступ к видео-буферу, устанавливая семафор LockLCD, и скидывает полученное двоично-десятичное значение напряжения в видео-буфер. Далее процесс освобождает доступ к видео-буферу и выставляет флаг события появления новых данных в видео-буфере – NewLCDData. После чего запускает очередное преобразование и снова уходит в «спячку» на 10 мс. И так далее…
  2. Появления события ADC0Changed ждал процесс RunPWM, который, получив управление, вытаскивает из ОЗУ новое двоичное значение напряжения и скидывает его в регистр сравнения ШИМ-таймера. После чего возвращается в ожидание события ADC0Changed.
  3. Появления события NewLCDData ждал процесс LCD_Driver, который, получив в свою очередь управление, тут же блокирует доступ к видео-буферу семафором LockLCD и выгребает из буфера все новые данные в свой вспомогательный буфер, чтобы как можно быстрее освободить доступ к видео-буферу для других процессов. Как только данные скопированы, флаг события NewLCDData сбрасывается, потому как в промежуток времени между получением этим процессом управления и блокированием доступа к своему видео-буферу процесс RunADC мог успеть запихнуть очередную порцию новых данных в видео-буфер и опять выставить флаг NewLCDData, который на момент вывода всех новых данных из буфера будет уже не актуальным. После чего доступ к видео-буферу освобождается, процесс начинает вывод данных из вспомогательного буфера в ЖКИ. Как только вывод будет окончен, процесс проверяет событие NewLCDData. Если оно не установлено, процесс переходит в его ожидание. Если флаг события уже установлен, процесс начинает очередной сеанс вывода данных в ЖКИ. И так далее…
  4. Процесс RunUSART находится в постоянном ожидании события rs232RX_Event, сигнализирующего о поступлении через USART новых данных во входящий буфер. Если такое событие происходит, процесс получает управление и извлекает из входящего буфера USART пришедшие данные, после чего анализирует их. Если поступил символ «t», процесс выводит в ответ фразу «Hello, World!». Если поступил символ «e», процесс выводит текущий номер ошибки из перемменной ErrorID (удобно для отладки).
  5. Процесс InitLED только зажигает и гасит светодиод активности через определённые интервалы.

Вот, вроде, и всё, что я могу рассказать о своей системе. Если есть вопросы, с удовольствием отвечу.
  • +3
  • 28 сентября 2011, 17:07
  • uRTOS
  • 2
Файлы в топике: uRTOS-11.zip, uRTOS.zip

Комментарии (47)

RSS свернуть / развернуть
Спасибо за Ваш ТИТАНИЧЕСКИЙ труд!
0
Не за что! Лишь бы польза была :)
0
Хорошая и, главное, доступно написаная статья. +1
0
Первое впечатление очень хорошеее.
Попробую.
0
  • avatar
  • mzw
  • 28 сентября 2011, 18:33
не обладаю образованием программиста-системотехника, поэтому буду объяснять всё своими словами.
видимо образование и знание это не совсем одно и тоже :)
0
статья хорошая, а на форуме некоторые чуть человека не загнобили. Стыдно товарищи.
0
Ту ветку удалить бы надо, как ненужный козлосрач!
0
Специально посмотрел, что на форуме — нда-а. Очевидно что профессионализм и умение и главное желание себя прилично вести никак не связаны. У автора терпение ну неимоверное, снимаю шляпу ).
0
Перенес в тематический блог.
0
Дочитал до середины, а потом оказалось что ОС на асме… Было бы удобно узнать об этом в начале… Добавьте, пожалуста!
0
Во втором абзаце я указал на это: «uRTOS – событийная операционная система реального времени с вытесняющей многозадачностью, написанная на ассемблере и предназначенная ...» ;)
0
отличная статья! за асм +100500!!!
0
Спасибо за отзывы, друзья! Буду рад, если кому-то пригодится моя система.
Я перезалил архив с проектом. Добавил в проект процесс-драйвер USART.
0
  • avatar
  • uRTOS
  • 28 сентября 2011, 22:42
Вступите в блог «OS и RTOS» а то когда вы правите пост он возвращается обратно в личный блог.
0
we.easyelectronics.ru/page/about/

тут небольшая справка по сообществу.
0
Спасибо, Ди! Сделано!
0
А теперь все это на Си?
0
  • avatar
  • DVF
  • 28 сентября 2011, 23:49
asm is true
0
Зачем? На Си и так полно ОС. Настоящих монстров :)
0
Аффтор решил отдать свое творение за «так»? или все еще просит 150 зеленый енотов
-1
Придётся ответить Вашей же фразой: «150 баксов за то, что лежит в свободном доступе… Нонсенс»
0
Хорошо написанная, грамотная статья. Молодец автор.
0
Вопрос к уважаемым пользователям сообщества и к автору uRTOS:
-Можно ли uRTOS оформить как ассемблерные вставки для IAR например и использовать проектах написанных на С.(минимальный размер кода достигнут функциональность достаточная)

Интересуют мнения.
0
К сожалению, ничего не могу ответить Вам, так как не пишу на С для AVR и не знаком с IAR. Представляю, о чём Вы спрашиваете, но моих знаний в этой области недостаточно, чтобы дать Вам ответ.
0
Отвечу за автора — конечно можно! Выбираете из uRTOS необходимые Вам фрагменты, правильно их оформляете и используйте на здоровье.
0
Есть вопрос к сообществу.
Добавил возможность работы ОС в кооперативном режиме, могу черкнуть статейку. Будет ли это интересно кому-то?
0
  • avatar
  • uRTOS
  • 17 декабря 2011, 02:00
Да. Пиши конечно.
0
OK. В ближайшее время выложу.
0
что-то я перевела на ATMega128, оставила одну задачу (мигание PD5), компилируется, но сходу как-то не мигает на STK500// буду рыть, что накосячила…
0
нее, мигаеть. Эт я поторопилась. Тестю дальше…
0
Однако, мне нравится. респект автору :-)
0
Добавил возможность работы ОС в кооперативном режиме, могу черкнуть статейку. Будет ли это интересно кому-то?
Когда добавите статейку? Прошел уже год.
0
Уважаемый Автор, прошу ответить Вас по поводу статейки работы ОС в кооперативном режиме.
Если Вас не затруднит, конечно.
0
Вечер добрый! Статью про кооперативный режим я тогда почти дописал, но потом обстоятельства изменились, и работа была отложена в сторону. Потом как-то всё руки не доходили её закончить. Год пролетел незаметно. Месяца два назад возникло несколько идей, взялся переписать целиком всё ядро системы, добавив в него некоторые новые функции, и написать новый мануал. Но занимаюсь этим только если выдаётся свободное время, а случается это нечасто, так как сейчас нахожусь в море. Так что, будет уже новый, можно сказать, релиз :) с поддержкой вытесняющего режима, кооперативного, с разными и одинаковыми приоритетами процессов, с обменом приоритетов, с выбором минимального размера кода ядра либо его максимального быстродействия и ещё кое-какими функциями :) Будет и новый мануал. Только по срокам пока ничего сказать не могу — делаю всё исключительно из интереса и чтобы мозги в море не закисали от однообразия окружающей обстановки)))) Надеюсь, я ответил на Ваш вопрос. P.S.: Благодарю Вас за интерес к моему детищу ;)
0
А почему бы не отписыватьсяч периодически о прогрессе? К тому же в обсуждениях могут выплыть незамеченные баги или интересные идеи.
Еще, если затягивается, можно недописанную статью завершить словами в духе «to be continued» или же сделать фичекат и опубликовать.
0
Да, можно, конечно же. Постараюсь поскорее все собрать в читаемый вид и опубликовать статейку.
0
Добрый день Морским ВОЛКАМ.
Тема, которую Вы описываете крайне интересна, и многие с нетерпением ожидают продолжения. Так как Вы в море, и как я полагаю у Вас достаточно свободных минут, то опубликовав незаконченные идеи, Вы могли бы общаться с теми кому интересна Ваша работа (да и время бы полетело быстрей). В любом случае народ ждет Ваш труд.
С Уважением!
0
Приветствую!)))
Свободных минут, как это ни странно, не так уж и много. Но дело движется, пускай и не так быстро, как хотелось бы. Спасибо Вам за отзывы.
0
Я так понимаю в тини ее не впихнуть, ибо 360 байт срам минимум?
0
Я запускал систему с 5 пользовательскими процессами на ATtiny45. Сомневаюсь, что в подобном случае контроллер способен на какую-то полезную работу, но система работала нормально. А так, почему бы и не впихнуть в 5 задач какой-нибудь полезный код?! ;)
0
Уважаемый автор, смею надеяться, что к Новому году Вы все таки сумеете опубликовать хотя бы часть Ваших соображений
по поводу кооперативной ОС. Новогодние каникулы будут длинными, поэтому такой подарок был бы очень кстати.
В любом случае примите Поздравления с наступающим Новым 2013 годом, пусть он будет более удачливым, чем прошедший!
С Уважением.
0
Похоже мы не дождемся Ваших статей.
Спасибо за обещания.
С Уважением.
0
Вы так иезуитски назойливо-вежливы, прямо как менеджер заинтересованной фирмы, которая пока еще не имеет реальных рычагов давления на источник получения гешефта :D Конечно же это не так, но сам стиль доставил.

P.S. RTOS очень хороша, но может безвременно сгинуть в небытье, если… если кто-то не прикрутит ее к Arduino, где именно такой RTOS и не хватает(только ее надо переписать на C++), и где она проживет очень долго. А в версии для нового ARM чипа Atmel она может подрасти от nano-размера до полноценного micro.
0
Хочу поблагодарить всех, кто оставил свои отзывы о моей системе. Также хочу принести извинения за до сих пор не написанную статью. Почему так и не написал? Причин несколько. Во-первых, я перестал видеть необходимость в существовании такой системы. Изначально мне хотелось создать очень маленькое вытесняющее ядро, что, вроде бы, получилось. Но зачем оно теперь нужно, когда с каждым днём появляются всё новые и новые чипы с кучей памяти на борту? А если и отталкиваться от малого размера кода и потребляемой SRAM, то всё равно не вижу круга задач, которые могли бы быть решены микроконтроллерами с малыми объёмами памяти при помощи моей системы. Всё, что можно выжать из таких контроллеров, делается и без RTOS. Во-вторых, я понял, что программирование на ассемблере имеет вполне ощутимый предел по объёму кода, при достижении которого написание и отладка программы превращается в разрыв мозга. Разработка нескольких устройств, в программном коде которых используются операции умножения и деления с плавающей запятой, подтвердили это. В связи с чем я был вынужден перейти на C и C++. И всё равно, большинство задач решаю без применения RTOS. А если и возникает необходимость в ней, то использую scmRTOS, которой хватает за глаза. Возвращаться к программированию на ассемблере уже, честно говоря, неохота.

Была, правда, идея создать и разместить в инете простенький IDE на PHP+HTML+AJAX, который позволял бы скомпоновать и настроить произвольную модульную систему путём добавления в неё объектов (кнопка, ЖКИ, USART, I2C, произвольная шина. шаговый двигатель и т.д.) и задач (контроль уровня, ШИМ, таймеры и т.д.) с последующим выводом готового ASM-кода на основе uRTOS. Другими словами, предполагалась возможность, не будучи программистом, настроить поведение любого входа или выхода (или даже целого порта) микроконтроллера путём создания алгоритмов его работы и взаимосвязи с другими объектами и получить готовую прошивку для системы, выполняющей несколько раздельных задач. Но руки так и не дошли. Да и к тому же всё это очень напоминает Ардуину. Так что, ещё раз приношу извинения перед всеми, кто ждал от меня продолжения статей по uRTOS. Тем не менее, последняя версия моей системы ещё более оптимизированная по размеру и с поддержкой кооперативного режима вполне работоспособна. Если кому-то интересно, могу выложить. Исходники хорошо прокомментированы, так что, уверен, разобраться можно будет и без поясняющих статей. Всем удачи! :)
0
Тем не менее, последняя версия моей системы ещё более оптимизированная по размеру и с поддержкой кооперативного режима вполне работоспособна. Если кому-то интересно, могу выложить. Исходники хорошо прокомментированы, так что, уверен, разобраться можно будет и без поясняющих статей.
Да, обязательно выложи. Ну и статьи если есть в черновиках, но не собираешься дописывать — опубликуй as is с дисклеймерои.
+1
Поддерживаю Vga, сейчас появился новый ATtiny 1634 с 16 К ПЗУ, поэтому uRTOS может вполне удачно встать на эту машину и места для пользовательских задач хватит вполне с избытком. Интерес представляет именно механизмы передачи управления при активизации задач с более высоким приоритетом, и в общем все остальное тоже. В 80-х годах была опубликована книжка Элфринга, но там не расшифровывались макросы, а здесь понятно как все работает.
Для well-man2000.Вы так иезуитски назойливо-вежливы, прямо как менеджер заинтересованной фирмы…
Очень бы хотелось быть менеджером, а еще лучше ТОП-менеджером. Но к сожалению мне это не грозит.
0
у меня более годная операционка вышла, т.к. срача в комментах на порядки больше.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.