工作中libusb的使用


用libusb在虚拟机上采集相机的数据
连接方式如下图
在这里插入图片描述

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",用这个接收不到返回的指
令,可以尝试其他端点,图片数据就可以
  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值