STM32的USB键盘及鼠标例程

stm32的usb键盘及鼠标例程通过网络可以搜到很,但是在同一个设备中集成键盘及鼠标的例程却比较少见(我通过google只搜到圈圈的基于51+d12的版本)。以下为我参考圈圈的例程做出来的集成键盘及鼠标的stm32的程序。
程序上除了usb_desc.c及usb_endp.c外,其它部份同单一的键盘鼠标一样。下面着重说一下usb_desc.c及usb_endp.c的不同之处。
单一键盘鼠标跟集成键盘鼠标这区别主要是报告描述符不同。单一键盘鼠标的报告描述符因只有一组报告输入/输出,故没有报告id,而集成的有两组报告(键盘及鼠标),所以每一组报告都有一个报告id加以区别。
另外就是在usb_endp.c中对端点的数据发送不知道是不是我的原因,待发送数据长度有问题,原因还未找到,只能在后面增加一条设置发送数据长度的语句。(如果不加的话,pc端只会收到8位数据,尽管我程序里设置了9位数据)
完整的usb_desc.c文件如下:
#include stm32lib\\usblib\\usb_lib.h
#include usb_desc.h
// km_devicedescriptor
const u8 hid_devicedescriptor[hid_size_device_desc]=
{
0x12, // blength
usb_device_descriptor_type, // bdescriptortype
0x00, // bcdusb
0x02,
0x00, // bdeviceclass
0x00, // bdevicesubclass
0x00, // bdeviceprotocol
0x40, // bmaxpacketsize40
0x34, // idvendor (0x0483)
0x12,
0x78, // idproduct = 0x5710
0x56,
0x00, // bcddevice rel.20.00
0x02,
1, // index of string descriptor describing manufacturer
2, // index of string descriptor describing product
3, // index of string descriptor describing the device serial number
0x01 // bnumconfigurations
};
// usb configuration descriptor
const u8 hid_configdescriptor[hid_size_config_desc]=
{
0x09, // blength
usb_configuration_descriptor_type, // bdescriptortype
hid_size_config_desc, // wtotallength
0x00,
0x01, // bnuminterfaces 接口数目
0x01, // bconfigurationvalue set_configuration命令所需要的参数值
0x00, // iconfiguration
0xe0, // bmattributes
0x32, // maxpower 100ma
//***************接口1配置***************
0x09,
usb_interface_descriptor_type,
0x00, // 接口编号
0x00,
0x02, // 端点数
0x03,
0x01, // 1 = boot 0 = no boot
0x01, // 0 = none 1 = keyboard 2 = mouse
0, //接口描述符索引值
//***************hid 描述符****************
0x09,
hid_descriptor_type,
0x10,
0x01,
0x00,
0x01,
0x22,
hid_size_report_desc,
0x00,
//***************端点1输入描述***************
0x07,
usb_endpoint_descriptor_type,
0x81,
0x03,
0x0a,
0x00,
0x20,
//***************端点1输出描述***************
0x07,
usb_endpoint_descriptor_type,
0x01,
0x03,
0x0a,
0x00,
0x20,
};
// mouse configdescriptor
const u8 hid_reportdescriptor[hid_size_report_desc]=
{
/************************usb键盘部分报告描述符**********************/
/*******************************************************************/
//这是一个全局(btype为1)条目,将用途页选择为普通桌面generic desktop page(0x01)
//后面跟一字节数据(bsize为1),后面的字节数就不注释了,
//自己根据bsize来判断。
0x05, 0x01, // usage_page (generic desktop)
//这是一个局部(btype为2)条目,说明接下来的集合用途用于键盘
0x09, 0x06, // usage (keyboard)
//这是一个主条目(btype为0)条目,开集合,后面跟的数据0x01表示
//该集合是一个应用集合。它的性质在前面由用途页和用途定义为
//普通桌面用的键盘。
0xa1, 0x01, // collection (application)
//报告id,这里定义键盘报告的id为1(报告id 0是保留的)
0x85, 0x01, //report id (1)
//这是一个全局条目,选择用途页为键盘(keyboard/keypad(0x07))
0x05, 0x07, // usage_page (keyboard/keypad)
//这是一个局部条目,说明用途的最小值为0xe0。实际上是键盘左ctrl键。
//具体的用途值可在hid用途表中查看。
0x19, 0xe0, // usage_minimum (keyboard leftcontrol)
//这是一个局部条目,说明用途的最大值为0xe7。实际上是键盘右gui键。
0x29, 0xe7, // usage_maximum (keyboard right gui)
//这是一个全局条目,说明返回的数据的逻辑值(就是我们返回的数据域的值)
//最小为0。因为我们这里用bit来表示一个数据域,因此最小为0,最大为1。
0x15, 0x00, // logical_minimum (0)
//这是一个全局条目,说明逻辑值最大为1。
0x25, 0x01, // logical_maximum (1)
//这是一个全局条目,说明数据域的数量为八个。
0x95, 0x08, // report_count (8)
//这是一个全局条目,说明每个数据域的长度为1个bit。
0x75, 0x01, // report_size (1)
//这是一个主条目,说明有8个长度为1bit的数据域(数量和长度
//由前面的两个全局条目所定义)用来做为输入,
//属性为:data,var,abs。data表示这些数据可以变动,var表示
//这些数据域是独立的,每个域表示一个意思。abs表示绝对值。
//这样定义的结果就是,当某个域的值为1时,就表示对应的键按下。
//bit0就对应着用途最小值0xe0,bit7对应着用途最大值0xe7。
0x81, 0x02, // input (data,var,abs)
//这是一个全局条目,说明数据域数量为1个
0x95, 0x01, // report_count (1)
//这是一个全局条目,说明每个数据域的长度为8bit。
0x75, 0x08, // report_size (8)
//这是一个主条目,输入用,由前面两个全局条目可知,长度为8bit,
//数量为1个。它的属性为常量(即返回的数据一直是0)。
//该字节是保留字节(保留给oem使用)。
0x81, 0x03, // input (cnst,var,abs)
//这是一个全局条目。定义位域数量为6个。
0x95, 0x06, // report_count (6)
//这是一个全局条目。定义每个位域长度为8bit。
//其实这里这个条目不要也是可以的,因为在前面已经有一个定义
//长度为8bit的全局条目了。
0x75, 0x08, // report_size (8)
//这是一个全局条目,定义逻辑最小值为0。
//同上,这里这个全局条目也是可以不要的,因为前面已经有一个
//定义逻辑最小值为0的全局条目了。
0x15, 0x00, // logical_minimum (0)
//这是一个全局条目,定义逻辑最大值为255。
0x25, 0xff, // logical_maximum (255)
//这是一个全局条目,选择用途页为键盘。
//前面已经选择过用途页为键盘了,所以该条目不要也可以。
0x05, 0x07, // usage_page (keyboard/keypad)
//这是一个局部条目,定义用途最小值为0(0表示没有键按下)
0x19, 0x00, // usage_minimum (reserved (no event indicated))
//这是一个局部条目,定义用途最大值为0x65
0x29, 0x65, // usage_maximum (keyboard application)
//这是一个主条目。它说明这六个8bit的数据域是输入用的,
//属性为:data,ary,abs。data说明数据是可以变的,ary说明
//这些数据域是一个数组,即每个8bit都可以表示某个键值,
//如果按下的键太多(例如超过这里定义的长度或者键盘本身无法
//扫描出按键情况时),则这些数据返回全1(二进制),表示按键无效。
//abs表示这些值是绝对值。
0x81, 0x00, // input (data,ary,abs)
//以下为输出报告的描述
//逻辑最小值前面已经有定义为0了,这里可以省略。
//这是一个全局条目,说明逻辑值最大为1。
0x25, 0x01, // logical_maximum (1)
//这是一个全局条目,说明数据域数量为5个。
0x95, 0x05, // report_count (5)
//这是一个全局条目,说明数据域的长度为1bit。
0x75, 0x01, // report_size (1)
//这是一个全局条目,说明使用的用途页为指示灯(led)
0x05, 0x08, // usage_page (leds)
//这是一个局部条目,说明用途最小值为数字键盘灯。
0x19, 0x01, // usage_minimum (num lock)
//这是一个局部条目,说明用途最大值为kana灯。
0x29, 0x05, // usage_maximum (kana)
//这是一个主条目。定义输出数据,即前面定义的5个led。
0x91, 0x02, // output (data,var,abs)
//这是一个全局条目。定义位域数量为1个。
0x95, 0x01, // report_count (1)
//这是一个全局条目。定义位域长度为3bit。
0x75, 0x03, // report_size (3)
//这是一个主条目,定义输出常量,前面用了5bit,所以这里需要
//3个bit来凑成一字节。
0x91, 0x03, // output (cnst,var,abs)
//下面这个主条目用来关闭前面的集合。bsize为0,所以后面没数据。
0xc0, // end_collection
//以下注释不包括第一字节报告id。
//通过上面的报告描述符的定义,我们知道返回的输入报告具有8字节。
//第一字节的8个bit用来表示特殊键是否按下(例如shift、alt等键)。
//第二字节为保留值,值为常量0。第三到第八字节是一个普通键键值的
//数组,当没有键按下时,全部6个字节值都为0。当只有一个普通键按下时,
//这六个字节中的第一字节值即为该按键的键值(具体的键值请看hid的
//用途表文档),当有多个普通键同时按下时,则同时返回这些键的键值。
//如果按下的键太多,则这六个字节都为0xff(不能返回0x00,这样会让
//操作系统认为所有键都已经释放)。至于键值在数组中的先后顺序是
//无所谓的,操作系统会负责检查是否有新键按下。我们应该在中断端点1
//中按照上面的格式返回实际的键盘数据。另外,报告中还定义了一个字节
//的输出报告,是用来控制led情况的。只使用了低7位,高1位是保留值0。
//当某位的值为1时,则表示对应的led要点亮。操作系统会负责同步各个
//键盘之间的led,例如你有两块键盘,一块的数字键盘灯亮时,另一块
//也会跟着亮。键盘本身不需要判断各种led应该何时亮,它只是等待主机
//发送报告给它,然后根据报告值来点亮相应的led。我们在端点1输出中断
//中读出这1字节的输出报告,然后对它取反(因为学习板上的led是低电平时
//亮),直接发送到led上。这样main函数中按键点亮led的代码就不需要了。
/************************usb鼠标部分报告描述符**********************/
/*******************************************************************/
//这是一个全局(btype为1)条目,选择用途页为普通桌面generic desktop page(0x01)
0x05, 0x01, // usage_page (generic desktop)
//这是一个局部(btype为2)条目,说明接下来的应用集合用途用于鼠标
0x09, 0x02, // usage (mouse)
//这是一个主条目(btype为0)条目,开集合,后面跟的数据0x01表示
//该集合是一个应用集合。它的性质在前面由用途页和用途定义为
//普通桌面用的鼠标。
0xa1, 0x01, // collection (application)
//报告id,这里定义鼠标报告的id为2
0x85, 0x02, //report id (2)
//这是一个局部条目。说明用途为指针集合
0x09, 0x01, // usage (pointer)
//这是一个主条目,开集合,后面跟的数据0x00表示该集合是一个
//物理集合,用途由前面的局部条目定义为指针集合。
0xa1, 0x00, // collection (physical)
//这是一个全局条目,选择用途页为按键(button page(0x09))
0x05, 0x09, // usage_page (button)
//这是一个局部条目,说明用途的最小值为1。实际上是鼠标左键。
0x19, 0x01, // usage_minimum (button 1)
//这是一个局部条目,说明用途的最大值为3。实际上是鼠标中键。
0x29, 0x03, // usage_maximum (button 3)
//这是一个全局条目,说明返回的数据的逻辑值(就是我们返回的数据域的值啦)
//最小为0。因为我们这里用bit来表示一个数据域,因此最小为0,最大为1。
0x15, 0x00, // logical_minimum (0)
//这是一个全局条目,说明逻辑值最大为1。
0x25, 0x01, // logical_maximum (1)
//这是一个全局条目,说明数据域的数量为三个。
0x95, 0x03, // report_count (3)
//这是一个全局条目,说明每个数据域的长度为1个bit。
0x75, 0x01, // report_size (1)
//这是一个主条目,说明有3个长度为1bit的数据域(数量和长度
//由前面的两个全局条目所定义)用来做为输入,
//属性为:data,var,abs。data表示这些数据可以变动,var表示
//这些数据域是独立的,每个域表示一个意思。abs表示绝对值。
//这样定义的结果就是,第一个数据域bit0表示按键1(左键)是否按下,
//第二个数据域bit1表示按键2(右键)是否按下,第三个数据域bit2表示
//按键3(中键)是否按下。
0x81, 0x02, // input (data,var,abs)
//这是一个全局条目,说明数据域数量为1个
0x95, 0x01, // report_count (1)
//这是一个全局条目,说明每个数据域的长度为5bit。
0x75, 0x05, // report_size (5)
//这是一个主条目,输入用,由前面两个全局条目可知,长度为5bit,
//数量为1个。它的属性为常量(即返回的数据一直是0)。
//这个只是为了凑齐一个字节(前面用了3个bit)而填充的一些数据
//而已,所以它是没有实际用途的。
0x81, 0x03, // input (cnst,var,abs)
//这是一个全局条目,选择用途页为普通桌面generic desktop page(0x01)
0x05, 0x01, // usage_page (generic desktop)
//这是一个局部条目,说明用途为x轴
0x09, 0x30, // usage (x)
//这是一个局部条目,说明用途为y轴
0x09, 0x31, // usage (y)
//这是一个局部条目,说明用途为滚轮
0x09, 0x38, // usage (wheel)
//下面两个为全局条目,说明返回的逻辑最小和最大值。
//因为鼠标指针移动时,通常是用相对值来表示的,
//相对值的意思就是,当指针移动时,只发送移动量。
//往右移动时,x值为正;往下移动时,y值为正。
//对于滚轮,当滚轮往上滚时,值为正。
0x15, 0x81, // logical_minimum (-127)
0x25, 0x7f, // logical_maximum (127)
//这是一个全局条目,说明数据域的长度为8bit。
0x75, 0x08, // report_size (8)
//这是一个全局条目,说明数据域的个数为3个。
0x95, 0x03, // report_count (3)
//这是一个主条目。它说明这三个8bit的数据域是输入用的,
//属性为:data,var,rel。data说明数据是可以变的,var说明
//这些数据域是独立的,即第一个8bit表示x轴,第二个8bit表示
//y轴,第三个8bit表示滚轮。rel表示这些值是相对值。
0x81, 0x06, // input (data,var,rel)
//下面这两个主条目用来关闭前面的集合用。
//我们开了两个集合,所以要关两次。bsize为0,所以后面没数据。
0xc0, // end_collection
0xc0 // end_collection
//以下注释不包括第一字节报告id。
//通过上面的报告描述符的定义,我们知道返回的输入报告具有4字节。
//第一字节的低3位用来表示按键是否按下的,高5位为常数0,无用。
//第二字节表示x轴改的变量,第三字节表示y轴的改变量,第四字节表示
//滚轮的改变量。我们在中断端点1中应该要按照上面的格式返回实际的
//鼠标数据。
};
// usb string descriptors
const u8 hid_stringlangid[hid_size_string_langid]=
{
hid_size_string_langid,
usb_string_descriptor_type,
0x09,
0x04
};
const u8 hid_stringvendor[hid_size_string_vendor]=
{
hid_size_string_vendor,
usb_string_descriptor_type,
's', 0, 't', 0, 'm', 0, 'i', 0, 'c', 0, 'r', 0, 'o', 0, 'e', 0,
'l', 0, 'e', 0, 'c', 0, 't', 0, 'r', 0, 'o', 0, 'n', 0, 'i', 0,
'c', 0, 's', 0
};
const u8 hid_stringproduct[hid_size_string_product] =
{
hid_size_string_product, /* blength */
usb_string_descriptor_type, /* bdescriptortype */
0x34, 0x6c, //水
0x62, 0x97, //面
0x4b, 0x4e, //之
0x0b, 0x4e, //下
0x84, 0x76, //的
0x55, 0x00, //u
0x53, 0x00, //s
0x42, 0x00, //b
0x4b, 0x6d, //测
0xd5, 0x8b, //试
};
u8 hid_stringserial[hid_size_string_serial] =
{
hid_size_string_serial, /* blength */
usb_string_descriptor_type, /* bdescriptortype */
0x73, 0x00, //s
0x6e, 0x00, //n
0x69, 0x00, //i
0x63, 0x00, //c
0x5f, 0x00, //_
0x6b, 0x00, //k
0x84, 0x76, //的
0x55, 0x00, //u
0x53, 0x00, //s
0x42, 0x00, //b
0x2e, 0x95, //键
0xd8, 0x76, //盘
};

基于FPGA的cy7c68013a双向通信教程
最霸气的国产SUV-潍柴英致U80,配置如何?
光纤光栅传感器的基本原理及实际应用
首个无监督3D点云物体实例分割算法
射频滤波器的类型
STM32的USB键盘及鼠标例程
S5PV210的I2C控制器简介
接触器的自锁和互锁图文详解
马云:与其担心AI抢工作 不如拥抱技术解决新问题
基于FPGA的 I²C 接口的芯片通信设计
合肥省打造“点线面”布局智能制造,实施“5G+工业互联网”创新工程
输电线路外破的原因和预防措施以及激光防外破监测装置的应用
有哪些佩戴舒适的降噪耳机?国庆出游必备佩戴舒适的降噪蓝牙耳机
自动气象站中接地系统的应用设计方案
各路大牛探讨人工智能未来的好与坏 普京强调人工智能关系国家未来
开口式霍尔电流传感器帮助直流配电系统智能改造
华为总裁表示排除华为将破坏公平竞争并导致极为负面的影响
中国移动宣布政企客户总数累计已突破718万服务集团成员数超过2.8亿
多普光电:销售额已超过1.1亿,营收同比大幅增长
太阳能电池特性