本帖最后由 飞鸿踏雪 于 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
|
|