我们通常使用的鼠标发送的都是相对坐标,比如:输出X=100,Y=200, 意思是通知系统将鼠标从目前的位置移动(100,200)个单位。此外,Windows 还有一个鼠标优化机制,比如,你的鼠标输出范围是 +/- 127, 如果你的屏幕宽度是 1920, 经过优化之后无需滑动 1920/127 次才能将鼠标指针从最左边移动到最右边。 这次带来的PVD(Physical Virtual Device)是一款绝对值鼠标,它模拟了一个绝对值鼠标,能够将从USB串口输入的数据转化为绝对值鼠标数据发送出来。这样,可以方便的实现鼠标点击屏幕上的任意位置。 硬件部分使用和上一次设计相同【参考1】,核心是 WCH出品的 CH554e单片机。 代码是Arduino 来实现的,屏幕左上角是(0,0), 输出坐标为 0-32767 。实践表明:送出来的数据和屏幕分辨率存在定比例关系。比如,在1920*1024 屏幕上,如果想让鼠标移动到(300, 200) 的位置,那么需要设备输出(300/1920*32768, 200/1024 *32768) 这样的数据。 主程序代码如下:
- #ifndef USER_USB_RAM
- #error "This example needs to be compiled with a USER USB setting"
- #endif
-
- #include <WS2812.h>
- #include "src/CdcHidCombo/USBCDC.h"
- #include "src/CdcHidCombo/USBHIDKeyboardMouse.h"
-
- #define NUM_LEDS 1
- #define COLOR_PER_LEDS 3
- #define NUM_BYTES (NUM_LEDS*COLOR_PER_LEDS)
-
- __xdata uint8_t ledData[NUM_BYTES];
-
- #define KeyboardReportID 0x01
- #define rMouseReportID 0x02
- #define aMouseReportID 0x03
- #define OnBoardLED 0x04
-
- // Data format
- // Keyboard(Total 9 bytes): 01(ReportID 01) + Keyboard data (8 Bytes)
- // Mouse(Total 5 bytes): 02(ReportID 02) + Mouse Data (4 Bytes)
- uint8_t recvStr[9];
- uint8_t recvStrPtr = 0;
- unsigned long Elsp;
-
- void setup() {
- USBInit();
- Serial0_begin(115200);
- delay(1000);
- Serial0_print("start");
- Elsp=0;
- }
-
- void loop() {
- while (USBSerial_available()) {
- uint8_t serialChar = USBSerial_read();
- recvStr[recvStrPtr++] = serialChar;
- if (recvStrPtr == 10) {
- for (uint8_t i = 0; i < 9; i++) {
- Serial0_write(recvStr[i]);
- }
- if (recvStr[0] == KeyboardReportID) { // Keyboard
- USB_EP3_send(recvStr, 9);
- }
- if (recvStr[0] == rMouseReportID) {
- USB_EP3_send(recvStr, 5); // Relative Mouse
- }
- if (recvStr[0] == aMouseReportID) {
- USB_EP3_send(recvStr, 7); // Absolute Mouse
- }
- if (recvStr[0] == OnBoardLED) {
- set_pixel_for_GRB_LED(ledData, 0, recvStr[1], recvStr[2], recvStr[3]);
- neopixel_show_P1_5(ledData, NUM_BYTES);
- }
-
-
-
- recvStrPtr = 0;
- }
- Elsp=millis();
- }
- // If there is no data in 100ms, clear the receive buffer
- if (millis()-Elsp>100) {
- recvStrPtr = 0;
- Elsp=millis();
- }
- }
复制代码
如果USB CDC 收到以 aMouseReportID起始的9个字节,那么会将数据直接转到绝对值鼠标端点然后发送给主机。 VS2019 编写一个上位机程序进行测试,代码如下: - // CDC_vKBMSTest.cpp : This file contains the 'main' function. Program execution begins and ends there.
- //
-
- #include <windows.h>
- #include <SetupAPI.h>
- #include <tchar.h>
- #include <iostream>
- #include <cstring>
- #include <atlstr.h>
-
- #pragma comment(lib, "Setupapi.lib")
-
- #define MY_USB_PID_VID _T("VID_1209&PID_C55C")
- #define SCREENWIDTH 1920
- #define SCREENHEIGHT 1200
- int Port;
-
- class ComPortException : public std::exception {
- public:
- ComPortException(DWORD errorCode) : errorCode(errorCode) {}
-
- DWORD getErrorCode() const {
- return errorCode;
- }
-
- private:
- DWORD errorCode;
- };
-
- // 对串口portName 发送数据
- // 无法打开串口返回 1
- // 无法设置串口参数 2
- void SendToComPort(const int port, const char* data) {
- int Result = 0;
- HANDLE hCom = INVALID_HANDLE_VALUE;
- TCHAR portName[10]; // 用于存储 "COMp" 字符串
-
- // 将整数 p 转换为 "COMp" 字符串
- #ifdef UNICODE
- swprintf(portName, 10, _T("\\\\.\\COM%d"), port);
- #else
- sprintf(portName, "COM%d", port);
- #endif
- try {
- // 打开串口
- hCom = CreateFile(portName,
- GENERIC_READ | GENERIC_WRITE,
- 0,
- NULL,
- OPEN_EXISTING,
- 0,
- NULL);
-
- if (hCom == INVALID_HANDLE_VALUE) {
- throw ComPortException(GetLastError());
- }
-
- // 设置串口参数
- DCB dcb;
- SecureZeroMemory(&dcb, sizeof(DCB));
- dcb.DCBlength = sizeof(DCB);
-
- if (!GetCommState(hCom, &dcb)) {
- throw ComPortException(GetLastError());
- }
-
- dcb.BaudRate = CBR_115200; // 波特率
- dcb.ByteSize = 8; // 数据位
- dcb.StopBits = ONESTOPBIT; // 停止位
- dcb.Parity = NOPARITY; // 校验位
-
- if (!SetCommState(hCom, &dcb)) {
- throw ComPortException(GetLastError());
- }
-
- // 设置超时参数
- COMMTIMEOUTS timeouts;
- timeouts.ReadIntervalTimeout = 50;
- timeouts.ReadTotalTimeoutConstant = 50;
- timeouts.ReadTotalTimeoutMultiplier = 10;
- timeouts.WriteTotalTimeoutConstant = 50;
- timeouts.WriteTotalTimeoutMultiplier = 10;
-
- if (!SetCommTimeouts(hCom, &timeouts)) {
- throw ComPortException(GetLastError());
- }
-
- // 发送数据
- DWORD bytesWritten;
- if (!WriteFile(hCom, data, 10, &bytesWritten, NULL)) {
- std::cerr << "Failed to write to COM port." << std::endl;
- }
- else {
- std::cout << "Successfully sent data to COM port: [" << port << "]" << std::endl;
- }
-
- for (int i = 0; i < 10; i++) {
- printf("%02x ", data[i]&0xFF);
- }
- printf("\n");
- // 关闭串口
- if (hCom != INVALID_HANDLE_VALUE) {
- CloseHandle(hCom);
- }
- }
- catch (const ComPortException& ex) {
- std::cerr << "Error: " << ex.getErrorCode() << std::endl;
- if (hCom != INVALID_HANDLE_VALUE) {
- CloseHandle(hCom);
- }
- }
-
- }
-
- /************************************************************************/
- /* 根据USB描述信息字符串中读取
- /************************************************************************/
- int MTGetPortFromVidPid(CString strVidPid)
- {
- // 获取当前系统所有使用的设备
- int nPort = -1;
- int nStart = -1;
- int nEnd = -1;
- int i = 0;
- CString strTemp, strName;
- DWORD dwFlag = (DIGCF_ALLCLASSES | DIGCF_PRESENT);
- HDEVINFO hDevInfo = INVALID_HANDLE_VALUE;
- SP_DEVINFO_DATA sDevInfoData;
- TCHAR szDis[2048] = { 0x00 };// 存储设备实例ID
- TCHAR szFN[MAX_PATH] = { 0x00 };// 存储设备实例属性
- DWORD nSize = 0;
-
- // 准备遍历所有设备查找USB
- hDevInfo = SetupDiGetClassDevs(NULL, L"USB", NULL, dwFlag);
- if (INVALID_HANDLE_VALUE == hDevInfo)
- goto STEP_END;
-
- // 开始遍历所有设备
- memset(&sDevInfoData, 0x00, sizeof(SP_DEVICE_INTERFACE_DATA));
- sDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
- for (i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &sDevInfoData); i++)
- {
- nSize = 0;
-
- // 无效设备
- if (!SetupDiGetDeviceInstanceId(hDevInfo, &sDevInfoData, szDis, sizeof(szDis), &nSize))
- goto STEP_END;
-
- // 根据设备信息寻找VID PID一致的设备
- strTemp.Format(_T("%s"), szDis);
- strTemp.MakeUpper();
- if (strTemp.Find(strVidPid, 0) == -1)
- continue;
-
- // 查找设备属性
- nSize = 0;
- SetupDiGetDeviceRegistryProperty(hDevInfo, &sDevInfoData,
- SPDRP_FRIENDLYNAME,
- 0, (PBYTE)szFN,
- sizeof(szFN),
- &nSize);
-
- // "XXX Virtual Com Port (COM7)"
- strName.Format(_T("%s"), szFN);
- _tprintf(_T("%s"), szFN);
- if (strName.IsEmpty())
- //goto STEP_END;
- continue;
-
- // 寻找串口信息
- nStart = strName.Find(_T("(COM"), 0);
- nEnd = strName.Find(_T(")"), 0);
- if (nStart == -1 || nEnd == -1)
- //goto STEP_END;
- continue;
-
- strTemp = strName.Mid(nStart + 4, nEnd - nStart - 4);
- nPort = _ttoi(strTemp);
-
- }
- STEP_END:
-
- // 关闭设备信息集句柄
- if (hDevInfo != INVALID_HANDLE_VALUE)
- {
- SetupDiDestroyDeviceInfoList(hDevInfo);
- hDevInfo = INVALID_HANDLE_VALUE;
- }
-
- return nPort;
- }
-
- int ScreenX2Abs(int X) {
- return (X* 32768 / SCREENWIDTH);
- }
- int ScreenY2Abs(int Y) {
- return (Y * 32768 / SCREENHEIGHT);
- }
-
- void ChangeColor(
- int X,
- int Y
- ) {
- char Data[10];
-
- memset(Data, 0, sizeof(Data));
- Data[0] = 3; // 绝对值鼠标
- Data[1] = 1; // 点击
- Data[2] = ScreenX2Abs(X) & 0xFF;
- Data[3] = (ScreenX2Abs(X) >> 8) & 0xFF;
- Data[4] = ScreenY2Abs(Y) & 0xFF;
- Data[5] = (ScreenY2Abs(Y) >> 8) & 0xFF;
- SendToComPort(Port, (char*)&Data);
- Sleep(100);
- memset(Data, 0, sizeof(Data));
- Data[0] = 3; // 绝对值鼠标
- Data[1] = 0; // 点击
- Data[2] = ScreenX2Abs(X) & 0xFF;
- Data[3] = (ScreenX2Abs(X) >> 8) & 0xFF;
- Data[4] = ScreenY2Abs(Y) & 0xFF;
- Data[5] = (ScreenY2Abs(Y) >> 8) & 0xFF;
- SendToComPort(Port, (char*)&Data);
- Sleep(100);
- }
-
- void LedColor(
- int Color
- ) {
- char Data[10];
-
- memset(Data, 0, sizeof(Data));
- Data[0] = 4; // LED
- Data[1] = Color&0xFF; // 点击
- Data[2] = (Color>>8) & 0xFFF;
- Data[3] = (Color >>16) & 0xFF;
-
- SendToComPort(Port, (char*)&Data);
- }
-
- void DrawRect(
- int StartX,
- int StartY,
- int Width,
- int Height,
- int Dx,
- int Dy
- ) {
- char Data[10];
-
- //从左到右横线
- //line(StartX, StartY, StartX + Width, StartY);
- memset(Data, 0, sizeof(Data));
- Data[0] = 3; // 绝对值鼠标
- Data[1] = 1; // 点击
- Data[2] = ScreenX2Abs(StartX) & 0xFF;
- Data[3] = (ScreenX2Abs(StartX) >> 8) & 0xFF;
- Data[4] = ScreenY2Abs(StartY) & 0xFF;
- Data[5] = (ScreenY2Abs(StartY) >> 8) & 0xFF;
- SendToComPort(Port, (char*)&Data);
- Sleep(100);
-
- Data[0] = 3; // 绝对值鼠标
- Data[1] = 1; // 点击
- Data[2] = ScreenX2Abs(StartX+Width) & 0xFF;
- Data[3] = (ScreenX2Abs(StartX+Width) >> 8) & 0xFF;
- Data[4] = ScreenY2Abs(StartY) & 0xFF;
- Data[5] = (ScreenY2Abs(StartY) >> 8) & 0xFF;
- SendToComPort(Port, (char*)&Data);
- Sleep(100);
-
- Data[0] = 3; // 绝对值鼠标
- Data[1] = 0; // 抬起
- Data[2] = ScreenX2Abs(StartX + Width) & 0xFF;
- Data[3] = (ScreenX2Abs(StartX + Width) >> 8) & 0xFF;
- Data[4] = ScreenY2Abs(StartY) & 0xFF;
- Data[5] = (ScreenY2Abs(StartY) >> 8) & 0xFF;
- SendToComPort(Port, (char*)&Data);
- Sleep(100);
-
- //从上到下,竖线
- //line(StartX + Width, StartY,StartX+Width,StartY+Height);
- memset(Data, 0, sizeof(Data));
- Data[0] = 3; // 绝对值鼠标
- Data[1] = 1; // 点击
- Data[2] = ScreenX2Abs(StartX+Width) & 0xFF;
- Data[3] = (ScreenX2Abs(StartX + Width) >> 8) & 0xFF;
- Data[4] = ScreenY2Abs(StartY) & 0xFF;
- Data[5] = (ScreenY2Abs(StartY) >> 8) & 0xFF;
- SendToComPort(Port, (char*)&Data);
- Sleep(100);
-
- Data[0] = 3; // 绝对值鼠标
- Data[1] = 1; // 点击
- Data[2] = ScreenX2Abs(StartX + Width) & 0xFF;
- Data[3] = (ScreenX2Abs(StartX + Width) >> 8) & 0xFF;
- Data[4] = ScreenY2Abs(StartY+Height) & 0xFF;
- Data[5] = (ScreenY2Abs(StartY+Height) >> 8) & 0xFF;
- SendToComPort(Port, (char*)&Data);
- Sleep(100);
-
- Data[0] = 3; // 绝对值鼠标
- Data[1] = 0; // 抬起
- Data[2] = ScreenX2Abs(StartX + Width) & 0xFF;
- Data[3] = (ScreenX2Abs(StartX + Width) >> 8) & 0xFF;
- Data[4] = ScreenY2Abs(StartY + Height) & 0xFF;
- Data[5] = (ScreenY2Abs(StartY + Height) >> 8) & 0xFF;
- SendToComPort(Port, (char*)&Data);
- Sleep(100);
-
- //从右到左,横线
- //line(StartX + Width, StartY + Height,StartX+Dx,StartY+Height);
- memset(Data, 0, sizeof(Data));
- Data[0] = 3; // 绝对值鼠标
- Data[1] = 1; // 点击
- Data[2] = ScreenX2Abs(StartX + Width) & 0xFF;
- Data[3] = (ScreenX2Abs(StartX + Width) >> 8) & 0xFF;
- Data[4] = ScreenY2Abs(StartY+Height) & 0xFF;
- Data[5] = (ScreenY2Abs(StartY+Height) >> 8) & 0xFF;
- SendToComPort(Port, (char*)&Data);
- Sleep(100);
-
- Data[0] = 3; // 绝对值鼠标
- Data[1] = 1; // 点击
- Data[2] = ScreenX2Abs(StartX + Dx) & 0xFF;
- Data[3] = (ScreenX2Abs(StartX + Dx) >> 8) & 0xFF;
- Data[4] = ScreenY2Abs(StartY + Height) & 0xFF;
- Data[5] = (ScreenY2Abs(StartY + Height) >> 8) & 0xFF;
- SendToComPort(Port, (char*)&Data);
- Sleep(100);
-
- Data[0] = 3; // 绝对值鼠标
- Data[1] = 0; // 抬起
- Data[2] = ScreenX2Abs(StartX + Dx) & 0xFF;
- Data[3] = (ScreenX2Abs(StartX + Dx) >> 8) & 0xFF;
- Data[4] = ScreenY2Abs(StartY + Height) & 0xFF;
- Data[5] = (ScreenY2Abs(StartY + Height) >> 8) & 0xFF;
- SendToComPort(Port, (char*)&Data);
- Sleep(100);
-
- //从下到上竖线
- //line(StartX + Dx, StartY + Height, StartX + Dx, StartY + Dy);
- memset(Data, 0, sizeof(Data));
- Data[0] = 3; // 绝对值鼠标
- Data[1] = 1; // 点击
- Data[2] = ScreenX2Abs(StartX + Dx) & 0xFF;
- Data[3] = (ScreenX2Abs(StartX + Dx) >> 8) & 0xFF;
- Data[4] = ScreenY2Abs(StartY + Height) & 0xFF;
- Data[5] = (ScreenY2Abs(StartY + Height) >> 8) & 0xFF;
- SendToComPort(Port, (char*)&Data);
- Sleep(100);
-
- Data[0] = 3; // 绝对值鼠标
- Data[1] = 1; // 点击
- Data[2] = ScreenX2Abs(StartX + Dx) & 0xFF;
- Data[3] = (ScreenX2Abs(StartX + Dx) >> 8) & 0xFF;
- Data[4] = ScreenY2Abs(StartY + Dy) & 0xFF;
- Data[5] = (ScreenY2Abs(StartY + Dy) >> 8) & 0xFF;
- SendToComPort(Port, (char*)&Data);
- Sleep(100);
-
- Data[0] = 3; // 绝对值鼠标
- Data[1] = 0; // 抬起
- Data[2] = ScreenX2Abs(StartX + Dx) & 0xFF;
- Data[3] = (ScreenX2Abs(StartX + Dx) >> 8) & 0xFF;
- Data[4] = ScreenY2Abs(StartY + Dy) & 0xFF;
- Data[5] = (ScreenY2Abs(StartY + Dy) >> 8) & 0xFF;
- SendToComPort(Port, (char*)&Data);
- Sleep(100);
- }
-
- int main()
- {
-
- printf("%d[%x] %d[%x]\n", ScreenX2Abs(100), ScreenX2Abs(100), ScreenY2Abs(300), ScreenY2Abs(300));
- printf("Virutal KB MS Test\n");
- Port = MTGetPortFromVidPid(MY_USB_PID_VID);
- if (Port == -1) {
- printf("No device is found\n");
- goto EndProgram;
- }
- else {
- printf("Found COM%d\n",Port);
- }
- Sleep(5000);
- for (int i = 0; i < 8; i++) {
- DrawRect(100+20*i, 300+20*i, SCREENWIDTH - 200-20*i*2, SCREENHEIGHT - 500-20*i*2, 20, 20);
- if ((i % 3) == 0) {
- LedColor(0x01);
- }
- if ((i % 3) == 1) {
- LedColor(0x0100);
- }
- if ((i % 3) == 2) {
- LedColor(0x010000);
- }
- }
-
- ChangeColor(1329,125);
- for (int i = 8; i < 18; i++) {
- DrawRect(100 + 20 * i, 300 + 20 * i, SCREENWIDTH - 200 - 20 * i * 2, SCREENHEIGHT - 500 - 20 * i * 2, 20, 20);
- if ((i % 3) == 0) {
- LedColor(0x01);
- }
- if ((i % 3) == 1) {
- LedColor(0x0100);
- }
- if ((i % 3) == 2) {
- LedColor(0x010000);
- }
- }
-
-
- EndProgram:
- printf("End\n");
- getchar();
- }
复制代码
这个代码的作用是在画笔中绘制蛇形线,在绘制过程中还有更换颜色的动作。 工作的视频: 【PVD 计划:绝对值鼠标测试】
参考:
|