Skip to content

Commit 871b843

Browse files
authored
Merge pull request #3561 from hathach/rp2040-hcd-epx
rp2 hcd rework: epx for non-interrupt and ping-pong double buffered
2 parents eae25b5 + b6f04f8 commit 871b843

File tree

15 files changed

+1366
-863
lines changed

15 files changed

+1366
-863
lines changed

.claude/commands/build-doc.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# build-doc
2+
3+
Scan all example READMEs and build the Sphinx documentation.
4+
5+
## Instructions
6+
7+
1. Install docs dependencies:
8+
```bash
9+
pip install -r docs/requirements.txt
10+
```
11+
12+
2. Build the docs from the repo root:
13+
```bash
14+
sphinx-build -b html docs docs/_build
15+
```
16+
`conf.py` automatically scans all `examples/{device,host,dual}/*/README.md`, copies them into `docs/examples/`, and regenerates `examples.rst` with the toctree.
17+
18+
3. Use a timeout of at least 60 seconds.
19+
20+
4. After the build completes:
21+
- Show the build output to the user.
22+
- Report total warnings and errors.
23+
- List which example READMEs were discovered and included.
24+
- If there are errors, suggest fixes.

.claude/commands/hil.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# hil
2+
3+
Run Hardware-in-the-Loop (HIL) tests on physical boards.
4+
5+
## Arguments
6+
- $ARGUMENTS: Optional flags (e.g. board name, extra args). If empty, runs all boards with default config.
7+
8+
## Instructions
9+
10+
1. Determine the HIL config file:
11+
```bash
12+
HIL_CONFIG=$( (systemctl list-units --type=service --state=running 2>/dev/null; systemctl --user list-units --type=service --state=running 2>/dev/null) | grep -q 'actions\.runner' && echo tinyusb.json || echo local.json )
13+
```
14+
Default is `local.json` for local development.
15+
16+
2. Parse $ARGUMENTS:
17+
- If $ARGUMENTS contains `-b BOARD_NAME`, run for that specific board only.
18+
- If $ARGUMENTS is empty or has no `-b`, run for all boards in the config.
19+
- Pass through any other flags (e.g. `-v` for verbose) directly to the command.
20+
21+
3. Run the HIL test from the repo root directory:
22+
- Specific board: `python test/hil/hil_test.py -b BOARD_NAME -B examples $HIL_CONFIG $EXTRA_ARGS`
23+
- All boards: `python test/hil/hil_test.py -B examples $HIL_CONFIG $EXTRA_ARGS`
24+
25+
4. Use a timeout of at least 20 minutes (600000ms). HIL tests take 2-5 minutes. NEVER cancel early.
26+
27+
5. After the test completes:
28+
- Show the test output to the user.
29+
- Summarize pass/fail results per board.
30+
- If there are failures, suggest re-running with `-v` flag for verbose output to help debug.

examples/host/cdc_msc_hid/src/msc_app.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,14 @@ static bool inquiry_complete_cb(uint8_t dev_addr, tuh_msc_complete_data_t const
4141
}
4242

4343
// Print out Vendor ID, Product ID and Rev
44-
printf("%.8s %.16s rev %.4s\r\n", inquiry_resp.vendor_id, inquiry_resp.product_id, inquiry_resp.product_rev);
44+
printf("%.8s %.16s %.4s\r\n", inquiry_resp.vendor_id, inquiry_resp.product_id, inquiry_resp.product_rev);
4545

4646
// Get capacity of device
4747
uint32_t const block_count = tuh_msc_get_block_count(dev_addr, cbw->lun);
4848
uint32_t const block_size = tuh_msc_get_block_size(dev_addr, cbw->lun);
4949

50-
printf("Disk Size: %" PRIu32 " MB\r\n", block_count / ((1024*1024)/block_size));
51-
printf("Block Count = %" PRIu32 ", Block Size: %" PRIu32 "\r\n", block_count, block_size);
50+
printf("Disk Size: %" PRIu32 " %" PRIu32 "-byte blocks: %" PRIu32 " MB\r\n",
51+
block_count, block_size, block_count / ((1024 * 1024) / block_size));
5252

5353
return true;
5454
}

examples/host/cdc_msc_hid_freertos/src/msc_app.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,14 @@ static bool inquiry_complete_cb(uint8_t dev_addr, tuh_msc_complete_data_t const
4747
}
4848

