Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: scmRTOS для начинающего.
Форум разработчиков электроники ELECTRONIX.ru > Cистемный уровень проектирования > Операционные системы > scmRTOS
Beginning
Я конечно вижу (и понимаю) что подобных тем создано много, ну хотя бы несколько. Но почитав их, увидел, что они мне не подходят, точнее не дают ответы на необходимые мне вопросы. А вопросов будет походу много. Поэтому решил создать новую тему в немного другом стиле – т.е. с моими промежуточными выводами, дабы на примере эволюции чайника помочь новым юзерам.
Ось решил прикрутить к уже готовому проекту – железо(на ATmega32) + дрова периферии (написано на С). Что бы грамотно прикрутить ось нужно понять как она работает. В общем как работает ось, я в принципе представляю, по крайней мере, в общих чертах. Как то даже написал свой вытесняющий планировщик для ARM (Работает до сих пор smile.gif. Ну да ладно. Почитал доку по оси v.2 почитал форумы. В голове образовалась определённый набор знаний в виде каши, ну или первичного супа, типа из которого на планете жизнь зарождалась smile.gif Понял, что пора внедрять знания. Вставил иссодники оси в мой проект. Расставил их соответственно папковой иерархии проекта. Долго бился над путями. Потом плюнул и прикрутил исходники к проекту 1-EventFlag. Ну вроде нормально. Компилиться без ошибок.
Теперь пару тезисов которые я для себя определил, как некие точки от которых можно оттолкнуться.
1.Поток (кусок кода в while(1){}) будет выполняться пока
a) надо вызвать паузу (за это отвечает функция, как я понял, Sleep)
б)не произойдёт прерывание переферии, внутри которого произойдёт переключения контекста. Для этого мы в обработчики прерывания вставляем объект класса TISR_Wrapper. Вставляем этот объект, например, в прерывание UART recive.
в) Прерывание системного таймера. Таймер настроенный на срабатывание через определённый промежуток времени для проверки надо ли переключать контекст.
2. Шедулер или планировщик. Кусок кода который контролирует паузы для потоков – т.е. инкрементирует их если надо, смотрит какие флаги взвелись для ожидающих их потоков и т.д. ну и соответственно переключает потоки. Примерно так.

Вроде определились с необходимыми вещами. Приступаем.
Нам нужен системный таймер. На его роль определили Timer0. В общем проинитил его. Включил прерывание на переполнение. Время срабатывания решил поставить 1мс. И для этого в теле прерывания надо таймер инитить TCNT0 = 0x8D, это при кварце 7.3728. Порыл код и догадалcя что вроде для этого есть функция void OS::SystemTimerUserHook(), короче туда и вставил. Правильно сделал?
C таймером разобрались. Теперь эпопея номер два – програмное прерывание. Почитал за него. Блин, сложно въехать в сакраментальный смысл его использования, ну это и понятно надо сначала поюзать ось и столкнувшись с проблемами всё станет на свои полочки. Но как я уже говорил железо уже определено и костыль в виде соединения пинов мне не подходит. Отсюда мой первый вопрос. Как выпутаться из этой проблемы? Конечно, хотеться всё же использовать именно прерывание, т.к. использования прямого вызова шедулера очень не кашерно, как я прочитал в документации. Желательно не просто сказать – бери гэто, а привести пример кода, именно в контексте оси.
Спасибо.
Сергей Борщ
Цитата(Beginning @ Apr 28 2010, 18:13) *
1.Поток (кусок кода в while(1){}) будет выполняться пока
a) надо вызвать паузу (за это отвечает функция, как я понял, Sleep)
а) не захочет сам отдать управление. Например, на определенное время функцией Sleep() или обратившись к функции одного из сервисов, для которой не готовы данные. Как то: TEventFlag::Wait() если флаг не взведен, TMutex::Lock() если данный мутекс уже залочен другим потоком, channel::pop() если в канале нет данных, channel::push() если в канале нет свободного места ну и т.д.
Цитата(Beginning @ Apr 28 2010, 18:13) *
б)не произойдёт прерывание переферии, внутри которого произойдёт переключения контекста. Для этого мы в обработчики прерывания вставляем объект класса TISR_Wrapper. Вставляем этот объект, например, в прерывание UART recive.
внутри которого будет вызвана одна из функций сервисов, в результате выполнения которой готовым к выполнению окажется процесс с приоритетом выше текущего.
Цитата(Beginning @ Apr 28 2010, 18:13) *
в) Прерывание системного таймера. Таймер настроенный на срабатывание через определённый промежуток времени для проверки надо ли переключать контекст.
Таймер просто считает таймауты для процессов, усыпленных с таймаутом. Таймаут истек - процесс готов к выполнению. Далее работает тот же механизм, что и при обычных "осевых" прерываниях - если "пробудился" поток с приоритетом большим текущего - вызывается планировщик.
Цитата(Beginning @ Apr 28 2010, 18:13) *
2. Шедулер или планировщик. Кусок кода который контролирует паузы для потоков – т.е. инкрементирует их если надо, смотрит какие флаги взвелись для ожидающих их потоков и т.д. ну и соответственно переключает потоки.
Нет. планировщик только передает управление потоку с наивысшим приоритетом из готовых к выполнению.

Цитата(Beginning @ Apr 28 2010, 18:13) *
Время срабатывания решил поставить 1мс. И для этого в теле прерывания надо таймер инитить TCNT0 = 0x8D, это при кварце 7.3728.
Вы не учитываете, что "за время пути собака могла подрасти". Пока исполнение дойдет до точки, где вы перезаписываете счетчик, таймер может сделать несколько тиков. Поэтому надо делать TCNT0 -= 0x100-0x8D.

Цитата(Beginning @ Apr 28 2010, 18:13) *
Порыл код и догадалcя что вроде для этого есть функция void OS::SystemTimerUserHook(), короче туда и вставил. Правильно сделал?
Да.
Цитата(Beginning @ Apr 28 2010, 18:13) *
Теперь эпопея номер два – програмное прерывание. Почитал за него. Блин, сложно въехать в сакраментальный смысл его использования,
Вам надо переключить контекст. Процедура восстановления контекста считывает со стека процесса данные и в определнном порядке записывает их в регистры. Значит, в том же порядке содержимое регистров должно быть записано на стек процедурой сохранения контекста. Поскольку перепланировка может потребоваться в каком-либо из прерываний, а компилятор в обработчике этого прерывания уже сложил на стек какое-то количество регистров получится, что часть регистров сохранена на стеке дважды. Чтобы этого избежать, используется дополнительное прерывание, в ассемблерном обработчике которого регистры складываются на стек "с чистого листа".
Цитата(Beginning @ Apr 28 2010, 18:13) *
костыль в виде соединения пинов мне не подходит. Отсюда мой первый вопрос. Как выпутаться из этой проблемы?
Используйте прерывание SPM. Пример есть в портах под avr-gcc.
Вы не указали свой компилятор smile.gif
Beginning
Спасибо, не ожидал столь тёплого и развёрнутого ответа, особенно после того как модератор сделал приписку к названию темы.
Цитата(Сергкй Борщ)
Вы не учитываете, что "за время пути собака могла подрасти". Пока исполнение дойдет до точки, где вы перезаписываете счетчик, таймер может сделать несколько тиков. Поэтому надо делать TCNT0 -= 0x100-0x8D.

Не совсем понял, что вы имели ввиду, но попытаюсь ответить, так как я понял. Собака(watchdog)… а при чём здесь собака? Я обычно собаку ставлю на 2 секунды, и сбрасываю в прерывании от таймера которое делаю на 0,1с. Здесь тоже собаку буду в SystemTimerUserHook сбрасывать.Пусть таймер делает сколько угодно тиков, вроде как главное что перед выходом я ставлю TCNT0=0x8D?
В чём смысл выражения TCNT0 -= 0x100-0x8D? Компилятор его переделает же в TCNT0 -= 0x73.
Про прерывание в общем понятно.
Цитата(Сергей Борщ)
Используйте прерывание SPM. Пример есть в портах под avr-gcc.

