【官方原创】STM32 USBx Host HID standardalone移植示例 LAT1449

关键字:USBx, Host, standalone

1.设计目的

目前USBx host standalone的官方示例较少,仅有一个。不过使用CubeMX可以快速地生成

USBx Host相关类的示例工程,会很方便大家的开发。这里以NUCLEO-H563为例,实现

USBx Host HID类,大家可以以此为参考移植到其他的USBx Host类。

2.示例移植

官方示例参考代码:

\STM32Cube\Repository\STM32Cube_FW_H5_V1.2.0\Projects\STM32H573I

DK\Applications\USBX\Ux_Host_HID_Standalone

2.1.生成CubeMX工程

新建CubeMX工程 :STM32H563ZIT6U,选择without TrustZone activated. 另外,

CubeMX中未作说明的配置保持默认.

2.1.1 System Core相关配置

在System Core框架下,Cortex_M33标签页面下使用的是HCLK. 如下图:

RCC的标签页下面:由于作为USB Host,所以这里使用外部时钟作为USB的时钟源, 采用

BYPASS Clock Source MCO 引脚输出作为MCU的系统时钟源(来源于板载ST-LINK的输出时

钟),如下图:

在ICACHE的标签页的配置如下图:

另外,SYS标签页下面的Timebase Source为Systick

2.1.2 Connectivity 的相关配置

根据NUCLEO-H563的硬件原理图定义,这里选择USART3打印输出相关的USB操作信息。

不用开中断或者DMA,波特率默认115200.

注意修改USART3使用的端口引脚,这儿是PD8与PD9,与默认CubeMX配置引脚不一样。

在USB下面的配置如下:中断优先等级设置为6;

2.1.3 Middleware 的相关配置

在USBx下面, 由于是standalone的示例,所以这儿不用选择操作系统的中间件。

在Host Class FS 下面选择, 也可以只选择Keyboard或者Mouse,或者两者都选。

USBx 的具体配置如下图,可以看出主要检查或修改了默认的如下5处地方,

UXHost memory pool size 和 USBX Host System Stack Size 均由默认的 1024 设置为

22K(22*1024 = 22528),可以根据项目情况适当调整这个大小。

2.1.4 System Clock 相关配置

由于选择了Bypass模式的8Mhz,这里注意要修改为一致。USB Host IP的时钟需要48Mhz,

这里选择PLL1Q=48Mhz ;

2.1.5 生成项目工程

为项目命名,生成工程,配置堆栈的大小:

这时候可能会提示警告,如下图所示,不用担心,直接选择yes生成代码。

产生问题的原因是这儿的Driver VBUS_FS没有相关的GPIO或者硬件去配置,如下图:

后续为了能驱动这儿的VBUS,需要注意的是连接NUCLEO-H563板上的JP2选择

'STLK'(JP2:1~2)和 'USB USER'(JP2:9~10)用于 VBUSC 供电。如下图所示:

生成工程代码,编译没有问题,这时候可以接着添加相关的应用代码。

2.2.添加运用代码

2.2.1 完善串口打印

不同的IAR版本,它的重定位函数可能不一样,参考\STM32H573I

DK\Applications\USBX\Ux_Host_HID_Standalone 的例程,添加相关函数,这里不再过多描

述,相关截图如下:

在main.c中添加:

复制代码
/* USER CODE BEGIN Includes */ // 包含标准C头文件 
#include "stdio.h" 
/* USER CODE END PFP */ 
 
/* USER CODE BEGIN PFP */ 
#if defined(__ICCARM__) 
/* New definition from EWARM V9, compatible with EWARM8 */ 
int iar_fputc(int ch); 
#define PUTCHAR_PROTOTYPE int iar_fputc(int ch) 
size_t __write(int file, unsigned char const *ptr, size_t len); 
#elif defined ( __CC_ARM ) || defined(__ARMCC_VERSION) 
/* ARM Compiler 5/6*/ 
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) 
#elif defined(__GNUC__) 
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch) 
#endif /* __ICCARM__ */ 
 
/* USER CODE END PFP */ 

/* USER CODE BEGIN 4 */ 
#if defined(__ICCARM__) 
size_t __write(int file, unsigned char const *ptr, size_t len) 
{ 
  size_t idx; 
  unsigned char const *pdata = ptr; 
 
  for (idx = 0; idx < len; idx++) 
  { 
    iar_fputc((int)*pdata); 
    pdata++; 
  } 
  return len; 
} 
#endif /* __ICCARM__ */ 
 
/** 
  * @brief  Retargets the C library printf function to the USART. 
  */ 
PUTCHAR_PROTOTYPE 
{ 
  /* Place your implementation of putchar here */ 
  /* e.g. write a character to the USART3 and Loop until the end of transmission */ 
  HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 0xFFFF); 
 
  return ch; 
} 
/* USER CODE END 4 */

注意NUCLEO-H563是USART3,例程中默认的是USART1,注意修改。

在app_usbx_host.h文件中,添加如下代码:

复制代码
/* USER CODE BEGIN EM */ 
#define USBH_UsrLog(...)   printf(__VA_ARGS__);\ 
                           printf("\r\n"); 
 
#define USBH_ErrLog(...)   printf("ERROR: ") ;\ 
                           printf(__VA_ARGS__);\ 
                           printf("\r\n"); 
/* USER CODE END EM */

验证串口打印的功能,在main函数中随便输出一个字符串看看,确认没有问题(可选)。

2.2.2 添加必要的USBx的初始化函数

复制代码
/* USER CODE BEGIN MX_USBX_Host_Init1 */ 
/* Initialize USBX_Host */ 
USBX_APP_Host_Init(); 
/* USER CODE END MX_USBX_Host_Init1 */

在MX_USBX_Host_Init 中去添加应用的初始代码USBX_APP_Host_Init的初始化;

复制代码
/* USER CODE BEGIN PFP */ 
VOID USBX_APP_Host_Init(VOID); 
extern HCD_HandleTypeDef hhcd_USB_DRD_FS; 
/* USER CODE END PFP */

添加该函数的声明和指针 句柄hhcd_USB_DRD_FS;

添加头文件:#include "ux_hcd_stm32.h"

复制代码
/* USER CODE BEGIN Includes */ 
#include "ux_hcd_stm32.h" 
/* USER CODE END Includes */

添加应用的初始化的具体代码:

此时还没有开始枚举,打印的log为如下:

复制代码
/* USER CODE BEGIN 1 */ 
   
/** 
  * @brief  USBX_APP_Host_Init 
  *         Initialization of USB host. 
  * @param  none 
  * @retval none 
  */ 
VOID USBX_APP_Host_Init(VOID) 
{ 
  /* USER CODE BEGIN USB_Host_Init_PreTreatment_0 */ 
 
  /* USER CODE END USB_Host_Init_PreTreatment_0 */ 
 
  /* Initialize the LL driver */ 
  /* Register all the USB host controllers available in this system. */ 
  ux_host_stack_hcd_register(_ux_system_host_hcd_stm32_name, 
                             _ux_hcd_stm32_initialize, (ULONG)USB_DRD_FS, 
                             (ULONG)&hhcd_USB_DRD_FS); 
 
  /* Enable USB Global Interrupt */ 
  HAL_HCD_Start(&hhcd_USB_DRD_FS); 
 
  /* USER CODE BEGIN USB_Host_Init_PostTreatment1 */ 
 
  /* Start Application Message */ 
  USBH_UsrLog("**** USB DRD HID Host **** \r\n"); 
  USBH_UsrLog("USB Host library started.\r\n"); 
 
  /* Wait for Device to be attached */ 
  USBH_UsrLog("Starting HID Application"); 
  USBH_UsrLog("Connect your HID Device"); 
 
  /* USER CODE END USB_Host_Init_PostTreatment1 */ 
} 
 
/* USER CODE END 1 */ 

2.2.2 添加USBx的处理函数

复制代码
  /* USER CODE BEGIN 3 */ 
    USBX_Host_Process(NULL); 
     
  } 
  /* USER CODE END 3 */

在main函数的while循环中添加函数 USBX_Host_Process(NULL);

添加该函数的定义和申明在app_usbx_host.c文件中实现,请注意这里还需要添加键盘和鼠标的

应用处理函数。

复制代码
/* USER CODE BEGIN EFP */ 
VOID USBX_Host_Process(VOID *arg); 
/* USER CODE END EFP */ 

/* USER CODE BEGIN 1 */ 
VOID USBX_Host_Process(VOID *arg) 
{ 
ux_host_stack_tasks_run(); 
USBX_HOST_HID_MOUSE_Task(); 
USBX_HOST_HID_KEYBORAD_Task(); 
} 
/* USER CODE END 1 */

2.2.3 修改 USBx Host 的 timming 函数

2.2.4 修改USBx 的ux_host_event_callback 函数和 ux_host_error_callback 函数。

STM32CubeMX生成的event几乎为空,没有执行函数,不难理解为什么没有实现枚举了。复

制例程的函数内容,并定义HID的Mouse和Keyboard的相关句柄。

复制代码
/* USER CODE BEGIN PV */ 
UX_HOST_CLASS_HID          
*hid_instance; 
UX_HOST_CLASS_HID_MOUSE    *mouse; 
UX_HOST_CLASS_HID_KEYBOARD *keyboard; 
/* USER CODE END PV */

/* USER CODE BEGIN _ux_utility_time_get */ 
time_tick = HAL_GetTick(); 
/* USER CODE END _ux_utility_time_get */ 

在ux_host_event_callback 中添加必要的设备插入,HID检测等处理。

复制代码
UINT ux_host_event_callback(ULONG event, UX_HOST_CLASS *current_class, VOID *current_instance) 
{ 
  UINT status = UX_SUCCESS; 
 
  /* USER CODE BEGIN ux_host_event_callback0 */ 
 
  /* Get current Hid Client */ 
  UX_HOST_CLASS_HID_CLIENT *client = (UX_HOST_CLASS_HID_CLIENT *) current_instance; 
 
  /* USER CODE END ux_host_event_callback0 */ 
 
  switch (event) 
  { 
    case UX_DEVICE_INSERTION: 
 
      /* USER CODE BEGIN UX_DEVICE_INSERTION */ 
      USBH_UsrLog("\n usb Client Plugged"); 
      /* Get current Hid Class */ 
      if (current_class->ux_host_class_entry_function == ux_host_class_hid_entry) 
      { 
        if (hid_instance == UX_NULL) 
        { 
          /* Get current Hid Instance */ 
          hid_instance = (UX_HOST_CLASS_HID *)current_instance; 
        } 
      } 
      /* USER CODE END UX_DEVICE_INSERTION */ 
      break; 
    case UX_DEVICE_REMOVAL: 
      /* USER CODE BEGIN UX_DEVICE_REMOVAL */ 
 
      /* Free HID Instance */ 
      if ((VOID*)hid_instance == current_instance) 
      { 
        hid_instance = UX_NULL; 
      } 
      /* USER CODE END UX_DEVICE_REMOVAL */ 
      break; 
 
    case UX_HID_CLIENT_INSERTION: 
 
      /* USER CODE BEGIN UX_HID_CLIENT_INSERTION */ 
 
      USBH_UsrLog("\nHID Client Plugged"); 
 
      /* Check the HID_client if this is a HID keyboard device */ 
      if (client->ux_host_class_hid_client_handler == ux_host_class_hid_keyboard_entry) 
      { 
        /* Get current Hid Client */ 
        if (keyboard == UX_NULL) 
        { 
          keyboard = client -> ux_host_class_hid_client_local_instance; 
 
          USBH_UsrLog("HID_Keyboard_Device"); 
          USBH_UsrLog("PID: %#x ", 
(UINT)keyboard->ux_host_class_hid_keyboard_hid->ux_host_class_hid_device->ux_device_descriptor.idProduct); 
          USBH_UsrLog("VID: %#x ", 
(UINT)keyboard->ux_host_class_hid_keyboard_hid->ux_host_class_hid_device->ux_device_descriptor.idVendor); 
          USBH_UsrLog("USB HID Host Keyboard App..."); 
          USBH_UsrLog("keyboard is ready...\n"); 
        } 
      } 
      /* USER CODE END UX_HID_CLIENT_INSERTION */ 
 
      break;
case UX_HID_CLIENT_REMOVAL: 
 
      /* USER CODE BEGIN UX_HID_CLIENT_REMOVAL */ 
 
      /* Clear hid client local instance */ 
      if ((VOID*)keyboard == client->ux_host_class_hid_client_local_instance) 
      { 
        /* Clear hid keyboard instance */ 
        keyboard = UX_NULL; 
 
        USBH_UsrLog("\nHID Client Keyboard Unplugged"); 
      } 
 
      if ((VOID*)mouse == client->ux_host_class_hid_client_local_instance) 
      { 
        /* Clear hid mouse instance */ 
        mouse = UX_NULL; 
 
        USBH_UsrLog("\nHID Client Mouse Unplugged"); 
      } 
 
      /* USER CODE END UX_HID_CLIENT_REMOVAL */ 
 
      break; 
 
    case UX_DEVICE_CONNECTION: 
 
      /* USER CODE BEGIN UX_DEVICE_CONNECTION */ 
 
      /* USER CODE END UX_DEVICE_CONNECTION */ 
 
      break; 
 
    case UX_DEVICE_DISCONNECTION: 
 
      /* USER CODE BEGIN UX_DEVICE_DISCONNECTION */ 
 
      /* USER CODE END UX_DEVICE_DISCONNECTION */ 
 
      break; 
 
    default: 
 
      /* USER CODE BEGIN EVENT_DEFAULT */ 
 
      /* USER CODE END EVENT_DEFAULT */ 
      break; 
  } 
  /* USER CODE BEGIN ux_host_event_callback1 */ 
  /* USER CODE END ux_host_event_callback1 */ 
 
  return status; 
}

在ux_host_error_callback中添加必要的错误处理。在本LAT中只是添加了一些打印信息,并

没有做太多处理。

复制代码
case UX_DEVICE_ENUMERATION_FAILURE: 
/* USER CODE BEGIN UX_DEVICE_ENUMERATION_FAILURE */ 
USBH_UsrLog("USB Device Enumeration Failure"); 
/* USER CODE END UX_DEVICE_ENUMERATION_FAILURE */ 
break; 
case  UX_NO_DEVICE_CONNECTED: 
/* USER CODE BEGIN UX_NO_DEVICE_CONNECTED */ 
USBH_UsrLog("USB Device disconnected"); 
/* USER CODE END UX_NO_DEVICE_CONNECTED */ 
break; 

编译这时会发现,枚举成功了。(注意,这儿实验的是USB Host的功能,所以通过串口打印的

log 查看相关流程操作,PC电脑端查看不了)。

请注意在这里如果要运行,请注释掉USBX_HOST_HID_MOUSE_Task(); 和

USBX_HOST_HID_KEYBORAD_Task();

2.2.5 添加相关类的操作函数

打开函数USBX_HOST_HID_MOUSE_Task();和BX_HOST_HID_KEYBORAD_Task();添加必要的

HID实现函数。

复制代码
/* USER CODE BEGIN Includes */ 
#include "app_usbx_host.h" 
/* USER CODE END Includes */ 
 
 
/* USER CODE BEGIN PV */ 
extern UX_HOST_CLASS_HID_KEYBOARD *keyboard; 
 
/* USER CODE END PV */ 
 
/* USER CODE BEGIN 1 */ 
 
/** 
  * @brief  USBX_HOST_HID_KEYBORAD_Task 
  * @param  none 
  * @retval none 
  */ 
VOID USBX_HOST_HID_KEYBORAD_Task(VOID) 
{ 
  ULONG keyboard_key; 
  ULONG keyboard_state; 
 
  /* Start if the hid client is a keyboard and connected */ 
  if ((keyboard != NULL) && 
      (keyboard->ux_host_class_hid_keyboard_state == (ULONG) UX_HOST_CLASS_INSTANCE_LIVE)) 
  { 
    /* Get a key and state from the keyboard */ 
    if ((keyboard != NULL) && ux_host_class_hid_keyboard_key_get(keyboard, &keyboard_key, &keyboard_state) == UX_SUCCESS) 
    { 
      /* Print the key pressed */ 
      USBH_UsrLog("%c", (CHAR)keyboard_key); 
    } 
  } 
} 
 
/* USER CODE END 1 */ 
ux_host_keyboard.c 
/* USER CODE BEGIN EFP */ 
VOID USBX_HOST_HID_KEYBORAD_Task(VOID); 
/* USER CODE END EFP */
 
/* USER CODE BEGIN Includes */ 
#include "app_usbx_host.h" 
/* USER CODE END Includes */ 
/* USER CODE BEGIN PV */ 
extern UX_HOST_CLASS_HID_MOUSE *mouse; 
/* USER CODE END PV */ 
/* USER CODE BEGIN 1 */ 
VOID USBX_HOST_HID_MOUSE_Task(VOID) 
{ 
  static LONG old_Pos_x = 0; 
  static LONG old_Pos_y = 0; 
  LONG actual_Pos_x = 0; 
  LONG actual_Pos_y = 0; 
  ULONG actual_mouse_buttons = 0; 
  static ULONG old_mouse_buttons = 0; 
  SLONG actual_mouse_wheel = 0; 
  static SLONG old_mouse_wheel = 0; 
 
  /* Check if mouse instance is Null */ 
  if ((mouse != NULL) && 
      (mouse -> ux_host_class_hid_mouse_state == (ULONG) UX_HOST_CLASS_INSTANCE_LIVE)) 
  { 
    /* Get Mouse position */ 
    if ((mouse != NULL) && ux_host_class_hid_mouse_position_get(mouse, &actual_Pos_x, &actual_Pos_y) == UX_SUCCESS) 
    { 
      if (((actual_Pos_x != old_Pos_x) || (actual_Pos_y != old_Pos_y)) && 
        (actual_Pos_x != 0) && (actual_Pos_y != 0)) 
      { 
        USBH_UsrLog("Pos_x = %ld Pos_y= %ld", actual_Pos_x, actual_Pos_y); 
        /* Update (x,y) old position */ 
        old_Pos_x = actual_Pos_x; 
        old_Pos_y = actual_Pos_y; 
      } 
    } 
    /* Get Mouse buttons value */ 
    if ((mouse != NULL) && ux_host_class_hid_mouse_buttons_get(mouse, &actual_mouse_buttons) == UX_SUCCESS) 
    { 
      if (actual_mouse_buttons != old_mouse_buttons) 
      { 
        /* Check which button is pressed */ 
        if (actual_mouse_buttons & UX_HOST_CLASS_HID_MOUSE_BUTTON_1_PRESSED) 
        { 
          USBH_UsrLog("Left Button Pressed"); 
        } 
        if (actual_mouse_buttons & UX_HOST_CLASS_HID_MOUSE_BUTTON_2_PRESSED) 
        { 
          USBH_UsrLog("Right Button Pressed"); 
        } 
        if (actual_mouse_buttons & UX_HOST_CLASS_HID_MOUSE_BUTTON_3_PRESSED) 
        { 
          USBH_UsrLog("Middle Button Pressed"); 
        } 
        /* Update button old value */ 
        old_mouse_buttons = actual_mouse_buttons; 
      } 
    } 
    /* Get hid wheel mouse position */ 
    if ((mouse != NULL) && ux_host_class_hid_mouse_wheel_get(mouse, &actual_mouse_wheel) == UX_SUCCESS) 
    { 
      if ((actual_mouse_wheel != old_mouse_wheel) && (actual_mouse_wheel != 0)) 
      { 
        USBH_UsrLog("Pos_weel = %ld", actual_mouse_wheel); 
 
        /* Update wheel mouse movement value */ 
        old_mouse_wheel = actual_mouse_wheel; 
      } 
    } 
  } 
} 
/* USER CODE END 1 */
/* USER CODE BEGIN EFP */ 
VOID USBX_HOST_HID_MOUSE_Task(VOID); 
/* USER CODE END EFP */

