1. USB主机控制器:从协议到硬件的核心逻辑
搞嵌入式开发,尤其是涉及到人机交互或者数据采集存储,USB接口几乎是绕不开的一环。我们经常用现成的USB库,插上设备就能用,但底层到底是怎么“对话”的,很多朋友可能并不清楚。最近在调试TMS320F2838x的USB主机功能时,我不得不把芯片手册里关于事务处理的部分翻了个底朝天。这个过程让我意识到,真正理解USB主机控制器(Host Controller)如何调度和处理IN/OUT事务,是写出稳定、高效USB驱动代码的关键,而不是仅仅调用API。USB通信的本质是一种严格的主从式、轮询式总线。主机掌握绝对主动权,它通过发送令牌(Token)来“点名”某个设备的某个端点(Endpoint),然后进行数据交换。这个过程,在硬件层面被抽象为“事务”(Transaction)。一个完整的事务通常包含令牌包、数据包(可选)和握手包三个阶段。主机控制器的核心职责,就是精确地生成这些包,并处理设备的响应。TMS320F2838x内置的USB控制器,其主机模式的设计非常典型,通过一系列精心设计的寄存器来控制整个流程。理解这些寄存器位(如REQPKT、TXRDY、RXRDY)如何联动,以及背后的调度器(Transaction Scheduler)如何工作,你就能从“知其然”进阶到“知其所以然”,面对通信超时、数据丢失等问题时,也能快速定位到是软件配置问题,还是硬件或协议层面的异常。
2. IN事务的硬件处理流程与细节拆解
IN事务,即主机从设备读取数据,是USB通信中最常见的数据流方向之一。在TMS320F2838x的USB主机控制器中,这个过程被高度硬件化,但需要软件进行正确的初始化和状态管理。
2.1 事务发起与请求包管理
IN事务的起点,是软件告诉硬件:“我准备从这个端点收数据了”。这个“告诉”的动作,就是设置对应端点控制状态寄存器(例如USBCSRL0用于端点0)中的REQPKT位。这个位就像一个开关,一旦置起,就向内部的事务调度器(Transaction Scheduler)宣告:“这个端点上有活跃的事务请求等待处理”。
这里有一个关键细节:REQPKT通常是在端点配置完成后,由软件主动设置的。它标志着软件侧数据缓冲区(通常是FIFO的抽象)已准备就绪,可以接收数据。事务调度器会周期性地扫描所有配置好的端点,寻找REQPKT被设置的端点。一旦发现,它就会在总线上发起一个IN令牌包,这个包包含了目标设备的地址和端点号。
注意:
REQPKT位是主机发起IN请求的唯一信号。如果你发现主机始终没有发出IN令牌,第一件事就是检查这个位是否被正确设置。同时,要确保该端点的其他配置(如设备地址、端点类型、最大包长)已经完成,否则调度器可能不会处理一个配置不完整的端点请求。
2.2 数据接收与FIFO状态同步
当目标设备收到IN令牌后,如果它有数据要发送,就会回应一个数据包。主机控制器接收到这个数据包后,会将其存入为该端点分配的接收FIFO中。此时,硬件会自动将RXRDY(Receive Ready)位置位。这个位是给软件的核心中断信号,它意味着:“FIFO里有新鲜数据了,快来取!”
软件响应中断后,需要从FIFO中读取数据。数据读取完成后,一个至关重要的步骤是必须手动清除RXRDY位。这个清除动作有两个意义:第一,告诉硬件本批次数据已处理完毕,FIFO位置可被复用;第二,在某些配置下,清除RXRDY会触发硬件自动向设备发送ACK握手包,完成本次事务的确认。
为了简化编程,芯片提供了两个非常实用的自动控制位:
AUTOCL(Auto Clear):位于USBRXCSRHn寄存器。当此位置位时,如果从FIFO中卸载的数据包长度等于该端点设定的最大包长(MAXLOAD),硬件会在卸载完成后自动清除RXRDY位。这对于处理连续的最大长度数据包流非常方便,可以减少软件开销。AUTORQ(Auto Request):同样位于USBRXCSRHn寄存器。这是IN事务流控的“神器”。当此位置位时,RXRDY位被清除的瞬间,硬件会自动重新置位REQPKT位,从而立即发起下一次IN事务请求。这就实现了一个自动的“乒乓”操作:取完一包数据,马上请求下一包,非常适合高速连续数据传输。
2.3 传输长度管理与事务终止
对于已知总长度的传输(例如读取一个文件中的特定字节数),盲目使用AUTORQ会导致无限循环。这时就需要用到包计数寄存器USBRQPKTCOUNTn。软件在开始传输前,需要将待传输的总数据包数写入该寄存器。主机控制器每成功完成一次IN事务(即收到数据并回复ACK),就将该计数值减1。当计数值递减到0时,硬件会自动清除AUTORQ位,从而停止继续发起新的IN请求,优雅地终止传输。
对于未知长度的传输(例如实时数据流,直到收到一个短包标识结束),则应将USBRQPKTCOUNTn寄存器清零。此时,AUTORQ位将一直保持有效,持续请求数据。传输的终止由设备端控制:当设备发送了一个长度小于MAXLOAD的“短包”(Short Packet)时,主机在接收并处理这个短包后,本次传输即告结束。AUTORQ机制在此场景下保证了数据流的无缝衔接。
2.4 错误与异常处理机制
USB通信并非总是一帆风顺,主机控制器必须能妥善处理设备的各类异常响应。
- NAK响应:设备暂时无法提供数据(例如它的FIFO还没准备好)。主机控制器收到NAK后,不会认为这是错误,而是会根据预设的“NAK重试限制”(NAK Limit)在后续的调度周期中不断重试该IN事务。这体现了USB的流控特性,允许低速的设备让主机等待。
- STALL响应:设备端点处于停滞状态(Halted),通常意味着发生了功能或协议错误,需要软件干预。主机控制器收到STALL后,会停止重试该事务,并设置
STALLED状态位,同时产生中断。软件必须检测并处理这个中断,通常需要重新配置或复位该端点。 - 超时与总线错误:如果设备在指定时间内无响应,或者接收到的数据包存在CRC校验或位填充错误,主机控制器会启动重试。通常,在连续3次尝试失败后,控制器会认为通信失败,自动清除
REQPKT位以停止请求,并设置ERROR状态位,通知软件进行错误处理。
实操心得:在调试IN传输时,如果数据流突然中断,不要只看
RXRDY。务必同时检查USBCSRL0寄存器中的STALLED和ERROR位。我曾遇到一个案例,设备端固件有bug,意外返回了STALL,导致主机端一直收不到数据,而RXRDY中断再也不产生。最终就是通过查询STALLED位才发现问题根源。
3. OUT事务的硬件处理流程与核心要点
OUT事务,即主机向设备发送数据,其逻辑与IN事务对称,但方向相反。核心思想是:软件把数据装入FIFO,通知硬件“数据已备好”;硬件负责将数据发送出去,并处理设备的反馈。
3.1 数据装载与发送就绪
OUT事务的启动信号是TXRDY(Transmit Ready)位,位于发送端点控制状态寄存器USBTXCSRLn中。当软件将一包数据写入发送FIFO后,必须手动置位TXRDY。这个操作告诉事务调度器:“这个端点的发送FIFO里有数据待发,可以安排发送了”。
与IN事务类似,为了方便连续发送,硬件提供了AUTOSET位(位于USBTXCSRHn寄存器)。当AUTOSET使能时,如果��件写入FIFO的数据包长度达到了该端点的最大包长(MAXLOAD),硬件会自动置位TXRDY,省去了软件每次写数据后都要设置该位的操作。
事务调度器在轮询时,发现某个发送端点的TXRDY位(或FIFONE位,指示FIFO非空)被置位,就会在总线上发起一个OUT令牌包,紧接着将FIFO中的数据包发送出去。
3.2 握手响应处理与错误恢复
设备在收到数据包后,会回复一个握手包。
- ACK:设备成功接收数据。主机控制器收到ACK后,会清除
TXRDY位(如果FIFO中还有数据,可能会根据AUTOSET规则再次置位),表示本次事务成功完成。 - NAK:设备暂时无法接收数据(例如它的接收FIFO已满)。主机控制器的处理逻辑与IN事务类似:在达到NAK重试限制前,会在后续调度周期中不断重试发送同一包数据。
- STALL:设备端点停滞。主机控制器将停止重试,设置
STALLED位并产生中断,等待软件处理。 - 超时或总线错误:如果设备无响应或数据包传输出错,主机会重试发送。连续3次失败后,控制器会执行一个关键操作:刷新(Flush)发送FIFO,丢弃当前未能发送出去的数据包,同时设置
ERROR位。这是OUT事务与IN事务错误处理的一个重要区别,因为滞留在FIFO中的旧数据通常已经无效,需要清除以避免混淆。
3.3 IN与OUT事务的对称性与差异总结
为了更清晰地理解,我将IN和OUT事务的核心流程和寄存器映射总结如下表:
| 特性 | IN 事务 (主机读) | OUT 事务 (主机写) | 说明与注意事项 |
|---|---|---|---|
| 启动信号 | 设置REQPKT位 | 设置TXRDY位 | 分别是读/写操作的“发令枪”。 |
| 数据就绪信号 | RXRDY位 (硬件置位) | TXRDY位 (软件置位) | RXRDY表示“有数据可读”,TXRDY表示“有数据待发”。 |
| 自动控制位 | AUTORQ,AUTOCL | AUTOSET | AUTORQ/AUTOCL用于自动化读流程;AUTOSET用于自动化写流程。 |
| 包计数寄存器 | USBRQPKTCOUNTn | 通常不用于OUT | OUT事务的结束通常由软件控制(停止写入),或由短包(仅对控制传输)标记。 |
| 事务成功完成 | 收到数据包,回复ACK,清除RXRDY | 发出数据包,收到ACK,清除TXRDY | 成功完成一次数据交换。 |
| 设备响应NAK | 主机按限制重试IN令牌 | 主机按限制重试OUT令牌+数据 | 流控机制,允许设备让主机等待。 |
| 设备响应STALL | 停止重试,置位STALLED | 停止重试,置位STALLED | 致命错误,需要软件干预端点状态。 |
| 超时/错误重试失败 | 清除REQPKT,置位ERROR | 刷新发送FIFO,置位ERROR | OUT事务失败需清空FIFO,这是关键差异。 |
| 核心状态寄存器 | USBCSRL0/USBRXCSRLn | USBTXCSRLn | 需定期查询或通过中断处理其中的状态位。 |
理解这份对称性,能让你在编程时建立起清晰的逻辑模型。无论是读还是写,核心都是“请求-响应-确认”的循环,只是方向和控制信号不同。
4. 事务调度机制:总线时间的仲裁者
事务调度器(Transaction Scheduler)是USB主机控制器内部的“交通警察”,它决定了在1毫秒的USB帧(Frame)内,各个端点的IN/OUT事务以何种顺序、何种频率被执行。它的工作目标是公平、高效地利用总线带宽,并满足不同传输类型的时序要求。
4.1 帧结构与SOF包
USB时间被划分为连续的1ms帧(全速)或125μs的微帧(高速)。对于TMS320F2838x支持的全速/低速主机模式,调度基于1ms帧。每一帧的开始,如果连接的是全速设备,主机会自动发送一个SOF(Start of Frame)包。这个包有两个作用:一是作为时间同步的基准;二是其包含的帧号,用于等时(Isochronous)传输等需要时间戳的场景。如果连接的是低速设备,主机则会在总线上发送一个特殊的“K”状态(SEO,Single-Ended Zero)作为保活信号,防止低速设备因长时间无活动而进入挂起(SUSPEND)模式。
4.2 不同传输类型的调度策略
调度器在SOF包之后,开始在本帧剩余的时间内,循环扫描所有已配置的端点。
- 中断(Interrupt)传输:这是具有周期性要求的传输。每个中断端点都有一个间隔(Interval)计数器,其值通过
USBTXINTERVALn或USBRXINTERVALn寄存器设置,范围是1-255帧。调度器在每个帧的第一轮扫描中,会检查每个中断端点的间隔计数器是否已递减到0。如果是,且该端点有活跃事务(REQPKT或TXRDY置位),则调度器立即安排一次该端点的事务。执行完毕后,间隔计数器会重新加载设定值。这就保证了中断传输最差情况下也能在设定的时间间隔内得到一次服务机会,满足了鼠标、键盘等HID设备对响应时间的要求。 - 批量(Bulk)传输:这类传输对延迟不敏感,但要求数据的正确性。它没有固定的调度间隔。调度器在每一轮扫描中,只要发现某个批量端点有活跃事务,并且当前帧剩余的时间足够完成一次该端点的事务(包括令牌、数据、握手包及包间延迟),就会立即安排执行。如果时间不够,则推迟到下一帧。这种“尽力而为”的调度方式,使得批量传输能充分利用总线空闲带宽。
- 控制(Control)传输:用于枚举和命令传输,由建立、数据和状态三个阶段组成。它享有最高的优先级(仅次于SOF包),通常会被调度器优先处理,以确保设备的枚举和配置能快速完成。
4.3 公平性与错误隔离机制
调度器的设计体现了很好的公平性。例如,当一个批量端点因设备返回NAK而需要重试时,调度器不会“死等”在这个端点上。它会先完成本轮对所有其他端点的扫描与服务,然后再回来重试这个出错的端点。这防止了一个不响应的设备端点“霸占”总线,阻塞其他正常端点的通信。
此外,用户可以为批量端点设置一个“NAK超时限制”。如果一个端点连续返回NAK的次数超过此限制,主机控制器可以主动超时,并可能产生中断通知软件,从而避免因某个设备故障导致主机陷入无限重试循环。
踩坑记录:在同时使用多个USB设备(如一个HID鼠标和一个U盘)时,我曾遇到鼠标移动卡顿的问题。排查后发现,是批量传输(U盘)的NAK重试限制设置得过大,且没有启用NAK超时。当U盘忙于内部操作频繁返回NAK时,调度器虽然会轮询其他端点,但重试U盘端点占用了大量本可用于中断传输的微小时间片。将批量端点的NAK重试次数调低,并启用合理的超时机制后,鼠标的响应立刻变得流畅。这说明理解调度器的“时间片”概念,对于优化多设备并发性能至关重要。
5. 端点配置与FIFO管理实战
理解了事务处理流程和调度原理后,要让一个USB端点真正工作起来,正确的配置是第一步。TMS320F2838x的USB控制器提供了灵活的端点配置和FIFO管理机制。
5.1 端点类型与基本配置
在主机模式下配置一个端点,本质上是建立一个主机端端点寄存器与远端USB设备上某个物理端点之间的映射关系。你需要通过寄存器明确��知控制器:
- 端点类型:通过
USBTXTYPEn(发送)或USBRXTYPEn(接收)寄存器配置。必须与设备端点描述符中声明的类型一致,如控制(Control)、批量(Bulk)、中断(Interrupt)。类型错误会导致通信失败。 - 目标设备地址与端点号:同样在上述寄存器中设置。地址是在枚举阶段由主机分配的,端点号是设备固有的。
- 最大包长(Max Packet Size):在
USBRXMAXPn或USBTXMAXPn寄存器中设置。这个值必须小于或等于设备端点描述符中声明的wMaxPacketSize。如果设置得比设备能力小,会导致性能浪费;如果设置得比设备能力大,在接收数据时会导致缓冲区溢出错误。通常直接采用设备报告的值。 - Hub连接信息(如果使用Hub):如果设备通过USB Hub连接,还需要在
USBRXHUBADDRn/USBRXHUBPORTn或USBTXHUBADDRn/USBTXHUBPORTn寄存器中记录Hub的地址和端口号。这是USB Tiered Star拓扑结构所要求的,以便主机能将事务正确地路由到目标设备。
5.2 FIFO内存分配与双缓冲策略
USB控制器内部有4KB的共享RAM作为所有端点的FIFO缓冲区。端点0固定占用前64字节。剩余的FIFO空间需要软件动态分配给其他端点。这是通过设置每个端点的USBRXFIFOSZ/USBTXFIFOSZ和USBRXFIFOADD/USBTXFIFOADD等寄存器来实现的。
分配FIFO时,一个核心原则是:为每个端点分配的FIFO大小,至少应能容纳一个最大长度的数据包。对于批量或中断传输,最大包长通常是64字节(全速)。
为了提升吞吐量,尤其是对延迟敏感的中断传输或高速率批量传输,强烈建议使用双缓冲(Double Buffering)。你可以将一个端点的FIFO大小配置为两倍最大包长(如128字节)。硬件会将这128字节视为两个独立的64字节缓冲区(Buffer0和Buffer1)。其工作流程如下:
- 对于IN端点(主机读):当Buffer0被设备的数据填满(
RXRDY置位)时,硬件可以立即切换使用Buffer1来接收下一个数据包,同时软件处理Buffer0中的数据。这实现了接收数据的“乒乓”操作,几乎消除了总线等待时间。 - 对于OUT端点(主机写):当软件填满Buffer0并置位
TXRDY后,硬件开始发送Buffer0的数据。此时,软件可以立即向Buffer1填充下一包数据。当Buffer0发送完毕,硬件可以无缝切换到发送Buffer1,只要TXRDY在Buffer1就绪前被置位。
启用双缓冲通常是通过配置USBRXCSRHn或USBTXCSRHn寄存器中的AUTORQ、AUTOSET等位,结合DMAMODE(如果支持DMA)或特定的双缓冲使能位来实现的。具体位域需参考芯片手册。
5.3 端点0的特殊性与控制传输
端点0是所有USB设备都必须具备的控制端点,且是双向的(包含IN和OUT方向)。它的配置相对固定,FIFO大小通常为64字节。控制传输用于枚举、配置和发送类特定请求,其流程比普通批量/中断传输复杂,分为建立(Setup)、数据(Data,可选)和状态(Status)三个阶段。
在主机模式下实现控制传输,软件需要实现一个状态机来管理这三个阶段的切换:
- 建立阶段:主机向端点0发送一个8字节的Setup包(本质是一个特殊的OUT事务)。这需要配置端点0为控制OUT模式,并装载Setup数据。
- 数据阶段(可选):根据Setup包中的请求方向,可能包含一个或多个IN或OUT事务来传输数据。软件需要根据
wLength字段判断是否有数据阶段及其方向。 - 状态阶段:方向与数据阶段相反。如果数据阶段是IN,则状态阶段是一个OUT事务(主机发送0长度数据包);如果数据阶段是OUT或无数据阶段,则状态阶段是一个IN事务(主机读取0长度数据包)。设备通过状态阶段的握手包(ACK)来报告整个控制传输的成功与否。
配置心得:在分配FIFO时,建议画一张内存映射图。例如,4KB FIFO,端点0占0x00-0x3F。你可以将端点1的IN FIFO分配在0x40-0xBF(128字节,双缓冲),端点1的OUT FIFO分配在0xC0-0xFF(64字节),以此类推。清晰的规划能避免FIFO区域重叠,这是很多诡异通信错误的根源。另外,在动态切换端点配置(如设备重枚举)前,务必确保该端点上的所有进行中事务都已完成,否则会导致数据错乱或总线挂起。
6. 高级主题:Hub支持、电源管理与总线状态
在实际的USB主机应用中,经常会遇到连接Hub、管理设备电源和总线复位等场景。TMS320F2838x的控制器也提供了相应的硬件支持。
6.1 与USB Hub的协同工作
当主机通过USB Hub连接设备时,事务的路由变得复杂。主机控制器需要知道目标设备连接在哪个Hub的哪个端口上。这就是USBRXHUBADDRn/USBRXHUBPORTn和USBTXHUBADDRn/USBTXHUBPORTn寄存器的作用。在配置端点时,除了设备地址和端点号,还必须正确设置这些Hub信息。
一个重要的特性是,为了最大化支持设备数量(USB拓扑限制),主机控制器允许动态更新这些地址和端口信息。这意味着,当一个设备从Hub的一个端口拔下,另一个设备插上时,软件可以简单地更新对应端点的Hub地址和端口寄存器,就可以复用该端点资源与新设备通信,而无需完全重新初始化端点。当然,在更新前,必须确保该端点上的任何进行中事务都已彻底完成。
6.2 电源管理与总线控制
作为主机,有责任管理总线的电源和状态。
- VBUS供电控制:主机通过
USB0EPEN引脚控制外部电源电路,为USB端口提供5V VBUS电源。在初始化阶段,必须确保USB0EPEN处于无效状态,避免意外上电。USB0PFLT是电源故障检测引脚,可以配置为在故障时自动关闭USB0EPEN或产生中断通知软件。 - 总线复位(RESET):当新设备连接后,主机必须发起一次总线复位(置位
USBPOWER寄存器的RESET位至少20ms),使设备进入默认状态,才能开始枚举过程。 - 挂起与恢复(SUSPEND/RESUME):为了节能,当总线空闲超过3ms,主机应进入挂起模式(置位
SUSPEND位)。此时控制器停止调度器和帧计数器。需要恢复时,置位RESUME位并清除SUSPEND位,控制器会产生20ms的恢复信号,然后恢复正常操作。主机也支持检测设备的远程唤醒(Remote Wake-up)信号。 - 总线异常:Babble:如果一帧时间(1ms)结束时,总线上仍有数据活动,控制器会认为连接的设备发生故障(“说个不停”,即Babble),它会暂停所有事务并产生Babble中断,由软件进行错误恢复。
6.3 地址/数据总线桥接的注意事项
TMS320F2838x的USB控制器最初是为ARM的AHB总线设计的,后适配到C28x的16位总线。这对软件访问带来一个关键影响:USB控制器的寄存器空间是8位宽的,而C28x的默认访问是16位的。
这意味着,当你用32位或16位指针去读写USB寄存器时,硬件桥接逻辑会进行透明处理,但你在CCS(Code Composer Studio)的内存窗口中看到的数据可能不是“一对一”的。例如,一个16位访问(*(short*)0x1000)实际上会读取USB控制器地址0x1000和0x1001的两个8位寄存器,并将其组合成一个16位数。在CCS的8位视图下,你可能会看到数据被复制或排列顺序与预期不同。
安全的做法是,在访问明确的8位寄存器时,使用C28x编译器提供的__byte()内部函数进行8位访问,如手册示例所示:__byte((int *)0x00, 0)。这能确保你访问的是确切的8位地址单元,避免因总线宽度转换带来的理解偏差和潜在错误。
7. 初始化、配置与软件开发指南
纸上得来终觉浅,绝知此事要躬行。最后,我们把这些理��知识串联起来,看看如何让TMS320F2838x的USB主机控制器真正跑起来。
7.1 硬件与时钟初始化
在写任何USB相关代码前,必须完成底层硬件使能:
- 使能外设时钟:通过系统控制模块的
PCLKCR11寄存器使能USB模块的时钟。 - 配置USB引脚:通过GPIO模块的
GPBAMSEL寄存器,将USB0DM(GPIO42)和USB0DP(GPIO43)引脚功能切换到USB PHY。这一步至关重要,如果引脚复用错误,USB物理层根本无法工作。 - 配置辅助PLL:USB模块需要一个精确的60MHz时钟。你需要配置系统的辅助PLL,生成并供给USB模块这个时钟。
- 管理VBUS电源:在初始化序列中,确保控制外部电源的
USB0EPEN信号处于关闭状态,直到软件明确要为一个连接的主机设备供电。
7.2 软件开发流程与示例解析
TI的C2000Ware软件包提供了丰富的USB示例代码,这是最好的学习起点。根据你的项目需求,选择合适的示例进行修改。
- 主机示例:如
usb_ex5_host_mouse(鼠标主机)、usb_ex6_host_keyboard(键盘主机)、usb_ex7_host_msc(U盘主机)。这些示例展示了如何初始化主机控制器、检测设备连接、解析描述符、配置端点以及处理不同USB类(HID, MSC)的数据。 - 开发流程:
- 初始化硬件:完成上述时钟、引脚配置。
- 主机控制器初始化:设置
USBPOWER等全局寄存器,使能主机模式。 - 设备连接检测:使能连接检测中断。当设备插入,会产生中断。
- 总线复位与枚举:检测到设备后,发起总线复位。然后通过控制传输(端点0)读取设备描述符、配置描述符等,为设备分配地址,并设置配置。
- 端点配置:根据设备接口和端点描述符,配置主机端的对应端点寄存器(类型、地址、最大包长、FIFO分配等)。
- 启动传输:对于中断端点,设置好
REQPKT或TXRDY,调度器会自动处理。对于批量传输,在需要读写时设置相应标志。 - 中断服务程序(ISR)编写:这是USB驱动的核心。ISR需要读取USB中断状态寄存器,判断是哪个端点产生了何种中断(传输完成
RXRDY/TXRDY、错误ERROR、停滞STALLED等),然后进行相应的数据处理或错误恢复。
7.3 常见问题排查与调试技巧
调试USB问题,逻辑分析仪或专用的USB协议分析仪是终极武器。但如果手头没有,可以依靠以下软件方法:
通信完全无反应:
- 检查硬件:时钟是否60MHz?USB数据线(DM/DP)是否接对?上拉电阻(对于设备模式)或VBUS供电(对于主机模式)是否正常?
- 检查软件:引脚复用配置(
GPBAMSEL)是否正确?USB模块时钟是否使能(PCLKCR11)? - 在枚举阶段,可以在控制传输的ISR中打印出每次收到的设备描述符数据,看是否正确。
枚举成功,但数据传输失败:
- 检查端点配置:最大的嫌疑点。确认主机端配置的端点类型、方向、最大包长是否与设备描述符完全一致。一个字节的差异都可能导致通信失败。
- 检查FIFO分配:是否发生了FIFO内存区域重叠?分配的大小是否足够(至少一个最大包)?
- 查看状态寄存器:在传输超时或停止时,立刻读取
USBCSRL0、USBTXCSRLn、USBRXCSRLn等寄存器,查看ERROR、STALLED、NAKTO(NAK超时)等错误位。这是定位问题的直接证据。 - 检查NAK重试和超时设置:如果设备响应慢,是否因NAK重试次数过多阻塞了总线?是否因超时设置太短而误判设备故障?
数据传输不稳定,偶发错误:
- 电源完整性:USB对电源噪声比较敏感。确保MCU和USB接口的电源干净、稳定,去耦电容靠近芯片电源引脚放置。
- 信号完整性:USB数据线应尽量短,并做好差分走线,避免与其他高速信号线平行走线过长。
- 软件时序:在ISR中处理数据是否耗时过长?是否错过了某些实时性要求高的中断?考虑优化ISR,或将非实时任务移到主循环。
使用Hub时设备无法识别:
- 确认Hub本身已被正确枚举和配置。
- 检查端点配置中的Hub地址和端口号:这是最容易被忽略的一点。主机端为设备端点配置的
USBRXHUBADDRn和USBRXHUBPORTn必须指向该设备所连接的具体Hub和端口。
调试是一个耐心和逻辑分析的过程。从电源、时钟、引脚这些最底层开始,再到枚举流程,最后才是具体的数据传输。充分利用芯片的状态寄存器和示例代码的框架,你能更快地驯服这颗强大的USB控制器。