Как работает stm32-diag-record
Два маршрута записывают значения прошивки в MCAP или bag без ROS: компьютер опрашивает память через OpenOCD либо заглушка в SRAM снимает кадры по прерыванию таймера и передаёт их через RTT. Флеш не перепрошивается в обоих случаях, но вмешательство в выполнение программы различается.
Выбор маршрута записи
| Свойство | A — опрос памяти компьютером | B — выборка на MCU и передача RTT |
|---|---|---|
| Ключ | --backend openocd | --backend rtt-timer |
| Кто снимает значения | Python на компьютере запрашивает mdw | Заглушка в SRAM читает поля в IRQ свободного таймера |
| Ритм выборки | Планировщик хоста и задержки SWD; по умолчанию 10 Гц | Таймер MCU; на F411CE проверены номинальные 1000 Гц |
| Вмешательство | Без добавления кода и остановки ядра при опросе; reset только по явному ключу | Временный код/буфер SRAM, таймер и перехват вектора; halt при установке и снятии, нагрузка ISR при записи |
| Время | Метка цикла от компьютера; счётчик MCU можно записать полем | Tick прошивки в кадре; его реальную длительность нужно калибровать |
| Потери | Изменения между опросами не видны | Исходные seq/tick и оценка пропусков между принятыми кадрами; не полная трасса событий |
| Завершение | Закрытие записи и собственных соединений/процессов | GDB: выход из IRQ, восстановление регистров и проверка продолжения tick; отчёт teardown |
Оба маршрута используют SWD физически. RTT — буферизованная передача кадров из RAM, а не отдельный UART. Renode — вариант чтения памяти без платы, с собственным временем эмуляции.
Путь данных
fields.toml
что записывать: имя, тип, выражение
expr = "&probe_telemetry.frames"прошивка.elf
символы и отладочная информация DWARF
сборка с -gфайл SVD по желанию
имена регистров периферии от производителя
resolve_addresses.py
выражения C вычисляет GDB по DWARF, поля svd: вычисляет svd_lookup.py
addresses.json
для каждого поля: адрес, тип, сдвиг и маска
/probe/frames → 0x20000030Микроконтроллер
прошивка работает как обычно; RAM и регистры периферии
STM32, AT32, nRF52, RP2040…Отладчик
любой, который понимает OpenOCD
ST-Link, CMSIS-DAP, J-LinkOpenOCD
запускается скриптом без сброса цели; принимает команды по telnet
127.0.0.1:4444, mdwrecord_diag.py
с частотой --rate читает слова, переводит их в значения, ставит метку времени
Файл
mcap_writer.py или bag_writer.py, только стандартная библиотека Python
diag/record-…mcap | .bagБез платы: Renode
Вместо микроконтроллера, отладчика и OpenOCD работает эмулятор. Скрипт читает те же адреса через монитор Renode, эмуляция не прерывается.
--backend renode, telnet 127.0.0.1:1234, sysbus ReadDoubleWordТаймер MCU
IRQ задаёт моменты выборки
свободный TIM, проверенная частота тактированияЗаглушка в SRAM
читает поля и формирует кадр с исходными seq и tick
Буфер RTT в RAM
OpenOCD забирает накопленные кадры через SWD
record_diag.py
декодирует кадры, проверяет seq, пишет поля и служебные каналы
MCAP / bag + JSON
данные, статистика RTT и отдельный отчёт снятия перехвата
topic..mcap
открывается загрузчиком MCAP в PlotJuggler под Windows и Linux
проверка: mcap infoPlotJuggler
графики каналов, их сравнение, масштаб по времени
Windows, Linux.bag (ROS1)
в PlotJuggler открывается только с плагином ROS1, то есть под Linux с ROS; под Windows такого плагина нет
rosbag info, Python rosbagsМаршрут A: один цикл опроса (10 раз в секунду по умолчанию)
kind: как целое, число со знаком, float или миллисекунды в секунды.min и max не пишутся, а считаются отдельно.Откуда синтаксис описания полей
# файл в формате TOML; [[field]] — одно поле [[field]] topic = "/probe/frames" message = "std_msgs/UInt32" kind = "uint32" expr = "&probe_telemetry.frames" [[field]] topic = "/probe/pin_frame" message = "std_msgs/UInt32" kind = "uint32" expr = "svd:GPIOB.ODR.ODR15"
| Ключ | Чем определяется |
|---|---|
| файл | общий формат конфигураций TOML |
expr | выражение языка C, как в команде GDB print: &var, &obj.field, &arr[3]. GDB вычисляет его по ELF, результат — адрес |
svd: | соглашение навыка svd:ПЕРИФЕРИЯ.РЕГИСТР[.ПОЛЕ]; имена берутся из SVD-файла производителя |
message | имена стандартных типов ROS1 std_msgs: так файл понимают PlotJuggler и инструменты ROS |
kind | короткий список навыка: uint32, int32, float32, uint32_seconds |
topic | любое имя; по обычаю ROS — /группа/имя |
Маршрут A: не только STM32
Навык проверен на STM32, но работает с любым Cortex-M, с которым работает OpenOCD. Подходит ли MCU, решают инструменты, а не сам навык:
- OpenOCD знает отладчик (
--interface) и цель (--target, штатный скрипт или скрипт производителя); - память читается без остановки ядра: нет защиты от чтения, скрипт цели не сбрасывает её при подключении;
- прошивка собрана с отладочной информацией, есть GDB с Python;
- для полей
svd:— SVD производителя, для работы без платы — модель MCU в Renode.
Проверка нового MCU начинается с доктора с теми же ключами, что и у записи:
python record_diag.py --doctor \ --interface interface/cmsis-dap.cfg \ --target target/nrf52.cfg \ --elf app.elf --fields my_fields.toml
Доктор показывает, найдены ли конфигурации, отвечает ли отладчик, какое ядро и в каком состоянии цель, вычисляются ли адреса и читается ли слово на ходу.
Что важно понимать о базовом опросе SWD
--reset). UART остаётся свободным.Общая база диагностики
Оба навыка используют одинаковые модули чтения OpenOCD, декодирования полей, поиска GDB и управления дочерними процессами. Исправления транспорта применяются к записи в файл и к публикации ROS. Поддерживаются маски, сдвиги и невыровненные поля.
Источник модулей — stm32-ros-alt-channel; каждый навык содержит полную копию
нужных скриптов и устанавливается самостоятельно. CI проверяет совпадение копий и оба потребителя,
в Emulation используются адреса из ELF stm32-skills-probe.
Назначение маршрутов сохраняется: ROS/WSL и rosserial — в ROS-навыке, запись без ROS, Renode и RTT — в diag-record. В CI тест ROS-публикации подменяет middleware. Локально проверена также публикация через настоящий ROS Noetic в WSL с TCP-имитатором OpenOCD; SWD→ROS и RTT/Ctrl+C после рефакторинга проверены на F411CE; UART требует отдельной проверки.
Отдельный режим RTT на таймере
--backend rtt-timer устанавливает заглушку в SRAM и временно перехватывает
IRQ свободного таймера. Флеш не меняется, но ядро останавливается при установке и снятии;
этот режим влияет на выполнение прошивки. Время кадров берётся из tick прошивки.
Запись сохраняет исходные seq/tick и статистику потерь между принятыми кадрами. Завершение через GDB проверяет отсутствие active IRQ, восстановление регистров и рост tick. Обычный выход и Ctrl+C проверены владельцем на F411CE при номинальных 1000 Гц до выделения общей базы; после рефакторинга повторён RTT/Ctrl+C: 7971 кадр, снятие успешно, LED продолжает мигать. CI использует прошивку-зонд и Renode.
Описание навыка (SKILL.md) и скрипты · версия 1.8.0
Повтор на F411CE после общей базы
19.10.2026, версия d003ca5: RTT с номинальными 1000 Гц и остановкой Ctrl+C — 7971 кадр, seq без разрывов, шаг tick 1 мс. Код 0, регистры восстановлены, TIM3 не активен, tick после resume вырос на 244 мс, LED продолжает мигать. Завершение началось вне ISR.
Ровный tick не подтверждает абсолютную частоту: отдельное сравнение с Windows показало отставание счётчика этой прошивки на 2,72%. Опыт HSI → HSE → HSI локализовал основную разницу на HSI данного экземпляра. Короткий опыт не заменяет абсолютную калибровку.