关于STM32上用HID HOST调鼠标数据的解析

news2025/2/26 0:25:08

一、前言

关于这章主要是基于我前面的那篇文章
链接: 关于怎么用Cubemx生成的USBHID设备实现读取一体的鼠标键盘设备(改进版)
https://blog.csdn.net/qq_29187987/article/details/139535648?spm=1001.2014.3001.5501
在这里插入图片描述

引用的文章的简介

引用的这篇文章主要就是讲如何在STM32上配置用于界面的鼠标和键盘,一般的STM32的配置都是只有一个鼠标或者一个键盘,这篇文章主要是用STM32读取现在市面上的一体化的USB鼠标键盘,实现这种一个USB的鼠标键盘的STM32配置。

关于这篇文章的实现

由于上面那篇文章的实现,可以读取到鼠标和键盘的数据,但是其实鼠标并没有完全实现,鼠标移动的时候会有漂移的现象,所以这篇文章主要根据linux读取USB的方法,将STM32也实现linux下的USB鼠标键盘操作。并显示在界面上。

二、解决鼠标漂移的方法

要完全复现linux下的鼠标,就要先了解鼠标会给STM32发送多少数据。根据网上的各种文章参考,鼠标给STM32和给电脑的数据都是8个字节。

插述:关于网上的一些说法

参考文章:链接: 罗技 无线鼠标 USB HID数据格式
https://blog.csdn.net/sinat_23998749/article/details/123572543

这篇文章是我参考的文章,但是有一些和我现在这个杂牌子的鼠标不太一样,不知道是不是鼠标不一样还是什么情况,文章中数据字节的意义不对。

鼠标的数据解析

STM32中,配置为USB HOST后,Cubemx会自动生成代码,其中有一个文件usbh_hid_mouse.c里,是关于解析鼠标数据的函数。下面是部分解析用的代码。

下面展示一些 内联代码片

static const HID_Report_ItemTypedef prop_x =
{
  (uint8_t *)(void *)mouse_report_data + 1, /*zijie3 data*/
  8,     /*size*/
  0,     /*shift*/
  0,     /*count (only for array items)*/
  1,     /*signed?*/
  0,     /*min value read can return*/
  0xFFFF,/*max value read can return*/
  0,     /*min vale device can report*/
  0xFFFF,/*max value device can report*/
  1      /*resolution*/
};

1、函数添加

其中,mouse_report_data + 1的部分就是第1个字节的数据。,所以,要解析所有的鼠标数据,所以要多创建几个这个结构体,并且修改下要要解析第几个数据。具体如下:

创建prop_y,读取第二个字节数据。

下面展示一些 内联代码片

static const HID_Report_ItemTypedef prop_y =
{
  (uint8_t *)(void *)mouse_report_data + 2, /*zijie4  data*/
  8,     /*size*/
  0,     /*shift*/
  0,     /*count (only for array items)*/
  1,     /*signed?*/
  0,     /*min value read can return*/
  0xFFFF,/*max value read can return*/
  0,     /*min vale device can report*/
  0xFFFF,/*max value device can report*/
  1      /*resolution*/
};

创建prop_z,读取第三个字节数据。

下面展示一些 内联代码片

static const HID_Report_ItemTypedef prop_z =
{
  (uint8_t *)(void *)mouse_report_data + 3, /*zijie4  data*/
  8,     /*size*/
  0,     /*shift*/
  0,     /*count (only for array items)*/
  1,     /*signed?*/
  0,     /*min value read can return*/
  0xFFFF,/*max value read can return*/
  0,     /*min vale device can report*/
  0xFFFF,/*max value device can report*/
  1      /*resolution*/
};

创建prop_extra,读取第四个字节数据。

下面展示一些 内联代码片

static const HID_Report_ItemTypedef prop_extra =
{
  (uint8_t *)(void *)mouse_report_data + 4, /*zijie4  data*/
  8,     /*size*/
  0,     /*shift*/
  0,     /*count (only for array items)*/
  1,     /*signed?*/
  0,     /*min value read can return*/
  0xFFFF,/*max value read can return*/
  0,     /*min vale device can report*/
  0xFFFF,/*max value device can report*/
  1      /*resolution*/
};

