ITPub博客

首页 > 嵌入式/内核开发 > 嵌入式/内核开发 > 《STM32H7R7开发指南 V1.1 》第七十二章 USB鼠标键盘(Host)实验

《STM32H7R7开发指南 V1.1 》第七十二章 USB鼠标键盘(Host)实验

原创 嵌入式/内核开发 作者:正点原子 时间:2025-10-29 09:50:33 0 删除 编辑

第七十二章 USB鼠标键盘(Host)实验


       本章我们介绍如何使用STM32H7R7的USB HOST来驱动USB鼠标/键盘。

       本章分为如下几个小节:

       72.1 USB鼠标键盘简介 

       72.2 硬件设计

       72.3 程序设计

       72.4 下载验证


        72.1 USB鼠标键盘简介

       传统的鼠标和键盘是采用PS/2接口和电脑通信的,但是现在PS/2接口在电脑上逐渐消失,所以现在越来越多的鼠标键盘采用的是USB接口,而不是PS/2接口的了。

       USB鼠标键盘属于USB HID设备。USB HID即:Human Interface Device(人机交互设备)的缩写,键盘、鼠标与游戏杆等都属于此类设备。不过HID设备并不一定要有人机接口,只要符合HID类别规范的设备都是HID设备。关于USB HID的知识,我们这里就不详细介绍了,请大家自行百度学习。

       我们直接移植官方的USB HID例程,官方例程路径:8,STM32参考资料→1,STM32CubeH7RS固件包 →STM32Cube_FW_H7RS_V1.0.0 →Projects →STM32H7S78-DK →Applications →USB_Host →HID_RTOS,该例程支持USB鼠标和键盘等USB HID设备,本章我们将移植这个例程到我们的开发板上。


        72.2 硬件设计


       1. 例程功能

       本实验代码,开机的时候先显示一些提示信息,然后初始化USB HOST,并不断轮询。当检测到USB鼠标/键盘的插入后,显示设备类型,并显示设备输入数据:

       如果是USB鼠标:将显示鼠标移动的坐标(X,Y坐标)以及按键(左中右)。

       如果是USB键盘:将显示键盘输入的数字/字母等内容(不是所有按键都支持,部分按键没有做解码支持,比如F1~F12)。


       2. 硬件资源


       1)LED灯

              LED0: LED0 – PD14

              LED1 :LED1 – PC0


       2)串口1(PB14/PB15连接在板载USB转串口芯片CH340上面)

       3)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(包括MCU屏和RGB屏,都支持)


       4)HyperRAM芯片


       5)USB_HOST接口(D-/D+连接在PM5/PM6上)


        72.3 程序设计 


       72.3.1 程序解析

       这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。

       本实验,我们在上一个实验的基础上修改,对照官方HID例子,然后,我们在工程里面添加USB HID相关代码,由于官方提供的USB_APP文件不太好用,我们正点原子团队直接编写了一套USB_APP代码,读者可以直接拷贝我们例程中的USB_APP文件夹,即:实验61 USB鼠标键盘(Host)实验→Middlewares→USB→USB_APP。如图72.3.2.1所示:


图72.3.2.1 USB鼠标键盘工程分组


       1. USB驱动代码

       usbh_hid_mouse.h,首先介绍usbh_hid_mouse.h里面定义的一个结构体,具体如下:

/* 鼠标信息结构体*/
typedef struct _HID_MOUSE_Info
{
  uint8_t              x;           /* x轴增量(强制转换成signed char后使用)*/
  uint8_t              y;           /* y轴增量(强制转换成signed char后使用)*/
  uint8_t              buttons[3];  /* 将buttons修改为button,存储按键状态 */
}
HID_MOUSE_Info_TypeDef;

       HID_MOUSE_Info_TypeDef结构体中,成员x和y表示鼠标的横向/纵向移动的增量值,在使用的时候,需要强制转换成unsigned char类型后使用(有±)。button的bit0、bit1、bit2分别表示鼠标的:左键、右键和滚轮键的按下状态。我们通过这个结构体,就可以获得当前鼠标的输入状态。

       usbh_hid_mouse.c,总共有三个函数,首先介绍的是USBH鼠标初始化函数,其定义如下:

/* 鼠标信息(坐标+按钮状态)*/
HID_MOUSE_Info_TypeDef    mouse_info;           
/* 鼠标上报数据长度,最多USBH_HID_MOUSE_REPORT_SIZE个字节*/
uint8_t   mouse_report_data[USBH_HID_MOUSE_REPORT_SIZE];
uint8_t   mouse_rx_report_buf[USBH_HID_MOUSE_REPORT_SIZE];
     
 
/**
 * @brief       USBH 鼠标初始化
 * @param       phost       : USBH句柄指针
 * @retval      USB状态
 *   @arg       USBH_OK(0)   , 正常;
 *   @arg       USBH_BUSY(1) , 忙;
 *   @arg       其他         , 失败;
 */
USBH_StatusTypeDef USBH_HID_MouseInit(USBH_HandleTypeDef *phost)
{
  uint32_t i;
  HID_HandleTypeDef *HID_Handle = (HID_HandleTypeDef *) 
phost->pActiveClass->pData;
 
  mouse_info.x = 0U;
  mouse_info.y = 0U;
  mouse_info.buttons[0] = 0U;
  mouse_info.buttons[1] = 0U;
  mouse_info.buttons[2] = 0U;
 
  for (i = 0U; i < sizeof(mouse_report_data); i++)
  {
    mouse_report_data[i] = 0U;
    mouse_rx_report_buf[i] = 0U;
  }
 
  if (HID_Handle->length > sizeof(mouse_report_data))
  {
    HID_Handle->length = (uint16_t)sizeof(mouse_report_data);
  }
  HID_Handle->pData = mouse_rx_report_buf;
 
  if ((HID_QUEUE_SIZE * sizeof(mouse_report_data)) > sizeof(phost->
device.Data))
  {
    return USBH_FAIL;
  }
  else
  {
    USBH_HID_FifoInit(&HID_Handle->fifo, phost->device.Data, (uint16_t)(HID_QUEUE_SIZE * sizeof(mouse_report_data)));
  }
 
  return USBH_OK;
}

       USBH_HID_MouseInit函数,用于初始化鼠标,指定USB鼠标输出数据的缓存,初始化FIFO等,该函数在USBH_HID_InterfaceInit函数里面被调用。

       接下来介绍的是USBH获取鼠标信息函数,其定义如下:

/**
 * @brief       USBH 获取鼠标信息
 * @param       phost       : USBH句柄指针
 * @retval      鼠标信息(HID_MOUSE_Info_TypeDef)
 */
HID_MOUSE_Info_TypeDef *USBH_HID_GetMouseInfo(USBH_HandleTypeDef *phost)
{
    if (USBH_HID_MouseDecode(phost) == USBH_OK)
    {
        return &mouse_info;
    }
    else
    {
        return NULL;
    }
}

       USBH_HID_GetMouseInfo函数,用于获取鼠标信息,通过USBH_HID_MouseDecode函数获取当前鼠标传回来的数据。该函数在USB_Demo函数里面被调用。

       最后介绍的是USBH 鼠标数据解析函数,其定义如下:

/**
 * @brief       USBH 鼠标数据解析函数
 * @param       phost       : USBH句柄指针
 * @retval      USB状态
 *   @arg       USBH_OK(0)   , 正常;
 *   @arg       USBH_BUSY(1) , 忙;
 *   @arg       其他           , 失败;
 */
static USBH_StatusTypeDef USBH_HID_MouseDecode(USBH_HandleTypeDef *phost)
{
  HID_HandleTypeDef *HID_Handle = (HID_HandleTypeDef *) 
phost->pActiveClass->pData;
 
  if ((HID_Handle->length == 0U) || (HID_Handle->fifo.buf == NULL))
  {
    return USBH_FAIL;
  }
  /*Fill report */
  if (USBH_HID_FifoRead(&HID_Handle->fifo, &mouse_report_data, HID_Handle
->length) == HID_Handle->length)
  {
    /*Decode report */
mouse_info.x = (uint8_t)HID_ReadItem((HID_Report_ItemTypedef *) &prop_x,
 0U);
mouse_info.y = (uint8_t)HID_ReadItem((HID_Report_ItemTypedef *) &prop_y,
0U);
 
mouse_info.buttons[0] = (uint8_t)HID_ReadItem((HID_Report_ItemTypedef *)
&prop_b1, 0U);
mouse_info.buttons[1] = (uint8_t)HID_ReadItem((HID_Report_ItemTypedef *)
&prop_b2, 0U);
mouse_info.buttons[2] = (uint8_t)HID_ReadItem((HID_Report_ItemTypedef *)
&prop_b3, 0U);
 
    return USBH_OK;
  }
  return   USBH_FAIL;
}

       USBH_HID_MouseDecode函数,用于解析USB HOST获取到的USB鼠标数据,并存放到mouse_info结构体里面。该函数被USBH_HID_GetMouseInfo函数调用。


       2. main.c代码

       下面是main.c的程序,具体如下:

