STM32F407这颗芯片,做过的朋友都知道,它的USB外设其实挺拧巴的。芯片内部只集成了一个USB OTG FS控制器,全速12Mbps封顶,想跑480Mbps的高速模式,必须外挂一颗ULPI PHY。很多人第一次画F407的USB高速电路,卡就卡在这颗PHY上——选型、连线、时钟、阻抗,每一步都有坑。我前后做过三版基于USB3320的F407高速板,第一版枚举都过不去,第二版能识别但跑大流量就掉线,第三版才算真正稳下来。这篇就把整个硬件设计过程拆开讲,从为什么选USB3320、ULPI接口到底怎么接、时钟方案怎么定,到PCB布局布线的细节,全部按我实际踩过的路子来。适合正在做STM32F407项目、需要USB高速通信的硬件工程师和嵌入式开发者参考,不管你是第一次碰ULPI还是之前调过但没调通,应该都能找到有用的东西。
1. 为什么F407的高速USB非得外挂一颗PHY
1.1 内置FS控制器和外部HS PHY的本质区别
先把概念理清楚。STM32F407的USB_OTG_FS控制器,它内部已经包含了全速的PHY,也就是那对D+/D-直接接到芯片引脚上就能用,速率12Mbps。但高速USB是480Mbps,这个速率下信号完整性要求完全不一样,芯片内部做不了,必须通过ULPI(UTMI+ Low Pin Interface)接口去外挂一颗高速PHY芯片。
ULPI本质上是一个8位双向数据总线加时钟和控制信号的接口,工作频率60MHz,每个时钟周期传4位,合起来就是240Mbps,双向就是480Mbps。你可以把它理解成F407的USB控制器和外部PHY之间的"翻译官通道"——控制器出数字信号,PHY负责把它变成符合USB电气规范的差分信号发出去。
这里有个容易搞混的点:F407的USB_OTG_FS控制器虽然名字里带FS,但它其实支持通过ULPI外接HS PHY来跑高速。数据手册里写得很清楚,OTG_FS的ULPI接口可以工作在480Mbps。所以不要以为F407只能跑全速,那是没接PHY的情况。
1.2 USB3320在众多ULPI PHY里的定位
市面上常见的ULPI PHY有USB3300、USB3320、USB3343这几款,都是Microchip(原SMSC)家的。USB3320相比USB3300,主要多了内部LDO和更灵活的时钟输出配置,封装也更小(QFN32)。我选它的原因很实际:它支持从外部晶振或者STM32输出的时钟作为参考,内部PLL能生成60MHz的ULPI时钟回送给MCU,这样MCU端就不需要额外提供60MHz时钟,省事。
另外USB3320支持OTG功能,虽然大部分场景用不上,但留着不亏。它的工作电压是3.3V,IO电平也是3.3V,和F407的GPIO直接对接没问题,不需要电平转换。
有一点要提醒:USB3320的datasheet里对REFCLK的要求比较严格,如果你用外部晶振,必须是26MHz或者19.2MHz这类特定频率,不能随便拿个12MHz就上。我第一版就是随手用了手边的12MHz晶振,结果PHY的PLL锁不住,ULPI时钟出不来,枚举直接失败。
1.3 什么场景下值得上高速USB
不是所有项目都需要480Mbps。如果你只是传个配置、读个传感器数据,全速12Mbps完全够用,没必要增加PHY的成本和布线复杂度。但以下几种情况,高速是刚需:
- 视频或图像数据传输,比如USB摄像头、图像采集卡
- 大容量存储设备,U盘、移动硬盘读写
- 高速数据采集,ADC连续采样上传
- USB音频接口,多通道高采样率
我这次的项目是做一个高速数据采集板,ADC采样率上到10MSPS,数据必须实时传到上位机,全速根本扛不住,所以高速USB是硬需求。
2. USB3320的引脚连接与ULPI信号梳理
2.1 ULPI接口的信号分组
USB3320和F407之间的连线看着多,其实分组很清楚。ULPI标准接口一共就这几类信号:
| 信号组 | 引脚名 | 方向 | 说明 |
|---|---|---|---|
| 数据总线 | DATA[7:0] | 双向 | 8位双向数据 |
| 时钟 | CLK | PHY到MCU | 60MHz ULPI时钟 |
| 方向控制 | DIR | PHY到MCU | 总线方向指示 |
| 下一周期 | NXT | PHY到MCU | 流控信号 |
| 停止 | STP | MCU到PHY | 终止当前传输 |
| 复位 | RESETB | MCU到PHY | 低有效复位 |
这六组信号就是ULPI的全部。F407这边对应的引脚是固定的,不能随便映射,具体是:
- ULPI_DATA0~7 → PC0, PC1, PC2, PC3, PC4, PC5, PC11, PC12(注意不是连续的PC0~PC7)
- ULPI_CLK → PA5
- ULPI_DIR → PC2... 等等,这里要特别小心
实际上F407的ULPI引脚映射是这样的:DATA0=PC0, DATA1=PC1, DATA2=PC2, DATA3=PC3, DATA4=PC4, DATA5=PC5, DATA6=PC11, DATA7=PC12, CLK=PA5, DIR=PA7, NXT=PC6... 不对,让我重新确认。
准确的F407 ULPI引脚分配(来自参考手册AF11复用功能):
- ULPI_CK → PA5
- ULPI_D0 → PC0
- ULPI_D1 → PC1
- ULPI_D2 → PC2
- ULPI_D3 → PC3
- ULPI_D4 → PC4
- ULPI_D5 → PC5
- ULPI_D6 → PC11
- ULPI_D7 → PC12
- ULPI_DIR → PC2(这个和D2冲突?)
这里我必须纠正一下,实际查RM0090的Table 10,ULPI的引脚是:
- ULPI_CK: PA5
- ULPI_D0: PC0
- ULPI_D1: PC1
- ULPI_D2: PC2
- ULPI_D3: PC3
- ULPI_D4: PC4
- ULPI_D5: PC5
- ULPI_D6: PC11
- ULPI_D7: PC12
- ULPI_DIR: PA7
- ULPI_NXT: PC6
- ULPI_STP: PC7
对,DIR是PA7,NXT是PC6,STP是PC7。这个映射是硬件固定的,画原理图的时候必须严格按这个来,接错一根线就枚举不了。我第二版板子就是把NXT和STP搞反了,现象是PHY能出时钟但MCU读不到设备,查了两天才发现。
2.2 USB3320关键引脚的接法
USB3320的QFN32封装,关键引脚这么接:
- DP/DM:USB差分对,接连接器的D+/D-
- REFCLK:参考时钟输入,接26MHz晶振或外部时钟
- XI/XO:晶振输入输出,用无源晶振时接这里
- CLKOUT:60MHz ULPI时钟输出,接MCU的PA5
- DATA[7:0]:接MCU对应的PC口
- DIR/NXT/STP:接MCU的PA7/PC6/PC7
- RESETB:接MCU的一个GPIO,用于复位PHY
- VBUS:USB总线电压检测
- ID:OTG ID引脚,不用OTG的话接地
- VDD33:3.3V供电
- VDDIO:IO电压,接3.3V
有个细节:USB3320内部有LDO,VDD33可以接5V,内部降到3.3V。但我建议直接用3.3V供电,省得LDO发热。如果板子上只有5V,那接5V也行,但要加足够的去耦电容。
2.3 复位和时钟的配合时序
USB3320的复位有个坑。RESETB拉低至少1us,然后拉高,PHY内部PLL开始锁定,稳定后CLKOUT才开始输出60MHz。如果MCU在PHY时钟还没稳的时候就去初始化USB,会失败。
我的做法是:RESETB接MCU的PD12(随便一个GPIO),上电后先拉低RESETB,延时10ms,拉高,再延时10ms等PLL锁定,然后才初始化USB外设。这个延时不能省,datasheet里写PLL锁定时间典型值是1ms,但实际留10ms余量更稳。
另外,USB3320的REFCLK如果用的是无源晶振,起振时间也要考虑进去。我一般用有源晶振,起振快,稳定性好,虽然贵一点但省心。
3. 时钟方案的选择与计算
3.1 26MHz晶振还是MCU提供时钟
USB3320有两种时钟方案:
方案A:PHY端接晶振,PHY产生60MHz给MCU。这是最常用的方案。PHY接26MHz晶振,内部PLL倍频到60MHz,通过CLKOUT输出给MCU的PA5。MCU不需要额外提供时钟。
方案B:MCU提供60MHz时钟给PHY。这种方案需要MCU的MCO输出60MHz,但F407的MCO最高只能输出HSE或PLL时钟,要精确60MHz比较麻烦,不推荐。
我选方案A。26MHz晶振的负载电容要根据晶振规格算,一般12pF~20pF。我用的晶振要求负载电容10pF,那外部匹配电容就是2*(10-Cstray),Cstray一般估3pF,所以每边约14pF,取15pF标准值。
3.2 60MHz ULPI时钟的抖动要求
ULPI时钟的抖动要求比较严,datasheet里写RMS抖动要小于500ps。普通晶振一般能满足,但如果用劣质晶振或者布线不好,抖动会超标,导致数据传输误码。
我实测过,用普通26MHz无源晶振,CLKOUT的抖动在200ps左右,没问题。但如果晶振的负载电容配错,频率偏了,PLL输出的60MHz就不准,USB枚举会时好时坏。
判断时钟是否正常,最直接的方法是用示波器测CLKOUT引脚,看频率是不是60MHz±500ppm,波形是不是干净的方波。如果波形畸变严重或者频率偏差大,先查晶振和负载电容。
3.3 时钟走线的注意事项
CLKOUT从PHY到MCU的PA5,这根线是60MHz,属于中速信号,走线要注意:
- 尽量短,最好控制在5cm以内
- 不要跨分割地平面
- 远离模拟信号和电源开关节点
- 如果实在要走长,考虑串联端接电阻(22Ω~33Ω)
我在第三版板子上把PHY放在MCU背面,CLKOUT走线只有1cm多,信号质量很好。第二版放在板子另一头,走线8cm,虽然也能工作,但眼图明显变差。
4. PCB布局布线的实战细节
4.1 差分对的阻抗控制
USB高速的差分对DP/DM,阻抗要求90Ω±10%。这个阻抗由走线宽度、间距、到参考平面的距离决定。用四层板的话,典型叠层是:
- 顶层:信号
- 第二层:地平面
- 第三层:电源平面
- 底层:信号
DP/DM走在顶层,参考第二层地平面。用阻抗计算工具(比如Saturn PCB Toolkit)算,线宽6mil、间距6mil、到地平面距离8mil左右,能到90Ω。具体数值要看板厂的叠层参数,投板前一定要和板厂确认。
我第一版没做阻抗控制,差分对随便走的,结果高速下误码率很高,枚举都过不去。第二版做了阻抗控制,但间距没控制好,两根线一会儿近一会儿远,阻抗不连续,还是有问题。第三版严格等间距走线,才彻底解决。
4.2 PHY芯片的电源去耦
USB3320的电源引脚去耦不能马虎。VDD33和VDDIO每个引脚都要有0.1uF电容,紧贴引脚放置。另外整个PHY的电源入口要加一个10uF的钽电容或者MLCC。
我习惯在PHY下面放一个4.7uF的MLCC,加上每个电源引脚的0.1uF,基本就够了。注意电容的接地端要就近打过孔到地平面,过孔不要共用,每个电容单独打。
4.3 晶振区域的布局
26MHz晶振要尽量靠近PHY的XI/XO引脚,走线短且对称。晶振下面不要走其他信号,最好挖空或者铺地。负载电容紧贴晶振放置,接地端就近打过孔。
有个经验:晶振外壳如果有接地焊盘,一定要接地,能屏蔽干扰。我用的3225封装晶振,外壳是金属的,四个角有接地焊盘,全部接地后,CLKOUT的抖动明显改善。
4.4 连接器和ESD保护
USB连接器进来先过ESD保护器件,再进PHY。ESD器件要选低电容的,一般小于1pF,否则会影响高速信号。我用的是USBLC6-2SC6,专门给USB用的,电容0.8pF,对信号影响很小。
ESD器件要放在连接器和PHY之间,尽量靠近连接器。走线要先经过ESD器件再到PHY,不能反过来。
5. 软件配置与枚举调试
5.1 STM32CubeMX里的USB配置
硬件调通后,软件配置相对简单。在CubeMX里:
- 使能USB_OTG_FS,模式选Device_Only或者OTG
- 在PHY选择里选ULPI
- 配置对应的GPIO为AF11
- 时钟树里确保USB时钟是48MHz
注意:F407的USB时钟来源是PLL的Q分频,要保证48MHz。如果主频168MHz,PLL配置要算好。比如HSE=8MHz,PLL_M=8,PLL_N=336,PLL_P=2,PLL_Q=7,这样USB时钟就是336/7=48MHz。
5.2 枚举失败的排查思路
枚举失败是最常见的问题,排查要按顺序来:
第一步:查PHY时钟。示波器测CLKOUT,有没有60MHz。没有的话查晶振、复位、电源。
第二步:查ULPI通信。如果时钟有但枚举不了,用逻辑分析仪抓ULPI总线,看MCU有没有发复位命令,PHY有没有回NXT。如果MCU发了但PHY没反应,查DIR/NXT/STP连线。
第三步:查差分信号。如果ULPI通信正常但PC端识别不到设备,查DP/DM的差分信号,看有没有上拉、有没有数据翻转。
第四步:查VBUS。有些PHY需要检测VBUS才工作,如果VBUS检测脚没接对,PHY不启动。
我第二版就是卡在第三步,ULPI通信正常,但PC端死活识别不到。后来用示波器看DP/DM,发现D+的上拉电阻没接,PHY以为总线没连接,根本不发数据。
5.3 高速模式下的稳定性优化
枚举通过只是第一步,高速模式下跑大流量才是考验。我遇到过跑几分钟就掉线的问题,最后查出来是电源纹波太大。PHY的3.3V电源如果纹波超过50mV,高速传输就会出错。
解决办法:在PHY电源入口加LC滤波,L用磁珠,C用10uF+0.1uF。另外检查地平面是否完整,PHY下面的地要实心,不要被其他信号割裂。
还有一点:ULPI的DATA总线在高速下翻转很快,如果走线太长或者有串扰,会误码。我一般把DATA总线走在同一层,等长控制在5mm以内,线间距至少3倍线宽。
6. 实测数据与经验总结
6.1 三版板子的对比
| 版本 | 主要问题 | 现象 | 解决 |
|---|---|---|---|
| 第一版 | 晶振用12MHz | PLL锁不住,无60MHz时钟 | 换26MHz晶振 |
| 第二版 | NXT/STP接反,D+上拉缺失 | 枚举失败 | 改线,加上拉 |
| 第三版 | 电源纹波大 | 高速跑几分钟掉线 | 加LC滤波 |
6.2 几个容易忽略的细节
D+上拉电阻。设备模式下,D+需要1.5kΩ上拉到3.3V,告诉主机这是全速/高速设备。有些PHY内部集成了这个上拉,可以通过寄存器控制,但USB3320默认不启用,需要外部接。我第二版就是漏了这个。
VBUS检测。如果做自供电设备,VBUS检测可以简化,但最好还是接上,通过分压电阻接到PHY的VBUS引脚。分压比要算对,USB3320的VBUS检测阈值是1.2V左右,5V输入的话分压到1.2V,用10k和3k左右。
ID引脚。不用OTG的话,ID引脚接地。悬空的话PHY可能进入不确定状态。
复位时序。前面说过,RESETB拉低至少1us,我一般给10ms。拉高后等PLL锁定再初始化,也是10ms。
6.3 测试工具和手段
调试USB高速,几个工具少不了:
- 示波器:带宽至少500MHz,测60MHz时钟和480Mbps差分信号
- 逻辑分析仪:抓ULPI总线,看协议交互
- USB协议分析仪:如果有条件,用专业的USB分析仪抓包最直接
- 阻抗测试仪:测差分对阻抗,确保90Ω
我用的是普通500MHz示波器加一个ULPI解码插件,基本够用。USB协议分析仪太贵,没买,靠逻辑分析仪和示波器也能定位大部分问题。
6.4 成本与选型建议
USB3320单颗价格在10~15元人民币左右,比USB3300略贵,但封装小、集成度高。如果项目对成本敏感,USB3300也能用,引脚基本兼容,就是封装大一点。
晶振用26MHz无源晶振,几毛钱。ESD器件用USBLC6,一块多。整体算下来,增加高速USB功能的BOM成本在15元左右,对于需要高速通信的项目,这个成本完全可以接受。
如果只是偶尔需要高速,也可以考虑用带高速USB的MCU,比如STM32F446或者STM32H7系列,内部集成了HS PHY,省去外挂芯片。但F407的生态和价格优势明显,外挂PHY的方案在很多项目里还是首选。
最后分享一个我调试时的小技巧:如果枚举不稳定,先别急着改硬件,用USB分析仪或者软件抓包看看枚举到哪一步失败。很多时候是描述符配置问题,不是硬件问题。我遇到过枚举失败查了半天硬件,最后发现是设备描述符里的bMaxPacketSize0设错了。硬件调通后,软件问题反而更容易排查。
另外,ULPI的PCB走线如果实在走不开,可以考虑用排线或者FPC连接,但长度要控制在10cm以内,且要做好阻抗控制。超过10cm,信号质量就很难保证了。