精华内容
下载资源
问答
  • STM32 USB设备开发详解

    2018-01-26 22:03:28
    USB 的用途就不多说了,下面的内容主要就是讲解如何利用ST 提供的USB 驱动库和libusb 上位机驱动库实现一个USB 数据传输功能,为了降低开发难度,我们仅仅讲解Bulk 传输模式,当然这也是用得比较多的传输模式
  • USB设备驱动程序开发

    2020-11-07 02:58:17
    由于其众多的优点,USB总线越来越多的被应用到计算机与外设的接口中,芯片厂家也提供了多种USB接口芯片供设计者使用,为了开发出功能强大的USB设备,设计者往往需要自己开发USB设备驱动程序,驱动程序开发一直是...
  • USB的用途就不多说了,下面的内容主要就是讲解如何利用ST提供的USB驱动库和libusb的上位机驱动库实现一个USB数据传输功能,为了降低开发难度,我们仅仅讲解Bulk传输模式,当然这也是用得比较多的传输模式。...
  • USB的用途就不多说了,下面的内容主要就是讲解如何利用ST提供的USB驱动库和libusb上位机驱动库实现一个USB数据传输功能,为了降低开发难度,我们仅仅讲解Bulk传输模式,当然这也是用得比较多的传输模式。 开发...
    前言
    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