2.5 USB Device Example Code
This project is based on the Raspberry Pi Pico SDK and TinyUSB, and uses PIO to implement USB functions on RP2350/Pico 2.
The project currently contains two main examples:
device_mouse: Emulates RP2350 as a USB mouse and automatically sends relative displacement reports according to a preset trajectory.keyboard_cdc: Emulates RP2350 as a USB keyboard, with the BOOTSEL button configured as a single key.
2.5.1 Project Structure
RP2350-USB-A-1/
├── CMakeLists.txt # Project entry, initializes Pico SDK and adds examples
├── CMakePresets.json # CMake configuration for RP2350/Pico 2
├── pico_sdk_import.cmake # Pico SDK import script
├── src/
│ ├── pio_usb.c # PIO USB core logic
│ ├── pio_usb_device.c # PIO USB device mode
│ ├── pio_usb_host.c # PIO USB host mode
│ ├── usb_tx.pio # USB transmit PIO program
│ ├── usb_rx.pio # USB receive PIO program
│ ├── usb_crc.c # USB CRC calculation
│ └── pio_usb_configuration.h # Default configuration for PIO, state machine, DMA, and pins
├── examples/
│ ├── CMakeLists.txt # Common example configuration
│ ├── device_mouse/
│ │ ├── device_mouse.c # USB mouse device and trajectory logic
│ │ └── CMakeLists.txt
│ └── keyboard_cdc/
│ ├── keyboard_cdc.c # USB keyboard, BOOTSEL button, and CDC status output
│ ├── usb_descriptors.c # USB descriptors for Type-C CDC
│ ├── tusb_config.h # TinyUSB device configuration
│ └── CMakeLists.txt
├── .vscode/ # VS Code build, debug, and task configuration
├── build/pico2/ # CMake/Ninja generated build directory
└── uf2/ # Copies of UF2 files saved in the project
The root CMakeLists.txt generates the pico_pio_usb interface library, which is then linked by the two examples. During compilation, CMake automatically generates corresponding PIO header files from .pio files.
2.5.2 Environment
Current project configuration uses the following tool versions:
- Raspberry Pi Pico SDK: 2.3.1
- ARM GNU Toolchain: 15_2_Rel1
- picotool: 2.3.1
- CMake: version supported by the Pico extension is recommended
- Ninja: current VS Code configuration uses 1.13.2
- VS Code (optional): Raspberry Pi Pico, C/C++, and Cortex-Debug extensions
The default build board is pico2. The default SDK location is:
C:\Users\<username>\.pico-sdk\sdk\2.3.1
If the SDK or toolchain is installed elsewhere, modify CMakePresets.json. Do not only modify temporary settings in the VS Code UI.
2.5.3 Build Output and UF2 Files
The UF2 files for the examples are located at:
build/pico2/examples/device_mouse/device_mouse.uf2
build/pico2/examples/keyboard_cdc/keyboard_cdc.uf2
When flashing, preferentially use the files just generated under build/pico2/examples/.... The files under the root uf2/ directory are only saved copies; the build system does not automatically copy newly generated UF2 files to that directory. Therefore, after modifying and compiling the program, use the corresponding target file under build/pico2.
2.5.4 Example Function Description
1. device_mouse
This example enumerates RP2350 as a USB HID mouse via PIO USB. After connecting to a computer host with a USB male-to-male cable, the computer cursor will move according to a preset trajectory, and "RP2350 USB MINI Mouse" can be seen in Settings → Bluetooth & other devices. If the computer shows that the device cannot be recognized, unplug the device and reconnect it, or re-flash the firmware.
The current default trajectory is:
left → back to center → right → back to center → up → down → back to center → repeat.
The mouse movement direction is output through the Type-C CDC serial port. Direction information such as left, right, up, and down is output only when a USB host is connected and activity detection has completed.
Modify mouse movement distance, speed, and trajectory.
The movement rules of device_mouse are mainly controlled by the top macros and mouse_trajectory.
Method 1: Modify top macros (device_mouse.c)
#define MOUSE_HORIZONTAL_DISTANCE_PIXELS 200
#define MOUSE_VERTICAL_DISTANCE_PIXELS 200
#define MOUSE_DOWN_DISTANCE_PIXELS (2 * MOUSE_VERTICAL_DISTANCE_PIXELS)
#define MOUSE_ENABLE_DIAGONAL 0
#define MOUSE_DIAGONAL_DISTANCE_PIXELS 200
#define MOUSE_DIAGONAL_DOWN_DISTANCE_PIXELS (2 * MOUSE_DIAGONAL_DISTANCE_PIXELS)
#define MOUSE_STEP_PIXELS 10
The role of each macro:
MOUSE_HORIZONTAL_DISTANCE_PIXELS // Horizontal movement distance
Horizontal movement (only horizontal distance retained):
#define MOUSE_HORIZONTAL_DISTANCE_PIXELS 0
#define MOUSE_DOWN_DISTANCE_PIXELS 0
#define MOUSE_ENABLE_DIAGONAL 0
Vertical movement (only vertical distance retained):
#define MOUSE_HORIZONTAL_DISTANCE_PIXELS 0
#define MOUSE_VERTICAL_DISTANCE_PIXELS 200
#define MOUSE_DOWN_DISTANCE_PIXELS (2 * MOUSE_VERTICAL_DISTANCE_PIXELS)
#define MOUSE_ENABLE_DIAGONAL 0
Diagonal movement (enable diagonal movement, diagonal distance not 0):
#define MOUSE_HORIZONTAL_DISTANCE_PIXELS 0
#define MOUSE_VERTICAL_DISTANCE_PIXELS 0
#define MOUSE_DOWN_DISTANCE_PIXELS 0
#define MOUSE_ENABLE_DIAGONAL 1
#define MOUSE_DIAGONAL_DISTANCE_PIXELS 200
#define MOUSE_DIAGONAL_DOWN_DISTANCE_PIXELS (2 * MOUSE_DIAGONAL_DISTANCE_PIXELS)
MOUSE_STEP_PIXELS controls the step size, and MOUSE_REPORT_INTERVAL_MS controls the speed.
Method 2: Modify mouse_trajectory[] (device_mouse.c line 118)
Direction format: {x, y, distance}
Modify the start coordinates, intermediate waypoint coordinates, and end coordinates to control direction. distance is the distance to move in the current coordinate direction.
Direction mapping:
| Value | Direction |
|---|---|
| x = -1 | Left |
| y = -1 | Up |
| x = +1 | Right |
| y = +1 | Down |
| x = 0 | No horizontal movement |
| y = 0 | No vertical movement |
Custom diagonal direction:
Ensure the top macro enables diagonal movement and sets the diagonal movement distance.
#define MOUSE_ENABLE_DIAGONAL 1
#define MOUSE_DIAGONAL_DISTANCE_PIXELS 200
Modify the diagonal direction coordinates (MOUSE_ENABLE_DIAGONAL) (the following is an example of moving 200 pixels to the upper right):
{+1, -1, distance} // upper right
{-1, +1, 2 * distance} // lower left
{+1, -1, distance} // back to center
2. keyboard_cdc
This example includes two USB functions:
- The USB-A side enumerates as an HID keyboard via PIO USB.
- The Type-C native USB side provides a CDC serial monitor.
After connecting to a computer host with a USB male-to-male cable, "RP2350 USB MINI Keyboard" can be seen in Settings → Bluetooth & other devices. The program reads the state of the RP2350 BOOTSEL/QSPI_SS button, sends corresponding keyboard key reports when pressed and released, and outputs status in the serial port. The current key defaults to uppercase letter A. If the computer shows that the device cannot be recognized, unplug the device and reconnect it, or re-flash the firmware.
Modify key value:
Set the key and case at the top of keyboard_cdc.c, as follows:
#define BOOT_KEY A
#define BOOT_KEY_UPPERCASE 1
BOOT_KEY can be set to a key name supported by TinyUSB HID, for example:
#define BOOT_KEY B
For letter keys, BOOT_KEY_UPPERCASE is configured as:
0: fixed lowercase input b1: fixed uppercase input B
For example:
#define BOOT_KEY B
#define BOOT_KEY_UPPERCASE 0
Then pressing the button inputs lowercase letter b.