Перейти к содержанию
    

scmRTOS для начинающего.

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

Ось решил прикрутить к уже готовому проекту – железо(на ATmega32) + дрова периферии (написано на С). Что бы грамотно прикрутить ось нужно понять как она работает. В общем как работает ось, я в принципе представляю, по крайней мере, в общих чертах. Как то даже написал свой вытесняющий планировщик для ARM (Работает до сих пор :). Ну да ладно. Почитал доку по оси v.2 почитал форумы. В голове образовалась определённый набор знаний в виде каши, ну или первичного супа, типа из которого на планете жизнь зарождалась :) Понял, что пора внедрять знания. Вставил иссодники оси в мой проект. Расставил их соответственно папковой иерархии проекта. Долго бился над путями. Потом плюнул и прикрутил исходники к проекту 1-EventFlag. Ну вроде нормально. Компилиться без ошибок.

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

1.Поток (кусок кода в while(1){}) будет выполняться пока

a) надо вызвать паузу (за это отвечает функция, как я понял, Sleep)

б)не произойдёт прерывание переферии, внутри которого произойдёт переключения контекста. Для этого мы в обработчики прерывания вставляем объект класса TISR_Wrapper. Вставляем этот объект, например, в прерывание UART recive.

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

2. Шедулер или планировщик. Кусок кода который контролирует паузы для потоков – т.е. инкрементирует их если надо, смотрит какие флаги взвелись для ожидающих их потоков и т.д. ну и соответственно переключает потоки. Примерно так.

 

Вроде определились с необходимыми вещами. Приступаем.

Нам нужен системный таймер. На его роль определили Timer0. В общем проинитил его. Включил прерывание на переполнение. Время срабатывания решил поставить 1мс. И для этого в теле прерывания надо таймер инитить TCNT0 = 0x8D, это при кварце 7.3728. Порыл код и догадалcя что вроде для этого есть функция void OS::SystemTimerUserHook(), короче туда и вставил. Правильно сделал?

C таймером разобрались. Теперь эпопея номер два – програмное прерывание. Почитал за него. Блин, сложно въехать в сакраментальный смысл его использования, ну это и понятно надо сначала поюзать ось и столкнувшись с проблемами всё станет на свои полочки. Но как я уже говорил железо уже определено и костыль в виде соединения пинов мне не подходит. Отсюда мой первый вопрос. Как выпутаться из этой проблемы? Конечно, хотеться всё же использовать именно прерывание, т.к. использования прямого вызова шедулера очень не кашерно, как я прочитал в документации. Желательно не просто сказать – бери гэто, а привести пример кода, именно в контексте оси.

Спасибо.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1.Поток (кусок кода в while(1){}) будет выполняться пока

a) надо вызвать паузу (за это отвечает функция, как я понял, Sleep)

а) не захочет сам отдать управление. Например, на определенное время функцией Sleep() или обратившись к функции одного из сервисов, для которой не готовы данные. Как то: TEventFlag::Wait() если флаг не взведен, TMutex::Lock() если данный мутекс уже залочен другим потоком, channel::pop() если в канале нет данных, channel::push() если в канале нет свободного места ну и т.д.

б)не произойдёт прерывание переферии, внутри которого произойдёт переключения контекста. Для этого мы в обработчики прерывания вставляем объект класса TISR_Wrapper. Вставляем этот объект, например, в прерывание UART recive.
внутри которого будет вызвана одна из функций сервисов, в результате выполнения которой готовым к выполнению окажется процесс с приоритетом выше текущего.

в) Прерывание системного таймера. Таймер настроенный на срабатывание через определённый промежуток времени для проверки надо ли переключать контекст.
Таймер просто считает таймауты для процессов, усыпленных с таймаутом. Таймаут истек - процесс готов к выполнению. Далее работает тот же механизм, что и при обычных "осевых" прерываниях - если "пробудился" поток с приоритетом большим текущего - вызывается планировщик.
2. Шедулер или планировщик. Кусок кода который контролирует паузы для потоков – т.е. инкрементирует их если надо, смотрит какие флаги взвелись для ожидающих их потоков и т.д. ну и соответственно переключает потоки.
Нет. планировщик только передает управление потоку с наивысшим приоритетом из готовых к выполнению.

 

Время срабатывания решил поставить 1мс. И для этого в теле прерывания надо таймер инитить TCNT0 = 0x8D, это при кварце 7.3728.
Вы не учитываете, что "за время пути собака могла подрасти". Пока исполнение дойдет до точки, где вы перезаписываете счетчик, таймер может сделать несколько тиков. Поэтому надо делать TCNT0 -= 0x100-0x8D.

 

