MCU的USB枚举与传输不能共用完成判据
USB 设备能被识别,却在换主机后枚举失败或读数据超时,常见原因是完成条件被混淆。MCU 固件既要遵守控制请求的分阶段提交规则,也要让批量数据的终止方式与主机请求一致,不能用发送函数返回代替协议完成。
控制传输由建立、可选的数据及状态阶段构成。收到建立包,只能说明一个新请求开始,不能直接认定请求已经成功。设备需要根据请求类型、方向和长度建立对应状态,并在规定方向完成状态握手。若应用把所有零长度请求都当成可以立刻结束的命令,就可能省掉主机正在等待的状态事务。主机随后重试或重置设备,表面上像链路不稳定,实际却是设备端状态机没有推进到主机预期的位置。
设置设备地址尤其能暴露阶段错误。按照协议要求,新地址应在相关请求的状态阶段成功完成后生效;如果收到建立包就立即切换地址,主机仍使用原地址完成状态握手时,设备可能已经不再响应。某些外设提供延迟生效的硬件机制,另一些实现由协议栈在完成回调中处理。修改底层驱动时必须理解它与协议栈的分工,不能在两个层次都抢先写地址,也不能以寄存器赋值已经完成推断总线事务已经完成。
控制端点还要处理新建立包打断旧请求的情形。缓冲指针、剩余长度与当前阶段必须及时回到新请求的上下文,不能把前一请求的完成回调误用于后一请求。设备描述符读取可能分多次进行,主机第一次只请求部分长度也属于正常交互。测试应覆盖短长度读取、请求重试与总线复位后的再次枚举,并用协议抓包核对建立包和状态包是否配对,而不是只确认系统设备列表里曾经出现过名称。
进入批量传输后,报文结束又有不同语义。端点最大包长是总线上单个数据包的上限,不是应用消息长度。主机通常按事先提交的接收长度等待,达到该长度或收到短包等条件时完成对应传输。若设备实际数据少于主机请求,且最后一个包小于最大包长,短包可以表示本次数据结束;但实际长度恰好是最大包长整数倍时,最后一个满包并没有提供同样的结束信息。
在需要用短包终止、主机仍期待更多数据的场景下,设备可能需要补发零长度数据包。它不携带应用内容,却提供短包终止条件。例如最大包长六十四字节,设备准备发送一百二十八字节,而主机请求更大长度,两个满包后可能还需要一个零长包。若主机本就只请求一百二十八字节,则长度满足已经足以完成,不应把“整数倍必须补包”当成普遍规则。是否补发应与协议栈及主机接口的约定一起决定。
零长包也不能自动成为所有应用消息的分隔符。串流式接口可能把多次应用写入合并交付,业务若需要恢复消息边界,应另设长度字段或明确的分帧规则。MCU 驱动还应区分应用数据已进入软件队列与端点事务得到主机确认,缓冲区回收应遵守栈接口契约。验证可组合最大包长前一字节、恰好整数倍和整数倍后一字节,并改变主机读取长度,从而把端点包边界、传输完成和业务消息三者真正分开。
枚举应在正确阶段切换地址,批量传输应依据主机长度与短包条件结束,业务消息则另行分帧。区分这三种边界,才能避免把局部发送完成误判为整次通信完成。





