Stm32f4 прошивка через uart

Содержание
  1. 19dx.ru — R0WBH
  2. Прошивка STM32 через UART при наличии hex-файла
  3. Stm32f4 прошивка через uart
  4. STM32F405: прошить 400кб за 10 секунд или быстрый UART-загрузчик заточенный под USB-UART, размером менее 4 килобайт
  5. Оглавление
  6. Предыстория
  7. Анализ причин низкой скорости протокола AN3155
  8. Требования к моему загрузчику
  9. Описание протокола загрузчика
  10. Общий принцип
  11. Структура пакета:
  12. Обработка ошибок на уровне пакетов и таймаутов
  13. Порядок работы со стороны устройства
  14. Порядок работы со стороны хоста
  15. Описание команд
  16. Утилита загрузки для ПК
  17. UART Программатор на базе CP2103
  18. Реализация прошивки загрузчика
  19. Библиотека usart_mini.c
  20. Библиотека packet_receiver.c
  21. Библиотека sfu_commands.с
  22. Оптимизация размера
  23. Оптимизация SPL
  24. Оптимизация секции .data и .ro_data
  25. Оптимизация printf и его тени impure_data и impure_ptr
  26. Оптимизация кода запуска и удаление повторного кода
  27. Уменьшение таблицы прерываний
  28. Итог и результаты

19dx.ru — R0WBH

Личный блог Гладышева Дмитрия

Прошивка STM32 через UART при наличии hex-файла

Данная статья вам поможет прошить STM32 через UART, если у вас есть скомпилированный hex-файл прошивки.

Нам понадобится любой USB-UART адаптер из следующих:

  • Silicon Labs CP2102;
  • FTDI FT232RL;
  • Prolific PL2303;
  • или любой другой.

Качаем Flash Loader Demonstrator. Подключаем плату к компьютеру через USB-UART адаптер. Схема подключения такая:

A9 (TX) — RX
A10 (RX) — TX

Перемычку BOOT0 ставим в положение 1 и нажимаем Reset. Это переключит плату в режим загрузчика. В положении 0 данная перемычка включает режим выполнения пользовательской программы сразу после включения/сброса.

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

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

Здесь мы видим адреса страниц памяти, а также их состояние (защита от чтения/записи). Просто жмём Next.

Вот здесь уже предстоит выбрать, что мы хотим сделать с микроконтроллером, а точнее с его памятью. Можно очистить память (полностью или частично), загрузить программу, выгрузить программу из памяти в файл, установить защиту, либо произвольно отредактировать данные. Выбираем загрузку на устройство (Download to device). Выбираем hex-файл с прошивкой. Галочка «Verify after download» позволяет проверить правильность записи программы. Если же поставить галочку «Jump to the user program», то программа будет выполнена сразу же после загрузки, даже без возвращения перемычки BOOT0 на место и перезагрузки контроллера.

Нажимаем Next и если всё пройдёт успешно, то МК будет прошит.

Источник

Stm32f4 прошивка через uart

В популярных платах компании ST (STM32F4DISCOVERY, 32F429IDISCOVERY и других) основанных на процессоре STM32F4xx, имеется встроенный отладчик ST-LINK. Этот отладчик работает на отдельном процессоре STM32F103CBT6. Некоторые версии прошивки для этого отладчика поддерживают комбинированное USB-устройство, в котором отладчик ST-LINK совмещен с виртуальным COM-портом VCP. Это дает удобную возможность организовать отладочный вывод в консоль терминала.

Для обновления firmware ST-LINK есть 2 утилиты: STSW-LINK007 [1] и STSW-LINK004 [2]. Обе работают через интерфейс USB, используя встроенные возможности для обновления в прошивке отладчика ST-LINK. STSW-LINK007 более простая утилита, STSW-LINK004 более продвинутая (STSW-LINK007 входит как составная часть в пакет установки STSW-LINK004), но в сущности они делают одно и то же.

К сожалению, не все прошивки отладчика ST-LINK позволяют обновиться до версии, которая поддерживает комбинированное устройство ST-LINK+VCP (подробнее см. [3]). Если у Вас старая версия прошивки ST-LINK, то для использования утилиты обновления [1] или [2] для получения версии ST-LINK с поддержкой VCP (ST-LINK/V2-A или ST-Link V2.1) необходимо сначала воспользоваться ROM-загрузчиком чипа STM32F103CBT6, чтобы записать firmware отладчика ST-LINK версии 2.1.

