libusb开发

来源:互联网 发布:禁止启动程序软件 编辑:程序博客网 时间:2024/05/17 07:02
前言
USB的用途就不多说了,下面的内容主要就是讲解如何利用ST提供的USB驱动库和libusb上位机驱动库实现一个USB数据传输功能,为了降低开发难度,我们仅仅讲解Bulk传输模式,当然这也是用得比较多的传输模式。

开发流程
1,完成STM32单片机端的USB程序;
2,利用linusb自带的inf-wizard工具生成USB驱动;
3,基于libusb编写USB通信程序;
4,测试PC和单片机的数据通信;

STM32程序编写
1,完成描述符的修改,修改后的描述符如下(在usb_desc.c文件中)
设备描述符:
[C] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
constuint8_t CustomHID_DeviceDescriptor[CUSTOMHID_SIZ_DEVICE_DESC] =
{
    0x12,                      /*bLength */
    USB_DEVICE_DESCRIPTOR_TYPE,/*bDescriptorType*/
    0x00,                      /*bcdUSB */
    0x02,
    0x00,                      /*bDeviceClass*/
    0x00,                      /*bDeviceSubClass*/
    0x00,                      /*bDeviceProtocol*/
    0x40,                      /*bMaxPacketSize40*/
    LOBYTE(USBD_VID),          /*idVendor*/
    HIBYTE(USBD_VID),          /*idVendor*/
    LOBYTE(USBD_PID),          /*idVendor*/
    HIBYTE(USBD_PID),          /*idVendor*/
    0x00,                      /*bcdDevice rel. 2.00*/
    0x02,
    1,                         /*Index of string descriptor describing manufacturer */
    2,                         /*Index of string descriptor describing product*/
    3,                         /*Index of string descriptor describing the device serial number */
    0x01                       /*bNumConfigurations*/
};/* CustomHID_DeviceDescriptor */


配置描述符:
[C] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
constuint8_t CustomHID_ConfigDescriptor[CUSTOMHID_SIZ_CONFIG_DESC] =
{
    0x09,/* bLength: Configuation Descriptor size */
    USB_CONFIGURATION_DESCRIPTOR_TYPE,/* bDescriptorType: Configuration */
    CUSTOMHID_SIZ_CONFIG_DESC,
    /* wTotalLength: Bytes returned */
    0x00,
    0x01,        /* bNumInterfaces: 1 interface */
    0x01,        /* bConfigurationValue: Configuration value */
    0x00,        /* iConfiguration: Index of string descriptor describing
                                 the configuration*/
    0xE0,        /* bmAttributes: Bus powered */
                  /*Bus powered: 7th bit, Self Powered: 6th bit, Remote wakeup: 5th bit, reserved: 4..0 bits */
    0xFA,        /* MaxPower 500 mA: this current is used for detecting Vbus */
    /************** Descriptor of Custom HID interface ****************/
    /* 09 */
    0x09,        /* bLength: Interface Descriptor size */
    USB_INTERFACE_DESCRIPTOR_TYPE,/* bDescriptorType: Interface descriptor type */
    0x00,        /* bInterfaceNumber: Number of Interface */
    0x00,        /* bAlternateSetting: Alternate setting */
    0x04,        /* bNumEndpoints */
    0xDC,        /* bInterfaceClass: Class code = 0DCH */
    0xA0,        /* bInterfaceSubClass : Subclass code = 0A0H */
    0xB0,        /* nInterfaceProtocol : Protocol code = 0B0H */
    0,           /* iInterface: Index of string descriptor */
    /******************** endpoint descriptor ********************/
    /* 18 */
    0x07,        /* endpoint descriptor length = 07H */
    USB_ENDPOINT_DESCRIPTOR_TYPE,/* endpoint descriptor type = 05H */
    0x81,        /* endpoint 1 IN */
    0x02,                                       /* bulk transfer = 02H */
    0x40,0x00,   /* endpoint max packet size = 0040H */
    0x00,        /* the value is invalid when bulk transfer */
 
    0x07,        /* endpoint descriptor length = 07H */
    USB_ENDPOINT_DESCRIPTOR_TYPE,/* endpoint descriptor type = 05H */
    0x01,        /* endpoint 1 OUT */
    0x02,                                       /* bulk transfer = 02H */
    0x40,0x00,   /* endpoint max packet size = 0040H */
    0x00,        /* the value is invalid when bulk transfer */
                 
    0x07,        /* endpoint descriptor length = 07H */
    USB_ENDPOINT_DESCRIPTOR_TYPE,/* endpoint descriptor type = 05H */
    0x82,        /* endpoint 2 IN */
    0x02,                                       /* bulk transfer = 02H */
    0x40,0x00,   /* endpoint max packet size = 0040H */
    0x00,        /* the value is invalid when bulk transfer */
                 
    0x07,        /* endpoint descriptor length = 07H */
    USB_ENDPOINT_DESCRIPTOR_TYPE,/* endpoint descriptor type = 05H */
    0x02,        /* endpoint 2 OUT */
    0x02,                                       /* bulk transfer = 02H */
    0x40,0x00,   /* endpoint max packet size = 0040H */
    0x00,        /* the value is invalid when bulk transfer */
};/* CustomHID_ConfigDescriptor */

