USB之基本协议和数据波形1

 

=============  本系列参考  =============html

《圈圈教你玩USB》、《Linux那些事儿之我是USB》缓存

协议文档:https://www.usb.org/document-library/usb-20-specification  usb_20_20190524/usb_20.pdf工具

调试工具:Beagle USB 480 逻辑分析仪编码

====================================spa

前言:翻译

  咱们先不一上来说USB大而全的协议规范文档, 会让人退而却步, 只要有协议, 在数据传输上波形就有规律可循, 翻译成数据, 也先无论USB1.1/2.0等版本, 由于最终的传输单元是同样的3d

 

一. 最基本传输单位 --包(packet)

1.电气信号:

  a. 采用D+/D-差分信号传输, LSB在前, NRZI编码也就是0反转, 1不反转, 遇到连续6个1强插一个0调试

 

  b. 低速Lowspeed 1.5Mb/s, 全速Fullspeed 12Mb/s, 高速Highspeed 480Mb/s, USB1.1支持L/F  USB2.0支持L/F/H USB3.0也支持L/F/H 同时支持OTG功能code

  c. OTG(on the go) 就是多了根ID线, 用于判断主控器做Host仍是device视频

  d. L/F S采用电压传输(3.3v), HS采用电流传输(等效电阻后示波器显示400mv)

  e. 传输方向以Host为准, 即IN表示device数据到Host, OUT表示Host数据到device

  f. 插入上电波形分析(下面单独抽出来分析)

2. packet格式

      SYNC同步域  +  PID域  +  数据域  +  CRC  +  EOP

  a. 同步域: L/FS 固定00000001, HS前面31个0后一个1

  b. PID占一个字节,高4bit是低4bit的反码, 用于校验PID自己, 而PID[3:0] 表示该packet的类型(协议文档8.3.1):

    

    打*表示USB1.1不支持的, 而USB2.0全支持, 这里要特别注意令牌包, 任何事务传输, 必须先发个令牌包说明意图, 至于后面是否须要数据包仍是握手吧取决事务类型(下面会说)

  c. 数据域是可选的, 取决PID是否是数据包(DATA0/1/2/M) 

  d. CRC也是可选的, PID自校验,因此只对数据域校验,  若数据域没有那CRC也没有, 令牌包的数据域采用CRC5校验, 数据包的数据域采用CRC16校验

  e. EOP结束包, 对于L/FS是两个数据位宽的SE0信号(D+/D-都是0),  对于HS使用故意位填充表示(待具体解释)

  f. 空闲状态, 在SYNC同步域前和EOP后 总线上处于空闲状态, L/FS是一根高电平一根低电平(也就是J或K状态, 后面会讲), HS是SE0表示空闲状态

  g. SYNC同步域、EOP、CRC是硬件发射器自动添加和硬件接收器自动解析的, 软件看到的只有PID域和数据域

    

