1. PIC32 USB设备开发全景

USB 设备开发之所以让许多嵌入式工程师感到门槛较高,并不是因为某一行代码难写,而是因为“协议—外设—工具链—应用”这四层同时存在,任何一层理解不清都会在调试时表现为“枚举失败”“设备不识别”“传输超时”这类笼统现象。本章先建立一张全景图:USB 协议规定了主机与设备如何对话,PIC32 的 USB 外设负责把这些对话翻译成寄存器与缓冲区操作,工具链把描述符、端点配置和固件组织成可下载的镜像,而典型应用场景决定了你该选择哪种传输类型与端点结构。

一、USB 协议基础:设备开发真正要掌握的部分

USB 是严格的主从总线,所有传输由主机发起,设备只能响应。对设备开发者而言,最需要吃透的是四件事:描述符体系、端点与传输类型、枚举过程、以及控制传输的请求格式。

描述符是设备的“自我介绍”,采用树状结构:设备描述符给出 VID、PID、bNumConfigurations;配置描述符给出供电方式与接口数量;接口描述符给出 bInterfaceClass、bNumEndpoints;端点描述符给出 bEndpointAddress、bmAttributes、wMaxPacketSize 和 bInterval。主机在枚举时逐层读取,任何一层的长度字段或索引写错,都会导致主机在“取配置描述符”阶段停止。

端点(Endpoint)是设备侧的数据通道,方向以设备为参照:IN 表示设备到主机,OUT 表示主机到设备。端点 0 固定为控制端点,用于枚举和标准请求;其余端点按传输类型分为控制、中断、批量、同步四类。选择依据很直接:需要确定性延迟的小数据量(如 HID 按键、鼠标)用中断传输;大数据量且允许重传(如 CDC 串口、Mass Storage)用批量传输;音视频等要求恒定速率、允许少量丢包的场景用同步传输。

枚举过程可以概括为:主机复位设备 → 通过端点 0 读取设备描述符(先读 8 字节获取 bMaxPacketSize0)→ 再次读取完整设备描述符 → 设置地址 → 读取配置描述符集合 → 选择配置(SetConfiguration)→ 设备进入配置状态。此后主机才会加载对应类驱动并开始正常通信。

控制传输的请求由 8 字节 SETUP 包定义:bmRequestType、bRequest、wValue、wIndex、wLength。例如标准请求 GET_DESCRIPTOR 的 bmRequestType 为 0x80,bRequest 为 0x06,wValue 高字节为描述符类型、低字节为索引。理解这个格式,才能在抓包时快速判断是主机请求有问题还是设备响应有问题。

PIC32 USB 设备开发全景:协议层 → 外设层 → 工具链 → 应用 USB 协议层(主机视角) 设备描述符 · 配置 · 接口 · 端点 端点0控制传输:SETUP 8 字节 中断 / 批量 / 同步 传输类型 枚举:复位→取描述符→设地址→选配置 PIC32 USB 外设层 USB SRAM:端点缓冲区(BDT) U1OTG / U1CON / U1ADDR 寄存器 UEP0~UEPn:端点使能与握手 中断:U1IR / U1IE 事件分发 开发工具链 MPLAB X IDE + XC32 编译器 Harmony 3:USB 协议栈与类驱动 MCC 图形化配置描述符与端点 USB 分析仪 / 总线抓包验证 典型应用场景与端点/传输类型选择 HID 人机接口 键盘 / 鼠标 / 自定义 中断 IN,1~8 字节 bInterval 1~10 ms 免驱,主机内置类驱动 CDC 虚拟串口 批量 IN + 批量 OUT 64 字节包,全速 需 ACM 控制接口 替代 UART 调试通道 Mass Storage 批量 IN + 批量 OUT SCSI 命令 + 数据 U 盘 / SD 卡读卡器 吞吐优先,允许重传 音频 / 数据采集 同步 IN / OUT 等时,无重传 带宽预留,抖动敏感 需精确 bInterval

二、PIC32 USB 外设架构:从寄存器到缓冲区

