Matrix variable (array, curve, map)

Besides the scalar variable, QInsight also supports the Matrix variable type — a contiguous block of bytes interpreted as a table of values with optional axes. The typical use case is calibration data of control units: value blocks, characteristics, and maps (the ASAP2 types VAL_BLK, CURVE, and MAP).

Depending on which axes are defined, the variable takes one of three shapes:

ShapeAxesDataExample
ArraynoneN valuesa block of 16 correction constants
CurveX1×N valuesa sensor characteristic (8 points)
MapX + YN×M valuesan ignition map speed × temperature

The axes carry the breakpoints (the points where the map is defined), the data section carries the values themselves. The whole matrix — axes and data — is read from the device as one contiguous block (only the changed cells are written) and is displayed by the Matrix control.

Creating the variable

  1. Open Home → Project Configuration → Variables.
  2. In the drop-down next to the + button choose Matrix and click +.
  3. In the variable detail, fill in — besides the usual properties (Name, Label…) — the three matrix-specific fields: Default Data Type, Endianness, and Layout.

The ThermalField matrix variable in Project ConfigurationThe ThermalField matrix variable in Project Configuration

The screenshot shows the 8 × 6 thermal field from the Simulation: all signals example: element type UShort, little endian and the Layout

xCount="8";xPres="posMm";xLabel="X [mm]";yCount="6";yPres="posMm";yLabel="Y [mm]";dataPres="tempC"

Default Data Type and Endianness

  • Default Data Type — the element type used by every section that does not set its own type in the Layout. The default is byte.
  • Endianness — the byte order of multi-byte elements (Little / Big). It applies to the whole block, i.e. to the axes and the data.

Supported element types (the string type cannot be used in a matrix):

TypeElement size
byte, sbyte1 B
ushort, short2 B
uint, int, float4 B
ulong, long, double8 B

Layout — defining the sections

The Layout field describes the matrix sections with the same key="value";… notation you know from the Settings and Comm params fields. Sections are defined in numbers of values (not bytes) — byte offsets and the total size are derived automatically from the counts and element types.

KeyMeaning
xCountnumber of X-axis breakpoints (columns); providing the key creates the X axis
xTypeelement type of the X axis (otherwise Default Data Type)
xPresname of the presentation for the X axis
xLabelX-axis caption — the Matrix control shows it above the table
yCountnumber of Y-axis breakpoints (rows); requires the X axis
yType, yPres, yLabelthe same for the Y axis (the caption is drawn vertically on the left)
dataCountnumber of data values — only for an array without axes
dataTypeelement type of the data section (otherwise Default Data Type)
dataPresname of the presentation for the data

Rules:

  • An array (no axes) must specify dataCount. With axes, dataCount is not given — the data count is derived: xCount for a curve, xCount × yCount for a map.
  • The Y axis requires the X axis (yCount without xCount is an error).
  • xType/xPres/xLabel without xCount is an error (same for Y).
  • Presentations (xPres, yPres, dataPres) reference the project presentations by name — they must exist. The data section is captioned by the Label of the variable itself; there is no separate dataLabel.
  • An invalid Layout shows up immediately — the field gets a red border and the error is displayed as a tooltip.

Examples

Array — 16 values of type float (64 B):

dataCount="16";dataType="float"

Curve — 8 speed breakpoints and 8 torque values:

xCount="8";xPres="Speed";xLabel="Speed [RPM]";dataPres="Torque"

Map — 3 columns (speed) × 2 rows (temperature), with captions on both axes:

xCount="3";xPres="Speed";xLabel="Speed [RPM]";yCount="2";yPres="Temp";yLabel="Temp. [K]";dataPres="Torque"

Memory layout of the block

The block always has the order: X axis → Y axis → data; map data is stored row-major (index = y × xCount + x). For the 3×2 map from the example above with Default Data Type = ushort and no per-section types:

SectionValuesBytes (offset)
X axis30–5
Y axis26–9
data (2 rows × 3 columns)610–21

11 values = 22 B in total. If you change the type of a section (xType, yType, dataType), the offsets are recomputed automatically.

Per-section presentation and conversion

Each section has its own presentation — axes usually have different units and scaling than the data (speed × temperature × torque). The same rules as for scalar variables apply:

  • A linear conversion turns the raw value from the block into an engineering value (eng = raw × Multiplier + Offset); the presentation's Print Format and Unit define how it is shown.
  • An enum conversion shows the state name instead of the number.
  • A section without a presentation shows the raw value directly.

When editing in write mode you always enter the engineering value — the raw value written to the device is obtained by the inverse conversion (rounded and range-checked against the element data type).

Size limits