ROM-загрузчик STM32F103CBT6 работает через ножки порта USART PA9 TX (ножка 30 корпуса LQFP48) и PA10 RX (ножка 31 корпуса LQFP48). Активируется ROM-загрузчик при включении питания, если в этот момент вывод BOOT0 (ножка 44 корпуса LQFP48) притянут к уровню лог. 1 (+3.3V). Для взаимодействия с ROM-загрузчиком (передачи ему прошивки) используется утилита STM32 Flash loader demonstrator [4].

[Обновление до ST-LINK/V2 с помощью ROM-загрузчика]

1. Подключите USB — TTL UART адаптер к ножкам U2 STM32F103CBT6 — PA9 TX (выв. 30) и PA10 RX (выв. 31), ножку BOOT0 (выв. 44) соедините с питанием 3.3V. Ниже на рисунке в качестве примера показано подключение к микроконтроллеру STM32F103CBT6 отладчика ST-LINK на плате STM32F4DISCOVERY. Сигналы TX и RX можно найти на SMD-резисторах R4 и R17 соответственно, а BOOT0 на R7, +3.3V удобно взять с конденсатора C7, землю с ножки 3 коннектора CN2 SWD:

2. Включите питание STM32F103CBT6, и с помощью FLASHER-STM32 [4] запишите а память STM32F103CBT6 файл Protected-2-1-Bootloader.bin (это прошивка, которая взята из статьи [6]).

3. Отключите питание от платы, отсоедините TTL UART адаптер от ножек PA9 TX и PA10 RX STM32F103CBT6, ножку BOOT0 соедините с GND (т. е. верните все как было).

4. Подключите питание, соединив с компьютером плату STM32F4DISCOVERY через коннектор CN1. В Диспетчере Устройств Windows должно появиться устройство STM32 STLink.

5. Запустите STM32 ST-LINK Utility (версии 4.3.0.0), выберите в меню ST-LINK -> Furmware update. Откроется окно утилиты обновления.

Важное замечание : STM32 ST-LINK Utility нужна именно версии 4.3. Более новая утилита может не работать, например версия утилиты 4.6 на прошивку Protected-2-1-Bootloader.bin выдаст сообщение «неизвестная версия firmware». Правильную версию STM32 ST-LINK Utility 4.3 и прошивки ST-LINK см. в архиве [7].

Нажмите кнопку Device Connect, появятся возможные варианты обновления:

— STM32 Only . Отладчик ST-LINK будет поддерживать только микроконтроллеры STM32.
— STM8 Only . Отладчик ST-LINK будет поддерживать только микроконтроллеры STM8. Редко используемый вариант.
— STM32+STM8 . Получите возможность отлаживать микроконтроллер на этой плате, и также можно будет прошивать и отлаживать внешние устройства как STM32, так и STM8. Для подключения внешних отлаживаемых устройств используется коннектор CN2 SWD, при этом надо снять перемычки с коннектора CN3.
— STM32+MSD+VCP . В этом варианте отладчик ST-LINK будет работать как комбинированное устройство USB: отладчик и виртуальный COM-порт. Мне этот вариант нравится больше всего, потому что дает возможность в программе использовать отладочную консоль через USART2 микроконтроллера STM32F407.
— STM32+Audio . Не знаю, что означает эта опция. Однако она тоже поддерживает виртуальный COM-порт. Если кто-то разобрался, оставьте сообщение ниже в комментарии.

После выбора варианта нажмите кнопку «Yes >>>>». Через несколько секунд ST-Link обновится до нужной версии.

[ Виртуальный COM-порт в отладчике ST-Link ]

Если выбрать прошивку STM32+MSD+VCP, то вместе с отладчиком ST-Link получается виртуальный COM-порт (комбинированное устройство USB), который очень удобно использовать как консоль терминала. Компьютер хоста отладки может посылать и принимать символьные сообщения через USART2 отлаживаемого микроконтроллера U4 STM32F407VGT6.

