Adresy proměnných pro QInsight

Proč: QInsight nemá A2L. Každá proměnná je adresována absolutní adresou objektu v obrazu vašeho firmwaru (address="0x…", addressExtension="0" v Comm params) a tato adresa se může s každým sestavením posunout. Balíček obsahuje dva nástroje, které adresy vyčtou z ladicích informací DWARF v ELF souboru a vytvoří soubor pro import do QInsight, takže žádnou adresu nepíšete ručně.

Požadavky: Python 3 a pip install pyelftools; ELF musí obsahovat ladicí informace (-g, které preset CubeMX Debug má — Release s -g0 ne).

tools/elf_vars_scan.py — vše řídí jeden soubor JSON

Zkopírujte tools/elf_vars.template.json k vašemu projektu, např. jako my_firmware.vars.json, a upravte jej:

{
  "elf": "build/Debug/my_firmware.elf",
  "folders": [ "Core/Src", "App" ],
  "symbols": [ "uwTick" ],
  "output": {
    "csv": "my_firmware.vars.csv",
    "qinsight": "my_firmware_variables.xml",
    "namespace": "my_ecu"
  },
  "options": { "includeConst": false, "arrays": "matrix", "maxArray": 16, "all": false }
}
KlíčVýznam
elfobraz firmwaru s ladicími informacemi.
folderssložky zdrojů, jejichž proměnné chcete. Absolutní cesty fungují všude; relativní složka se řeší vůči souboru JSON; pokud tam neexistuje (firmware byl přeložen jinde), hledá se místo toho jako úsek cesty ve zdrojových cestách zaznamenaných v ELF — "Core/Src" pak najde D:/Projects/MyEcu/Core/Src/main.c, ať překlad běžel kdekoli. Uveďte tolik složek, kolik potřebujete. Zdroje se neparsují; složky jen vybírají, které kompilační jednotky ELF se vypíší.
symbolsvolitelný seznam názvů proměnných, které se vypíší, i když jejich kompilační jednotka leží mimo folders — např. "uwTick", 1ms tick HAL, bez výpisu celé HAL (options.all). Jen prostá globální jména.
output.csvtabulka všech proměnných: file;scope;name;type;size;address;volatile;collapsed;kind;elemType;elemSize;dim1;dim2 (kind = scalar nebo matrix, viz Pole, křivky a mapy).
output.jsontytéž řádky jako JSON (volitelné).
output.qinsightsoubor pro import do QInsight (scanner za vás spustí elf_vars_to_qinsight.py). Alespoň jeden výstup je povinný.
output.namespacezapíše se do každé importované proměnné (volitelné).
options.includeConstvypsat i const data ve flash (výchozí false — nelze je měřit ani kalibrovat).
options.arrays"matrix" (výchozí): každé číselné 1D/2D pole je jeden řádek druhu matrix — blok hodnot, křivka nebo mapa pro QInsight, přenášená jako jeden blok XCP. "expand": prvky jeden po druhém jako samostatné skalární proměnné.
options.maxArraypři arrays = "expand": rozepsat pole až do N prvků; delší se stanou jedním sbaleným řádkem name[0..n-1] s collapsed = 1 (výchozí 16, 0 = všechny). Sbalený řádek se do QInsight neimportuje.
options.allignorovat folders a vypsat každou kompilační jednotku včetně HAL a lwIP (výchozí false).

Všechny relativní cesty jsou relativní ke složce souboru JSON, takže příkaz funguje z libovolného adresáře:

python <sdk>/tools/elf_vars_scan.py my_firmware.vars.json

Příklad obsahuje examples/nucleo_h723zg/nucleo_h723zg.vars.json ("folders": ["app"], ELF presetu Debug) a příklad bez RTOS examples/nucleo_h723zg_baremetal/nucleo_h723zg_baremetal.vars.json: přeložte příklad, spusťte nad tímto souborem scanner a získáte skalární proměnné demo signálů s adresami, např. gDemoSignals.channels_[0].value na 0x2400005C v přiloženém sestavení Debug (ověřte proti vlastnímu sestavení), a tři maticové objekty z demo_map.c.

Jména členů struktur a prvků polí se pro QInsight zplošťují (gDemoSignals.channels_[0].value → gDemoSignals_channels_0_value); původní cesta v C, zdrojový soubor a adresa zůstávají v popisu proměnné: XCP: address="0x2400005C" addressExtension="0". Bitová pole se hlásí s velikostí 0 a při importu se přeskakují — čtěte obsahující celé číslo a použijte Bit Shift / Bit Mask v QInsight.

Pole, křivky a mapy (proměnné Matrix)

