
前不久深圳的一个大学朋友联系我,他们公司需要做一个USB蓝牙接收器,功能如下:
USB蓝牙接收器插入计算机,并被识别为键盘。
手机程序连接USB蓝牙接收器;
手机程序向计算机发送键盘输入指令。
总的来说,他的需求有些非主流。它看起来像一个蓝牙键盘,但实际上它是一个带有USB接口的HID设备,而不是BLE的HID。BLE在这里只用来接收手机发送的数据。
一开始并没有真正去思考如何实现,就发到交流群,于是各路专家提出了自己的方案:
群友“喵喵一个麦克风”的方案是:单片机模拟USB键盘蓝牙串口透明传输,可以用CH551 KT6368A,KT6368A可以参考之前的文章《成本不足2元的蓝牙芯片,你能想得到吗?》。
群友“heibus”的方案是:串口转USB HID芯片蓝牙串口透明传输,可以用CH9328 KT6368A。
群友“oxlm”和“鹏飞”的方案是:使用单个蓝牙SOC,可以使用挪威的NRF52840,恩智浦的QN9080等。蓝牙芯片自带USB接口,一个芯片就能搞定。
群友“鲍蕾”的方案是:CH340 KT6368A,通过器件仿真框架在PC端编写一个上位机软件,将串口接收到的数据转换成虚拟HID。
以上四种方案原则上可以满足我这位同学的需求(说到我这位大学同学,请允许我临时跑个题。我上学的时候,他住在我宿舍对面。他是一个不折不扣的单片机迷。他先是玩51单片机,然后摆弄AVR单片机,再自学uCosII操作系统。后来他不知道怎么自学Java了,还特别爱专门研究技术。大学毕业后,我们走南闯北,他直接去深圳工作,从事Android相关的研发。我在这个领域这么多年,在深圳买房纯粹是凭个人能力。不得不感觉做移动互联网比做嵌入式更容易赚钱。
那他为什么要修这个USB蓝牙接收器?因为国外用的是他们新开发的APP,而这个蓝牙接收器是用来控制彩票机的。大致意思就是在手机上点击,实现在彩票机购买彩票的功能。
至于他为什么不直接在摇号机上买,他给我解释了智能的概念,我不知所措。
厉害了,最近和BLE打交道比较多,前段时间刚研究了超便宜的BLE芯片KT6368A,带来了BLE的另一个要求,所以我就考虑用群友“heibus”提出的CH9328 KT6368A方案来实现。
但是后来随着进一步的需求沟通,发现CH9328 KT6368A是不够的,因为手机发出的指令并不是原封不动的传输,实际上是需要转换的。比如手机发十进制1,USB HID对应的两组8字节十六进制数据:00 00 08 00 00 00 00 00 00 00和00000000000。
关于00 00 08 00 00 00 00 00 00 00和0000000000000的含义,就要简单的死记硬背一下USB HID的基础知识。
键盘发送给PC的数据一次8字节:
字节1字节2字节3字节4字节5字节6字节7字节8
定义如下:
BYTE1:特殊键,具体含义如下:
|-bit 0:左控制键是否按下,为1 |-bit 1:左shift是否按下,左alt是否按下,左alt是否按下,左GUI是否按下,左GUI是否按下,为1 |-bit 4:右控制键是否按下,为1 |-bit 5:右shift是否按下,为1 |-bit 6:右alt是否按下,为1 |-bit 7:右GUI是否按下,为1。
字节2:保留
BYTE3-BYTE8:这六个键是普通键。键值请参考USB HID到PS/2扫描码转换表。
例如,例如,对应于关键字A的一帧数据是:00 000x 040x 00 00 00 00 00 00,并且第三字节04由下表定义:
还是有点抽象,需要更直观。在电脑端,可以使用Bushound等USB分析软件,我这里用的是免费的USB分析仪:
我用的是笔记本电脑,我先连接一个USB键盘。
在软件左侧找到USB键盘对应的设备,开始监控。这里,只选择了原始数据视图。
按下并释放A键,软件界面会显示收到一串数据,实际上对应的是两组8字节的数据。你可以看到A实际上对应于04,00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
如果您一直按住A不松手,将显示以下信息:
00 00 00 00 00 00 00 00 00 00只有在您按下a键时才会显示。
如果你想同时按下SHIFT a组合键然后同时松开,对应的数据如下:
第一个字节代表左边的Shift键。
当然,如果先按Shift键,再按A键,然后松开A键,最后松开Shift键,那么就有四组数据,分别是:
我花了很长时间才明白。毕竟我之前没实际用过USB。再次回到他的蓝牙接收器要求,手机的输入范围是数字1-83,有的数字对应两个8字节数据,表示一个键的按下和释放,有的数字对应四个字节,表示Shift键的组合按下和释放,每个8字节数据之间的时间间隔是200ms。
既然KT6368A不行,我可以换一个可编程的蓝牙模块,比如TI的CC2541模块,Nordic的NRF51822模块。因为以前支持恩智浦的QN9021芯片,比较熟悉,所以用QN9021来实现。
用QN9021实现以上软件功能(蓝牙接收手机发来的一串数据然后转码输出)我以为分分钟就搞定了,实际调试并没有我想象的那么简单。因为常规的蓝牙透明传输方式是通过串口接收数据,然后通过蓝牙发送,这个要求只是逆向操作。这里涉及几个关键问题:
手机发送的是一串可能长也可能短的数据。因为QN9021是BLE 4.0芯片,一次发送的字节数最多为20字节,所以要考虑超过20字节的情况。
蓝牙芯片通过蓝牙接收数据,同时通过串口发送数据。需要考虑串口发送不完整,蓝牙再次发送数据的情况。
手机发送的不同键值要在程序中进行转码(有的对应发送两个8字节数据,有的对应4个8字节数据,每个8字节数据中间200ms)。
有经验的程序员可能不会觉得这是个问题,但是对于我这种很久没有真正写过代码的人来说,还是很久的。
以上问题1可以通过手机端分包解决;
问题2的解决方法是增加一个队列,将蓝牙接收到的数据放入队列并缓存,从另一个地方的队列通过串口发送。如何用C语言实现队列?我不能直接写。我在github上使用了一个开源代码:https://github.com/kuaileguyue/Ring-Buffer.
问题3:我在200ms定时器函数里做了一个小状态机来解决。状态机可以通过开关/case和标志位来实现。
最后,我们来总结一下这些方案:
该方案的特点是价格(元件)CH551 KT6368ACH551是可编程的,而蓝牙只传输几块钱而不用编程Ch 9328 BLE(NRF 51822/CC 2541/QN 9021等。)BLE是可编程的。CH9328硬件实现串口转USB HID,以及数十个单BLE(NRF52840/QN9080等。)SOC可以编程。CH340 KT6368A硬件不编程,几块钱就能在PC上编程。从硬件上看,这些方案都有BLE和USB功能,只是软件运行在不同的地方,最终都能实现所需的功能。
至于在实际项目或产品中选择哪种方案,实际上需要综合考虑很多因素,比如开发周期、成本、软件开发难度,甚至芯片是否好买。
接下来我研究第一个方案的实现,也就是CH551 KT6368A,这个方案以后大概率会用到,原因大家应该明白。
作者:wuyage来源:TopSemic嵌入式在此特别感谢









