参考:
libusb常用函数 (https://blog.csdn.net/weixin_41466668/article/details/78898543)
libusb枚举设备的过程
libusb学习笔记(5)
Linux libusb开发教程<二> API编程接口介绍
USB描述符
usb驱动的基本结构和函数简介
libusb实现读写USB设备
简介
USB3 Vision是一个基于USB 3.0接口的机器视觉通信协议。它由自动化成像协会 (AIA)开发,用于支持高速、高带宽的视频流和相机控制。
在Linux环境下,使用USB3 Vision协议与USB相机设备进行连接和通信,通常需要以下步骤:
设备发现
首先,你需要发现并识别连接到系统的USB3 Vision设备。这通常通过读取设备的描述符和属性来完成。
libusb_device** devices;
libusb_device* device = NULL;
libusb_context *context = NULL;
ssize_t device_count;
int i = 0;
// 初始化 libusb。 必须在调用任何其他 libusb 函数之前调用此函数。
int result = libusb_init(&context);
if (result < 0) {
std::cerr << "Failed to initialize libusb: " << libusb_error_name(result) << std::endl;
return 1;
}
// 返回当前连接到系统的 USB 设备列表。 这是查找要操作的 USB 设备的入口点。
device_count = libusb_get_device_list(context, &devices);
if (device_count < 0)
{
std::cerr << "Failed to get device list: " << libusb_error_name(device_count) << std::endl;
libusb_exit(context);
return 1;
}
// 遍历所有usb可连接设备 筛选出需要连接的设备 并记录ID(可以自定义修改)
while ((device = devices[i++]) != NULL)
{
unsigned long long ID = 0;
struct libusb_device_descriptor desc;
result = libusb_get_device_descriptor(device, &desc);
if (result < 0) {
std::cerr << "Failed to get device descriptor: " << libusb_error_name(result) << std::endl;
continue;
}
// 筛选相机usb设备 !根据你自己需要连接的设备进行修改 记录ID
if (desc.bDeviceClass == 0xFF && desc.bDeviceSubClass == 0xFF && desc.bDeviceProtocol == 0xFF)
{
uint8_t portID = libusb_get_port_number(device);
unsigned long long DevID = desc.idVendor << 16 | desc.idProduct;
ID = DevID << 32 | portID;
vecId.push_back(ID);
}
}
// 释放先前使用 libusb_get_device_list() 发现的设备列表。 如果设置了 unref_devices 参数,则列表中每个设备的引用计数减 1。
libusb_free_device_list(devices, 1);
libusb_exit(context);
设备打开
一旦设备被识别,你需要打开设备以进行进一步的通信。这通常涉及到获取设备的句柄和初始化设备。
// 获取设备和设备句柄 打开设备
libusb_device_handle* m_usbHandle;
libusb_device* m_usbDev;
libusb_device** devs;
libusb_device* dev;
ssize_t cnt;
// 根据初始化时获取的相机列表获取出相关相机
// 返回当前连接到系统的 USB 设备列表。 这是查找要操作的 USB 设备的入口点。
cnt = libusb_get_device_list(NULL, &devs);
if (cnt < 0)
{
libusb_exit(NULL);
return 0;
}
while ((dev = devs[i++]) != NULL)
{
struct libusb_device_descriptor desc;
libusb_get_device_descriptor(dev, &desc);
uint8_t portID = libusb_get_port_number(dev);
if (desc.idVendor == vid && desc.idProduct == pid && portID == portid)
{
break;
}
}
int result = libusb_open(dev, &m_usbHandle);
if (result < 0) {
std::cerr << "Failed to open device: " << libusb_error_name(result) << std::endl;
}
m_usbDev = libusb_get_device(m_usbHandle);
if (m_usbDev == NULL) {
std::cerr << "Failed to libusb_get_device. " << std::endl;
}
libusb_free_device_list(devs, 1);
// 获取设备标准描述符 用于通信
struct libusb_device_descriptor desc;
// libusb_get_device_descriptor获取指定设备的USB设备描述符
libusb_get_device_descriptor(m_usbDev, &desc);
bool specFlag = false;
if (m_usbDev)
{
// 遍历所有可能的配置数
for (int i = 0; i < desc.bNumConfigurations; i++)
{
// usb设备配置描述结构体
struct libusb_config_descriptor* config = NULL;
//libusb_get_config_descriptor获取配置描述符、接口描述符、端点描述符信息。
libusb_get_config_descriptor(m_usbDev, i, &config);
// 配置所支持的接口数,指该配置配备的接口数量,也表示该配置下接口描述符数量。
for (int j = 0; (config != NULL && j < config->bNumInterfaces); j++)
{
// 给当前设备绑定接口到全局变量
struct libusb_interface interface = config->interface[j];
// interface.num_altsetting:备用设置数
for (int k = 0; k < interface.num_altsetting; k++)
{
// libusb_interface_descriptor
// usb描述符,主要有四种usb描述符,设备描述符,配置描述符,接口描述符和端点描述符,
// 协议里规定一个usb设备是必须支持这四大描述符的
struct libusb_interface_descriptor intfDesc = interface.altsetting[k];
// intfDesc.bInterfaceProtocol:接口协议
if (intfDesc.bInterfaceProtocol == 0)
{
// intfDesc.bInterfaceNumber:接口号
unsigned char m_ctrlInterface = static_cast<unsigned char>(intfDesc.bInterfaceNumber);
// intfDesc.bNumEndpoints:接口拥有的端点数量
for (int h = 0; h < intfDesc.bNumEndpoints; h++)
{
// libusb端点描述符
struct libusb_endpoint_descriptor endDesc = intfDesc.endpoint[h];
// __u8 bEndpointAddress;
// 它的bits 0~3表示的就是端点号,USB_ENDPOINT_NUMBER_MASK;bit7是方向,USB_ENDPOINT_DIR_MASK
// 判断是输入端点还是输出端点
if (GET_BIT(endDesc.bEndpointAddress, 7))
{
unsigned char m_ctrlInEp = static_cast<int>(endDesc.bEndpointAddress);
}
else
{
unsigned char m_ctrlOutEp = static_cast<int>(endDesc.bEndpointAddress);
}
}
}
else if (intfDesc.bInterfaceProtocol == 1)
{
unsigned char m_eventInterface = static_cast<unsigned char>(intfDesc.bInterfaceNumber);
for (int h = 0; h < intfDesc.bNumEndpoints; h++)
{
struct libusb_endpoint_descriptor endDesc = intfDesc.endpoint[h];
unsigned char m_eventEp = static_cast<int>(endDesc.bEndpointAddress);
}
}
else if (intfDesc.bInterfaceProtocol == 2)
{
unsigned char m_streamInterface = static_cast<unsigned char>(intfDesc.bInterfaceNumber);
for (int h = 0; h < intfDesc.bNumEndpoints; h++)
{
struct libusb_endpoint_descriptor endDesc = intfDesc.endpoint[h];
unsigned char m_streamEp = static_cast<int>(endDesc.bEndpointAddress);
}
}
}
}
}
}
if (m_usbHandle)
{
// 内核驱动激活与分离
// libusb_kernel_active获取驱动是否被加载
if (1 == libusb_kernel_driver_active(m_usbHandle, m_ctrlInterface))
{
if (0 == libusb_detach_kernel_driver(m_usbHandle, m_ctrlInterface))
{
printf("Kernel Driver Detached\n");
}
}
// 声明接口
int rtn = libusb_claim_interface(m_usbHandle, m_ctrlInterface);
if (1 == libusb_kernel_driver_active(m_usbHandle, m_eventInterface))
{
if (0 == libusb_detach_kernel_driver(m_usbHandle, m_eventInterface))
{
printf("Kernel Driver Detached\n");
}
}
rtn = libusb_claim_interface(m_usbHandle, m_eventInterface);
if (1 == libusb_kernel_driver_active(m_usbHandle, m_streamInterface))
{
if (0 == libusb_detach_kernel_driver(m_usbHandle, m_streamInterface))
{
printf("Kernel Driver Detached\n");
}
}
// 注册一个通信接口
// 返回值:成功0;EBUSY接口无效,无法注册;ENOMEM内存不足。
rtn = libusb_claim_interface(m_usbHandle, m_streamInterface);
}
设备配置
在设备打开后,你需要配置设备的参数,如分辨率、帧率、曝光时间等。这通常通过发送控制传输请求来完成。
int ReadMemory(unsigned long long startAddr, unsigned char* readBuff, unsigned int length)
{
unsigned int len = 4294967295; // 根据自己需求修改
auto cmdBuffPtr = std::unique_ptr<char[]>(new char[len / sizeof(char)]);
unsigned int maxReadLength = 1024; // 根据自己需求修改
unsigned int bytes2Read = length;
unsigned long long startAddrOnce = startAddr;
unsigned char* pBuff = readBuff;
while (bytes2Read)
{
unsigned int readOnce = maxReadLength;
if (bytes2Read < maxReadLength)
{
readOnce = bytes2Read;
}
unsigned short sendReqId = m_reqId;
std::lock_guard < std::mutex> lock(m_mutex);
// 拷贝内存
new (cmdBuffPtr.get()) GENCP_CMD_HEADER(GENCP_CMD::GENCP_READMEM_CMD, sizeof(READ_CMD_DATA), sendReqId);
GENCP_CMD_ADDRESS address;
memcpy(&address, &startAddrOnce, sizeof(GENCP_CMD_ADDRESS));
READ_CMD_DATA cmdData = { address,0 ,readOnce };
memcpy(cmdBuffPtr.get() + sizeof(GENCP_CMD_HEADER), &cmdData, sizeof(READ_CMD_DATA));
auto ackBuffPtr = std::unique_ptr<char[]>(new char[m_maxAckTransLength / sizeof(char)]);
//printf("%s:%d \n", __FILE__, __LINE__);
int rtn = TransceiveData(reinterpret_cast<char*>(cmdBuffPtr.get()),
len,
readOnce,
enumType(GENCP_CMD::GENCP_READMEM_ACK),
reinterpret_cast<char*>(ackBuffPtr.get()),
m_maxAckTransLength);
//printf("%s:%d \n", __FILE__, __LINE__);
if (rtn < 0)
{
printf("%s:%d ERR_INVALID_DATA_RECEIVED\n", __FILE__, __LINE__);
return ERR_INVALID_DATA_RECEIVED;
}
GENCP_CMD_HEADER* pData = reinterpret_cast<GENCP_CMD_HEADER*>(ackBuffPtr.get());
unsigned int recvReqId = pData->req_id;
if (recvReqId != sendReqId)
{
printf("%s:%d ERR_INVALID_DATA_RECEIVED\n", __FILE__, __LINE__);
return ERR_INVALID_DATA_RECEIVED;
}
memcpy(pBuff, ackBuffPtr.get() + sizeof(GENCP_CMD_HEADER), readOnce);
startAddrOnce += readOnce;
pBuff += readOnce;
bytes2Read -= readOnce;
}
//printf("%s:%d \n", __FILE__, __LINE__);
return length;
}
- 数据流控制: 配置好设备后,你需要启动数据流以开始接收视频数据。这通常涉及到分配和提交传输缓 冲区,然后启动数据流。
- 数据接收:一旦数据流启动,你就可以开始接收视频数据了。这通常通过异步或同步的方式来完成,取决于你的应用需求。
- 设备关闭: 最后,当你完成所有操作后,你需要关闭设备以释放资源
在每个步骤中,你都可能需要发送和接收各种类型的信息,包括设备描述符、设备属性、控制传输请求、数据流命令等。具体的信息类型和格式取决于USB3 Vision协议的规范和你的设备的实现。
注意,要在Linux环境下使用USB3 Vision协议,你可能需要一个支持该协议的库,如libusb或者专门的USB3 Vision SDK。这些库通常会提供一套API,使你能够更容易地进行上述步骤。