There is a post (STM32 And Custom USB HID Device? Yes Please!) that gets a lot of attention, although at the time it was created, only vague “how-to” was presented. I decided to break down this to smaller parts of modifying STM32 USB HID code, generated with STM32CubeMx. Here is an example of a basic – default code, which turns out to be USB HID mouse, and a guide how to transform this code to keyboard. STM32L100 discovery board was used for test.

Update (31.5.2020): see the latest project with STM32 + USB + AHK here!

Default USB HID device with STM32CubeMx

  1. Intialise all peripheral: USB, HSE, SWD, button (to send USB reports), leds (just for testing purposes).
    Note 1: USB in general need a proper reliable clock. In my case, I used 16MHz external oscillator.
    Note 2: Initialise USB as Full-Speed device, as Human Interface Device Class. This is a template HID mouse, that works straight out of the box and is instantly recognised by PC when connecting USB cable.
    Note 3: Button and LEDs are optionall. Button for triggering sending USB reports and LEDs for status.
  2. Export project, build and upload.
    When this is done, connect your usb cable to PC and wait for OS to initialise device. It should show as additional Mouse device under “Mice and other pointing devices”.

You might wonder where this mouse came from. Lets take a look on CubeMX generated files:

  1. USB device library/Middlewares
    – usbd_core.c (provides all USB device core functions)
    – usbd_ioreq.cusbd_ctlreq.c (provides the USB IO requests)
    – usbd_hid.c (provides everything to interact with USB host, send reports, …)
  2. Application files:
    – usbd_conf.c (provides low layer / HAL functions)
    – usbd_device.c (provides initialising function called from main())
    – usbd_desc.c (provides USB device descriptors)

More about file hierarchy can be found in chapter 2 of STM32Cube USB device library user manual. It is wise to get familiar with basics of USB before you get on to more serious work.

There is no “singe file/function” to implement USB mouse like arduino: Mouse.begin(). But with a little knowledge and look-around, you can find all USB HID specifics in this files:

  1. USB VID/PID in usbd_desc.c
    You can change this defines but be carefull when changing VID and PID, since PC uses the

    VID/PID combination to find the drivers for this specific device. Also, VID is something usb.org manage and costs around 2000€ to obtain your own Vendor ID. I suggest to leave VID/PID for the time of testing.

  2. USB configuration defines in usbd_conf.h
    This is set with CubeMX so don’t change this defines.
  3. Descriptor sizes in usbd_hid.h
  4. USB struct handle in usbd_device.c
  5. USB device/report configuration in usbd_hid.c
    This is the file that holds all informations about what kind of a USB device this is, how its reports will look.
    As you can see, “default” file explains where our mouse (detected by PC) came from. In fact, mouse itself is merged into one report containing buttons and  “joystick” – axes (X, Y, wheel)!

We can learn more about this “default” implemented device by exploring device descriptors in this file (usbd_hid.c). The “important” ones are:

  1. USBD_HID_CfgDesc[USB_HID_CONFIG_DESC_SIZ]
    This is USB HID device Configuration Descriptor – defines general device informations about power, number/type of interface/s … Code is quite neatly commented by default.
  2. HID_MOUSE_ReportDesc[HID_MOUSE_REPORT_DESC_SIZE]
    This is an description of all interface reports so the host can know what to expect. This descriptors are sent to host at initialisation and host afterwards expect data – called reports – as they were set here.
    There are “standards or guidelines” for different USB HID devices like mouse, keyboard, joystick, … and therefore drivers can be generalised amongs manufacturers. As there are no comments in this descriptors, we can ask Google for what this descriptor tell us:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

0x05, 0x01, // Usage Page (Generic Desktop Ctrls)

0x09, 0x02, // Usage (Mouse)

0xA1, 0x01, // Collection (Application)

0x09, 0x01, // Usage (Pointer)

0xA1, 0x00, // Collection (Physical)

0x05, 0x09, // Usage Page (Button)

0x19, 0x01, // Usage Minimum (0x01)

0x29, 0x03, // Usage Maximum (0x03)

0x15, 0x00, // Logical Minimum (0)

0x25, 0x01, // Logical Maximum (1)

0x95, 0x03, // Report Count (3)

0x75, 0x01, // Report Size (1)

0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)

0x95, 0x01, // Report Count (1)

0x75, 0x05, // Report Size (5)

0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)

0x05, 0x01, // Usage Page (Generic Desktop Ctrls)

0x09, 0x30, // Usage (X)

0x09, 0x31, // Usage (Y)

0x09, 0x38, // Usage (Wheel)

0x15, 0x81, // Logical Minimum (-127)

0x25, 0x7F, // Logical Maximum (127)

0x75, 0x08, // Report Size (8)

0x95, 0x03, // Report Count (3)

0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)

0xC0, // End Collection

0x09, 0x3C, // Usage (Motion Wakeup)

0x05, 0xFF, // Usage Page (Reserved 0xFF)

0x09, 0x01, // Usage (0x01)

0x15, 0x00, // Logical Minimum (0)

0x25, 0x01, // Logical Maximum (1)

0x75, 0x01, // Report Size (1)

0x95, 0x02, // Report Count (2)

0xB1, 0x22, // Feature (Data,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position,Non-volatile)

0x75, 0x06, // Report Size (6)

0x95, 0x01, // Report Count (1)

0xB1, 0x01, // Feature (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)

0xC0, // End Collection

You can spend hours googling what each line means, how can different things be implemented, … `For a start, if we take “mouse usage”:

1

2

3

4

5

6

7

8

9

10

0xA1, 0x00, // Collection (Physical)

0x05, 0x09, // Usage Page (Button)

0x19, 0x01, // Usage Minimum (0x01)

0x29, 0x03, // Usage Maximum (0x03)

0x15, 0x00, // Logical Minimum (0)

0x25, 0x01, // Logical Maximum (1)

0x95, 0x03, // Report Count (3)

0x75, 0x01, // Report Size (1)

0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)

… we can see, our mouse has 3 physical buttons, each with logical state either ‘0’ or ‘1’. Part of report to send button presses is 3 bits long, padded with constant 5 bits to fullfill one byte.

1

2

3

0x95, 0x01, // Report Count (1)

0x75, 0x05, // Report Size (5)

0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)

Since this is single interface, single report device, USB host expects fixed report length (number of bytes), so in order to send mouse click to host, we must send exact number of bytes as this descriptor defines. I counted (1 byte for buttons, 3 bytes for X, Y and wheel, and 1 byte for motion wakeup) 5 bytes altogether.
Now, let’s implement mouse click for a test before we implement our keyboard.

  • We will obviously need an array of bytes in size of 5 bytes – report:

1

2

#define CLICK_REPORT_SIZE 5

uint8_t click_report[CLICK_REPORT_SIZE] = {0};

All zeroes means there was no change in any of report fields.

  • We need to add our usb data structure to main.c file, so we can pass it to functions. We can find it in usb_device.c and reference it in main.c as:

1

extern USBD_HandleTypeDef hUsbDeviceFS;

  • We must send clicks. In order to avoid sending clicks until reset, use your button or large delay.We must send button press and release. A certain amout of time must pass by before new report can be registered by host driver/OS.

1

HID_FS_BINTERVAL, /*bInterval: Polling Interval (10 ms) in USBD_HID_CfgDesc[USB_HID_CONFIG_DESC_SIZ] (usbd_hid.c) */

  • Code in while(1) in main():

1

2

3

4

5

6

7

8

9

10

11

12

13

14

if(HAL_GPIO_ReadPin(USER_BUTTON_GPIO_Port, USER_BUTTON_Pin) == GPIO_PIN_SET){

  HAL_GPIO_WritePin(USER_LED_1_GPIO_Port, USER_LED_1_Pin, GPIO_PIN_SET);

  click_report[0] = 1; // send button press

  USBD_HID_SendReport(&hUsbDeviceFS, click_report, CLICK_REPORT_SIZE);

  HAL_Delay(50);

  click_report[0] = 0; // send button release

  USBD_HID_SendReport(&hUsbDeviceFS, click_report, CLICK_REPORT_SIZE);

  HAL_Delay(200);

  HAL_GPIO_WritePin(USER_LED_1_GPIO_Port, USER_LED_1_Pin, GPIO_PIN_RESET);

}

Unplug USB cable, upload code, plug it in and click  There it is, our STM32 USB HID mouse. I strogly recommend that you do this mouse example before modifying example to keyboard.

Implement USB HID Keyboard

  1. USBD_HID_CfgDesc[] and USB_HID_CONFIG_DESC_SIZ
    In order to set up host for keyboard device, we must edit this configuration decriptor. Beside comments, only line:

1

0x01, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/

… is changed to 0x01 which is keyboard. Size remains the same.

HID_MOUSE_ReportDesc[] and HID_MOUSE_REPORT_DESC_SIZE
This is where we must fill data about keyboard report descriptor. Since USB HID keyboard is very well defined, we can find our report descriptor on google or we can build it from ground up using official USB HID Descriptor tool.  Anyway, this is how report descriptor looks now:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

0x05, 0x01, // USAGE_PAGE (Generic Desktop)

0x09, 0x06, // USAGE (Keyboard)

0xa1, 0x01, // COLLECTION (Application)

0x05, 0x07, // USAGE_PAGE (Keyboard)

0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)

0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)

0x15, 0x00, // LOGICAL_MINIMUM (0)

0x25, 0x01, // LOGICAL_MAXIMUM (1)

0x75, 0x01, // REPORT_SIZE (1)

0x95, 0x08, // REPORT_COUNT (8)

0x81, 0x02, // INPUT (Data,Var,Abs) //1 byte

0x95, 0x01, // REPORT_COUNT (1)

0x75, 0x08, // REPORT_SIZE (8)

0x81, 0x03, // INPUT (Cnst,Var,Abs) //1 byte

0x95, 0x06, // REPORT_COUNT (6)

0x75, 0x08, // REPORT_SIZE (8)

0x15, 0x00, // LOGICAL_MINIMUM (0)

0x25, 0x65, // LOGICAL_MAXIMUM (101)

0x05, 0x07, // USAGE_PAGE (Keyboard)

0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))

0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)

0x81, 0x00, // INPUT (Data,Ary,Abs) //6 bytes

0xc0 // END_COLLECTION

This is generated with official HID tool, altogether 8 bytes, with new descriptor size of 45 bytes (update HID_MOUSE_REPORT_DESC_SIZE). 
How keyboard report descriptor should look and what each byte means… Google it. There is a ton of information about it, so it really shouldn’t be a problem.
Renaming (optionally, just as an example):

  • HID_MOUSE_ReportDesc to HID_Keyboard_ReportDesc
  • HID_MOUSE_REPORT_DESC_SIZE to HID_KEYBOARD_REPORT_DESC_SIZE
  • CLICK_REPORT_SIZE to PRESS_REPORT_SIZE
  • Update this define to 8 (bytes – acordingly to descriptor).- click_report[] to press_report[]
  • Edit main.c:

1

press_report[2] = 7; // send 'd'

Note: third byte (index = 2) in report descriptor is a first key byte out of 8 report bytes. Also, update “release” index to 2.
Note 2: ‘d’ is 7 acordingly to http://www.usb.org/developers/hidpage/Hut1_12v2.pdf page 53.

Again, unplug, build, upload, plug back and voila! ‘d’ as ‘domen’, the legend.

STM32 USB HID Mouse And Keyboard (guide)相关推荐

  1. 记录一下 开发STM32 USB HID踩过的坑

    记录一下 开发STM32 USB HID踩过的坑 一.前言 二.代码配置 一.前言 MCU: STM32F103C8T6 CubeMX: STM32CubeMX 5.3.0 二.代码配置 引脚配置 时 ...

  2. stm32 USB HID+CDC 鼠标键盘串口 组合设备配置解析

    前言 查阅网上的博客与代码,很多都是关于USB的鼠标配置.USB的键盘配置.USB的虚拟串口配置,稍微深入一点的会将鼠标键盘合在一起,但移植起来就会报很多错误,要么是检测不到,要么是警告,这很正常,因 ...

  3. STM32 USB HID IAP升级

    找了网上大量的资料,最后发现这个东西人家还出售源码.又不是什么算法级的东西,实在理解不了. 至于为什么要用HID,不用官方的DFU,因为驱动呀,DFU识别USB的时候还是要装驱动,客户你永远理解不了他 ...

  4. STM32 USB HID设置(STM32CubeMX)

    STM32F070F6P USB HID设置 1.打开STM32CubeMX软件,选择"NEW"新建一个工程 2.选择芯片型号STM32F070F6P 3.在Swap PA9/10 ...

  5. STM32 USB VCOM和HID的区别,配置及Echo功能实现(HAL)

    STM32 USB VCOM和HID的区别,配置及Echo功能实现(HAL ) STM32的USB功能模块可以配置为虚拟串口(VCOM: Visual Port Com)或人机交互设备(HID: Hu ...

  6. [转载]基于Stm32,LD3320的非特定语音识别USB HID Keyboar

    基于Stm32,LD3320的非特定语音识别USB HID Keyboard实现 ---用声音跟机器沟通 鉴于手头拥有一块ST官方的stm32f407VG discover板子以及一块ICRoute公 ...

  7. 62 stm32 usb自定义hid复合设备修改实验

    1.引言 最近因为项目需要,我们希望单片机既能有hid键盘功能,又能有hid设备的功能.即单片机的一个usb接口插入电脑后,电脑能识别出键盘设备和hid设备,两者同时存在的. 基于项目只是要求实现功能 ...

  8. STM32 USB复合设备(VCP虚拟串口+HID键盘)详解

    USB复合设备 介绍 USB复合设备与组合设备区别 USB描述符修改 修改CustomHID_Reset 修改CustomHID_Data_Setup 介绍 本次使用的是Keil 5+STM32F10 ...

  9. STM32 USB 开发(一)HID Slave 通信

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.开发板USB硬件 二.STM32CubeMX 设置 1.设置小灯灯和串口 2.开启USB 3.时钟设置 三.KEI ...

最新文章

  1. C语言经典小游戏---猜数字游戏 (包含C语言中如何实现随机数的生成)
  2. 那些让我唏嘘不已的嫡亲同学
  3. mysql 苏勇,你不知道的Linux使用技巧~
  4. POJ1915 BFS双向BFS
  5. 为什么初创企业应该计算 LTV / CAC,以及如何正确应用它?
  6. scrapy 搜索关键字_Scrapy 新浪微博搜索爬虫
  7. RHEL 5基础篇—常见系统启动类故障
  8. 耳机的L和R是什么意思?
  9. 程序员的自我修养笔记3 内存管理
  10. 蓝桥杯 ALGO-79 算法训练 删除数组零元素
  11. [转载]使用Vitamio打造自己的Android万能播放器(2)—— 手势控制亮度、音量、缩放...
  12. 一文带你了解手机运营商类api接口
  13. 关于POSTSQL 的语言编码问题!
  14. Remote Desktop Connection Manager (RDCMan) 介绍
  15. Python中print的用法
  16. 自己动手撸一个Jlink-TinyJlink诞生记
  17. ChatGPT神器免费使用,告别昂贵低效工具
  18. FPGA数字信号处理(九)Vivado FFT IP核实现
  19. Geoserver+Geomesa+HBase时空大数据环境搭建
  20. C# 8.0核心技术指南

热门文章

  1. LiteCAD参考文档的学习四(单文本、多文本、弧形文本、光栅图像、图像引用、ECW/Jpeg2000 Image图像、填充图案、尺寸、引线)
  2. 在意的越多,心理负担就越重
  3. Serv-u + 花生壳实现FTP内网穿透
  4. USACO--Milking Cows (C语言)挤奶牛
  5. USER_用户_数据库知识点
  6. 图片路径不存在,替换问题图片
  7. mysql list类型_数据库list是什么类型数据
  8. 《深入理解JAVA虚拟机》周志明 第三版 - 第四章 虚拟机性能监控、故障处理工具
  9. 第五章 生活无处不数据,大数据真的能算命?
  10. 初学Bootstrap,制作响应式布局