STM32之USB host库:USBH_Process学习理解

本文详细解析了USB主机状态机的运作流程,包括从设备连接到枚举完成、类请求处理等多个阶段的状态转换及关键函数调用。通过状态机的设计确保了USB主机能够正确高效地与外设交互。

void USBH_Process(USB_OTG_CORE_HANDLE *pdev , USBH_HOST *phost)
{
  volatile USBH_Status status = USBH_FAIL;
 
 
 
  if ((HCD_IsDeviceConnected(pdev) == 0)&& (phost->gState != HOST_IDLE))  // 若是 未连接&&不空闲状态下
  {
    if(phost->gState != HOST_DEV_DISCONNECTED)
    {
      phost->gState = HOST_DEV_DISCONNECTED;
    }
  }
   
  switch (phost->gState)
  {
 
  case HOST_IDLE :    //空闲状态
   
    if (HCD_IsDeviceConnected(pdev))   //如果 设备连接上了
    {
      phost->gState = HOST_DEV_ATTACHED;  //就把状态转入 -> HOST_DEV_ATTACHED
      USB_OTG_BSP_mDelay(100);     //延时
    }
    break;
  
  case HOST_DEV_ATTACHED :   //接触状态(但是没有连接上)
   
    phost->usr_cb->DeviceAttached();  //调用usbh_usr.c中的用户回调函数 ->USBH_USR_DeviceAttached()
    phost->Control.hc_num_out = USBH_Alloc_Channel(pdev, 0x00);    //给USB终端设备分配的传出通道
    phost->Control.hc_num_in = USBH_Alloc_Channel(pdev, 0x80);      //分配USB设备的传入通道
 
   
    if ( HCD_ResetPort(pdev) == 0)  //如果 检测到 复位命令
    {
      phost->usr_cb->ResetDevice();  //那么执行 USB复位,不是主机复位!
     
     
      phost->device_prop.speed = HCD_GetCurrentSpeed(pdev);   //获得当前设备的速度
     
      phost->gState = HOST_ENUMERATION;        //然后 状态转入 -> HOST_ENUMERATION
      phost->usr_cb->DeviceSpeedDetected(phost->device_prop.speed);  //调用用户回调函数,在LCD上显示设备速度信息
       
     
      USBH_Open_Channel (pdev,      //开启入口通道
                           phost->Control.hc_num_in,
                           phost->device_prop.address,
                           phost->device_prop.speed,
                           EP_TYPE_CTRL,
                           phost->Control.ep0size);
     
     
      USBH_Open_Channel (pdev,      //开启出口通道
                           phost->Control.hc_num_out,
                           phost->device_prop.address,
                           phost->device_prop.speed,
                           EP_TYPE_CTRL,
                           phost->Control.ep0size);         
   }
    break;
   
  case HOST_ENUMERATION:          //枚举进程
     
    if ( USBH_HandleEnum(pdev , phost) == USBH_OK)  //如果枚举完成
    {
     
     
     
      phost->usr_cb->EnumerationDone();   //就执行用户回调,可以在显示器上显示枚举完毕信息
     
      phost->gState  = HOST_USR_INPUT;       //然后转入下一个状态 -> HOST_USR_INPUT
    }
    break;
   
  case HOST_USR_INPUT:       //用户输入行为
   
    if ( phost->usr_cb->UserInput() == USBH_USR_RESP_OK) //如果对用户输入有响应
    {
      if((phost->class_cb->Init(pdev, phost))\
        == USBH_OK)
      {
        phost->gState  = HOST_CLASS_REQUEST;      //然后转入下一个状态 -> HOST_CLASS_REQUEST
      }    
    }  
    break;
   
  case HOST_CLASS_REQUEST: 
   
    status = phost->class_cb->Requests(pdev, phost);
   
     if(status == USBH_OK)    //如果类请求事件完成
     {
       phost->gState  = HOST_CLASS;   //则进入下一状态 -> HOST_CLASS
     } 
    
     else         //否则
     {
       USBH_ErrorHandle(phost, status);   //主机进行错误处理
     }
 
   
    break;   
  case HOST_CLASS:      //主机类
   
    status = phost->class_cb->Machine(pdev, phost);
    USBH_ErrorHandle(phost, status);
    break;      
   
  case HOST_CTRL_XFER:
   
    USBH_HandleControl(pdev, phost);   
    break;
   
  case HOST_SUSPENDED:
    break;
 
  case HOST_ERROR_STATE:
   
    USBH_DeInit(pdev, phost);    //重初始化
    phost->usr_cb->DeInit();    //执行用户回调:重初始化
    phost->class_cb->DeInit(pdev, &phost->device_prop);
    break;
   
  case HOST_DEV_DISCONNECTED :
   
   
    phost->usr_cb->DeviceDisconnected();  //用户回调:在显示器上显示->设备未连接
   
   
    USBH_DeInit(pdev, phost);  //主机重新初始化,进入新的枚举过程
    phost->usr_cb->DeInit(); //执行用户回调:重初始化
    phost->class_cb->DeInit(pdev, &phost->device_prop);
    USBH_DeAllocate_AllChannel(pdev);     //收回通道
    phost->gState = HOST_IDLE;     //主机回到初始状态 -> HOST_IDLE(空闲)
   
    break;
   
  default :
    break;
  }

}

