4、SPI总线通信:SPI协议基础、多从机级联配置、高速SPI设计要点与调试
SPI,全称是Serial Peripheral Interface。说白了,就是串行外设接口。在CT探测器数据采集系统里,它几乎是连接ADC和FPGA的标配。为什么?因为它简单、快速、可靠。我做了这么多年CT硬件,SPI用得最多,踩的坑也最多。今天咱们就好好聊聊这个。
4.1 SPI协议基础:四根线的事儿
SPI是同步、全双工的通信协议。主设备(通常是FPGA或MCU)控制时钟,从设备(比如ADC)响应数据。一共就四根线:
- SCLK:串行时钟,由主设备产生
- MOSI:主出从入,数据从主设备流向从设备
- MISO:主入从出,数据从从设备流向主设备
- CS:片选信号,低电平有效,选中哪个从设备就拉低哪个
嗯,这里要注意:SPI没有标准的应答机制。你发一个字节,从设备同时回一个字节。所以如果你只关心发送,接收到的数据可以忽略。反过来也一样。
核心要点:SPI的四种模式由时钟极性(CPOL)和时钟相位(CPHA)决定。CPOL决定空闲时时钟电平,CPHA决定数据采样沿。CT探测器里,ADC通常工作在模式0(CPOL=0, CPHA=0)或模式3(CPOL=1, CPHA=1)。我个人习惯用模式0,因为大多数ADC默认就是这个。
举个例子,一个典型的SPI读操作时序:
// 伪代码:FPGA读取ADC转换结果
CS = 0; // 选中ADC
for (i = 0; i < 16; i++) { // 16位数据
SCLK = 0;
MOSI = cmd_bit[i]; // 发送命令位
delay();
SCLK = 1;
data_bit[i] = MISO; // 读取数据位
delay();
}
CS = 1; // 释放片选
我在项目中遇到过一个问题:ADC的CS信号拉低后,需要等待一小段时间才能开始发时钟。这个时间叫tCSSU(片选建立时间)。很多芯片手册里写的是ns级别,但实际调试时我发现,如果时钟跑得很快,这个时间很容易被忽略。结果就是第一个数据位读错了。
4.2 多从机级联配置:菊花链与独立片选
CT探测器里,一个FPGA往往要管理几十甚至上百个ADC通道。怎么连?两种主流方案:
方案一:独立片选
每个ADC都有自己的CS线。FPGA通过GPIO分别控制。优点是时序独立,互不干扰。缺点是CS线太多,FPGA引脚不够用。我记得有一次做128通道的探测器板,光CS线就占了128个引脚,最后不得不换更大封装的FPGA。
方案二:菊花链(Daisy Chain)
所有ADC共用SCLK和CS,但数据线串起来。ADC1的MISO接ADC2的MOSI,ADC2的MISO接ADC3的MOSI,以此类推。最后一个ADC的MISO接回FPGA。这样只需要一根CS、一根MISO、一根MOSI。
我的经验:菊花链适合数据量不大、通道数多的场景。但要注意,链越长,延迟越大。我曾经在一条链上挂了16个ADC,结果时钟频率只能降到5MHz才能稳定工作。如果你需要高速采样,建议用独立片选,或者分组菊花链(比如4个一组)。
菊花链的配置流程:
- 拉低CS,所有ADC进入数据模式
- FPGA发送N个16位数据(N为ADC数量),每个数据对应一个ADC的配置
- 数据在链中逐级传递,最后一个ADC的数据从MISO返回
- 拉高CS,所有ADC锁存配置
// 菊花链配置示例:配置4个ADC
#define ADC_NUM 4
uint16_t config[ADC_NUM] = {0x8001, 0x8002, 0x8003, 0x8004};
CS = 0;
for (int i = 0; i < ADC_NUM; i++) {
for (int j = 15; j >= 0; j--) {
SCLK = 0;
MOSI = (config[i] >> j) & 0x01;
delay();
SCLK = 1;
delay();
}
}
CS = 1;
4.3 高速SPI设计要点:别让时钟成为瓶颈
CT探测器的采样率越来越高,SPI时钟也越跑越快。从最初的10MHz,到现在的50MHz甚至100MHz。高速SPI设计,说白了就是信号完整性设计。我总结了几条铁律:
| 要点 | 说明 | 我的建议 |
|---|---|---|
| 阻抗匹配 | SCLK走线需要控制特性阻抗,通常50Ω | 串一个22Ω或33Ω电阻,靠近源端 |
| 走线长度 | SCLK和MISO/MOSI走线尽量等长 | 长度差不超过时钟上升时间的1/10 |
| 地平面 | SPI信号下方必须有完整地平面 | 不要跨分割,否则噪声会很大 |
| 去耦电容 | 每个ADC的电源引脚就近放0.1μF电容 | 我习惯再加一个1μF的钽电容 |
警告:高速SPI最怕的是信号反射。我曾经调试一块板子,50MHz时钟下MISO数据总是错位。用示波器一看,SCLK信号上有明显的过冲和振铃。后来在SCLK上串了一个47Ω电阻,问题立刻解决。记住:串阻不是万能的,但不串阻是万万不能的。
还有一个容易被忽略的点:SPI的时钟占空比。理想情况是50%,但实际中由于驱动能力差异,占空比可能偏移。如果偏移太大,数据建立时间就不够了。我一般要求占空比在45%~55%之间,超出这个范围就要调整驱动强度或加缓冲器。
4.4 调试技巧:从波形到数据
调试SPI,示波器是必备工具。但光看波形还不够,你得会分析。我分享几个实战技巧:
- 看眼图:用示波器的余晖模式看SCLK和MISO的眼图。眼睛张得越大,信号质量越好。如果眼睛闭合了,说明时序裕量不够。
- 测建立时间:在SCLK的采样沿(上升沿或下降沿)之前,MISO数据必须稳定。用示波器的光标测量这个时间,必须大于芯片手册要求的tSU(建立时间)。
- 抓异常:用逻辑分析仪长时间抓取SPI数据,看有没有丢帧或错位。我曾经发现一个ADC每隔1000次采样就会丢一个字节,后来发现是CS信号有毛刺,导致ADC误复位。
避坑指南:我曾经调试一个高速SPI链路,数据总是随机出错。折腾了两天,最后发现是FPGA的IO bank电压和ADC的IO电压不匹配。FPGA是3.3V,ADC是1.8V,电平转换芯片的速率跟不上。换了一个高速电平转换芯片后,问题消失。所以,电平匹配也是高速SPI的关键。
最后,给一个调试检查清单:
- 检查电源:ADC供电是否干净?纹波是否在允许范围内?
- 检查时钟:SCLK频率是否正确?占空比是否合格?
- 检查时序:CS建立时间、保持时间是否满足?
- 检查数据:MISO上的数据是否与预期一致?
- 检查地线:所有ADC和FPGA是否共地?地回路是否过大?
嗯,SPI看似简单,但要做好真不容易。尤其是CT探测器这种对数据可靠性要求极高的场景,每一个细节都不能放过。希望今天的分享能帮你少走一些弯路。