STM32 USB相关知识扫盲-程序员宅基地

技术标签: STM32  USB  USB描述符  USB复合设备  USB设备描述符  单片机  

STM32 USB相关知识扫盲



本篇文章为我个人在学习USB相关知识的整理,可能有不对的地方,欢迎指正!

我学习USB相关知识参考了以下网友的帖子:

http://www.51hei.com/bbs/dpj-40953-1.html

https://blog.csdn.net/alien75/article/details/4622319

http://www.usbzh.com/


1、基础知识

等级划分:

接口类型:

STM32基础型(F1系列)所带的USB是全速。

2、电气属性

USB的通信都是由主机发起的,这一点与IIC协议是类似的。

2.1 数据线

USB使用差分传输模式,有两条数据线,分别是:

  • USB数据正信号线,USB Data Positive,即USB-DP线,简写为D+
  • USB数据负信号线,USB Data Minus, 即USB-DM线,简写为D-

剩下的就是电源线(5V-Vbus)和地线(GND)。

2.2 USB主机是如何识别设备是高速设备/全速设备/低速设备?

主机的D+和D-都接有15K下拉电阻。

  • 全速USB设备的数据线D+接有1.5K的上拉电阻,一旦接入主机,主机的D+被拉高
  • 低速USB设备的数据线D-接有1.5K的上拉电阻,一旦接入主机,主机的D-会被拉高

因此,主机就可以根据检测到自己的D+为高还是D-为高,从而判断接入的设备是一个全速还是低速设备。

所以你可以看到STM32板子上的USB口D+有一个上拉电阻,而且是必须有的:

3、USB设备分类


类别详细描述在这里:https://www.usb.org/defined-class-codes

看一个CDC虚拟串口的设备分类:

在看一个MSC的(模拟U盘):

不同的类有不同的用途;不同的应用场合对应不同的产品形态;不同的产品形态可能会有自己特殊的描述符,比如: HID类有报告描述符、CDC类有ACM、Union描述符等。

4、描述符详解

以STM32里的MSC设备为例,MSC类所需要的描述符有:设备描述符+配置描述符+接口描述符(数量由配置描述符里的bNumInterfaces字段决定)+端点描述符(数量由配置描述符里的bNumEndpoints决定),所以MSC类结构就是这样的:

配置描述符
{
    
	接口描述符1
	{
    
		端点描述符1
		{
    
			
		}
		端点描述符2
		{
    
			
		}
	}
}

而CDC类是这样的:

配置描述符
{
    
	接口描述符1(通信接口)
	{
    
		其他描述符(特殊描述符)
		{
    
			/*Header Functional Descriptor*/
			/*Call Management Functional Descriptor*/
			/*ACM Functional Descriptor*/
			/*Union Functional Descriptor*/
		}
		端点描述符(命令端点)
		{
    
			
		}
	}
	接口描述符2(数据接口)
	{
    
		端点描述符1(输出端口)
		{
    
			
		}
		端点描述符2(输入端口)
		{
    
			
		}
	}
}
4.1 设备描述符

每个USB设备都必须且只有一个设备描述符,摘取一下STM32的MSC设备里的实例代码:

每个字段的含义:

  • bLength:描述符大小.固定为0x12;
  • bDescriptorType:设备描述符类型.固定为0x01;
  • bcdUSB:USB 规范发布号.表示了本设备能适用于那种协议,如2.0=0200,1.1=0110;
  • bDeviceClass:类型代码(由USB指定)。当它的值是0时,表示所有接口在配置描述符里,并且所有接口是独立的。当它的值是1到FEH时,表示不同的接口关联的。当它的值是FFH时,它是厂商自己定义的;
  • bDeviceSubClass:子类型代码(由USB分配),如果 bDeviceClass值是0,一定要设置为0。其它情况就跟据USB-IF组织定义的编码;
  • bDeviceProtocol:协议代码(由USB分配),如果使用USB-IF组织定义的协议,就需要设置这里的值,否则直接设置为0。如果厂商自己定义的可以设置为FFH;
  • bMaxPacketSize0:端点0最大分组大小(只有8,16,32,64有效);
  • idVendor:供应商ID(由USB分配);
  • idProduct : 产品ID(由厂商分配),由供应商ID和产品ID,就可以让操作系统加载不同的驱动程序
  • bcdDevice :设备出产编码.由厂家自行设置;
  • iManufacturer :厂商描述符字符串索引.索引到对应的字符串描述符. 为0则表示没有;
  • iProduct :产品描述符字符串索引.同上;
  • iSerialNumber:设备序列号字符串索引.同上;
  • bNumConfigurations :可能的配置数.指配置字符串的个数。
