当前位置: 首页 > news >正文

【星闪】Hi2821 | USB HID设备类 + HID键盘例程

1. 简介

        USB(Universal Serial Bus),全称通用串行总线,是一个外部总线标准,规范电脑与外部设备的连接和通讯。USB接口具有热插拔功能。USB 接口可枚举成多种外设,如鼠标、键盘等。USB 是在 1994 年底由英特尔等多家公司联合;并在 1996 年推出后,现已成为当今电脑与大量智能设备的必配接口。USB 版本经历了多年的发展,到如今已经发展为 USB4 版本。

关于 USB 协议更详细的讲解在之前的文章——从零开始学GD32单片机 | USB通用串行总线接口 + HID键盘例程有包含,感兴趣的可移步阅读。

2. 例程

        hi2821 内部包含一个 USB 2.0 外设,包含 PHY 控制器 Host 控制器,例程中将基于它来实现一个 HID 键盘例程。

2.1 Kconfig

        USB 的 Kconfig 配置项非常多,但我们只需注意以下几项即可:

  • Enable USB Controller:使能 USB 控制器;
  • Enable USB2.0 Device Controller:使能 USB 2.0 设备控制器;
  • Enable USB3.0 Device Controller:使能 USB 3.0 设备控制器,Hi2821 不支持;
  • Slave Core Extra Board Configuration:从设备板级配置,里面主要配置输出(主机下发)和输入(从机上报)的端点(endpoint)数,正常来说 HID 键盘只需要 1 个输入端点和 1 个输出端点,SDK 默认是各 3 个;
  • Enable USB Gadget Support:USB 设备支持,里面主要配置 USB 设备,打开所有 HID 的编译选项即可;HID Interface 配置里面的 HID Report Map Num 用于配置 HID 描述符的数量,一般来说只需要用 1 个,默认 3 个;下面 Use custom HID 可以使能自定义 HID 设备的支持。
  • Enable HID Output Report Transfer:使能输出报告传输;
    • select HID output report function:输出报告函数类型;选项一表示通过事件栈报告,即用户要通过轮询(polling)的方式获取报告内容;选项二表示通过回调报告,即用户注册回调函数,报告到达时调用回调获取报告内容;

2.2 代码

#include <stdbool.h>#include "soc_osal.h"
#include "gadget/f_hid.h"
#include "usb_init_keyboard_app.h"#define USB_INIT_APP_MANUFACTURER  { 'H', 0, 'i', 0, 's', 0, 'i', 0, 'l', 0, 'i', 0, 'c', 0, 'o', 0, 'n', 0 }
#define USB_INIT_APP_PRODUCT  { 'M', 0, 'y', 0, 'K', 0, 'e', 0, 'y', 0, 'b', 0, 'o', 0, 'a', 0, 'r', 0, 'd', 0 }
#define USB_INIT_APP_SERIAL   { '2', 0, '0', 0, '2', 0, '0', 0, '0', 0, '6', 0, '2', 0, '4', 0 }static bool g_usb_inited = false;
static uint8_t g_usb_report_index = 0;static const uint8_t g_report_desc_hid[] = {0x05, 0x01,  /* USAGE_PAGE (Generic Desktop) */0x09, 0x06,  /* USAGE (Keyboard) */0xa1, 0x01,  /* COLLECTION (Application) */0x05, 0x07,  /* USAGE_PAGE (Keyboard/Keypad) */0x19, 0xe0,  /* USAGE_MINIMUM (Keyboard LeftControl) */0x29, 0xe7,  /* USAGE_MAXIMUM (Keyboard Right GUI) */0x15, 0x00,  /* LOGICAL_MINIMUM (0) */0x25, 0x01,  /* LOGICAL_MAXIMUM (1) */0x95, 0x08,  /* REPORT_COUNT (8) */0x75, 0x01,  /* REPORT_SIZE (1) */0x81, 0x02,  /* INPUT (Data,Var,Abs) */0x95, 0x01,  /* REPORT_COUNT (1) */0x75, 0x08,  /* REPORT_SIZE (8) */0x81, 0x03,  /* INPUT (Cnst,Var,Abs) */0x95, 0x06,  /* REPORT_COUNT (6) */0x75, 0x08,  /* REPORT_SIZE (8) */0x15, 0x00,  /* LOGICAL_MINIMUM (0) */0x26, 0xFF, 0x00,  /* LOGICAL_MAXIMUM (255) */0x05, 0x07,  /* USAGE_PAGE (Keyboard/Keypad) */0x19, 0x00,  /* USAGE_MINIMUM (Reserved (no event indicated)) */0x29, 0x65,  /* USAGE_MAXIMUM (Keyboard Application) */0x81, 0x00,  /* INPUT (Data,Ary,Abs) */0xc0         /* END_COLLECTION */
};int usb_keyboard_init(void)
{if (g_usb_inited) {return -1;}const char manufacturer[] = USB_INIT_APP_MANUFACTURER;struct device_string str_manufacturer = {.str = manufacturer,.len = sizeof(manufacturer)};const char product[] = USB_INIT_APP_PRODUCT;struct device_string str_product = {.str = product,.len = sizeof(product)};const char serial[] = USB_INIT_APP_SERIAL;struct device_string str_serial_number = {.str = serial,.len = sizeof(serial)};struct device_id dev_id = {.vendor_id = 0x1111,.product_id = 0x0009,.release_num = 0x0800};g_usb_report_index = hid_add_report_descriptor(g_report_desc_hid, sizeof(g_report_desc_hid), 0);osal_printk("usb report index: %d\r\n", g_usb_report_index);if (usbd_set_device_info(DEV_HID, &str_manufacturer, &str_product, &str_serial_number, dev_id) != 0) {return -1;}if (usb_init(DEVICE, DEV_HID) != 0) {return -1;}g_usb_inited = true;return g_usb_report_index;
}int usb_keyboard_deinit(void)
{if (g_usb_inited == false) {return 0;}(void)usb_deinit();g_usb_inited = false;return 0;
}int usb_keyboard_send_data(const void* data, uint32_t len)
{if (!g_usb_inited) {return -1;}return fhid_send_data(g_usb_report_index, (const char*) data, len);
}

        SDK 将 USB HID 部分的代码封装得相当好了,所以只需要调用几个函数就能跑起来。

