本帖最后由 飞鸿踏雪 于 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,
USB_DEVICE_DESCRIPTOR_TYPE,
0x00,
0x02,
0x00,
0x00,
0x00,
0x40,
LOBYTE(USBD_VID),
HIBYTE(USBD_VID),
LOBYTE(USBD_PID),
HIBYTE(USBD_PID),
0x00,
0x02,
1,
2,
3,
0x01
};
|
配置描述符:
[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,
USB_CONFIGURATION_DESCRIPTOR_TYPE,
CUSTOMHID_SIZ_CONFIG_DESC,
0x00,
0x01,
0x01,
0x00,
0xE0,
0xFA,
0x09,
USB_INTERFACE_DESCRIPTOR_TYPE,
0x00,
0x00,
0x04,
0xDC,
0xA0,
0xB0,
0,
0x07,
USB_ENDPOINT_DESCRIPTOR_TYPE,
0x81,
0x02,
0x40,0x00,
0x00,
0x07,
USB_ENDPOINT_DESCRIPTOR_TYPE,
0x01,
0x02,
0x40,0x00,
0x00,
0x07,
USB_ENDPOINT_DESCRIPTOR_TYPE,
0x82,
0x02,
0x40,0x00,
0x00,
0x07,
USB_ENDPOINT_DESCRIPTOR_TYPE,
0x02,
0x02,
0x40,0x00,
0x00,
};
|
配置描述符就包含了端点描述符,我们用了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
|
const uint8_t CustomHID_StringLangID[CUSTOMHID_SIZ_STRING_LANGID] =
{
CUSTOMHID_SIZ_STRING_LANGID,
USB_STRING_DESCRIPTOR_TYPE,
0x09,
0x04
};
const uint8_t CustomHID_StringVendor[CUSTOMHID_SIZ_STRING_VENDOR] =
{
CUSTOMHID_SIZ_STRING_VENDOR,
USB_STRING_DESCRIPTOR_TYPE,
'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,
USB_STRING_DESCRIPTOR_TYPE,
'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,
USB_STRING_DESCRIPTOR_TYPE,
'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
|
#define BTABLE_ADDRESS (0x00)
#define ENDP0_RXADDR (0x18)
#define ENDP0_TXADDR (0x58)
#define ENDP1_RXADDR (0x98)
#define ENDP1_TXADDR (0x98+64)
#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
|
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);
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);
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
|
void EP1_OUT_Callback( void )
{
EP1_ReceivedCount = GetEPRxCount(ENDP1);
PMAToUserBufferCopy(USB_Receive_Buffer, ENDP1_RXADDR, EP1_ReceivedCount);
SetEPRxStatus(ENDP1, EP_RX_VALID);
}
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();
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
|
int __stdcall USBScanDev( int NeedInit)
{
if (NeedInit){
usb_init();
usb_find_busses();
usb_find_devices();
}
return scan_dev(pBoard);
}
|
打开设备
[C] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
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
|
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
|
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
ret = transfer_bulk_async(dev, pipenum, sendbuffer, len, waittime);
#else
ret = usb_bulk_write(pBoardHandle[nBoardID], pipenum, sendbuffer, len, 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
|
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
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
|
#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);
ret = USBOpenDev(0);
if (ret == SEVERITY_ERROR){
printf ( "打开设备失败!\n" );
return SEVERITY_ERROR;
} else {
printf ( "打开设备成功!\n" );
}
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" );
}
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);
ret = USBBulkWriteData(0,EP2_OUT,WriteTestData+64,64,500);
if (ret != 64){
printf ( "端点2写数据失败!%d\n" ,ret);
return SEVERITY_ERROR;
} else {
printf ( "端点2写数据成功!\n" );
}
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)
|