VSCode :枚举 USB 设备

本文讲述了如何通过USB端口进行设备操作,介绍了使用VID和PID识别设备的方法,以及如何通过枚举设备和硬件ID来确保跨平台兼容性。作者还提供了一个C++示例代码,展示了如何在Windows环境中检测和操作特定VID/PID的USB设备。

从过去的历史走来,往USB端口读写设备相较于通过串口或并口都复杂了一个层次。

要开通USB端口(host主机那端),先枚举连接端口的可见设备,指定了相关的端口(连接了需要操作设备的那个端口),才能向它连接的设备作读写的操作。刚接触USB操作的时候真的是难倒了。

本文主要是描述第一个步骤(获取相关USB端口信息后,至于对它的读写操作,将在本文末端需要补充)。

枚举USB设备的方式其中一种是使用GUID,个人认为使用这种方式所开发的应用程序有个不方便的地方,它是定制了host的环境情况,一旦目标设备改插到另一个host上或host的系统有变更,GUID将有所改变,导致应用程序对相关目标设备无法识别。

本文例子是使用VID和PID两个信息来识别设备。VID和PID这两个信息容易获取,例如用 bus hound 可以读取插在host上的设备,再读取该设备的这个信息。另外,设备买回来的时候,也可以向厂家处取得。

当然写应用程序的时候,可以使用较普及的设计,就是显示在host上所有可见的USB设备,然后再选取目标设备,(这种做法比较适合同类型设备但VIP&PID较有可能变动的情况)。如果将VIP&PID 固化在程序里面,一旦VIP&PID改变了,就要变更应用程序才能继续操作。

例程如下:

/**
 * @file main.cpp
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * 
 * @copyright Copyright (c) 2023
 * 
 */
#include <Windows.h>
#include <SetupAPI.h>
#include <stdio.h>

// 定义要枚举目标设备的 VID and PID
#define MY_VID 0x1234    //这里我随便举例
#define MY_PID 0x5678

int main()
{
    // Define variables
    HDEVINFO hDevInfo;
    SP_DEVINFO_DATA devInfoData;
    DWORD index;
    DWORD dataType;
    DWORD dataSize;
    BYTE propertyBuffer[1024];
    DWORD requiredSize;
    HANDLE hDevice;
    BOOL result;
    DWORD bytesReturned;

    // Initialize the device information set
    hDevInfo = SetupDiGetClassDevs(NULL, "USB", NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT);
    if (hDevInfo == INVALID_HANDLE_VALUE) {
        printf("Failed to get device information set. Error code: %d\n", GetLastError());
        return 1;
    }

    // 枚举插在host的各可见设备
    devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
    index = 0;
    while (SetupDiEnumDeviceInfo(hDevInfo, index, &devInfoData)) {
        // Get the device instance ID
        result = SetupDiGetDeviceInstanceId(hDevInfo, &devInfoData, propertyBuffer, sizeof(propertyBuffer), &requiredSize);
        if (!result) {
            printf("Failed to get device instance ID. Error code: %d\n", GetLastError());
            SetupDiDestroyDeviceInfoList(hDevInfo);
            return 1;
        }

        // 检查相关设备是否匹配指定的 VID and PID
        result = SetupDiGetDeviceRegistryProperty(hDevInfo, &devInfoData, SPDRP_HARDWAREID, &dataType, propertyBuffer, sizeof(propertyBuffer), &requiredSize);
        if (result && (dataType == REG_MULTI_SZ)) {
            char* ptr = (char*)propertyBuffer;
            while (*ptr) {
                if (sscanf(ptr, "USB\\VID_%x&PID_%x", &dataType, &dataSize) == 2) {
                    if ((dataType == MY_VID) && (dataSize == MY_PID)) {
                        // 如果匹配VID&PID成功,就创建句柄以便读写该设备
                        hDevice = CreateFileA((LPCSTR)propertyBuffer, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
                        if (hDevice == INVALID_HANDLE_VALUE) {
                            printf("打开设备失败. Error code: %d\n", GetLastError());
                            SetupDiDestroyDeviceInfoList(hDevInfo);
                            return 1;
                        }

                        // Send a request to the device
                        result = DeviceIoControl(hDevice, /* ... */);
                        if (!result) {
                            printf("向目标设备发生请求失败. Error code: %d\n", GetLastError());
                            CloseHandle(hDevice);
                            SetupDiDestroyDeviceInfoList(hDevInfo);
                            return 1;
                        }

                        // Close the device handle
                        CloseHandle(hDevice);
                    }
                }
                ptr += strlen(ptr) + 1;
            }
        }

        index++;
    }

    // Clean up the device information set
    SetupDiDestroyDeviceInfoList(hDevInfo);

    return 0;
}

补充一点,虽然环境变量不一定很重要,但我使用的tasks.json是:

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "build",
            "type": "shell",
            "command": "\\编译器的路径\\g++.exe",
            "args": [
                "${fileDirname}\\*.cpp",
                "-o",
                "${fileBasenameNoExtension}.exe",
                "-std=c++11",
                "-g"
            ],
            "presentation": {
                "echo": true,
                "reveal": "always",
                "panel": "new"
            },
            "problemMatcher": "$msCompile",
            "group": "build"
        },
        {
            "label": "run",
            "type": "shell",
            "dependsOn": "build",
            "command": "${fileDirname}\\${fileBasenameNoExtension}.exe",
            "group": "build",
            "presentation": {
                "echo": true,
                "reveal": "always",
                "focus": true,
                "panel": "new"
            }
        },
        {
            "type": "cppbuild",
            "label": "C/C++: g++.exe build active file",
            "command": "\\编译器的路径\\g++.exe",
            "args": [
                "-fdiagnostics-color=always",
                "-std=c++11",
                "-g",
                "${fileDirname}\\*.cpp",
                "-o",
                "${fileDirname}\\${fileBasenameNoExtension}.exe"
            ],
            "options": {
                "cwd": "\\放编译器的文件夹路径\\bin"
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "detail": "Task generated by Debugger."
        }
    ]
}

【本文完】

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值