2. packet类型

  根据PID[3:0]能够将包的类型分红4类

  a. 令牌包: 一次USB传输必须首发令牌包, 告知意图, 同时后面数据表示跟哪一个设备及端点通讯, 这点很重要, 设想一下一个Host接了不少外设, Host发出的信号会到达全部hub和普通外设, 如何避免串扰呢?

        那就是总线某一时刻只有一个外设与Host通讯, 外设硬件接口只响应令牌包, 由于令牌包的数据域表示设备的地址和端点地址, 外设能够解析是否和本身匹配, 若是是则响应(使能硬件接收数据), 以及后续的数据包交互, 若是不是

        就不响应, 固然后续的数据包也会被外设硬件屏蔽, 不理会总线信号, 除非一段时间后又检测到令牌包, 再次进行地址匹配, 符合才使能硬件接收总线上的信号

   IN OUT SETUP 包的数据域包含7bit设备地址和4bit端点地址, 因此一个Host可以最多接127个设备(0是外设刚插入时的默认地址, 握手后必须赋值非0, 否则下一个设备也是0就冲突了),  一个设备端点最多只能16个(端点0是必须的, 因此其余最多15个)

      

 

 

   SOF(帧起始包) 至关于心跳包, 让全部外设知道Host还在活动(哪怕Host不是跟该设备通讯但起码知道跟其余设备通讯), L/FS每隔1ms发一次, 每发一次11bit帧号加1, HS把1ms分割8份即每隔125us发一次, 但这8份里面的11bit帧号是相同的

  

    这个心跳包主要用于休眠唤醒用的, 当Host没有发SOF超过3ms时(通常是Host本身进入休眠或者想外设休眠), 外设设置本身进入低功耗状态(若是支持), 而后进入监听模式若是检测到总线有信号变化(只要跟睡眠前不同)当即唤醒,

  多是Host要召唤设备了, 固然设备也能够唤醒Host, Host进入休眠也会设置监听总线状态,外设被人为唤醒改变总线信号接着唤醒Host

 

  b. 数据包: 这没啥好说的就是PID代表本身是数据包(DATA0仍是DATA1主要用于 确保对方收到), 后面就是字节数据了, 这里须要注意就是没有告知这个数据包到底多少个数据, 因此我猜测外设接收PID域后, 每接收一个字节counter计数器加1

        直到EOP, 而后减2 CRC16校验值就是数据量, 接着对FIFO数据CRC16和最后两个字节对比, 不一致就产生数据错误中断, 一致就产生数据成功中断并将数据量填充RX counter寄存器

     

  c. 握手包: 告知对方状态, 好比Host发送IN令牌包, 接着设备发送数据包, 而后Host接收完发送ACK握手包告知设备成功接收

    不用数据域!

  d. 特殊包主要用于高速, 好比上面Host发完IN令牌包后, 设备应该要发数据包的, 但设备还没准备好数据, 致使Host等待超时, Host能够再次发IN包让设备进入发送数据, Host切换等待接收数据状态, 

    这里有两个小问题, 一是设备数据未准备好, 却没有有效方式告知Host, 只能啥都不作靠超时告知, 浪费Host时间, 二是IN包让设备进入发送数据模式, 设备有数据早发了还等你吹, 还让外设进入发送模式影响准备数据

    而PING特殊包就是当第一次超时后, Host不发IN包改发PING包询问设备准备好没, 设备若准备好了回复ACK握手包, 接着Host再发IN包, 若是还没准备好就发NAK告知, Host就知道设备还没准备好而不用死等超时,

    其余几个读者可自行查阅

 

   总结: 总线是一个一个packet传输的, 且信号达到全部外设, 当发送SYNC域全部外设接收并调整时钟采样点作好同步, 接着解析PID域,  若是是非令牌包就不理会(只有已被选中的外设才理会), 若是是令牌包就解析后面地址是否和本身匹配,

      不匹配继续不会理, 匹配的使能硬件接收数据功能, 并根据PID是IN OUT SETUP SOF再细分, 若是是OUT,产生OUT中断, 软件应该清空使能FIFO准备接收数据, 若是是IN, 产生IN中断, 软件要填充好即将发的数据而后使能端点发送,

      若是是SETUP包(Host会接着发DATA0数据包数据域包含8个字节的标准请求), 设备要清空特殊FIFO并作好接受下一个数据, 接受完才产生SETUP中断, 软件就解析FIFO里的8byte标准请求, 而后准备数据, 好比是获取设备描述符请求

      那软件得准备好设备描述符缓存并ACK(必须ACK不能NAK)回复, 而后Host会发IN包, 接着设备IN中断将刚才准备好的设备描述符缓存丢到端点0发出去!

      若是是SOF包, 设备会重置时间计数器, 当3ms内没有新的SOF包, 就会产生中断, 设备知道总线如今是空闲状态, 能够自行决定是否休眠

 

 

 2、 事务--四种传输类型

  一个个packet只是人心涣散, 经过组织起来做为一个有效传输咱们称之为事务, 因此一个事务起码包含:

  一个令牌包, 经过地址选中具体外设

  可选的数据包, 若是是IN/OUT/SETUP包那后续有数据包, 若是是 SOF则数据包和握手吧都没有

  可选的握手包, 像视频聊天这种实时传输不须要ACK应该, 丢了就丢了, 省下带宽不如用来发数据

  所以, 根据具体的使用场景, 事务能够分红四种传输类型:

1. 批量传输(Bulk transfers

  一个批量事务包含三个阶段, 令牌包阶段 + 数据包阶段 + 握手包阶段, 其中数据包阶段能够发一个或多个数据包

              

 

  以Beagle USB 480 逻辑分析仪抓U盘上电时序时为例, 期间Host(PC机)会读取U盘数据(bluk传输), 咱们能够猜想应该发一个读取U盘根目录命令, 而后读取扇区信息, 以下:    

        

  一个读取扇区信息命令分别为 Command + Data + Status, Command是一个写操做, 往设备发送数据告知想干吗, 而后就是读数据, 最后检查状态, 能够看到这些操做都由三个packet构成 IN/OUT令牌包 + 数据包 + 握手包

  由于U盘每次操做只能512byte/block, 因此想读取多个扇区只能分屡次IN操做(传输最大字节数端点描述符有说明)

 

2. 中断传输(Interrupt transfers

   一个中断事务跟批量事务相似, 不一样在于传输量比较少, 且但愿Host每隔一段时间来访问设备(不是靠硬件中断告知系统, 而是端点描述符有个时间间隔变量, 告知Host最好小于这个时间间隔来访问设备), 像鼠标键盘都是这类传输模式, 

   以Beagle USB 480 逻辑分析仪抓键盘为例:

  

  这里能够看出三点, 一是Host每间隔x时间就发起一次读取键盘数据操做(仍是老样子 IN包 + DATA0包 + ACK包); 二是若是我没敲键盘, 则设备NAK告知Host没有数据; 三是间隔时间约 72/10  344/44 = 8ms

  查看键盘端点描述符bInterval=1, 根据datasheet表明1ms, 即键盘但愿Host每隔1ms读取一次数据, 但采不采纳在于Host端

          

 

 3. 等时传输(Isochronous transfers

  等时事务跟前两种也差很少, 不一样在于对时间敏感, 对数据准确性不关心, 因此不须要握手包, 主要用于音频、视频类设备

 

4. 控制传输(Control transfers

   控制传输稍微复杂一点, 上面三个一个传输就是一个事务, 但控制传输有三个状态, 每一个状态对应一个事务, 因此须要三次事务

三次过程分别为:

  创建过程:SETUP令牌包 + DATA0数据包(标准请求就在这) + ACK握手包(设备必须返回ACK, 不能NAK 若是设备连这个都不能保证的话就别玩了)

  数据过程: 可选, 如上面是获取设备描述符这里就是 IN令牌包 + DATA1数据包 + ACK握手包; 若是是设置地址请求, 地址在请求内部了, 不须要数据过程 

  状态过程: 上面的数据过程必须是同一个方向的, 若是方向改变, 则就是状态过程, 若是没有数据过程, 则这个数据包就是状态过程无论哪一个方向

      

  以Beagle USB 480 逻辑分析仪抓U盘为例:

     

  从捕捉的数据可看到, 创建过程的数据包包含着标准请求 80 06 00 01 00 00 12 00 (小端排序) , 前面的C3是PID, 后面E0 F4 是CRC16, 能够经过http://www.ip33.com/crc.html 验证

 80 06 0100 0000 0012
struct usb_ctrlrequest {
    __u8 bRequestType; //0x80
    __u8 bRequest;    //0x06
    __le16 wValue;     //0x100   
    __le16 wIndex;    //0
    __le16 wLength;  //0x12
} __attribute__ ((packed));
具体请参考协议文档9-4

 

  上面log还有个有趣的现象: 状态过程发送1字节0x00数据包,  U盘居然返回NAK, 不知为什么,  因为是高速模式下, 因此Host接下来会发PING包探测U盘是否ready, 直到U盘回复ACK才再次发送OUT包,若是是L/FS则继续发OUT包直到接收ACK

 

剩余数据的解析将在下一篇博文讲解!

相关文章
相关标签/搜索