首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

STM32 适配 WinUSB

  • 24-03-18 04:23
  • 3964
  • 5409
blog.csdn.net

基本上来说,STM32 在CubeMX生成的不同class的设备,都是支持windows免驱的,唯独在DFU模式的情况下,需要手动安装st的驱动才能实现功能,那么有什么办法能够在DFU模式下免驱呢,答案就是WinUSB。

废话不多说,我们用最简单明了的方式来实现此功能,上代码!

目前我们选用的都是Microsoft OS 2.0 描述符规范,因为1.0的描述符规范已经逐渐被微软抛弃了,在这里都没有什么存在的意义,1.0是通过请求0xEE的描述符来进行识别,到2.0是通过BOS的请求来获取完整的内容。

通过CubeMX生成基础程序

我这边选择的芯片是STM32F103,当然,其他有USB功能的STM芯片都是适用于这个功能的,我想既然打算开发WinUSB,这部分的内容读者都应该轻车熟路,所以我在这里省略,直接进入正题

修改代码

1.修改设备描述符

一般来说F4的设备需要使能USBD_LPM_ENABLED这个功能,最重要的一点,需要把bcdUSB的版本改为0x0210,这样windows才会试图请求BOS描述符,如下所示:

  1. __ALIGN_BEGIN uint8_t USBD_FS_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END =
  2. {
  3. 0x12, /*bLength */
  4. USB_DESC_TYPE_DEVICE, /*bDescriptorType*/
  5. 0x10, /*bcdUSB */ 修改此值
  6. 0x02,
  7. 0x00, /*bDeviceClass*/
  8. 0x00, /*bDeviceSubClass*/
  9. 0x00, /*bDeviceProtocol*/
  10. USB_MAX_EP0_SIZE, /*bMaxPacketSize*/
  11. LOBYTE(USBD_VID), /*idVendor*/
  12. HIBYTE(USBD_VID), /*idVendor*/
  13. LOBYTE(USBD_PID_FS), /*idProduct*/
  14. HIBYTE(USBD_PID_FS), /*idProduct*/
  15. 0x00, /*bcdDevice rel. 2.00*/
  16. 0x02,
  17. USBD_IDX_MFC_STR, /*Index of manufacturer string*/
  18. USBD_IDX_PRODUCT_STR, /*Index of product string*/
  19. USBD_IDX_SERIAL_STR, /*Index of serial number string*/
  20. USBD_MAX_NUM_CONFIGURATION /*bNumConfigurations*/
  21. };

2.修改二进制描述符

将程序自动生成的二进制描述符改为如下所示,其中USB_REQ_GET_OS_FEATURE_DESCRIPTOR为请求的VendorCode,我们在下面会有提及

  1. __ALIGN_BEGIN uint8_t USBD_FS_BOSDesc[33] __ALIGN_END =
  2. {
  3. ///
  4. /// WCID20 BOS descriptor
  5. ///
  6. 0x05, /* bLength */
  7. USB_DESC_TYPE_BOS, /* bDescriptorType */
  8. 0x21, 0x00, /* wTotalLength */
  9. 0x01, /* bNumDeviceCaps */
  10. ///
  11. /// WCID20 device capability descriptor
  12. ///
  13. 0x1c, /* bLength */
  14. 0x10, /* bDescriptorType */
  15. 0x05, /* bDevCapabilityType */
  16. 0x00, /* bReserved */
  17. 0xdf, 0x60, 0xdd, 0xd8, 0x89, 0x45, 0xc7, 0x4c, /* bPlatformCapabilityUUID_16 */
  18. 0x9c, 0xd2, 0x65, 0x9d, 0x9e, 0x64, 0x8a, 0x9f, /* bPlatformCapabilityUUID_16 */
  19. 0x00, 0x00, 0x03, 0x06, /* dwWindowsVersion */
  20. LOBYTE(WINUSB20_WCID_DESC_SET_SIZE), HIBYTE(WINUSB20_WCID_DESC_SET_SIZE),/* wDescriptorSetTotalLength */
  21. USB_REQ_GET_OS_FEATURE_DESCRIPTOR, /* bVendorCode */
  22. 0x00,
  23. };

3.增加WCID描述符

