STM32自定义USB设备开发详细流程讲解及全套资料源码下载(基于libusb)

本帖最后由 飞鸿踏雪 于 2014-10-16 13:05 编辑

前言
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
const uint8_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
const uint8_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) */
const uint8_t CustomHID_StringLangID[CUSTOMHID_SIZ_STRING_LANGID] =
{
     CUSTOMHID_SIZ_STRING_LANGID,
     USB_STRING_DESCRIPTOR_TYPE,
     0x09,
     0x04
}; /* LangID = 0x0409: U.S. English */
 
const uint8_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
};
 
const uint8_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.
*******************************************************************************/
void EP1_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.
*******************************************************************************/
void EP2_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
int main( 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( int NeedInit)
{
         if (NeedInit){
                 usb_init(); /* initialize the library */
                 usb_find_busses(); /* find all busses */
                 usb_find_devices(); /* find all connected devices */
         }
         return scan_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( int DevIndex)
{
         pBoardHandle[DevIndex] = open_dev(DevIndex,pBoard);
         if (pBoardHandle[DevIndex]==NULL){
                 return SEVERITY_ERROR;
         } else {
                 return SEVERITY_SUCCESS;
         }
}


关闭设备
[C]  纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
/**
   * @brief  关闭指定的USB设备
   * @param  devNum        需要关闭的设备号
   * @retval 打开状态
   */
int __stdcall USBCloseDev( int DevIndex)
{
         return close_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 int nBoardID, int pipenum, char *sendbuffer, int len, int waittime)
{
         int ret=0;
         if (pBoardHandle[nBoardID] == NULL){
                 return SEVERITY_ERROR;
         }
#ifdef TEST_SET_CONFIGURATION
     if (usb_set_configuration(pBoardHandle[nBoardID], MY_CONFIG) < 0)
     {
         usb_close(pBoardHandle[nBoardID]);
         return SEVERITY_ERROR;
     }
#endif
 
#ifdef TEST_CLAIM_INTERFACE
     if (usb_claim_interface(pBoardHandle[nBoardID], 0) < 0)
     {
         usb_close(pBoardHandle[nBoardID]);
         return SEVERITY_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
     return ret;
}


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 int nBoardID, int pipenum, char *readbuffer, int len, int waittime)
{
         int ret=0;
         if (pBoardHandle[nBoardID] == NULL){
                 return SEVERITY_ERROR;
         }
#ifdef TEST_SET_CONFIGURATION
     if (usb_set_configuration(pBoardHandle[nBoardID], MY_CONFIG) < 0)
     {
         usb_close(pBoardHandle[nBoardID]);
         return SEVERITY_ERROR;
     }
#endif
 
#ifdef TEST_CLAIM_INTERFACE
     if (usb_claim_interface(pBoardHandle[nBoardID], 0) < 0)
     {
         usb_close(pBoardHandle[nBoardID]);
         return SEVERITY_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
     return ret;
}


到此,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( int argc, _TCHAR* argv[])
{
         int DevNum;
         int ret;
         char WriteTestData[256]={1,2,3,4,5,6,7,8,9};
         char ReadTestData[256]={0};
         for ( int i=0;i<256;i++){
                 WriteTestData[i] = i;
         }
         //扫描设备连接数,需要初始化
         DevNum = USBScanDev(1);
         printf ( "设备连接数为:%d\n" ,DevNum);
         //打开设备0
         ret = USBOpenDev(0);
         if (ret == SEVERITY_ERROR){
                 printf ( "打开设备失败!\n" );
                 return SEVERITY_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);
                 return SEVERITY_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);
                 return SEVERITY_ERROR;
         } else {
                 printf ( "端点1读数据成功!\n" );
                 for ( int i=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);
                 return SEVERITY_ERROR;
         } else {
                 printf ( "端点2写数据成功!\n" );
         }
         //端点2读数据
         ret = USBBulkReadData(0,EP2_IN,ReadTestData,64,500);
         if (ret != 64){
                 printf ( "端点2读数据失败!%d\n" ,ret);
                 return SEVERITY_ERROR;
         } else {
                 printf ( "端点2读数据成功!\n" );
                 for ( int i=0;i<64;i++){
                         printf ( "%02X " ,ReadTestData[i]);
                         if (((i+1)%16)==0){
                                 printf ( "\n" );
                         }
                 }
                 printf ( "\n" );
         }
         getchar ();
         return 0;
}


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

串口打印输出
 

PC端测试程序输出
 

Bus Hound抓取到的USB数据
 

程序源码下载
libusb驱动生成工具下载:  inf_tool.rar (778.26 KB, 下载次数: 592) 
STM32程序源码下载:  USB_DriverSTM32F103.rar (677.81 KB, 下载次数: 611) 
PC端USB驱动下载:  USB Driver.rar (266.56 KB, 下载次数: 456) 
PC端USB驱动程序源码下载:  USB_DriverBulk.rar (20.61 KB, 下载次数: 336) 
PC端USB驱动测试程序源码下载:  USB_DriverTest.rar (12.34 KB, 下载次数: 352) 
libusb驱动包下载:  libusb-win32-bin-1.2.6.0.rar (821.57 KB, 下载次数: 529) 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值