Замечание: вариант STM32+MSD+VCP подойдет в том случае, если у Вас STM32F103 с памятью программ 128 килобайт (STM32F103CBx или китайский чип STM32F103C8x). Если же размер памяти 64 килобайта (STM32F103C8x), то вместо варианта STM32+MSD+VCP выберите STM32+Audio, у него тоже реализован VCP.

Схема соединений для STM32F4DISCOVERY получается следущая:

[U2 STM32F103CBT6 ST-LINK] [U4 STM32F407VGT6]
PA2 TX ———— > ———— PA3 RX
PA3 RX ————

Примечание: для платы 32F429IDISCOVERY с микроконтроллером STM32F429ZIT6 соединения между портами USART уже разведены. Вместо USART2 отлаживаемого микроконтроллера STM32F429ZIT6 здесь используется USART1 и его ножки портов PA9 (TX) и PA10 (RX)

[U2 STM32F103CBT6 ST-LINK] [U5 STM32F429ZIT6]
PA2 TX ———— > ———— PA10 RX
PA3 RX ————

Источник

STM32F405: прошить 400кб за 10 секунд или быстрый UART-загрузчик заточенный под USB-UART, размером менее 4 килобайт

C утилитой для ПК и платой — программатором,
с использованием SPL,
с полноценной системой команд и проверкой CRC32,
с гарантией доставки и переотправки сбойной или потерянной команды,
с проверками ошибок, отладочными сообщениями и урезанным printf’ом.
Оптимизирован под современные USB-UART преобразователи и потоковую передачу.

Оглавление

Предыстория

Мы почти во всех своих устройствах используем STM32. Например в наших шлюзах.
В очень многих устройствах стоит STM32F405/407 и имеется USB UART мост на базе CP2103, но иногда FTDI.
У всех STM32 согласно AN3155 имеется встроенный загрузчик UART и мы его используем на всех стадиях: от разработки и производства до техподдержки наших пользователей.
Фирма STM также предлагает утилиты ПК для того чтоб воспользоваться этим протоколом.
У CP2103 есть штатные GPIO, которыми можно устройство сбросить в основную программу или в штатный загрузчик.
Ещё хорошо то, что эти GPIO винда не трогает, когда ищет Plug&Play устройства на RS232, и поэтому устройство не сбрасывается при подключении к ПК и не “улетает” в непонятный для пользователя режим.

Читайте также:  Java version check error

Казалось бы всё хорошо, но у этого штатного загрузчика есть ощутимая проблема: больно уж долго он прошивает: около двух минут, особенно когда отлаживаешь схемотехнику, а не свой код.
При этом обычно вносишь минимальные правки и дольше ждёшь когда прошьётся, успеваешь отвлечься и тд.
Да и не дело терять за день более четырёх часов на прошивку изделия, притом что порой разработка длится не один месяц.
Эта проблема долгой прошивки заставила искать причину проблемы и начать её решать.

И решение представленное в этой статье — это вторая попытка.
Первая попытка заключалась в том, что я написал свою утилиту для ПК использующую AN3155 — «ARMkaProg».
Она вышла на порядок удобнее и умнее штатной, смогла использовать GPIO у CP2103 и прочие аппаратные удобства.
Но…
Штатная утилита и AN3155 не рекомендовали шить на скорости выше 115200 бод,
Моя же программа могла шить даже 1000к бодах, но это давало прибавку к скорости всего в 2 раза (по сравнению с 115200).
А не в 9, и в итоге вопрос скорости прошивки остался не решённым.

Анализ причин низкой скорости протокола AN3155

