超级版主
 
- 积分
- 5830
- 金钱
- 5830
- 注册时间
- 2019-5-8
- 在线时间
- 1606 小时
|
|
第七十二章 USB鼠标键盘(Host)实验
1)实验平台:正点原子STM32H7R7开发板
2)章节摘自【正点原子】STM32H7R7开发指南 V1.1
3)购买链接: https://detail.tmall.com/item.htm?id=820823382459
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/stm32/zdyz_stm32h7rx.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子STM32开发板技术交流群:756580169
本章我们介绍如何使用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 鼠标初始化
- * [url=home.php?mod=space&uid=271674]@param[/url] 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无线键盘,可以使用,因为键盘在第一个描述符,鼠标在第二个描述符。 |
|