问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

USB原理:从零基础入门到放弃

创作时间:
作者:
@小白创作中心

USB原理:从零基础入门到放弃

引用
CSDN
1.
https://blog.csdn.net/jimaofu0494/article/details/109233087

从零基础到开发USB设备,需要经历怎样的过程?本文将带你从USB设备接入主机开始,逐步了解USB通信过程、设备属性以及枚举过程等核心概念。本文提供一种自上而下的学习过程,帮助读者建立完整的USB知识体系架构认知。

理论学习

本章将由浅入深介绍USB原理,逐步解释以下问题:

  • USB设备接入主机后经历了哪些过程
  • USB设备和主机之间如何通信
  • 如何区分不同类型的USB设备
  • 主机认识USB设备的具体过程

另外,下文将用主机/从机统一描述USB主机和USB设备:

  • 主机:USB主机(Win/Android/Mac等)
  • 从机:USB设备(鼠标/键盘/U盘等)

USB从接入到使用

主机发现从机接入后,开始识别从机,成功识别后就可以使用从机的功能了。其中,发现从机接入/拔出的过程称为USB拔插,识别从机的过程称为枚举

USB拔插:主机发现从机的接入/拔出

【摘要】主机通过检测USB D+/D-的电平变化感知从机接入/拔出。

一般USB接口包含4根线(OTG为5根),分别是:Vcc, D+, D-, GND。如图所示:

主机端D+/D-下拉15KΩ电阻到GND(0V),从机端D+/D-上拉1.5KΩ电阻到3.3V。当从机接入主机时,D+/D-上的电压变为3V,双方通过电平变化就可以发现USB的拔插事件。 USB拔插事件会触发主机的中断(或回调),执行从机的加载、释放过程。

USB枚举:主机认识从机的方式

【摘要】主机通过获取设备的描述符集合来识别USB设备,这个过程称为“枚举”。

USB设备(从机)的类型非常多,常见的有鼠标、键盘、游戏手柄等USB HID(Human Interface Device)设备,串口调试的CDC(Communication Device Class)设备,User自定义传输内容的WINUSB设备等。

那么对于新接入的从机,主机如何区分它属于哪种类型呢?

当然是让从机“介绍”自己。但主机是很忙的(软件、其他从机、其他接口的设备等),不会时时刻刻等着新从机的加入。从机不合时宜地发消息,只会对主机造成困扰。因此,从机就像门口排队的面试者一样,手里拿着自己的“简历”,等待着主机问询递交。

从机的“简历”,称为描述符集合(Descriptor Collection)。它包含从机的名字、籍贯、性别等最基本的信息(设备描述符)、从事的职业(配置描述符)、掌握的技能(接口描述符、端点描述符)和补充信息(字符串描述符、其他特殊描述符)。他们都必须遵循相应的格式,以便主机可以快速了解从机的所有信息。只要从机正确地遵循主机的流程(枚举),按固定格式提供主机索要的信息,就可以通过“面试”,成为主机的USB部门的一员。

每个USB设备都必须有描述符集合来详细介绍自己的所有功能和用途。USB连接后,主机通过访问描述符集合来识别从机并配置从机(枚举过程),就可以根据从机提供的信息使用从机的功能。

USB使用:主机使用从机的功能

【摘要】从机以等待主机轮询的方式发数据,以中断的方式收数据,从而实现相应的功能。

枚举成功后,从机开始履行自己的职责。

前文提到,从机不能擅自介绍自己,因为主机是很忙的。同样,在主机认识并接受从机后,从机依然不能擅自报告自己的行为、状态等信息。那么从机和主机要如何通信呢?

主机会定期到USB部门来视察工作,依次询问USB部门的所有成员是否需要汇报工作。当然,有些从机希望主机询问自己的频率高一些,就必须在“简历”中附上声明:请每隔XXX的时间问一次我的情况。对于没有附上声明的从机,主机会以自己的设定定期询问,或者由应用软件“催促”主机询问(自定义的USB设备)。

以鼠标为例,它一般会声明:请每隔10毫秒来问一下我的情况。主机会尽量遵守这个声明,及时询问鼠标。在某一次询问中,鼠标报告自己:我刚刚移动了10个像素点。那么主机就会让屏幕上的光标移动。

因此,从机准备好发送的数据后必须进入等待(一般不会等太久),直到主机轮询到此功能时,才开始发送。假设从机可以任意触发数据的发送过程,且主机连接多个从机,那么当多个从机同时发送数据到主机的USB总线上时就会引发冲突。

反之,当主机需要发送数据时,从机必须尽快接收,所以从机一般会用中断处理主机发送数据的请求。这是因为主机需要轮询很多从机,每次轮询都有固定的时间,超时后就通信失败了。

【Q】从机发送/接收数据,主机发送/接收数据是否容易概念混淆?

【A】是的。因此USB的数据传输过程描述以主机端为主。“从机–>主机”(Device-to-host)方向的数据传输称为输入(Data In)“从机<–主机”(Host-to-device)方向的数据传输为输出(Data Out)

【Q】主机轮询到从机的输入功能时,没有数据要发送怎么办?

【A】当然是PASS,从机直接回复NAK(即没有数据)或STALL(设备挂起)。

USB通信过程

主机如何访问指定USB设备?

【摘要】主机为所有从机分配唯一的设备地址,通过该地址来访问从机。

以PC为例,一般PC的USB设备可能包括鼠标、键盘、HUB扩展坞、蓝牙/WiFi适配器等。那么假如PC想访问鼠标设备时,该如何实现呢?

答案是设备地址。主机给所有已连接的从机分配设备地址,并确保不会重复。对刚接入还没来得及分配地址的从机,主机使用默认地址与之通信。交换少量的信息后,主机分配新地址,然后双方用新地址(Addr1~AddrN)通信。

【Q】枚举成功后,从机再次拔插还可以用之前分配的地址通信吗?

【A】主机会重新分配设备地址,但可能分配的碰巧就是之前的地址。

【Q】主机为分配地址前,如何与从机通信?

【A】USB规定,对于刚接入的从机,主机用默认地址(Addr0)通信。

主机如何访问指定USB设备的指定功能?

【摘要】主机通过<设备地址(Address),设备端点(Endpoint)>访问指定从机的指定接口(功能)。

假设设备A是USB复合设备,同时支持鼠标、键盘、CDC功能,那么主机给设备A分配设备地址后,如何访问从机A的其中一个功能(比如键盘功能)?且当这个键盘功能同时支持发送和接收数据时,如何避免收发冲突呢?

答案是用端点(Endpoint,EP)加以区分。主机通过设备地址找到从机后,再通过端点访问从机的指定功能的指定用途。端点具有唯一性,它们和从机的功能及用途一一对应,按照端点的属性构建专用的端点通道(Pipe)来通信。另外,端点还标识了特定用途的数据传输方向。因此,对于USB复合设备A,通过端点号可区分键盘功能的发送或接收。

【Q】有多少个功能/用途就分配多少个设备地址不就可以了吗?

【A】如果这么做,当主机接入多个USB设备,而每个USB设备又支持多种功能、每个功能又包含多个用途时,主机需要分配的地址数量非常之多,且每次拔插设备需要多次分配地址,最终通信效率变低了。

【Q】主机未识别从机的功能之前用什么端点通信?

【A】与默认地址0一样,从机也会有默认端点0(Default Endpoint, EP0)。准确来讲,对初次接入的从机,双方通过<Addr0,EP0>进行通信

主机、从机如何读/写数据

【摘要】主机用默认端点0(EP0)创建通道枚举从机,根据描述符集中的其他端点创建对应通道访问其他功能。

首先,从机必须支持默认端点EP0。对刚接入的从机,主机使用<Addr0, EP0>访问从机,创建EP0的端点通道,开始枚举并分配地址,然后使用<new Addr, EP0>重新枚举。枚举成功后,主机根据从机提供的信息创建相应的资源和通道,访问从机的功能。

当然,从机的功能多种多样,可能要持续传输大量数据,也可能要求实时性高,或是偶尔传输数据等。那么访问的需求不一样,主机怎么区分呢?

当然是给端点加上属性(Attribute)。在端点描述符中声明属性,可以告诉主机构建什么样的数据通道,以何种方式读/写数据。

一次完整的通信过程

【摘要】一次完整的通信分为三个过程:请求过程(令牌包)、数据过程(数据包)和状态过程(握手包),没有数据要传输时,跳过数据过程。

通信过程包含以下三种情况:

主机发送令牌包(Token)开始请求过程,如果请求中声明有数据要传输则有数据过程,最后由数据接收方(有数据过程)或从机(无数据过程)发起状态过程,结束本次通信。

与USB全速设备通信时,主机将每秒等分为1000个帧(Frame)。主机在每帧开始时,向所有从机广播一个帧起始令牌包(Start Of Frame,SOF包)。它的作用有两个:一是通知所有从机,主机的USB总线正常工作;二是从机以此同步主机的时序。