4.2 配置描述符

配置描述符定义了设备的配置信息,一个设备可以有多个配置描述符。摘一个STM32的MSC设备的配置描述符:

用C语言组合就是这样的一个结构:

typedef struct _USB_CONFIGURATION_DESCRIPTOR_
{
    
    BYTE      bLength,
    BYTE      bDescriptorType,
    uint16_t  wTotalLength,
    BYTE      bNumInterfaces,
    BYTE      bConfigurationValue,
    BYTE      iConfiguration,
    BYTE      bmAttributes,
    BYTE      MaxPower
}USB_CONFIGURATION_DESCRIPTOR;

每个字段含义如下:

  • bLength:描述符大小,固定为0x09.
  • bDescriptorType:配置描述符类型,固定为0x02.
  • wTotalLength:返回整个数据的长度,指此配置返回的配置描述符,接口描述符以及端点描述符的全部大小
  • bNumInterfaces:配置所支持的接口数。指该配置配备的接口数量也表示该配置下接口描述符数量
  • bConfigurationValue:作为Set Configuration的一个参数选择配置值
  • iConfiguration:用于描述该配置字符串描述符的索引
  • bmAttributes:供电模式选择,Bit4-0保留,D7:总线供电,D6:自供电,D5:远程唤醒
  • MaxPower:总线供电的USB设备的最大消耗电流.以2mA为单位
4.3 接口描述符

接口描述符说明了接口所提供的配置,一个配置所拥有的接口数量通过配置描述符的bNumInterfaces决定,摘取STM32的MSC设备类的接口描述符:

用C语言组合就是这样的一个结构:

typedef struct _USB_INTERFACE_DESCRIPTOR_
{
    
    BYTE      bLength,
    BYTE      bDescriptorType,
    BYTE      bInterfaceNumber,
    BYTE      bAlternateSetting,
    BYTE      bNumEndpoint,
    BYTE      bInterfaceClass,
    BYTE      bInterfaceSubClass,
    BYTE      bInterfaceProtocol,
    BYTE      iInterface
}USB_INTERFACE_DESCRIPTOR;

每个字段含义如下:

  • bLength : 描述符大小.固定为0x09.
  • bDescriptorType : 接口描述符类型.固定为0x04.
  • bInterfaceNumber: 该接口的编号.
  • bAlternateSetting : 用于为上一个字段选择可供替换的位置.即备用的接口描述符标号.
  • bNumEndpoint : 使用的端点数目.端点0除外.
  • bInterfaceClass : 类型代码(由USB分配).
  • bInterfaceSubClass : 子类型代码(由USB分配).
  • bInterfaceProtocol : 协议代码(由USB分配).
  • iInterface : 字符串描述符的索引
4.4 端点描述符

USB设备中的每个端点都有自己的端点描述符,由接口描述符中的bNumEndpoint决定其数量。摘取STM32的MSC设备类的端口描述符:

用C语言组合就是这样的一个结构:

typedef struct _USB_ENDPOINT_DESCRIPTOR_
{
    
    BYTE        bLength,
    BYTE        bDescriptorType,
    BYTE        bEndpointAddress,
    BYTE        bmAttributes,
    uint16_t    wMaxPacketSize,
    BYTE        bInterval
}USB_ENDPOINT_DESCRIPTOR;

每个字段含义如下:

  • bLength : 描述符大小.固定为0x07
  • bDescriptorType : 接口描述符类型.固定为0x05
  • bEndpointType : USB设备的端点地址.Bit7决定方向,1为IN端点,0为OUT端点,对于控制端点可以忽略;Bit6-4,保留;BIt3-0:端点号
  • bmAttributes : 端点属性.Bit7-2,保留.BIt1-0:00控制,01同步,02批量,03中断
  • wMaxPacketSize : 本端点接收或发送的最大信息包大小
  • bInterval : 轮训数据传送端点的时间间隔.对于批量传送和控制传送的端点忽略.对于同步传送的端点,必须为1,对于中断传送的端点,范围为1-255
4.5 字符串描述符

字符串描述符是可选的,如果不支持字符串描述符,其设备描述符、配置描述符、接口描述符内的所有字符串描述符索引都必须为0。