配置描述符就包含了端点描述符,我们用了4个端点,两个BULK-OUT端点,两个BULK-IN端点。

其他的描述符:
[C] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/* USB String Descriptors (optional) */
constuint8_t CustomHID_StringLangID[CUSTOMHID_SIZ_STRING_LANGID] =
{
    CUSTOMHID_SIZ_STRING_LANGID,
    USB_STRING_DESCRIPTOR_TYPE,
    0x09,
    0x04
};/* LangID = 0x0409: U.S. English */
 
constuint8_t CustomHID_StringVendor[CUSTOMHID_SIZ_STRING_VENDOR] =
{
    CUSTOMHID_SIZ_STRING_VENDOR,/* Size of Vendor string */
    USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType*/
    // Manufacturer: "STMicroelectronics"
    'M', 0, 'y', 0, 'U', 0,'S', 0,'B', 0, '_', 0, 'H', 0,'I',0,'D',0
};
 
constuint8_t CustomHID_StringProduct[CUSTOMHID_SIZ_STRING_PRODUCT] =
{
    CUSTOMHID_SIZ_STRING_PRODUCT,         /* bLength */
    USB_STRING_DESCRIPTOR_TYPE,       /* bDescriptorType */
    'B', 0, 'y', 0, ' ', 0, 'e', 0, 'm', 0, 'b', 0,'e',0,'d',0,'-',0,'n',0,'e',0,'t',0
};
uint8_t CustomHID_StringSerial[CUSTOMHID_SIZ_STRING_SERIAL] =
{
    CUSTOMHID_SIZ_STRING_SERIAL,          /* bLength */
    USB_STRING_DESCRIPTOR_TYPE,       /* bDescriptorType */
    'x', 0, 'x', 0, 'x', 0,'x', 0,'x', 0, 'x', 0, 'x', 0
};


2,根据端点缓冲区大小配置端点缓冲区地址,配置信息如下(在usb_conf.h文件中):
[C] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
/* buffer table base address */
#define BTABLE_ADDRESS      (0x00)
 
/* EP0  */
/* rx/tx buffer base address */
#define ENDP0_RXADDR        (0x18)
#define ENDP0_TXADDR        (0x58)
 
/* EP1  */
/* tx buffer base address */
//地址为32位对其,位4的倍数,不能超过 bMaxPacketSize
//EP1
#define ENDP1_RXADDR        (0x98)
#define ENDP1_TXADDR        (0x98+64)
////EP2
#define ENDP2_RXADDR        (0xA0+64+64)
#define ENDP2_TXADDR        (0xA0+64+64+64)


3,初始化每个端点(在usb_prop.c文件中的CustomHID_Reset函数中)
[C] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* Initialize Endpoint 0 */
SetEPType(ENDP0, EP_CONTROL);
SetEPTxStatus(ENDP0, EP_TX_STALL);
SetEPRxAddr(ENDP0, ENDP0_RXADDR);
SetEPTxAddr(ENDP0, ENDP0_TXADDR);
Clear_Status_Out(ENDP0);
SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);
SetEPRxValid(ENDP0);
 
/* Initialize Endpoint 1 */
       SetEPType(ENDP1, EP_BULK);
       SetEPRxAddr(ENDP1, ENDP1_RXADDR);
       SetEPTxAddr(ENDP1, ENDP1_TXADDR);
       SetEPRxCount(ENDP1, EP_SIZE);
       SetEPRxStatus(ENDP1, EP_RX_VALID);
 SetEPTxStatus(ENDP1, EP_TX_NAK);
 
/* Initialize Endpoint 2 */
       SetEPType(ENDP2, EP_BULK);
       SetEPRxAddr(ENDP2, ENDP2_RXADDR);
       SetEPTxAddr(ENDP2, ENDP2_TXADDR);
       SetEPRxCount(ENDP2, EP_SIZE);
       SetEPRxStatus(ENDP2, EP_RX_VALID);
       SetEPTxStatus(ENDP2, EP_TX_NAK);


4,实现端点的回调函数(需要在usb_conf.h中注释掉对应的回调函数宏定义)
[C] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*******************************************************************************
* Function Name  : EP1_OUT_Callback.
* Description    : EP1 OUT Callback Routine.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
voidEP1_OUT_Callback(void)
{
        EP1_ReceivedCount = GetEPRxCount(ENDP1);
        PMAToUserBufferCopy(USB_Receive_Buffer, ENDP1_RXADDR, EP1_ReceivedCount);
        SetEPRxStatus(ENDP1, EP_RX_VALID);
}
/*******************************************************************************
* Function Name  : EP2_OUT_Callback.
* Description    : EP2 OUT Callback Routine.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
voidEP2_OUT_Callback(void)
{
        EP2_ReceivedCount = GetEPRxCount(ENDP2);
        PMAToUserBufferCopy(USB_Receive_Buffer, ENDP2_RXADDR, EP2_ReceivedCount);
        SetEPRxStatus(ENDP2, EP_RX_VALID);
}


5,完成主函数的测试程序
[C] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
intmain(void)
{
        uint8_t data[256];
        uint32_t i=0;
        Set_System();//系统时钟初始化
        USART_Configuration();//串口1初始化
        printf("\x0c\0");printf("\x0c\0");//超级终端清屏
        printf("\033[1;40;32m");//设置超级终端背景为黑色,字符为绿色
        printf("\r\n*******************************************************************************");
        printf("\r\n************************ Copyright 2009-2012, EmbedNet ************************");
        printf("\r\n*************************** [url=http://www.embed-net.com]http://www.embed-net.com[/url] **************************");
        printf("\r\n***************************** All Rights Reserved *****************************");
        printf("\r\n*******************************************************************************");
        printf("\r\n");
 
        USB_Interrupts_Config();
        Set_USBClock();
        USB_Init();
 
        while(1)
        {
                if(EP1_ReceivedCount > 0){
                        USB_GetData(ENDP1,data,EP1_ReceivedCount);
                        USB_SendData(ENDP1,data,EP1_ReceivedCount);
                        printf("usb EP1 get data %d byte data\n\r",EP1_ReceivedCount);
                        for(i=0;i<EP1_ReceivedCount;i++){
                                printf("0x%02X ",data[i]);
                        }
                        printf("\n\r");
                        EP1_ReceivedCount=0;
                }
                if(EP2_ReceivedCount > 0){
                        USB_GetData(ENDP2,data,EP2_ReceivedCount);
                        USB_SendData(ENDP2,data,EP2_ReceivedCount);
                        printf("usb EP2 get data %d byte data\n\r",EP2_ReceivedCount);
                        for(i=0;i<EP2_ReceivedCount;i++){
                                printf("0x%02X ",data[i]);
                        }
                        printf("\n\r");
                        EP2_ReceivedCount=0;       
                }
        }
}


到此,STM32的程序基本上编写完成,然后编译下载程序,如果一切顺利,系统会检测到一个新的设备并试图加载对应的驱动,由于我们还没做驱动程序,所以肯定会加载驱动失败,如下图所示:
 

驱动程序生成
下面我们就利用libusb自带的inf-wizard工具生成USB驱动程序,该工具可以到本文章的附件下载,其具体过程如下:
 

运行该程序,出现下图对话框,点击“Next”;
 

出现下图对话框后选择我们需要生成驱动程序的设备;
 

这里可以写该Device Name,我们保持默认值,其他的都不需要修改;
 

点击Next后出现下图对话框,我们选择一个目录保存这个inf文件;
 

保存后的文件
 

若要立即安装驱动,可以点击下面对话框的红色框按钮;
 

Win7下可能会出现如下对话框,点击始终安装;
 

到此,USB驱动程序自动生成完毕,若安装了驱动,则在设备管理器里面会看到如下信息
 

基于libusb的上位机驱动程序编写
首先建立一个驱动程序工程,然后将libusb的库(附件有下载)添加到工程里面,编写以下几个函数
设备扫描函数,该函数用来找到插入电脑上的USB设备
[C] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
/**
  * @brief  扫描设备连接数
  * @param  NeedInit 是否需要初始化,第一次调用该函数需要初始化
  * @retval 识别到的指定设备个数
  */
