STEPBYSTEP设计一个USB调试助手之十一:基于libusb异步传输+FIFO的USB测试工具

原创 嵌入式Lee 2024-04-14 08:00

一. 前言

最开始版本的GUI工具时同步传输实现的,后面我们又实现了异步传输,进一步实现了一步传输+FIFOGUI和底层的解耦。现在我们将这几部分合并起来,实现一个异步传输的GUI工具。

二. 实现

前面已经分别实现了异步传输+FIFO,以及GUI,先把他们放在一起即可,不在赘述,直接

贴出关键代码。


mainwindow.cpp

#include #include #include "mainwindow.h"#include "ui_mainwindow.h"#include "usbdev.h"
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow){ usbdev_init(); ui->setupUi(this); this->on_pushButton_8_clicked(); ui->textEdit_5->setText("800600020000FF00"); ui->checkBox->setChecked(true); ui->checkBox_2->setChecked(true); ui->checkBox_3->setChecked(true); ui->lineEdit_2->setText("10"); ui->lineEdit_3->setText("10"); ui->lineEdit->setText("1000"); ui->textEdit_3->setText("11223344556677889900"); tx_status = 0; rx_status = 0; tx_time = 0; tx_num = 0; rx_time = 0;
/* 创建定时器1ms执行一次 */ timer_update = new QTimer(this); timer_update->setInterval(1); timer_update->setSingleShot(false); connect(timer_update,SIGNAL(timeout()),this,SLOT(timer_update_cb())); timer_update->start(1);}
MainWindow::~MainWindow(){ usbdev_deinit(); delete timer_update; delete ui;}
void MainWindow::on_pushButton_8_clicked(){ int devsnum = 0; char* devstr = 0; usbdev_get_device_list(); devsnum = usbdev_get_device_num(); ui->comboBox->clear(); for(int i=0; i { devstr = usbdev_get_device_name(i); if(devstr == 0) { break; } else { ui->comboBox->addItems(QStringList(devstr)); } }}

void MainWindow::on_pushButton_clicked(){ int res; int index = ui->comboBox->currentIndex(); if(index < 0) { return; } if(0 == usbdev_get_openstatus()) { printf("to open dev %d\r\n",index); if(0 == (res = usbdev_open(index))) { ui->pushButton->setText("关闭");
/* 这里更新显示接口下拉框的内容 */ ui->comboBox_3->clear(); ui->comboBox_4->clear(); ui->comboBox_2->clear(); ui->comboBox_5->clear(); usbdev_update_interface_endpoint_list(); /* 更新接口和端点信息 */ int int_num = usbdev_get_interface_num(); int itf_id; int alt_id; for(int i = 0; i { if(0 == usbdev_get_interface_id(i,&itf_id,&alt_id)) { QString itfstr= QString::number(i)+":"+QString::number(itf_id)+":"+QString::number(alt_id); ui->comboBox_3->addItems(QStringList(itfstr)); ui->comboBox_4->addItems(QStringList(itfstr)); } } } else { QMessageBox::information(nullptr, "information", "打开设备失败"+QString::number(res), QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton); return; } } else { usbdev_close(); ui->pushButton->setText("打开");
/* 这里清除显示接口下拉框的内容 */ ui->comboBox_3->clear(); ui->comboBox_4->clear(); ui->comboBox_2->clear(); ui->comboBox_5->clear(); }}

void MainWindow::on_pushButton_6_clicked(){ ui->textEdit_6->setText(QString(""));}
QString ByteArrayToHexString(QByteArray &ba){ QDataStream out(&ba,QIODevice::ReadWrite); //将str的数据 读到out里面去 QString buf; while(!out.atEnd()) { qint8 outChar = 0; out >> outChar; //每次一个字节的填充到 outchar QString str =QString("%1").arg(outChar&0xFF,2,16,QLatin1Char('0')).toUpper() + QString(" "); //2 字符宽度 buf += str; } return buf;}
void MainWindow::on_pushButton_7_clicked(){ QString setup_str = ui->textEdit_5->toPlainText(); QByteArray setup_data = QByteArray::fromHex (setup_str.toLatin1().data());
if(setup_data.size() != 8) { QMessageBox::information(nullptr, "information", "SETUP必须是8字节", QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton); return; }
uint8_t bmRequestType = setup_data[0]; uint8_t bRequest = setup_data[1]; uint16_t wValue = ((uint16_t)(setup_data[2]) & 0x00FF) | (((uint16_t)(setup_data[3])<<8) & 0xFF00); uint16_t wIndex = ((uint16_t)(setup_data[4]) & 0x00FF) | (((uint16_t)(setup_data[5])<<8) & 0xFF00); uint16_t wLength = ((uint16_t)(setup_data[6]) & 0x00FF) | (((uint16_t)(setup_data[7])<<8) & 0xFF00); unsigned int timeout = 1000; /* 超时时间单位mS */
unsigned char* data = (unsigned char*)malloc(wLength); if(data == NULL) { return; }
QString ctrl_str = ui->textEdit_6->toPlainText(); QByteArray ctrl_data = QByteArray::fromHex (ctrl_str.toLatin1().data());
if((bmRequestType & 0x80) == 0) { printf("CONTROL OUT:%02X, %d\r\n",ctrl_data.size(),wLength); printf("[SETUP]\r\n"); for(int i=0; i { printf("%02x ",setup_data[i]); } printf("\r\n");
printf("[DATA]\r\n"); for(int i=0; i { printf("%02x ",ctrl_data[i]); } printf("\r\n");
/* OUT传输 获取DATA编辑框的内容 */ if(ctrl_data.size() != wLength) { QMessageBox::information(nullptr, "information", "wLength != DATA长度", QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton); return; }
memcpy(data, ctrl_data, ctrl_data.size()); }
int res = usbdev_control_transfer(bmRequestType,bRequest, wValue,wIndex, data, wLength, timeout);
if(res < 0) { QMessageBox::information(nullptr, "information", "控制传输失败", QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton); } else { if((bmRequestType & 0x80) != 0) { /* IN传输 获取数据到DATA编辑框 */ ctrl_data.resize(res); memcpy(ctrl_data.data(), data,res); //ctrl_str = QString(ctrl_data); ctrl_str = ByteArrayToHexString(ctrl_data); ui->textEdit_6->setText(ctrl_str); } }
free(data);}

void MainWindow::on_comboBox_3_activated(int index){ int epnum = usbdev_get_endpoint_num(index); ui->comboBox_2->clear(); for(int i=0; i< epnum; i++) { int address; int type; int mps; if(0 == usbdev_get_endpoint_info(index, i, &address, &type, &mps)) { QString epstr; if((address & 0x80) != 0) { epstr= QString::number(i)+":IN"+ QString("%1").arg(address & 0xFF, 2, 16, QLatin1Char('0'))+":"+ QString("%1").arg(mps & 0xFF, 2, 16, QLatin1Char('0'))+":"; } else { epstr= QString::number(i)+":OUT"+ QString("%1").arg(address & 0xFF, 2, 16, QLatin1Char('0'))+":"+ QString("%1").arg(mps & 0xFF, 2, 16, QLatin1Char('0'))+":"; } switch(type & 0x03) { case 0: epstr = epstr + "CTRL"; break; case 1: epstr = epstr + "ISOC"; break; case 2: epstr = epstr + "BULK"; break; case 3: epstr = epstr + "INT"; break; } ui->comboBox_2->addItems(QStringList(epstr)); } }}
void MainWindow::on_comboBox_4_activated(int index){ int epnum = usbdev_get_endpoint_num(index); ui->comboBox_5->clear(); for(int i=0; i< epnum; i++) { int address; int type; int mps; if(0 == usbdev_get_endpoint_info(index, i, &address, &type, &mps)) { QString epstr; if((address & 0x80) != 0) { epstr= QString::number(i)+":IN"+ QString("%1").arg(address & 0xFF, 2, 16, QLatin1Char('0'))+":"+ QString("%1").arg(mps & 0xFF, 2, 16, QLatin1Char('0'))+":"; } else { epstr= QString::number(i)+":OUT"+ QString("%1").arg(address & 0xFF, 2, 16, QLatin1Char('0'))+":"+ QString("%1").arg(mps & 0xFF, 2, 16, QLatin1Char('0'))+":"; } switch(type & 0x03) { case 0: epstr = epstr + "CTRL"; break; case 1: epstr = epstr + "ISOC"; break; case 2: epstr = epstr + "BULK"; break; case 3: epstr = epstr + "INT"; break; } ui->comboBox_5->addItems(QStringList(epstr)); } }}
void MainWindow::on_pushButton_3_clicked(){ ui->textEdit_3->setText("");}
void MainWindow::on_pushButton_4_clicked(){ ui->textEdit_4->setText("");}
/* 开始/停止发送按钮处理 */void MainWindow::on_pushButton_2_clicked(){ int address; int type; int mps; int ep_index; int itf_index; int len;
ep_index = ui->comboBox_2->currentIndex(); itf_index = ui->comboBox_3->currentIndex(); len = ui->lineEdit_2->text().toUInt(); if(0 != usbdev_get_endpoint_info(itf_index, ep_index, &address, &type, &mps)) { QMessageBox::information(nullptr, "information", "不能获取当前接口和端点的信息", QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton); return; } if((address & 0x80) != 0) { QMessageBox::information(nullptr, "information", "不是OUT端点,不能发送", QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton); return; }
if(tx_status == 0) { tx_status = 1; tx_time = 0; tx_num = 0; ui->pushButton_2->setText("停止发送"); usbdev_start_tx_transfer(itf_index, ep_index,len); } else { tx_status = 0; tx_time = 0; tx_num = 0; ui->pushButton_2->setText("开始发送"); usbdev_stop_tx_transfer(); }}
/* 开始/停止接收按钮处理 */void MainWindow::on_pushButton_5_clicked(){ int address; int type; int mps; int ep_index; int itf_index; int len;
ep_index = ui->comboBox_5->currentIndex(); itf_index = ui->comboBox_4->currentIndex(); len = ui->lineEdit_3->text().toUInt(); if(0 != usbdev_get_endpoint_info(itf_index, ep_index, &address, &type, &mps)) { QMessageBox::information(nullptr, "information", "不能获取当前接口和端点的信息", QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton); return; }
if((address & 0x80) == 0) { QMessageBox::information(nullptr, "information", "不是IN端点,不能接收", QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton); return; }
if(rx_status == 0) { rx_status = 1; rx_time = 0; ui->pushButton_5->setText("停止接收"); usbdev_start_rx_transfer(itf_index, ep_index,len); } else { rx_status = 0; rx_time = 0; ui->pushButton_5->setText("开始接收"); usbdev_stop_rx_transfer(); }}
/** * @brief MainWindow::timer_update_cb * 1ms进行一次处理刷新显示等。 * 1. 如果使能发送,则按指定时间将数据写入tx fifo。 * 2. 如果是能接收,则从rx fifo读出数据显示。 */void MainWindow::timer_update_cb(){ /* 发送处理 */ if(tx_status != 0) { tx_time++; uint32_t tx_tick = ui->lineEdit->text().toUInt(); bool iscontinue = ui->checkBox_3->isChecked();
/* 只有一次都没有发送过,或者是重复发送模式才进行发送 */ if((tx_num == 0) || iscontinue) { if(tx_time >= tx_tick) { tx_num = 1; /* 只有发送一次才置位 */ /* 只有时间间隔到才发送 */ bool ishex; int len; int textlen; int ep_index; int itf_index; int address; int type; int mps; int transferred; int res; int itf_id; int itf_alt_id; QString data_str; QByteArray data;
tx_time = 0;
ishex = ui->checkBox->isChecked(); len = ui->lineEdit_2->text().toUInt(); data_str = ui->textEdit_3->toPlainText();
/* 获取编辑框的内容根据是否16进制数据转为QByteArray */ if(ishex) { data = QByteArray::fromHex(data_str.toLatin1().data()); } else { data = QByteArray::fromRawData(data_str.toLatin1().data(),data_str.size()); } textlen = data.size();
/* 分配指定长度的缓存 */ unsigned char* buffer = (unsigned char*)calloc(len,1); if(buffer != NULL) { /* 拷贝数据到缓存,最多指定长度,不够则填充0 */ memcpy(buffer, data, (textlen>len)?len:textlen);
/* 数据写入tx fifo */ usbdev_put_tx_transfer_data(buffer, len);
/* 释放缓存 */ free(buffer); } } } }
/* 接收处理 */ if(rx_status != 0) { /* 先获取编辑框原有的数据 */ bool ishex; QString data_str; QString data_add_str; QByteArray data; uint32_t len; uint8_t* buffer = 0; uint32_t rxlen;
ishex = ui->checkBox_2->isChecked(); data_str = ui->textEdit_4->toPlainText();
/* 从接收缓冲区读出数据显示 */ len = usbdev_get_rx_transfer_datalen(); if(len > 0) { /* 分配内存存储接收缓冲区读出的数据 */ buffer = (uint8_t*)malloc(len); if(buffer != 0) { rxlen = usbdev_get_rx_transfer_data(buffer, len);
printf("rx len:%d\r\n",rxlen); if(rxlen > 0) { /* 将buffer中新的数据添加到data 根据是否16进制转为字符串 */ data.resize(rxlen); memcpy(data.data(), buffer, rxlen); if(ishex) { data_add_str = ByteArrayToHexString(data); } else { data_add_str = QString(data); }
/* 追加到编辑框,限制编辑框最大长度 */ if(data_str.size() > 1024*1024ul) {
ui->textEdit_4->setText(data_add_str); } else { ui->textEdit_4->setText(data_str + data_add_str); } }
free(buffer); } } }}

usbdev.c

#include #include #include #include "libusb.h"#include "usbdev.h"#include "fifo.h"#include "usbdev_fifo.h"
#define MAX_DEVS 64#define MAX_DEVS_STR_LEN 256#define MAX_ENDPOINTS 16#define MAX_INTERFACE 16
static void* usb_event_thread(void *arg); /* USB事件线程处理函数 */static void* usb_handle_thread(void *arg); /* USB业务线程处理函数 */pthread_t s_usb_event_thread; /* USB事件处理线程句柄 */pthread_t s_usb_handle_thread; /* USB业务处理线程句柄 */
char s_devs_str[MAX_DEVS][MAX_DEVS_STR_LEN];
libusb_device **s_devs = NULL;
libusb_device_handle *s_opened_handle = NULL;libusb_device *s_opened_dev = NULL;
int s_devs_num = 0;
int s_interface_num = 0; /* 打开的设备的接口数 */
int s_tx_itf_idx = -1; /* 当前打开的发送接口 */int s_tx_busy = 0; /* 发送忙标志 */int s_tx_ep_idx = -1; /* 当前发送的端点 */uint32_t s_tx_size = 0; /* 一次发送的大小 */
int s_rx_itf_idx = -1; /* 当前打开的接收接口 */int s_rx_busy = 0; /* 接收忙标志 */int s_rx_ep_idx = -1; /* 当前发送的端点 */uint32_t s_rx_size = 0; /* 一次接收的大小 */
/** * 记录接口信息结构体 */typedef struct{ int interfacenumber; int alternateSetting; int numendpoints; struct libusb_endpoint_descriptor endpoints[MAX_ENDPOINTS];} usbddev_interface_info_st;
usbddev_interface_info_st s_interface_info[MAX_INTERFACE];
int usbdev_init(void){ int r; r = libusb_init_context(/*ctx=*/NULL, /*options=*/NULL, /*num_options=*/0); if (r < 0) { return r; }
usbdev_fifo_init();
/* 创建usb事件处理线程 */ r = pthread_create(&s_usb_event_thread,0,usb_event_thread,0); if (r != 0) { return r; }
/* 创建usb业务处理线程 */ r = pthread_create(&s_usb_handle_thread,0,usb_handle_thread,0); if (r != 0) { return r; }}
int usbdev_deinit(void){ usbdev_close(); libusb_exit(NULL);
usbdev_fifo_deinit();
return 0;}
int usbdev_get_device_num(void){ return s_devs_num;}

char* usbdev_get_device_name(int index){ if(index >= s_devs_num) { return 0; } else { return s_devs_str[index]; }}
int usbdev_get_device_list(void){ ssize_t cnt; cnt = libusb_get_device_list(NULL, &s_devs); if (cnt < 0) { //libusb_exit(NULL); return (int) cnt; }
libusb_device *dev; int i = 0, j = 0; uint8_t path[8]; memset(path,0,sizeof(path)); memset(s_devs_str,0,sizeof(s_devs_str)); s_devs_num = 0; while ((dev = s_devs[i]) != NULL) { struct libusb_device_descriptor desc; int r = libusb_get_device_descriptor(dev, &desc); if (r < 0) { fprintf(stderr, "failed to get device descriptor"); return -1; } printf("%04x:%04x (bus %d, device %d)", desc.idVendor, desc.idProduct, libusb_get_bus_number(dev), libusb_get_device_address(dev)); r = libusb_get_port_numbers(dev, path, sizeof(path)); if (r > 0) { printf(" path: %d", path[0]); for (j = 1; j < r; j++) printf(".%d", path[j]); } printf("\n");
snprintf(s_devs_str[i],MAX_DEVS_STR_LEN,"%04x:%04x (bus %d, device %d) path: %d .%d .%d .%d .%d .%d .%d .%d", desc.idVendor, desc.idProduct, libusb_get_bus_number(dev), libusb_get_device_address(dev), path[0],path[1],path[2],path[3],path[4],path[5],path[6],path[7]); i++; s_devs_num++; }
libusb_free_device_list(s_devs, 1);}

int usbdev_open(int index){ int vid; int pid; printf("index=%d,s_devs_num=%d\r\n",index,s_devs_num); if(index >= s_devs_num) { return -1; }
if(s_opened_handle != NULL) { return 0; }
sscanf(s_devs_str[index],"%04x:%04x",&vid,&pid);
s_opened_handle = libusb_open_device_with_vid_pid(NULL, vid, pid);
if (s_opened_handle == NULL) { printf("Open device %d vid:%x,pid:%x failed.\n",index,vid,pid); return -2; } else { printf("dev opened\r\n"); }
s_opened_dev = libusb_get_device(s_opened_handle); uint8_t bus = libusb_get_bus_number(s_opened_dev); return 0;}
int usbdev_close(void){ if(s_opened_handle != NULL) { libusb_close(s_opened_handle); s_opened_handle = NULL; printf("dev closed\r\n"); }
s_tx_busy = 0; s_tx_ep_idx = -1; s_tx_itf_idx = -1; s_tx_size = 0;
s_rx_busy = 0; s_rx_ep_idx = -1; s_rx_itf_idx = -1; s_rx_size = 0; return 0;}
int usbdev_get_openstatus(void){ printf("get dev status\r\n"); if(s_opened_handle == NULL) { return 0; } else { return 1; }}
int usbdev_control_transfer (uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned char * data, uint16_t wLength, unsigned int timeout){ if(s_opened_handle == NULL) { return -1; } printf("bmRequestType:%d\r\n",bmRequestType); printf("bRequest:%d\r\n",bRequest); printf("wValue:%d\r\n",wValue); printf("wIndex:%d\r\n",wIndex); printf("wLength:%d\r\n",wLength);
return libusb_control_transfer(s_opened_handle, bmRequestType,bRequest, wValue,wIndex, data,wLength,timeout);}

int usbdev_update_interface_endpoint_list(void){ struct libusb_config_descriptor * config; struct libusb_interface * interface; struct libusb_interface_descriptor* altsetting; struct libusb_endpoint_descriptor * endpoint; if(s_opened_dev != NULL) { memset(&s_interface_info,0,sizeof(s_interface_info)); if(0 == libusb_get_active_config_descriptor(s_opened_dev,&config)) { s_interface_num = 0; if(config != NULL) { interface = config->interface; for(int i=0; i< config->bNumInterfaces; i++) { for(int j=0; j { altsetting = interface[i].altsetting; s_interface_info[s_interface_num].interfacenumber = altsetting[j].bInterfaceNumber; s_interface_info[s_interface_num].alternateSetting = altsetting[j].bAlternateSetting; s_interface_info[s_interface_num].numendpoints = altsetting[j].bNumEndpoints; for(int k=0; k { endpoint = altsetting[j].endpoint; s_interface_info[s_interface_num].endpoints[k] = endpoint[k]; } s_interface_num++; } } libusb_free_config_descriptor(config); } } else { return -2; } } else { return -1; } return 0;}
int usbdev_get_interface_num(void){ return s_interface_num;}
int usbdev_get_interface_id(int itf_index, int* interface, int* altsetting){ if((itf_index >= s_interface_num) || (itf_index == -1)) { return -1; } *interface = s_interface_info[itf_index].interfacenumber; *altsetting = s_interface_info[itf_index].alternateSetting; return 0;}
int usbdev_get_endpoint_num(int itf_index){ if((itf_index >= s_interface_num) || (itf_index == -1)) { return -1; } return s_interface_info[itf_index].numendpoints;}
int usbdev_get_endpoint_info(int itf_index, int ep_index, int* address, int* type, int* mps){ struct libusb_endpoint_descriptor* endpoint; if((itf_index >= s_interface_num) || (itf_index == -1)) { return -1; } if((ep_index >= s_interface_info[itf_index].numendpoints) || (ep_index == -1)) { return -1; } endpoint = s_interface_info[itf_index].endpoints; *address = endpoint[ep_index].bEndpointAddress; *type = endpoint[ep_index].bmAttributes; *mps = endpoint[ep_index].wMaxPacketSize; return 0;}
int usbdev_bulk_transfer(int itf_id,unsigned char endpoint, unsigned char *data, int length, int* transferred, unsigned int timeout){ int res; if(s_opened_handle == NULL) { return -1; } if(0 != libusb_claim_interface(s_opened_handle,itf_id)) { return -1; } res = libusb_bulk_transfer(s_opened_handle, endpoint, data, length, transferred, timeout); libusb_release_interface(s_opened_handle,itf_id); return res;}
int usbdev_interrupt_transfer(int itf_id,unsigned char endpoint, unsigned char *data, int length, int* transferred, unsigned int timeout){ int res; if(s_opened_handle == NULL) { return -1; } if(0 != libusb_claim_interface(s_opened_handle,itf_id)) { return -1; } res = libusb_interrupt_transfer(s_opened_handle, endpoint, data, length, transferred, timeout); libusb_release_interface(s_opened_handle,itf_id); return res;}
int usbdev_start_tx_transfer(int itf_index, int ep_index, uint32_t size){ if(itf_index == -1) { return -1; } int itf_id; int itf_alt_id; s_tx_size = size; if(itf_index==s_tx_itf_idx) { /* 如果和tx接口一样,且已经打开(s_tx_itf_idx!=-1),无需处理接口,只需要修改端点即可 */ s_tx_ep_idx = ep_index; } else { /* 如果和tx接口不一样,要看是不是和rx接口一样 */
if(itf_index==s_rx_itf_idx) { /* 如果和rx接口一样,说明接口也已经打开,无需再打开 * 此时如果原来tx接口是打开的需要关闭,并更新 */ s_tx_ep_idx = ep_index; if(s_tx_itf_idx != -1) { usbdev_get_interface_id(s_tx_itf_idx, &itf_id, &itf_alt_id); libusb_release_interface(s_opened_handle,itf_id); } s_tx_itf_idx = itf_index; } else { /* 如果和rx接口也不一样,则是全新的接口,需要先关闭原来的tx接口,再打开新的接口 * 此时如果原来tx接口是打开的需要关闭,并更新 */ if(s_tx_itf_idx != -1) { usbdev_get_interface_id(s_tx_itf_idx, &itf_id, &itf_alt_id); libusb_release_interface(s_opened_handle,itf_id); }
usbdev_get_interface_id(itf_index, &itf_id, &itf_alt_id); if(0 == libusb_claim_interface(s_opened_handle,itf_id)) { //libusb_clear_halt(s_opened_handle,s_interface_info[itf_index].endpoints[ep_index].bEndpointAddress); s_tx_itf_idx = itf_index; s_tx_ep_idx = ep_index; } else { s_tx_itf_idx = -1; return -1; } } }
return 0;}
int usbdev_stop_tx_transfer(void){ int itf_id; int itf_alt_id; s_tx_busy = 0; /* 只有tx接口已经打开,且rx没有在用才关闭*/ if(s_tx_itf_idx != -1) { if(s_tx_itf_idx != s_rx_itf_idx) { usbdev_get_interface_id(s_tx_itf_idx, &itf_id, &itf_alt_id); libusb_release_interface(s_opened_handle,itf_id); s_tx_itf_idx = -1; } } return 0;}
int usbdev_start_rx_transfer(int itf_index, int ep_index, uint32_t size){ if(itf_index == -1) { return -1; } int itf_id; int itf_alt_id;
s_rx_size = size; if(itf_index==s_rx_itf_idx) { /* 如果和rx接口一样,且已经打开(s_rx_itf_idx!=-1),无需处理接口,只需要修改端点即可 */ s_rx_ep_idx = ep_index; } else { /* 如果和rx接口不一样,要看是不是和tx接口一样 */
if(itf_index==s_tx_itf_idx) { /* 如果和tx接口一样,说明接口也已经打开,无需再打开 * 此时如果原来rx接口是打开的需要关闭,并更新 */ s_rx_ep_idx = ep_index; if(s_rx_itf_idx != -1) { usbdev_get_interface_id(s_rx_itf_idx, &itf_id, &itf_alt_id); libusb_release_interface(s_opened_handle,itf_id); } s_rx_itf_idx = itf_index; } else { /* 如果和tx接口也不一样,则是全新的接口,需要先关闭原来的rx接口,再打开新的接口 * 此时如果原来rx接口是打开的需要关闭,并更新 */ if(s_rx_itf_idx != -1) { usbdev_get_interface_id(s_rx_itf_idx, &itf_id, &itf_alt_id); libusb_release_interface(s_opened_handle,itf_id); }
usbdev_get_interface_id(itf_index, &itf_id, &itf_alt_id); if(0 == libusb_claim_interface(s_opened_handle,itf_id)) { //libusb_clear_halt(s_opened_handle,s_interface_info[itf_index].endpoints[ep_index].bEndpointAddress); s_rx_itf_idx = itf_index; s_rx_ep_idx = ep_index; } else { s_rx_itf_idx = -1; return -1; } } }
return 0;}
int usbdev_stop_rx_transfer(void){ int itf_id; int itf_alt_id; s_rx_busy = 0; /* 只有rx接口已经打开,且tx没有在用才关闭*/ if(s_rx_itf_idx != -1) { if(s_rx_itf_idx != s_tx_itf_idx) { usbdev_get_interface_id(s_rx_itf_idx, &itf_id, &itf_alt_id); libusb_release_interface(s_opened_handle,itf_id); s_rx_itf_idx = -1; } } return 0;}
uint32_t usbdev_get_rx_transfer_data(uint8_t* buffer, uint32_t len){ return usbdev_rx_fifo_get(buffer,len);}
uint32_t usbdev_get_rx_transfer_datalen(void){ return usbdev_rx_fifo_datalen();}
uint32_t usbdev_put_tx_transfer_data(uint8_t* buffer, uint32_t len){ return usbdev_tx_fifo_put(buffer,len);}
void tx_cb(struct libusb_transfer *transfer){ if (transfer->status == LIBUSB_TRANSFER_COMPLETED) { /* 成功 */ printf("tx_cb ok\r\n"); } else { /* 失败 */ printf("tx_cb err %d\r\n",transfer->status); //libusb_submit_transfer(transfer); } free(transfer->buffer); /* 在free传输之前 */ libusb_free_transfer(transfer); s_tx_busy = 0;}
void rx_cb(struct libusb_transfer *transfer){ if (transfer->status == LIBUSB_TRANSFER_COMPLETED) { /* 成功 */ printf("rx_cb ok\r\n"); } else { /* 失败 */ printf("rx_cb err %d\r\n",transfer->status); }
if(transfer->actual_length > 0) { printf("rx len %d\r\n",transfer->actual_length); usbdev_rx_fifo_put(transfer->buffer,transfer->actual_length); } free(transfer->buffer); /* 在free传输之前 */ libusb_free_transfer(transfer); s_rx_busy = 0;}
static void* usb_handle_thread(void *arg){ while(1) { /* 发送处理 */ if((s_tx_itf_idx != -1) && (s_tx_busy == 0)) { int address; int type; int mps; int rc = 0; uint32_t sendlen; struct libusb_transfer* transfer; uint8_t* buffer = malloc(s_tx_size); if(buffer != NULL) { sendlen = usbdev_tx_fifo_get(buffer,s_tx_size); if(sendlen > 0) { transfer = libusb_alloc_transfer(0); if(transfer != NULL) { if(0 == usbdev_get_endpoint_info(s_tx_itf_idx, s_tx_ep_idx, &address, &type, &mps)) { switch(type & 0x03) { case 0: //epstr = epstr + "CTRL"; break; case 1: //epstr = epstr + "ISOC"; break; case 2: libusb_fill_bulk_transfer(transfer,s_opened_handle,address,buffer,sendlen,&tx_cb,0,100); break; case 3: libusb_fill_interrupt_transfer(transfer,s_opened_handle,address,buffer,sendlen,&tx_cb,0,100); break; } s_tx_busy = 1; if(libusb_submit_transfer(transfer) < 0) { s_tx_busy = 0; libusb_free_transfer(transfer); free(buffer); } } else { libusb_free_transfer(transfer); free(buffer); } } else { free(buffer); } } else { free(buffer); } } }
/* 接收处理 */ if((s_rx_itf_idx != -1) && (s_rx_busy == 0)) { int address; int type; int mps; uint32_t sendlen; struct libusb_transfer* transfer; uint8_t* buffer = malloc(s_rx_size); if(buffer != NULL) { transfer = libusb_alloc_transfer(0); if(transfer != NULL) { if(0 == usbdev_get_endpoint_info(s_rx_itf_idx, s_rx_ep_idx, &address, &type, &mps)) { switch(type & 0x03) { case 0: //epstr = epstr + "CTRL"; break; case 1: //epstr = epstr + "ISOC"; break; case 2: libusb_fill_bulk_transfer(transfer,s_opened_handle,address,buffer,s_rx_size,&rx_cb,0,100); break; case 3: libusb_fill_interrupt_transfer(transfer,s_opened_handle,address,buffer,s_rx_size,&rx_cb,0,100); break; } s_rx_busy = 1;
if(libusb_submit_transfer(transfer) < 0) { s_rx_busy = 0; libusb_free_transfer(transfer); free(buffer); } else {
} } else { libusb_free_transfer(transfer); free(buffer); } } else { free(buffer); } } }
const struct timespec interval= { .tv_nsec = 1000000, .tv_sec = 0, }; pthread_delay_np(&interval); } return 0;}

static void* usb_event_thread(void *arg){ while(1) { /* 至少打开了一个设备才进行事件循环 */ if(s_opened_handle != 0) { libusb_handle_events(0); }
const struct timespec interval= { .tv_nsec = 1000000, .tv_sec = 0, }; pthread_delay_np(&interval); } return 0;}

一. 测试

3.1单次发送模式

不勾选重复

点击开始接收,点击发送进行一次发送,可以看到收到一次发送的数据,停止发送以便下一次继续发送。

3.2重复发送模式

勾选重复,点击开始可以看到自动重复发送

3.3确认发送间隔时间

设置发送间隔时间1,10,100ms查看发送间隔是否正确

3.4确认发送大小是否正确

设置一次发送100字节,但是编辑框只有10字节,可以看到填充0发送

3.5ASCII/HEX模式

前面都是HEX模式,不勾选HEX ASCII模式显示也正确

3.6其他功能

清楚显示等测试

四. 优化窗口自适应

我们可以优化下界面自适应窗口的大小,得益于我们使用了嵌套layout的模式,所以整个界面就是一个layotu整体。只需要做一下两件事即可实现自动适应窗口大小。

1.确认所有控件的sizePolicy的策略为Expanding

2.界面空白处点击右键-布局-垂直布局

我们最外层的布局一样,这样整个界面作为了一个垂直布局的整体和窗口大小自适应

可以看到缩放的效果

最小化

最大化


五. 总结

目前实现了批量传输/中断传输(异步模式), 控制传输(同步模式)这几项功能的测试,工具基本具备了实用价值,后面我们还有一些继续完善的工作。

1.添加ISO传输功能。

2.实现批量,同步传输的最大效率,即能实现不间断的流,可以通过一开始就挂在多个传输来实现。

3.GUI交互方面的优化,比如提供控制传输的GUI模板,自动选择常见固定的控制请求。

4.其他使用中可以继续优化的问题。






评论
  • 随着AI大模型训练和推理对计算能力的需求呈指数级增长,AI数据中心的网络带宽需求大幅提升,推动了高速光模块的发展。光模块作为数据中心和高性能计算系统中的关键器件,主要用于提供高速和大容量的数据传输服务。 光模块提升带宽的方法有两种:1)提高每个通道的比特速率,如直接提升波特率,或者保持波特率不变,使用复杂的调制解调方式(如PAM4);2)增加通道数,如提升并行光纤数量,或采用波分复用(CWDM、LWDM)。按照传输模式,光模块可分为并行和波分两种类型,其中并行方案主要应用在中短距传输场景中成本
    hycsystembella 2025-01-25 17:24 245浏览
  • 嘿,咱来聊聊RISC-V MCU技术哈。 这RISC-V MCU技术呢,简单来说就是基于一个叫RISC-V的指令集架构做出的微控制器技术。RISC-V这个啊,2010年的时候,是加州大学伯克利分校的研究团队弄出来的,目的就是想搞个新的、开放的指令集架构,能跟上现代计算的需要。到了2015年,专门成立了个RISC-V基金会,让这个架构更标准,也更好地推广开了。这几年啊,这个RISC-V的生态系统发展得可快了,好多公司和机构都加入了RISC-V International,还推出了不少RISC-V
    丙丁先生 2025-01-21 12:10 1126浏览
  • 故障现象 一辆2007款日产天籁车,搭载VQ23发动机(气缸编号如图1所示,点火顺序为1-2-3-4-5-6),累计行驶里程约为21万km。车主反映,该车起步加速时偶尔抖动,且行驶中加速无力。 图1 VQ23发动机的气缸编号 故障诊断接车后试车,发动机怠速运转平稳,但只要换挡起步,稍微踩下一点加速踏板,就能感觉到车身明显抖动。用故障检测仪检测,发动机控制模块(ECM)无故障代码存储,且无失火数据流。用虹科Pico汽车示波器测量气缸1点火信号(COP点火信号)和曲轴位置传感器信
    虹科Pico汽车示波器 2025-01-23 10:46 232浏览
  • 数字隔离芯片是一种实现电气隔离功能的集成电路,在工业自动化、汽车电子、光伏储能与电力通信等领域的电气系统中发挥着至关重要的作用。其不仅可令高、低压系统之间相互独立,提高低压系统的抗干扰能力,同时还可确保高、低压系统之间的安全交互,使系统稳定工作,并避免操作者遭受来自高压系统的电击伤害。典型数字隔离芯片的简化原理图值得一提的是,数字隔离芯片历经多年发展,其应用范围已十分广泛,凡涉及到在高、低压系统之间进行信号传输的场景中基本都需要应用到此种芯片。那么,电气工程师在进行电路设计时到底该如何评估选择一
    华普微HOPERF 2025-01-20 16:50 179浏览
  •     IPC-2581是基于ODB++标准、结合PCB行业特点而指定的PCB加工文件规范。    IPC-2581旨在替代CAM350格式,成为PCB加工行业的新的工业规范。    有一些免费软件,可以查看(不可修改)IPC-2581数据文件。这些软件典型用途是工艺校核。    1. Vu2581        出品:Downstream     
    电子知识打边炉 2025-01-22 11:12 305浏览
  • 项目展示①正面、反面②左侧、右侧项目源码:https://mbb.eet-china.com/download/316656.html前言为什么想到要做这个小玩意呢,作为一个死宅,懒得看手机,但又想要抬头就能看见时间和天气信息,于是就做个这么个小东西,放在示波器上面正好(示波器外壳有个小槽,刚好可以卡住)功能主要有,获取国家气象局的天气信息,还有实时的温湿度,主控采用ESP32,所以后续还可以开放更多奇奇怪怪的功能,比如油价信息、股票信息之类的,反正能联网可操作性就大多了原理图、PCB、面板设计
    小恶魔owo 2025-01-25 22:09 316浏览
  • 临近春节,各方社交及应酬也变得多起来了,甚至一月份就排满了各式约见。有的是关系好的专业朋友的周末“恳谈会”,基本是关于2025年经济预判的话题,以及如何稳定工作等话题;但更多的预约是来自几个客户老板及副总裁们的见面,他们为今年的经济预判与企业发展焦虑而来。在聊天过程中,我发现今年的聊天有个很有意思的“点”,挺多人尤其关心我到底是怎么成长成现在的多领域风格的,还能掌握一些经济趋势的分析能力,到底学过哪些专业、在企业管过哪些具体事情?单单就这个一个月内,我就重复了数次“为什么”,再辅以我上次写的:《
    牛言喵语 2025-01-22 17:10 382浏览
  • 书接上回:【2022年终总结】阳光总在风雨后,启航2023-面包板社区  https://mbb.eet-china.com/blog/468701-438244.html 总结2019,松山湖有个欧洲小镇-面包板社区  https://mbb.eet-china.com/blog/468701-413397.html        2025年该是总结下2024年的喜怒哀乐,有个好的开始,才能更好的面对2025年即将
    liweicheng 2025-01-24 23:18 226浏览
  • 飞凌嵌入式基于瑞芯微RK3562系列处理器打造的FET3562J-C全国产核心板,是一款专为工业自动化及消费类电子设备设计的产品,凭借其强大的功能和灵活性,自上市以来得到了各行业客户的广泛关注。本文将详细介绍如何启动并测试RK3562J处理器的MCU,通过实际操作步骤,帮助各位工程师朋友更好地了解这款芯片。1、RK3562J处理器概述RK3562J处理器采用了4*Cortex-A53@1.8GHz+Cortex-M0@200MHz架构。其中,4个Cortex-A53核心作为主要核心,负责处理复杂
    飞凌嵌入式 2025-01-24 11:21 209浏览
  • 高速先生成员--黄刚这不马上就要过年了嘛,高速先生就不打算给大家上难度了,整一篇简单但很实用的文章给大伙瞧瞧好了。相信这个标题一出来,尤其对于PCB设计工程师来说,心就立马凉了半截。他们辛辛苦苦进行PCB的过孔设计,高速先生居然说设计多大的过孔他们不关心!另外估计这时候就跳出很多“挑刺”的粉丝了哈,因为翻看很多以往的文章,高速先生都表达了过孔孔径对高速性能的影响是很大的哦!咋滴,今天居然说孔径不关心了?别,别急哈,听高速先生在这篇文章中娓娓道来。首先还是要对各位设计工程师的设计表示肯定,毕竟像我
    一博科技 2025-01-21 16:17 213浏览
  • 前篇文章中『服务器散热效能不佳有解吗?』提到气冷式的服务器其散热效能对于系统稳定度是非常重要的关键因素,同时也说明了百佳泰对于散热效能能提供的协助与服务。本篇将为您延伸说明我们如何进行评估,同时也会举例在测试过程中发现的问题及改善后的数据。AI服务器的散热架构三大重点:GPU导风罩:尝试不同的GPU导风罩架构,用以集中服务器进风量,加强对GPU的降温效果。GPU托盘:改动GPU托盘架构,验证出风面积大小对GPU散热的影想程度。CPU导风罩:尝试封闭CPU导风罩间隙,集中风流,验证CPU降温效果。
    百佳泰测试实验室 2025-01-24 16:58 116浏览
  • 2024年是很平淡的一年,能保住饭碗就是万幸了,公司业绩不好,跳槽又不敢跳,还有一个原因就是老板对我们这些员工还是很好的,碍于人情也不能在公司困难时去雪上加霜。在工作其间遇到的大问题没有,小问题还是有不少,这里就举一两个来说一下。第一个就是,先看下下面的这个封装,你能猜出它的引脚间距是多少吗?这种排线座比较常规的是0.6mm间距(即排线是0.3mm间距)的,而这个规格也是我们用得最多的,所以我们按惯性思维来看的话,就会认为这个座子就是0.6mm间距的,这样往往就不会去细看规格书了,所以这次的运气
    wuliangu 2025-01-21 00:15 600浏览
  • 不让汽车专美于前,近年来哈雷(Harley-Davidson)和本田(Honda)等大型重型机车大厂的旗下车款皆已陆续配备车载娱乐系统与语音助理,在路上也有越来越多的普通机车车主开始使用安全帽麦克风,在骑车时透过蓝牙连线执行语音搜寻地点导航、音乐播放控制或免持拨打接听电话等各种「机车语音助理」功能。客户背景与面临的挑战以本次分享的客户个案为例,该客户是一个跨国车用语音软件供货商,过往是与车厂合作开发前装车机为主,且有着多年的「汽车语音助理」产品经验。由于客户这次是首度跨足「机车语音助理」产品,因
    百佳泰测试实验室 2025-01-24 17:00 146浏览
  •  万万没想到!科幻电影中的人形机器人,正在一步步走进我们人类的日常生活中来了。1月17日,乐聚将第100台全尺寸人形机器人交付北汽越野车,再次吹响了人形机器人疯狂进厂打工的号角。无独有尔,银河通用机器人作为一家成立不到两年时间的创业公司,在短短一年多时间内推出革命性的第一代产品Galbot G1,这是一款轮式、双臂、身体可折叠的人形机器人,得到了美团战投、经纬创投、IDG资本等众多投资方的认可。作为一家成立仅仅只有两年多时间的企业,智元机器人也把机器人从梦想带进了现实。2024年8月1
    刘旷 2025-01-21 11:15 886浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