ITPub博客

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

《STM32H562开发指南 V1.0 》第六十章 USB鼠标键盘(Host)实验

原创 嵌入式/内核开发 作者:正点原子 时间:2026-01-14 09:48:51 0 删除 编辑

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


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

       本章分为如下几个部分:

       60.1 USB鼠标键盘简介 

       60.2 硬件设计

       60.3 程序设计

       60.4 下载验证


        60.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,STM32CubeH5 固件包 →STM32Cube_FW_H5_V1.0.0 →ProjectsàSTM32H573I-DK →Applications →USBX →Ux_Host_HID,该例程支持USB鼠标和键盘等USB HID设备。本实验我们将移植这个例程到我们的开发板上。 


        60.2 硬件设计


       1. 例程功能

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

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

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


       2. 硬件资源


       1)LED灯

              LED0 - PB4

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

       3)正点原子2.8/3.5/4.3/7寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)

       4)USB_HOST接口(D-/D+连接在PA11/PA12上)

       5)TIM6


        60.3 程序设计 


       60.3.1 程序流程图


图60.3.1.1 USB鼠标键盘实验(Host)程序流程图


       60.3.2 程序解析

       这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。本实验,我们在上一个实验的基础上修改,对照官方HID例子,添加USB相关代码到工程中,最终得到如图60.3.2.1所示的工程: 


图60.3.2.1 USB鼠标键盘工程分组


       1. USB驱动代码

       ux_host_hid_mouse.c文件存放鼠标处理函数,总共有两个,定义如下:

UX_HOST_CLASS_HID_MOUSE *mouse = UX_NULL;   /* 鼠标实例 */
 
/**
 * @brief   鼠标用户处理函数
 * @param   mouse: 鼠标实例
 * @retval  无
 */
__weak void ux_host_hid_mouse_user_process(UX_HOST_CLASS_HID_MOUSE *mouse)
{
    UX_PARAMETER_NOT_USED(mouse);
    
    return;
}
 
/**
 * @brief   鼠标处理函数
 * @param   无
 * @retval  无
 */
void ux_host_hid_mouse_process(void)
{
    if (mouse != UX_NULL)
    {
        ux_host_hid_mouse_user_process(mouse);
    }
}

       ux_host_hid_mouse_user_process()函数是弱函数,最终会在main.c中重新定义。

       ux_host_hid_keyboard.c文件存放键盘处理函数,总共有两个,定义如下:

UX_HOST_CLASS_HID_KEYBOARD *keyboard = UX_NULL; /* 键盘实例 */
 
/**
 * @brief   键盘用户处理函数
 * @param   keyboard: 键盘实例
 * @retval  无
 */
__weak void ux_host_hid_keyboard_user_process(UX_HOST_CLASS_HID_KEYBOARD
*keyboard)
{
    UX_PARAMETER_NOT_USED(keyboard);
    
    return;
}
 
/**
 * @brief   键盘处理函数
 * @param   无
 * @retval  无
 */
void ux_host_hid_keyboard_process(void)
{
    if (keyboard != UX_NULL)
    {
        ux_host_hid_keyboard_user_process(keyboard);
    }
}

       ux_host_hid_keyboard_user_process()函数是弱函数,最终会在main.c中重新定义。

       app_usbx_host.c提供了USB主机库的回调及MSP初始化函数等,当USB状态机处理完不同事务的时候,会调用这些回调函数,我们通过这些回调函数,就可以知道USB当前状态,比如:是否连接上了?是否断开了?等,根据这些状态,用户应用程序可以执行不同操作,完成特定功能。app_usbx_host_init()和HAL_HCD_MspInit()函数和上一个实验差不多,这里就不贴出来了。下面查看USB主机事件回调函数,其定义如下:

/**
 * @brief   USB主机事件回调函数
 * @param   event: 事件
 * @param   current_class: 类
 * @param   current_instance: 实例
 * @retval  状态
 */
