STM32 USB Host 同时连接多个设备样例(如鼠标和键盘)--原创

    技术2024-05-20  82

    STM32 USB Host 同时连接多个设备样例(如鼠标和键盘)

    在网上搜了很多都是USB Host单独连接鼠标或键盘的样例, 而当前很多无线鼠标键盘都是并到一个USB口上的, 也就是同一个USB有两个interface. 而官方提供的USB Host库中只会对一个Interface进行处理, 要么键盘要么鼠标, 所以为了能够同时使用键盘和鼠标要对USB Host库进行修改才行. 具体修改如下: 首先使用CubeMX创建了一个带USB HID类功能的工程, 创建步骤很简单网上有很多文档不在这里讨论. 接着进入正题, 修改USB Host库文件.

    usbh_def.h 修改USBH_ClassTypeDef, 使当前Class支持多接口: /* USB Host Class structure */ typedef struct { const char *Name; uint8_t ClassCode; USBH_StatusTypeDef(*Init)(struct _USBH_HandleTypeDef *phost); USBH_StatusTypeDef(*DeInit)(struct _USBH_HandleTypeDef *phost); USBH_StatusTypeDef(*Requests)(struct _USBH_HandleTypeDef *phost); USBH_StatusTypeDef(*BgndProcess)(struct _USBH_HandleTypeDef *phost); USBH_StatusTypeDef(*SOFProcess)(struct _USBH_HandleTypeDef *phost); /* [ */ void *pData[USBH_MAX_NUM_INTERFACES]; /* HID interfaces Handle */ uint8_t InterfaceNum; /* interface 数量 */ __IO uint8_t CurrInterface; /* 当前interface */ /* ] */ } USBH_ClassTypeDef;

    修改HID_HandleTypeDef, 记录interface的协议类型:

    /* Structure for HID process */ typedef struct _HID_Process { uint8_t OutPipe; uint8_t InPipe; HID_StateTypeDef state; uint8_t OutEp; uint8_t InEp; HID_CtlStateTypeDef ctl_state; FIFO_TypeDef fifo; uint8_t *pData; uint16_t length; uint8_t ep_addr; uint16_t poll; uint32_t timer; uint8_t DataReady; HID_DescTypeDef HID_Desc; USBH_StatusTypeDef(* Init)(USBH_HandleTypeDef *phost); /* [ */ uint8_t Protocol; /* 协议类型: HID_KEYBRD_BOOT_CODE / HID_MOUSE_BOOT_CODE */ /* ] */ } HID_HandleTypeDef; usbh_hid.c 修改USBH_HID_InterfaceInit函数, 在初始化的时候判断当前枚举了几个HID接口, 如果有多个接口, 则创建多个HID_HandleTypeDef: static USBH_StatusTypeDef USBH_HID_InterfaceInit(USBH_HandleTypeDef *phost) { USBH_StatusTypeDef status = USBH_OK; HID_HandleTypeDef *HID_Handle; uint8_t max_ep; uint8_t num = 0U; /* [ */ uint8_t boot = HID_KEYBRD_BOOT_CODE; uint8_t interface_num = 0; uint8_t interface; for (interface_num = 0; interface_num < phost->device.CfgDesc.bNumInterfaces; interface_num++) { interface = USBH_FindInterface(phost, phost->pActiveClass->ClassCode, HID_BOOT_CODE, boot); /* ] */ if ((interface == 0xFFU) || (interface >= USBH_MAX_NUM_INTERFACES)) /* No Valid Interface */ { USBH_DbgLog("Cannot Find the interface for %s class.", phost->pActiveClass->Name); status = USBH_FAIL; break; } status = USBH_SelectInterface(phost, interface); if (status != USBH_OK) { return USBH_FAIL; } phost->pActiveClass->pData[interface_num] = (HID_HandleTypeDef *)USBH_malloc(sizeof(HID_HandleTypeDef)); /* [ */ phost->pActiveClass->InterfaceNumber++; phost->pActiveClass->CurrInterface = interface_num; /* ] */ HID_Handle = (HID_HandleTypeDef *) phost->pActiveClass->pData[interface_num]; if (HID_Handle == NULL) { USBH_DbgLog("Cannot allocate memory for HID Handle"); return USBH_FAIL; } /* Initialize hid handler */ USBH_memset(HID_Handle, 0, sizeof(HID_HandleTypeDef)); HID_Handle->state = HID_ERROR; /*Decode Bootclass Protocol: Mouse or Keyboard*/ HID_Handle->Protocol = phost->device.CfgDesc.Itf_Desc[interface].bInterfaceProtocol; ... ... /* 配置完成 */ if (boot == HID_KEYBRD_BOOT_CODE) boot = HID_MOUSE_BOOT_CODE; else boot = HID_KEYBRD_BOOT_CODE; } return status; }

    修改USBH_HID_Process, 在POLL空闲时如果10次连续空闲未收到有效数据则切换接口, 使下次执行Process的时候切换到另一个接口:

    static USBH_StatusTypeDef USBH_HID_Process(USBH_HandleTypeDef *phost) { USBH_StatusTypeDef status = USBH_OK; HID_HandleTypeDef *HID_Handle = (HID_HandleTypeDef *) phost->pActiveClass->pData[phost->pActiveClass->CurrInterface]; uint32_t XferSize; /* [ */ uint8_t interface; uint8_t protocol; static __IO uint16_t num = 0; /* ] */ switch (HID_Handle->state) { ... case HID_POLL: if (USBH_LL_GetURBState(phost, HID_Handle->InPipe) == USBH_URB_DONE) { XferSize = USBH_LL_GetLastXferSize(phost, HID_Handle->InPipe); if ((HID_Handle->DataReady == 0U) && (XferSize != 0U)) { USBH_HID_FifoWrite(&HID_Handle->fifo, HID_Handle->pData, HID_Handle->length); HID_Handle->DataReady = 1U; USBH_HID_EventCallback(phost); #if (USBH_USE_OS == 1U) phost->os_msg = (uint32_t)USBH_URB_EVENT; #if (osCMSIS < 0x20000U) (void)osMessagePut(phost->os_event, phost->os_msg, 0U); #else (void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL); #endif #endif /* 如果检测到有效数据, 则清除空闲计数 [ */ num = 0; /* ] */ } } else { /* 如果空闲计数达到10次, 则切换到其他interface [ */ num++; if (num > 10) { if (phost->pActiveClass->InterfaceNumber > 1) { if (phost->pActiveClass->CurrInterface == 0) phost->pActiveClass->CurrInterface = 1; else phost->pActiveClass->CurrInterface = 0; protocol = ((HID_HandleTypeDef *)phost->pActiveClass->pData[phost->pActiveClass->CurrInterface])->Protocol; interface = USBH_FindInterface(phost, phost->pActiveClass->ClassCode, HID_BOOT_CODE, protocol); USBH_SelectInterface(phost, interface); } num = 0; } /* ] */ /* IN Endpoint Stalled */ if (USBH_LL_GetURBState(phost, HID_Handle->InPipe) == USBH_URB_STALL) { /* Issue Clear Feature on interrupt IN endpoint */ if (USBH_ClrFeature(phost, HID_Handle->ep_addr) == USBH_OK) { /* Change state to issue next IN token */ HID_Handle->state = HID_GET_DATA; } } } break; ... } }

    另外其他涉及到"phost->pActiveClass->pData"的地方全部改为"phost->pActiveClass->pData[phost->pActiveClass->CurrInterface]", 因为pData已经不再是一个结构体指针而是一个结构体指针数组, 主要涉及的文件包括: usbh_hid.c, usbh_hid_keybs.c, usbh_hid_mouse.c 3. 修改键盘和鼠标的驱动 最后就是按照键盘鼠标的协议定义相应的回调函数, 因为不同的无线鼠标键盘有不同的协议, 在此不做描述. 主要涉及的文件包括: usb_host.c, usbh_hid_keybs.c, usbh_hid_mouse.c 做好驱动以后就可以同时使用键盘和鼠标了, 此方法也可适用于USB连接其他多设备场景, 只要USB初始化的时候能够检测到相应的设备.

    测试视频:

    STM32 USB Host同时连接鼠标和键盘演示视频

    转载请注明出处.

    Processed: 0.016, SQL: 9