Tag Archives: RTOS

Установка и конфигурация FreeRTOS

На самом деле это скорей интеграция ее в проект. С технической точки зрения выглядит как подключение библиотек. Как той же CMSIS или SPL. Добавляем инклюдники, добавляем файлы в проект и все. Можно взять готовый пример и переколхозить, но в этом случае есть шанс прозевать какие-нибудь детали и получить странные эффекты. Поэтому начну с нуля, в качестве основы будет модуль Pinboard STM32F103C8T6 и Keil uVision. Под него все мы и соберем.
 

▌Качаем ОС
Тащим архив с freertos.org. Это довольно толстая солянка где 99% занимают примеры под разные архитектуры. Вот его примерная структура:

 

Вся ОС по большей части прячется вот в этих нескольких файлах:

  • queue.c — функции очередей и мутексов
  • tasks.c — функции работы с задачами
  • timers.c — функции работы с таймерами
  • croutine.c — функции работы с сопрограммами
  • event_groups.c — функции работы с флагами
  • list.c — тут все для отладки
  • port.c — платформозависимые параметры. У каждого МК этот файл свой
  • portmacro.h — настройки платформы. Тоже индивидуальный для каждого типа МК
  • FreeRTOSConfig.h — настройки ОС. Платформозависимо, а еще зависит от целей и проекта

 
(далее…)

Read More »

FreeRTOS для чайников. Краткое описание.


Бытует мнение, что RTOS это некий хардкор для избранных. Что там все сложно, замудрено и новичкам туда соваться бестолку. Отчасти тут есть доля истины, такие системы крайне сложны в отладке, но и то лишь тогда, когда вы забиваете контроллер под завязку и работаете на пределе оперативной памяти и быстродействия. Тогда да, словить какой-нибудь dead lock или пробой стека можно на раз. И попробуй найти где это случилось в этой асинхронной системе. Но простые задачи на RTOS реализуются еще проще и с меньшим количеством мозга.
 

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

▌FreeRTOS?
Почему именно она? Она популярна, она Free и она портирована на огромное количество архитектур, под нее существуют плагины для Keil и IAR и всякие примочки для PC. При этом она довольно легкая и функциональная.
 

Я не буду вам сейчас тут расписывать все эти прототипы функций, порядок записи, технические тонкости и прочее. Это все есть в технической документации и в замечательном цикле статей Андрей Курница, что был в журнале Компоненты и Технологии в 2011 году. PDF статьи вы найдете в конце.
 

Я лишь на пальцах и псевдокоде быстро распишу те инструменты которыми владеет FreeRTOS, чтобы когда вы будете читать более подробную документацию за деревьями не потеряли лес :)
 

Ну и все сказанное тут справедливо и для большинства других RTOS. Т.к. механизмы в целом все одни и те же и никто ничего нового еще не придумал.
 
(далее…)

Read More »

Пример виртуальной машины

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

Простой пример для чего это нужно — фрезерный станок с ЧПУ. И надо на нем выточить голову Сократа из цельного куска металла. Задача, на самом деле, не шибко сложная.

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

Другое дело если разбить программу на элементарные операции, вроде «Резец вверх», «резец вниз», «шаг на n мм», а прошивке скормить последовательность этих микроопераций в виде байт-кода или текстового скрипта. Как все серьезно упрощается. Да и попутно можно нашинковать Платона с Гераклом, было бы желание, да образец для копирования.

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

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

Ну, хватит воды, приведу пример того, что у меня получилось за вечерок курения в код. Код рабочий, но я там ничего не оптимизировал. Так, накидал чтоб работало, до ума доведете сами. Сделано все на базе ядра диспетчера. Я его уже описывал, поэтому работу его функций пояснять не буду
(далее…)

Read More »

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

