STM32自定义USB设备开发详细流程讲解及全套资料源码下载(基于libusb的)

前言
USB的用途就不多说了,下面的内容主要就是讲解如何利用ST提供的USB驱动库和libusb的上位机驱动库实现一个USB数据传输功能,为了降低开发难度,我们仅仅讲解Bulk传输模式,当然这也是用得比较多的传输模式。

开发流程
1,完成STM32单片机端的USB程序; 
2,利用libusb自带的inf-wizard工具生成USB驱动; 
3,基于libusb编写USB通信程序; 
4,测试PC和单片机的数据通信; 

STM32程序编写
1,完成描述符的修改,修改后的描述符如下(在usb_desc.c文件中)
设备描述符:

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 */

配置描述符:

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端点。

其他的描述符:

/* 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文件中):

/* 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函数中)

/* 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中注释掉对应的回调函数宏定义)

/*******************************************************************************
* 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,完成主函数的测试程序

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 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

到此,STM32的程序基本上编写完成,然后编译下载程序,如果一切顺利,系统会检测到一个新的设备并试图加载对应的驱动,由于我们还没做驱动程序,所以肯定会加载驱动失败,如下图所示:
 

驱动程序生成
下面我们就利用libusb的自带的INF-向导工具生成的USB驱动程序,该工具可以到本文章的附件下载,其具体过程如下:
 

运行该程序,出现下图对话框,点击“下一步”。
 

出现下图对话框后选择我们需要生成驱动程序的设备;
 

这里可以写该设备名称,我们保持默认值,其他的都不需要修改;
 

点击下一步后出现下图对话框,我们选择一个目录保存这个INF文件;
 

保存后的文件
 

若要立即安装驱动,可以点击下面对话框的红色框按钮;
 

Win7的下可能会出现如下对话框,点击始终安装;
 

到此,USB驱动程序自动生成完毕,若安装了驱动,则在设备管理器里面会看到如下信息
 

基于libusb的上位的机驱动程序关系编写

首先建立一个驱动程序工程,然后将libusb的的库(附件有下载)添加到工程里面,以下关系编写几个函数
设备扫描函数,该函数用来找到插入电脑上的USB设备

/**
  * @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);
}

打开设备

/**
  * @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;
        }
}

关闭设备

/**
  * @brief  关闭指定的USB设备
  * @param  devNum        需要关闭的设备号
  * @retval 打开状态
  */
int __stdcall USBCloseDev(int DevIndex)
{
        return close_dev(DevIndex,pBoardHandle);
}

BULK端点写数据

/**
  * @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端点读数据

/**
  * @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文件中的函数,测试程序如下:

// USB_DriverTest.cpp : 定义控制台应用程序的入口点。
//
 
#include "stdafx.h"
 
#define        EP1_OUT_SIZE        64
#define        EP1_IN_SIZE        64
 
int _tmain(int argc, _TCHAR* argv[])
{
        int DevNum;
        int ret;
        char WriteTestData[256]={1,2,3,4,5,6,7,8,9};
        char ReadTestData[256]={0};
        for(int i=0;i<256;i++){
                WriteTestData[i] = i;
        }
        //扫描设备连接数,需要初始化
        DevNum = USBScanDev(1);
        printf("设备连接数为:%d\n",DevNum);
        //打开设备0
        ret = USBOpenDev(0);
        if(ret == SEVERITY_ERROR){
                printf("打开设备失败!\n");
                return SEVERITY_ERROR;
        }else{
                printf("打开设备成功!\n");
        }
 
        //端点1写数据
        ret = USBBulkWriteData(0,EP1_OUT,WriteTestData,EP1_OUT_SIZE,500);
        if(ret != EP1_OUT_SIZE){
                printf("端点1写数据失败!%d\n",ret);
                return SEVERITY_ERROR;
        }else{
                printf("端点1写数据成功!\n");
        }
        //端点1读数据
        ret = USBBulkReadData(0,EP1_IN,ReadTestData,EP1_IN_SIZE,500);
        if(ret != EP1_IN_SIZE){
                printf("端点1读数据失败!%d\n",ret);
                return SEVERITY_ERROR;
        }else{
                printf("端点1读数据成功!\n");
                for(int i=0;i

到此,整个开发流程基本完成,的英文下面本。套程序的测试图片

串口打印输出
 

PC端测试程序输出
 

Bus Hound抓取到的USB数据
 

程序源码下载

libusb的驱动生成工具下载:  inf_tool.rar 
STM32程序源码下载:  USB_DriverSTM32F103.rar 
PC端USB驱动下载:  USB Driver.rar 
PC端USB驱动程序源码下载:  USB_DriverBulk.rar 
PC端USB驱动测试程序源码下载:  USB_DriverTest.rar 
libusb的驱动包下载:  libusb-win32-bin-1.2.6.0.rar 

你可能感兴趣的:(STM32,设备驱动)