第10课【STM32 USB通讯协议实战】HID键盘+CDC虚拟串口组合设备

2023-05-16

目录

  • 前言
  • USB设备类别
  • 未定义设备
    • 设备描述符/配置描述符分析
    • 配置从机类型
    • 配置设备专用的描述符
    • 配置从机端点
    • 实现设备类特定请求
  • HID设备
    • 特点
    • HID键盘描述符实例
    • HID报文描述符
      • 短条目
      • 报文生成
      • HID键盘报文描述符实例
      • 总结
  • CDC设备
    • 特点
    • CDC-ACM虚拟串口描述符实例
    • 类特定请求
  • USB组合设备
    • 实现思路
    • 代码实例

前言

文章中的部分概念可参考第9课【USB协议】USB总线 接口 端点 管道 数据包 枚举 STM32_USB-FS-Device_Lib V4.1.0

USB设备类别

USB协议中为了提供对多样设备的支持,定义了许多外部设备子类,常见的包括:

  • 人机交互类设备HID(Human Interface Device)
  • 通信类设备CDC(Communicate Device Class)
  • 大容量存储设备MSC(Mass Storage Class)
  • 视频类设备UVC(USB Video Class)
  • 音频类设备UAC(USB Audio Class)

USB支持的设备众多,实现方法上会有很多不同点。但从实现思路上可以重点关注两个点:

  • 设备描述符实现:配置从机类型,方便主机加载从机对应驱动及对从机数据的识别
  • 配置描述符实现:配置设备专用的描述符,方便启用特定的设备操作和特性;配置从机端点信息,方便主机从端点读/写数据

未定义设备

指仅能让计算机识别出其USB设备身份,没有具体指明其功能的USB设备

识别USB设备的硬件前提
USB集线器识别USB设备是否接入依赖的是USB总线上D+/D-的电平变化。在USB集线器上,D+/D-信号线上都会接一个15kΩ的下拉电阻,而对应的USB设备上都会在D+/D-接上一个1.5kΩ上拉电阻(高速全速设备上拉电阻在D+,低速设备的上拉电阻在D-),当集线器检测到空闲的USB接口D+/D-信号线的电平突然被拉高时,就知道有USB设备接入了,进而进入美剧流程

设备描述符/配置描述符分析

const uint8_t Undefined_USB_DeviceDescriptor[18] =
{
	0x12,                       // bLength:描述符长度,固定为0x12
	0x01,                       // bDescriptorType:描述符类型,固定为0x01,表示设备描述符
	0x00, 0x02,                 // bcdUSB:USB协议版本号,这里是2.0
	0x00,                       // bDeviceClass:设备类别,0表示未定义
	0x00,                       // bDeviceSubClass:设备子类别,0表示未定义
	0x00,                       // bDeviceProtocol:设备协议,0表示未定义
	0x40,                       // bMaxPacketSize:最大数据包大小,这里是64字节
	0x83, 0x04,                 // idVendor:USB设备厂商ID,这里是0x0483
	0x20, 0x57,                 // idProduct:USB设备产品ID,这里是0x5720
	0x00, 0x01,                 // bcdDevice:设备版本号,这里是1.0
	0x01,                       // iManufacturer:USB设备制造商字符串描述符的索引值,这里是1
	0x02,                       // iProduct:USB设备产品字符串描述符的索引值,这里是2
	0x03,                       // iSerialNumber:USB设备序列号字符串描述符的索引值,这里是3
	0x01                        // bNumConfigurations:USB设备支持的配置数量,这里是1
}

const uint8_t Undefined_USB_ConfigDescriptor[32] =
{
    /************** 配置描述符 ****************/
	0x09                      // bLength: 描述符长度,固定为9
	0x02                      // bDescriptorType: 描述符类型,表示该描述符的类型为Configuration Descriptor,固定为2
	0x20 
	0x00                   // wTotalLength: 该配置描述符及其所包含的所有描述符的总长度,单位为字节
	0x01                      // bNumInterfaces: 该配置所包含的接口数量
	0x01                      // bConfigurationValue: 该配置的值,用于选择设备的配置
	0x00                      // iConfiguration: 该配置的字符串描述符的索引,如果不存在则为0
	0xA0                      // bmAttributes: 该配置的属性,包括供电方式和远程唤醒功能等
	0x32                      // bMaxPower: 该配置所需的最大电流,单位为2mA
    /************** 接口描述符 ****************/
	0x09                      // bLength: 描述符长度,固定为9
	0x04                      // bDescriptorType: 描述符类型,表示该描述符的类型为Interface Descriptor,固定为4
	0x00                      // bInterfaceNumber: 该接口的编号
	0x00                      // bAlternateSetting: 备用设置的编号,用于支持多种设置
	0x02                      // bNumEndpoints: 该接口所包含的端点数量
	0x00                      // bInterfaceClass: 该接口的类别,0表示未定义
	0x00                      // bInterfaceSubClass: 该接口的子类别,0表示未定义
	0x00                      // bInterfaceProtocol: 该接口的协议,0表示未定义
	0x00                      // iInterface: 该接口的字符串描述符的索引,如果不存在则为0
    /************** 端点描述符 ****************/
	0x07                      // bLength: 描述符长度,固定为7
	0x05                      // bDescriptorType: 描述符类型,表示该描述符的类型为Endpoint Descriptor,固定为5
	0x81                      // bEndpointAddress: 该端点的地址,包括端点方向和端点编号,表示为IN端点1
	0x02                      // bmAttributes: 该端点的属性,包括传输类型和数据类型等,表示为Bulk传输类型
	0x40 
	0x00                      // wMaxPacketSize: 该端点所支持的最大数据包大小,单位为字节
	0x00                      // bInterval: 该端点所需的轮询间隔,单位为毫秒
    /************** 端点描述符 ****************/
	0x07                      // bLength: 描述符长度,固定为7
	0x05                      // bDescriptorType: 描述符类型,表示该描述符的类型为Endpoint Descriptor,固定为5
	0x02                      // bEndpointAddress: 该端点的地址,包括端点方向和端点编号,表示为OUT端点2
	0x02                      // bmAttributes: 该端点的属性,包括传输类型和数据类型等,表示为Bulk传输类型
	0x40 
	0x00                      // wMaxPacketSize: 该端点所支持的最大数据包大小,单位为字节
	0x00                      // bInterval: 该端点所需的轮询间隔,单位为毫秒
};

设备描述符定义了一个未指定功能的USB设备,支持的USB协议为USB2.0,单次传输最大数据包传输大小为64字节,配置描述符描述了未定义USB设备的接口,接口对应功能也是未定义的,所以也不能通过端点传输数据

配置从机类型

以上设备描述符中有三个重要的值,用于指定USB设备身份:

  • bDeviceClass:表明设备类别
  • bDeviceSubClass:表明设备子类别
  • bDeviceProtocol:表明设备协议