这是基础的单配置的最简单的WCID描述符,用于识别设备可用的window版本等信息

  1. __ALIGN_BEGIN const uint8_t WINUSB20_WCIDDescriptorSet[WINUSB20_WCID_DESC_SET_SIZE] __ALIGN_END = {
  2. ///
  3. /// WCID20 descriptor set descriptor
  4. ///
  5. 0x0a, 0x00, /* wLength */
  6. 0x00, 0x00, /* wDescriptorType */
  7. 0x00, 0x00, 0x03, 0x06, /* dwWindowsVersion */
  8. 0xa2, 0x00, /* wDescriptorSetTotalLength */
  9. ///
  10. /// WCID20 compatible ID descriptor
  11. ///
  12. 0x14, 0x00, /* wLength */
  13. 0x03, 0x00, /* wDescriptorType */
  14. /* WINUSB */
  15. 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, /* cCID_8 */
  16. /* */
  17. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* cSubCID_8 */
  18. ///
  19. /// WCID20 registry property descriptor
  20. ///
  21. 0x84, 0x00, /* wLength */
  22. 0x04, 0x00, /* wDescriptorType */
  23. 0x07, 0x00, /* wPropertyDataType */
  24. 0x2a, 0x00, /* wPropertyNameLength */
  25. /* DeviceInterfaceGUIDs */
  26. 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, /* wcPropertyName_21 */
  27. 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, /* wcPropertyName_21 */
  28. 't', 0x00, 'e', 0x00, 'r', 0x00, 'f', 0x00, /* wcPropertyName_21 */
  29. 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, /* wcPropertyName_21 */
  30. 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, /* wcPropertyName_21 */
  31. 0x00, 0x00, /* wcPropertyName_21 */
  32. 0x50, 0x00, /* wPropertyDataLength */
  33. /* {36FC9E60-C465-11CF-8056-444553540000} */
  34. '{', 0x00, '3', 0x00, '6', 0x00, 'F', 0x00, /* wcPropertyData_40 */
  35. 'C', 0x00, '9', 0x00, 'E', 0x00, '6', 0x00, /* wcPropertyData_40 */
  36. '0', 0x00, '-', 0x00, 'C', 0x00, '4', 0x00, /* wcPropertyData_40 */
  37. '6', 0x00, '5', 0x00, '-', 0x00, '1', 0x00, /* wcPropertyData_40 */
  38. '1', 0x00, 'C', 0x00, 'F', 0x00, '-', 0x00, /* wcPropertyData_40 */
  39. '8', 0x00, '0', 0x00, '5', 0x00, '6', 0x00, /* wcPropertyData_40 */
  40. '-', 0x00, '4', 0x00, '4', 0x00, '4', 0x00, /* wcPropertyData_40 */
  41. '5', 0x00, '5', 0x00, '3', 0x00, '5', 0x00, /* wcPropertyData_40 */
  42. '4', 0x00, '0', 0x00, '0', 0x00, '0', 0x00, /* wcPropertyData_40 */
  43. '0', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00 /* wcPropertyData_40 */
  44. };

以上都是在usbd_desc.h中添加的数据,下接下来还有2个文件需要修改

4.增加描述符的获取接口

打开usbd_def.h

先增加两个宏定义

  1. #define USB_REQ_GET_STATUS 0x00U
  2. #define USB_REQ_CLEAR_FEATURE 0x01U
  3. #define USB_REQ_SET_FEATURE 0x03U
  4. #define USB_REQ_SET_ADDRESS 0x05U
  5. #define USB_REQ_GET_DESCRIPTOR 0x06U
  6. #define USB_REQ_SET_DESCRIPTOR 0x07U
  7. #define USB_REQ_GET_CONFIGURATION 0x08U
  8. #define USB_REQ_SET_CONFIGURATION 0x09U
  9. #define USB_REQ_GET_INTERFACE 0x0AU
  10. #define USB_REQ_SET_INTERFACE 0x0BU
  11. #define USB_REQ_SYNCH_FRAME 0x0CU
  12. #define USB_REQ_GET_OS_FEATURE_DESCRIPTOR 0x20U //新增的
#define  MS_OS_20_DESCRIPTOR_INDEX                      0x07U //新增的

