USB作为一种串行接口,应用日益广泛。如同每个工程设计人员必须掌握I2C,RS232这些接口一样,我们也必须掌握USB.但是USB的接口协议实在有点费解,Linux UCHI驱动作者之一Alan Stern曾经就说过“The USB documentation is downright evil. Most of it is just crap, written by a committee. You're better off ignoring most of it ”。本文将从整体上介绍USB 协议,包括USB host ,USB hub,USB Function。希望能给读者一个总体上的了解。也因此,文章将分成相应的三部分讲解 。

一、USB Function

1、初识USB .

      USB 是一种串行接口协议,它靠D+,D-两条数据线构成的差分线来进行数据传输,这让我们非常感兴趣它到底和我们通常熟悉两线 RS232/485有何区别。了解这种区别有助于我们对USB 作一个深入的了           解。那么让我们回想一下到底一个两线RS232的数据是如何传送的,如图一:

         在这里我们的重点在于,我们发现要在串行口传送数据一个最体码的要求恐怕就是:要知道数据传输何时开始,何时结束。即如何delimit.那么 RS232怎么做的。显然,在idle(空闲)时,即无数据传送时,       数据线处于高电平,等到有数据开始传送,发送方首先拉低数据线(start),表示数 据传输开始,接受端也因为这个“start”信号开始准备接受即将到来的数据,类似一次握手,随后,在两者之间的数据传送开始,     结束后主方再次拉高数据 线,表示结束传输,自此两者重新进入Idle状态。等待下一轮传送开始。了解了RS232,那么我们自然想到USB如何做到这个呢,既然是串行位流传输,也理所当然的解决这个问题。没         错,USB协议必然要解决这个问题,让我们作一个类似RS232的比较吧!类似于RS232,USB的传输桢如图二:

(这里我们暂时忽略这个传输所代表的意义)为了说明问题,我们对一些问题简化,我们定义这样几个状态:
        假设D+,D-分别表示USB信号线的电平信号。那么对于USB full speed Function(high speed ,low speed是不同的),我们定义差分数据线上可能出现的四个状态:

Data J state:D+=1,D-=0;

Data K state:D+=0,D-=1;

SE0:D+=D-=0;

SE1:D+=D-=1;

那么上面的图中,相应的也可表示为图三:

    这个对USB full speed Function来说,idle状态将处于Data J state,se0表示一桢结束。看这个图是不是很像我们熟悉的RS232。没错!!!他们确实很相似。在无数据传输时,它们都处于Idle状态,当  要开 始传输数据时,先发一个sync(同步信号,RS232为start,USB为一sync字节,见协议说明)信号进行“握手”,而后开始传输,当传输要结 束时,发一stop信号(USB为一个se0状态表示要结束传输),最  后又进入idle态等待新的传输。不过,你可能更加注意到,他们还是不同的。不同在 于USB是按"packet" 进行传输的,就是说它传输的最小单位是packet,而RS232是按字节传送的,也即它的最小传送单位是  字节。既然是按pakcet传送,想想我们相较 于RS232的按字节单位传输,我们可以得到哪些“好处”。想想看,pakcet的好处不就在于我们可以灵活的定义数据的传送格式,传送方式,从而可以适 应各种各样的  串行设备,这不就是所谓的“通用串行总线”吗?

  简介:从本节开始,我们将介绍USB的传输机制。这节先介绍USB现有传输方式的背景知识,做为对下节将要展开的四种传输类型,描述符,等相关知识的一个导引。

