基于Linux 平台USB驱动开发,主要有内核驱动的开发和libusb的无驱设计;首先为什么要选第三方库Libusb,可能是基于Libusb的程序只涉及到应用层,使用起来更加方便;如果是在内核驱动,就要考虑到内核大小、内核版本的兼容,如果客户需要把你的USB模块加入他们的平台,那岂不是还要重新根据客户要求裁剪、编写内核?又假如有许多客气,而且每个客气的平台不一样,那是不是需要为每个客户定制一个内核呀?所以……
记得前两天一直在搭建基于S5P4418平台Embedded arm_5.8QT和Embedded host_ 5.8QT,感触最多就是装的Linux环境和编译器版的不同,结局就可能会不同,不清楚开发环境对本文的例子的例子有多大的影响,我还是负责任地贴出我的环境,还介绍了UBS设备的连接以及库文件的安装。
USB工作模式:①存储模式,②传输模式。
要读取USB所传的实时数据,把UBS的模式设置为传输模式;
在Linux上所读到USB的Vid与Pid值,是用来寻找相匹配的USB设备,endpoint是在传输数据时需要用到的端口。
下载源码包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
#include
#include
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;
}
#include
#include
#include
#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; jnum_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来控制数据的读写,我因为赶项目,换了老的第三方库,等有时间再学习下新库怎么用的,在这里只是提醒大家正确地选用库的版本,没有完全对的库,只有最适合自己的,;
#include
#include
#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 //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格式,才能看到真正的成像,由于成果属公司所有,比如后来的图像处理(比如图像降噪、增强、非均匀矫正等)完整的部分就不介绍了。写完工作笔记之后,发现右眼皮这两天一直跳,用眼过度呀!这又是一个悲伤的故事~~~