Низкая скорость прошивки проистекает из специфики и простоты этого протокола:

  1. Рекомендованная максимальная скорость — 115200, но многие USB мосты поддерживают как минимум мегабод, но на скоростях выше 115200 протокол активируется с десятой — сотой попытки, а каждая попытка это минимум 300 — 500мс.
  2. Стирать надо всю флеш, даже если размер прошивки 10к из доступных 1024к байт — в некоторых кристаллах были баги с частичным стиранием.
  3. Штатный бутлоадер (bootloader) — универсальный и, поэтому, жутко медленный, он не использует внешний кварец и не использует PLL и высокую частоту, а так же замечено, что флеш медленнее стирается на низкой тактовой — это сделано для батарейного применения.
  4. Маленький размер команды. Максимум за 1 раз можно прошить 256 байт, а на некоторых версиях чипов не более 128.
  5. Куча лишних телодвижений: надо активировать загрузчик, получать списки команд, для производства надо узнать уникальный ChipID, не всегда его удаётся прочитать одной командой и не всегда это даже возможно из бутлоадера.
  6. Из соображений безопасности у нас всегда любые прошивки залочены, даже временные или отладочные. Хоть разлочка по факту — стирание, но штатную процедуру стирания делать обязательно надо, т.к. нигде не сказано, что разлочка гарантированно стирает, а не забивает например мусором / нулями и тд. Поэтому выходит, что надо стирать дважды, а это уже почти минута!
  7. Надо прошивку верифицировать, т.е. гонять туда сюда прошивку дважды: первый раз для записи, а второй раз — считывать и сверять.
  8. В контроллере есть очень много ОЗУ: 192к, но загрузчик этим не пользуется, он единый для всех чипов включая те, что имеют очень мало ОЗУ.
  9. На каждую команду приходится три подтверждения (АСК): на код команды, на адрес и на данные. Но USB работает интервалами по 1мс, в итоге на каждую команду тратится по 3мс даже если надо считать или записать 1 байт и мы можем послать не более 333 команд и без того мизерных блоков данных.

Рассмотрим подробнее команду записи из даташита на AN3155:

где красным отмечено: (1) — подтверждение кода команды, (2) — подтверждение адреса, (3) — блока данных

Смотрим на команду записи осциллографом (скорость 1000к бод)

и реально видим эти состояния АСК: (1), (2) и (3), и их заметную задержку из пункта 9,
а так же видим, что время записи огромное и больше времени тратится на прошивку, чем на передачу данных (см пункт 3).

И если глянуть на десяток команд записи

то видно что пауз очень много и они занимают 2/3 времени.

Хмм, а что если не ждать на каждую команду по три штуки ACK и просто слать данные потоком?
Но ничего хорошего из этого не выходит. Загрузчик прост настолько, что он теряет данные если их не ждёт, приёма по DMA или прерыванием в нём нет, да и ROM памяти тоже нет чтоб на каждый интерфейс сделать всё идеально.

Из всех этих причин логичным образом вытекают:

Требования к моему загрузчику

Эти пункты устраняют соответствующие причины низкой скорости.

Но на практике нужны ещё мелочи:

  • Точка начала прошивки и точка входа должны различаться: первые страницы удобны для организации псевдо-EEPROM и поэтому обычно туда код прошивки и её вектора прерываний не кладут. А начинаются они со смещения 0x08010000 или далее, т.е. в тех страницах, которые большие и долго стираются.
  • Нужен монитор состояний и текстовые сообщения об ошибках: Не люблю когда нет отладочных и статусных сообщений, т.к. адово тяжело отлаживать “чёрный ящик”.
  • Загрузчик должен быть как можно меньше: устройства у нас разные и с разной периферией, но с общей платой и общей прошивкой, включая все загрузчики, клиентский код и код самодиагностики — всё в одном. А так же моих загрузчиков в одном образе тоже множество, мой вариант AN3155 с шифрованием, GPRS, полноценный FSK-модем, и все их надо суметь уместить в одну-две первых страницы размером 16-32к байта. В итоге, каждый байт на счету.
  • Загрузчик должен быть надёжным, в AN3155 защита команд от сбоев и мусора слабая — просто XOR всех байт. Было несколько случаев когда она давала сбои.

Описание протокола загрузчика

Решил сделать более подробное описание своего протокола на случай если кто нибудь захочет портировать программу загрузки для ПК под другие ОС нежели Windows.

Общий принцип

Структура пакета:

адрес размер содержимое
4 Сигнатура начала команды, для передачи в устройство 0x817EA345, для приёма из устройства 0x45A37E81
4 1 код команды, задаёт тип действия или события
5 1 побитово-инверсный код команды (для проверки)
6 2 N — размер дополнительной информации в байтах (обязан быть кратным 4)
8 N дополнительная информация — зависит от кода команды
8+N 4 встроенный аппаратный в STM32 CRC32 пакета с адреса 4 по N (исключая сигнатуру)