2、USB传输。
    我们在上一节中了解到了USB的“packet”的感念,了解到了USB传送一个packet总是以sync开始,以 eop结束,这个称为delimiter,即标记packet的始末。有了packet,我们就可以在USB总线上传输数据  了。但是这还不够,比如数据传 送方向,即传回USB主机还是传下USB从机,数据传送的地址,数据传送的类型(这些后面我们将会知道)这些信息在传输之前是必须搞清楚的,那么这个信息 如何得知呢,看来  这就需要我们定一套基于packet的“协议”了。主机与从机在传输中均遵循这套“协议”,那么这些问题就可以迎刃而解。事实上,USB的一次数据传输总是遵循这样的“协议”的:

    首先,主机发第1个packet给从机,声明数据传送方向,数据传输地址,数据传输类型。
    其次,主机发第2个至第n个packet载有实际数据
    最后,从机返回一个packet是一个ACK包,报告数据传输的结果,比如接受出错或成功等信息,这样主机就可以借此了解到这次传输情况,从而有可能来作出相应措施如决定是否重发

  这里我们考虑的是主机发数据给从机的情况,那么从机发数据给主机时,是不是也可以这样呢?当然可以,比如从机要发数据给主机时,也可以采取同主机类似的方式:

    首先,从机发第1个packet给主机,声明数据传送方向,数据传输地址,数据传输类型。
    其次,从机发第2个至第n个packet载有实际数据
    最后,主机返回一个packet是一个ACK包,报告数据传输的结果,比如接受出错或成功等信息,这样从机就可以借此了解到这次传输情况,从而有可能来作出相应措施如决定是否重发。

  基本上可以归结为一个“三段式”传输

  这里有人可能注意到了,对这样一个传输机制,从机和主机的功能将是一样的,因为这样的实现机制,从机可能在某一时刻是主机,某一时刻又可能是从机,因为他们要实现同样的功能。这样实现起来的复杂性也  将是一样的。

  注:这里概念或许容易混淆,其实,我们这里的主机(master)和从机(slaver)是一个transceiver,即可收可发。相应的,在某一时刻,master在发数据,我们称其为transmitter,在接受时我们称为receiver.对     slaver同样。

    我们可能还注意到了,USB这种按pakcet传输的方式在实现时已经很复杂了(至少比RS232要复杂多吧),至少我们目前看来主从机功能一样这 样的实现方式似乎还是可行,但是后面我们谈到USBhost时将  会了解到host的功能是如何的复杂,以至于让一个USBFunction 也带上如此的功能成本和实现复杂性将陡然上升。作为面向广范应用的USB,这是我们不允许的。我们期望的是一个使用USB的udisk,使用USB  的光驱,使用USB的耳麦等等这些东西不要因为USB而变得昂贵,复杂。正是因为这个原因,USB从机的传输发式便由上面的方式改成了下面的方式进行:

    首先,主机发第1个packet给从机,声明数据传送方向,数据传输地址,数据传输类型。
    其次,从机收到主机送来的第一个packet后,再发第2个至第n个packet载有实际数据
    最后,主机返回一个packet是一个ACK包,报告数据传输的结果,比如接受出错或成功等信息,这样从机就可以借此了解到这次传输情况,从而有可能来作出相应措施如决定是否重发。

  而对于USB主机传输方式保持不变。

    对于这样的改变,我们马上就有疑问了:这个改变的传输方式是和未改变之前的等价吗。当然,不全等价。问题在哪里?仔细观察一下便知,两者区 别在于第一个packet是由谁发起的。未改变之前,第一个  packet总是由要传送数据的一方发起,而改变之后的第一个Packet总是由主机发起。这 样,就变成如果从机要发送数据给主机时,总是由主机发起(第一个packet),然后从机开始传送。
  可能初次接触我们会感觉怪怪的,怎么从机要给主机发送数据前反而要主机先发packet给从机。   这样行吗,我们要说这样是可以的,因为通常一次传输交互的产生,并非无来由的产生,这些都是由程序员控制  的,控制USB何时收,何时发,及发给谁!!!

    这里我们就注意到了,USB Function(总是作为从机)的功能一下从原来与主机具有相同功能的tranceiver变成了现在仅具发送(或接收)功能的transmitter(或Receiver)实现的复杂性及成本可想而知也  就相应得减小了。

  简介:本节介绍USB full speed Function的四种传输类型。

    上节中我们了解到了USBhost 与USBFunction 之间采用的是一种“非对称”的传输,也就是说,无论USB接受数据还是发送数据,都是由USBhost首先发起。即传输的第一个packet总是由USBhost发出   的。这个packet将声明本次即将进行的数据传输方向,数据传输地址和数据传输类型。