对于常用的设备类型,它们有以下组合方式:

设备类别bDeviceClassbDeviceSubClass(多选一)bDeviceProtocol(多选一)
未定义0x000x000x00
人机交互类设备 HID0x03未定义:0x00
启动接口子类:0x01
鼠标接口子类:0x02
摇杆接口子类:0x03
游戏手柄接口子类:0x04
键盘接口子类:0x05
未定义:0x00
键盘协议:0x01
鼠标协议:0x02
摇杆协议:0x03
游戏手柄协议:0x04
通信类设备 CDC0x02未定义:0x00
ACM子类:0x01
ECM子类:0x02
NCM子类:0x03
自定义子类:0xFE
未定义:0x00
V.25ter协议:0x01
AT命令协议:0x02
PPP协议:0x03
Ethernet Emulation协议:0x04
ATM Emulation协议:0x05
供应商自定义协议: 0xFF
大容量存储设备 MSC0x08未定义:0x00
公开SCSI命令集子类:0x01
ATAPI命令集子类:0x02
USB命令集子类:0x03
自定义子类:0xFE
未定义:0x00
USB批量传输协议:0x01
公开SCSI协议:0x50
UAS传输协议:0x62
自定义协议:0x80~0xFF
组合设备0xEF通用子类:0x01
IAD多接口子类:0x02
供应商自定义子类:0x0F
未定义:0x00
IAD多接口协议:0x01
供应商自定义协议:0xFF

配置设备专用的描述符

要配置设备专用描述符和从机端点,首先要了解配置描述符的组成。常见的配置描述符结构如下:

在这里插入图片描述
能看出配置描述符的一些特点:

  • 上层描述符如果包含下层描述符,那么下层描述符的实现必须紧挨着上层描述符
  • 一个上层描述符如果包含了多个下层描述符,那多个下层描述符的实现按照顺序紧挨着上层描述符
  • 一个配置描述符中可以有多个接口描述符
  • 一个接口描述符可以包含多个端点描述符
  • 多个接口描述符通过接口关联描述符整合

接口关联描述符
Interface Associate Descriptor接口关联描述符。在USB部分设备中,一般实现某个设备功能只会要求使用一个接口,但也可能会要求使用两个或以上的接口,这时候就需要将使用到的多个接口,通过IAD描述符关联起来,以便于主机识别这些关联接口之间的关系。一般来说IAD描述符出现在配置描述符之后,接口描述符之前,IAD描述符之后会紧跟着相关联的接口描述符

根据上述的特点,设备专用的描述符只要插入了其上层描述符之后就可以了,如果有多个,按照包含顺序紧挨着实现。一般来说因为接口描述符和设备的功能有紧密的联系,所以设备专用的描述符位置都和接口描述符并列,或者包含于接口描述符之中

配置从机端点

端点描述符的配置较为简单,一般要:

  • 确定需要的端点数量,以及每个端点的数据方向:输入/输出
  • 配置缓冲区描述表,为缓冲区描述表头和端点分配读写缓冲区

缓冲区及缓冲区描述表
缓冲区是在STM32 USB标准库开发中的概念。缓冲区实际大小为512Bytes,位于STM32的RAM中,地址为0x40006000~0x400063FF(大小为1KByte,由于对于内核是通过32位的读取方式读取缓冲的,而USB外设是通过16位的方式读取缓冲区。内核读取到的四个字节只有两个是有意义的,另一半由于没有对应的实际存储器,会读取不到东西),用于存放缓冲区描述表,同时还用作端点收发数据Buffer。缓冲区描述表是缓冲区中的一个区域,记录缓冲区端点相关寄存器信息。缓冲区中除了描述表的以外空间可以分配给端点,作为收发数据Buffer,Buffer之间不能重叠,一个Buffer的最大为64Bytes
缓冲区描述表头和端点分配
缓冲区描述表头应该位于缓冲区的最前面,记录每个端点相关的四个寄存器信息,包括缓冲地址寄存器,发送数据字节寄存器,接收缓冲地址寄存器,接收数据字节寄存器,每个寄存器占用两个字节,所以每增加一个端点会让表头增大八个字节。一般来说紧挨着表头是端点0Buffer地址,会定义在距离表头64Bytes的位置,因为单个USB设备可用端点为8个,所以所有的寄存器信息大小为8*8=64Bytes。之后的表头地址就根据各个端点Buffer需求的大小定义即可,可参考以下示例:

// 端点数量
#define EP_NUM     (5)

// 缓冲区描述表定义
// 缓冲区描述表头
#define BTABLE_ADDRESS      (0x00)

// 端点0发送/接收Buffer首地址,大小都为64Bytes
#define ENDP0_RXADDR        (0x40)
#define ENDP0_TXADDR        (0x80)
// 端点1发送/接收Buffer首地址,大小都为64Bytes
#define ENDP1_TXADDR        (0xC0)
#define ENDP1_RXADDR        (0x100)

// 端点2发送/接收Buffer首地址,大小都为64Bytes
#define ENDP2_TXADDR        (0x140)
#define ENDP2_RXADDR        (0x180)

// 端点3发送Buffer首地址,大小都为64Bytes
#define ENDP3_TXADDR        (0x1C0)

实现设备类特定请求

USB设备类特定请求属于USB设备标准请求的拓展,其数据内容类似于标准请求,都有请求码,索引,值和长度。USB设备标准请求是所有USB设备都支持的,它可以实现最基础的USB设备配置和控制功能。而USB设备类特定请求指的是针对特定设备的请求,例如HID设备的GET_REPORT/SET_REPORT请求,CDC设备的GET_LINE_CODING/SET_LINE_COFING请求。具体到每种设备,其对类特定请求的定义和实现都不同,这类请求一般由开发者来实现

HID设备

HID设备是USB协议中定义的一种广泛运用于计算机,游戏机,工业设备等电子设备中的输入设备子类,包括鼠标,键盘,摇杆,游戏手柄等。符合HID规范的设备不仅可以通过USB,还可以通过蓝牙,无线电的方式同主机相连接,通过特殊的HID报文同主机进行数据传输

HID设备有以下优点:

  • 免驱动:HID设备的驱动通常都已经内置于操作系统之中
  • 低功耗:HID设备通常都通过总线供电或自带电池供电
  • 多样性:HID设备种类繁多,基本的输入设备都可以作为HID设备,例如鼠标,键盘,手柄,指点杆等等

特点

USB HID协议中额外规定了HID设备/主机之间的通信格式和通信协议。所以相对于标准USB设备,HID设备实现上有以下的特点:

  • 设备描述符不同,配置描述符中多出了HID设备专用的HID主描述符,HID报文描述符,HID实体描述符(可选)

在这里插入图片描述

  • 仅需定义三个端点:一个端点0用传输控制;一个IN端点,用于设备向主机发送数据;一个OUT端点,用于主机向设备发送数据,其中IN,OUT端点都使用中断传输模式