再增加两个接口的函数指针

  1. typedef struct
  2. {
  3. uint8_t *(*GetDeviceDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length);
  4. uint8_t *(*GetLangIDStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length);
  5. uint8_t *(*GetManufacturerStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length);
  6. uint8_t *(*GetProductStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length);
  7. uint8_t *(*GetSerialStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length);
  8. uint8_t *(*GetConfigurationStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length);
  9. uint8_t *(*GetInterfaceStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length);
  10. #if (USBD_LPM_ENABLED == 1U)
  11. uint8_t *(*GetBOSDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length);
  12. #if (USBD_WINUSB_ENABLED == 1U)
  13. uint8_t *(*GetWCIDDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length); //新增的
  14. #endif
  15. #endif
  16. } USBD_DescriptorsTypeDef;

5.实现描述符的获取函数

首先在usbd_desc.c中声明函数

  1. uint8_t * USBD_FS_DeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
  2. uint8_t * USBD_FS_LangIDStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
  3. uint8_t * USBD_FS_ManufacturerStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
  4. uint8_t * USBD_FS_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
  5. uint8_t * USBD_FS_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
  6. uint8_t * USBD_FS_ConfigStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
  7. uint8_t * USBD_FS_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
  8. #if (USBD_LPM_ENABLED == 1U)
  9. uint8_t * USBD_FS_BOSDescriptor(USBD_SpeedTypeDef speed, uint16_t *length); //新增
  10. #if (USBD_WINUSB_ENABLED == 1U)
  11. uint8_t * USBD_FS_WCIDDescriptor(USBD_SpeedTypeDef speed, uint16_t *length); //新增
  12. #endif
  13. #endif

在结构体中增加函数调用

  1. USBD_DescriptorsTypeDef FS_Desc =
  2. {
  3. USBD_FS_DeviceDescriptor
  4. , USBD_FS_LangIDStrDescriptor
  5. , USBD_FS_ManufacturerStrDescriptor
  6. , USBD_FS_ProductStrDescriptor
  7. , USBD_FS_SerialStrDescriptor
  8. , USBD_FS_ConfigStrDescriptor
  9. , USBD_FS_InterfaceStrDescriptor
  10. #if (USBD_LPM_ENABLED == 1U)
  11. , USBD_FS_BOSDescriptor //新增
  12. #if (USBD_WINUSB_ENABLED == 1U)
  13. , USBD_FS_WCIDDescriptor //新增
  14. #endif
  15. #endif
  16. };

最后,增加函数实现

  1. #if (USBD_LPM_ENABLED == 1U)
  2. uint8_t * USBD_FS_BOSDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
  3. {
  4. UNUSED(speed);
  5. *length = sizeof(USBD_FS_BOSDesc);
  6. return (uint8_t*)USBD_FS_BOSDesc;
  7. }
  8. #if (USBD_WINUSB_ENABLED == 1U)
  9. uint8_t * USBD_FS_WCIDDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
  10. {
  11. UNUSED(speed);
  12. *length = sizeof(WINUSB20_WCIDDescriptorSet);
  13. return (uint8_t*)WINUSB20_WCIDDescriptorSet;
  14. }
  15. #endif
  16. #endif

6.增加Vendor接口的实现

打开usbd_ctlreq.c文件,在文件中新增函数声明

  1. static void USBD_GetDescriptor(USBD_HandleTypeDef *pdev,
  2. USBD_SetupReqTypedef *req);
  3. static void USBD_SetAddress(USBD_HandleTypeDef *pdev,
  4. USBD_SetupReqTypedef *req);
  5. static void USBD_SetConfig(USBD_HandleTypeDef *pdev,
  6. USBD_SetupReqTypedef *req);
  7. static void USBD_GetConfig(USBD_HandleTypeDef *pdev,
  8. USBD_SetupReqTypedef *req);
  9. static void USBD_GetStatus(USBD_HandleTypeDef *pdev,
  10. USBD_SetupReqTypedef *req);
  11. static void USBD_SetFeature(USBD_HandleTypeDef *pdev,
  12. USBD_SetupReqTypedef *req);
  13. static void USBD_ClrFeature(USBD_HandleTypeDef *pdev,
  14. USBD_SetupReqTypedef *req);
  15. #if (USBD_LPM_ENABLED == 1)
  16. static void USBD_GetVendor(USBD_HandleTypeDef *pdev,
  17. USBD_SetupReqTypedef *req); //新增的
  18. #endif
  19. static uint8_t USBD_GetLen(uint8_t *buf);

