编程技术分享

分享编程知识,探讨技术创新

0%

简介:通过USB实现主控电脑模拟鼠标键盘控制被控电脑操作,亦可开发自动按键操作实现嘿嘿嘿嘿

好的,作为一名高级嵌入式软件开发工程师,我将深入分析这张嵌入式产品图片,并围绕通过USB实现主控电脑模拟鼠标键盘控制被控电脑操作,以及自动按键操作(“嘿嘿嘿嘿”暗示自动化任务)这一核心功能,构建一个可靠、高效、可扩展的嵌入式系统平台。
关注微信公众号,提前获取相关推文

项目需求分析与系统架构设计

1. 需求分析

  • 核心功能:
    • USB 鼠标模拟: 主控设备(图中嵌入式设备)通过USB连接到被控电脑,模拟USB鼠标设备,实现鼠标移动、点击、滚轮等操作的控制。
    • USB 键盘模拟: 主控设备模拟USB键盘设备,实现按键输入,包括字母、数字、符号、功能键等。
    • 自动按键操作: 预设或动态配置一系列按键操作序列,设备能够自动执行这些序列,实现自动化任务,例如游戏脚本、重复性工作自动化等。
  • 性能需求:
    • 低延迟: 鼠标和键盘操作需要响应迅速,延迟尽可能低,保证用户体验。
    • 高可靠性: 系统运行稳定可靠,不易出错或崩溃。
    • 高兼容性: 兼容多种操作系统(Windows, macOS, Linux等)和USB主机。
  • 可扩展性需求:
    • 功能扩展: 预留接口和架构,方便未来扩展更多功能,例如宏按键、组合键、更复杂的自动化脚本等。
    • 硬件扩展: 考虑未来可能需要更换更强大的主控芯片或增加外围设备。
  • 维护升级需求:
    • 固件可升级: 支持固件在线升级,方便修复bug和添加新功能。
    • 易于维护: 代码结构清晰,模块化设计,方便后期维护和修改。

2. 系统架构设计

基于以上需求,我将采用分层架构来设计这个嵌入式系统,这种架构具有良好的模块化、可维护性和可扩展性。系统架构主要分为以下几个层次:

  • 硬件层 (Hardware Layer):

    • 主控芯片 (Microcontroller): 选择一款具备USB Device功能的微控制器,例如STM32系列、NXP LPC系列、Microchip PIC系列等。根据项目复杂度、性能需求和成本预算选择合适的型号。 图中的芯片封装为 DIP,可能是早期的 AVR 或 PIC 系列,但现代项目通常会选择 ARM Cortex-M 系列,例如 STM32F103C8T6 (俗称蓝 pill) 或更高级的型号。
    • USB 接口: 标准的USB Type-A 公头,用于连接主控电脑。
    • Micro-USB 接口: 图中还有一个 Micro-USB 接口,可能是用于固件烧录、调试或供电。也可能用于连接其他设备或实现双USB功能(例如,同时作为USB Host 和 USB Device)。
    • 按键 (Button): 图中有一个按键,可能用于模式切换、配置或触发某些特定功能。
    • LED 指示灯 (LED): 图中有一个 LED,用于指示设备状态,例如连接状态、工作模式等。
    • 晶振 (Crystal Oscillator): 为微控制器和USB模块提供精确的时钟源。
    • 电源管理电路 (Power Management): 将USB供电转换为微控制器和其他组件所需的电压。
    • PCB (Printed Circuit Board): 承载所有电子元件的印刷电路板。
  • 驱动层 (Driver Layer):

    • 微控制器 HAL (Hardware Abstraction Layer): 微控制器的硬件抽象层,提供统一的API接口,方便上层应用访问硬件资源,例如GPIO、定时器、USB控制器等。 HAL层需要针对具体的微控制器型号进行实现。
    • USB 设备驱动 (USB Device Driver): 实现USB设备协议栈,处理USB枚举、配置、数据传输等细节。通常可以采用现有的USB库,例如:
      • STM32Cube HAL USB库 (针对 STM32): 官方提供的库,功能完善,性能稳定。
      • libusb (开源库): 跨平台的USB库,可以用于嵌入式设备和主机端开发。
      • TinyUSB (开源库): 轻量级的USB库,资源占用小,适合资源受限的嵌入式系统。
  • 核心层 (Core Layer):

    • USB HID 描述符 (USB HID Descriptors): 定义USB设备的HID(Human Interface Device)描述符,包括设备描述符、配置描述符、接口描述符、端点描述符和HID报告描述符。 鼠标和键盘都需要定义相应的HID报告描述符,描述鼠标的移动、按键状态和键盘的按键码。
    • 鼠标模拟模块 (Mouse Emulation Module): 负责处理鼠标操作逻辑,接收来自上层应用的鼠标控制指令,生成符合HID报告格式的鼠标数据,并通过USB设备驱动发送给主机。
    • 键盘模拟模块 (Keyboard Emulation Module): 负责处理键盘操作逻辑,接收来自上层应用的按键指令,生成符合HID报告格式的键盘数据,并通过USB设备驱动发送给主机。
    • 自动化引擎模块 (Automation Engine Module): 负责实现自动按键操作功能。可以采用以下几种方式实现:
      • 预设脚本: 将按键序列预先存储在固件中,例如存储在数组或文件中。
      • 配置参数: 通过配置界面(例如,通过串口或USB虚拟串口)设置按键序列和执行参数。
      • 外部指令: 通过USB自定义协议接收来自主控电脑的按键序列和执行指令。
        自动化引擎需要使用定时器来控制按键操作的间隔和持续时间。
  • 应用层 (Application Layer):

    • 配置管理模块 (Configuration Management Module): 负责管理设备配置参数,例如工作模式、自动化脚本、按键映射等。 可以通过按键、LED指示灯和可能的串口或USB虚拟串口进行配置交互。
    • 模式切换模块 (Mode Switching Module): 根据用户输入(例如,按键)切换设备的工作模式,例如鼠标模式、键盘模式、自动化模式等。
    • 用户接口模块 (User Interface Module): 提供用户与设备交互的接口,例如通过LED指示灯显示设备状态,通过按键接收用户输入。 如果需要更复杂的配置,可以考虑使用串口或USB虚拟串口提供命令行或简单的图形界面。

