Как стать автором
Обновить

Диспетчер Задач для Микроконтроллера

Уровень сложностиПростой
Время на прочтение7 мин
Количество просмотров10K
Всего голосов 26: ↑24 и ↓2+22
Комментарии26

Комментарии 26

Было дело, я как то писал простого планировщика под ардуино с похожим принципом работы, только задачам при их создании не задавался интервал времени, а задавался только приоритет. Частота вызова задачи зависит от неё самой, то есть от вызова sleep() или yield() и т.д.. Также можно в процессе работы задачи изменить её приоритет. А планировщик раскидывает очередь выполнения задач в зависимости от их состояния и приоритетов.

https://github.com/MikeKozlovAVR/Arduino_MultiTasker

FreeRTOS вполне работает на PC на базе родных потоков. Я работал над проектами использующими FreeRTOS, где целевым устройством был микроконтроллер на PowerPC, а отладка так же велась на PC.

Я работал над проектами использующими FreeRTOS, где целевым устройством был микроконтроллер на PowerPC

А что это был конкретно за PowerPC микроконтроллер? Случайно не этот

https://www.st.com/en/automotive-microcontrollers/spc58nn84e7.html

SPC58NN84E7RMHBR, ядро e200z4, 200Mhz, 32bit, Flash:6576 MByte, SRAM: 128 KByte, QFP 177pins, 3x cores, Arch: Harvard, D-Cache: 8kByte, big endian?

Что-то из серии MPC57xx. Тогда это еще был Freescale. Чуть позже они же стали NXP.

Всё-таки думается, что для кооперативного планировщика очень нужна функция yield, которую можно внутри задачи вызвать, чтоб вернуть управление планировщику и уступить процессор другой задаче. Без этого не торт :(

Зачем?

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

Сложный и долгий числодробильный алгоритм, который надо прервать, передать управление системе? Так опять же это не задача для yield() .. Суть этой функции - вызвать нечто, когда задача не знает что делать (ожидает ввода-вывода, замера и т.д.) но и отдавать управление не хочет, а простаивать - плохо.. Решается реорганизаций архитектуры: разбиваем задачу на отдельные конечные автоматы (они там есть) и работаем с ними как обычно.

YAGNI.

Подписываюсь под каждым Вашим словом.

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

Пожалуйста, добавьте в опрос " Какую RTOS вы использовали при программировании микроконтроллеров?" вариант "Свою самописную".

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

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

Но теперь на freertos делаю - намного удобнее писать логику не конечными автоматами а в явном виде. Послать запрос, подождать ответа, если чексумма сошлась обработать, повторить. Из минусов freertos разве что тики не быстрее 1мс, но это легко решается - запускаю таймер на 100мкс, в нем выставляю семафор, а в задаче вместо vTaskDelay делаю ожидание семафора.

А вот если опрашивать клавиатуру раз в 200мсек то не нужно отслеживать дребезг. А если функция вызывается через строгое время то этим вполне можно внутри функции пользоваться для отсчета времени ))

Ну и поймаете вместо дребезга (часто меняющегося значения) неверное значение, потому что опрос с периодом 200ms выпал неудачно на время дребезга...

Тогда надо пропускать отчёты измерений с кнопки через цифровой fir фильтр.

0.2с ? Мне казалось я быстрее клавиши нажимаю, т.е. просто будете пропускать нажатия...

Видимо, автор допустил опечатку: вместо 200мсек подразумевал 20 мс. Именно такой интервал обычно выбирают для подавления дребезга контактов.

Проголсовал за пункт "супер-цикл, который прокручивает конечные автоматы + прерывания"

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

PS мой планировщик:

mkLoop :: SystemClock -> [Task] -> Def ('[] :-> ())
mkLoop systemClock tasks = proc "loop" $ body $ do
    let (scheduled, immediately) = partition (isJust . period) tasks
    clocks <- replicateM (length scheduled) (local (ival 0))
    forever $ do
        t <- getSystemTime systemClock
        zipWithM_ (run t) clocks scheduled
        mapM_ runTask immediately
    where
        run t1 clock task = do
            t0 <- deref clock
            let Period interval phase = fromJust $ period task
            when (t1 - t0 >=? interval + phase) $ do
                runTask task
                store clock $ t1 - phase

И пример тасков:

addTask $ delay      15_000       (name <> "_search"             ) $ searchDevices      
addTask $ delayPhase 15_000 6_000 (name <> "_measure_temperature") $ measureTemperature 
addTask $ delayPhase 15_000 6_700 (name <> "_get_temperature"    ) $ getTemperature     

Гениально! Управлять фазой запуска задач.

Это я подсмотрел вот здесь: https://copilot-language.github.io/
Там алгоритмы пишутся как обработка виртуальных бесконечных потоков изменения состояния системы. И таймер реализуется как раз периодом и фазой

Я так понял, пример кода на Rust. Верно?

Это Haskell =)

Я время от времени сравниваю языки программирования.
Если не сложно, то я был бы признателен за заполнение строчки про Haskell 
https://docs.google.com/spreadsheets/d/1GJQqpEBGsIMhReNVLeo6LmvwbcFg2ttXLAXgHXlaqjQ/edit#gid=0

Заполнил!

А почему в таблице у С в графе "параллельность" нолик. Прототреды не считаются?

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории