用libusb在虚拟机上采集相机的数据
连接方式如下图
![在这里插入图片描述](https://img-blog.csdnimg.cn/8b07176dde3048deae223d96a310a264.png)
1、下载编译
官网下载,选择Downloads
,会跳转到Github
,选择你想要的版本下载即可,以1.0.21版本为例。然后放到虚拟机上,解压,进入目录libusb-1.0.21
。
下面就是安装的老一套了,没权限的话sudo
执行即可。执行make install
的时候可能会报错"configure: error: "udev support requested but libudev not installed"
,缺东西,那安装就行了,执行apt-get install libudev-dev
,再次make install
就ok了
(多说一句,直接make默认是在x86-64
平台下,如下用file
查看该库,在虚拟机上编译后可以直接使用。之前是在虚拟机上读,现在又添加需求要求在arm板子上读取usb数据。
使用在虚拟机上编译的库文件会报错libusb-1.0.so: file not recognized: file format not recognized
,就是说平台不兼容。)
编译为arm平台下要先执行以下命令:
./configure --build=i686-linux --host=arm-linux --prefix=/home/book/code/libusb/libusb_install CC=arm-linux-gnueabihf-gcc --disable-udev
分别表示:编译平台,目标平台,安装路径,我使用的工具链,不需要依赖udev
然后执行
make
make install
默认编译:
file查看为x86-64平台:
shimengwei@ubuntu1804:~/nfs/test$ file libusb-1.0.so
libusb-1.0.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=ca0617f4fe19694b0f82ad8dcbade21b25b13706, with debug_info, not stripped
重新编译为arm平台:
book@100ask:~/code/libusb/libusb_install/lib$ file libusb-1.0.so.0.1.0
libusb-1.0.so.0.1.0: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, BuildID[sha1]=44d7c7dc4a6ccc728e4840e7720567a352309a6f, with debug_info, not stripped
默认编译命令,可以在虚拟机上使用
./configure
make
make install
默认安装路径的话,会在/usr/local/include
目录下找到头文件目录libusb-1.0
在/usr/local/lib
目录下也会有如下几个库
2、使用
1、虚拟机设置
当usb线插到主机上时,虚拟机会选择让你连接主机还是连接虚拟机,这里当然是连接虚拟机了,如果想连接到主机,在下图位置设置,断开连接
一般虚拟机都有USB控制器的吧!那就没问题了
2、查看usb设备
插usb前后,我这里多出了4206:3702
,这俩ID要记住,后边要用
3、代码读取相机数据
关于代码,我也没怎么了解,就大致看了一下他有哪些函数,怎么用的,然后我就搜了一下读取的例子然后改的,代码中有好多没用到的变量我也删除,有的是别人代码遗留的,有的是我加的没使用,现在脑袋太大了不想删了!(最后面已经添加整理后的代码)
代码中给出了注释
#include <stdio.h>
#include <sys/types.h>
#include <libusb.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#define USB_VID 0x4206 //USB的产商ID,这里就是你lsusb得到的哪两个ID
#define USB_PID 0x3702 //USB的产品ID
//这里的通道好像是插入USB的时候打印的信息中的东西,我直接使用的
#define EP0ADDR 0x01 //Write端口0地址,通道0
#define EP1ADDR 0x81 //Read 端口1地址,通道1
#define EP2ADDR 0x02 //Write端口2地址,通道2
#define EP3ADDR 0x86 //Read 端口3地址,通道3
#define USB_TIMEOUT 0 //传输数据的时间延迟
#define COL 256 //图像宽
#define IR_ROW 192 //图像高
#define IR_IMAGE_SIZE (COL*IR_ROW*2) //一帧图像的大小,每个点2个字节
#define IR_IMAGE_SIZE1 (101440) //这个值放到代码外部说,太多了
static libusb_device_handle *dev_handle = NULL;
void printdev(libusb_device *dev){
struct libusb_device_descriptor desc;
int r = libusb_get_device_descriptor(dev, &desc);
if(r <0){
printf("failed to get device descriptor\n");
return;
}
printf("Number of possible configurations: %d\n,Device Class: %d\n",(int)desc.bNumConfigurations,(int)desc.bDeviceClass);
printf("VendorID: %d\n",desc.idVendor);
printf("ProductID: %d\n",desc.idProduct);
struct libusb_config_descriptor *config;
libusb_get_config_descriptor(dev, 0, &config);
printf("Interfaces: %d\n",(int)config->bNumInterfaces);
const struct libusb_interface *inter;
const struct libusb_interface_descriptor *interdesc;
const struct libusb_endpoint_descriptor *epdesc;
for(int i=0; i<(int)config->bNumInterfaces; i++){
inter =&config->interface[i];
printf("Number of alternate settings: %d\n",inter->num_altsetting);
for(int j=0; j<inter->num_altsetting; j++){
interdesc =&inter->altsetting[j];
printf("Interface Number: %d\n",(int)interdesc->bInterfaceNumber);
printf("Number of endpoints: %d\n",(int)interdesc->bNumEndpoints);
for(int k=0; k<(int)interdesc->bNumEndpoints; k++){
epdesc =&interdesc->endpoint[k];
printf("Descriptor Type: %d\n",(int)epdesc->bDescriptorType);
printf("EP Address: %d\n",(int)epdesc->bEndpointAddress);
}
}
}
libusb_free_config_descriptor(config);
}
int main(int argc, char **argv) {
int i = 0;
int ret = 1;
unsigned int transferred = 0;
ssize_t cnt;
unsigned char cmd_ir_start[IR_IMAGE_SIZE1+1] = "\0";
memset(cmd_ir_start, 0, IR_IMAGE_SIZE1+1);
unsigned char cmd_stop[50] = {0x5e, 0xaa};
char cmd_state[64];
unsigned short data_ir[IR_ROW][COL];
struct libusb_device_descriptor desc;
libusb_device **devs;
libusb_context *ctx = NULL;
//下面这个if没测试,应该能用
if(argc < 3)
{
printf("Usage: sudo ./a.out 0 20\n");
}
ret = libusb_init(NULL);
if(ret < 0)
{
fprintf(stderr, "failed to initialise libusb\n");
return 1;
}
cnt = libusb_get_device_list(ctx, &devs);
if(cnt < 0)
{
perror("Get Device Error\n");
return 1;
}
dev_handle = libusb_open_device_with_vid_pid(NULL, USB_VID, USB_PID);
if(dev_handle == NULL)
{
perror("Cannot open device\n");
}
else
{
printf("Device Opened\n");
}
printf("******************______************\n");
printf("cnt = %ld\n", cnt);
for(i =0; i < cnt; i++)
{
printdev(devs[i]);
printf("__________________******_____________\n");
}
libusb_free_device_list(devs, 1);
if(libusb_kernel_driver_active(dev_handle, 0) == 1)
{
printf("Kernel Driver Active\n");
if(libusb_detach_kernel_driver(dev_handle, 0) == 0)
{
printf("Kernel Driver Detached!\n");
}
}
ret = libusb_claim_interface(dev_handle, 0);
if(ret < 0)
{
perror("Cannot Claim Interface\n");
return 1;
}
libusb_clear_halt(dev_handle, 129);
struct timeval lasttime,currtime,temp,cutime;//定义起始变量
float timeuse=0;
int exit_count;
int timeout,err_cnt = 0, count = 0;
char name[50] = "\0";
memset(name, 0, 50);
FILE *fp;
for(int k=0; k<200; k++)
{
gettimeofday(&temp,NULL);
while(1)
{
sscanf(argv[1], "%d", &timeout);
sscanf(argv[2], "%d", &exit_count);
//这就是传输函数,dev_handle不用管,前边代码设置的,第二个参数129就是十六进制的0x81,代表读,0x86我没有使用过。
//cmd_ir_start这个是接收数据的buff,IR_IMAGE_SIZE1是你想接收多少字节,transferred是实际取到的字节数,timeout超时时间
ret = libusb_bulk_transfer(dev_handle, 129, cmd_ir_start, IR_IMAGE_SIZE1, &transferred, timeout);
count++;
gettimeofday(&currtime,NULL);
printf("currtime.tv_sec = %ld currtime.tv_usec = %ld\n", currtime.tv_sec,currtime.tv_usec);
printf("count= %d\n", count);
//批量写入文件
sprintf(name, "./%d.raw", count);
FILE *fp = fopen(name, "w+");
fwrite(cmd_ir_start, IR_IMAGE_SIZE1, 1, fp);
//判断返回值
if(ret==0){
printf("read Successful!\n");
printf("ret = %d transferred==%d\n\n", ret ,transferred);
memset(cmd_ir_start, 0, IR_IMAGE_SIZE1+1);
}else{
printf("read error!\n");
printf("ret = %d transferred==%d\n\n", ret, transferred);
memset(cmd_ir_start, 0, IR_IMAGE_SIZE1+1);
err_cnt++;
}
//执行count次退出
if(count == exit_count){
printf("break,----USB_TIMEOUT=%d\n", timeout);
break;
}
//超过一秒打印count值
if(currtime.tv_sec - temp.tv_sec > 0){
temp.tv_sec = currtime.tv_sec;
temp.tv_usec = currtime.tv_usec;
printf("count------------------------------= %d\n", count);
count = 0;
}
}
break;
}
ret = libusb_release_interface(dev_handle, 0);
if(ret != 0)
{
printf("Cannot Released Interface!\n");
}
else
{
printf("Released Interface!\n");
}
libusb_close(dev_handle);
libusb_exit(ctx);
return 0;
}
代码编译:我这里没有设置环境变量,而是直接给出路径
x86平台下:libusb代码编译
gcc -I /usr/local/include/libusb-1.0/ usb_read.c -L /usr/local/lib/ -lusb-1.0
arm平台下:
arm-linux-gnueabihf-gcc -I /home/book/code/libusb/libusb_install/include/libusb-1.0 usb_read.c -L /home/book/code/libusb/libusb_install/lib -lusb-1.0
执行方式都一样
sudo ./a.out 0 20
--------------新增整理后的代码-----------------
#include <stdio.h>
#include <sys/types.h>
#include <libusb.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#define USB_VID 0x4206 //USB的产商VID
#define USB_PID 0x3702 //USB的产品PID
#define EP1ADDR 0x81 //Read 端口1地址,通道1
#define EP2ADDR 0x02 //Write端口2地址,通道2
#define USB_TIMEOUT 0 //阻塞时间
#define IR_IMAGE_SIZE1 (101440)
//一次性读取101440字节,前64字节为文件头,后续256*192*2字节为图像本身
static libusb_device_handle *dev_handle = NULL;
//打印usb设备的详细信息,可不使用
void printdev(libusb_device *dev){
struct libusb_device_descriptor desc;
int r = libusb_get_device_descriptor(dev, &desc);
if(r <0){
printf("failed to get device descriptor\n");
return;
}
printf("Number of possible configurations: %d\n,Device Class: %d\n",(int)desc.bNumConfigurations,(int)desc.bDeviceClass);
printf("VendorID: %d\n",desc.idVendor);
printf("ProductID: %d\n",desc.idProduct);
struct libusb_config_descriptor *config;
libusb_get_config_descriptor(dev, 0, &config);
printf("Interfaces: %d\n",(int)config->bNumInterfaces);
const struct libusb_interface *inter;
const struct libusb_interface_descriptor *interdesc;
const struct libusb_endpoint_descriptor *epdesc;
for(int i=0; i<(int)config->bNumInterfaces; i++){
inter =&config->interface[i];
printf("Number of alternate settings: %d\n",inter->num_altsetting);
for(int j=0; j<inter->num_altsetting; j++){
interdesc =&inter->altsetting[j];
printf("Interface Number: %d\n",(int)interdesc->bInterfaceNumber);
printf("Number of endpoints: %d\n",(int)interdesc->bNumEndpoints);
for(int k=0; k<(int)interdesc->bNumEndpoints; k++){
epdesc =&interdesc->endpoint[k];
printf("Descriptor Type: %d\n",(int)epdesc->bDescriptorType);
printf("EP Address: %d\n",(int)epdesc->bEndpointAddress);
}
}
}
libusb_free_config_descriptor(config);
}
int main()
{
int i = 0;
int ret = 1;
int transferred = 0;
ssize_t cnt;
unsigned char cmd_ir_start[IR_IMAGE_SIZE1+1] = "\0";
memset(cmd_ir_start, 0, IR_IMAGE_SIZE1+1);
libusb_device **devs;
libusb_context *ctx = NULL;
//初始化
ret = libusb_init(NULL);
if(ret < 0){
fprintf(stderr, "failed to initialise libusb\n");
return 1;
}
//获取设备上所有usb设备
cnt = libusb_get_device_list(ctx, &devs);
if(cnt < 0) {
perror("Get Device Error\n");
return 1;
}
//使用PID和VID打开usb设备
dev_handle = libusb_open_device_with_vid_pid(NULL, USB_VID, USB_PID);
if(dev_handle == NULL)
{
perror("Cannot open device\n");
}else{
printf("Device Opened\n");
}
printf("******************______************\n");
//打印每一个usb设备信息,可不使用
for(i =0; i < cnt; i++)
{
printdev(devs[i]);
printf("__________________******_____________\n");
}
//释放获取的设备
libusb_free_device_list(devs, 1);
/* 确定接口上的内核驱动程序是否处于活动状态。如果内核驱动程序处于活动状态,则不能声明该接口,
* libusb将无法执行I/O。
* 若内核驱动程序处于活动状态,使用libusb_detach_kernel_driver从接口分离内核驱动
*/
if(libusb_kernel_driver_active(dev_handle, 0) == 1) {
printf("Kernel Driver Active\n");
if(libusb_detach_kernel_driver(dev_handle, 0) == 0){
printf("Kernel Driver Detached!\n");
}
}
//接口声明
ret = libusb_claim_interface(dev_handle, 0);
if(ret < 0) {
perror("Cannot Claim Interface\n");
return 1;
}
//发送数据控制挡片
unsigned char data[1024] = {0x6e,0x00,0x00,0x04,0x00,0x00,0x0d,0x6b};//6e00000400000d6b
ret = libusb_bulk_transfer(dev_handle, EP2ADDR, data, 8, &transferred, 100);
if(ret < 0)
{
printf("transfer failed! ret = %d\n", ret);
}
//从usb设备读取数据
ret = libusb_bulk_transfer(dev_handle, EP1ADDR, cmd_ir_start, IR_IMAGE_SIZE1, &transferred, USB_TIMEOUT);
if(ret==0){
printf("read Successful!\n");
printf("ret = %d transferred==%d\n\n", ret ,transferred);
}else{
printf("read error!\n");
printf("ret = %d transferred==%d\n\n", ret, transferred);
}
//将数据保存为raw格式图片
FILE *fp = fopen("a.raw", "w+");
fwrite(cmd_ir_start, IR_IMAGE_SIZE1, 1, fp);
ret = libusb_release_interface(dev_handle, 0);
if(ret != 0){
printf("Cannot Released Interface!\n");
}else{
printf("Released Interface!\n");
}
libusb_close(dev_handle);
libusb_exit(ctx);
return 0;
}
这个镜头是256*192像素的,每个像素2字节,总大小256*192*2=98304
,当我定义宏#define IR_IMAGE_SIZE1 98304
时,我读到的数据保存为raw
的图片,然后用这个网站打开,如下图,这是一个原始数据,不用管图像是啥。发现没,图片左边像被切割了一样。为什么呢?
用十六进制打开看一下图片,蓝色部分怎么那么有规律,后来发现这是图片的“头”,多获取几张,前边的64
字节都是一样的东西,于是我把前64
字节删除了再保存,再查看。
对比一下发现,哎,好了,但是仔细看左侧图的右下角
右下角有一条黑线,跟上图右侧图中的右下角不一样,这又是为什么?
嘿嘿,因为删除了64字节的头,导致数据少了,补0就是黑色的啊
于是乎我把宏改成#define IR_IMAGE_SIZE1 (98304+64)
,再去删除头就没问题了,但是读数据函数会报错。
无奈改回了98304
,然后我连续读取98304
字节,发现一个规律,先读到98304
字节,然后读到3136
字节,这是为啥,有脏东西吗?
我把这3136
字节保存下来看了一下没看出什么名堂。
我又改了宏,#define IR_IMAGE_SIZE1 (98304+3136)
,也就是代码中的101440
,连续读取的话我发现,返回值都正常,且都能读取成功。
猜测这3136字节应该是图片的说明信息。当然还包含了图片的右下角的64字节数据。
到此读取工作结束了。
读取101440
字节以256*204
,单通道,16bits显示
读取101440
字节以256*192
,单通道,16bits显示
读取101440
字节以256*204
,单通道,16bits显示,其中删除了前64字节头
3、发送指令接收返回的数据
更新于2023年5月18日
这个镜头,叫tf25a
,给它发送指令可以接收到数据,例如获取软件版本号指令,发送后可以获取软件版本号,这是在叫Bus Hound
的软件中测试的
怎么得到的指令呢?
在上位机,点击“获取软件版本号”,在Bus Hound
中就能得到指令,然后用Bus Hound
的发送功能测试,跟上位机的效果是一致的
在代码中,libusb的发送和接收函数是一样的,使用不同的端口即可实现
使用libusb_bulk_transfer()先发送指令,再接收
根据返回结果可以看到,有效的的版本号是12个字符,在返回的22字节中,处于data[8]-data[19],取出来就好了
只是需要判断接收到的数据是镜头的图像还是返回指令数据,返回指令的数据是0x6e开头的
看到第三张图中返回的原始图片数据,65536,35904算是两个一组返回的
结果呢 65536 + 35904 = 101400
,这TM不就是读取摄像头数据的宏定义大小吗
#define IR_IMAGE_SIZE1 (98304+3136)
虽然但是,至今还是不知道这后边的数据是什么,不过挺有规律的,当成图片的结尾吧,冰毕竟图片开头都那么有规律
#define EP0ADDR 0x01 //Write端口0地址,通道0
#define EP1ADDR 0x81 //Read 端口1地址,通道1
#define EP2ADDR 0x02 //Write端口2地址,通道2
#define EP3ADDR 0x86 //Read 端口3地址,通道3
static void usb_transfer(libusb_device_handle *hdl, int ep, U8 *data, int len, int *transferred, int timeout)
注意事项:使用libusb_bulk_transfer()发送数据或指令都可以,该函数适用于大量数据的
传输,但是接收返回的指令时,使用的"ep"参数不能是"EP2ADDR",用这个接收不到返回的指
令,可以尝试其他端点,图片数据就可以