3. 代码设计架构

我将采用事件驱动状态机相结合的设计模式,来构建核心层和应用层的代码。

  • 事件驱动: 系统对各种事件做出响应,例如USB数据接收事件、定时器事件、按键事件等。事件驱动的设计可以提高系统的响应速度和效率。
  • 状态机: 使用状态机来管理系统的不同工作模式和状态,例如空闲状态、鼠标模式、键盘模式、自动化模式等。状态机可以使代码结构更清晰,逻辑更易于理解和维护。

详细C代码实现 (框架代码,非完整3000行)

由于3000行代码的要求过于庞大,我无法在此提供完整的3000行可编译运行的代码。但我将提供关键模块的C代码框架,并详细注释,帮助你理解代码结构和实现思路。你可以基于这些框架代码,结合具体的微控制器和USB库,完成完整的项目代码。

1. 头文件 (main.h)

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#ifndef __MAIN_H__
#define __MAIN_H__

#include <stdint.h>
#include <stdbool.h>

// 定义设备状态
typedef enum {
DEVICE_STATE_IDLE,
DEVICE_STATE_MOUSE_MODE,
DEVICE_STATE_KEYBOARD_MODE,
DEVICE_STATE_AUTOMATION_MODE,
DEVICE_STATE_ERROR
} DeviceState_t;

// 定义按键事件
typedef enum {
BUTTON_EVENT_NONE,
BUTTON_EVENT_SHORT_PRESS,
BUTTON_EVENT_LONG_PRESS
} ButtonEvent_t;

// 定义鼠标事件
typedef enum {
MOUSE_EVENT_MOVE,
MOUSE_EVENT_BUTTON_PRESS,
MOUSE_EVENT_BUTTON_RELEASE,
MOUSE_EVENT_SCROLL
} MouseEvent_t;

// 定义键盘事件
typedef enum {
KEYBOARD_EVENT_PRESS,
KEYBOARD_EVENT_RELEASE
} KeyboardEvent_t;

// 定义自动化事件
typedef enum {
AUTOMATION_EVENT_START,
AUTOMATION_EVENT_STOP
} AutomationEvent_t;

// 设备状态全局变量
extern DeviceState_t deviceState;

// 函数声明
void System_Init(void);
void USB_Device_Init(void);
void USB_Device_Process(void); // USB 设备事件处理
void Button_Init(void);
ButtonEvent_t Button_Process(void); // 按键事件处理
void LED_Init(void);
void LED_SetState(DeviceState_t state); // LED 状态显示
void Mouse_Emulate(MouseEvent_t event, int16_t x, int16_t y, int8_t buttons, int8_t scroll);
void Keyboard_Emulate(KeyboardEvent_t event, uint8_t keycode);
void Automation_Start(void);
void Automation_Stop(void);
void Automation_Process(void); // 自动化引擎处理
void Configuration_Load(void);
void Configuration_Save(void);
void Error_Handler(uint32_t error_code);

#endif // __MAIN_H__

2. 主函数 (main.c)

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include "main.h"

DeviceState_t deviceState = DEVICE_STATE_IDLE;

int main(void)
{
System_Init(); // 系统初始化 (时钟、外设等)
LED_Init(); // LED 初始化
Button_Init(); // 按键初始化
USB_Device_Init(); // USB 设备初始化
Configuration_Load(); // 加载配置

deviceState = DEVICE_STATE_MOUSE_MODE; // 默认进入鼠标模式
LED_SetState(deviceState);

while (1)
{
USB_Device_Process(); // USB 设备事件处理

ButtonEvent_t buttonEvent = Button_Process(); // 按键事件处理
if (buttonEvent != BUTTON_EVENT_NONE) {
// 处理按键事件,例如切换模式
if (buttonEvent == BUTTON_EVENT_SHORT_PRESS) {
if (deviceState == DEVICE_STATE_MOUSE_MODE) {
deviceState = DEVICE_STATE_KEYBOARD_MODE;
} else if (deviceState == DEVICE_STATE_KEYBOARD_MODE) {
deviceState = DEVICE_STATE_AUTOMATION_MODE;
} else if (deviceState == DEVICE_STATE_AUTOMATION_MODE) {
deviceState = DEVICE_STATE_MOUSE_MODE;
}
LED_SetState(deviceState);
} else if (buttonEvent == BUTTON_EVENT_LONG_PRESS) {
// 长按按键,例如进入配置模式或触发特定功能
// ...
}
}

if (deviceState == DEVICE_STATE_AUTOMATION_MODE) {
Automation_Process(); // 自动化引擎处理
}

// 其他后台任务...
}
}

// 系统初始化 (需要根据具体硬件平台实现)
void System_Init(void)
{
// 初始化时钟、GPIO、中断等
// ...
}

// 错误处理函数
void Error_Handler(uint32_t error_code)
{
deviceState = DEVICE_STATE_ERROR;
LED_SetState(deviceState);
while(1) {
// 错误处理逻辑,例如闪烁LED,重启系统等
}
}

3. USB 设备驱动 (usb_device.c 和 usb_device.h) - 框架代码,需结合具体USB库实现

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
// usb_device.h
#ifndef __USB_DEVICE_H__
#define __USB_DEVICE_H__

#include "main.h"

// USB 初始化函数
void USB_Device_Init(void);

// USB 事件处理函数,在主循环中调用
void USB_Device_Process(void);

// 发送鼠标报告
void USB_Device_SendMouseReport(int16_t x, int16_t y, uint8_t buttons, int8_t scroll);

// 发送键盘报告
void USB_Device_SendKeyboardReport(uint8_t keycode[], uint8_t keycode_len);

#endif // __USB_DEVICE_H__


// usb_device.c
#include "usb_device.h"
#include "usb_hid.h" // 假设 HID 描述符和报告定义在 usb_hid.h 中

// USB 设备状态
typedef enum {
USB_DEVICE_STATE_DEFAULT,
USB_DEVICE_STATE_ADDRESSED,
USB_DEVICE_STATE_CONFIGURED
} USB_DeviceState_t;

USB_DeviceState_t usbDeviceState = USB_DEVICE_STATE_DEFAULT;

// USB 初始化函数 (需要根据具体的USB库和硬件实现)
void USB_Device_Init(void)
{
// 初始化 USB 控制器硬件
// 设置 USB 设备描述符、配置描述符、接口描述符、端点描述符、HID报告描述符 (参考 usb_hid.h)
// 注册 USB 事件回调函数 (例如,USB枚举完成、数据接收等)
// ...
usbDeviceState = USB_DEVICE_STATE_DEFAULT;
}

// USB 事件处理函数,在主循环中调用
void USB_Device_Process(void)
{
// 处理 USB 总线事件,例如 USB 枚举过程、数据传输等
// (具体实现取决于使用的 USB 库,例如轮询 USB 事件标志或在中断服务程序中处理)
// ...

// 示例: 检查 USB 配置状态,如果配置完成,则设置状态为 CONFIGURED
// if (USB_IsConfigured()) {
// usbDeviceState = USB_DEVICE_STATE_CONFIGURED;
// } else {
// usbDeviceState = USB_DEVICE_STATE_ADDRESSED; // 或 DEFAULT
// }
}

// 发送鼠标报告
void USB_Device_SendMouseReport(int16_t x, int16_t y, uint8_t buttons, int8_t scroll)
{
if (usbDeviceState != USB_DEVICE_STATE_CONFIGURED) {
return; // USB 未配置,无法发送数据
}

uint8_t mouseReport[4]; // 鼠标报告数据,假设格式为 [buttons, x_offset_low, x_offset_high, y_offset_low, y_offset_high, scroll] (具体格式参考 HID 报告描述符)
mouseReport[0] = buttons;
mouseReport[1] = x & 0xFF;
mouseReport[2] = (x >> 8) & 0xFF;
mouseReport[3] = y & 0xFF;
mouseReport[4] = (y >> 8) & 0xFF;
mouseReport[5] = scroll;

// 调用 USB 库函数发送 HID 报告 (需要根据具体的 USB 库实现)
// USB_HID_SendReport(MOUSE_REPORT_ID, mouseReport, sizeof(mouseReport)); // 假设有 USB_HID_SendReport 函数
}

