×
单片机 > 单片机程序设计 > 详情

USB Device应用笔记(基于STM32F103)

发布时间:2020-06-06 发布时间:
|

常见USB设备类

音频类(Audio),通信设备类(CDC),设备固件升级类(DFU),人机接口类(HID),大容量存储设备类(Mass Storage)


USB的数据由Packet(包)组成Transaction(事务),Transaction组成Transfer(传输),不同传输类型每Frame(帧)占用带宽的特性不同。同步传输每帧占用固定带宽;中断传输每帧都占用带宽,但所占带宽不固定;控制传输和批量传输在需要时才占用帧带宽,批量传输将会占用帧的所有剩余带宽。除同步传输外,一个Transaction由token包,数据包,握手包构成,STM32的每次中断都完成一个Transaction,token包和握手包的收发由硬件完成,数据包由应用程序完成。


四种传输类型

用于描述端点(Endpoint)或通道(Pipe)的特性 

1. 中断传输(Interrupt Transfer) 

2. 控制传输(Control Transfer) 

3. 同步传输(Isochronous Transfer) 

4. 批量传输(Bulk Transfer) 

同步传输与中断传输是周期性的,控制传输和批量传输是突发的


通道类型

数据流:单向 ->批量,中断,同步传输

消息: 双向 -> 控制传输

事务(Transaction)

分类:SETUP, OUT, IN 


Packet格式

SYNC Packet Content EOP 

其中,Packet Content的组成 

PID 地址 帧号 数据 CRC 

注意:不是每种包都包含完整Packet Content


Packet种类及其组成

命令包 token 

PID + ADDR(设备地址,端点地址) + CRC5

帧首包 SOF(Start of frame) 

PID + 帧号 + CRC5

数据包 DATA 

PID + 数据 + CRC16

握手包 Handshake 

PID

PID域的类型

Token 

IN, OUT, SETUP

SOF 

SOF

Data 

DATA0, DATA1, DATA2, DATAM

Handshake 

ACK, NAK, STALL, NYET/ERR(HS)

帧格式


说明: 

- USB规范规定SETUP分组不能以非ACK握手分组应答,如果SETUP分组失败,则会引起下一个SETUP分组。因此,以NAK或STALL分组响应主机的SETUP分组是被禁止的。 

- 控制传输使用双向端点


举例: Control Transfer的SETUP Transaction的组成 

SETUP Packet + DATA0 Packet + ACK Package 

SETUP包只跟以DATA0为PID的数据包,且数据包的方向为Host到Device。 


USB设备状态

插入 -> 供电 -> 复位 -> 地址 -> 配置 挂起 


实现一个USB设备的软件流程(以Mass Storage为例)

1. 系统初始化

(1)初始化系统时钟,配置USB时钟为48MHz; 

(2)清除挂起的中断标志; 

(3)复位USB模块 

(4)配置本应用关心的中断,设备状态为UNCONNECTED 

(5)初始化媒介层(SD卡,Flash)


2. USB复位(RESET中断)

(1)设置分组缓冲区基地址; 

(2)配置端点的类型,发送状态,接收状态,端点地址(EP_TYPE, EP_KIND, EA),发送缓冲区或接收缓冲区地址,接收端点还需要设置接收长度; 

(3)初始化设备地址为0,置位USB_DADDR. EF使能USB模块; 

(4)初始化BOT状态机,CBW.dSignature; 

(5)设备状态为ATTACHED


3. SETUP阶段和数据阶段

SETUP阶段使用标准请求和类特定请求完成设备枚举,由端点0的控制传输完成。 

IN过程 

(1)CTR_TX置位,发生中断,根据USB_ISTR的EP_ID和DIR位识别出端点号和方向; 

(2)清除USB_EPnR. CTR_TX位; 

(3)填写发送缓冲区,设置COUNTn_TX; 

(4)将STAT_TX设置为”11”,使能该端点的TX。 

OUT过程 

注意:SL_SIZE和NUM_BLOCK决定了最大接收字节数 

STAT_RX在接收数据后变为10(NAK) 

(1)CTR_RX置位,发生中断,根据USB_ISTR. EP_ID位和USB_ISTR. DIR位识别出端点号和方向; 

(2)根据USB_EPnR. SETUP位确定事务类型,是OUT还是SETUP,同时清除USB_EPnR. CTR_RX位; 

(3)读出缓冲区描述表指向的COUNTn_RX,获得此次传输的字节数; 

(4)从ADDRn_RX处获得数据; 

(5)使能下次接收,即设置USB_EPnR. STAT_RX位为”11”,使能该端点。


说明: 

- USB_CNTR. USB置位后所有的配置寄存器不会被复位,但设备的地址寄存器和端点寄存器会被USB复位所复位; 

- 使用双缓冲机制时,STAT_TX和STAT_RX不会因为完成一次IN/OUT分组而被置为NAK; 

- 对于同步端点,端点的状态只能是有效或者禁用,因此硬件不会在数据传输结束时改变端点的状态; 

- 端点地址不一定要与端点号一致,由于用4位二进制表示(EA[3:0]),故端点地址取值为0 - 15,端点0作为控制端点,必须使其端点地址为0;


描述符(Descriptor)分类

设备描述符(Device Descriptor)

配置描述符(Configuration Descriptor)

接口描述符(Interface Descriptor)

端点描述符(Endpoint Descriptor)

字符描述符(String Descriptor)

报告描述符(Report Descriptor)

(*pProperty->Init)()

(*pProperty->Init)()完成了全速/低速设备上拉电阻检测,复位,设置应用关心的中断屏蔽位 

