Comments 74

Несколько оффтопик, но хотелось бы узнать мнение автора: смотрели ли вы в сторону Rust и его подхода (см. ещё rtfm) к программированию микроконтроллеров? Чего по вашему мнению не хватает на данный момент языку в данной области (ну кроме официальной поддержки производителями железа)?

К сожалению о Rust знаю лишь по наслышке. По-этому не могу ничего сказать на этот счет. Думаю есть на хабре люди, которые смогу высказать свое мнение по этому вопросу.
Опять же ИМХО, но для себя выбрал путь «Си для драйверов + Lua для логики». Поскольку в моей практике редко встречаются устройства с «золотой серединой». Это либо совсем уж «преобразователи протоколов» (там нет никакой Lua. Драйвера и пара функций). Либо уж совсем монстры (GUI, логика переходов сравнимая с игровыми ботами...). Но к Rust присматриваюсь чисто в академических целях. Может в будущем займусь всерьез.
Современные разработчики просто не имеют бэкграунда в виде создания кода без РТОС, поэтому для них состояние «отсутствия РТОС» не является альтернативным подходом, и у них нет выбора между этими подходами с оценкой преимуществ и недостатков каждого.
Сомневаюсь, что дело в этом. ОСРВ в МК не дает особых преимуществ над железом (если речь о FreeRTOS). Поскольку она дает лишь базовые вещи. Потоки и их средства синхронизации. Другое дело «ОС со встроенным аппаратным уровнем», каких сейчас тоже не мало. Там да. Есть и uart.tx() встроенные и прочие.
Так эти базовые вещи и решают их базовые проблемы. Они иначе просто не могут ресурсы времени разделять, как через потоки.
Ну если в этом смысле, то возможно. Хотя и не факт. Например бытует мнение о том, что в прерываниях нужно делать «минимум работы». А остатки уже в основной программе. Это не совсем справедливо для «больших» микроконтроллеров. Из-за наличия NVIC (о чем было в статье).
Кто эти разработчики? Вот я вообще не использую rtos, как и любой другой разработчик, пишущий код для устройств критичных к времени реакции, читай real-time. RTOS — это безусловно удобно, но во многих задачах их не применить по объективным причинам.
Наверно вы не современный разработчик… Хотя бы потому, что RTOS как раз и создана для того чтоб писать код устройств критичных по времени реакции — и c ее помощью может быть решен весь класс таких задач. Вопрос лишь в цене такого решения, и моменте, когда эта цена становиться оправданной.

Как только вы начинаете решать с помощью RTOS задачи, для которых не нужно реальное время, что-то начинает идти не так...

Как только задач становится под сотню возникают вопросы, какая из них вдруг не с того ни с сего жрет ресурс — и для выяснения этого любой примитивный шедуллер плавно обрастает костылями и превращается в конечном счете в полноценную RTOS.
Вот только нужны ли в микроконтроллере «под сотню» задач — вопрос отдельный.
Вот на прошлой работе делал: STM32F4, FreeRTOS, LWIP, на нем подняты самописные HTTP, FTP, ModbusTCP, ModbusRTU. При этом все, что по TCP — должно держать до 4х одновременных клиентов.
+ к этому — на этом же контроллере работает основная логика устройства.
Сможете сами посчитать сколько потоков (задач) крутиться одновременно?
Не понимал ModbusTCP и FTP на МК. Для остального:
LWIP — сам по себе отжирает 1 поток в «стандартной поставке».
ModbusRTU — у меня живет в прерываниях + 1 поток для ответов (на каждого клиента, понятное дело)
HTTP — т.к. это по сути TCP, то 1 поток. Суммарно я смогу даже с учетом 4-х клиентов выделить около 10.
Ни че го! Но эта та задача, которая через RTOS решается проще, быстрее и удобнее.
Особенно, если планируется дальнейшая поддержка и модификация проекта.
Может подскажите ОС, которую можно запустить на STM32F4 в связке с LWIP и чтоб по ресурсоемкости и доступности для изучения было как FreeRTOS?
нет, «дешевле» freertos в режиме ОС использовать, чем linux на stm32f4 поднимать

За свою карьеру я научился понимать, что стоимость владения и сопровождения конкретной технологии зависит от очень многих факторов, которые не всегда бросаются в глаза при первоначальном выборе.
Поднять freertos может и легче, чем Linux, но найти разработчиков и техническую поддержку для Linux, я думаю, гораздо легче, чем для freertos… как пример.

