STM32 USB-OTG学习笔记

本文简介:
第一部分大部分是STM32F4系列中文手册关于USB外设的引用,第二部分是STM32 USB的实际应用。内容均来自互联网。


相关链接:


目录

全速USB OTG-FS

OTG_FS 是一款双角色设备 (DRD) 控制器,同时支持从机功能和主机功能,完全符合 USB 2.0 规范的 On-The-Go 补充标准。此外,该控制器也可配置为“仅主机”模式或“仅从机” 模式,完全符合 USB 2.0 规范。在主机模式下,OTG_FS 支持全速(FS,12 Mb/s)和低速 (LS,1.5 Mb/s)收发器,而从机模式下则仅支持全速(FS,12 Mb/s)收发器。OTG_FS 同时支持 HNP 和 SRP。主机模式下需要的唯一外部设备是提供 VBUS 的电荷泵。

通用特性

OTG_FS 接口的通用特性如下:

  • 经 USB-IF 认证,符合通用串行总线规范第 2.0 版
  • 模块内嵌的 PHY 还完全支持定义在标准规范 OTG 补充第 1.3 版中的 OTG 协议
    • 支持 A-B 器件识别(ID 线)
    • 支持主机协商协议 (HNP) 和会话请求协议 (SRP)
    • 允许主机关闭 VBUS 以在 OTG 应用中节省电池电量
    • 支持通过内部比较器对 VBUS 电平采取监控
    • 支持主机到从机的角色动态切换
  • 可通过软件配置为以下角色:
    • 具有 SRP 功能的 USB FS 从机(B 器件)
    • 具有 SRP 功能的 USB FS/LS 主机(A 器件)
    • USB On-The-Go 全速双角色设备
  • 支持 FS SOF 和 LS Keep-alive 令牌
    • SOF 脉冲可通过 PAD 输出
    • SOF 脉冲从内部连接到定时器 2 (TIM2)
    • 可配置的帧周期
    • 可配置的帧结束中断
  • 具有省电功能,例如在 USB 挂起期间停止系统、关闭数字模块时钟、对 PHY 和 DFIFO
    电源加以管理
  • 具有采用高级 FIFO 控制的 1.25 KB 专用 RAM
    • 可将 RAM 空间划分为不同 FIFO,以便灵活有效地使用 RAM
    • 每个 FIFO 可存储多个数据包
    • 动态分配存储区
    • FIFO 大小可配置为非 2 的幂次方值,以便连续使用存储单元
  • 一帧之内可以无需要应用程序干预,以达到最大 USB 带宽

主机模式特性

OTG_FS 接口在主机模式下具有以下主要特性和要求:

  • 通过外部电荷泵生成 VBUS 电压。
  • 多达 8 个主机通道(管道):每个通道都可以动态实现重新配置,可支持任何类型的
    USB 传输。
  • 内置硬件调度器可:
    • 在周期性硬件队列中存储多达 8 个中断加同步传输请求
    • 在非周期性硬件队列中存储多达 8 个控制加批量传输请求
  • 管理一个共享 RX FIFO、一个周期性 TX FIFO 和一个非周期性 TX FIFO,以有效使用USB 数据 RAM。

从机模式特性

OTG_FS 接口在从机模式下具有以下特性:

  • 1 个双向控制端点 0
  • 3 个 IN 端点 (EP),可配置为支持批量传输、中断传输或同步传输
  • 3 个 OUT 端点,可配置为支持批量传输、中断传输或同步传输
  • 管理一个共享 Rx FIFO 和一个 Tx-OUT FIFO,以高效使用 USB 数据 RAM
  • 管理多达 4 个专用 Tx-IN FIFO(分别用于每个使能的 IN EP),降低应用程序负荷
  • 支持软断开功能。

功能框图

OTG 全速模块

USB OTG FS 通过外部石英时钟从复位和时钟控制器 (RCC) 接收 48 MHz ±0.25% 的时钟。 USB 时钟用于全速 (12 Mb/s) 驱动 48 MHz 域,必须在配置 OTG FS 模块前使能。
CPU 通过 AHB 外设总线对 OTG FS 模块寄存器进行读写操作,通过第 30.15 节:OTG_FS 中断中所述的 USB OTG 中断线接收 USB 事件通知。
CPU 通过向特定的 OTG_FS 单元(压栈寄存器)写入 32 位字来向 USB 提交数据。数据随 即自动存储到 USB 数据 RAM 中配置的数据发送 FIFO 中。每个 IN 端点(从机模式)或 OUT 通道(主机模式)都有一个 Tx-FIFO 压栈寄存器。
CPU 从特定的 OTG_FS 地址(出栈寄存器)读取 32 位字,以接收来自 USB 的数据。数据 随即从在 1.25 KB USB 数据 RAM 内配置的共享 Rx-FIFO 中弹出。每个 OUT 端点或 IN 通 道都有一个 Rx-FIFO 出栈寄存器。
USB 协议层通过串行接口引擎 (SIE) 驱动,并通过片上物理层 (PHY) 中的全速/低速收发器 模块经由 USB 进行数据的串行通信。

OTG 双角色设备 (DRD)

ID 线检测

