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

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




    Оглавление


    Предыстория
    Анализ причин низкой скорости протокола AN3155
    Требования к моему загрузчику
    Описание протокола загрузчика
    Утилита загрузки для ПК
    UART Программатор на базе CP2103
    Реализация прошивки загрузчика
    Оптимизация размера
    Итог и результаты



    Предыстория


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


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


    И решение представленное в этой статье — это вторая попытка.
    Первая попытка заключалась в том, что я написал свою утилиту для ПК использующую 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 памяти тоже нет чтоб на каждый интерфейс сделать всё идеально.


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



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


    1. Фиксированная скорость UART в 921600 BOD: без стадии автоопределения скорости, которая часто сбоит.
    2. Частичное стирание выбранных страниц.
    3. Работа на максимальной тактовой частоте: использовать PLL и прошивать флеш быстрее чем приходят новые данные.
    4. Поддерживать команды с большими блоками данных, минимум 2к байта.
    5. Не должно быть никаких стадий типа активации, следует сразу выдавать одной командой всё необходимое: включая ChipID и все опции и версии.
    6. Возможность работы в залоченном чипе, без его разлочки.
    7. Проверка целостности без считывания в хост: например, отправкой и сверкой CRC всей прошивки.
    8. Использование всего ОЗУ: пока идёт стирание уже можно передать как минимум целых 100 кбайт содержимого прошивки.
    9. Не требовать ожидания подтверждения на каждую команду. Входящий поток от Хоста не должен зависеть или ожидать исходящий от загрузчика. Хост может выдавать команды на максимальной скорости. А загрузчик должен только сообщать, что последний удачно прошитый адрес такой — то. И, в случае сбоя, хост мог бы продолжить с этого адреса. Например, подобным образом работает протокол TCP.

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


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


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


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


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


    Развернуть описание протокола

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


    • Два участника: Хост и Устройство.
    • Хост прошивает Устройство.
    • Хост ведущий и инициирует обмен.
    • Устройство ведомое.
    • Обмен производится командами.
    • Команды оформлены в пакеты.
    • Порядок байт в пакетах: Little endian
    • Один пакет — одна команда.
    • Одна команда — одно действие или событие.
    • Устройство отвечает на команды Хоста с тем же кодом команды что и хост.
    • Устройство генерирует без запроса только аварийные команды о перезагрузке и таймауте.
    • Каждый пакет начинается с 32-х битной сигнатуры начала пакета.
    • Сигнатуры разные для направлений в Хост и из Устройства.
    • Остальной формат одинаковый для обоих направлений.
    • Каждый пакет защищён CRC32 в конце пакета.
    • Размер пакета в байтах должен быть кратен четырём.
    • Таймаут на общий сброс Устройства в 500мс, Хост обязан гарантировать что пауз более 500мс не будет.
    • Помимо команд можно выдавать из Устройства отладочные текстовые сообщения в голом ASCII, они не должны начинаться или содержать сигнатуру начала пакета и должны выводиться вне тела пакетов.

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


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

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


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


    • Если код команды не соответствует проверочному инверсному коду, то начало пакета не засчитывается, и поиск новой команды начинается со следующих байт.
    • Если принят пакет с размером дополнительной информации больше чем внутренний буфер, то пакет игнорируется, и поиск новой команды начинается со следующих после размера байт.
    • Если принятая в теле пакета CRC32 не равна фактически расчётной, то содержимое пакета игнорируется и поиск новой команды начинается с следующих после CRC32 байт.
    • Если приходят байты, но сигнатура начала пакета не задетектирована. То эти байты считать отладочными текстовыми сообщениями и накапливать до кода 13 (перевод строки), а после этого кода выводить в отладочную консоль.
    • Если с момента приёма последнего пакета прошло более 500мс, то загрузчик сбрасывается в изначальное состояние. Пакет, который не успел приняться до конца, игнорируется и так-же сбрасывается. О таймауте Устройство сообщает пакетом с специальным кодом команды "таймаут".
    • При запуске загрузчика генерируется другой пакет со специальным кодом "перезагрузка".

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


    1. Отвечаем на команду информации, а при сбросе или таймауте оповещаем хост об этом.
    2. Прошивка записывается последовательно с начала свободного диапазона до конца прошивки или свободного места.
    3. В устройстве содержится адрес текущего блока, который должен быть записан.
    4. По умолчанию адрес текущего блока установлен в 0, что означает, что стирание не было выполнено и запись недопустима.
    5. Команда стирания: стираем сколько запрошено округлив по размеру страницы и отчитываясь о каждой стёртой странице.
    6. По окончанию стирания адрес текущего блока устанавливается на начало свободного диапазона — можно начать запись.
    7. Команда записи: если адрес принятого блока равен адресу текущего блока, то записываем его и обновляем текущий адрес.
    8. Команда записи: если адрес принятого блока НЕ равен адресу текущего блока, то игнорируем запись.
    9. В обоих случаях 6 и 7 сообщаем в хост адрес текущего для записи блока.
    10. По окончании записи Хост должен выдать команду "Старт" с CRC32 всей прошивки, и если она верна, то прошивка запускается.
    11. Если в течении 5 секунд команд не поступало, то запустить основную прошивку если она есть.
    12. При таймауте 500мс или аппаратном сбросе адрес текущего для записи блока сбрасывается в 0.

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


    1. Получаем информацию с устройства: размер флеша, ChipID, размер буфера приёма, адрес старта, версия загрузчика и чипа.
    2. Даём команду стирания либо всей прошивки, либо части указав размер стираемой области.
    3. По завершению стирания начинаем непрерывно отправлять команды записи блоками друг за другом, проверяя что текущая записанная позиция в устройстве увеличивается.
    4. Если позиция перестала увеличиваться (приняли два ответа на команду "запись" с одинаковым адресом), то скорректировать адрес на Хосте, сбросить буферы отправки и начать передавать с нового скорректированного адреса.
    5. По завершению записи подать команду "Старт" передав CRC32 всей прошивки, в ответ Устройство сообщит фактическое CRC32.
    6. Если фактическое CRC32 равно расчётному, то загрузка успешно закончена и прошивка запущена.

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


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


    (0x97) SFU_CMD_INFO Команда: Запрос информации

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


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


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

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



    (0xC5) SFU_CMD_ERASE Команда: Стирание флеш памяти

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


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


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


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

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



    (0xB3) SFU_CMD_ERASE_PART Сообщение: страница стёрта (для опции -PreWrite)

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


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


    (0x38) SFU_CMD_WRITE Команда: Запись

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


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


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


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



    (0x26) SFU_CMD_START Команда: Старт

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


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


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


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



    (0xAA) SFU_CMD_TIMEOUT Сообщение: Истёк таймаут в 500мс

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


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


    (0x55) SFU_CMD_WRERROR Сообщение: Ошибка записи флеш памяти

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


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


    (0x11) SFU_CMD_HWRESET Сообщение: Аппаратный сброс загрузчика

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


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



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


    Внешний вид:



     
    Написана для 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#{a5dcbf10-6530-11d2-901f-00c04fb951ed}


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


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


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


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

    Можно скачать отсюда:
    https://github.com/Mirn/Boot_F4_fast_uart/tree/master/delphi/Release



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


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


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

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


    IO.Mode    = 1100001101010100
    IO.Reset   = 0000110011111111
    IO.Suspend = 0000111111111111
    IO.EnhFxn  = 10

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


    Схема

    Плата

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



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


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


    • Среда: Eclipse Kepler Service Release 1
    • Компилятор: GCC v5.4 2016q2, с настройками:
      -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Os -fmessage-length=0 -ffunction-sections -fdata-sections -ffreestanding -fno-builtin -Wunused -Wuninitialized -Wall -Wextra -Wpointer-arith -Wshadow -Wlogical-op -Waggregate-return -Wfloat-equal -Wno-sign-compare 
    • Линковка и компоновка при помощи файла 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 данных, т.к. время приостановки становится больше времени приёма байта. Поэтому функция обработки прерывания была вынесена в ОЗУ вот таким вот образом:


    __attribute__ ((long_call, section(".data")))
    void USART1_IRQHandler(void)

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


    usart_mini.c: relocation truncated to fit: R_ARM_THM_CALL against symbol `demo' defined in .text.demo section in ./src/main.o

    Это вызвано ограничением архитектуры 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


    вариант для STM32F7xx:
    https://github.com/Mirn/Boot_F745_SFU



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


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


    Но нужно соблюдать меру и не забывать о тех удобствах, которые помогают лучше понять что твориться в загрузчике, и поэтому остались отладочные сообщения, контроль и подсчёт ошибок и прочие мелочи и удобства. А так же я не стал всё лепить в одну функцию и разложил всё по полочкам, и разбил на модули. Хоть это и приводит к увеличению размера прошивки на пару сотен байт, мне её ещё много лет придётся поддерживать, развивать и на базе неё создавать новые. Ещё небольшой вклад в увеличение размера дала необходимость поместить часть функций в 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а ручками таким кодом:


    __attribute__ ((section(".isr_vector_minimal"))) void (* const StartVectors_minimal[])(void) =
    {
      (void *)&_estack,
      Reset_Handler,
    };
    
    __attribute__ ((section(".isr_vector_RAM"))) void (* StartVectors_RAM_actual[128])(void) = {0};
    
    void Default_Reset_Handler(void)
    {
    ...
      StartVectors_RAM_actual[0xD4 / 4] = USART1_IRQHandler;
      SCB->VTOR = (uint32_t)StartVectors_RAM_actual;
      main();
    }

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


        .text : { 
            KEEP(*(.isr_vector_minimal*))
            *(.text .text.* .gnu.linkonce.t.*)        
            *(.rodata .rodata* .gnu.linkonce.r.*)
        } > rom
    
        .bss (NOLOAD) : { 
            _sbss = . ;
    
            . = ALIGN(512); 
            *(.isr_vector_RAM*)
    
            *(.bss .bss.*) 
            *(COMMON) 
            . = ALIGN(4); 
            _ebss = . ; 
        } > ram
    

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



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


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


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


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


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



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



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



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


    Ещё раз ссылка на гитхаб:
    https://github.com/Mirn/Boot_F4_fast_uart


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


    Добавлено:
    Сделан вариант SFU для STM32F7xx:
    https://github.com/Mirn/Boot_F745_SFU

    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 63

      +3
      Обожаю такие статьи, автор пиши еще!
        0
        если не секрет, каким образом в Delphi происходит обращение к порту со скоростью 921600?
          +1
          при помощи WinAPI она позволяет задать явно скорость
          speed := 921600;

          fillchar(DCB, sizeof(DCB), 0);
          DCB.DCBlength := sizeof(DCB);
          GetCommState(handle, DCB);
          DCB.BaudRate:=speed;
          DCB.Parity:=parity;
          DCB.ByteSize:=8;
          DCB.StopBits:=ONESTOPBIT;
          DCB.Flags:=1;
          SetCommState(handle, DCB);

            0
            a 1 843 200 он может выдать, не пробовали?
              0
              CP210x больше 921600 не выдаст
                0
                понял, а теоретически WinAPI такую скорость позволит использовать?
              0
              Да, WinAPI творит чудеса. Сам был любителем работать напрямую через WinAPI, когда желал получить предсказуемое поведение. Отличная тёплая ламповая статья. Отдельное спасибо за бэкграунд главного окна приложения с цветом Win 9x :) Надеюсь, потенциальные работодатели оценят и променяют вашу уйму свободного времени на достойную оплату труда.
                +1

                "Вам шашечки или ехать?".
                Проблемы устаревших интерфейсов, спорной или излишней оптимизации и тд, уже решена на основной работе так:
                платится премия за продаваемые изделия, те которые разработал разработчик, до 25% с маржи (надеюсь понимаете, хотя бы это — что это минимум немало), а так же сопровождение клиентов и производства делает сам разработчик, и хоть заоптимизируйся, хоть строй "rocket science", никого не волнует.


                Главное чтоб всё работало и не вызывало возвратов оборудования. Шансы повысить зарплату только один — сделать побольше качественных разработок, а как — никого не волнует, и тут уже предпочтение выдаётся тому коду который с 2004ого года стабильно работает везде, даже под вайном кстати ;)

                  0
                  Если не секрет, что за девайсы разрабатываете?
                    +2

                    на основной работе: связь, телеком, шлюзы, исдн е1, запись разговоров, оповещатели, STM32 + altera
                    хобби: умные гаджеты, стильные, стальные, со стеклом, без тормозов, да так чтоб батарейка месяц тянула, плавно интерфейс просто летал, при этом чтоб код без переделок компилировался как под мк и винду так и под веб, т.е. то чего нет даже у аппла, чем действительно стоит гордится и что нереально ахуенно. Но об этом хобби как нибудь потом

                      +2
                      Вы бы с таким хобби на кикстартер шли. Я серьёзно.
            +1
            Скорость супер. Но зачем переход USB <->UART, есть в STM32F405 уже есть USB?
              0
              у нас это так исторически сложилось что нужен был COM порт виртуальный для связи с GSM и не на всех шлюзах был такой крутой проц с USB контроллером. Более того софтовый усб-сом порт не так просто делать как кажется. Кубовые библиотеки глючат, и не проходят моих примитивных тестов на приём/передачу большого куска данных (затыкаются на первых сотнях килобайт).
                0
                Да, про глюки кубовых либ в режиме CDC (да и вообще с USB) кто-то уже писал статью, разбирался и находил ошибки.
                  0

                  к сожалению эти статьи не помогли. и в новых версиях добавили ещё более заковыристые глюки, ноги которых растут из ерраты где workaround примерно такой: "понять и простить" в случае моего применения и моего ТЗ.

                0
                Помнится года 2-3 назад приходилось стучать в бубны чтобы вытянуть скорость usb-cdc (виртуального ком порта) до реальных 10Мб/с, положенных usb Full Speed. Интересно, уже починили?
                  0
                  Починили ли в либах, не знаю, я от CDC давно отказался, глючно. Выдернешь кабель — программа виснет, брррр. Нельзя сделать так, чтобы 2 программы использовали устройство(.
                  Если маленькая скорость, то я использую usb hid, если большая то надо писать свой драйвер.
                  Вот нашёл статью
                  https://habrahabr.ru/post/248729/
                0
                Может немного не в тему. На github не планируется выложить исходники от GSM-шлюза?
                  +1

                  Целиком не планируется.
                  Моя первая статья про шлюз про старую схемотехнику, в ней есть ряд недостатков и ряд "маячков". Это такие закладки которые у специалистов сразу вызвали бы вопросы. Но только у тех кто знаком с темой моей статьи. Таким образом я хотел получить фидбек о том что кто-то знакомый с темой прочитал вдумчиво статью. Но они или не сработали или было им лень. Поэтому и упала мотивация продолжать кому-то, что-то про шлюз рассказывать.
                  Зато отлично сработал маячок на перфекционистов и прочих любителей придраться не поделу — я просто выложил до ката фотку платы с "топорной" разводкой — ну они и набежали.


                  В этой же статьи тоже есть "маячки", в том числе такие — это упоминания делфи, винапи, ну и до кучи я натянул манифест заставляющий даунгрейдить интерфейс до винды 98, хотя при запуске видно что используются фишки современной винды включая вин 7 которые видны сразу.
                  И уже нашёлся любитель покритиковать не глядя и не запуская ничего (извини чувак, честно извени, не хотел тебя аггрить).


                  А про будущие планы:
                  Я планирую начать потихоньку выкладывать технологии из шлюза:


                  1. Например хороший и качественный помехостойки детектор модуляции бит в формате FSK, который используется везде в голосовой обработке, от АОН и DTMF набора, до детекторов тонов и полноценном модеме для диагностики шлюза.
                  2. Так же планирую описать как минимум два способа расчёта нелинейных искажений и шума. Один из них очень вкусный — не использует FFT и можно запускать хоть на атттини и простой как валенок (но в рунете не встречал, вычитал на японском про него).
                  3. Ещё можно расписать поподробнее про фильтры, в плане простой и надёжной реализации без потерь качества обработки звука и что многие считают фильтры неправильно и вносят немного но искажений и как этого избежать.
                  4. Ну и можно выложить свои наработки компактного и грамотного парсинга АТ команд на скриптах подобных регэкспу.
                  5. Можно рассказать о нововедениях и багфиксах предыдущей статьи про шлюз.

                  Если что то заинтересовало то пишите что, с удовольствием поделюсь как будет время и желание. Ну и запросившего можно наплюсовать чтоб я смог понять что больше всего заинтересовало.

                    +1
                    Есть такой проект www.jackpair.com, но похоже, что там что-то не заладилось. Думаю, что можно GSM-шлюз (часть из него) использовать как такое же устройство. По крайне мере я с модуля GSM через I2S забирал, простенько кодировал и отправлял другой стороне. Но много времени заниматься нет — поэтому хотел часть из Вашего GSM-шлюза попользовать (как я понял из часте 1,2 там есть что).
                      0
                      Если этот чувак, критикующий про Delphi, винапи и Win9x — я, то тут мимо по всем пунктам. Манифест, включающий стиль оформления Win 7, я заметил. Именно поэтому заметил и фон Win9x, поскольку если его намерено не выставить, его бы не было. Дельфи — это инструмент. И инструмент для подобного софта более чем рабочий. Винапи я и подавно критиковать не собирался, а вовсе даже наоборот. Если вас задела фраза «потенциальные работодатели оценят и променяют вашу уйму свободного времени на достойную оплату труда», то мне из последнего абзаца показалось, что в связи с кризисом вы вынуждены искать работу, отчего и пожелал вам счастливо её найти. А про фильтры и способы расчёта искажений и шума без FFT я бы почитал.
                        0

                        извиняюсь. уже поздно. устал и невнимательно стал читать.


                        А про фильтры и способы расчёта искажений и шума без FFT я бы почитал.

                        что именно интересует? на что упор делать? на реализацию? кросплатформенность? или на метрологию (сравнение двух способов, и тд)?
                        могу в выходные написать. но времени не много (если будет) и поэтому объёма будет мало и смогу только что-то одно показать.

                          +1
                          Кроссплатформенность можно опустить. А реализацию и сравнение стоит затронуть.
                    +1
                    Эх, сделал бы кто красивый AT&T парсер с нормальной обработкой GPRS включения. Свой уже полгода пилю — сколько глюков — ужас)
                      0

                      хех. Я изначально хотел выложить другой вариант загрузчика, GPRS размером в 10к, с всем лексическим феншуем: нормализатор одиночных символов, последовательностей символов, выделитель слов и фраз, преобразователь hex строк и чисел в значения переменных, с форматом похожим на регексп, очень простой в использовании. Ну и с толикой аппаратной чёрной магии когда аппаратура МК сама пишет в класс локфрии очереди.
                      Мне показалось всё это через-чур сложным и у меня проблемы с графоманством, да и разбить на несколько частей и выкладывать по частям тоже не удобно — быстро мотивация пропадает и забиваю посередине. Может привыкну к хабру и научусь нормально и кратко писать статьи тратя мало своего времени, тогда и опубликую и свои наработки.


                      Но есть вопрос: в чем проблема GPRS? я загрузчик делал как вспомогательный и особо на него времени не тратил, и случаев включения его было буквально пара штук клиентами и поэтому саму логику включения GPRS я не прорабатывал как следует.
                      Поэтому очень буду благодарен если поделитесь своими проблемами.

                        0
                        Я тоже использую загрузчик через GPRS (вернее через http), кусочная загрузка, сохранение либо во внешнем фдэш, либо во внутреннем. После полной закачки, прошивает. И на STM32 и на EFM32 — проблем никаких нет там.
                          0
                          Возможно я не совсем понятно выразился. Вот есть у меня UART стек в ядре, к примеру. Поверх него кладется более высокоуровневый стек, например, GSM, GPS и так далее. С GPS все просто. Всё строго определено стандартами.
                          С GSM все сложнее. Мало того, что у разных производителей (не одним SIMcom мы живы) команды отличаются, так что самое важное — нет четкого способа отличить конец команды, служебное сообщение. Ну вот например, что я пытаюсь делать. Давно уже не писал — времени не хватает. Функция
                          bool Modem::wait_for_reply(CMD::ATCMD cmd, ATRESPONSE expected, word timeout)
                          

                          https://github.com/Pugnator/CoreTex/blob/HAL/firmware/src/drivers/wireless/gsm/atcmd.cc

                          Это как-то работает, и то неоптимально. Но как быть при переключении в GPRS? Как отделять? как ждать.
                          Была бы статья. где на пальцах рассказывалось. как правильно ждать и обрабатывать AT команды, как ожидать ответа и прочее.
                          Описания команд есть в даташитах, да. Но многие команды не возвращают OK или вообще что-либо. А хардкодить — некрасиво и неправильно. Хочется нормального парсера. Не бизон же с флексом прикручивать.
                        0
                        больно уж долго он прошивает: около двух минут, особенно когда отлаживаешь схемотехнику, а не свой код.
                        При этом обычно вносишь минимальные правки и дольше ждёшь когда прошьётся, успеваешь отвлечься и тд.
                        Да и не дело терять за день более четырёх часов на прошивку изделия, притом что порой разработка длится не один месяц.

                        А почему нельзя использовать нормальный SWD/JTAG программатор?
                          0
                          1. Чтоб прошивка не утекла мы всегда держим чипы залоченными. Залочка не просто отключает SWD, а при попытке обращения по SWD превращает чип в кирпич пока не выключишь питание.
                          2. Он тоже далеко не 10 секунд прошивает, а в ряде плат на этих ножках стоят нехилые кондёры и SWD заведётся на очень маленькой скорости.
                            0
                            а возможность слития дампа залоченного чипа через заниженное напряжение питания уже устранили?
                            просто последний раз с таким железом в 2004-2006 году возился и там дампы снимались только в путь.
                          0
                          А это может работать на STM32F1?
                            0

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

                            0
                            Простите, я не специалист, но правильно ли понял, что вся прошивка сначала грузится в ОЗУ МК, сверяется контрольная сумма и бутлоадер прошивает флеш сразу целиком? Или все происходит параллельно — бутлоадер и принимает пакеты, и сразу же их пишет, а ОЗУ используется как некий буфер?
                              0

                              второй вариант,
                              но содержимое прошивки отправляется уже на стадии стирания до полного заполнения буфера (экономия ещё в 2 сек).
                              Но как стирание прошло начинается запись и по мере освобождения озу на мк, прога прошивки докидывает ещё данные и на этой стадии процесс идёт уже параллельно, мк прошивает и рапартует о том что до такого то адреса прошито и настолько занят буфер мк, и одновременно принимает новые данные, т.к. хост видит что сколько ещё можно дозакинуть.
                              Смотри на видео справа команду WR 0x080xxxxx yyyyy где 0x080xxxxx — адрес до которого дописал, и yyyy — сколько занято озу.
                              На видео же заметно что идёт рывок в начале (мк прошивает быстрее чем принимает новое), а под конец скорость падает.

                              0
                              Прошивка (full erase/flash/verify) STM32F107 через USB-bootloader занимает 3-5 секунд. Использую DfuSe USB firmware, согласно мануалу UM0412. Ничего против представленной статьи не хочу сказать. Это всегда полезно и увлекательно пилить мозги таракана начиная с while(1) до 0x2E 0xFE, но если задача ускорения времени прошивки, то ST для этого все уже сделали.
                                +1
                                Выше в комментах уже написали, что у 32F1xx флеш устроен по-другому и шьется быстрее. А в этой статье — для STM32F4xx.
                                  0
                                  Не освоился еще на хабре, поэтому прошу заглянуть чуть ниже, в мой комментарий со сведениями из ТУ на оба семейства. Не все так однозначно.
                                  0

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


                                  Это всегда полезно и увлекательно пилить мозги таракана начиная

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

                                  +1
                                  1. По поводу оптимизации STD_Periph_Lib — согласен. Но выпиливание векторов — как Вы правильно подметили — «Остапа понесло»;) Единственное что я бы лишний раз обходил бы стороной — оптимизация RAM. Может не совсем понял, что Вы там сделали, но речь же идет о хитрейшей архитектуре Cortex M4.Пусть компилятор делает так, как ему нравиться, а то можно запросто нарваться на возрастание времени исполнения кода, а если еще и есть атомарные операции с прерываниями — это вообще может плохо закончится.

                                  2. Раз уж пошла такая песня. Почему не DMA? Он как раз для этих целей как никак лучше других справится.

                                  3. И по поводу времени прошивки, позвольте не согласится. Время прошивки 32-битного поля у 405/7 почти в 6 раз быстрее чем у сотки. Время посекторного стирания у 405/7 — от 7 до 25 милисекунд/килобайт. У сотки тоже между 20 и 40 мсек/кБ. Единственное что значительно дает преимуществ — mass erase. Тут разница несоизмерима в пользу сотки.
                                    0
                                    спасибо что читали статью

                                    1-2. Насчёт того почему функции в RAM и почему не DMA — это просто мой косяк и нежелание баг понормальному затыкать: я почти всё сделал но на 500кбод, почти всё протестировал и под конец решил макс скорость проверить и тут и заглючило, и опять вспомнил что встал на свои грабли — опять про время стирания флеша забыл совершенно. ну т.к. хотелось уже приступить и к статье и вообще больше 2 недель не тратить то просто закостылячил вынеся функции в RAM что существенно проще и быстрее. вот такой вот ляп и такой вот костыль.

                                    3. хоть прошивается каждое слово и быстрее но их просто больше и постраничное стирание тормозит существенно — около 25-30 секунд, но надо его делать дважды из за локов и нашей политики безопасности (которую либо полностью соблюдаем, либо не мучаем ни себя, ни чип).
                                      0
                                      Вам спасибо за статью. Всегда интересно познакомиться с опытом специалистов в решении подобного рода задач. Статья как никак кстати моему текущему проекту. Пишу программатор на STM32F107 для STM8S003 посредством программно-аппаратной реализации SWIM интерфейса в 32-битном мк. По сути, тот же путь прохожу, только без штатного бутлоадера в конечном устройстве. Можно конечно купить дешевый китайский STLink, но это же не наш метод;) А так, посмотрев на Ваш пример, может и сам попробую на Хабре поделиться своим опытом.
                                    0
                                    сделал порт моего быстрого загрузчика под STM32F745,
                                    на новом HAL версии июль 2016, а не на старом SPL.
                                    Пока без оптимизации по размеру (вышел в 12к)
                                    и возможно не вся периферия деинициализируется правильно,
                                    лучше быть внимательным.

                                    если кому интересно, то могу опубликовать на гитхабе.
                                    пишите или плюсуйте — мне интересно кол-во реквестов
                                      0
                                      Только UART?
                                      в любом случае интересно, публикуйте.
                                      кстати SPL на STM32F7xx вроде и нет.
                                        0
                                        извиняюсь за задержку,
                                        вот опубликовал в вариант SFU для STM32F7xx
                                        только сейчас руки дошли и довёл код до такого уровня чтоб показать другим было не стыдно.
                                        Но для F745 вышло куда больше объём (11кб) т.к. используется офф. STM32 HAL без изменений,
                                        потому что он сложнее чем SPL и его инлайнить и оптимизировать не самая хорошая затея,
                                        а делать свой уровень аппаратной абстракции тоже сомнительно,
                                        так-что пусть будет так — в первую страницу 16к влазит и отлично.
                                          0
                                          Ок, спасибо.
                                      0
                                      Не смог найти в main_start проверки кода с MAIN_RUN_FROM кроме первых двух слов перед переходом на 0х8010000. Пропустил или так задумано?
                                        0
                                        а какую ещё проверку надо?
                                        кроме того что стек указывает на рам, а точка входа на флеш, мне этого показалось достаточным, обычно никто даже этого не проверяет, ни JTAG ни SWD загрузчики ни стмовский встроенный не проверяют, просто запускают и всё.
                                        Но если есть ещё способы и проверки, то мне интересно какие?
                                          0
                                          Я использую котрольную для всей прошивки. Но это зависит от применения устройства.
                                            0
                                            После прошивки с ПК приходит команда SFU_CMD_START в теле которой содержится одно 32 битное слово — CRC32 для всей прошивки.

                                            Если переданное CRC32 совподает с расчётной, то прошивка запускается, иначе возвращает в ПК ошибку.
                                            Эта же команда выдаёт в ПК три 32 битных слова: с какого адреса, сколько байт, и их CRC32 расчётное.
                                            Это сделано для логов и чтоб видеть что не сошлось, один байт или много или несовпадение адреса начала / кол-ва.
                                            в логе команда утилиты ПК всё это наглядно видно
                                              0
                                              Я имел ввиду при старте — без прошивки. Ещё для надежности последняя прошивка сохраняется во флэш (если конечно место позволяет) и если основная прошивка испорчена, то перепрошивается из копии (там же сохраняется новая прошивка, если изменение по GPRS или RF). Это конечно не сильно относится к теме. Хорошая статья по преимуществам асинхронного обмена, очередей и использованию прерываний.
                                                0
                                                это пока не сделано. да надо бы сделать.
                                                Но как я писал в статье я решал только проблему быстрой отладки схемотехники.
                                                И поэтому в отладочной версии у меня есть код, который запускает её только один раз.
                                                Второй запуск будет сразу остановом с миганием всех красных светодиодов и выдачей в терминал сообщения что это отладочная версия.
                                                Поэтому проверка целостности не сделана именно при запуске по таймауту если обновление прошивки не началось — отладочная версия при повторном запуске всё равно работать не будет.
                                                Для остальных случаев у меня есть остальные загрузчики: AN3155 с шифрованием, gprs, звуковой FSK модем — отсюда и возникла необходимость впихать в оставшиеся 5-6кб от 32к уместить этот быстрый новый. Отсюда такая раскладка памяти и получилась.
                                                  +1
                                                  В любом случае пост полезный. Успехов и не забывайте про github!
                                        0
                                        А почему Delphi 6? Delphi 5 и 7 имеют намного меньше ошибок в VCL. В шестерке делали переход по kylix и немало багов насажали.
                                          0

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

                                            0
                                            Если лицензия позволяет даунгрейд до Delphi 5 — лучше переползайте на пятерку. Будет стабильней работать.
                                          0

                                          Здравствуйте!
                                          Под какую IDE ваш проект?
                                          FLASH_ProgramWord_inline(wr_addr, *data) — в каком файле у вас реализована эта функция? Что-то я ее не могу найти. В моем бутлоадере именно функция запиши флеш памяти блокирует время.
                                          И еще, ваша программа FastTest.exe на моем экране 1080х1920 не помещается и меньше не делается, нет доступа к кнопкам внизу программы.

                                            0
                                            Eclipse IDE for C/C++ Developers
                                            Version: Kepler Service Release 1
                                            Build id: 20130919-0819

                                            список плагинов
                                            Eclipse IDE for C/C++ Developers
                                            GNU ARM C/C++ Cross Compiler Support
                                            GNU ARM C/C++ STM32Fx Project Templates
                                            остальные плагины это украшательство, гитхаб, расчёт резисторов и таймеров, адаптер к кубу и тд

                                            FLASH_ProgramWord_inline находится в src/cmsis_lib/include_inline/stm32f4xx_flash_inline.h
                                            Поэтому я скопировал код использованных функций SPL в отдельный заголовочник, объявил их как static inline и добавил к каждой такой функции суффикс _inline, например GPIO_DeInit_inline. Так же заинлайнил все вызванные ими функции. Это сразу сократило код в разы.

                                            А по сути это СТМовский SPL переделанный под инлайн, причины так же описаны в статье.

                                            1080х1920? альбомная ориентация экрана и кнопки снизу не помещаются? извиняюсь, я не понял, можно скрин всего экрана?

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

                                            при этом обработчик прерывания уарта буферирует данные в озу, а пк сообщается размер буфера, пк обязан сам расчитывать кол-во записанных данных в этот буфер и следить за чтоб он не был переполненым.
                                            0

                                            Спасибо за ответ. Есть еще вопрос. Ваш способ прошивки годится только для F4 серии или для других серий тоже? На STM32F103 пробовали?

                                              0
                                              у меня на гитхабе есть публичные порты этого исходного кода под stm32F7xx и под stm32H743. Под младшие делать смысла не было т.к. те которые мы использовали были достаточно быстры.
                                                0

                                                Мой (usb/uart) бутлоадер для L1 занимает 16K из 128K (12%) и пишет со скоростью 82 KB за 1:24 мин.
                                                Сначала стираются все страницы, далее передаются блоки по 256 байт (страница), после записи и отправки подтверждения передается следующая страница. Снимается блокировка страницы один раз перед записью 256 байт и блокируется в конце. Вся задержка на 306 строке, если ее закоментировать, задержки нет. Эта одна команда (запись 32 bit во flash) блокирует программу на 233,7 ms / (256/4) = 3,7 ms.


                                                image

                                                Объясните, пожалуйста, как вы обошли аппаратное ограничение записи во flash.

                                                  0
                                                  «аппаратное ограничение записи во flash.» если Вы про сильные тормоза на прошивку одного слова, то в случае F7 там были микросекунды которые мешали UART т.к. он был без дма и время блокирования флеша было больше чем время приёма двух символов и буфер уарта переполнялся.
                                                  То это я обошёл тем что закинул весь код в RAM. и код перестал быть зависимым от шины Flash
                                                    0

                                                    Ок.

                                              0

                                              Насчет размера окна не правильно написал (1920х1080). Мой косяк, у меня на Windows 10 было включено масштабирование ярлыков 150%. Поменял на 100%, все помещается.

                                              Only users with full accounts can post comments. Log in, please.