Control Transfers)

     或许你已经注意到了,一个USBhost端口并不是仅仅支持一个USBFunction.如图1,

    通过USB hub,一个USB host端口可以连接USB鼠标,USB键盘,USB写字板......。要连接这么多东西在同一个USB host上,我们通常会有一个基本问题,即USB host如何识别这些被连接在它的端口上  的设备呢。正如通常的主从式通讯系统一样,如rs485多机通讯,我们通常是用一个特定的地址标志每一个从设备。 对这里的USB,我们采用同样的方法,将为每个挂接在该USB host上的USB Function指定一个  特定地址,通过这个特定地址来识别每个USB Function.看来这将是一个USB Function在数据传输之前必须解决的问题--得到它的地址分配。

    这个“地址指定”的过程需要USB host通知USB Function才能完成,这个交互过程就是一个控制式传输。通过这个“控制式传输”,USB host将指定地址给USB Function ,以为即将进行的正式通讯做好准备  工作。这里细心的读者可能已经注意到了,既然USB host总要分配地址给USB Function才能进行正式的数据传输工作,那么USB host将如何与一个初始时未分配地址的USB Function进行交互来分配地址呢。  这里,是这样解决的:USB协议保留了一个“通用地址”0,USB host 通过这个地址0来和初始未分配地址的USB Function进行通讯,进行一些初始的准备工作,诸如这里的为它非配一个特定地址。后面我们就会  了解到,USB除了配置地址外,还有一些其它参数需要 事先主从双方达成共识。这些参数也都是通过控制式传输完成的。一个USB的控制式传输如图二示:

  一个USB的控制式传输总是分为两个或三个阶段进行传输:setup stage,data stage(视情况而定),status stage。 

  1. 首先是setup stage,联系上节所说的USB传输模式,USBHost总是先发起第一个packet--这里它
    1. 首先发起setup,
    2. 之后发起以data0为起始的setup data,
    3. 最后USBFunction回应ack结束一次交互。
  2. 其次如果有data stage,类似的,还是按照上节说的USB传输模式,
      1. USBhost总是先发起第一个Packet--Out(或in),
      2. 之后USBhost(或USBFunction)发起以data1为起始的payload data,
      3. 最后USBfuction(或USBhost)回应ack结束一次交互。
    1. 如果数据未传完,继续data stage,同上继续。
  3. 最后是status stage,类似的,
    1. USBhost首先发起第一个Packet--in(或out),
    2. 之后USBFunction(或USBhost)发起以data1为起始的Null data(0长度),
    3. 最后USBhost(或USBFunction)回应ack结束一次交互。

    如此,整个控制式传输结束。 你或许有疑问,data stage为什么进行了多次而非一次完成?实际上,USB总是将一批大量的数据分成了许多小段来进行传输,称为一个pay load。这样传输的目的是容易对传  输进行控制。既然一次大量的数据总是被分成一段一段来分次传输,那么这里就出现了一个需要事先确定的参数 (wMaxPacketSize):即每次这个小段有多大。这个参数如地址指派一样,正式传输之前需要事  先达成共识。通过控制式传输,现在我们已经完成了 USBFunction的地址指定等参数的设置工作,下一步可以进行正式的数据传输了。