Но ведь линуху вроде нужно MMU для полноценной работы, чего у подавляющего большинства контроллеров нет. Что есть вариации которым мму не нужно, не так уж сильно по специфике от того-же фриртоса отличается. А в которых есть — просто по деньгам не пролезут в проект. Так что да — freertos во всех смыслах дешевле линуха.

Да пожалуйста, я только предупредил. И так как вижу у вас LWIP, HTTP, и FTP то могу привести еще один аргумент — Cybersecurity.

Удобно оно пока работает, а если уж такого не получилось, то проще переписать без нее, чем разбираться.

А еще некоторые разработчики думают, что ОСРВ это такие же ОС, как и общего назначения, только упрощенные. Да, и многие реализации так называемых РТОС в качестве планировщика имеют RR, когда в реальности должен быть либо Rate-Monotonic, либо EDF. Не многие знают, что надо производить Wcet анализ и прочие анализы, что система справится в пиковых нагрузках и не будет встреча с deadline-ом. и.т.п.

А еще, чтобы облегчить себе жизнь, есть инструменты вроде
www.state-machine.com/qm
www.uppaal.org
и.т.п.
Vadimatorikda, что же насчёт мьютексов? Тут очень важный аспект.
Нет потоков и ОС — нет mutex-ов.
А если серьезно, то правильно настроив NVIC — можно получить такой код, когда такой проблемы не возникает.
Насколько я знаю, еще есть средства самого языка для атомарного доступа. Но ими я не пользовался, увы. Не было необходимости. Так что буду знать, если знатоки напомнят. std::...;
Иногда приходилось применять «костыль» с блокировкой всех прерываний и последующим возобновлением. Так как это дело пары команд ассемблера, то ничего ИМХО, страшного.
неубедительно :)
— умение работать с периферией ортогонально использованию ОС.
— что за жуткие таймеры занимающие 1кб? для того что бы использовать один аппаратный таймер для отметки 10 интервалов достаточно 10 32-битных переменных или task_sleep. Неумение использовать таймеры никоим образом не относится к недостаткам ОС. А для аппаратных таймеров найдутся другие важные задачи. У меня в одном из проектов аппаратными задачами были заняты 11 таймеров из 13, и как быть по вашему, если нужно 5 интервалов?
— какая религия запрещает использовать конечные автоматы совместно с ОС?
Я не фанат ОС, использую приблизительно в 1 из 3 проектов, и не всегда FreeRtos, где полегче TNKernel и ее форк TNeo использую, но Ваши аргументы абсолютно неубедительны и сводятся к тому, что инструмент в кривых руках работает криво. Так это вроде очевидно и не только к ОС и МК относится.
Скорее смысл статьи сводится к тому, что не стоит использовать OC по умолчанию, а лишь в тех случаях, когда без неё действительно не обойтись.
И да, применяя такой сложный инструмент, как OC, нужно знать её сильные и слабые стороны и понимать цену её применения.
Это к тому, что та же RTOS воспринимается у неискушенных разработчиков типа меня, прежде всего как универсальное средство решения широкого круга задач, что, в свою очередь, побуждает пихать её в большинство домашних проектов, даже не особо задумываясь о целесообразности. Простота этих проектов не дает возможности наступить на грабли, что в свою очередь вызывает ложное ощущение профессонализма.
Замечательно что статьи, подобные этой, позволяют развернуть дискуссию и обратить внимание на интересные мнения, Ваше в том числе, и более критически оценить собственные навыки и стереотипы.

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


А потом оказывается, что...

При использовании FreeRTOS на stm32 прерывания, которые используют системные вызовы, обязательно должны иметь приоритет ниже, чем прерывание, которое вызывает диспетчер. Источник. Конечно, это должен проверять ассерт, жаль, что он по-умолчанию выключен!


Ну или

что в какой-то момент происходило прерывание, которое переполняло стек текущего потока, залезая в стек другого потока, который падал в совсем другой момент.


И это я молчу про "классические" обычные многопоточные баги.
Если же все написано в автоматном стиле, то все как-то сильно проще отлаживается.

По первому — RTFM.
По второму тоже самое, но дам подсказку: www.freertos.org/Stacks-and-stack-overflow-checking.html
Учитесь сначала читать документацию, а потом использовать все доступные средства. А если все делать методом "тыка", то да, баги становятся трудноуловимыми…

Спасибо, я уже в курсе.