Порыл код и догадалcя что вроде для этого есть функция void OS::SystemTimerUserHook(), короче туда и вставил. Правильно сделал?
Да.

Теперь эпопея номер два – програмное прерывание. Почитал за него. Блин, сложно въехать в сакраментальный смысл его использования,
Вам надо переключить контекст. Процедура восстановления контекста считывает со стека процесса данные и в определнном порядке записывает их в регистры. Значит, в том же порядке содержимое регистров должно быть записано на стек процедурой сохранения контекста. Поскольку перепланировка может потребоваться в каком-либо из прерываний, а компилятор в обработчике этого прерывания уже сложил на стек какое-то количество регистров получится, что часть регистров сохранена на стеке дважды. Чтобы этого избежать, используется дополнительное прерывание, в ассемблерном обработчике которого регистры складываются на стек "с чистого листа".

костыль в виде соединения пинов мне не подходит. Отсюда мой первый вопрос. Как выпутаться из этой проблемы?
Используйте прерывание SPM. Пример есть в портах под avr-gcc.

Вы не указали свой компилятор :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо, не ожидал столь тёплого и развёрнутого ответа, особенно после того как модератор сделал приписку к названию темы.

Вы не учитываете, что "за время пути собака могла подрасти". Пока исполнение дойдет до точки, где вы перезаписываете счетчик, таймер может сделать несколько тиков. Поэтому надо делать TCNT0 -= 0x100-0x8D.

Не совсем понял, что вы имели ввиду, но попытаюсь ответить, так как я понял. Собака(watchdog)… а при чём здесь собака? Я обычно собаку ставлю на 2 секунды, и сбрасываю в прерывании от таймера которое делаю на 0,1с. Здесь тоже собаку буду в SystemTimerUserHook сбрасывать.Пусть таймер делает сколько угодно тиков, вроде как главное что перед выходом я ставлю TCNT0=0x8D?

В чём смысл выражения TCNT0 -= 0x100-0x8D? Компилятор его переделает же в TCNT0 -= 0x73.

Про прерывание в общем понятно.

Используйте прерывание SPM. Пример есть в портах под avr-gcc.

Посмотрел все три проекта(v3.1) и везде используеться TIMER1_COMPA, или не там смотрел? Тогда где?

Кстати использую IAR.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Не совсем понял, что вы имели ввиду, но попытаюсь ответить, так как я понял. Собака(watchdog)… а при чём здесь собака?
Простите. Это была строка из детского стихотворения Маршака. Я как-то не подумал, что кто-то в детстве мог его не слышать. Watchdog тут совершенно не при чем :laughing: .

Пусть таймер делает сколько угодно тиков, вроде как главное что перед выходом я ставлю 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 а гораздо больше. При вычитании эта ошибка не будет накапливаться.

 

Компилятор его переделает же в TCNT0 -= 0x73.
Принцип IBM: "Машина должна работать, а человек - думать". Если бы я написал TCNT0 -= 0x73, то возник бы вопрос - откуда взялось это число. А так сразу видно - это столько тиков, сколько осталось до переполнения таймера, чтобы получить ваше число 0x8D. На выходной код не влияет, но делает программу более читабельной. Вообще писать в исходнике подобные "магические числа" - дурной тон. Гораздо понятнее писать примерно так:

#define OS_TICK_RATE    1000 // Hz
#define SYSTIMER_FREQ      (F_CPU / 4)

...
TCNT0 -= SYSTIMER_FREQ / OS_TICK_RATE;

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

 

Посмотрел все три проекта(v3.1) и везде используеться TIMER1_COMPA, или не там смотрел? Тогда где?

Кстати использую IAR.

Посмотрите порт под avr-gcc

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо за разъяснение про таймер. Ход ваших мыслей мне стал понятен. Кстати как вариант точного тика это первой инструкцией прерывания сделать TCNT0=0x8D.

Блин, не могу найти инициализацию программного прерывания в исходниках. Вы можете ткнуть пальцем?

Интересуют 2 вещи:

Сама инициализация прерывания. По идее должна быть где то в старте.

Обработчик прерывания с потрохами.

