USB 是很常用的接口,目前大多数的设备都是 USB 接口的,比如鼠标、键盘、 USB 摄像头等,我们在实际开发中也常常遇到 USB 接口的设备。
1 USB接口简介
1.1 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 等公司组成的 USBIF(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和 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 标准的命名, USBIF 公布了最新的 USB 命名规范,原来的 USB3.0 和 USB3.1 命名将不会采用,所有的 3.0 版本的 USB 都命名为 USB3.2,以前的 USB3.0、 USB3.1 和 USB3.2 分别叫做 USB3.2 Gen1、 USB3.2 Gen2、 USB3.2 Gen 2X2。
USB4.0: 目前还在标准定制中,目前还没有设备搭载,据说是在 Inter 的雷电接口上改进而来。 USB4.0 的速度将提升到了 40Gbps,最高支持 100W 的供电能力,只需要一根线就可以完成数据传输与供电,极大的简化了设备之间的链接线数,期待 USB4.0 设备上市。
如果按照接口类型划分的话 USB 就要分为很多种了,最常见的就是 USB A 插头和插座。
使用过JLINK 调试器的朋友应该还见过USB B插头和插座。
USB 插头在不断的缩小,由此产生了 Mini USB 接口
比 Mini USB 更小的就是 Micro USB 接口了,以前的智能手机基本都是 Micro USB 接口的
现在最流行的就是 USB Typec
1.2 USB电气特性
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 插头有 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 的电流。
1.3 USB拓扑结构
USB 是主从结构的,也就是分为主机和从机两部分,一般主机叫做 Host,从机叫做 Device。主机就是提供 USB A 插座来连接外部的设备,比如电脑作为主机,对外提供 USB A 插座,我们可以通过 USB 线来连接一些 USB 设备,比如声卡、手机等。因此电脑带的 USB A 插座数量就决定了你能外接多少个 USB 设备,如果不够用的话我们可以购买 USB 集线器来扩展电脑的USB 插口, USB 集线器也叫做 USB HUB。
一个一拖四的 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 的拓扑结构就是一个分层的金字塔形,如图所示(参考自USB2.0 协议中文版.pdf):
图中可以看出从 Root Hub 开始,一共有 7 层,金字塔顶部是 Root Hub,这个是USB 控制器内部的。图中的 Hub 就是连接的 USB 集线器, Func 就是具体的 USB 设备。
USB 主机和从机之间的通信通过管道(Pipe)来完成,管道是一个逻辑概念,任何一个 USB设备一旦上电就会存在一个管道,也就是默认管道, USB 主机通过管道来获取从机的描述符、配置等信息。在主机端管道其实就是一组缓冲区,用来存放主机数据,在设备端管道对应一个特定的端点。
1.4 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 线的接口,USB_OTG 连接到了 I.MX6ULL 的USB1 接口上。 如果只有一个 Type-C USB 接口的话如果要使用 OTG 的主机模式,那么就需要一根 OTG 线,Type-C USB OTG 线。
可以看到,一头是 USB A 插座, 另外一头是 Type-C USB 插头,将Type-C USB 插头插入机器的 Type-C 接口上,需要连接的 USB 设备插到另一端的 USB A 插座上,比如 U 盘啥的。USB OTG 线会将 ID 线拉低,这样机器就知道自己要做为一个主机,用来连接外部的从机设备(U 盘)。
1.5 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,一个做OTG,基本上是USB DEVICE,用来烧写代码。另外一个用来连接USB HUB,扩展USB HOST接口。
- 两个USB接口都集成了PHY,最好支持到480M。
每个 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 出现的目的就是为了替换前面三个。
2 硬件原理图
mini板的USB原理图如下,mini板是没有USB hub的。
注意,USB的SHILED不是ID线,这里是固定座子。
从图中可以看到,当什么也没有插或者插入的是普通USB从设备,R83把USB OTG1 ID默认拉高,工作在从机模式下。当插入 OTG 线,ID引脚接地,USB_OTG1_ID拉到0V, MT9700HT5 输出,打开 MOSFET(SI2302),VBUS 输出 5V 给对端设备用。
3 USB协议简析
3.1 USB描述符
USB 描述符就是用来描述 USB 信息的,描述符就是一串按照一定规则构建的字符串, USB 设备使用描述符来向主机报告自己的相关属性信息,常用的描述符如下
1设备描述符
设备描述符用于描述 USB 设备的一般信息, USB 设备只有一个设备描述符。设备描述符里面记录了设备的 USB 版本号、设备类型、 VID(厂商 ID)、 PID(产品 ID)、设备序列号等。
2配置描述符
设备描述符的 bNumConfigurations 域定义了一个 USB 设备的配置描述符数量,一个 USB设备至少有一个配置描述符。配置描述符描述了设备可提供的接口(Interface)数量、配置编号、供电信息等 。
![]()
3字符串描述符
字符串描述符是可选的,字符串描述符用于描述一些方便人们阅读的信息,比如制造商、设备名称啥的。如果一个设备没有字符串描述符,那么其他描述符中和字符串有关的索引值都必须为 0 。
wLANGID[0]~wLANGID[x] 指 明 了 设 备 支 持 的 语 言 , 具 体 含 义 要 查 阅 文 档《 USB_LANGIDs.pdf 》
主机会再次根据自己所需的语言向设备请求字符串描述符,这次会主机会指明要得到的字符串索引值和语言。设备返回 Unicode 编码的字符串描述符 。
4接口描述符
配置描述符中指定了该配置下的接口数量,可以提供一个或多个接口,接口描述符用于描述接口属性。接口描述符中一般记录接口编号、接口对应的端点数量、接口所述的类。
5端口描述符
接口描述符定义了其端点数量,端点是设备与主机之间进行数据传输的逻辑接口,除了端点 0 是双向端口,其他的端口都是单向的。端点描述符描述了传输类型、方向、数据包大小、端点号等信息。
3.2 USB数据包类型
USB 是串行通信,需要一位一位的去传输数据, USB 传输的时候先将原始数据进行打包,所以 USB 中传输的基本单元就是数据包。根据用途的不同, USB 协议定义了 4 种不同的包结构:令牌(Token)包、数据(Data)包、握手(Handshake)包和特殊(Special)包。这四种包通过包标识符 PID 来区分, PID 共有 8 位, USB 协议使用低 4 位 PID3
PID0,另外的高四位 PID7PID4 是PID3PID0 的取反,传输顺序是 PID0、 PID1、 PID2、 PID3…PID7。令牌包的 PID10 为 01,数据包的 PID10 为 11,握手包的 PID10 为 10,特殊包的 PID1~0 为 00。每种类型的包又有多种具体的包。
一个完整的包分为多个域,所有的数据包都是以同步域(SYNC)开始,后面紧跟着包标识符(PID),最终都以包结束(EOP)信号结束。不同的数据包中间位域不同,一般有包目标地址(ADDR)、包目标端点(ENDP)、数据、帧索引、 CRC 等,这个要具体数据包具体分析。接下来简单看一下这些数据包的结构。
1 令牌包
首先是 SYNC 同步域,包同步域为 00000001,也就是连续 7 个 0,后面跟一个 1,如果是高速设备的话就是 31 个 0 后面跟一个 1。紧跟着是 PID,这里是 SETUP 包,为 0XB4,大家可能会好奇为什么不是 0X2D(00101101), 0XB4 的原因如下:
- SETUP 包的 PID3
PID0 为 1101,因此对应的 PID7PID4 就是 0010。- ID 传输顺序为 PID0、 PID1、 PID2…PID7,因此按照传输顺序排列的话此处的 PID 就是 10110100=0XB4,并不是 0X2D。
PID 后面跟着地址域(ADDR)和端点域(ENDP),为目标设备的地址和端点号。 CRC5 域是 5位 CRC 值,是 ADDR 和 ENDP 这两个域的校验值。最后就是包结束域(EOP),标记本数据包结束。其他令牌包的结构和 SETUP 基本类似,只是 SOF 令包中间没有 ADDR 和 ENDP 这两个域,而是只有一个 11 位的帧号域。
2 数据包
数据包比较简单,同样的,数据包从 SYNC 同步域开始,然后紧跟着是 PID,这里就是DATA0, PID 值为 0XC3。接下来就是具体的数据,数据完了以后就是 16 位的 CRC 校验值,最后是 EOP。
3 握手包
首先是 SYNC 同步域,然后就是 ACK 包的 PID,为0X4B,最后就是 EOP。其他的 NAK、 STALL、 NYET 和 ERR 握手包结构都是一样的,只是其中的 PID 不同而已。
3.3 USB传输类型
在端点描述符中 bmAttributes 指定了端点的传输类型,一共有 4 种,本节我们来看一下这四种传输类型的区别。
控制传输
控制传输一般用于特定的请求,比如枚举过程就是全部由控制传输来完成的,比如获取描述符、设置地址、设置配置等。控制传输分为三个阶段:建立阶段(SETUP)、数据阶段(DATA)和状态阶段(STATUS),其中数据阶段是可选的。建立阶段使用 SETUP 令牌包, SETUP 使用DATA0 包。数据阶段是 0 个、 1 个或多个输入(IN)/输出(OUT)事务,数据阶段的所有输入事务必须是同一个方向的,比如都为 IN 或都为 OUT。数据阶段的第一个数据包必须是 DATA1,每次正确传输以后就在 DATA0 和 DATA1 之间进行切换。数据阶段完成以后就是状态阶段,状态阶段的传输方向要和数据阶段相反,比如数据阶段为 IN 的话状态阶段就要为 OUT,状态阶段使用 DATA1 包。
同步传输
同步传输用于周期性、低时延、数据量大的场合,比如音视频传输,这些场合对于时延要求很高,但是不要求数据 100%正确,允许有少量的错误。因此,同步传输没有握手阶段,即使数据传输出错了也不会重传。
批量传输
提起“批量”,我们第一反应就是“多”、“大”等,因此,批量传输就是用于大批量传输大块数据的,这些数据对实时性没有要求,比如 MSD 类设备(存储设备), U 盘之类的。批量传输分为批量读(输入)和批量写(输出),如果是批量读的话第一阶段的 IN 令牌包,如果是批量写那么第一阶段就是 OUT 令牌包。
我们就以批量写为例简单介绍一下批量传输过程:
①、主机发出 OUT 令牌包,令牌包里面包含了设备地址、端点等信息。
②、如果 OUT 令牌包正确的话,也就是设备地址和端点号匹配,主机就会向设备发送一个数据(DATA)包,发送完成以后主机进入接收模式,等待设备返回握手包,一切都正确的话设备就会向主机返回一个 ACK 握手信号。
批量读的过程刚好相反:
①、主机发出 IN 令牌包,令牌包里面包含了设备地址、端点等信息。发送完成以后主机就进入到数据接收状态,等待设备返回数据。
②、如果 IN 令牌包正确的话,设备就会将一个 DATA 包放到总线上发送给主机。主机收到这个 DATA 包以后就会向设备发送一个 ACK 握手信号。
中断传输
这里的中断传输并不是我们传统意义上的硬件中断,而是一种保持一定频率的传输,中断传输适用于传输数据量小、具有周期性并且要求响应速度快的数据,比如键盘、鼠标等。中断的端点会在端点描述符中报告自己的查询时间间隔,对于时间要求严格的设备可以采用中断传输
3.4 USB枚举
当 USB 设备与 USB 主机连接以后主机就会对 USB 设备进行枚举,通过枚举来获取设备的描述符信息,主机得到这些信息以后就知道该加载什么样的驱动、如何进行通信等。 USB 枚举过程如下:
①、第一回合,当 USB 主机检测到 USB 设备插入以后主机会发出总线复位信号来复位设备。 USB 设备复位完成以后地址为 0,主机向地址 0 的端点 0 发送数据,请求设备的描述符。设备得到请求以后就会按照主机的要求将设备描述符发送给主机,主机得到设备发送过来的设备描述符以后,如果确认无误就会向设备返回一个确认数据包(ACK)。
②、第二回合,主机再次复位设备,进入地址设置阶段。主机向地址 0 的端点 0 发送设置地址请求数据包,新的设备地址就包含在这个数据包中,因此没有数据过程。设备进入状态过程,等待主机请求状态返回,收到以后设备就会向主机发送一个 0 字节状态数据包,表明设备已经设置好地址了,主机收到这个 0 字节状态数据包以后会返回一个确认包(ACK)。设备收到主机发送的 ACK 包以后就会使用这个新的设备地址,至此设备就得到了一个唯一的地址。
③、第三回合,主机向新的设备地址端点 0 发送请求设备描述符数据包,这一次主机要获取整个设备描述符,一共是 18 个字节。
④、和第③步类似,接下来依次获取配置描述符、配置集合、字符串描述符等等。
4 Linux内核自带HOST实验
USB hub驱动也不需要编写,不管换什么hub芯片,都不需要编写。
首先做一下 USB HOST 试验,也就是 I.MX6U-ALPHA 开发板做 USB 主机,然后外接 USB设备,比如 USB 鼠标键盘、 USB 转 TTL 串口
线、 U 盘等设备。
4.1 USB鼠标键盘测试
NXP 官方的 Linux 内核默认已经使能了 USB 键盘鼠标驱动!
USB 鼠标键盘属于 HID 设备,内核已经集成了相应的驱动, NXP 官方提供的 linux 内核默认已经使能了 USB 鼠标键盘驱动,但是我们还要学习一下如何手动使能这些驱动。 输入“make menuconfig”打开 linux 内核配置界面,首先打开 HID 驱动,按照如下路径到相应的配置项目:
1
2
3
4 -> Device Drivers
-> HID support
-> HID bus support (HID [=y])
-> <*> Generic HID driver //使能通用 HID 驱动
接下来需要使能 USB 键盘和鼠标驱动,配置路径如下:
1
2
3
4 -> Device Drivers
-> HID support
-> USB HID support
-> <*> USB HID transport layer //USB 键盘鼠标等 HID 设备驱动
此选项对应配置项就是 CONFIG_USB_HID,也就是 USB 接口的 HID 设备。如果要使用USB 接口的 keyboards(键盘)、 mice(鼠标)、 joysticks(摇杆)、 graphic tablets(绘图板)等其他的 HID设备,那么就需要选中“USB HID Transport layer”。但是要注意一点,此驱动和 HIDBP(Boot Protocol)键盘、鼠标的驱动不能一起使用!
4.2 U盘实验
NXP 提供的 Linux 内核默认也已经使能了 U 盘驱动,因此我们可以直接插上去使用。但是我们还是需要学习一下如何手动配置 Linux 内核,使能 U 盘驱动。
U 盘使用 SCSI 协议,因此要先使能 Linux 内核中的 SCSI 协议,配置路径如下:
1
2
3 -> Device Drivers
-> SCSI device support
-> <*> SCSI disk support //选中此选项
还需要使能 USB Mass Storage,也就是 USB 接口的大容量存储设备,配置路径如下:
1
2
3
4 -> Device Drivers
-> USB support (USB_SUPPORT [=y])
-> Support for Host-side USB (USB [=y])
-> <*> USB Mass Storage support //USB 大容量存储设备
U 盘要为 FAT32 格式的!NTFS 和 exFAT 由于版权问题所以在 Linux下支持的不完善,操作的话可能会有问题,比如只能读,不能写或者无法识别等。
U盘设备文件为/dev/sda和/dev/sda1,大家可以查看一下/dev 目录下有没有 sda 和 sda1 这两个文件。 /dev/sda 是整个 U盘, /dev/sda1 是 U 盘的第一个分区,我们一般使用 U 盘的时候都是只有一个分区。要想访问 U盘我们需要先对 U 盘进行挂载,理论上挂载到任意一个目录下都可以,这里我创建一个/mnt/usb_disk 目录,然后将 U 盘挂载到/mnt/usb_disk 目录下,命令如下:
1
2 mkdir /mnt/usb_disk -p //创建目录
mount /dev/sda1 /mnt/usb_disk/ -t vfat -o iocharset=utf8 //挂载-t 指定挂载所使用的文件系统类型,这里设置为 vfat,也就是 FAT 文件系统,“-o iocharset”设置硬盘编码格式为 utf8,否则的话 U 盘里面的中文会显示乱码!
至此 U 盘就能正常读写操作了,直接对/mnt/usb_disk 目录进行操作就行了。如果要拔出 U盘要执行一个 sync 命令进行同步,然后在使用 unmount 进行 U 盘卸载,命令如下所示:
1
2
3 sync //同步
cd / //如果处于/mnt/usb_disk 目录的话先退出来,否则卸载的时候提示设备忙,导致卸载失败,切记!
umount /mnt/usb_disk //卸载
5 Linux 内核自带 USB OTG 实验
5.1 修改设备树
查询原理图,可以看到ID引脚连接到GPIO1_IO00 ,USB OTG 默认工作在主机(HOST)模式下,因此 ID 线应该是低电平。这里需要修改设备树中 GPIO1_IO00 这个引脚的电气属性,将其设置为默认下拉。
1
2
3
4
5
6
7
8
9
10
11
12 &iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog_1>;
imx6ul-evk {
pinctrl_hog_1: hoggrp-1 {
fsl,pins = <
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 /* SD1 CD */
MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT 0x17059 /* SD1 VSELECT */
/*MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0x17059 */ /* SD1 RESET */
MX6UL_PAD_GPIO1_IO00__ANATOP_OTG1_ID 0x13058 /*OTG1 ID */
>;
};将 GPIO1_IO00 复用为 OTG1 ID,并且设置电气属性为 0X13058,默认下拉,设备树修改好以后重新编译并用新的设备树启动系统。
5.2 OTG主机实验
直接在 ALPHA 的OTG HOST 接口上插入 USB 鼠标键盘、 U 盘等设备。
5.3 OTG从机实验
OTG 从机就是将开发板作为一个 USB 设备连接到其他的主机上,这里我们来做两个 USB从机实验:模拟 U 盘以及 USB 声卡。
模拟 U 盘实验就是将开发板当做一个 U 盘,可以将开发板上的 U 盘或者 TF 卡挂载到 PC上去,首先需要配置 Linux,配置路径如下:
1
2
3
4
5 -> 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 //大容量存储需要将驱动编译为模块!使用的时候直接输入命令加载驱动模块即可。配置好以后重新编译 Linux 内核,会得到三个.ko 驱动模块(带路径):
1
2
3 drivers/usb/gadget/libcomposite.ko
drivers/usb/gadget/function/usb_f_mass_storage.ko
drivers/usb/gadget/legacy/g_mass_storage.ko将上述三个.ko 模块拷贝到开发板根文件系统中
1
2
3
4 cd drivers/usb/gadget/ //进入 gadget 目录下
sudo cp libcomposite.ko /home/zwl/linux/nfs/rootfs/lib/modules/4.1.15/
sudo cp function/usb_f_mass_storage.ko /home/zwl/linux/nfs/rootfs/lib/modules/4.1.15/
sudo cp legacy/g_mass_storage.ko /home/zwl/linux/nfs/rootfs/lib/modules/4.1.15/拷贝完成以后使用新编译出来的 zImage 启动开发板,在开发板上插入一个 U 盘,记住这个 U 盘对应的设备文件,比如我们这里是/dev/sda 和/dev/sda1,以后要将/dev/sda1挂载到 PC 上,也就是把/dev/sda1 作为模拟 U 盘的存储区域。
依次加载 libcomposite.ko、 usb_f_mass_storage.ko 和 g_mass_storage.ko 这三个驱动文件,
1
2
3
4 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 盘就是我们开发板模拟的。
我们可以直接在电脑上对这个 U 盘进行读写操作,实际上操作的就是插在开发板上的 U盘。
1 rmmod g_mass_storage.ko注意!不要将开发板上的 EMMC 或者 NAND 作为模拟 U 盘的存储区域,因为 linux 下EMMC和NAND使用的文件系统一般都是EXT3/EXT4和UBIFS,这些文件系统类型和windows下的不兼容,如果挂载的话就会在 windows 下提示要你格式化 U 盘!