Сигнатура начала команды для разных направлений выбрана разная для того, чтобы по ошибке не воспринять свои же данные, которые попали себе же на приём. Например когда КЗ на ножках RX и TX контроллера или сбое программатора / кабеля.

Обработка ошибок на уровне пакетов и таймаутов

Порядок работы со стороны устройства

Порядок работы со стороны хоста

Описание команд

В описании команд распишу только дополнительные параметры команды, которые идут после кода и размера параметров.
Код команды дан в круглых скобках, далее константа в исходниках и название на русском

Дополнительные параметры команды из Хоста:
отсутствуют.

Ответ устройства:

размер в байтах Описание
12 Уникальный ChipID
4 Модель и ревизия чипа, взят из DBGMCU->IDCODE
2 Размер доступной для записи загрузчиком флеш-памяти в KiB (*1024 байт)
2 Версия загрузчика 0x0100
4 Размер буфера приёма устройства (для опции -PreWrite)
4 Адрес начала доступной для записи памяти
4 Адрес таблицы векторов прерываний и место с которого берётся контекст запуска (стек + точка входа)

пример из логов:

Дополнительные параметры команды из Хоста:
4 байта — размер стираемой области в байтах

Ответ устройства: выдаётся по окончанию стирания всех страниц,
4 байта: Если успешно, то размер стёртой области равный тому, что передал Хост. Если произошёл сбой, то 0.

Во время стирания устройство:

  1. Стирает страницы начиная с №1 и до необходимой согласно размеру округлённому в большую сторону.
  2. После стирания каждой страницы отвечает командой SFU_CMD_ERASE_PART (“страница стёрта”).
  3. Накапливает поступающие команды в буфере приёма, но на них не отвечает и обрабатывает только после завершения стирания.
  4. Время стирания первых трёх страниц около 300 мс, последующих около 2 секунд.

пример из логов:

Хост не должен выдавать команду с таким кодом — она будет проигнорирована.
Хост во время стирания может заранее передавать данные для записи командами SFU_CMD_WRITE (“запись”) — для ускорения записи.
Но передавать команд можно не более чем размер буфера приёма устройства, иначе он переполнится и первые пакеты заменятся новыми, а последующие будут проигнорированы.

Параметры команды с Устройства:
4 байта: номер стёртой страницы начиная с №1 до №11.

Дополнительные параметры команды из Хоста:
4 байта — адрес, начиная с которого необходимо записывать содержимое.
X*4 байт — содержимое прошивки, где Х — количество 32 битных слова и должно быть: 1… 1023.

Запись игнорируется если адрес указанный Хостом не равен текущему адресу записи в устройстве.
Устройство отвечает всегда вне зависимости от того произведена была запись или проигнорирована.

Ответ устройства:
4 байта: адрес следующего для записи блока увеличивается если запись произошла успешно, не изменяется если команда проигнорирована
4 байта: кол-во необработанных данных в буфере приёма устройства (для отладки и мониторинга).

Пример ответов нескольких успешных команд «Запись» из логов:

Дополнительные параметры команды из Хоста:
4 байта: CRC32 всей записанной прошивки, начало прошивки указано в команде «Информация», конец — последнее подтверждённое устройством записанное слово командой «запись» — адрес следующего для записи блока (не включительно).

Ответ устройства:
4 байта: Адрес начала прошивки.
4 байта: Количество записанных байт (Внимание, не 32-х битных слов!), кратно четырём.
4 байта: CRC32 для сверки Хостом, вычисляется начиная с «Адрес начала прошивки», размером с «Количество записанных байт».

После этой команды Устройство проверяет CRC32 и если оно совпадает с тем что дал Хост, то запускает прошивку, выполнив полную деинициализацию оборудования.

Пример из логов:

Прошло более 500 мс с момента получения последней команды и таймаут истёк, устройство сбросилось в изначальное состояние

Без параметров.
Хост ничего не должен на неё отвечать.
Это аварийное сообщение — команда, которую выдаёт только устройство.

Запись в флеш память завершилась с ошибкой. Такое встречается если питания недостаточно или поддельные китайские чипы типа GD32F4xx.

Без параметров.
Хост ничего не должен на неё отвечать.
Это аварийное сообщение — команда, которую выдаёт только устройство.

