
" Когда сами константы становятся переменными. "
Пролог
В этом тексте я расскажу про одну полезную и широко распространенную утилиту для калибровки микроконтроллерных прошивок под названием TunerPRO.
Стандарт программирования ISO-26262 требует раздельного хранения калибровочных данных, конфигурационных данных и кода внутри прошивки. Это требование возникло не на пустом месте, а как раз для того, чтобы после выпуска релиза с программой можно было проводить гибкую товарную политику. Для этого и была разработана программа TunerPRO.
TunerPRO - это бесплатный бинарный редактор прошивок. По своей сути это функциональный аналог утилиты STM32Studio, только для любого микроконтроллера.
Эта программа позволит вам редактировать константы в готовом .bin файле. Минуя стадию повторной пересборки всего проекта прошивки (компиляции и компоновки). Можно сказать, что TunerPRO - хакерская tool-а. Ещё TunerPRO позволяет сравнивать bin файлы.
Эта утилита связывает воедино всю информацию про переменные: глобальные и константы. В данном случае константы. Это адрес ячейки памяти в bin-аре, размерность переменной, размер параметра, формат ее хранения в памяти, имя переменной, множитель, единицу измерения, максимальное и минимальное значение. В то время как map файл дает только инфу про имя ,адрес в памяти и размер.
Определения
Параметр - это переменная, которая помимо адреса в памяти, значения и размера несет в себе ещё и метаданные про физическую величину, размерность, тип данных, единицы измерения, множитель, тип доступа, формат записи и прочее, и прочее.
patch - (заплатка) - маленькое изменение в коде или в бинарном файле.
метаданные - информация о другой информации, или данные, относящиеся к дополнительной информации о содержимом или объекте. Метаданные раскрывают сведения о признаках и свойствах, характеризующих какие-либо сущности, позволяющие автоматически искать и управлять ими в больших информационных потоках.
XDF Файл - Данный файл содержит информацию о параметрах, адреса таблиц и их параметры, числовые константы, функции и др., что позволяет визуализировать калибровки прошивки в программе, чтобы иметь возможность интуитивно понятно редактировать прошивку прибора. Это родной формат для утилиты TunerPro. XDF-файл должен четко подходить к прошивке вашего прибора. Xdf это база данных про переменные в компьютерной программе. В высшем своем проявлении этот файл говорит как интерпретировать каждый байт в бинарном файле с прошивкой.
Вот пример реального XDF файла
<!-- Written 11/15/2025 14:38:18 --> <XDFFORMAT version="1.80"> <XDFHEADER> <flags>0x1</flags> <fileversion>"ver 1"</fileversion> <deftitle>nucleo_f401re_esp_01</deftitle> <description>ARM CORTEX_M4</description> <author>aabzel</author> <BASEOFFSET offset="0" subtract="0" /> <DEFAULTS datasizeinbits="8" sigdigits="2" outputtype="2" signed="1" lsbfirst="0" float="0" /> <REGION type="0xFFFFFFFF" startaddress="0x0" size="0x80000" regioncolor="0x0" regionflags="0x0" name="Binary File" desc="This region describes the bin file edited by this XDF" /> </XDFHEADER> <XDFCONSTANT uniqueid="0x6298" flags="0xC"> <title>tuner_pro_s8</title> <CATEGORYMEM index="0" category="1" /> <EMBEDDEDDATA mmedtypeflags="0x01" mmedaddress="0x3461C" mmedelementsizebits="8" mmedmajorstridebits="0" mmedminorstridebits="0" /> <outputtype>2</outputtype> <rangehigh>120.000000</rangehigh> <rangelow>-120.000000</rangelow> <datatype>3</datatype> <unittype>2</unittype> <DALINK index="0" /> <MATH equation="X"> <VAR id="X" /> </MATH> </XDFCONSTANT> <XDFCONSTANT uniqueid="0x4478"> <title>tuner_pro_float</title> <EMBEDDEDDATA mmedtypeflags="0x10002" mmedaddress="0x34624" mmedelementsizebits="32" mmedmajorstridebits="0" mmedminorstridebits="0" /> <datatype>2</datatype> <unittype>3</unittype> <DALINK index="0" /> <MATH equation="X"> <VAR id="X" /> </MATH> </XDFCONSTANT> <XDFCONSTANT uniqueid="0x5C94" flags="0xC"> <title>tuner_pro_u8</title> <EMBEDDEDDATA mmedaddress="0x34614" mmedelementsizebits="8" mmedmajorstridebits="0" mmedminorstridebits="0" /> <outputtype>2</outputtype> <rangehigh>200.000000</rangehigh> <datatype>3</datatype> <unittype>3</unittype> <DALINK index="0" /> <MATH equation="X"> <VAR id="X" /> </MATH> </XDFCONSTANT> </XDFFORMAT>
Входные данные для утилиты TunerPro
Тип файла | Расширение | Тип файла | Пояснение |
binary file | bin | бинарный | Файл с прошивкой |
bin definition file | XDF | текстовый | Аналог map файла |
XDF файлы можно генерировать из MATLAB файлов, т.к. только там есть физический смысл переменной. XDF файлы чем-то похоже на a2l файлы из протокола XCP.
Информацию про названия глобальных константных переменных их размер и расположение в физической памяти можно выделить из *.elf файла командой read elf. Буквально одной строчкой.
readelf -a firmware.elf | grep OBJECT | grep GLOBAL | grep "DEFAULT 1" C:\projects\debug\source\projects\nda\build>readelf -a nda.elf | grep OBJECT | grep GLOBAL | grep "DEFAULT 1" 6502: 010747f8 40 OBJECT GLOBAL DEFAULT 1 __mprec_tinytens 6687: 01072268 240 OBJECT GLOBAL DEFAULT 1 CanConfig 6725: 01072178 16 OBJECT GLOBAL DEFAULT 1 SysTickConfig 6750: 0106e6e4 8 OBJECT GLOBAL DEFAULT 1 ClockInfo 6836: 01072f68 60 OBJECT GLOBAL DEFAULT 1 CliConfig 6849: 010712c8 336 OBJECT GLOBAL DEFAULT 1 ClockReg 6931: 01072188 16 OBJECT GLOBAL DEFAULT 1 SuperCycleConfig 6976: 0106f88c 512 OBJECT GLOBAL DEFAULT 1 crc16LookUpTable 6997: 01073810 608 OBJECT GLOBAL DEFAULT 1 GpioConfig 7021: 01071c28 120 OBJECT GLOBAL DEFAULT 1 PortReg 7061: 01074730 200 OBJECT GLOBAL DEFAULT 1 __mprec_tens 7066: 0106e9b4 840 OBJECT GLOBAL DEFAULT 1 s_aFlexCan_Norma[...] 7072: 01073010 144 OBJECT GLOBAL DEFAULT 1 LedMonoConfig 7109: 01072258 8 OBJECT GLOBAL DEFAULT 1 StoreFsConfig 7255: 01071ce4 16 OBJECT GLOBAL DEFAULT 1 ClockOutConfig 7269: 01072fa4 108 OBJECT GLOBAL DEFAULT 1 LittleFsConfig 7281: 01074294 4 OBJECT GLOBAL DEFAULT 1 _global_impure_ptr 7311: 01074708 40 OBJECT GLOBAL DEFAULT 1 __mprec_bigtens 7395: 01071bd4 84 OBJECT GLOBAL DEFAULT 1 GpioReg 7400: 010731e8 84 OBJECT GLOBAL DEFAULT 1 WriterConfig 7408: 0106ef2c 540 OBJECT GLOBAL DEFAULT 1 CanReg 7543: 0107452c 32 OBJECT GLOBAL DEFAULT 1 __sf_fake_stderr 7599: 0107323c 20 OBJECT GLOBAL DEFAULT 1 CoreConfig 7603: 01070094 16 OBJECT GLOBAL DEFAULT 1 SwComponentConfig 7622: 0106ecfc 560 OBJECT GLOBAL DEFAULT 1 s_aFlexCan_DataB[...] 7652: 01072198 24 OBJECT GLOBAL DEFAULT 1 BoardConfig 7657: 01071d44 80 OBJECT GLOBAL DEFAULT 1 RamSectorConfig 7666: 01071cf4 0 OBJECT GLOBAL DEFAULT 1 DmaConfig 7672: 010721b0 168 OBJECT GLOBAL DEFAULT 1 Wires 7687: 01070108 864 OBJECT GLOBAL DEFAULT 1 test_list 7713: 01071cf4 48 OBJECT GLOBAL DEFAULT 1 FlashSectorConfig 7757: 01073170 120 OBJECT GLOBAL DEFAULT 1 UartConfig 7788: 01074628 96 OBJECT GLOBAL DEFAULT 1 __month_lengths 7796: 010730f0 120 OBJECT GLOBAL DEFAULT 1 StringReaderConfig 7850: 01073768 168 OBJECT GLOBAL DEFAULT 1 PinConfig 7903: 01071cd0 20 OBJECT GLOBAL DEFAULT 1 BootConfig 7962: 01073168 6 OBJECT GLOBAL DEFAULT 1 TimeConfig 7978: 0107454c 32 OBJECT GLOBAL DEFAULT 1 __sf_fake_stdin 7993: 01071fb0 456 OBJECT GLOBAL DEFAULT 1 ParamArray 8018: 01071d94 540 OBJECT GLOBAL DEFAULT 1 InterruptConfig 8281: 01072358 640 OBJECT GLOBAL DEFAULT 1 CanMessageBuffer[...] 8335: 01071d24 32 OBJECT GLOBAL DEFAULT 1 FlashConfig 8346: 01074614 12 OBJECT GLOBAL DEFAULT 1 _C_numeric_locale 8366: 01073250 1304 OBJECT GLOBAL DEFAULT 1 IntNumInfo 8427: 010730c4 20 OBJECT GLOBAL DEFAULT 1 PostponeFunConfig 8483: 01073a70 5 OBJECT GLOBAL DEFAULT 1 LogConfig 8583: 0107456c 32 OBJECT GLOBAL DEFAULT 1 __sf_fake_stdout 8605: 01074190 257 OBJECT GLOBAL DEFAULT 1 _ctype_ 8622: 0106f4e4 208 OBJECT GLOBAL DEFAULT 1 SystemInitInstance 8635: 01071b1c 120 OBJECT GLOBAL DEFAULT 1 FlashRegisters 8693: 0106e920 12 OBJECT GLOBAL DEFAULT 1 UartRegs
Что надо из ПО?
Название утилиты | Назначение |
ST-LINK_CLI.exe | Для загрузки .bin файла во flash память МК |
TunerPro.exe | Для редактирования bin файлов |
WinMerge.exe | Для сравнения файлов: как текстовых так и бинарных |
Free Hex Editor Neo | Еще один hex редактор для просмотра bin файлов |
ARM GCC ToolChain | Для сборки Си программ под ARM микроконтроллеры |
Практическая часть
Посмотрим, как работает утилита TunerPro. Вот я накропал простецкий модульный тест, который просто печатает константы из Flash памяти.
#include "test_tuner_pro.h" #include "log.h" #include "unit_test_check.h" #include "array_diag.h" const char tuner_pro_text[16] = "test_data"; const uint8_t tuner_pro_u8 = 0x12; const uint16_t tuner_pro_u16 = 0x1234; const uint32_t tuner_pro_u32 = 0x12345678; const int8_t tuner_pro_s8 = -8; const int16_t tuner_pro_s16 = -16; const int32_t tuner_pro_s32 = -32; const float tuner_pro_float = 5.5; const double tuner_pro_double = 7.7; /* tr tuner_pro+ */ bool test_tuner_pro_types(void) { bool res = true; LOG_DEBUG(SYS, "%s():", __FUNCTION__); EXPECT_EQ(1, sizeof(tuner_pro_u8)); EXPECT_EQ(1, sizeof(tuner_pro_s8)); EXPECT_EQ(2, sizeof(tuner_pro_s16)); EXPECT_EQ(2, sizeof(tuner_pro_u16)); EXPECT_EQ(4, sizeof(tuner_pro_s32)); EXPECT_EQ(4, sizeof(tuner_pro_u32)); EXPECT_EQ(4, sizeof(tuner_pro_float)); EXPECT_EQ(8, sizeof(tuner_pro_double)); EXPECT_EQ(16, sizeof(tuner_pro_text)); return res; } bool test_tuner_pro_vals(void) { bool res = true; LOG_DEBUG(SYS, "%s():", __FUNCTION__); LOG_INFO(SYS, "U8:0x%x", tuner_pro_u8); LOG_INFO(SYS, "U16:0x%x", tuner_pro_u16); LOG_INFO(SYS, "U32:0x%x", tuner_pro_u32); LOG_INFO(SYS, "S8:%d", tuner_pro_s8); LOG_INFO(SYS, "S16:%d", tuner_pro_s16); LOG_INFO(SYS, "S32:%d", tuner_pro_s32); LOG_INFO(SYS, "f:%f", tuner_pro_float); LOG_INFO(SYS, "d:%f", tuner_pro_double); LOG_INFO(SYS, "text:[%s]", tuner_pro_text); return res; } bool test_tuner_pro_mem(void) { bool res = true; LOG_DEBUG(SYS, "%s():", __FUNCTION__); LOG_INFO(SYS, "U8:0x%s", ArrayToStr((uint8_t*)&tuner_pro_u8,sizeof(tuner_pro_u8))); LOG_INFO(SYS, "U16:0x%s", ArrayToStr((uint8_t*)&tuner_pro_u16,sizeof(tuner_pro_u16))); LOG_INFO(SYS, "U32:0x%s", ArrayToStr((uint8_t*)&tuner_pro_u32,sizeof(tuner_pro_u32))); LOG_INFO(SYS, "S8:0x%s", ArrayToStr((uint8_t*)&tuner_pro_s8,sizeof(tuner_pro_s8))); LOG_INFO(SYS, "S16:0x%s", ArrayToStr((uint8_t*)&tuner_pro_s16,sizeof(tuner_pro_s16))); LOG_INFO(SYS, "S32:0x%s", ArrayToStr((uint8_t*)&tuner_pro_s32,sizeof(tuner_pro_s32))); LOG_INFO(SYS, "tuner_pro_float:0x%s", ArrayToStr((uint8_t*)&tuner_pro_float,sizeof(tuner_pro_float))); LOG_INFO(SYS, "tuner_pro_double:0x%s", ArrayToStr((uint8_t*)&tuner_pro_double,sizeof(tuner_pro_double))); LOG_INFO(SYS, "tuner_pro_text:0x%s", ArrayToStr((uint8_t*)&tuner_pro_text,sizeof(tuner_pro_text))); return res; } bool test_tuner_pro_address(void) { bool res = true; LOG_DEBUG(SYS, "%s():", __FUNCTION__); LOG_INFO(SYS, "&tuner_pro_u8:0x%p", &tuner_pro_u8); LOG_INFO(SYS, "&tuner_pro_u16:0x%p", &tuner_pro_u16); LOG_INFO(SYS, "&tuner_pro_u32:0x%p", &tuner_pro_u32); LOG_INFO(SYS, "&tuner_pro_s8:0x%p", &tuner_pro_s8); LOG_INFO(SYS, "&tuner_pro_s16:0x%p", &tuner_pro_s16); LOG_INFO(SYS, "&tuner_pro_s32:0x%p", &tuner_pro_s32); LOG_INFO(SYS, "&tuner_pro_float:0x%p", &tuner_pro_float); LOG_INFO(SYS, "&tuner_pro_double:0x%p", &tuner_pro_double); LOG_INFO(SYS, "&tuner_pro_text:0x%p", tuner_pro_text); return res; }
В результате исполнения я увидел
1.348--> 1.502-->tr tuner+ cmd_unit_test_run() argc 1 5.832,+5803,103 I,[TEST] key [tuner+] 5.834,+2,104 W,[TEST] unit_test_run_key() key tuner ************* Run test tuner_pro_types .1/49 !OKTEST ************* Run test tuner_pro_mem .2/49 6.140,+306,105 I,[SYS] U8:0x12 6.142,+2,106 I,[SYS] U16:0x3412 6.144,+2,107 I,[SYS] U32:0x78563412 6.146,+2,108 I,[SYS] S8:0xF8 6.148,+2,109 I,[SYS] S16:0xF0FF 6.150,+2,110 I,[SYS] S32:0xE0FFFFFF 6.152,+2,111 I,[SYS] tuner_pro_float:0x0000B040 6.154,+2,112 I,[SYS] tuner_pro_double:0xCDCCCCCCCCCC1E40 6.157,+3,113 I,[SYS] tuner_pro_text:0x746573745F6461746100000000000000 !OKTEST ************* Run test tuner_pro_const_vals .3/49 6.462,+305,114 I,[SYS] text:[U8:0x12,S8:-8,U16:0x1234,U32:0x12345678,S16:-16,S32:-32,f:5.500000,d:7.700000,text:[test_data],] !OKTEST ************* Run test tuner_pro_vals .4/49 6.769,+307,115 I,[SYS] text:[U8:0x12,S8:-8,U16:0x1234,U32:0x12345678,S16:-16,S32:-32,f:5.500000,d:7.700000,text:[test_data],] !OKTEST ************* Run test tuner_pro_address .5/49 7.076,+307,116 I,[SYS] &tuner_pro_u8:0x8034614 7.079,+3,117 I,[SYS] &tuner_pro_u16:0x8034616 7.081,+2,118 I,[SYS] &tuner_pro_u32:0x8034618 7.083,+2,119 I,[SYS] &tuner_pro_s8:0x803461c 7.086,+3,120 I,[SYS] &tuner_pro_s16:0x803461e 7.088,+2,121 I,[SYS] &tuner_pro_s32:0x8034620 7.090,+2,122 I,[SYS] &tuner_pro_float:0x8034624 7.093,+3,123 I,[SYS] &tuner_pro_double:0x8034628 7.095,+2,124 I,[SYS] &tuner_pro_text:0x8034604 !OKTEST 7.399,+304,125 I,[TEST] TestDuration:1565 ms=1.565 s=0.026083333 min 7.403,+4,126 I,[TEST] All 5 tests passed!
Теперь я закрываю IDE и далее работаю только с бинарём. Вот у меня есть *.bin прошивка. В ней есть глобальные переменные. Надо изменить константу без пере сборки всего проекта прошивки и залить эту модифицированную прошивку в flash память микроконтроллер.
Название константы | Адрес в памяти | bin file addr | Size | Начальное |
tuner_pro_s8 | 0x803461c | 3461c | 1 | -8 |
tuner_pro_s16 | 0x803461e | 3461e | 2 | -16 |
tuner_pro_s32 | 0x8034620 | 34620 | 4 | -32 |
tuner_pro_text | 0x8034604 | 34604 | 16 | test_data |
tuner_pro_double | 0x8034628 | 34628 | 8 | 5.5 |
tuner_pro_float | 0x8034624 | 34624 | 4 | 5.5 |
tuner_pro_u8 | 0x8034614 | 34614 | 1 | 0x12 |
tuner_pro_u16 | 0x8034616 | 34616 | 2 | 0x1234 |
tuner_pro_u32 | 0x8034618 | 34618 | 4 | 0x12345678 |
Сам файл XDF можно создать прямо внутри утилиты TunerPRO. Первым делом надо объявить заголовок. Открываем XDF -> XDF Header Editor. Тут надо только указать размер прошивки.

