自制HID键盘问题事宜 | 基本解决了
<p>万能的技术支持们,使用8H8K64U做主芯片,做一个PC HID键盘,碰到以下问题。</p><p>一般PC HID键盘在按下和弹起时都会发送数据到PC,使用市场购买的HID键盘,</p>
<p>通过Bus Hound抓数据,按下SHIFT键和弹起是,抓到的数据如下:</p>
<p>红色箭头表示按下时转到的数据,绿色箭头表示弹起时抓到的数据。</p>
<p><img src="data/attachment/forum/202504/12/094415b8wdxclo1wm1zuws.png" alt="image.png" title="image.png" /></p>
<p>按下“1”和弹起时抓到的数据如下:</p>
<p><img src="data/attachment/forum/202504/12/094900xtstahhejdh7mrdm.png" alt="image.png" title="image.png" /></p>
<p>###################################################</p>
<p>而我做使用STC8H8K64U,按下SHIFT键和弹起时,抓到的数据:</p>
<p><img src="data/attachment/forum/202504/12/094623twa3uxxo1hxapa6p.png" alt="image.png" title="image.png" /></p>
<p>按下“1”键和弹起时,抓到的数据如下:</p>
<p><img src="data/attachment/forum/202504/12/094802q888azhgqr4rgfz8.png" alt="image.png" title="image.png" /></p>
<p>可以确定是,橙色框第2个数据和绿色框的数据不是代码自己发送的,不清除这些数据哪里来的,并且橙色框第2个数据还是12个字节,好像完全不符合HID报告的要求。</p>
<p>键盘的HID报告描述符使用STC官方的,如下:</p>
<p>char code HIDREPORTDESC =<br />
{<br />
0x05,0x01, // USAGE_PAGE(Generic Desktop); 0x01 代表通用桌面<br />
0x09,0x06, // USAGE(Keyboard); 0x06 代表键盘<br />
0xa1,0x01, // COLLECTION(Application); 0x01 代表集合开始<br />
0x05,0x07, //USAGE_PAGE(Keyboard); 0x07 代表键盘</p>
<p>//第1个自己描述<br />
0x19,0xe0, //USAGE_MINIMUM(224); 定义修饰键的输入报告部分:<br />
0x29,0xe7, //USAGE_MAXIMUM(255);<br />
0x15,0x00, //LOGICAL_MINIMUM(0); 全局逻辑最小值0<br />
0x25,0x01, //LOGICAL_MAXIMUM(1); 全局逻辑最大值1<br />
0x75,0x01, //REPORT_SIZE(1); 全局报告大小1bit<br />
0x95,0x08, //REPORT_COUNT(8); 全局报告数量8<br />
0x81,0x02, //INPUT(Data,Variable,Absolute);</p>
<p>//第2个字节描述<br />
0x75,0x08, //REPORT_SIZE(8);<br />
0x95,0x01, //REPORT_COUNT(1);<br />
0x81,0x01, //INPUT(Constant);</p>
<p>//第3-8字节描述<br />
0x19,0x00, //USAGE_MINIMUM(0);<br />
0x29,0x65, //USAGE_MAXIMUM(101);<br />
0x15,0x00, //LOGICAL_MINIMUM(0);<br />
0x25,0x65, //LOGICAL_MAXIMUM(101);<br />
0x75,0x08, //REPORT_SIZE(8);<br />
0x95,0x06, //REPORT_COUNT(6);<br />
0x81,0x00, //INPUT(Data,Array);</p>
<pre><code>0x05,0x08, //USAGE_PAGE(LEDs);
0x19,0x01, //USAGE_MINIMUM(1);
0x29,0x03, //USAGE_MAXIMUM(3);
0x15,0x00, //LOGICAL_MINIMUM(0);
0x25,0x01, //LOGICAL_MAXIMUM(1);
0x75,0x01, //REPORT_SIZE(1);
0x95,0x03, //REPORT_COUNT(3);
0x91,0x02, //OUTPUT(Data,Variable,Absolute);
0x75,0x05, //REPORT_SIZE(5);
0x95,0x01, //REPORT_COUNT(1);
0x91,0x01, //OUTPUT(Constant);
0xc0, // END_COLLECTION;
</code></pre>
<p>};</p>
<p>尝试过使用网上其它的键盘HID报告描述符,也是一样的结果。</p>
<p>STC技术支持是否有什么建议,我尝试下。</p>
在开发基于STC8H9K64U的PC HID键盘时,您遇到的数据传输问题可能与HID协议的实现细节有关。以下是对问题的分析及可能的解决方案:
1. HID协议基础
HID(Human Interface Device)协议是USB设备与主机通信的标准协议之一。HID键盘在按键按下和弹起时,通常会发送不同的数据包来通知主机按键状态的变化。每个数据包通常包含8个字节,其中前两个字节用于表示修饰键(如Shift、Ctrl等)的状态,后续字节用于表示普通按键的键码。
2. 问题分析
根据您提供的信息,市场购买的HID键盘在按下和弹起时发送的数据包是完整的,而您自制的HID键盘在按键弹起时发送的数据包可能存在问题。具体表现为:
按下时发送的数据包符合预期。
弹起时发送的数据包可能未正确清除按键状态或未发送完整的数据包。
3. 可能的原因及解决方案
3.1 数据包未正确清除
在按键弹起时,HID键盘应发送一个数据包,其中按键状态被清除(即键码字段为0)。如果未正确清除键码字段,主机可能无法识别按键弹起事件。
解决方案:
确保在按键弹起时,发送的数据包中键码字段被正确清除。例如,如果按下“1”键时发送的键码为0x1E,弹起时应发送键码为0x00的数据包。
3.2 数据包未完整发送
HID协议要求每个数据包都包含8个字节。如果数据包未完整发送,主机可能无法正确解析按键状态。
解决方案:
确保每次发送的数据包都包含8个字节,即使某些字段未使用也应填充为0。例如,按下“1”键时发送的数据包可能为,弹起时应发送。
3.3 时序问题
HID键盘在按键按下和弹起时,数据包的发送时序可能影响主机的识别。如果数据包发送过快或过慢,主机可能无法正确处理按键事件。
解决方案:
确保按键按下和弹起时数据包的发送间隔符合HID协议的要求。通常,按键按下和弹起之间的时间间隔应大于10ms,以确保主机能够正确识别按键事件。
4. 调试建议
使用Bus Hound或类似工具:继续使用Bus Hound抓取数据包,对比市场购买的HID键盘和自制HID键盘的数据包,找出差异。
逐步调试:在代码中逐步调试,确保按键按下和弹起时发送的数据包符合预期。
参考HID协议文档:详细阅读HID协议文档,确保数据包的格式和内容符合标准。
5. 总结
在开发基于STC8H9K64U的PC HID键盘时,确保按键按下和弹起时发送的数据包符合HID协议的要求是关键。通过正确清除键码字段、确保数据包完整发送以及控制数据包发送时序,可以有效解决您遇到的问题。希望以上分析和建议对您有所帮助。
如有进一步问题,欢迎继续交流。
[本文内容由国芯人工智能辅助生成,仅供参考] 上传完整工程看看 完整工程 HID报告的格式是由报告描述符决定的,不同的设备报告格式不同是正常的,因为根据需要实现的功能的不同,描述符是可以自定义的
想读出描述符,据我所知由于操作系统的限制,除操作系统底层外的软件无法读取,bushound是过滤驱动也不行,必须用硬件USB协议分析仪在上电瞬间抓包
描述符可以根据你需要实现的功能修改,不必抄袭买的成品键盘
USBSendData发送逻辑有问题,未检测UsbInBusy,可能导致多包粘包现象
可参考8H官方例程包键盘范例对比测试。
代码的USB发送函数就是参考STC原有的,只不过把赋值部分不一样而已。
STC官网代码:
void usb_class_in()
{
BYTE key;
BYTE i;
if (DeviceState != DEVSTATE_CONFIGURED)
return;
if (!UsbInBusy && fKeyOK)
{
fKeyOK = 0;
key = 0;
key = 0;
key = 0;
key = 0;
key = 0;
key = 0;
key = 0;
key = 0;
switch (bKeyCode)
{
case 0xfe:key = 0x1e; break;
case 0xfd:key = 0x1f; break;
case 0xfb:key = 0x20; break;
case 0xf7:key = 0x21; break;
case 0xef:key = 0x22; break;
case 0xdf:key = 0x23; break;
case 0xbf:key = 0x24; break;
case 0x7f:key = 0x25; break;
}
IE2 &= ~0x80; //EUSB = 0;
UsbInBusy = 1;
usb_write_reg(INDEX, 1);
for (i=0; i<8; i++)
{
usb_write_reg(FIFO1, key);
}
usb_write_reg(INCSR1, INIPRDY);
IE2 |= 0x80; //EUSB = 1;
}
}
我的代码:
void USBSendData(void)
{
BYTE i;
if ( DeviceState != DEVSTATE_CONFIGURED ) //如果USB配置没有完成,就直接退出
return;
IE2 &= ~0x80; //EUSB = 0;
UsbInBusy = 1;
usb_write_reg(INDEX, 1);
for (i=0; i<8; i++)
usb_write_reg(FIFO1, USBHIDKeyBuffer); //发送按键码
usb_write_reg(INCSR1, INIPRDY);
IE2 |= 0x80; //EUSB = 1;
/****************************************** 发送空结束字节,缓存区全0表示所有按键结束 *****************************************/
/*
while( UsbInBusy );
for(i=0; i<8; i++) //清除发送缓冲区
USBHIDKeyBuffer = 0;
IE2 &= ~0x80; //EUSB = 0;
UsbInBusy = 1;
usb_write_reg(INDEX, 1);
for (i=0; i<8; i++)
usb_write_reg(FIFO1, USBHIDKeyBuffer); //发送按键码
usb_write_reg(INCSR1, INIPRDY);
IE2 |= 0x80; //EUSB = 1;
*/
}
这个两个发送代码,没看出来哪里不同啊。 这个UsbInBusy是我原来要按下一个按键后,马上结束按键而设置的,等待USB发送完成。 我刚找出来STC官方的代码,修改了按键扫描部分代码,官方的是P0口4x4扫描,我是P0口直接对地的的。
代码如下:
void usb_class_in()
{
BYTE key;
BYTE i;
if (DeviceState != DEVSTATE_CONFIGURED)
return;
if (!UsbInBusy && fKeyOK)
{
fKeyOK = 0;
key = 0;
key = 0;
key = 0;
key = 0;
key = 0;
key = 0;
key = 0;
key = 0;
switch (bKeyCode)
{
case 0xfe:key = 0x1e; break;
case 0xfd:key = 0x1f; break;
case 0xfb:key = 0x20; break;
case 0xf7:key = 0x21; break;
case 0xef:key = 0x22; break;
case 0xdf:key = 0x23; break;
case 0xbf:key = 0x24; break;
case 0x7f:key = 0x25; break;
}
IE2 &= ~0x80; //EUSB = 0;
UsbInBusy = 1;
usb_write_reg(INDEX, 1);
for (i=0; i<8; i++)
{
usb_write_reg(FIFO1, key);
}
usb_write_reg(INCSR1, INIPRDY);
IE2 |= 0x80; //EUSB = 1;
}
}
void scan_key()
{
BYTE key;
P0M0 = 0x00;
P0M1 = 0x00;
P0PU = 0xFF;
/*
key = 0;
P0 = 0xff;
P06 = 0;
_nop_();
_nop_();
key |= P0 & 0x0f;
P06 = 1;
P07 = 0;
_nop_();
_nop_();
key |= (P0 & 0x0f) << 4;
P07 = 1;
*/
key=0;
P0 = 0xff;
_nop_();
_nop_();
key = P0;
if (key != bKeyCode)
{
bKeyCode = key;
bKeyDebounce = 20;
}
else
{
if (bKeyDebounce)
{
bKeyDebounce--;
if (bKeyDebounce == 0)
{
fKeyOK = 1;
}
}
}
}
测试结果也是一样的啊,如下:
红色箭头是按下按键时抓到的数据,绿色箭头是按键弹起时抓的数据。
STC官方工程文件
页:
[1]
2