По первому — да, конечно, кто ж спорит. Только вот ссылку на это я сейчас полчаса искал — и это я знал, что искать! Документация на FreeRTOS оставляет желать лучшего, прямо скажем.


По второму — как вы, наверное, тоже в курсе, эта защита от переполнения не является идеальной (по вашей ссылке так и написано); полностью спастись можно только с MPU, которое есть далеко не всегда.

Ну и настройка MPU — это далеко не тривиальная задача. Чего стоит только выравнивание.
Ну да, если она переполнит стек положив туда точно такие же значения, какие использует RTOS для проверки — тогда да, не сработает.

Или, например, если переполнение вызвано прерыванием и приведет к hard fault'у до проверки. Вариантов достаточно много.
Разумеется, проверки — вещь не бесполезная, просто нужно осознавать, что это не панацея.

Ну а так же, что «не бесплатная». Именно поэтому нужно понимать, когда стоит их включать, а когда нет. В конечном продукте (в идеале) они бесполезны и их можно отключать. Само собой, если просчитаны всевозможные случаи и гарантируется, что стек не будет переполнен ни при каких обстоятельствах.
Вроде во FreeRTOS прерывания на другом стеке работают, даже регистры разные?

Может быть, вы имеете в виду, что регистр SP другой (не MSP, а PSP)?

Именно. А что, там одно и то же значение оказывается. А то я не вникал в тему, если честно.

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

habr.com/ru/users/Amomum
Всё так, только полностью наоборот. MSP который использовался в системе без ос — теперь обслуживает прерывания. PSP предназначен для задач, и его можно переключать из прерывания.
Я в не очень крупных проектах использую кооперативную многозадачность.
Каждый объект имеет функцию processEvents() в которой он проверяет, нужно ли ему что-то сделать, выполняет это, или сразу делает возврат из функции.
В main() у меня бесконечный цикл, в котором для каждого объекта вызывается processEvents():
for(;;) {
    uart.processEvents();
    spi.processEvents();
    webServer.processEvents();
    ...
    sleep();
}

В конце цикла сон. Возврат из сна по любому прерыванию, включая systick.

Преимущества такого подхода:
1. Все предельно просто. Не нужно никаких RTOS, есть только одна функция, которая периодически вызывается.
2. Не нужны мьютексы для синхронизации между задачами. Однако, не забываем про прерывания!
3. Не нужно выделять память на каждую задачу отдельно. Достаточно такого количества памяти, что потребляет самая ресурсоемкая задача.

Недостатки:
1. Не так круто, когда у тебя под капотом банальный бесконечный цикл, а не RTOS.
2. Время исполнения задачи не должно быть слишком большим. Это не значит что нельзя делать какой-то очень длительный расчет, просто придется его бить на части и запоминать промежуточные результаты.
3. Возможно, не так энергоэффективно, как RTOS. Хотя если работы у задач нету, они все быстро исполнятся и CPU уйдет в сон.

С RTOS мне лично не понравился именно момент с назначением количества памяти каждой задаче. Мало дашь — получишь очень трудноуловимый баг с порчей соседних задач. Много дашь — памяти не хватит, задач то много.
По моему опыту — не всегда помогает. Увы. Например работал с одной GUI библиотекой, которая позиционировала себя «как для МК», а на деле там был ад. Так вот. Она отжирала порядка 40 кб для 128 на 64 пикселя монохромного экрана (как потом выяснилось). При этом еще и умудрилась успеть залезть на кусок переменных планировщика и структур описания потоков и других внутренних штук. Это было очень сложно отлавливать, потому что в hard я падал именно при попытке сменить задачу)
Для такого экрана можно и самому писать библиотеку. Ничего сложного там нет. Я делал для экрана на чипе ST7920 кажется. И надо там всего 1 Кб под буфер экрана.
Но куда проще взять чужой непроверенный код, а потом жаловаться, что "RTOS — это плохо". Если там ошибка, которая заставляет её срать в память, что точно так же она может убить что-нибудь и без RTOS. Причем если это не выяснится при написании(и вы будете думать, что все хорошо), то выяснится уже в продакшене, что гораздо хуже.
Ну так я и не говорю, что ОС в этом виновата. Я лишь отметил, что при таком стечении обстоятельств разобраться без ОС было бы проще. Поскольку в pc-регистре было бы чуть более адекватное значение, чем функция переключения контекста. А про библиотеку — требование заказчика. У них эта библиотека использовалась для цветного экрана прошлым программистом. И в ТЗ она была обязательной. А т.к. с закрытым кодом, то пришлось еще и писать авторам и выяснять, что да почему (они лишь давали .a + .h под разные архитектуры).
Не было бы проще! Насрала бы она куда-нибудь в память и упал бы ваш код никак не связанный с этой библиотекой. И точно так же мучились бы пытаясь понять, что происходит. С заказчиками тоже договариваться надо уметь.
Однако в большинстве случаев эта мысль ошибочна. И в данной статье я расскажу, почему.