bulk Transactions)

  我们终于等到USBFunction 配置完成,现在我们的任务是要传送一批数据,这里可以使用批量数据传输(bulk Transactions)。 一个批量传输总是按照如图所示方式进行,

  1. 首先,USBhost发起第一个Packet--in(或out),表示要开始数据传输了。
  2. 其次,USBFunction(或USBhost)发起以data1(或data0)为起头的payload data,开始一次交互。
  3. 再其次,USBhost(或USBFunction)发起ack回应这次交互。 如果数据还为传完,继续上述过程,即:
    1. 首先,USBhost再次发起一个Packet--in(或out),表示又要开始数据传输了。
    2. 其次,USBFunction(或USBhost)发起以data0(或data1)为起头的payload data,开始又一次交互。
    3. 再次,USBhost(或USBFunction)发起ack回应这次交互。

    如此继续直至传输完成

    这里的疑问依然是为什么一次可能传完的数据为什么分成多次进行传输,原因在上次介绍控制式传输式已经说明。后面我们就会明白,为什么这样可以方便控制传输过程。 仔细看看控制式的data stage采用的  传输方式,是否就是批量传输方式呢?!注意,每次payload data的“牵头人”(preamble)在轮番掉换,最先是data1,接着data0,再是data1,......如此接替,只要有一次交互出现问题,这个接替规则就会被打破  进而被USBhost识别而发现传输异常。所以这个交替的“牵头人”规则是可靠数据传输的所采取的措施之一。 

  Isochronous Transactions)和中断式传输(Interrupt Transactions)

    在批量数据传输中,触发一次批量数据传输总是“被动”的,就是说需要数据传输时USBhost并不会主动发起传输,而是需要得到你的指令。当你告诉它:“一切ok,让我们开始吧!” 这时它才开始数据传输。  这种方式显然在某些情况下并不适合。比如音视频流,你无法要求它听从你的“指挥”,让它等你发指令给USB host,然后开始一次传输。我们需要的是一种“及时”传输。一个好的方案就是设置一个timer,按照tick  发起USB传输。这个tick通常以 1ms(USBfull speed)为最小单位。这时,可以设置为每次1ms tick出现,USB host“自动”发起一次数据传输。那么这种方案具体如何来实现呢?看来最基本的要素便是一个发出  tick的timer,而这个“timer”需要USBhost和USBFunction(事实上还要包括USBhub)双方均能“看到”,从而协调工作,否则单方面的timer又有何意义?这个"timer"(或tick)在USB中使用一个特殊的packet实   现,即是SOF。这个SOF由USB HOST 相当精确的以每1.00 ms ±0.0005 ms的时间周期发送给USBdevice,来在二者之间定时。从而USB Function能够“及时”的了解到“现在时刻”。 现在我们在USBhost和  USBFunction之间建立起了“对时”机制。那么接下来看看刚才设想的“自动”传输如何实现。事实上,一旦USBhost及USBFunction双方建立了一种时间机制,那么这种“自动”传输是很容易实现的。USB实现同步  式传输或中断式传输总是以一种类似于批量数据传输的方式进行的,唯一不同的是传输的触发不再是“被动”的,而是由SOF所建立的tick触发。

  1. 首先,时间到达,USBhost发起第一个Packet--in(或out),表示要开始数据传输了。
  2. 其次,USBFunction(或USBhost)发起以data1(或data0)为起头的payload data,开始一次交互。
  3. 再其次,如果是中断式传输,USBhost(或USBFunction)发起ack回应这次交互,如果是同步式传输,该步跳过。

  如此重复上述步骤,即USBhost等待下一个tick到达,并开始新一轮的交互。

  这里我们注意到了,同步式传输和中断式传输二者虽然都是时间触发,但是中断式传输需要ack应答,而相反,同步式传输不需要。这个最大的区别决定了 同步式传输是一种非可靠传输,但是因此换来了更多的    USB传输时间。也因此,同步式传输的 payload data(对应wMaxPacketSize )通常相较于其他传输方式比较大,因为它消掉了ack所占有数据传输时间。这里还有一个地方值得注意的是tick的设定,这个tick也  是需要事先USBhost 和USBFunction达成共识的参数之一。

摘自:http://blog.chinaunix.net/uid-12461657-id-3044136.html

