系列文章目录 第一节 USB协议及建立USB-HID工程
第二节 配置描述符及HID报文格式
第三节 PCB按键映射(基于稚晖君开源)
文章来源地址https://www.toymoban.com/news/detail-485801.html
文章目录
- 系列文章目录
- 前言
- 一、配置函数定位的汇总
-
二、具体配置
- 1.设备配置报文的修改
- 2.键盘报文描述修改
- 3.键盘报文数据发送及实现
- 4.值得注意的几点
- 5.附录
- 总结
文章来源:https://www.toymoban.com/news/detail-485801.html
前言
本文主要在上一节的基础上,对相关的设备以及报文描述符配置,实现USB键盘。
一、配置函数的定位汇总
这里只是汇总各个配置在哪个文件中,方便笔者后续复习时,方便查找。可先看下一部分,再回来看这。
生成后的工程文件夹如下图:(划斜线的为笔者自己创建的)
在USB_DEVICE/App路径下的usbd_desc.c及其头文件包含了设备描述符。
即配置VID信息,USB版本以及速度等信息,保持默认即可(如下图),还有一些其他描述符,具体有什么用,需了解USB协议发送的具体过程。这里笔者没深究。
usb_device.c主要写的USB初始化的相关操作,其中定义了USB的句柄,后续在main函数以及其他函数要用时需要声明(extern USBD_HandleTypeDef hUsbDeviceFS;)
usbd_custom.hid_if.c文件则是配置后续要发送报文的格式(即要送的报文代表什么含义,要发送多少个字节)。(需要修改的文件)。
在Middlewares文件夹下的usbd_customhid.c文件则包含了配置描述符(PC要对该USB口配置成什么类型,输出电流大小等信息,是否支持3.0,配置为HS、FS、或其他速度)。其头文件还定义各种报文以及缓冲区的大小。(需要修改的文件)。
二、具体配置
1.设备配置报文的修改
打开usbd_customhid.c文件,找到如下代码,每一行配置的什么,后面都有注释详解的。
因为笔者这款芯片只支持FS模式,且在cubemx中也选择的FS模式,故修改FS这个数组,若为HS或其他速度模式,则在C文件下翻,找到对应的修改即可。
__ALIGN_BEGIN static uint8_t USBD_CUSTOM_HID_CfgFSDesc[USB_CUSTOM_HID_CONFIG_DESC_SIZ] __ALIGN_END =
{
0x09, /* bLength: Configuration Descriptor size */
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
USB_CUSTOM_HID_CONFIG_DESC_SIZ,
/* wTotalLength: Bytes returned */
0x00,
0x01, /*bNumInterfaces: 1 interface*/
0x01, /*bConfigurationValue: Configuration value*/
0x00, /*iConfiguration: Index of string descriptor describing
the configuration*/
0xC0, /*bmAttributes: bus powered */
0x32, /*MaxPower 100 mA: this current is used for detecting Vbus*/
/************** Descriptor of CUSTOM HID interface ****************/
/* 09 */
0x09, /*bLength: Interface Descriptor size*/
USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/
0x00, /*bInterfaceNumber: Number of Interface*/
0x00, /*bAlternateSetting: Alternate setting*/
0x02, /*bNumEndpoints*/
0x03, /*bInterfaceClass: CUSTOM_HID*/
0x00, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
0x00, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
0, /*iInterface: Index of string descriptor*/
/******************** Descriptor of CUSTOM_HID *************************/
/* 18 */
0x09, /*bLength: CUSTOM_HID Descriptor size*/
CUSTOM_HID_DESCRIPTOR_TYPE, /*bDescriptorType: CUSTOM_HID*/
0x11, /*bCUSTOM_HIDUSTOM_HID: CUSTOM_HID Class Spec release number*/
0x01,
0x00, /*bCountryCode: Hardware target country*/
0x01, /*bNumDescriptors: Number of CUSTOM_HID class descriptors to follow*/
0x22, /*bDescriptorType*/
USBD_CUSTOM_HID_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor*/
0x00,
/******************** Descriptor of Custom HID endpoints ********************/
/* 27 */
0x07, /*bLength: Endpoint Descriptor size*/
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
CUSTOM_HID_EPIN_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
0x03, /*bmAttributes: Interrupt endpoint*/
CUSTOM_HID_EPIN_SIZE, /*wMaxPacketSize: 2 Byte max */
0x00,
CUSTOM_HID_FS_BINTERVAL, /*bInterval: Polling Interval */
/* 34 */
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: */
CUSTOM_HID_EPOUT_ADDR, /*bEndpointAddress: Endpoint Address (OUT)*/
0x03, /* bmAttributes: Interrupt endpoint */
CUSTOM_HID_EPOUT_SIZE, /* wMaxPacketSize: 2 Bytes max */
0x00,
CUSTOM_HID_FS_BINTERVAL, /* bInterval: Polling Interval */
/* 41 */
};
修改此三行
0x32, /*MaxPower 100 mA: this current is used for detecting Vbus*/
0x00, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
0x00, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
0x32表示为该口提供的最大输出电流。(0x32换算十进制即50,再×2 即100)即这里提供最大100ma输出电流。USB2.0最大提供输出500ma电流,即最大设置为0xFA;
这里使用的开发板且只有一个按键,0x32绰绰有余。若单独画PCB制作键盘,设置0xFA
0x00, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/这里设置有无BOOT
笔者还不太了解这块,故给的0x00
0x00, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/这里设置0x01表示键盘。
其他不变
2.键盘报文描述符设置
在usbd_custom.hid_if.c这个文件中。
题外话:具体为什么这样配置,参考这篇文章:HID报文讲解_skdev的博客-CSDN博客
想做其他操作,可以配合手册和生成软件,实现其他HID类设备
具体配置:找到 __ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END = 这个数组
将其中内容修改为:
__ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END =
{
0x05, 0x01, // Usage Page (Generic Desktop),
0x09, 0x06, // Usage (Keyboard),
0xA1, 0x01, // Collection (Application),
0x85, 0x01, // REPORT_ID (1)
// bitmap of modifiers
0x75, 0x01, // Report Size (1),
0x95, 0x08, // Report Count (8),
0x05, 0x07, // Usage Page (Key Codes),
0x19, 0xE0, // Usage Minimum (224),
0x29, 0xE7, // Usage Maximum (231),
0x15, 0x00, // Logical Minimum (0),
0x25, 0x01, // Logical Maximum (1),
0x81, 0x02, // Input (Data, Variable, Absolute), ;Modifier byte
// bitmap of keys
0x95, 0x78, // Report Count (120),
0x75, 0x01, // Report Size (1),
0x15, 0x00, // Logical Minimum (0),
0x25, 0x01, // Logical Maximum(1),
0x05, 0x07, // Usage Page (Key Codes),
0x19, 0x00, // Usage Minimum (0),
0x29, 0x77, // Usage Maximum (),
0x81, 0x02, // Input (Data, Variable, Absolute),
0xC0 /* END_COLLECTION */
};
细数一共有41个字节,故USBD_CUSTOM_HID_REPORT_DESC_SIZE这个宏定义要修改为41
即这个描述数组有多少个字节,USBD_CUSTOM_HID_REPORT_DESC_SIZE这个宏定义就必须是多少,有一点误差都会导致识别失败。
在usbd_conf.h这个头文件里面修改USBD_CUSTOM_HID_REPORT_DESC_SIZE的大小
修改为41,数字后面的U表示无符号数。
此时,我们保存编译下载到单片机。
会发现,识别到键盘设备,到此,成功一大半了。接下来进行按键发送报文。
上述报文描述符格式对应下图这种表。(已打包到资源文件)
3.按键发送报文数据
首先单独新建一个C文件和一个头文件,写报文发送函数。
新建一个枚举体,存放要放入的HID报文数据。
(枚举体小知识点:未给初始值时,默认值由0开始,之后自加1赋值
即:下列枚举体中的RESERVED=0,则ERROR_ROLL_OVER=1,POST_FAIL=2...以此类推。)
typedef enum
{
/*------------------------- HID report data -------------------------*/
LEFT_CTRL = -8,LEFT_SHIFT = -7,LEFT_ALT = -6,LEFT_GUI = -5,
RIGHT_CTRL = -4,RIGHT_SHIFT = -3,RIGHT_ALT = -2,RIGHT_GUI = -1,
RESERVED = 0,ERROR_ROLL_OVER,POST_FAIL,ERROR_UNDEFINED,
A,B,C,D,E,F,G,H,I,J,K,L,M,
N,O,P,Q,R,S,T,U,V,W,X,Y,Z,
NUM_1/*1!*/,NUM_2/*2@*/,NUM_3/*3#*/,NUM_4/*4$*/,NUM_5/*5%*/,
NUM_6/*6^*/,NUM_7/*7&*/,NUM_8/*8**/,NUM_9/*9(*/,NUM_0/*0)*/,
ENTER,ESC,BACKSPACE,TAB,SPACE,
MINUS/*-_*/,EQUAL/*=+*/,LEFT_U_BRACE/*[{*/,RIGHT_U_BRACE/*]}*/,
BACKSLASH/*\|*/,NONE_US/**/,SEMI_COLON/*;:*/,QUOTE/*'"*/,
GRAVE_ACCENT/*`~*/,COMMA/*,<*/,PERIOD/*.>*/,SLASH/*/?*/,
CAP_LOCK,F1,F2,F3,F4,F5,F6,F7,F8,F9,F10,F11,F12,
PRINT,SCROLL_LOCK,PAUSE,INSERT,HOME,PAGE_UP,DELETE,END,PAGE_DOWN,
RIGHT_ARROW,LEFT_ARROW,DOWN_ARROW,UP_ARROW,PAD_NUM_LOCK,
PAD_SLASH,PAD_ASTERISK,PAD_MINUS,PAD_PLUS,PAD_ENTER,
PAD_NUM_1,PAD_NUM_2,PAD_NUM_3,PAD_NUM_4,PAD_NUM_5,
PAD_NUM_6,PAD_NUM_7,PAD_NUM_8,PAD_NUM_9,PAD_NUM_0,
PAD_PERIOD , NONUS_BACKSLASH,APPLICATION,POWER,PAD_EQUAL,
F13,F14,F15,F16,F17,F18,F19,F20,F21,F22,F23,F24, EXECUTE,
HELP,MENU,SELECT,STOP,AGAIN,UNDO,CUT,COPY,PASTE,FIND,MUTE,VOLUME_UP,VOLUME_DOWN,
FN = 1000
/*------------------------- HID report data -------------------------*/
}KeyCode_t;
一共128个位,即16个字节(对应上文的报文描述数组)。一个位表示一个按键。接下来定义uint8_t hidbuffer[17];一个17位的数组。有17位的原因:第一个字节表示是ID号,后16个字节表示按键的状态,即128位,一位对应一个按键。(比如hidbuffer[1]=0x01,其他均为0,就表示LEFT_RIGHT这个按键被按下了。)(发送的报文: 0表示没按下,1表示没按下)
现在就可以写按键检测并发送函数了:
uint8_t hidbuffer[17];
extern USBD_HandleTypeDef hUsbDeviceFS;
void keyboard()
{
memset(hidbuffer,0,17);
hidbuffer[0] = 1;
if((HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_5))==0)
{
DelayUs(100);
if((HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_5))==0)
{
hidbuffer[3] = 0x01;
}
}
USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS,hidbuffer,17);
}
memset函数在"string.h"这个头文件中,这个函数的作用是将hidbuffer这个数组全部请0。
hidbuffer【0】=1;即ID号等于1,即是键盘的ID号(在上面报文描述数组中0x85,0x01这一句设置的)。这里因为我用的开发板只用了一个按键,故我把这个按键表示为E,"E"键在枚举体中是第17个,即在hidbuffer数组中就是hidbuffer【3】的第0位。
DelayUs(100);是为了按键消抖,让hidbuffer【3】=0x01,即表示按键按下时,让对应"E"这位等于1.然后发送报文即可(利USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS,hidbuffer,17);这个函数,该函数在 "usbd_customhid.h"中)
注:需要hUsbDeviceFS这个结构体不是在main中定义的,而是在USB_DEVICE.H中,故需要写 extern USBD_HandleTypeDef hUsbDeviceFS;
main函数调用即可,延时1ms表示1ms扫描一次按键。
然后编译烧录进单片机,开始测试
测试正常。至此实现完成。
4.值得注意的几个点
发送报文数据需要及时发送。否则PC机一直读的上一次报文,导致按键一直在被打印。
将HAL_DELAY(1)改为1000,相当于1秒扫描一次,并发送报文。此时烧录进单片机,按下按键一次会发现"e"键在一直被打印,因为没有即使更新报文(即以及松开了,没发送过去)。
消抖若过久,会导致有时候按下,并没有反应。因为有可能按下这个时间,程序还在上一次消抖那里延时。
笔者曾想,按下一个按键实现给主机保送多个不同的数据,比如按下一个按键就输入了密码这种操作,但在多次发送过程中,要么乱序,要么一直发送,目前未解决。(想了下,如果一个按键就输入了全部密码,那还要密码干嘛,就没继续研究了)
附录:工程代码下载链接:https://download.csdn.net/download/qq_21566881/87078754
HID手册及配置工具下载链接:https://download.csdn.net/download/qq_21566881/87073789?spm=1001.2014.3001.5503
(审核完成后上传)
2022.11.21补充:
推荐下载这个软件 查看电脑接受到的报文数据,根据接受的数据来调试
软件叫BUS HOUND 几MB大小,上手很快
总结
本文实现了STM32的USB-HID键盘的应用,下一篇文章将介绍基于稚晖君开源DIY的瀚文键盘的按键映射源码分析,看完下一篇后,便可动手画PCB板,DIY自己的一个USB键盘。
到了这里,关于基于STM32的USB键盘制作(保姆级)(二)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!