4949
// Print out Vendor ID, Product ID and Rev
50-
printf("%.8s %.16s rev %.4s\r\n", scsi_resp.inquiry.vendor_id, scsi_resp.inquiry.product_id, scsi_resp.inquiry.product_rev);
50+
printf("%.8s %.16s %.4s\r\n", scsi_resp.inquiry.vendor_id, scsi_resp.inquiry.product_id, scsi_resp.inquiry.product_rev);
5151

5252
// Get capacity of device
5353
uint32_t const block_count = tuh_msc_get_block_count(dev_addr, cbw->lun);
5454
uint32_t const block_size = tuh_msc_get_block_size(dev_addr, cbw->lun);
5555

56-
printf("Disk Size: %" PRIu32 " MB\r\n", block_count / ((1024 * 1024) / block_size));
57-
printf("Block Count = %" PRIu32 ", Block Size: %" PRIu32 "\r\n", block_count, block_size);
56+
printf("Disk Size: %" PRIu32 " %" PRIu32 "-byte blocks: %" PRIu32 " MB\r\n",
57+
block_count, block_size, block_count / ((1024 * 1024) / block_size));
5858

5959
return true;
6060
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# MSC File Explorer
2+
3+
This host example implements an interactive command-line file browser for USB Mass Storage devices.
4+
When a USB flash drive is connected, the device is automatically mounted using FatFS and a shell-like
5+
CLI is presented over the board's serial console.
6+
7+
## Features
8+
9+
- Automatic mount/unmount of USB storage devices
10+
- FAT12/16/32 filesystem support via FatFS
11+
- Interactive CLI with command history
12+
- Read speed benchmarking with `dd`
13+
- Support for up to 4 simultaneous USB storage devices (via hub)
14+
15+
## Supported Commands
16+
17+
| Command | Usage | Description |
18+
|---------|--------------------|------------------------------------------------------|
19+
| help | `help` | Print list of available commands |
20+
| cat | `cat <file>` | Print file contents to the console |
21+
| cd | `cd <dir>` | Change current working directory |
22+
| cp | `cp <src> <dest>` | Copy a file |
23+
| dd | `dd [count]` | Read sectors and report speed (default 1024 sectors) |
24+
| ls | `ls [dir]` | List directory contents |
25+
| pwd | `pwd` | Print current working directory |
26+
| mkdir | `mkdir <dir>` | Create a directory |
27+
| mv | `mv <src> <dest>` | Rename/move a file or directory |
28+
| rm | `rm <file>` | Remove a file |
29+
30+
## Build
31+
32+
Build for a specific board using CMake (see [Getting Started](https://docs.tinyusb.org/en/latest/getting_started.html)):
33+
34+
```bash
35+
# Example: build for Raspberry Pi Pico
36+
cmake -B build -DBOARD=raspberry_pi_pico -DFAMILY=rp2040 examples/host/msc_file_explorer
37+
cmake --build build
38+
```
39+
40+
## Usage
41+
42+
1. Flash the firmware to your board.
43+
2. Open a serial terminal (e.g. `minicom`, `screen`, `PuTTY`) at 115200 baud.
44+
3. Plug a USB flash drive into the board's USB host port.
45+
4. The device is auto-mounted and the prompt appears:
46+
47+
```
48+
TinyUSB MSC File Explorer Example
49+
50+
Device connected
51+
Vendor : Kingston
52+
Product : DataTraveler 2.0
53+
Rev : 1.0
54+
Capacity: 1.9 GB
55+
56+
0:/> _
57+
```
58+
59+
### Browsing Files
60+
61+
```
62+
0:/> ls
63+
----a 1234 readme.txt
64+
d---- 0 photos
65+
d---- 0 docs
66+
67+
0:/> cd photos
68+
0:/photos> ls
69+
----a 520432 vacation.jpg
70+
----a 312088 family.png
71+
72+
0:/> cat readme.txt
73+
Hello from USB drive!
74+
```
75+
76+
### Copying and Moving Files
77+
78+
```
79+
0:/> cp readme.txt backup.txt
80+
0:/> mv backup.txt docs/backup.txt
81+
```
82+
83+
### Measuring Read Speed
84+
85+
```
86+
0:/> dd
87+
Reading 1024 sectors...
88+
Data speed: 823 KB/s
89+
```
90+
91+
### Multiple Devices
92+
93+
When using a USB hub, multiple drives are mounted as `0:`, `1:`, etc. Use the drive prefix to
94+
navigate between them:
95+
96+
```
97+
0:/> cd 1:
98+
1:/> ls
99+
```
100+
101+
## Testing
102+
103+
This example is part of the TinyUSB HIL (Hardware-in-the-Loop) test suite. The HIL test
104+
automatically flashes, runs the example, and verifies MSC enumeration and file operations
105+
against a known USB drive.

examples/host/msc_file_explorer/src/msc_app.c

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
#define CLI_RX_BUFFER_SIZE 16
4747
#define CLI_CMD_BUFFER_SIZE 64
4848
#define CLI_HISTORY_SIZE 32
49-
#define CLI_BINDING_COUNT 8
49+
#define CLI_BINDING_COUNT 9
5050

5151
static EmbeddedCli *_cli;
5252
static CLI_UINT cli_buffer[BYTES_TO_CLI_UINTS(CLI_BUFFER_SIZE)];
@@ -56,7 +56,11 @@ static CFG_TUH_MEM_SECTION FATFS fatfs[CFG_TUH_DEVICE_MAX]; // for simplicity on
5656
static volatile bool _disk_busy[CFG_TUH_DEVICE_MAX];
5757

5858
static CFG_TUH_MEM_SECTION FIL file1, file2;
59-
static CFG_TUH_MEM_SECTION uint8_t rw_buf[512];
59+
60+
#ifndef CFG_EXAMPLE_MSC_FILE_EXPLORER_RW_BUFSIZE
61+
#define CFG_EXAMPLE_MSC_FILE_EXPLORER_RW_BUFSIZE 4096
62+
#endif
63+
static CFG_TUH_MEM_SECTION uint8_t rw_buf[CFG_EXAMPLE_MSC_FILE_EXPLORER_RW_BUFSIZE];
6064

6165
// define the buffer to be place in USB/DMA memory with correct alignment/cache line size
6266
CFG_TUH_MEM_SECTION static struct {
@@ -114,15 +118,15 @@ static bool inquiry_complete_cb(uint8_t dev_addr, const tuh_msc_complete_data_t
114118
}
115119

116120
// Print out Vendor ID, Product ID and Rev
117-
printf("%.8s %.16s rev %.4s\r\n", scsi_resp.inquiry.vendor_id, scsi_resp.inquiry.product_id,
121+
printf("%.8s %.16s %.4s\r\n", scsi_resp.inquiry.vendor_id, scsi_resp.inquiry.product_id,
118122
scsi_resp.inquiry.product_rev);
119123

120124
// Get capacity of device
121125
const uint32_t block_count = tuh_msc_get_block_count(dev_addr, cbw->lun);
122126
const uint32_t block_size = tuh_msc_get_block_size(dev_addr, cbw->lun);
123127

124-
printf("Disk Size: %" PRIu32 " MB\r\n", block_count / ((1024 * 1024) / block_size));
125-
// printf("Block Count = %lu, Block Size: %lu\r\n", block_count, block_size);
128+
printf("Disk Size: %" PRIu32 " %" PRIu32 "-byte blocks: %" PRIu32 " MB\r\n",
129+
block_count, block_size, block_count / ((1024 * 1024) / block_size));
126130

127131
// For simplicity: we only mount 1 LUN per device
128132
const uint8_t drive_num = dev_addr - 1;
@@ -279,6 +283,7 @@ DRESULT disk_ioctl(BYTE pdrv, /* Physical drive nmuber (0..) */
279283
void cli_cmd_cat(EmbeddedCli *cli, char *args, void *context);
280284
void cli_cmd_cd(EmbeddedCli *cli, char *args, void *context);
281285
void cli_cmd_cp(EmbeddedCli *cli, char *args, void *context);
286+
void cli_cmd_dd(EmbeddedCli *cli, char *args, void *context);
282287
void cli_cmd_ls(EmbeddedCli *cli, char *args, void *context);
283288
void cli_cmd_pwd(EmbeddedCli *cli, char *args, void *context);
284289
void cli_cmd_mkdir(EmbeddedCli *cli, char *args, void *context);
@@ -316,6 +321,9 @@ bool cli_init(void) {
316321
embeddedCliAddBinding(_cli, (CliCommandBinding){"cp", "Usage: cp SOURCE DEST\r\n\tCopy SOURCE to DEST.", true, NULL,
317322
cli_cmd_cp});
318323

324+
embeddedCliAddBinding(_cli, (CliCommandBinding){"dd", "Usage: dd [COUNT]\r\n\t" "Read COUNT sectors (default 1024) and report speed.", true, NULL,
325+
cli_cmd_dd});
326+
319327
embeddedCliAddBinding(_cli, (CliCommandBinding){"ls",
320328
"Usage: ls [DIR]...\r\n\tList information about the FILEs (the "
321329
"current directory by default).",
@@ -339,6 +347,69 @@ bool cli_init(void) {
339347
return true;
340348
}
341349

350+
void cli_cmd_dd(EmbeddedCli *cli, char *args, void *context) {
351+
(void)cli;
352+
(void)context;
353+
354+
uint32_t count = 1024; // default sectors to read
355+
if (embeddedCliGetTokenCount(args) >= 1) {
356+
count = (uint32_t)atoi(embeddedCliGetToken(args, 1));
357+
if (count == 0) {
358+
count = 1024;
359+
}
360+
}
361+
362+
// find first mounted MSC device
363+
uint8_t dev_addr = 0;
364+
for (uint8_t i = 1; i <= CFG_TUH_DEVICE_MAX; i++) {
365+
if (tuh_msc_mounted(i)) {
366+
dev_addr = i;
367+
break;
368+
}
369+
}
370+
if (dev_addr == 0) {
371+
printf("no MSC device mounted\r\n");
372+
return;
373+
}
374+
375+
const uint8_t lun = 0;
376+
const uint32_t block_size = tuh_msc_get_block_size(dev_addr, lun);
377+
const uint32_t block_count = tuh_msc_get_block_count(dev_addr, lun);
378+
if (count > block_count) {
379+
count = block_count;
380+
}
381+
382+
const uint16_t sectors_per_xfer = (uint16_t)(sizeof(rw_buf) / block_size);
383+
const uint32_t xfer_count = (count + sectors_per_xfer - 1) / sectors_per_xfer;
384+
385+
printf("dd: reading %" PRIu32 " sectors (%" PRIu32 " bytes), %u sectors/xfer ...\r\n",
386+
count, count * block_size, sectors_per_xfer);
387+
388+
const uint32_t start_ms = tusb_time_millis_api();
389+
const uint8_t pdrv = dev_addr - 1;
390+
391+
for (uint32_t i = 0; i < count; i += sectors_per_xfer) {
392+
const uint16_t n = (uint16_t)((count - i < sectors_per_xfer) ? (count - i) : sectors_per_xfer);
393+
_disk_busy[pdrv] = true;
394+
tuh_msc_read10(dev_addr, lun, rw_buf, i, n, disk_io_complete, 0);
395+
wait_for_disk_io(pdrv);
396+
}
397+
398+
const uint32_t elapsed_ms = tusb_time_millis_api() - start_ms;
399+
const uint32_t total_data = count * block_size;
400+
// each SCSI transaction has 31-byte CBW + data + 13-byte CSW
401+
const uint32_t total_bus = total_data + xfer_count * (31 + 13);
402+
403+
if (elapsed_ms > 0) {
404+
const uint32_t data_kbs = total_data / elapsed_ms; // KB/s (bytes/ms = KB/s)
405+
const uint32_t bus_kbs = total_bus / elapsed_ms;
406+
printf("dd: %" PRIu32 " bytes in %" PRIu32 " ms = %" PRIu32 " KB/s (bus %" PRIu32 " KB/s)\r\n",
407+
total_data, elapsed_ms, data_kbs, bus_kbs);
408+
} else {
409+
printf("dd: %" PRIu32 " bytes in <1 ms\r\n", total_data);
410+
}
411+
}
412+
342413
void cli_cmd_cat(EmbeddedCli *cli, char *args, void *context) {
343414
(void)cli;
344415
(void)context;

hw/bsp/ch32v20x/boards/ch32v203g_r0_1v0/board.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ function(update_board TARGET)
99
target_compile_definitions(${TARGET} PUBLIC
1010
SYSCLK_FREQ_144MHz_HSI=144000000
1111
CFG_EXAMPLE_MSC_DUAL_READONLY
12+
CFG_EXAMPLE_MSC_FILE_EXPLORER_RW_BUFSIZE=1024
1213
)
1314
endfunction()

hw/bsp/rp2040/family.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,11 @@ void board_init(void)
165165
{
166166
#if (CFG_TUH_ENABLED && CFG_TUH_RPI_PIO_USB) || (CFG_TUD_ENABLED && CFG_TUD_RPI_PIO_USB)
167167
// Set the system clock to a multiple of 12mhz for bit-banging USB with pico-usb
168-
set_sys_clock_khz(120000, true);
168+
#if defined(PICO_RP2350) && PICO_RP2350 == 1
169+
set_sys_clock_khz(156000, true); // rp2350 default is 150Mhz
170+
#else
171+
set_sys_clock_khz(120000, true); // rp2040 default is 125Mhz
172+
#endif
169173
// set_sys_clock_khz(180000, true);
170174
// set_sys_clock_khz(192000, true);
171175
// set_sys_clock_khz(240000, true);

0 commit comments

Comments
 (0)