虚拟机Linux访问usb设备,通过Libusb库方法实现读写usb设备所获取的实时图像数据

一、前言

基于Linux 平台USB驱动开发,主要有内核驱动的开发和libusb的无驱设计;首先为什么要选第三方库Libusb,可能是基于Libusb的程序只涉及到应用层,使用起来更加方便;如果是在内核驱动,就要考虑到内核大小、内核版本的兼容,如果客户需要把你的USB模块加入他们的平台,那岂不是还要重新根据客户要求裁剪、编写内核?又假如有许多客气,而且每个客气的平台不一样,那是不是需要为每个客户定制一个内核呀?所以……

记得前两天一直在搭建基于S5P4418平台Embedded arm_5.8QT和Embedded host_ 5.8QT,感触最多就是装的Linux环境和编译器版的不同,结局就可能会不同,不清楚开发环境对本文的例子的例子有多大的影响,我还是负责任地贴出我的环境,还介绍了UBS设备的连接以及库文件的安装。

环境: windows7_32bit+VMware_10.0.1_32bit+Ubuntu_12.04.5 LTS_32bit
编译器: gcc与g++ version 4.9.4

二、USB设备与虚拟机Linux的连接

USB工作模式:①存储模式,②传输模式。
要读取USB所传的实时数据,把UBS的模式设置为传输模式;
点击虚拟机的设置
至少选中上面两项_只能在ubuntu系统关机状态下更改
点击连接设备
这里写图片描述
这里写图片描述
在Linux上所读到USB的Vid与Pid值,是用来寻找相匹配的USB设备,endpoint是在传输数据时需要用到的端口。

三、第三方Libusb-1.0.9库的安装

下载源码包libusb-1.0.9.tar.bz2,用tar -xvf命令解压任意文件夹,进入解压的文件夹运行①./configure②make③make install;安装的头文件与库文件一般在/usr目录下,④cd /usr⑤find -name “libusb.h“可以找到安装库的头文件,动态库.so文件一般头文件上层目录的lib目录下,为什么要这样?等下用到的时候再作解释;
这里写图片描述
在测试libusb库的时候,直接用gcc编译出现fatal error: libusb.h: No such file or directory,上面所提到的头文件与动态库文件路径就有用了,编译的时候添加 -I/头文件路径与-L/动态库文件路径,编译OK。当然,理论上应该可以把它们的路径加入环境变量,由于时间有限……
这里写图片描述
这里写图片描述
可以看到example中的demo能正常运行了~
Libusb库example测试源码

#include <stdio.h>
#include <sys/types.h>

#include <libusb.h>

static void print_devs(libusb_device **devs)
{
        libusb_device *dev;
        int i = 0;

        while ((dev = 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;
                }

                printf("%04x:%04x (bus %d, device %d)\n",
                        desc.idVendor, desc.idProduct,
                        libusb_get_bus_number(dev), libusb_get_device_address(dev));
        }
}

int main(void)
{
        libusb_device **devs;
        int r;
        ssize_t cnt;r = libusb_init(NULL);
        if (r < 0)
                return r;

        cnt = libusb_get_device_list(NULL, &devs);
        if (cnt < 0)
                return (int) cnt;

        print_devs(devs);
        libusb_free_device_list(devs, 1);

        libusb_exit(NULL);
        return 0;
}

三、Libusb-1.0.9库的来实现USB的数据读取

#include <stdio.h>  
#include <sys/types.h>  
#include <libusb.h> 

#define USB_VID         0x0547          //USB的产商ID
#define USB_PID         0x0503          //USB的产品ID

#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     10000           //传输数据的时间延迟

#define COL 1024                        //图像每一行均为1024个点

/********** IMage ************/
#define IR_ROW         485 //一帧图像总行数
#define IR_IMAGE_SIZE       IR_ROW*COL*2        //一帧图像的大小,每个点2个字节


static libusb_device_handle *dev_handle = NULL; 

void printdev(libusb_device *dev){
    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);

    libusb_config_descriptor *config;
    libusb_get_config_descriptor(dev, 0, &config);
    printf("Interfaces: %d\n",(int)config->bNumInterfaces);
    const libusb_interface *inter;
    const libusb_interface_descriptor *interdesc;
    const 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[50] = {0x55, 0xaa, 0x00, 0x00,0x05};    //读图像指令
        unsigned char cmd_stop[50] = {0x5e, 0xaa};                         //结束指令
        char cmd_state[64];

        unsigned short data_ir[IR_ROW][COL];
    libusb_device_descriptor desc;  
    libusb_device **devs; 

        libusb_context *ctx = NULL;   

    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"); 
    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;
        }

    // ret = usb_bulk_write(dev_handle, EP0ADDR, cmd_ir_start, sizeof(cmd_ir_start), USB_TIMEOUT);
    //ret = libusb_control_transfer(dev_handle, 0x21, 0x09, 0x0300, 0x00, dataOut+1, 0x20, USB_TIMEOUT);

    /* 1. 发送读Image数据指令,使用0号通道??????????????????*/   
        ret = libusb_bulk_transfer(dev_handle, EP0ADDR, cmd_ir_start, 5, &transferred, USB_TIMEOUT);
        if(ret==0 && transferred==5){
        printf("write Successful!\n");
    }else{
        printf("write error!\n");
    }

    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;
}

