1 Мая, 2011

КАК НАПИСАТЬ СОБСТВЕННУЮ МИКРООПЕРАЦИОННУЮ СИСТЕМУ

Филипп Гриценко
При словах операционная система мы тут же себе представляем себе что-то громадное и вообще чудовищно большое,вроде Windows который занимает сотни мегабайт,требующие кучу оперативки только блин для того чтобы запуститься. Кто более продвинут (имею в виду программистов моего уровня знаний) вспомнит *nix-семейство,и всякие там QNX и ucLunix. Но и это жирная вещь,которой нужно не меньше мегабайта памяти и как минимум 32-разрядный процессор. А что у нас,а у нас хардкор,бля!!!!!! Для пущей жести возьмем не какую-нибудь ATMega 128,но ATTiny2313 с 2 Кб памяти. Если учесть что надо выделить место под прикладную часть-тратить на операционную систему более 700 байт мне,честно говоря совершенно западло. Слабо накатать операционку на 600 байт? А вот сейчас и посмотрим. Прежде чем идти на штурм AVR Studio.надо все же подумать,а нахрен нам операционная система??? Какие задачи мы хотим на нее повесить??? Первое,что требуется от операционной системы,это облегчить труд программиста. Сделать так чтобы можно было создать единую шаблонную прошивку в которую без проблем дописывается без проблем что угодно,к примеру мой АНТИХРИСТ,но о нем отдельно... То-есть операционная система должна выполнять задачи,под задачами лично я понимаю любые действияс контроллером и периферией. Например мигнуть светодиодом,считать байт из внешней памяти,обновить изображение на экране (кстати,обновление продлевает жизнь видеокарте!),послать байт в последовательный порт. Короче любое действие выполняемое микроконтроллером. Задачи должны подключатся легко и просто,чтобы нам с вами не приходилось думать как бы одновременно и дисплей обновить и диодом мигать,принимать данные по порту,не забывая слать диагностические сообщения. Также ОС (Операционная Система) должна брать на себя некоторые сервисные функций,скажем организовать службу таймеров,чтобы прикладные задачи не парились по поводу распределений временных интервалов. Классическая операционная система реального времени работает примерно следующим образом-на каждую задачу отводится свой кусочек оперативной памяти,свой стек,свое адресное пространство. А диспетчер задач ОС постоянно тасует их между собой,по очереди запуская не определенное время то одну то другую задачу. Период работы задачи называется квантом времени,такое можно реализовать на AVR,но на это удет очень много оперативки,которой и так кот...наплакал. Да и в отличий от х86-архитектуры тут нет никаких аппаратных средств,вроде защищенного режима. Поэтому мы пойдем другим путем... Собственно полная ось реального времени нам особо не нужна,достаточно чтобы события выполнялись в срок. Поэтому мы организуем диспетчер и очередь событий. Каждое задание будет выполнятся по этому списку,ставя туда либо вновь себя либо уже другую задачу. Все внешние вопрсы,которые требуют отличной реакций к примеру прием байтов по UART или какой-нибудь INT0 будут по прежнему осуществлятся прерываниями. С той разницей что теперь обработчик может или сам выполнять задачу,если это очень срочно или добавить ее в очередь,и она будет исполнена,когда до нее соотвественно дойдет очередь. Одной очереди мало,надо бы еще службу драйверов добавить. Таймер-это бля чертовский ценный ресурс!!!!!!!! Отмерять время нужно всегда и под разные задачи. Таймеров в контроллере мало,обычно все три,поэтому надо придумать средство чтобы один таймер мог отмерять множество разных интервалов. Сделать это проще всего той же очередью! Хотим мы с вами чтобы через секунду случилось событие какой-нибудь ну скажем опять чтобы засветился светодиод. Не вопрос-кидаем в таймерную очередь индетификатор события время которого нам надо отчитать и вперед!!! Таймер будет выбрасывать на скажем каждый 10.000 импульс тактового генератора свое прерывание,в нем мы по быстрому инкрементируем счетчик конкретного задания,а если он стал равен нулю,то это задание мы отправляем в очередь для дальнейшего исполнения. Потом переходим к второй записи в цепочке таймеров и проверяем ее. Итак,до ьех пор пока не обслужим все временные интервалы. Просто и эффективно,послушайте меня лучше,самоучку,я знаю что пишу!!! Каждое аппаротное действие обычно сопровождается генерацией прерываний. Пришел байт в порт-прерывание. АЦП обработал аналоговый сигнал-прерывание. Ушел байт-прерывание. Буквально на каждый чих,если говорить по простому!!! Можно всю программу развесить на обработчики,оставив в главном цикле пустой код. Но есть одна проблема-приоритеты прерываний. На AVR нет аппаратного контроллера,так что когда приходит прерывание,то аппаратно запрещаются другие. И правильно-иначе может произойте срыв стека. Однако!!! Запретив вообще все,мы рискуем проморгать важное событие. Приведу очередной пример-есть у нас АЦП и еще мы ждем байты по UART,запустили обработку АЦП,сигнал оцифровался,и преобразователь сгенеривал прерывание,мол готово забирай! Обработчик АЦП кинулся переживывать важные данные,да вот засада-в этот момент пришла посылка в UART,а прерывание запрещены! В итоге посылку мы посылку прозеваем и вовремя не успеем среагировать. Как быть? Выход вижу только один-делать обработку прерываний как можно короче. В идеале-две три команды. Тогда проблема решается следующим образом. АЦП сработал и дал приказ закинуть в очередь на исполнение задачу по обработке байта. UART принял байт и сразу закинул в очередь свое задание на исполнение. Так и другие прерывание,не делают нечего сами,а пихают все в копилку! А вот главный цикл программы,который крутится постоянно и имеет низший приоретет. Спокойно и методично разберет эту очередь и выполнит все не в порядке поступление,а в порядке важности. То-есть UART-немедленно,а АЦП может и подождать. Короче приступим,лучший способ научится программировать-взять дебаггер и посмотреть как это делают другие. Идея микро ОС была нагло слизанна с реализаций прошивок в сотовых телефонах Motorola,и в начале портировал ее на микроконтроллер архитектуры С51,а потом и на AVR перетащил. Очистил от всей шелухи и сейчас подробно распишу тебе анатомию работы ядра ОС. Во главу угла тут ставится манипуляция программным счетчиком на базе таблицы переходов. Счетчик есть обсолютно в любом процессоре. Программа находится в памяти,и каждая ассемблерная инструкция расположена по какоиу-либо адресу. Значение программного счетчика указывает на адрес (главное у нас у самоучек,это логика!),куда на следующем такте перейдет процессор,есть команды типа RJMP или BRхх,которые закидают в РС адрес перехода. Так осуществляется ветвление. Наши подпрограммы-задачи просто представляют собой участки кода. Нам нужно лишь передать на них управление. Сделать это проше всего с помощью таблицы переходов. Где-нибудь в памяти мы создаем массив в котором у нас хранятся адреса задач,по порядку. Каждой подпрограмме присвается номер в таблице. И теперь зная номер нужной задачи,очень легко вычислить адрес перехода. Делается это обычным смещением от начала таблицы. Так как длина адреса всегда равна двум байтам,то достаточно умножить номер на два и прибавить к начальному адресу. Для формирования этой таблицы мы в файле defconst.inc создаем таблицу имен. Она нужна нам чисто для удобства,чтобы оперировать именами,а не цифрами... Выглядит она именно так:
.egu EV_Idle =0 ; Простой - NOP
.egu EV_KbdDataReceived=1
.egu EV_UnlockKeys =2
.egu EV_DisplRegen =3 ; Регенирация дисплея
.egu EV_SendPacket =4
.egu EV_Timeout 1 =5
.egu EV_Timeout 2 =6 ; Test (регенирация дисплея)
.egu EV_Timeout 3 =7 ; Test (моргание светодиодом)
.egu EV_Show =8
.egu EV_TxComplete =9
Названия совершенно произвольные и взяты из программы кейлоггера,как есть. Ты можеш их назвать как тебе угодно,главное сам не забудь где что,потом ко мне претензий некаких!!! Тут мы задаем лишь имена функций и номер на который функция будет отклткатся. А затем в файле с основной программой LoggerAttiny2313.asm в самом низу,в памяти программ создадим таблицу переходов. Там сами разберетесь. Вот мой собственный алгоритм вычисления адреса:
0014 rcall LeadOff;
0015 R19
0016 nop
0017 ret-------------------------//00FА6 +0 OFA6*2+0
хххххххххххххххххххххххххххххххххххххххххххххххххх
=12
=2+2 (адреса в памяти программ нумеруются в словах по 2 байта). 129 =+06А Z=0В14 (Z-это индексный регистр через него идут все обращения к памяти Также он может адресовать индексный переход)
А72201201--<14=114хххххххххххххххххххх=120=0х14 В 0х00 ВВ= А
Думаю вы уже поняли что легко,просто надо хорошо пораскинуть мозгами и все!!!
или введите имя

CAPTCHA