采取主机还是从机(默认设置)角色取决于 ID 输入引脚的电平。插入 USB 时即可根据哪一 端 USB 电缆连接到 micro-AB 插座来确定 ID 线状态。

  • 如果 USB 电缆的 B 端连入,其 ID 线悬空,则由于设备在 ID 线上的集成上拉电阻设备将检测到 ID 高电平并确认采取默认的从机角色。在此配置中,OTG_FS 符合“USB2.0On-The-Go 规范第 1.3 版补充标准中第 6.8.2 章节 On-The-Go B 器件”中所述的 FSM标准。
  • 如果 USB 电缆的 A 端连入,其 ID 线接地,则 OTG_FS 将发出 ID 线状态更改中断(OTG_FS_GINTSTS 中的 CIDSCHG 位)以初始化主机软件,并自动切换为主机角色。在此配置中,OTG_FS 符合 USB2.0 On-The-Go 规范第 1.3 版补充标准中第 6.8.1章节 On-The-Go A 器件。

ADP协议(Attach Detection Protocol 连接检测协议)

Attach Detection Protocol (ADP): Allows an OTG device, embedded host or USB device to determine attachment status in the absence of power on the USB bus, enabling both insertion-based behavior and the capability to display attachment status. It does so by periodically measuring the capacitance on the USB port to determine whether there is another device attached, a dangling cable, or no cable. When a large enough change in capacitance is detected to indicate device attachment, an A-device will provide power to the USB bus and look for device connection. At the same time, a B-device will generate SRP (see below) and wait for the USB bus to become powered.

允许 OTG 设备、嵌入式主机或 USB 设备在 USB 总线断电的情况下确定连接状态,从而实现基于插入的行为和显示连接状态的功能。它通过定期测量 USB 端口上的电容来确定是否连接了其他设备、悬空电缆或没有电缆来实现此目的。当检测到足够大的电容变化以指示设备连接时,A设备将为USB总线供电并寻找设备连接。同时,B 设备将生成 SRP(见下文)并等待 USB 总线供电。

HNP 双角色设备

全局 USB 配置寄存器中的 HNP 使能位(OTG_FS_GUSBCFG 中的 HNPCAP 位)可使 OTG_FS 模块根据主机协商协议 (HNP) 动态切换角色,例如从 A 主机切换为 A 从机(反之 亦然),或者从 B 从机切换为 B 主机(反之亦然)。通过全局 OTG 控制和状态寄存器中的 连接器 ID 状态位(OTG_FS_GOTGCTL 中的 CIDSTS 位)及全局中断和状态寄存器中的当 前工作模式位(OTG_FS_GINTSTS 中的 CMOD 位)二者的组合值可读取设备当前状态。

HNP协议(Host Negotiation Protocol 主机协商协议)

  • Host Negotiation Protocol (HNP): Allows the two devices to exchange their host/peripheral roles, provided both are OTG dual-role devices. By using HNP for reversing host/peripheral roles, the USB OTG device is capable of acquiring control of data-transfer scheduling. Thus, any OTG device is capable of initiating data-transfer over USB OTG bus. The latest version of the supplement also introduced HNP polling, in which the host device periodically polls the peripheral during an active session to determine whether it wishes to become a host.
  • The main purpose of HNP is to accommodate users who have connected the A and B devices (see below) in the wrong direction for the task they want to perform. For example, a printer is connected as the A-device (host), but cannot function as the host for a particular camera, since it does not understand the camera's representation of print jobs. When that camera knows how to talk to the printer, the printer will use HNP to switch to the device role, with the camera becoming the host so pictures stored on the camera can be printed out without reconnecting the cables. The new OTG protocols cannot pass through a standard USB hub since they are based on electrical signaling via a dedicated wire.
  • 允许两个设备交换其主机/外设角色,前提是两者都是 OTG 双角色设备。通过使用 HNP 来反转主机/外设角色,USB OTG 设备能够获取数据传输调度的控制权。因此,任何 OTG 设备都能够通过 USB OTG 总线启动数据传输。最新版本的补充还引入了 HNP 轮询,其中主机设备在活动会话期间定期轮询外设,以确定它是否希望成为主机。
  • HNP 的主要目的是为那些以错误方向连接 A 和 B 设备的用户提供他们想要执行的任务。例如,打印机作为 A 设备(主机)连接,但不能充当特定相机的主机,因为它不理解相机对打印作业的表示。当相机知道如何与打印机通信时,打印机将使用 HNP 切换到设备角色,相机成为主机,因此可以打印相机上存储的照片,而无需重新连接电缆。新的 OTG 协议无法通过标准 USB 集线器,因为它们基于通过专用线路的电气信号。

SRP 双角色设备

全局 USB 配置寄存器中的 SRP 使能位(OTG_FS_GUSBCFG 中的 SRPCAP 位)可使 OTG_FS 模块关闭 VBUS 供电,为 A 器件节省电能。注意,无论 OTG_FS 采取主机角色还 是从机角色,A 器件将始终负责 VBUS 的提供。

SRP协议(Session Request Protocol 会话请求协议)

Allows both communicating devices to control when the link's power session is active; in standard USB, only the host is capable of doing so. That allows fine control over the power consumption, which is very important for battery-operated devices such as cameras and mobile phones. The OTG or embedded host can leave the USB link unpowered until the peripheral (which can be an OTG or standard USB device) requires power. OTG and embedded hosts typically have little battery power to spare, so leaving the USB link unpowered helps in extending the battery runtime.

