USB系列-LibUSB使用指南(1)-Windows下的报错与踩坑

2023-05-16

成就更好的自己

时隔一年再次开始撰写博客,这一年的时间经历了很多,现在终于稳定下来。以后很长一段时间都能够稳定的学习和更新。时间将会聚焦于USB和PCIe的开发进行,能和大家共同进步真的很高兴。

本篇为USB系列的LibUSB使用指南的第一篇。

USB系列主要围绕USB的知识、协议、开发总结、使用说明等进行。

LibUSB使用指南主要围绕LibUSB库的使用进行。


LibUSB中的描述符结构分析

LibUSB中的描述符结构主要分为一下几种层次:

设备描述符->配置描述符->接口描述符(备用接口描述符)->端点描述符

看到这篇博客的都应该知道上述描述符的包含关系和可能的存在数量,基础知识不再赘述,到时候会专门写对应的基础知识博客。

LibUSB中的描述符包含关系与结构如下图所示:

 

从图中可以看出是没有设备描述符libusb_device_descriptor的,因为设备描述符一般可以看做是以设备为对象的一种描述结构,因此每个设备只有一个而且地位特殊。

图中最高层是配置描述符libusb_config_descripto,使用libusb_get_config_descriptor函数传入索引可以获得该设备中的某一个配置描述符。配置描述符中有一项bNumInterfaces为接口描述符的个数,可通过该项获得在该配置描述符中接口描述符的个数。

在配置描述符中还有一项为libusb_interface *interface,该项就是接口描述符的结构体数组。这个接口描述符与我们广义上理解的接口描述符不同,这里的接口描述符是由备用接口描述符结构体数组和备用描述符个数组成的,也就是libusb_interface_descriptor *altsetting和num_altsetting这两项。

要注意的是bNumInterfaces所指是接口描述符数组的个数,num_altsetting所指的是每个接口描述符数组成员中的备用接口描述符的数量。因此实际的配置描述符是由备用接口描述符组成的一个二维数组,这个二维数组的主索引是bNumInterfaces,副索引是num_altsetting。

在备用接口描述符数组中的每个成员都携带着在该备用接口下所属的若干端点的描述符结构体。

整个上面的过程中只有这个备用接口的概念和广义理解上的不太一样,剩下各个描述符中的各个字段均与协议栈中我们所通识的一样。实际上,这些结构体的存在方式和使用LibUSB函数获取描述符的方式与USB设备的枚举过程和方式的有着本质上的关联(后期博客提到)。

下面就是以博主电脑上为例,输出能探测到设备的详细信息(太长了,部分截图):

Windows下的USB驱动问题

Windows下LibUSB产生的很多报错与驱动有着很大关系。

我先言简意骇的简要介绍一下在你的Windows中可能存在的各种USB驱动,我暂时驱动分为高级的功能性驱动和低级的设备性驱动:

高级的功能性驱动:比如插入MSD类设备、移动硬盘、蓝牙适配器、HID类设备、网卡等具有实际功能性意义的设备时,Windows底层会自动的识别该设备的类和功能,从而找到一款能够直接适配该设备功能的通用高级驱动,这样就实现了目前Windows下多数陌生设备的免驱。

低级的设备性驱动:当插入的设备极为特殊,Windows认不出来这个玩意是干嘛的(比如设备描述符和接口描述符中的类字段与子类字段全部为0xff,即厂家定义),这个时候仅仅只是能够进行设备的基础枚举操作和通信。此时能够支撑设备枚举和通信的驱动就是设备性驱动,比如libusb-win32或WinUSB等。

假如我现在插入一个U盘就开始调用LibUSB库进行Open操作,这样是一定会返回错误的,因为LibUSB本身不是驱动,实际上也是要调用驱动留出的系统调用接口进行数据和指令传递的(这里类比linux的特性,表述可能些许不合适)。当这个设备底层根本不是LibUSB需要的接口的时候,当然是无法正常调用。

此时需要将该设备的驱动换成合适的低级驱动(linux的优势就出来了),LibUSB库在Windows平台上是基于WinUSB底层驱动去开发的。因此最适合LibUSB开发的底层驱动当然是WinUSB。博主尝试过使用linusb-win32等其他的驱动,他们可以open设备但是在claim接口的时候会有掉设备的情况(一claim就听到Windows掉USB设备的声音,正常后不会这样),直接导致transfer的时候返回LIBUSB_ERROR_IO(-1)。因此,在进行Windows下的LibUSB开发之前先解决你的驱动问题。

驱动问题的解决方式是通过zadig工具进行驱动替换,首先插入你的设备,list一下获得设备名单,选择你的设备,选择winUSB驱动,replace即可。前提是要有网(公司的机器没有网,GG)。如图(博主使用的SMT32USBD虚拟为com):

注意,使用此种方式他只会根据选择设备的设备号进行定向更换,这样就产生了两个问题:第一,替换的效果只能适用同一VID和PID的设备。第二,被替换的设备从此以后只能使用这个低级驱动,原来高级驱动的功能将不能使用。若不小心将重要设备的驱动进行重置和降级,那就去zadig工具的官网上寻找答案吧(当时无知把移动硬盘驱动给降级了,人都裂开了)。

zadig官网与技术支持:

​​​​​​https://www.baidu.com/link?url=UuDz3G0f6UbanYtYLC8SIC6sDu-aTCuBlW1BkriYHqK&wd=&eqid=cd9600c2000f6899000000046358207b

Open_device过程

这一节咱们直接面向对象来说:

  1. libusb_open_device_with_vid_pid或者open_device:打开设备,得到一个libusb_device_handle,没啥说的,驱动没安好你就是开不了,气不气。
  2. libusb_get_device:由libusb_device_handle结构体反推libusb_device_descriptor结构体用的(open_device的不用)。
  3. libusb_get_device_descriptor:获取端点信息的第一步—获取设备描述符。
  4. libusb_get_config_descriptor:获取端点信息的第二步—获取配置描述符。、
  5. 使用上一步的信息,通过一系列if筛选,找到你想要建立通信的备用接口和备用接口中的端点地址。
  6. libusb_claim_interface和libusb_set_interface_alt_setting:声明接口和备用接口,传入参数为索引值,上一步得到的。驱动没安好你就是claim不了(ret=-5),气不气。
  7. libusb_clear_halt:清除端点和对应通道中的残存的数据,为下一次通信做好准备。
  8. 该free的free,该release的release。

到此为止,正常情况下你就应该得到一个已经open的设备和一些(>=1)准备传输的端点。注意:第六第七步的顺序千万不能倒过来,否则你的libusb_clear_halt会报错LIBUSB_ERROR_NOT_FOUND (ret=-5)。而且在Windows开发中,LibUSB中的detach_kernel系列的函数都是多余的,人家Windows的原理就不需要这些操作。

Transfer过程

博主使用的是libusb_bulk_transfer传输的,中间出了点小问题,简要说一下:

  1. libusb_bulk_transfer返回LIBUSB_ERROR_IO(-1)大概率是因为你的驱动没有装对,libusb0 (v2.6.0)的驱动能够open和claim不报错,但是就是在传输的时候报错。
  2. libusb_bulk_transfer返回LIBUSB_ERROR_NOT_FOUND (-5)大概率是因为你claim这一步没有正常claim。注意如果你的端点出自备用接口,记得claim备用接口(claim标准接口好像是默认claim该接口下索引为0的备用接口,保险起见不管备用接口索引是几,记得claim一下备用接口)。

贴一下程序(bulk)

main.c

#include <stdio.h>
#include <main.h>
#include <libusb.h>
#include "log.h"
#include <stdint.h>
#include "usb_device_opt.h"

const uint16_t VID = 0x1234;
const uint16_t PID = 0x1234;
//const uint16_t VID = 0x0bda;
//const uint16_t PID = 0x8179;
int main(void)
{
	libusb_device **devs;
	USB_MSD_ST msd = {NULL, NULL, 0x00, 0x00};
	uint8_t err = 0;
	int16_t dev_num;

    Log_Init(3);
	err = libusb_init(NULL);
	if(err > 0)
    {
        LOG_ERROR("libusb_init() err with %d", err);
        goto init_err;
    }
    //libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL , LIBUSB_LOG_LEVEL_INFO);

	dev_num = libusb_get_device_list(NULL, &devs);
	if(dev_num < 0)
    {
        LOG_ERROR("libusb_get_device_list() err with %d", err);
        goto getlist_err;
    }

	USB_Dev_Scan_A_Print(dev_num, devs);

    err = USB_MSD_Open(VID, PID, &msd);
    if(err > 0)
    {
        USB_MSD_Close(&msd);
        goto getlist_err;
    }

    uint8_t buff[512];
    int size = 0;
    memset(buff, 9, 512);
    err = USB_MSD_Bulk_Write(&msd, buff, 64, &size, 1000);
    if(err > 0)
    {
        USB_MSD_Close(&msd);
        goto getlist_err;
    }
    else
    {
        LOG_INFO("Send OK!");
    }
    err = USB_MSD_Close(&msd);

getlist_err:
	libusb_free_device_list(devs, 1);
init_err:
	libusb_exit(NULL);
	return 0;
}

 usb_device_opt.c

#include <stdio.h>
#include <libusb.h>
#include "log.h"
#include <stdint.h>
#include "usb_device_opt.h"

uint8_t USB_Dev_Scan_A_Print(int16_t dev_num, libusb_device **devs)
{
    struct libusb_device_descriptor usb_dev_desc;
    struct libusb_config_descriptor *usb_cfg_desc;
    uint8_t err = 0;


    LOG_INFO("Dev_Num:%d", dev_num);
    for(uint16_t i = 0;i < dev_num; i++)
    {
        err = libusb_get_device_descriptor(devs[i], &usb_dev_desc);
        if(err > 0)
        {
            LOG_ERROR("libusb_get_device_descriptor()  err with %d", err);
            goto getdevdesc_err;
        }

        printf("|--[Vid:0x%04x, Pid:0x%04x]-[Class:0x%02x, SubClass:0x%02x]-[bus:%d, device:%d, port:%d]-[cfg_desc_num:%d]\n",
            usb_dev_desc.idVendor, usb_dev_desc.idProduct, usb_dev_desc.bDeviceClass, usb_dev_desc.bDeviceSubClass,
            libusb_get_bus_number(devs[i]), libusb_get_device_address(devs[i]), libusb_get_port_number(devs[i]), usb_dev_desc.bNumConfigurations);
        //printf()
        for(uint8_t j = 0;j < usb_dev_desc.bNumConfigurations; j++)
        {
            err = libusb_get_config_descriptor(devs[i], j, &usb_cfg_desc);
            if(err > 0)
            {
                LOG_ERROR("libusb_get_config_descriptor(cfg_index:%d)  err with %d", j, err);
                goto getcfgdesc_err;
            }
            printf("|  |--cfg_desc:%02d-[cfg_value:0x%01x]-[infc_desc_num:%02d]\n",
                j, usb_cfg_desc->bConfigurationValue, usb_cfg_desc->bNumInterfaces);
            for(uint8_t l = 0;l < usb_cfg_desc->bNumInterfaces; l++)
            for(uint8_t n = 0;n < usb_cfg_desc->interface[l].num_altsetting; n++)
            {
                printf("|  |  |--intfc_desc: %02d:%02d-[Class:0x%02x, SubClass:0x%02x]-[ep_desc_num:%02d]\n",
                    l, n, usb_cfg_desc->interface[l].altsetting[n].bInterfaceClass, usb_cfg_desc->interface[l].altsetting[n].bInterfaceSubClass,
                    usb_cfg_desc->interface[l].altsetting[n].bNumEndpoints);
                 for(uint8_t m = 0;m < usb_cfg_desc->interface[l].altsetting[n].bNumEndpoints; m++)
                 {
                    printf("|  |  |  |--ep_desc:%02d-[Add:0x%02x]-[Attr:0x%02x]-[MaxPkgLen:%02d]\n",
                        m, usb_cfg_desc->interface[l].altsetting[n].endpoint[m].bEndpointAddress,
                        usb_cfg_desc->interface[l].altsetting[n].endpoint[m].bmAttributes,
                        usb_cfg_desc->interface[l].altsetting[n].endpoint[m].wMaxPacketSize);
                 }
            }
        }
    }
    return 0;
getdevdesc_err:
    return 0xff;
getcfgdesc_err:
    return 0xff;
}