字符串描述符结构如下:

typedef struct _USB_STRING_DESCRIPTION_
{
    
    BYTE      bLength,
    BYTE      bDescriptionType,
    BYTE      bString[1];
}USB_STRING_DESCRIPTION;

各个字段含义:

  • bLength : 描述符大小.由整个字符串的长度加上bLength和bDescriptorType的长度决定.
  • bDescriptorType : 接口描述符类型,固定为0x03.
  • bString[1] : Unicode编码字符串
4.6 IAD描述符

USB组合设备一般用Interface Association Descriptor(IAD)实现,就是在要合并的接口前加上IAD描述符。例如你想用一个硬件USB接口实现两个功能,又能到U盘又能当虚拟串口,那么在USB配置描述符中就需要加上IAD描述符来指明。

typedef struct _USBInterfaceAssociationDescriptor 
{
    
    BYTE  bLength:                  0x08        //描述符大小,固定
    BYTE  bDescriptorType:          0x0B        //IAD描述符类型,固定
    BYTE  bFirstInterface:          0x00        //起始接口编号
    BYTE  bInterfaceCount:          0x02        //本个IAD下设备类的接口数量
    BYTE  bFunctionClass:           0x0E        //类型代码,本个IAD指示的是什么类型的设备,例如CDC是0X02,MSC是0X08
    BYTE  bFunctionSubClass:        0x03        //子类型代码
    BYTE  bFunctionProtocol:        0x00        //协议代码
    BYTE  iFunction:                0x04        //描述字符串索引
}

以MSC+CDC为例,他的配置描述符结构就是这样的:

配置描述符
{
    
	IAD描述符1(CDC)
	{
    
		接口描述符1(通信接口)
		{
    
			其他描述符(特殊描述符)
			{
    
				/*Header Functional Descriptor*/
				/*Call Management Functional Descriptor*/
				/*ACM Functional Descriptor*/
				/*Union Functional Descriptor*/
			}
			端点描述符(命令端点)
			{
    
				
			}
		}
		接口描述符2(数据接口)
		{
    
			端点描述符1(输出端口)
			{
    
				
			}
			端点描述符2(输入端口)
			{
    
				
			}
		}
	}

	IAD描述符(MSC)
	{
    
		接口描述符1
		{
    
			端点描述符1
			{
    
				
			}
			端点描述符2
			{
    
				
			}
		}
	}
}

5、STM32-USB详解

这个512字节SRAM叫做Packet Buffer Memory Area(简称PMA),这个很重要,后面会详细讲解。

在这里插入图片描述
根据描述可以,一共有8个端点,16个寄存器,一个端点关联两个寄存器,所以我们可以将他们规划为8个输入端点(0x80-0X87)+8个输出端点(0X00-0X07)。

6、STM32-PMA详解

先说一下USB的数据包大小,全速设备的最大包大小为64字节,高速最大为1024字节。

Packet Buffer Memory Area(简称PMA)

STM32F1/F3/L1系列都有且结构相同(其他系列暂未考证),译过来就是包数据缓存区,大小为512字节,按2字节进行寻址。

这个PMA的作用就是USB设备模块用来实现MCU与主机进行数据通信的一个专门的数据缓冲区,我们称之为USB硬件缓冲区。

说得具体点就是USB模块把来自主机的数据接收进来后先放到PMA,然后再被拷贝到用户数据缓存区;或者MCU要发送到主机的数据,先从用户数据缓存区拷贝进PMA,再通过USB模块负责发送给主机

很多人利用ST官方的USB库修改自己的USB应用时候卡住,获取改完之后懵懵懂懂出现错误,估计大多数原因就在此处的修改!

摘取一下STM32F1参考手册里的PMA描述表:

名称含义:

ADDR0_TX:输出端点0发送缓冲区地址
COUNT0_TX:输出端点0发送缓冲区大小
ADDR0_RX:输入端点0发送缓冲区地址
COUNT0_RX:输入端点0发送缓冲区大小

可以看到一个完整的端点描述包括:缓冲区地址+缓冲区大小。

PMA的头部为端点的描述,每个端点占8个字节,实际使用了几个端点就有几个描述头,例如使用了0、1、2这三个连续端点(这里注意是连续端点),那么PMA头部的3x8=24(十六进制的0X18)字节就是描述,倘若你使用的是0、1、3这三个端点,其中编号为2的端点虽然没使用但是占用空间,那么PMA头部的端点描述就是4x8=32字节,编号为2的端点8字节的空间就浪费了(严格来说没浪费,就是不方便使用)。

