Формат hex файла stm32
Многие наверняка замечали, что большинство файлов прошивок для различных контроллеров, процессоров, микросхем памяти и прочих подобных вещей хранится либо в бинарных файлах, в которых информация записана именно в том виде и в том порядке, в котором она записана в памяти железки (т.е. это тупо дамп памяти), либо в файлах с расширением hex. Вот в этой статье мы и расскажем о том, что же это за формат и зачем он вообще нужен.
Для начала разберёмся — чем же оказались неудобны бинарники? Во-первых, в таблице ASCII некоторым кодам соответствуют непечатные символы, соответственно, бинарный файл не может быть целиком просмотрен или распечатан в текстовом режиме. Во-вторых, прошивка редко занимает целиком всю память железки, а бинарник — это, как уже было сказано выше, дамп памяти целиком. И ладно, если бы вся полезная информация всегда располагалась, например, в начале файла, пустое окончание можно было бы от бинарника просто отрезать. Но нет, чаще всего информация в бинарнике расположена не одним куском, а находится в различных частях файла и между кусками с полезной информацией расположены пустые места, которые и хранить и распечатывать особого смысла нет.
Почесав репу над этими двумя недоразумениями, джедаи из Intel придумали формат hex или Intel-hex, ставший впоследствии стандартом де-факто для записи всяких разных прошивок. Мне больше нравится говорить Intel-hex, поскольку в этом случае не возникает путаницы и сразу понятно, что речь идёт об информации в файлах *.hex, а не просто о представлении данных в шестнадцатиричном виде. Ну ладно, вернёмся к проблемам Intel и к их решению.
Проблему с непечатными символами решили очень просто, — в Intel-hex формате двоичные данные, представленные в шестнадцатиричном виде, записываются символами ASCII. Например, число «00111111» в шестнадцатиричном виде равно «3F» и в формате Intel-hex будет записано двумя символами: «3» и «F».
Не на много сложнее оказалось и решение проблемы с пустыми местами. В *.hex файлы решили писать не всё подряд, а только полезные данные (т.е. пустые места бинарника решили не писать). Но в этом случае нужно было кроме самих данных ещё и как-то указывать адреса, по которым эти данные расположены. Окей, стали писать ещё и адреса.
Далее добавили ещё данные о точке входа, ну чтоб можно было записанную таким образом программку сразу и исполнять, и придумали разбивать всю информацию на специальные блоки, называемые «записями», чтоб отличить где что записано: где данные, где адреса, где точки входа. Вот, собственно, из этих записей и состоит весь *.hex файл.
Записи бывают следующих типов:
- Data Record (данные); для всех форматов данных
- End of File Record (конец файла); для всех форматов данных
- Extended Segment Address Record (расширенный адрес сегмента); для 16- или 32-битного форматов данных
- Start Segment Address Record (начальный адрес сегмента); для 16- или 32-битного форматов данных
- Extended Linear Address Record (расширенный линейный адрес); только 32-битного формата данных
- Start Linear Address Record (начальный линейный адрес); только для 32-битного формата данных
Все записи имеют следующий формат:
RECORD MARK ‘:’ | RECLEN | LOAD OFFSET | RECTYPE | INFO or DATA | CHKSUM |
1 byte | 2 bytes | 1 byte | n bytes | 1 byte | |
1 ASCII | 2 ASCII | 4 ASCII | 2 ASCII | 2*n ASCII | 2 ASCII |
В данном случае:
- RECORD MARK — метка начала записи, всегда ‘:’ (в шестнадцатиричном виде 3Ah)
- RECLEN — число байт информации или данных, следующих за полем RECTYPE. Помните, что в Intel-hex формате один байт данных записывается двумя символами ASCII. Максимальное значение этого поля — ‘FF’ (в шестнадцатиричном виде 4646h)
- LOAD OFFSET — 16-ти битное начальное смещение данных. Поскольку это поле используется только в записях данных, то в остальных записях оно кодируется как четыре ASCII символа нуля (‘0000’ или в шестнадцатиричном виде 30303030h)
- RECTYPE — поле, определяющее тип записи. Может принимать следующие значения:
- ’00’ — Data Record
- ’01’ — End of File Record
- ’02’ — Extended Segment Address Record
- ’03’ — Start Segment Address Record
- ’04’ — Extended Linear Address Record
- ’05’ — Start Linear Address Record
А теперь о некоторых типах записей подробнее:
Extended Linear Address Record RECORD MARK RECLEN LOAD OFFSET RECTYPE ULBA CHKSUM ‘:’ ’02’ ‘0000’ ’04’ 2 bytes 1 byte Эта запись используется в 32-битных прошивках для определения битов 16-31 линейного базового адреса (LBA), при этом биты 0-15 равны нулю. Сами биты 16-31 называются верхним базовым адресом (ULBA).
Абсолютное значение адреса байта данных в памяти получается добавлением LBA к смещению, вычисленному сложением поля LOAD OFFSET в последующих записях данных и индекса байта в поле DATA этих записей. Все суммирования делаются по модулю 4G, таким образом мы получаем циклический (от FFFFFFFFh происходит переход к 00000000h) 4-х гигабитный (4G=2 32 ) линейный адрес (Linear Address).
ByteAddr=(LBA+DRLO+DRI) mod 4G, где
DRLO — значение поля LOAD OFFSET в записи данных
DRI — индекс байта в поле DATA записи данных
Когда запись «Extended Linear Address» встречается в файле, — заданный с помощью неё линейный базовый адрес (LBA) действует для всех последующих записей данных, пока не встретится новая запись «Extended Linear Address». По умолчанию LBA=00000000h.
Extended Segment Address Record RECORD MARK RECLEN LOAD OFFSET RECTYPE USBA CHKSUM ‘:’ ’02’ ‘0000’ ’02’ 2 bytes 1 byte Эта запись используется для определения битов 4-19 базового адреса сегмента (SBA), при этом биты 0-3 равны нулю. Сами биты 4-19 называются верхним адресом сегмента (USBA).
Абсолютное значение адреса байта данных в памяти получается добавлением SBA к смещению, вычисленному сложением поля LOAD OFFSET в последующих записях данных и индекса байта в поле DATA этих записей. Сложение LOAD OFFSET и индекса выполняется по модулю 64K, таким образом мы получаем циклический (от смещения FFFFh происходит переход к 0000h) 64-х килобитный (64K=2 16 ) адрес в заданном сегменте.
ByteAddr=SBA+[(DRLO+DRI) mod 64K], где
DRLO — значение поля LOAD OFFSET в записи данных
DRI — индекс байта в поле DATA записи данных
Когда запись «Extended Segment Address» встречается в файле, — заданный с помощью неё базовый адрес сегмента (SBA) действует для всех последующих записей данных, пока не встретится новая запись «Extended Segment Address». По умолчанию базовый адрес сегмента (SBA) равен нулю.
Start Linear Address Record RECORD MARK RECLEN LOAD OFFSET RECTYPE EIP CHKSUM ‘:’ ’04’ ‘0000’ ’05’ 4 bytes 1 byte Эта запись используется для указания адреса, с которого начинается исполнение объектного файла. Значение поля EIP определяет адрес, который заносится в регистр EIP процессора. Отметим, что эта запись определяет только адрес точки старта кода в пределах 32-х битного линейного адресного пространства защищённого режима процессора 80386. В реальном режиме для определения точки старта должна использоваться запись Start Segment Address Record, поскольку она описывает содержимое пары регистров CS:IP, необходимое для реального режима.
Запись «Start Linear Address» может быть расположена в любом месте файла, если же такой записи нет, то загрузчик использует адрес старта по умолчанию.
Start Segment Address Record RECORD MARK RECLEN LOAD OFFSET RECTYPE CS:IP CHKSUM ‘:’ ’04’ ‘0000’ ’03’ 4 bytes 1 byte Эта запись используется для указания адреса, с которого начинается исполнение объектного файла. Значение поля CS:IP определяет 20-ти битный адрес, заносимый в регистры CS:IP процессора. Отметим, что эта запись определяет только адрес входа в 20-ти битном сегментированном адресном пространстве процессоров 8086/80186.
Запись «Start Segment Address» может быть расположена в любом месте файла, если же такой записи нет, то загрузчик использует адрес старта по умолчанию.
Когда дописывал прошлую статью, на радиокоте наткнулся на тему где обсуждали в каком формате передавать прошивку бутлоадеру. У меня тоже возникал такой вопрос и в конце прошлой статьи описал почему передавать прошивку удобнее hex файлом, чем бинарником, кому интересно могут почитать тут.
Значит так, нам надо рассмотреть какие бывают типы записей в hex файле и дополнить код из прошлой статьи. Все описанное ниже справедливо для KEIL и по сути является вольным переводом этой статьи.
Keil генерирует прошивку в формате Intel HEX, в нём хранится шестнадцатеричное представление двоичного файла закодировано с помощью цифробуквенных символов ASCII. В файле с расширением hex может быть любой количество записей, каждая из которых имеет следующий формат:
- : начало записи
- ll количество данных dd в записи
- aaaa адрес начала записи
- tt тип записи:
- dd — данные которые пишутся в память МК
- сc — чек сумма
Запись с данными.
- 10 количество байт в записи
- 2462 адрес, начала записи
- 00 тип записи
- 464C. 464C данные
- 33 чек сумма записи
Напомню, что данные в hex файле хранятся в шестнадцатеричном представлении и количество байт в записи равняется 16, а не 10 как можно было бы подумать 0x10 = 16. Это относится ко всем остальным полям.Последняя запись в файле.
- 00 количество байт в записи
- 0000 в данной записи это поле игнорируется.
- 01 тип записи
- FC чек сумма записи рассчитывается как
01h + NOT(00h + 00h + 00h + 01h).
Файл формата Intel HEX должен заканчиваться записью такого типа.Дополнительный адрес.(HEX386)
- 02 количество байт в записи
- 0000 адресное поле, для данного типа записи всегда равно 0000
- 04 тип записи
- FFFF старшие 16 бит адреса
- FC чек сумма записи рассчитывается как
01h + NOT(02h + 00h + 00h + 04h + FFh + FFh).Дело в том, что в записи с данными указывается только 16 бит адреса, например 0x2462, а адресное пространство у нас 32-битное. Получается для записи полного адреса не хватает ещё 16 бит, которые как раз и содержатся в этой записи. Для вычисления полного адреса, по которому будут писаться данные, надо адрес из этой записи сдвинуть на 16 бит влево и прибавить к нему адрес указанный в записи с данными.
Оставшиеся два типа записей нам не понадобятся. Запись типа 02 вообще не встретил в своем хексе, она используется в формате HEX86, а у нас, судя по всему, HEX386(чем они отличаются не разбирался). А запись типа 05(адрес начала приложения, а именно функции main) включает в себя 32-битный адрес и может находиться где угодно, а главное, она не несет полезной информации для записи флэш памяти, то есть её можно игнорировать, так написано в документации.
Теперь пользуясь полученными знаниями, обновим код из прошлой статьи.
Таким образом, мы разработали шаблон для написания собственного бутлоадера, который разбирает hex.
Получен он из бутлоадера, которым пользуюсь сам, заменой функции, читающей определенное количество символов из hex файла следующим выражением /*читаем n байт*/. Конечно, его можно оптимизировать и это не готовое решение, но на то чтобы с этим разобраться было потрачено несколько недель и выложено это с целью экономии твоего времени мой дорогой читатель)))Кстати, при компиляции прошивки, которая будет скармливаться бутлоадеру, надо указать, что она должна располагаться не с начала памяти, а после бутлоадера.
Так как тема достаточно обширная один момент был упущен, а именно перенос векторов прерываний при переходе в основную программу. Дело в том, что в бутлоадере таблица векторов прерываний располагается по одному адресу, а в основной программе по другому(у них не может быть общий вектор прерываний так как это две отдельные программы и отдельно компилируются). Для того, чтобы при переходе в основную программу мы могли работать с векторами прерываний их надо перенести либо перед прыжком в основную программу, либо в начале основной программы.В одном из проектов использовал следующий механизм для управления бузером, включал его вручную, запускал таймер и выключал в прерывании, которое возникало при переполнении таймера. Со временем количество прерываний в проекте выросло и часто другие прерывания не давали сработать прерыванию, в котором выключался бузер. В результате чего, бузер всегда издавал разный по длительности звуковой сигнал.
Протокол I2S, не путать с I2C, предназначен для передачи аудио потока. В моем устройстве этот протокол используется для передачи аудио потока между кодеком и МК. Сразу хотелось бы отметить, что как такового отдельного модуля I2S у STM32 нет, а тот что есть реализуется на основе модуля SPI и тот же регистр DR[0:15] у них общий.
В последнее время все чаще натыкаюсь на негативные отзывы о шине I2C у STM32, мол работа с ней это танцы с бубном и тд. За последний месяц мне удалось запустить две микросхемы, работающие по I2C и ни каких танцев, только вдумчивое чтение даташита.
Думаю постоянные посетители сайта заметили, что предпочитаю все делать на регистрах, но иногда все таки использую SPL. Например, в статье про самописный бутлоадер, использовались библиотечные функции для работы с флэш памятью. В очередной раз, подключая эту библиотеку, решил, что она занимает много места и решил самостоятельно реализовать необходимые функции. Для работы с флэш памятью.
Начнем с того, что RTC — это аббревиатура которая расшифровывается следующим образом Real-time clock или по-русски, часы реального времени. В былые времена, при использовании МК AVR в качестве RTC, использовал отдельную микросхему, общение с которой происходило по определенному протоколу. У STM32 RTC же представляет собой модуль, реализованный внутри МК. У STM32 RTC обладает следующими.
В одной из прошлых статей описывал, как подключить дисплей к микроконтроллеру семейства STM32, используя интерфейс LTDC. Статья оканчивалась видеороликом, в котором на встроенный в DISCOVERY дисплей, выводилось изображение. Конечно же следующим желанием было запустить, что-то побольше и подключить это самостоятельно, чтобы в голове сложилась полная картина. С Китая был заказан 7-дюймовый дисплей.
При переносе одного из проектов с сотой серии на трехсотую столкнулся с проблемой, посылки SPI всегда были длиной 16 бит, а мне нужно было 8. Что интересно у сотой серии для настройки длины пакета SPI выделен один бит DFF(Data frame format) , когда он сброшен длина посылки 8 бит, когда установлен 16. В трехсотой серии под это дело выделили битовое поле длиной 4 бита и длину посылки можно.
Около полугода назад один из постоянных посетителей сайта отправил мне отладочную плату STM32F429I DISCO. На мои рассказы, что она мне не нужна, он ответил, что это подарок и его следует принять. А ещё он где-то вычитал, что оперативка на плате нужна для работы дисплея и интересовался у меня для чего именно, но ответа на этот вопрос у меня не было. Понятно было, что оперативка выступает в.
В одной из прошлых статей уже описывал, что такое DMA и приводил пример его работы в связке с АЦП. В том примере, результаты преобразования АЦП записывались в буфер без участия ядра. В этот раз мне надо было перенести буфер из одного участка памяти в другой и я точно знал, что это можно сделать с помощью DMA используя режим MEM2MEM.
В одной из прошлых статей рассказывал про режим захвата у STM32 и приводил пример как с его помощью измерить период сигнала. Но таким способом не получится ТОЧНО измерить период высокочастотного сигнала.(дальше станет понятно какой сигнал считать высокочастотным) Предположим, что частота исследуемого сигнала 1MНz, а наш таймер отсчитывает 72млн в секунду. Переведем значения частоты в период.
Когда дописывал прошлую статью, на радиокоте наткнулся на тему где обсуждали в каком формате передавать прошивку бутлоадеру. У меня тоже возникал такой вопрос и в конце прошлой статьи описал почему передавать прошивку удобнее hex файлом, чем бинарником, кому интересно могут почитать тут. Значит так, нам надо рассмотреть какие бывают типы записей в hex файле и дополнить код из прошлой статьи. Все.
Путь к инструменту: ваш путь установки \ MDK \ ARM \ ARMCC \ bin
Сгенерируйте шестнадцатеричный файл командой: fromelf --i32 --output Test01.hex Test01.axf
Сгенерировано дизассемблированием fromelf -c --output Test01_c.txt Test01.axf
Файл bin - это чистые двоичные данные, без специального формата
hex - это формат файла, разработанный Intel, который использует текст ASCII для записи машинного кода или постоянных данных. Этот файл часто используется для записи данных, которые будут храниться в ПЗУ (Flash). Большинство загрузчиков поддерживают этот формат.
Шестнадцатеричный файл состоит из нескольких записей, и каждая запись состоит из пяти частей, формат такой: ": llaaaatt [dd . ] cc", например, вновь созданное шестнадцатеричное значение:
":": двоеточие используется в начале каждой записи для обозначения начала записи;
ll: выражает длину области основных данных этой записи в шестнадцатеричном числе (то есть длину [dd . ] позади);
aaaa: указывает, что содержимое этой записи должно храниться по начальному адресу во FLASH;
tt: указывает тип этой записи. Она содержит различные типы:значение tt Типичный представитель 00 запись данных 01 Конец этого документа 02 Расширенная адресная запись 04 Расширенная линейная запись адреса (указывающая, что следующие записи увеличиваются на этот адрес) 05 Указывает начало линейной записи адреса (применимо только к ARM) Dd: указывает один байт данных, в записи может быть несколько байтов данных, область ll указывает, сколько байтов данных в ней;
cc: указывает контрольную сумму этой записи, которая является дополнением суммы всех предыдущих шестнадцатеричных данных (кроме двоеточия, два как группа) к результату операции 256 по модулю кодНапример, первая запись вышеуказанного шестнадцатеричного
(1) 02: указывает, что длина области данных этой записи составляет 2 байта;
(2) 0000: указывает адрес, где эта запись будет храниться;
(3) 04: указывает, что это запись расширенного линейного адреса;
(4) 0800: поскольку это расширенная запись линейного адреса, эта часть представляет старшие 16 бит адреса в сочетании с предыдущим "0000", чтобы указать линейный адрес, который будет расширен. Это «0x0800 0000», который является первым адресом внутренней флэш-памяти STM32;
(5) F2: означает контрольную сумму, ее значение равно (0x02 + 0x00 + 0x00 + 0x04 + 0x08 + 0x00)% 256 значения, а затем дополнение.Разница и подключение файлов hex, bin и axf
Файлы bin, hex и axf содержат коды инструкций, но их информационная насыщенность различна.
Файл bin - это изображение с наиболее прямым кодом. Записываемое в нем содержимое - это двоичные данные, которые должны храниться во FLASH (машинный код - это, по сути, двоичные данные). Это то, в какой форме во FLASH, без какой-либо вспомогательной информации, включая большие и малые значения. Формат отсутствует, поэтому загрузчик должен иметь вспомогательные файлы для платформы FLASH микросхемы для нормальной загрузки (обычно программа-загрузчик будет иметь соответствующую информацию);
Hex-файл - это запись кода, выраженная в шестнадцатеричной системе счисления, которая записывает адрес FLASH, где должен храниться код, и загрузчик может помочь загрузке в соответствии с этой информацией;
Axf файл, он не только содержит данные кода, но также содержит различную информацию о проекте, поэтому он также является самым большим из трех файлов.
Разобрать контент-анализ через bin, hex, axf
axf дизассемблирует часть содержимого
Часть мусорного ведра
Часть шестнадцатеричного содержания
Проанализируйте эти данные, потому что бин и шестнадцатеричный принимают выравнивание с прямым порядком байтов, а разборка показывает выравнивание с прямым порядком байтов, поэтому требуется преобразование
Адрес старшей памяти помещает старший бит целого числа, а адрес младшей памяти помещает младший бит целого числа.Этот метод вызывается в перевернутом виде, и термин - это выравнивание с прямым порядком байтов. Компьютер X86 и мобильный телефон ARM выровнены с прямым порядком байтов
Старший адрес памяти помещает младший бит целого числа, а младший адрес памяти помещает старший бит целого числа. Этот метод называется прямым размещением, а термин - прямым порядком байтов. Процессоры многих серверов Unix выровнены с прямым порядком байтов.
Сначала посмотрите на первую строку разборки axf: 0X08000000 20000400 ----> указывает, что данные, хранящиеся по адресу 0X08000000, - это 0X20000400
Снова посмотрев на файл bin, поскольку адрес флэш-памяти STM32 - 0X08000000, первые четыре байта корзины указывают первое сохраненное число: 0X 00 04 00 20 Поскольку корзина является парой с прямым порядком байтов, она преобразуется в выравнивание с прямым порядком байтов следующим образом: 0X 20000400 точно так же, как axf
Анализировать шестнадцатеричный формат, последняя запись означает: 02 0000 04 0800 F2, что означает старшие 16 бит адреса: 0X0800, вторая запись: 10 0000 00 00040020 01010008 09010008 0B010008 9C, 0X10 означает, что длина данных записи составляет 16 байт, 0X 0000 представляет собой адрес данных записи в сочетании с предыдущей расширенной записью, это означает, что первый адрес флэш-памяти, которая будет сохранена в этой записи, (0x0800 0000 + 0x0000), 00040020 01010008 09010008 0B010008 Данные, хранящиеся по адресу, вы можете видеть, что они хранятся в 0X 08000000 Число - 0X 00040020, преобразованное в выравнивание с прямым порядком байтов: 0X20000400.
Наблюдение и анализ показывают, что содержимое данных в файлах bin, hex и axf одинаково, и в них хранится машинный код.
Потому что все файлы хранятся в машинном коде. Таким образом, опытные люди могут загружать файлы из bin или hex
Ассемблерный код, восстановленный из , является дорогостоящим, но не невозможным.Если в микросхеме нет мер шифрования, используйте загрузчик, чтобы напрямую прочитать данные, хранящиеся во флэш-памяти, из микросхемы, чтобы получить файл изображения бункера, и восстановить часть кода в соответствии с моделью микросхемы. Вносите изменения, даже не изменяя код. Скопируйте ту же плату непосредственно в соответствии с аппаратной платой целевого продукта и загрузите чип из образа корзины, чтобы напрямую скопировать целевой продукт. Поэтому в реальном производстве вы должны уделять внимание мерам шифрования. Поскольку файл axf содержит много информации, а код можно дизассемблировать, используя непосредственно fromelf, не допускайте утечки только файла axf. Файл lib также может использовать файл fromelf для дизассемблирования кода, но он не может восстановить код C. Поскольку основной целью файла lib является защита исходного кода C, его можно рассматривать как отвечающее его требованиям.
Давайте сначала проанализируем операцию «Метод один» подробно:
1. Мы устанавливаем компилятор компилятора (как показано на рисунке), этот параметр означает загрузку программы в начало вспышки 0x0800 0000, а затем компилируйте программу
2. После составления предшествующего предыдущего найдите файл .hex после составления после вывода папки каталога проекта;
Откройте программу с Notepad ++ или UltraeDit. Hex-файл
Формат Hex файла:
(1) Возьмите толстую кишку с линией поведения, все из которых 16 Введите код (в ASCII Дисплей кода)
(2) В файле Hex каждая строка представляет запись. Основной формат записи:
двоеточие Длина банка данных Адрес запуска данных банка Тип данных данные Проверьте код 1 byte 2 bytes 1 byte n byte 1 byte Первый байт Представляет длину банка;
Во-вторых, три байта представляют собой начальный адрес банка данных;
Четвертое представление байта Тип данных Тип данных: 0x00 、 0x01 、 0x02 、 0x03 、 0x04 、 0x05 。
'00' Data Rrecord : Используется для записи данных, HEX Большинство записей файлов являются записями данных
'01' End of File Record: Используется для определения файлов, окончательного, идентификации файлов HEX Конец неудачи
'02' Extended Segment Address Record: Записи, используемые для определения адреса расширения
'03' Start Segment Address Record: Запустите сегмент адрес записи
'04' Extended Linear Address Record: Записи, используемые для определения расширенных линейных адресов
'05' Start Linear Address Record: Начать линейную запись адреса
Тогда есть данные, последний байт Для контрольной суммы.
Алгоритм контрольной суммы: расчет Проверьте и проверить Все доступно 16 Ухождение кода накопление и ( Включать в себя ) , Осмотр и = 0x100 - Накапливаться
Открыть. Содержание шестнадцатерика выглядит следующим образом: (Средняя часть данных опущена)
Читайте также: