День добрый! Недавно начал использовать C++ для программирования STM32 и появились вопросы. 1) Почему все очень рекомендуют не использовать Exceptions? Понятное дело, что это съедает некоторые ресурсы (при нужной конфигурации около 70кБ флеша как минимум), но если у меня жирный МК, то почему нет? Или есть другие подводные камни? Просто как по мне - сделать стектрейс на эксепшнах это очень удобно. Вот пропустил ты в коде какой-то нюанс и поделилось что-то где-то на ноль. И всё - провалился в Hardfault, а откуда и почему - черт его знает. А с эксепшнами (если пробрасывать throw с описанием) мы можем найти как минимум метод.функцию, в которой произошла ошибка и отправить эту инфу в лог. Но может я чего не знаю... 2) Почему используется связка FreeRTOS С++ вместо, казалось бы, очевидного std::task? Если язык уже предусматривает многозадачность и всё такое, то почему не использовать эти плюшки (тут я только рассуждаю, до практики не дошел)?
Команда SVC это тоже исключение. Которое специально разработано для использования пользователем для организации вызова функции ядра. Так и называется: SerVice_Call. Ну как INT # у х86. Так что лично моё мнение: если монолитный маленький код то лучше исключения не трогать (ну разве что правильно оформить HardFault, чтобы понимать кто вызвал). А если у вас любая ОС да ещё и загружаемые исполняемые модули то как раз надо использовать исключения по максимуму.
_________________ Репозиторий STM32: https://cloud.mail.ru/public/2i19/Y4w8kKEiZ Актуальность репозитория: 16 мая 2025 года Если чего-то не хватает с сайта st.com - пишите, докачаю.
1) Почему все очень рекомендуют не использовать Exceptions?
70к на исключения, 60к на stream. Тупо тратить время на заливку жалко.
Цитата:
Вот пропустил ты в коде какой-то нюанс..
...а твой МК управляет двигателем внутреннего сгорания. А ты там стэк раскручиваешь в поисках виновника, да?
Цитата:
поделилось что-то где-то на ноль
Для этого есть отдельные прерывания. Занимаюсь ПЛК, поэтому что там погромист понапишет в своей проге априори не известно, поэтому деление на ноль для меня нормальная ситуация, обработка которой занимает минимум тактов.
Цитата:
очевидного std::task
Так это всего лишь шаблон. Наполни его смыслом и используй. А я по-старинке:
твой МК управляет двигателем внутреннего сгорания. А ты там стэк раскручиваешь в поисках виновника, да?
И пусть весь мир подождёт! (с)
_________________ Репозиторий STM32: https://cloud.mail.ru/public/2i19/Y4w8kKEiZ Актуальность репозитория: 16 мая 2025 года Если чего-то не хватает с сайта st.com - пишите, докачаю.
Компания MEAN WELL пополнила ассортимент своей широкой линейки светодиодных драйверов новым семейством XLC для внутреннего освещения. Главное отличие – поддержка широкого спектра проводных и беспроводных технологий диммирования. Новинки представлены в MEANWELL.market моделями с мощностями 25 Вт, 40 Вт и 60 Вт. В линейке есть модели, работающие как в режиме стабилизации тока (СС), так и в режиме стабилизации напряжения (CV) значением 12, 24 и 48 В.
А если у вас любая ОС да ещё и загружаемые исполняемые модули то как раз надо использовать исключения по максимуму
Лично я за время написания кода убедился, что код надо унифицировать максимально. Есть у тебя сегодня проект, в котором используетс какой-то парсер - делай так, как будто завтра тебе его использовать в новом проекте. Соответственно, если уж начал работать с эксепшнами, то и делай так же, чтоб в любом проекте в дальнейшем этот код переиспользовать можно было. Само собой есть исключения - когда надо например засунуть что-то большое во что-то маленькое. Но и тут как бы стоит ли овчинка выделки... Цена на МК сейчас не космос, а время разработки ресурс ценный. Если только мы не о крупносерйном производстве, тогда можно и подумать. Это мое мнение.
tonyk писал(а):
60к на stream
Исключение и без стримов вроде работают.
tonyk писал(а):
а твой МК управляет двигателем внутреннего сгорания
Тут тоже палка о двух концах. С одной стороны да, тратим ресурсы вычислительные, но с другой стороны - исключения штука исключительная и если мы попали в catch, то значит всё хреново. И в таком случае, в некоторых ситуациях, предпочтительнее будет узнать место возникновение ошибки. Но опять таки, все индивидуально - если у нас счет на микросекунды, то там в прерываниях за каждый таки жопа горит) Кстати, а что будет если мы словили исключение и в это время вызвался обработчик прерывания? У него приоритет будет выше или эксепшны работают на каком-то высшем приоритете?
tonyk писал(а):
Так это всего лишь шаблон
Я просто вообще ни одного упоминания не видел в сети об использовании тасков. Везде FreeRTOS. Как будто эти таски в принципе не поддерживаются или требуют мегабайт памяти. Вот и стало интересно)
Как любитель плюсов, не могу пройти мимо и не вставить 5 копеек: 1. В общем-то только лишнее потребление памяти + использование кучи для создания объектов исключений, что в серьезных проектах не допускается. Даже в десктопах многие отказываются от исключений, тем более в c++23 появился std::expected 2. Тут всё просто, в тулчейне для МК просто нет реализации std::task (и многих других элементов стандартной библиотеки).
лишнее потребление памяти + использование кучи для создания объектов исключений
Потребление памяти это да, но создание объектов в куче будет только в исключительных случаях - когда без эксепшна будет HardFault. Из обработчика HardFault, насколько мне известно, нельзя продолжить программу и он не дает информации о файле и методе, в котором было выброшено исключение.
azhel12 писал(а):
Даже в десктопах многие отказываются от исключений
А как тогда делать нормальное логгирование? Ладно МК, но в десктопе кода в разы больше и без эксепшнов (особенно используя сторонние библиотеки) отладка вообще превращается в n-й круг ада)
azhel12 писал(а):
std::expected
Не слышал о такой штуке, спасибо, изучу на досуге.
azhel12 писал(а):
в тулчейне для МК просто нет реализации std::task
Даже если выбрать полношенный GNU17?
tonyk писал(а):
...то это означает, что работа программы проверена не со всеми возможными значениями входных данных.
У меня как-то мама спрашивала: "А зачем программисты постоянно переписывают то, что уже написали, почему сразу не написать чтоб нормально работало?"
но создание объектов в куче будет только в исключительных случаях
Промышленные стандарты на функциональную безопасность ПО запрещают использовать динамическое распределение памяти. Некоторые стандарты допускают его использование до входа в главный цикл программы.
isx писал(а):
У меня как-то мама спрашивала: "А зачем программисты постоянно переписывают то, что уже написали, почему сразу не написать чтоб нормально работало?"
Потому, что не все умеют писать сразу правильно. Вы часто едите в автосервис для очередного апгрейда ПО в ЭБУ двигателя вашего авто?
Вообще, довольно интересная тема затронута. Я ранее тоже задавался таким вопросом. Ведь возникший аппаратный HardFault - это уже констатация факта, что "шеф, всё пропало". А вот программный "отловитель" ошибочных данных - штука весьма даже полезная. Гипотетический пример - есть три датчика температуры воды в нагревателе. И вдруг с одного датчика приходят сильно отличающиеся от остальных датчиков показания. Нужно выполнить отбраковку этого датчика. И стандартный механизм проброски исключения, встроенный в язык С++, здесь как раз пригодится. То есть, будет какой-то общий вид, общий синтаксис для исключительных ситуаций работы кода. С другой стороны, унифицированные решения, как правило, более тяжеловесные по сравнению со специализированным решением "по месту". Что и подтверждается увеличенным расходом памяти при использовании межанизма исключений языка С++. Механизм исключений микроконтроллера - это уже аппаратная защита, а не программная, и вобщемто, кроме перезапуска МК ничего более сделать нельзя. То есть, когда неверные данные с заглючившего датчика вызовут HF, останется только перезапустить МК с потерей накопленной работы. А вот программное исключение позволит отбраковать датчик, дающий ложные показания и не учитывать его в работе. Аппаратное исключение ядра SVC используется RTOS для переключения контекста, поэтому не подходит для пользовательских исключений.
По второй части вопроса: А вот потому, что ядро FreeRTOS написано на Си, а не на С++. И опять же, универсальное решение на базе языка С++ будет более тяжеловесным, чем написанное в FreeRTOS. В ней же не зря есть участки кода даже на ассемблере, потому что это именно специализированное решение для более быстрой работы.
Цитата:
Лично я за время написания кода убедился, что код надо унифицировать максимально.
В целом, нет, не согласен. Потому что, еще раз повторюсь, унифицированное решение - медленнее и тяжеловеснее специализированного. Если вы говорите, что типа да какая разница, памяти и частоты нынче в МК хватает, то посмотрите на смартфоны - размеры памяти в них выросли уже до 512 - 1024 ГБ, но и размеры приложений не отстают - каждое приложение по несколько сотен мегабайт, хотя оно работает практически так же, как и несколько лет назад, когда размер приложения измерялся в десятках мегабайт. И с аккумулятором та же ерунда - его ёмкость растет, уже 5,5 А-ч, а время автономности ровно то же самое, что и 10 лет назад с гораздо меньшим аккумулятором. Человечество "вошло в петлю", когда гонка за скоростью выдачи обновлений ПО или новой модификации смартфона стала выдавать весьма низкокачественные продукты, практически не оттестированные. То есть, уже никого не интересует качество продукта, интересует только "а чо нового сделали". Рано или поздно такая концепция подойдет к "красной черте". Хотя, я прекрасно понимаю, что сейчас переубедить никого невозможно, все ослепли и оглохли в этой погоне, не замечая, что их творение уже через пару месяцев никому не нужно, потому как было выпущено "обновление с целью исправления багов".
Промышленные стандарты на функциональную безопасность ПО запрещают использовать динамическое распределение памяти
Интересно, не знал об этом.
tonyk писал(а):
Потому, что не все умеют писать сразу правильно. Вы часто едите в автосервис для очередного апгрейда ПО в ЭБУ двигателя вашего авто?
Подискутировал бы, но не знаком глубоко с устройством ЭБУ. Может там отдельный МК для критических задач, которых в целом, не так и много, да и сложности я не вижу в них большой. А может и в чем другом дело...
Базилюк писал(а):
Гипотетический пример - есть три датчика температуры воды в нагревателе
Вот это как раз случай, когда исключения лучше не использовать. Эта ошибка может быть обработана просто возвратом сообщения о невалидных данных. Я имел в виду более сложные в обработке ситуации типа обращения к указателю с несуществующим блоком памяти, либо когда память была затерта и т.п.
Кстати, я тут поразмышлял и придумал интересную схему (а может и изобрел велосипед). Как я говорил, мне исключения интересны больше тем, что позволяют размотать стек и посмотреть из каого класса и метода оно было выброшено. Схема такая: - создаем в каждом классе статическое константное поле с именем класса и в каждом методе такое же, но с именем метода - делаем два глобальных статических указателя - при каждом заходе в какую-либо функцию или метод назначаем этим указателям адреса наименований класса и фонкции\метода соответственно - в обработчике HardFault берем значения из этих переменных, копируем в выделенную область энергонезависимой памяти, в которой также ставим флаг о наличии ошибки. Можно еще добавить инфу об инструкции из регистров. - при перезапуске МК после инициализации добавляем проверку этого флага и если он выставлен, то что-то делаем (например передаем на сервер сохраненные строки с методом и классом), очищаем флаг и строки в энергонезависимой памяти.
Вместо наименования класса можно использовать namespace, имя файла или т.п. Пока не пришел к решению, что более эффективно.
Из недостатков: - дополнительная память под строки, хотя этим полагаю, можно пренебречь. - лишние такты в каждой функции на копирование адреса в статические переменные. Операции насколько я помню атомарные и потребуется два такта, что в целом терпимо. В обработчиках прерываний только вот нужно будет 6, так как сначала текущие адреса нужно сохранить во временную переменную, изменить указатели на имя обработчика, а потом при выходе вернуть прежние значения. - необходимость вручную указывать наименование методов, классов и функций. Возможно есть средства языка, которые помогают получить их автоматом на этапе компиляции, но я не в курсе. Если кто знает - подскажите, буду признателен.
В качестве расширения можно реализовать операции изменения статических переменных в виде define, что позволит иметь возможность изменять логику работы из одной точки кода + включать и отключать их когда нужно (например, отключить в продакшене, по каким-то причинам).
Я имел в виду более сложные в обработке ситуации типа обращения к указателю с несуществующим блоком памяти, либо когда память была затерта и т.п.
Необходимо делать ещё исключение, которое отловит, что память затёрлась в вызове того исключения. И желательно ещё одно исключение, которое беду и с этим сторожевым исключением отловит. В общем, вместо main сразу писать try
Зря смеетесь - на десктопе в c# я примерно так и делаю - в итоге код отсаживается в разы быстрее, так как ни одна проблема не может привести к крашу приложения (если catch правильно отработан), даже сторонние библиотеки, которые имеют свойство падать по неизвестным причинам, а абсолютно вся инфа о проблемах сохраняется в логи.
Тоже работаю с C#. Но так не делаю. И как-то никто пока не жаловался. Кроме того, программирование десктопных приложений и "железяк" несколько отличается.
А что такое "код отсаживается"?
Добавлено after 5 hours 44 seconds: А вообще, это, наверное, всё субъективно плюс местячково (то есть, зависит от внешних условий, накладываемых компанией, командой, ТЗ и т.д.). В конечном итоге, важен лишь результат.
Тоже работаю с C#. Но так не делаю. И как-то никто пока не жаловался.
Да я не к тому что жаловался, а к тому, что такой подход имеет место быть. Если будет проект с кучей кривых библиотек, то попробуйте, отладка в разы упростится) Ну да, самом собой применимо к десктопу, на МК бы так не делал (пока не знаю всей нутряную блока try, поэтому непонятно как оно бьет по производительности. Надо будет затестить)
Martian писал(а):
А что такое "код отсаживается"?
Отлаживается)
Ну про стандарты компаний это да. Мне повезло в этом плане, свою софтину пишу единолично, как хочу и чем хочу. Правда переписывал практически с нуля несколько раз, в итоге пришел к практически идеальной архитектуре для этой задачи - баги выявлялись практически моментально, новые фичи добавляются за минимальное время, никаких жестких зависимостей - все на интерфейсах и абстракциях. Потратил пару месяцев на переделки, за то теперь можно не париться)
Но это оффтоп. Кто еще даст замечания по архитектуре «заменителя эксешнов»?
[q обработке ситуации типа обращения к указателю с несуществующим блоком памяти, либо когда память была затерта и т.п..
Аппаратный HF прекрасно справляется с обращением к несуществующей памяти. Да, это ошибка программиста. Поврежденные данные в ОЗУ, вернее, ошибочный доступ к ним, может отслеживать аппаратный механизм MPU, там, где он есть. Так же, API FreeRTOS имеет механизм отслеживания переполнения стека задачи. Иными способами определить, затерлись ли данные, не получится. Данные сами по себе никакого флага валидности не имеют и затертые данные ничем не отличаются от незатертых. Их повреждение можно определить только косвенно.
Схема такая: - создаем в каждом классе статическое константное поле с именем класса и в каждом методе такое же, но с именем метода - делаем два глобальных статических указателя - при каждом заходе в какую-либо функцию или метод назначаем этим указателям адреса наименований класса и фонкции\метода соответственно - в обработчике HardFault берем значения из этих переменных, копируем в выделенную область энергонезависимой памяти, в которой также ставим флаг о наличии ошибки. Можно еще добавить инфу об инструкции из регистров.
Ну-ну... Про многозадачные системы вы видимо никогда не слышали и всегда программируете только суперлуп да ещё и без прерываний. В любой многозадачной среде такой подход бесперспективен. Так как первое же переключение контекста (задачи), поставит крест на вашей супер-системе.
В обработчиках прерываний только вот нужно будет 6, так как сначала текущие адреса нужно сохранить во временную переменную, изменить указатели на имя обработчика, а потом при выходе вернуть прежние значения.
А во вложенных обработчиках прерываний? не подумали? А в ARM уровень вложенности ISR может быть весьма большим....
PS: Очередной лисапед с квадратными колёсами, который и по ровной дороге-то толкать надо. А на ухабах так и вся попа велосипедостроителя в занозах будет. Для отслеживания места сбоя сохраняют регистры, кадр стека и регистры HF. А не монстроидальные классы лепят. А ещё лепше, когда микроконтроллер имеет ETB. Вот тут просто халва, а не отладка.
Сейчас этот форум просматривают: Google Adsense [Bot] и гости: 20
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения