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

一、前言

关于这章主要是基于我前面的那篇文章
链接: 关于怎么用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.mzph.cn/web/28359.shtml

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

相关文章

数据库概述1

数据&#xff1a;描述事物的符号记录称为数据&#xff1b; 包括数字、图片、音频等&#xff1b; 数据库&#xff1a;长期储存在计算机内有组织、可共享的大量数据的集合&#xff1b;数据库中的数据按照一定的数据模型组织、描述和存储&#xff0c;具有较小的数据冗余、较高的数…

STM32学习笔记(一)--时钟树详解

&#xff08;1&#xff09;时钟概述&#xff1b;时钟是具有周期性的脉冲信号&#xff0c;最常用的是占空比50%的方波。&#xff08;时钟相当于单片机的脉搏&#xff1b;STM32本身非常复杂&#xff0c;外设非常的多&#xff0c;为了保持低功耗工作&#xff0c;STM32 的主控默认不…

【Python】深入了解 AdaBoost:自适应提升算法

我们都找到天使了 说好了 心事不能偷藏着 什么都 一起做 幸福得 没话说 把坏脾气变成了好沟通 我们都找到天使了 约好了 负责对方的快乐 阳光下 的山坡 你素描 的以后 怎么抄袭我脑袋 想的 &#x1f3b5; 薛凯琪《找到天使了》 在机器学习的领域中&#x…

算法工程师 | 如何快速 了解,掌握一个算法!脚踏实地,迎着星辰,向前出发 ~

本文是一些碎碎念 希望对正在迈向 算法工程师道路的你 有所裨益 一般来说&#xff0c;代码 中会有很多 算法实现的细节&#xff0c;但论文可能并没有体现&#xff0c;所以能够尝试自己 仔细阅读论文&#xff0c;手动复现代码&#xff0c;基本上来说对 这个 算法 你有了全…

夏季城市环境卫生挑战多:TSINGSEE青犀智慧环卫方案助力城市垃圾站智能管理

一、背景分析 夏季&#xff0c;随着气温的攀升&#xff0c;城市垃圾的数量和种类也随之增加&#xff0c;这给环卫工作带来了极大的挑战。环卫垃圾站点作为城市垃圾处理的重要一环&#xff0c;其管理效率直接关系到城市环境的整洁与卫生。近年来&#xff0c;随着视频监控技术的…

【Redis】Redis常见问题——缓存更新/内存淘汰机制/缓存一致性

目录 回顾数据库的问题如何提高 mysql 能承担的并发量&#xff1f;缓存解决方案应对的场景 缓存更新问题定期生成如何定期统计定期生成的优缺点 实时生成maxmemory 设置成多少合适呢&#xff1f;项目类型上来说 新的问题 内存淘汰策略Redis淘汰策略为什么redis要内存淘汰内存淘…

ESP32 IDF ADF 加入音频

需要把mp3制作成音频bin 用ADF自带工具 果用户需要生成自己的 audio-esp.bin&#xff0c;则需要执行 mk_audio_bin.py 脚本&#xff08;位于 $ADF_PATH/tools/audio_tone/mk_audio_tone.py&#xff09;&#xff0c;并且指定相关文件的路径。 源 MP3 文件在 tone_mp3_folder …

红黑树(C++)

文章目录 写在前面1. 红黑树的概念及性质1. 1 红黑树的概念1. 2 红黑树的性质 2. 红黑树节点的定义3. 红黑树的插入3.1 按照二叉搜索的树规则插入新节点3.2 检测新节点插入后&#xff0c;红黑树的性质是否造到破坏 4.红黑树的删除5.红黑树的验证6.源码 写在前面 在上篇文章中&…

5.3.1_2 二叉树的层次遍历

&#x1f44b; Hi, I’m Beast Cheng&#x1f440; I’m interested in photography, hiking, landscape…&#x1f331; I’m currently learning python, javascript, kotlin…&#x1f4eb; How to reach me --> 458290771qq.com 喜欢《数据结构》部分笔记的小伙伴可以订…

c++模板模式

