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