static UINT app_usbx_host_event_callback(ULONG event, 
UX_HOST_CLASS *current_class, VOID *current_instance)
{
    switch (event)
    {
        case UX_HID_CLIENT_INSERTION:
        {
            if (((UX_HOST_CLASS_HID_CLIENT *)current_instance)
->ux_host_class_hid_client_handler == ux_host_class_hid_mouse_entry)
            {
                mouse = (UX_HOST_CLASS_HID_MOUSE *)((UX_HOST_CLASS_HID_CLIENT *)
current_instance)->ux_host_class_hid_client_local_instance;
            }
            else if (((UX_HOST_CLASS_HID_CLIENT *)current_instance)->
ux_host_class_hid_client_handler == ux_host_class_hid_keyboard_entry)
            {
                keyboard = (UX_HOST_CLASS_HID_KEYBOARD *)
((UX_HOST_CLASS_HID_CLIENT *)current_instance)
->ux_host_class_hid_client_local_instance;
            }
            break;
        }
        case UX_HID_CLIENT_REMOVAL:
        {
            if (((UX_HOST_CLASS_HID_CLIENT *)current_instance)
->ux_host_class_hid_client_handler == ux_host_class_hid_mouse_entry)
            {
                mouse = UX_NULL;
            }
            else if (((UX_HOST_CLASS_HID_CLIENT *)current_instance)->
ux_host_class_hid_client_handler == ux_host_class_hid_keyboard_entry)
            {
                keyboard = UX_NULL;
            }
            break;
        }
        default:
        {
            break;
        }
    }
    
    return UX_SUCCESS;
}

       该函数用于更新鼠标或者键盘的状态,可以更新鼠标或键盘的插入或拔出状态。

       2. main.c代码

       下面是main.c的代码,具体如下:

/**
 * @brief   鼠标用户处理函数
 * @param   mouse: 鼠标实例
 * @retval  无
 */
void ux_host_hid_mouse_user_process(UX_HOST_CLASS_HID_MOUSE *mouse)
{
    SLONG mouse_x_position;
    SLONG mouse_y_position;
    ULONG mouse_buttons;
    SLONG mouse_wheel_movement;
 
    /* 更新显示位置信息 */
if (ux_host_class_hid_mouse_position_get(mouse, &mouse_x_position, 
&mouse_y_position) == UX_SUCCESS)
    {
        /* 更新显示X轴位置信息 */
        if (mouse_x_position < 0)
        {
            lcd_show_string(54, 150, 200, 16, 16, "-", BLUE);
            lcd_show_num(62, 150, -mouse_x_position, 5, 16, BLUE);
        }
        else
        {
            lcd_show_string(54, 150, 200, 16, 16, " ", BLUE);
            lcd_show_num(62, 150, mouse_x_position, 5, 16, BLUE);
        }
        
        /* 更新显示Y轴位置信息 */
        if (mouse_y_position < 0)
        {
            lcd_show_string(54, 170, 200, 16, 16, "-", BLUE);
            lcd_show_num(62, 170, -mouse_y_position, 5, 16, BLUE);
        }
        else
        {
            lcd_show_string(54, 170, 200, 16, 16, " ", BLUE);
            lcd_show_num(62, 170, mouse_y_position, 5, 16, BLUE);
        }
    }
    
    /* 更新显示按钮信息 */
if (ux_host_class_hid_mouse_buttons_get(mouse, &mouse_buttons) == 
UX_SUCCESS)
    {
        /* 更新显示左键按钮信息 */
        if (mouse_buttons & UX_HOST_CLASS_HID_MOUSE_BUTTON_1_PRESSED)
        {
            lcd_show_string(94, 130, 200, 16, 16, "Left", RED);
        }
        else
        {
            lcd_show_string(94, 130, 200, 16, 16, "Left", BLUE);
        }
        
        /* 更新显示右键按钮信息 */
        if (mouse_buttons & UX_HOST_CLASS_HID_MOUSE_BUTTON_2_PRESSED)
        {
            lcd_show_string(190, 130, 200, 16, 16, "Right", RED);
        }
        else
        {
            lcd_show_string(190, 130, 200, 16, 16, "Right", BLUE);
        }
        
        /* 更新显示中间按钮信息 */
        if (mouse_buttons & UX_HOST_CLASS_HID_MOUSE_BUTTON_3_PRESSED)
        {
            lcd_show_string(134, 130, 200, 16, 16, "Middle", RED);
        }
        else
        {
            lcd_show_string(134, 130, 200, 16, 16, "Middle", BLUE);
        }
    }
    
    /* 更新显示滚轮信息 */
if (ux_host_class_hid_mouse_wheel_get(mouse, &mouse_wheel_movement) 
== UX_SUCCESS)
    {
        if (mouse_wheel_movement < 0)
        {
            lcd_show_string(54, 190, 200, 16, 16, "-", BLUE);
            lcd_show_num(62, 190, -mouse_wheel_movement, 5, 16, BLUE);
        }
        else
        {
            lcd_show_string(54, 190, 200, 16, 16, " ", BLUE);
            lcd_show_num(62, 190, mouse_wheel_movement, 5, 16, BLUE);
        }
    }
}
 