在这里插入图片描述

  • 通过HID报文描述符定义的报文格式同主机沟通(报文格式多变,几乎可以传输任何数据,但其内容也几乎相当于一门编程语言)

HID键盘描述符实例

const uint8_t HID_Keyboard_DeviceDescriptor[18] =
{
	0x12,                       // bLength: 描述符长度,固定为0x12
	0x01,                       // bDescriptorType: 描述符类型,固定为0x01,表示设备描述符
	0x00, 0x02,                 // bcdUSB: USB协议版本号,这里是2.0
	0x03,                       // bDeviceClass: 设备类别,3表示HID设备
	0x01,                       // bDeviceSubClass: 设备子类别,1表示启动接口子类
	0x01,                       // bDeviceProtocol: 设备协议,1表示键盘协议
	0x40,                       // bMaxPacketSize0: 最大数据包大小,这里是64字节
	0x83, 0x04,                 // idVendor: USB设备厂商ID,这里是0x0483
	0x20, 0x57,                 // idProduct: USB设备产品ID,这里是0x5720
	0x00, 0x01,                 // bcdDevice: 设备版本号,这里是1.0
	0x01,                       // iManufacturer: USB设备制造商字符串描述符的索引值,这里是1
	0x02,                       // iProduct: USB设备产品字符串描述符的索引值,这里是2
	0x03,                       // iSerialNumber: USB设备序列号字符串描述符的索引值,这里是3
	0x01                        // bNumConfigurations: USB设备支持的配置数量,这里是1
}

const uint8_t HID_Keyboard_ConfigDescriptor[41] =
{
    /************** 配置描述符 ****************/
	0x09                      // bLength: 描述符长度,固定为9
	0x02                      // bDescriptorType: 描述符类型,表示该描述符的类型为Configuration Descriptor,固定为2
	0x29 
	0x00                      // wTotalLength: 该配置描述符及其所包含的所有描述符的总长度,单位为字节
	0x01                      // bNumInterfaces: 该配置所包含的接口数量
	0x01                      // bConfigurationValue: 该配置的值,用于选择设备的配置
	0x00                      // iConfiguration: 该配置的字符串描述符的索引,如果不存在则为0
	0xA0                      // bmAttributes: 该配置的属性,包括供电方式和远程唤醒功能等
	0x32                      // bMaxPower: 该配置所需的最大电流,单位为2mA
    /************** 接口描述符 ****************/
	0x09                      // bLength: 描述符长度,固定为9
	0x04                      // bDescriptorType: 描述符类型,表示该描述符的类型为Interface Descriptor,固定为4
	0x00                      // bInterfaceNumber: 该接口的编号
	0x00                      // bAlternateSetting: 备用设置的编号,用于支持多种设置
	0x02                      // bNumEndpoints: 该接口所包含的端点数量
	0x03                      // bInterfaceClass: 该接口的类别,表示为Human Interface Device
	0x01                      // bInterfaceSubClass: 该接口的子类别,表示为启动接口子类
	0x01                      // bInterfaceProtocol: 该接口的协议,表示为键盘协议
	0x00                      // iInterface: 该接口的字符串描述符的索引,如果不存在则为0
	/************** HID主描述符 ****************/
	0x09                      // bLength: 描述符长度,固定为9
	0x21                      // bDescriptorType: 描述符类型,表示该描述符的类型为HID Descriptor,固定为33
	0x10, 0x01,               // bcdHID:HID规范版本(1.1)
	0x00,                     // bCountryCode:国家代码(未指定)
	0x01,                     // bNumDescriptors:支持的描述符类型数
	0x22,                     // bDescriptorType:描述符类型(报告描述符)
	0x3F, 0x00,               // wDescriptorLength:描述符长度
    /************** 端点描述符 ****************/
	0x07                      // bLength: 描述符长度,固定为7
	0x05                      // bDescriptorType: 描述符类型,表示该描述符的类型为Endpoint Descriptor,固定为5
	0x01                      // bEndpointAddress: 该端点的地址,包括端点方向和端点编号,表示为OUT端点1
	0x03                      // bmAttributes: 该端点的属性,包括传输类型和数据类型等,表示为Interrup传输类型
	0x40 
	0x00                      // wMaxPacketSize: 该端点所支持的最大数据包大小64字节
	0x0A                      // bInterval: 该端点所需的轮询间隔10ms
    /************** 端点描述符 ****************/
	0x07                      // bLength: 描述符长度,固定为7
	0x05                      // bDescriptorType: 描述符类型,表示该描述符的类型为Endpoint Descriptor,固定为5
	0x81                      // bEndpointAddress: 该端点的地址,包括端点方向和端点编号,表示为IN端点1
	0x03                      // bmAttributes: 该端点的属性,包括传输类型和数据类型等,表示为Interrup传输类型
	0x40 
	0x00                      // wMaxPacketSize: 该端点所支持的最大数据包大小64字节
	0x0A                      // bInterval: 该端点所需的轮询间隔10ms
};

以上描述符实现了一个HID键盘设备,其描述符分别指出:

  • 设备描述符:支持USB 1.1协议,支持启动接口的HID键盘
  • 配置描述符:包含一个接口
  • 接口描述符:定义了设备为HID设备,支持在BIOS使用,支持键盘HID协议
  • HID主描述符:HID设备有两个端点,最大包大小为64字节,最大电流为100mA,支持自供电,不支持远程唤醒。同时还指定了HID设备支持HID报文描述符,报文描述符长度为63字节
  • 备注:HID报文描述符的实现需要额外定义,不在配置描述符中实现

HID报文描述符

前面提到HID设备通过特殊的HID报文同主机进行数据传输。这里的报文,指的是可变长度指定格式的特殊数据,其中:

  • 可变长度:HID报文描述符中没有指定描述符长度,其长度由开发者定义
  • 指定格式:HID报文数据需要根据HID报文描述符中描述的格式进行解析,才能变成有效的信息

HID报文描述符中最基本的单元是条目(Item),条目又分为长条目和短条目,长条目不经常使用,所以此处仅讨论短条目

短条目

短条目由前缀+可选的数据组成,其中:

  • 前缀:1字节的数据,用于描述可选数据含义的元信息

短条目的前缀部分大小为一个字节,这一个字节的八个比特位被分为三部分:在这里插入图片描述

  • bSize:D0~D1,占用1位,用于表示该条目的数据长度,单位为字节。0表示0字节,1表示1字节,2表示2字节,3表示4字节
  • bType:D2~D3,占用2位,用于表示该条目的数据类型。0表示主条目,1表示全局条目,2表示局部条目
  • bTag:D4~D7,占用4位,用于表示该条目的功能。根据不同的条目类型,同样的bTag会有不同的含义
    在这里插入图片描述
  • 可选的数据:可以是0字节,1字节,2字节或者4字节大小的数据,一般使用1字节居多

