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.其他使用中可以继续优化的问题。






评论
  • 本文介绍Linux系统(Ubuntu/Debian通用)挂载exfat格式U盘的方法,触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。修改对应的内核配置文件# 进入sdk目录cdrk3562_linux# 编辑内核配置文件vi./kernel-5.10/arch/arm64/configs/rockchip_linux_defconfig注:不清楚内核使用哪个defc
    Industio_触觉智能 2024-12-10 09:44 92浏览
  • 习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习笔记&记录学习习笔记&记学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记
    youyeye 2024-12-10 16:13 105浏览
  • 近日,搭载紫光展锐W517芯片平台的INMO GO2由影目科技正式推出。作为全球首款专为商务场景设计的智能翻译眼镜,INMO GO2 以“快、准、稳”三大核心优势,突破传统翻译产品局限,为全球商务人士带来高效、自然、稳定的跨语言交流体验。 INMO GO2内置的W517芯片,是紫光展锐4G旗舰级智能穿戴平台,采用四核处理器,具有高性能、低功耗的优势,内置超微高集成技术,采用先进工艺,计算能力相比同档位竞品提升4倍,强大的性能提供更加多样化的应用场景。【视频见P盘链接】 依托“
    紫光展锐 2024-12-11 11:50 47浏览
  • 【萤火工场CEM5826-M11测评】OLED显示雷达数据本文结合之前关于串口打印雷达监测数据的研究,进一步扩展至 OLED 屏幕显示。该项目整体分为两部分: 一、框架显示; 二、数据采集与填充显示。为了减小 MCU 负担,采用 局部刷新 的方案。1. 显示框架所需库函数 Wire.h 、Adafruit_GFX.h 、Adafruit_SSD1306.h . 代码#include #include #include #include "logo_128x64.h"#include "logo_
    无垠的广袤 2024-12-10 14:03 69浏览
  • 我的一台很多年前人家不要了的九十年代SONY台式组合音响,接手时只有CD功能不行了,因为不需要,也就没修,只使用收音机、磁带机和外接信号功能就够了。最近五年在外地,就断电闲置,没使用了。今年9月回到家里,就一个劲儿地忙着收拾家当,忙了一个多月,太多事啦!修了电气,清理了闲置不用了的电器和电子,就是一个劲儿地扔扔扔!几十年的“工匠式”收留收藏,只能断舍离,拆解不过来的了。一天,忽然感觉室内有股臭味,用鼻子的嗅觉功能朝着臭味重的方向寻找,觉得应该就是这台组合音响?怎么会呢?这无机物的东西不会腐臭吧?
    自做自受 2024-12-10 16:34 136浏览
  • 肖特基具有很多的应用场景, 可以做同步整流,防止电流倒灌和电源反接等,但是随着电源电流的增大,肖特基导通正向压降0.3~0.7v的劣势也越发明显,产生了很多的热,对于工程师的散热设计是个考验,增加了工程师的设计难度和产品成本,目前一种新的理想二极管及其控制器,目前正在得到越来越广泛的应用- BMS,无人机,PLC,安防,家电,电动工具,汽车等都在快速普及理想二极管有三种架构,内置电荷泵的类似无锡明芯微MX5050T这种,驱动能力会弱点,静态功耗200uA,外置电荷泵MX74700T的这种驱动能力
    王萌 2024-12-10 08:51 85浏览
  •         在有电流流过的导线周围会感生出磁场,再用霍尔器件检测由电流感生的磁场,即可测出产生这个磁场的电流的量值。由此就可以构成霍尔电流、电压传感器。因为霍尔器件的输出电压与加在它上面的磁感应强度以及流过其中的工作电流的乘积成比例,是一个具有乘法器功能的器件,并且可与各种逻辑电路直接接口,还可以直接驱动各种性质的负载。因为霍尔器件的应用原理简单,信号处理方便,器件本身又具有一系列的du特优点,所以在变频器中也发挥了非常重要的作用。  &nb
    锦正茂科技 2024-12-10 12:57 76浏览
  •         霍尔传感器是根据霍尔效应制作的一种磁场传感器。霍尔效应是磁电效应的一种,这一现象是霍尔(A.H.Hall,1855—1938)于1879年在研究金属的导电机构时发现的。后来发现半导体、导电流体等也有这种效应,而半导体的霍尔效应比金属强得多,利用这现象制成的各种霍尔元件,广泛地应用于工业自动化技术、检测技术及信息处理等方面。霍尔效应是研究半导体材料性能的基本方法。通过霍尔效应实验测定的霍尔系数,能够判断半导体材料的导电类型、载流子浓度及载流子
    锦正茂科技 2024-12-10 11:07 64浏览
  • 一、SAE J1939协议概述SAE J1939协议是由美国汽车工程师协会(SAE,Society of Automotive Engineers)定义的一种用于重型车辆和工业设备中的通信协议,主要应用于车辆和设备之间的实时数据交换。J1939基于CAN(Controller Area Network)总线技术,使用29bit的扩展标识符和扩展数据帧,CAN通信速率为250Kbps,用于车载电子控制单元(ECU)之间的通信和控制。小北同学在之前也对J1939协议做过扫盲科普【科普系列】SAE J
    北汇信息 2024-12-11 15:45 77浏览
  • RK3506 是瑞芯微推出的MPU产品,芯片制程为22nm,定位于轻量级、低成本解决方案。该MPU具有低功耗、外设接口丰富、实时性高的特点,适合用多种工商业场景。本文将基于RK3506的设计特点,为大家分析其应用场景。RK3506核心板主要分为三个型号,各型号间的区别如下图:​图 1  RK3506核心板处理器型号场景1:显示HMIRK3506核心板显示接口支持RGB、MIPI、QSPI输出,且支持2D图形加速,轻松运行QT、LVGL等GUI,最快3S内开
    万象奥科 2024-12-11 15:42 68浏览
  • 概述 通过前面的研究学习,已经可以在CycloneVGX器件中成功实现完整的TDC(或者说完整的TDL,即延时线),测试结果也比较满足,解决了超大BIN尺寸以及大量0尺寸BIN的问题,但是还是存在一些之前系列器件还未遇到的问题,这些问题将在本文中进行详细描述介绍。 在五代Cyclone器件内部系统时钟受限的情况下,意味着大量逻辑资源将被浪费在于实现较大长度的TDL上面。是否可以找到方法可以对此前TDL的长度进行优化呢?本文还将探讨这个问题。TDC前段BIN颗粒堵塞问题分析 将延时链在逻辑中实现后
    coyoo 2024-12-10 13:28 101浏览
  • 时源芯微——RE超标整机定位与解决详细流程一、 初步测量与问题确认使用专业的电磁辐射测量设备,对整机的辐射发射进行精确测量。确认是否存在RE超标问题,并记录超标频段和幅度。二、电缆检查与处理若存在信号电缆:步骤一:拔掉所有信号电缆,仅保留电源线,再次测量整机的辐射发射。若测量合格:判定问题出在信号电缆上,可能是电缆的共模电流导致。逐一连接信号电缆,每次连接后测量,定位具体哪根电缆或接口导致超标。对问题电缆进行处理,如加共模扼流圈、滤波器,或优化电缆布局和屏蔽。重新连接所有电缆,再次测量
    时源芯微 2024-12-11 17:11 74浏览
  • 全球知名半导体制造商ROHM Co., Ltd.(以下简称“罗姆”)宣布与Taiwan Semiconductor Manufacturing Company Limited(以下简称“台积公司”)就车载氮化镓功率器件的开发和量产事宜建立战略合作伙伴关系。通过该合作关系,双方将致力于将罗姆的氮化镓器件开发技术与台积公司业界先进的GaN-on-Silicon工艺技术优势结合起来,满足市场对高耐压和高频特性优异的功率元器件日益增长的需求。氮化镓功率器件目前主要被用于AC适配器和服务器电源等消费电子和
    电子资讯报 2024-12-10 17:09 87浏览
  • 天问Block和Mixly是两个不同的编程工具,分别在单片机开发和教育编程领域有各自的应用。以下是对它们的详细比较: 基本定义 天问Block:天问Block是一个基于区块链技术的数字身份验证和数据交换平台。它的目标是为用户提供一个安全、去中心化、可信任的数字身份验证和数据交换解决方案。 Mixly:Mixly是一款由北京师范大学教育学部创客教育实验室开发的图形化编程软件,旨在为初学者提供一个易于学习和使用的Arduino编程环境。 主要功能 天问Block:支持STC全系列8位单片机,32位
    丙丁先生 2024-12-11 13:15 49浏览
  • 智能汽车可替换LED前照灯控制运行的原理涉及多个方面,包括自适应前照灯系统(AFS)的工作原理、传感器的应用、步进电机的控制以及模糊控制策略等。当下时代的智能汽车灯光控制系统通过车载网关控制单元集中控制,表现特殊点的有特斯拉,仅通过前车身控制器,整个系统就包括了灯光旋转开关、车灯变光开关、左LED前照灯总成、右LED前照灯总成、转向柱电子控制单元、CAN数据总线接口、组合仪表控制单元、车载网关控制单元等器件。变光开关、转向开关和辅助操作系统一般连为一体,开关之间通过内部线束和转向柱装置连接为多,
    lauguo2013 2024-12-10 15:53 81浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