1. 初始化。

        调用 hid_add_report_descriptor 函数添加 HID 报告描述符,它的内容可参考 USB 官方的文档——Device Class Definition for HID 1.11。

        函数会返回一个 HID 报告句柄,需要保存起来,后面的通讯需要用到。

        调用 usbd_set_device_info 函数去设置设备信息,参数一为设备类,这里选 DEV_HID 其他可选如下:

typedef enum device_type {DEV_START,		/* start value of the device type */DEV_SERIAL,		/* used for serial */DEV_ETHERNET,	/* used for rndis */DEV_SER_ETH,	/* used for serial and rndis */DEV_DFU,		/* used for DFU */DEV_MASS,		/* used for mass */DEV_UVC,		/* used for USB video */DEV_UAC,		/* used for USB audio */DEV_CAMERA,		/* used for USB camera */DEV_HID,		/* used for USB hid */DEV_CUSTOM,		/* used for USB custom */DEV_UAC_HID,    /* used for USB uac and hid */DEV_SER_HID,    /* used for USB serial and hid */DEV_END			/* end value of the device type */
} device_type;

        参数二为厂商名,参数三为产品名,参数四为序列号,这三个参数都要用 device_string 结构体封装,需要注意的是因为 USB 的字符串使用 Unicode 格式一个字符占 2 个字节,所以每个字符之间都要使用“0”来隔开。

        参数五为设备 ID,通过 device_id 结构体定义,如下:

struct device_id
{uint16_t vendor_id;		/* Vendor id */uint16_t product_id;	/* Product id */uint16_t release_num;	/* Device release number */
};

        如果不是产品开发,里面的内容随便填也没问题。

        最后调用 usb_init 即可使能 USB 功能,如果 USB 插入,驱动会自动与主机握手并通信。

2. 发送数据。

        调用 fhid_send_data 函数向主机发送数据,参数一为 HID 报告句柄,参数二为数据数组,参数三为数据长度;返回值为发送的数据长度。

        发送的数据根据 HID 的报告描述符的内容而定,对于标准的 HID 键盘数据长度应为 8 字节,格式如下:

        其中 Modifier keys 的格式如下:

3. 主函数。

        主函数的内容可以参考前面文章——Pinctrl、GPIO + LED灯和按键输入例程,写一个按键驱动,在按键按下时调用发送函数发送键位。

HID 的键盘键位键值可以参考官方文档——HID Usage Tables 1.6

#include "common_def.h"
#include "soc_osal.h"
#include "app_init.h"
#include "gpio.h"
#include "pinctrl.h"#include "usb_init_keyboard_app.h"#define KEY_GPIO 11static osal_task* task_handle = NULL;static int key_task(void* args)
{unused(args);while (1) {if (uapi_gpio_get_val(KEY_GPIO) == GPIO_LEVEL_LOW) {/* 去抖 */while (uapi_gpio_get_val(KEY_GPIO) == GPIO_LEVEL_LOW) {osal_msleep(10);}/* 发送键位 */uint8_t keycode[8] = {0};keycode[2] = 0x04;int ret = usb_keyboard_send_data(keycode, sizeof(keycode));if (ret < 0) {osal_printk("send key failed\r\n");} else {osal_printk("send key ok\r\n");}keycode[2] = 0x00;usb_keyboard_send_data(keycode, sizeof(keycode));}osal_msleep(10);}return 0;
}static void usb_keyboard_entry(void)
{int ret = 0;/* 初始化GPIO */uapi_pin_set_mode(KEY_GPIO, HAL_PIO_FUNC_GPIO);uapi_pin_set_pull(KEY_GPIO, PIN_PULL_UP);uapi_gpio_set_dir(KEY_GPIO, GPIO_DIRECTION_INPUT);/* 初始化USB */ret = usb_keyboard_init();if (ret) {osal_printk("usb keyboard init failed\r\n");return;}/* 创建任务 */osal_kthread_lock();task_handle = osal_kthread_create(key_task, NULL, "KeyTask", 2048);if (task_handle) {osal_kthread_set_priority(task_handle, OSAL_TASK_PRIORITY_MIDDLE);}osal_kthread_unlock();
}/* Run the usb_keyscan_entry. */
app_run(usb_keyboard_entry);

发送完指定的键位后,需要立即发送全 0 的键位,表示复位键位,不然主机端会认为键位被长按,一直保持输入。

2.3 测试

        如果系统正常启动,那么可以在电脑的设备管理器中看到枚举多出了一个 HID 键盘设备。

        按下用户按键(IO11接地),那么可以看到输入了对应的键值。

http://www.dtcms.com/a/562208.html

相关文章:

  • o2o网站建站wordpress json接口
  • 成都自助建站模板网站建设评审表
  • 边界扫描测试原理 15 -- BSDL 9 应用示例
  • 唐山专业网站建设公司阿里云虚拟主机和云服务器的区别
  • 济南旅游网站建设前程无忧网深圳网站建设类岗位
  • 查企业资质上什么网站东华软件是外包公司吗
  • 整体设计 全面梳理复盘 之15 :整体设计属性体系构建与实体表格落地
  • 自建服务器做网站要备案英文企业网站源码
  • 广州 营销型网站图书宣传推广方案
  • 网站设计教学网页站点设计
  • 东莞在哪里学网站建设嘉兴网站制作计划
  • 计算机图形学·4 OpenGL编程1 背景知识
  • 做外贸的有些什么网站wordpress编辑器不行
  • 各大网站logo图标wap站开发
  • 山西 网站制作旅游网站开发说明
  • springboot基于java的少数民族音乐网站的设计与实现(代码+数据库+LW)
  • 强化学习2.4 MDP作业汇总(持续更新)
  • 使用Requests和正则表达式实现塔读小说网小说爬取
  • Guava TreeRangeSet:区间运算的数学之美!
  • 双指针问题(同向)
  • seo公司哪家便宜宁波关键词排名优化平台
  • 安徽静安集团网站建设网站打不开了怎么办
  • 建设ftp网站怎么创建数据库帮忙做ppt的网站
  • Java 大视界 -- Java 大数据在智能医疗手术风险评估与术前方案制定中的应用探索
  • 做外贸网站推广成都网站建设外包
  • linux命令-压缩-12
  • 手机网站的视频怎么才能下载wordpress top0.9主题
  • 专门做房地产设计的图片网站上谷网络网站建设
  • 网站上怎么做弹目提醒定制app开发平台
  • 下载 | Win11 25H2 正式版更新!(ISO映像、年度更新版本、26200.7019、Windows 11)