Skip to content

CLI AutoComplete stalls on HELIOSPRING/STRIXF10/MODE2FLUX boards #1106

@nerdCopter

Description

@nerdCopter

AI generated issue-ticket


Problem

CLI AutoComplete builder intermittently stalls with double "Building AutoComplete Cache..." messages on HELIOSPRING (HESP), STRIXF10 (SX10), and MODE2FLUX (FLUX) boards, requiring USB unplug/replug to recover.

Behavior:

  • Works reliably (~70-90% success rate) if user waits and low system activity
  • Fails when switching tabs rapidly or during high background activity (OSD rendering, telemetry, blackbox)
  • Stall manifests as: double build text, configurator 3s watchdog timeout, sync loss

Root Cause

USB VCP write starvation under scheduler load on boards with USE_DMA_SPI_DEVICE + USE_GYRO_IMUF9001:

  1. Gyro EXTI interrupt fires at NVIC priority 0 (highest), reading IMUF data at 8–32 kHz
  2. USB OTG interrupt runs at priority 2 (lower)
  3. During CLI dump command (~100 USB flushes of 64-byte buffer):
    • Each flush calls CDC_Send_DATA() with 50ms timeout
    • Gyro EXTI constantly preempts USB interrupt
    • Under scheduler load, USB endpoint can't drain before timeout expires
    • Bytes silently dropped → sentinel echoes lost → watchdog fires → stall

Affected boards:

  • HELIOSPRING (STM32F405 + STM32F3, dma_spi.c)
  • STRIXF10 (STM32F7x2RE, dma_spi_hal.c)
  • MODE2FLUX (STM32F405, dma_spi.c)

All three use USE_DMA_SPI_DEVICE + USE_GYRO_IMUF9001 + MSP_OVER_CLI.

Solution

Disable gyro EXTI interrupt during CLI mode (FC is arming-disabled anyway; IMUF keeps running independently):

File: src/main/interface/cli.c

In cliEnter():

void cliEnter(serialPort_t *serialPort) {
    cliMode = 1;
    cliPort = serialPort;
    setPrintfSerialPort(cliPort);
    cliWriter = bufWriterInit(cliWriteBuffer, sizeof(cliWriteBuffer), (bufWrite_t)serialWriteBufShim, serialPort);
#ifdef USE_DMA_SPI_DEVICE
    // Disable gyro EXTI during CLI to prevent preemption of USB writes
    EXTIEnable(IOGetByTag(IO_TAG(MPU_INT_EXTI)), false);
#endif
    // ...rest of cliEnter()
}

In cliExit():

static void cliExit(char *cmdline) {
    UNUSED(cmdline);
    cliPrintHashLine("leaving CLI mode, unsaved changes lost");
    bufWriterFlush(cliWriter);
    *cliBuffer = '\0';
    bufferIndex = 0;
    cliMode = 0;
    mixerResetDisarmedMotors();
#ifdef USE_DMA_SPI_DEVICE
    // Re-enable gyro EXTI before reboot
    EXTIEnable(IOGetByTag(IO_TAG(MPU_INT_EXTI)), true);
#endif
    cliReboot();
    cliWriter = NULL;
}

In cliRebootEx():

static void cliRebootEx(bool bootloader) {
    UNUSED(bootloader);
    cliPrintHashLine("rebooting");
    bufWriterFlush(cliWriter);
    *cliBuffer = '\0';
    bufferIndex = 0;
    cliMode = 0;
    mixerResetDisarmedMotors();
#ifdef USE_DMA_SPI_DEVICE
    // Re-enable gyro EXTI before reboot
    EXTIEnable(IOGetByTag(IO_TAG(MPU_INT_EXTI)), true);
#endif
    // ...rest of cliRebootEx()
}

Why This Works

  • ✅ USB drains cleanly (no preemption from gyro EXTI during CLI output)
  • ✅ IMUF version query still works (uses blocking SPI, not EXTI)
  • ✅ Flight unaffected (EXTI re-enabled on CLI exit)
  • ✅ IMUF safe (continues filtering internally; FC just stops reading data-ready signal)
  • ✅ Resource cost negligible (~10 bytes code, zero SRAM)

Testing

  1. Enter CLI immediately after USB connect (no manual wait)
  2. Rapid tab switching before/during autocomplete build (repeat 10× → should work 100%)
  3. Verify # version returns IMUF version correctly
  4. Exit CLI, fly 30-second hover, verify gyro response is smooth
  5. Test on HELIOSPRING, STRIXF10, MODE2FLUX
  6. Verify no impact on boards without USE_DMA_SPI_DEVICE

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions