制作缘由

公司内网只有PS/2接口,希望可以使用无线鼠标(貌似没有PS/2接口的)。而那种USB转PS/2的转接头只是简单的连线,需要键盘或鼠标本身支持PS/2模式才可以正常工作,现代的USB鼠标接收器显然没有考虑这一点。无意中发现有人用Arduino制作过USB键盘转PS/2的装置,那么鼠标一定也可以。这个装置,从原理上来说,就是一个转发器。对于USB鼠标(非蓝牙无线鼠标对主机来说仍是USB鼠标,和有线的没啥区别),他是一个主机,接收来自鼠标的数据;对于真正的主机,他是一个PS/2鼠标,负责向其发送转换过的数据。


PS/2部分

物理接口

如下图,实际有用的只有4根线,1 - Data:用于传输数据(双向);5 - Clock:用于向主机发送脉冲,控制读写数据;3 - Ground:接地;4 - Vcc (+5V) :电源。其中1和2可以接在Arduino任意的数字引脚上(但要避开USB Host Sheild占用的那些引脚,我实际使用的是2号、3号引脚),而3和4分别接在Arduino的5V和GND引脚上,这样不用额外供电,连到主机的PS/2接口就能正常工作。

数据传输(1位)

设备 > 主机 :由设备控制Clock以产生脉冲,首先,设置Data为要发送的位(高/低电平表示1/0),同时保持Clock为高电平一段时间,然后拉低Clock为低电平,产生一个下降沿,通知主机读取(锁存)Data,继续保持Clock为低电平一段时间后释放Clock(恢复为高电平,为传输下1位数据做准备)。。

主机 > 设备:仍然由设备控制Clock脉冲。当主机拉低Data或Clock时表示想要向设备发送(命令)数据,此时设备应该停止发送数据,优先读取来自主机的数据,待主机释放Clock(变为高电平)时,表示设备可以开始读入数据。首先,设备让Clock保持高电平一段时间,然后拉低Clock,产生一个下降沿,通知主机设置Data,继续保持Clock为低电平一段时间后,释放Clock为高电平(此时设备可以锁存当前Data,并且准备读入下1位数据)

数据传输(1字节)

每传输1字节数据,需要额外附带起始位(0),奇校验位,停止位(1),共11位

代码实现

最初在Arduino官方网站找到了一个叫做ps2dev的示例代码,里面的PS2dev类看上去实现了单个字节的读写,但是版本过于老旧。后来发现这个ps2dev已经是Arduino的一个库了,在库管理器中就可以搜索安装,但还是不够新,存在一些问题。他的最新版本在这里 https://github.com/Harvie/ps2dev,其中简单模拟鼠标的例子在某些时候会死循环出不来,我的修正已经被作者合并进去了。进一步地,为了提高通讯速度,我修改了时钟频率以及发送单个字节的间隔,目前工作正常。:)

鼠标协议

总的来说,主机向鼠标发送各种命令,以设置或询问鼠标的状态或参数;而鼠标呢,除了答复来自主机的命令,就是向主机发送位移数据包(处于Stream模式并且“数据报告”使能状态下,可以主动上报);更具体的可以参考网上的文档。(有中文版哦,搜索“PS2 鼠标 键盘技术参考”)下面列出我抓到的命令序列:

19:44:22.522 -> process_cmd FF       重置命令,进入Reset模式,重置各种状态,回复自检完成、设备ID,进入Steam模式
19:44:22.556 -> process_cmd F2       询问设备ID,首次询问应该回复00(标准鼠标),但我一律回复的03(滚轮鼠标)也没事哦
19:44:22.556 -> process_cmd E8       设置解析度
19:44:22.556 -> set resolution 0        读取解析度,1count/mm
19:44:22.556 -> process_cmd E6       设置缩放比例1:1
19:44:22.556 -> process_cmd E6       设置缩放比例1:1 
19:44:22.556 -> process_cmd E6       设置缩放比例1:1,为啥连续发了3次?
19:44:22.556 -> process_cmd E9       询问鼠标状态,此时应该回复3字节的状态包
19:44:22.556 -> process_cmd E8       设置解析度
19:44:22.589 -> set resolution 3        读取解析度,8count/mm
19:44:22.589 -> process_cmd F3       设置采样率
19:44:22.589 -> set sample rate 200  读取采样率 200/sec
19:44:22.589 -> process_cmd F3       设置采样率
19:44:22.589 -> set sample rate 100  读取采样率 100/sec
19:44:22.589 -> process_cmd F3       设置采样率
19:44:22.589 -> set sample rate 80    读取采样率 80/sec,注意,当主机按此顺序设置采样率,表示他支持03鼠标(滚轮鼠标)
19:44:22.589 -> process_cmd F2       询问设备ID,此时应该回复03,如果鼠标支持的话
19:44:27.052 -> process_cmd FF       重置命令
19:44:27.052 -> process_cmd F2       询问设备ID
19:44:27.052 -> process_cmd E8       设置解析度
19:44:27.052 -> set resolution 0        读取解析度,1count/mm
19:44:27.052 -> process_cmd E6       设置缩放比例1:1
19:44:27.052 -> process_cmd E6       设置缩放比例1:1
19:44:27.052 -> process_cmd E6       设置缩放比例1:1,同样的,为啥连续发了3次?我猜可能有含义的。
19:44:27.086 -> process_cmd E9       询问鼠标状态,此时应该回复3字节的状态包
19:44:27.086 -> process_cmd E8       设置解析度
19:44:27.086 -> set resolution 3        读取解析度,8count/mm
19:44:27.086 -> process_cmd F3       设置采样率
19:44:27.086 -> set sample rate 200  读取采样率 200/sec
19:44:27.086 -> process_cmd F3       设置采样率
19:44:27.086 -> set sample rate 100  读取采样率 100/sec
19:44:27.086 -> process_cmd F3       设置采样率
19:44:27.086 -> set sample rate 80    读取采样率 80/sec,为啥又来一遍?囧
19:44:27.086 -> process_cmd F2       询问设备ID,此时应该回复03,如果鼠标支持的话
19:44:27.120 -> process_cmd F3       设置采样率
19:44:27.120 -> set sample rate 200  读取采样率 200/sec
19:44:27.120 -> process_cmd F3       设置采样率
19:44:27.120 -> set sample rate 200  读取采样率 200/sec
19:44:27.120 -> process_cmd F3       设置采样率
19:44:27.120 -> set sample rate 80    读取采样率 80/sec,注意,当主机按此顺序设置采样率,表示他支持04鼠标(5键滚轮)
19:44:27.120 -> process_cmd F2       询问设备ID,此时应该回复04,如果鼠标支持的话,但是我没有。:)
19:44:27.120 -> process_cmd F3       设置采样率
19:44:27.120 -> set sample rate 100  读取采样率 100/sec
19:44:27.120 -> process_cmd E8       设置解析度
19:44:27.154 -> set resolution 3        读取解析度,8count/mm
19:44:27.154 -> process_cmd F4        使能“数据报告”,下面鼠标就可以开始主动上报“位移数据包”了。。


USB部分

使用USB Host Shield扩展板(采用MAX3421E芯片,支持USB2.0),加上USB Host Shield 2.0(它也是Arduino的一个库),最新版本在这里 https://github.com/felis/USB_Host_Shield_2.0,只要继承HIDUniversal类并且覆盖虚函数ParseHIDData就能得到HID设备的数据(数据格式可用USBBlyzer分析)。实际使用中发现,如果本次数据和上次的完全一致,则不会调用ParseHIDData。(比如同方向滚动滚轮,后面的都被忽略了)只需略微修改hiduniversal.cpp,已提合并请求,作者暂时没理我。^_^ (我的修改见 https://github.com/felis/USB_Host_Shield_2.0/pull/522)

USB是一种主从结构,只有当主机向设备发送IN令牌包(见上述库中 USB::InTransfer)时,设备才可以向主机发送数据。在鼠标设备内部,分为USB芯片(用于与USB主机通讯)和MCU(用于执行鼠标固件程序)。当检测到鼠标状态变化(比如按键按下或发生移动),MCU将状态数据写入USB芯片的缓冲区,待收到IN令牌包时由USB芯片将数据发给主机。如果主机请求数据速度过慢会怎样?我猜数据会丢失?从目前能找到的固件程序代码看,都是直接向芯片缓冲区写数据的,并没有额外的处理。或许当缓冲区满了之后,合并后续数据会好一点?


鼠标回报率

也就是上面提到的采样率。

PS/2理论上支持:10、20、40、60、80、100、200。我的主机最终设置的值为100,目前USB部分耗时2ms,PS/2部分耗时6-7ms,已经可以满足。曾经试过把时钟频率调的更快、发送单个字节的间隔改的更短,没成功。:(

USB1.1支持:125

USB2.0支持:250、500、1000

考虑到USB和PS/2收发速率可能不匹配,所以实现了一个循环队列用于存放来自USB鼠标的数据,并尽可能地将新的数据与队尾元素合并。但是,实际测试中,队列长度从未超过1并且合并函数从未被调用,即便在loop()中尝试读取多次USB数据再向PS/2发送也是如此,Why?可能是因为我的USB鼠标只有125的回报率吧。


呼吸灯

为了了解转换器的工作状态(以及更炫酷一点),额外加了一个全彩LED灯在盒子顶部。繁忙时,以红色快速闪烁;而空闲时,则以绿色缓慢呼吸;


最终成品

完整源代码在这里 https://github.com/liumazi/MzMouse


参考资料

https://www.zhihu.com/question/46855816/answer/619886526

https://www.arduino.cn/forum.php?mod=viewthread&tid=22851

http://www.burtonsys.com/ps2_chapweske.htm

http://usb.baiheee.com/usb_projects/easy_usb_51_programer_plus/usb_hid_mouse.html

https://blog.csdn.net/suipingsp/article/details/30238891

Arduino + USB Host Sheild 实现USB鼠标转PS/2接口相关推荐

  1. 网络摄像头转usb接口_Arduino + USB Host Sheild 实现USB鼠标转PS/2接口

    制作缘由 公司内网只有PS/2接口,希望可以使用无线鼠标(貌似没有PS/2接口的).而那种USB转PS/2的转接头只是简单的连线,需要键盘或鼠标本身支持PS/2模式才可以正常工作,现代的USB鼠标接收 ...

  2. android usb host hid,Android USB Host与HID通讯

    前端时间捣鼓一个HID的硬件, 需要和android通信, 网上搜索了一圈,收获不小. 其中代码之处有些地方需要注意的, 特此注明一下: /*** USB HOST 连接 HID *@authorIV ...

  3. linux usb host复位,Linux USB Host-Controller的初始化代码框架分析

    Linux USB Host-Controller的初始化代码框架分析 http://blog.csdn.net/zkami usb_hcd_omap_probe (const struct hc_d ...

  4. S3C2440 WINCE6将USB DEVICE改成USB HOST,实现两个USB HOST

    S3C2440一般默认的是一个USB DEVICE,一个USB HOST,即一个主口,一个从口,先来看看USB Device与USB Host相关知识. USB Host: 最底层就是USB Host ...

  5. USB学习6---Linux Android USB软件架构设计

    下面学习针对高通平台的HS-USB(HS:high speed高速)堆栈(stack)软件架构设计和源代码布局的细节. Android HS-USB堆栈基于下面几点: Gadget driver fr ...

  6. STM32 USB Host 同时连接多个设备样例(如鼠标和键盘)--原创

    STM32 USB Host 同时连接多个设备样例(如鼠标和键盘) 在网上搜了很多都是USB Host单独连接鼠标或键盘的样例, 而当前很多无线鼠标键盘都是并到一个USB口上的, 也就是同一个USB有 ...

  7. 一步一步解决 kernel 2.6 usb host driver

    2.6在s3c2410上usb host不工作的直接结果就是提示110错误:  usb 1-1: device descriptor read/64, error -110 追踪错误代码,我们来看看能 ...

  8. WINCE对USB HOST供电的控制

    上图的USBH_EN(对应于GPG6)是用于对USB HOST供电的,这个需要在BSP中进行控制,可在init.c文件的OEMInit() ->ConfigureGPIO()函数中假如下面的代码 ...

  9. Android开发者指南(29) —— USB Host and Accessory

    前言 本章内容为Android开发者指南的 USB章节,译为"USB主从设备",版本为Android 4.0 r1,翻译来自:"太阳火神的美丽人生",欢迎访问他 ...

  10. 安卓USB开发教程 一 USB Host 与 Accessory

    安卓通过两种模式:USB Accessory 与 USB Host 模式支持多种 USB 外设与安卓 USB 配件(实现安卓配件协议的硬件).在 USB 配件模式下,外部 USB 硬件充当 USB 主 ...

最新文章

  1. codeforces 547B【单调栈】
  2. optee运行时来了一个REE(linux)中断--代码导读
  3. 【Python基础】利用 Python 搞定精美网络图!
  4. BGP属性+13条选路原则(转载)
  5. 向mvc controller传递json数组
  6. [react] 在react中遍历的方法有哪些?它们有什么区别呢?
  7. 《系统分析与设计方法》 练习计算投资回收分析
  8. 罗永浩谈乔纳森离职:乔布斯才是苹果的灵魂设计师
  9. 通用mapper_通用Mapper快速开发,搭建项目
  10. python程序双向链表_Python 实现双向链表(图解)
  11. 揭露QPS增高后的秘密
  12. Keras:基于Python的深度学习库
  13. Python实现文件搜索
  14. qcloud_cos 的安装问题
  15. bootstrap table表头列名转行
  16. centos 把文件打包为tar.gz命令
  17. 微信开发工具BUG(漏洞),魔法加法
  18. java bcd asc_BCD码和ASCII码的区别
  19. 大电流输出信号隔离转换模块
  20. 树莓派 docker homeassistant supervisor安装

热门文章

  1. 视频教程-Cisco CCNP路由实验专题讲解视频课程--路由重分发篇-思科认证
  2. autocad.net-图片打印合成
  3. 学习笔记 css border-radius
  4. 网站建设中做到需求分析细致,网站优化也就顺理成章了
  5. java开发autocad_.NET AutoCAD二次开发之路(四、文字篇)
  6. 服务器搬迁方案_机房搬迁的一般步骤及实施方案
  7. 如何解决Flash “此Flash Player 与您的地区不相容,请重新安装Flash”的提示?
  8. 2022年十大数据可视化工具,值得收藏
  9. Ae导出 计算机内存,ae导出视频太大怎么办-缩小Ae导出视频大小的方法 - 河东软件园...
  10. 2109-全国大学生电子设计竞赛-F-纸张数识别(内含arduino代码以及题解)