4. PIC32 USB外设寄存器详解

PIC32 的 USB 模块并不是“写一个地址就能跑起来”的简单外设,它由一组分工明确的寄存器共同控制:有的负责全局使能与中断汇总,有的负责端点缓冲区的读写与握手,有的负责设备地址与端点使能,还有的负责传输状态与错误标志。理解这些寄存器的位域含义和相互依赖关系,是后续编写 USB 设备固件、调试枚举失败和排查传输异常的基础。本节以 PIC32MX 系列常见的 USB 模块为对象,围绕 USBCSR、USBIEN、USBADDR、USBSTAT、USBCFG 以及端点控制/状态寄存器展开,说明“每个寄存器管什么、配置顺序是什么、写完之后如何判断生效”。

一、寄存器分工与访问方式

PIC32 的 USB 寄存器分为两类:一类是全局寄存器,如 USBCSR、USBIEN、USBADDR、USBSTAT、USBCFG、USBOTG;另一类是端点相关寄存器,如 USBE0CSR、USBE1CSR……以及对应的端点缓冲区和计数寄存器。全局寄存器通常按 32 位访问,端点寄存器按端点编号成组出现。需要特别注意的是,USB 模块的许多状态位是“写 1 清零”或“写 1 翻转”的,不能按普通变量的读改写方式处理,否则会误清标志或误触发动作。

在 PIC32 中,USB 模块的时钟来自 UPLL,使用前必须先在系统时钟配置中使能 USB PLL 并等待其锁定,然后才能访问 USB 寄存器。若 UPLL 未稳定就写 USBCSR,配置可能被忽略或产生不可预期的行为。因此,寄存器配置的第一步不是写 USBCSR,而是确认系统时钟与 USB 模块时钟已经就绪。

PIC32 USB 关键寄存器协作关系 UPLL 时钟 使能并等待锁定 USBCSR 全局使能 / 中断汇总 USBEN / SUSPEND / RESUME USBIEN 中断使能掩码 SOF / UERR / TRN / IDLE USBADDR 设备地址 SET_ADDRESS 后写入 USBSTAT 传输状态 / 错误标志 PPBRST / SPEED / NAK USBCFG 端点缓冲区配置 UTEYE / UVCON / FSDEV USBE n CSR 端点控制 / 状态 TXPKTRDY / RXPKTRDY 配置顺序:UPLL 就绪 → USBCSR 使能 → USBCFG 端点缓冲 → USBADDR 地址 → USBE n CSR 端点使能 → USBIEN 中断使能 状态判断:USBSTAT 错误标志、USBE n CSR 的 TXPKTRDY/RXPKTRDY 握手位

二、USBCSR:全局控制与中断汇总

USBCSR 是 USB 模块的总开关和状态汇总寄存器。它的关键位包括:USBEN(模块使能)、SUSPEND(挂起状态)、RESUME(恢复信号)、TOKPKT(令牌包完成)、SOF(帧起始)、UERR(错误)、IDLE(空闲)等。其中 USBEN 必须置 1,USB 模块才会开始响应总线信号;在 USBEN 为 0 时,端点寄存器的写入不会真正生效。

USBCSR 中的中断标志位与 USBIEN 中的使能位一一对应。典型做法是:先通过 USBCSR 清除历史标志(写 1 清零),再在 USBIEN 中打开需要的中断源,最后在中断服务程序中读取 USBCSR 判断具体事件。例如,当主机发送 SETUP 包时,USBCSR 的 TOKPKT 或端点 0 的 SETUP 标志会置位,固件据此进入控制传输处理流程。

三、USBIEN:中断使能掩码

USBIEN 决定哪些 USB 事件可以触发 CPU 中断。常见使能位包括:SOFIE(帧起始中断)、UERRIE(错误中断)、TRNIE(传输完成中断)、IDLEIE(空闲中断)、RESUMEIE(恢复中断)、SUSPENDIE(挂起中断)等。使能位与 USBCSR 中的标志位同名对应,只有标志位置 1 且使能位置 1 时,才会向中断控制器发出请求。

实际配置中,建议先只打开必要的中断源,例如枚举阶段打开 UERR、TRN 和 SUSPEND,避免 SOF 每 1 ms 触发一次造成 CPU 负担。若使用轮询方式,也可以将 USBIEN 全部清零,仅通过查询 USBCSR 和端点状态寄存器推进流程。两种方式没有绝对优劣,取决于系统对实时性和 CPU 占用的要求。

四、USBADDR:设备地址寄存器

USBADDR 保存 USB 设备当前使用的地址,复位后为 0。主机在枚举过程中通过 SET_ADDRESS 请求分配新地址,固件必须在正确时机把该地址写入 USBADDR。写入过早或过晚都会导致后续通信失败:如果主机已经切换到新地址,而设备仍用旧地址响应,设备将不再被主机识别。

标准做法是:在控制传输的状态阶段完成后,再把 SET_ADDRESS 请求中的地址值写入 USBADDR。例如主机分配地址 5,固件在端点 0 的 IN 状态阶段完成后执行 USBADDR = 5;,随后所有通信都使用地址 5。若使用 USB 库,通常由库函数在 SET_ADDRESS 回调中完成,但理解这一时序对排查“枚举到地址阶段就失败”的问题非常关键。

五、USBSTAT 与 USBCFG:状态与配置

USBSTAT 反映 USB 模块的实时状态,包括传输速度(全速/低速)、错误类型、NAK 状态、PPBRST(并行端口复位)等。调试时若发现设备无法被主机识别,可以先读 USBSTAT 确认速度位是否正确、是否处于复位状态。USBCFG 则用于配置端点缓冲区的方向、双缓冲、自动握手等特性,例如 UVCON 位控制 USB 模块是否连接到总线,FSDEV 位选择全速设备模式。

端点缓冲区配置是 USB 开发中最容易出错的部分之一。PIC32 的 USB 模块为每个端点提供独立的缓冲区和 CSR 寄存器,端点 0 通常用于控制传输,必须支持 SETUP、IN、OUT 三种阶段。配置时需要设置端点方向、缓冲区大小和双缓冲模式,并确保端点编号与主机请求一致。

六、端点 CSR 与传输握手

每个端点都有一组 CSR 寄存器,典型位包括 TXPKTRDY(发送包就绪)、RXPKTRDY(接收包就绪)、SETUP(收到 SETUP 包)、DATAEND(数据结束)、STALL(端点挂起)、FIFONOTEMPTY(FIFO 非空)等。发送数据时,固件把数据写入端点 FIFO,然后置 TXPKTRDY;硬件发送完成后自动清除该位。接收数据时,硬件收到包后置 RXPKTRDY,固件读取 FIFO 后写 1 清除该位。

下面给出一个端点 0 控制传输的简化配置示例,用于说明寄存器写入顺序和关键位操作。示例参数为教学假设,实际使用时需结合具体器件数据手册和 USB 库。

// 假设 UPLL 已锁定,USB 模块时钟已就绪
USBCSR = 0x00000000;        // 先清标志
USBCSRbits.USBEN = 1;       // 使能 USB 模块
USBCFGbits.UVCON = 1;       // 连接到 USB 总线
USBCFGbits.FSDEV = 1;       // 全速设备模式

// 端点 0 配置:控制传输,双向
USBE0CSRbits.TXMAXP = 64;   // 最大包长 64 字节
USBE0CSRbits.RXMAXP = 64;
USBE0CSRbits.CLRDT = 1;     // 清除数据翻转
USBE0CSRbits.TXPKTRDY = 0;
USBE0CSRbits.RXPKTRDY = 0;

// 中断使能:先只开错误和传输完成
USBIEN = 0;
USBIENbits.UERRIE = 1;
USBIENbits.TRNIE = 1;
USBIENbits.SUSPENDIE = 1;

// 枚举阶段收到 SET_ADDRESS 后
USBADDR = 5;                // 示例地址 5

写完之后如何判断生效?可以依次检查:USBCSR 的 USBEN 是否为 1;USBSTAT 的速度位是否与预期一致;端点 0 的 CSR 中 RXPKTRDY 是否在收到 SETUP 包后置位;USBADDR 是否等于主机分配的地址。若 USBEN 已置 1 但 USBSTAT 显示复位状态,通常说明 UPLL 未稳定或 UVCON 未正确设置。

调试建议:在枚举失败时,优先读取 USBCSR 和 USBSTAT 的原始值,再对照端点 CSR 的握手位,往往比直接单步跟踪更快定位问题。注意区分“写 1 清零”和“写 1 置位”的位,避免误操作。

七、配置检查清单

检查项寄存器/位预期值或现象
USB 时钟就绪UPLL 锁定状态锁定位为 1
模块使能USBCSR.USBEN读回为 1
总线连接USBCFG.UVCON读回为 1
设备地址USBADDR等于主机分配地址
端点 0 接收USBE0CSR.RXPKTRDY收到 SETUP 后置 1
中断使能USBIEN仅打开必要中断源