Вытесняющий диспетчер
Давным-давно, когда я учился в школе, мне не давал покоя вопрос. Как работают параллельные операционки? Как тот же самый Windows умудряется переключать процессы, не терять регистры (да, я тогда уже начинал учить асму), как он определяет момент переключения, почему все это работает? Виртуальная память, проверка на ошибочный код — никто ничего этого не объяснял. Все твердили про какие-то там объекты, классы, и говорили очень виртуально. А мне мясо, мясо давай!

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

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

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

При срабатывании прерывания процессор идет на обработчик, а там прописано примерно так:

  • 1) Сохранение регистров.
    • 1.1) Сохранение виртуальной памяти — в файл подкачки или еще куда-нибудь.
  • 2) Сохранение текущего адреса.
  • 3) Загрузка регистров след. процесса.
    • 3.1) Загрузка виртуальной памяти.
  • 4) Загрузка адреса возврата.
  • 5) Переход по этому адресу.

По сути — не так уж и много работы, если исключить пункты X.Y. Всего и делов — сохранить, загрузить и вернуться. Вот именно это я и реализовал — простейшее ядро операционки, БЕЗ проверок и т.д.
(далее…)

Read More »

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

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

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

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

Read More »

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

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

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

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

Благодаря такой системе управляющую структуру можно вынести в отдельную библиотеку, сварганить к ней конфиг и таскать за собой ее туда сюда. Очень удобно.

А теперь подробно распишу тот диспетчер который стоит в 90% моих проектов на Си.
(далее…)

Read More »

М. Дамке «Операционные системы микроЭВМ»

Автор:		М. Дамке
Название: 	Операционные системы микроЭВМ
Издательство: 	Финансы и статистика

Старожил сайта и один из самых активных и толковых комментаторов, камрад SWG сделал замечательную вещь— отсканировал и пожал в DejaVu книгу по написанию операционных систем под микро-ЭВМ.

Я пока основательно не врубался, по диагонали пролистал — рулез! Особого грузилова нет, все в виде алгоритмов и концепций. Написано все простым и понятным языком. В частности все разобрано на примере Z80 и i8080 В общем, замечательная книга. Как говорил SWG где то в комментах: «Почитай эту книгу и изобретение велосипедов пойдет куда веселей» :)

Read More »

AVR. Учебный Курс. Управляемый вектор прерывания

Бывает такая ситуация, когда надо на один периферийный девайс повесить много разных задач, а он всего один и что то надо с этим делать.

Простой пример — таймер и его прерывание по переполнению.
Мы можем задавать выдержку и по прерыванию делать какие-нибудь операции. Но если в один момент времени мы хотим чтобы таймер по прерванию сделал одну операцию, а потом другую, третью. Да сколько угодно, в зависимости от состояния. А вектор один.

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

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

То есть в свитче вида:

1
2
3
4
5
6
7
switch(x)
	{
	1: Действие 1
	2: Действие 2
	3: Действие 3
	4: Действие 4
	}

Будет последовательное сравнение х вначале с 1, потом с 2, потом с 3 и так до перебора всех вариантов. А в таком случае реакция на Действие 1 будет быстрей чем реакция на Действие 4. Особо важно это при расчете точных временных интервалов на таймере.

Но есть простое решение этой проблемы — индексный переход. Достаточно перед тем как мы начнем ожидать прерывание предварительно загрузить в переменные (а можно и сразу в индексный регистр Z) направление куда нам надо перенаправить наш вектор и воткнуть в обработчик прерывания индексный переход. И вуаля! Переход будет туда куда нужно, без всякого сравнения вариантов.
(далее…)

Read More »

AVR. Учебный Курс. Оценка загрузки контроллера.

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

В один момент мне пришла в голову одна идея — а почему бы не заставить рисовать временные диаграммы работы задач сам контроллер? Это же просто! Берем и в диспетчере, перед вызовом задачи выставляем бит порта в 1. А когда диспетчер задач опустошается полностью, то есть выполняется переход на Idle — сбрасываем бит в 0. В результате, у нас на выходе будет подобие ШИМ. Если постоянно крутится Idle — будут нули перманентно. Если же проц в поте лица гонит через себя непрерывно код, то будут высокий уровнь сплошняком. А если все прерывисто — что то ШИМообразное. Причем чем больше загрузка процессора тем выше заполнение. Можно поставить интегрирующую RC цепочку и получим аналоговый сигнал. Хоть на стрелочный индикатор заводи :). Сказано — сделано.
(далее…)

