前言
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)
|