Pull to refresh

Comments 49

Есть способ включить защиту чуть проще и без использования нескольких библиотек:
для STM32F1
if (!(FLASH->OBR & FLASH_OBR_RDPRT))
{
FLASH->KEYR = 0x45670123;
FLASH->KEYR = 0xCDEF89AB;

FLASH->OPTKEYR = 0x45670123;
FLASH->OPTKEYR = 0xCDEF89AB;

FLASH->CR |= FLASH_CR_OPTER;
FLASH->CR|= FLASH_CR_STRT;
while ((FLASH->SR & FLASH_SR_BSY) != 0 );

FLASH->CR |= FLASH_CR_LOCK;
}

для STM32F4 включает первый уровень защиты
if ((FLASH->OPTCR & 0x0000FF00) == 0xAA00)
{
while ((FLASH->SR & FLASH_SR_BSY) != 0 );
FLASH->KEYR = 0x45670123;
FLASH->KEYR = 0xCDEF89AB;

while ((FLASH->SR & FLASH_SR_BSY) != 0 );
FLASH->OPTKEYR = 0x08192A3B;
FLASH->OPTKEYR = 0x4C5D6E7F;

while ((FLASH->SR & FLASH_SR_BSY) != 0 );
FLASH->OPTCR &= 0xFFFF00FF;
FLASH->OPTCR |= FLASH_OPTCR_OPTSTRT;

while ((FLASH->SR & FLASH_SR_BSY) != 0 );
FLASH->OPTCR |= FLASH_OPTCR_OPTLOCK;
FLASH->CR |= FLASH_CR_LOCK;
}
Согласен, что можно, но мне так не нравится.

И дело даже не в том, что у вас кода больше. Мне не нравится привязка к регистрам и магическим числам конкретных микроконтроллеров. Чтобы это правильно написать и отладить, нужно хорошо разбираться во внутренностях микроконтроллера каждой серии.

А при использовании функций HAL, эта работа уже сделана другими разработчиками.
Может быть там написано и не оптимально, но как говорится, «лучшее враг хорошего».
Так привязка есть, просто она в библиотеке куда вы, видимо, не заглядываете. Тут будет вопрос для каждого программиста по его задаче — нужно ли ему лезть под капот всего этого и разбираться, нужно ли ему обходиться без сторонних библиотек для экономии места и оптимизации кода, или не нужно. Учитывая размеры флешки у STM32 не так уж часто приходится беспокоится о экономии.
UFO just landed and posted this here
Эта сторонняя библиотека (HAL) сделана и поддерживается производителем самого железа. И я обоснованно считаю, что они лучше знают тонкости аппаратной реализации, чем кто либо другой.
Судя по всему тонкости аппаратной реализации знают одни люди, а вот HAL пишут другие, я тоже пользуюсь HAL, но некоторые функции в ней реализованы крайне неудачно, например работа с различными портами через HAL это отдельное извращение.
Да, такое тоже бывает. И ошибки в HAL, это одна из причин, когда можно словить проблему на ровном месте. К счастью, разобраться с такими косяками гораздо легче, т.к. с ним уже могли сталкиваться другие разработчики и решение легко гуглится.

Проблема с HAL в том, что она слабо коррелирует с Reference Manual. То есть, если в мануале есть какая-то функция, обычно трудно понять, как она реализована в HAL, а по HAL полного писания нет. Начинаются танцы с анализом кода самой библиотеки и т.д. Уж лучше ардуино.
Лучше смотря для чего. Если для пет-проекта, то можно и Ардуино взять.
А вот если разрабатывает прошивку для конкретного оборудования или ты его же сам и разрабатываешь, то тут без вариантов.
Никому ничего не советую, но сам я использую регистры через ардуино. Это позволяет делать быстрый критичный код, и не заморачиваться со стандартными функциями там, где нет ограничений по производительности. СТМ32, 3кВт источник питания с цифровым управлением и анализом частотного отклика.
На мой взгляд (!), HAL, даже будучи разработанным самим производителем не удобен для серьезных разработок. Да, пет проект накидать в HAL можно, а когда речь идет о каких-то более серьезных функциях, влияющих на безопасность, всё равно нужно обращаться в RM, проверить как именно работает та или иная функция. Но как оттуда перенести на HAL, однозначного решения нет. То есть сперва всё-равно нужно разобраться с RM, а потом еще дополнительно искать в библиотеке, как они там это реализовали. Ну вот честно, ни быстрее, ни надежнее это не выглядит. Ну и портянки структур с отдельными параметрами утомляют.
А я не буду с вами спорить, т.к. согласен :-)
Критичный код я и сам предпочитаю лишний перепроверить, и это если не вспоминать про наличие errat.
UFO just landed and posted this here
Вполне возможно. Но меня не сильно беспокоит абстрактная «оптимальность». Для меня важно, что бы код работал так как ожидается, а на его поддержку тратилось как можно меньше времени и сил.
Если HAL работает так как нужно, то зачем мне переписывать эту же функциональность, добавляя лишних проблем с последующей поддержкой этого кода?
Эти магические цифры одинаковы для всех СТМ32, и, во-вторых, они задефайнены в .h-файле, поэтому Pelemeshka рекомендовал бы использовать дефайны вместо констант.
UFO just landed and posted this here
А разве разработчик не должен разбираться априори в том с чем работает?))
Тут ключевое слово разбираться может иметь разное значения для каждого разработчика читающего. Для одного будет достаточно уровня базовых функциональных возможностей, а для другого, нужны тонкости настройки всей периферии.
Я придерживаюсь подхода «по необходимости». Если функциональность HAL работает как заявлено, то мне не интересно залезать в её потроха и медитировать над исходниками.
Вроде как временных затрат на это практически нет
А вот с этим я категорически не соглашусь. Может быть временных затрат на копипаст действительно нет, вот только «разработка» и «поддержка», это совершенно разные процессы и второй вы совершенно не принимаете в расчет. А именно он является самым важным и дорогим.
UFO just landed and posted this here
Основные затраты, что на разработку, что на поддержку берет на себя бизнес-логика устройства, а не разовая настройка периферии.