// 发送键盘报告 (发送按键按下和释放事件)
void USB_Device_SendKeyboardReport(uint8_t keycode[], uint8_t keycode_len)
{
if (usbDeviceState != USB_DEVICE_STATE_CONFIGURED) {
return; // USB 未配置,无法发送数据
}

uint8_t keyboardReport[8] = {0}; // 键盘报告数据,假设格式为 [modifier, reserved, keycode1, keycode2, keycode3, keycode4, keycode5, keycode6] (具体格式参考 HID 报告描述符)

// 将 keycode 数组复制到 keyboardReport 的 keycode 部分
for (int i = 0; i < keycode_len && i < 6; i++) {
keyboardReport[2 + i] = keycode[i];
}

// 调用 USB 库函数发送 HID 报告 (需要根据具体的 USB 库实现)
// USB_HID_SendReport(KEYBOARD_REPORT_ID, keyboardReport, sizeof(keyboardReport)); // 假设有 USB_HID_SendReport 函数
}

4. HID 描述符 (usb_hid.h) - 示例代码,需要根据具体需求调整

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#ifndef __USB_HID_H__
#define __USB_HID_H__

#include "usb_device.h"

// 设备描述符 (Device Descriptor)
#define USB_DEVICE_DESCRIPTOR { \
0x12, /* bLength */ \
USB_DESCRIPTOR_TYPE_DEVICE, /* bDescriptorType */ \
0x00, 0x02, /* bcdUSB = 2.00 */ \
0x00, /* bDeviceClass (Interface Class defined in interfaces) */ \
0x00, /* bDeviceSubClass */ \
0x00, /* bDeviceProtocol */ \
0x40, /* bMaxPacketSize0 = 64 */ \
0xXX, 0xXX, /* idVendor */ \
0xYY, 0xYY, /* idProduct */ \
0x00, 0x02, /* bcdDevice = 2.00 */ \
0x01, /* iManufacturer String Index */ \
0x02, /* iProduct String Index */ \
0x03, /* iSerialNumber String Index */ \
0x01 /* bNumConfigurations = 1 */ \
}

// 配置描述符 (Configuration Descriptor)
#define USB_CONFIGURATION_DESCRIPTOR { \
0x09, /* bLength */ \
USB_DESCRIPTOR_TYPE_CONFIGURATION, /* bDescriptorType */ \
0xZZ, 0x00, /* wTotalLength (will be calculated later) */ \
0x01, /* bNumInterfaces = 1 */ \
0x01, /* bConfigurationValue */ \
0x00, /* iConfiguration String Index */ \
0x80, /* bmAttributes = Bus Powered */ \
0xFA /* bMaxPower = 500mA */ \
}

// 接口描述符 (Interface Descriptor) - 鼠标接口
#define USB_INTERFACE_DESCRIPTOR_MOUSE { \
0x09, /* bLength */ \
USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
0x00, /* bInterfaceNumber = 0 */ \
0x00, /* bAlternateSetting */ \
0x01, /* bNumEndpoints = 1 */ \
0x03, /* bInterfaceClass = HID */ \
0x01, /* bInterfaceSubClass = Boot Interface Subclass */ \
0x02, /* bInterfaceProtocol = Mouse Protocol */ \
0x04 /* iInterface String Index */ \
}

// HID 描述符 (HID Descriptor) - 鼠标
#define USB_HID_DESCRIPTOR_MOUSE { \
0x09, /* bLength */ \
USB_DESCRIPTOR_TYPE_HID, /* bDescriptorType */ \
0x11, 0x01, /* bcdHID = 1.11 */ \
0x00, /* bCountryCode */ \
0x01, /* bNumDescriptors = 1 */ \
0x22, /* bDescriptorType = Report Descriptor */ \
sizeof(mouseReportDescriptor), 0x00 /* wDescriptorLength */ \
}

// 端点描述符 (Endpoint Descriptor) - 鼠标输入端点
#define USB_ENDPOINT_DESCRIPTOR_MOUSE_IN { \
0x07, /* bLength */ \
USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
0x81, /* bEndpointAddress = IN endpoint 1 */ \
0x03, /* bmAttributes = Interrupt endpoint */ \
0x04, 0x00, /* wMaxPacketSize = 4 bytes */ \
0x0A /* bInterval = 10ms */ \
}

// 鼠标报告描述符 (Report Descriptor) - 鼠标
const uint8_t mouseReportDescriptor[] = {
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x02, // Usage (Mouse)
0xA1, 0x01, // Collection (Application)
0x09, 0x01, // Usage (Pointer)
0xA1, 0x00, // Collection (Physical)
0x05, 0x09, // Usage Page (Buttons)
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 (Const,Var,Abs)
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) // 相对坐标移动
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
};

// ... (键盘接口、HID描述符、报告描述符等,类似鼠标的定义,需要根据键盘协议定义)

#endif // __USB_HID_H__

5. 鼠标模拟模块 (mouse_emulation.c 和 mouse_emulation.h)

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
// mouse_emulation.h
#ifndef __MOUSE_EMULATION_H__
#define __MOUSE_EMULATION_H__

#include "main.h"

// 初始化鼠标模拟模块
void Mouse_Init(void);

// 模拟鼠标操作
void Mouse_Emulate(MouseEvent_t event, int16_t x, int16_t y, int8_t buttons, int8_t scroll);

#endif // __MOUSE_EMULATION_H__


// mouse_emulation.c
#include "mouse_emulation.h"
#include "usb_device.h"

void Mouse_Init(void)
{
// 初始化鼠标模拟模块相关资源,例如状态变量等
// ...
}

void Mouse_Emulate(MouseEvent_t event, int16_t x, int16_t y, int8_t buttons, int8_t scroll)
{
// 根据鼠标事件类型,生成鼠标报告数据,并调用 USB_Device_SendMouseReport 发送
if (event == MOUSE_EVENT_MOVE) {
USB_Device_SendMouseReport(x, y, buttons, scroll);
} else if (event == MOUSE_EVENT_BUTTON_PRESS) {
USB_Device_SendMouseReport(0, 0, buttons, 0); // 发送按钮按下事件,坐标偏移为 0
} else if (event == MOUSE_EVENT_BUTTON_RELEASE) {
USB_Device_SendMouseReport(0, 0, buttons, 0); // 发送按钮释放事件,坐标偏移为 0
} else if (event == MOUSE_EVENT_SCROLL) {
USB_Device_SendMouseReport(0, 0, buttons, scroll); // 发送滚轮事件,坐标偏移为 0
}
}

6. 键盘模拟模块 (keyboard_emulation.c 和 keyboard_emulation.h)

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
// keyboard_emulation.h
#ifndef __KEYBOARD_EMULATION_H__
#define __KEYBOARD_EMULATION_H__

#include "main.h"

// 初始化键盘模拟模块
void Keyboard_Init(void);

// 模拟键盘操作
void Keyboard_Emulate(KeyboardEvent_t event, uint8_t keycode);

#endif // __KEYBOARD_EMULATION_H__


// keyboard_emulation.c
#include "keyboard_emulation.h"
#include "usb_device.h"

void Keyboard_Init(void)
{
// 初始化键盘模拟模块相关资源,例如状态变量等
// ...
}

void Keyboard_Emulate(KeyboardEvent_t event, uint8_t keycode)
{
// 根据键盘事件类型,生成键盘报告数据,并调用 USB_Device_SendKeyboardReport 发送
if (event == KEYBOARD_EVENT_PRESS) {
uint8_t keycode_array[] = {keycode};
USB_Device_SendKeyboardReport(keycode_array, 1); // 发送按键按下事件
} else if (event == KEYBOARD_EVENT_RELEASE) {
uint8_t keycode_array[] = {0}; // 发送空数组表示释放所有按键
USB_Device_SendKeyboardReport(keycode_array, 0); // 发送按键释放事件
}
}

7. 自动化引擎模块 (automation_engine.c 和 automation_engine.h)

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// automation_engine.h
#ifndef __AUTOMATION_ENGINE_H__
#define __AUTOMATION_ENGINE_H__

#include "main.h"

// 初始化自动化引擎模块
void Automation_Init(void);

// 启动自动化
void Automation_Start(void);

// 停止自动化
void Automation_Stop(void);

// 自动化引擎处理函数,在主循环中调用
void Automation_Process(void);

#endif // __AUTOMATION_ENGINE_H__


// automation_engine.c
#include "automation_engine.h"
#include "keyboard_emulation.h"
#include "mouse_emulation.h"
#include "timer.h" // 假设有定时器模块

// 自动化状态
typedef enum {
AUTOMATION_STATE_IDLE,
AUTOMATION_STATE_RUNNING,
AUTOMATION_STATE_PAUSED
} AutomationState_t;

AutomationState_t automationState = AUTOMATION_STATE_IDLE;

// 自动化脚本 (示例,可以根据实际需求扩展)
typedef struct {
uint32_t delay_ms; // 延迟时间 (毫秒)
uint8_t action_type; // 动作类型 (例如,按键、鼠标移动、鼠标点击)
uint8_t keycode; // 按键码 (如果动作类型是按键)
int16_t mouse_x; // 鼠标 X 坐标偏移 (如果动作类型是鼠标移动)
int16_t mouse_y; // 鼠标 Y 坐标偏移 (如果动作类型是鼠标移动)
uint8_t mouse_buttons; // 鼠标按键状态 (如果动作类型是鼠标点击)
} AutomationAction_t;

AutomationAction_t automationScript[] = {
{1000, 1, KEYCODE_A, 0, 0, 0}, // 延迟 1 秒,按下 'A' 键
{500, 1, KEYCODE_B, 0, 0, 0}, // 延迟 0.5 秒,按下 'B' 键
{500, 1, KEYCODE_C, 0, 0, 0}, // 延迟 0.5 秒,按下 'C' 键
{1000, 0, 0, 10, 0, 0}, // 延迟 1 秒,鼠标向右移动 10 像素
{500, 2, 0, 0, 0, 1}, // 延迟 0.5 秒,鼠标左键单击
{0, 0, 0, 0, 0, 0} // 脚本结束标记 (延迟 0 表示结束)
};

uint32_t automationScriptIndex = 0;
uint32_t automationTimerHandle = 0; // 定时器句柄

void Automation_Init(void)
{
automationState = AUTOMATION_STATE_IDLE;
automationScriptIndex = 0;
// 初始化定时器模块
// Timer_Init();
}

void Automation_Start(void)
{
if (automationState == AUTOMATION_STATE_IDLE) {
automationState = AUTOMATION_STATE_RUNNING;
automationScriptIndex = 0;
Automation_Process(); // 立即执行第一个动作
}
}

void Automation_Stop(void)
{
automationState = AUTOMATION_STATE_IDLE;
// 停止定时器 (如果正在运行)
// Timer_Stop(automationTimerHandle);
}

void Automation_Process(void)
{
if (automationState != AUTOMATION_STATE_RUNNING) {
return;
}

AutomationAction_t *currentAction = &automationScript[automationScriptIndex];

if (currentAction->delay_ms == 0) {
// 脚本结束
Automation_Stop();
return;
}

if (currentAction->action_type == 1) { // 键盘动作
Keyboard_Emulate(KEYBOARD_EVENT_PRESS, currentAction->keycode);
Keyboard_Emulate(KEYBOARD_EVENT_RELEASE, currentAction->keycode); // 模拟按下后立即释放
} else if (currentAction->action_type == 0) { // 鼠标移动
Mouse_Emulate(MOUSE_EVENT_MOVE, currentAction->mouse_x, currentAction->mouse_y, 0, 0);
} else if (currentAction->action_type == 2) { // 鼠标点击
Mouse_Emulate(MOUSE_EVENT_BUTTON_PRESS, 0, 0, currentAction->mouse_buttons, 0);
Mouse_Emulate(MOUSE_EVENT_BUTTON_RELEASE, 0, 0, 0, 0); // 模拟按下后立即释放
}

// 启动定时器,等待延迟时间后执行下一个动作
// automationTimerHandle = Timer_Start(currentAction->delay_ms, Automation_NextActionCallback); // 假设有 Timer_Start 和 Timer_Stop 函数
// 这里为了简化,假设定时器回调函数直接调用 Automation_Process,实际应用中需要更完善的定时器管理机制
// 简化的轮询方式实现延迟 (不推荐在实际项目中使用,会阻塞主循环)
uint32_t startTime = System_GetTick(); // 假设有获取系统 Tick 的函数
while (System_GetTick() - startTime < currentAction->delay_ms) {
// 阻塞等待,不推荐
}
Automation_NextActionCallback(); // 延迟结束后执行下一个动作
}

// 定时器回调函数 (简化版本,实际应用中需要更完善的定时器管理)
void Automation_NextActionCallback(void)
{
automationScriptIndex++;
Automation_Process(); // 执行下一个动作
}

8. 按键处理模块 (button.c 和 button.h)

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// button.h
#ifndef __BUTTON_H__
#define __BUTTON_H__

#include "main.h"

// 初始化按键
void Button_Init(void);

// 处理按键事件,返回按键事件类型
ButtonEvent_t Button_Process(void);

#endif // __BUTTON_H__


// button.c
#include "button.h"
#include "delay.h" // 假设有延时函数

#define BUTTON_PIN // 定义按键 GPIO 引脚
#define BUTTON_PRESSED_LEVEL 0 // 按键按下时的电平 (低电平或高电平)
#define BUTTON_DEBOUNCE_TIME_MS 50 // 按键消抖时间 (毫秒)
#define BUTTON_LONG_PRESS_TIME_MS 1000 // 长按时间 (毫秒)

bool buttonPressed = false;
uint32_t buttonPressStartTime = 0;

void Button_Init(void)
{
// 初始化按键 GPIO 引脚为输入模式,并使能上拉或下拉电阻 (根据硬件设计)
// GPIO_InitTypeDef GPIO_InitStruct = {0};
// GPIO_InitStruct.Pin = BUTTON_PIN;
// GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
// GPIO_InitStruct.Pull = GPIO_PULLUP; // 或 GPIO_PULLDOWN
// HAL_GPIO_Init(BUTTON_GPIO_PORT, &GPIO_InitStruct);
}

ButtonEvent_t Button_Process(void)
{
ButtonEvent_t event = BUTTON_EVENT_NONE;
uint8_t buttonState = HAL_GPIO_ReadPin(BUTTON_GPIO_PORT, BUTTON_PIN); // 读取按键引脚电平

if (buttonState == BUTTON_PRESSED_LEVEL) { // 按键按下
if (!buttonPressed) { // 第一次检测到按下
buttonPressed = true;
Delay_ms(BUTTON_DEBOUNCE_TIME_MS); // 消抖延时
if (HAL_GPIO_ReadPin(BUTTON_GPIO_PORT, BUTTON_PIN) == BUTTON_PRESSED_LEVEL) { // 再次确认按下
buttonPressStartTime = System_GetTick(); // 记录按下时间
} else {
buttonPressed = false; // 消抖期间释放,忽略
}
} else { // 按键持续按下
if (System_GetTick() - buttonPressStartTime >= BUTTON_LONG_PRESS_TIME_MS) { // 长按判断
event = BUTTON_EVENT_LONG_PRESS;
buttonPressed = false; // 避免重复触发长按
}
}
} else { // 按键释放
if (buttonPressed) { // 之前是按下状态
buttonPressed = false;
if (System_GetTick() - buttonPressStartTime < BUTTON_LONG_PRESS_TIME_MS) { // 短按判断
event = BUTTON_EVENT_SHORT_PRESS;
}
}
}
return event;
}

9. LED 状态显示模块 (led.c 和 led.h)

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// led.h
#ifndef __LED_H__
#define __LED_H__

#include "main.h"

// 初始化 LED
void LED_Init(void);

// 设置 LED 状态,根据设备状态显示不同颜色或闪烁模式
void LED_SetState(DeviceState_t state);

#endif // __LED_H__


// led.c
#include "led.h"

#define LED_PIN // 定义 LED GPIO 引脚
#define LED_ON_LEVEL 1 // LED 亮起时的电平 (高电平或低电平)
#define LED_OFF_LEVEL 0 // LED 熄灭时的电平

void LED_Init(void)
{
// 初始化 LED GPIO 引脚为输出模式,并默认熄灭
// GPIO_InitTypeDef GPIO_InitStruct = {0};
// GPIO_InitStruct.Pin = LED_PIN;
// GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
// GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
// HAL_GPIO_Init(LED_GPIO_PORT, &GPIO_InitStruct);
// HAL_GPIO_WritePin(LED_GPIO_PORT, LED_PIN, LED_OFF_LEVEL); // 默认熄灭
}

void LED_SetState(DeviceState_t state)
{
if (state == DEVICE_STATE_IDLE) {
// 空闲状态,LED 慢闪
// ... (使用定时器控制 LED 闪烁)
HAL_GPIO_WritePin(LED_GPIO_PORT, LED_PIN, LED_OFF_LEVEL); // 示例: 熄灭
} else if (state == DEVICE_STATE_MOUSE_MODE) {
// 鼠标模式,LED 常亮绿色 (假设有 RGB LED)
// ...
HAL_GPIO_WritePin(LED_GPIO_PORT, LED_PIN, LED_ON_LEVEL); // 示例: 常亮
} else if (state == DEVICE_STATE_KEYBOARD_MODE) {
// 键盘模式,LED 常亮蓝色 (假设有 RGB LED)
// ...
HAL_GPIO_WritePin(LED_GPIO_PORT, LED_PIN, LED_ON_LEVEL); // 示例: 常亮
} else if (state == DEVICE_STATE_AUTOMATION_MODE) {
// 自动化模式,LED 快闪红色 (假设有 RGB LED)
// ...
// ... (使用定时器控制 LED 闪烁)
HAL_GPIO_WritePin(LED_GPIO_PORT, LED_PIN, LED_ON_LEVEL); // 示例: 快闪
} else if (state == DEVICE_STATE_ERROR) {
// 错误状态,LED 快速闪烁红色
// ... (快速闪烁红色 LED)
}
}

9. 配置管理模块 (configuration.c 和 configuration.h) - 简化版本,实际应用中可以更复杂

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
// configuration.h
#ifndef __CONFIGURATION_H__
#define __CONFIGURATION_H__

#include "main.h"

// 加载配置
void Configuration_Load(void);

// 保存配置
void Configuration_Save(void);

#endif // __CONFIGURATION_H__


// configuration.c
#include "configuration.h"

// 配置参数结构体 (示例,可以根据实际需求扩展)
typedef struct {
uint8_t default_mode; // 默认工作模式
// ... 其他配置参数
} Configuration_t;

Configuration_t currentConfiguration;

void Configuration_Load(void)
{
// 从 Flash 或 EEPROM 加载配置参数
// ...
// 示例: 默认配置
currentConfiguration.default_mode = DEVICE_STATE_MOUSE_MODE;
}

void Configuration_Save(void)
{
// 将配置参数保存到 Flash 或 EEPROM
// ...
}

项目中采用的各种技术和方法

  • 模块化设计: 将系统分解为独立的模块,例如USB驱动、鼠标模拟、键盘模拟、自动化引擎、配置管理等,提高代码的可维护性和可扩展性。
  • 分层架构: 采用硬件层、驱动层、核心层、应用层分层架构,清晰地组织代码,降低层与层之间的耦合度。
  • 事件驱动: 系统基于事件响应机制,提高系统的实时性和效率。
  • 状态机: 使用状态机管理设备的不同工作模式,简化控制逻辑。
  • USB HID 协议: 使用标准的USB HID协议,确保设备能够被各种操作系统识别为鼠标和键盘设备,实现免驱动即插即用。
  • 硬件抽象层 (HAL): 使用HAL层隔离硬件差异,提高代码的移植性。
  • 固件在线升级 (DFU 或 Bootloader): 预留固件升级机制,方便后期维护和功能升级。
  • 按键消抖: 软件层面实现按键消抖,避免按键误触发。
  • LED 状态指示: 通过LED指示灯显示设备状态,提供用户反馈。
  • 配置管理: 实现配置参数的加载和保存功能,方便用户自定义设备行为。
  • 定时器: 使用定时器实现自动化脚本的延迟控制和LED闪烁等功能。
  • C 语言编程: 采用C语言进行嵌入式软件开发,C语言效率高、可移植性好,是嵌入式领域常用的编程语言。

实践验证

在项目开发过程中,每个技术和方法都需要经过实践验证,确保其可靠性和有效性。

  • USB 驱动验证: 使用USB分析仪抓包分析USB通信数据,验证USB描述符和HID报告的正确性,确保设备能够被主机正确识别和配置。
  • 鼠标键盘模拟验证: 在不同的操作系统和应用软件下测试鼠标和键盘模拟功能,验证其兼容性和响应速度。
  • 自动化引擎验证: 编写各种自动化脚本,测试自动化引擎的执行效果和稳定性,验证脚本语法的灵活性和易用性。
  • 按键和LED测试: 进行充分的按键和LED功能测试,确保按键响应灵敏可靠,LED状态显示准确。
  • 性能测试: 测试系统的响应延迟、资源占用等性能指标,确保满足项目性能需求。
  • 稳定性测试: 进行长时间运行测试,模拟各种异常情况,验证系统的稳定性和可靠性。

维护升级

为了方便后期的维护和升级,需要考虑以下方面:

  • 代码注释: 编写清晰详细的代码注释,方便他人理解和维护代码。
  • 版本控制: 使用版本控制系统 (例如 Git) 管理代码,方便代码的版本管理和协作开发。
  • 模块化设计: 模块化设计使得修改和升级某个模块时,不会影响其他模块,降低维护风险。
  • 固件升级机制: 实现固件在线升级功能,方便远程修复bug和添加新功能。
  • 文档编写: 编写详细的开发文档、用户手册和维护手册,方便用户和维护人员了解和使用设备。

总结

这个嵌入式USB鼠标键盘模拟器项目,从需求分析到系统实现,再到测试验证和维护升级,都体现了一个完整的嵌入式系统开发流程。通过采用模块化设计、分层架构、事件驱动和状态机等先进的设计方法,结合USB HID协议、HAL层、固件升级等实用技术,构建了一个可靠、高效、可扩展的嵌入式系统平台。 虽然这里提供的代码框架只是冰山一角,但希望能帮助你理解整个系统的设计思路和关键技术点。 实际开发过程中,你需要根据具体的硬件平台和USB库,逐步完善代码,并进行充分的测试和验证,才能最终实现一个高质量的嵌入式产品。

欢迎关注我的其它发布渠道