C#网络编程

使用 STM32 制作鼠标

作者:陈广 日期:2019-6-14


掌握了 HID 报告描述符后,我们就可以动手实现一个鼠标了。当然,首先必须要先有一块开发板。我用的是 STM32,原因是 STM32 性能高,价钱便宜,最重要的是学习资料丰富,现在又得加一条,配套软件功能强大。STM32CubeMx 的出现使得 STM32 系列单片机编程的难度下降了几个层级。开发板必须支持 USB,我使用的芯片是 STM32F103RCT6,原来是用它做键盘的,支持 USB 2.0。开发板应当至少带有 6 个按钮,我手头有这样的板子,就是键盘 PCB,但不能拿来写文章啊。总不能让通过这篇文章学习的同学去打一块键盘吧。还好,我手上有一块 STM32F103RCT6 的评估板,网上随便可以买到,价钱便宜。缺点是没有按键,那只能通过定时器来演示了,效果会差一些。STM32开发板我肯定会设计一款,上课要用,不过不是现在。最完美的情况是自己设计并打造一块带 9 个按键的 STM32 开发板,然后学习这篇文章,但那需要较有钱才行。先放设备图片:

图 1:STM32F103RCT6 评估板

购买链接:https://item.taobao.com/item.htm?spm=a1z09.2.0.0.2ee92e8dxp3NCM&id=571177356496&_u=dhtvhlo156b

这只是我的购买链接,30 大洋,很便宜,这块板网上到处都是,可自行选择购买。(我写这篇文章,在调试的时候,突然发现这块板子挂掉了,杯具!)

另外虽然这块开发板可以使用串口一键下载,但用起来不是太方便,所以最好买一个 ST-LINK,如图右上角所示。使用它可以直接在 Keil 软件中烧写程序,十来块钱就可买到,以后设计其它的 STM 开发板只需接3条线出来就可以使用了。

使用 STM32CUBEMX 初始化程序

以往我是不敢开 STM32 单片机这门课的,因为光一个 GPIO 的配置就足够复杂,让人望而却步,并不适合高职学生实习。但意法公司出了 STM32CUBEMX 后,改变了这种情况,它使用图形化的方式对单片机进行配置,并自动生成代码,让我们可以专注于业务逻辑,将 STM32 的学习难度直接降低几个层级,甚至于都不需要读芯片数据手册就可以完成程序的开发。要知道,学 STM32 最大的难点就在于读数据手册,大部分是全英文的。现在,我终于可以开设这门课程了。

STM32CUBEMX 和 Keil 的安装我不再赘述,B 站上有太多的资料,看看就懂了,我们直接进入主题。

配置烧写口

打开 STM32CUBDMX,单击屏幕中央【ACCESS TO MCU SELECTOR】按钮,在弹出的窗口中搜索并选中【STM32F103RCTx】,有两种封装:LQFP64 和 WLCSP64,根据实际情况选择。选好后,项目也创建好了。

首先配置外部晶振,在左侧栏中展开【System Core】项,选中【RCC】,在中间【RCC Mode and Configuration】窗口下的【High Speed Clock (HSE)】组合框中选择【Crystal/Ceramic Resonator】,选中这项,意味着我们使用了外部晶振,开发板上至少有一块 8M 的外部晶振。如下图所示:

图 2:配置烧写口

接下来,选择左边栏的【SYS】项,在【SYS Mode and Configuartion】窗口中【Debug】组合框中选中【Serial Wire】项,以选择 ST-LINK 方式烧写程序。这个需要确认你所使用的开发板已经引出【SWCLK】和【SWDIO】两个引脚。

配置 HID

首先使能 USB,展开左侧栏的【Connectivity】项,选中其中的【USB】项,在【USB Mode and Configuration】窗口中勾选【Device(FS)】项,如下图所示:

图 3:配置 USB

接下来配置 HID,展开左侧栏【Middleware】项,选中其中的【USB_DEVICE】项,在【USB_DEVICE Mode and Configuration】窗口下的【Class For FS IP】项中选中【Human Interface Device Class(HID)】项。在下方【Configuration】窗口中你可以将 VID 和 PID 更改为喜欢的数字,将【MANUFACTURER_STRING】更改为你喜欢的内容,如下图所示:

图 4:配置 HID

接下来将 USB 中断优先级改低点。展开侧边栏【System Core】项,选中【NVIC】,在右边【NVIC Mode and Configuration】窗口中将【USB low priority or CAN RX0 interrupts】项的优先级改为5。如下图所示:

图 5:配置 USB 中断

配置时钟

下面我们来配置时钟,在屏幕上方选项卡中选中【Clock Configuration】项。并按照下图所示配置时钟:

图 6:配置时钟

注意,先使能 USB 才能配置时钟中的【USB Prescaler】项,所以把时钟配置放在后面。

生成代码

选中窗体上方【Project Manager】选项卡,在【Project】栏中的【Toolchain/IDE】组合框选择【MDK-ARM】项,【Min Version】选择【V5.27】,表明使用的是 Keil 的最新版。

图 7:指定开发工具

打开【Code Generator】栏,勾选其中的【Generate peripheral initialzation as a pair of '.c/ch' files per peripheral】项。然后单击【GENEREATE CODE】按钮生成代码。

图 8:生成代码

Keil 配置

在生成项目的文件夹中打开【MDK-ARM】文件夹,双击里面后缀名为.uvprojx的文件(图标为 Keil 的文件),打开 CubeMx 自动生成的项目。

将 ST-Link 连至开发板,只需连三条线:SWCLK、SWDIO、GND。然后开发板连上位机,有两个 USB 口,有一个口附近是 CH340 芯片,这个是串口,连接另一个 USB。然后在 Keil 中的工具栏上点魔术棒按钮(Options for Target),选择【Debug】选项卡,在【Use】组合框中选择【ST-Link Debugger】项,如下图所示:

图 9:配置 ST-Link

然后点击【Use】组合框右侧的【Settings】按钮打开【Target Driver Setup】窗口,如果在【SW Device】面板下的【SWDIO】栏中显示的是No target connected,则表明 ST-Link 连接有问题,或设备损坏。如果上面为设备名称,则说明 ST-Link 已经可以使用。很不幸,我在文章开头给大家介绍的开发板就卡这了,居然坏掉了,太坑了!没办法,只能拿我做的键盘来烧程序了。注意,随便买一块 STM32F103RCT6 开发板,只要连接了 USB 口,都可以使用我将要写的程序。

STM32CubeMX 自动生成的 HID 默认为一个鼠标设备,我们可以在【Middlewares/USB_Device_Library】下的【usbd_hid.c】文件下查找HID_MOUSE_ReportDesc定义,如下图所示:

图 10:STM32CubeMX自动生成的鼠标报告描述符

注意,此报告描述符和我们之前讲解的 HID Descriptor Tool 自带的不一样,此报告描述符数据长度为 4 个字节,多一个字节用于存放滚轮数据,另外还声明了一个上位机传给鼠标的数据,具体作用我就不研究了。我们就直接使用这个更为正规的鼠标报告描述符来写程序吧。

要在程序中使用 HID 设备,需要增加一个外部声明。我们首先编译一下自动生成的程序,以方便通过展开【Application/User】下的【main.c】文件查看相应的头文件。编译后展开【main.c】,找到并打开【usb_device.h】文件,添加一句代码:

extern USBD_HandleTypeDef hUsbDeviceFS;

添加位置如下图所示:

图 11:添加 HID 设备的外部引用

添加这句代码后,在【main.c】文件中就可以使用hUsbDeviceFS了。

打开【main.c】文件,使用以下代码引入 HID 库:

#include "usbd_hid.h"

插入位置应当在/* USER CODE BEGIN Includes *//* USER CODE END Includes */之间。

编写鼠标动作代码

最后在 main 函数中插入主逻辑代码:

 /* USER CODE BEGIN 2 */
	int8_t report[4]={0,0,0,0}; //4 字节长度遵照鼠标报告描述符指示
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		HAL_Delay(2000);
		//单击鼠标右键
		report[2]=0;
		report[0]=2; //右键按下
		USBD_HID_SendReport(&hUsbDeviceFS,(uint8_t *)report,4);
		HAL_Delay(100);
		report[0]=0; //右键弹起
		USBD_HID_SendReport(&hUsbDeviceFS,(uint8_t *)report,4);
		HAL_Delay(2000);
		//鼠标向左移动50个像素
		report[1]=-50;
		USBD_HID_SendReport(&hUsbDeviceFS,(uint8_t *)report,4);
		HAL_Delay(2000);
		//单击鼠标左键
		report[1]=0;
		report[0]=1; //左键按下
		USBD_HID_SendReport(&hUsbDeviceFS,(uint8_t *)report,4);
		HAL_Delay(100);
		report[0]=1; //左键弹起
		USBD_HID_SendReport(&hUsbDeviceFS,(uint8_t *)report,4);
		HAL_Delay(2000);
		//鼠标向下移动50个像素
		report[2]=50;
		USBD_HID_SendReport(&hUsbDeviceFS,(uint8_t *)report,4);
		HAL_Delay(2000);
		//鼠标向右移动50个像素
		report[2]=0;
		report[1]=50;
		USBD_HID_SendReport(&hUsbDeviceFS,(uint8_t *)report,4);
		HAL_Delay(2000);
		//鼠标向上移动50个像素
		report[1]=0;
		report[2]=-50;
		USBD_HID_SendReport(&hUsbDeviceFS,(uint8_t *)report,4);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */

需要注意的是电脑端鼠标需要按下并弹起,才算一个完整的单击,所以单片机这端要完成一个单击操作,需发送两次数据。

USB 设备好像插麻烦,烧完程序后,上位机不认 USB 设备,非得断电重启后才认。需要注意的是此程序让鼠标自动移动和单击,所以最好打开一个记事本,将鼠标放置在中央再启动开发板,观察鼠标的动作。

怎么样?写个鼠标程序还是很简单的吧,这在以前是完全不可想像的,下篇文章,我们来模拟一个键盘程序。

;

© 2018 - IOT小分队文章发布系统 v0.3