относительно это. Иногда изменение настройки периферии существенно меняет «бизнес-логику» проекта в целом. Даже при однократной настройке по включению устройства.

В добавок, сопровождение проекта это ведь не только про устранение найденных багов, но и иногда добавление новой «фичи». А это в свою очередь может привести к пересмотру задействованной периферии и её переконфигурации. В итоге: да, казалось бы 10% от всего кода, но пришлось большую часть переписывать.

Я думаю, что у каждого есть такой случай в практике, а то и не один.

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

ИМХО, Больше удивление вызывает, когда немного поковырявшись с настройками периферии реализуешь аппаратно большую часть того, что было реализовано программно. Опять же, способ настройки может сказаться и при разработке схемы. А это уже экономия на компонентах. Так что, учитывать надо все :)
А у меня вызывает удивление считать настройку периферии отдельно от разработки. Какая разница заказчику или пользователю, по какой причине его устройство не работает? Хоть ошибка в ПО, хоть в настройке оборудования (которая по большому счету тоже часть ПО)?

Да и бюджет на разработку обычно не подразумевает выделенной части «настройка периферии». Все это одна сплошная разработка, причем затраты разовые. А вот поддержка, точнее затраты на поддержку — постоянные.
UFO just landed and posted this here
Это хорошо когда есть возможность отделить бизнес-логику от железа.
Причем отделить до уровня «пох на чем запускать — на ПК i7-9900 c виндой или stm32f100»
Мы с вами говорим про разные уровни абстракции. Под словом «разработка» вы понимаете только процесс написание кода (драйвера, бизнес логики и т.д.), а я вам пытаюсь пояснить про финансирование данного процесса, т.е. про расходы на заплату этого самого разработчика.

Это очень хорошо, если ваши устройства разработанные однажды, больше не требует ни обновления прошивок ни наращивания функционала, т.е. платить зарплату программисту. И если дела обстоят именно так, тогда я с вами соглашусь, что лучше один раз потратится на вылизывание кода при разработке, т.к. постоянные затраты на поддержку будут отсутствовать.