int__stdcall USBScanDev(intNeedInit)
{
        if(NeedInit){
                usb_init();/* initialize the library */
                usb_find_busses();/* find all busses */
                usb_find_devices();/* find all connected devices */
        }
        returnscan_dev(pBoard);
}


打开设备
[C] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
/**
  * @brief  打开指定的USB设备
  * @param  devNum        需要打开的设备号
  * @retval 打开状态
  */
int__stdcall USBOpenDev(intDevIndex)
{
        pBoardHandle[DevIndex] = open_dev(DevIndex,pBoard);
        if(pBoardHandle[DevIndex]==NULL){
                returnSEVERITY_ERROR;
        }else{
                returnSEVERITY_SUCCESS;
        }
}


关闭设备
[C] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
/**
  * @brief  关闭指定的USB设备
  * @param  devNum        需要关闭的设备号
  * @retval 打开状态
  */
int__stdcall USBCloseDev(intDevIndex)
{
        returnclose_dev(DevIndex,pBoardHandle);
}


BULK端点写数据
[C] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
  * @brief  USB Bulk端点写数据
  * @param  nBoardID 设备号
  * @param  pipenum 端点号
  * @param  sendbuffer 发送数据缓冲区
  * @param  len 发送数据字节数
  * @param  waittime 超时时间
  * @retval 成功发送的数据字节数
  */
 
int__stdcall USBBulkWriteData(unsigned intnBoardID,intpipenum,char*sendbuffer,intlen,intwaittime)
{
        intret=0;
        if(pBoardHandle[nBoardID] == NULL){
                returnSEVERITY_ERROR;
        }
#ifdef TEST_SET_CONFIGURATION
    if(usb_set_configuration(pBoardHandle[nBoardID], MY_CONFIG) < 0)
    {
        usb_close(pBoardHandle[nBoardID]);
        returnSEVERITY_ERROR;
    }
#endif
 
#ifdef TEST_CLAIM_INTERFACE
    if(usb_claim_interface(pBoardHandle[nBoardID], 0) < 0)
    {
        usb_close(pBoardHandle[nBoardID]);
        returnSEVERITY_ERROR;
    }
#endif
 
#if TEST_ASYNC
    // Running an async write test
    ret = transfer_bulk_async(dev, pipenum, sendbuffer, len, waittime);
#else
        ret = usb_bulk_write(pBoardHandle[nBoardID], pipenum, sendbuffer, len, waittime);
        /*if((len%64) == 0){
                usb_bulk_write(pBoardHandle[nBoardID], pipenum, sendbuffer, 0, waittime);
        }*/
#endif
#ifdef TEST_CLAIM_INTERFACE
    usb_release_interface(pBoardHandle[nBoardID], 0);
#endif
    returnret;
}


BULK端点读数据
[C] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
  * @brief  USB Bulk读数据
  * @param  nBoardID 设备号
  * @param  pipenum 端点号
  * @param  readbuffer 读取数据缓冲区
  * @param  len 读取数据字节数
  * @param  waittime 超时时间
  * @retval 读到的数据字节数
  */
int__stdcall USBBulkReadData(unsigned intnBoardID,intpipenum,char*readbuffer,intlen,intwaittime)
{
        intret=0;
        if(pBoardHandle[nBoardID] == NULL){
                returnSEVERITY_ERROR;
        }
#ifdef TEST_SET_CONFIGURATION
    if(usb_set_configuration(pBoardHandle[nBoardID], MY_CONFIG) < 0)
    {
        usb_close(pBoardHandle[nBoardID]);
        returnSEVERITY_ERROR;
    }
#endif
 
#ifdef TEST_CLAIM_INTERFACE
    if(usb_claim_interface(pBoardHandle[nBoardID], 0) < 0)
    {
        usb_close(pBoardHandle[nBoardID]);
        returnSEVERITY_ERROR;
    }
#endif
 
#if TEST_ASYNC
    // Running an async read test
    ret = transfer_bulk_async(pGinkgoBoardHandle[nBoardID], pipenum, sendbuffer, len, waittime);
#else
        ret = usb_bulk_read(pBoardHandle[nBoardID], pipenum, readbuffer, len, waittime);
#endif
#ifdef TEST_CLAIM_INTERFACE
    usb_release_interface(pBoardHandle[nBoardID], 0);
#endif
    returnret;
}


