精华内容
下载资源
问答
  • STM32 HAL库之USB

    2021-01-06 09:06:11
    stm32f1xx_hal_msp.c 文件定义了两个函数 HAL_MspInit 和 HAL_MspDeInit。这两个函数分别被文件 stm32f1xx_hal.c 中的 HAL_Init 和 HAL_DeInit 所调用。HAL_MspInit 函数的主要作用是进行 MCU相关的硬件初始化操作。...
  • STM32 HAL库实现USB组合设备CDC+MSC 目录STM32 HAL库实现USB组合设备CDC+MSC

    STM32 HAL库实现USB组合设备CDC+MSC


    本博客完整代码下载地址:https://download.csdn.net/download/qq153471503/18160432?spm=1001.2014.3001.5501

    或关注以下公众号,回复关键字CDCMSC获取下载链接!
    在这里插入图片描述



    工程试验环境

    • STM32F103RC
    • STM32CUBEIDE1.5.1

    1、本教程默认你已经会使用STM32CUBEMX生成CDC代码和MSC代码,这两个工程的生成很简单,网络上的教程一搜遍地是。

    2、USB组合设备的移植修改需要具备一定的USB知识储备,如果没有强烈建议看一下我的另一篇博客:STM32 USB相关知识扫盲


    首先说一下STM32的USB库的初始化操作,MX_USB_DEVICE_Init函数中使用USBD_RegisterClass函数注册绑定了实际的端口初始化控制等操作,如果是CDC那么注册的就是USBD_CDC这个结构,如果是MSC那么就是注册的USB_MSC这个结构,所以我们的组合设备思路就是用哪个的时候,就将这个结构切换成对应的操作结构。


    第一步:基础工程生成

    首先先用STM32CUBEMX生成CDC的工程和MSC的工程,并测试通过没有问题后,我这里使用CDC工程为基础工程进行修改成USB组合设备。

    基本步骤如下:

    1. USB设备描述符修改成组合设备类型
    2. 修改PMA端点分布
    3. 修改USB配置描述符并添加MSC的配置描述
    4. 修改初始化函数接口,改写成我们自己的组合设备初始化操作函数
    5. 修改MX_USB_DEVICE_Init函数,注册成我们自己的组合设备

    下面进行分布修改。

    第二步:USB设备描述符的修改

    这一步很简单的,就是修改usbd_desc.c中的设备描述符数组USBD_FS_DeviceDesc,将设备类型改为组合设备类型:

    第三步:修改PMA端点分布

    首先修改一下CDC所用到的端点地址,CDC的输入输出端点不动,将命令端点成0X83:

    在改一下MSC的输入输出端点,改成0X82和0X02:

    然后进入usbd_conf.c文件中,找到USBD_LL_Init函数,修改PMA端点初始化:

    这里的修改非常关键,为什么addr要从0X38开始呢?

    因为我们目前用到了:

    • 默认的两个端点(0X80、0X00)
    • CDC的三个端点(0X81、0X01、0X83)
    • MSC的两个端点(0X82、0X02)

    共7个端点,每个端点占用8字节,总共56字节,十六进制就是0X38!

    好多人修改自己的USB应用时,就是卡在了这里!如果起始地址修改不对,那么USB端点缓冲就会覆盖PMA头部的端点描述!

    有关PMA的详细描述及使用,请看文档头部的《STM32 USB相关知识扫盲》链接文章!


    第四步:编写我们自己的组合设备配置

    这里我为了方便修改,新建了一个usbd_composite.c文件,用于编写组合设备驱动,文件内容如下:

    #include "usbd_def.h"
    #include "usbd_msc.h"
    #include "usbd_cdc.h"
    #include "usbd_storage_if.h"
    #include "usbd_cdc_if.h"
    
    
    #define USB_MC_CONFIG_DESC_SIZ 106
    
    
    extern USBD_HandleTypeDef hUsbDeviceFS;
    
    
    extern uint8_t USBD_MSC_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx);
    extern uint8_t USBD_MSC_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx);
    extern uint8_t  USBD_MSC_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req);
    extern uint8_t USBD_MSC_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum);
    extern uint8_t USBD_MSC_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum);
    
    
    extern uint8_t  USBD_CDC_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx);
    extern uint8_t  USBD_CDC_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx);
    extern uint8_t  USBD_CDC_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req);
    extern uint8_t  USBD_CDC_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum);
    extern uint8_t  USBD_CDC_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum);
    extern uint8_t  USBD_CDC_EP0_RxReady(USBD_HandleTypeDef *pdev);
    
    
    static uint8_t  USBD_Composite_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx);
    static uint8_t  USBD_Composite_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx);
    static uint8_t  USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req);
    static uint8_t  USBD_Composite_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum);
    static uint8_t  USBD_Composite_DataOut (USBD_HandleTypeDef *pdev, uint8_t epnum);
    static uint8_t  *USBD_Composite_GetHSCfgDesc (uint16_t *length);
    static uint8_t  *USBD_Composite_GetFSCfgDesc (uint16_t *length);
    static uint8_t  *USBD_Composite_GetOtherSpeedCfgDesc (uint16_t *length);
    static uint8_t  *USBD_Composite_GetDeviceQualifierDescriptor (uint16_t *length);
    static uint8_t USBD_Composite_RxReady (USBD_HandleTypeDef *pdev);
    static void USBD_Composite_Switch_MSC(USBD_HandleTypeDef *pdev);
    static void USBD_Composite_Switch_CDC(USBD_HandleTypeDef *pdev);
    
    
    USBD_ClassTypeDef  USBD_Composite_CDC_MSC =
    {
      USBD_Composite_Init,
      USBD_Composite_DeInit,
      USBD_Composite_Setup,
      NULL, /*EP0_TxSent*/
      USBD_Composite_RxReady, /*EP0_RxReady*/
      USBD_Composite_DataIn,
      USBD_Composite_DataOut,
      NULL, /*SOF */
      NULL,
      NULL,
      USBD_Composite_GetHSCfgDesc,
      USBD_Composite_GetFSCfgDesc,
      USBD_Composite_GetOtherSpeedCfgDesc,
      USBD_Composite_GetDeviceQualifierDescriptor,
    };
    
    
    /*   All Descriptors (Configuration, Interface, Endpoint, Class, Vendor */
    __ALIGN_BEGIN uint8_t USBD_Composite_CfgDesc[USB_MC_CONFIG_DESC_SIZ] __ALIGN_END =
    {
      /*Configuration Descriptor*/
      0x09,   /* bLength: Configuration Descriptor size */
      USB_DESC_TYPE_CONFIGURATION,      /* bDescriptorType: Configuration */
      USB_MC_CONFIG_DESC_SIZ,           /* wTotalLength:no of returned bytes */
      0x00,
      0x03,   /* bNumInterfaces: 3 interface */ /* +++lakun:CDC用了两个接口,MSC用了一个接口,所以是3 */
      0x01,   /* bConfigurationValue: Configuration value */
      0x00,   /* iConfiguration: Index of string descriptor describing the configuration */
      0xC0,   /* bmAttributes: self powered */
      0x32,   /* MaxPower 0 mA */
    
      /*---------------------------------------------------------------------------*/
    
      //
      // +++lakun: IAD(Interface Association Descriptor),用于指示CDC
      //
      0X08,  // bLength: Interface Descriptor size,固定值
      0X0B,  // bDescriptorType: IAD,固定值
      0X00,  // bFirstInterface,第一个接口的起始序号,从0开始
      0X02,  // bInterfaceCount,本IAD下的接口数量
      0X02,  // bFunctionClass: CDC,表明该IAD是一个CDC类型的设备
      0X02,  // bFunctionSubClass:子类型,默认即可
      0X01,  // bFunctionProtocol:控制协议,默认即可
      0X00,  // iFunction
    
      /*Interface Descriptor */
      0x09,   /* bLength: Interface Descriptor size */
      USB_DESC_TYPE_INTERFACE,  /* bDescriptorType: Interface */
      /* Interface descriptor type */
      0x00,   /* bInterfaceNumber: Number of Interface */                   /* +++lakun:接口编号  */
      0x00,   /* bAlternateSetting: Alternate setting */
      0x01,   /* bNumEndpoints: One endpoints used */
      0x02,   /* bInterfaceClass: Communication Interface Class */          /* +++lakun:表明这是一个通信接口 */
      0x02,   /* bInterfaceSubClass: Abstract Control Model */
      0x01,   /* bInterfaceProtocol: Common AT commands */
      0x00,   /* iInterface: */
    
      /*Header Functional Descriptor*/
      0x05,   /* bLength: Endpoint Descriptor size */
      0x24,   /* bDescriptorType: CS_INTERFACE */
      0x00,   /* bDescriptorSubtype: Header Func Desc */
      0x10,   /* bcdCDC: spec release number */
      0x01,
    
      /*Call Management Functional Descriptor*/
      0x05,   /* bFunctionLength */
      0x24,   /* bDescriptorType: CS_INTERFACE */
      0x01,   /* bDescriptorSubtype: Call Management Func Desc */
      0x00,   /* bmCapabilities: D0+D1 */
      0x01,   /* bDataInterface: 1 */
    
      /*ACM Functional Descriptor*/
      0x04,   /* bFunctionLength */
      0x24,   /* bDescriptorType: CS_INTERFACE */
      0x02,   /* bDescriptorSubtype: Abstract Control Management desc */
      0x02,   /* bmCapabilities */
    
      /*Union Functional Descriptor*/
      0x05,   /* bFunctionLength */
      0x24,   /* bDescriptorType: CS_INTERFACE */
      0x06,   /* bDescriptorSubtype: Union func desc */
      0x00,   /* bMasterInterface: Communication class interface */           /* +++lakun:这里是用来指示CDC通信接口的编号的 */
      0x01,   /* bSlaveInterface0: Data Class Interface */                    /* +++lakun:这里是用来指示CDC数据接口的编号的 */
    
      /*Endpoint 2 Descriptor*/
      0x07,                           /* bLength: Endpoint Descriptor size */
      USB_DESC_TYPE_ENDPOINT,   /* bDescriptorType: Endpoint */
      CDC_CMD_EP,                     /* bEndpointAddress */
      0x03,                           /* bmAttributes: Interrupt */
      LOBYTE(CDC_CMD_PACKET_SIZE),     /* wMaxPacketSize: */
      HIBYTE(CDC_CMD_PACKET_SIZE),
      CDC_FS_BINTERVAL,                           /* bInterval: */
      /*---------------------------------------------------------------------------*/
    
      /*Data class interface descriptor*/
      0x09,   /* bLength: Endpoint Descriptor size */
      USB_DESC_TYPE_INTERFACE,  /* bDescriptorType: */
      0x01,   /* bInterfaceNumber: Number of Interface */                     /* +++lakun:CDC数据接口的编号为1 */
      0x00,   /* bAlternateSetting: Alternate setting */
      0x02,   /* bNumEndpoints: Two endpoints used */
      0x0A,   /* bInterfaceClass: CDC */
      0x00,   /* bInterfaceSubClass: */
      0x00,   /* bInterfaceProtocol: */
      0x00,   /* iInterface: */
    
      /*Endpoint OUT Descriptor*/
      0x07,   /* bLength: Endpoint Descriptor size */
      USB_DESC_TYPE_ENDPOINT,      /* bDescriptorType: Endpoint */
      CDC_OUT_EP,                        /* bEndpointAddress */
      0x02,                              /* bmAttributes: Bulk */
      LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),  /* wMaxPacketSize: */
      HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
      0x00,                              /* bInterval: ignore for Bulk transfer */
    
      /*Endpoint IN Descriptor*/
      0x07,   /* bLength: Endpoint Descriptor size */
      USB_DESC_TYPE_ENDPOINT,      /* bDescriptorType: Endpoint */
      CDC_IN_EP,                         /* bEndpointAddress */
      0x02,                              /* bmAttributes: Bulk */
      LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),  /* wMaxPacketSize: */
      HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
      0x00,                               /* bInterval: ignore for Bulk transfer */
    
      //
      // +++lakun: IAD(Interface Association Descriptor)
      //
      0X08,  // bLength: Interface Descriptor size,固定值
      0X0B,  // bDescriptorType: IAD,固定值
      0X02,  // bFirstInterface,接口的起始序号(第0、1编号的接口用于CDC1,所以从2开始)
      0X01,  // bInterfaceCount,本IAD下的接口数量
      0X08,  // bFunctionClass: MSC,表明该IAD是一个MSC类型的设备
      0X06,  // bFunctionSubClass:子类型,默认即可
      0X50,  // bFunctionProtocol:控制协议,默认即可
      0X05,  // iFunction
    
      /********************  Mass Storage interface ********************/
      0x09,   /* bLength: Interface Descriptor size */
      0x04,   /* bDescriptorType: */
      0x02,   /* bInterfaceNumber: Number of Interface */  /* +++lakun:第0和1编号的用给了CDC,所以MSC接口的编号从2开始 */
      0x00,   /* bAlternateSetting: Alternate setting */
      0x02,   /* bNumEndpoints*/
      0x08,   /* bInterfaceClass: MSC Class */
      0x06,   /* bInterfaceSubClass : SCSI transparent*/
      0x50,   /* nInterfaceProtocol */
      0x05,          /* iInterface: */
      /********************  Mass Storage Endpoints ********************/
      0x07,   /*Endpoint descriptor length = 7*/
      0x05,   /*Endpoint descriptor type */
      MSC_EPIN_ADDR,   /*Endpoint address (IN, address 1) */
      0x02,   /*Bulk endpoint type */
      LOBYTE(MSC_MAX_FS_PACKET),
      HIBYTE(MSC_MAX_FS_PACKET),
      0x00,   /*Polling interval in milliseconds */
    
      0x07,   /*Endpoint descriptor length = 7 */
      0x05,   /*Endpoint descriptor type */
      MSC_EPOUT_ADDR,   /*Endpoint address (OUT, address 1) */
      0x02,   /*Bulk endpoint type */
      LOBYTE(MSC_MAX_FS_PACKET),
      HIBYTE(MSC_MAX_FS_PACKET),
      0x00     /*Polling interval in milliseconds*/
    } ;
    
    
    /* USB Standard Device Descriptor */
    uint8_t USBD_Composite_DeviceQualifierDesc[USB_LEN_DEV_QUALIFIER_DESC] =
    {
      USB_LEN_DEV_QUALIFIER_DESC,
      USB_DESC_TYPE_DEVICE_QUALIFIER,
      0x00,
      0x02,
      0x00,
      0x00,
      0x00,
      0X40,
      0x01,
      0x00,
    };
    
    
    // 这个函数是修改的USBD_MSC_Init
    static void USBD_Composite_MSC_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
    {
    	USBD_Composite_Switch_MSC(pdev);
    
    	/* Open EP OUT */
    	USBD_LL_OpenEP(pdev, MSC_EPOUT_ADDR, USBD_EP_TYPE_BULK, MSC_MAX_FS_PACKET);
    	pdev->ep_out[MSC_EPOUT_ADDR & 0xFU].is_used = 1U;
    
    	/* Open EP IN */
    	USBD_LL_OpenEP(pdev, MSC_EPIN_ADDR, USBD_EP_TYPE_BULK, MSC_MAX_FS_PACKET);
    	pdev->ep_in[MSC_EPIN_ADDR & 0xFU].is_used = 1U;
    
    	/* Init the BOT  layer */
    	MSC_BOT_Init(pdev);
    }
    
    // 这个函数是修改的USBD_CDC_Init
    static void USBD_Composite_CDC_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
    {
    	USBD_CDC_HandleTypeDef   *hcdc = NULL;
    
    	USBD_Composite_Switch_CDC(pdev);
    
    	/* Open EP IN */
    	USBD_LL_OpenEP(pdev, CDC_IN_EP, USBD_EP_TYPE_BULK, CDC_DATA_FS_IN_PACKET_SIZE);
    	pdev->ep_in[CDC_IN_EP & 0xFU].is_used = 1U;
    
    	/* Open EP OUT */
    	USBD_LL_OpenEP(pdev, CDC_OUT_EP, USBD_EP_TYPE_BULK,CDC_DATA_FS_OUT_PACKET_SIZE);
    	pdev->ep_out[CDC_OUT_EP & 0xFU].is_used = 1U;
    
    	/* Open Command IN EP */
    	USBD_LL_OpenEP(pdev, CDC_CMD_EP, USBD_EP_TYPE_INTR, CDC_CMD_PACKET_SIZE);
    	pdev->ep_in[CDC_CMD_EP & 0xFU].is_used = 1U;
    
    	hcdc = (USBD_CDC_HandleTypeDef *) pdev->pClassData;
    
    	/* Init  physical Interface components */
    	((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Init();
    
    	/* Init Xfer states */
    	hcdc->TxState = 0U;
    	hcdc->RxState = 0U;
    
    	/* Prepare Out endpoint to receive next packet */
    	USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer, CDC_DATA_FS_OUT_PACKET_SIZE);
    }
    
    uint8_t USBD_Composite_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx)
    {
    	USBD_Composite_MSC_Init(pdev, cfgidx);  // 初始化MSC
    	USBD_Composite_CDC_Init(pdev, cfgidx);  // 初始化CDC
    	return USBD_OK;
    }
    
    uint8_t  USBD_Composite_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx)
    {
    	return USBD_OK;
    }
    
    uint8_t  USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req)
    {
    	switch(req->wIndex)  // wIndex是接口编号,在配置描述符里面我们第一个IAD是CDC使用的是编号0和1两个接口,第二个IAD是MSC使用的是编号2的接口
    	{
    
    	//
    	// 第一个IAD
    	//
    	case 0:  // CDC的命令接口编号为0
    	case 1:  // CDC的数据接口编号为1
    		USBD_Composite_Switch_CDC(pdev);
    		USBD_CDC_Setup(pdev, req);
    		break;
    
    	//
    	// 第二个IAD
    	//
    	case 2:  // MSC只有一个接口,编号为2
    		USBD_Composite_Switch_MSC(pdev);
    		USBD_MSC_Setup(pdev, req);
    		break;
    
    	//
    	// 第三个IAD(如果有,在这里初始化)
    	//
    	case 3:
    		break;
    	default:break;
    	}
    	return USBD_OK;
    }
    
    uint8_t  USBD_Composite_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum)
    {
    	switch(epnum | 0X80)
    	{
    	case MSC_EPIN_ADDR:
    	    USBD_Composite_Switch_MSC(pdev);
    	    USBD_MSC_DataIn(pdev, epnum);
    		break;
    	case CDC_IN_EP:
    	    USBD_Composite_Switch_CDC(pdev);
    	    USBD_CDC_DataIn(pdev, epnum);
    		break;
    	default:break;
    	}
    
    	return USBD_OK;
    }
    
    uint8_t  USBD_Composite_DataOut (USBD_HandleTypeDef *pdev, uint8_t epnum)
    {
    	switch(epnum)
    	{
    	case MSC_EPOUT_ADDR:
    		USBD_Composite_Switch_MSC(pdev);
    		USBD_MSC_DataOut(pdev, epnum);
    		break;
    	case CDC_OUT_EP:
    		USBD_Composite_Switch_CDC(pdev);
    		USBD_CDC_DataOut(pdev, epnum);
    		break;
    	default:break;
    	}
    	return USBD_OK;
    }
    
    uint8_t  *USBD_Composite_GetHSCfgDesc (uint16_t *length)
    {
      *length = sizeof (USBD_Composite_CfgDesc);
      return USBD_Composite_CfgDesc;
    }
    
    uint8_t  *USBD_Composite_GetFSCfgDesc (uint16_t *length)
    {
      *length = sizeof (USBD_Composite_CfgDesc);
      return USBD_Composite_CfgDesc;
    }
    
    uint8_t  *USBD_Composite_GetOtherSpeedCfgDesc (uint16_t *length)
    {
      *length = sizeof (USBD_Composite_CfgDesc);
      return USBD_Composite_CfgDesc;
    }
    
    uint8_t  *USBD_Composite_GetDeviceQualifierDescriptor (uint16_t *length)
    {
      *length = sizeof (USBD_Composite_DeviceQualifierDesc);
      return USBD_Composite_DeviceQualifierDesc;
    }
    
    static uint8_t  USBD_Composite_RxReady (USBD_HandleTypeDef *pdev)
    {
    	uint8_t ret = 0;
    	switch(pdev->request.wIndex)  // wIndex是接口编号,这里我们通过接口编号确定CDC还是其他设备的EP0接收
    	{
    		case 0:  // CDC的命令接口编号是0
    		case 1:  // CDC的数据接口编号是1
    			USBD_Composite_Switch_CDC(pdev);
    			ret = USBD_CDC_EP0_RxReady(pdev);
    			break;
    		// 如果有其他设备还用到了EP0接收,在这里加入
    		case 2:
    			break;
    		default:break;
    	}
        return ret;
    }
    
    static void USBD_Composite_Switch_MSC(USBD_HandleTypeDef *pdev)
    {
    	static USBD_MSC_BOT_HandleTypeDef USBD_MSC_Handle;
    	USBD_MSC_RegisterStorage(pdev, &USBD_Storage_Interface_fops_FS);
    	pdev->pClassData = (void *)&USBD_MSC_Handle;
    }
    
    static void USBD_Composite_Switch_CDC(USBD_HandleTypeDef *pdev)
    {
    	static USBD_CDC_HandleTypeDef USBD_CDC_Handle;
    	USBD_CDC_RegisterInterface(pdev, &USBD_Interface_fops_FS);
    	pdev->pClassData = (void *)&USBD_CDC_Handle;
    }
    
    

    其中有关设备描述符的修改,我都用+++lakun进行了标注,并写上注释,方便查看。


    第五步:MX_DEVICE_USB_Init修改

    上面我们已经完成了统一接口的编写,现在就可以修改MX_DEVICE_USB_Init函数了,屏蔽掉接口注册函数USBD_CDC_RegisterInterface,然后将类注册换成我们自己的组合设备,如下图:

    到这里就已经改完了,运行程序之后设备管理器会出现:一个组合设备、一个虚拟的串口、一个USB大容量存储设备,如下图:

    这样就是成功了!

    !!如果修改过后发现设备管理器的MSC或者CDC显示感叹号,那么在回头检查PMA的配置以及组合设备描述符!!


    遇到的问题

    我修改完毕之后,将板子接入电脑,虚拟出来的串口没问题,但是就是死活认不出U盘,后来找到问题的原因是因为我先使用的CDC串口这个单独的工程,我的win10已经默认枚举成了CDC,所以认不出U盘。

    解决办法:

    1. 卸载识别出来的串口,重新拔插
    2. 在程序里修改一下PID
      我是用的第二种办法解决的!

    ends…

    展开全文
  • 本工程由stm32Cube生成,修改了usb接收函数,实现虚拟串口接收数据并返回的功能
  • hal库的usb虚拟串口,系统时钟是8m的晶振,RTC时钟是32.768k的晶振,使用serial wire调试,用systick作为系统时钟,并1ms触发一个中断
  • STM32 USB HAL_PCDEx_SetRxFiFo、HAL_PCDEx_SetTxFiFo内存分配注意问题 一、遇到的问题 当我配置完缓冲区后,用HID端点上传数据发现到电脑端接收的数据是错的,而且没有什么规律。以下是我的配置代码: HAL_PCDEx_...

    STM32 USB HAL_PCDEx_SetRxFiFo、HAL_PCDEx_SetTxFiFo内存分配注意问题

    一、遇到的问题

    当我配置完缓冲区后,用HID端点上传数据发现到电脑端接收的数据是错的,而且没有什么规律。以下是我的配置代码:

      HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_FS, 0x60);
      HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 0, 0x20);
      HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 1, 0x40);
      HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 2, 0x100);
      HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 3, 0x40);
    

    有几个问题

    1. 分配内存的时候以为最后面的参数是字节,根据手册其实最后的参数为DWORD。也就是4字节。
    2. 分配的总内存大小超过总的内存大小。根据手册1.25Kbyte的缓冲。也就是最大是 0x140 * 4 = 1248byte

    二、详细分析下配置函数

    以下是STM32F105 USB库里面的函数。

    //设置发送端点缓冲
    HAL_StatusTypeDef HAL_PCDEx_SetTxFiFo(PCD_HandleTypeDef *hpcd, uint8_t fifo, uint16_t size)
    {
        uint8_t i;
        uint32_t Tx_Offset;
        Tx_Offset = hpcd->Instance->GRXFSIZ;
        if (fifo == 0U)
        {
            hpcd->Instance->DIEPTXF0_HNPTXFSIZ = ((uint32_t)size << 16) | Tx_Offset;
        }
        else
        {
            //端点零Tx
            Tx_Offset += (hpcd->Instance->DIEPTXF0_HNPTXFSIZ) >> 16;
            for (i = 0U; i < (fifo - 1U); i++)
            {
                //其它端点Tx
                Tx_Offset += (hpcd->Instance->DIEPTXF[i] >> 16);
            }
            /* Multiply Tx_Size by 2 to get higher performance */
            hpcd->Instance->DIEPTXF[fifo - 1U] = ((uint32_t)size << 16) | Tx_Offset;
        }
        return HAL_OK;
    }
    
    //设置接收端点缓冲
    HAL_StatusTypeDef HAL_PCDEx_SetRxFiFo(PCD_HandleTypeDef *hpcd, uint16_t size)
    {
        hpcd->Instance->GRXFSIZ = size;
        return HAL_OK;
    }
    

    根据上面的配置函数,有以下要注意的问题:

    1. 分配内存时是有顺序的,要先分配HAL_PCDEx_SetRxFiFo。所有Rx端点共用一个Fifo。为什么要先分配Rx的呢?因为GRXFSIZ变量在HAL_PCDEx_SetTxFiFo函数中用到。
    2. 内存的总大小不要超过缓冲的大小。
    3. Tx Fifo分配也要按顺序,中间有没有使用的端点也要分配最小16个words的空间。

    技术交流

    由于百度网盘会更新,可以进群看QQ公告.所以资料都会在同一个地址分享.
    群号:339420387
    在这里插入图片描述

    展开全文
  • STM32 HAL库实现USB组合设备之多路CDC实现 目录STM32 HAL库实现USB组合设备之多路CDC实现

    STM32 HAL库实现USB组合设备之多路CDC实现


    本博客完整代码下载地址:https://download.csdn.net/download/qq153471503/18169362

    或关注以下公众号,回复关键字MultiCDC获取下载链接!
    在这里插入图片描述



    1、本篇文章已经默认您已经会使用STM32CUBEMX生成CDC工程并测试通过了,如果你还不会可参考我的另一篇博客:STM32快速实现USB虚拟串口+回环测试+USB转TTL的功能

    2、USB组合设备的编写需要具备一定的USB相关知识,如果你不了解,那么请先看一下我的这篇博客:STM32 USB相关知识扫盲


    工程环境:

    • STM32F103RC
    • STM32CubeIDE 1.5.1

    第一步:基础工程生成

             ~~~~~~~~         首先先用STM32CUBEMX生成CDC工程,并测试通过没有问题后,就可以着手开始下一步的修改,如果你还不了解CDC虚拟串口,那么可以参考文章开头链接第一条说明里的博客内容。


    第二步:USB设备描述符的修改

             ~~~~~~~~         这一步很简单的,就是修改usbd_desc.c中的设备描述符数组USBD_FS_DeviceDesc,将设备类型改为组合设备类型:


    第三步:修改PMA端点分布

    然后进入usbd_conf.c文件中,找到USBD_LL_Init函数,修改PMA端点初始化:

    说一下PMA为什么这么改,目前我们用到的端点有:

    • 0X80、0X00为USB所必须的端点
    • 0X81、0X01为CDC1的输入输出端点
    • 0X82、0X02为CDC2的输入输出端点
    • 0X83为CDC1的命令控制端点(0X03端点未使用,但是占用空间)
    • 0X84为CDC2的命令控制端点(0X04端点未使用,但是占用空间)

    所以一共10个端点,10x8=80字节,十六进制为0X50,所以端点的缓存地址就是从PMA偏移0X50地址处开始。

    输入输出端点最大可配置为64字节的缓存,所以是自增0X40。

    命令端点的缓存数据大小是8字节缓存,自增16字节是因为光用到了输入端点,输出端点没用但是占用PMA空间,所以是自增0X10。

    如下图指示是8字节:

    接下修改端点初始化,找到USBD_CDC_Init函数,添加CDC2的端点初始化操作:

    static uint8_t  USBD_CDC_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
    {
      uint8_t ret = 0U;
      USBD_CDC_HandleTypeDef   *hcdc;
    
      if (pdev->dev_speed == USBD_SPEED_HIGH)
      {
        /* Open EP IN */
        USBD_LL_OpenEP(pdev, CDC_IN_EP, USBD_EP_TYPE_BULK,
                       CDC_DATA_HS_IN_PACKET_SIZE);
    
        pdev->ep_in[CDC_IN_EP & 0xFU].is_used = 1U;
    
        /* Open EP OUT */
        USBD_LL_OpenEP(pdev, CDC_OUT_EP, USBD_EP_TYPE_BULK,
                       CDC_DATA_HS_OUT_PACKET_SIZE);
    
        pdev->ep_out[CDC_OUT_EP & 0xFU].is_used = 1U;
    
      }
      else
      {
        /* Open EP IN */
        USBD_LL_OpenEP(pdev, CDC_IN_EP, USBD_EP_TYPE_BULK,
                       CDC_DATA_FS_IN_PACKET_SIZE);
    
        pdev->ep_in[CDC_IN_EP & 0xFU].is_used = 1U;
    
        /* Open EP OUT */
        USBD_LL_OpenEP(pdev, CDC_OUT_EP, USBD_EP_TYPE_BULK,
                       CDC_DATA_FS_OUT_PACKET_SIZE);
    
        pdev->ep_out[CDC_OUT_EP & 0xFU].is_used = 1U;
    
        / +++lakun 
        /* Open EP IN */
        USBD_LL_OpenEP(pdev, CDC2_IN_EP, USBD_EP_TYPE_BULK,
                       CDC_DATA_FS_IN_PACKET_SIZE);
    
        pdev->ep_in[CDC2_IN_EP & 0xFU].is_used = 1U;
    
        /* Open EP OUT */
        USBD_LL_OpenEP(pdev, CDC2_OUT_EP, USBD_EP_TYPE_BULK,
                       CDC_DATA_FS_OUT_PACKET_SIZE);
    
        pdev->ep_out[CDC2_OUT_EP & 0xFU].is_used = 1U;
      }
      /* Open Command IN EP */
      USBD_LL_OpenEP(pdev, CDC_CMD_EP, USBD_EP_TYPE_INTR, CDC_CMD_PACKET_SIZE);
      pdev->ep_in[CDC_CMD_EP & 0xFU].is_used = 1U;
    
      / +++lakun 
      /* Open Command IN EP */
      USBD_LL_OpenEP(pdev, CDC2_CMD_EP, USBD_EP_TYPE_INTR, CDC_CMD_PACKET_SIZE);
      pdev->ep_in[CDC2_CMD_EP & 0xFU].is_used = 1U;
    
      pdev->pClassData = USBD_malloc(sizeof(USBD_CDC_HandleTypeDef));
    
      if (pdev->pClassData == NULL)
      {
        ret = 1U;
      }
      else
      {
        hcdc = (USBD_CDC_HandleTypeDef *) pdev->pClassData;
    
        /* Init  physical Interface components */
        ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Init();
    
        /* Init Xfer states */
        hcdc->TxState = 0U;
        hcdc->RxState = 0U;
    
        if (pdev->dev_speed == USBD_SPEED_HIGH)
        {
          /* Prepare Out endpoint to receive next packet */
          USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer,
                                 CDC_DATA_HS_OUT_PACKET_SIZE);
        }
        else
        {
          /* Prepare Out endpoint to receive next packet */
          USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer,
                                 CDC_DATA_FS_OUT_PACKET_SIZE);
    
          / +++lakun 
          /* Prepare Out endpoint to receive next packet */
          USBD_LL_PrepareReceive(pdev, CDC2_OUT_EP, hcdc->RxBuffer,
                                 CDC_DATA_FS_OUT_PACKET_SIZE);
        }
      }
      return ret;
    }
    
    

    注:这一步骤的修改都非常挂关键,好多人组合设备修改出错的原因就是在修改PMA这里出的问题!在本文章起始处的蓝色链接《STM32 USB知识扫盲》文中有对PAM的详细讲解,一定要仔细看一下并理解!


    第四步:修改配置描述符

    贴一下我修改好的两路CDC配置,修改过的地方都有+++lakun样的标记:

    /* USB CDC device Configuration Descriptor */
    __ALIGN_BEGIN uint8_t USBD_CDC_CfgFSDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END =
    {
      /*Configuration Descriptor*/
      0x09,   /* bLength: Configuration Descriptor size */
      USB_DESC_TYPE_CONFIGURATION,      /* bDescriptorType: Configuration */
      USB_CDC_CONFIG_DESC_SIZ,                /* wTotalLength:no of returned bytes */
      0x00,
      0x04,   /* bNumInterfaces: 4 interface */  /* +++lakun:一个CDC用到了连个接口,两个CDC就是4个接口 */
      0x01,   /* bConfigurationValue: Configuration value */
      0x00,   /* iConfiguration: Index of string descriptor describing the configuration */
      0xC0,   /* bmAttributes: self powered */
      0x32,   /* MaxPower 0 mA */
    
      /*---------------------------------------------------------------------------*/
    
      //
      // +++lakun: IAD(Interface Association Descriptor)
      //
      0X08,  // bLength: Interface Descriptor size,固定值
      0X0B,  // bDescriptorType: IAD,固定值
      0X00,  // bFirstInterface,第一个接口的起始序号
      0X02,  // bInterfaceCount,本IAD下的接口数量
      0X02,  // bFunctionClass: CDC,表明该IAD是一个CDC类型的设备
      0X02,  // bFunctionSubClass:子类型,默认即可
      0X01,  // bFunctionProtocol:控制协议,默认即可
      0X02,  // iFunction
    
      /*Interface Descriptor */
      0x09,   /* bLength: Interface Descriptor size */
      USB_DESC_TYPE_INTERFACE,  /* bDescriptorType: Interface */
      /* Interface descriptor type */
      0x00,   /* bInterfaceNumber: Number of Interface */                   /* +++lakun:接口编号,从0开始 */
      0x00,   /* bAlternateSetting: Alternate setting */
      0x01,   /* bNumEndpoints: One endpoints used */
      0x02,   /* bInterfaceClass: Communication Interface Class */
      0x02,   /* bInterfaceSubClass: Abstract Control Model */
      0x01,   /* bInterfaceProtocol: Common AT commands */
      0x00,   /* iInterface: */
    
      /*Header Functional Descriptor*/
      0x05,   /* bLength: Endpoint Descriptor size */
      0x24,   /* bDescriptorType: CS_INTERFACE */
      0x00,   /* bDescriptorSubtype: Header Func Desc */
      0x10,   /* bcdCDC: spec release number */
      0x01,
    
      /*Call Management Functional Descriptor*/
      0x05,   /* bFunctionLength */
      0x24,   /* bDescriptorType: CS_INTERFACE */
      0x01,   /* bDescriptorSubtype: Call Management Func Desc */
      0x00,   /* bmCapabilities: D0+D1 */
      0x01,   /* bDataInterface: 1 */
    
      /*ACM Functional Descriptor*/
      0x04,   /* bFunctionLength */
      0x24,   /* bDescriptorType: CS_INTERFACE */
      0x02,   /* bDescriptorSubtype: Abstract Control Management desc */
      0x02,   /* bmCapabilities */
    
      /*Union Functional Descriptor*/
      0x05,   /* bFunctionLength */
      0x24,   /* bDescriptorType: CS_INTERFACE */
      0x06,   /* bDescriptorSubtype: Union func desc */
      0x00,   /* bMasterInterface: Communication class interface */                 /* +++lakun:这里指示的是本CDC的通信接口编号 */
      0x01,   /* bSlaveInterface0: Data Class Interface */                          /* +++lakun:这里指示的是本CDC的数据接口编号 */
    
      /*Endpoint 2 Descriptor*/
      0x07,                           /* bLength: Endpoint Descriptor size */
      USB_DESC_TYPE_ENDPOINT,   /* bDescriptorType: Endpoint */
      CDC_CMD_EP,                     /* bEndpointAddress */
      0x03,                           /* bmAttributes: Interrupt */
      LOBYTE(CDC_CMD_PACKET_SIZE),     /* wMaxPacketSize: */
      HIBYTE(CDC_CMD_PACKET_SIZE),
      CDC_FS_BINTERVAL,                           /* bInterval: */
      /*---------------------------------------------------------------------------*/
    
      /*Data class interface descriptor*/
      0x09,   /* bLength: Endpoint Descriptor size */
      USB_DESC_TYPE_INTERFACE,  /* bDescriptorType: */
      0x01,   /* bInterfaceNumber: Number of Interface */                          /* +++lakun:CDC1的数据接口编号 */
      0x00,   /* bAlternateSetting: Alternate setting */
      0x02,   /* bNumEndpoints: Two endpoints used */
      0x0A,   /* bInterfaceClass: CDC */
      0x00,   /* bInterfaceSubClass: */
      0x00,   /* bInterfaceProtocol: */
      0x00,   /* iInterface: */
    
      /*Endpoint OUT Descriptor*/
      0x07,   /* bLength: Endpoint Descriptor size */
      USB_DESC_TYPE_ENDPOINT,      /* bDescriptorType: Endpoint */
      CDC_OUT_EP,                        /* bEndpointAddress */
      0x02,                              /* bmAttributes: Bulk */
      LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),  /* wMaxPacketSize: */
      HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
      0x00,                              /* bInterval: ignore for Bulk transfer */
    
      /*Endpoint IN Descriptor*/
      0x07,   /* bLength: Endpoint Descriptor size */
      USB_DESC_TYPE_ENDPOINT,      /* bDescriptorType: Endpoint */
      CDC_IN_EP,                         /* bEndpointAddress */
      0x02,                              /* bmAttributes: Bulk */
      LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),  /* wMaxPacketSize: */
      HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
      0x00,                               /* bInterval: ignore for Bulk transfer */
    
    
      //
      // +++lakun: IAD(Interface Association Descriptor)
      //
      0X08,  // bLength: Interface Descriptor size,固定值
      0X0B,  // bDescriptorType: IAD,固定值
      0X02,  // bFirstInterface,第一个接口的起始序号(第0、1编号的接口用于CDC1,现在是第二个CDC了,所以从2开始)
      0X02,  // bInterfaceCount,本IAD下的接口数量
      0X02,  // bFunctionClass: CDC,表明该IAD是一个CDC类型的设备
      0X02,  // bFunctionSubClass:子类型,默认即可
      0X01,  // bFunctionProtocol:控制协议,默认即可
      0X02,  // iFunction
    
      /*Interface Descriptor */
      0x09,   /* bLength: Interface Descriptor size */
      USB_DESC_TYPE_INTERFACE,  /* bDescriptorType: Interface */
      /* Interface descriptor type */
      0x02,   /* bInterfaceNumber: Number of Interface */ /* +++lakun:这里就是第二个CDC了,第0、1编号的接口给CDC1使用了,所以是2开始的 */
      0x00,   /* bAlternateSetting: Alternate setting */
      0x01,   /* bNumEndpoints: One endpoints used */
      0x02,   /* bInterfaceClass: Communication Interface Class */
      0x02,   /* bInterfaceSubClass: Abstract Control Model */
      0x01,   /* bInterfaceProtocol: Common AT commands */
      0x00,   /* iInterface: */
    
      /*Header Functional Descriptor*/
      0x05,   /* bLength: Endpoint Descriptor size */
      0x24,   /* bDescriptorType: CS_INTERFACE */
      0x00,   /* bDescriptorSubtype: Header Func Desc */
      0x10,   /* bcdCDC: spec release number */
      0x01,
    
      /*Call Management Functional Descriptor*/
      0x05,   /* bFunctionLength */
      0x24,   /* bDescriptorType: CS_INTERFACE */
      0x01,   /* bDescriptorSubtype: Call Management Func Desc */
      0x00,   /* bmCapabilities: D0+D1 */
      0x01,   /* bDataInterface: 1 */
    
      /*ACM Functional Descriptor*/
      0x04,   /* bFunctionLength */
      0x24,   /* bDescriptorType: CS_INTERFACE */
      0x02,   /* bDescriptorSubtype: Abstract Control Management desc */
      0x02,   /* bmCapabilities */
    
      /*Union Functional Descriptor*/
      0x05,   /* bFunctionLength */
      0x24,   /* bDescriptorType: CS_INTERFACE */
      0x06,   /* bDescriptorSubtype: Union func desc */
      0x02,   /* bMasterInterface: Communication class interface */                    /* +++lakun:这里指示的是本CDC的通信接口编号 */
      0x03,   /* bSlaveInterface0: Data Class Interface */                             /* +++lakun:这里指示的是本CDC的数据接口编号 */
    
      /*Endpoint 2 Descriptor*/
      0x07,                           /* bLength: Endpoint Descriptor size */
      USB_DESC_TYPE_ENDPOINT,   /* bDescriptorType: Endpoint */
      CDC2_CMD_EP,                     /* bEndpointAddress */
      0x03,                           /* bmAttributes: Interrupt */
      LOBYTE(CDC_CMD_PACKET_SIZE),     /* wMaxPacketSize: */
      HIBYTE(CDC_CMD_PACKET_SIZE),
      CDC_FS_BINTERVAL,                           /* bInterval: */
      /*---------------------------------------------------------------------------*/
    
      /*Data class interface descriptor*/
      0x09,   /* bLength: Endpoint Descriptor size */
      USB_DESC_TYPE_INTERFACE,  /* bDescriptorType: */
      0x03,   /* bInterfaceNumber: Number of Interface */ /* +++lakun:CDC2的数据接口编号 */
      0x00,   /* bAlternateSetting: Alternate setting */
      0x02,   /* bNumEndpoints: Two endpoints used */
      0x0A,   /* bInterfaceClass: CDC */
      0x00,   /* bInterfaceSubClass: */
      0x00,   /* bInterfaceProtocol: */
      0x00,   /* iInterface: */
    
      /*Endpoint OUT Descriptor*/
      0x07,   /* bLength: Endpoint Descriptor size */
      USB_DESC_TYPE_ENDPOINT,      /* bDescriptorType: Endpoint */
      CDC2_OUT_EP,                        /* bEndpointAddress */
      0x02,                              /* bmAttributes: Bulk */
      LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),  /* wMaxPacketSize: */
      HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
      0x00,                              /* bInterval: ignore for Bulk transfer */
    
      /*Endpoint IN Descriptor*/
      0x07,   /* bLength: Endpoint Descriptor size */
      USB_DESC_TYPE_ENDPOINT,      /* bDescriptorType: Endpoint */
      CDC2_IN_EP,                         /* bEndpointAddress */
      0x02,                              /* bmAttributes: Bulk */
      LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),  /* wMaxPacketSize: */
      HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
      0x00,                               /* bInterval: ignore for Bulk transfer */
    
    } ;
    
    

    第五步:修改函数接口

    默认的HAL库函数是只针对一路CDC的情况,所以我们需要修改成多路CDC操作函数,将端口参数传递出来,一共有下面几个函数:

    • USBD_CDC_DataOut:USB接收函数回调,修改提供端口参数
    • CDC_Receive_FS(uint8_t *Buf, uint32_t *Len):USB CDC接收函数
    • CDC_Transmit_FS(uint8_t* Buf, uint16_t Len):USB CDC发送函数
    • USBD_CDC_TransmitPacket(USBD_HandleTypeDef *pdev)
    • USBD_CDC_ReceivePacket(USBD_HandleTypeDef *pdev)

    这几个函数的调用关系为:

    CDC_Transmit_FS->
         ~~~~     USBD_CDC_TransmitPacket->
             ~~~~~~~~         USBD_CDC_DataOut->
                 ~~~~~~~~~~~~             CDC_Receive_FS

    USBD_CDC_DataOut函数有一个参数是epnum,这个是端点号,我们就可以使用该参数来区分是哪一路CDC!

    所以USBD_CDC_DataOut函数添加上epnum参数,改为:

    USBD_CDC_TransmitPacket函数改为:

    USBD_CDC_ReceivePacket函数改为:

    CDC_Transmit_FS函数改为:

    CDC_Receive_FS函数改为:

    到此就已经修改完毕了,将USB接入电脑后,设备管理区会多出现一个USB复合设备和两个端口,这样就是成功了:

    下面进行测试,使用串口助手同时对两路CDC进行收发测试效果如下:

    互不影响,USB两路CDC成功!


    注意事项及问题

    如果你移植的时候将USB接入电脑后,设备管理器里面只会出现1路串口,那么可能是因为你先用的CDC单独的工程测试的,电脑已经默认枚举为了VCP,那么此时有两种办法解决:

    1. 设备管理器卸载掉出现的虚拟串口,重新拔插USB
    2. 修改程序里的USBD_VID,换个其他编号

    我是用第二个办法解决的!


    补充:3路CDC实现

    实现3路CDC修改步骤跟实现2路一样,首先是PMA端点的配置:

    为什么要把CDC_DATA_FS_MAX_PACKET_SIZE改成32呢?

    现在我们算一下使用到的端点:

    • 0X80、0X00用于USB必须的
    • 0X81、0X01用于CDC1输入输出端点
    • 0X82、0X02用于CDC2输入输出端点
    • 0X83、0X03用于CDC3输入输出端点
    • 0X84用于CDC1命令控制端点、0X04未用但占用空间
    • 0X85用于CDC2命令控制端点、0X05未用但占用空间
    • 0X86用于CDC3命令控制端点、0X06未用但占用空间

    可以看到我们用到的端点很多,光算输入输出端点一共8个,每个端点最大占用64,那么就是512字节,而PMA一共才512字节,所以如果将端点缓冲设置为最大64字节的话,3路CDC串口缓存空间不够用!不改成32的话你会发现第1和2路串口收发没问题,而第三路串口返回的都是0!

    如下图:

    改成32后,效果如下:

    三路虚拟串口收发数据互不影响,到这里就成功了!


    ends…

    展开全文
  • 自制游戏手柄ps:开发环境初始配置...stm32f103rct6 stm32cubemx keil joystick模块(5针)两轴 初始配置 开启时钟 USB时钟 一定 48M 打开USB 配置HID 配置完成 代码编写 打开usbd_hid.c 应为stm32cbemx 自动生成

    ps:

    耗费了一天去了解怎么实现usb协议下的HID传输,因为各个文章没有说清楚,导致我数据的内容总时接收不到或者错位,所以特意写一篇文章来具体指出了各个步骤的坑

    开发环境

    windows
    stm32f103rct6
    stm32cubemx
    keil
    joystick模块(5针)两轴

    初始配置

    开启时钟
    在这里插入图片描述
    USB时钟 一定 48M
    在这里插入图片描述

    在这里插入图片描述
    打开USB
    在这里插入图片描述
    配置HID
    在这里插入图片描述
    配置完成

    代码编写

    打开usbd_hid.c
    在这里插入图片描述
    应为stm32cbemx 自动生成的HID是鼠标,所以需要更该报告描述符
    如下

    __ALIGN_BEGIN static uint8_t HID_MOUSE_ReportDesc[HID_MOUSE_REPORT_DESC_SIZE]  __ALIGN_END =
    {
        0x05,   0x01,
        0x09,   0x04,//joystick
        0xA1,   0x00,
    
        0x05,   0x01,
        0x09,   0x30,//x
        0x09,   0x31,//y
        0x09,   0x32,//z
        0x09,   0x33,//rx  旋转x轴
        0x15,   0x00,//最小数据输入 0
        0x25,   0x7F,//最大数据输入  127
        0x75,   0x08,//数据字节 8
        0x95,   0x04,//一次的数据个数 4个  即 x y z rx
        0x81,   0x02,
        0xc0//终止
    };
    

    找到 ,并修改

     0x00,         /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
    

    找到如下内容

    HID_MOUSE_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor*/
    

    更换成
    因为上面的报告符数 为27个,可自己添加然后更改这个数字
    应该有4个位置

      27,//HID_MOUSE_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor*/
    

    以下为main.c中内容
    加入头文件,以及相关句柄

    #include "usbd_hid.h"
    extern USBD_HandleTypeDef hUsbDeviceFS;
    

    创建相关结构体
    一定注意顺序,这里的顺序要和报告符中的描述一致

    struct mouseHID_t {
    	 uint8_t x;
          uint8_t y;
          uint8_t buttons;
         
          int8_t wheel;
      }; 
     
     struct mouseHID_t mouseHID;
    

    读取adc 并转化为 0到20的范围

    uint16_t AD_Value_Buf[2];
    uint16_t AD_X_Value = 0;
    uint16_t AD_Y_Value = 0;
    int16_t AD_Value_map(int16_t x, int16_t in_min, int16_t in_max, int16_t out_min, int16_t out_max)
    {
        return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
    } 
    
    void USB_send(void)
    {
    	ADC_ChannelConfTypeDef sConfig;		//通道初始化
    
    	
    for(uint8_t i=0;i<2;i++)
    		{	    switch(i)							//选择ADC通道
    				{
    					case 0:sConfig.Channel = ADC_CHANNEL_0;break;	
    					case 1:sConfig.Channel = ADC_CHANNEL_1;break;
    	      }
    					sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;		//采用周期239.5周期
    					sConfig.Rank = 1;
    					HAL_ADC_ConfigChannel(&hadc1,&sConfig);	
    					if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
    						{
    							Error_Handler();
    						}		
    					HAL_ADC_Start(&hadc1);
    					if(HAL_OK==HAL_ADC_PollForConversion(&hadc1,10))
    							{
    								AD_Value_Buf[i] = HAL_ADC_GetValue(&hadc1);
    							//	USE_MAIN_INFO("AD_Value_Buf[%d] %d",i,AD_Value_Buf[i]);
    							}  
    								HAL_ADC_Stop(&hadc1);
    		}
    
       
    		 AD_X_Value = AD_Value_Buf[0];
        AD_Y_Value = AD_Value_Buf[1];
    
        mouseHID.x =(uint8_t) AD_Value_map(AD_X_Value,0,4095,-20,20);//从0-4095映射到-20~20
        mouseHID.y = (uint8_t)AD_Value_map(AD_Y_Value,0,4095,20,-20);//从0-4095映射到20~-20
    		//USE_MAIN_INFO("mouseHID.x  %d\r\n",mouseHID.x);
    		//USE_MAIN_INFO("mouseHID.y  %d", mouseHID.y );
        USBD_HID_SendReport(&hUsbDeviceFS, (uint8_t*)&mouseHID, sizeof(struct mouseHID_t));
    
    }
    

    在main函数里面初始化

    
     mouseHID.buttons =0;
      mouseHID.x =0;
      mouseHID.y = 0;
      mouseHID.wheel = 0;
    

    各功能初始化后进行adc校准
    然后进行循环采集

     /* USER CODE BEGIN 2 */
    HAL_Delay(100);
    HAL_ADCEx_Calibration_Start(&hadc1);
      /* USER CODE END 2 */
      while (1)
      { 	
    		USB_send();
    		HAL_Delay(10);
        /* USER CODE END WHILE */
    
        /* USER CODE BEGIN 3 */
      }
    

    此时通过stm32的usb口(非usart)连接电脑,
    打开电脑上的游戏管理器,可以看见stm32 human interface
    打开校准,控制手柄,应该可以看见光标移动则说明成功

    其他功能可以看usb协议(我也不会)来实现

    推荐工具:

    usb bound
    HID Descriptor tool

    参考文章:

    Stm32-usb手柄(一)`
    STM32下实现joystick
    利用stm32cubemx生成usb_hid工程

    date:2021.4.26
    author:_zs_dawn

    展开全文
  • 两年前入门ROS用了RIKI的底盘套件,后来19年自己搭建底盘做比赛和项目,因为懒用来HAL,然后参考了CSDN一些其他大佬的移植使用,后来发现不稳定,效果一般。...平台TX2+ubuntu 18.04LTS+ROS Melodic STM32F103RC..
  • [fondar]STM32 USB HAL 库自定义类使用

    千次阅读 2018-02-09 16:50:17
    fondar 再调试STM32F407 HS高速 USB 中途遇到接受数据不准的问题,总是上位机发送第二次才能接受到数据,而且缺少一帧512字节的数据后来发现是程序问题Linux USB驱动 发送 接收搞定typedef struct _Device_cb{ ...
  • STM32F7xx的HAL库实现的USB通信中,里面存在着多个库文件和函数的调用,这一章节主要对USB接收数据的函数调用流程来进行分析,USB的数据发送部分相对来说比较容易分析。 在usb通信中,STM32F7xx作为从设备,当USB...
  • STM32 USB 系列之大容量设备MSC建立STM32Cube工程FLASH芯片驱动FATFS移植USB大容量设备移植附件 这次的项目使用STM32的大容量设备进行文件传输,通过STM32USB大容量设备将电脑中的文件放到外部FALSH中,然后在程序...
  • 2018.1.19 HAL库详解见STM32HAL库详解 及 手动移植 STM32 Embedded Software   工作以来一直使用ST的STM32系列芯片,ST为开发者提供了非常方便的开发库。到目前为止,有标准外设库(STD库)、HAL库、LL库 三...
  • STM32 USB IAP

    千次阅读 2017-06-01 17:25:11
    使用STM32 USB IAP使用stm32usb做IAP升级功能,利用stm32cubemx生成代码。参考http://www.51hei.com/bbs/dpj-40235-1.html http://www.51hei.com/bbs/dpj-40938-1.html http://www.stm32cube.com/question/500...
  • 解决STM32 HAL USB VBUS当做普通IO口异常问题 在新更新的硬件板上将VBUS当做普通IO口使用,在测试时发现VBUS控制的LED异常闪烁,怀疑为VBUS功能没有完全关闭导致。查阅相关资料STM32 USBD VBUS PA9 as GPIO 解决方案...
  • 最近弄了串口IAP升级,始终感觉升级很麻烦, 需要超级终端或者其他支持ymodem的软件, 但是为用户考虑, 如果升级最简单的方式就是把设备模拟为一个U盘来...我想过这样的方法, 也去试过,但是发现stm32的扇区大小不一样, 每.
  • 标题在Linux上测试接收stm32发过来的消息 查看USB设备 dmesg | grep tty 提高串口权限 sudo chmod 777 /dev/ttyUSB0 linux上要先安装rosserial库,如果有就忽略这一步 sudo apt-get install ros-kinetic-...
  • STM32 USB HID设置(STM32CubeMX)

    万次阅读 2018-09-27 13:28:28
    STM32F070F6P USB HID设置 1、打开STM32CubeMX软件,选择“NEW”新建一个工程 2、选择芯片型号STM32F070F6P 3、在Swap PA9/10-&gt;PA11/12前打勾 4、在“USB”下的“Device(FS)”前打勾 5、设置晶振,...
  • 很多stm32的开发版都带有一个usb接口,可以用cubeMx将其配置成一个虚拟串口,使用USB线就可以打印调试信息,省去了用TTL转USB的步骤,而且USB虚拟串口的速度和稳定性比TTL转USB要好,在开发中...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,517
精华内容 1,006
关键字:

halstm32usb