在文件中新增函数实现

  1. #if (USBD_LPM_ENABLED == 1)
  2. static void USBD_GetVendor(USBD_HandleTypeDef *pdev,
  3. USBD_SetupReqTypedef *req)
  4. {
  5. uint16_t len = 0U;
  6. uint8_t *pbuf = NULL;
  7. switch (req->wIndex)
  8. {
  9. case MS_OS_20_DESCRIPTOR_INDEX: //MS OS 2.0 的7号请求
  10. if (pdev->pDesc->GetWCIDDescriptor != NULL)
  11. {
  12. pbuf = pdev->pDesc->GetWCIDDescriptor(pdev->dev_speed, &len);
  13. }
  14. else
  15. {
  16. USBD_CtlError(pdev, req);
  17. }
  18. break;
  19. }
  20. if((len != 0)&& (req->wLength != 0))
  21. {
  22. len = MIN(len , req->wLength);
  23. USBD_CtlSendData (pdev,
  24. pbuf,
  25. len);
  26. }
  27. }
  28. #endif

最后,修改USBD_StdDevReq函数的前部分为

  1. USBD_StatusTypeDef USBD_StdDevReq(USBD_HandleTypeDef *pdev,
  2. USBD_SetupReqTypedef *req)
  3. {
  4. USBD_StatusTypeDef ret = USBD_OK;
  5. switch (req->bmRequest & USB_REQ_TYPE_MASK)
  6. {
  7. case USB_REQ_TYPE_VENDOR:
  8. #if (USBD_LPM_ENABLED == 1)
  9. USBD_GetVendor(pdev, req);
  10. break;
  11. #endif
  12. case USB_REQ_TYPE_CLASS:
  13. pdev->pClass->Setup(pdev, req);
  14. break;

修改USBD_StdItfReq函数的前部分为

  1. USBD_StatusTypeDef USBD_StdItfReq(USBD_HandleTypeDef *pdev,
  2. USBD_SetupReqTypedef *req)
  3. {
  4. USBD_StatusTypeDef ret = USBD_OK;
  5. switch (req->bmRequest & USB_REQ_TYPE_MASK)
  6. {
  7. case USB_REQ_TYPE_VENDOR:
  8. #if (USBD_LPM_ENABLED == 1)
  9. USBD_GetVendor(pdev, req);
  10. break;
  11. #endif
  12. case USB_REQ_TYPE_CLASS:
  13. case USB_REQ_TYPE_STANDARD:

修改USBD_StdEPReq的前部分为

  1. USBD_StatusTypeDef USBD_StdEPReq(USBD_HandleTypeDef *pdev,
  2. USBD_SetupReqTypedef *req)
  3. {
  4. USBD_EndpointTypeDef *pep;
  5. uint8_t ep_addr;
  6. USBD_StatusTypeDef ret = USBD_OK;
  7. ep_addr = LOBYTE(req->wIndex);
  8. switch (req->bmRequest & USB_REQ_TYPE_MASK)
  9. {
  10. case USB_REQ_TYPE_VENDOR:
  11. #if (USBD_LPM_ENABLED == 1)
  12. USBD_GetVendor(pdev, req);
  13. break;
  14. #endif
  15. case USB_REQ_TYPE_CLASS:
  16. pdev->pClass->Setup(pdev, req);
  17. break;

这个目的是获取vendor的支持

最后

到此,程序应该可以被识别成winUSB设备了,如图所示

网上有很多教程混淆了1.0和2.0的概念,导致大家可能遇到0xEE请求无法收到的情况,这主要是目前使用的2.0已经废除了这个读取的步骤,在此我作为记录,以后再有需求就不会忘记

参考链接

STM32F407实现USB BULK传输+WINUSB免驱

Microsoft OS 2.0 描述符规范

注:本文转载自blog.csdn.net的合理说明的文章"https://blog.csdn.net/gorgeousa/article/details/135370128"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

124
嵌入式
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top