QInsight zobrazuje kalibrační mapu, křivku nebo blok hodnot (v terminologii ASAP2 MAP / CURVE / VAL_BLK) v Matrix controlu a přenáší jej jako jeden blok XCP — s block módem enginu je mapa 16 × 16 float jeden UPLOAD burst a jeden blok DOWNLOAD na zapsané okno buněk místo stovek potvrzovaných paketů. Scanner exportuje každé číselné 1D nebo 2D pole jako matrixVariable (options.arrays = "matrix"); rozložení, které QInsight očekává, je jeden souvislý blok osa X, osa Y, data s daty po řádcích (data[y][x]).

Mapu i s osami získáte ze scanu, když objekty pojmenujete podle této konvence a udržíte je v paměti souvisle — buď jako členy jedné struktury s pack(1), nebo jako tři po sobě jdoucí globální proměnné:

#pragma pack(push, 1)
typedef struct {
    volatile uint16_t x[16];      /* X axis breakpoints, e.g. rpm            */
    volatile uint8_t  y[16];      /* Y axis breakpoints, e.g. load [%]       */
    volatile float    data[16][16]; /* data[y][x]                            */
} IgnitionMap;
#pragma pack(pop)
IgnitionMap ignitionMap;          /* -> one map "ignitionMap", 1072 B block  */

volatile uint16_t torqueCurve_x[8];   /* or: <name>_x, <name>_y, <name>      */
volatile uint16_t torqueCurve[8];     /* -> one curve "torqueCurve"           */

Pravidla, která převodník kontroluje (při porušení exportuje pole jako samostatné bloky hodnot a vypíše varování): osa X má tolik bodů, kolik mají data sloupců, osa Y tolik, kolik mají data řádků, a X, Y i data na sebe v paměti navazují bez mezer (#pragma pack(1) na struktuře; 4bajtová data jako float držte na 4bajtovém offsetu — 16 + 16 bajtů os to splňuje). Samotné pole se stane blokem hodnot (samotné 2D pole se zploští po řádcích). Typy prvků se mapují jako u skalárů; osy mohou mít vlastní typ (dataType u xAxis / yAxis), typ dat je výchozím typem proměnné. Adresa bloku v popisu je adresa osy X (dat, nejsou-li osy); proměnnou přiřaďte k protokolu XCP a zkopírujte address / addressExtension přesně jako u skaláru. Navažte ji na periodickou událost (polluje se jako celý blok) nebo na událost On Request (čte se jen stiskem Read); DAQ a STIM matice nepřenášejí. Největší matice, kterou QInsight přenese, je 64 × 64 × 4 B dat plus osy.

Příklad (app/src/demo_map.c) obsahuje tři takové objekty: qfwDemoMap (16 × 16 float, osy uint16 / uint8), qfwDemoCurve (8 × uint16) a qfwDemoBigMap (64 × 64 float, limit QInsight); qfwDemoMapOut je skalár závislý na jejich buňkách, takže Watch Table ukáže účinek zapsané buňky okamžitě. Použití v QInsight ukazuje kapitola XCP on TCP na NUCLEO-H723ZG.

tools/elf_vars_to_qinsight.py

Převodník stojící za output.qinsight; ručně jej spouštějte jen tehdy, když chcete jeho volby: --namespace, --strip-prefix <name> (odstranit z zploštělých jmen úvodní jméno struktury), --keep-underscores, --presentation <ref> (přiřadit každé proměnné prezentaci), --address-extension.

python <sdk>/tools/elf_vars_to_qinsight.py my_firmware.vars.csv -o my_firmware_variables.xml --namespace my_ecu

Tři kroky v QInsight

  1. Scan po každém sestavení (elf_vars_scan.py, výše).
  2. Import: Home → Project Configuration → Variables → Import, vyberte XML. Import přeskakuje jména, která v projektu už existují, takže druhý import po novém sestavení nic nezduplikuje — ale ani neaktualizuje adresy. Pokud se adresy posunuly, nejprve staré proměnné smažte (nebo upravte jejich Comm params).
  3. Přiřaďte každou proměnnou k protokolu XCP v Project Configuration a zkopírujte address a addressExtension z popisu do Comm params spolu s direction a eventRef (viz Přidání proměnné).

Důležité: Naimportovaná (nebo ručně založená) proměnná, která není přiřazena k protokolu, nemá zdroj dat a nelze ji navázat na control. Poznáte ji v Solution Exploreru: tooltip proměnné ukazuje Source: not found a její ikona nemá modrý odznak, který přiřazené proměnné mají. Nápravou je krok 3 — přiřadit proměnnou k protokolu XCP v Project Configuration a vyplnit Comm params.

Alternativy bez nástrojů: linkerový .map vedle ELF (vyhledat symbol, ručně přičíst offset člena — příklad offsety fixuje přes static_assert), nebo debugger (print &gDemoSignals v gdb). Obojí scanner automatizuje.

Dále

Řešení potíží — regenerace projektu CubeMX a tabulka příznaků.