Linux应用编程(五)USB应用开发-libusb库
一、基础知识
1. USB接口是什么?
USB接口(Universal Serial Bus)是一种通用串行总线,广泛使用的接口标准,主要用于连接计算机与外围设备(如键盘、鼠标、打印机、存储设备等)之间的数据传输和电力供应。它旨在简化计算机与外部设备之间的连接方式,同时提供更高的传输速度和更好的兼容性。
在1990年代初,计算机外围设备通常使用专用接口,如鼠标和键盘常见的PS/2接口,打印机使用的LPT接口(并口)等。这些接口虽然在当时各自具有一定的专用性和高效性,但也存在很多问题,随着技术的发展,通用性更强的接口逐渐成为主流,USB(Universal Serial Bus)便是这一发展潮流的代表。
1996年,由Intel、微软、康柏、DEC、IBM、NEC和北方电信公司等七家业界巨头组成的非盈利组织USB标准化组织(USB Implementers Forum,简称USB-IF)开始推动USB(Universal Serial Bus)接口的标准化工作。USB的诞生标志着计算机外围设备连接方式的革命性转变。
USB 1.0/1.1 最早的版本,传输速度为12 Mbps。 USB 2.0 提供更高的传输速度(最高480 Mbps),广泛应用于各种设备。
USB 3.0/3.1 提供更高的数据传输速率,最高可达5 Gbps及以上,支持更大的电流供电。 USB 4.0 最新的USB版本,支持更高的传输速率,最高可达40 Gbps,并且兼容Thunderbolt 3协议。
2. USB命名规则
在2013年,USB 3.0和USB 3.1推出后,USB实现了更高的传输速度和更多功能,但随着技术的进步和市场的需求,USB-IF(USB Implementers Forum)为了更清晰地区分不同的版本和性能,进行了命名上的调整。
第一次命名改变:
USB 3.0 改名为 USB 3.1 Gen 1
USB 3.1 改名为 USB 3.1 Gen 2
第二次命名改变:
USB 3.1 Gen1 = USB 3.2 Gen1
USB 3.1 Gen2 = USB 3.2 Gen2
USB 3.2 = USB 3.2 Gen2x2其本质上就是, USB 3.0 就是 USB 3.2 Gen1 ,USB 3.1是USB 3.2 Gen2,USB 3.2是USB 3.2 Gen2x2。
3. USB HOST和USB OTG
USB Host(主设备):是指具备控制和管理USB总线的设备。它负责控制所有USB连接的设备,发起数据传输,并管理设备间的通信。USB Host的核心功能是控制和调度USB数据的流向。典型的USB Host设备包括:计算机(台式机、笔记本电脑等)、打印机(具有USB接口的)等。在USB通信中,Host设备通过发送命令来请求从设备发送或接收数据。Host设备管理USB总线上的电源供给和数据传输。
USB Slave(从设备):是指依赖于主设备控制的设备。USB从设备由主设备管理,它没有独立的数据传输能力,所有的数据传输都必须经过主设备发起或协调。USB从设备通常是外设设备,例如:键盘、鼠标、U盘等。
USB OTG(On-The-Go):是一项功能,使得USB设备在需要时可以切换角色,既可以作为主设备,也可以作为从设备,从而直接与其他USB设备进行通信。USB OTG标准通常适用于移动设备,如智能手机、平板电脑等。它允许用户无需通过传统的主设备(如计算机)即可实现设备间的直接连接和数据交换。例如,一台支持OTG的手机可以在连接U盘时充当主设备,而在连接键盘时又可以充当从设备。
4. USB集线器(hub)
USB Hub(集线器)是一种设备,允许多个USB设备通过一个USB端口与计算机进行连接。它实际上是一个多端口的USB接口扩展器,可以将计算机的一个USB端口扩展为多个端口,方便用户连接多个USB设备。 集线器是为了扩展更多的接口。但是并不是可以无休止的可以扩展,USB2.0协议中最多扩展七层。每一层所有设备相加不能超过127个(包括集线器) 。
所有从机都必须经过集线器(hub)才能与主机连接,也就是设备不能直接和主机相连接。根集线器:与主机直接连接的集线器叫做根集线器,用户外接的叫做普通集线器。
下图的集线器就是普通集线器。
下图中集线器就是根集线器, 例如U盘的插入就是连接到根集线器上。
5. USB描述符
描述符通常是一种数据结构,在 Linux里面就是一个结构体,使用结构体来描述当前的设备有哪些特征,用于描述某个对象或设备的属性和特征。在USB和其他通信协议中,描述符扮演着重要的角色,它们包含了设备的基本信息,如设备类别、供应商ID、产品ID等,这些信息对于主机来说至关重要,因为主机需要依靠这些信息来识别和管理连接的设备。例如下述内容。
/* USB Device Descriptor */ struct usb_device_descriptor {uint8_t bLength; // 描述符的长度(以字节为单位),对于设备描述符来说总是18字节uint8_t bDescriptorType; // 描述符类型,对于设备描述符来说总是0x01uint16_t bcdUSB; // USB规范版本号,以二进制编码的十进制数表示uint8_t bDeviceClass; // 设备类别代码uint8_t bDeviceSubClass; // 设备子类代码uint8_t bDeviceProtocol; // 设备协议代码uint8_t bMaxPacketSize0; // 端点0的最大数据包大小(以字节为单位)uint16_t idVendor; // 供应商IDuint16_t idProduct; // 产品IDuint16_t bcdDevice; // 设备版本号,以二进制编码的十进制数表示uint8_t iManufacturer; // 供应商字符串描述符的索引uint8_t iProduct; // 产品字符串描述符的索引uint8_t iSerialNumber; // 序列号字符串描述符的索引uint8_t bNumConfigurations; // 支持的配置数量 } __attribute__((packed));
USB 描述符主要有:设备描述符,配置描述符,接口描述符,端点描述符。这些描述符共同构建了USB设备的特征和功能,通过读取这些描述符,系统可以配置驱动和参数,使得操作系统可以正确识别设备和通信。
(1)设备描述符:是USB设备中最重要的描述符之一,它包含了设备的基本信息,如设备类型、供应商ID、产品ID、设备版本等。这个描述符通常位于设备的EEPROM或闪存中,并在设备枚举过程中被主机读取。通过读取设备描述符,主机可以了解设备的基本属性,并决定如何与设备进行通信。设备描述符是设备连接到主机时第一个被请求和返回的信息。它提供了设备的本特征。
(2)配置描述符:描述设备支持的不同配置。
(3)接口描述符:描述了配置中的一个接口。
(4)端点描述符:描述接口中的一个端点。端点是数据在设备和主机之间传输的终点。一个具体的端点只能属于四种传输模式中的一种。
注意:
一个USB设备有1个设备描述符。
一个USB设备有1个或多个配置描述符。
一个USB配置有1个或多个接口描述符。
一个USB接口有0个或多个点描述符(不括端点0)。
6. 传输模式
数据通过USB总线传输,通常是通过端点(Endpoints)进行单向或双向的传输。USB使用一种层次化的协议栈,有四种传输模式:批量传输、中断传输、实时传输、控制传输。
(1)批量传输:适用于大数据量传输,且对实时性要求不高。主要用于传输不需要定时保证的数据,传输时没有时间限制,可以在空闲带宽下传输数据。常见应用:U盘、外部硬盘等。
(2)中断传输:用于传输少量、具有时间限制的数据,主机定期请求数据,传输间隔由设备和主机协商。实时性要求较高,但数据量较小,且数据传输频率是固定的。常见应用:鼠标、键盘、游戏手柄等输入设备。
(3)实时传输:适用于需要固定传输速率的场景,实时性要求高,但允许一定程度的丢包或误差。一般用于音频、视频等流式数据传输,确保数据能够以固定速率传输,尽管可能会丢失一些数据。常见应用:音视频设备、USB音响、摄像头等。
(4)控制传输(Control Transfer):用于设备配置、查询和命令的传输,数据量较小,且是双向传输。常用于 USB 设备的初始化和命令交换,传输的内容通常较为简单。常见应用:设备配置、读取设备状态、设置设备功能等。控制传输分为三个阶段:①建立阶段。②数据阶段。③确认阶段。
7. USB数据格式
US8通信数据格式由域、包、事务、传输组成。多个域组成包,多个包组成事务,多个事务组成传输。所以域是 USB 数据传输中最小的单位。事务是最基本的单位,对于我们开发人员来说关注事务和传输就可以了。
(1)域(Domain)
- 域是 USB 数据传输中的最小单位。每个域有特定的作用和格式,可以包含一个或多个字段。
- 七种类型的域:
- 同步域(SYNC):用于同步传输时钟,确保数据传输的时序性。
- 地址域(ADDR):标识 USB 设备的地址,通常用于指定数据的目标设备。
- 端点域(ENDPT):指定数据的目标端点,用于区分设备的不同数据流。
- 帧号域(FRAME):用于标识当前传输的帧号,在多帧传输中起到区分作用。
- 标识域(ID):用于标识不同的传输或者数据包。
- 数据域(Data):实际的数据内容区域,是传输的核心部分。
- 校验域(CRC):包含用于检验数据完整性的校验码,确保数据传输过程中没有错误。
(2)包(Packet)
包是由多个域组成的数据单元。根据包的不同类型,域的结构会有所不同。USB 包有四种主要类型:
令牌包(Token Packet):用于向设备发送请求或命令,包含地址和端点等信息。
数据包(Data Packet):用于传输实际的数据,包含数据域和校验域。
握手包(Handshake Packet):用于确认数据传输的结果,常见的有 ACK(确认)和 NAK(未确认)等。
特殊包(Special Packet):用于特殊的控制或管理操作,例如 RESET 或 SOF(帧开始信号)。
在 USB 中,PID 代表 Packet ID,即 数据包标识符。它用于标识 USB 数据包的类型,并且是在令牌包(Token Packet)和握手包(Handshake Packet)中必不可少的字段。常见的 PID 类型:
令牌包(Token Packet):
- OUT:用于主机向设备传输数据。
- IN:用于设备向主机传输数据。
- SETUP:用于设置设备的控制传输。
数据包(Data Packet):
- DATA0:表示数据包的第一个数据包,常用于控制传输。
- DATA1:表示数据包的第二个数据包。
握手包(Handshake Packet):
- ACK:确认数据包传输成功。
- NAK:表示数据包传输失败。
- STALL:表示设备在处理数据时发生错误,无法完成请求。
特殊包(Special Packet):
- PRE:预留包(用于扩展等特殊用途)。
(3)事务(Transaction)
- 事务是 USB 数据传输的基本单位,是由令牌包、数据包和握手包组成的。事务是传输过程中最小的可操作单元。
- 事务的结构:
- 令牌包(Token Packet):发起事务,标识目标设备、端点等信息。
- 数据包(Data Packet):可选,用于传输实际的数据。
- 握手包(Handshake Packet):可选,用于确认数据传输是否成功。
- 对开发者而言,事务是关注的重点,因为它直接影响数据的传输成功与否。
(4)传输(Transfer)
- 传输是更高层次的概念,通常由多个事务组成,多个事务之间在时间上有一定的顺序。
- 一个完整的传输可能涉及多个包、事务和域,最终确保数据从主机到设备(或反向)成功传送。
举例:
8. USB枚举
USB枚举(USB Enumeration)是指USB设备连接到计算机时,操作系统对该设备进行识别和配置的过程。这个过程包括一系列步骤,确保计算机能够正确识别设备、安装必要的驱动程序并为设备分配资源。
在USB枚举过程中主要使用控制传输模式。通常用于设备和主机之间的命令和状态交换。它的工作流程分为三个阶段:建立阶段(Setup)、数据阶段(Data) 和 确认阶段(Status)。
(1)建立阶段。
由USB主机发起,该阶段使用的是一个SETUP数据包,其结构包含了请求的命令、参数、设备的目标等信息。通过SETUP数据包,主机向设备发送命令,或者设备准备接收或发送数据。
如果是输入请求(例如获取设备信息),则设备会准备好数据供主机接收。如果是输出请求(例如配置设备),则设备会准备好数据接收主机发出的命令。
(2)数据阶段。
如果建立阶段是输入请求,则数据阶段由设备向主机传输数据。如果建立阶段是输出请求,则数据阶段由主机向设备传输数据。如果没有实际的数据传输需求(即请求的长度为零),此阶段依然会发送一个长度为0的数据包来表示数据传输完成。
(3)确认阶段。
确认阶段刚好跟建立阶段相反。如果数据阶段是输入请求(即主机接收设备数据),那么确认阶段是设备向主机发送一个输出数据包。如果数据阶段是输出请求(即主机向设备发送数据),那么确认阶段是主机向设备发送一个输入数据包。
确认阶段的主要目的是验证数据是否已经正确传输。如果传输成功,确认阶段不会包含任何数据;它只是作为一种状态确认,以告知数据传输是否正常完成。通常,成功的确认阶段只包含一个零长度数据包,这意味着数据传输完成且没有错误。
查看USB枚举过程中的数据内容,如下所示:
(1)传输。
(2) 传输和事务。
(3) 传输、事务、包和域。
★举例USB枚举过程:
二、应用编程
1. libusb库安装
libusb
是一个用 C 语言编写的开源库,主要用于简化 USB 设备的操作。通过 libusb
,开发者可以方便地与 USB 设备进行通信,而不需要深入了解 USB 协议的底层细节。
libusb
提供了跨平台的 API,可以在 Linux、macOS 和 Windows 等操作系统中使用,极大地提升了应用的可移植性。libusb
提供了一套简洁的 API,支持从 USB 1.0 到 USB 3.1 等不同版本的 USB 协议。无论是在传输数据、控制 USB 设备还是处理设备的其他操作时,API 接口保持一致,使用起来非常方便。
(1)下载。
下载地址:libusb官网。如下图所示,下载最新的版本即可。
(2)编译安装(X86下)。
我们下载所得到的就是libusb库的源码包,我们需要对源码包进行编译操作。在不同平台上使用的编译器不同,例如X86使用gcc,ARM使用交叉编译器。
<1> 安装依赖包。
sudo apt install -y libudev-dev
<2> 指定编译环境,这里为
x86_64-linux
架构。./configure --build=x86_64-linux
<3> 编译和安装。
make sudo make install
<4> 验证:进入/usr/local/lib查看。
(3)编译安装(ARM下)。
<1> 安装依赖包。
sudo apt install -y libudev-dev
<2> 指定编译环境,这里为ARM架构。(先在源码包路径下新建install文件夹,用于存放编译后的libusb库)
//--build:编译平台 //--host:目标平台 //--prefix:安装路径 //CC: 编译器 //CXX:g++编译器 //--disable-udev:用于禁用对 udev 的支持 ./configure --build=x86_64-linux --host=arm-linux --prefix=/home/qjl/libusb/libusb-1.0.27/install CC=arm-linux-gnueabihf-gcc CXX=arm-linux-gnueabihf-g++ --disable-udev
<3> 编译和安装。
make sudo make install
<4> 使用验证。
将源码目录下的example中的一个测试源码(例如listdevs.c)拷贝到install文件夹中。然后使用交叉编译器编译这个源码生成可执行文件。如下所示。将生产的可执行文件使用命令file来查看格式是否正确。将可执行文件传至开发板运行。
//-I ./include/libusb-1.0 :头文件路径 //-L ./lib :静态库路径 //-lusb-1.0 -lpthread -static 连接usb静态库 和线程thread库arm-linux-gnueabihf-gcc listdevs.c -o listdevs -I ./include/libusb-1.0 -L ./lib -lusb-1.0 -lpthread -static
2. 使用libusb库编写应用程序
编写应用程序,在编译时和第一小节编译验证类似,使用交叉编译器,并且需要连接静态库和头文件。注意:USB每次传输8个字节的数据!!
实验(一):打印USB设备的ID。
#include <stdio.h>
#include <libusb-1.0/libusb.h>int main() {libusb_device **device_list; //定义设备列表,包含ID等各种信息libusb_device *dev; struct libusb_device_descriptor desc; int ret, i = 0;ssize_t cnt;//初始化libusb库ret = libusb_init(NULL);if (ret < 0) {printf("libusb init error\n");return ret;}//获取设备列表数量cnt = libusb_get_device_list(NULL, &device_list);if (cnt < 0) {printf("libusb get device list error\n");return (int)cnt;}// Iterate through the deviceswhile ((dev = device_list[i++]) != NULL) {ret = libusb_get_device_descriptor(dev, &desc);if (ret < 0) {printf("libusb get device descriptor error\n");return ret;}// 打印厂商ID和产品IDprintf("Device %d: Vendor ID: 0x%04x, Product ID: 0x%04x\n", i, desc.idVendor, desc.idProduct);}// 释放设备列表资源libusb_free_device_list(device_list, 1);// 退出libusblibusb_exit(NULL);return 0;
}
实验(二):操作USB设备。(同步传输)
将键盘插到开发板的USB接口,读取键盘按下的数据。同步传输:只有执行完上一次,才可以执行下一次。注意:USB每次传输8个字节的数据!!
总体流程:初始化 libusb → 2. 获取设备列表 → 3. 遍历设备并查找中断传输端点 → 4. 打开设备并分离内核驱动 → 5. 进行中断传输 → 6. 打印接收到的数据并继续传输 → 7. 释放接口并关闭设备 → 8. 释放设备列表和退出 libusb。
#include <stdio.h>
#include <libusb-1.0/libusb.h>int main() {libusb_device **device_list; //定义设备列表libusb_device *dev; //定义设备struct libusb_device_descriptor desc; //定义设备描述struct libusb_config_descriptor *config_desc; //定义配置描述const struct libusb_interface_descriptor *interface_desc; //接口描述libusb_device_handle *dev_handle; //设备句柄unsigned char buffer[16]; //数据缓冲区int ret, i = 0, j, k, flag = 0;ssize_t cnt;int endpoint; // 假设是设备的输入端点地址ssize_t transferred;// 初始化 libusbret = libusb_init(NULL);if (ret < 0) {printf("libusb 初始化失败\n");return ret;}// 获取设备列表cnt = libusb_get_device_list(NULL, &device_list);if (cnt < 0) {printf("获取设备列表失败\n");return (int)cnt;}// 遍历设备列表while ((dev = device_list[i++]) != NULL) {
/*ret = libusb_get_device_descriptor(dev, &desc); if (ret < 0) {printf("获取设备描述符失败\n");return ret;}
*/// 获取设备的配置描述符ret = libusb_get_config_descriptor(dev, 0, &config_desc);if (ret < 0) {printf("获取配置描述符失败\n");return ret;}// 遍历接口for (k = 0; k < config_desc->bNumInterfaces; k++) {interface_desc = &config_desc->interface[k].altsetting[0];// 检查接口是否为人机设备接口(比如鼠标或键盘)if (interface_desc->bInterfaceClass == 3 || interface_desc->bInterfaceProtocol == 1) {// 遍历端点for (j = 0; j < interface_desc->bNumEndpoints; j++) {if ((interface_desc->endpoint[j].bmAttributes & 3) == LIBUSB_TRANSFER_TYPE_INTERRUPT) {// 找到中断传输类型的端点printf("找到中断端点,端点地址: 0x%02x\n", interface_desc->endpoint[j].bEndpointAddress);endpoint = interface_desc->endpoint[j].bEndpointAddress; // 设置实际的端点地址flag = 1;break;}}}if (flag) break;}if (flag) break;// 释放配置描述符libusb_free_config_descriptor(config_desc);}if (!flag) {printf("未找到支持中断传输的端点\n");libusb_free_device_list(device_list, 1);libusb_exit(NULL);return -1;}// 打开设备ret = libusb_open(dev, &dev_handle);if (ret < 0) {printf("打开设备失败\n");return ret;}// 自动分离内核驱动ret = libusb_set_auto_detach_kernel_driver(dev_handle, 1);if (ret < 0) {printf("自动分离内核驱动失败\n");return ret;}// 声明接口ret = libusb_claim_interface(dev_handle, interface_desc->bInterfaceNumber);if (ret < 0) {printf("声明接口失败\n");return ret;}// 进行中断传输while (1) {ret = libusb_interrupt_transfer(dev_handle, endpoint, buffer, sizeof(buffer), &transferred, 5000); // 5秒超时if (ret < 0) {printf("中断传输失败\n");break;}// 打印接收到的数据printf("接收到的数据: ");for (i = 0; i < transferred; i++) {printf("%02x ", buffer[i]);}printf("\n");}// 释放接口libusb_release_interface(dev_handle, interface_desc->bInterfaceNumber);// 关闭设备libusb_close(dev_handle);// 释放设备列表libusb_free_device_list(device_list, 1);// 退出 libusblibusb_exit(NULL);return 0;
}
实验结果:
实验(三):操作USB设备。(异步传输)
将键盘插到开发板的USB接口,读取键盘按下的数据。异步传输:程序能够异步地接收数据,而不需要每次都等待传输完成,可以有效地提高程序的响应能力。注意:USB每次传输8个字节的数据!!
总体流程:初始化 libusb → 2. 获取设备列表 → 3. 遍历设备并查找合适接口和端点 → 4. 打开设备并分离内核驱动 → 5. 配置并分配传输对象 → 6. 提交传输并处理事件 → 7. 回调函数处理接收数据并重新提交传输 → 8. 释放资源并退出。
#include <stdio.h>
#include <libusb-1.0/libusb.h>struct libusb_transfer *keyboard_transfer = NULL; // 用于传输的对象
#define BUFFER_SIZE 16
unsigned char buffer[BUFFER_SIZE]; // 数据缓冲区// 回调函数,处理接收到的数据
void callback_revc(struct libusb_transfer *transfer) {int i;int ret;if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {if (transfer->actual_length > 0) {// 打印接收到的数据unsigned char *buffer = transfer->buffer;for (i = 0; i < transfer->actual_length; i++) {printf("%02x ", buffer[i]);}printf("\n");}// 重新提交传输,继续接收数据ret = libusb_submit_transfer(transfer);if (ret < 0) {printf("重新提交传输失败\n");libusb_cancel_transfer(transfer);libusb_free_transfer(transfer);}} else {printf("传输状态错误: %d\n", transfer->status);libusb_cancel_transfer(transfer);libusb_free_transfer(transfer);}
}int main() {libusb_device **device_list; // 定义设备列表libusb_device *dev; // 定义设备struct libusb_device_descriptor desc; // 设备描述struct libusb_config_descriptor *config_desc; // 配置描述struct libusb_interface_descriptor *interface_desc; // 接口描述libusb_device_handle *dev_handle; // 设备句柄int ret, i = 0, j, k, flag = 0;ssize_t cnt;int endpoint; // 假设是设备的输入端点地址// 初始化 libusbret = libusb_init(NULL);// 获取设备列表cnt = libusb_get_device_list(NULL, &device_list);// 遍历设备列表while ((dev = device_list[i++]) != NULL) {ret = libusb_get_device_descriptor(dev, &desc); // 获取设备的配置描述符ret = libusb_get_config_descriptor(dev, 0, &config_desc);// 遍历接口for (k = 0; k < config_desc->bNumInterfaces; k++) {interface_desc = &config_desc->interface[k].altsetting[0];// 检查接口是否为人机设备接口(比如鼠标或键盘)if (interface_desc->bInterfaceClass == 3 || interface_desc->bInterfaceProtocol == 1) {// 遍历端点for (j = 0; j < interface_desc->bNumEndpoints; j++) {if ((interface_desc->endpoint[j].bmAttributes & 3) == LIBUSB_TRANSFER_TYPE_INTERRUPT) {// 找到中断传输类型的端点printf("找到中断端点,端点地址: 0x%02x\n", interface_desc->endpoint[j].bEndpointAddress);endpoint = interface_desc->endpoint[j].bEndpointAddress; // 设置实际的端点地址flag = 1;break;}}}if (flag) break;}if (flag) break;// 释放配置描述符libusb_free_config_descriptor(config_desc);}if (!flag) {printf("未找到支持中断传输的端点\n");libusb_free_device_list(device_list, 1);libusb_exit(NULL);return -1;}// 打开设备ret = libusb_open(dev, &dev_handle);// 自动分离内核驱动ret = libusb_set_auto_detach_kernel_driver(dev_handle, 1);// 声明接口ret = libusb_claim_interface(dev_handle, interface_desc->bInterfaceNumber);// 分配传输对象keyboard_transfer = libusb_alloc_transfer(0);// 配置中断传输libusb_fill_interrupt_transfer(keyboard_transfer, dev_handle, endpoint, buffer, BUFFER_SIZE, callback_revc, keyboard_transfer, 0);// 提交传输ret = libusb_submit_transfer(keyboard_transfer);// 处理事件while (1) {// libusb_handle_events 处理传输完成的事件libusb_handle_events(NULL);}libusb_release_interface(dev_handle, interface_desc->bInterfaceNumber); // 释放接口libusb_close(dev_handle); // 关闭设备libusb_free_device_list(device_list, 1); // 释放设备列表libusb_exit(NULL); // 退出 libusbreturn 0;
}
相关文章:
Linux应用编程(五)USB应用开发-libusb库
一、基础知识 1. USB接口是什么? USB接口(Universal Serial Bus)是一种通用串行总线,广泛使用的接口标准,主要用于连接计算机与外围设备(如键盘、鼠标、打印机、存储设备等)之间的数据传输和电…...
Swift语言探索:Sequence与Collection的详细解读
在Swift编程语言中,Sequence和Collection是两个非常重要的协议,它们定义了遍历和访问元素集合的方式。理解这两个协议不仅有助于我们更好地掌握Swift的集合类型,还能让我们在编写代码时更加灵活和高效。本文将详细解读Sequence与Collection&a…...
解锁C# EF/EF Core:从入门到进阶的技术飞跃
一、EF/EF Core 初相识 在.NET 开发的广阔天地中,Entity Framework (EF) 及其轻量级、可扩展、跨平台的版本 Entity Framework Core (EF Core),犹如两颗璀璨的明星,照亮了数据访问层开发的道路。它们是开源的对象关系映射器(ORM&…...
大模型搜广推?对算法工作的影响
大模型与传统应用结合的性质 长期看是一种范式革新。算力和模型定义的边界发生变化,选择生成式AI或大模型发展方向时,会不断发现新的增长曲线,目前在不断被验证。 现阶段大模型确实带来了增量信息,但推荐过程仍在原有流程基础上…...
【教程】最好的pytorch教程
文章目录 Materials for the Learn PyTorch for Deep Learning: Zero to Mastery course from: https://github.com/mrdbourke/pytorch-deep-learning/blob/main/01_pytorch_workflow.ipynb...
基于java线程池和EasyExcel实现异步导出
基于java线程池和EasyExcel实现异步导出 1.controller层 GetMapping("export") public void exportExcel(HttpServletResponse response) throws IOException, InterruptedException {exportService.exportExcel(response); }2. service public void exportExcel(H…...
vue3组件传值具体使用
问: left.vue文件调用接口获取了后端返回的urlLink字段,我该怎么传递给总的父组件index.vue中,我需要点击父组件的一个按钮来触发跳转? 回答: 在 Vue 3 中使用 TypeScript 和 setup 语法糖时,可以通过 e…...
《CPython Internals》阅读笔记:p336-p352
《CPython Internals》学习第 17天,p336-p352 总结,总计 17 页。 一、技术总结 1.GDB GDB 是 GNU Dbugger 的缩写。 (1)安装 sudo apt install gdb(2)创建 .gdbinit 文件 touch ~/.gdbinitvim ~/.gdbinit(3)配置 .gdbinit 文件 add-auto-load-saf…...
WPF基础 | WPF 常用控件实战:Button、TextBox 等的基础应用
WPF基础 | WPF 常用控件实战:Button、TextBox 等的基础应用 一、前言二、Button 控件基础2.1 Button 的基本定义与显示2.2 按钮样式设置2.3 按钮大小与布局 三、Button 的交互功能3.1 点击事件处理3.2 鼠标悬停与离开效果3.3 按钮禁用与启用 四、TextBox 控件基础4.…...
Langchain+讯飞星火大模型Spark Max调用
1、安装langchain #安装langchain环境 pip install langchain0.3.3 openai -i https://mirrors.aliyun.com/pypi/simple #灵积模型服务 pip install dashscope -i https://mirrors.aliyun.com/pypi/simple #安装第三方集成,就是各种大语言模型 pip install langchain-comm…...
2025年华为云一键快速部署幻兽帕鲁联机服务器教程
华为云如何快速部署幻兽帕鲁联机服务器?为了方便新手玩家搭建专属游戏联机服务器,华为云推出了云游戏专场,无需专业技术,新手小白也能一键快速部署幻兽帕鲁联机服务器! 华为云快速部署幻兽帕鲁联机服务器教程如下&…...
Langchain+文心一言调用
import osfrom langchain_community.llms import QianfanLLMEndpointos.environ["QIANFAN_AK"] "" os.environ["QIANFAN_SK"] ""llm_wenxin QianfanLLMEndpoint()res llm_wenxin.invoke("中国国庆日是哪一天?") print(…...
C++AVL树(一)详解
文章目录 AVL树概念AVL树的插入平衡因子的更新旋转的规则左单旋右单旋抽象的情况h0h1h 2h 3 AVL树 概念 AVL树是一棵平衡二叉查找树,AVL树是空树,保证左右子树都是AVL树,AVL树要求高度差的绝对值不超过1,因为最好情况是1&#…...
ceph新增节点,OSD设备,标签管理(二)
一、访问客户端集群方式 方式一: 使用cephadm shell交互式配置 [rootceph141 ~]# cephadm shell # 注意,此命令会启动一个新的容器,运行玩后会退出! Inferring fsid c153209c-d8a0-11ef-a0ed-bdb84668ed01 Inferring config /var/lib/ce…...
c++学习第七天
创作过程中难免有不足,若您发现本文内容有误,恳请不吝赐教。 提示:以下是本篇文章正文内容,下面案例可供参考。 一、const成员函数 //Date.h#pragma once#include<iostream> using namespace std;class Date { public:Date…...
F/V/F/I频率脉冲信号转换器
F/V/F/I频率脉冲信号转换器 概述:捷晟达科技的JSD TFA-1001系列是一进一出频率脉冲信号转换器(F/V转换器),该频率转换器是将频率脉冲信号(方波、正弦波、锯齿波)转换成国际标准的模拟量电压(电流)信号,并远距离无失真传送到控制室(如:PLC,DCS,AD,PC采集系统)产品的输…...
SQLServer中DBCC INPUTBUFFER显示从客户端发送到 SQL Server 实例的最后一个语句
SQLServer中DBCC INPUTBUFFER显示从客户端发送到 SQL Server 实例的最后一个语句 1、本文内容 语法参数结果集权限示例 适用于: SQL ServerAzure SQL 数据库Azure SQL 托管实例 显示从客户端发送到 SQL Server 实例的最后一个语句。 2、语法 DBCC INPUTBUFFE…...
Mysql面试题----MySQL中CHAR和VARCHAR的区别
存储方式 CHAR:是一种固定长度的字符串类型。它会按照定义的长度来分配存储空间,无论实际存储的字符串长度是多少。例如,定义一个 CHAR (10) 的列,如果存储的值是 ‘ab’,那么它仍然会占用 10 个字符的存储空间&#…...
github汉化
本文主要讲述了github如何汉化的方法。 目录 问题描述汉化步骤1.打开github,搜索github-chinese2.打开项目,打开README.md3.下载安装脚本管理器3.1 在README.md中往下滑动,找到浏览器与脚本管理器3.2 选择浏览器对应的脚本管理器3.2.1 点击去…...
探索JavaScript前端开发:开启交互之门的神奇钥匙(二)
目录 引言 四、事件处理 4.1 事件类型 4.2 事件监听器 五、实战案例:打造简易待办事项列表 5.1 HTML 结构搭建 5.2 JavaScript 功能实现 六、进阶拓展:异步编程与 Ajax 6.1 异步编程概念 6.2 Ajax 原理与使用 七、前沿框架:Vue.js …...
什么是网络爬虫?Python爬虫到底怎么学?
最近我在研究 Python 网络爬虫,发现这玩意儿真是有趣,干脆和大家聊聊我的心得吧!咱们都知道,网络上的信息多得就像大海里的水,而网络爬虫就像一个勤劳的小矿工,能帮我们从这片浩瀚的信息海洋中挖掘出需要的…...
React 中hooks之 React.memo 和 useMemo用法总结
1. React.memo 基础 React.memo 是一个高阶组件(HOC),用于优化函数组件的性能,通过记忆组件渲染结果来避免不必要的重新渲染。 1.1 基本用法 const MemoizedComponent React.memo(function MyComponent(props) {/* 渲染逻辑 *…...
springboot基于微信小程序的手机银行系统
Spring Boot基于微信小程序的手机银行系统是一种结合现代Web技术和移动应用优势的创新金融服务平台。 一、系统背景与意义 随着信息技术的快速发展和用户对便捷金融服务需求的日益增长,传统手机银行系统的人工管理方法已逐渐显露出效率低下、安全性低以及信息传输…...
Kafka后台启动命令
#保存日志 nohup ./kafka-server-start.sh ../config/server.properties > /path/to/logfile.log 2>&1 &#不保存日志 nohup ./kafka-server-start.sh ../config/server.properties >/dev/null 2>&1 & nohup: 是一个Unix/Linux命令,用于…...
面试题目1
文章目录 C语言函数调用详细过程C语言中const与C中const的区别关系运算符有哪些互斥锁与读写锁的区别gcc的编译过程 C语言函数调用详细过程 调用函数:当程序执行到函数调用语句时,会将当前函数的返回地址、参数列表等信息压入栈中,然后跳转到…...
Android AutoMotive --CarService
1、AAOS概述 Android AutoMotive OS是谷歌针对车机使用场景打造的操作系统,它是基于现有Android系统的基础上增加了新特性,最主要的就是增加了CarService(汽车服务)模块。我们很容易把Android AutoMotive和Android Auto搞混&…...
CSDN 博客之星 2024:默语的技术进阶与社区耕耘之旅
CSDN 博客之星 2024:默语的技术进阶与社区耕耘之旅 🌟 默语,是一位在技术分享与社区建设中坚持深耕的博客作者。今年,我有幸再次入围成为 CSDN 博客之星TOP300 的一员,这既是对过往努力的肯定,也是对未来探…...
【Vim Masterclass 笔记24】S10L43 + L44:同步练习10 —— 基于 Vim 缓冲区的各类基础操作练习(含点评课)
文章目录 S10L43 Exercise 12 - Vim Buffers1 训练目标2 操作指令2.1. 打开 buf* 文件2.2. 查看缓冲区 View the buffers2.3. 切换缓冲区 Switch buffers2.4. 同时编辑多个缓冲区 Edit multiple buffers at once2.5. 缓冲区的增删操作 Add and delete buffers2.6. 练习 Vim 内置…...
python如何使得pdf加水印后的大小尽可能小
在 Python 中为 PDF 添加水印并尽可能减少文件大小,可以采取以下优化策略: 1. 使用合适的库 常用的 PDF 处理库: PyMuPDF(fitz):高效且优化的 PDF 处理reportlab pdfrw:可实现水印合并&#…...
Leetcode 3429. Paint House IV
Leetcode 3429. Paint House IV 1. 解题思路2. 代码实现 题目链接:3429. Paint House IV 1. 解题思路 这一题解法上就是一个动态规划的思路,由于题目有两个限制条件,即相邻不可以同色,以及前后同位置不可以同色,因此…...
ASP.NET Core 实战:JWT 身份验证
一、引言 在当今数字化时代,Web 应用的安全性至关重要。ASP.NET Core 作为一种广泛应用的开发框架,为开发者提供了强大的工具来构建安全可靠的应用程序。而 JWT(JSON Web Token)身份验证则是保障应用安全的关键环节之一。 JWT 身…...
【学习笔记15】如何在非root服务器中,安装属于自己的redis
一、下载安装包 官网下载黑马程序员给的安装包(redis-6.2.6) 二、将安装包上传至服务器 我将安装包上传在我的文件夹/home/XXX,指定路径中/src/local/redis/,绝对路径为/home/XXX/src/local/redis/解压安装包 XXXomega:~$ cd …...
基于深度学习的微出血自动检测及解剖尺度定位|文献速递-视觉大模型医疗图像应用
Title 题目 Toward automated detection of microbleeds with anatomical scale localization using deep learning 基于深度学习的微出血自动检测及解剖尺度定位 01 文献速递介绍 基于深度学习的脑微出血(CMBs)检测与解剖定位 脑微出血ÿ…...
Couchbase UI: Dashboard
以下是 Couchbase UI Dashboard 页面详细介绍,包括页面布局和功能说明,帮助你更好地理解和使用。 1. 首页(Overview) 功能:提供集群的整体健康状态和性能摘要 集群状态 节点健康状况:绿色(正…...
Python
1 变量 1.1 定义 变量:为快速定义目标,将数据在内存占据的存储空间分配的一个名称。 定义:变量名 数据值 作用:临时存储数据 message "hello" print(message)#输出:hello message "hello Pytho…...
一个软件分发和下载的网站源码,带多套模板
PHP游戏应用市场APP软件下载平台网站源码手机版 可自行打包APP,带下载统计,带多套模板,带图文教程 代码下载:百度网盘...
war包 | Docker部署flowable-ui
文章目录 引言I war包部署flowable-ui下载war包配置Tomcat访问 flowable-uiII Docker启动flowable-ui并修改配置Docker启动flowable-ui修改配置访问Flowable UI界面。III 知识扩展加速源docker run -i -t -d 参数引言 Flowable 支持 BPMN 2.0 行业标准,同时提供了一些 Flowab…...
07_游戏加载窗口
隐藏动态提示窗口 创建空节点 命名为 LoadingWnd 意为加载窗口 并设置全屏 在子级下创建Image作为加载背景 也设置成全屏 将以下资源放进Art文件夹中 设置好精灵模式后拖拽至 Image的Source Image框选 创建文本作为提示内容 增加描边组件OutLine可以美化字体 创建Image作为加载…...
proxyman抓包Java中feign请求以及断点请求响应内容修改或流转到本地
proxyman抓包Java中feign请求以及断点请求响应内容修改或流转到本地 配置流程第一步: 借助arthas配置请求代理第二步: 借助proxyman配置远程映射第三步: 借助SwitchHosts配置hosts域名最后: 借助ssh的LocalForward功能, 打通网络(这步网络不通才需要) 最近在修bug的过程中, 因为…...
PyTorch使用教程(10)-torchinfo.summary网络结构可视化详细说明
1、基本介绍 torchinfo是一个为PyTorch用户量身定做的开源工具,其核心功能之一是summary函数。这个函数旨在简化模型的开发与调试流程,让模型架构一目了然。通过torchinfo的summary函数,用户可以快速获取模型的详细结构和统计信息࿰…...
centos9编译安装opensips 二【进阶篇-定制目录+模块】推荐
环境:centos9 last opensips -V version: opensips 3.6.0-dev (x86_64/linux) flags: STATS: On, DISABLE_NAGLE, USE_MCAST, SHM_MMAP, PKG_MALLOC, Q_MALLOC, F_MALLOC, HP_MALLOC, DBG_MALLOC, CC_O0, FAST_LOCK-ADAPTIVE_WAIT ADAPTIVE_WAIT_LOOPS1024, MAX_RE…...
MongoDB 备份与恢复综述
目录 一、基本概述 二、逻辑备份 1、全量备份 2、增量备份 3、恢复 三、物理备份 1、cp/tar/fsync 2、WiredTiger 热备份 3、恢复 四、快照备份 一、基本概述 MongoDB 是一种流行的 NoSQL 数据库,它使用文档存储数据,支持丰富的查询语言和索引…...
Apache Hive3定位表并更改其位置
Apache Hive3表 1、Apache Hive3表概述2、Hive3表存储格式3、Hive3事务表4、Hive3外部表5、定位Hive3表并更改位置6、使用点表示法引用表7、理解CREATE TABLE行为 1、Apache Hive3表概述 Apache Hive3表类型的定义和表类型与ACID属性的关系图使得Hive表变得清晰。表的位置取决于…...
Flutter项目和鸿蒙平台的通信
Flutter项目和鸿蒙平台的通信 前言Flutter和Harmonyos通信MethodChannelBasicMessageChannelEventChannel 前言 大家在使用Flutter开发项目的时候, Flutter提供了Platfrom Channel API来和个个平台进行交互。 Flutter官方目前提供了一下三种方式来和个个平台交互&…...
5. 马科维茨资产组合模型+政策意图AI金融智能体(Qwen-Max)增强方案(理论+Python实战)
目录 0. 承前1. AI金融智能体1.1 What is AI金融智能体1.2 Why is AI金融智能体1.3 How to AI金融智能体 2. 数据要素&计算流程2.1 参数集设置2.2 数据获取&预处理2.3 收益率计算2.4 因子构建与预期收益率计算2.5 协方差矩阵计算2.6 投资组合优化2.7 持仓筛选2.8 AI金融…...
嵌入式知识点总结 ARM体系与架构 专题提升(一)-硬件基础
嵌入式知识点总结 ARM体系与架构 专题提升(一)-硬件基础 目录 1.NAND FLASH 和NOR FLASH异同 ? 2.CPU,MPU,MCU,SOC,SOPC联系与差别? 3.什么是交叉编译? 4.为什么要交叉编译? 5.描述一下嵌入式基于ROM的运行方式和基于RAM的运行方式有什么区别? 1…...
thinkphp8在使用apidoc时, 4层的接口会有问题 解决办法
thinkphp8 4层的接口会有问题, 比如这样的接口 /adminapi/notice/announcements/lists, 应该换成 /adminapi/notice.announcements/lists 这样才行, 有没有人处理过? 实际上在官网的帮助里有描述 自动生成的url不对? | Apidoc // config/apidoc.php //... auto_url…...
【jmeter】下载及使用教程【mac】
1.安装java 打开 Java 官方下载网站https://www.oracle.com/java/technologies/downloads/选择您想要下载的 Java 版本,下载以 .dmg 结尾的安装包,注意 JMeter 需要 Java 8下载后打开安装包点击“安装”按钮即可 2.下载jmeter 打开 Apache JMeter 官方…...
C# ASP.NET MVC项目内使用ApiController
1.在App_Start文件夹新建WebApiConfig.cs文件,建立webApi路由的注册方法。 using System.Web.Http;namespace PrivilegeManager {public class WebApiConfig{public static void Register(HttpConfiguration config){config.MapHttpAttributeRoutes();config.Route…...
Langchain+FastApi+Vue前后端Ai对话(超详细)
一、引入 首先可以先看下作者的文章 FastApi相关文章:创建最简单FastApi的项目Vue相关文章:最简单的aixos二次封装Langchain相关文章:如何使用LangSmith跟踪deepseek模型 二、后端搭建 1 项目文件结构 routers:存放api接口se…...