打印
[应用相关]

自定义USB设备开发详细流程讲解(基于libusb)

[复制链接]
2816|23
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
gaonaiweng|  楼主 | 2021-11-29 15:14 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
USB的用途就不多说了,下面的内容主要就是讲解如何利用ST提供的USB驱动库和libusb上位机驱动库实现一个USB数据传输功能,为了降低开发难度,我们仅仅讲解Bulk传输模式,当然这也是用得比较多的传输模式。

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

使用特权

评论回复
沙发
小叶三千| | 2021-11-29 15:17 | 只看该作者
感谢分享,这个Bulk传输模式应该是需要驱动程序吧?可以无驱运行吗

使用特权

评论回复
板凳
gaonaiweng|  楼主 | 2021-11-29 15:17 | 只看该作者
STM32程序编写
1,完成描述符的修改,修改后的描述符如下(在usb_desc.c文件中)
设备描述符:

使用特权

评论回复
地板
gaonaiweng|  楼主 | 2021-11-29 15:18 | 只看该作者
 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 */

使用特权

评论回复
5
gaonaiweng|  楼主 | 2021-11-29 15:19 | 只看该作者
配置描述符:
  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 */
            

使用特权

评论回复
6
gaonaiweng|  楼主 | 2021-11-29 15:24 | 只看该作者
配置描述符就包含了端点描述符,我们用了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
            

             };
            

使用特权

评论回复
7
gaonaiweng|  楼主 | 2021-11-29 15:25 | 只看该作者
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)
            

使用特权

评论回复
8
gaonaiweng|  楼主 | 2021-11-29 15:28 | 只看该作者
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);
            

使用特权

评论回复
9
gaonaiweng|  楼主 | 2021-11-29 15:29 | 只看该作者
4,实现端点的回调函数(需要在usb_conf.h中注释掉对应的回调函数宏定义)

使用特权

评论回复
10
gaonaiweng|  楼主 | 2021-11-29 15:31 | 只看该作者
       /*******************************************************************************
            

             * 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);
            

             }

使用特权

评论回复
11
gaonaiweng|  楼主 | 2021-11-29 15:33 | 只看该作者
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*************************** http://www.embed-net.com **************************"
             );
            

                     
             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;        
            

                             
             }
            

                     
             }
            

             }

使用特权

评论回复
12
gaonaiweng|  楼主 | 2021-11-29 15:34 | 只看该作者
到此,STM32的程序基本上编写完成,然后编译下载程序,如果一切顺利,系统会检测到一个新的设备并试图加载对应的驱动,由于我们还没做驱动程序,所以肯定会加载驱动失败。

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

使用特权

评论回复
13
gaonaiweng|  楼主 | 2021-11-29 15:36 | 只看该作者
运行该程序,出现下图对话框,点击“Next”;


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


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


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


保存后的文件

使用特权

评论回复
14
gaonaiweng|  楼主 | 2021-11-29 15:37 | 只看该作者
若要立即安装驱动,可以点击下面对话框的红色框按钮;


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


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


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

使用特权

评论回复
15
gaonaiweng|  楼主 | 2021-11-29 15:39 | 只看该作者
  /**
            

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

             }
            

使用特权

评论回复
16
gaonaiweng|  楼主 | 2021-11-29 15:39 | 只看该作者
打开设备
  /**
            

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

                     
             }
            

             }
            

使用特权

评论回复
17
gaonaiweng|  楼主 | 2021-11-29 15:41 | 只看该作者
关闭设备
     /**
            

               
             * @brief  关闭指定的USB设备
            

               
             * @param  devNum        需要关闭的设备号
            

               
             * @retval 打开状态
            

               
             */
            

             int
              __stdcall USBCloseDev(
             int
              DevIndex)
            

             {
            

                     
             return
              close_dev(DevIndex,pBoardHandle);
            

             }
            

使用特权

评论回复
18
gaonaiweng|  楼主 | 2021-11-29 15:52 | 只看该作者
BULK端点写数据

使用特权

评论回复
19
gaonaiweng|  楼主 | 2021-11-29 15:54 | 只看该作者
      /**
            

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

             }

使用特权

评论回复
20
gaonaiweng|  楼主 | 2021-11-29 15:55 | 只看该作者
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;
            

             }
            

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

69

主题

697

帖子

3

粉丝