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个一组)。

菊花链的配置流程:

  1. 拉低CS,所有ADC进入数据模式
  2. FPGA发送N个16位数据(N为ADC数量),每个数据对应一个ADC的配置
  3. 数据在链中逐级传递,最后一个ADC的数据从MISO返回
  4. 拉高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的关键。

最后,给一个调试检查清单:

  1. 检查电源:ADC供电是否干净?纹波是否在允许范围内?
  2. 检查时钟:SCLK频率是否正确?占空比是否合格?
  3. 检查时序:CS建立时间、保持时间是否满足?
  4. 检查数据:MISO上的数据是否与预期一致?
  5. 检查地线:所有ADC和FPGA是否共地?地回路是否过大?

嗯,SPI看似简单,但要做好真不容易。尤其是CT探测器这种对数据可靠性要求极高的场景,每一个细节都不能放过。希望今天的分享能帮你少走一些弯路。