Затем вручную добавляем интересующие нас переменные. Одну за другой. XDF -> Create New XDF Parameter

Можно добавлять всяческие метаданные для переменной. Граничные значения, физическая величина, тип данных, единицы измерения, размерность, категорию и т. д. Всё то, про что не знал Си-компилятор. Эти метаданные кристаллизируется потом в XDF файле.

Утилита увидела в bin файле значение для переменной

Теперь мы можем преспокойно изменить циферку в переменной.

Чтобы занести изменения в bin файл нажимаем File-> Save Bin. Проверить наличие изменение можно в утилите WinMerge. Вот я внес patch в прошивку и это явно видно в bin файле.

Разницу между бинарями можно вычислить даже не выходя из TunerPRO инструментом Difference Tool

Осталось только просто прошить про патченый бинарь во flash память микроконтроллера STM32F401RE . В случае с STM32 микроконтроллером это делает утилита ST-LINK_CLI.exe
echo off cls set project_name=nucleo_f401re_esp_01_m set project_dir=%cd% set artefact_bin=%project_name%.bin set FlashTool=ST-LINK_CLI.exe set Device= set options=-c %Device% set options= %options% SWD freq=4000 set options= %options% HOTPLUG LPM set options= %options% -P %artefact_bin% 0x08000000 set options= %options% -V "after_programming" set options= %options% -Log -TVolt call %FlashTool% %options% rem Reset System call %FlashTool% -Rst
Лог загрузки прошивки в память

