【GD32F427开发板试用】+使用USBFS轻松实现HID键盘应用

news2024/12/23 15:23:04

本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动,更多开发板试用活动请关注极术社区网站。作者:不锈钢铁侠

前言

最近有项目需要用到键盘自动输入功能,提升工作效率。故使用该开发板实现自定义输入内容并通过按键控制自动通过usb输出。

简介

在官方GD32F4xx_Firmware_Library_V3.0.2 里的example上进行修改,使用外设分别有USB device(HID)、gpio(KEY,LED)、time2(usb)

项目结构

Application--用户文件  
CMSIS---CMSIS文件  
GD32F4xx_StdPeriph_Driver--外设驱动  
USB_Drivers--USB核心驱动文件  
USB_Device--USB设备驱动  
USB_Class----USB类文件  
Startup--启动文件

USB接口使用(HID按键)

USB HID类是USB设备的一个标准设备类,包括的设备非常多。HID类设备定义它属于人机交互操作的设备,用于控制计算机操作的一些方面,如USB鼠标、USB键盘、USB游戏操纵杆等。但HID设备类不一定要有人机接口,只要符合HID类别规范的设备都是HID设备。

USB HID设备的一个好处就是操作系统自带了HID类的驱动程序,而用户无需去开发驱动程序,只要使用API系统调用即可完成通信。

HID设备的描述符除了**5个USB的标准描述符(设备描述符、配置描述符、接口描述符、端点描述符、字符串描述符)**外,还包括三个HID设备类特定的描述符:HID描述符、报告描述符(Report)、实体描述符(Physical)。

standard_hid_core.C

//设备描述符
//设备描述符主要包括厂商ID(vendorID)和产品ID(productID)、USB协议等内容。一个设备只有一个设备描述符。

__ALIGN_BEGIN const usb_desc_dev hid_dev_desc __ALIGN_END =
{
    .header =
    {
        .bLength          = USB_DEV_DESC_LEN,//描述符长度(18字节)
        .bDescriptorType  = USB_DESCTYPE_DEV//描述符类型(设备描述符为0x01)
    },
    .bcdUSB                = 0x0200U,//设备使用的USB协议版本
    .bDeviceClass          = 0x00U,//类代码
    .bDeviceSubClass       = 0x00U,//子类代码
    .bDeviceProtocol       = 0x00U,//设备使用的协议
    .bMaxPacketSize0       = USB_FS_EP0_MAX_LEN,//端点0最大包长
    .idVendor              = USBD_VID,//厂商ID
    .idProduct             = USBD_PID,//产品ID
    .bcdDevice             = 0x0100U,//设备版本号
    .iManufacturer         = STR_IDX_MFC,//描述厂商的字符串的索引
    .iProduct              = STR_IDX_PRODUCT,//描述产品的字符串的索引
    .iSerialNumber         = STR_IDX_SERIAL,//产品序列号字符串的索引
    .bNumberConfigurations = USBD_CFG_MAX_NUM//可能的配置数
};


//配置描述符
//配置描述符,定义了设备的配置信息。一个设备可以有多个配置描述符。配置描述符描述了该配置的接口数、供电模式等信息。
__ALIGN_BEGIN const usb_hid_desc_config_set hid_config_desc __ALIGN_END = 
{
    .config =
    {
        .header =
        {
            .bLength         = sizeof(usb_desc_config),//该描述符字节数长度(9字节)
            .bDescriptorType = USB_DESCTYPE_CONFIG//描述符类型(设备描述符为0x01)
        },
        .wTotalLength         = USB_HID_CONFIG_DESC_LEN,//此配置信息的总长度,(包括配置,接口,端点和设备类及厂商定义的描述符)
        .bNumInterfaces       = 0x01U,//该配置所支持的接口个数
        .bConfigurationValue  = 0x01U,//在SetConfiguration()请求中用做参数来选定此配置
        .iConfiguration       = 0x00U,//描述此配置的字串描述表索引
        .bmAttributes         = 0xA0U,//配置特性
        .bMaxPower            = 0x32U//在此配置下的总线电源耗费量 2mA为一个单位
    },
//接口描述符
//接口描述符描述了该接口的端点数目、以及子类代码等。由配置描述符可知一个设备可以有多个接口描述符。
    .hid_itf =
    {
        .header =
        {
            .bLength         = sizeof(usb_desc_itf),
            .bDescriptorType = USB_DESCTYPE_ITF
        },
        .bInterfaceNumber     = 0x00U,
        .bAlternateSetting    = 0x00U,
        .bNumEndpoints        = 0x01U,
        .bInterfaceClass      = USB_HID_CLASS,
        .bInterfaceSubClass   = USB_HID_SUBCLASS_BOOT_ITF,//bios可认到
        .bInterfaceProtocol   = USB_HID_PROTOCOL_KEYBOARD,//键盘
        .iInterface           = 0x00U
    },
//HID描述符描述符

    .hid_vendor =
    {
        .header =
        {
            .bLength         = sizeof(usb_desc_hid),
            .bDescriptorType = USB_DESCTYPE_HID
        },
        .bcdHID               = 0x0111U,
        .bCountryCode         = 0x00U,
        .bNumDescriptors      = 0x01U,
        .bDescriptorType      = USB_DESCTYPE_REPORT,
        .wDescriptorLength    = USB_HID_REPORT_DESC_LEN,
    },

    .hid_epin =
    {
        .header =
        {
            .bLength         = sizeof(usb_desc_ep),
            .bDescriptorType = USB_DESCTYPE_EP
        },
        .bEndpointAddress     = HID_IN_EP,
        .bmAttributes         = USB_EP_ATTR_INT,
        .wMaxPacketSize       = HID_IN_PACKET,
        .bInterval            = 0x10U
    }
};

键值发送函数

STANDARD_HID_CORE.H

typedef struct {
    uint32_t protocol;
    uint32_t idle_state;
/*
 * buffer[0] - bit0: Left CTRL
 *           -bit1: Left SHIFT
 *           -bit2: Left ALT
 *           -bit3: Left GUI
 *           -bit4: Right CTRL
 *           -bit5: Right SHIFT
 *           -bit6: Right ALT
 *           -bit7: Right GUI 
 * buffer[1] - Padding = Always 0x00
 * buffer[2] - Key 1
 * buffer[3] - Key 2
 * buffer[4] - Key 3
 * buffer[5] - Key 4
 * buffer[6] - Key 5
 * buffer[7] - Key 6
 */
    uint8_t data[HID_IN_PACKET];//用于传输键盘参数的,Byte0是传控制键,Byte1是保留键,不用改;Byte3~byte7都可以存放传输的按键值。
    __IO uint8_t prev_transfer_complete;
} standard_hid_handler;

键盘发送给PC的数据每次8个字节
BYTE1 BYTE2 BYTE3 BYTE4 BYTE5 BYTE6 BYTE7 BYTE8

定义分别是:

BYTE1 –
|–bit0: Left Control是否按下,按下为1
|–bit1: Left Shift 是否按下,按下为1
|–bit2: Left Alt 是否按下,按下为1
|–bit3: Left GUI 是否按下,按下为1
|–bit4: Right Control是否按下,按下为1
|–bit5: Right Shift 是否按下,按下为1
|–bit6: Right Alt 是否按下,按下为1
|–bit7: Right GUI 是否按下,按下为1

BYTE2 – 暂不清楚,有的地方说是保留位
BYTE3–BYTE8 – 这六个为普通按键

例如:键盘发送一帧数据 02 00 0x04 0x05 00 00 00 00
表示同时按下了Left Shift + ‘a’+‘b’三个键

void MYhid_key_data_send(usb_core_driver *udev)
{
 standard_hid_handler *hid = (standard_hid_handler *)udev->dev.class_data[USBD_HID_INTERFACE];
  if (hid->prev_transfer_complete)
  {
    hid->data[2]=0x04;//将‘a’存入数据帧
    if (0U != hid->data[2])
      {
          hid_report_send(udev, hid->data, HID_IN_PACKET);//发送键值
      }
  } 
}

main函数

main.c

int main(void)
{
    
        
    systick_config();
    usb_gpio_config();
    usb_rcu_config();
    usb_timer_init();
        LED1_init();
    hid_itfop_register(&hid_keyboard, &fop_handler);

    usbd_init(&hid_keyboard,
#ifdef USE_USB_FS
              USB_CORE_ENUM_FS,
#elif defined(USE_USB_HS)
              USB_CORE_ENUM_HS,
#endif
              &hid_desc,
              &usbd_hid_cb);
    usb_intr_config();

    /* check if USB device is enumerated successfully */
    while(USBD_CONFIGURED != hid_keyboard.dev.cur_status)
    {
    }

    while(1)
    {

        //  fop_handler.hid_itf_data_process(&hid_keyboard);
                if(k==SET)
                {
                
                    gpio_bit_set(GPIOC, GPIO_PIN_6);
                    hid_key_data_send(&hid_keyboard,KEY_H);
                    hid_key_data_send(&hid_keyboard,KEY_E);
                    hid_key_data_send(&hid_keyboard,KEY_L);
                    hid_key_data_send(&hid_keyboard,KEY_L);
                    hid_key_data_send(&hid_keyboard,KEY_O);
                    hid_key_data_send(&hid_keyboard,KEY_SPACE);
                    hid_key_data_send(&hid_keyboard,KEY_G);
                    hid_key_data_send(&hid_keyboard,KEY_D);        
                    hid_key_data_send(&hid_keyboard,KEY_3);
                    hid_key_data_send(&hid_keyboard,KEY_2);    
                    
                    hid_key_data_send_shift(&hid_keyboard,KEY_1);    
                    hid_key_data_send(&hid_keyboard,KEY_ENTER);
                    k=0;
                    gpio_bit_reset(GPIOC, GPIO_PIN_6);
                }


    }
}

代码下载(文末也可以下载)

链接:https://pan.baidu.com/s/1Ey7qg5tBUQPb3ERa7M6rcQ?pwd=gd32提取码: gd32

视频演示

https://www.bilibili.com/video/BV1Be4y1T74q​www.bilibili.com/video/BV1Be4y1T74q

参考资料

GD32F4XX固件库下载
HID键盘值参考
hid键盘值参考
键盘发送数据帧详解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/173622.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32——外部中断

目录 外部中断简述 什么是外部中断 传统单片机与新型单片机外部中断区别 STM32外部中断请求 STM32中断线与IO口的对应 STM32 中断服务函数 外部中断与中断服务函数的对应 中断服务函数列表 STM32外部中断程序编写 常用的库函数 外部中断的一般配置步骤 外部中断简…

【ArcGIS微课1000例】0058:波段合成(CompositeBands)工具的使用

波段合成工具常见于遥感软件,例如Envi和Erdas等,用于将多个单波段数据合成为一个多波段数据集,在ArcGIS中也提供了波段合成的工具,使用灵活方便。 文章目录 一、波段合成工具介绍二、波段合成工具案例1. 输出Esri Grid格式2. 输出tif格式3. 输出jgp格式4. 输出其它格式一、…

sqlite 使用distinct时组合索引可能不是你想的那样

目录先来唠唠嗑吧~那一探究竟吧!表结构及索引信息我的查询场景到底命中什么索引了?简单小结下~先来唠唠嗑吧~ 在使用sqlite作为词条数据库查询数据,发现有的sql执行很快,有的sql执行很慢,你以为是没有走索引吗&#x…

深入浅出进程控制

文章目录进程控制浅谈fork写时拷贝fork调用失败的原因进程终止进程退出的场景进程常见退出方法查看进程退出码echo $? :查看进程退出码exit和_exit进程等待进程等待的方法waitwaitpid获取子进程status宏定义查看进程是否正常退出,查看退出码再谈僵尸进程浅谈阻塞等…

基于.Net Core开发的支付SDK,简化支付功能开发

更多开源项目请查看:一个专注推荐.Net开源项目的榜单 在我们做项目中,不管是电商系统、外卖系统、还是上门维修系统等等,都需要支付功能,这就需要我们与第三方支付平台进行对接,但是第三方平台文档,往往都存…

05语法分析——自下而上分析

文章目录一、自下而上分析基本问题二、算符优先分析构造FIRSTVT(P)的算法构造LASTVT(P)的算法构造优先表的算法三、LR分析法1.LR(0)构造LR(0)项目集规范族构造识别活前缀的DFA构造LR(0)分析表2.SLRSLR解决冲突办法SLR(1)分析表的构造算法3.LR(1)【规范LR】LR(1)项目集I的闭包状…

【MySQL基础】运算符及相关函数详解

序号系列文章3【MySQL基础】MySQL基本数据类型4【MySQL基础】MySQL表的七大约束5【MySQL基础】字符集与校对集详解6【MySQL基础】MySQL单表操作详解文章目录前言MySQL运算符1,算术运算符1.1,算术运算符的基本使用1.2,常用数学函数的基本使用2…

iOS:OpenGLES 实验室之2D篇 第一弹 の 智能弹幕

本文字数:3046字预计阅读时间:15 分钟iOS:OpenGLES 实验室之2D篇 第一弹 の 智能弹幕笔者之前发表的音视频文章,有图像的处理,音频的重采样等等,都属于入门级别。通过阅读它们,读者能对音视频有…

【复习 自用】JavaScript知识汇总(DOM)

注:之前学过JavaScript,本贴仅用于复习(自用),建议没基础的朋友先学基础。会混入typescript! 更新中~~~~~ Dom核心内容 创建节点 ① document.write() 是直接将内容写入页面的内容流,但是文档流执行完毕&#xff0c…

云原生技能树-docker caontainer 操作

运行 一个Docker镜像(image)运行后,就是一个容器实例,称为container 以镜像hello-world为例,启动容器: docker container run -it hello-world 可以看到输出了Hello World 信息: 以下描述错误的是? 答…

数字逻辑理论——从卡诺图到门电路

卡诺图化简 卡诺图化简 第一步:在卡诺图中圈出相邻为1的小方格(方格的个数为2m2^{m}2m),圈里面的1越多越好,并且这个小方格可以重复使用。 第二步:上一步中的方格或者圈出来的方框——每一个都代表一个与…

Linux软件安装及管理程序

Linux安装及管理程序Linux软件安装及管理程序一、Linux应用程序基础二、RPM软件包管理工具2.1、RPM介绍2.2、RPM命令三、源码编译安装四、yum安装Linux软件安装及管理程序 一、Linux应用程序基础 应用程序与系统命令的关系 角色系统命令应用程序文件位置般在/bin和/sbin目录…

linux系统中实现智能家居的基本方法

大家好,今天主要和大家分享一下,智能家居物联网的基本实现与操作方法。 目录 第一:智能家居基本简介 第二:测试WIFI模块功能 第三:智能家居物联UI界面开发 第四:核心代码的具体实现 第五:最…

【阅读笔记】《重构》 第三四章

第三章 代码的味道 DuplicatedCode(重复代码) 同一个类的两个函数含有相同的表达式两个互为兄弟的子类含有相同表达式两个毫不相干的类出现重复代码 LongMethod(过长函数) 函数不宜过长,函数越长越难理解如果想利用单个类做太多事情,其内往往就会出现…

联合证券|港股再融资“春江水暖” 资本争购热门赛道企业

进入2023年,港股再融资商场有所回暖。到1月18日,已有27家港股上市公司发布拟配售股份(简称“配股”)再融资,募资总额164.01亿港元,较上一年同期增加148.16%。其间,微盟集团的配股再融资吸引了众…

fpga实操训练(lcd测试)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 前面我们测试过vga输出,当时是找了一个老式的显示器来完成的,也就是本身自带vga接口的显示器。但是,现在市面上大部分显示器都是默认支持hdmi接口的。所以说,如果真的想用fpga测…

都说InnoDB好,那还要不要使用Memory引擎?

我在上一篇文章末尾留给你的问题是:两个 group by 语句都用了 order by null,为什么使用内存临时表得到的语句结果里,0 这个值在最后一行;而使用磁盘临时表得到的结果里,0 这个值在第一行? 今天我们就来看看,出现这个问题的原因吧。 内存表的数据组织结构 为了便于分…

如何在SpringBoot项目中访问静态资源

在springboot项目中如果要在不集成templates的情况下访问静态资源需要做以下配置 1.在项目的application.yml文件中做如下配置 spring:profiles:active: devmvc:view:prefix: /suffix: .html 重点在 配置后生成为WebMvcProperties 配置类。该配置类中有一个内部类View Conf…

各式各样图标的特点

笔者近期在翻看各种样式的图标,逛了下 Iconfont、IconFinder 等图标网站,现根据自己的经验,总结了一些图标特点的描述用语 线性/面性/线面组合平面/立体无层次感(或阴影)/有层次感(或阴影)无填…

Oracle强制加了hint实效三种连接方式使用场合判断

开发写了一个语句使用了connect by level函数 SELECT DISTINCT CTMID FROM ( SELECT CTMID, REGEXP_SUBSTR(FLTUSERIDSTR, ‘[^;]’, 1, l) AS userid FROM s_userinfo,(SELECT LEVEL l FROM DUAL CONNECT BY LEVEL<300) b WHERE l < LENGTH(FLTUSERIDSTR) - LENGTH(rege…