Read More »

AVR. Учебный курс. Операционная система. Пример.

Отлично, с теорией работы ОС ознакомил. Устанавливать научил, осталось научить использовать весь этот конвеерно таймерный шухер. Чем я сейчас и займусь. Сразу берем быка за рога и формулируем учебно-боевую программу.
Тестовое задание:
Пусть у нас будет ATMega8, с несколькими кнопками. АЦП и подключеним к компу через UART. На меге будет три светодиода.

  • Девайс должен при включении начинать мигать зеленым диодом, мол работаю.
  • При этом раз в секунду сканировать показания АЦП и если показания ниже порога — Моргать красным диодом.
  • По сигналу с UARТ с целью защиты от ошибок сделать по байту ‘R’ установку флага готовности, а потом, в течении 10ms если не придет байт ‘A’ сбросить флаг готовности и игнорировать все входящие байты кроме ‘R’. Если ‘A’ придет в течении 10мс после ‘R’, то отправить в UART ответ и зажечь белый диод на 1 секунду.

Вот так вот, не сильно сложно. Но мне просто лень делать что либо сложней, а для тестовой задачи сгодится. (далее…)

Read More »

AVR. Учебный курс. Операционная система. Установка

Ядро у нас есть, теперь осталось это все хозяйство запихать на МК. Для этого всего лишь надо рассовать нужные части кода в исходник. Показывать буду на примере ATmega8. Для других МК разница минимальная. Может быть с таймером что нибудь помудрить придется, но не более того.
Например, недавно, вкорячивал ту же схему на ATmega168, так пришлось подправить иницилизацию таймера — регистры там зовутся по другому. Пришлось изменить макрос OUTI — так как многие привычные уже регистры перестали загружаться через комадну OUT — выпали из диапазона, только через LDS/STS ну и, собственно, все хлопоты. Потратил минут 20 на переименование регистров и заработало.
(далее…)

Read More »

AVR. Учебный курс. Операционная система. Таймерная служба

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

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

В чем ее суть ее работы:
Время разбивается на интервалы, скажем, по 1мс. Такой выдержки хватает для большинства задач. Также у нас должна быть очередь программных таймеров, размещенных в ОЗУ. На каждый таймер отводится три байта:
Первый — идентификатор задачи. Два других — выдержка в миллисекундах.

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

Один из свободных аппаратных таймеров программируем на то, чтобы он генерировал прерывание каждые 0.001с

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

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

Read More »

AVR. Учебный курс. Операционная система. Диспетчер задач.

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

Общая диаграмма работы ОС

Что из себя представляет задача
Это практически то же самое, что и процедура, вызываемая командой RCALL с тремя отличиями:

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

Сама задача представляет собой обычную процедуру, записанную без каких либо замудреностей.
(далее…)

Read More »

AVR. Учебный курс. Операционная система. Введение.

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

В результате было написано микроядро. Камрад Serg2x2 подглядел концепцию в прошивке сотового телефона Motorola и портировал на микроконтроллер АТ89С2051, после ее перенесли на AVR, а я привел все в библиотечный и структурированный вид, обвязал все удобными макросами, а также подробно описал и задокументировал. Так что теперь интеграция ядра операционки в проект под микроконтроллер AVR занимает буквально пару минут работы Copy-Paste.

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

Параметры и системные требования микроядра:

  • Занимаемый обьем в Flash — 500 байт, при желании можно ужать до 400 байт, выкинув ненужные функции.
  • Рекомендуемый объем RAM — не менее 20 байт+стек, впрочем, можно еще ужать если нагрузка небольшая.
  • Крайне желательная поддержка команд STS и LDS, можно и без них, но неудобно. Впрочем, макросы решают.

(далее…)

Read More »