Troubleshooting the SDK integration

Regenerating a CubeMX project

The example is a CubeMX project; Generate Code overwrites everything outside USER CODE BEGIN/END. Before you regenerate, read examples/nucleo_h723zg/REGEN-NOTES.md (for the bare-metal project examples/nucleo_h723zg_baremetal/REGEN-NOTES.md) — the short version:

  • Put your calls only in USER CODE sections with names CubeMX knows. An invented section name is silently dropped by the next generation.
  • Task names, priorities and stacks live in the .ioc (Tasks and Queues) — change them there, not in freertos.c.
  • lwipopts.h values that CubeMX knows are carried by the .ioc; the USER CODE block in it (LWIP_TCP_KEEPALIVE, LWIP_SO_LINGER, …) survives; FreeRTOSConfig.h likewise (configNUM_THREAD_LOCAL_STORAGE_POINTERS in USER Defines).
  • STM32H7 memory: the Ethernet DMA cannot reach DTCM RAM, so the example's linker script places .data/.bss/heap/stack in AXI SRAM (RAM_D1) and the DMA descriptors plus the RX buffer pool in RAM_D2 (32 KB on the H723). The linker script is fully overwritten by regeneration — restore it from your version control.
  • LWIP/Target/ethernetif.c: INTERFACE_THREAD_STACK_SIZE (ST default 350 B = stack overflow with CMSIS v2, use 2048) and ETH_RX_BUFFER_CNT (16 in the example; the RX pool must fit RAM_D2, an ASSERT in the linker script fails the build otherwise) are reverted by every regeneration — they are not carried by the .ioc.
  • The lwIP heap in the example is placed by LWIP_RAM_HEAP_POINTER at a raw address at the top of AXI SRAM; RAM_D1 is shortened in the linker script so that linker-placed data can never reach it.
  • 550 MHz on the H723 needs the CPU_FREQ_BOOST option byte; HAL code does not program option bytes: STM32_Programmer_CLI -c port=SWD -ob CPU_FREQ_BOOST=1 once per board.
  • After every regeneration: git diff is the checklist, then build (the linker ASSERTs guard the RAM layout), then re-run the address scanner (Variable addresses for QInsight).

Symptoms, causes, fixes

SymptomLikely causeWhat to do
Red LED after reset (QfwXcpServer_Init false)get_clock_us == NULL; recursive mutex or task not created (configSUPPORT_STATIC_ALLOCATION, configUSE_RECURSIVE_MUTEXES); qfw_xcp_init refused the config (init failure reasons); on CAN: unsupported bitrate or HAL FDCAN init failedset log_level 3, read the virtual COM port; check the FreeRTOS config; compare with the example's xcp_server.c
Bare metal: QfwXcpTcpRawGlue_Init returns false (Error_Handler)called before MX_LWIP_Init() (the listening pcb cannot be created), get_clock_us == NULL, qfw_xcp_init refused the configcall order in main.c as in the example (Integration without an RTOS); log_level 3
Bare metal: the glue fails to compile with #error or _Static_assertNO_SYS is not 1, LWIP_TCP_KEEPALIVE 1 is missing, or TCP_SND_BUF is smaller than an engine segment (1480 B)add the options to the USER CODE BEGIN 1 block of lwipopts.h (lwIP without an RTOS)
Nothing listens on port 5555 (no listening line on the COM port)glue task not running (stack, priority, static allocation), lwIP not up when QfwXcpGlue_Init ran (call it after MX_LWIP_Init()), wrong IP or subnet; bare metal: MX_LWIP_Process() is not called in the loopping the board first; check task creation; call order in StartDefaultTask (bare metal: the loop body in main.c)
QInsight: connect timeout although ping worksfirewall on the PC, wrong port, board already holds a connection of a previous master that vanished (see next row)check the port; wait for the keepalive to expire (~5 s) or reset the board
Master vanished, board stays connected and refuses the next master for longer than ~5 sLWIP_TCP_KEEPALIVE not 1 (the glue's TCP_KEEPIDLE/INTVL/CNT then have no effect), or LWIP_SO_LINGER not 1 (close lingers; FreeRTOS glue only)set both in lwipopts.h (CubeMX settings); the glue then resets to accept within ~5 s — or at once when the board's own link goes down and the link callback in lwip.c is wired (FreeRTOS, bare metal)
QInsight: connected and right away forcibly closed by the remote host, repeating every secondthe board has another master connected (a second QInsight instance, another client on port 5555) and refuses the new connection; or this PC's previous session has not timed out yet after an outage (keepalive ~11 s)end the first session or wait for the keepalive; the driver connects by itself (connections without an RTOS)
Bare metal: responses and DAQ data arrive late or in batchesa long main-loop pass (log output at level 3 ≈ 4 ms per line, blocking work in the loop), or Nagle and the master's delayed ACK on slow streamsshorten the work per pass, log_level 2; see the context model
Timestamps jump, go backwards or run at the wrong rateClock64_Update not called, called from more than one task, or CLOCK_TICKS_PER_S ≠ real CPU clockone caller (QfwXcpServer_Tick from the default task, period < 7.8 s); set CLOCK_TICKS_PER_S from the clock tree
DAQ data stops, QInsight reports counter gaps / overruntransmit queue overflow or transport backpressure (qfw_xcp_is_backpressured()); on CAN the bus limit (sizing and throughput); bare metal: TCP_SND_BUF too small for a sustained streamreduce the measurement set or rate; on TCP enlarge GLUE_QUEUE_SEGMENTS (bare metal: TCP_SND_BUF too); check Ethernet load
A matrix loads but contains garbage or lacks its axesaddress from another build; the object is not one contiguous block X axis, Y axis, data (other order, padding between members); the axes have a different number of points than the datare-run the scanner; follow the convention of Arrays, curves and maps (pack(1), <name>_x / <name>_y / <name>)
STIM value never appliedthe STIM list's event is not triggered after your update, or your code overwrites the variable after the triggertrigger once per cycle after the update; see the raw/value/bypass split in demo_signals.cpp
Random corruption / hard fault under loadcritical section not reentrant, rx and poll from two contexts, trigger from an ISR with a mutex-based section; bare metal: qfw_xcp_rx / qfw_xcp_poll or lwIP called from an interrupt (engine contract)keep the shipped glue structure; bare metal: from an interrupt only qfw_xcp_trigger_event (covered by the glue's PRIMASK section), nothing else
CAN: no response to CONNECTtransceiver wiring or termination, bitrate or ids do not match QInsight, frames on the master id do not reach qfw_xcp_rx_can_frame (filter)check [XCP I] SDK server on CAN: … on the COM port, then the bus with a CAN monitor (XCP on classic CAN)
CAN: [XCP W] CAN bus-off, recovering repeatingno other node acknowledging (transceiver, termination, bitrate mismatch)fix the bus; the glue recovers automatically and resets the session
QInsight: variable shows Source: not found, no blue badge, cannot be bound to a controlthe variable is defined but not assigned to a protocolassign it to the XCP protocol and fill the Comm params (three steps in QInsight)
QInsight reads garbage or writes hit the wrong memoryaddresses from an older build, Release image without volatile, or a rebuilt image with moved objectsre-run the scanner, re-import or fix Comm params; make the variables volatile globals

Support: info@qenex.net — include qfw_xcp_version(), the glue log around the problem and your glue config values. The QInsight side of XCP problems is covered in Troubleshooting — XCP and DAQ.