STM32自定義USB設備開發詳細流程講解及全套資料源碼下載(基於libusb)

前言
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
相關文章
相關標籤/搜索