这里写图片描述

最关键是在读写数据部分,我度娘到的大部分都是用usb开头,以usb开始的函数拥有write与read函数,而libusb开头的函数接口读写没分开(它们的版本不同,函数接口不同),到/usr/local/lib/libusb-1.0/libusb.h查看源码,能用到的同步传输接口如图:
这里写图片描述
但不知道怎么用函数接口,度娘介绍的少之又少,最后在自己解压的第三方库源码的example中找到了文件dpfp_threaded.c例子有介绍,部分源码如下

static int print_f0_data(void)
{
    unsigned char data[0x10];
    int r;
    unsigned int i;

    r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0xf0, 0, data,
        sizeof(data), 0);
    if (r < 0) {
        fprintf(stderr, "F0 error %d\n", r);
        return r;
    }
    if ((unsigned int) r < sizeof(data)) {
        fprintf(stderr, "short read (%d)\n", r);
        return -1;
    }

    printf("F0 data:");
    for (i = 0; i < sizeof(data); i++)
        printf("%02x ", data[i]);
    printf("\n");
    return 0;
}

static int get_hwstat(unsigned char *status)
{
    int r;

    r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0x07, 0, status, 1, 0);
    if (r < 0) {
        fprintf(stderr, "read hwstat error %d\n", r);
        return r;
    }
    if ((unsigned int) r < 1) {
        fprintf(stderr, "short read (%d)\n", r);
        return -1;
    }

    printf("hwstat reads %02x\n", *status);
    return 0;
}

static int set_hwstat(unsigned char data)
{
    int r;

    printf("set hwstat to %02x\n", data);
    r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x07, 0, &data, 1, 0);
    if (r < 0) {
        fprintf(stderr, "set hwstat error %d\n", r);
        return r;
    }
    if ((unsigned int) r < 1) {
        fprintf(stderr, "short write (%d)", r);
        return -1;
    }

    return 0;
}

static int set_mode(unsigned char data)
{
    int r;
    printf("set mode %02x\n", data);

    r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x4e, 0, &data, 1, 0);
    if (r < 0) {
        fprintf(stderr, "set mode error %d\n", r);
        return r;
    }
    if ((unsigned int) r < 1) {
        fprintf(stderr, "short write (%d)", r);
        return -1;
    }

    return 0;
}

关键是CTRL_IN与CTRL_OUT来控制数据的读写,我因为赶项目,换了老的第三方库,等有时间再学习下新库怎么用的,在这里只是提醒大家正确地选用库的版本,没有完全对的库,只有最适合自己的,;

四、用Libusb-0.1.12库来实现USB的数据读取

  1. 库文件的安装方法同Libusb-1.0.9类似,可能不同的就是头文件与动态库.so文件的路径不同,利用find -name命令查找即可,我的存放的路径在/usr/local/include与/usr/ local/lib下面,头文件usb.h与Libusb-1.0. 9的libusb.h不同,当然库函数也不会相同;因为要做一个封装的库文件供第三方使用,只需把重要的函数API与重要信息提供出来即可。
    主程序main()函数:
#include <stdio.h>
#include <sys/types.h>

#include "libusbir.h"//封装的库头文件

#define USB_VID         0x0547          //USB设备的产商ID
#define USB_PID         0x0503          //USB设备的产品ID

int main(int argc, char *argv[]){

        int ret = 0, i = 0;
        char data_ir[IR_IMAGE_SIZE] = {0};

        struct usb_device* device = NULL;
        usb_dev_handle* device_handle = NULL;;
        //1. 通过USB的VID与PID找到设备
        if((device = find_device(USB_VID, USB_PID)) == NULL) {
                printf("USB_DEVICE not found!\n");
                exit(0);
        }
        //2. 打开找到的设备返回传输数据所要操作的句柄Handle
        if((device_handle=open_device(device)) == NULL) {
                printf("Open USB_DEVICE failed!\n");
                exit(0);
        }

        while(1){
        //3. 数据是实时的,采用while(1)读取数据
        ret = bulk_read_data(device_handle, data_ir);
            if(ret < 0){
                    printf("Read Data fail!\n");
            exit(0);
             }
        //打印数据的测试函数
        print_data(data_ir);
    }
        //4. 关闭USB设备
        close_usb_handle(device_handle);
        return 0;
}

封装好的库头文件源码:

#ifndef _LIBUSBIR_H_
#define _LIBUSBIR_H_
#endif
#include <usb.h>//libusb-0.1.12库的头文件

#define EP0ADDR         0x01            //端口0地址,通道0
#define EP1ADDR         0x81            //端口1地址,通道1
#define EP2ADDR         0x02            //端口2地址,通道2
#define EP3ADDR         0x86            //端口3地址,通道3
#define USB_TIMEOUT     10000           //传输数据的时间延迟