The largest possible matrix depends on the protocol you assign it to:

ProtocolLimitReason
Modbus Master — direction="read"125 registers = 250 B (axes + data)the whole matrix is read with a single FC03/FC04 request
Modbus Master — write / readWrite123 registers = 246 B (axes + data)Write Multiple Registers (FC16) carries at most 123 registers
XCP (on CAN and on TCP)data section 16,384 B (64 × 64 × 4) + axesblock-mode transfer, see below

The limit is checked by the protocol, not by the Project Configuration editor — the editor accepts a matrix of any size (only the Layout itself is validated). A matrix that does not fit its protocol's limit is rejected by the protocol: the log shows the warning Protocol variable specification for variable … could not be created (…) with the reason (e.g. …exceeding the Modbus limit of 123 registers (246 bytes) per request. or …the XCP limit is 16384 bytes (64 × 64 × 4).) and the variable is not communicated.

Assigning to a protocol

You assign a matrix to a protocol the same way as a scalar variable (the arrow button into Communicated). Matrices are currently supported by the Modbus Master and XCP (on CAN and on TCP); in the Simulation Signals example the thermal signal generates one as well.

Modbus Master

For the Modbus Master, enter Comm params such as:

registerType="holdingRegister";address="100";direction="readWrite";eventRef="poll500ms"

Differences from a scalar variable:

  • A matrix may only live in a register table (holdingRegister, or inputRegister for reading) — the bit tables coil/discreteInput are rejected by validation.
  • The block occupies a contiguous register range starting at address; register count = size in bytes / 2 (rounded up). The example map (22 B) occupies registers 100–110.
  • The whole matrix is read with a single request FC03/FC04; writes send only the registers of the changed cells (FC06/FC16).
  • wordOrder does not apply to a matrix — the byte order is driven by the variable's Endianness. A Modbus register is 2 bytes, high byte first.

Tip: with ushort cells and Endianness = Big, one cell maps exactly to one register and the values in QInsight match the device registers 1:1. Beware of the traps: byte cells pack two cells into one register, and ushort + Little swaps the bytes (5 becomes 1280).

XCP (on CAN, on TCP)

For XCP the Comm params keys are the same as for a scalar variable, e.g. the ignition map of the Nucleo H723 example:

address="0x24014270";addressExtension="0";direction="readWrite";multiplier="1";eventRef="OnRequest"

The qfwDemoMap (Ignition map) matrix variable assigned to XCP on TCP with the OnRequest eventThe qfwDemoMap (Ignition map) matrix variable assigned to XCP on TCP with the OnRequest event

  • address (+ addressExtension) is the start of the whole block, i.e. the first X-axis element (the start of the data for an array without axes). The section order in ECU memory must follow the memory layout of the block: X axis → Y axis → data row by row.
  • The optional size key must equal the size of the whole block (axes + data), otherwise it is a configuration error. dataType is ignored for a matrix — the element types come from the Layout.
  • The variable's Endianness must match the ECU. The block travels raw and is decoded by the variable's Endianness; a mismatch with the ECU byte order is reported by a warning after connecting (the data is not changed).
  • Event: periodic — the whole matrix is polled as one block every period; On Request — the matrix is not polled and is read with the Read button of the Matrix control. A missing eventRef (with a direction other than write) is a configuration error.
  • A matrix cannot be bound to a DAQ/STIM event — the protocol rejects it with the error cannot bind to the DAQ/STIM event channel … a matrix is polled by a periodic event or read on request.

Block transfers. A block of up to MAX_CTO − 1 bytes (247 B on TCP, 7 B on CAN) is read with a single SHORT_UPLOAD. A larger block is read with one SET_MTA and a series of UPLOADs of at most 255 B, which the ECU answers in block mode with a burst of packets without further requests. Writes use SET_MTA + DOWNLOAD/DOWNLOAD_NEXT in blocks (with master block mode). If the ECU does not support block mode, QInsight chains the transfer packet by packet — it works, only slower. A multi-packet transfer is not atomic.

Cell writes. A write does not send the whole matrix: the edited cells are sorted by offset, neighbouring cells are merged into contiguous windows and each window is written separately. A pasted row of 64 cells is thus a single transfer.

As a rough guide: on TCP (loopback against the QFW SDK pc_server) reading a 1,072 B map takes about 3.5 ms and a 64 × 64 map (16,640 B) about 6 ms. On CAN (500 kbit/s) the times are orders of magnitude longer.

Complete projects with maps, a curve and a 64 × 64 map are in the XCP on TCP — calibration maps and XCP on CAN — calibration maps examples.

Next

Display and edit the matrix with the Matrix control.