我正在尝试建立从 PI 4 到 Pi Pico 的“快速”数据传输。根据规格,Pico 的 USB 控制器应该能够达到 12 Mbit/s。
我当前使用的代码速度很慢。如果我们接收到一个字节,代码将 GPIO 引脚 2 设置为高电平;在接收到 2048 个字节后或在超时的情况下设置为低电平:
#include <stdio.h>
#include "pico/stdlib.h"
const uint8_t DEBUG_PIN = 2;
int main() {
stdio_usb_init();
uint16_t numBytesReceived = 0;
int currentChar = PICO_ERROR_TIMEOUT;
gpio_init(DEBUG_PIN);
gpio_set_dir(DEBUG_PIN, GPIO_OUT);
while (true)
{
currentChar = getchar_timeout_us(50000);
if (currentChar != PICO_ERROR_TIMEOUT) {
gpio_put(DEBUG_PIN, 1);
numBytesReceived++;
if (2048 == numBytesReceived) {
gpio_put(DEBUG_PIN, 0);
numBytesReceived = 0;
}
}
else {
gpio_put(DEBUG_PIN, 0);
numBytesReceived = 0;
}
}
}
在我的 Pi 4 上,我使用代码进行测试
import serial
import time
serialPort = "/dev/ttyACM0"
ser = serial.Serial(
port=serialPort
)
while True:
data_array = bytearray(2048)
ser.write(data_array)
ser.flush()
time.sleep(0.2)
用示波器测量,传输时间在24ms到29ms之间。在最好的情况下,我的吞吐量为
2048 * 8 / 0.024ms = 680 kBit/s,
距离 12 Mbit/s 的目标还很远。
使用 pico 示例 dev_lowlevel。我注释掉了所有 printf() 调用并修改了 main() 和 ep1_out_handler() 以用于测量目的,如下所示:
int main(void) {
stdio_init_all();
//printf("USB Device Low-Level hardware example\n");
usb_device_init();
gpio_init(DEBUG_PIN);
gpio_set_dir(DEBUG_PIN, GPIO_OUT);
g_debug_state = 0;
gpio_put(DEBUG_PIN, g_debug_state);
g_debug_state = 1;
// Wait until configured
while (!configured) {
tight_loop_contents();
}
// Get ready to rx from host
usb_start_transfer(usb_get_endpoint_configuration(EP1_OUT_ADDR), NULL, 64);
// Everything is interrupt driven so just loop here
while (1) {
tight_loop_contents();
}
}
void ep1_out_handler(uint8_t *buf, uint16_t len) {
gpio_put(DEBUG_PIN, g_debug_state);
g_debug_state = ! g_debug_state;
struct usb_endpoint_configuration *ep = usb_get_endpoint_configuration(EP2_IN_ADDR);
usb_start_transfer(ep, NULL, 0);
}
在主机端,我修改了 dev_lowlevel_loopback.py,如下所示:
import usb.core
import usb.util
import time
# find our device
dev = usb.core.find(idVendor=0x0000, idProduct=0x0001)
# was it found?
if dev is None:
raise ValueError('Device not found')
# get an endpoint instance
cfg = dev.get_active_configuration()
intf = cfg[(0, 0)]
outep = usb.util.find_descriptor(
intf,
# match the first OUT endpoint
custom_match= \
lambda e: \
usb.util.endpoint_direction(e.bEndpointAddress) == \
usb.util.ENDPOINT_OUT)
inep = usb.util.find_descriptor(
intf,
# match the first IN endpoint
custom_match= \
lambda e: \
usb.util.endpoint_direction(e.bEndpointAddress) == \
usb.util.ENDPOINT_IN)
assert inep is not None
assert outep is not None
test_string = bytearray(64)
while True:
for _ in range(32):
outep.write(test_string)
inep.read(0)
time.sleep(0.2)
这里,传输时间在 16 毫秒到 19 毫秒之间变化,吞吐量为:
2048 * 8 / 0.016ms = 1024 kBit/s - 仍然很慢。
我可能会在每次写入后调用 inep.read(0) 来浪费一些时间,但是将其忽略会给出
回溯(最近一次调用最后一次):文件 “/home/pi/repositories/pico-examples/usb/device/dev_lowlevel/dev_lowlevel_loopback.py”, 第 56 行,在 outep.write(test_string)
[...]
usb.core.USBTimeoutError: [Errno 110] 操作超时
下一次写。
在函数usb_start_transfer(...)我们可以找到注释
void usb_start_transfer(struct usb_endpoint_configuration *ep, uint8_t *buf, uint16_t len) {
// We are asserting that the length is <= 64 bytes for simplicity of the example.
// For multi packet transfers see THE tinyusb port.
我试图找到tinusb端口,但迷失在Pico的所有tinusb示例中。
我尝试使用usb-test。我修改了 static bool usbtest_xfer_cb(...) 以用于测量和调试目的,如下所示:
static bool usbtest_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
{
USBTEST_LOG1("%s: ep_addr=0x%02x result=%u xferred_bytes=%u\n", __func__, ep_addr, result, xferred_bytes);
board_led_activity();
TU_VERIFY(result == XFER_RESULT_SUCCESS);
if (!xferred_bytes)
USBTEST_LOG2(" ZLP\n");
if (ep_addr == _bulk_out) {
TU_ASSERT ( usbd_edpt_xfer(rhport, _bulk_out, _bulk_out_buf, sizeof(_bulk_out_buf)) );
board_led_on();
gpio_put(DEBUG_PIN, g_debug_state);
g_debug_state = !g_debug_state;
}
else if (ep_addr == _bulk_in) {
TU_ASSERT ( usbd_edpt_xfer(rhport, _bulk_in, _bulk_in_buf, sizeof(_bulk_in_buf)) );
//gpio_put(DEBUG_PIN, g_debug_state);
//g_debug_state = !g_debug_state;
}
else
return false;
return true;
}
在 test.py 中,我仅启用了测试编号 27,该测试被标记为“批量写入”。像这样调用脚本
sudo python test.py -t -d
给出输出
#27 迭代=1 长度=512 变化=256 sglen=1 :: 0.616 ms - 811.7 kB/s dmesg: usbtest 1-1.3:1.0: TEST 27: 批量写入 0Mbytes
根据输出,应该已经发送了一些数据。但 LED 均未亮起(请参阅上面的代码),示波器也未显示 GPIO-2 上的任何移动。看起来根本不起作用。
如何通过 USB 从 Pi 4 到 Pi Pico 快速传输数据?
如果您深入研究源代码(SDK 版本 1.5.1),您将看到 getchar_timeout_us(uint32_t timeout_us) 为每个字节调用 int stdio_usb_in_chars(char *buf, int length) (源):
int stdio_usb_in_chars(char *buf, int length) {
// [...]
int rc = PICO_ERROR_NO_DATA;
if (stdio_usb_connected() && tud_cdc_available()) {
if (!mutex_try_enter_block_until(&stdio_usb_mutex, make_timeout_time_ms(PICO_STDIO_DEADLOCK_TIMEOUT_MS))) {
return PICO_ERROR_NO_DATA; // would deadlock otherwise
}
if (stdio_usb_connected() && tud_cdc_available()) {
int count = (int) tud_cdc_read(buf, (uint32_t) length);
rc = count ? count : PICO_ERROR_NO_DATA;
} else {
// because our mutex use may starve out the background task, run tud_task here (we own the mutex)
tud_task();
}
mutex_exit(&stdio_usb_mutex);
}
return rc;
}
对于每个字节,我们检查 USB 是否已连接、cdc 是否可用并锁定互斥体。
直接使用stdio_usb_in_chars,我可以达到迄今为止最高的传输速率。这是 pico 的测试代码:
extern "C" {
int stdio_usb_in_chars(char *buf, int length);
}
bool dataAvailableCallback(struct repeating_timer *t) {
printf("%lu %f\n", bytes_read, (float)bytes_read / num_reads);
data_available = false;
bytes_read = 0;
num_reads = 0;
return true;
}
int main() {
stdio_init_all();
struct repeating_timer timer;
add_repeating_timer_ms(-1000, dataAvailableCallback, NULL, &timer);
int bytesRed = 0;
while(true) {
bytesRed = stdio_usb_in_chars(cdc_buffer, sizeof(cdc_buffer));
if (bytesRed > 0) {
bytes_read += bytesRed;
num_reads++;
}
}
}
代码读取尽可能多的数据。每秒我们都会打印红色的字节数(以及每次调用stdio_usb_in_chars的红色字节数)。
在 Pi4 上我使用了这个脚本:
#!/usr/bin/env python3
import sys, struct, serial
out_data = bytes([x % 255 for x in range(0, 2048)])
with serial.serial_for_url(sys.argv[1], timeout = 1) as f:
while True:
f.write(out_data)
f.flush()
waiting = f.in_waiting
if waiting > 0:
data = f.read(waiting)
print(data)
输出示例:
b'512192 64.000000\r\n'
b'511616 64.000000\r\n'
b'509184 64.000000\r\n'
b'510016 64.000000\r\n'
因此,我们可以传输 500 kByte/sec = 4 MBit/sec。这距离 12 MBit/sec 的理论最大值还很远,但比之前的所有东西都要好。