基于STM32的触控USB鼠标设计

一、系统概述与核心功能

1. 系统定位

基于STM32的触控USB鼠标以"触摸输入采集-坐标转换-USB HID协议封装-即插即用"为核心,将触摸传感器(电容/电阻式)的触摸位置、手势动作转换为标准USB鼠标事件(移动、点击、滚动),实现无需驱动的即插即用触控输入设备,适用于触摸板替代、嵌入式人机交互、创意控制器等场景。

2. 核心功能模块

模块 功能描述
触摸传感 电容触摸板(I2C接口)或电阻触摸屏(ADC接口),检测单点/多点触摸坐标(X,Y)及触摸状态(按下/释放)
坐标处理 原始坐标滤波(滑动平均/卡尔曼滤波)、坐标映射(将触摸区域映射到屏幕分辨率)、手势识别(单击、双击、拖拽、双指滚动)
USB HID STM32内置USB FS(全速)外设配置为HID鼠标设备,定义报告描述符(标准鼠标:X/Y相对移动、左/中/右键、滚轮)
事件转换 触摸坐标→鼠标移动(相对位移)、触摸按下→鼠标点击、双指滑动→滚轮滚动
低功耗设计 无触摸时进入STOP模式(触摸传感器中断唤醒),USB挂起时进入SUSPEND模式,待机电流<1mA

二、硬件设计方案

1. 核心硬件选型

模块 型号 关键参数 接口方式
主控MCU STM32F103C8T6 72MHz Cortex-M3,64KB Flash,20KB RAM,全速USB 2.0(PA11/PA12),12位ADC,2个I2C 核心控制器(USB HID设备)
电容触摸板 GT911/FT6236 I2C接口,支持最多5点触摸,分辨率320×240,中断输出(INT) I2C1(PB6=SCL,PB7=SDA)+ INT(PA0)
电阻触摸屏 4线电阻屏+XPT2046 SPI接口,12位ADC,压力检测,需校准(4点校准法) SPI1(PA5=SCK,PA6=MISO,PA7=MOSI,PA4=CS)+ IRQ(PA1)
USB接口 Micro-USB插座 USB 2.0全速,D+(PA12)、D-(PA11),内置1.5kΩ上拉电阻(通过软件控制) USB DP/DM直接连接MCU
电源管理 AMS1117-3.3V 5V USB供电→3.3V给MCU/触摸传感器,最大电流500mA 供电一体化(带ESD保护)
状态指示 WS2812 RGB LED 单线控制,显示触摸状态(红:触摸中,绿:USB已连接,蓝:手势识别) GPIO(PB0,单线时序)

2. 硬件电路设计要点

2.1 核心电路连接(以电容触摸板GT911为例)
  • STM32最小系统:8MHz外部晶振+32.768kHz RTC晶振(可选),SWD调试接口(PA13/PA14),复位电路(10kΩ上拉+0.1μF电容)。
  • USB接口:D+(PA12)、D-(PA11)直接连接MCU,D+通过1.5kΩ电阻上拉至3.3V(STM32内部软控制),VBUS(5V)接AMS1117输入。
  • 电容触摸板(GT911)
    • SCL=PB6,SDA=PB7(I2C1,4.7kΩ上拉电阻至3.3V);
    • INT=PA0(中断输出,下降沿触发);
    • RST=PA1(复位引脚,上电时低电平复位);
    • VCC=3.3V,GND共地。
  • 抗干扰设计:USB D+/D-走线等长且包地,触摸板I2C线加10pF电容滤波,电源入口加TVS管(ESD保护)。
2.2 备选方案:电阻触摸屏+XPT2046
  • 电阻屏:4线(X+、X-、Y+、Y-)接XPT2046对应引脚;
  • XPT2046:SPI接口(SCK=PA5,MISO=PA6,MOSI=PA7,CS=PA4),IRQ=PA1(触摸中断);
  • 校准:需存储4点校准参数(ax,bx,cx,ay,by,cy)至Flash,开机时加载。

三、软件设计与核心代码

1. 系统架构(FreeRTOS多任务)

采用FreeRTOS实时操作系统,划分3个核心任务(优先级从高到低):

  1. 触摸采集任务(优先级3):周期性读取触摸坐标(I2C/SPI),滤波后发送至消息队列。
  2. 手势识别任务(优先级2):解析触摸序列(单击、双击、拖拽、双指滑动),生成鼠标事件(移动、点击、滚动)。
  3. USB HID任务(优先级1):将鼠标事件封装为HID报告,通过USB发送给主机。

2. STM32CubeMX配置(关键步骤)

  1. 时钟配置:HSE=8MHz,PLL输出72MHz,USB时钟需为48MHz(PLL输出72MHz后经1.5分频)。
  2. USB配置
    • 模式:Device Only;
    • Class:Human Interface Device Class(HID);
    • HID参数:Vendor ID=0x0483(ST),Product ID=0x5750,Polling Interval=10ms。
  3. I2C/SPI配置:根据触摸传感器选择I2C1(400kHz)或SPI1(8MHz)。
  4. GPIO配置:触摸中断引脚(PA0)设为外部中断下降沿触发,USB DP(PA12)上拉通过软件控制。

3. 核心代码实现(基于HAL库+电容触摸板GT911)

3.1 USB HID报告描述符(usbd_hid.c中修改)
c 复制代码
// 标准鼠标报告描述符(8字节)
__ALIGN_BEGIN static uint8_t HID_MOUSE_ReportDesc[] __ALIGN_END = {
  0x05, 0x01,        // USAGE_PAGE (Generic Desktop)
  0x09, 0x02,        // USAGE (Mouse)
  0xA1, 0x01,        // COLLECTION (Application)
  0x09, 0x01,        //   USAGE (Pointer)
  0xA1, 0x00,        //   COLLECTION (Physical)
  // 按钮(1字节,8个按钮位)
  0x05, 0x09,        //     USAGE_PAGE (Button)
  0x19, 0x01,        //     USAGE_MINIMUM (Button 1)
  0x29, 0x03,        //     USAGE_MAXIMUM (Button 3)
  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)
  0x95, 0x01,        //     REPORT_COUNT (1)
  0x75, 0x05,        //     REPORT_SIZE (5)
  0x81, 0x03,        //     INPUT (Cnst,Var,Abs)
  // X/Y相对移动(2字节,-127~127)
  0x05, 0x01,        //     USAGE_PAGE (Generic Desktop)
  0x09, 0x30,        //     USAGE (X)
  0x09, 0x31,        //     USAGE (Y)
  0x15, 0x81,        //     LOGICAL_MINIMUM (-127)
  0x25, 0x7F,        //     LOGICAL_MAXIMUM (127)
  0x75, 0x08,        //     REPORT_SIZE (8)
  0x95, 0x02,        //     REPORT_COUNT (2)
  0x81, 0x06,        //     INPUT (Data,Var,Rel)
  // 滚轮(1字节,-127~127)
  0x09, 0x38,        //     USAGE (Wheel)
  0x15, 0x81,        //     LOGICAL_MINIMUM (-127)
  0x25, 0x7F,        //     LOGICAL_MAXIMUM (127)
  0x75, 0x08,        //     REPORT_SIZE (8)
  0x95, 0x01,        //     REPORT_COUNT (1)
  0x81, 0x06,        //     INPUT (Data,Var,Rel)
  0xC0,              //   END_COLLECTION
  0xC0               // END_COLLECTION
};
3.2 触摸采集任务(GT911驱动)
c 复制代码
#include "gt911.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

QueueHandle_t touch_queue;  // 触摸数据队列

// 触摸数据结构体
typedef struct {
  uint16_t x;        // X坐标(0~320)
  uint16_t y;        // Y坐标(0~240)
  uint8_t  touch_num; // 触摸点数(0~5)
  uint8_t  event;    // 事件:0=释放,1=按下,2=移动
} TouchPoint_t;

void GT911_Task(void *pvParameters) {
  TouchPoint_t tp;
  TickType_t xLastWakeTime = xTaskGetTickCount();
  const TickType_t xFrequency = pdMS_TO_TICKS(10);  // 10ms采集一次(100Hz)
  
  GT911_Init();  // 初始化GT911(I2C地址0x14/0x5D)
  
  while (1) {
    // 读取触摸状态(通过I2C读取GT911寄存器)
    uint8_t status = GT911_ReadReg(GT911_STATUS_REG);
    if (status & 0x80) {  // 有新数据
      tp.touch_num = status & 0x0F;  // 触摸点数
      if (tp.touch_num > 0) {
        // 读取第一个触摸点坐标(支持多点,此处简化取第一个)
        GT911_ReadTouchPoint(&tp.x, &tp.y, 0);
        tp.event = (tp.touch_num > 0) ? 1 : 0;  // 按下/移动
        xQueueSend(touch_queue, &tp, 0);  // 发送至队列
      }
      GT911_WriteReg(GT911_STATUS_REG, 0x00);  // 清除状态
    }
    vTaskDelayUntil(&xLastWakeTime, xFrequency);
  }
}
3.3 手势识别与鼠标事件转换
c 复制代码
// 鼠标事件结构体(对应HID报告)
typedef struct {
  int8_t  x_rel;   // X相对移动(-127~127)
  int8_t  y_rel;   // Y相对移动(-127~127)
  uint8_t buttons; // 按钮状态(bit0=左键,bit1=右键,bit2=中键)
  int8_t  wheel;   // 滚轮(-127~127)
} MouseReport_t;

void Gesture_Task(void *pvParameters) {
  TouchPoint_t tp;
  MouseReport_t mouse = {0};
  uint16_t last_x = 0, last_y = 0;
  uint32_t last_touch_time = 0;
  uint8_t click_count = 0;
  
  while (1) {
    if (xQueueReceive(touch_queue, &tp, portMAX_DELAY) == pdPASS) {
      // 1. 坐标映射(触摸区域320×240 → 屏幕分辨率)
      int16_t dx = 0, dy = 0;
      if (tp.event == 1) {  // 按下/移动
        if (last_x != 0 || last_y != 0) {
          dx = (tp.x - last_x) * 127 / 320;  // 缩放为相对移动(-127~127)
          dy = (tp.y - last_y) * 127 / 240;
          // 限制范围
          dx = (dx < -127) ? -127 : (dx > 127) ? 127 : dx;
          dy = (dy < -127) ? -127 : (dy > 127) ? 127 : dy;
        }
        last_x = tp.x;
        last_y = tp.y;
        mouse.buttons = 0x01;  // 左键按下
      } else {  // 释放
        last_x = last_y = 0;
        mouse.buttons = 0x00;  // 所有按钮释放
        // 单击/双击判断(时间阈值300ms)
        if (HAL_GetTick() - last_touch_time < 300) click_count++;
        else click_count = 1;
        last_touch_time = HAL_GetTick();
        if (click_count == 2) {  // 双击
          mouse.buttons = 0x01;  // 模拟左键双击(快速按下释放两次)
          click_count = 0;
        }
      }
      
      // 2. 双指手势判断(滚动)
      if (tp.touch_num == 2) {
        // 读取第二个触摸点坐标(简化:假设垂直滑动为滚轮)
        uint16_t x2, y2;
        GT911_ReadTouchPoint(&x2, &y2, 1);
        int16_t dy2 = y2 - tp.y;
        mouse.wheel = (dy2 > 10) ? -10 : (dy2 < -10) ? 10 : 0;  // 滚动步长
      } else {
        mouse.wheel = 0;
      }
      
      // 3. 更新鼠标报告
      mouse.x_rel = dx;
      mouse.y_rel = dy;
      
      // 4. 发送至USB HID任务
      xQueueSend(mouse_queue, &mouse, 0);
    }
  }
}
3.4 USB HID发送任务
c 复制代码
#include "usbd_hid.h"
extern USBD_HandleTypeDef hUsbDeviceFS;

void USB_HID_Task(void *pvParameters) {
  MouseReport_t mouse;
  uint8_t hid_report[4] = {0};  // 标准鼠标报告:buttons, x, y, wheel
  
  while (1) {
    if (xQueueReceive(mouse_queue, &mouse, portMAX_DELAY) == pdPASS) {
      // 封装HID报告(标准鼠标报告格式)
      hid_report[0] = mouse.buttons;  // 按钮状态
      hid_report[1] = mouse.x_rel;    // X相对移动
      hid_report[2] = mouse.y_rel;    // Y相对移动
      hid_report[3] = mouse.wheel;    // 滚轮
      
      // 发送USB HID报告(阻塞式,超时10ms)
      USBD_HID_SendReport(&hUsbDeviceFS, hid_report, 4);
    }
    vTaskDelay(pdMS_TO_TICKS(10));  // 10ms发送间隔(与HID描述符一致)
  }
}

四、关键技术与优化

1. 触摸坐标处理

  • 滤波算法:对原始坐标进行5点滑动平均滤波,消除抖动。
  • 坐标映射 :将触摸板物理坐标(如320×240)线性映射到屏幕分辨率(如1920×1080),公式:
    xscreen=xtouch×ScreenWidthTouchWidthx_{\text{screen}} = \frac{x_{\text{touch}} \times \text{ScreenWidth}}{\text{TouchWidth}}xscreen=TouchWidthxtouch×ScreenWidth
  • 加速度曲线:根据移动速度动态调整映射比例(慢速时精细控制,快速时快速移动),提升操作手感。

2. USB HID优化

  • 报告速率:设置Polling Interval=10ms(100Hz),平衡响应速度与USB带宽。
  • 复合设备:可扩展为鼠标+键盘复合设备(通过修改报告描述符),支持快捷键操作。
  • 即插即用:Windows/Linux/macOS无需额外驱动,系统自动识别为HID鼠标。

3. 低功耗设计

  • 触摸中断唤醒:配置GT911的INT引脚为下降沿触发,无触摸时STM32进入STOP模式(功耗<10μA)。
  • USB挂起:检测到USB总线挂起时,关闭USB时钟,进入SUSPEND模式。

参考代码 STM32 触控USB鼠标 www.youwenfan.com/contentcst/133749.html

五、系统调试与扩展

1. 调试步骤

阶段 操作 工具
USB枚举测试 连接电脑,查看设备管理器是否识别为"HID-compliant mouse" USBlyzer(抓包工具)、设备管理器
触摸采集测试 打印触摸原始坐标(串口调试),验证坐标范围与响应速度 串口助手、逻辑分析仪(I2C波形)
手势识别测试 模拟单击、拖拽、双指滑动,观察鼠标光标动作 屏幕录像、鼠标光标跟踪软件
性能测试 测量触摸到光标移动的延迟(目标<20ms) 高速摄像机、延时测试工具

2. 扩展功能

  • 多点触控:利用GT911的5点触摸,实现双指缩放、旋转等高级手势。
  • 自定义按键:增加物理按键(如左/右键、DPI切换),通过USB HID报告扩展按钮数量。
  • 无线化:替换USB为蓝牙模块(如HC-05),实现无线触控鼠标(需修改为蓝牙HID协议)。
  • 触摸屏模式:修改HID描述符为"Touch Screen"类,支持绝对坐标上报(需Windows 10+支持)。

六、总结

基于STM32的触控USB鼠标通过电容/电阻触摸传感器采集坐标,经手势识别算法转换为鼠标事件,再通过USB HID协议上报主机,实现低成本、高定制化的触控输入方案。

相关推荐
天狼IoT2 小时前
STM32-keil+CubeMX快速开发:GPIO功能
stm32·单片机·嵌入式硬件
芯岭技术2 小时前
PY32C673单片机特性,应用场景介绍,最高主频可达 72 MHz
单片机·嵌入式硬件
ytttr8732 小时前
STM32 独立看门狗(IWDG)程序设计与实现
stm32·单片机·嵌入式硬件
木下~learning2 小时前
嵌入式Linux 小项目:RK3399 基于 MPlayer 实现视频播放器(从环境搭建到完整播放列表)
linux·运维·嵌入式硬件·音视频
学习噢学个屁2 小时前
基于51单片机心率仪—体温心率血氧蓝牙
c语言·单片机·嵌入式硬件·51单片机
济6172 小时前
FreeRTOS 通信任务设计(2)----UART+DMA 环形缓冲 + 空闲中断+ 流缓冲区--- 高效接收方案详解
stm32·单片机·嵌入式·freertos
QH139292318803 小时前
是德科技KEYSIGHT N5183B 9 kHz~40 GHz微波模拟信号发生器
网络·数据库·科技·嵌入式硬件·集成测试
W.W.H.3 小时前
嵌入式常见面试题——硬件与中断篇
经验分享·单片机·嵌入式硬件
灰子学技术3 小时前
Envoy 中 UDP 网络通信实现分析
网络·单片机·嵌入式硬件·网络协议·udp