虚拟u盘下载(虚拟u盘)
STM32 USB知识扫盲实现快速应用
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设备分类看一个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 : 描述符大小.固定为0x07bDescriptorType : 接口描述符类型.固定为0x05bEndpointType : USB设备的端点地址.Bit7决定方向,1为IN端点,0为OUT端点,对于控制端点可以忽略;Bit6-4,保留;BIt3-0:端点号bmAttributes : 端点属性.Bit7-2,保留.BIt1-0:00控制,01同步,02批量,03中断wMaxPacketSize : 本端点接收或发送的最大信息包大小bInterval : 轮训数据传送端点的时间间隔.对于批量传送和控制传送的端点忽略.对于同步传送的端点,必须为1,对于中断传送的端点,范围为1-2554.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的缓冲区这里注意一点,缓冲区分配好之后访问是不会溢出的,也就是说缓冲区之间完全隔离。
声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送至邮件举报,一经查实,本站将立刻删除。转载务必注明出处:http://www.hixs.net/article/20240322/169625476385433.html