Аппаратный сброс устройства. Устройство АППАРАТНО сбросилось в изначальное состояние — загрузчик перезапустился.

Без параметров.
Хост ничего не должен на неё отвечать.
Это аварийное сообщение — команда, которую выдаёт только устройство.

Утилита загрузки для ПК

Написана для Windows на Delphi 6 (2001 года, тот что имеет 8 битный тип char и не уникоде). Скомпилировал на Delphi XE5 и проверил работоспособность. Такой старый делфи выбран потому что мне так было проще: ещё с начала 2000-ых были большие наработки по работе с CP210x, COM портами и тд.

Работа с устройством на уровне байт выделена в отдельный поток tCOMclient не зависящий от задержек визуального интерфейса. Связь с этим отдельным потоком сделана при помощи очередей на считывание и запись размером в 65536 байт.
Уровень парсинга с оформлением команд и уровень логики работы с командами разделён на два отдельных класса tSFUcmd и tSFUboot.
Обновление прошивки производится на скорости 921600 бод, без чётности, 8 бит, один стоп бит.

Устройства можно указывать:
По имени COM порта, например COM123.
По серийному номеру записанному в CP210x
По системному пути WinNT, например \??\USB#VID_10C4&PID_EA60#GM18_E_0010#

В любом случае, если открытое устройство CP2103, то утилита может попробовать его сбросить через GPIO1 (18 пин) выставив 0-1-0.
Платка-программатор с её схемой, также приложена и описана далее.

Если запущена без параметров командной строки, то восстанавливает при запуске и сохраняет при завершения настройки из текстового файла: FastTest.exe.config
Если параметры командной строки присутствуют, то настройки из этого файла игнорируются и он не изменяется. Вместо этого настройки в визуальные компоненты берутся из командной строки и прошивка запускается если это указано.

Можно использовать следующие параметры командной строки:

  • Прошивка указывается без перфиксов
  • -DEV: Имя устройства или COM порта указывается в
  • -reset или -RST сбросить устройство если COM порт идёт через CP2103.
  • -fast Быстрое стирание, стирать только те страницы, в которых будет размещаться прошивка
  • -exit Закрыть программу по завершению если прошивка прошла успешно или указана опция -no-Errors-Keep
  • -no-Errors-Keep Закрыть программу по завершению в любом случае
  • -no-Prewrite Не посылать данные для записи во время стирания
  • -go или -run или -start Начать прошивку сразу после запуска приложения, иначе настройки применяются, но запуск не производится.

UART Программатор на базе CP2103

Выкладываю наш маленький и простенький программатор который:

  • Использует для прошивки у CP2103 UART и GPIO, всего пять проводов: GND, TX, RX, BOOT, RST.
  • Им можно шить все виды STM32 по уарту, и использовать мой загрузчик из этой статьи.
  • В нём есть защита от горячего подключения, и за всё время использования он ни разу не сгорел (пока, надеюсь. ).
  • Контакты подписаны шелкографией, что очень удобно (не забудьте заказать шелкографию в слое топ)
  • Маленький, чуть больше USB разъёма.
  • В комплекте есть утилита ConfigProg\CP2102_Enum.exe для настройки GPIO в CP2103: выбрать устройство и нажать «USB write».
  • ВНИМАНИЕ: он не развязан гальванически.

настройки должны быть такие:

Утилита FastTest из предыдущей главы как раз на этом программаторе разрабатывалась и отлаживалась.

Исходные файлы на программатор качать отсюда:
https://github.com/Mirn/ProgCP2103

Реализация прошивки загрузчика

Средства разработки и сторонние библиотеки:

  • Среда: Eclipse Kepler Service Release 1
  • Компилятор: GCC v5.4 2016q2, с настройками:
  • Линковка и компоновка при помощи файла ld скрипта с секциями.
  • Проект собирается при помощи утилиты Makefile можно собирать без среды командой make all
  • Код запуска и инициализации взят из CooIDE версии 1.6, потому что он минимальный и известный мне.
  • Библиотека работы с периферией от STM: standard peripheral library (SPL V1.3.0)

Профиль использования памяти:

  • Стек я расположил в отдельной CCM памяти 65к.
  • Основной кольцевой буфер приёма из UART в 100 килобайт расположен в отдельной секции в начале RAM
  • Все прочие буфера и переменные расположил в .bss в оставшихся 28к RAM памяти.