PIC32 的 USB 模块是一个全速(12 Mbps)设备控制器,部分型号还带 OTG 支持。它的核心资源包括:一组 USB 控制与状态寄存器、一块专用 USB SRAM、以及端点缓冲区描述符表(BDT)。

关键寄存器的作用需要形成条件反射:U1CON 控制 USB 模块使能与复位;U1ADDR 保存主机分配的设备地址;U1IE 与 U1IR 分别管理中断使能与中断标志;U1EPn 配置每个端点的方向、类型、握手与使能。端点数据并不在普通 RAM 中,而是放在 USB SRAM 里,通过 BDT 中的地址指针访问。BDT 每个表项包含缓冲区地址、字节计数和状态位(如 UOWN、DTS、BSTALL)。

这里有一个容易出错的点:UOWN 位表示缓冲区当前归 USB 硬件所有还是归 CPU 所有。CPU 要发送数据时,先写入缓冲区、设置字节计数、清 UOWN 交给硬件;硬件发送完成后置位 UOWN 并产生中断,CPU 再取回控制权。如果 CPU 在 UOWN 仍为 1 时改写缓冲区,就会出现数据错乱或传输卡死。

中断处理是设备固件的骨架。典型流程是:进入 USB 中断 → 读取 U1IR → 判断是 UEP0 控制传输、还是某个数据端点完成、还是复位/挂起事件 → 分派到对应处理函数 → 清除中断标志。控制传输的中断尤其要小心,因为一个 SETUP 阶段可能跟随 IN 数据阶段或 OUT 数据阶段,需要状态机跟踪。

三、开发工具链:从描述符到可下载镜像

实际开发通常不手写全部寄存器操作,而是使用 MPLAB Harmony 3 的 USB 协议栈。工具链的典型使用路径是:在 MPLAB X 中新建 Harmony 项目 → 用 MCC(MPLAB Code Configurator)添加 USB 组件 → 选择设备类(CDC、HID、MSD 或自定义)→ 配置 VID/PID、端点数量与大小、字符串描述符 → 生成代码 → 在应用层实现类回调。

以 CDC 虚拟串口为例,MCC 会生成描述符数组、端点初始化代码和 USB 中断服务框架。开发者需要实现的主要是数据收发回调,例如在 APP_USB_CDC_ReadCallback 中把收到的字节放入环形缓冲区,在主循环中解析。这样既保留了协议栈的正确性,又让应用逻辑保持清晰。

验证环节建议至少准备两种手段:一是用 USB 分析仪抓取枚举过程,确认描述符请求与响应字节完全匹配;二是在主机端用系统工具观察设备是否被正确识别。例如 Windows 下可在设备管理器中查看是否出现“USB 串行设备(COMx)”,Linux 下用 lsusb -v 查看描述符树。若设备反复出现又消失,通常是描述符长度或端点配置不一致导致主机重试枚举。

初学者最容易忽略的是描述符中的 wTotalLength 字段。它必须等于配置描述符、接口描述符、类描述符和所有端点描述符的字节数之和。少算或多算都会让主机在读取配置时提前截断或越界。

四、典型应用场景与选型判断

面对一个具体项目,选型可以按以下顺序判断:先确定数据量和实时性要求,再确定传输类型,最后确定端点数量和缓冲区大小。HID 适合小数据量、低延迟、免驱的场景;CDC 适合需要双向可靠字节流的调试或通信通道;Mass Storage 适合需要被主机识别为存储设备的场景;同步传输适合音频或高速采集,但必须接受无重传机制带来的丢包可能。

以“用 PIC32 做一个 USB 数据采集卡,每 1 ms 上传 64 字节传感器数据”为例:数据量不大但要求周期稳定,应选择中断传输而非批量传输,端点配置为 IN、中断类型、wMaxPacketSize 为 64、bInterval 为 1。若改为“上传 1 MB 的日志文件”,则应选择批量传输,因为批量传输会利用全部可用带宽并自动重传,吞吐更高且不丢数据。