创建prop_extra2,读取第五个字节数据。

下面展示一些 内联代码片

static const HID_Report_ItemTypedef prop_extra2 =
{
  (uint8_t *)(void *)mouse_report_data + 5, /*zijie4  data*/
  8,     /*size*/
  0,     /*shift*/
  0,     /*count (only for array items)*/
  1,     /*signed?*/
  0,     /*min value read can return*/
  0xFFFF,/*max value read can return*/
  0,     /*min vale device can report*/
  0xFFFF,/*max value device can report*/
  1      /*resolution*/
};

2、修改函数

完成后,就是要怎么使用1中自己创建的代码。这些代码也都是在usbh_hid_mouse.c中使用。

在usbh_hid_mouse.c中,有一个函数叫作
USBH_StatusTypeDef USBH_HID_MouseDecode(USBH_HandleTypeDef *phost)
这个函数如下:

下面展示一些 内联代码片

USBH_StatusTypeDef USBH_HID_MouseDecode(USBH_HandleTypeDef *phost)
{
  HID_HandleTypeDef *HID_Handle = (HID_HandleTypeDef *) phost->pActiveClass->pData[1];

  if (HID_Handle->length == 0U)
  {
    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.z = (uint8_t)HID_ReadItem((HID_Report_ItemTypedef *) &prop_z, 0U);
		mouse_info.extra = (uint8_t)HID_ReadItem((HID_Report_ItemTypedef *) &prop_extra, 0U);
		mouse_info.extra2 = (uint8_t)HID_ReadItem((HID_Report_ItemTypedef *) &prop_extra2, 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;
}

3、修改mouse_info

我们找到mouse_info,这个变量在哪里定义的,发现是HID_MOUSE_Info_TypeDef类型的,所以我们要修改的就是这个HID_MOUSE_Info_TypeDef类型。
通过查找功能,我们找到HID_MOUSE_Info_TypeDef这个的定义如下:
下面展示一些 内联代码片

typedef struct _HID_MOUSE_Info
{
  uint8_t              x;
  uint8_t              y;
  /*下面是自己加的*/
	uint8_t              z;
	uint8_t              extra;
		uint8_t              extra2;
		  /*上面是自己加的*/
  uint8_t              buttons[3];
}
HID_MOUSE_Info_TypeDef;

可以看到,我自己在mouse_info的定义中加了三个结构体中的变量,z,extra,extra2,这三个都是后面会用到的。

关于完成上面的步骤后要了解的知识

到了这里,一般跑起来不会有什么问题,但是STM32的代码并不能解析出鼠标数据,因为没有参考上一篇文章,根据文章头部的文章
链接: 关于怎么用Cubemx生成的USBHID设备实现读取一体的鼠标键盘设备(改进版)
根据这篇文章可知,实现USB鼠标键盘的读取最后是在USBH_HID_EventCallback(USBH_HandleTypeDef *phost,uint8_t flag)这个函数中的。函数如下:
下面展示一些 内联代码片

void USBH_HID_EventCallback(USBH_HandleTypeDef *phost,uint8_t flag)
{
  char c;
  HID_KEYBD_Info_TypeDef *k_pinfo;
  HID_MOUSE_Info_TypeDef *m_pinfo;
  if(1 == flag)
	{
		m_pinfo = USBH_HID_GetMouseInfo(phost); /* 这里解析鼠标数据 */
		if (m_pinfo != NULL)
		{
				
				Mousedataprocess(mouse_info);//我自己的函数
				memset(m_pinfo ,0,sizeof(HID_MOUSE_Info_TypeDef ));
		}
	}
	else
	{
		k_pinfo = USBH_HID_GetKeybdInfo(phost); /* ?????? */
		if (k_pinfo != NULL)
		{
				c = USBH_HID_GetASCIICode(k_pinfo); /* ???ASCII? */
				Keypaddataprocess(c);  //我自己的函数
				memset(k_pinfo ,0,sizeof(HID_KEYBD_Info_TypeDef ));
		}
   
	}
}

根据改进版的实现USB鼠标键盘可知,每次鼠标移动或者按键或者键盘按键,USBH_HID_EventCallback就会运行一次。

那么,我们只要在USBH_HID_EventCallback中跑USBH_HID_GetMouseInfo就可以解析鼠标数据了。

因为USBH_HID_MouseDecode在USBH_HID_GetMouseInfo函数中跑了的。

printf出来发现,x是鼠标的按键数据,y,z,extra是鼠标的移动数据。
也就是鼠标的第二个字节,第三个字节,第四个字节。

【注释】:其实这里的第二个字节,第三个字节,第四个字节只是针对我的鼠标,不知道适不适用于读者的鼠标,所以读者最好printf出现看看,读者的鼠标移动后,第几个字节的数据会变化。

根据我解析出来的结果可知,我的y的8个位,和z的前4个位,组合在一起是X移动的数据,extra和z的后4个位组合在一起,是Y移动的数据。

在这里插入图片描述
因为移动数据是12位的,所以当12位的最高位是1的时候,表示是向负向移动(补码表示),最高位(第12位)是0的时候,表示鼠标向正向移动。

解释如下:
对于字节5-7 的意义:
字节6的低4位bits + 字节5,构成12bits的X移动数据,补码表示
字节7 +字节6的高4bits,构成12bits的Y移动数据,补码表示
似乎是高4bits为符号位,但可以统一这样解码:
如果最高bit0, 其值直接为正向移动值;
如果最高bit1,则12bits数据按位取反,然后+1,得到移动值,但表示为负向移动值;

原文链接:https://blog.csdn.net/sinat_23998749/article/details/123572543

我的鼠标移动解析代码

我的解析代码如下:
status1代表是y,status2代表是z,status3代表是extra。

下面展示一些 内联代码片

void Showmousemovedstatus(uint8_t status1,uint8_t status2,uint8_t status3)
{
			uint16_t X_move = 0;
			X_move = (status2&0x0F)<<8;
			X_move = X_move | status1;
			uint16_t Y_move = 0;
			Y_move = (status2&0xF0)>>0;
			Y_move = (Y_move | (status3<<8))>>4;
			//printf("%d,%d,%d,%d,%d\r\n",data.x,data.y,data.z,data.extra,data.extra2);
			printf("%d,%d\r\n",X_move,Y_move);
			if((X_move&0x0800)!=0)
			{
				X_move = ~(X_move-1);
				X_move = X_move<<4;
				X_move = X_move>>4;
				if(_mymouse.x >= X_move){
				_mymouse.x-=X_move;}
				else{
					_mymouse.x=0;}
			}
			else
			{
				_mymouse.x+=X_move;
				if(_mymouse.x >= HOR_RES_SIZE)
				{
					_mymouse.x=HOR_RES_SIZE;
				}
			}
			if((Y_move&0x0800)!=0)
			{
				Y_move = ~(Y_move-1);
				Y_move = Y_move<<4;
				Y_move = Y_move>>4;
				if(_mymouse.y >= Y_move){
					_mymouse.y -= Y_move;}
				else{
				_mymouse.y = 0;}
			}
			else
			{
				_mymouse.y+=Y_move;
				if(_mymouse.y >= VER_RES_SIZE )
				{
					_mymouse.y=VER_RES_SIZE ;
				}
			}
			//printf("%d,%d\r\n",X_move,Y_move);
}

结尾结合下LVGL

由于我的项目是要结合LVGL,所以我把我的_mymouse.y和_mymouse.x作为全局变量,给了LVGL里的函数。
下面展示一些 内联代码片

static void mouse_get_xy(lv_coord_t * x, lv_coord_t * y)
{
    /*Your code comes here*/
		
	
    (*x) = _mymouse.x;
    (*y) = _mymouse.y;
}

实现鼠标显示在界面上。

LVGL+USB鼠标

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1841854.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【文末附gpt升级秘笈】AI音乐大模型崛起:版权归属与创意产业的新挑战

AI音乐大模型崛起&#xff1a;版权归属与创意产业的新挑战 随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;已经深入到社会生活的各个领域&#xff0c;音乐领域也不例外。最近一个月&#xff0c;轮番上线的音乐大模型不仅为普通人打开了音乐创作的大门&…

am62x芯片安全类型确认(HS-SE, HS-FS or GP)

文章目录 芯片安全类型设置启动方式获取串口信息下载脚本运行脚本示例sk-am62x板卡参考芯片安全类型 AM62x 芯片有三个安全级别。 • GP:通用版本 • HS-FS:高安全性 - 现场安全型 • HS-SE:高安全性 - 强制安全型 在SD卡启动文件中,可以查看到, 但板上的芯片,到底是那…

手把手教你挖赏金系列(2)如何挖掘短信验证码漏洞

免责声明 由于传播、利用本公众号所发布的而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人承担。LK安全公众号及原文章作者不为此承担任何责任&#xff0c;一旦造成后果请自行承担&#xff01;如有侵权烦请告知&#xff0c;我们会立即删除并致歉。谢谢&#…

计算机网络 —— 应用层(DHCP)

计算机网络 —— 应用层&#xff08;DHCP&#xff09; 什么是DHCPDHCP工作过程DHCP DISCOVERDHCP OFFERDHCP RQUESTDHCP ACK DHCP租约机制中继代理工作原理功能与优势 我们今天来计网的DHCP&#xff1a; 什么是DHCP DHCP&#xff08;Dynamic Host Configuration Protocol&…

人工智能这么厉害,比如GPT-4,为什么没有看到程序员大量失业?

从ChatGPT第一版发布到现在&#xff0c;还不到一年的时间中&#xff0c;可是它使用的GPT架构已经从3.5版本进化到现在的4.0版本&#xff0c;随之而来的是其能力的极大提升。下面是GPT-4在其官网的介绍中的一句话&#xff1a; GPT-4是OpenAI最先进的系统&#xff0c;可以产生更安…

FlowUs:打造沉浸式协作体验感受

直观的用户体验 从我个人的角度来看&#xff0c;FlowUs的界面设计非常符合现代审美&#xff0c;简洁而不失功能性。每次打开FlowUs&#xff0c;我都能迅速找到我需要的功能&#xff0c;这大大提升了我的工作效率。 实时协作的流畅性 在FlowUs中&#xff0c;我最喜欢的功能之一就…

记录:[android] SSLHandshakeException: Handshake failed 问题;已解决!

1、问题描述&#xff1a;在使用Retrofit2 时在安卓老设备上&#xff08;安卓6.0&#xff09;网络无法请求、安卓 10 、 11 未出现此问题&#xff1f;what? 原因&#xff1a;服务端 TLS 版本过高 2、废话不多说、解决方案A 、添加依赖&#xff1a;implementation org.conscrypt…

[面试题]Kafka

[面试题]Java【基础】[面试题]Java【虚拟机】[面试题]Java【并发】[面试题]Java【集合】[面试题]MySQL[面试题]Maven[面试题]Spring Boot[面试题]Spring Cloud[面试题]Spring MVC[面试题]Spring[面试题]MyBatis[面试题]Nginx[面试题]缓存[面试题]Redis[面试题]消息队列[面试题]…

如何才能入手到好的开放式耳机,总结六招耳机亲测好方法

作为一款当代年轻人基本离不开的数码产品&#xff0c;很有理由进入这次618的必买清单&#xff1b;但是如果不是耳机领域或者是数码领域的&#xff0c;对于耳机的参数、数据和使用等都不了解&#xff0c;就很容易造成踩雷&#xff0c;不仅浪费钱&#xff0c;还浪费时间&#xff…

国际版游戏陪练源码电竞系统源码支持Android+IOS+H5

&#x1f3ae;电竞之路的得力助手 一、引言&#xff1a;电竞新纪元&#xff0c;陪练小程序助力成长 在电竞热潮席卷全球的今天&#xff0c;每一个电竞爱好者都渴望在竞技场上脱颖而出。然而&#xff0c;独自一人的游戏之路往往充满了挑战和困难。幸运的是&#xff0c;国际版游…

14:补充-变量作用域-页面加载

试问&#xff1a;在控制台打印person1、person2结果是什么&#xff1f; var person2{name:"张三",age:21}//页面加载完成执行的事件window.onloadfunction(){var person1{name:"李四",age:18}} 打印结果&#xff1a;在控制台中发现person1这个变量不存在 原…

iptables(3)规则管理

简介 上一篇文章中,我们已经介绍了怎样使用iptables命令查看规则,那么这篇文章我们就来介绍一下,怎样管理规则,即对iptables进行”增、删、改”操作。 注意:在进行iptables实验时,请务必在个人的测试机上进行,不要再有任何业务的机器上进行测试。 在进行测试前,为保障…

MAVEN-SNAPSHOT和RELEASE + 打包到远程仓库

一、快照版本SNAPSHOT和发布版本RELEASE区别 快照版本SNAPSHOT和发布版本RELEASE区别-CSDN博客 在使⽤maven过程中&#xff0c;我们在开发阶段经常性的会有很多公共库处于不稳定状态&#xff0c;随时需要修改并发布&#xff0c;可能⼀天就要发布⼀次&#xff0c;遇到bug时&am…

Vue 前后端分离开发:懒人必备的API SDK

在前后端分离的项目中,前后端通过API进行通信和数据交换。随着项目规模的扩大,API的数量可能从几十个增加到几百个。为了简化API的编写和维护,我们可以利用JavaScript的特性,通过动态生成接口方法来实现懒人开发。本文将详细介绍如何统一接口调用方式、抽象和封装接口,并利…

模版与策略模式

一&#xff0c;怎么选择 如果需要固定的执行流程&#xff0c;选模版 如果不需要固定的执行流程&#xff0c;只需要对一个方法做具体抽象&#xff0c;选策略 参考文章&#xff1a; 常用设计模式汇总&#xff0c;告诉你如何学习设计模式 二&#xff0c;常用写法 子类 exten…

天池人脸识别项目复现

1 项目背景 #c 概述 项目的目的 图像分类是整个计算机视觉领域中最基础的任务&#xff0c;也是最重要的任务之⼀&#xff0c;最适合拿来进⾏学习实践。为了让新⼿们能够⼀次性体验⼀个⼯业级别的图像分类任务的完整流程&#xff0c;本次我们选择带领⼤家完成⼀个对图片中⼈脸进…

《计算机组成原理》(学习笔记)(王道)

目录 一、计算机系统概述 *1.1 计算机发展历程 *1.1.1 计算机硬件的发展 *1.1.2 计算机软件的发展 1.2 计算机系统层次结构 1.2.1 计算机系统的组成 1.2.2 计算机硬件的基本组成 冯诺依曼体系结构特点&#xff08;6&#xff09;&#xff1a; 1.2.3 计算机软件的分类 …

RAG 与微调在大模型应用中如何抉择

随着大型语言模型热度的不断升温&#xff0c;越来越多的开发者和企业投身于基于这些大模型的应用程序开发中。然而&#xff0c;面对预训练基座模型未能达到预期的表现时&#xff0c;如何提升应用程序的性能就成为了一个迫在眉睫的问题。我们终将会问自己&#xff1a;为了优化结…

常用的Java日志框架:Log4j、SLF4J和Logback

日志是软件开发中不可或缺的一部分&#xff0c;它有助于记录应用程序的运行状态、调试问题和监控系统。Java中有多个流行的日志框架&#xff0c;如Log4j、SLF4J和Logback。 一、Log4j 1.1 什么是Log4j&#xff1f; Log4j是Apache基金会开发的一个开源日志框架&#xff0c;它…

【无线传感网】LEACH路由算法

1、LEACH路由算法简介 LEACH协议,全称是“低功耗自适应集簇分层型协议” (Low Energy Adaptive Clustering Hierarchy),是一种无线传感器网络路由协议。基于LEACH协议的算法,称为LEACH算法。 2、LEACH路由算法的基本思想 LEACH路由协议与以往的路由协议的不同之处在于其改变…