允许两个通信设备控制链路的电源会话何时处于活动状态;在标准 USB 中,只有主机能够这样做。这样可以对功耗进行精细控制,这对于相机和手机等电池供电设备非常重要。OTG 或嵌入式主机可以让 USB 链路处于断电状态,直到外围设备(可以是 OTG 或标准 USB 设备)需要电源为止。OTG 和嵌入式主机通常没有多少备用电池电量,因此保持 USB 链路不通电有助于延长电池运行时间。

USB 设备

本节介绍 OTG_FS 在 USB 设备模式下所具有的功能。在以下情形下,OTG_FS 用作 USB 设备:

  • OTG B 器件
    • OTG B 器件插入 USB 电缆 B 端时的默认状态
  • OTG A 器件
    • OTG A 器件被 HNP 切换为设备角色后的状态
  • B 器件
    • 如果 ID 线有效,器件与 USB 电缆的 B 端相连,全局 USB 配置寄存器中的 HNP功能位(OTG_FS_GUSBCFG 中的 HNPCAP 位)清零(请参见 On-The-Go 第1.3 版的第 6.8.3 节)。
  • 仅作设备(请参见图 359:USB 仅做设备的连接)
    • 全局 USB 配置寄存器中的强制设备模式位(OTG_FS_GUSBCFG 中的 FDMOD)置 1,强制 OTG_FS 模块仅用作 USB 设备(请参见 On-The-Go 第 1.3 版的第6.8.3 节)。这种情况下,即使 USB 连接器上存在 ID 线,也会将该 ID 线忽略。

注意:要在 B 器件或仅作设备配置情形下构建总线供电的设备方案,需要添加一个外部调压器,用 于从 VBUS 生成 VDD 芯片电源。
关闭 VBUS 感应选项可以释放 VBUS 引脚。可通过在 OTG_FS_GCCFG 寄存器中将 NOVBUSSENS 位置 1 来完成此操作。这种情况下,内部将 VBUS 视为始终处于 VBUS 有效电平 (5 V)。

支持 SRP 功能的设备

全局 USB 配置寄存器中的 SRP 功能位(OTG_FS_GUSBCFG 中的 SRPCAP 位)可使 OTG_FS 支持会话请求协议 (SRP)。这样一来,远程 A 器件便可以在 USB 会话挂起时,通 过关闭 VBUS 来节省电能。

设备状态

供电状态

VBUS 输入检测到 B 会话有效电压,就会使 USB 设备进入供电状态(请参见 USB2.0 第 9.1 节)。然后,OTG_FS 自动连接 DP 上拉电阻,发出全速设备与主机相连的信号并生成会话 请求中断(OTG_FS_GINTSTS 中的 SRQINT 位),指示进入供电状态。
此外,VBUS 输入还可确保主机在 USB 操作期间提供有效的 VBUS 电平。如果检测到 VBUS 降至 B 会话有效电压以下(例如,因电源干扰或主机端口关闭引发),OTG_FS 将自动断开 连接并生成检测到会话结束中断(OTG_FS_GOTGINT 中的 SEDET 位),指示 OTG_FS 已退出供电状态。
供电状态下,OTG_FS 期望收到来自主机的复位信号。其它 USB 操作则无法执行。收到复 位信号后,立即生成检测到复位中断(OTG_FS_GINTSTS 中的 USBRST)。复位信号结 束后,将生成枚举完成中断(OTG_FS_GINTSTS 中的 ENUMDNE 位),OTG_FS 随即进 入默认状态。

软断开

供电状态可借助软断开功能通过软件退出。将设备控制寄存器中的软断开位(OTG_FS_DCTL 中的 SDIS 位)置 1 即可移除 DP 上拉电阻,此时尽管没有从主机端口实 际拔出 USB 电缆,但主机端仍会发生设备断开检测中断。

默认状态

默认状态下,OTG_FS 期望从主机收到 SET_ADDRESS 命令。其它 USB 操作则无法执 行。当 USB 上解码出有效 SET_ADDRESS 命令时,应用程序会将相应的地址值写入设备配 置寄存器中的设备地址字段(OTG_FS_DCFG 中的 DAD 位)。OTG_FS 随即进入地址状 态,并准备好以所配置的 USB 地址对主机事务进行应答。

挂起状态

OTG_FS 设备持续监视 USB 活动。在 USB 空闲时间达到 3 ms 后,将发出早期挂起中断 (OTG_FS_GINTSTS 中的 ESUSP 位),并在 3 ms 后由挂起中断(OTG_FS_GINTSTS 中的 USBSUSP 位)确认设备进入挂起状态。然后,设备状态寄存器中的设备挂起位 (OTG_FS_DSTS 中的 SUSPSTS 位)自动置 1,OTG_FS 随即进入挂起状态。
可通过设备本身退出挂起状态。这种情况下,应用程序会将设备控制寄存器中的远程唤醒信号位(OTG_FS_DCTL 中的 RWUSIG 位)置 1,并在 1 ms 到 15 ms 后将其清零。
但若设备检测到主机发出的恢复信号,将生成恢复中断(OTG_FS_GINTSTS 中的 WKUPINT 位),设备挂起位自动清零。

设备端点