头部的端点描述之后就是各个端点的缓冲区了,例如使用了0、1、2三个端点,占用了PMA头部3x8=24字节的空间,那么这三个端点的缓冲区地址就是从PMA偏移24字节开始的,当然只要是大于24就都可以,这里就是最关键的地方了,很多人修改ST官方库实现自己USB应用时候就是没改这里的地址,导致缓冲区的使用覆盖了PMA头部的端点描述从而出错!

下面摘取一个STM32官方MSC设备的实例进行分析:

可以看到一共用到了4个端点,分别是输入端点0X80和0X81,输出端点0X00和0X01,其中0X00端点和0X80端点是供USB使用必须有的,0X81和0X01端点则是MSC设备输入输出端点。

那么一共使用了4个端点,按理来说PMA头部的端点描述大小应该是4X8=32(十六进制的0X20)字节,0X20之后的才是各个端点缓冲区,但是ST这里的却是从0X18开始,也就是说使用了三个端点,这个地方我还没有搞明白为什么,欢迎各位补充!

至于为什么0X18之后是0X58,是因为USB全速设备的最大包是64字节(十进制的0X40),所以这里PMA的划分就是:

  • 头部0X18字节为各个端点的描述
  • 0X18地址开始的64字节为输出端点0的缓冲区
  • 0X58地址开始的64字节为输入端点0的缓冲区
  • 0X98地址开始的64字节为输出端点1的缓冲区
  • 0XD8地址开始的64字节为输入端点1的缓冲区

这里注意一点,缓冲区分配好之后访问是不会溢出的,也就是说缓冲区之间完全隔离。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq153471503/article/details/116053851

智能推荐

从零开始搭建Hadoop_创建一个hadoop项目-程序员宅基地

文章浏览阅读331次。第一部分:准备工作1 安装虚拟机2 安装centos73 安装JDK以上三步是准备工作,至此已经完成一台已安装JDK的主机第二部分:准备3台虚拟机以下所有工作最好都在root权限下操作1 克隆上面已经有一台虚拟机了,现在对master进行克隆,克隆出另外2台子机;1.1 进行克隆21.2 下一步1.3 下一步1.4 下一步1.5 根据子机需要,命名和安装路径1.6 ..._创建一个hadoop项目

心脏滴血漏洞HeartBleed CVE-2014-0160深入代码层面的分析_heartbleed代码分析-程序员宅基地

文章浏览阅读1.7k次。心脏滴血漏洞HeartBleed CVE-2014-0160 是由heartbeat功能引入的,本文从深入码层面的分析该漏洞产生的原因_heartbleed代码分析

java读取ofd文档内容_ofd电子文档内容分析工具(分析文档、签章和证书)-程序员宅基地

文章浏览阅读1.4k次。前言ofd是国家文档标准,其对标的文档格式是pdf。ofd文档是容器格式文件,ofd其实就是压缩包。将ofd文件后缀改为.zip,解压后可看到文件包含的内容。ofd文件分析工具下载:点我下载。ofd文件解压后,可以看到如下内容: 对于xml文件,可以用文本工具查看。但是对于印章文件(Seal.esl)、签名文件(SignedValue.dat)就无法查看其内容了。本人开发一款ofd内容查看器,..._signedvalue.dat

基于FPGA的数据采集系统(一)_基于fpga的信息采集-程序员宅基地

文章浏览阅读1.8w次,点赞29次,收藏313次。整体系统设计本设计主要是对ADC和DAC的使用,主要实现功能流程为:首先通过串口向FPGA发送控制信号,控制DAC芯片tlv5618进行DA装换,转换的数据存在ROM中,转换开始时读取ROM中数据进行读取转换。其次用按键控制adc128s052进行模数转换100次,模数转换数据存储到FIFO中,再从FIFO中读取数据通过串口输出显示在pc上。其整体系统框图如下:图1:FPGA数据采集系统框图从图中可以看出,该系统主要包括9个模块:串口接收模块、按键消抖模块、按键控制模块、ROM模块、D.._基于fpga的信息采集

微服务 spring cloud zuul com.netflix.zuul.exception.ZuulException GENERAL-程序员宅基地