编译运行,请不要注释掉USBX_HOST_HID_MOUSE_Task(); 和

USBX_HOST_HID_KEYBORAD_Task();这时候会发现USBx Host HID 的示例就成功了。


重要通知 - 请仔细阅读 意法半导体公司及其子公司 ("ST")保留随时对 ST 产品和 / 或本文档进行变更的权利,恕不另行通知。买方在订货之前应获取关于 ST 产 品的最新信息。 ST 产品的销售依照订单确认时的相关 ST 销售条款。 买方自行负责对 ST 产品的选择和使用, ST 概不承担与应用协助或买方产品设计相关的任何责任。 ST 不对任何知识产权进行任何明示或默示的授权或许可。 转售的 ST 产品如有不同于此处提供的信息的规定,将导致 ST 针对该产品授予的任何保证失效。 ST 和 ST 徽标是 ST 的商标。若需 ST 商标的更多信息,请参考 www.st.com/trademarks。所有其他产品或服务名称均为其 各自所有者的财 产。 本文档是ST中国本地团队的技术性文章,旨在交流与分享,并期望借此给予客户产品应用上足够的帮助或提醒。若文中内容存有局限或与ST 官网资料不一致,请以实际应用验证结果和ST官网最新发布的内容为准。您拥有完全自主权是否采纳本文档(包括代码,电路图等)信息, 我们也不承担因使用或采纳本文档内容而导致的任何风险。 本文档中的信息取代本文档所有早期版本中提供的信息。 © 2020 STMicroelectronics - 保留所有权利

相关推荐
可乐鸡翅yeah_4 分钟前
hls.js 播放质量埋点实战,采集卡顿、起播、错误指标定位线上用户问题
开发语言·前端·javascript·ecmascript·音视频·m3u8·音视频在线播放
szarron20 分钟前
RF Demo Kit|NanoVNA 射频演示测试板完整上手教程,滤波器、衰减器、SOLT 校准学习板
开发语言·人工智能·学习·php·射频工程·频谱仪
tedcloud12324 分钟前
Wand-Enhancer:如何搭建一套远程开发与测试环境
前端·人工智能·macos·开源·流程图
边境悍匪32 分钟前
蜗牛学苑 Java 智能体学习 Day39|SpringAI 会话存储、Function‑Call 工具调用、MCP 思维导图复盘
java·开发语言·spring boot·学习·阿里云
万物智能信息科技1 小时前
RK3568 的多路显示移植—【万物智能之开源鸿蒙OpenHarmony系统实战开发系列教程】
linux·开发语言·华为·开源·harmonyos
掘金挖土1 小时前
前端手摸手跑路之 AI 应用开发(三)
前端·后端
蓝悦无人机1 小时前
Qt Design Studio(QDS)4.8.2项目文件结构详解
开发语言·qt·qt creator·qds·项目文件结构·qtc
数字新视界1 小时前
能耗管理系统的综合方案与优化实战解析
嵌入式硬件·物联网·数据中心·数据中心基础设施管理·dcim管理系统
深圳市恒锐丰科技杨生1 小时前
EG3525S 推挽 / 半桥 PWM 控制器|屹晶 EGmicro
嵌入式硬件·硬件工程
计算机魔术师1 小时前
OpenAI 宣称破解千禧年难题,数学家却说是「反例搜索」?
前端