文章目录 模板模式什么是模板模式为什么使用模板模式模板模式实现步骤 示例模板模式优缺点 模板模式 什么是模板模式 模板模式&#xff08;Template Method Pattern&#xff09;是一种行为设计模式&#xff0c;它定义了一个操作中的算法骨架&#xff0c;将某些步骤的具体实现延…

[DDR4] DDR 简史

依公知及经验整理&#xff0c;原创保护&#xff0c;禁止转载。 专栏 《深入理解DDR4》 存和硬盘&#xff0c;这对电脑的左膀右臂&#xff0c;共同扛起了存储的重任。内存以其超凡的存取速度闻名&#xff0c;但一旦断电&#xff0c;内存中的数据也会消失。它就像我们的工作桌面&…

tokenization(二)子词切分方法

文章目录 概述BPE构建词表词元化代码实现 WordPieceUnigram估算概率&#xff08;E&#xff09;删除词元&#xff08;M&#xff09; 参考资料 概述 接上回&#xff0c;子词词元化&#xff08;Subwords tokenization&#xff09;是平衡字符级别和词级别的一种方法&#xff0c;也…

网络通信架构

BS架构/CS架构 使用协议分别对应&#xff1a; TCP / HTTP 在计算机网络和软件开发中&#xff0c;CS架构&#xff08;Client-Server Architecture&#xff0c;客户端-服务器架构&#xff09;和BS架构&#xff08;Browser-Server Architecture&#xff0c;浏览器-服务器架构&am…

云和运维(SRE)的半生缘-深读实证02

这个标题不算太夸张&#xff0c;云计算和很多IT岗位都有缘&#xff0c;但是和运维&#xff08;SRE&#xff09;岗位的缘分最深。 “深读实证”系列文章都会结合一些外部事件&#xff0c;点明分析《云计算行业进阶指南》书中的内容。本次分享介绍了下列内容&#xff1a; 我以运维…

Matlab电话按键拨号器设计

前言 这篇文章是目前最详细的 Matlab 电话按键拨号器设计开源教程。如果您在做课程设计或实验时需要参考本文章&#xff0c;请注意避免与他人重复&#xff0c;小心撞车。博主做这个也是因为实验所需&#xff0c;我在这方面只是初学者&#xff0c;但实际上&#xff0c;从完全不…

USB2.0高速转接芯片CH347应用开发手册

CH347应用开发手册 V1.3 一、简介 CH347是一款USB2.0高速转接芯片&#xff0c;以实现USB-UART(HID串口/VCP串口)、USB-SPI、USB-I2C、USB-JTAG以及USB-GPIO等接口&#xff0c;分别包含在芯片的四种工作模式中。 CH347DLL用于为CH347芯片提供操作系统端的UART/SPI/I2C/JTAG/B…

Linux_应用篇(17) FrameBuffer 应用编程

本章学习 Linux 下的 Framebuffer 应用编程&#xff0c; 通过对本章内容的学习&#xff0c; 大家将会了解到 Framebuffer 设备究竟是什么&#xff1f;以及如何编写应用程序来操控 FrameBuffer 设备。 本章将会讨论如下主题。 ⚫ 什么是 Framebuffer 设备&#xff1f; ⚫ LCD 显…

N32G031 ADC初始化

目录 1. ADC初始化概述 2. ADC初始化详细步骤 2.1 ADC配置 2.2 ADC初始化函数调用 2.3 DMA配置&#xff08;可选&#xff09; 3. 初始化结果验证 4. 注意事项 ADC采样注意事项 1. ADC初始化概述 在N32G031单片机中&#xff0c;ADC的初始化是确保ADC模块能够正常工作的…

安卓在Fragment控制状态栏显示隐藏

废话不多上效果 隐藏 显示 核心代码 首先是Framgrent package com.zx.tab;import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button;impor…

【redis】Redis的经典使用场景

目录 1.最常见——缓存2.数据共享分布式3.分布式锁4.全局ID5.计数器6.限流7.位统计8.购物车9.用户消息时间线timeline10.消息队列11.抽奖点赞、签到、打卡13.商品标签14.商品筛选15.用户关注、推荐模型16排行榜 1.最常见——缓存 数据类型&#xff1a;string例如&#xff1a;热…