文章浏览阅读2.5w次。1.背景错误信息:-- [http-nio-9904-exec-5] o.s.c.n.z.filters.post.SendErrorFilter : Error during filteringcom.netflix.zuul.exception.ZuulException: Forwarding error at org.springframework.cloud..._com.netflix.zuul.exception.zuulexception

邻接矩阵-建立图-程序员宅基地

文章浏览阅读358次。1.介绍图的相关概念  图是由顶点的有穷非空集和一个描述顶点之间关系-边(或者弧)的集合组成。通常,图中的数据元素被称为顶点,顶点间的关系用边表示,图通常用字母G表示,图的顶点通常用字母V表示,所以图可以定义为:  G=(V,E)其中,V(G)是图中顶点的有穷非空集合,E(G)是V(G)中顶点的边的有穷集合1.1 无向图:图中任意两个顶点构成的边是没有方向的1.2 有向图:图中..._给定一个邻接矩阵未必能够造出一个图

随便推点

MDT2012部署系列之11 WDS安装与配置-程序员宅基地

文章浏览阅读321次。(十二)、WDS服务器安装通过前面的测试我们会发现,每次安装的时候需要加域光盘映像,这是一个比较麻烦的事情,试想一个上万个的公司,你天天带着一个光盘与光驱去给别人装系统,这将是一个多么痛苦的事情啊,有什么方法可以解决这个问题了?答案是肯定的,下面我们就来简单说一下。WDS服务器,它是Windows自带的一个免费的基于系统本身角色的一个功能,它主要提供一种简单、安全的通过网络快速、远程将Window..._doc server2012上通过wds+mdt无人值守部署win11系统.doc

python--xlrd/xlwt/xlutils_xlutils模块可以读xlsx吗-程序员宅基地

文章浏览阅读219次。python–xlrd/xlwt/xlutilsxlrd只能读取,不能改,支持 xlsx和xls 格式xlwt只能改,不能读xlwt只能保存为.xls格式xlutils能将xlrd.Book转为xlwt.Workbook,从而得以在现有xls的基础上修改数据,并创建一个新的xls,实现修改xlrd打开文件import xlrdexcel=xlrd.open_workbook('E:/test.xlsx') 返回值为xlrd.book.Book对象,不能修改获取sheett_xlutils模块可以读xlsx吗

关于新版本selenium定位元素报错:‘WebDriver‘ object has no attribute ‘find_element_by_id‘等问题_unresolved attribute reference 'find_element_by_id-程序员宅基地

文章浏览阅读8.2w次,点赞267次,收藏656次。运行Selenium出现'WebDriver' object has no attribute 'find_element_by_id'或AttributeError: 'WebDriver' object has no attribute 'find_element_by_xpath'等定位元素代码错误,是因为selenium更新到了新的版本,以前的一些语法经过改动。..............._unresolved attribute reference 'find_element_by_id' for class 'webdriver

DOM对象转换成jQuery对象转换与子页面获取父页面DOM对象-程序员宅基地

文章浏览阅读198次。一:模态窗口//父页面JSwindow.showModalDialog(ifrmehref, window, 'dialogWidth:550px;dialogHeight:150px;help:no;resizable:no;status:no');//子页面获取父页面DOM对象//window.showModalDialog的DOM对象var v=parentWin..._jquery获取父window下的dom对象

什么是算法?-程序员宅基地

文章浏览阅读1.7w次,点赞15次,收藏129次。算法(algorithm)是解决一系列问题的清晰指令,也就是,能对一定规范的输入,在有限的时间内获得所要求的输出。 简单来说,算法就是解决一个问题的具体方法和步骤。算法是程序的灵 魂。二、算法的特征1.可行性 算法中执行的任何计算步骤都可以分解为基本可执行的操作步,即每个计算步都可以在有限时间里完成(也称之为有效性) 算法的每一步都要有确切的意义,不能有二义性。例如“增加x的值”,并没有说增加多少,计算机就无法执行明确的运算。 _算法

【网络安全】网络安全的标准和规范_网络安全标准规范-程序员宅基地

文章浏览阅读1.5k次,点赞18次,收藏26次。网络安全的标准和规范是网络安全领域的重要组成部分。它们为网络安全提供了技术依据,规定了网络安全的技术要求和操作方式,帮助我们构建安全的网络环境。下面,我们将详细介绍一些主要的网络安全标准和规范,以及它们在实际操作中的应用。_网络安全标准规范

推荐文章

热门文章

相关标签