Посмотрел все три проекта(v3.1) и везде используеться TIMER1_COMPA, или не там смотрел? Тогда где?
Кстати использую IAR.
Сергей Борщ
Цитата(Beginning @ Apr 28 2010, 22:50) *
Не совсем понял, что вы имели ввиду, но попытаюсь ответить, так как я понял. Собака(watchdog)… а при чём здесь собака?
Простите. Это была строка из детского стихотворения Маршака. Я как-то не подумал, что кто-то в детстве мог его не слышать. Watchdog тут совершенно не при чем laughing.gif .
Цитата(Beginning @ Apr 28 2010, 22:50) *
Пусть таймер делает сколько угодно тиков, вроде как главное что перед выходом я ставлю TCNT0=0x8D?
В чём смысл выражения TCNT0 -= 0x100-0x8D?
Вам надо, чтобы между прерываниями прошло 0x73 тиков. Допустим, действуем по-вашему. Происходит прерывание, за время до перезаписи, допустим, проходит еще 2 тика. Вы записываете 0x8D. С этого момента таймер отсчитывает 0x100-0x8D = 0x73 тиков и вызывается новое прерывание. Но еще два тика было да перезаписи, итого между прерываниями прошло 0x75 тиков. Теперь заменяем TCNT0 = 0x8D на TCNT0 -= 0x100-0x8D. На момент этой строки в TCNT находится 2, выполняяя вычитание получаем в нем 8F. Итого таймеру до прерывания осталось 0x100 - 0x8F = 0x71 тик и еще два было до вычитания. Итого между прерываниями прошло 0x73 тика, как и требовалось. А если было какое-то еще прерывание, и прерывание таймера было отложено - там может натикать не 2 а гораздо больше. При вычитании эта ошибка не будет накапливаться.

Цитата(Beginning @ Apr 28 2010, 22:50) *
Компилятор его переделает же в TCNT0 -= 0x73.
Принцип IBM: "Машина должна работать, а человек - думать". Если бы я написал TCNT0 -= 0x73, то возник бы вопрос - откуда взялось это число. А так сразу видно - это столько тиков, сколько осталось до переполнения таймера, чтобы получить ваше число 0x8D. На выходной код не влияет, но делает программу более читабельной. Вообще писать в исходнике подобные "магические числа" - дурной тон. Гораздо понятнее писать примерно так:
Код
#define OS_TICK_RATE    1000 // Hz
#define SYSTIMER_FREQ      (F_CPU / 4)

...
TCNT0 -= SYSTIMER_FREQ / OS_TICK_RATE;
Компилятор посчитает, ему не трудно - это его работа. Эти же именованные константы можно использовать и в других местах исходника, и при создании на базе этого проекта другого, с другим кварцем, вам достаточно будет изменить всего одну строку и не перелопачивать весь исходник в поисках "магических чисел".