Библиотека usart_mini.c

Сделана максимально просто: DMA не используется, передача сделана прямой отправкой в периферию без прерываний при помощи функций SPL. Но раз основная цель ускорения протокола — непрерывный поток команд с содержимым для прошивки, то приём данных из UART сделан по прерыванию USART1_IRQHandler.

Так же в UART я реализовал контроль и учёт ошибок и проверку и коррекцию буфера при переполнении данных, если их записано больше, чем его размер.

При реализации приёма UART в прерываниях возникла проблема:
по умолчанию код находится во флеше и во время прошивки флеш памяти, шина флеша блокируется и исполнение останавливается полностью включая прерывания. И на скоростях выше 500к БОД это приводит к потере принимаемых из UART данных, т.к. время приостановки становится больше времени приёма байта. Поэтому функция обработки прерывания была вынесена в ОЗУ вот таким вот образом:

при этом есть важная тонкость, что если функция, лежащая в RAM, вызывает другие функции в флеш, то получим ошибку вида:

Это вызвано ограничением архитектуры ARM инструкций Thumb2 на максимальное адресное расстояние между вызовами. А в этом случае оно больше допустимого. Я исправил это добавив к всем вызванным из RAM функциям атрибут-модификатор long_call.

Библиотека packet_receiver.c

Принимает пакеты по протоколу описанному в этой статье и проверяет их целостность. При этом, на всех стадиях разбора пакета проверяет на наличие ошибок и подсчитывает их количество если они встретятся. Но ошибки не замалчиваются, а выводятся текстовые сообщения и раз в секунду выводится строка с всеми ошибками как UART так и уровня пакетов и случаев таймаута в 500 мс. Этот таймаут в 500 мс контролируется и вырабатывается этой же библиотекой.

Библиотека sfu_commands.с

Обрабатывает логику команд SFU_CMD_XXX как описано выше. Стирает и прошивает флеш, при этом функция прошивки слова в флеш память также вынесена в RAM, чтоб данные по приёму из UART не терялись. В неё же реализован запуск основной прошивку, при этом проверяя что её контекст указывает на реальную флеш память и RAM память. Перед запуском основной прошивки вся периферия и тактовые полноценно деинициализируются и сбрасываются.

Работоспособность прошивки проверена на моделях: STM32F405RG, STM32F405VG,
и на скоростях от 115200 до 921600 бод.

все исходники прошивки доступны на моём гитхабе по ссылке:
https://github.com/Mirn/Boot_F4_fast_uart

Оптимизация размера

В первую очередь и самое главное что влияет на размер — это общая архитектура алгоритма и использованных данных. Я старался делать всё максимально просто и, даже местами, примитивно. При этом я старался самые сложные вещи в логике перекладывать на Хост. Одним словом, порядок и краткость в коде начинается с порядка в голове разработчика.

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

Также не стоит забывать, как работает компилятор и его оптимизатор. Компилировал я естественно на -Os, но каких-то других особых ключей не использовал и даже не заморачивался с этим. Если дать побольше конкретики, то компилятор сможет получше оптимизировать: параметры подписывать const где это можно, локальные в пределах одного файла функции как static и т.д.

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

Оптимизация SPL

Standard peripheral library от STM имеет очень большой потенциал уменьшения в размере. Она написано очень просто — многие функции перекладывают данные из переданных им заполненных структур, в соответствующие регистры периферии. Эти функции не содержат внутренних статичных переменных, не обращаются к глобальным переменным и обычно не требуют указатели на какие либо хранилища состояний. Они очень редко обращаются к другим своим или чужим функциям. Но у них есть недостаток: они содержат очень много дублирующегося кода, например GPIO_DeInit проверяет равенство переданного GPIO к каждому порту GPIOA, GPIOB… GPIOI, и сбрасывает каждый порт по отдельности отдельным кодом. Т.е. там внутри действительно пачка из десяти if и двадцати RCC_AHB1PeriphResetCmd. И поэтому SPL потребляет очень много флеша. На связку UART и GPIO с RCC обычно приходится около 8 килобайт.
Поэтому я скопировал код использованных функций SPL в отдельный заголовочник, объявил их как static inline и добавил к каждой такой функции суффикс _inline, например GPIO_DeInit_inline. Так же заинлайнил все вызванные ими функции. Это сразу сократило код в разы.

