libusb_bulk_transfer仅接收1字节问题排查与解决咨询
USB Bulk传输仅读取单字节数据的问题排查与解决
问题背景
使用libusb实现USB设备数据读取时,99%的场景下每次libusb_bulk_transfer仅能读取1字节数据,但设备实际会发送19-20字节的完整响应。
测试代码
#include <iostream> #include <libusb.h> #include <string.h> #define VENDOR_ID 0x10C4 #define PRODUCT_ID 0xEA60 #define IN_ENDPOINT 0x81 int main(int argc, char *argv[]) { int err = libusb_init(NULL); libusb_device_handle *device_handle = NULL; unsigned char value_str[64]; device_handle = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID); libusb_set_auto_detach_kernel_driver(device_handle, 1); err = libusb_claim_interface(device_handle, 0); int transfered = 0; unsigned char out[6] = {'A', 'T', '$', 'S', 'R', '\r'}; libusb_bulk_transfer(device_handle, 0x01, out, 6, &transfered, 0); int res = libusb_bulk_transfer(device_handle, IN_ENDPOINT, value_str, 64, &transfered, 0); while (res == 0) { if (transfered == 0) continue; std::cout << "Reading Successful: "; for (int i = 0; i < transfered; i++) { std::cout << value_str[i]; } std::cout << std::endl; res = libusb_bulk_transfer(device_handle, IN_ENDPOINT, value_str, 64, &transfered, 0); } libusb_release_interface(device_handle, 0); libusb_close(device_handle); libusb_exit(NULL); return 0; }
实际输出
Reading Successful: A Reading Successful: T Reading Successful: $ Reading Successful: S Reading Successful: R Reading Successful: Reading Successful: - Reading Successful: 4 Reading Successful: 1 Reading Successful: 8 Reading Successful: 0 Reading Successful: 0 Reading Successful: 0 Reading Successful: 7 Reading Successful: 9 Reading Successful: 5 Reading Successful: x Reading Successful: x Reading Successful: x Reading Successful: : Reading Successful: h Reading Successful: e Reading Successful: l Reading Successful: p Reading Successful: : Reading Successful: b Reading Successful: a Reading Successful: t Reading Successful: o Reading Successful: k Reading Successful: Reading Successful:
原因分析
- USB Bulk传输的分包特性:Bulk端点传输时,
libusb_bulk_transfer默认收到任意长度的有效数据包就立即返回,不会等待填满请求的缓冲区。设备可能将完整响应拆分为多个小数据包(甚至单字节包)发送,这完全符合USB规范。 - 设备硬件逻辑:你的设备是Silicon Labs的CP210x串口转USB芯片(VID 0x10C4/PID 0xEA60),这类设备默认配置为“每收到串口的一个字节就立即打包成USB包转发”,而非积累到一定长度再发送。
解决方案
方式1:缓冲区拼接+超时判断(通用方案)
修改读取逻辑,循环累加数据直到超时(代表设备停止发送),或匹配到响应结束标记(比如换行符),最终拼接出完整响应。
修改后的示例代码:
#include <iostream> #include <libusb.h> #include <string.h> #define VENDOR_ID 0x10C4 #define PRODUCT_ID 0xEA60 #define IN_ENDPOINT 0x81 #define RESPONSE_TIMEOUT 1000 // 超时时间,单位ms #define MAX_RESPONSE_LEN 64 int main(int argc, char *argv[]) { int err = libusb_init(NULL); if (err != 0) { std::cerr << "libusb初始化失败: " << libusb_error_name(err) << std::endl; return 1; } libusb_device_handle *device_handle = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID); if (!device_handle) { std::cerr << "打开设备失败" << std::endl; libusb_exit(NULL); return 1; } libusb_set_auto_detach_kernel_driver(device_handle, 1); err = libusb_claim_interface(device_handle, 0); if (err != 0) { std::cerr << "抢占接口失败: " << libusb_error_name(err) << std::endl; libusb_close(device_handle); libusb_exit(NULL); return 1; } // 发送指令 int transfered = 0; unsigned char out[6] = {'A', 'T', '$', 'S', 'R', '\r'}; err = libusb_bulk_transfer(device_handle, 0x01, out, 6, &transfered, 0); if (err != 0) { std::cerr << "发送指令失败: " << libusb_error_name(err) << std::endl; libusb_release_interface(device_handle, 0); libusb_close(device_handle); libusb_exit(NULL); return 1; } unsigned char full_response[MAX_RESPONSE_LEN] = {0}; int total_read = 0; int res; unsigned char buffer[64]; // 循环读取直到超时或达到最大长度 while (total_read < MAX_RESPONSE_LEN) { res = libusb_bulk_transfer(device_handle, IN_ENDPOINT, buffer, 64, &transfered, RESPONSE_TIMEOUT); if (res == LIBUSB_ERROR_TIMEOUT) { // 超时表示设备已停止发送数据 break; } else if (res != 0) { std::cerr << "读取失败: " << libusb_error_name(res) << std::endl; break; } if (transfered > 0) { memcpy(full_response + total_read, buffer, transfered); total_read += transfered; // 可选:如果响应有结束标记(比如换行),提前终止读取 for (int i = 0; i < transfered; i++) { if (buffer[i] == '\n' || buffer[i] == '\r') { goto end_read; } } } } end_read: // 输出完整响应 std::cout << "完整响应:\n"; for (int i = 0; i < total_read; i++) { std::cout << full_response[i]; } std::cout << std::endl; // 释放资源 libusb_release_interface(device_handle, 0); libusb_close(device_handle); libusb_exit(NULL); return 0; }
方式2:修改设备串口缓冲配置(针对CP210x芯片)
利用CP210x的厂商自定义控制请求,修改设备的“发送缓冲区触发阈值”,让设备积累指定字节数后再发送USB包,减少分包次数。
在libusb_claim_interface成功后添加以下代码:
// 设置CP210x发送缓冲区触发阈值为20字节(积累20字节再发送) err = libusb_control_transfer(device_handle, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, 0x02, 0x0014, 0x0000, NULL, 0, 1000); if (err != 0) { std::cerr << "设置缓冲阈值失败: " << libusb_error_name(err) << std::endl; }
内容的提问来源于stack exchange,提问作者rudolfninja
相关产品推荐
相关产品推荐