int main(void)
{
    uint32_t t = 0;
    HID_TypeTypeDef hid_type = HID_UNKNOWN;
    HID_MOUSE_Info_TypeDef *hid_mouse_info_struct;
    HID_KEYBD_Info_TypeDef *hid_keybd_info_struct;
    int16_t mouse_x_abs;
    int16_t mouse_y_abs;
    uint8_t keyboard_ascii_code;
    
    sys_mpu_config();                   /* 配置MPU */
    sys_cache_enable();                 /* 使能Cache */
    HAL_Init();                         /* 初始化HAL库 */
    sys_stm32_clock_init(300, 6, 2);    /* 配置时钟,600MHz */
    delay_init(600);                    /* 初始化延时 */
    usart_init(115200);                 /* 初始化串口 */
    led_init();                         /* 初始化LED */
    hyperram_init();                    /* 初始化HyperRAM */
    lcd_init();                         /* 初始化LCD */
    my_mem_init(SRAMIN);                /* 初始化AXI-SRAM1~4内存池 */
    my_mem_init(SRAMEX);                /* 初始化XSPI2 HyperRAM内存池 */
    my_mem_init(SRAM12);                /* 初始化AHB-SRAM1~2内存池 */
    my_mem_init(SRAMDTCM);              /* 初始化DTCM内存池 */
    my_mem_init(SRAMITCM);              /* 初始化ITCM内存池 */
    
    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30, 70, 200, 16, 16, "USB Mouse/Keyboard TEST", RED);
    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    
    USBH_Init(&g_usbh_handle, usbh_user_process, HOST_HS);
    USBH_RegisterClass(&g_usbh_handle, USBH_HID_CLASS);
    USBH_Start(&g_usbh_handle);
    
    while (1)
    {
        USBH_Process(&g_usbh_handle);
        
        switch (g_status)
        {
            case STA_IDLE:
            {
                break;
            }
            case STA_READY:
            {
                if (hid_type == HID_UNKNOWN)
                {
                    hid_type = USBH_HID_GetDeviceType(&g_usbh_handle);
                    if (hid_type == HID_MOUSE)
                    {
                        hid_mouse_info_struct = NULL;
                        mouse_x_abs = 0;
                        mouse_y_abs = 0;
                        lcd_show_string(30, 110, 200, 16, 16, "Mouse", BLUE);
                        lcd_show_string(30, 130, 200, 16, 16, "Button: Left
Middle Right", BLUE);
                        lcd_show_string(30, 150, 200, 16, 16, "X:   0", BLUE);
                        lcd_show_string(30, 170, 200, 16, 16, "Y:   0", BLUE);
                    }
                    else if (hid_type == HID_KEYBOARD)
                    {
                        hid_keybd_info_struct = NULL;
                        keyboard_ascii_code = 0;
                        lcd_show_string(30, 110, 200, 16, 16, "Keyboard",BLUE);
                        lcd_show_string(30, 130, 200, 16, 16, "ASCII:  ",BLUE);
                        lcd_show_string(30, 150, 200, 16, 16, "ASCII Code:   ",
 BLUE);
                    }
                }
                break;
            }
            case STA_DISCONNECT:
            {
                g_status = STA_IDLE;
                hid_type = HID_UNKNOWN;
                lcd_fill(30, 110, 240, 186, WHITE);
                break;
            }
        }
        
        if (hid_type == HID_MOUSE)
        {
            hid_mouse_info_struct = USBH_HID_GetMouseInfo(&g_usbh_handle);
            if (hid_mouse_info_struct != NULL)
            {
                /* 处理鼠标左键数据 */
                if (hid_mouse_info_struct->buttons[0] != 0)
                {
                    lcd_show_string(94, 130, 200, 16, 16, "Left", RED);
                }
                else
                {
                    lcd_show_string(94, 130, 200, 16, 16, "Left", BLUE);
                }
                
                /* 处理鼠标右键数据 */
                if (hid_mouse_info_struct->buttons[1] != 0)
                {
                    lcd_show_string(190, 130, 200, 16, 16, "Right", RED);
                }
                else
                {
                    lcd_show_string(190, 130, 200, 16, 16, "Right", BLUE);
                }
                
                /* 处理鼠标中键数据 */
                if (hid_mouse_info_struct->buttons[2] != 0)
                {
                    lcd_show_string(134, 130, 200, 16, 16, "Middle", RED);
                }
                else
                {
                    lcd_show_string(134, 130, 200, 16, 16, "Middle", BLUE);
                }
                
                /* 处理鼠标X轴移动数据 */
                mouse_x_abs += (int8_t)hid_mouse_info_struct->x;
                if (mouse_x_abs > 9999)
                {
                    mouse_x_abs = 9999;
                }
                else if (mouse_x_abs < -9999)
                {
                    mouse_x_abs = -9999;
                }
                if (mouse_x_abs < 0)
                {
                    lcd_show_string(54, 150, 200, 16, 16, "-", BLUE);
                    lcd_show_num(62, 150, -mouse_x_abs, 4, 16, BLUE);
                }
                else
                {
                    lcd_show_string(54, 150, 200, 16, 16, " ", BLUE);
                    lcd_show_num(62, 150, mouse_x_abs, 4, 16, BLUE);
                }
                
                /* 处理鼠标Y轴移动数据 */
                mouse_y_abs += (int8_t)hid_mouse_info_struct->y;
                if (mouse_y_abs > 9999)
                {
                    mouse_y_abs = 9999;
                }
                else if (mouse_y_abs < -9999)
                {
                    mouse_y_abs = -9999;
                }
                if (mouse_y_abs < 0)
                {
                    lcd_show_string(54, 170, 200, 16, 16, "-", BLUE);
                    lcd_show_num(62, 170, -mouse_y_abs, 4, 16, BLUE);
                }
                else
                {
                    lcd_show_string(54, 170, 200, 16, 16, " ", BLUE);
                    lcd_show_num(62, 170, mouse_y_abs, 4, 16, BLUE);
                }
            }
        }
        else if (hid_type == HID_KEYBOARD)
        {
            hid_keybd_info_struct = USBH_HID_GetKeybdInfo(&g_usbh_handle);
            if (hid_keybd_info_struct != NULL)
            {
                keyboard_ascii_code = USBH_HID_GetASCIICode
(hid_keybd_info_struct);
                if (keyboard_ascii_code != 0)
                {
                    /* 显示键盘输入字符 */
                    lcd_show_char(86, 130, keyboard_ascii_code, 16, 0, BLUE);
                    
                    /* 显示键盘输入字符的ASCII码 */
                    lcd_show_num(126, 150, keyboard_ascii_code, 2, 16, BLUE);
                }
                else
                {
                    lcd_show_string(86, 130, 200, 16, 16, " ", BLUE);
                    lcd_show_string(126, 150, 200, 16, 16, "  ", BLUE);
                }
            }
        }
        
        if (++t == 200000)
        {
            t = 0;
            LED0_TOGGLE();
        }
        
        delay_us(1);
    }
}

       main函数,初始化各个外设后,通过USBH_Init等3个函数初始化并启动USB主机通信,然后进入死循环,不停的调用USBH_Process,判断USB HOST枚举是否死机、处理连接状态、判断HID设备类型、以及执行各种USB处理,确定HID 设备后,通过调用USBH_HID_GetMouseInfo或者USBH_HID_GetKeybdInfo函数读取相应的设备数据,然后进行相应的处理,最后输出到屏幕中。


        72.4 下载验证

       将程序下载到开发板后,然后在USB_HOST端子插入USB鼠标/键盘,注意:此时USB SLAVE口不要插USB线到电脑,否则会干扰!

       等USB鼠标/键盘成功识别后,便可以看到LCD显示USB Connected,并显示设备类型:USB Mouse或者USB KeyBoard,同时也会显示输入的数据,如图72.4.1和图72.4.2所示:


图72.4.1 USB鼠标测试


图72.4.2 USB键盘测试


       其中,图72.4.1是USB鼠标测试界面,图72.4.2是USB键盘测试界面。

       最后,特别提醒大家,由于例程的HID内核,只处理了第一个接口描述符,所以对于USB符合设备,只能识别第一个描述符所代表的设备。体现到实际使用中,就是:USB无线鼠标,一般是无法使用(被识别为键盘),而USB无线键盘,可以使用,因为键盘在第一个描述符,鼠标在第二个描述符  。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70040860/viewspace-3098948/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2024-08-30

  • 博文量
    588
  • 访问量
    764682