【正点原子STM32连载】 第六十章 USB鼠标键盘(Host)实验 摘自【正点原子】MiniPro STM32H750 开发指南

您所在的位置:网站首页 ps2鼠标键盘可以互换吗视频 【正点原子STM32连载】 第六十章 USB鼠标键盘(Host)实验 摘自【正点原子】MiniPro STM32H750 开发指南

【正点原子STM32连载】 第六十章 USB鼠标键盘(Host)实验 摘自【正点原子】MiniPro STM32H750 开发指南

2024-07-08 20:06:39| 来源: 网络整理| 查看: 265

1)实验平台:正点原子MiniPro H750开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=677017430560 3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-336836-1-1.html 4)对正点原子STM32感兴趣的同学可以加群讨论:879133275

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

本章我们介绍如何使用STM32H750的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,STM32CubeH7固件包 STM32Cube_FW_H7_V1.6.0Projects STM32H743I-EVALApplicationsUSB_HostHID_Standalone,该例程支持USB鼠标和键盘等USB HID设备,本章我们将移植这个例程到我们的开发板上。

60.2 硬件设计 例程功能 本实验代码,开机的时候先显示一些提示信息,然后初始化USB HOST,并不断轮询。当检测到USB鼠标/键盘的插入后,显示设备类型,并显示设备输入数据: 如果是USB鼠标:将显示鼠标移动的坐标(X,Y坐标),滚轮滚动数值(Z坐标)以及按键(左中右)。 如果是USB键盘:将显示键盘输入的数字/字母等内容(不是所有按键都支持,部分按键没有做解码支持,比如F1~F12)。硬件资源 1)RGB灯 RED :LED0 - PB4 GREEN :LED1 - PE6 2)串口1(PA9/PA10连接在板载USB转串口芯片CH340上面) 3)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动) 4)USB_HOST接口(D-/D+连接在PA11/PA12上) 60.3 程序设计 60.3.1 程序流程图 在这里插入图片描述

图60.3.1.1 USB鼠标键盘实验(Host)程序流程图 60.3.2 程序解析 这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。 本实验,我们在上一个实验的基础上修改,对照官方HID例子,然后,我们在工程里面添加USB HID相关代码,如图60.3.2.1所示: 在这里插入图片描述

图60.3.2.1 USB鼠标键盘工程分组 可以看到,USB部分代码,同上一章的在结构上是一模一样的,只是.c文件有些变化。我们重点介绍下USB_HOST里面的usbh_hid_mouse.c这个文件,该文件拷贝自ST官方例程,但是ST官方例程对鼠标的支持不好(兼容性差,不支持滚轮等),我们重写了该文件,下面介绍该文件。

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 z; /* z轴增量(强制转换成signed char后使用)*/ uint8_t button; /* 将buttons修改为button,存储按键状态 */ } HID_MOUSE_Info_TypeDef;

HID_MOUSE_Info_TypeDef结构体中,成员x和y表示鼠标的横向/纵向移动的增量值,成员z表示鼠标的滚轮的滚动增量值,在使用的时候,需要强制转换成unsigned char类型后使用(有±)。button的bit0、bit1、bit2分别表示鼠标的:左键、右键和滚轮键的按下状态。我们通过这个结构体,就可以获得当前鼠标的输入状态。 usbh_hid_mouse.c,总共有三个函数,首先介绍的是USBH鼠标初始化函数,其定义如下:

/* 鼠标信息(坐标+按钮状态)*/ HID_MOUSE_Info_TypeDef mouse_info; /* 鼠标上报数据长度,最多HID_QUEUE_SIZE个字节*/ uint8_t mouse_report_data[HID_QUEUE_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) { HID_HandleTypeDef *HID_Handle = (HID_HandleTypeDef *) phost->pActiveClass->pData; mouse_info.x = 0; mouse_info.y = 0; mouse_info.button = 0; if (HID_Handle->length > sizeof(mouse_report_data)) { HID_Handle->length = sizeof(mouse_report_data); } HID_Handle->pData = (uint8_t *)mouse_report_data; USBH_HID_FifoInit(&HID_Handle->fifo, phost->device.Data, HID_QUEUE_SIZE); return USBH_OK; } USBH_HID_MouseInit函数,用于初始化鼠标,指定USB鼠标输出数据的缓存,初始化FIFO等,该函数在USBH_HID_Process函数里面被调用。 接下来介绍的是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 其他 , 失败; */ USBH_StatusTypeDef USBH_HID_MouseDecode(USBH_HandleTypeDef *phost) { HID_HandleTypeDef *HID_Handle = (HID_HandleTypeDef *) phost->pActiveClass->pData; if (HID_Handle->length == 0)return USBH_FAIL; if (USBH_HID_FifoRead(&HID_Handle->fifo, &mouse_report_data, HID_Handle->length) == HID_Handle->length) /* 读取FIFO*/ { mouse_info.button = mouse_report_data[0]; mouse_info.x = mouse_report_data[1]; mouse_info.y = mouse_report_data[2]; mouse_info.z = mouse_report_data[3]; return USBH_OK; } return USBH_FAIL; }