И… не рассказали.
Дана противоположная точка зрения вида «можно выделить часть ресурсов под ОС, получив взамен понятность кода». Дана ваша точка зрения о том, что можно было в таких и таких случаях сделать иначе.
Но нет описания и примеров того, где выделение ресурсов под ОС оказалось фатальной ошибкой.
Да, про фатальность получилось не сильно ярко. Я привел пример с UART-ом. Это можно сказать «из реального прадакшена». Просто чуть упрощенный, чтобы ко мне не было вопросов. Там видно, что код становится неявным. Поток данных почему-то засовывается в очередь. Из этой очереди потом идет отправка обратно. Какое-то накопление байт зачем-то в буфере…
Да, но в этом примере как раз просто показано, что есть два подхода: один, который вам люб по тем или иным причинам, и другой, который нет.
Чем плоха стыковка через очередь — из этого описания непонятно. Т.е. если это съедает слишком много ресурсов, что становится критичным — тогда вопросов нет. Но иначе-то — в чем проблема использования такой абстракции?
Согласен. Данный пример только дает возможность выбрать между методами. Но к сожалению, кода аналогичного тому, с которым сталкиваюсь в прадакшене под рукой не нашлось. Но если вкратце, то сталкивался со следующим:
Принимаемые данные с последовательного порта клались в очередь, а затем раз в 20 мс выдавались пачкой по другому интерфейсу. Проблема в том, что это нагружало процессор очень значительно. Поскольку прием велся по DMA на частоте примерно 1 мегабит, вызывались прерывания, там вызывалось добавление в очередь ресурсом до 50к элементов, а затем другой поток раз в 20 мс извлекал в массив данные и по DMA отправлял в другой порт. А ведь можно было убрать этот уровень абстракции с очередью и просто использовать кольцевые буферы. Поскольку никакой проверки на корректность не производится (по ТЗ).
Так а в чем проблема?
Задача приема реализует кольцевой буфер. Вызывается шедулером каждую мс, проверяет совпадает ли NTDR с сохраненным значением, если нет — выгребает все что принято. В таком случае прерывания от UART пообще не нужны.
Задача передачи вызывает функцию у задачи приема "дай мне порцию данных". В этой функции задача приема использует мютекс, который захватывается так же при добавлении данных в кольцевой буфер — и никакие очереди не нужны.
Понятно, что можно наворотить все что угодно, насоздавать кучу задач, а потом ругать RTOS.
Про запутанность отписал ниже. Это пример с недооцененным загрузом процессора.
Ну а вообще да. Не вина ОС, что ее используют через одно место. Сам использую ОС периодически. Проблема в том, как ее МОЖНО при КРИВЫХ РУКАХ использовать. Что и происходит периодически.
При КРИВЫХ РУКАХ что угодно МОЖНО использовать криво — что с RTOS, что без. Так что получается статья ни о чем.
Не совсем. Статья о том, что не надо пихать ОС везде, куда лезет. Просто пример подобрал не самый удачный.
Какой тогда удачный? Статья вырождается в «делайте так, а не так» — почему, собственно?
Удачно было бы привести пример, когда пихают под сотню потоков на решение задачи, которая без кучи уровней абстракции решалась бы в пару десятков строк внутри нескольких прерываний (подобный пример был написан здесь).
Касательно же запутанности. Встречал задачу, которая в принципе решалась одним конечным автоматом в прерывании. Вместо этого создавался отдельный поток для каждого состояния. И прерывание отдавало симафор соответствующему потоку (чтобы началась обработка). А вот разблокированный поток уже мог отправлять аналогичные семафоры для разблокировки соседних потоков (как в случае, если бы пришло событие по прерыванию для его разблокировки). В таком коде нельзя было адекватно сказать, произошла разблокировка из другого потока, или же из прерывания.
Возьмем типичный МК проект. Считать датчики(АЦП, i2C и тп), считать регистры с какой нибудь модбас(и тп) фигни, зажечь пару лампочек (нарисовать картинку) включить реле, пошимить пару транзисторов. 99% процентов проектов прекрасно обойдется библиотекой уровня АбДурины ))