uint8_t USB_MSD_Open(uint16_t VID, uint16_t PID, USB_MSD_ST *msd)
{
    struct libusb_device_descriptor usb_dev_desc;
    struct libusb_config_descriptor *usb_cfg_desc;
    uint8_t intfc_index = 0;
    uint8_t err = 0;

    msd->msd_handle = libusb_open_device_with_vid_pid(NULL, VID, PID);
    if(msd->msd_handle == NULL)
    {
        LOG_ERROR("[0x%04x:0x%04x] MSD Open failed!", VID, PID);
        goto opendev_err;
    }
    msd->msd_dev = libusb_get_device(msd->msd_handle);
    if(msd->msd_dev == NULL)
    {
        LOG_ERROR("[0x%04x:0x%04x] get dev failed!", VID, PID);
        goto getdev_err;
    }
    err = libusb_get_device_descriptor(msd->msd_dev, &usb_dev_desc);
    if(err > 0)
    {
        LOG_ERROR("[0x%04x:0x%04x] get dev_desc failed err with %d", VID, PID, err);
        goto getdevdesc_err;
    }
    err = libusb_get_config_descriptor(msd->msd_dev, 0, &usb_cfg_desc);
    if(err > 0)
    {
        LOG_ERROR("[0x%04x:0x%04x] get cfg_desc failed err with %d", VID, PID, err);
        goto getcfgdesc_err;
    }
    for(uint8_t m = 0;m < usb_cfg_desc->bNumInterfaces; m++)
    {
        for(uint8_t n = 0;n < usb_cfg_desc->interface[m].num_altsetting;n++)
        {
            if(usb_cfg_desc->interface[m].altsetting[n].bInterfaceClass == 0x0a && usb_cfg_desc->interface[m].altsetting[n].bInterfaceSubClass == 0x00)
            {
                for(uint8_t i = 0;i < usb_cfg_desc->interface[m].altsetting[n].bNumEndpoints;i++)
                {
                    if((usb_cfg_desc->interface[m].altsetting[n].endpoint[i].bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_BULK)
                    {
                        if((usb_cfg_desc->interface[m].altsetting[n].endpoint[i].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN)
                        {
                            msd->endpoint_in = usb_cfg_desc->interface[m].altsetting[n].endpoint[i].bEndpointAddress;
                        }
                        if((usb_cfg_desc->interface[m].altsetting[n].endpoint[i].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_OUT)
                        {
                            msd->endpoint_out = usb_cfg_desc->interface[m].altsetting[n].endpoint[i].bEndpointAddress;
                        }
                    }
                }
                if(msd->endpoint_in != 0x00 && msd->endpoint_out != 0x00)
                {
                    intfc_index = m;
                }
                else
                {
                    msd->endpoint_in = 0x00;
                    msd->endpoint_out = 0x00;
                }
            }
        }
    }
    if(msd->endpoint_in == 0x00 || msd->endpoint_out == 0x00)
    {
        LOG_ERROR("[0x%04x:0x%04x] get ep_addr failed!", VID, PID);
        goto getepaddr_err;
    }

    err = libusb_claim_interface(msd->msd_handle, 1);
    err = libusb_set_interface_alt_setting(msd->msd_handle, 1, 0);
    err = libusb_claim_interface(msd->msd_handle, 0);
    err = libusb_set_interface_alt_setting(msd->msd_handle, 0, 0);
    if(err > 0)
    {
        LOG_ERROR("[0x%04x:0x%04x] claim intfc failed err with %d", VID, PID, err);
        goto claimintfc_err;
    }

    err = libusb_clear_halt(msd->msd_handle, msd->endpoint_out);
    if(err > 0)
    {
        LOG_ERROR("[0x%04x:0x%04x] ep_out:%x clear halt failed err with %d", VID, PID, msd->endpoint_out, (int8_t)err);
        goto epclrhalt_err;
    }

    err = libusb_clear_halt(msd->msd_handle, msd->endpoint_in);
    if(err > 0)
    {
        LOG_ERROR("[0x%04x:0x%04x] ep_in:%x clear halt failed err with %d", VID, PID, msd->endpoint_in, (int8_t)err);
        goto epclrhalt_err;
    }

    libusb_free_config_descriptor(usb_cfg_desc);
    libusb_reset_device(msd->msd_handle);
    LOG_INFO("[0x%04x:0x%04x]-[EP_IN:0x%02x, EP_OUT:0x%02x] Open success!", VID, PID, msd->endpoint_in, msd->endpoint_out);

    return 0;
//ÓÐÎÊÌâ

claimintfc_err:
    libusb_release_interface(msd->msd_handle, 1);
    libusb_release_interface(msd->msd_handle, 0);

detachkernel_err:

epclrhalt_err:

getepaddr_err:

getcfgdesc_err:

getdevdesc_err:

getdev_err:

opendev_err:
    libusb_free_config_descriptor(usb_cfg_desc);
    libusb_close(msd->msd_handle);
    return 0xff;
}

uint8_t USB_MSD_Close(USB_MSD_ST *msd)
{
    if(msd->msd_handle != NULL)
    {
        libusb_release_interface(msd->msd_handle, 0);
        libusb_close(msd->msd_handle);
    }
    return 0;
}

uint8_t USB_MSD_Bulk_Write(USB_MSD_ST* msd, uint8_t* buffer, int len, int* size, uint32_t ms)
{
	int err = 0;

	err = libusb_bulk_transfer(msd->msd_handle, msd->endpoint_out, buffer, len, size, ms);
	if (err < 0)
	{
		LOG_ERROR("Write:%d", err);
		return -1;
	}

	return 0;
}

uint8_t USB_MSD_Bulk_Read(USB_MSD_ST* msd, uint8_t* buffer, int len, int* size, uint32_t ms)
{
	int err = 0;

	err = libusb_bulk_transfer(msd->msd_handle, msd->endpoint_in, buffer, len, size, ms);
	if (err < 0)
	{
		LOG_ERROR("Read:%d", err);
		return -1;
	}

	return 0;
}


usb_device_opt.h

#ifndef _USB_DEVICE_OPT_
#define _USB_DEVICE_OPT_
#include <stdio.h>
#include <libusb.h>
#include "log.h"
#include <stdint.h>

typedef struct USB_MSD
{
    libusb_device* msd_dev;
    libusb_device_handle* msd_handle;
    uint8_t endpoint_in;
    uint8_t endpoint_out;
} USB_MSD_ST;

extern uint8_t USB_Dev_Scan_A_Print(int16_t dev_num, libusb_device **devs);
extern uint8_t USB_MSD_Open(uint16_t VID, uint16_t PID, USB_MSD_ST *msd);
extern uint8_t USB_MSD_Bulk_Write(USB_MSD_ST* msd, uint8_t* buffer, int len, int* size, uint32_t ms);
extern uint8_t USB_MSD_Bulk_Read(USB_MSD_ST* msd, uint8_t* buffer, int len, int* size, uint32_t ms);
extern uint8_t USB_MSD_Close(USB_MSD_ST *msd);

#endif // _USB_DEVICE_OPT_

Good Night!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

USB系列-LibUSB使用指南(1)-Windows下的报错与踩坑 的相关文章

  • Eclipse 安装程序内部错误:注册表目录不可用

    我曾经像平常一样使用 Oomph Eclipse 安装程序 直到我更改了注册表中的用户配置文件 以删除烦人的空格 从那时起 除了 eclipse 安装程序之外 我没有遇到任何问题 它不断尝试在旧用户文件夹中查找注册表目录 该文件夹已不存在
  • 从某个文件夹启动随机批处理文件

    问题是这样的 我有一个名为 abc 的文件夹 其中包含几个批处理文件 它们的命名如下 abc1 batabc2 batabc3 batabc4 bat 等等 我需要一个脚本 当我单击它时 它会随机启动其中一个批处理文件 我需要的脚本将存储在
  • 安装 JDK 时出错:keytool 命令需要已安装的 proc fs (/proc)。 Linux 的 Windows 子系统

    我尝试在 Linux 的 Windows 子系统 Ubuntu 14 04 上安装 Oracle JDK 1 7 但出现以下错误 the keytool command requires a mounted proc fs proc Jav
  • BeagleBone Black 如何用作大容量存储设备?

    是否可以使用 BB 作为大容量存储设备 我希望将其连接到可以从 USB 连接 例如 USB 闪存驱动器 读取文件的音频播放器并充当包含一个特定文件夹的数据存储设备 及其子文件夹 从文件系统 如果可能 在连接到开发板的闪存驱动器上 正如设备规
  • 将目录添加到path环境变量

    我的要求是将目录之一添加到path安装我的应用程序时 Windows 中的环境变量并将其删除path使用批处理文件卸载时的环境变量 在与此相关的 stackoverflow 答案之一中 建议将目录添加到路径环境变量中 setx path C
  • WinVerifyTrust 检查特定签名?

    我正在为 Windows 实现一个进程提升助手 该程序将以提升模式运行并以管理员权限启动其他程序 而不会显示其他 UAC 提示 出于安全原因 我想确保只有使用我公司的 Authenticode 密钥进行数字签名的二进制文件才能执行 The
  • 为什么 %processor_architecture% 总是返回 x86 而不是 AMD64

    我正在尝试检索环境变量来检测系统是32位还是64位 但在64位服务器上环境变量 processor architecture 正在返回x86代替AMD64 有人对此有任何线索吗 您可能获得了错误的环境变量 如果您的应用程序是在 64 位操作
  • .NET 或 Windows 同步原语性能规范

    我目前正在写一篇科学文章 我需要非常准确地引用 有人可以向我指出 MSDN MSDN 文章 一些已发表的文章来源或一本书 我可以在其中找到 Windows 或 NET 同步原语的性能比较 我知道这些是按性能降序排列的 互锁 API 关键部分
  • 使用 Linq to XML 将 XElement 添加到 XML 文件

    使用 Linq to XML 我尝试将 XElement 添加到现有 XML 文件 它必须在 Windows Phone NET 框架中完成 目前我的 XML 文件如下所示
  • NSIS获取参数

    to get n 0值作为字符串 不需要选项 传递给安装程序 我不太明白这个函数的工作原理是什么 GetParameters input none output top of stack replaces with e g whatever
  • select() 可以在 Windows 下使用 Python 中的文件吗?

    我正在尝试在 Windows 下运行以下 python 服务器 An echo server that uses select to handle multiple clients at a time Entering any line o
  • 如何在Windows上安装Xlib(eclipse、c语言)

    好的 我需要使用它进行图形编程 GUI cs 349 uwaterloo 第一次作业 我在谷歌搜索结果中看到随机的 Xlib h 和 Xutil h 文件 但它没有在哪里说明我如何从某个网站安装或下载 Xlib 我什至尝试将 Xlib h
  • 匿名、身份验证、模拟和委派之间有什么区别,为什么委派需要 Kerberos?

    当我们的客户安装我们的软件时 他们通常选择 拆分安装 其中服务在一个机器上运行 数据库在另一个机器上 这些服务可能与其他服务通信 或者数据库可能包含需要与另一个数据库通信的存储过程 这将我们带入了 Kerberos 和 SetSPN 的黑暗
  • 作为附件的 Android 设备

    我有 2 个 Android 设备 我想用 USB OTG 电缆连接它们 并在两个设备之间进行来回通信 据我了解 一台 Android 设备将充当 USB 主机 运行 4 4 的 Nexus 7 另一台 Android 设备将充当 USB
  • 从命令行启用/禁用 Windows 10 中的设备 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我有一个特定的硬件 我想在每次 Windows 重新启动时禁用并重新启用它 我创建了一个批处理脚本 该脚本应该执行此操作 然后运行我的程序
  • csc.exe 引用外部 .dll 文件

    我正在尝试做一个简单的c 程序使用Growl C API 我尝试用两种不同的方式编译我的程序 我保留了我的 dll文件与我的在同一目录中 cs文件 比我跑的 csc r Growl Connector dll Growl CoreLibra
  • 在 Windows 7 中,不从命令行强制终止 JVM

    我在 JVM 中运行了 Cobertura 仪器化 jar 当我在运行应用程序的 Windows 控制台中按 Ctrl C 时 JVM 结束并Cobertura 结果已成功刷新到 cobertura ser 文件 但我需要使用命令 工具 不
  • 如何将最新的 Windows SDK 版本传递给 CMake?

    如何将最新的 Windows SDK 版本传递给 CMake 这样我就不需要进入 Visual Studio 并从配置属性 常规中手动放置它 我在互联网上搜索 找到了 CMAKE SYSTEM VERSION 变量 并且尝试使用 set C
  • 如何在 C# 中知道 PID 和 VID 来发现虚拟 COM 端口名称

    如果我知道 PID 和 VID 我会尝试找到查找 COM 端口名称的正确方法 到目前为止 我写了解决方法 但我不相信没有更优雅和正确的方法 顺便说一句 我知道我可以使用 REGEX 编写此代码只是为了测试解决方法 我知道还有很大的改进空间
  • Mercurial/extdiff 没有更改为临时目录(正如我认为应该的那样)

    使用 Windows Mercurial 和 extdiff 扩展 适用于 Mercurial 我试图设置 extdiff 以使用 WinDiff 作为外部比较工具 但我认为我已经将问题范围缩小到足以说明问题在我还没有做到这一点之前 根据我

随机推荐

  • STM32内存结构介绍和FreeRTOS内存分配技巧

    概述 这是我第一次使用FreeRTOS构建STM32的项目 xff0c 踩了好些坑 xff0c 又发现了我缺乏对于操作系统的内存及其空间的分配的知识 xff0c 故写下文档记录学习成果 文章最后要解决的问题是 xff0c 如何恰当地分配Fr
  • 使用JetPack4.2为NVIDIA Xavier刷机

    1 准备 Host xff1a Ubuntu 16 04或18 04系统 xff0c 配备鼠标 键盘 显示器 xff0c 联网 Xavier xff1a 配备鼠标 键盘 显示器 HDMI转VGA接口 xff08 连接显示器和Xavier x
  • Java与设备(PLC)通信

    通信名词 上位机 指可以直接发出操控命令的计算机 xff0c 一般是PC host computer master computer upper computer 控制者和提供服务者是上位机 xff1b 工控机 工作站 触摸屏可作为上位机
  • DAMA学习笔记

    第1章 数据管理 1 1 引言 1 数据管理 xff1a 为了实现数据价值 xff0c 制定计划 制度并执行 监督 2 数据管理专业人员 xff1a 技术人员 xff08 数据库管理员 网络管理员 程序员 xff09 和业务人员 xff08
  • 2023 学习日志

    2023 01 03 mybatis源码学习 学习视频 xff1a https www bilibili com video BV1Tp4y1X7FM p 61 1 amp vd source 61 cc82a52a9b9b8b31eca3
  • EL表达式两个foreach嵌套循环

    集合List中加List泛型的泛型 List lt RiskEvaSuitQuestion gt riskEvaSuitQuestionList 61 riskEvaSuitQuestionService getQueList 100003
  • JQuery通过radio,select改变隐藏显示div

    1 xff09 select下拉框控制div的隐藏与显示 lt script gt function checkYear var selectValue 61 34 select name 61 39 periodType 39 34 va
  • 开科唯识笔试

    对于这次的笔试 xff0c 我只想说BiShi 几道编程题加一道SQL题 1 找出所有三位数中的水仙花数 public void getNarcissusNums int g 61 0 s 61 0 b 61 0 sum 61 0 for
  • JDBC批量插入

    最近项目中有用到JDBC技术 xff0c 存在大量数据要进行插入 xff0c 通过研究采用批量插入速度快的不是一点点 下面简单比较了一下普通插入与批量插入5W条数据的时间效率 常规插入 xff1a 耗时12952ms public stat
  • 面试经历---YY欢聚时代(2015年11月21日上午初试、25日下午复试)

    YY欢聚时代一年多前去面试过一次 xff0c 当时鄙视了 xff0c 在现在的公司呆了1年半了 xff0c 感觉做得很不爽 xff0c 而且薪资又不满意 xff0c 所以想找个新工作 xff0c 就想去YY面试 下面将两次YY面试的经历写出
  • exe应用程序无法启动,因为应用程序的并行配置不正确

    问题 xff1a exe应用程序无法启动 xff0c 因为应用程序的并行配置不正确 有关详细信息 xff0c 请参阅应用程序事件日志 xff0c 或使用命令行 sxstrace exe 工具 原因查找 xff1a 1 xff09 开始 所有
  • TortoiseSVN is locked in another working copy

    TortoiseSVN提交报错 TortoiseSVN is locked in another working copy 原因 xff1a 可能是因为打开了多个commit会话 xff0c 然后又去修改了提交文件的内容 xff0c 导致文
  • Java对接企业微信

    最近需要对接企业微信 xff0c 例如将风险测评结果推送给企业微信中对应的用户 xff0c 然后用户对结果进行查看与确认操作 xff0c 所以这里就涉及到两方面 xff1a 1 xff09 将外部系统内容推送到企业微信 xff1b 2 xf
  • 微众银行面试

    机缘巧合 xff0c 其实并没有换工作的想法 xff0c 却收到了微众的面试邀请 xff0c 就想着去看看当是增长见识吧 xff0c 因为已经好久没准备面试的事情了 xff0c 而且微众毕竟作为腾讯系的看起来好像也不错 说实话那边离地铁站是
  • #TP4056#--3.7V锂电池充放电电路(实践日志篇)

    成就更好的自己 本篇为小型电源的实践日志 xff0c 内附各种充电应用电路 xff0c 并开源TP4056应用电路AD的原理图和PCB xff1b 先放一点锂电池常识性的知识 xff1a 锂电池是一类由锂金属或锂合金为负极材料 使用非水电解
  • ROS四旋翼无人机快速上手指南(1):无人机系统硬件概述与指南简介

    成就更好的自己 ROS无人机快速上手指南旨在于让使用此无人机开发平台的比赛参赛人员 xff0c 算法设计人员 xff0c 无人机爱好者更加快速的了解底层控制运作原理 xff0c 从而缩短开发周期 xff0c 减少掉坑次数 xff0c 快速验
  • ROS四旋翼无人机快速上手指南(2):Ubuntu18.04与ROS系统

    成就更好的自己 目录 Jetson版Ubuntu以及ROS的安装 xff1a ROS特性及Nano开发问题 PX4与Gazebo仿真环境 ROS与MATMAB仿真 Jetson版Ubuntu以及ROS的安装 xff1a ROS机器人系统运行
  • ROS四旋翼无人机快速上手指南(4):阿木实验室PX4功能包飞行控制分析与讲解(重点章节)

    成就更好的自己 这一章详细讲解一下阿木实验室 AMOV 的开源项目px4 command功能包 xff0c 此功能包通过mavlink协议直接控制烧录px4固件的自驾仪 xff0c 还融合了来自各个传感器的位姿 xff0c 距离等信息 xf
  • ROS四旋翼无人机快速上手指南(5):快速部署上层算法的操作与思路

    成就更好的自己 经过本系列上一篇文章关于PX4 command飞行控制功能包的分析 xff0c 相信大家对于飞整个流程有个大概的了解 xff0c 本章将在此基础上详细讲解一下应用级算法构建的思路与操作方法 关于PX4 command飞行控制
  • USB系列-LibUSB使用指南(1)-Windows下的报错与踩坑

    成就更好的自己 时隔一年再次开始撰写博客 xff0c 这一年的时间经历了很多 xff0c 现在终于稳定下来 以后很长一段时间都能够稳定的学习和更新 时间将会聚焦于USB和PCIe的开发进行 xff0c 能和大家共同进步真的很高兴 本篇为US