一、系统概述与核心功能
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个核心任务(优先级从高到低):
- 触摸采集任务(优先级3):周期性读取触摸坐标(I2C/SPI),滤波后发送至消息队列。
- 手势识别任务(优先级2):解析触摸序列(单击、双击、拖拽、双指滑动),生成鼠标事件(移动、点击、滚动)。
- USB HID任务(优先级1):将鼠标事件封装为HID报告,通过USB发送给主机。
2. STM32CubeMX配置(关键步骤)
- 时钟配置:HSE=8MHz,PLL输出72MHz,USB时钟需为48MHz(PLL输出72MHz后经1.5分频)。
- USB配置 :
- 模式:Device Only;
- Class:Human Interface Device Class(HID);
- HID参数:Vendor ID=0x0483(ST),Product ID=0x5750,Polling Interval=10ms。
- I2C/SPI配置:根据触摸传感器选择I2C1(400kHz)或SPI1(8MHz)。
- 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协议上报主机,实现低成本、高定制化的触控输入方案。