Речь о чём, сам вопрос надо ставить подругому: не злоупотребление, а почему пользователь выбирает вариант с РТОС. Чем-то же ртос завлекает, быстрым получением работающей поделки, ненужностью разбираться в периферии и тд, а вовсе не необходимостью «паралелить» процессы. Когда есть хороший набор типовых библиотек позволяющий не напрягаться на работе — мало кто спрыгнет на ртос ;P С другой стороны сейчас не 2000год, есть куча РТОС для МК которые позволяют вообще не вспоминать о регистрах периферии, потому и злоупотребляют

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

Отсутствие желания и времени разбираться в периферии — это для пользователей публичных библиотек. ОС тут не имеет значения.
Человек просто берёт готовый пример, адаптирует под свои условия, и наслаждается готовым результатом. Ничего плохого в этом нет.
Трудно быть первопроходцем: когда примеров нет, публичных библиотек нет, и вокруг одни юзвери — умеющие только скачивать готовое.
Куда столько?
обслуживание wdt (если ОС упала, то задача его не сбросит и устройство перезагрузится);

Если ос упала — значит девайс глюкавый, и требует на выбор: тотальной отладки или утилизации. Перезапуск — это для простых устройств, типа примитивных конечных автоматов. Там даже ос не нужна.
А для сложных девайсов циклический перезапуск — повышает шанс пожара или удара током, или всё вместе.
Ну и насчёт собаки.
Выгуливать её должна одна задача, в идеале — системная. Уж если цикл системной задачи перестаёт корректно выполняться, то всё остальное уже не так важно. Сама системная задача может контролировать что угодно — у неё для этого есть все ресурсы и права.
Обрабатывать событие собаки необходимо отдельным ресурсом, который гарантированно будет выполняться, в не зависимости от общей температуры больных. В самом простом случае — это набор средств тушения пожара: корректное закрытие записи во флешь память, принудительная установка ног мк в состояние стоп, сохранение отчёта или отправка отчёта, и ожидание отключения питания. У вас уже сбой — изображать бурную деятельность не стоит.
Мы выделяем 1 задачу как раз. С самым низким приоритетом (выше idle). Если ее вытеснили на сильно долгое время, то что-то выжирает сильно много ресурса. Обычно МК берется с сильно большим запасом (потому что дешевые). Перезагрузка гарантирует, что МК будет инициализирован заново как положено и продолжит свою работу и вертолет не упадет от того, что сервопривод перегрелся и отвалился кварц, например. Будет сделана попытка запуститься без него и жить дальше.
Событие собаки никто не обрабатывает. Собака все убивает, если ее не сбросить.
Надо отметить, что в устройствах с которыми я работаю — нет надобности в «закрытии сессии» (за исключением пары случаев, но они блокируются при нормальной работе).
А, ну так это разные классы задач: выжить любой ценой, и выжить с минимальными потерями.
Под первый вариант попадают все вторичные системы, временный глюк которых не приводит к необратимой катастрофе.
Второй вариант…
— Шеф, у нас тут верхний ключ на 600А пробило, и линия зависла на отметке 1200В. Давай ещё раз стартанём, вдруг её отпустит…
В этом случае нужно не только корректно завершить работу, но и запретить дальнейшую эксплуатацию до завершения ремонта.
Аааааа… Ну тут конечно же от задачи. Просто у нас так проектируют схемы, что в случае если МК умрет, то все линии по умолчанию не должны ничего натворить (если только внутри МК не произойдет замыкание на питание). Иногда даже закладываем на такие штуки декодеры на логике. Если нужный код — можно реле открыть… Любое иное — закрыть.
Мне вот интересно, как вы определите "отвалившийся кварц" на запуске?
STM32 умеет такое делать, в случае отвала внешнего кварца переходит на внутренний RC генератор.
Ну и из-за того, что у нас «отвалился кварц» надо произвести инициализацию по другому алгоритму. Например, без этого кварца уже нельзя использовать ETH, но можно технологический UART и т.д.
На запуске — не проблема, я всегда это делаю (по крайней мере в STM).
А вот в работе. Тут вопрос. И, кроме резервирования, ничего в голову не приходит.
Но это совсем отдельная тема. Хотя, весьма интересная.
Only those users with full accounts are able to leave comments. Log in, please.