Спасибо.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Кстати как вариант точного тика это первой инструкцией прерывания сделать TCNT0=0x8D.
Не поможет - сам вход в обработчик может быть задержан обработкой более высокоприоритетного прерывания.
Блин, не могу найти инициализацию программного прерывания в исходниках. Вы можете ткнуть пальцем?
Архив по ссылке из предыдыущего сообщения. Пример 3-Channel. Глянул в репозитории - в примерах под IAR во всех трех примерах используется прерывание SPM. Вот прямо взял файл scmRTOS.3.10.avr.iar.rar и в нем все три примера с прерыванием SPM.

Сама инициализация прерывания. По идее должна быть где то в старте.

Обработчик прерывания с потрохами.

Все необходимые действия хранятся в файле scmRTOS_TARGET_CFG.h Это описание имени вектора, функции RaiseContextSwitch() и BlockContextSwitch(), класс-обертка прерывания. Никакой специальной инициализации для этого (SPM) прерывания не нужно. Обработчик прерывания живет в исходниках ОС.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Набросал проект. Переключение процессов естественно нет :biggrin: Что я упустил?
Его нет в протеусе или в железе? Если в протеусе и примеры из архива ОСи в нем тоже не работают - выкиньте протеус.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот заготовка протеуса, контроллер и светодиод. По идеи светодиод должен моргать.

scmRTOS.rar

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот заготовка протеуса, контроллер и светодиод. По идеи светодиод должен моргать.
Еще раз спрашиваю: Вы проверяли на реальном железе? У меня нет протеуса и я не собираюсь разбираться, почему в нем работает не так, как в реальном мире. Ибо в реальном железе примеры из архива ОСи работают. Если протеус делает что-то не так, как это делает реальное железо - это его беда и уж явно не вина ОС.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я не хотел вас как то задеть, даже и в мыслях не было. Разумеется, ось работает, её же все используют и ни каких проблем. Это у меня какие то траблы в коде. Я новичок и поэтому прошу помощи у форума. А насчёт PROTEUS, так я думал он у всех есть, поэтому и кинул проект. Ну нет так нет.

В реальном железе не отлаживал, т.к. из средств отладки в реальном железе есть только uart_puts. А в протеусе удобно – можно прогу остановить и посмотреть все внутренности. Ладно буду ковырять дальше.

Спасибо.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Интересный эффект – когда убираешь оптимизацию в IAR вылетают море предупреждений типа:

Too low level of optimization to inline function "OS::TKernel::RegisterProcess" и т.д.

И при этом программа постоянно сбрасывается, т.е. стартует работает, потом опять стартует. Это в протеусе.

P.S. Насчёт протеуса, пока что не замечал от него явных глюков. Во всех случаях, когда я был уверен, что глючит протеус, оказывалось что глючу я :)

P.S.S. Но это не означает что глюков нет :) Просто я их пока не встречал. Или не помню :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В реальном железе не отлаживал, т.к. из средств отладки в реальном железе есть только uart_puts.
Еще можно ногами помахать. Этого достататочно в очень большом количестве случаев.

Интересный эффект – когда убираешь оптимизацию в IAR вылетают море предупреждений типа:

Too low level of optimization to inline function "OS::TKernel::RegisterProcess" и т.д.

Все правильно. А зачем убирать оптимизацию? Это ведь все равно, что искать ошибки в совершенно другой программе.

И при этом программа постоянно сбрасывается, т.е. стартует работает, потом опять стартует. Это в протеусе.
Разберетесь - почему, глубже поймете, как именно работает ОС. Эти знания помогут в дальнейшем ее использовать. Только отлаживайтесь с оптимизацией. Без оптимизации возможны чудеса именно из-за невстраивания функций.

P.S. Насчёт протеуса, пока что не замечал от него явных глюков. Во всех случаях, когда я был уверен, что глючит протеус, оказывалось что глючу я :)
Ой, на этом форуме достаточно сообщений "устройство не работает, виновато железо - программа рабочая, в протеусе проверял". Потом оказывается наоборот.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Кстати вспомнил один глюк в модели DS18B20. Ну да, глюки есть, но скажем они не мешают отладке. Не смертельные они. Главное чётко представлять что протеус может, а что нет. Да бог с ним с протеусом.

А оптимизацию я выключал мол для улучшения дебагинга. А получилось наоборот. :(

А где вы включаете глобальные прерывания? Что то поискал sei не нашёл.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А где вы включаете глобальные прерывания? Что то поискал sei не нашёл.

Включаются автоматически при первом восстановлении контекста. Смотрите OS::Run() и всё что она вызывает.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...