USBH_HID_MouseDecode函数,用于解析USB HOST获取到的USB鼠标数据,并存放到mouse_info结构体里面。该函数被USBH_HID_GetMouseInfo函数调用。 另外,为了提高对鼠标键盘的识别率和兼容性,对usbh_hid_core.c里面的两处代码进行了修改: 1,在usbh_hid.c里面,USBH_HID_Process函数,修改case HID_IDLE部分代码(366行)为:

case HID_IDLE: status = USBH_HID_GetReport(phost, 0X01U, 0U, HID_Handle->pData, (uint8_t) HID_Handle->length); if(status == USBH_OK) { fifo_write(&HID_Handle->fifo, HID_Handle->pData, HID_Handle->length); HID_Handle->state = HID_SYNC; }else if (status == USBH_BUSY) { HID_Handle->state = HID_IDLE; status = USBH_OK; }else if (status == USBH_NOT_SUPPORTED) { HID_Handle->state = HID_SYNC; status = USBH_OK; }else { HID_Handle->state = HID_ERROR; status = USBH_FAIL; } #if (USBH_USE_OS == 1) osMessagePut ( phost->os_event, USBH_URB_EVENT, 0); #endif break; 2,在usbh_ctlreq.c里面,USBH_CtlReq函数,修改case CMD_WAIT代码(552行)为: case CMD_WAIT: status = USBH_HandleControl(phost); if (status == USBH_OK) { /* Commands successfully sent and Response Received */ phost->RequestState = CMD_SEND; phost->Control.state =CTRL_IDLE; status = USBH_OK; }else if (status == USBH_FAIL) { /* Failure Mode */ phost->RequestState = CMD_SEND; status = USBH_FAIL; }else if(status == USBH_NOT_SUPPORTED) { phost->RequestState = CMD_SEND; } break;

经过以上两处修改,可以显著提高USB鼠标的兼容性,基本上各种鼠标都可以正常识别和使用了。 2. main.c代码 下面是main.c的程序,具体如下:

USBH_HandleTypeDef hUSBHost; /* USB Host处理结构体 */ /* 应用状态结构体类型定义 */ typedef enum { APPLICATION_IDLE = 0, /* 挂起状态 */ APPLICATION_DISCONNECT, /* 断开连接 */ APPLICATION_START, /* 应用开始(连接上了) */ APPLICATION_READY, /* 准备完成(可以执行相关应用代码了) */ APPLICATION_RUNNING, /* 运行中 */ } HID_ApplicationTypeDef; extern HID_MOUSE_Info_TypeDef mouse_info; HID_ApplicationTypeDef App_State = APPLICATION_IDLE; uint8_t g_usb_first_plugin_flag = 0; /* USB第一次插入标志,如果为1,说明是第一次插入 */ /** * @brief USB信息显示 * @param msg : 信息类型 * @arg 0, USB无连接 * @arg 1, USB键盘 * @arg 2, USB鼠标 * @arg 3, 不支持的USB设备 * @retval 无 */ void usbh_msg_show(uint8_t msgx) { switch (msgx) { case 0: /* USB无连接 */ lcd_show_string(30, 130, 200, 16, 16, "USB Connecting...", RED); lcd_fill(0, 150, lcddev.width, lcddev.height, WHITE); break; case 1: /* USB键盘 */ lcd_show_string(30, 130, 200, 16, 16, "USB Connected ", RED); lcd_show_string(30, 150, 200, 16, 16, "USB KeyBoard", RED); lcd_show_string(30, 180, 210, 16, 16, "KEYVAL:", RED); lcd_show_string(30, 200, 210, 16, 16, "INPUT STRING:", RED); break; case 2: /* USB鼠标 */ lcd_show_string(30, 130, 200, 16, 16, "USB Connected ", RED); lcd_show_string(30, 150, 200, 16, 16, "USB Mouse", RED); lcd_show_string(30, 180, 210, 16, 16, "BUTTON:", RED); lcd_show_string(30, 200, 210, 16, 16, "X POS:", RED); lcd_show_string(30, 220, 210, 16, 16, "Y POS:", RED); lcd_show_string(30, 240, 210, 16, 16, "Z POS:", RED); break; case 3: /* 不支持的USB设备 */ lcd_show_string(30, 130, 200, 16, 16, "USB Connected ", RED); lcd_show_string(30, 150, 200, 16, 16, "Unknow Device", RED); break; } } /** * @brief 完成USBH不同用户状态下的数据处理 * @param phost : USBH句柄指针 * @param id : USBH当前的用户状态 * @retval 无 */ void USBH_UserProcess(USBH_HandleTypeDef *phost, uint8_t id) { uint8_t dev_type = 0XFF; /* 设备类型,1,键盘;2,鼠标;其他,不支持的设备 */ switch (id) { case HOST_USER_SELECT_CONFIGURATION: break; case HOST_USER_DISCONNECTION: usbh_msg_show(0); /* 显示已经断开连接,准备重新连接 */ App_State = APPLICATION_DISCONNECT; break; case HOST_USER_CLASS_ACTIVE: App_State = APPLICATION_READY; dev_type = phost->device.CfgDesc.Itf_Desc[phost-> device.current_interface].bInterfaceProtocol; if (dev_type == HID_KEYBRD_BOOT_CODE) /* 键盘设备 */ { g_usb_first_plugin_flag = 1; /* 标记第一次插入 */ usbh_msg_show(1); /* 显示键盘界面 */ } else if (dev_type == HID_MOUSE_BOOT_CODE) /* 鼠标设备 */ { g_usb_first_plugin_flag = 1; /* 标记第一次插入 */ usbh_msg_show(2); /* 显示鼠标界面 */ } else { usbh_msg_show(3); /* 显示不支持 */ } break; case HOST_USER_CONNECTION: App_State = APPLICATION_START; break; default: break; } } /* 临时数组,用于存放鼠标坐标/键盘输入内容(4.3屏,最大可以输入2016字节) */ __align(4) uint8_t g_temp_buffer[2017]; /** * @brief USB鼠标数据处理 * @param data : USB鼠标数据结构体指针 * @retval 无 */ void mouse_data_process(HID_MOUSE_Info_TypeDef *data) { static signed short x, y, z; if (g_usb_first_plugin_flag) /* 第一次插入,将数据清零 */ { g_usb_first_plugin_flag = 0; x = y = z = 0; } x += (signed char)data->x; if (x > 9999)x = 9999; if (x y; if (y > 9999)y = 9999; if (y z; if (z > 9999)z = 9999; if (z button & 0X01)strcat((char *)g_temp_buffer, "LEFT "); if ((data->button & 0X03) == 0X02) { strcat((char *)g_temp_buffer, "RIGHT"); } else { if ((data->button & 0X03) == 0X03)strcat((char *)g_temp_buffer,"+RIGHT"); } if ((data->button & 0X07) == 0X04) { strcat((char *)g_temp_buffer, "MID "); } else { if ((data->button & 0X07) > 0X04)strcat((char *)g_temp_buffer, "+MID"); } lcd_fill(30 + 56, 180, lcddev.width - 1, 180 + 16, WHITE); lcd_show_string(30, 180, 210, 16, 16, (char *)g_temp_buffer, BLUE); sprintf((char *)g_temp_buffer, "X POS:%05d", x); lcd_show_string(30, 200, 200, 16, 16, (char *)g_temp_buffer, BLUE); sprintf((char *)g_temp_buffer, "Y POS:%05d", y); lcd_show_string(30, 220, 200, 16, 16, (char *)g_temp_buffer, BLUE); sprintf((char *)g_temp_buffer, "Z POS:%05d", z); lcd_show_string(30, 240, 200, 16, 16, (char *)g_temp_buffer, BLUE); //printf("btn,X,Y,Z:0x%x,%d,%d,%d\r\n",data->button,(signed char) data->x,(signed char)data->y,(signed char)data->z); } /** * @brief USB键盘数据处理 * @param data : USB键盘键值 * @retval 无 */ void keybrd_data_process(uint8_t data) { static uint16_t pos; static uint16_t endx, endy; static uint16_t maxinputchar; uint8_t buf[4]; if (g_usb_first_plugin_flag) /* 第一次插入,将数据清零 */ { g_usb_first_plugin_flag = 0; endx = ((lcddev.width - 30) / 8) * 8 + 30; /* 得到endx值 */ endy = ((lcddev.height - 220) / 16) * 16 + 220;/* 得到endy值 */ maxinputchar = ((lcddev.width - 30) / 8); maxinputchar *= (lcddev.height - 220) / 16;/* 当前LCD最大可以显示的字符数 */ pos = 0; } sprintf((char *)buf, "%02X", data); lcd_show_string(30 + 56, 180, 200, 16, 16, (char *)buf, BLUE); /* 显示键值 */ if (data >= ' ' && data if (pos)pos--; g_temp_buffer[pos] = 0; /* 添加结束符 */ } if (pos char c; HID_KEYBD_Info_TypeDef *k_pinfo; HID_MOUSE_Info_TypeDef *m_pinfo; if (App_State == APPLICATION_READY) { if (USBH_HID_GetDeviceType(phost) == HID_KEYBOARD) /* 键盘设备 */ { k_pinfo = USBH_HID_GetKeybdInfo(phost); /* 获取键盘信息 */ if (k_pinfo != NULL) { c = USBH_HID_GetASCIICode(k_pinfo); /* 转换成ASCII码 */ keybrd_data_process(c); /* 在LCD上显示出键盘字符 */ } } else if (USBH_HID_GetDeviceType(phost) == HID_MOUSE)/* 鼠标设备 */ { m_pinfo = USBH_HID_GetMouseInfo(phost); /* 获取鼠标信息 */ if (m_pinfo != NULL) { mouse_data_process(&mouse_info); /* LCD上显示鼠标信息 */ } } } } int main(void) { sys_cache_enable(); /* 打开L1-Cache */ HAL_Init(); /* 初始化HAL库 */ sys_stm32_clock_init(240, 2, 2, 4); /* 设置时钟, 480Mhz */ delay_init(480); /* 延时初始化 */ usart_init(115200); /* 串口初始化为115200 */ mpu_memory_protection(); /* 保护相关存储区域 */ led_init(); /* 初始化LED */ lcd_init(); /* 初始化LCD */ key_init(); /* 初始化按键 */ my_mem_init(SRAMIN); /* 初始化内部内存池(AXI) */ my_mem_init(SRAM12); /* 初始化SRAM12内存池(SRAM1+SRAM2) */ my_mem_init(SRAM4); /* 初始化SRAM4内存池(SRAM4) */ my_mem_init(SRAMDTCM); /* 初始化DTCM内存池(DTCM) */ my_mem_init(SRAMITCM); /* 初始化ITCM内存池(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); lcd_show_string(30, 110, 200, 16, 16, "USB Connecting...", RED); USBH_Init(&hUSBHost, USBH_UserProcess, 0); USBH_RegisterClass(&hUSBHost, USBH_HID_CLASS); USBH_Start(&hUSBHost); while (1) { USBH_Process(&hUSBHost); usb_demo(&hUSBHost); } }

这里总共六个函数: usbh_msg_show函数,用于显示一些提示信息,在USBH_UserProcess函数里面被调用。 USBH_UserProcess函数,用于执行USB主机不同用户状态下的数据处理,在usbh_core.c等相关文件里面被调用,该函数仅针对两个状态进行处理: 1、HOST_USER_DISCONNECTION,表示连接断开了,我们在屏幕显示连接断开,并设置App_State状态为APPLICATION_DISCONNECT; 2、HOST_USER_CLASS_ACTIVE,表示USB主机类激活,即接入USB设备已经准备就绪,可以正常工作了,我们设置App_State状态为:APPLICATION_READY,并根据接入设备的不同(鼠标/键盘)显示不同的信息。 mouse_data_process函数,用于处理USB鼠标数据,在液晶屏上面显示鼠标的移动坐标值和按键状态等信息,该函数在USB_Demo函数里面被调用。 keybrd_data_process函数,用于处理键盘数据,在液晶屏上面显示键盘的输入内容,最多支持2017个字符的输入显示。该函数在USB_Demo函数里面被调用。 usb_demo函数,根据当前用户状态和USB接入设备的类型,执行不同的数据处理。该函数在main函数里面被调用。 main函数,初始化各个外设后,通过USBH_Init等3个函数初始化并启动USB主机通信,然后进入死循环,不停的调用USBH_Process和usb_demo,执行各种USB处理。 60.4 下载验证 将程序下载到开发板后,然后在USB_HOST端子插入USB鼠标/键盘,注意:此时USB SLAVE口不要插USB线到电脑,否则会干扰! 等USB鼠标/键盘成功识别后,便可以看到LCD显示USB Connected,并显示设备类型:USB Mouse或者USB KeyBoard,同时也会显示输入的数据,如图60.4.1和图60.4.2所示: 在这里插入图片描述

图60.4.1 USB鼠标测试 在这里插入图片描述

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



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