目录

  • USB 接口简介
    • 什么是USB?
    • USB 电气特性
    • USB 拓扑结构
    • 什么是USB OTG?
    • I.MX6ULL USB 接口简介
  • 硬件原理图分析
    • USB HUB 原理图分析
    • V2.4 版本以前底板USB OTG 原理图分析
    • V2.4 及以后版本底板USB OTG 原理图分析
  • USB 协议简析
    • USB 描述符
    • USB 数据包类型
    • USB 传输类型
    • USB 枚举
  • Linux 内核自带HOST 实验
    • USB 鼠标键盘测试
    • U 盘实验
  • Linux 内核自带USB OTG 实验
    • 修改设备树
    • OTG 主机实验
    • OTG 从机实验

USB 是很常用的接口,目前大多数的设备都是USB 接口的,比如鼠标、键盘、USB 摄像
头等,我们在实际开发中也常常遇到USB 接口的设备,本章我们就来学习一下如何使能Linux内核自带的USB 驱动。注意!本章并不讲解具体的USB 开发,因为USB 接口很复杂,不同的设备其协议也不同,这不是简简单单一章内容就能说完的,USB 驱动开发本身就是一门复杂的课程。所以,如果想要学习如何编写代码开发一个全新的USB 设备那就可以跳过本章。

USB 接口简介

关于USB 详细的协议内容请参考《USB2.0 协议中文版.pdf》和《USB3.0 协议中文版.pdf》,
这两份文档已经放到了开发板光盘中,存放在“4、参考资料”中。

什么是USB?

USB 全称为Universal Serial Bus,翻译过来就是通用串行总线。由英特尔与众多电脑公司提出来,用于规范电脑与外部设备的连接与通讯。目前USB 接口已经得到了大范围的应用,已经是电脑、手机等终端设备的必配接口,甚至取代了大量的其他接口。比如最新的智能手机均
采用USB Typec 取代了传统的3.5mm 耳机接口,苹果最新的MacBook 只有USB Typec 接口,至于其他的HDMI、网口等均可以通过USB Typec 扩展坞来扩展。
按照大版本划分,USB 目前可以划分为USB1.0、USB2.0、USB3.0 以及即将到来的USB4.0。
USB1.0:USB 规范于1995 年第一次发布,由Inter、IBM、Microsoft 等公司组成的USB-IF(USB Implement Forum)组织提出。USB-IF 于1996 年正式发布USB1.0,理论速度为1.5Mbps。
1998 年USBIF 在USB1.0 的基础上提出了USB1.1 规范。
USB2.0:USB2.0 依旧由Inter、IBM、Microsoft 等公司提出并发布,USB2.0 分为两个版本:Full-Speed 和High-Speed,也就是全速(FS)和高速(HS)。USB2.0 FS 的速度为12Mbps,USB2.0 HS 速度为480Mbps。目前大多数单片机以及低端Cortex-A 芯片配置的都是USB2.0 接口,比如STM32 和ALPHA 开发板所使用的I.MX6ULL。USB2.0 全面兼容USB1.0 标准。
USB3.0:USB3.0 同样有Inter 等公司发起的,USB3.0 最大理论传输速度为5.0Gbps,USB3.0引入了全双工数据传输,USB2.0 的480Mbps 为半双工。USB3.0 中两根线用于发送数据,另外两根用于接收数据。在USB3.0 的基础上又提出了USB3.1、USB3.2 等规范,USB3.1 理论传输速度提升到了10Gbps,USB3.2 理论传输速度为20Gbps。为了规范USB3.0 标准的命名,USB-IF 公布了最新的USB 命名规范,原来的USB3.0 和USB3.1 命名将不会采用,所有的3.0 版本的USB 都命名为USB3.2,以前的USB3.0、USB3.1 和USB3.2 分别叫做USB3.2 Gen1、USB3.2Gen2、USB3.2 Gen 2X2。

USB4.0:目前还在标准定制中,目前还没有设备搭载,据说是在Inter 的雷电接口上改进而
来。USB4.0 的速度将提升到了40Gbps,最高支持100W 的供电能力,只需要一根线就可以完
成数据传输与供电,极大的简化了设备之间的链接线数,期待USB4.0 设备上市。
如果按照接口类型划分的话USB 就要分为很多种了,最常见的就是USB A 插头和插座,
如图67.1.1.1 所示:

使用过JLINK 调试器的朋友应该还见过USB B 插头和插座,USB B 插头和插座如图67.1.1.2
所示:

USB 插头在不断的缩小,由此产生了Mini USB 接口,正点原子的I.MX6ULL-ALPHA 开
发板使用的就是Mini USB,Mini USB 插头和插座如图67.1.1.3 所示:

比Mini USB 更小的就是Micro USB 接口了,以前的智能手机基本都是Micro USB 接口的,
Micro USB 插头和插座如图67.1.1.4 所示:

现在最流行的就是USB Typec 了,USB Typec 插头和插座如图67.1.1.5 所示:

USB 电气特性

由于正点原子I.MX6U-ALPHA 开发板使用的Mini USB 接口,因此我们就以Mini USB 为
例讲解一下USB 的基本电气属性。Mini USB 线一般都是一头为USB A 插头,一头为Mini USB
插头。一共有四个触点,也就是4 根线,这四根线的顺序如图67.1.2.1 所示:

如图67.1.2.1 所示,USB A 插头从左到右线序依次为1,2,3,4,第1 根线为VBUS,电压为
5V,第2 根线为D-,第3 根线为D+,第4 根线为GND。USB 采用差分信号来传输数据,因
此有D-和D+两根差分信号线。大家仔细观察的话会发现USB A 插头的1 和4 这两个触点比较
长,2 和3 这两个触点比较短。1 和4 分别为VBUS 和GND,也就是供电引脚,当插入USB 的
时候会先供电,然后再接通数据线。拔出的时候先断开数据线,然后再断开电源线。
大家再观察一下Mini USB 插头,会发现Mini USB 插头有5 个触点,也就是5 根线,线序
从左往右依次是1~5。第1 根线为VCC(5V),第2 根线为D-,第3 根线为D+,第4 根线为ID,
第5 根线为GND。可以看出Mini USB 插头相比USB A 插头多了一个ID 线,这个ID 线用于
实现OTG 功能,通过ID 线来判断当前连接的是主设备(HOST)还是从设备(SLAVE)。
USB 是一种支持热插拔的总线接口,使用差分线(D-和D+)来传输数据,USB 支持两种供
电模式:总线供电和自供电,总线供电就是由USB 接口为外部设备供电,在USB2.0 下,总线
供电最大可以提供500mA 的电流。

USB 拓扑结构

USB 是主从结构的,也就是分为主机和从机两部分,一般主机叫做Host,从机叫做Device。
主机就是提供USB A 插座来连接外部的设备,比如电脑作为主机,对外提供USB A 插座,我
们可以通过USB 线来连接一些USB 设备,比如声卡、手机等。因此电脑带的USB A 插座数量
就决定了你能外接多少个USB 设备,如果不够用的话我们可以购买USB 集线器来扩展电脑的
USB 插口,USB 集线器也叫做USB HUB,USB HUB 如图67.1.3.1 所示:

图67.1.3.1 是一个一拖四的USB HUB,也就是将一个USB 接口扩展为4 个。主机一般会
带几个原生的USB 主控制器,比如I.MX6ULL 就有两个原生的USB 主控制器,因此I.MX6ULL
对外提供两个USB 接口,这两个接口肯定不够用,正点原子的ALPHA 开发板上有4 个HOST
接口,其中一路是USB1 的OTG 接口,其他的三路就是USB2 通过USB HUB 芯片扩展出来
的,稍后我们会讲解其原理图。

虽然我们可以对原生的USB 口数量进行扩展,但是我们不能对原生USB 口的带宽进行扩
展,比如I.MX6ULL 的两个原生USB 口都是USB2.0 的,带宽最大为480Mbps,因此接到下面的所有USB 设备总带宽最大为480Mbps。
USB 只能主机与设备之间进行数据通信,USB 主机与主机、设备与设备之间是不能通信的。
因此两个正常通信的USB 接口之间必定有一个主机,一个设备。为此使用了不同的插头和插座
来区分主机与设备,比如主机提供USB A 插座,从机提供Mini USB、Micro USB 等插座。在
一个USB 系统中,仅有一个USB 主机,但是可以有多个USB 设备,包括USB 功能设备和USB
HUB,最多支持127 个设备。一个USB 主控制器支持128 个地址,地址0 是默认地址,只有在
设备枚举的时候才会使用,地址0 不会分配给任何一个设备。所以一个USB 主控制器最多可以
分配127 个地址。整个USB 的拓扑结构就是一个分层的金字塔形,如图67.1.3.2 所示(参考自
USB2.0 协议中文版.pdf):

图67.1.3.2 中可以看出从Root Hub 开始,一共有7 层,金字塔顶部是Root Hub,这个是
USB 控制器内部的。图中的Hub 就是连接的USB 集线器,Func 就是具体的USB 设备。
USB 主机和从机之间的通信通过管道(Pipe)来完成,管道是一个逻辑概念,任何一个USB
设备一旦上电就会存在一个管道,也就是默认管道,USB 主机通过管道来获取从机的描述符、
配置等信息。在主机端管道其实就是一组缓冲区,用来存放主机数据,在设备端管道对应一个
特定的端点。

什么是USB OTG?

前面我们讲了,USB 分为HOST(主机)和从机(或DEVICE),有些设备可能有时候需要做
HOST,有时候又需要做DEVICE,配两个USB 口当然可以实现,但是太浪费资源了。如果一
个USB 接口既可以做HOST 又可以做DEVICE 那就太好了,使用起来就方便很多。为此,USB OTG 应运而生,OTG 是On-The-Go 的缩写,支持USB OTG 功能的USB 接口既可以做HOST,
也可以做DEVICE。那么问题来了,一个USB 接口如何知道应该工作在HOST 还是DEVICE
呢?这里就引入了ID 线这个概念,前面讲解USB 电气属性的时候已经说过了,Mini USB 插头
有5 根线,其中一条就是ID 线。ID 线的高低电平表示USB 口工作在HOST 还是DEVICE 模
式:

ID=1:OTG 设备工作在从机模式。
ID=0:OTG 设备工作在主机模式。

支持OTG 模式的USB 接口一般都是那些带有ID 线的接口,比如正点原子的I.MX6ULL-ALPHA 开发板的USB_OTG 接口就是支持OTG 模式的,USB_OTG 连接到了I.MX6ULL 的
USB1 接口上。如果只有一个Mini USB 或者Type-C USB 接口的话如果要使用OTG 的主机模
式,那么就需要一根OTG 线,Mini USB 的OTG 线如图67.1.4.1 所示:

Type-C USB OTG 线如图67.1.4.2 所示:


可以看出,不管是Mini USB OTG 还是Type-C USB OTG 线,一头都是USB A 插座,另外
一头是Mini USB 或者Type-C USB 插头,将Mini USB 或者Type-C USB 插头插入机器的Mini USB 或Type-C 接口上,需要连接的USB 设备插到另一端的USB A 插座上,比如U 盘啥的。
USB OTG 线会将ID 线拉低,这样机器就知道自己要做为一个主机,用来连接外部的从机设备
(U 盘)。

I.MX6ULL USB 接口简介

I.MX6ULL 内部集成了两个独立的USB 控制器,这两个USB 控制器都支持OTG 功能。
I.MX6ULL 内部USB 控制器特性如下:
①、有两个USB2.0 控制器内核分别为Core0 和Core1,这两个Core 分别连接到OTG1 和
OTG2。
②、两个USB2.0 控制器都支持HS、FS 和LS 模式,不管是主机还是从机模式都支持
HS/FS/LS,硬件支持OTG 信号、会话请求协议和主机协商协议,支持8 个双向端点。
③、支持低功耗模式,本地或远端可以唤醒。
④、每个控制器都有一个DMA。
每个USB 控制器都有两个模式:正常模式(normal mode)和低功耗模式(low power mode)。

每个USB OTG 控制器都可以运行在高速模式(HS 480Mbps)、全速模式(LS 12Mbps)和低速模式
(1.5Mbps)。正常模式下每个OTG 控制器都可以工作在主机(HOST)或从机(DEVICE)模式下,每
个USB 控制器都有其对应的接口。低功耗模式顾名思义就是为了节省功耗,USB2.0 协议中要
求,设备在上行端口检测到空闲状态以后就可以进入挂起状态。在从机(DEVICE)模式下,端口
停止活动3ms 以后OTG 控制器内核进入挂起状态。在主机(HOST)模式下,OTG 控制器内核不
会自动进入挂起状态,但是可以通过软件设置。不管是本地还是远端的USB 主从机都可以通过
产生唤醒序列来重新开始USB 通信。

两个USB 控制器都兼容EHCI,这里我们简单提一下OHCI、UHCI、EHCI 和xHCI,这三
个是用来描述USB 控制器规格的,区别如下:

OHCI:全称为Open Host Controller Interface,这是一种USB 控制器标准,厂商在设计USB
控制器的时候需要遵循此标准,用于USB1.1 标准。OHCI 不仅仅用于USB,也支持一些其他的
接口,比如苹果的Firewire 等,OHCI 由于硬件比较难,所以软件要求就降低了,软件相对来说
比较简单。OHCI 主要用于非X86 的USB,比如扩展卡、嵌入式USB 控制器。
UHCI:全称是Universal Host Controller Interface,UHCI 是Inter 主导的一个用于USB1.0/1.1
的标准,与OHCI 不兼容。与OHCI 相比UHCI 硬件要求低,但是软件要求相应就高了,因此
硬件成本上就比较低。

EHCI:全称是Enhanced Host Controller Interface,是Inter 主导的一个用于USB2.0 的USB
控制器标准。I.MX6ULL 的两个USB 控制器都是2.0 的,因此兼容EHCI 标准。EHCI 仅提供
USB2.0 的高速功能,至于全速和低速功能就由OHCI 或UHCI 来提供。
xHCI:全称是eXtensible Host Controller Interface,是目前最流行的USB3.0 控制器标准,
在速度、能效和虚拟化等方面比前三个都有较大的提高。xHCI 支持所有速度种类的USB 设备,
xHCI 出现的目的就是为了替换前面三个。
关于I.MX6ULL 的USB 控制器就简单的讲解到这里,至于更详细的内容请参考I.MX6ULL
参考手册中的“Chapter 56 Universal Serial Bus Controller(USB)”章节。

硬件原理图分析

正点原子的I.MX6ULL-ALPHA 开发板USB 部分原理图可以分为两部分:USB HUB 以及
USB OTG,我们依次来看一下这两部分的硬件原理图。

USB HUB 原理图分析

1、V2.4 版本以前底板USB HUB 原理图分析
首先来看一下USB HUB 原理图,I.MX6ULL-ALPHA 使用GL850G 这个HUB 芯片将
I.MX6ULL 的USB OTG2 扩展成了4 路HOST 接口,其中一路供4G 模块使用,因此就剩下了
三个通用的USB A 插座,原理图如图67.2.1.1 所示:

2、V2.4 版本及以后底板USB HUB 原理图分析
V2.4 及以后版本的I.MX6ULL-ALPHA 使用SL2.1A 这个HUB 芯片将I.MX6ULL 的USB OTG2 扩展成了4 路HOST 接口,其中一路供4G 模块使用,因此就剩下了三个通用的USB A
插座,原理图如图67.2.1.2 所示:

图67.2.1.1 和图67.2.1.2 中的GL850G 和SL2.1A,这两个都是符合USB2.0 标准的USB HUB 芯片,支持一拖四扩展,可以将一路USB 扩展为4 路USB HOST 接口。这里我们将
I.MX6ULL 的USB OTG2 扩展出了4 路USB HOST 接口,分别为HUB_DP1/DM1 、
HUB_DP2/DM2、HUB_DP3/DM3 和HUB_DP4/DM4。其中HUB_DP4/DM4 用于4G 模块,因
此对外提供的只有三个USB HOST 接口,这三个USB HOST 接口如图67.2.1.3 所示:


注意,使用GL850G 扩展出来的4 路USB 接口只能用作HOST!

V2.4 版本以前底板USB OTG 原理图分析

I.MX6U-ALPHA 开发板上还有一路USB OTG 接口,使用I.MX6ULL 的USB OTG1 接口。
此路USB OTG 既可以作为主机(HOST),也可以作为从机(DEVICE),从而实现完整的OTG 功
能,原理图如图67.2.2.1 所示:

图67.2.2.1 中左侧的为Mini USB 插座,当OTG 作为从机(DEVICE)的时候USB 线接入此
接口。右侧为USB A 插座,当OTG 作为主机的时候将USB 设备插入到此接口中。前面我们讲
了,如果只有一个Mini USB 插座的话如果要学习OTG 那么就需要再购买一个OTG 线,这样
不方便我们使用。为此正点原子在开发板上集成了一个USB HOST 接口,这样在做OTG 实验
的时候就不需要再另外单独购买一根USB OTG 线了。这里就涉及到硬件对USB ID 线的处理,
图67.2.2.1 中R111 和R31 就是完成此功能的,我们分两部分来分析,既OTG 分别工作在HOST
和DEVICE 的时候硬件工作方式:

从机(DEVICE)模式:图67.2.2.1 中USB_OTG_VBUS 是Mini USB 的电源线,只有插入
Mini USB 线以后USB_OTG_VBUS 才有效(5V)。插入Mini USB 线就表示开发板此时要做从机
(此时不考虑接OTG 线的情况),USB_OTG_VBUS 就是电脑供的5V 电压,由于分压电阻R111
和R31 的作用,此时USB_OTG1_ID 的电压就是4.5V 左右,很明显这一个高电平。前面我们
讲了,当ID 线为高的时候就表示OTG 工作在从机模式。
主机(HOST)模式:主机模式下必须将Mini USB 线拔出来,将USB 设备连接到对应的USB HOST 接口上。Mini USB 线拔出来以后USB_OTG_VBUS 就没有电压了,此时USB_OTG1_ID线就被R31 这个100K 电阻下拉到地,因此USB_OTG1_ID 线的电压就为0,当ID 线为0 的时候就表示OTG 工作在主机模式。
优点就是省去了购买一根Mini USB OTG 线的麻烦,方便我们学习开发,但是在使用的时
候要注意一下几点:

①、我们需要软件设置USB_OTG1_ID 这个IO 的电气属性,默认设置为下拉,也就是默认
工作在主机(HOST)模式下。
②、由于我们修改了OTG 硬件电路,因此就不能在Mini USB 接口上接OTG 线了,如果
要使用HOST 功能就将设备插到开发板板载的USB HOST 接口上。
I.MX6U-ALPHA 开发板上的USB OTG 接口如图67.2.2.2 所示:


图67.2.2.2 中上面的就是主机(HOST)接口,下面的是从机(DEVICE)接口,两个不能同时使
用!

V2.4 及以后版本底板USB OTG 原理图分析

I.MX6U-ALPHA 开发板上有一路USB OTG 接口,使用I.MX6ULL 的USB OTG1 接口。此
路USB OTG 既可以作为主机(HOST),也可以作为从机(DEVICE),从而实现完整的OTG 功能,
原理图如图67.2.3.1 所示:

图67.2.3.1 中只有一个Type-C USB 插座,当OTG 作为从机(DEVICE)的时候USB 线接入
此接口。当OTG 作为主机的时候需要使用Type-C OTG 线。
USB OTG 通过ID 线来完成主从机切换,这里就涉及到硬件对USB ID 线的处理,图67.2.2.1中Type-C 的CC1 和CC2 引脚就是完成传统的USB ID 线功能的。我们简单分析一下主从机切换原理。
从机(DEVICE)模式:图67.2.3.1 中R111 这一个49.9K 的电阻,默认将USB_OTG1_ID 线
拉高,前面我们讲了,当ID 线为高的时候就表示OTG 工作在从机模式。此时由于USB_OTG1_ID
为高电平,因此MOS1(SI2302)导通,因此MT9700HT5 的EN 脚就接地,此时MT9700HT5 的
OUT 引脚就没有输出,所以USB_OTG_VBUS 电压关闭。USB_OTG_VBUS 电压用于在OTG
做HOST 功能的时候,向外部设备提供5V 电源。很明显,在OTG 做从机的时候,OTG 就不
需要向外界提供USB_OTG_VBUS 电源了。这里使用MT9700HT5 这个芯片来实VBUS 电源的
开关控制。

主机(HOST)模式:如果要使用OTG 的HOST 功能,那么必须要使用到Type-C OTG 线。
Type-C OTG 线会将CC1 和CC2 拉低,因此USB_OTG1_ID 线也会被拉低,当ID 线为0 的时
候就表示OTG 工作在主机模式。此时由于USB_OTG1_ID 为低,因此MOS1(SI2302)不导通,
,因此MT9700HT5 的EN 脚就会被R31 这个10K 电阻上拉到5V,所以MT9700HT5 的OUT
引脚就会输出5V 电压,也就是说USB_OTG_VBUS 此时是5V,可以向外部设备提供5V 电源。

USB 协议简析

USB 协议中有很多的基础概念,本节就来看一下这些概念。

USB 描述符

顾名思义,USB 描述符就是用来描述USB 信息的,描述符就是一串按照一定规则构建的
字符串,USB 设备使用描述符来向主机报告自己的相关属性信息,常用的描述符如表67.3.1.1所示:


我们依次来看一下表67.3.1.1 中这5 个描述符的含义:

1、设备描述符
设备描述符用于描述USB 设备的一般信息,USB 设备只有一个设备描述符。设备描述符
里面记录了设备的USB 版本号、设备类型、VID(厂商ID)、PID(产品ID)、设备序列号等。设
备描述符结构如表67.3.1.2 所示:



2、配置描述符
设备描述符的bNumConfigurations 域定义了一个USB 设备的配置描述符数量,一个USB
设备至少有一个配置描述符。配置描述符描述了设备可提供的接口(Interface)数量、配置编号、
供电信息等,配置描述符结构如表67.3.1.3 所示:

3、字符串描述符
字符串描述符是可选的,字符串描述符用于描述一些方便人们阅读的信息,比如制造商、
设备名称啥的。如果一个设备没有字符串描述符,那么其他描述符中和字符串有关的索引值都
必须为0,字符串描述符结构如表67.3.1.4 所示:

wLANGID[0]~wLANGID[x] 指明了设备支持的语言,具体含义要查阅文档
《USB_LANGIDs.pdf 》,此文档已经放到了开发板光盘中,路径为:4 、参考资料-> USB_LANGIDs.pdf。
主机会再次根据自己所需的语言向设备请求字符串描述符,这次会主机会指明要得到的字
符串索引值和语言。设备返回Unicode 编码的字符串描述符,结构如表67.3.1.5 所示:

4、接口描述符
配置描述符中指定了该配置下的接口数量,可以提供一个或多个接口,接口描述符用于描
述接口属性。接口描述符中一般记录接口编号、接口对应的端点数量、接口所述的类等,接口
描述符结构如表67.3.1.6 所示:

5、端口描述符
接口描述符定义了其端点数量,端点是设备与主机之间进行数据传输的逻辑接口,除了端
点0 是双向端口,其他的端口都是单向的。端点描述符描述了传输类型、方向、数据包大小、
端点号等信息,端点描述符结构如表67.3.1.7 所示:


USB 数据包类型

USB 是串行通信,需要一位一位的去传输数据,USB 传输的时候先将原始数据进行打包,
所以USB 中传输的基本单元就是数据包。根据用途的不同,USB 协议定义了4 种不同的包结
构:令牌(Token)包、数据(Data)包、握手(Handshake)包和特殊(Special)包。这四种包通过包标识
符PID 来区分,PID 共有8 位,USB 协议使用低4 位PID3PID0,另外的高四位PID7PID4 是
PID3~PID0 的取反,传输顺序是PID0、PID1、PID2、PID3…PID7。令牌包的PID1~0 为01,数
据包的PID1~0 为11,握手包的PID1~0 为10,特殊包的PID1~0 为00。每种类型的包又有多
种具体的包,如表67.3.3.1 所示:


一个完整的包分为多个域,所有的数据包都是以同步域(SYNC)开始,后面紧跟着包标识符
(PID),最终都以包结束(EOP)信号结束。不同的数据包中间位域不同,一般有包目标地址(ADDR)、
包目标端点(ENDP)、数据、帧索引、CRC 等,这个要具体数据包具体分析。接下来简单看一下
这些数据包的结构。

1、令牌包
令牌包结构如图67.3.3.1 所示:

图67.3.3.1 是一个SETUP 令牌包结构,首先是SYNC 同步域,包同步域为00000001,也
就是连续7 个0,后面跟一个1,如果是高速设备的话就是31 个0 后面跟一个1。紧跟着是PID,
这里是SETUP 包,为0XB4,大家可能会好奇为什么不是0X2D(00101101),0XB4 的原因如下:
①、SETUP 包的PID3~PID0 为1101,因此对应的PID7~PID4 就是0010。
②、PID 传输顺序为PID0、PID1、PID2…PID7,因此按照传输顺序排列的话此处的PID 就
是10110100=0XB4,并不是0X2D。
PID 后面跟着地址域(ADDR)和端点域(ENDP),为目标设备的地址和端点号。CRC5 域是5
位CRC 值,是ADDR 和ENDP 这两个域的校验值。最后就是包结束域(EOP),标记本数据包结
束。其他令牌包的结构和SETUP 基本类似,只是SOF 令包中间没有ADDR 和ENDP 这两个
域,而是只有一个11 位的帧号域。

2、数据包
数据包结构如图67.3.3.2 所示:

数据包比较简单,同样的,数据包从SYNC 同步域开始,然后紧跟着是PID,这里就是DATA0,PID 值为0XC3。接下来就是具体的数据,数据完了以后就是16 位的CRC 校验值,最后是EOP。
3、握手包
握手包结构如图67.3.3.3 所示:

图67.3.3.3 是ACK 握手包,很简单,首先是SYNC 同步域,然后就是ACK 包的PID,为0X4B,最后就是EOP。其他的NAK、STALL、NYET 和ERR 握手包结构都是一样的,只是其中的PID 不同而已。

USB 传输类型

在端点描述符中bmAttributes 指定了端点的传输类型,一共有4 种,本节我们来看一下这
四种传输类型的区别。

1、控制传输
控制传输一般用于特定的请求,比如枚举过程就是全部由控制传输来完成的,比如获取描
述符、设置地址、设置配置等。控制传输分为三个阶段:建立阶段(SETUP)、数据阶段(DATA)
和状态阶段(STATUS),其中数据阶段是可选的。建立阶段使用SETUP 令牌包,SETUP 使用
DATA0 包。数据阶段是0 个、1 个或多个输入(IN)/输出(OUT)事务,数据阶段的所有输入事务必须是同一个方向的,比如都为IN 或都为OUT。数据阶段的第一个数据包必须是DATA1,每
次正确传输以后就在DATA0 和DATA1 之间进行切换。数据阶段完成以后就是状态阶段,状态
阶段的传输方向要和数据阶段相反,比如数据阶段为IN 的话状态阶段就要为OUT,状态阶段
使用DATA1 包。比如一个读控制传输格式如图67.3.4.1 所示:

2、同步传输
同步传输用于周期性、低时延、数据量大的场合,比如音视频传输,这些场合对于时延要
求很高,但是不要求数据100%正确,允许有少量的错误。因此,同步传输没有握手阶段,即使
数据传输出错了也不会重传。
3、批量传输
提起“批量”,我们第一反应就是“多”、“大”等,因此,批量传输就是用于大批量传输大
块数据的,这些数据对实时性没有要求,比如MSD 类设备(存储设备),U 盘之类的。批量传输
分为批量读(输入)和批量写(输出),如果是批量读的话第一阶段的IN 令牌包,如果是批量写那
么第一阶段就是OUT 令牌包。
我们就以批量写为例简单介绍一下批量传输过程:
①、主机发出OUT 令牌包,令牌包里面包含了设备地址、端点等信息。
②、如果OUT 令牌包正确的话,也就是设备地址和端点号匹配,主机就会向设备发送一个
数据(DATA)包,发送完成以后主机进入接收模式,等待设备返回握手包,一切都正确的话设备
就会向主机返回一个ACK 握手信号。
批量读的过程刚好相反:
①、主机发出IN 令牌包,令牌包里面包含了设备地址、端点等信息。发送完成以后主机就
进入到数据接收状态,等待设备返回数据。
②、如果IN 令牌包正确的话,设备就会将一个DATA 包放到总线上发送给主机。主机收
到这个DATA 包以后就会向设备发送一个ACK 握手信号。
4、中断传输
这里的中断传输并不是我们传统意义上的硬件中断,而是一种保持一定频率的传输,中断
传输适用于传输数据量小、具有周期性并且要求响应速度快的数据,比如键盘、鼠标等。中断
的端点会在端点描述符中报告自己的查询时间间隔,对于时间要求严格的设备可以采用中断传
输。

USB 枚举

当USB 设备与USB 主机连接以后主机就会对USB 设备进行枚举,通过枚举来获取设备的
描述符信息,主机得到这些信息以后就知道该加载什么样的驱动、如何进行通信等。USB 枚举
过程如下:
①、第一回合,当USB 主机检测到USB 设备插入以后主机会发出总线复位信号来复位设
备。USB 设备复位完成以后地址为0,主机向地址0 的端点0 发送数据,请求设备的描述符。
设备得到请求以后就会按照主机的要求将设备描述符发送给主机,主机得到设备发送过来的设备描述符以后,如果确认无误就会向设备返回一个确认数据包(ACK)。
②、第二回合,主机再次复位设备,进入地址设置阶段。主机向地址0 的端点0 发送设置
地址请求数据包,新的设备地址就包含在这个数据包中,因此没有数据过程。设备进入状态过
程,等待主机请求状态返回,收到以后设备就会向主机发送一个0 字节状态数据包,表明设备
已经设置好地址了,主机收到这个0 字节状态数据包以后会返回一个确认包(ACK)。设备收到
主机发送的ACK 包以后就会使用这个新的设备地址,至此设备就得到了一个唯一的地址。
③、第三回合,主机向新的设备地址端点0 发送请求设备描述符数据包,这一次主机要获
取整个设备描述符,一共是18 个字节。
④、和第③步类似,接下来依次获取配置描述符、配置集合、字符串描述符等等。

Linux 内核自带HOST 实验

USB 鼠标键盘测试

首先做一下USB HOST 试验,也就是I.MX6U-ALPHA 开发板做USB 主机,然后外接USB
设备,比如USB 鼠标键盘、USB 转TTL 串口线、U 盘等设备。Linux 内核已经集成了大量的
USB 设备驱动,尤其是我们常见的USB 鼠标键盘、U 盘等,本节我们就来学习一下如何使能
Linux 内核常见的USB 设备驱动。

1、USB 鼠标键盘驱动使能

注意,NXP 官方的Linux 内核默认已经使能了USB 键盘鼠标驱动!
USB 鼠标键盘属于HID 设备,内核已经集成了相应的驱动,NXP 官方提供的linux 内核默
认已经使能了USB 鼠标键盘驱动,但是我们还要学习一下如何手动使能这些驱动。输入“make
menuconfig”打开linux 内核配置界面,首先打开HID 驱动,按照如下路径到相应的配置项目:

-> Device Drivers-> HID support-> HID bus support (HID [=y])-> <*> Generic HID driver     //使能通用HID 驱动

使能以后如图67.4.1.1 所示:

接下来需要使能USB 键盘和鼠标驱动,配置路径如下:

-> Device Drivers-> HID support-> USB HID support-> <*> USB HID transport layer    //USB 键盘鼠标等HID 设备驱动

使能以后如图67.4.1.2 所示:

大家可以将光标放到图67.4.1.2 中“USB HID Transport layer”这一行,然后按下“?”键打
开对应的帮助信息就可以看到对于这个配置项的描述,简单总结一下:
此选项对应配置项就是CONFIG_USB_HID,也就是USB 接口的HID 设备。如果要使用
USB 接口的keyboards(键盘)、mice(鼠标)、joysticks(摇杆)、graphic tablets(绘图板)等其他的HID
设备,那么就需要选中“USB HID Transport layer”。但是要注意一点,此驱动和HIDBP(Boot
Protocol)键盘、鼠标的驱动不能一起使用!所以大家要是在网上查阅linux 内核USB 键盘鼠标
驱动的时候,发现推荐使用“USB HIDBP Keyboard (simple Boot) support”和“USB HIDBP Mouse (simple Boot) support”这两个配置项的时候也不要觉得教程这里写错了。
2、测试USB 鼠标和键盘
完成以后重新编译linux 内核并且使用得到的zImage 启动开发板。启动以后插入USB 鼠
标,会有如图67.4.1.3 所示的提示信息:

从图67.4.1.3 可以看出,系统检测到了Logitech(罗技)的鼠标,如果成功驱动的话就会在
/dev/input 目录下生成一个名为eventX(X=0,1,2,3…)的文件,这个就是我们前面讲的输入子系统,
鼠标和键盘都是作为输入子系统设备的。笔者这里对应的就是/dev/input/event3 这个设备,使用
如下命令查看鼠标的原始输入值,结果如图67.4.1.4 所示:

图67.4.1.4 就是鼠标作为输入子系统设备的原始输入值,这里就不去分析了,我们在移植
GUI 图形库以后就可以直接使用鼠标,比如QT 等。
注意,有些鼠标可能会出现隔一段时间自动断开重连的现象,比如我测试的电脑城送的几
块钱的罗技鼠标就会这样,我用NXP 官方的EVK 开发板测试也有这个问题!我自己用的雷蛇
鼠标就不会有这种现象。但是,如果你一直使用这个鼠标,比如用hexdump 命令一直查看鼠标
上报值,或者在QT 里面一直使用鼠标,那么这些鼠标就不会自动断开并重连。
最后再来测试一下USB 键盘,屏幕已经驱动起来了,所以我们可以直接将屏幕作为终端,
然后接上键盘直接输入命令来进行各种操作。首先将屏幕设置为控制台,打开开发板根文件系
统中的/etc/inittab 文件,然后在里面加入下面这一行:

tty1::askfirst:-/bin/sh

完成以后重启开发板,此时屏幕就会作为终端控制台,会有“Please press Enter to activate this console.”这样提示,如图67.4.1.5 所示:

接上键盘,然后根据图67.4.1.5 中的提示,按下键盘上的Enter(回车)键即可使能LCD 屏幕
控制台,然后我们就可以输入各种命令来执行相应的操作,如图67.4.1.6 所示:

U 盘实验

注意,NXP 官方的Linux 内核默认已经使能了U 盘!
NXP 提供的Linux 内核默认也已经使能了U 盘驱动,因此我们可以直接插上去使用。但是
我们还是需要学习一下如何手动配置Linux 内核,使能U 盘驱动。

1、使能U 盘驱动
U 盘使用SCSI 协议,因此要先使能Linux 内核中的SCSI 协议,配置路径如下:

-> Device Drivers-> SCSI device support-> <*> SCSI disk support //选中此选项

结果如图67.4.2.1 所示:

我们还需要使能USB Mass Storage,也就是USB 接口的大容量存储设备,配置路径如下:

-> Device Drivers-> USB support (USB_SUPPORT [=y])-> Support for Host-side USB (USB [=y])-> <*> USB Mass Storage support //USB 大容量存储设备

结果如图67.4.2.2 所示:

2、U 盘测试
准备好一个U 盘,注意U 盘要为FAT32 格式的!NTFS 和exFAT 由于版权问题所以在Linux
下支持的不完善,操作的话可能会有问题,比如只能读,不能写或者无法识别等。准备好以后
将U 盘插入到开发板USB HUB 扩展出来的HOST 接口上,此时会输出如图67.4.2.3 所示信息:

从图67.4.2.3 可以看出,系统检测到U 盘插入,大小为32GB,对应的设备文件为/dev/sda
和/dev/sda1,大家可以查看一下/dev 目录下有没有sda 和sda1 这两个文件。/dev/sda 是整个U
盘,/dev/sda1 是U 盘的第一个分区,我们一般使用U 盘的时候都是只有一个分区。要想访问U
盘我们需要先对U 盘进行挂载,理论上挂载到任意一个目录下都可以,这里我创建一个
/mnt/usb_disk 目录,然后将U 盘挂载到/mnt/usb_disk 目录下,命令如下:

mkdir /mnt/usb_disk -p //创建目录
mount /dev/sda1 /mnt/usb_disk/ -t vfat -o iocharset=utf8 //挂载

-t 指定挂载所使用的文件系统类型,这里设置为vfat,也就是FAT 文件系统,“-o iocharset”
设置硬盘编码格式为utf8,否则的话U 盘里面的中文会显示乱码!
挂载成功以后进入到/mnt/usb_disk 目录下,输入ls 命令查看U 盘文件,如图67.4.2.4 所示:

至此U 盘就能正常读写操作了,直接对/mnt/usb_disk 目录进行操作就行了。如果要拔出U
盘要执行一个sync 命令进行同步,然后在使用unmount 进行U 盘卸载,命令如下所示:

sync         //同步
cd /        //如果处于/mnt/usb_disk 目录的话先退出来,否则卸载的时候提示设//备忙,导致卸载失败,切记!
umount /mnt/usb_disk //卸载

Linux 内核自带USB OTG 实验

修改设备树

注意,如果使用的是正点原子I.MX6U-ALPHA 开发板,那么就需要修改OTG ID 引脚的电
气属性。如果使用的其他6ULL 开发板,就要去咨询一下厂商,看看需不需要修改ID 引脚的电
气属性。
查阅原理图可以知道,USB OTG1 的ID 引脚连接到了I.MX6ULL 的GPIO1_IO00 这个引脚上,在67.2.2 小节分析ALPHA 开发板USB OTG 原理图的时候已经说过了,USB OTG 默认
工作在主机(HOST)模式下,因此ID 线应该是低电平。这里需要修改设备树中GPIO1_IO00 这
个引脚的电气属性,将其设置为默认下拉。打开设备树imx6ull-alientek-emmc.dts,在iomuxc 节
点的pinctrl_hog_1 子节点下添加GPIO1_IO00 引脚信息,如下所示:

示例代码67.5.1.1 GPIO1_IOI00 引脚描述信息
1 &iomuxc {2 pinctrl-names = "default";
3 pinctrl-0 = <&pinctrl_hog_1>;
4 imx6ul-evk {5   pinctrl_hog_1: hoggrp-1 {6       fsl,pins = <
7           ......
8           MX6UL_PAD_GPIO1_IO00__ANATOP_OTG1_ID 0x13058 /*OTG1 ID */
9       >;
10  };
11  ......
12 };

第8 行就是将GPIO1_IO00 复用为OTG1 ID,并且设置电气属性为0X13058,默认下拉,
设备树修改好以后重新编译并用新的设备树启动系统。

OTG 主机实验

系统重启成功以后就可以正常使用USB OTG1 接口,OTG 既可以做主机,也可以做从机,
做主机的话测试方法和67.4 小节一模一样。如果是V2.4 版本以前的底板,直接在ALPHA 的
OTG HOST 接口上插入USB 鼠标键盘、U 盘等设备。如果是V2.4 及以后版本底板,要使用
Type-C OTG 线连接USB 鼠标键盘,U 盘等设备。
注意!如果使用正点原子的V2.4 版本以前的ALPHA 开发板,切记不要使用Mini OTG 线
来外接USB 设备,原因已经在67.2.2 小节说明了,只需要将USB 设备插入到开发板上的OTG HOST 接口上即可!

OTG 从机实验

OTG 从机就是将开发板作为一个USB 设备连接到其他的主机上,这里我们来做两个USB
从机实验:模拟U 盘以及USB 声卡。
1、模拟U 盘实验
模拟U 盘实验就是将开发板当做一个U 盘,可以将开发板上的U 盘或者TF 卡挂载到PC
上去,首先需要配置Linux,配置路径如下:

-> Device Drivers-> USB support (USB_SUPPORT [=y])-> USB Gadget Support (USB_GADGET [=y]-> [M]USB Gadget Drivers (<choice> [=m]) //选中USB Gadget 驱动->[M]Mass Storage Gadget            //大容量存储

如图67.5.3.1 所示:

这里我们需要将驱动编译为模块!使用的时候直接输入命令加载驱动模块即可。配置好以
后重新编译Linux 内核,会得到三个.ko 驱动模块(带路径):

drivers/usb/gadget/libcomposite.ko
drivers/usb/gadget/function/usb_f_mass_storage.ko
drivers/usb/gadget/legacy/g_mass_storage.ko

将上述三个.ko 模块拷贝到开发板根文件系统中,命令如下:

cd drivers/usb/gadget/ //进入gadget 目录下
sudo cp libcomposite.ko /home/zuozhongkai/linux/nfs/rootfs/lib/modules/4.1.15/
sudo cp function/usb_f_mass_storage.ko /home/zuozhongkai/linux/nfs/rootfs/lib/modules/4.1.15/
sudo cp legacy/g_mass_storage.ko /home/zuozhongkai/linux/nfs/rootfs/lib/modules/4.1.15/

拷贝完成以后使用新编译出来的zImage 启动开发板,在开发板上插入一个U 盘,记住这
个U 盘对应的设备文件,比如我们这里是/dev/sda 和/dev/sda1,以后要将/dev/sda1 挂载到PC 上,也就是把/dev/sda1 作为模拟U 盘的存储区域。
V2.4 版本以前的底板使用Mini USB 线将开发板的USB OTG Mini 接口与电脑连接起来,

如图67.5.3.2 所示:

V2.4 及以后版本的底板使用Type-C 线将开发板的USB Type-C 接口与电脑连接起来,如图
67.5.3.3 所示:

连接好以后依次加载libcomposite.ko、usb_f_mass_storage.ko 和g_mass_storage.ko 这三个驱
动文件,顺序不能错了!命令如下:

depmod
modprobe libcomposite.ko
modprobe usb_f_mass_storage.ko
modprobe g_mass_storage.ko file=/dev/sda1 removable=1

加载g_mass_storage.ko 的时候使用file 参数指定使用的大容量存储设备,我这里使用U 盘
对应的/dev/sda1。如果加载成功的话电脑就会出现一个U 盘,这个U 盘就是我们开发板模拟的,如图67.5.3.3 所示:

我们可以直接在电脑上对这个U 盘进行读写操作,实际上操作的就是插在开发板上的U
盘。操作完成以后要退出的话执行如下命令:

rmmod g_mass_storage.ko

注意!不要将开发板上的EMMC 或者NAND 作为模拟U 盘的存储区域,因为linux 下
EMMC 和NAND 使用的文件系统一般都是EXT3/EXT4 和UBIFS,这些文件系统类型和windows
下的不兼容,如果挂载的话就会在windows 下提示要你格式化U 盘!

2、USB 声卡实验
USB 声卡就是USB 接口的外置声卡,一般电脑内部都自带了声卡,但是内部自带的声卡
效果相对来说比较差,不能满足很多HIFI 玩家的需求。USB 声卡通过USB 接口来传递音频数据,具体的ADC 和DAC 过程由声卡完成,摆脱了电脑主板体积的限制,外置USB 声卡就可以做的很好。ALPHA 开发板板载了音频解码芯片,因此可以将ALPHA 开发板作为一个外置USB 声卡,配置Linux 内核,配置路径如下:

-> Device Drivers-> USB support (USB_SUPPORT [=y])-> USB Gadget Support (USB_GADGET [=y]-> [M]USB Gadget Drivers   //选中USB Gadget 驱动->[M] Audio Gadget      //选中音频->UAC 1.0 (Legacy)     //选中UAC

配置如图67.5.3.4 所示:

注意,这里也是编译为驱动模块,配置完成以后重新编译内核,得到新的zImage 和三个.ko
驱动模块文件:

drivers/usb/gadget/libcomposite.ko
drivers/usb/gadget/function/usb_f_uac1.ko
drivers/usb/gadget/legacy/g_audio.ko

将上述三个.ko 模块拷贝到开发板根文件系统中,命令如下:

cd drivers/usb/gadget/
sudo cp libcomposite.ko /home/zuozhongkai/linux/nfs/rootfs/lib/modules/4.1.15/
sudo cp function/usb_f_uac1.ko /home/zuozhongkai/linux/nfs/rootfs/lib/modules/4.1.15/
sudo cp legacy/g_audio.ko /home/zuozhongkai/linux/nfs/rootfs/lib/modules/4.1.15/

拷贝完成以后使用新编译出来的zImage 启动开发板,首先按照65.5.2 小节讲解的方法配
置ALPHA 的声卡,保证声卡播放正常!使用Mini USB 线将开发板与电脑连接起来,最后依次
加载libcomposite.ko、usb_f_uac1.ko 和g_audio.ko 这三个驱动模块,命令如下:

depmod
modprobe libcomposite.ko
modprobe usb_f_uac1.ko
modprobe g_audio.ko

加载完成以后稍等一会虚拟出一个USB 声卡,打开电脑的设备管理器,选择“声音、视频
和游戏控制器”,会发现有一个名为“AC Interface”设备,如图67.5.3.5 所示:

图67.5.3.5 中的“AC Interface”就是开发板模拟出来的USB 声卡,设置windows,选择音
频输出使用“AC Interface”,Windows10 设置如图67.5.3.6 所示:

一切设置好以后就可以从开发板上听到电脑输出的声音,此时开发板就完全是一个USB 声
卡设备了。

关于USB 驱动就讲解到这里,本章并没有深入到USB 驱动具体编写方式,只是对USB 的
协议做了简单的介绍,后面讲解了一下Linux 内核自带的USB HOST 和DEVICE 驱动的使用,
Linux 内核已经集成了大量的USB 设备驱动,至于其他特殊的就需要具体情况具体分析了,比
如本教程后面讲解的USB WIFI 和4G 模块驱动。

Linux USB 驱动实验相关推荐

  1. 【正点原子Linux连载】第六十七章 Linux USB驱动实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  2. Linux USB驱动程序设计

    Linux USB驱动程序设计 1. USB发展史 USB(Universal Serial Bus ),通用串行总线,是一种外部总线标准,用于规范电脑与外部设备的连接和通讯. USB是在1994年底 ...

  3. cups源码下载 linux_正点原子Linux第七十章Linux WIFI驱动实验

    1)资料下载:点击资料即可下载 2)对正点原子Linux感兴趣的同学可以加群讨论:935446741 3)关注正点原子公众号,获取最新资料更新 第七十章Linux WIFI驱动实验 WIFI的使用已经 ...

  4. 【正点原子Linux连载】第七十章 Linux WIFI驱动实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  5. Linux USB驱动框架分析 【转】

    转自:http://blog.chinaunix.net/uid-11848011-id-96188.html 初次接触与OS相关的设备驱动编写,感觉还挺有意思的,为了不至于忘掉看过的东西,笔记跟总结 ...

  6. linux i2c adapter 增加设备_「正点原子Linux连载」第六十二章Linux SPI驱动实验(一)...

    1)实验平台:正点原子Linux开发板 2)摘自<正点原子I.MX6U嵌入式Linux驱动开发指南>关注官方微信号公众号,获取更多资料:正点原子 第六十二章Linux SPI驱动实验 上一 ...

  7. 韦东山 IMX6ULL和正点原子_正点原子Linux第五十七章Linux MISC驱动实验

    1)资料下载:点击资料即可下载 2)对正点原子Linux感兴趣的同学可以加群讨论:935446741 3)关注正点原子公众号,获取最新资料更新 第五十七章Linux MISC驱动实验 misc的意思是 ...

  8. 【正点原子MP157连载】第四十章 Linux I2C驱动实验-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7

    1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...

  9. Linux USB 驱动开发实例(七)—— 基于USB 总线的无线网卡浅析

    回顾一下USB的相关知识 USB(Universal Serial Bus)总线又叫通用串行外部总线, 它是20世纪90年代发展起来的.USB接口现在得到了广泛的应用和普及,现在的PC机中都带有大量的 ...