到此,PC端的驱动程序编写基本完成,下面就是驱动程序的测试,我们可以把之前这个程序生成为一个dll文件,然后单独建立一个测试工程来测试这个dll文件中的函数,测试程序如下:
[C] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// USB_DriverTest.cpp : 定义控制台应用程序的入口点。
//
 
#include "stdafx.h"
 
#define        EP1_OUT_SIZE        64
#define        EP1_IN_SIZE        64
 
int_tmain(intargc, _TCHAR* argv[])
{
        intDevNum;
        intret;
        charWriteTestData[256]={1,2,3,4,5,6,7,8,9};
        charReadTestData[256]={0};
        for(inti=0;i<256;i++){
                WriteTestData[i] = i;
        }
        //扫描设备连接数,需要初始化
        DevNum = USBScanDev(1);
        printf("设备连接数为:%d\n",DevNum);
        //打开设备0
        ret = USBOpenDev(0);
        if(ret == SEVERITY_ERROR){
                printf("打开设备失败!\n");
                returnSEVERITY_ERROR;
        }else{
                printf("打开设备成功!\n");
        }
 
        //端点1写数据
        ret = USBBulkWriteData(0,EP1_OUT,WriteTestData,EP1_OUT_SIZE,500);
        if(ret != EP1_OUT_SIZE){
                printf("端点1写数据失败!%d\n",ret);
                returnSEVERITY_ERROR;
        }else{
                printf("端点1写数据成功!\n");
        }
        //端点1读数据
        ret = USBBulkReadData(0,EP1_IN,ReadTestData,EP1_IN_SIZE,500);
        if(ret != EP1_IN_SIZE){
                printf("端点1读数据失败!%d\n",ret);
                returnSEVERITY_ERROR;
        }else{
                printf("端点1读数据成功!\n");
                for(inti=0;i<EP1_IN_SIZE;i++){
                        printf("%02X ",ReadTestData[i]);
                        if(((i+1)%16)==0){
                                printf("\n");
                        }
                }
                printf("\n");
        }
        Sleep(100);
        //端点2写数据
        ret = USBBulkWriteData(0,EP2_OUT,WriteTestData+64,64,500);
        if(ret != 64){
                printf("端点2写数据失败!%d\n",ret);
                returnSEVERITY_ERROR;
        }else{
                printf("端点2写数据成功!\n");
        }
        //端点2读数据
        ret = USBBulkReadData(0,EP2_IN,ReadTestData,64,500);
        if(ret != 64){
                printf("端点2读数据失败!%d\n",ret);
                returnSEVERITY_ERROR;
        }else{
                printf("端点2读数据成功!\n");
                for(inti=0;i<64;i++){
                        printf("%02X ",ReadTestData[i]);
                        if(((i+1)%16)==0){
                                printf("\n");
                        }
                }
                printf("\n");
        }
        getchar();
        return0;
}


到此,整个开发流程基本完成,下面是本套程序的测试图片

串口打印输出
 

PC端测试程序输出
 

Bus Hound抓取到的USB数据
 

程序源码下载
libusb驱动生成工具下载:  inf_tool.rar (778.26 KB, 下载次数: 776) 
STM32程序源码下载:  USB_DriverSTM32F103.rar (677.81 KB, 下载次数: 792) 
PC端USB驱动下载:  USB Driver.rar (266.56 KB, 下载次数: 585) 
PC端USB驱动程序源码下载:  USB_DriverBulk.rar (20.61 KB, 下载次数: 454) 
PC端USB驱动测试程序源码下载:  USB_DriverTest.rar (12.34 KB, 下载次数: 468) 
libusb驱动包下载:  libusb-win32-bin-1.2.6.0.rar (821.57 KB, 下载次数: 682) 

0 0
原创粉丝点击