端点位于USB 外设内部,所有通信数据的来源或目的都基于这些端点,是一个可寻址的FIFO
每个USB 外设有一个唯一的地址,可能包含最多十六个端点。主机通过发出器件地址和每次数据传输的端点号,向一个具体端点(FIFO)发送数据。
每个端点的地址为0 到15,一个端点地址对应一个方向。所以,端点2-IN 与端点2-OUT 完全不同。 每个器件有一个默认的双向控制端点0,因此不存在端点0-IN 和端点0-OUT。

OTG_FS 模块实现了以下 USB 端点:

  • 控制端点 0:
    • 双向且仅处理控制消息
    • 使用一组单独的寄存器来处理 IN 和 OUT 事务
    • 专用控制 (OTG_FS_DIEPCTL0/OTG_FS_DOEPCTL0) 寄存器、传输配置 (OTG_FS_DIEPTSIZ0/OTG_FS_DIEPTSIZ0) 寄存器和状态中断(OTG_FS_DIEPINTx/OTG_FS_DOEPINT0) 寄存器。控制和传输大小寄存器中可用的位组与其它端点中稍有不同
  • 3 个 IN 端点
    • 每个端点都可配置为支持同步传输、批量传输或中断传输类型
    • 每个端点都有专用控制 (OTG_FS_DIEPCTLx) 寄存器、传输配置 (OTG_FS_DIEPTSIZx)寄存器和状态中断 (OTG_FS_DIEPINTx) 寄存器
    • 设备 IN 端点通用中断屏蔽寄存器 (OTG_FS_DIEPMSK) 可用于使能/禁止所有 IN 端点(包括 EP0)上的同一类端点中断源
    • 支持未完成的同步 IN 传输中断(OTG_FS_GINTSTS 中的 IISOIXFR 位),该中断将在当前帧中至少有一个同步 IN 端点上的传输未完成时触发。该中断和周期性帧中断 (OTG_FS_GINTSTS/EOPF) 一起触发
  • 3 个 OUT 端点
    • 每个端点都可配置为支持同步传输、批量传输或中断传输类型
    • 每个端点都有专用控制 (OTG_FS_DOEPCTLx) 寄存器、传输配置(OTG_FS_DOEPTSIZx) 寄存器和状态中断 (OTG_FS_DOEPINTx) 寄存器
    • 设备 OUT 端点通用中断屏蔽寄存器 (OTG_FS_DOEPMSK) 可用于使能/禁止所有OUT 端点(包括 EP0)上的同一类端点中断源
    • 支持未完成的同步 OUT 传输中断(OTG_FS_GINTSTS 中的 INCOMPISOOUT位),该中断将在当前帧中至少有一个同步 OUT 端点上的传输未完成时触发。该中断和周期性帧中断 (OTG_FS_GINTSTS/EOPF) 一起触发

端点控制

  • 应用程序可通过设备端点 x IN/OUT 控制寄存器 (DIEPCTLx/DOEPCTLx) 对端点采取
    以下控制:
    • 端点使能/禁止
    • 在当前配置下激活端点
    • 设置 USB 传输类型(同步、批量和中断)
    • 设置支持的数据包大小
    • 设置与 IN 端点相关的 Tx-FIFO 编号
    • 设置希望收到的或发送时要使用到的 data0/data1 PID(仅限批量/中断传输)
    • 设置接收或发送事务时所对应的奇/偶帧(仅限同步传输)
    • 可以设置 NAK 位,从而不论此时 FIFO 的状态如何,都对主机的请求回复 NAK
    • 可以设置 STALL 位,使得主机对该端点的令牌都被硬件回复 STALL
    • 可以将 OUT 端点设置为侦听模式,即对接收到的数据不进行 CRC 检查

端点传输

设备端点 x 传输尺寸寄存器 (DIEPTSIZx/DOEPTSIZx) 允许应用程序对传输尺寸参数进行编 程并读取传输状态。必须在端点控制寄存器中的端点使能位置 1 之前完成对此寄存器的设 置。使能端点后,这些字段立即变为只读状态,同时 OTG FS 模块根据当前传输状态对这些 字段进行更新。
可对以下传输参数进行编程:

  • 以字节为单位的传输大小
  • 构成整个传输的数据包个数

端点状态/中断

设备端点 x 中断寄存器 (DIEPINTx/DOPEPINTx) 指示端点在出现 USB 和 AHB 相关事件时 的状态。当模块中断寄存器中的 OUT 端点中断位或 IN 端点中断位(分别为 OTG_FS_GINTSTS 中的 OEPINT 位或 OTG_FS_GINTSTS 中的 IEPINT 位)置 1 时,应用程序必须读取这些寄 存器以获得详细信息。在应用程序读取这些寄存器之前,必须先读取设备全体端点中断(OTG_FS_DAINT) 寄存器,以获取设备端点 x 中断寄存器的端点编号。应用程序必须将此寄 存器中的相应位清零,才能将 DAINT 和 GINTSTS 寄存器中的相应位清零。
模块提供以下状态检查和中断产生功能:

  • 传输完成中断,指示应用程序 (AHB) 和 USB 端均已完成数据传输
  • Setup 阶段已完成(仅针对控制传输类型的 OUT 端点)
  • 相关的发送 FIFO 为半空或全空状态(IN 端点)
  • NAK 应答已发送到主机(仅针对同步传输的 IN 端点)
  • Tx-FIFO 为空时接收到 IN 令牌(仅针对批量和中断传输类型的 IN 端点)
  • 尚未使能端点时接收到 OUT 令牌
  • 检测到 babble 错误
  • 应用程序关闭端点生效
  • 应用程序对端点设置 NAK 生效(仅针对同步传输类型的 IN 端点)
  • 接收到 3 个以上连续 setup 数据包(仅针对控制类型的 OUT 端点)
  • 检测到超时状况(仅针对控制传输类型的 IN 端点)
  • 同步传输类型的数据包未产生中断而丢失

USB 主机

本节介绍了 OTG_FS 在 USB 主机模式下所具有的功能。在以下情形下,OTG_FS 用作 USB 主机:

  • OTG A 主机
    • OTG A 器件在插入 USB 电缆 A 端时的默认状态
  • OTG B 主机
    • OTG B 器件被 HNP 切换为主机角色后的状态
  • A 器件
    • 如果 ID 线有效,器件与 USB 电缆的 A 端相连,全局 USB 配置寄存器中的 HNP
      功能位(OTG_FS_GUSBCFG 中的 HNPCAP 位)清零。DP/DM 线上的集成下拉
      电阻自动使能。
  • 仅作主机(请参见图 360:USB 仅作主机的连接)。
    • 全局 USB 配置寄存器中的强制模式位(OTG_FS_GUSBCFG 中的 FHMOD 位)将
      强制 OTG_FS 模块作为 USB 仅作主机运行。这种情况下,即使 USB 连接器上存
      在 ID 线,也会将该 ID 线忽略。DP/DM 线上的集成下拉电阻自动使能。

注意:微控制器不能输出 5V 以提供 VBUS。为此,必须在微控制器以外添加电荷泵或电源开关(如 果应用电路板提供 5 V 电源)来驱动 5 V VBUS 线。外部电荷泵可通过任何 GPIO 输出驱 动。OTG A 主机、A 器件和仅作主机配置都需要使用电荷泵。
VBUS 输入可确保电荷泵在 USB 操作期间提供有效的 VBUS 电平,同时电荷泵过流输出可连 接到任意配置成外部中断的 GPIO 引脚。在过流 ISR 中必须立即关闭 VBUS。
关闭 VBUS 感应选项可以释放 VBUS 引脚。可通过在 OTG_FS_GCCFG 寄存器中将 NOVBUSSENS 位置 1 来完成此操作。这种情况下,内部将 VBUS 视为始终处于 VBUS 有效 电平 (5 V)。

支持 SRP 功能的主机

全局 USB 配置寄存器中的 SRP(OTG_FS_GUSBCFG 中的 SRPCAP 位)可提供 SRP 支 持。使能 SRP 功能后,主机可在 USB 会话挂起时通过关闭 VBUS 电源来节省电能。

USB 主机状态

给主机端口供电

微控制器不能输出 5V 以提供 VBUS。为此,必须在微控制器以外添加电荷泵或基本电源开关 (如果应用电路板提供 5 V 电源)来驱动 5 V VBUS 线。外部电荷泵可通过任何 GPIO 输出 驱动。当应用程序确定使用 GPIO 来控制外部器件输出 VBUS,还必须将主机端口控制和状 态寄存器中的端口电源位(OTG_FS_HPRT 中的 PPWR 位)置 1。

VBUS 有效

使能 HNP 或 SRP 后,应将 VBUS 感应引脚 (PA9) 连接到 VBUS。VBUS 输入可确保电荷泵 在 USB 操作期间提供有效的 VBUS 电平。如果 VBUS 电压意外降至 VBUS 有效阈值 (4.25 V) 以下,将通过会话结束检测位(OTG_FS_GOTGINT 中的 SEDET 位)触发 OTG 中断。之 后应用必须断开 VBUS 电源并使端口电源位清零。
同时禁止 HNP 和 SRP 时,应断开 VBUS 感应引脚 (PA9) 与 VBUS 之间的连接。该引脚可用 作 GPIO。
电荷泵过流标志也可用来防止电气损坏。将电荷泵的过流标志输出连接到任意 GPIO 输入, 然后将其配置为出现有效电平时生成端口中断。过流 ISR 必须立即关闭 VBUS 并清零端口电 源位。

主机检测设备连接

如果使能 SRP 或 HNP,即使可以随时连接 USB 外设或 B 器件,但是 OTG_FS 也只有在 VBUS 有效后 (5V) 才能检测到设备的连接。当 VBUS 处于有效电平且已连接远程 B 器件时, OTG_FS 模块将发出主机端口中断信号,该中断由主机端口控制和状态寄存器中的设备连接 位(OTG_FS_HPRT 中的 PCDET 位)触发。
在 HNP 和 SRP 同时关闭的情况下,USB 设备或 B 器件将在连接后立即被检测到。OTG_FS 模块将发出主机端口中断信号,该中断由主机端口控制和状态寄存器中的设备连接位(OTG_FS_HPRT 中的 PCDET 位)触发。

主机检测设备断开

设备断开事件将触发断开连接检测中断(OTG_FS_GINTSTS 中的 DISCINT 位)。

主机枚举

检测到设备连接后,若又有新的设备连接进来,主机必须通过向新的设备发送 USB 复位和 配置命令来启动枚举过程。
开始驱动 USB 复位前,应用程序必须等待去抖动完成位(OTG_FS_GOTGINT 中的 DBCDNE 位)触发 OTG 中断,这表示由于在 DP (FS) 或 DM (LS) 上连接上拉电阻而发生电气抖动之 后,总线恢复稳定状态。
应用程序通过将主机端口控制和状态寄存器中的端口复位位(OTG_FS_HPRT 中的 PRST 位)置 1,使该过程最少持续 10 ms、最多持续 20 ms,以此通过 USB 驱动 USB 复位信号 (单端零)。应用程序计算这个过程的持续时间,然后将端口复位位清零。
USB 复位序列完成后,端口使能/禁止更改位(OTG_FS_HPRT 中的 PENCHNG 位)立即 触发主机端口中断,进而向应用程序发出通知,指示可从主机端口控制和状态寄存器中的端口速度字段(OTG_FS_HPRT 中的 PSPD)读取枚举的设备速度,以及主机已经开始驱动 SOF (FS) 或 Keep-alive 令牌 (LS)。此时主机已就绪,可通过对设备发送命令来完成对设备 的枚举。

主机挂起

应用程序通过将主机端口控制和状态寄存器中的端口挂起位(OTG_FS_HPRT 中的 PSUSP)置 1 来挂起 USB 活动。OTG_FS 模块停止发送 SOF 并进入挂起状态。
可由远程设备的自主活动(远程唤醒)使总线退出挂起状态。这种情况下,远程唤醒信号将触发远程唤醒中断(OTG_FS_GINTSTS 中的 WKUPINT 位),硬件把主机端口控制和状态 寄存器中的端口恢复位(OTG_FS_HPRT 中的 PRES 位)自行复位,并通过 USB 自动驱动 恢复信号。应用程序必须为恢复窗口定时,然后将端口恢复位清零以退出挂起状态并重新启动 SOF。
如果由主机发起退出挂起状态,则应用程序必须将端口恢复位置 1 以启动主机端口上的恢复 信号,为恢复窗口定时并最终将端口恢复位清零。

主机通道

OTG_FS 模块实现了 8 个主机通道。每个主机通道均可用于 USB 主机传输(USB 管道)。 主机最多能同时处理 8 个传输请求。如果应用程序有 8 个以上的传输请求挂起,则在通道从 之前任务释放后(即,接收到传输完成和通道停止中断后),主机控制器驱动器 (HCD) 必须 为未处理的传输请求重新对通道进行分配。
每个主机通道都可配置为支持输入/输出以及周期性/非周期性事务。每个主机通道都使用专用控制 (HCCHARx) 寄存器、传输配置 (HCTSIZx) 寄存器/中断 (HCINTx) 寄存器以及和其相 关的中断屏蔽寄存器 (HCINTMSKx)。

主机通道控制

  • 应用程序可通过主机通道 x 特性寄存器 (HCCHARx) 对主机通道作以下控制:
    • 通道使能/禁止
    • 设置目标 USB 设备的速度:FS/LS
    • 设置目标 USB 设备的地址
    • 设置与该通道通信的目标 USB 设备上的端点的编号
    • 设置该通道上的传输方向:IN/OUT
    • 设置该通道上的 USB 传输的类型:控制/批量/中断/同步
    • 设置与该通道通信的设备端点的最大包长
    • 设置要进行周期传输的帧:奇帧/偶帧

主机通道传输

主机通道传输大小寄存器 (HCTSIZx) 允许应用程序对传输大小参数进行编程并读取传输状 态。必须在主机通道特性寄存器中的通道使能位置 1 之前完成对此寄存器的设置。使能端点 后,数据包计数字段立即变为只读状态,同时 OTG FS 模块根据当前传输状态对该字段进行 更新。

  • 可对以下传输参数进行编程:
    • 以字节为单位的传输大小
    • 构成整个传输大小的数据包个数
    • 初始数据 PID

主机通道状态/中断

主机通道 x 中断寄存器 (HCINTx) 指示端点在出现 USB 和 AHB 相关事件时的状态。当中断 寄存器中的主机通道中断位(OTG_FS_GINTSTS 中的 HCINT 位)置 1 时,应用程序必须 读取这些寄存器以获得详细信息。在读取这些寄存器之前,应用程序必须先读取主机全体通道中断 (HCAINT) 寄存器,以获取主机通道 x 中断寄存器的通道编号。应用程序必须将 此寄存器中的相应位清零,才能将 HAINT 和 GINTSTS 寄存器中的相应位清零。 OTG_FS_HCINTMSK-x 寄存器还提供每个通道各中断源的屏蔽位。

  • 主机模块提供以下状态检查和中断产生功能:
    • 传输完成中断,指示应用程序 (AHB) 和 USB 端均已完成数据传输
    • 通道因传输完成、USB 事务错误或应用程序发出禁止命令而停止
    • 相关的发送 FIFO 为半空或全空状态(IN 端点)
    • 接收到 ACK 响应
    • 接收到 NAK 响应
    • 接收到 STALL 响应
    • 由于 CRC 校验失败、超时、位填充错误和错误的 EOP 导致 USB 事务错误
    • 串扰错误
    • 帧上溢
    • 用于数据同步的翻转位出错

主机调度器