Оптимизация секции .data и .ro_data

В секции .data хранятся стартовые значения переменных, которые заданы на стадии компилирования. Они размещаются в флеше, и в коде есть цикл, который их копирует при старте в RAM.
Я написал код так, чтоб таких переменных не было вообще, и не пришлось бы писать код, который вручную выставляет им нужные параметры.

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

Оптимизация printf и его тени impure_data и impure_ptr

Я взял из CoIDE готовую реализацию урезанного printf, в ней многое упрощено, а поддержки плавающей запятой вообще нет. Но она неявно использует структуру impure_data и указатель impure_ptr. Они занимают сотни байт и тянут за собой ещё много чего. Компилятор gcc скрыто от программиста помещает stderr и stdin именно в этой структуре и их и необходимо не использовать в коде.

Изначально пример printf как раз содержал stderr и stdout, их упоминания я и убрал, попутно заменив на более прямые вызовы и закомментировал ненужные опции printf. И убрал не используемые варианты вывода например строк, знаковых целых, шестнадцетиричных и тд.

Оптимизация кода запуска и удаление повторного кода

Из CoIDE я взял самый минимальный, который нашел, код запуска и первичной инициализации. Он копирует .data из флеша в RAM, запускает кварец и настраивает частоты, обнуляет .bss и настраивает процессор: стек, плавающие запятые, CCM память и тд.

Но часть этих задач уже реализованы в SPL и использованы мною. Я их заменил прямым вызовом соответствующей не инлайн функции SPLа.

Также было много повторов кода, когда, например, плавающие запятые включаются аж в трёх местах.
Прибил гвоздями SystemCoreClock к дефайну и выкинул функцию SystemCoreClockUpdate.
Код запуска использовал таблицы констант для расчётов которые хранились в RAM как volatile (интересно зачем?). Перенёс в флеш, а при оптимизации компилятор часть из них заменил на прямой расчёт (там где были степени двойки, в тридцать два слова).

Уменьшение таблицы прерываний

Таблица прерываний содержит в первых двух 32-и битных ячейках контекст исполнения: адрес кода и адрес стека. А в последующих содержит указатели на все возможные прерывания. А это почти 500 байт. Так как «Остапа понесло» и я уже не мог смириться, что кода больше 4к (привет 4к демо сцене!). То я избавился и от таблицы тем, что ужал её до первых двух ячеек. А в стартап-коде перенёс вектор на таблицу в RAM, куда добавил только один обработчик UARTа ручками таким кодом:

и поправил ld файл прописав так, чтоб секция для таблицы прерываний в RAM была выровнена как и полагается по 512 байтам

Экономия на таблице векторов вышла почти в 400 байт.

Итог и результаты

Время прошивки размером 400 килобайт.

встроенным загрузчиком по AN3155 с скоростью 256000 БОД: 95 секунд
встроенным загрузчиком по AN3155 с скоростью 500000 БОД: 78 секунд
встроенным загрузчиком по AN3155 с скоростью 921600 БОД: 70 секунд
во всех случаях с разлочкой и залочкой, с полным стиранием

моим загрузчиком со скоростью 921600 БОД: 9 секунд,
что быстрее в 8 раз.

Видео работы нового загрузчика (в начале), и старого по AN3155, запускается после нового.

Проверяем осциллографом на предмет непрерывности потока данных и отсутствия пауз по UART

или более развёрнуто один пакет:

пауз нет, поток непрерывен, ускорение в 8 раз получено.
Получилось всё что было запланировало и к чему стремились.

Это мой первый проект на гитхабе и опубликован с целью его изучения и вхождения в сообщество.
Я решил сделать не «хеллоу ворлд», а что-то реально полезное. Гитхаб для сообщества и глупо начинать с бесполезного всем проекта. Я вспомнил про то, до чего руки чесались, но из за лени много лет не доходили. И вдруг выдался повод: из-за кризиса у меня скоро возникнет просто уйма свободного времени, а что-то делать надо сейчас. В итоге так и родился этот турбо-загрузчик.

Источник

Smartadm.ru
Adblock
detector