/**
 * @brief   键盘用户处理函数
 * @param   keyboard: 键盘实例
 * @retval  无
 */
void ux_host_hid_keyboard_user_process(UX_HOST_CLASS_HID_KEYBOARD *keyboard)
{
    ULONG keyboard_key;
    ULONG keyboard_state;
    
    /* 更新显示键值信息 */
if (ux_host_class_hid_keyboard_key_get(keyboard, &keyboard_key, 
&keyboard_state) == UX_SUCCESS)
    {
        lcd_show_char(86, 130, keyboard_key, 16, 0, BLUE);
        lcd_show_num(126, 150, keyboard_key, 2, 16, BLUE);
    }
}
 
int main(void)
{
    uint8_t t = 0;
    uint8_t res;
    uint8_t mode = 0;
 
    HAL_Init();                           /* HAL库初始化 */
    HAL_ICACHE_Enable();                  /* 使能指令缓存 */
    sys_stm32_clock_init(250, 4, 2, 2);  /* 配置时钟,250MHz */
    delay_init(250);                      /* 初始化延时 */
    usart_init(115200);                   /* 初始化串口 */
    led_init();                           /* 初始化LED */
    lcd_init();                           /* 初始化LCD */
    
    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);
    
    /* USB主机初始化 */
    res = app_usbx_host_init();
    while (res != 0)
    {
        lcd_show_string(30, 110, 200, 16, 16, "USB Host Init Failed!", RED);
        delay_ms(500);
        lcd_show_string(30, 110, 200, 16, 16, "Please Check!        ", RED);
        delay_ms(500);
    }
    
    /* 每1毫秒运行一次独立模式下的USBX系统任务 */
    btim_timx_int_init(1000 - 1, 250 - 1);
    
    while (1)
    {
        if (mouse != UX_NULL)
        {
            if (mode != 1)
            {
                mode = 1;
                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);
                lcd_show_string(30, 190, 200, 16, 16, "W:      0", BLUE);
            }
            ux_host_hid_mouse_process();
        }
        else if (keyboard != UX_NULL)
        {
            if (mode != 2)
            {
                mode = 2;
                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);
            }
            ux_host_hid_keyboard_process();
        }
        else
        {
            if (mode != 0)
            {
                mode = 0;
                lcd_fill(0, 110, 239, 319, WHITE);
            }
        }
        
        if (++t == 20)
        {
            t = 0;
            LED0_TOGGLE();
        }
        
        delay_ms(10);
    }
}

       这里总共3个函数:

       ux_host_hid_mouse_user_process()是鼠标用户处理函数,用于更新显示鼠标坐标、左键、右键和滚轮的一些提示信息。

       ux_host_hid_keyboard_user_process是键盘用户处理函数,用于更新显示键值信息。

       main函数中,先进行系统和外设初始化,显示实验信息、初始化USB等,然后每1毫秒运行一次独立模式下的USBX系统任务,最后在while(1)循环中更新鼠标和键盘的键值等信息,每200ms翻转LED0,提示程序正在运行。


        60.4 下载验证

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

       当USB鼠标/键盘成功识别后,便会显示设备类型:Mouse或者KeyBoard,同时也会显示输入的数据,如图60.4.1和图60.4.2所示:


图60.4.1 USB鼠标测试


图60.4.2 USB键盘测试


       其中,图60.4.1是USB鼠标测试界面,图60.4.2是USB键盘测试界面。

       USB鼠标测试界面中,Left:表示鼠标左键,Middle:表示鼠标滚轮键,Right:便是鼠标右键。当对应符号变成红色表示该按键按下,如上图中,表示鼠标右键按下。X:表示鼠标X轴坐标位置,Y:表示鼠标Y轴坐标位置,W表示鼠标滚轮位置。

       USB键盘测试界面中,ASCII:表示键盘的字符,ASCII Code:表示键盘字符对应的ASCII码值。


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

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

注册时间:2024-08-30

  • 博文量
    588
  • 访问量
    764196