USB详解的更多相关文章

  1. linux usb 驱动详解

    linux usb 驱动详解 USB 设备驱动代码通过urb和所有的 USB 设备通讯.urb用 struct urb 结构描述(include/linux/usb.h ). urb 以一种异步的方式 ...

  2. Linux下usb设备驱动详解

    USB驱动分为两块,一块是USB的bus驱动,这个东西,Linux内核已经做好了,我们可以不管,我们只需要了解它的功能.形象的说,USB的bus驱动相当于铺出一条路来,让所有的信息都可以通过这条USB ...

  3. USB协议枚举过程详解

    一 枚举过程之文字描述 ?主机集线器监视着每个端口的信号电压,当有新设备接入时便可觉察.(集线器端口的两根信号线的每一根都有15kΩ的下拉电阻,而每一个设备在D+都有一个1.5kΩ的上拉电阻.当用US ...

  4. Android USB 开发详解

    Android USB 开发详解 先附上 Android USB 官方文档 Android通过两种模式支持各种 USB 外设和 Android USB 附件(实现Android附件协议的硬件):USB ...

  5. Linux USB 鼠标驱动程序详解(转)

    Linux USB 鼠标驱动程序详解 USB 总线引出两个重要的链表!一个 USB 总线引出两个重要的链表,一个为 USB 设备链表,一个为 USB 驱动链表.设备链表包含各种系统中的 USB 设备以 ...

  6. nrf52——DFU升级USB/UART升级方式详解(基于SDK开发例程)

    摘要:在前面的nrf52--DFU升级OTA升级方式详解(基于SDK开发例程)一文中我测试了基于蓝牙的OTA,本文将开始基于UART和USB(USB_CDC_)进行升级测试. 整体升级流程: 整个过程 ...

  7. Android Notification 详解(一)——基本操作

    Android Notification 详解(一)--基本操作 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Notification 文中如有纰 ...

  8. Android Notification 详解——基本操作

    Android Notification 详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 前几天项目中有用到 Android 通知相关的内容,索性把 Android Notificatio ...

  9. android adb 命令详解

    ADB (Android Debug Bridge)  是android SDK中的工具,需要先配置环境变量才能使用.起调试桥的作用,可以管理安卓设备.(也叫debug工具) ---------查看设 ...

随机推荐

  1. 文艺青年装B指南

        和大龄文艺青年们去凤凰的时候,很难不注意到狭窄小道旁边的文艺小店.有提供焦糖玛奇朵的咖啡店,有兜售梦露赫本明信片和烟雨 凤凰笔记本的店铺,还有复古式的静吧,常驻唱民谣小众歌曲的流浪歌手.我每看 ...

  2. C语言实现单链表(带头节点)

    C语言在实现单链表存储时需要注意的几点: 1.定义结构体,typedef:用于给结构体另命名 // 定义结构体类型 typedef struct Node{ int data; struct Node ...

  3. synchronized和volatile的使用方法以及区别

    先看看下面的例子: public class ThreadTest { public static void main(String[] args) { final Counter counter = ...

  4. $$wname

    w变量名为变量,减少重复代码. <?php function w_w($w_arr, $link) { $wres = true; foreach ($w_arr AS $w) { $wname ...

  5. LeetCode_Symmetric Tree

    Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center). For e ...

  6. Golang&Python测试thrift

    接上篇,安装好之后,就开始编写IDL生成然后测试. 一.生成运行 参考 http://www.aboutyun.com/thread-8916-1-1.html 来个添加,查询. namespace ...

  7. 11个你应该知道的django博客引擎

    这段时间一直在学python和django,准备写个小小的blog巩固下自己学到的东西,看到了GAE上的一些程序,大部分都是纯python的,想找一些基于django的,还真是难.无意中搜索到一篇文章 ...

  8. 配置stun服务器实现穿墙

    Turn服务器的配置流程 Webrtc是基于P2P的,在两个客户端建立连接之前需要服务器建立连接,这时两台设备一般都处于一个或者多个NAT中,那么两台设备建立连接就需要穿墙技术. 这时就用到了turn ...

  9. 如何制作一款HTML5 RPG游戏引擎——第二篇,烟雨+飞雪效果

    今天我们来实现烟雨+飞雪效果.首先来说,一款经典的RPG游戏难免需要加入天气的变化.那么为了使我们的RPG游戏引擎更完美,我们就只好慢慢地实现它. 本文为该系列文章的第二篇,如果想了解以前的文章可以看 ...

  10. PAT 1039 Course List for Student[难]

    1039 Course List for Student (25 分) Zhejiang University has 40000 students and provides 2500 courses ...