可选数据不是独立存在的,需要通过条目的前缀去解析可选数据,到官方提供的HID Usage Table中查询到对应的意义


实际的代码中,通常将短条目分为前缀和可选数据,分别用两位十六进制数表示,其形式为:

//  前缀     数据
	0x81,	0x06 // 表示主条目的Input

其形式类似于键值对,前缀为键,数据为值

报文生成

通过将相关的条目进行组合,能得到有层次结构的HID报文描述符。基本的报文描述符结构如下:

在这里插入图片描述
可以看出,报文描述符分为两部分:

  • 外层绿色部分:由Usage Page/Usage/Collection/End Collection控制符组成,被称为集合,它描述了此报文的最基本功能,定义了报文的使用页面,使用类型和字段组合方式。值得注意的是,最外层并不一定就包含了此报文的所有信息,复杂的报文,可能会涉及多语言设计,加密,压缩等机制,需要具体分析
  • 内层橙色/蓝色部分:由Usage Page/Usage Minimum/Usage Maximum/Logical Minimum/Logical Maximum/Input/Output等控制符组成,被称为字段,它描述了报文数据的具体组合方式,各个元素的含义和取值范围

HID键盘报文描述符实例

通过对一个HID键盘的基础报文描述符的分析,可以很好的对以上所有的HID描述符相关概念进行收尾

const uint8_t CustomHID_ReportDescriptor[63] ={
    0x05, 0x01,           // Usage Page (Generic Desktop)
    0x09, 0x06,           // Usage (Keyboard)
    0xA1, 0x01,           // Collection (Application)
    
    0x05, 0x07,           // Usage Page (Key Codes)
    0x19, 0xE0,           // Usage Minimum (224)
    0x29, 0xE7,           // Usage Maximum (231)
    0x15, 0x00,           // Logical Minimum (0)
    0x25, 0x01,           // Logical Maximum (1)
    0x75, 0x01,           // Report Size (1)
    0x95, 0x08,           // Report Count (8)
    0x81, 0x02,           // Input (Data, Variable, Absolute) ; Modifier byte
    0x75, 0x08,           // Report Size (8)
    0x95, 0x01,           // Report Count (1)
    0x81, 0x01,           // Input (Constant) ; Reserved byte

    0x05, 0x08,           // Usage Page (LEDs)
    0x19, 0x01,           // Usage Minimum (1)
    0x29, 0x05,           // Usage Maximum (5)
    0x75, 0x01,           // Report Size (1)
	0x95, 0x05,           // Report Count (5)
    0x91, 0x02,           // Output (Data, Variable, Absolute) ; LED report
    0x75, 0x03,           // Report Size (3)
    0x95, 0x01,           // Report Count (1)
    0x91, 0x01,           // Output (Constant) ; LED report padding

    0x05, 0x07,           // Usage Page (Key Codes)
    0x15, 0x00,           // Logical Minimum (0)
    0x25, 0x65,           // Logical Maximum (101)
    0x19, 0x00,           // Usage Minimum (0)
    0x29, 0x65,           // Usage Maximum (101)
    0x75, 0x08,           // Report Size (1)
    0x95, 0x06,           // Report Count (48)
    0x81, 0x00,           // Input (Data, Array) ; Key array (6 keys)

    0xC0                  // End Collection
};

可以看出HID报文描述符在C语言中,是通过结构体实现的。其中每一行都是一个短条目,短条目由前缀+1字节数据构成(可以参考上面说到的条目表现形式)。最外层的集合告知计算机,要将此报文作为键盘数据进行解析。最内层的字段描述了报文数据的结构,报文数据大小为9个字节,共分为3部分:

阅读报文描述符的技巧
由于报文由条目组成,结构上有一定的特殊性,按照正常的顺序浏览可能会比较乱,所以阅读前先将报文合理分段,每段按照从后往前阅读,会比较好理解

  • Modifier Keys:结合Modifier Keys段报文描述符代码进行单独解析


    0x05, 0x07, // Usage Page (Key Codes)                                 【表明字段的用途是表示键码】
    0x19, 0xE0, // Usage Minimum (224)
    0x29, 0xE7, // Usage Maximum (231)                                      【表示键码的值范围从224~231】
    0x15, 0x00, // Logical Minimum (0)
    0x25, 0x01, // Logical Maximum (1)                                         【表示单个按键可存在的逻辑状态,为0释放/1按下】
    0x75, 0x01, // Report Size (1)                                                  【表示报文大小,单位比特】
    0x95, 0x08, // Report Count (8)                                                【表示报文数量】
    0x81, 0x02, // Input (Data, Variable, Absolute) ; Modifier byte 【表示该段报文的数据方向,数据类型】

    【分界线】

    0x95, 0x01, // Report Count (1)                                                【表示报文大小,单位比特】
    0x75, 0x08, // Report Size (8)                                                   【表示报文数量】
    0x81, 0x01, // Input (Constant) ; Reserved byte                       【表示报文的数据方向,数据类型】


    代码可以通过【分界线】分为两部分来看:
    前半部分:这部分定义了报文数据类型为输入计算机的绝对值变量,用途为键码,报文数据数量为8,单个报文数据大小为1bit,所以总的数据量为8x1bit=8bits=1Byte。而八个比特位能表示八个按键,键码范围从224~231,对应如下图的八个特殊按键(参考官方HID Usage Table)
    在这里插入图片描述八个比特位分别按照高位对大键码,低位位对小键码的关系一一对应,每个比特位的0/1装袋分别表示按键被释放/按下。例如0b0000 0001对应就是LeftControl键按下
    后半部分:这部分定义了报文数据类型为输入计算机的常量,没有指明实际用途,主要起到了填充数据的作用,保持整体数据按照8bits对齐,报文数据数量为8,单个报文数据大小为1bit,所以后半部分的数据量为8x1bit=8bits=1Byte
    整体字段描述的数据的大小为2Bytes,描述的数据内容为Modified Keys

  • LED:这部分字段定义了报文数据类型为输出计算机的绝对值变量,用途为LED灯,报文数据数量为48,单个报文数据大小为1bit,所以总的数据量为5x1bit=5bits。五个比特位能表示五个LED灯,灯代表的含义范围从1~5,对应如下图的五个LED(参考官方HID Usage Table):
    在这里插入图片描述
    LED由计算机控制亮灭。这部分字段还额外带了一个 3bits的填充数据。
    整体字段描述的数据的大小为1Bytes,描述的数据内容为LED

  • Normal Keys:这部分字段定义了报文数据类型为输入计算机的绝对值变量,用途为键码,报文数据数量为48,单个报文数据大小为1bit,所以总的数据量为48x1bit=48bits=6Byte。而六个字节能表示四十八个按键,键码范围从0~101,对应如下图的102个特殊按键(参考官方HID Usage Table)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    整体字段描述的数据的大小为6Bytes,描述的数据内容为Normal Keys

报文数据大小为2+1+6=9Bytes,报文内容如下

Byte 9Byte 8Byte 7Byte 6Byte 5Byte 4Byte 3Byte 2Byte 1Byte 0
------Normal KeysLED+PaddingPaddingModif Keys

报文数据本身由HID键盘生成并放置到对应IN端点,等待计算机按照端点描述符中定义的中断时间间隔内来获取。计算机取到报文数据后,自带的HID驱动会按照HID键盘报文描述符解析此报文,得到键盘释放/按下的消息。最终按键消息会转发给操作系统底层,由操作系统执行对应响应操作

总结

HID报文描述符可能是所有USB描述符中最复杂的,抽象的概念相当多,理解起来也需要一定的时间。但是复杂的定义带来的却是报文易用性和灵活性,它几乎可以描述任何形式的数据,而且长度都由开发者来定义,形式也较为简洁清晰

CDC设备

CDC设备USB协议中定义的一种通信设备(通常指电信通信设备和中速网络通信设备)子类,包括调制解调器,网络适配器,手机,PDA等。通信设备领域,使用到很多种无线/有线通信协议以及各式的数据传输方式,CDC协议提供了对这些设备的协议和数据传输支持,例如常见的COM口UART协议,以太网口IP协议等。通过CDC协议可以将USB设备虚拟成通信设备,通过指定的传输协议和数据传输方式同USB设备进行通信

CDC设备有以下优点:

  • 免驱动:使用标准的USB接口和通信协议,部署方便。例如USB虚拟COM口,无需安装驱动即可使用
  • 支持多种通信协议:支持很多通信领域的通信协议,可以满足不同行业,不同场景的需求

特点

USB CDC协议中同样额外规定了CDC设备/主机之间的通信格式和通信协议,以CDC-ACM设备为例,相对于标准USB设备,它实现上有以下的特点:

  • 设备描述符不同,配置描述符中多出了CDC设备专用的CDC控制接口描述符,头部功能描述符,呼叫管理描述符,抽象控制模型描述符,联合功能描述符,CDC数据接口描述符,
    在这里插入图片描述

CDC设备的接口组成
不同于常见的单接口设备,CDC设备由多个接口类组成,分别是【CDC控制接口类】和【CDC数据接口类】(可选)。所以在较新的操作系统中需要用额外的IAD描述符将两个接口关联起来。其中:

  • CDC控制接口类:负责完成通信设备的配置和管理,包含了CDC控制接口,头部功能描述符,呼叫管理功能描述符,抽象控制模型描述符,联合功能描述符。各个描述符的作用分别为:
    【CDC控制接口】:用于设备管理/电话管理(可选),设备管理一般涉及请求(request)和通知(notification),请求一般由端点0进行处理,通知需要额外配置其他端点进行处理
    【头部功能描述符】:用于表示CDC功能描述符的开始,紧跟其后的是CDC设备的其他功能描述符
    【呼叫管理功能描述符】:负责电话,AT指令相关的功能
    【抽象控制模型描述符】:PSTN(Public Switched Telephone Net)下定义的抽象控制模型。用于描述使用AT指令,或AT.25Ver指令的调制解调设备
    【联合功能描述符】:用于告知计算机,将哪几个接口联合起来,用来表示CDC功能。类似于IAD接口关联描述符
  • CDC数据接口类:负责进行数据的传输,包含了数据接口,数据调制解调器接口(可选),后者用于模拟调制解调设备的控制信号

接口关联描述符和联合功能描述符的关系
在USB协议的初期,没有规划好多接口设备的支持,基本上都是一个设备只使用一个接口。后来出现了CDC设备这样需要使用多接口的设备,于是便专门出了联合功能描述符用于应对。等到后期USB协议逐渐完善,推出了接口关联描述符这样的用于多个接口关联的描述符。两者使用起来并不会冲突,在旧版操作系统中,会忽略接口关联描述符,识别联合功能描述符;在新版操作系统中会忽略联合功能描述符,仅识别接口关联描述符

CDC-ACM虚拟串口描述符实例

const uint8_t VirtualComPort_DeviceDescriptor[18] =
{
	0x12,                       // bLength: 描述符长度,固定为0x12
	0x01,                       // bDescriptorType: 描述符类型,固定为0x01,表示设备描述符
	0x00, 0x02,                 // bcdUSB: USB协议版本号,这里是2.0
	0x02,                       // bDeviceClass: 设备类别,2表示CDC设备
	0x01,                       // bDeviceSubClass: 设备子类别,1表示使用抽象控制模型
	0x01,                       // bDeviceProtocol: 设备协议,1表示使用AT指令交互 
	0x40,                       // bMaxPacketSize0: 最大数据包大小,这里是64字节
	0x83, 0x04,                 // idVendor: USB设备厂商ID,这里是0x0483
	0x20, 0x57,                 // idProduct: USB设备产品ID,这里是0x5720
	0x00, 0x01,                 // bcdDevice: 设备版本号,这里是1.0
	0x01,                       // iManufacturer: USB设备制造商字符串描述符的索引值,这里是1
	0x02,                       // iProduct: USB设备产品字符串描述符的索引值,这里是2
	0x03,                       // iSerialNumber: USB设备序列号字符串描述符的索引值,这里是3
	0x01                        // bNumConfigurations: USB设备支持的配置数量,这里是1
}

const uint8_t VirtualComPort_ConfigDescriptor[41] =
  {
    /************** 配置描述符 ****************/
	0x09                      // bLength: 描述符长度,固定为9
	0x02                      // bDescriptorType: 描述符类型,表示该描述符的类型为Configuration Descriptor,固定为2
	0x29 
	0x00                      // wTotalLength: 该配置描述符及其所包含的所有描述符的总长度,单位为字节
	0x01                      // bNumInterfaces: 该配置所包含的接口数量
	0x01                      // bConfigurationValue: 该配置的值,用于选择设备的配置
	0x00                      // iConfiguration: 该配置的字符串描述符的索引,如果不存在则为0
	0xA0                      // bmAttributes: 该配置的属性,包括供电方式和远程唤醒功能等
	0x32                      // bMaxPower: 该配置所需的最大电流,单位为2mA

    /************** 接口关联描述符 ****************/
    /* 09 */
    0x08,                     // bLength:描述符长度,固定为8
    0x0B,                     // bDescriptorType:描述符类型,表示该描述符的类型为接口关联描述符,固定为b
    0x00,                     // bFirstInterface:第一个接口描述符编号 0
    0x02,                     // bInterfaceCount:关联接口描述符总数 2
    0x02,                     // bFunctionClass:功能类 CDC
    0x02,                     // bFunctionSubClass:功能子类
    0x01,                     // bFunctionProtocol:功能协议
    0x00,                     // iFunction: 字符串描述符编号
    /************** 控制接口描述符 ****************/
    /* 17 */
    0x09,                     // bLength:描述符长度,固定为9
    0x04,                     // bDescriptorType:描述符类型,表示该描述符的类型为接口描述符,固定为4
    0x00,                     // bInterfaceNumber:接口描述符编号
    0x00,                     // bAlternateSetting:替换设置
    0x01,                     // bNumEndpoints 包含端点数
    0x02,                     // bInterfaceClass: 接口类型 CDC
    0x02,                     // bInterfaceSubClass:接口子类 抽象控制模型
    0x01,                     // nInterfaceProtocol:接口协议 Common AT commands V.250
    2,                        // iInterface: 字符串描述符编号
    /************** 头部功能描述符 ****************/
    /* 26 */
    0x05,                     // bLength:描述符长度,固定为5
    0x24,                     // bDescriptorType:描述符类型 CS_INTERFACE
    0x00,                     // bDescriptorSubtype:描述符子类 头部功能描述符
    0x10,                     // bcdCD:CDC协议版本1.10
    0x01,
    /************** 呼叫功能描述符 ****************/
    /* 31 */
    0x05,                     // bFunctionLength:描述符长度,固定为5
    0x24,                     // bDescriptorType:描述符类型 CS_INTERFACE
    0x01,                     // bDescriptorSubtype:描述符子类 头部功能描述符
    0x00,                     // bmCapabilities: D0+D1 */
    0x01,                     // bDataInterface: 数据描述符接口号 1 */
    /************** 抽象控制模型描述符 ****************/
    /* 36 */
    0x04,                     // bFunctionLength:描述符长度,固定为4
    0x24,                     // bDescriptorType:描述符类型 CS_INTERFACE
    0x02,                     // bDescriptorSubtype:描述符子类 头部功能描述符
    0x02,                     // bmCapabilities */
    /************** 联合功能描述符 ****************/
    /* 40 */
    0x05,                     // bFunctionLength:描述符长度,固定为5
    0x24,                     // bDescriptorType:描述符类型 CS_INTERFACE
    0x06,                     // bDescriptorSubtype:描述符子类 联合功能描述符
    0x00,                     // bMasterInterface:主接口描述符描述符
    0x01,                     // bSlaveInterface:附属接口描述符描述符
    /************** 控制端点描述符 ****************/
    /* 45 */
    0x07,                     // bLength:描述符长度,固定为7
    0x05,                     // bDescriptorType:描述符类型 端点描述符
    0x83,                     // bEndpointAddress:IN端点3
    0x03,                     // bmAttributes:端点类型 中断
    0x40                      // wMaxPacketSize:最大数据包容量 64字节 
    0x00,
    0x02,                     // bInterval:数据获取间隔 2ms
    /************** 数据接口描述符 ****************/
    /* 52 */
    0x09,                     // bLength:描述符长度,固定为9
    0x04,                     // bDescriptorType:描述符类型,表示该描述符的类型为接口描述符,固定为4
    0x02,                     // bInterfaceNumber:接口描述符编号
    0x00,                     // bAlternateSetting:替换设置
    0x02,                     // bNumEndpoints 包含端点数
    0x0A,                     // bInterfaceClass: 接口类型 CDC数据接口
    0x00,                     // bInterfaceSubClass:接口子类 未定义
    0x00,                     // nInterfaceProtocol:接口协议 未定义
    0,                        // iInterface: 字符串描述符编号
    /************** 数据端点描述符 ****************/
    /* 61 */
    0x07,                     // bLength:描述符长度,固定为7
    0x05,                     // bDescriptorType:描述符类型 端点描述符
    0x02,                     // bEndpointAddress:OUT端点2
    0x02,                     // bmAttributes:端点类型 批量
    0x40                      // wMaxPacketSize:最大数据包容量 64字节 
    0x00,
    0x00,                     // bInterval:数据获取间隔 忽略
    /* 68 */
    0x07,                     // bLength:描述符长度,固定为7
    0x05,                     // bDescriptorType:描述符类型 端点描述符
    0x82,                     // bEndpointAddress:IN端点2
    0x02,                     // bmAttributes:端点类型 批量
    0x40                      // wMaxPacketSize:最大数据包容量 64字节 
    0x00,
    0x00,                     // bInterval:数据获取间隔 忽略
    /* 75 */
  };

以上描述符实现了一个CDC虚拟串口设备,其描述符分别指出:

  • 设备描述符:支持USB 1.1协议,支持抽象控制模型,使用AT指令的CDC设备
  • 配置描述符:包含两个接口,分别是CDC控制接口,CDC数据接口
  • 数据关联描述符:实现多个接口对应一个功能,例子中是CDC设备功能
  • 控制接口描述符:定义一个其他端点,用作异步传输
  • 头部功能描述符:CDC设备支持的协议版本1.10
  • 呼叫功能描述符:只有在电话功能中会使用,虚拟串口设备中没有使用
  • 抽象控制模型描述符:表明CDC设备使用抽象控制模型进行通信
  • 联合功能描述符:关联CDC设备的控制接口和数据接口
  • 控制端点描述符:实现设备控制功能,控制功能可以处理通信设备需要的请求和通知
  • 数据接口描述符:定义2个数据传输所使用的端点
  • 数据端点描述符:处理数据传输

类特定请求

相对于HID设备通过特殊的HID报文进行数据传输,CDC-ACM设备直接通过端点发送/接收数据,无需进行其他转换和解析。但是需要在CDC-ACM设备端实现CDC-ACM类的设备类特定请求SetLineCoding和GetLineCoding,这两个请求主要实现了设置和获取虚拟串口的串行通信参数,包括波特率,数据传输位数,奇偶校验位,停止位,具体实现如下:

typedef struct
{
  uint32_t bitrate;
  uint8_t  format;
  uint8_t  paritytype;
  uint8_t  datatype;
}LINE_CODING;

LINE_CODING linecoding =
{
	115200, // 波特率
	0x00,   // 停止位 1
    0x00,   // 奇偶校验位 无
    0x08    // 数据位数 8
};

uint8_t *Virtual_Com_Port_GetLineCoding(uint16_t Length)
{
  if (Length == 0)
  {
    pInformation->Ctrl_Info.Usb_wLength = sizeof(linecoding);
    return NULL;
  }
  return(uint8_t *)&linecoding;
}

// 函数等待实现
uint8_t *Virtual_Com_Port_SetLineCoding(uint16_t Length)
{
  if (Length == 0)
  {
    pInformation->Ctrl_Info.Usb_wLength = sizeof(linecoding);
    return NULL;
  }
  return(uint8_t *)&linecoding;
}

USB组合设备

在一个USB设备上实现多个功能,例如HID键盘+CDC虚拟串口。有两种方法:

  • 组合设备:Composite Device,内部通过组合设备描述符,以及组织多个功能的接口实现多功能组合。所有功能共用一套PID/VID/DID
  • 复合设备:Compound Device,内部通过USB HUB来实现,将多个单一功能的USB设备,通过USB HUB连接整合,每个设备有自己独立的PID/VID/DID。这种方法复杂度相当高,具体可以参考USB2.0协议

实现思路

通过以上对HID设备和CDC设备的解析。通过STM32去实现USB组合设备的思路就相当清晰了:

  • 首先需要将USB的设备描述符中设备类型改为Miscellaneous Device,表示组合设备
  • 然后将HID键盘的配置描述符和CDC虚拟串口的配置描述符整合,主要注意处理接口关联描述符,接口描述符,CDC控制接口描述符,CDC数据接口描述符,HID描述符的次序即可,HID报文描述符可以直接复用
  • 之后实现HID键盘的GET_REPORT/SET_REPORT设备类特定请求,以及CDC虚拟串口的GET_LINE_CODING/SET_LINE_COFING请求
  • 最后按照自己的需求完成端点数据的填充即可,USB主机会按照端点描述符要求进行数据传输

代码实例

USB组合设备代码已上传Github:Sinuxtm32

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

第10课【STM32 USB通讯协议实战】HID键盘+CDC虚拟串口组合设备 的相关文章

  • STM32 上的 ADC 单次转换

    我正在研究 STM32 F103x 上的 ADC 编程 并从最简单的情况 单次转换开始 测量内部温度传感器 连接到 ADC1 的值 并使用 USART 将其发送到 COM 端口 目标似乎很明确 但是当我尝试将源代码下载到闪存时 它不会向 C
  • 从 .net 向操纵杆发送反馈/效果

    感谢这个答案https stackoverflow com a 13734766 637142 https stackoverflow com a 13734766 637142我能够知道何时按下按钮或何时旋转方向盘 现在我的问题是如何将效
  • STM32 上的位置无关代码 - 指针

    我已成功在 STM32 上构建并运行位置无关的代码 向量表和 GOT 已修补 一切正常 但我对这样的代码有问题 double myAdd double x return x 0 1 double ptrmyAdd double myAdd
  • 是否有可通过 USB 密钥在 Mac OS X 10.6 上运行的便携式 python 解释器?

    我一直在努力寻找一个可以通过工作计算机上的 USB 密钥运行的便携式解释器 Work comp 运行的是 Mac OS X 10 6 环境相当受限 无法访问终端 无法安装应用程序 但我知道便携式应用程序可以从 USB 驱动器运行 我一直在使
  • 在简单的 main() 中获取rawinputdata

    我正在尝试使用简单的 C 技术和 Windows 从操纵杆读取值 我的目标是编写一个程序 每当操纵杆信号超过预定义阈值时 该程序就会发送键盘命令 键盘命令将由当时处于活动状态的窗口拾取 我的 C 编码技能有限 因此我希望以最简单的方式完成此
  • Android:与充当主机的 USB 设备通信

    我们制作了一个可以充当 USB 主机或从机并处理其接收到的数据的设备 我想使用Android手机通过USB向其发送数据 我的研究使我得出结论 如果不修改硬件或操作系统 我们就无法在 USB 主机模式下使用 Android 设备 这样做不是一
  • PWM DMA 到整个 GPIO

    我有一个 STM32F4 我想对一个已与掩码进行 或 运算的 GPIO 端口进行 PWM 处理 所以 也许我们想要 PWM0b00100010一段时间为 200khz 但随后 10khz 后 我们现在想要 PWM0b00010001 然后
  • 如何对 USB/HID/蓝牙适配器进行逆向工程。使用什么工具?

    需要对蓝牙USB鼠标适配器进行逆向工程 并使用鼠标板测量距离 并用绘图库 http matplotlib org 我发现这些工具可以帮助完成此任务 Linux lsusb usbmon usbhid转储 rfcomm hidrd 转换 xx
  • 如何枚举 USB 设备*并*对其进行读/写?

    我需要与一个自定义 USB 设备进行通信 它没有特定的驱动程序 该设备不显示为 dev tty I 发现 http www signal11 us oss udev 我该如何使用libudev枚举 USB 设备 假设我设法获得相应的stru
  • 如何在 DriverKit 中将我的 dex 与 USB 设备匹配?

    我正在编写一个驱动程序包扩展 其目标是阻止 USB 设备 例如闪存驱动器 作为起点 我选择了示例项目https developer apple com documentation driverkit communicating Betwee
  • WriteFile 返回错误代码 87

    我正在开发一个写入 HID 设备的程序 但收到错误 87 WriteFile 函数参数无效 我从 Jan Axelson 的 USB Complete 获得了这些功能 所以我不确定为什么会收到错误 我用它来查找我的设备 private vo
  • 如何枚举所有HID设备? C#

    我需要枚举连接到我的 PC 的所有 HID 设备 我尝试使用这个答案 https stackoverflow com a 3331509 902424 但它枚举了 USBHub 设备 但我在那里找不到我的 HID 设备 EDIT 我很高兴知
  • 适用于 Android 的 USB 主机

    I am trying to communicate with my freeduino board which is similar to arduino uno via usb through android device nexus
  • Java 两个独立键盘的独立输入

    我有两个 USB 键盘插入同一台机器 我正在开发一个 Java 应用程序 是否有办法允许 Java 应用程序分离出它来自哪个 USB 键盘 即是否有类似的东西http docs oracle com javase 6 docs api ja
  • c#.NET USB设备持久标识符

    我正在寻找一种在 C 中为 USB 插槽 更具体地说是 SD 卡读卡器 提供持久标识符 在最终用户拔下集线器 重新启动计算机时 的方法 驱动器号并不总是分配给同一个插槽 但我需要一种方法来物理识别插槽 A 一旦我能够识别它 我就可以连接到驱
  • 我可以从哪里开始使用可编程硬件?

    一段时间以来 我一直渴望至少学习一点有关硬件编程的知识 并想在这里提出要求以获得一些起点 我是一位相当有成就的程序员 具有 Delphi 和 Objective c 经验 但从未听过设备端口 中断 我什至不知道术语 更不用说对硬件进行编程了
  • 如何使用 python / pywinusb 将 hid 数据发送到设备?

    我正在尝试使用 pywinusb 将输出报告发送到 pic18f4550 该设备可以接收数据 我已经使用 C 应用程序对其进行了测试 效果很好 另外 我可以使用 pywinusb 从设备读取数据 但我在尝试发送数据时遇到问题 这是我正在运行
  • libusb 和轮询/选择

    我正在使用 Linux 操作系统 想知道是否有任何文件描述符可以轮询 选择 当数据等待从 USB 设备读取时会触发这些文件描述符 我也在使用 libusb 库 但尚未找到可以使用的文件描述符 Use libusb 的轮询函数 http li
  • Delphi - 如何获取 USB 可移动硬盘和记忆棒的列表?

    在我的应用程序 Delphi 中 我需要列出所有 USB 存储设备 这些可以是闪存棒or外部存储驱动器 有一个Jvcl成分JvDriveCombo 并且它有DriveType属性 问题是我是否选择DriveType Fixed那么除了外部驱
  • 我的 Android 设备需要安装哪个驱动程序才能运行我的应用程序?

    我购买了 intex mobile 来在真实设备中测试我的 Android 应用程序 然而 该设备不存在于 OEM USB 驱动程序列表中 android 提供的设备列表中 我检查了 intex 官方网站 但不确定到底需要安装哪个驱动程序

