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 CODEsections 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 infreertos.c. lwipopts.hvalues that CubeMX knows are carried by the.ioc; theUSER CODEblock in it (LWIP_TCP_KEEPALIVE,LWIP_SO_LINGER, …) survives;FreeRTOSConfig.hlikewise (configNUM_THREAD_LOCAL_STORAGE_POINTERSin USER Defines).- STM32H7 memory: the Ethernet DMA cannot reach DTCM RAM, so the example's
linker script places
.data/.bss/heap/stackin AXI SRAM (RAM_D1) and the DMA descriptors plus the RX buffer pool inRAM_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) andETH_RX_BUFFER_CNT(16 in the example; the RX pool must fitRAM_D2, anASSERTin 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_POINTERat a raw address at the top of AXI SRAM;RAM_D1is shortened in the linker script so that linker-placed data can never reach it. - 550 MHz on the H723 needs the
CPU_FREQ_BOOSToption byte; HAL code does not program option bytes:STM32_Programmer_CLI -c port=SWD -ob CPU_FREQ_BOOST=1once per board. - After every regeneration:
git diffis the checklist, then build (the linkerASSERTs guard the RAM layout), then re-run the address scanner (Variable addresses for QInsight).
Symptoms, causes, fixes
| Symptom | Likely cause | What 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 failed | set 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 config | call 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_assert | NO_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 loop | ping the board first; check task creation; call order in StartDefaultTask (bare metal: the loop body in main.c) |
QInsight: connect timeout although ping works | firewall 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 s | LWIP_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 second | the 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 batches | a 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 streams | shorten the work per pass, log_level 2; see the context model |
| Timestamps jump, go backwards or run at the wrong rate | Clock64_Update not called, called from more than one task, or CLOCK_TICKS_PER_S ≠ real CPU clock | one 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 / overrun | transmit 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 stream | reduce 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 axes | address 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 data | re-run the scanner; follow the convention of Arrays, curves and maps (pack(1), <name>_x / <name>_y / <name>) |
| STIM value never applied | the STIM list's event is not triggered after your update, or your code overwrites the variable after the trigger | trigger once per cycle after the update; see the raw/value/bypass split in demo_signals.cpp |
| Random corruption / hard fault under load | critical 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 CONNECT | transceiver 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 repeating | no 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 control | the variable is defined but not assigned to a protocol | assign it to the XCP protocol and fill the Comm params (three steps in QInsight) |
| QInsight reads garbage or writes hit the wrong memory | addresses from an older build, Release image without volatile, or a rebuilt image with moved objects | re-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.