/********** IMAGE ************/
#define IR_ROW 288     //IMAGE Line
#define COL 512*2      //IMAGE Column
#define IR_IMAGE_SIZE       IR_ROW*COL*2        //IMAGE一帧图像的大小      

static struct usb_bus *bus = NULL;
static struct usb_device* dev = NULL;
static usb_dev_handle *device_handle = NULL;

struct usb_device* find_device(int usb_vid, int usb_pid)
{
        /* 1. 初始化相关数据并寻找相关设备,一开始就要调用 */
        usb_init();

        /* 2. 寻找系统上的usb总线,任何usb设备都通过usb总线与主机通讯,返回总线数 */
        usb_find_busses();

        /* 3. 寻找usb总线上的设备*/
        usb_find_devices();

        /* 4. 获得系统总线链表的句柄,modified by Su */
        for (bus = usb_busses; bus; bus = bus->next)
        {
                /* 遍历总线上的设备 */
                for (dev = bus->devices; dev; dev = dev->next)
                {
                        if(dev->descriptor.idVendor == usb_vid && dev->descriptor.idProduct == usb_pid) {
                                printf("Found USB_DEVICE!!!\n");
                                return dev;
                        }
                }
        }

        return NULL;
}

usb_dev_handle* open_device(struct usb_device* udev)
{
        if(udev != NULL) {
                device_handle = usb_open(udev);

                /* 进行设备的初始化 
                 1.设置当前的设备使用的configuration,参数2是要使用配置描述符中的bConfigurationValue */
                usb_set_configuration(device_handle, 1);

                /* 2.注册与操作系统通讯的接口,必须被调用,只有注册接口才能做相应的操作,参数2指bInterfaceNumber */
                usb_claim_interface(device_handle, 0);

                /* 3.设置当前的设备使用的interface descriptor,参数2是指向接口描述符中的bAlternateSetting */
                usb_set_altinterface(device_handle, 0);

        }
        return device_handle;
}

int bulk_read_data(usb_dev_handle* device_handle, char* data_ir){

        int ret = -1;
        char cmd_ir_start[50] = {0x55, 0xaa, 0x00, 0x00,0x05};    //读Image指令
        char cmd_stop[50] = {0x5e, 0xaa};                         //结束指令
        char cmd_state[64];

        /* 1. 发送读Image数据指令,使用0号通道 */
        ret = usb_bulk_write(device_handle, EP0ADDR, cmd_ir_start, 5, USB_TIMEOUT);
        if(ret < 0){
                printf("Failed to write the start transfer camand!\n");

        /* 2. 读Image数据, 使用通道3,最小2KB,最大3MB */
        ret = usb_bulk_read(device_handle, EP3ADDR, data_ir, IR_IMAGE_SIZE, USB_TIMEOUT);
        //printf("Have read data length is: %d\n", ret);
        if(ret != IR_IMAGE_SIZE){
                printf("Failed to read data!\n");
        }

        /* 3. 发送结束指令(以0x5e,0xaa开头,使用通道0, 最大64字节)*/
        ret = usb_bulk_write(device_handle, EP0ADDR, cmd_stop, 2, USB_TIMEOUT);
        if(ret < 0){
                printf("Failed to write the stop transfer camand!\n");
        }

        /* 4. 读状态指令(以0x5e,0xaa开头,使用通道1, 最大64字节)*/
        ret = usb_bulk_read(device_handle, EP1ADDR, cmd_state, sizeof(cmd_state), USB_TIMEOUT);
        if(ret < 0){
                printf("Failed to read data!\n");
        }
        return ret; 
}

int close_usb_handle(usb_dev_handle* device_handle){

        usb_release_interface(device_handle, 0);
        usb_close(device_handle);
        return 0;   
}

int print_data(char* data_ir){

         int i = 0;

         printf("*** Start To Reading Data After Read operation***\n");
         for( i= 1024 * 240;i <1024 * 240 + 100 ;i++){
                printf("0x%x\t  ",data_ir[i]);
                if((i+1) %10 == 0){
                    printf("\n");
                }
         }
        printf("***Reading Data Done, ***\n");

        return 0;
}

暴露dev与device_handle 是为了方便第三方人员对该UBS的其他操作以及其他功能的扩展;
2.程序测试的结果:
图像可变的数据
图像冻结的数据
编码过程中只注重实现USB读取数据的功能,而且也是个半成品,程序风格还需优化,所得到的数据为图像的AD值(原始值),还需要转换成RGB格式,才能看到真正的成像,由于成果属公司所有,比如后来的图像处理(比如图像降噪、增强、非均匀矫正等)完整的部分就不介绍了。写完工作笔记之后,发现右眼皮这两天一直跳,用眼过度呀!这又是一个悲伤的故事~~~

  • 13
    点赞
  • 73
    收藏
    觉得还不错? 一键收藏
  • 14
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值