随机推荐

  • 用C++写一个UDP发送和接收程序

    发送程序Sender cpp include lt stdio h gt include lt string gt include lt iostream gt include lt winsock h gt using namespace
  • STM32自学笔记(五)串口通信

    xff08 想要深入理解就把前面的看下 xff0c 否则直接看使用总结即可 xff09 usart文件夹 usart 文件夹内包含了 usart c和usart h两个文件 这两个文件用于串口的初始化和中断接收 代码只针对了串口1 xff0
  • sockaddr_in详解

    struct sockaddr in short sin family Address family一般来说AF INET xff08 地址族 xff09 PF INET xff08 协议族 xff09 unsigned short sin
  • ubuntu下好用的TCP/UDP调试工具

    github官方链接 GitHub s kyo mNetAssist mNetAssist A UDP TCP Assistant 编译好的安装包 ubuntu下好用的TCP UDP调试工具 网络设备文档类资源 CSDN下载 1 解压dpk
  • Linux网络编程之connect函数分析

    在一个 CLIENT SERVER模型的网络应用中 xff0c 客户端的调用序列大致如下 xff1a socket gt connect gt recv send gt close 其中socket没有什么可疑问的 xff0c 主要是创建一
  • linux route 命令

    显示现在所有路由 route n root 64 Ubuntu route Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 10
  • 大量LAST_ACK 分析过程

    记录一下自己的思想过程 现象 在netstat的时候发现大量处于LAST ACK状态的TCP连接 xff0c 达到在ESTABLISHED状态的90 以上 root 64 ccsafe netstat ant fgrep 34 34 cut
  • const 与重载

    const到底是不是一个重载的参考对象 xff0c 请看下面的例子 class A public void f int i std cout lt lt 34 1 34 函数1 void f int i const std cout lt
  • 指向成员函数的指针

    指向成员函数的指针 取一个非静态成员函数的地址 xff0c 如果该函数是nonvirtual xff0c 则得到的结果是它在内存中真正的地址 然而这个值也不是完全的 xff0c 它也需要被绑定于某个class object的地址上 xff0
  • rviz 远程显示及控制移动机器人的导航

    环境 xff1a xff08 1 xff09 虚拟机本地端IP 192 168 10 30 xff08 虚拟机需要通过桥接的形式与PC本地机连接 xff0c PC 机IP 192 168 10 21 xff09 xff08 2 xff09
  • iptables - administration tools for packet filtering and NAT

    2 iptables administration tools for packet filtering and NAT Linux Iptables Manual Incoming Traffic V 43 43 PREROUTING 4
  • C++ 内部类、嵌套类

    1 嵌套类 nested class 一个类可以定义在另一个类的内部 xff0c 前者称为嵌套类 xff0c 嵌套类时一个独立的类 xff0c 和外层类基本没什么关系 它通过提供新的类型类作用域来避免名称混乱 嵌套类必须声明在类的内部 xf
  • SONY VAIO P VPCP118KJ索尼酷袋本 鸡肋上网本初体验

    随着消费者市场需求的不断多样化和个性化 xff0c 个人电脑厂家开始不断突破创新 xff0c 在产品线拼装出各种各样的个人电脑 xff0c 主打性能的游戏本 xff0c 主打便携轻薄本 xff0c 2009年的CES上索大法推出了一款与众不
  • 第2课【CMSIS和标准库开发】标准库 CMSIS 分层

    目录 基本知识框架课堂笔记CMSISCMSIS的分层关系和作用CMSIS层文件结构内核函数层文件外设函数访问层文件 标准库开发标准库开发 LED GPIO标准库开发 KEY GPIO标准库开发 位带操作位带位带位带别名区位带区和位带别名区的
  • 第4课【STM32的时钟】时钟 时钟源 内外部时钟 高低速时钟

    目录 基本知识框架课堂笔记时钟什么是时钟 xff1f 时钟有什么作用 时钟源HSE 外部高速时钟HSI 内部高速时钟LSE 外部低速时钟LSI 内部低速时钟PLL锁相环 主要时钟和其他时钟主要时钟其他时钟 配置系统时钟实验 基本知识框架Xm
  • 第7课【SysTick定时器】中断 系统定时器 寄存器

    目录 基本知识框架课堂笔记SysTick定时器简介什么是SysTick定时器SysTick定时器的作用 SysTick寄存器CTRL控制及状态寄存器LOAD重装载数值寄存器VALUE当前数值寄存器CALIB校准数值寄存器 SysTick实战
  • 第8课【通讯的基本概念】串行并行 单工半双工全双工 同步异步 比特率波特率

    目录 基本知识框架课堂笔记通讯传送方式串行通讯并行通讯串行通讯与并行通讯的特性 通讯方向单工通讯半双工通讯全双工通讯 同步方式同步通讯异步通讯同步通讯与异步通讯的区别 传输单位比特率波特率比特率和波特率的联系 通讯简介通讯标准 基本知识框架
  • 嵌入式宏定义中do...while(0)的意义

    目录 背景do while 0 形式宏定义使得宏定义拥有一些函数的特点 分析1 封装 xff1a 使得宏定义可以包含复杂的内容而不容易出错 xff0c 提高代码健壮性2 生命周期 xff1a 宏定义内 外部定义的同名变量不会冲突3 返回 x
  • 第9课【USB协议】USB总线 接口 端点 管道 数据包 枚举 STM32_USB-FS-Device_Lib V4.1.0

    目录 简介背景特点版本 数据流模型总线拓扑结构主 从设备通信流端点管道 物理层接口类型USB Type AUSB Type BUSB Type C 引脚定义4PIN9PIN5PIN24PIN16PIN6PIN 电平标准 协议层 通讯方式通讯
  • 第10课【STM32 USB通讯协议实战】HID键盘+CDC虚拟串口组合设备

    目录 前言USB设备类别未定义设备设备描述符 配置描述符分析配置从机类型配置设备专用的描述符配置从机端点实现设备类特定请求 HID设备特点HID键盘描述符实例HID报文描述符短条目报文生成HID键盘报文描述符实例总结 CDC设备特点CDC