利用stm32f103c6t6开发基于STM32的HID鼠标或键盘

为什么是stm32f103c6t6

好像是25年618的时候吧,在淘宝秒杀频道看到这个开发板参与秒杀,2块钱不到还包邮。博主接触过arduino以及pico,对stm32板一无所知,所以就想着买回来试试学习一下,结果一放就是9个月。前几天捣鼓pico模拟鼠标的时候又翻出来这个库存,依赖deepseek的耐心提示,终于跑通了一整个流程。

虽然stm32开发板烧录程序不如arduino和pico方便,但这个系列的板子胜在成熟廉价,是小批量制作的不二之选,另外,因为c6t6的内存较小,所以大家如果有兴趣购买学习,建议可以尝试stm32f103c8t6,以便将来可以跑更复杂一些的程序。

STM32CubeIDE 安装与环境配置

本节部分图片引用自简书,来源地址如下:
原文: https://www.jianshu.com/p/885f690edf16
作者: LLLENG

首先安装用来编译的官方ide程序,win7的小伙伴最好选择这个版本:

利用stm32f103c6t6开发基于STM32的HID鼠标或键盘

安装完 STM32CubeIDE 后在此处创建一个新的 STM32 工程;

利用stm32f103c6t6开发基于STM32的HID鼠标或键盘

选择单片机型号的时候软件会从网上获取新的数据,如果获取失败或卡顿可以直接点取消,STM32F103C6T6 的最小系统板的数据本地就有,获取失败也能正确被选中;

利用stm32f103c6t6开发基于STM32的HID鼠标或键盘

输入项目名称并确认后,开始进行项目的配置,STM32CubeIDE 内部集成了 CubeMX ,可以通过鼠标点点点的方式搭建好开发环境,后续的开发默认基于 HAL 库。首先我们需要设置时钟,在此处选择外部晶振;

利用stm32f103c6t6开发基于STM32的HID鼠标或键盘

使用st-link来传输程序的,需要进行如下操作,以避免写入一次后二次写入出错,不过博主用的是ttl方式写入,这个步骤只影响st-link模式;

利用stm32f103c6t6开发基于STM32的HID鼠标或键盘

在开启 USB 功能;

利用stm32f103c6t6开发基于STM32的HID鼠标或键盘

在 USB_DEVICE 选项中选择 HID 设备。默认创建就是一个鼠标设备;

利用stm32f103c6t6开发基于STM32的HID鼠标或键盘

在此处可以调整设备的 VID,PID。自行定义厂商和设备描述字符串。注意,假如你之前在你的电脑上开发过 USB 设备,需要更换和之前不同的 VID,PID,否则电脑不会加载正确的鼠标驱动;

利用stm32f103c6t6开发基于STM32的HID鼠标或键盘

之后配置时钟树,注意要填写你实际的晶振频率(应该大部分默认的都是 8M 吧),针对hid鼠标项目,一般官方ide会自动修正参数,不需要手动进行修改;

利用stm32f103c6t6开发基于STM32的HID鼠标或键盘

点击齿轮图标应用配置,ide会自动创建一个包含了必备库文件的文件夹;

利用stm32f103c6t6开发基于STM32的HID鼠标或键盘

接下来鼠标环境已经就绪,我们只需要编辑修改 core/src/main.c 这个文件,就可以自由的进行一些鼠标自定义操作了。

利用stm32f103c6t6开发基于STM32的HID鼠标或键盘

简单的鼠标模拟程序

程序看着挺长,其实自己只是定义了一些鼠标动作,大部分都是上面的操作流程中系统根据用户选择自动生成的环境代码,在移动端真机测试时,经常遇到位置偏移的情况,比如下滑200上滑200的时候,往往无法回到初始的下滑位置,所以博主一般会把上滑的距离适当调大一些:

#include "main.h"
#include "usb_device.h"
#include "usbd_hid.h"

/* USER CODE BEGIN PTD */
typedef struct {
  uint8_t buttons;
  int8_t x;
  int8_t y;
  int8_t wheel;
} Mouse_Report_TypeDef;
/* USER CODE END PTD */

/* USER CODE BEGIN PD */
#define MOVE_STEP 30
/* USER CODE END PD */

/* USER CODE BEGIN PV */
extern USBD_HandleTypeDef hUsbDeviceFS;
Mouse_Report_TypeDef MouseReport;
/* USER CODE END PV */

/* USER CODE BEGIN PFP */
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
void Mouse_Move(uint8_t button_state, int8_t x, int8_t y);
void Mouse_Release(void);
void Mouse_MoveUp(uint16_t distance);
void Mouse_MoveDown(uint16_t distance);
/* USER CODE END PFP */

/* USER CODE BEGIN 0 */
void Mouse_Move(uint8_t button_state, int8_t x, int8_t y) {
  MouseReport.buttons = button_state;
  MouseReport.x = x;
  MouseReport.y = y;
  MouseReport.wheel = 0;
  USBD_HID_SendReport(&hUsbDeviceFS, (uint8_t*)&MouseReport, sizeof(Mouse_Report_TypeDef));
  HAL_Delay(10);
}

void Mouse_Release(void) {
  MouseReport.buttons = 0;
  MouseReport.x = 0;
  MouseReport.y = 0;
  MouseReport.wheel = 0;
  USBD_HID_SendReport(&hUsbDeviceFS, (uint8_t*)&MouseReport, sizeof(Mouse_Report_TypeDef));
  HAL_Delay(10);
}

void Mouse_MoveUp(uint16_t distance) {
  uint16_t remaining = distance;
  while(remaining > 0) {
    int8_t step = (remaining > MOVE_STEP) ? MOVE_STEP : remaining;
    Mouse_Move(1, 0, -step);
    HAL_Delay(20);
    remaining -= step;
  }
}

void Mouse_MoveDown(uint16_t distance) {
  uint16_t remaining = distance;
  while(remaining > 0) {
    int8_t step = (remaining > MOVE_STEP) ? MOVE_STEP : remaining;
    Mouse_Move(0, 0, step);
    HAL_Delay(20);
    remaining -= step;
  }
}
/* USER CODE END 0 */

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USB_DEVICE_Init();

  /* USER CODE BEGIN 2 */
  HAL_Delay(2000);

  MouseReport.buttons = 0;
  MouseReport.x = 0;
  MouseReport.y = 0;
  MouseReport.wheel = 0;

  Mouse_Release();
  Mouse_MoveUp(500);
  HAL_Delay(1000);
  /* USER CODE END 2 */

  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    Mouse_MoveDown(360);
    HAL_Delay(100);

    Mouse_MoveUp(400);
    HAL_Delay(100);

    Mouse_Release();
    HAL_Delay(2000);
  }
  /* USER CODE END 3 */
}

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL6;
  HAL_RCC_OscConfig(&RCC_OscInitStruct);

  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1);

  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USB;
  PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL;
  HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
}

static void MX_GPIO_Init(void)
{
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
}

void Error_Handler(void)
{
  while(1) { }
}

在STM32CubeIDE中生成 hex 文件

  • 打开工程设置:在STM32CubeIDE中,右键点击你的项目名称,选择最后一项 Properties。
  • 找到设置项:在弹出的窗口中,依次展开 C/C++ Build -> Settings。
  • 勾选生成.hex:在右侧的 Tool Settings 标签页下,找到 MCU Post build outputs,勾选上 Convert to HEX file (-O ihex) 这个选项 。
  • 应用并编译:点击 Apply and Close。然后,像往常一样点击编译按钮(锤子图标)重新编译工程。编译成功后,你会在工程的 Debug 或 Release 文件夹里找到与项目同名的 .hex 文件,这个就是要烧录的文件。

硬件连接与BOOT模式设置

利用stm32f103c6t6开发基于STM32的HID鼠标或键盘

这是非常关键的一步,需要确保你的硬件连接和跳线正确无误。
准备硬件:你需要一个 USB转TTL模块(比如用CH340芯片的模块)和几根杜邦线 。
交叉连接线:将USB转TTL模块与你的STM32最小系统板按照交叉的原则连接起来 。

模块的 TXD → STM32的 PA10 (USART1_RX)
模块的 RXD → STM32的 PA9 (USART1_TX)
模块的 GND → STM32的 GND (地线必须共地)
(可选)模块的 3.3V → STM32的 3.3V (如果你的板子需要从模块取电,但大多数核心板独立供电更稳定)
设置BOOT引脚为“串口下载模式”:为了让STM32进入ISP模式,需要通过跳线帽设置BOOT引脚的状态 。
将 BOOT0 的跳线帽跳到 1 (高电平)。
将 BOOT1 的跳线帽跳到 0 (低电平)。

上电:将USB转TTL模块插入电脑USB口,并为你的STM32核心板提供电源(如果是通过模块供电,此时已上电;如果是独立供电,请打开电源)。

配置FlyMCU软件

FlyMCU软件是个第三方软件,网上很容易获取到,这里就不提供下载了,现在打开FlyMCU软件,界面很简洁,照着配就行:

  • 选择串口和波特率:点击“搜索串口”,选择你在设备管理器里看到的那个COM号。波特率建议保持默认 115200 或 76800。
  • 选择要烧录的程序文件:点击“...”按钮,找到你在STM32CubeIDE里编译生成的 .hex 文件。通常它在你工程目录的 Debug 或 Release 文件夹里。
  • 配置关键选项:这是最重要的一步,直接关系到能否烧录成功。在下方选项区,勾选 “校验” 和 “编程后执行”。
  • 最关键的是最下面那排选项,请务必选择:“DTR的低电平复位,RTS高电平进BootLoader”。
  • 注意:把 “选项字节区” 前面的勾去掉,不要选它。如果勾选了这个,烧录后程序可能不会自动运行。
  • 最后点击 “开始编程” 按钮。

烧录完成后记得将 BOOT0 的跳线帽跳回到 0 (低电平)。

标签: stm32

移动端可扫我直达哦~

推荐阅读

thumbnail 2026-03-23

stm32f103c6t6 模拟绝对位置鼠标

网上很多案例都是基于相对位置鼠标的,在手机上不太好计算按钮位置,就想尝试一下绝对位置鼠标,关于绝对位置鼠标,csdn有位大佬已经将整理的很详尽了,这里附上链接:https://blog.csdn.net/pengranxindong/...

少儿编程 stm32

thumbnail 2026-03-16

STM32F103C6T6与STM32F103C8T6核心板异同

淘宝活动的时候入了一块c6t6,因为烧录过程比之pico以及arduino稍嫌复杂,所以一直没有尝试。这几天尝试用pico与arduino实现鼠标,想起了这块吃灰的开发板。网上很多c8t6的图片,因为引脚一致,找不到c6t6的图,就用...

少儿编程 stm32

thumbnail 2026-03-16

STM32CubeIDE&STM32CubeMX&STM32CubeProg的区别

这三个工具都是由STM官方提供的、针对STM32开发全流程的三个不同环节的工具,可以理解为“设计、建造、交付”的关系。主要功能对比 工具 核心角色 主要功能 在整个流程中的位置 形象比喻 STM32CubeMX 图形化配置...

少儿编程 stm32

thumbnail 2026-03-15

深入了解一下 STM32F103C6T6 这款开发板

核心架构与性能心脏:强劲的Cortex-M3内核它搭载了32位的ARM Cortex-M3处理器,最高能跑到 72MHz。这个内核有高效的数据处理能力和快速的中断响应,对于工业控制、电机驱动这类需要实时响应的任务来说,非常合适。存储空...

少儿编程 stm32