主机模块内置硬件调度器,可自主对应用程序发出的 USB 事务请求重新排序和管理。每一 帧开始时,主机都先执行周期性(同步和中断)事务,然后执行非周期性(控制和批量)事务,以符合 USB 规范对同步和中断传输高优先级的保证。
主机通过请求队列(一个周期性请求队列和一个非周期请求队列)处理 USB 事务。每个请 求队列最多可存储 8 个条目。每个条目代表一个应用程序发起但还未得到响应的 USB 事务 请求,并存储了执行该 USB 事务所用到的 IN 或 OUT 通道的编号,以及其它相关信息。 USB 事务请求在队列中的写入顺序决定了事务在 USB 接口上的执行顺序。
每一帧开始时,主机都先处理周期性请求队列,然后处理非周期性请求队列。如果当前帧结束时,计划在当前帧执行的同步或中断类型的 USB 传输事务请求仍处于挂起状态,则主机 将发出未完成周期性传输中断(OTG_FS_GINTSTS 中的 IPXFR 位)。OTG HS 模块负责 对周期性和非周期性请求队列的管理。周期性发送 FIFO 和队列状态寄存器 (HPTXSTS) 与非周期性发送 FIFO 和队列状态寄存器 (HNPTXSTS) 都为只读寄存器,应用程序可使用它们 来读取各请求队列的状态。其中包括:

  • 周期性(非周期性)请求队列中当前可用的空闲条目数(最多 8 个)
  • 周期性(非周期性)Tx-FIFO(OUT 事务)中当前可用的空闲空间
  • IN/OUT 令牌、主机通道编号和其它状态信息。
    由于每个请求队列最多可存储 8 个 USB 事务请求,因此应用程序可以把主机 USB 事务请求 提前发送给调度器;实际的通信最晚会在调度器处理完已挂起的 8 个周期事务和 8 个非周期 事务完成之后出现在 USB 总线上。
    要向主机调度器(队列)发出事务请求,应用程序必须读取 OTG_FS_HNPTXSTS 寄存器中 的 PTXQSAV 位或 OTG_FS_HNPTXSTS 寄存器中的 NPTQXSAV 位,确保周期性(非周 期性)请求队列中至少有一个可用空间来存储当前请求。

USB 系统性能

凭借大容量 RAM 缓冲区、高度可配置的 FIFO 大小、通过 AHB 压栈/出栈寄存器进行 32 位 FIFO 快速访问,尤其是高级 FIFO 控制机制可获得最佳 USB 和系统性能。实际上,无论当 前 USB 序列如何,OTG_FS 均可通过该机制高效填充可用的 RAM 空间。借助这些特性:

  • 应用程序有足够的裕量来计算并校正CPU的负载,从而优化 CPU 带宽利用率:
    • 应用程序可先积累大量发送数据,再通过 USB 发送出去
    • 可带来足够的时间裕量,以从接收 FIFO 读取数据
  • USB 模块能够保持全速工作状态,也就是提供最大的全速带宽(尽量多的硬件自动运行,
    尽量少的软件参与):
    • USB 模块可提前积累大量发送数据供其支配,从而可对 USB 数据发送进行自主
      管理
    • 接收缓冲区中有大量空白空间,可通过 USB 中的数据自动填满
      由于 OTG_FS 模块能够高效填充 1.25 KB RAM 缓冲区且 1.25 KB 发送/接收数据足以满足一 个全速帧所能容纳的数据量,因此 USB 系统在一帧之内可以无需应用程序干预达到最大 USB 带宽。

应用

虚拟串口CDC

CDC类(Communication Device Class)

USB CDC类的通信部分主要包含三部分:枚举过程、虚拟串口操作和数据通信。

其中虚拟串口操作部分并不一定强制需要,因为若跳过这些虚拟串口的操作,实际上USB依然是可以通信的,这也就是为什么上图中,在操作虚拟串口之前会有两条数据通信的数据。之所以会有虚拟串口操作,主要是我们通常使用PC作为Host端,在PC端使用一个串口工具来与其进行通信,PC端的对应驱动将其虚拟成一个普通串口,这样一来,可以方便PC端软件通过操作串口的方式来与其进行通信,但实际上,Host端与Device端物理上是通过USB总线来进行通信的,与串口没有关系,这一虚拟化过程,起决定性作用的是对应驱动,包含如何将每一条具体的虚拟串口操作对应到实际上的USB操作。需要注意的是,Host端与Device端的USB通信速率并不受所谓的串口波特率影响,它就是标准的USB2.0全速(12Mbps)速度实际速率取决于总线的实际使用率、驱动访问USB外设有效速率(两边)以及外部环境对通信本身造成的干扰率等因素组成。

在STM32中配置虚拟串口

USB参数配置

打开STM32CubeMX,新建一个工程,配置好系统选项,在 Connectivity 中选择 USB 设置,并勾选 Device(FS) 激活 USB 设备。

Parameter Settings 进行具体参数配置。

  • Speed: Full Speed 12MBit/s(固定为全速)

  • Low Power: 默认 Disabled(在任何不需要使用usb模块的时候,通过写控制寄存器总可以使usb模块置于低功耗模式(low power mode ,suspend模式)。在这种模式下,不产生任何静态电流消耗,同时usb时钟也会减慢或停止。通过对usb线上数据传输的检测,可以在低功耗模式下唤醒usb模块。也可以将一特定的中断输入源直接连接到唤醒引脚上,以使系统能立即恢复正常的时钟系统,并支持直接启动或停止时钟系统。)

USB 的 DP 引脚必须上拉 1.5K 欧的电阻,电脑才能检测到 USB,否则检测不到。

配置时钟

选择 Clock Configuration,USB 时钟配置为 48MHz,且来源最好是外部晶振分频得到。