Цитата(Beginning @ Apr 28 2010, 22:50) *
Посмотрел все три проекта(v3.1) и везде используеться TIMER1_COMPA, или не там смотрел? Тогда где?
Кстати использую IAR.
Посмотрите порт под avr-gcc
Beginning
Спасибо за разъяснение про таймер. Ход ваших мыслей мне стал понятен. Кстати как вариант точного тика это первой инструкцией прерывания сделать TCNT0=0x8D.
Блин, не могу найти инициализацию программного прерывания в исходниках. Вы можете ткнуть пальцем?
Интересуют 2 вещи:
Сама инициализация прерывания. По идее должна быть где то в старте.
Обработчик прерывания с потрохами.
Спасибо.
Сергей Борщ
Цитата(Beginning @ Apr 29 2010, 14:23) *
Кстати как вариант точного тика это первой инструкцией прерывания сделать TCNT0=0x8D.
Не поможет - сам вход в обработчик может быть задержан обработкой более высокоприоритетного прерывания.
Цитата(Beginning @ Apr 29 2010, 14:23) *
Блин, не могу найти инициализацию программного прерывания в исходниках. Вы можете ткнуть пальцем?
Архив по ссылке из предыдыущего сообщения. Пример 3-Channel. Глянул в репозитории - в примерах под IAR во всех трех примерах используется прерывание SPM. Вот прямо взял файл scmRTOS.3.10.avr.iar.rar и в нем все три примера с прерыванием SPM.
Цитата(Beginning @ Apr 29 2010, 14:23) *
Сама инициализация прерывания. По идее должна быть где то в старте.
Обработчик прерывания с потрохами.
Все необходимые действия хранятся в файле scmRTOS_TARGET_CFG.h Это описание имени вектора, функции RaiseContextSwitch() и BlockContextSwitch(), класс-обертка прерывания. Никакой специальной инициализации для этого (SPM) прерывания не нужно. Обработчик прерывания живет в исходниках ОС.
Beginning
Огромное спасибо! a14.gif Буду разбираться.
Сергей Борщ
Цитата(Beginning @ Apr 29 2010, 17:28) *
Набросал проект. Переключение процессов естественно нет biggrin.gif Что я упустил?
Его нет в протеусе или в железе? Если в протеусе и примеры из архива ОСи в нем тоже не работают - выкиньте протеус.
Beginning
Вот заготовка протеуса, контроллер и светодиод. По идеи светодиод должен моргать.
Сергей Борщ
Цитата(Beginning @ Apr 30 2010, 09:00) *
Вот заготовка протеуса, контроллер и светодиод. По идеи светодиод должен моргать.
Еще раз спрашиваю: Вы проверяли на реальном железе? У меня нет протеуса и я не собираюсь разбираться, почему в нем работает не так, как в реальном мире. Ибо в реальном железе примеры из архива ОСи работают. Если протеус делает что-то не так, как это делает реальное железо - это его беда и уж явно не вина ОС.
Beginning
Я не хотел вас как то задеть, даже и в мыслях не было. Разумеется, ось работает, её же все используют и ни каких проблем. Это у меня какие то траблы в коде. Я новичок и поэтому прошу помощи у форума. А насчёт PROTEUS, так я думал он у всех есть, поэтому и кинул проект. Ну нет так нет.
В реальном железе не отлаживал, т.к. из средств отладки в реальном железе есть только uart_puts. А в протеусе удобно – можно прогу остановить и посмотреть все внутренности. Ладно буду ковырять дальше.
Спасибо.
Beginning
Интересный эффект – когда убираешь оптимизацию в IAR вылетают море предупреждений типа:
Too low level of optimization to inline function "OS::TKernel::RegisterProcess" и т.д.
И при этом программа постоянно сбрасывается, т.е. стартует работает, потом опять стартует. Это в протеусе.
P.S. Насчёт протеуса, пока что не замечал от него явных глюков. Во всех случаях, когда я был уверен, что глючит протеус, оказывалось что глючу я smile.gif
P.S.S. Но это не означает что глюков нет smile.gif Просто я их пока не встречал. Или не помню smile.gif
Сергей Борщ
Цитата(Beginning @ Apr 30 2010, 09:16) *
В реальном железе не отлаживал, т.к. из средств отладки в реальном железе есть только uart_puts.
Еще можно ногами помахать. Этого достататочно в очень большом количестве случаев.
Цитата(Beginning @ Apr 30 2010, 10:47) *
Интересный эффект – когда убираешь оптимизацию в IAR вылетают море предупреждений типа:
Too low level of optimization to inline function "OS::TKernel::RegisterProcess" и т.д.
Все правильно. А зачем убирать оптимизацию? Это ведь все равно, что искать ошибки в совершенно другой программе.
Цитата(Beginning @ Apr 30 2010, 10:47) *
И при этом программа постоянно сбрасывается, т.е. стартует работает, потом опять стартует. Это в протеусе.
Разберетесь - почему, глубже поймете, как именно работает ОС. Эти знания помогут в дальнейшем ее использовать. Только отлаживайтесь с оптимизацией. Без оптимизации возможны чудеса именно из-за невстраивания функций.
Цитата(Beginning @ Apr 30 2010, 10:47) *
P.S. Насчёт протеуса, пока что не замечал от него явных глюков. Во всех случаях, когда я был уверен, что глючит протеус, оказывалось что глючу я smile.gif
Ой, на этом форуме достаточно сообщений "устройство не работает, виновато железо - программа рабочая, в протеусе проверял". Потом оказывается наоборот.
Beginning
Цитата
Ой, на этом форуме достаточно сообщений "устройство не работает, виновато железо - программа рабочая, в протеусе проверял". Потом оказывается наоборот.

Кстати вспомнил один глюк в модели DS18B20. Ну да, глюки есть, но скажем они не мешают отладке. Не смертельные они. Главное чётко представлять что протеус может, а что нет. Да бог с ним с протеусом.
А оптимизацию я выключал мол для улучшения дебагинга. А получилось наоборот. sad.gif
А где вы включаете глобальные прерывания? Что то поискал sei не нашёл.
IgorKossak
Цитата(Beginning @ Apr 30 2010, 12:58) *
А где вы включаете глобальные прерывания? Что то поискал sei не нашёл.

Включаются автоматически при первом восстановлении контекста. Смотрите OS::Run() и всё что она вызывает.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.