CNTR. CTRM,CNTR. RESETM,未完成端点传输类型,缓冲区描述表的配置,设备地址DADDR的配置(仅置0并置位USB_DADDR. EF来使能USB),在函数的最后:bDeviceState = UNCONNECTED 

而USB_Init()的下一条语句是while (bDeviceState != CONFIGURED),显然接下来应该在中断中完成缓冲区描述表填写(USB_BTABLE),端点传输类型(USB_EPnR),枚举过程的工作。


//USB中断服务函数

USB_Istr()

{

...

#if (IMR_MSK & ISTR_RESET)

  if (wIstr & ISTR_RESET & wInterrupt_Mask)

  {

    _SetISTR((uint16_t)CLR_RESET);

    Device_Property.Reset();

#ifdef RESET_CALLBACK

    RESET_Callback();

#endif

  }

#endif

...

}


Device_Property.Reset()完成以下工作:

Device_Info.Current_Configuration = 0(同前)

pInformation->Current_Feature = MASS_ConfigDescriptor[7]

设置Buffer table的地址(BTABLE_ADDRESS) ,设备地址默认为0

初始化EP 

EP0:控制类型;发送NAK、接收Valid;设置接收buffer地址和长度;设置发送buffer地址

EP1:批量类型;发送NAK、接收Disable;设置发送buffer地址

EP2:批量类型;发送Disable、接收VALID;设置接收buffer地址和长度

bDeviceState = ATTACHED 全局变量,表示设备当前已被插入主机


关于Setup0_Process()中调用DataStageIn()的原因

Setup0_Process() - > 处理标准request / class相关request -> 根据数据阶段:如果是IN -> DataSatgeIn() 

USB Device收到IN Packet且地址正确后,访问ADDRn_TX和COUNTn_TX,将相应缓冲区的内容通过移位寄存器发送出去,并等待Host发送ACK Package。Device收到ACK后toggle DTOG_TX位,硬件设置STAT_TX位为”10”(NAK),使端点无效,CTR_TX置位。应用程序需要清除中断标志CTR_TX,把下次要发送的内容写进ADDRn_TX指向的hw_buf,更新COUNTn_TX为需要发送的字节数,然后设置STAT_TX为”11”(VALID),使能数据传输。当STAT_TX为”10”(NAK)时,所有IN请求都会被NAK,Host不断重发IN请求,直到该端点有效。


每次处理CTR_TX中断时,写进hw_buf的内容都是下次要发送的内容,因此,在首次处理IN请求之前需要准备好hw_buf,如果SETUP的数据阶段是IN,就要在Setup0_Process()中调用DataStageIn()填充首次需要准备的内容。


关于端点地址的设置

In0_Process() -> WAIT_STATUS_IN -> 如果是SET_ADDR命令:写寄存器 -> 设置各端点地址 

如果端点地址需要设置成与端点号不一致,需要修改本函数的部分


@

for(i=0; i

_SetEPAddress((uint8_t)i, (uint8_t)i);

}


修改为:


@

_SetEPAddress(0,0);

_SetEPAddress(1,EP_ADDR1);

_SetEPAddress(2,EP_ADDR2);

非零端点的处理


USB请求格式

USB请求由SETUP transaction完成,一条完整的USB请求存放在其中的DATA0 packet中,字节序为小端模式。


偏移量 域 长度 描述

0 bmRequestType 1 请求特征

D7:传输方向

0=主机至设备

1=设备至主机

D6..5:种类

0=标准

1=类

2=厂商

3=保留

D4..0:接受者

0=设备

1=接口

2=端点

3=其他

4..31:保留

1 bRequest 1 命令类型编码值

2 USBwValue 2 根据不同的命令,含义不同

4 USBwIndex 2 根据不同的命令,含义不同,主要用于传送索引或偏移

6 USBwLength 2 数据阶段的字节数,如果没有数据阶段则填0

USB Mass Storage Class

USB Mass Storage使用BOT(Bulk only Transfer)协议和SCSI指令来处理传输。相对于CBI(Control Bulk Interrupt)协议,BOT协议仅需要控制端点,一个Bulk IN端点和一个Bulk OUT端点即可完成命令、数据、状态的传输,BOT状态机如下图所示: 


CBW(Command Block Wrapper)是一个31字节长的包,由Host发起,格式如下: 

 

dCBWSignature: 0x43425355 

dCBWTag: 用户定义标签,dCSWTag应当原样返回 

dCBWDataTransferLength: 主机期望传输的数据长度。 

bmCBWFlags: 主要定义数据的传输方向,由bit 7定义(0-out, 1-in),其他比特默认为0 

bCBWLUN: 逻辑单元号 

bCBWCBLength: CB的有效长度 

CBWCB: 设备执行的命令块,这里是SCSI命令,一般是16字节

 

CSW(Command Status Wrapper)是Device收到Host发送的CBW并完成数据传输后向Host发送有关状态信息的包,长度为13字节,格式如下: 

  

dCSWSignature: 0x53425355 

dCSWTag: 应当与dCBWTag一致 

dCSWDataResidue: 

bCSWStatus: CBW传输的成功或失败状态,为0表示传输成功,非0表示传输失败, 如下表所示 


Class-Specific requests 

BOT协议要求支持两个类相关请求: 

1. Bulk-only mass storage reset 

该请求用于复位Mass Storage设备及与其相关的接口。Device接收到请求后,清除两个Bulk端点的data toggle,初始化CBW signature到默认值,设置BOT状态机到BOT_IDLE状态,以准备接收下一个CBW。 

该请求在Ma


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
实验八 交通灯控制(80C51单片机汇编语言编程)