RP2350: fix USB CDC stability (competing tud_task() timers)#47
RP2350: fix USB CDC stability (competing tud_task() timers)#47sensei-hacker wants to merge 11 commits intofeature/rp2350-portfrom
Conversation
Motors 1-4 move from GP8-11 → GP10-13, matching Betaflight's confirmed resource MOTOR1=A10, MOTOR2=A11, MOTOR3=A12, MOTOR4=A13. UART3 (PIO1) moves from GP12/13 → GP8/9 to free the motor pins. PIO capacity is unchanged: PIO0 keeps 4 SMs for DShot on GP10-13, PIO1 keeps 4 SMs for UART3/4 on GP8/9 + GP14/15 (all within 0-31 gpio_base window). Servo count is unchanged at 6 capable outputs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
UART2 (INAV) maps to RP2350 uart1. Per the RP2350 datasheet Table 3, uart1 TX/RX is available on GP6/GP7 via F11 (GPIO_FUNC_UART_AUX), not F2 (which is UART1 CTS/RTS only). SPI0 SCK/MOSI move from GP6/7 to GP2/3 (F1 = SPI0 SCK/TX), which are now free. Previously GP2/3 were incorrectly assigned as UART2 pins: GP2/3 in F2 is UART0 CTS/RTS, and F11 on GP2/3 is UART0 TX/RX — wrong peripheral. The driver also used GPIO_FUNC_UART (F2) for all UART pins; now it uses UART_FUNCSEL_NUM() from the Pico SDK, which selects GPIO_FUNC_UART_AUX (F11) for pins where bit 1 is set (GP6, GP7).
The RP2350 USB serial connection was dropping ~2 seconds after the configurator connected. Two bugs were responsible: 1. Race condition (primary cause): pico_stdio_usb was not informed that TinyUSB device mode was already linked by the application. Without LIB_TINYUSB_DEVICE=1, stdio_usb_init() set up its own low-priority IRQ + alarm calling tud_task(), racing with INAV's own 1 ms repeating timer. Concurrent tud_task() calls corrupt TinyUSB internal state, causing USB CDC to drop after a random interval. Fix: add LIB_TINYUSB_DEVICE=1 to RP2350_DEFINITIONS in cmake/rp2350.cmake. This tells pico_stdio_usb to skip its own timer/IRQ setup entirely. 2. MSP stream corruption (secondary cause): LIB_PICO_STDIO_USB=1 in pico_sdk_config.h allowed printf() output to be written into the same CDC TX FIFO as MSP responses, mixing debug text into the packet stream. The configurator received corrupt data and dropped the connection. Fix: set LIB_PICO_STDIO_USB=0. 3. Missing TinyUSB descriptor callbacks: setting LIB_TINYUSB_DEVICE=1 disables pico_stdio_usb/stdio_usb_descriptors.c (guarded by #if !defined(LIB_TINYUSB_DEVICE)), which previously provided the three mandatory tud_descriptor_*_cb() functions. These are now provided directly in serial_usb_vcp_rp2350.c with INAV-appropriate VID (0x2E8A), PID (0x000B = Pico 2 CDC), the target board name as the product string, and the unique board ID as the serial number. Tested: 30/30 MSP_IDENT requests successful over 30 seconds with no connection drops. USB device enumerates as "Raspberry Pi RP2350_PICO" with unique serial number; INAV 9.0.0 / FC variant "INAV" confirmed.
Review Summary by QodoRP2350: Fix USB CDC stability and correct pin assignments
WalkthroughsDescription• Fix USB CDC stability by eliminating competing tud_task() timers - Define LIB_TINYUSB_DEVICE=1 to prevent pico_stdio_usb from spawning duplicate timer - Disable LIB_PICO_STDIO_USB to prevent debug output corrupting MSP packets - Implement mandatory TinyUSB descriptor callbacks in serial_usb_vcp_rp2350.c • Correct motor and UART pin assignments to match Betaflight RP2350A reference - Renumber motors to GP10–13 (from GP8–11) for Betaflight compatibility - Move UART2 to GP6/7 and UART3 to GP8/9; relocate SPI0 to GP2/3 - Use UART_FUNCSEL_NUM() for proper GPIO function selection • Update comments and documentation for clarity Diagramflowchart LR
A["pico_stdio_usb<br/>competing timer"] -->|"LIB_TINYUSB_DEVICE=1<br/>disables"| B["Single tud_task()<br/>caller"]
C["Debug printf()<br/>to CDC FIFO"] -->|"LIB_PICO_STDIO_USB=0<br/>disables"| D["Clean MSP<br/>packet stream"]
E["Missing TinyUSB<br/>descriptors"] -->|"Add callbacks<br/>in serial_usb_vcp"| F["USB device<br/>enumerates"]
G["Motor pins<br/>GP8-11"] -->|"Renumber to<br/>GP10-13"| H["Betaflight<br/>compatible"]
I["UART2 on GP2/3<br/>SPI0 on GP6/7"] -->|"Swap pins<br/>per datasheet"| J["Correct function<br/>mapping"]
File Changes1. cmake/rp2350.cmake
|
Code Review by Qodo
1.
|
The 1ms repeating hardware alarm in systemInit() is the sole caller of tud_task(). Calling tud_task() from foreground code paths (usbVcpAvailable, usbVcpWriteBuf, usbVcpFlush) while the timer IRQ can preempt them creates re-entrant TinyUSB execution that corrupts shared state. USB full-speed frames are 1ms, so tud_task() at 1ms provides no less throughput than foreground polling; the TX buffer is flushed by tud_cdc_write_flush() and picked up by the host at the next SOF. Update the module comment to document the single-caller requirement.
Convert lib/main/pico-sdk from a git submodule to vendored source, consistent with how all other INAV libraries are managed (STM32 HAL, CMSIS, etc. are all committed directly in lib/). Using a submodule required CI to initialize nested submodules and broke standard git clone workflows. As vendored source, no special git steps are needed and ci.yml requires no changes. Unused pico-sdk nested submodules (cyw43-driver, lwip, mbedtls, btstack) are excluded. Only the SDK core and tinyusb 0.17.0 are included, which is everything the RP2350_PICO target requires. pico-sdk: 2.1.0 (95ea6acad131124694cda1c162c52cd30e0aece0) tinyusb: 0.17.0 (5217cee5de4cd555018da90f9f1bcc87fb1c1d3a)
Remove directories that are not referenced by cmake/rp2350.cmake: - lib/tinyusb/hw/ (1224 files, BSP for other MCUs) - lib/tinyusb/examples/ (328 files) - lib/tinyusb/test/ (246 files) - lib/tinyusb/lib/ (29 files, FatFs etc) - lib/tinyusb/tools/ - lib/tinyusb/.github/ - lib/tinyusb/docs/ - src/rp2040/ (RP2040-specific, wrong chip) - src/boards/ (generic board configs, we use pico_sdk_config.h) - src/host/ (host/PC platform) - test/ (pico-sdk test suite) - tools/ (pioasm compiler, uf2 utility) - .github/ (pico-sdk CI) - bazel/ (bazel build system) - docs/
Remove tinyusb src/ subdirectories not used by the INAV RP2350 build: - portable/* except raspberrypi/ (other MCU USB hardware drivers) - class/* except cdc/ (HID, MSC, MIDI, audio, DFU, etc.) - host/ (USB host stack, we are device-only) - typec/ (USB-C PD, not used)
…ules) Remove subdirectories of src/rp2_common/ not referenced in cmake/rp2350.cmake: - RISC-V specific: hardware_hazard3, hardware_riscv, hardware_riscv_platform_timer, hardware_rcp - Unused hardware: hardware_dcp, hardware_interp, hardware_rtc, hardware_sha256 - Networking/security: pico_btstack, pico_cyw43_arch, pico_cyw43_driver, pico_lwip, pico_mbedtls - Other unused: pico_async_context, pico_bit_ops, pico_bootsel_via_double_reset, pico_cxx_options, pico_fix, pico_i2c_slave, pico_sha256, pico_standard_link, pico_stdio_rtt, pico_stdio_semihosting, pico_stdio_uart
…-V files) Remove files not needed for the INAV RP2350 Cortex-M33 target: - All BUILD.bazel and CMakeLists.txt throughout the vendor tree (we reference sources directly in cmake/rp2350.cmake) - RP2040-specific sources: cmsis RP2040 device headers, pico_crt0 RP2040 linker scripts, pico_float and pico_double RP2040 ROM shims - RISC-V specific sources: exception_table_riscv.S, crt0_riscv.S, float_single_hazard3.S, riscv_dm.h register header - USB host driver hcd_rp2040.c (INAV is USB device only) - Debug/metadata files: RP2350.svd, interface pin JSON files, library.json, pico-sdk cmake/ directory and build infrastructure (pico_sdk_init.cmake, .bazel*, MODULE.bazel, WORKSPACE)
Remove from lib/tinyusb: - Top-level metadata/CI files (.circleci, .clang-format, .codespellrc, README.rst, CONTRIBUTORS, SConscript, pkg.yml, repository.yml, etc.) - CDC host and RNDIS files (INAV is USB device, not host) - src/class/cdc/serial/ headers (ch34x, cp210x, ftdi_sio - not used) - PIO USB drivers (bit-banged USB - INAV uses native USB hardware) - Unused OSAL layers (freertos, mynewt, rtthread, rtx4)
Remove 39 source (.c/.S) files that are not compiled by cmake/rp2350.cmake and are not #included by any file that is compiled: - boot_stage2/: all 10 flash-chip-specific bootloaders (RP2350 uses no boot2 stage; boot ROM handles flash setup directly) - Alternative float/double implementations (dcp, m33, none variants) for pico_float and pico_double - Alternative divider implementations (divider_hardware.S, divider.S) - RTOS and libc alternatives: llvm_libc_interface.c, picolibc_interface.c - Unused hardware drivers: exception.c, powman.c, boot_lock.c, aon_timer.c - Unused pico modules: multicore.c, rand.c, malloc.c, mem_ops.c, printf_none.S Retained: pico/asm_helper.S (included by crt0.S via include path), embedded_start_block.inc.S, embedded_end_block.inc.S (included by crt0.S)
Summary
The RP2350 USB serial connection was dropping ~2 seconds after the configurator connected, preventing the full tab set from loading. Root cause was two concurrent
tud_task()callers corrupting TinyUSB internal state.Changes
cmake/rp2350.cmake: AddLIB_TINYUSB_DEVICE=1toRP2350_DEFINITIONS. Without this flag,pico_stdio_usbcallsstdio_usb_init()which sets up its own low-priority IRQ + repeating alarm callingtud_task(), racing with INAV's own 1 ms timer. The race corrupts TinyUSB state and causes USB CDC to drop after a random interval (~2 s).src/main/target/RP2350_PICO/pico_sdk_config.h: ChangeLIB_PICO_STDIO_USBfrom1to0. When enabled,printf()writes to the same CDC TX FIFO as MSP responses, injecting debug text into the MSP packet stream and causing the configurator to disconnect.src/main/drivers/serial_usb_vcp_rp2350.c: Add the three mandatory TinyUSB descriptor callbacks (tud_descriptor_device_cb,tud_descriptor_configuration_cb,tud_descriptor_string_cb). SettingLIB_TINYUSB_DEVICE=1disablespico_stdio_usb/stdio_usb_descriptors.c(guarded by#if !defined(LIB_TINYUSB_DEVICE)), which previously provided these callbacks. The new descriptors use VID=0x2E8A, PID=0x000B (Pico 2 CDC), the target'sUSBD_PRODUCT_STRINGas the product name, and the unique board ID as the serial number.src/main/drivers/system_rp2350.c: Update comments to accurately describe the single-callertud_task()architecture.Testing
Raspberry Pi RP2350_PICOwith unique board serial numberlsusbshows VID/PID 2e8a:000b (correct for Pico 2 CDC application)