最新文章

  1. linux 系统如何防止攻击
  2. 文献学习(part47)--A novel consensus learning approach to incomplete multi-view clustering
  3. 函授计算机大专自我鉴定100字,函授大学毕业自我鉴定100字(精选5篇)
  4. linux系统如何创建python文件_Linux搭建python环境详解
  5. 在线溶氧仪 连接计算机,在线溶氧仪集成设计与数据处理算法分析.pdf
  6. Javascript图像处理之平滑处理
  7. Silverlight 2.0 RTW 正式版发布(附下载地址)
  8. cad.net objectarx 后台打开dwg
  9. Android 微信人脸识别+微信二维码
  10. 基于英雄联盟寻路背景的A星算法及python实现
  11. 如何以应届生的身份进入阿里巴巴?
  12. python爆破ZIP文件(支持纯数字,数字+字母,密码本)
  13. Thinkphp3.2.3安全开发须知
  14. 学生信息管理系统-web项目实训(基于JAVA)
  15. 【敬伟ps教程】平移、缩放、移动、选区
  16. 剑指offer之简单题(三)
  17. stm32中的或运算 ||
  18. 计算机专业学习阶段计划书怎么写,计算机专业学习计划优秀范文2篇
  19. python w3c_python w3c
  20. OS集 just do somthing

热门文章

  1. 大厂面试官问你这些面试题你答的出吗?(附答案)
  2. 保持坦然平和的心态!
  3. 学考计算机隐藏,2018年计算机一级考试WPS辅导知识:隐藏文字和添加拼音的用法...
  4. ibdata1文件过大问题
  5. PHPMailer发送邮件(PHP发送电子邮件)
  6. Java好还是网优好_大神告诉你|Java好还是Python好?
  7. 远​程​桌​面​登​陆​后​黑​屏​或​无​法​进​行​操​作​的​解​决​办​法
  8. 大数据公司Palantir融得7亿美元 曾追踪拉登
  9. 声音辨识系统专题报告(转载)
  10. word中录入打钩方框——设计调查问卷必备技能