У меня в прошивке есть UART-CLI. Поэтому я могу еще раз прогнать тот самый модульный тест и зарегистрировать изменения.
Как можно заметить, память в самом деле изменилась с 0x12 на 0x34. Другой вопрос почему printf оптимизировал и вывел старую прохардкоженную константу 0x12? Видимо это оптимизации компилятора. Моя гипотеза в том, что компилятор GCC при печати констант printf-ом переводит просто данные в сегмент .text. Но главное, что Flash память в самом деле изменилась в нужной ячейке NorFlash ROM!

Этот текст показывает как легко уязвимы .bin файлы. Все кому не лень могут их взять и подшаманить. В этом плане *.hex формат выглядит предпочтительнее так как в .hex для каждой строчки есть контрольная сумма.
TunerPRO можно иcпользовать как продвинутый hex editor.

Как это теперь применять на практике?
А вот как.
1--Допустим вы пишите прошивку для какого-то прибора. И вам клиенты говорят, что нужна примерно такая же прошивка только с измененными параметрами. В этом случае вы можете дать им ту же самую прошивку только прислать .xdf файл в котором или вы или они сами могут внести желаемое любое изменение.
2--После старого проекта у вас не осталось сорцов. В наличии только устройство программатор и бинарь. При этом возникла потребность слегка модифицировать функционал. В одном месте. Тут вам снова поможет TunerPRO.
3--Вам надо откалибровать какой-то агрегат. При этом пересборка прошивки с другими калибровочными константами занимает много времени. Ситуация осложняется тем, что в прошивке нет ни UART-CLI ни NVRAM. В этом случае Вы можете, по крайней, мере перебирать значения прямо в бинаре утилитой TunerPRO и пере накатывать новый модернизированный артефакт. До тех пор пока не подберете нужную калибровку. Красота!
Достоинства Tuner PRO
1++Эта утилита инвариантна к производителю микроконтроллера. Можно модифицировать прошивки для любого МК
2++Это бесплатная утилита
3++Утилита может интерпретировать базовые типы данных: float, uint8_t, int16_t и т. п.
Недостатки
1--Нет поддержки double переменных
2--Не ясно, как задавать параметры в виде Си-шных структур
Итог
Удалось получить представление о том, что такое утилита TunerPRO и как ей пользоваться.
Эта утилита позволяет вам переложить задачи калибровки с разработчика на пользователя прошивки. Вы просто даете .bin .xdf и пользователь уже сам настраивает прошивку так как требуется.
Словарь
Акроним | Расшифровка |
XDF | extensible calibration definition format |
RISC | reduced instruction set computer |
MCU | microcontroller unit |
ARM | Advanced RISC Machine |
IDE | integrated development environment |
Gdb | GNU Debugger |
elf | Executable and Linkable Format |
Ссылки
Название | URL |
Введение в TunerPRO RT | |
Размещение глобальных констант по фиксированным адресам | |
TunerPro Quick guide | https://oldskulltuning.com/wp-content/uploads/2023/03/Quick-guide-rev1.4.pdf |
UART-CLI | |
Пуск LittleFS (NVRAM с запретом до-записи flash) | |
STM Studio run-time variables monitoring and visualization tool for STM32 microcontrollers | https://www.st.com/en/development-tools/stm-studio-stm32.html |
Вопросы
--Вот я собрал *.elf файл с прошивкой. Захотелось изменить одну константу перед загрузкой прошивки в MCU. Как мне теперь получить XDF Файл для утилиты TunerPRO? Надо создавать его вручную. Однако этот файл может генерировать MatLab, так как Simulink придает переменным физический смысл.
--XDF файл для утилиты TunerPRO это бинарный файл или текстовый файл? Текстовый
--Существует ли спецификация формата *.xdf файлов, чтобы корректным образом заполнить метаданные: заголовок, контрольную сумму и сами переменные?
--Как извлечь абсолютные адреса членов структур из *.elf файла? Gdb их же как-то достает?
--Может ли TunerPRO работать в консольном режиме?
--Как при программировании на Си отключить оптимизацию при printf печати константных переменных из flash памяти в GCC?
--Можно ли как-то настроить утилиту TunerPRO, чтобы она позволяла непрерывно извлекать данные из глобальных переменных в RAM памяти по интерфейсам JTAG SWD и строить графики в реальном времени? И какой отладчик для этого нужен? Например в случае с STM32 микроконтроллерами.
