admin 管理员组文章数量: 887021
记录一下使用stm32_f105-07_f2_f4_usb-host-device_lib固件库用STM32F407实现USB BULK传输+WINUSB免驱的过程,作为以后忘记后的一个参考记忆
en.stm32_f105-07_f2_f4_usb-host-device_lib.zip V221版本,可以到ST官网下载
STSW-STM32046 - STM32F105/7, STM32F2 and STM32F4 USB on-the-go Host and device library (UM1021) - STMicroelectronics
首先要建立一个STM32F407的Keil模板工程,能编译无任何错误,再继续后面的,这部分不详细说了,这是最基础的东西了,还不会的可以参考原子教程
这里使用的是USB固件库里的HID例子进行修改为BULK传输端点,再修改为WINUSB驱动支持
先看一下固件库里,建立HID要用到哪些文件,对应有哪些目录
1.STM32_USB_Device_Library 对应的是USB设备库,因为要做的是从机,所以使用到设备库
2.STM32_USB_OTG_Driver 对应的是USB的驱动库
1.在工程里建立一个文件夹名为USB_Device,把STM32_USB_Device_Library里的Core文件夹复制到USB_Device目录里,再把STM32_USB_Device_Library里的hid文件夹也复制到USB_Device目录里
2.在工程里建立一个文件夹名为USB_Driver,把STM32_USB_OTG_Driver\inc里的6个文件
usb_bsp.h
usb_core.h
usb_dcd.h
usb_dcd_int.h
usb_defines.h
usb_regs.h
复制到USB_Driver文件夹里,再把STM32_USB_OTG_Driver\src里的3个文件
usb_core.c
usb_dcd.c
usb_dcd_int.c
复制到USB_Driver文件夹里
3.在工程里建立一个文件夹名为USB_User,把stm32_f105-07_f2_f4_usb-host-device_lib\Project\USB_Device_Examples\HID\inc的3个文件
usb_conf.h
usbd_conf.h
usbd_desc.h
复制到USB_User文件夹里,把stm32_f105-07_f2_f4_usb-host-device_lib\Project\USB_Device_Examples\HID\src的3个文件
usb_bsp.c
usbd_desc.c
usbd_usr.c
复制到USB_User文件夹里,到此USB HID所要用到的文件添加完毕
然后在Keil的Manage Project Items里添加三个目录
USB Drive
USB Device
USB User
把C文件添加进去,添加完成后文件目录如图:
接着添加USB的头文件引用目录
添加宏USE_USB_OTG_FS
然后点编译,然后会报错,定位到usb_conf.h 屏蔽掉#error "Missing define: Evaluation board (ie. USE_STM322xG_EVAL)",因为我们不是用这个开发板
usb_conf.h文件里添加#include "stm32f4xx.h"头文件引用
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __USB_CONF__H__
#define __USB_CONF__H__
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx.h"
#if defined (USE_STM322xG_EVAL)
#include "stm322xg_eval.h"
#include "stm322xg_eval_lcd.h"
#include "stm322xg_eval_ioe.h"
#elif defined(USE_STM324xG_EVAL)
#include "stm32f4xx.h"
#include "stm324xg_eval.h"
#include "stm324xg_eval_lcd.h"
#include "stm324xg_eval_ioe.h"
#elif defined(USE_STM324x9I_EVAL)
#include "stm32f4xx.h"
#include "stm324x9i_eval.h"
#include "stm324x9i_eval_lcd.h"
#include "stm324x9i_eval_ioe16.h"
#elif defined (USE_STM3210C_EVAL)
#include "stm32f10x.h"
#include "stm3210c_eval.h"
#include "stm3210c_eval_lcd.h"
#include "stm3210c_eval_ioe.h"
#else
//#error "Missing define: Evaluation board (ie. USE_STM322xG_EVAL)"
#endif
usb_conf.h里的#define USB_OTG_FS_LOW_PWR_MGMT_SUPPOR和//#define VBUS_SENSING_ENABLED两项也要屏蔽掉,因为不使用低功耗和VBUS电源测量,只使用USB的D+和D-两个引脚
/****************** USB OTG FS CONFIGURATION **********************************/
#ifdef USB_OTG_FS_CORE
#define RX_FIFO_FS_SIZE 128
#define TX0_FIFO_FS_SIZE 64
#define TX1_FIFO_FS_SIZE 128
#define TX2_FIFO_FS_SIZE 0
#define TX3_FIFO_FS_SIZE 0
//#define USB_OTG_FS_LOW_PWR_MGMT_SUPPORT
/* #define USB_OTG_FS_SOF_OUTPUT_ENABLED */
#endif
/****************** USB OTG MISC CONFIGURATION ********************************/
//#define VBUS_SENSING_ENABLED
usb_bsp.c文件里的函数里要屏蔽掉代码如下
void USB_OTG_BSP_Init(USB_OTG_CORE_HANDLE * pdev)
{
#ifndef USE_STM3210C_EVAL
GPIO_InitTypeDef GPIO_InitStructure;
#endif
#if defined (USB_OTG_HS_LOW_PWR_MGMT_SUPPORT) || defined (USB_OTG_FS_LOW_PWR_MGMT_SUPPORT)
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
#endif
#ifdef USE_STM3210C_EVAL
RCC_OTGFSCLKConfig(RCC_OTGFSCLKSource_PLLVCO_Div3);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_OTG_FS, ENABLE);
#else /* USE_STM322xG_EVAL */
#ifdef USE_USB_OTG_FS
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
/* Configure SOF ID DM DP Pins */
GPIO_InitStructure.GPIO_Pin = /*GPIO_Pin_8 |*/ GPIO_Pin_11 | GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_OTG1_FS);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource11, GPIO_AF_OTG1_FS);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource12, GPIO_AF_OTG1_FS);
// /* Configure VBUS Pin */
// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
// GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
// GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
// GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
// GPIO_Init(GPIOA, &GPIO_InitStructure);
// /* Configure ID pin */
// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
// GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
// GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
// GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
// GPIO_Init(GPIOA, &GPIO_InitStructure);
// GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_OTG1_FS);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_OTG_FS, ENABLE);
#else /* USE_USB_OTG_HS */
#ifdef USE_ULPI_PHY /* ULPI */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB |
RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOH |
RCC_AHB1Periph_GPIOI, ENABLE);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_OTG2_HS); /* D0 */
GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_OTG2_HS); /* CLK */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource0, GPIO_AF_OTG2_HS); /* D1 */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource1, GPIO_AF_OTG2_HS); /* D2 */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_OTG2_HS); /* D7 */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_OTG2_HS); /* D3 */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_OTG2_HS); /* D4 */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource12, GPIO_AF_OTG2_HS); /* D5 */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_OTG2_HS); /* D6 */
GPIO_PinAFConfig(GPIOH, GPIO_PinSource4, GPIO_AF_OTG2_HS); /* NXT */
GPIO_PinAFConfig(GPIOI, GPIO_PinSource11, GPIO_AF_OTG2_HS); /* DIR */
GPIO_PinAFConfig(GPIOC, GPIO_PinSource0, GPIO_AF_OTG2_HS); /* STP */
/* CLK */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* D0 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* D1 D2 D3 D4 D5 D6 D7 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 |
GPIO_Pin_5 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* STP */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_Init(GPIOC, &GPIO_InitStructure);
/* NXT */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_Init(GPIOH, &GPIO_InitStructure);
/* DIR */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_Init(GPIOI, &GPIO_InitStructure);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_OTG_HS |
RCC_AHB1Periph_OTG_HS_ULPI, ENABLE);
#else
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource12, GPIO_AF_OTG2_FS);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_OTG2_FS);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_OTG2_FS);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* USB OTG HS ULPI clock Disabled */
RCC_AHB1PeriphClockLPModeCmd(RCC_AHB1Periph_OTG_HS_ULPI, DISABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_OTG_HS, ENABLE);
#ifdef USB_OTG_HS_LOW_PWR_MGMT_SUPPORT
/* clear bit OTGHSULPILPEN in register AHB1LPENR */
RCC_AHB1PeriphClockLPModeCmd(RCC_AHB1LPENR_OTGHSULPILPEN, DISABLE);
/* set only bit OTGHSLPEN in order to keep the clock for USB HS IP during
* SLEEP mode */
RCC_AHB1PeriphClockLPModeCmd(RCC_AHB1LPENR_OTGHSLPEN, ENABLE);
#endif
#endif /* USE_ULPI_PHY */
#endif /* USB_OTG_HS */
#endif /* USE_STM3210C_EVAL */
/* enable the PWR clock */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
/* Configure the Key button in EXTI mode */
//STM_EVAL_PBInit(Button_KEY, Mode_EXTI);
#ifdef USB_OTG_FS_LOW_PWR_MGMT_SUPPORT
EXTI_ClearITPendingBit(EXTI_Line18);
EXTI_InitStructure.EXTI_Line = EXTI_Line18;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
EXTI_ClearITPendingBit(EXTI_Line18);
NVIC_InitStructure.NVIC_IRQChannel = OTG_FS_WKUP_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 5;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
EXTI_ClearITPendingBit(EXTI_Line18);
#endif
#ifdef USB_OTG_HS_LOW_PWR_MGMT_SUPPORT
EXTI_ClearITPendingBit(EXTI_Line20);
EXTI_InitStructure.EXTI_Line = EXTI_Line20;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
EXTI_ClearITPendingBit(EXTI_Line20);
NVIC_InitStructure.NVIC_IRQChannel = OTG_HS_WKUP_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 5;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
EXTI_ClearITPendingBit(EXTI_Line20);
#endif
//EXTI_ClearITPendingBit(KEY_BUTTON_EXTI_LINE);
}
usbd_usr.h文件要屏蔽掉#include "lcd_log.h",因为没有用到
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __USBD_USR_H__
#define __USBD_USR_H__
/* Includes ------------------------------------------------------------------*/
#include "usbd_ioreq.h"
#if ! defined (USE_STM32446_EVAL) && ! defined (USE_STM32469I_EVAL)
//#include "lcd_log.h"
#endif
usbd_usr.c要屏蔽的代码如下
/**
* @brief USBD_USR_Init
* Displays the message on LCD for host lib initialization
* @param None
* @retval None
*/
void USBD_USR_Init(void)
{
/* Initialize LEDs */
// STM_EVAL_LEDInit(LED1);
// STM_EVAL_LEDInit(LED2);
// STM_EVAL_LEDInit(LED3);
// STM_EVAL_LEDInit(LED4);
/* Configure the IOE on which the JoyStick is connected */
#if defined(USE_STM324x9I_EVAL)
IOE16_Config();
#else
// IOE_Config();
#endif
/* Setup SysTick Timer for 1 msec interrupts This interrupt is used to probe
* the joystick */
if (SysTick_Config(SystemCoreClock / 1000))
{
/* Capture error */
while (1);
}
/* Initialize the LCD */
#if defined (USE_STM322xG_EVAL)
STM322xG_LCD_Init();
#elif defined(USE_STM324xG_EVAL)
STM324xG_LCD_Init();
#elif defined(USE_STM324x9I_EVAL)
LCD_Init();
LCD_LayerInit();
/* Enable The Display */
LCD_DisplayOn();
/* Connect the Output Buffer to LCD Background Layer */
LCD_SetLayer(LCD_FOREGROUND_LAYER);
/* Clear the Background Layer */
LCD_Clear(LCD_COLOR_WHITE);
#elif defined (USE_STM3210C_EVAL)
STM3210C_LCD_Init();
#else
//#error "Missing define: Evaluation board (ie. USE_STM322xG_EVAL)"
#endif
// LCD_LOG_Init();
#ifdef USE_USB_OTG_HS
#ifdef USE_EMBEDDED_PHY
LCD_LOG_SetHeader((uint8_t *) " USB OTG HS_IN_FS HID Device");
#else
LCD_LOG_SetHeader((uint8_t *) " USB OTG HS HID Device");
#endif
#else
// LCD_LOG_SetHeader((uint8_t *) " USB OTG FS HID Device");
#endif
// LCD_UsrLog("> USB device library started.\n");
//LCD_LOG_SetFooter((uint8_t *) " USB Device Library V1.2.1");
/* Information panel */
// LCD_SetTextColor(Green);
// LCD_DisplayStringLine(LCD_PIXEL_HEIGHT - 42, USER_INFORMATION1);
// LCD_DisplayStringLine(LCD_PIXEL_HEIGHT - 30, USER_INFORMATION2);
}
/**
* @brief USBD_USR_DeviceReset
* Displays the message on LCD on device Reset Event
* @param speed : device speed
* @retval None
*/
void USBD_USR_DeviceReset(uint8_t speed)
{
switch (speed)
{
case USB_OTG_SPEED_HIGH:
//LCD_LOG_SetFooter((uint8_t *) " USB Device Library V1.2.1 [HS]");
break;
case USB_OTG_SPEED_FULL:
//LCD_LOG_SetFooter((uint8_t *) " USB Device Library V1.2.1 [FS]");
break;
default:
//LCD_LOG_SetFooter((uint8_t *) " USB Device Library V1.2.1 [??]");
break;
}
}
/**
* @brief USBD_USR_DeviceConfigured
* Displays the message on LCD on device configuration Event
* @param None
* @retval Status
*/
void USBD_USR_DeviceConfigured(void)
{
//LCD_UsrLog("> HID Interface started.\n");
}
/**
* @brief USBD_USR_DeviceConnected
* Displays the message on LCD on device connection Event
* @param None
* @retval Status
*/
void USBD_USR_DeviceConnected(void)
{
//LCD_UsrLog("> USB Device Connected.\n");
}
/**
* @brief USBD_USR_DeviceDisonnected
* Displays the message on LCD on device disconnection Event
* @param None
* @retval Status
*/
void USBD_USR_DeviceDisconnected(void)
{
//LCD_UsrLog("> USB Device Disconnected.\n");
}
/**
* @brief USBD_USR_DeviceSuspended
* Displays the message on LCD on device suspend Event
* @param None
* @retval None
*/
void USBD_USR_DeviceSuspended(void)
{
// LCD_UsrLog("> USB Device in Suspend Mode.\n");
/* Users can do their application actions here for the USB-Reset */
}
/**
* @brief USBD_USR_DeviceResumed
* Displays the message on LCD on device resume Event
* @param None
* @retval None
*/
void USBD_USR_DeviceResumed(void)
{
//LCD_UsrLog("> USB Device in Idle Mode.\n");
/* Users can do their application actions here for the USB-Reset */
}
然后再点编译应该没有错误了,再有错误定位一下,屏蔽掉没用到相应开发板对应的东西直到编译没任何错误
接着在main.c文件里添加USB的头引用文件和开启USB
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx.h"
#include "usbd_hid_core.h"
#include "usbd_usr.h"
#include "usbd_desc.h"
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* USB_OTG_HS_INTERNAL_DMA_ENABLED */
__ALIGN_BEGIN USB_OTG_CORE_HANDLE USB_OTG_dev __ALIGN_END;
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/**
* @brief Main program
* @param None
* @retval None
*/
int main(void)
{
USBD_Init(&USB_OTG_dev,USB_OTG_FS_CORE_ID,&USR_desc, &USBD_HID_cb, &USR_cb);
/* Infinite loop */
while (1)
{
}
}
接着来把stm32f4xx_it.h添加相应头文件引用
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32F4xx_IT_H
#define __STM32F4xx_IT_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx.h"
#include "usbd_hid_core.h"
/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* Exported macro ------------------------------------------------------------*/
/* Exported functions ------------------------------------------------------- */
void NMI_Handler(void);
void HardFault_Handler(void);
void MemManage_Handler(void);
void BusFault_Handler(void);
void UsageFault_Handler(void);
void SVC_Handler(void);
void DebugMon_Handler(void);
void PendSV_Handler(void);
void SysTick_Handler(void);
void OTG_FS_IRQHandler(void);
#ifdef __cplusplus
}
#endif
#endif /* __STM32F4xx_IT_H */
在stm32F4xx_it.c文件里添加相应的中断OTG_FS_IRQHandler,具体如下
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_it.h"
extern USB_OTG_CORE_HANDLE USB_OTG_dev;
extern uint32_t USBD_OTG_ISR_Handler(USB_OTG_CORE_HANDLE * pdev);
/** @addtogroup Template_Project
* @{
*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/******************************************************************************/
/* Cortex-M4 Processor Exceptions Handlers */
/******************************************************************************/
/**
* @brief This function handles NMI exception.
* @param None
* @retval None
*/
void NMI_Handler(void)
{
}
/**
* @brief This function handles Hard Fault exception.
* @param None
* @retval None
*/
void HardFault_Handler(void)
{
/* Go to infinite loop when Hard Fault exception occurs */
while (1)
{
}
}
/**
* @brief This function handles Memory Manage exception.
* @param None
* @retval None
*/
void MemManage_Handler(void)
{
/* Go to infinite loop when Memory Manage exception occurs */
while (1)
{
}
}
/**
* @brief This function handles Bus Fault exception.
* @param None
* @retval None
*/
void BusFault_Handler(void)
{
/* Go to infinite loop when Bus Fault exception occurs */
while (1)
{
}
}
/**
* @brief This function handles Usage Fault exception.
* @param None
* @retval None
*/
void UsageFault_Handler(void)
{
/* Go to infinite loop when Usage Fault exception occurs */
while (1)
{
}
}
/**
* @brief This function handles SVCall exception.
* @param None
* @retval None
*/
void SVC_Handler(void)
{
}
/**
* @brief This function handles Debug Monitor exception.
* @param None
* @retval None
*/
void DebugMon_Handler(void)
{
}
/**
* @brief This function handles PendSVC exception.
* @param None
* @retval None
*/
void PendSV_Handler(void)
{
}
/**
* @brief This function handles SysTick Handler.
* @param None
* @retval None
*/
void SysTick_Handler(void)
{
}
/**
* @brief This function handles OTG_HS Handler.
* @param None
* @retval None
*/
void OTG_FS_IRQHandler(void)
{
USBD_OTG_ISR_Handler(&USB_OTG_dev);
}
到此点编译,如果按照方式来操作,没任何报错后,下载代码到芯片上,连接USB到电脑,电脑应该能检测到一个Joystick in FS Mode的设备了,而且驱动能自动安装好的,如果编译还有报错误,应该都是一些没用到原来开发板上的一些声明或者定义或者调用,屏蔽掉即可,因为我们用不到这些,或者电脑上识别不到USB或者驱动不正常安装,请再一步步的检查一下,直到成功。
另外提一下,有的板子使用的晶振是25M,有的是8M的,别忘记system_stm32f4xx.c和stm32f4xx.h文件里修改对应的晶振频率,和USB的48MHZ时钟,时钟配置如果不对,主频可能超频严重刷入固件后芯片可能会死机,或者不死机但USB时钟不对,USB不能正常工作,更不可能让电脑 识别出USB设备了,还会可能让JLINK或者STLINK都无法识别芯片,无法重新下载固件,如果 出现这种问题,别担心,死按住板子上的复位键,然后点下载再放开复位键就能重新下载正常时钟的固件,就没问题了,原理是芯片刚复位后有个默认的启动频率,能正常的SW或者JTAG,或者更改boot0电平切换到进入DFU,或者串口ISP模式,以DFU或者串口ISP模式下载一个正常时钟的固件进芯片,通过这些办法就能让芯片重新下载正常固件恢复正常。
stm32_f105-07_f2_f4_usb-host-device_lib里的固件库默认是25M的,我板子使用的是8M晶振,如下 修改:
system_stm32f4xx.c
/************************* PLL Parameters *************************************/
#if defined(STM32F40_41xxx) || defined(STM32F427_437xx) || defined(STM32F429_439xx) || defined(STM32F401xx) || defined(STM32F469_479xx)
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */
#define PLL_M 8//25 修改为8
#elif defined(STM32F412xG) || defined(STM32F413_423xx) || defined (STM32F446xx)
#define PLL_M 8
#elif defined (STM32F410xx) || defined (STM32F411xE)
#if defined(USE_HSE_BYPASS)
#define PLL_M 8
#else /* !USE_HSE_BYPASS */
#define PLL_M 16
#endif /* USE_HSE_BYPASS */
#else
#endif /* STM32F40_41xxx || STM32F427_437xx || STM32F429_439xx || STM32F401xx || STM32F469_479xx */
/* USB OTG FS, SDIO and RNG Clock = PLL_VCO / PLLQ */ USB时钟按计算公式修改相应的值
#define PLL_Q 7
#if defined(STM32F446xx)
/* PLL division factor for I2S, SAI, SYSTEM and SPDIF: Clock = PLL_VCO / PLLR */
#define PLL_R 7
#elif defined(STM32F412xG) || defined(STM32F413_423xx)
#define PLL_R 2
#else
#endif /* STM32F446xx */
#if defined(STM32F427_437xx) || defined(STM32F429_439xx) || defined(STM32F446xx) || defined(STM32F469_479xx)
#define PLL_N 360
/* SYSCLK = PLL_VCO / PLL_P */
#define PLL_P 2
#endif /* STM32F427_437x || STM32F429_439xx || STM32F446xx || STM32F469_479xx */
#if defined (STM32F40_41xxx)
#define PLL_N 336
/* SYSCLK = PLL_VCO / PLL_P */
#define PLL_P 2
#endif /* STM32F40_41xxx */
stm32f4xx.h
/**
* @brief In the following line adjust the value of External High Speed oscillator (HSE)
used in your application
Tip: To avoid modifying this file each time you need to use different HSE, you
can define the HSE value in your toolchain compiler preprocessor.
*/
#if defined(STM32F40_41xxx) || defined(STM32F427_437xx) || defined(STM32F429_439xx) || defined(STM32F401xx) || \
defined(STM32F410xx) || defined(STM32F411xE) || defined(STM32F469_479xx)
#if !defined (HSE_VALUE) //修改为8000000
#define HSE_VALUE ((uint32_t)8000000/*25000000*/) /*!< Value of the External oscillator in Hz */
#endif /* HSE_VALUE */
#elif defined (STM32F412xG) || defined(STM32F413_423xx) || defined(STM32F446xx)
#if !defined (HSE_VALUE)
#define HSE_VALUE ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */
#endif /* HSE_VALUE */
#endif /* STM32F40_41xxx || STM32F427_437xx || STM32F429_439xx || STM32F401xx || STM32F411xE || STM32F469_479xx */
完成移植这一步才是刚开始,因为实现BULK传输+WINUSB免驱才是本文的重点,HID的工程移植只是能有个正确的模板工程,到这里可以先休息一下,毕竟用固件库弄个移植工程也是够复杂的了,接着再看下面的
接下来先实现BULK传输的修改,先定位到usbd_desc.c文件,相应的修改一下设备描述符,如下
/* USB Standard Device Descriptor */
__ALIGN_BEGIN uint8_t USBD_DeviceDesc[USB_SIZ_DEVICE_DESC] __ALIGN_END = {
0x12, /* bLength */
USB_DEVICE_DESCRIPTOR_TYPE, /* bDescriptorType */
0x00, /* bcdUSB */
0x02,
0xFF, /* bDeviceClass */
0x00, /* bDeviceSubClass */
0x00, /* bDeviceProtocol */
USB_OTG_MAX_EP0_SIZE, /* bMaxPacketSize */
LOBYTE(USBD_VID), /* idVendor */
HIBYTE(USBD_VID), /* idVendor */
LOBYTE(USBD_PID), /* idVendor */
HIBYTE(USBD_PID), /* idVendor */
0x00, /* bcdDevice rel. 2.00 */
0x02,
USBD_IDX_MFC_STR, /* Index of manufacturer string */
USBD_IDX_PRODUCT_STR, /* Index of product string */
USBD_IDX_SERIAL_STR, /* Index of serial number string */
USBD_CFG_MAX_NUM /* bNumConfigurations */
}; /* USB_DeviceDescriptor */
bDeviceClass修改为0xFF 为厂商自定义设备,VID和PID修改为自己所使用的即可
然后定位到usbd_cont.h文件,修改IN端点每包数据大小和添加OUT端点的地址定义如下
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __USBD_CONF__H__
#define __USBD_CONF__H__
/* Includes ------------------------------------------------------------------*/
#include "usb_conf.h"
/** @defgroup USB_CONF_Exported_Defines
* @{
*/
#define USBD_CFG_MAX_NUM 1
#define USBD_ITF_MAX_NUM 1
#define USB_MAX_STR_DESC_SIZ 64
#define USBD_SELF_POWERED
#define USBD_DYNAMIC_DESCRIPTOR_CHANGE_ENABLED
/** @defgroup USB_String_Descriptors
* @{
*/
/** @defgroup USB_HID_Class_Layer_Parameter
* @{
*/
#define HID_IN_EP 0x81
#define HID_IN_PACKET 64
#define HID_OUT_EP 0x01
#define HID_OUT_PACKET 64
然后定位到usbd_hid_core.c文件,先修改配置描述符,如下:
/* USB HID device Configuration Descriptor */
__ALIGN_BEGIN static uint8_t USBD_HID_CfgDesc[USB_HID_CONFIG_DESC_SIZ] __ALIGN_END =
{
0x09, /* bLength: Configuration Descriptor size */
USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType: Configuration */
USB_HID_CONFIG_DESC_SIZ,
/* wTotalLength: Bytes returned */
0x00,
0x01, /*bNumInterfaces: 1 interface*/
0x01, /*bConfigurationValue: Configuration value*/
0x00, /*iConfiguration: Index of string descriptor describing
the configuration*/
0x80, /*bmAttributes: bus powered and Support Remote Wake-up */
0xFA, /*MaxPower 100 mA: this current is used for detecting Vbus*/
/************** Descriptor of Joystick Mouse interface ****************/
/* 09 */
0x09, /*bLength: Interface Descriptor size*/
USB_INTERFACE_DESCRIPTOR_TYPE,/*bDescriptorType: Interface descriptor type*/
0x00, /*bInterfaceNumber: Number of Interface*/
0x00, /*bAlternateSetting: Alternate setting*/
0x02, /*bNumEndpoints*/
0xFF, /*bInterfaceClass: HID*/
0x00, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
0x00, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
0, /*iInterface: Index of string descriptor*/
/******************** Descriptor of Mouse endpoint ********************/
/* 18 */
//Endpoint 1 (In)
0x07, /*bLength: Endpoint Descriptor size*/
USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
HID_IN_EP, /*bEndpointAddress: Endpoint Address (IN)*/
0x02, /*bmAttributes: Interrupt endpoint*/
HID_IN_PACKET, /*wMaxPacketSize: 4 Byte max */
0x00,
0x00, /*bInterval: Polling Interval (10 ms)*/
/* 25 */
//Endpoint 1 (OUT)
0x07,
USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
HID_OUT_EP, /*bEndpointAddress: Endpoint Address (IN)*/
0x02, /*bmAttributes: Interrupt endpoint*/
HID_OUT_PACKET, /*wMaxPacketSize: 4 Byte max */
0x00,
0x00, /*bInterval: Polling Interval (10 ms)*/
/* 32 */
} ;
修改完成后别忘记了修改USB_HID_CONFIG_DESC_SIZ的定义值为32,因为配置描述符的大小已经改变,在usbd_hid_core.h文件里如下:
#define USB_HID_CONFIG_DESC_SIZ 32
这里不详细说明,自行查找相应的配置描述符定义的解释去吧,主要修改为自定义的接口类bInterfaceClass为0xFF,BULK端点,bmAttributes=2,端点数量bNumEndpoints=2
接下来要添加端点的回调函数,还是在usbd_hid_core.c文件,添加定义
uint8_t USBD_HID_DataOut (void *pdev, uint8_t epnum);
/** @defgroup USBD_HID_Private_FunctionPrototypes
* @{
*/
uint8_t USBD_HID_Init (void *pdev,
uint8_t cfgidx);
uint8_t USBD_HID_DeInit (void *pdev,
uint8_t cfgidx);
uint8_t USBD_HID_Setup (void *pdev,
USB_SETUP_REQ *req);
static uint8_t *USBD_HID_GetCfgDesc (uint8_t speed, uint16_t *length);
uint8_t USBD_HID_DataIn (void *pdev, uint8_t epnum);
uint8_t USBD_HID_DataOut (void *pdev, uint8_t epnum);
结构里添加USBD_HID_DataOut
USBD_Class_cb_TypeDef USBD_HID_cb =
{
USBD_HID_Init,
USBD_HID_DeInit,
USBD_HID_Setup,
NULL, /*EP0_TxSent*/
NULL, /*EP0_RxReady*/
USBD_HID_DataIn, /*DataIn*/
USBD_HID_DataOut, /*DataOut*/
NULL, /*SOF */
NULL,
NULL,
USBD_HID_GetCfgDesc,
#ifdef USB_OTG_HS_CORE
USBD_HID_GetCfgDesc, /* use same config as per FS */
#endif
};
USBD_HID_Init函数里添加 :
uint8_t USBD_HID_Init (void *pdev,
uint8_t cfgidx)
{
/* Open EP IN */
DCD_EP_Open(pdev,
HID_IN_EP,
HID_IN_PACKET,
USB_OTG_EP_BULK);
/* Open EP IN */
DCD_EP_Open(pdev,
HID_OUT_EP,
HID_OUT_PACKET,
USB_OTG_EP_BULK);
/* Prepare Out endpoint to receive next packet */
DCD_EP_PrepareRx(pdev,
HID_OUT_EP,
(uint8_t*)USB_Rx_Buffer,
HID_OUT_PACKET);
return USBD_OK;
}
USBD_HID_DeInit函数的修改:
uint8_t USBD_HID_DeInit (void *pdev,
uint8_t cfgidx)
{
/* Close HID EPs */
DCD_EP_Close (pdev , HID_IN_EP);
DCD_EP_Close (pdev , HID_OUT_EP);
return USBD_OK;
}
添加USBD_HID_DataOut回调函数的实现,这个函数就是接收PC主机发送过来数据的回调函数,我这里只写了个空函数,USBD_HID_DataIn默认本身因为HID的工程有一个IN端点
static uint8_t *USBD_HID_GetCfgDesc (uint8_t speed, uint16_t *length)
{
*length = sizeof (USBD_HID_CfgDesc);
return USBD_HID_CfgDesc;
}
/**
* @brief USBD_HID_DataIn
* handle data IN Stage
* @param pdev: device instance
* @param epnum: endpoint index
* @retval status
*/
uint8_t USBD_HID_DataIn (void *pdev,
uint8_t epnum)
{
/* Ensure that the FIFO is empty before a new transfer, this condition could
be caused by a new transfer before the end of the previous transfer */
DCD_EP_Flush(pdev, HID_IN_EP);
return USBD_OK;
}
uint8_t USBD_HID_DataOut (void *pdev,
uint8_t epnum)
{
/* Ensure that the FIFO is empty before a new transfer, this condition could
be caused by a new transfer before the end of the previous transfer */
return USBD_OK;
}
实际的使用USBD_HID_DataOut如下,USB_Rx_Buffer[]是你要接收的数据的数组缓冲区,要自己定义这个数组变量
static uint8_t USBD_HID_DataOut (void *pdev, uint8_t epnum)
{
uint16_t USB_Rx_Cnt;
USB_Rx_Cnt = ((USB_OTG_CORE_HANDLE*)pdev)->dev.out_ep[epnum].xfer_count;
// add your own data to process the received data
/*
* printf_the_received_data((char *)USB_Rx_Buffer, USB_Rx_Cnt);
*/
/* Prepare Out endpoint to receive next packet */
DCD_EP_PrepareRx(pdev,
HID_OUT_EP,
(uint8_t*)USB_Rx_Buffer,
HID_OUT_PACKET);
return USBD_OK;
}
DCD_EP_Tx 库函数是发送用的,即单片机发送到PC主机,但实际上DCD_EP_Tx和DCD_EP_PrepareRx两个函数只是从USB的寄存器地址写入数据 和取得数据 ,还存在FIFO缓存地址,比如发送,执行DCD_EP_Tx后它并不是马上就发送完成的,只是往相应的端点的FIFO寄存器里写入相应的数据 地址,然后等待端点向主机发送数据,当发送完成一包数据 后,就会触发USB中断,中断里调用回调函数USBD_HID_DataIn,在进入到这回调函数表示已经发送完毕。
DCD_EP_PrepareRx执行,也是只是表示 往某个端点,提交一个缓存地址到端点和对应的FIFO地址,当主机有数据发送过来时,接收完成一包后就会触发USBD_HID_DataOut 回调函数,进入到这函数时实际上已经接收完成一包的数据了,可以直接查看提交的缓存 USB_Rx_Buffer已经填充入了接收到的数据,所以进入到这个函数里先通过((USB_OTG_CORE_HANDLE*)pdev)->dev.out_ep[epnum].xfer_count得到接收到的数据长度,USB2.0全速度,最大也就64字节一个包,然后直接从USB_Rx_Buffer缓存 里取出数据,然后再执行DCD_EP_PrepareRx向相应的端点提交一个USB_Rx_Buffer缓存 地址,这个地址可以是不同的缓存区位置的,等待主机下一次发送数据过来时能有存放数据的缓存区,所以可以看到在USBD_HID_Init为何先执行一次DCD_EP_PrepareRx,因为先给端点指定了一个接收存放数据的缓存。
对于USB的接收和发送,是不能同时进行的,不像串口哪样两根传输线可以同时双工收发,USB的D+和D-只是一对差分数据线,只能是一个时间在接收,接收完成所有数据后才能让其发送,也是在发送的时间里不能再接收数据的,如果 在接收没完成所有数据包时,执行了发送,就会出错的,PC端可以是一次发送超过64字节的数据的,PC会把大于这个字节的数据 分成多个包,临时存放到发送缓冲区里,设备接收一个包后,PC再把下一包发送下去,反之接收也是这个原理,所以USB的一收和一发,最好自己定义一下收发协议,在数据包超过64一包的情况下,在第一包可以在包头定义一下总字节数据,接收方收到第一包在包头能读到总字节数据,接收完成所有数据字节后,才能执行发送处理
USB_OTG是带FIFO的,但这2.0全速FS下的批量传输端点最大也就64字节一个包,FIFO 接收和发送端点好像只能128字节,尝试过IN和OUT端点都设置为256后,IN端点只能发送4个字节的数据,具体什么原因得查看一下相关的USbB手册,暂时没去研究了,了解的朋友可以回复一下原因。
到此点编译,下载到STM32F407里,USB连接到PC电脑,此时电脑能识别设备但驱动没安装上,所以会显示一个黄色的"!"号,因为已经不再是HID设备了,此时是自定义的设备了,没有相关的驱动提供给电脑安装就会这样,会有一个黄色的"!"号在驱动名字显示,我们使用别的软件USBlyzer来查看,可以看到相应的描述符设定已经与我们修改的完全一样了,现在就是差了驱动程序,接着就是修改为支持WINUSB驱动,这样我们就可以使用WIN提供的WINUSB API接口来读写这个USB设备了,而且是免驱动,WIN8.1以上系统即插即用,不用安装任何驱动,系统自带这个驱动,WIN7和XP都要一个inf文件安装一下驱动才能使用的,WIN10上如果使用INF安装驱动,会被强制电子签名才行,后面会说如何制作INF安装包,可以自行百度WINUSB进行相关的了解
USBlyzer 能看到驱动对应的描述符:
接下来的WINUSB免驱要修改的文件也得好几个,也有点小复杂,一步步跟着来就可以了
先定位到文件usbd_desc.c添加
USBD_USR_OSStrDescriptor,
USBD_USR_ExtPropertiesFeatureDescriptor,
USBD_USR_ExtCompatIDFeatureDescriptor 到这里如下:
/** @defgroup USBD_DESC_Private_Variables
* @{
*/
USBD_DEVICE USR_desc = {
USBD_USR_DeviceDescriptor,
USBD_USR_LangIDStrDescriptor,
USBD_USR_ManufacturerStrDescriptor,
USBD_USR_ProductStrDescriptor,
USBD_USR_SerialStrDescriptor,
USBD_USR_ConfigStrDescriptor,
USBD_USR_InterfaceStrDescriptor,
//添加MS OS 1.0的返回描述符函数指针
USBD_USR_OSStrDescriptor,
USBD_USR_ExtPropertiesFeatureDescriptor,
USBD_USR_ExtCompatIDFeatureDescriptor
};
usbd_desc.c添加WINUSB MS OS 1.0相关的字符串描述符
__ALIGN_BEGIN uint8_t USBD_OSStrDesc[USB_LEN_OS_DESC] __ALIGN_END =
{
USB_LEN_OS_DESC,
USB_DESC_TYPE_STRING,
'M',0,'S',0,'F',0,'T',0,'1',0,'0',0,'0',0,
USB_REQ_GET_OS_FEATURE_DESCRIPTOR
};
__ALIGN_BEGIN uint8_t USBD_CompatIDDesc[CUSTOM_SIZ_STRING_IDDesc] __ALIGN_END =
{
///
/// WCID descriptor
///
0x28,0x00,0x00,0x00, /* dwLength */
0x00,0x01, /* bcdVersion */
0x04,0x00, /* wIndex */
0x01, /* bCount */
0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* bReserved_7 */
///
/// WCID function descriptor
///
0x00, /* bFirstInterfaceNumber */
0x01, /* bReserved */
/*WINUSB*/
'W','I','N','U','S','B',0x00,0x00, /* cCID_8 */
/* */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* cSubCID_8 */
0x00,0x00,0x00,0x00,0x00,0x00, /* bReserved_6 */
};
__ALIGN_BEGIN uint8_t USBD_ExtPropertiesDesc[CUSTOM_SIZ_STRING_ExtPropertiesDesc] __ALIGN_END =
{
///
/// WCID property descriptor
///
0x8e, 0x00, 0x00, 0x00, /* dwLength */
0x00, 0x01, /* bcdVersion */
0x05, 0x00, /* wIndex */
0x01, 0x00, /* wCount */
///
/// registry propter descriptor
///
0x84, 0x00, 0x00, 0x00, /* dwSize */
0x01, 0x00, 0x00, 0x00, /* dwPropertyDataType */
0x28, 0x00, /* wPropertyNameLength */
/* DeviceInterfaceGUID */
'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, /* wcName_20 */
'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, /* wcName_20 */
't', 0x00, 'e', 0x00, 'r', 0x00, 'f', 0x00, /* wcName_20 */
'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, /* wcName_20 */
'U', 0x00, 'I', 0x00, 'D', 0x00, 0x00, 0x00, /* wcName_20 */
0x4e, 0x00, 0x00, 0x00, /* dwPropertyDataLength */
/* {647A17F1-6ED1-4176-A9CE-0864804669B6} */
'{', 0x00, '6', 0x00, '4', 0x00, '7', 0x00, /* wcData_39 */
'A', 0x00, '1', 0x00, '7', 0x00, 'F', 0x00, /* wcData_39 */
'1', 0x00, '-', 0x00, '6', 0x00, 'E', 0x00, /* wcData_39 */
'D', 0x00, '1', 0x00, '-', 0x00, '4', 0x00, /* wcData_39 */
'1', 0x00, '7', 0x00, '6', 0x00, '-', 0x00, /* wcData_39 */
'A', 0x00, '9', 0x00, 'C', 0x00, 'E', 0x00, /* wcData_39 */
'-', 0x00, '0', 0x00, '8', 0x00, '6', 0x00, /* wcData_39 */
'4', 0x00, '8', 0x00, '0', 0x00, '4', 0x00, /* wcData_39 */
'6', 0x00, '6', 0x00, '9', 0x00, 'B', 0x00, /* wcData_39 */
'6', 0x00, '}', 0x00, 0x00, 0x00, /* wcData_39 */
};
描述符里的 USB_LEN_OS_DESC和USB_REQ_GET_OS_FEATURE_DESCRIPTOR在usbd_def.h里添加定义
#define USB_LEN_OS_DESC 0x12
#define USB_REQ_GET_OS_FEATURE_DESCRIPTOR 0x20
#define USB_LEN_DEV_QUALIFIER_DESC 0x0A
#define USB_LEN_DEV_DESC 0x12
#define USB_LEN_CFG_DESC 0x09
#define USB_LEN_IF_DESC 0x09
#define USB_LEN_EP_DESC 0x07
#define USB_LEN_OTG_DESC 0x03
#define USB_LEN_OS_DESC 0x12 //这里
#define USBD_IDX_LANGID_STR 0x00
#define USBD_IDX_MFC_STR 0x01
#define USBD_IDX_PRODUCT_STR 0x02
#define USBD_IDX_SERIAL_STR 0x03
#define USBD_IDX_CONFIG_STR 0x04
#define USBD_IDX_INTERFACE_STR 0x05
#define USBD_IDX_OS_STR 0xEE
#define USB_REQ_TYPE_STANDARD 0x00
#define USB_REQ_TYPE_CLASS 0x20
#define USB_REQ_TYPE_VENDOR 0x40
#define USB_REQ_TYPE_MASK 0x60
#define USB_REQ_RECIPIENT_DEVICE 0x00
#define USB_REQ_RECIPIENT_INTERFACE 0x01
#define USB_REQ_RECIPIENT_ENDPOINT 0x02
#define USB_REQ_RECIPIENT_MASK 0x03
#define USB_REQ_GET_STATUS 0x00
#define USB_REQ_CLEAR_FEATURE 0x01
#define USB_REQ_SET_FEATURE 0x03
#define USB_REQ_SET_ADDRESS 0x05
#define USB_REQ_GET_DESCRIPTOR 0x06
#define USB_REQ_SET_DESCRIPTOR 0x07
#define USB_REQ_GET_CONFIGURATION 0x08
#define USB_REQ_SET_CONFIGURATION 0x09
#define USB_REQ_GET_INTERFACE 0x0A
#define USB_REQ_SET_INTERFACE 0x0B
#define USB_REQ_SYNCH_FRAME 0x0C
#define USB_REQ_GET_OS_FEATURE_DESCRIPTOR 0x20 //这里
CUSTOM_SIZ_STRING_IDDesc和CUSTOM_SIZ_STRING_ExtPropertiesDesc在usbd_desc.h文件里添加定义:
#define CUSTOM_SIZ_STRING_IDDesc 40
#define CUSTOM_SIZ_STRING_ExtPropertiesDesc 142
/** @defgroup USB_DESC_Exported_Defines
* @{
*/
#define USB_DEVICE_DESCRIPTOR_TYPE 0x01
#define USB_CONFIGURATION_DESCRIPTOR_TYPE 0x02
#define USB_STRING_DESCRIPTOR_TYPE 0x03
#define USB_INTERFACE_DESCRIPTOR_TYPE 0x04
#define USB_ENDPOINT_DESCRIPTOR_TYPE 0x05
#define USB_SIZ_DEVICE_DESC 18
#define USB_SIZ_STRING_LANGID 4
#define CUSTOM_SIZ_STRING_IDDesc 40 //这里
#define CUSTOM_SIZ_STRING_ExtPropertiesDesc 142 //这里
usbd_desc.c添加三个描述符返回函数
uint8_t * USBD_USR_OSStrDescriptor( uint8_t speed , uint16_t *length)
{
*length = sizeof(USBD_OSStrDesc);
return USBD_OSStrDesc;
}
uint8_t * USBD_USR_ExtPropertiesFeatureDescriptor( uint8_t speed , uint16_t *length)
{
*length = sizeof(USBD_ExtPropertiesDesc);
return (uint8_t*)&USBD_ExtPropertiesDesc;
}
uint8_t * USBD_USR_ExtCompatIDFeatureDescriptor( uint8_t speed , uint16_t *length)
{
*length = sizeof(USBD_CompatIDDesc);
return (uint8_t*)&USBD_CompatIDDesc;
}
到usbd_desc.h文件添加三个描述符返回函数的声明
/** @defgroup USBD_DESC_Exported_FunctionsPrototype
* @{
*/
uint8_t * USBD_USR_DeviceDescriptor( uint8_t speed , uint16_t *length);
uint8_t * USBD_USR_LangIDStrDescriptor( uint8_t speed , uint16_t *length);
uint8_t * USBD_USR_ManufacturerStrDescriptor ( uint8_t speed , uint16_t *length);
uint8_t * USBD_USR_ProductStrDescriptor ( uint8_t speed , uint16_t *length);
uint8_t * USBD_USR_SerialStrDescriptor( uint8_t speed , uint16_t *length);
uint8_t * USBD_USR_ConfigStrDescriptor( uint8_t speed , uint16_t *length);
uint8_t * USBD_USR_InterfaceStrDescriptor( uint8_t speed , uint16_t *length);
//这里添加三个函数声明
uint8_t * USBD_USR_OSStrDescriptor( uint8_t speed , uint16_t *length);
uint8_t * USBD_USR_ExtPropertiesFeatureDescriptor( uint8_t speed , uint16_t *length);
uint8_t * USBD_USR_ExtCompatIDFeatureDescriptor( uint8_t speed , uint16_t *length);
修改usbd_core.c文件里的USBD_SetupStage函数修改如下:
/**
* @brief USBD_SetupStage
* Handle the setup stage
* @param pdev: device instance
* @retval status
*/
static uint8_t USBD_SetupStage(USB_OTG_CORE_HANDLE *pdev)
{
USB_SETUP_REQ req;
USBD_ParseSetupRequest(pdev , &req);
switch ((req.bmRequest & 0x60) >> 5) //当bmRequest为C0值时调用USBD_VendDevReq
{
case 0x00:
switch (req.bmRequest & 0x1F)
{
case USB_REQ_RECIPIENT_DEVICE:
USBD_StdDevReq (pdev, &req);
break;
case USB_REQ_RECIPIENT_INTERFACE:
USBD_StdItfReq(pdev, &req);
break;
case USB_REQ_RECIPIENT_ENDPOINT:
USBD_StdEPReq(pdev, &req);
break;
default:
DCD_EP_Stall(pdev , req.bmRequest & 0x80);
break;
}
break;
case 0x02://当bmRequest为C0值时调用USBD_VendDevReq
USBD_VendDevReq(pdev,&req);
break;
}
return USBD_OK;
}
定位到usbd_req.h文件添加USBD_Status USBD_VendDevReq (USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req);声明
/** @defgroup USBD_REQ_Exported_FunctionsPrototype
* @{
*/
USBD_Status USBD_StdDevReq (USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req);
USBD_Status USBD_StdItfReq (USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req);
USBD_Status USBD_StdEPReq (USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req);
USBD_Status USBD_VendDevReq (USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req);
到usbd_core.c添加USBD_VendDevReq函数的实现代码
USB设备连接上主机后,主要就会请求设备描述符号,主机读到设备描述符的bcdUSB为0x0200后,知道这个设备可能存在MS OS字符,在请求完配置描述符和端点等待配置后,就会向设备发送0xEE请求,设备接收到主机这个请求后,向主机发送“MSFT100”的OS字符描述符,OS字符描述符在vendor code 字段我们定义为0x20,主机接收到“MSFT100”的OS字符描述符回复后,在注册表生成OS 字符的osvc标识,接着再次向设备发送vendor+wIndex请求,即我们定义的vendor=0x20和主机一同发来的wIndex=4的请求,USBD_VendDevReq函数里就是回复这个请求,case USB_DESC_TYPE_OS_FEATURE_EXT_PROPERTIES:这一个是wIndex为4的请求回复,主机接收完成后,再次发出vendor+wIndex请求,这次是0x20和wIndex=5的请求,对应的是case USB_DESC_TYPE_OS_FEATURE_EXT_COMPAT_ID:,主机接收请求回复后,就会在注册表生成DeviceInterfaceGUID,如果这里没生成DeviceInterfaceGUID,可能是回复的描述符号不正确,或者是OS 字符的osvc标识不是01 0x20,为00 00就不会产生DeviceInterfaceGUID,如果这样,可以把048357530200目录整个删除掉,重新插入一下USB识别,就能生成DeviceInterfaceGUID,这样就可以用DeviceInterfaceGUID通过WINUSB API进行读写设备
注册表里的目录048057510200和是VID+PID+bcdUSB版本号,338836843435SB的序列号生成的,不同的VID和PID和序列号不一样,对应的名字不一样,这里这是举例
注册表查看位置
OS osvc标识位置:
计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\usbflags\048357530200
DeviceInterfaceGUID位置:
计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\VID_0483&PID_5753\338836843435\Device Parameters
/**
* @brief USBD_StdDevReq
* Handle standard usb device requests
* @param pdev: device instance
* @param req: usb request
* @retval status
*/
USBD_Status USBD_StdDevReq (USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req)
{
USBD_Status ret = USBD_OK;
switch (req->bRequest)
{
case USB_REQ_GET_DESCRIPTOR:
USBD_GetDescriptor (pdev, req) ;
break;
case USB_REQ_SET_ADDRESS:
USBD_SetAddress(pdev, req);
break;
case USB_REQ_SET_CONFIGURATION:
USBD_SetConfig (pdev , req);
break;
case USB_REQ_GET_CONFIGURATION:
USBD_GetConfig (pdev , req);
break;
case USB_REQ_GET_STATUS:
USBD_GetStatus (pdev , req);
break;
case USB_REQ_SET_FEATURE:
USBD_SetFeature (pdev , req);
break;
case USB_REQ_CLEAR_FEATURE:
USBD_ClrFeature (pdev , req);
break;
default:
USBD_CtlError(pdev , req);
break;
}
return ret;
}
/**
* @brief USBD_VendDevReq
* Handle vendor specific usb interface requests
* @param pdev: USB OTG device instance
* @param req: usb request
* @retval status
*/
USBD_Status USBD_VendDevReq (USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req)
{
uint16_t len;
uint8_t *pbuf;
USBD_Status ret = USBD_OK;
switch (req->wIndex)
{
case USB_DESC_TYPE_OS_FEATURE_EXT_PROPERTIES:
pbuf = pdev->dev.usr_device->GetExtCompatIDFeatureDescriptor(pdev->cfg.speed, &len);
break;
case USB_DESC_TYPE_OS_FEATURE_EXT_COMPAT_ID:
pbuf = pdev->dev.usr_device->GetExtPropertiesFeatureDescriptor(pdev->cfg.speed, &len);
break;
}
if((len != 0)&& (req->wLength != 0))
{
len = MIN(len , req->wLength);
USBD_CtlSendData (pdev,
pbuf,
len);
}
return ret;
}
/**
* @brief USBD_StdItfReq
* Handle standard usb interface requests
* @param pdev: USB OTG device instance
* @param req: usb request
* @retval status
*/
在usbd_def.h文件里添加定义wIndex请求标志
#define USB_DESC_TYPE_OS_FEATURE_EXT_PROPERTIES 4
#define USB_DESC_TYPE_OS_FEATURE_EXT_COMPAT_ID 5
#define USB_DESC_TYPE_DEVICE 1
#define USB_DESC_TYPE_CONFIGURATION 2
#define USB_DESC_TYPE_STRING 3
#define USB_DESC_TYPE_INTERFACE 4
#define USB_DESC_TYPE_ENDPOINT 5
#define USB_DESC_TYPE_DEVICE_QUALIFIER 6
#define USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION 7
#define USB_DESC_TYPE_BOS 0x0F
//这里添加
#define USB_DESC_TYPE_OS_FEATURE_EXT_PROPERTIES 4
#define USB_DESC_TYPE_OS_FEATURE_EXT_COMPAT_ID 5
#define USB_CONFIG_REMOTE_WAKEUP 2
#define USB_CONFIG_SELF_POWERED 1
#define USB_FEATURE_EP_HALT 0
#define USB_FEATURE_REMOTE_WAKEUP 1
#define USB_FEATURE_TEST_MODE 2
在usb_core.h文件里的结构定义_Device_TypeDef添加
uint8_t *(*GetOSStrDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetExtPropertiesFeatureDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetExtCompatIDFeatureDescriptor)( uint8_t speed , uint16_t *length);
typedef struct _Device_TypeDef
{
uint8_t *(*GetDeviceDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetLangIDStrDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetManufacturerStrDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetProductStrDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetSerialStrDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetConfigurationStrDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetInterfaceStrDescriptor)( uint8_t speed , uint16_t *length);
//这里添加函数指针定义
uint8_t *(*GetOSStrDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetExtPropertiesFeatureDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetExtCompatIDFeatureDescriptor)( uint8_t speed , uint16_t *length);
#if (USBD_LPM_ENABLED == 1)
uint8_t *(*GetBOSDescriptor)( uint8_t speed , uint16_t *length);
#endif
} USBD_DEVICE, *pUSBD_DEVICE;
再定位到usbd_core.c文件,USBD_GetDescriptor函数里,添加EE请求
case USBD_IDX_OS_STR:
pbuf = pdev->dev.usr_device->GetOSStrDescriptor(pdev->cfg.speed, &len);
break;
/**
* @brief USBD_GetDescriptor
* Handle Get Descriptor requests
* @param pdev: device instance
* @param req: usb request
* @retval status
*/
static void USBD_GetDescriptor(USB_OTG_CORE_HANDLE *pdev,
USB_SETUP_REQ *req)
{
uint16_t len;
uint8_t *pbuf;
len = req->wLength ;
switch (req->wValue >> 8)
{
#if (USBD_LPM_ENABLED == 1)
case USB_DESC_TYPE_BOS:
pbuf = pdev->pDesc->GetBOSDescriptor(pdev->dev_speed, &len);
break;
#endif
case USB_DESC_TYPE_DEVICE:
pbuf = pdev->dev.usr_device->GetDeviceDescriptor(pdev->cfg.speed, &len);
break;
case USB_DESC_TYPE_CONFIGURATION:
pbuf = (uint8_t *)pdev->dev.class_cb->GetConfigDescriptor(pdev->cfg.speed, &len);
#ifdef USB_OTG_HS_CORE
if((pdev->cfg.speed == USB_OTG_SPEED_FULL )&&
(pdev->cfg.phy_itface == USB_OTG_ULPI_PHY))
{
pbuf = (uint8_t *)pdev->dev.class_cb->GetOtherConfigDescriptor(pdev->cfg.speed, &len);
}
#endif
pbuf[1] = USB_DESC_TYPE_CONFIGURATION;
pdev->dev.pConfig_descriptor = pbuf;
break;
case USB_DESC_TYPE_STRING:
switch ((uint8_t)(req->wValue))
{
case USBD_IDX_LANGID_STR:
pbuf = pdev->dev.usr_device->GetLangIDStrDescriptor(pdev->cfg.speed, &len);
break;
case USBD_IDX_MFC_STR:
pbuf = pdev->dev.usr_device->GetManufacturerStrDescriptor(pdev->cfg.speed, &len);
break;
case USBD_IDX_PRODUCT_STR:
pbuf = pdev->dev.usr_device->GetProductStrDescriptor(pdev->cfg.speed, &len);
break;
case USBD_IDX_SERIAL_STR:
pbuf = pdev->dev.usr_device->GetSerialStrDescriptor(pdev->cfg.speed, &len);
break;
case USBD_IDX_CONFIG_STR:
pbuf = pdev->dev.usr_device->GetConfigurationStrDescriptor(pdev->cfg.speed, &len);
break;
case USBD_IDX_INTERFACE_STR:
pbuf = pdev->dev.usr_device->GetInterfaceStrDescriptor(pdev->cfg.speed, &len);
break;
//注意是这里
case USBD_IDX_OS_STR:
pbuf = pdev->dev.usr_device->GetOSStrDescriptor(pdev->cfg.speed, &len);
break;
default:
#ifdef USB_SUPPORT_USER_STRING_DESC
pbuf = pdev->dev.class_cb->GetUsrStrDescriptor(pdev->cfg.speed, (req->wValue) , &len);
break;
#else
USBD_CtlError(pdev , req);
return;
#endif /* USBD_CtlError(pdev , req)*/
}
break;
case USB_DESC_TYPE_DEVICE_QUALIFIER:
#ifdef USB_OTG_HS_CORE
if(pdev->cfg.speed == USB_OTG_SPEED_HIGH )
{
pbuf = (uint8_t *)pdev->dev.class_cb->GetConfigDescriptor(pdev->cfg.speed, &len);
USBD_DeviceQualifierDesc[4]= pbuf[14];
USBD_DeviceQualifierDesc[5]= pbuf[15];
USBD_DeviceQualifierDesc[6]= pbuf[16];
pbuf = USBD_DeviceQualifierDesc;
len = USB_LEN_DEV_QUALIFIER_DESC;
break;
}
else
{
USBD_CtlError(pdev , req);
return;
}
#else
USBD_CtlError(pdev , req);
return;
#endif
case USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION:
#ifdef USB_OTG_HS_CORE
if(pdev->cfg.speed == USB_OTG_SPEED_HIGH )
{
pbuf = (uint8_t *)pdev->dev.class_cb->GetOtherConfigDescriptor(pdev->cfg.speed, &len);
pbuf[1] = USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION;
break;
}
else
{
USBD_CtlError(pdev , req);
return;
}
#else
USBD_CtlError(pdev , req);
return;
#endif
default:
USBD_CtlError(pdev , req);
return;
}
if((len != 0)&& (req->wLength != 0))
{
len = MIN(len , req->wLength);
USBD_CtlSendData (pdev,
pbuf,
len);
}
}
USBD_IDX_OS_STR在usbd_def.h里添加定义EE请求标志
#define USBD_IDX_OS_STR 0xEE
#define USB_LEN_DEV_QUALIFIER_DESC 0x0A
#define USB_LEN_DEV_DESC 0x12
#define USB_LEN_CFG_DESC 0x09
#define USB_LEN_IF_DESC 0x09
#define USB_LEN_EP_DESC 0x07
#define USB_LEN_OTG_DESC 0x03
#define USB_LEN_OS_DESC 0x12
#define USBD_IDX_LANGID_STR 0x00
#define USBD_IDX_MFC_STR 0x01
#define USBD_IDX_PRODUCT_STR 0x02
#define USBD_IDX_SERIAL_STR 0x03
#define USBD_IDX_CONFIG_STR 0x04
#define USBD_IDX_INTERFACE_STR 0x05
#define USBD_IDX_OS_STR 0xEE //添加0xEE请求标志
到此,编译下载到板子上,连接上USB到PC 电脑,WIN10的设备管理能识别到驱动了,在驱动上点右键展性->事件,可以看到已经识别为WINUSB驱动,关于WINUSB更详细的是否正确识别驱动,还得看注册表相关的键值,可以到如下地址看更详细的说明
使用微软系统描述符1.0制作免驱动自定义USB设备 - USB中文网 (usbzh)
这个连接上的关于如何查询 WIN系统 是否正确识别到你的WINUSB设备相关如下 :
如果OS字符串描述符获取成功,会在注册表的这个位位置
[\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\usbflags]
建立一个以设备VID,PID,版本号为索引的项。 并在此项中建立一个osvc字段,字段值为01,<上报的vendor code>。
例如设备描述符中的idVendor为0x0483,idProduct为0x0001,bcdDevice为0x0100,OS字符串描述符中vendor code为0x17。注册表的[\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\usbflags]中会建立一个名为048300010100的项,里面会建立一个osvc字段,字段值为01 17。如下图所示
如果注册表中已经有了这个字段,系统将不会再发起OS字符串描述符的获取请求。
这里其实就是说如果使用同一个PID和VID和GUID,但是板子不相同了,即SerialNumber号不一样,再次连接电脑 时,由于注册表已经有osvc字段,这可能会让WINUSB驱动不正确的安装这块新的板子驱动了,能识别为WINUSB设备,但DeviceInterfaceGUID无法被 正确识别,这样无法直接用GUID作为WINUSB 路径搜索来读写设备,此时可以把对应的设备VID,PID,版本号为索引的项删除,如上例子的,0483000010100,这个目录在注册表上删除,重新连接USB,就能正确识别驱动,而且能识别到DeviceInterfaceGUID
再看这说明:
为了能让WinUSB设备被应用层软件访问,需要设置设备各个接口的DeviceInterfaceGUIDs,上述的描述符请求成功后,会在注册表的[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\VID_0483&PID_0001\TUSB123456\Device Parameters]
项中建立一个名为DeviceInterfaceGUIDs的键,内容为我们上报的Guid。其中VID和PID为我们设备的VID和PID,TUSB123456为设备的序列号。根据这个DeviceInterfaceGUIDs中的GUID,就可以通过Windows的API发现并打开设备接口。
仔细看上图会发现一个问题,我们上报的数据类型是1,应该是REG_SZ,而且属性名字是DeviceInterfaceGUID,在这里类型被替换成了REG_MULTI_SZ,名称也变成了DeviceInterfaceGUIDs。这个地方可能是兼容性的原因,Windows系统帮我们处理了。如果我们直接上报成类型为REG_MULTI_SZ的DeviceInterfaceGUIDs也是可以的。
这里说明在点开你设备对应的目录后,正确安装上WINUSB驱动后,会生成DeviceInterfaceGUIDs,对应的值就是WINUSB相应的USBD_ExtPropertiesDesc描述符里的GUID
如果不出现 DeviceInterfaceGUID,证明没被 正确识别,按刚上面连接里的操作即可
源码:
STM32F407实现USBBULK传输+WINUSB免驱资源-CSDN文库
另外还有一个可以自动生成INF的工具,连接如下
GitHub - xtoolbox/Introduction: An introduction to the xtoolbox
这个工具生成INF驱动安装包,但GUID无法直接设定,生成的签名只限于软件自动生成的GUID,后来想到了解决办法
这个工具生成INF安装包只是为了能在XP,WIN7系统上而用的,WIN8.1以上系统只要USB设备加入了MS OS字符识别WINUSB驱动后是完全免驱动的,插入就能自动安装好WINUSB驱动了,然后就可以用WINUSBAPI读写设备
XToolbox 生成INF安装包后软件的目录里多出一个output文件夹,这个文件夹就是安装包,把里面的签名cat文件删除掉,接着的INF文件打开,修改DeviceGUID为自己所使用的GUID,保存关闭,然后电脑上的开始-运行-CMD,在CMD里CD 目录转到当前放置gencat.exe的目录,输入如下执行命令
gencat.exe output\USBXX.cat USBXX output\USBXX.inf output\x86\WdfCoInstaller01011.dll output\x86\winusbcoinstaller2.dll output\amd64\winusbcoinstaller2.dll
三处名字USBXX只是这里的展示用法所标的名字,实际按所生成时的inf文件名字来写这条命令,执行完毕后,会再次生成的签名cat文件,如果不这样操作,直接按工具生成的驱动安装包里的INF文件修改DeviceGUID是不行了,因为签名里也包含了DeviceGUID部分的,所以必须 得修改后重新生成签名cat文件才行.
主要是DeviceInterfaceGUID是用来使用WINUSB的API取得设备路径时要使用的,简直比HID要方便得多了,所以按自行生成的GUID来做这个驱动才符合情况啊
还可以通过MS OS 2.0 标准的BOS描述符进行WINUSB驱动安装,按MS说明应该比1.0识别起来更稳定,但只能WIN8.1系统 以上支持。
只要在设备描述符的bcdUSB大于等于0x0210 ,WIN系统 就会发送请字0x0F,请求BOS描述符,在BOS描述符的字段vendor code和wIndex为7进行请求OS 2.0 描述符集,返回正确的OS 2.0 描述符集后就能被 WIN系统 正确识别为WINUSB设备了,MS OS 2.0 只能WIN8.1以上的系统 支持,XP,WIN7系统请使用MS OS 1.0,而且还要通过INF文件安装WINUSB驱动,具体到MS官网了解
针对 USB 设备的 Microsoft OS 描述符 - Windows drivers | Microsoft Learn
Microsoft OS 2.0 描述符规范 - Windows drivers | Microsoft Learn
Microsoft OS 1.0 描述符规范 - Windows drivers | Microsoft Learn
使用微软系统描述符2.0制作免驱动自定义USB设备 - USB中文网 (usbzh)
要实现 MS OS 2.0的方法与MS OS 1.0完全是一样的,通过请求字节返回相应的描述符号即可
还是在usbd_req.c文件里修改,实际上已经自带这个BOS请求的代码,可以看到USBD_GetDescriptor函数里的
#if (USBD_LPM_ENABLED == 1)
case USB_DESC_TYPE_BOS:
pbuf = pdev->pDesc->GetBOSDescriptor(pdev->dev_speed, &len);
break;
#endif
修改为如下:
#if (USBD_LPM_ENABLED == 1)
case USB_DESC_TYPE_BOS:
pbuf = pdev->dev.usr_device->GetBOSDescriptor(pdev->cfg.speed, &len);
break;
#endif
要自行定义USBD_LPM_ENABLED为1,后面会说
pbuf = pdev->pDesc->GetBOSDescriptor(pdev->dev_speed, &len);
修改为pbuf = pdev->dev.usr_device->GetBOSDescriptor(pdev->dev_speed, &len);
然后要给GetBOSDescriptor函数指针写上实现代码,进行返回所请求的BOS描述符号即可
首先usb_core.h文件里,添加MS OS 2.0描述符的返回函数指针结构
typedef struct _Device_TypeDef
{
uint8_t *(*GetDeviceDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetLangIDStrDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetManufacturerStrDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetProductStrDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetSerialStrDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetConfigurationStrDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetInterfaceStrDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetOSStrDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetExtPropertiesFeatureDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetExtCompatIDFeatureDescriptor)( uint8_t speed , uint16_t *length);
#if (USBD_LPM_ENABLED == 1)//MS OS 2.0 返回描述符函数指针
uint8_t *(*GetBOSDescriptor)( uint8_t speed , uint16_t *length);
uint8_t *(*GetWCIDDescriptor)( uint8_t speed , uint16_t *length);
#endif
} USBD_DEVICE, *pUSBD_DEVICE;
usbd.desc.h文件里添加两个函数声明
uint8_t * USBD_USR_DeviceDescriptor( uint8_t speed , uint16_t *length);
uint8_t * USBD_USR_LangIDStrDescriptor( uint8_t speed , uint16_t *length);
uint8_t * USBD_USR_ManufacturerStrDescriptor ( uint8_t speed , uint16_t *length);
uint8_t * USBD_USR_ProductStrDescriptor ( uint8_t speed , uint16_t *length);
uint8_t * USBD_USR_SerialStrDescriptor( uint8_t speed , uint16_t *length);
uint8_t * USBD_USR_ConfigStrDescriptor( uint8_t speed , uint16_t *length);
uint8_t * USBD_USR_InterfaceStrDescriptor( uint8_t speed , uint16_t *length);
uint8_t * USBD_USR_OSStrDescriptor( uint8_t speed , uint16_t *length);
uint8_t * USBD_USR_ExtPropertiesFeatureDescriptor( uint8_t speed , uint16_t *length);
uint8_t * USBD_USR_ExtCompatIDFeatureDescriptor( uint8_t speed , uint16_t *length);
//添加MS OS2.0描述符返回函数声明
uint8_t * USBD_USR_BOSDescriptor( uint8_t speed , uint16_t *length);
uint8_t * USBD_USR_WCIDDescriptor( uint8_t speed , uint16_t *length);
usbd_desc.c文件里添加MS OS2.0描述符和返回描述符的函数实现和相应的指针引用添加
USBD_DEVICE USR_desc = {
USBD_USR_DeviceDescriptor,
USBD_USR_LangIDStrDescriptor,
USBD_USR_ManufacturerStrDescriptor,
USBD_USR_ProductStrDescriptor,
USBD_USR_SerialStrDescriptor,
USBD_USR_ConfigStrDescriptor,
USBD_USR_InterfaceStrDescriptor,
USBD_USR_OSStrDescriptor,
USBD_USR_ExtPropertiesFeatureDescriptor,
USBD_USR_ExtCompatIDFeatureDescriptor,
//添加MS OS 2.0 返回描述符函数指针引用
#if (USBD_LPM_ENABLED == 1)
USBD_USR_BOSDescriptor,
USBD_USR_WCIDDescriptor,
#endif
};
//添加描述符定义
__ALIGN_BEGIN const uint8_t WINUSB20_WCIDBOS[33] __ALIGN_END = {
///
/// WCID20 BOS descriptor
///
0x05, /* bLength */
USB_DESC_TYPE_BOS, /* bDescriptorType */
0x21, 0x00, /* wTotalLength */
0x01, /* bNumDeviceCaps */
///
/// WCID20 device capability descriptor
///
0x1c, /* bLength */
0x10, /* bDescriptorType */
0x05, /* bDevCapabilityType */
0x00, /* bReserved */
0xdf, 0x60, 0xdd, 0xd8, 0x89, 0x45, 0xc7, 0x4c, /* bPlatformCapabilityUUID_16 */
0x9c, 0xd2, 0x65, 0x9d, 0x9e, 0x64, 0x8a, 0x9f, /* bPlatformCapabilityUUID_16 */
0x00, 0x00, 0x03, 0x06, /* dwWindowsVersion */
LOBYTE(WINUSB20_WCID_DESC_SET_SIZE), HIBYTE(WINUSB20_WCID_DESC_SET_SIZE),/* wDescriptorSetTotalLength */
USB_REQ_GET_OS_FEATURE_DESCRIPTOR, /* bVendorCode */
0x00, /* bAltEnumCode */
};
__ALIGN_BEGIN const uint8_t WINUSB20_WCIDDescriptorSet[WINUSB20_WCID_DESC_SET_SIZE] __ALIGN_END = {
///
/// WCID20 descriptor set descriptor
///
0x0a, 0x00, /* wLength */
0x00, 0x00, /* wDescriptorType */
0x00, 0x00, 0x03, 0x06, /* dwWindowsVersion */
0xa2, 0x00, /* wDescriptorSetTotalLength */
///
/// WCID20 compatible ID descriptor
///
0x14, 0x00, /* wLength */
0x03, 0x00, /* wDescriptorType */
/* WINUSB */
'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, /* cCID_8 */
/* */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* cSubCID_8 */
///
/// WCID20 registry property descriptor
///
0x84, 0x00, /* wLength */
0x04, 0x00, /* wDescriptorType */
0x07, 0x00, /* wPropertyDataType */
0x2a, 0x00, /* wPropertyNameLength */
/* DeviceInterfaceGUIDs */
'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, /* wcPropertyName_21 */
'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, /* wcPropertyName_21 */
't', 0x00, 'e', 0x00, 'r', 0x00, 'f', 0x00, /* wcPropertyName_21 */
'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, /* wcPropertyName_21 */
'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, /* wcPropertyName_21 */
0x00, 0x00, /* wcPropertyName_21 */
0x50, 0x00, /* wPropertyDataLength */
/* {27D99748-F3F8-4c52-A339-99B71A94EE6F} */
'{', 0x00, '2', 0x00, '7', 0x00, 'D', 0x00, /* wcPropertyData_40 */
'9', 0x00, '9', 0x00, '7', 0x00, '4', 0x00, /* wcPropertyData_40 */
'8', 0x00, '-', 0x00, 'F', 0x00, '3', 0x00, /* wcPropertyData_40 */
'F', 0x00, '8', 0x00, '-', 0x00, '4', 0x00, /* wcPropertyData_40 */
'c', 0x00, '5', 0x00, '2', 0x00, '-', 0x00, /* wcPropertyData_40 */
'A', 0x00, '3', 0x00, '3', 0x00, '9', 0x00, /* wcPropertyData_40 */
'-', 0x00, '9', 0x00, '9', 0x00, 'B', 0x00, /* wcPropertyData_40 */
'7', 0x00, '1', 0x00, 'A', 0x00, '9', 0x00, /* wcPropertyData_40 */
'4', 0x00, 'E', 0x00, 'E', 0x00, '6', 0x00, /* wcPropertyData_40 */
'F', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00 /* wcPropertyData_40 */
};
//添加返回描述符号的函数实现
uint8_t * USBD_USR_BOSDescriptor( uint8_t speed , uint16_t *length)
{
*length = sizeof(WINUSB20_WCIDBOS);
return (uint8_t*)WINUSB20_WCIDBOS;
}
uint8_t * USBD_USR_WCIDDescriptor( uint8_t speed , uint16_t *length)
{
*length = sizeof(WINUSB20_WCIDDescriptorSet);
return (uint8_t*)WINUSB20_WCIDDescriptorSet;
}
usbd_req.c文件里的USBD_VendDevReq函数添加ms os 2.0的7号请求返回
void USBD_VendDevReq (USB_OTG_CORE_HANDLE *pdev, USB_SETUP_REQ *req)
{
uint16_t len;
uint8_t *pbuf;
len = req->wLength ;
switch (req->wIndex)
{
case USB_DESC_TYPE_OS_FEATURE_EXT_PROPERTIES:
pbuf = pdev->dev.usr_device->GetExtCompatIDFeatureDescriptor(pdev->cfg.speed, &len);
break;
case USB_DESC_TYPE_OS_FEATURE_EXT_COMPAT_ID:
pbuf = pdev->dev.usr_device->GetExtPropertiesFeatureDescriptor(pdev->cfg.speed, &len);
break;
#if (USBD_LPM_ENABLED == 1)
case MS_OS_20_DESCRIPTOR_INDEX: //MS OS 2.0 的7号请求
pbuf = pdev->dev.usr_device->GetWCIDDescriptor(pdev->cfg.speed,&len);
break;
#endif
}
if((len != 0)&& (req->wLength != 0))
{
len = MIN(len , req->wLength);
USBD_CtlSendData (pdev,
pbuf,
len);
}
}
这函数已经修改为void过程,因为调用不用返回,自行修改一下函数声明部分,要不编译会报错
在usbd_def.h里添加如下的定义
#define MS_OS_20_DESCRIPTOR_INDEX 7
#define USB_DESC_TYPE_OS_FEATURE_EXT_PROPERTIES 4
#define USB_DESC_TYPE_OS_FEATURE_EXT_COMPAT_ID 5
#define MS_OS_20_DESCRIPTOR_INDEX 7
如此MS OS2.0就基本添加完成了
但还得在usb_conf.h文件里添加定义
/****************** USB OTG MISC CONFIGURATION ********************************/
//#define VBUS_SENSING_ENABLED
/****************** USB OTG MODE CONFIGURATION ********************************/
/* #define USE_HOST_MODE */
#define USE_DEVICE_MODE
/* #define USE_OTG_MODE */
//添加MS OS 2.0与1.0的切换
//#define MS_OS_20
#if defined(MS_OS_20)
#define bcdUSB 0x0210
#define USBD_LPM_ENABLED 1
#else
#define bcdUSB 0x0200
#define USBD_LPM_ENABLED 0
#endif
要使用MS OS2.0就把//#define MS_OS_20的屏蔽去掉,定义#define MS_OS_20后再编译就是MS OS2.0的WINUSb了,屏蔽时编译后为MS OS1.0的
别忘记还得把设备描述符这里的bcdUSB字段修改一下,跳转到usbd_desc.c文件
__ALIGN_BEGIN uint8_t USBD_DeviceDesc[USB_SIZ_DEVICE_DESC] __ALIGN_END = {
0x12, /* bLength */
USB_DEVICE_DESCRIPTOR_TYPE, /* bDescriptorType */
LOBYTE(bcdUSB), /* bcdUSB */
HIBYTE(bcdUSB),
0xFF, /* bDeviceClass */
0x00, /* bDeviceSubClass */
0x00, /* bDeviceProtocol */
USB_OTG_MAX_EP0_SIZE, /* bMaxPacketSize */
LOBYTE(USBD_VID), /* idVendor */
HIBYTE(USBD_VID), /* idVendor */
LOBYTE(USBD_PID), /* idVendor */
HIBYTE(USBD_PID), /* idVendor */
0x00, /* bcdDevice rel. 2.00 */
0x02,
USBD_IDX_MFC_STR, /* Index of manufacturer string */
USBD_IDX_PRODUCT_STR, /* Index of product string */
USBD_IDX_SERIAL_STR, /* Index of serial number string */
USBD_CFG_MAX_NUM /* bNumConfigurations */
}; /* USB_DeviceDescriptor */
这样一来,只要在设备描述符号的bcdUSB 为0x0200时会自动按MS OS 1.0返回字符 描述符号,bcdUSB 为0x0210时,自动返回MS OS 2.0描述符了,实际源码 就不再上传了,按上面一步步来就可以了,另外说明一下,如果用1.0描述符安装 过驱动 后,再刷入2.0描述符再连接电脑时可能无法自动安装上DeviceInterfaceGUID的,此时删除掉驱动 ,再拨出插入即可正确识别了,从2.0到1.0反过来也是这样操作就好了
实际上2.0与1.0的切换,可以通过代码在开启USB前通过IO引脚进行选择使用
补充一下,在Keil uVision5的目录 Arm\Packs\Keil\MDK-Middleware\7.8.0\Utilities\WinUSB_Test
有一个WinUSB_Test工具,只要输入USB的DeviceInterfaceGUID就能识别到设备了,可以进行端点读写访问,但使用起来并不好使,有源码可以按着自己觉得合适使用方式的来修改代码重新编译一下就好了,我用VS2013打开,说是旧版本的代码,得点一下确认代码版本升级,打开工程后资源文件部分无法访问,报 error RC2104,由于升级时把*.rc中的#include <windows.h>去掉了,加了一个resouce.h,在*.rc中将#include <windows.h>加上就可以了
这是利用HID例子进行修改,其实也可以用CDC例子进行修改,或者复合设备进行修改支持WINUSB驱动,举一反三就能更深刻的理解这个USB库的使用了,USB固件库看着一堆的文件感觉有点复杂,实际上一个个文件多看看理解 一下,也不是哪么难的
STM32_USB-FS-Device_Lib_V4.1.0 库为F10X,F303,L1XX,F37X支持批量传输和WINUSB其实也是差不多一样的,CubeMX生成的的代码进行修改其实也是差不多一样
到此作为一个笔记式的记录,从移植到改造批量传输,再到WINUSB免驱,经过多次修改,已经足够详细了,别说你弄出来电脑 无法识别USB设备啊,哈哈!望君成功....
待以后忘记了也可以回来作为一个回忆备忘录
版权声明:本文标题:STM32F407实现USB BULK传输+WINUSB免驱 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/jishu/1726376496h947998.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论