与USB高速设备通信时,主机将帧进一步等分为8个微帧(Microframe),每个微帧占125μ \muμs。在同一帧内,8个微帧的帧号都等于当前SOF包的帧号。

注意:下文所有USB包结构均不包括前导码(同步码)

#pragma data_alignment=1    //对齐方式为Byte
typedef struct _USB_Token_SOF_t{
    uint8_t  bPID;          // 0xA5, SOF(0101B)
    uint16_t b11FrameID:11; // 帧号
    uint16_t b5CRC:5;       // wFrameID字段(11bit)的CRC校验码
}USB_Token_SOF_t;

【Q】为什么PID是4bit的,字段长度却有8bit?

【A】因为PID字段高4bit是低4bit的校验位:pid(i+4) = ~pid(i)。

【Q】为什么CRC不校验PID字段?

【A】因为PID字段本身带有校验位。

请求过程(请求包)

主机广播SOF包之后,会发送带有地址和端点信息的令牌包(Token)来指定要访问的从机,分别有:建立令牌包(SETUP)、输出令牌包(OUT)、输入令牌包(IN)

这三种令牌包统称为请求包,结构如下:

#pragma data_alignment=1    //对齐方式为Byte
typedef struct _USB_Token_t{
    uint8_t bPID;           // 0xE1, OUT    (0001B);
                            0x69, IN     (1001B);
                            0x2D, SETUP  (1101B);
    uint16_t b7Addr:7;      // 要访问的设备地址
    uint16_t b4Endpoint:4;  // 要访问的端点号
    uint16_t b5CRC:5;       // wFrameID字段(11bit)的CRC校验码
}USB_Token_t;

主机可以通过请求包指定要访问的从机,发起请求过程,配置从机或指示从机准备发送/接收数据。在枚举过程中,主机使用SETUP包请求从机的信息。枚举成功后,主机使用IN包请求输入数据,OUT包请求输出数据。

枚举时,在SETUP包的后面会紧跟一个8B长度的请求(Request),用于描述主机的具体意图,结构如下:

#pragma data_alignment=1    //对齐方式为Byte
typedef struct _USB_Request_t{
    uint8_t  bmRequestType; // 请求类型
    uint8_t  bRequest;      // 具体请求,参考USB 2.0 Spec Chapter 9.4
    uint16_t wValue;        // 内容和Request有关
    uint16_t wIndex;        // 内容和Request有关
    uint16_t wLength;       // 数据过程可传输的最大字节数
}USB_Request_t;
typedef struct _bmRequestType_t{
    uint8_t b5Recipient:5;  // 0 = Device, 1 = Interface
                            2 = Endpoint, 3 = Other
                            4..31 = Reserved
    uint8_t b2Type:2;       // 0 = Standard, 1 = Class
                            2 = Vendor, 3 = Reserved
    uint8_t b1Direction:1;  // 0 = Host-to-device
                            1 = Device-to-host
}bmRequestType_t;

在“请求过程”阶段,被访问的从机会接收并解析请求,若wLength字段不为0,则进入数据过程,否则进入状态过程。

【Q】从机收到不支持的请求怎么办?

【A】可以直接进入状态过程,从机发送STALL包。

【Q】有了IN/OUT包,为什么还要在请求中声明传输方向(Direction)?

【A】IN/OUT包后面不会带有请求。从机在收到IN/OUT包后直接进入数据过程,发送数据或回复NAK(没有数据要发送)。

数据过程(数据包)

请求的bmRequestType字段中,Direction标志位声明了数据要传输的方向。

当请求为输出(Data OUT,Direction = 1)时,从机接收不超过wLength字段中声明长度的数据,并根据请求的内容解析接收到的数据;当请求为输入时(Data IN,Direction = 0)时,从机根据请求的内容发送对应的数据(不超过wLength中声明的长度)。

数据包(Data Packets)的结构如下:

#pragma data_alignment=1    //对齐方式为Byte
typedef struct _USB_Data_Packet_t{
    uint8_t bPID;           // 0xC3, DATA0 (0011B); even
                            0x4B, DATA1 (1011B); odd
                            0x87, DATA2 (0111B); for usb high speed
                            0x0F, MDATA (1111B); for usb high speed
    uint8_t bData[];        // 0 ~ 8192B
    uint16_t wCRC16;        // bData字段的CRC校验码
}USB_Data_P
© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号