К сожалению, у меня за все время работы, таких проектов не было ни разу. Да и сам бизнес стремится к тому, чтобы привязать пользователя и заставить его платить постоянно.
А для этого требуется всегда исправлять баги (случайные и не очень), поддерживать новые технологии или протоколы, расширить функционал по просьбе заказчика или обновлять саму аппаратную платформу. И вот это все и есть поддержка, т.е. постоянные затраты.
UFO just landed and posted this here
Вы передергиваете мои слова. Я не говорил про «херню на коленке», хотя быстрая поставка MVP фактически поощряет такой подход. Путь это будет на совести Product Owner`ов и владельцев бизнеса.

Я на это смотрю именно как разработчик/архитектор, который понимает движение денежных потоков. Ведь именно мне/моим сотрудникам придется закрывать данные задачи, а бюджет и время, которое отводится на их решение не безгранично.
А для встроенного загрузчика (тот что системный и через разные интерфейсы доступен) как правильно обходить данную защиту? Или он дефакто её отключает?
К сожалению имею пробел в данной области, хотелось бы восполнить
Неблокирующий вывод в первом примере тут же становится блокирующем при повторном вызове.
Только если уже идет передача по DMA или размер добавляемых данных превысит размер остатка буфера. Но тут уже ничего не сделаешь, чудес не бывает ;-(
А зачем здесь вообще DMA? Подобные задачи (низкоскоростной вывод) куда проще решаются через кольцевой буфер и прерывание по пустому буферу передачи. Никаких блокировок.
Копируем данные в кольцевой буфер и смотрим — свободен ли сейчас буфер UART. Если свободен — кидаем туда первый байт.
Дальше по прерыванию просто кидаем следующий байт и всё. Дошли до указателя последнего байта — остановились.
Согласен, так получается очень красиво!
1 Как правильно заметил An_private буфер должен быть кольцевой. Дополнительный трюк- размер буфера 0x100 а голова и хвост uint8_t. Тогда даже при какой нибудь проблеме у Вас не будет наезда по памяти. И код проще.
простите, случайно ткнул в минус. на самом деле плюсую!
Бывает. Плюсани тогда компании ;-)
В этом случае придется лишний раз дергать ядро МК прерываниями!
Самый идеальный вариант — двойная буферизация. Добавляем помаленьку в буфер, если набрели на '\n' и передатчик свободен — передаем данные средствами DMA, тем временем заполняя второй буфер.
Когда я отлаживал USB (пришлось самому писать CDC и HID, т.к. готовых вменяемых в открытом доступе нет), отладочный выхлоп гонял по UART на трех мегабодах. Правда, пришлось размеры буферов увеличить до 256Б. Но затыков не было.

А, скажем, у STM32F0 есть возможность настроить прерывание по символу. В этом случае можно и прием вести по DMA вплоть до полного заполнения буфера (в этом случае сбрасываем буфер и выставляем флаг buffer overflow), а если получаем символ '\n', вызывается прерывание, в котором прекращаем прием по DMA, копируем нужный кусок буфера в другой буфер, выставляем флаг rx_rdy и начинаем сначала. Весьма удобно и не напрягает и без того хилое ядро МК.

P.S. В приличном обществе не стоит выкладывать кал! Калокуб — удел абдуринщиков!..
PS: На всякий случай хочу заметить, что даже при использовании DMA чудеса ещё как бывают.
Стандартный приём при использовании DMA — наличие ДВУХ буферов — один сейчас «в эфире», второй копит данные. При окончании данных в первом буфере — натравливаем DMА на второй буфер, а данные льём в первый. И так они по очереди и работают. В таком случае мы избегаем любых блокировок или потерь данных.
В таком случае мы избегаем любых блокировок

Не совсем. Если пихать больше чем пролезает, все равно придется или выкидывать данные или блокировать писателя или динамически заказывать новые буфера, а потом все равно блокировать.
Бывает и не такое. Прошлый год делал проигрыватель звука с плавным воспроизведение ровно по этой же схеме (два буфера ДМА, которые попеременно меняются).
А один буфер с прерыванием по полутрансферу не вариант? :)
Получается тоже самое, только с боку ;-)
На самом деле, я хотел написать про другое.
Мне пришлось задействовать третий буфер для отдельной процедуры старта проигрывания нового файла, если воспроизведение уже работает. Без этого не получалось сделать слитное воспроизведение, т.к. между концом уже проигрывающегося файла и воспроизведением нового иногда проскакивали артефакты, заметные на слух.
Если есть JLink и есть не много свободного места, то можно использовать RTT Viewer. Данные идут по SWD в неблокируемом режиме.
+1. Удобная вещь, учитывая что:
  • умеет в цветовую дифференциацию штанов уровней логгирования;
  • в ней есть простенький способ отображения переменных на графике
  • она берет на себя организацию буферизации
  • ну и бонусом, достаточно высокая скорость работы
  • чуть не забыл, аппаратный детектор наличия отладчика. если его нет, то данные начинают скипаться и лог автоматически отключается.


При этом платим только за вызов функции конфигурации, отдаем немного ОЗУ под буфера и несколько сотен микроампер добавочного потребления.
Блокирующий отладочный вывод? Да вы наверное шутите, мистер Фейнман! Лично мне такого даже в голову не приходило почти что никогда.
Как минимум, кольцевой буфер и из него уже отправка по прерываниям.

как максимум (до которого на сейчас додумался) кольцевой буфер, по которому стартует дма и дальше подбирает.
логика примерно такая:
прерывания дма запрещены. работаем на прерываниях уарта.
приходит первый сивол — отправляем как обычно.
первый символ отправился, в прерывании смотрим буфер.
буфер не пуст — заряжаем передачу дма (до конца сообщения или до конца буфера).
в прерывании дма практически то же самое что и в прерывании от уарта.
Если ничего нет — запрещаю дма и дальше см.п.1
Но и с дма есть вопросы. Например, количество и приоритеты дма.
Блокирующий отладочный вывод?
Что может быть проще printf()?
Вопрос только в реализации этой функции. Хоть через CAN
Можно и через ethernet, но обычно начитают с самого простого HAL_UART_Transmit
Видел один раз такое. Основная программа была на ПК и работала с регистрами через Ethernet. На МК была шаблонная программа которые гоняла данные туда сюда.
А поправить putchar как нужно религия не позволяет?
printf для вывода символа вызывает putchar. Вот его и правим как нужно.
В данном случае (STM32CubeIDE), переопределять нужно не putchar, а __io_putchar, но я самого вопроса не понял.
А если речь идет о том, что и printf сделать не блокирующим, да пожалуйста.
Sign up to leave a comment.