网站制作产品资料,电影网站权重怎么做,服务器windos做网站,中迅做网站是模板站吗开篇感谢#xff1a;
【经验分享】STM32 USB相关知识扫盲 - STM32团队 ST意法半导体中文论坛
单片机学习记录_桃成蹊2.0的博客-CSDN博客
USB_不吃鱼的猫丿的博客-CSDN博客
1、USB鼠标_哔哩哔哩_bilibili
usb_冰糖葫的博客-CSDN博客
USB_lqonlylove的博客-CSDN博客
USB …开篇感谢
【经验分享】STM32 USB相关知识扫盲 - STM32团队 ST意法半导体中文论坛
单片机学习记录_桃成蹊2.0的博客-CSDN博客
USB_不吃鱼的猫丿的博客-CSDN博客
1、USB鼠标_哔哩哔哩_bilibili
usb_冰糖葫的博客-CSDN博客
USB_lqonlylove的博客-CSDN博客
USB -- STM32F103 USB AUDIO音频Speaker同步传输Out传输讲解七_stm32 usb audio-CSDN博客
基于SMT32的USB键盘制作附稚晖君键盘方案分析_大颜u的博客-CSDN博客
基础知识
等级划分 接口类型 STM32基础型(F1系列)所带的USB是全速。
电气属性
USB的通信都是由主机发起的这一点与IIC协议是类似的。
数据线
USB使用差分传输模式有两条数据线分别是
USB数据正信号线USB Data Positive即USB-DP线简写为D
USB数据负信号线USB Data Minus 即USB-DM线简写为D-
剩下的就是电源线(5V-Vbus)和地线(GND)。
USB主机是如何识别设备是高速设备/全速设备/低速设备
主机的D和D-都接有15K下拉电阻。
全速USB设备的数据线D接有1.5K的上拉电阻一旦接入主机主机的D被拉高
低速USB设备的数据线D-接有1.5K的上拉电阻一旦接入主机主机的D-会被拉高
因此主机就可以根据检测到自己的D为高还是D-为高从而判断接入的设备是一个全速还是低速设备。
所以你可以看到STM32板子上的USB口D有一个上拉电阻而且是必须有的 USB设备分类 看一个CDC虚拟串口的设备分类 在看一个MSC的模拟U盘 不同的类有不同的用途不同的应用场合对应不同的产品形态不同的产品形态可能会有自己特殊的描述符比如 HID类有报告描述符、CDC类有ACM、Union描述符等。
常用USB设备类大容量存储类Mass Storage Class - MSC人机接口设备类Human Interface Device - HID音频设备类Audio Device Class视频设备类Video Device Class成像设备类Imaging Device Class打印机设备类Printer Device Class
描述符详解
以STM32里的MSC设备为例MSC类所需要的描述符有设备描述符配置描述符接口描述符(数量由配置描述符里的bNumInterfaces字段决定)端点描述符(数量由配置描述符里的bNumEndpoints决定)
设备描述符
其中1个USB设备只能有一个设备描述符但是其他描述符可以有多个。
一般实现USB复合设备的方法主要是一个设备描述符、一个配置描述符、多个接口描述符、多个端点描述符......
每个USB设备都必须且只有一个设备描述符摘取一下STM32的MSC设备里的实例代码 每个字段的含义
bLength描述符大小固定为0x12
bDescriptorType设备描述符类型固定为0x01
bcdUSBUSB 规范发布号表示了本设备能适用于那种协议如2.002001.10110
bDeviceClass类型代码由USB指定。当它的值是0时表示所有接口在配置描述符里并且所有接口是独立的。当它的值是1到FEH时表示不同的接口关联的。当它的值是FFH时它是厂商自己定义的
bDeviceSubClass子类型代码由USB分配如果 bDeviceClass值是0一定要设置为0。其它情况就跟据USB-IF组织定义的编码
bDeviceProtocol协议代码由USB分配如果使用USB-IF组织定义的协议就需要设置这里的值否则直接设置为0。如果厂商自己定义的可以设置为FFH
bMaxPacketSize0端点最大分组大小只有8,16,32,64有效
idVendor供应商ID由USB分配
idProduct 产品ID由厂商分配)由供应商ID和产品ID就可以让操作系统加载不同的驱动程序
bcdDevice 设备出产编码由厂家自行设置
iManufacturer 厂商描述符字符串索引索引到对应的字符串描述符 为则表示没有
iProduct 产品描述符字符串索引同上
iSerialNumber设备序列号字符串索引同上
bNumConfigurations 可能的配置数指配置字符串的个数。
bDeviceClass、bDeviceSubClass和bDeviceProtocol可在USB官网进行查询idVendor是向USB机构申请的需要支付一笔费用如果针对开发者可以选择芯片厂商的idVendor进行开发。 配置描述符
配置描述符定义了设备的配置信息一个设备可以有多个配置描述符。摘一个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为单位0x32表示 100mA
接口描述符
接口描述符说明了接口所提供的配置一个配置所拥有的接口数量通过配置描述符的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 : 使用的端点数目端点除外
bInterfaceClass : 类型代码由USB分配
bInterfaceSubClass : 子类型代码由USB分配
bInterfaceProtocol : 协议代码由USB分配
iInterface : 字符串描述符的索引
端点描述符
USB设备中的每个端点都有自己的端点描述符由接口描述符中的bNumEndpoint决定其数量。摘取STM32的MSC设备类的端口描述符 用C语言组合就是这样的一个结构
typedef struct _USB_ENDPOINT_DESCRIPTOR_ {BYTE bLength,BYTE bDescriptorType,BYTE bEndpointAddress,BYTE bmAttributes,BYTE bInterval
}USB_ENDPOINT_DESCRIPTOR;每个字段含义如下
bLength : 描述符大小固定为0x07
bDescriptorType : 接口描述符类型固定为0x050
bEndpointType : USB设备的端点地址Bit7决定方向1为IN端点0为OUT端点对于控制端点可以忽略Bit6-4保留BIt3-0端点号
bmAttributes : 端点属性Bit7-2保留BIt1-000控制01同步02批量03中断
wMaxPacketSize : 本端点接收或发送的最大信息包大小
bInterval : 轮训数据传送端点的时间间隔对于批量传送和控制传送的端点忽略对于同步传送的端点必须为对于中断传送的端点范围为1-255
字符串描述符
字符串描述符是可选的如果不支持字符串描述符其设备描述符、配置描述符、接口描述符内的所有字符串描述符索引都必须为0。 字符串描述符结构如下
typedef struct _USB_STRING_DESCRIPTION_BYTE bLength,BYTE bDescriptionType,BYTE bString[1];
}USB_STRING_DESCRIPTION;
各个字段含义
bLength : 描述符大小由整个字符串的长度加上bLength和bDescriptorType的长度决定
bDescriptorType : 接口描述符类型固定为0x03
bString[1] : Unicode编码字符串
IAD描述符
USB组合设备一般用Interface Association DescriptorIAD实现就是在要合并的接口前加上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是0X02MSC是0X08BYTE bFunctionSubClass: 0x03 //子类型代码BYTE bFunctionProtocol: 0x00 //协议代码BYTE iFunction: 0x04 //描述字符串索引
}
以MSCCDC为例他的配置描述符结构就是这样的
配置描述符
{IAD描述符1CDC{接口描述符1通信接口{其他描述符特殊描述符{/*Header Functional Descriptor*//*Call Management Functional Descriptor*//*ACM Functional Descriptor*//*Union Functional Descriptor*/}端点描述符(命令端点){}}接口描述符2数据接口{端点描述符1输出端口{}端点描述符2输入端口{ }}}IAD描述符(MSC){接口描述符1{端点描述符1{}端点描述符2{}}}
}
其它描述符 Packet的组成
Packet主要由下面四部分组成
SOP起始帧从DILE状态(J状态)切换到K状态
SYNC同步域3个重复的KJ状态切换后跟随2个位时间的K状态
Packet Connent内容域主要包括以下内容
PID包标识
地址设备地址
帧号11位帧号
数据通信的数据
CRC校验
EOP结束帧持续2个位时间的SE0信号后跟随1个位时间的J状态
接下来重点讲解一下Packet Connent域里面的内容。 Packet的内容
Packet包的内容--PID域 PIDPacket Identifier包标识LSB在前前4个字节为PID码后四个字节是前四个字节的取反。
以下是PID的类型码。 Packet包的内容--地址域 地址由7位的设备地址和4位的端点地址组成。 Packet包的内容--帧号域 帧号域由以下特性
11位
主机每发出一个帧帧号都会自动加1全速和低速设备1ms发出一帧 高速设备0.125ms发出一帧
当帧号达到0x7FFF的时候将清零帧号重新计数
仅在每个帧的帧首才传输一次SOF包
Packet包的内容--数据域 根据传输类型的不同数据域的数据长度从0-1024字节不等。
Packet包的内容--CRC域 Packet的类型
Packet包的类型--令牌包 令牌包的组成为PID地址CRC
PIDIN、OUT、SETUP令牌
地址7位的设备地址和4位的端点号组成
CRC这里是对地址域计算 Packet包的类型--SOF包 SOF包的组成为PID帧号CRC
PIDSOF令牌
帧号LS/FS每1ms一个帧HS每0.125ms一个帧
CRC这里是对帧号域计算 Packet包的类型--数据包 数据包的组成为PID数据CRC
PIDDATA0、DATA1、DATA2、MDATA令牌
数据传输类型不同数据包的最大长度有所不同
CRC这里是对数据域计算 Packet包的类型--握手包 握手包的组成为PID
PIDACK、NAK、STALL、NYET令牌
ACK表示正确接收数据并且有足够的空间来容纳数据。主机和设备都可以用ACK来确认而NAK、STALLA、NYET只能设备返回主机不能使用这些握手包。
NAK表示没有数据需要返回或者数据正确接收但是没有足够的空间来容忍它们。 当主机收到NAK时知道设备还未准备好主机会在以后的合适的时机进行重新传输。
STALL表示设备无法执行这个请求或者端点已经被挂起它表示一种错误的状态。 设备返回STALL后需要主机进行干预才能解除这种STALL状态。
NYET只在USB2.0的高速设备输出事物中使用表示设备本次数据成功接收但是没有 足够的空间来接收下一次数据。主机在下一次输出数据时将先使用PING令牌包来试探 设备是否有空间接收数据以避免不必要的带宽浪费。
注意
1. 当收到SETUP包的时候设备只能回复ACK
2. 当同步传输的时候没有握手包。 传输类型
USB的传输类型分为控制传输、中断传输、批量传输和同步传输每种传输类型用在特定的场合。
控制传输
非周期性传输用于命令和状态的传输。每个USB设备都必须有控制端点支持控制传输来进行命令和状态的传输。USB主机驱动将通过控制传输与USB设备的控制端点通讯完成USB设备的枚举和配置。
控制传输是双向的传输必须有IN和OUT两个方向上的特定端点号的控制端点来完成两个方向上的控住传输。 中断传输
周期性低频率传输允许有限延迟的通信中断传输用于那些频率不高但对周期有一定要求的数据传输。具有保证的带宽并能在下个周期对先前错误的传输进行重传对于全速端点中断传输的时间间隔在1ms到255ms之间对于低速端点时间间隔限制在10ms到255ms之间对于高速端点 时间间隔为2bInterval-1*125usbInterval的值在1到16之间。
中断传输总算单向的可以用单向的中断端点来实现某个方向上的中断传输。 批量传输
非周期性大容量数据的通信数据可以占用任意带宽。大容量数据传输适用于那些需要大数据量传输但是对实时性对延迟性和带宽没有严格要求的应用。大容量传输可以占用任意可用的数据带宽。
大容量传输是单向的可以用单向的大容量传输端点来实现某个方向的大容量传输。 同步传输
周期性持续的传输用于传输与时效相关的信息并且在数据中保存时间戳的信息。同步传输用于传输那些需要保证带宽并且不能忍受延迟的信息。整个带宽都将用于保证同步传输的数据完整并且不支持出错重传。
同步传输总是单向的可以使用单向的同步端点来实现某个方向上的同步传输。 复位挂起和唤醒机制
USB使用的差分传输模式两个数据线D和D-VOH2.8V VOL0.3V
差分信号1D VOH and D- VOL
差分信号0D- VOH and D VOL
IDLE状态J状态
复位信号D and D- VOL for 10ms
挂起信号USB主机3m内不发送任何信号3ms以上的IDLE状态
唤醒信号K状态持续20ms以上并以低速EOP信号结尾。 USB的枚举过程
枚举就是从设备读取一些信息知道设备是什么样的设备如何进行通信这样主机就可以根据这些信息来加载合适的驱动程序。调试USB设备很重要的一点就是USB的枚举过程只要枚举成功了那么就已经成功大半了。
枚举通信过程具体如下
step1检测电压变化报告主机
首先USB设备上电后一直监测USB设备接口电平变化HUB检测到有电压变化将利用自己的中断端点将信息反馈给主控制器有设备连接。
Step2主机了解连接设备
主机在知道有设备接入后会发送一个Get_Port_Status请求(request)给hub以了解此次状态改变的确切含义。
Step3Hub检测所插入的设备是高速还是低速
hub通过检测USB总线空闲(Idle)时差分线的高低电压来判断所连接设备的速度类型当host发来Get_Port_Status请求时hub就可以将此设备的速度类型信息回复给host。USB 2.0规范要求速度检测要先于复位Reset操作。
Step4hub复位设备
主机一旦得知新设备已连上以后它至少等待100ms以使得插入操作的完成以及设备电源稳定工作。然后主机控制器就向hub发出一个 Set_Port_Feature请求让hub复位其管理的端口(刚才设备插上的端口)。hub通过驱动数据线到复位状态(D和D-全为低电平 )并持续至少10ms。当然hub不会把这样的复位信号发送给其他已有设备连接的端口所以其他连在该hub上的设备自然看不到复位信号不受影响。
Step5 Host检测所连接的全速设备是否是支持高速模式
因为根据USB 2.0协议高速High Speed设备在初始时是默认全速Full Speed 状态运行所以对于一个支持USB 2.0的高速hub当它发现它的端口连接的是一个全速设备时会进行高速检测看看目前这个设备是否还支持高速传输如果是那就切到高速信号模式否则就一直在全速状态下工作。
同样的从设备的角度来看如果是一个高速设备在刚连接bub或上电时只能用全速信号模式运行根据USB 2.0协议高速设备必须向下兼容USB 1.1的全速模式。随后hub会进行高速检测之后这个设备才会切换到高速模式下工作。假如所连接的hub不支持USB 2.0即不是高速hub不能进行高速检测设备将一直以全速工作。
Step6Hub建立设备和主机之间的信息通道
主机不停地向hub发送Get_Port_Status请求以查询设备是否复位成功。Hub返回的报告信息中有专门的一位用来标志设备的复位状态。
当hub撤销了复位信号设备就处于默认空闲状态Default state准备接收主机发来的请求。设备和主机之间的通信通过控制传输默认地址0端点号0进行。此时设备能从总线上得到的最大电流是100mA。(所有的USB设备在总线复位后其地址都为0这样主机就可以跟那些刚刚插入的设备通过地址0通信。)
Step7主机发送Get_Descriptor请求获取默认管道的最大包长度
默认管道Default Pipe在设备一端来看就是端点0。主机此时发送的请求是默认地址0端点0虽然所有未分配地址的设备都是通过地址0来获取主机发来的请求但由于枚举过程不是多个设备并行处理而是一次枚举一个设备的方式进行所以不会发生多个设备同时响应主机发来的请求。
设备描述符的第8字节代表设备端点0的最大包大小。虽然说设备所返回的设备描述符Device Descriptor长度只有18字节但系统也不在乎此时描述符的长度信息对它来说是最重要的其他的瞄一眼就过了。当完成第一次的控制传输后也就是完成控制传输的状态阶段系统会要求hub对设备进行再一次的复位操作USB规范里面可没这要求。再次复位的目的是使设备进入一个确定的状态。
Step8主机给设备分配一个地址
主机控制器通过Set_Address请求向设备分配一个唯一的地址。在完成这次传输之后设备进入地址状态Address state之后就启用新地址继续与主机通信。这个地址对于设备来说是终生制的设备在地址在设备消失被拔出复位系统重启地址被收回。同一个设备当再次被枚举后得到的地址不一定是上次那个了。
Step9主机获取设备的信息
主机发送 Get_Descriptor请求到新地址读取设备描述符这次主机发送Get_Descriptor请求可算是诚心它会认真解析设备描述符的内容。设备描述符内信息包括端点0的最大包长度设备所支持的配置Configuration个数设备类型VIDVendor ID由USB-IF分配 PIDProduct ID由厂商自己定制等信息。
之后主机发送Get_Descriptor请求读取配置描述符Configuration Descriptor字符串等逐一了解设备更详细的信息。事实上对于配置描述符的标准请求中有时wLength一项会大于实际配置描述符的长度9字节比如255。这样的效果便是主机发送了一个Get_Descriptor_Configuration 的请求设备会把接口描述符端点描述符等后续描述符一并回给主机主机则根据描述符头部的标志判断送上来的具体是何种描述符。
接下来主机就会获取配置描述符。配置描述符总共为9字节。主机在获取到配置描述符后根据里面的配置集合总长度再获取配置集合。配置集合包括配置描述符接口描述符端点描符等等。
如果有字符串描述符的话还要获取字符串描述符。另外HID设备还有HID描述符等。
Step10 主机给设备挂载驱动复合设备除外
主机通过解析描述符后对设备有了足够的了解会选择一个最合适的驱动给设备。 然后tell the worldannounce_device说明设备已经找到了最后调用设备模型提供的接口device_add将设备添加到 usb 总线的设备列表里然后 usb总线会遍历驱动列表里的每个驱动调用自己的 matchusb_device_match 函数看它们和你的设备或接口是否匹配匹配的话调用device_bind_driver函数现在就将控制权交到设备驱动了。
对于复合设备通常应该是不同的接口Interface配置给不同的驱动因此需要等到当设备被配置并把接口使能后才可以把驱动挂载上去。
Step11设备驱动选择一个配置
驱动注意这里是驱动之后的事情都是有驱动来接管负责与设备的通信根据前面设备回复的信息发送Set_Configuration请求来正式确定选择设备的哪个配置Configuration作为工作配置对于大多数设备来说一般只有一个配置被定义。至此设备处于配置状态(Configured)当然设备也应该使能它的各个接口Interface。
对于复合设备主机会在这个时候根据设备接口信息给它们挂载驱动。 以下是一个简单的枚举过程帮助大家理解软件层面的协议。
Host [ 80 06 00 01 00 00 40 00 ] 主机你是什么设备
Device [ 12 01 00 02 0A 00 00 40 70 34 08 00 00 02 01 02 03 01 ] 设备我是CDC设备
Host [ 00 05 21 00 00 00 00 00 ] 主机设置唯一设备地址地址只能从0-127以后使用此地址和设备通讯
Host [ 80 06 00 01 00 00 12 00 ] 主机你是什么设备
Device [ 12 01 00 02 02 02 02 40 70 34 08 00 00 02 01 02 03 01 ] 设备我是CDC设备
Host [ 80 06 00 02 00 00 FF 00 ] 主机你有几个接口每个接口使用了哪几个端点
Device [ 09 02 43 00 02 01 00 C0 32 09 04 00 00 01 02 02 01 00 05 24 00 10 01 05 24 01 00 01 04 24 02 02 05 24 06 00 01 07 05 82 03 08 00 FF 09 04 01 00 02 0A 00 00 00 07 05 03 02 40 00 00 07 05 81 02 ]
Device [ 40 00 00 ] 设备我有2个接口一个接口使用了输入端点2一个接口使用了输入端点1和输出端点3
Host [ 80 06 00 03 00 00 FF 00 ] 主机你的字符串使用的是什么编码格式
Device [ 04 03 09 04 ] 设备我使用的美国的编码格式
Host [ 80 06 02 03 09 04 FF 00 ] 主机你的产品名称是什么
Device [ 32 03 53 00 54 00 4D 00 33 00 32 00 20 00 56 00 69 00 72 00 74 00 75 00 61 00 6C 00 20 00 43 00 4F 00 4D 00 20 00 50 00 6F 00 72 00 74 00 20 00 20 00 ] 设备我的产品名称是…
Host [ 80 06 03 03 09 04 FF 00 ] 主机你的设备UID是什么
Device [ 1A 03 35 00 43 00 44 00 45 00 35 00 38 00 34 00 30 00 33 00 32 00 33 00 30 00 ] 设备我的
UID是....
STM32相关讲解
STM32-USB详解 这个512字节SRAM叫做Packet Buffer Memory Area(简称PMA)这个很重要后面会详细讲解。 根据描述可以一共有8个端点16个寄存器一个端点关联两个寄存器所以我们可以将他们规划为8个输入端点(0x80-0X87)8个输出端点(0X00-0X07)。
STM32-PMA详解
先说一下USB的数据包大小全速设备的最大包大小为64字节高速最大为1024字节STM32是USB全速设备所以最大为64字节。
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头部的3x824(十六进制的0X18)字节就是描述倘若你使用的是0、1、3这三个端点其中编号为2的端点虽然没使用但是占用空间那么PMA头部的端点描述就是4x832字节编号为2的端点8字节的空间就浪费了(严格来说没浪费就是不方便使用)。
头部的端点描述之后就是各个端点的缓冲区了例如使用了0、1、2三个端点占用了PMA头部3x824字节的空间那么这三个端点的缓冲区地址就是从PMA偏移24字节开始的当然只要是大于24就都可以这里就是最关键的地方了很多人修改ST官方库实现自己USB应用时候就是没改这里的地址导致缓冲区的使用覆盖了PMA头部的端点描述从而出错
下面摘取一个STM32官方MSC设备的实例进行分析 可以看到一共用到了4个端点分别是输入端点0X80和0X81输出端点0X00和0X01其中0X00端点和0X80端点是供USB使用必须有的0X81和0X01端点则是MSC设备输入输出端点。
那么一共使用了4个端点按理来说PMA头部的端点描述大小应该是4X832(十六进制的0X20)字节0X20之后的才是各个端点缓冲区但是ST这里的却是从0X18开始也就是说使用了三个端点这个地方我还没有搞明白为什么欢迎各位补充
至于为什么0X18之后是0X58是因为USB全速设备的最大包是64字节(十进制的0X40)所以这里PMA的划分就是
头部0X18字节为各个端点的描述
0X18地址开始的64字节为输出端点0的缓冲区
0X58地址开始的64字节为输入端点0的缓冲区
0X98地址开始的64字节为输入端点1的缓冲区
0XD8地址开始的64字节为输出端点1的缓冲区
这里注意一点缓冲区分配好之后访问是不会溢出的也就是说缓冲区之间完全隔离。
STM32F1-HAL库中 USB外设库的文件介绍
STM32_USB_Host_Library 中的文件介绍 STM32_USB_Device_Library 中的文件介绍 这篇内容很多很难完全看完对于像弄明白USB原理和过程的有一定意义但是对于只想快速做出东西的可以直接看后面的文章。