STM32 HOST USB代码 u8 USH_User_App(void) { u32 total,free; u8 res=0; Show_Str(30,140,200,16,"É豸Á¬½Ó³É¹¦!.",16,0); res=exf_getfree("2:",&total,&free); if(res==0) { POINT_COLOR=BLUE;//ÉèÖÃ×ÖÌåΪÀ¶É« LCD_ShowString(30,160,200,16,16,"FATFS OK!"); LCD_ShowString(30,180,200,16,16,"U Disk Total Size: MB"); LCD_ShowString(30,200,200,16,16,"U Disk Free Size: MB"); LCD_ShowNum(174,180,total>>10,5,16); //ÏÔʾUÅÌ×ÜÈÝÁ¿ MB LCD_ShowNum(174,200,free>>10,5,16); } while(HCD_IsDeviceConnected(&USB_OTG_Core))//É豸Á¬½Ó³É¹¦ { LED1=!LED1; delay_ms(200); } POINT_COLOR=RED;//ÉèÖÃ×ÖÌåΪºìÉ« Show_Str(30,140,200,16,"É豸Á¬½ÓÖÐ...",16,0); LCD_Fill(30,160,239,220,WHITE); return res; } int main(void) { u8 t; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//ÉèÖÃϵͳÖжÏÓÅÏȼ¶·Ö×é2 delay_init(168); //³õʼ»¯ÑÓʱº¯Êý uart_init(115200); //³õʼ»¯´®¿Ú²¨ÌØÂÊΪ115200 LED_Init(); //³õʼ»¯ÓëLEDÁ¬½ÓµÄÓ²¼þ½Ó¿Ú KEY_Init(); //°´¼ü LCD_Init(); //³õʼ»¯LCD W25QXX_Init(); //SPI FLASH³õʼ»¯ usmart_dev.init(84); //³õʼ»¯USMART my_mem_init(SRAMIN); //³õʼ»¯ÄÚ²¿ÄÚ´æ³Ø exfuns_init(); //ΪfatfsÏà¹Ø±äÁ¿ÉêÇëÄÚ´æ piclib_init(); //³õʼ»¯»­Í¼ f_mount(fs[0],"0:",1); //¹ÒÔØSD¿¨ f_mount(fs[1],"1:",1); //¹ÒÔØSD¿¨ f_mount(fs[2],"2:",1); //¹ÒÔØUÅÌ POINT_COLOR=RED; while(font_init()) //¼ì²é×Ö¿â { LCD_ShowString(60,50,200,16,16,"Font Error!"); delay_ms(200); LCD_Fill(60,50,240,66,WHITE);//Çå³ýÏÔʾ delay_ms(200); } Show_Str(30,50,200,16,"̽Ë÷ÕßSTM32F407¿ª·¢°å",16,0); Show_Str(30,70,200,16,"USB UÅÌʵÑé",16,0); Show_Str(30,90,200,16,"2014Äê7ÔÂ22ÈÕ",16,0); Show_Str(30,110,200,16,"ÕýµãÔ­×Ó@ALIENTEK",16,0); Show_Str(30,140,200,16,"É豸Á¬½ÓÖÐ...",16,0); //³õʼ»¯USBÖ÷»ú USBH_Init(&USB_OTG_Core,USB_OTG_FS_CORE_ID,&USB_Host,&USBH_MSC_cb,&USR_Callbacks); while(1) { USBH_Process(&USB_OTG_Core, &USB_Host); delay_ms(1); t++; if(t==200) { LED0=!LED0; t=0; } } }
<think>好的,我现在需要解决用户在STM32中无法进入USBH_HID_EventCallback函数的问题。用户提到他们参考了之前的引用内容,特别是关于配置USB HID主机和回调函数的部分。我需要结合这些引用和常见的问题点来分析可能的原因和解决方案。 首先,我需要回忆USB HID主机的工作流程。根据引用[2]和[3],STM32使用CubeMX生成代码时,USBH_HID_EventCallback是在HID设备事件发生时被调用的,比如数据接收。如果这个回调函数没有被执行,可能意味着事件没有被正确触发或者配置有误。 接下来,可能的原因有很多,我需要逐步排查: 1. **USB硬件连接问题**:检查物理连接是否正确,包括USB接口、电源和线路。如果设备没有被正确识别,可能无法触发回调。 2. **CubeMX配置错误**:引用[3]提到需要正确配置USB_OTG_HS为Host Only,并选择HID类。如果配置错误,比如PHY设置不正确或中断未启用,可能导致初始化失败,进而无法触发回调。 3. **中断配置问题**:USB主机需要正确的中断配置。用户提供的引用[1]中有一个发送中断数据的函数,但这里可能涉及接收中断的配置。如果中断未被正确启用或优先级设置不当,可能导致事件无法触发回调。 4. **堆栈大小不足**:引用[3]提到任务堆栈需要配置为512,默认的128会导致HardFault。如果相关任务的堆栈太小,可能导致系统崩溃或无法处理事件,从而不执行回调。 5. **设备枚举失败**:USB主机在枚举HID设备时,如果描述符解析错误或设备不被支持,可能导致无法进入回调函数。需要检查USB日志或使用调试器查看枚举状态。 6. **回调函数注册问题**:确保USBH_RegisterClass正确注册了HID类,并且事件回调被正确设置。如果注册过程有误,回调函数不会被调用。 7. **时钟配置错误**:USB模块需要正确的时钟源和频率。如果时钟配置错误,USB模块无法正常工作,导致整个HID主机功能失效。 针对这些可能的原因,我需要逐一提出解决方案,比如检查CubeMX配置中的PHY设置、确认中断配置、增加任务堆栈、检查设备枚举日志、验证回调注册等。此外,用户可能需要使用调试工具,如设置断点或查看USB状态寄存器,来进一步诊断问题。 最后,还需要考虑用户的具体情况,比如他们是否按照引用中的步骤正确修改了代码,特别是USBH_UsrLog的宏定义替换,以及是否正确处理了接收到的数据。如果有任何一步遗漏或配置错误,都可能导致回调函数无法执行。</think>### STM32 USBH_HID_EventCallback不执行的原因分析与解决方案 #### 一、问题原因分析 1. **USB硬件连接异常** - 物理接口(如VBUS、D+、D-)接触不良或未正确供电[^3]。 - 未使用符合HID协议的设备(如非标准鼠标/键盘)[^2]。 2. **CubeMX配置错误** - **USB模式选择错误**:未将USB_OTG_HS/IP配置为`Host Only`或未选择`Internal FS Phy`[^3]。 - **HID类未正确启用**:未在`USB_HOST`配置中选择`Human Interface Host Class (HID)`[^3]。 - **中断未开启**:USB全局中断或HID事件中断未启用。 3. **FreeRTOS任务堆栈不足** - CMSIS-RTOS任务堆栈默认值(128)过小,需调整为512[^3],否则触发HardFault导致回调无法执行。 4. **设备枚举失败** - USB主机未能正确识别HID设备,可能因描述符解析错误或设备兼容性问题[^2]。 5. **回调函数注册缺失** - 未通过`USBH_RegisterClass`注册HID类驱动,或未在代码中显式绑定事件回调函数。 6. **时钟配置错误** - USB模块时钟(如48MHz)未正确配置,导致USB核心无法正常工作。 --- #### 二、解决方案步骤 1. **硬件检查** - 测量VBUS电压(5V),检查D+/D-线路阻抗,确保设备插入后LED状态正常。 2. **CubeMX配置验证** - **USB_OTG_HS配置**: ```c USB_OTG_HS Mode: Host Only PHY Interface: Internal FS PHY ``` - **USB_HOST配置**: ```c Class For HS IP: HID HID_MOUSE / HID_KEYBOARD: Enabled ``` - **中断配置**: 启用`USB_OTG_HS`全局中断,并设置合理优先级(如高于FreeRTOS任务)。 3. **调整FreeRTOS堆栈** 在`CubeMX > CMSIS-RTOS > Config parameters`中: - 将`Default Task Stack Size`从128修改为512[^3]。 4. **添加调试日志** 在`USBH_User.c`中启用日志输出,检查枚举状态: ```c #define USBH_UsrLog(...) printf("USBH: " __VA_ARGS__) ``` 若日志显示`USBH_ERR_UNSUPPORTED`,需检查设备描述符兼容性。 5. **强制触发HID事件** 在`main.c`中添加手动事件检测(调试用): ```c if (hUsbHostHID.State == HOST_USER_CLASS_ACTIVE) { USBH_Process(&hUsbHostHID); } ``` 6. **验证回调函数绑定** 确保在`USBH_HID_Init`中注册了事件回调: ```c USBH_ClassTypeDef hid_driver = { .Init = USBH_HID_Init, .DeInit = USBH_HID_DeInit, .Requests = USBH_HID_ClassRequest, .Process = USBH_HID_Process, .SOFProcess = USBH_HID_SOFProcess, .EventCallback = USBH_HID_EventCallback // 必须存在 }; ``` 7. **时钟配置检查** 使用STM32CubeMX时钟树工具,确认USB时钟源为PLL且输出48MHz: $$ f_{\text{USB}} = \frac{\text{PLLCLK}}{\text{分频系数}} = 48\,\text{MHz} $$ --- #### 三、关键代码片段 1. **事件回调函数实现**(参考引用[2]): ```c void USBH_HID_EventCallback(USBH_HandleTypeDef *phost, uint8_t event) { if (event == HID_EVENT_INPUT_REPORT) { // 确保事件类型匹配 HID_MOUSE_Info_TypeDef *mouse_info = USBH_HID_GetMouseInfo(phost); if (mouse_info != NULL) { Mousedataprocess(mouse_info); // 用户数据处理函数 } } } ``` 2. **USB主机任务调度**(FreeRTOS): ```c void StartHostTask(void const *argument) { USBH_Init(&hUsbHostHID, USBH_UserProcess, HOST_FS); USBH_RegisterClass(&hUsbHostHID, &hid_driver); USBH_Start(&hUsbHostHID); while (1) { USBH_Process(&hUsbHostHID); // 必须周期性调用 osDelay(10); } } ``` --- #### 四、验证方法 1. **使用逻辑分析仪**捕获USB D+/D-信号,确认数据包交互正常。 2. **断点调试**:在`USBH_HID_EventCallback`入口设置断点,检查调用堆栈。 3. **USB协议分析工具**(如Wireshark + USBPCap)抓取设备描述符,确认HID接口被正确识别。 ---
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值