USB Device参数配置

Middleware 中选择 USB_DEVICE 设置,在 Class For FS IP 设备类别选择 Communication Device Class(Virtual Port Com) 虚拟串口:

参数配置保持默认:

设备描述符保持默认:

最后生成代码。

main.c添加头文件 #include "usbd_cdc_if.h"

main() 的死循环中添加 CDC_Transmit_FS() 函数。

while (1)
  {
    CDC_Transmit_FS("hello", 5);
    HAL_Delay(1000);
  }

烧录代码,连接电脑。就可以在PC的串口助手中收到信息:

USB接口使用(HID鼠标)

HID描述符

HID设备的描述符除了5个USB的标准描述符(设备描述符、配置描述符、接口描述符、端点描述符、字符串描述符)外,还包括三个HID设备类特定的描述符:HID描述符报告描述符(Report)、实体描述符(Physical)

下表为HID描述符的结构。

HID Descriptor Tool (DT) HID描述符工具

HID鼠标的描述符情况:

STM32中配置HID

其余步骤相同,从USB Device开始配置:

USB Device参数配置

Middleware 中选择 USB_DEVICE 设置,在 Class For FS IP 设备类别选择 Human Interface Device Class(HID) 人机接口设备:

参数配置保持默认:

  • HID_FS_BINTERVAL(主机读取设备数据时间间隔): 0xA(STM32将数据发送到一个缓存区,而不是直接发送到上位机,而上位机每隔一端时间会来访问缓冲区读取数据。读取时间间隔过快会导致多次数据发送,过慢会导致数据丢失)
  • USBD_MAX_NUM_INTERFACES (Maximum number of supported interfaces)(最大支持HID设备的接口数): 1(应为现在只有鼠标,所以1就行,如果是需要同时键盘,鼠标,手柄啥的,根据数量选择即可)

设备描述符保持默认:

usbd_hid.c文件的HID_MOUSE_ReportDesc数组定义处,默认生产HID设备为Mouse,故不需要做修改

代码配置

main.c添加头文件和USB设备句柄:

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "usbd_hid.h"
/* USER CODE END Includes */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
extern USBD_HandleTypeDef hUsbDeviceFS;

添加一个数组变量,用于传输鼠标动作信息:

/* USER CODE BEGIN PV */
/*
 * buffer[0] - bit0: Left button 左键
 *             bit1: Right button 右键
 *             bit2: Middle button 中键
 * buffer[1] - Cursor movement X axis 水平移动
 * buffer[2] - Cursor movement Y axis 垂直移动
 * buffer[3] - Wheel vertical movement 滚轮转动
 */
uint8_t buffer[4] = {0x00, 0x00, 0x00, 0x00};
/* USER CODE END PV */

添加鼠标单击左/右/中键、鼠标滚动和鼠标移动函数:

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void Click_L()
{
    memset(buffer, 0, 4);
	buffer[0] = 1;
	USBD_HID_SendReport(&hUsbDeviceFS,buffer,4);//按下鼠标左键
	HAL_Delay(10);
    buffer[0] = 0;
	USBD_HID_SendReport(&hUsbDeviceFS,buffer,4);//松开鼠标按键
	HAL_Delay(10);
}
void Click_R()
{
    memset(buffer, 0, 4);
	buffer[0] = 2;
	USBD_HID_SendReport(&hUsbDeviceFS,buffer,4);//按下鼠标右键
	HAL_Delay(10);
    buffer[0] = 0;
	USBD_HID_SendReport(&hUsbDeviceFS,buffer,4);//松开鼠标按键
	HAL_Delay(10);
}
void Click_M()
{
    memset(buffer, 0, 4);
	buffer[0] = 4;
	USBD_HID_SendReport(&hUsbDeviceFS,buffer,4);//按下鼠标右键
	HAL_Delay(10);
    buffer[0] = 0;
	USBD_HID_SendReport(&hUsbDeviceFS,buffer,4);//松开鼠标按键
	HAL_Delay(10);
}
void Scroll(int8_t x)
{
    memset(buffer, 0, 4);
    buffer[3] = x;
	USBD_HID_SendReport(&hUsbDeviceFS,buffer,4);//鼠标滚动
	HAL_Delay(10);
}
void Move(int8_t x,int8_t y)
{
    memset(buffer, 0, 4);
	buffer[1]=x;
	buffer[2]=y;
	USBD_HID_SendReport(&hUsbDeviceFS,buffer,4);//鼠标移动
	HAL_Delay(10);
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USB_DEVICE_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
//    usb_printf("\r\n****** USB-HID Mouse Example ******\r\n\r\n");
    Move(100,0);//向右移动100个像素
    Click_R();//单击右键
    HAL_Delay(1000);//延时1秒
		
    Move(0,100);//向下移动100个像素
    Click_R();//单击右键
    HAL_Delay(1000);//延时1秒
		
    Move(-100,0);//向左移动100个像素
    Click_R();//单击右键
    HAL_Delay(1000);//延时1秒
		
    Move(0,-100);//向上移动100个像素
    Click_R();//单击右键
    HAL_Delay(1000);//延时1秒
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

编译工程,下载到板子上,插上USB线连接到电脑上,识别出为鼠标设备:

posted @ 2023-11-25 01:57  C-Alen  阅读(2676)  评论(0编辑  收藏  举报