本帖最後由 飛鴻踏雪 於 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
|
|