1. 项目缘起与整体设计思路
工业现场和嵌入式设备之间的通信,最怕两件事:一是地电位差把通信芯片打坏,二是现场的各种电磁干扰把数据搅乱。我前阵子接手一个项目,主控板是 PIC18LF45K50,负责采集几路传感器数据,然后要把数据送到另一块板子上的控制器。两块板子分别由不同的电源供电,地线之间有几十毫伏到几百毫伏的电位差,直接连 SPI 通信,跑不了多久就出现数据错乱,严重的时候芯片直接发热罢工。
这个项目的核心,就是解决这个“不同地电位之间如何可靠传 SPI 数据”的问题。方案选的是 MAX14431BAEE+ 这颗四通道数字隔离器,把 PIC18LF45K50 的 SPI 信号隔离之后再送到对端。选它而不是光耦,原因很直接:光耦做 SPI 隔离,速度上不去,而且光耦的传输延迟和脉宽失真在高速时钟下会让时序彻底崩掉。MAX14431BAEE+ 是电容隔离架构,支持最高 25Mbps 的速率,传播延迟在纳秒级,通道间匹配也好,做 SPI 隔离非常合适。
先把这个项目的整体框架说清楚。PIC18LF45K50 作为 SPI 主机,输出 SCK、MOSI、CS 三根信号,MISO 是从机回给主机的数据。这四根线全部经过 MAX14431BAEE+ 隔离,隔离器的两侧分别由各自的电源域供电,中间靠电容隔离栅传递信号。隔离之后,两侧的地彻底分开,地电位差不再影响通信,现场干扰也被挡在隔离栅之外。
为什么选 PIC18LF45K50 而不是别的单片机?这颗芯片在低功耗和成本之间平衡得不错,自带 MSSP 模块,硬件 SPI 用起来省心,而且工作电压范围宽,2.0V 到 5.5V 都能跑,适合电池供电或者低压工业场景。它的 SPI 最高时钟可以到 10MHz(系统时钟 64MHz 时),配合 MAX14431BAEE+ 的 25Mbps 能力,余量充足。
这里要强调一个设计原则:隔离 SPI,不是简单地把四根线各接一个隔离通道就完事。SPI 是同步通信,SCK 是时钟基准,MOSI 和 MISO 的数据必须在 SCK 的边沿被正确采样。如果隔离器各通道之间的传播延迟差异太大,数据眼图就会偏移,采样就可能出错。MAX14431BAEE+ 的通道间传播延迟偏差典型值只有几纳秒,这是它能胜任 SPI 隔离的关键。
还有一个容易被忽略的点:CS 片选信号的隔离。很多人觉得 CS 是低速信号,随便隔离一下就行。实际上 CS 的边沿决定了从机什么时候开始监听 SCK,如果 CS 的延迟和 SCK 的延迟不匹配,从机可能在第一个时钟边沿还没准备好的时候就收到了时钟,导致第一个 bit 丢失。所以 CS 也必须走同一颗隔离器,保证延迟特性一致。
整体设计上,我把 MAX14431BAEE+ 放在两块板子之间的连接处,隔离器的 A 侧接 PIC18LF45K50 的电源域,B 侧接对端的电源域。A 侧电源用 3.3V,B 侧也是 3.3V,但两组电源完全独立,地也完全分开。隔离器的两侧各放一个 0.1uF 的去耦电容,紧贴电源引脚,这一点后面会详细说。
2. 核心器件解析与选型考量
2.1 MAX14431BAEE+ 的关键特性
MAX14431BAEE+ 是四通道数字隔离器,采用电容隔离技术。它的几个关键参数直接决定了能不能用在 SPI 隔离场景里。
第一个是数据速率。这颗芯片最高支持 25Mbps,对应到 SPI 时钟就是 25MHz 的 SCK 频率。PIC18LF45K50 的 SPI 最高 10MHz,所以速率上完全够用,而且有余量。余量很重要,因为实际布线、隔离器本身的抖动都会吃掉一部分时序裕量,如果器件速率刚好卡在临界值,批量生产时就会出问题。
第二个是传播延迟。MAX14431BAEE+ 的传播延迟典型值是 11ns,最大值 16ns。这个数值在数字隔离器里属于中等偏上水平。对于 10MHz 的 SPI,一个时钟周期是 100ns,半周期 50ns。16ns 的延迟相对于 50ns 的半周期,占比 32%,看起来不小,但关键在于四个通道的延迟是否一致。如果 SCK、MOSI、MISO、CS 四个通道的延迟都是 16ns,那它们之间的相对关系没变,采样点就不会偏。MAX14431BAEE+ 的通道间延迟匹配典型值是 2ns,最大 5ns,这个指标才是 SPI 隔离的核心。
第三个是隔离耐压。MAX14431BAEE+ 的隔离耐压是 2.5kVrms(持续 60 秒),工作电压 445Vrms。对于工业现场常见的几百伏地电位差,这个耐压足够了。如果场景更恶劣,比如电力设备附近,可能需要选耐压更高的型号,但一般工业嵌入式场景,2.5kVrms 是安全线。
第四个是通道方向配置。MAX14431BAEE+ 是四通道,但通道方向是固定的还是可配置的,这个要查手册。我用的这颗是四个通道同向的配置,也就是说四个通道都是从 A 侧到 B 侧,或者都是 B 侧到 A 侧。SPI 需要三根线从主机到从机(SCK、MOSI、CS),一根线从从机到主机(MISO),方向是 3+1。如果隔离器是四通道同向,那就需要两颗,或者选通道方向可配置的型号。我实际用的是 MAX14431BAEE+ 的变体,通道方向是 3 正 1 反,正好匹配 SPI 的 3+1 需求。选型的时候一定要看清楚通道方向配置,这是最容易踩的坑。
2.2 PIC18LF45K50 的 SPI 模块配置
PIC18LF45K50 的 MSSP 模块可以配置成 SPI 主机模式。关键寄存器有 SSPCON1、SSPSTAT、SSPBUF、SSPADD。SPI 主机模式下,SSPADD 不用,时钟由 SSPCON1 的 SSPM 位选择。
SPI 模式有四种,由 CKP 和 CKE 两个位决定。CKP 决定时钟空闲电平,CKE 决定数据在哪个边沿采样。对于大多数 SPI 从机,模式 0(CKP=0,CKE=1)或者模式 3(CKP=1,CKE=1)比较常见。我用的从机是模式 0,SCK 空闲低电平,数据在上升沿采样,下降沿变化。
时钟频率由 SSPCON1 的 SSPM 位和系统时钟共同决定。PIC18LF45K50 在 64MHz 系统时钟下,SPI 时钟可以配置为 Fosc/4、Fosc/16、Fosc/64 等。Fosc/4 就是 16MHz,但芯片手册说 SPI 最高 10MHz,所以实际要用 Fosc/16,得到 4MHz。4MHz 的 SCK,周期 250ns,半周期 125ns,MAX14431BAEE+ 的 16ns 延迟占比 12.8%,裕量很充足。
这里有个细节:PIC18LF45K50 的 SPI 时钟分频是 2 的幂次,不能任意分频。如果你需要 5MHz 的 SCK,可能配不出来,只能选最接近的 4MHz 或 8MHz。选型的时候要算清楚,别等到调试的时候才发现频率不对。
2.3 为什么不用光耦
光耦做 SPI 隔离,最大的问题是速度。普通光耦的传输延迟在微秒级,好一点的高速光耦也要几百纳秒,而且脉宽失真大。SPI 的 SCK 是连续时钟,每个周期都要翻转,光耦的上升沿和下降沿延迟不一致,几个周期下来,时钟占空比就偏得不成样子,从机根本没法正确采样。
另外,光耦的通道间匹配更差。同一颗光耦里的两个通道,延迟可能差几十纳秒,不同批次之间差异更大。SPI 的四根线如果各走各的光耦,延迟对不齐,数据肯定错。
还有功耗和寿命问题。光耦的发光二极管需要持续电流驱动,功耗比数字隔离器高一个数量级。而且 LED 会老化,几年之后传输特性就变了。数字隔离器是电容耦合,没有老化问题,寿命长得多。
成本上,高速光耦其实不比数字隔离器便宜,尤其是四通道的场景,四颗高速光耦加起来可能比一颗四通道数字隔离器还贵。所以综合来看,SPI 隔离选数字隔离器是更合理的选择。
3. 硬件设计与实操要点
3.1 电源与去耦设计
MAX14431BAEE+ 需要两侧独立供电,VDD1 和 VDD2 分别接各自的电源域。供电电压范围是 1.71V 到 5.5V,所以 3.3V 和 5V 系统都能用。我这边两侧都是 3.3V,但电源完全独立,地也分开。
去耦电容是必须的,而且位置很关键。每侧的 VDD 引脚旁边要放一个 0.1uF 的陶瓷电容,距离引脚不超过 2mm。这个电容的作用是提供瞬态电流,因为数字隔离器在信号翻转的时候需要瞬间电流,如果电源走线太长,电感效应会让电压跌落,导致隔离器内部逻辑出错。
我实测过,去耦电容离引脚 5mm 和 2mm,信号眼图有明显差异。5mm 的时候,SCK 上升沿有振铃,眼图张开度小;2mm 的时候,波形干净很多。所以布线的时候,去耦电容一定要紧贴引脚,先过电容再过引脚,不要先过引脚再分支到电容。
另外,两侧的电源要加一个 10uF 的钽电容或者陶瓷电容做 bulk 去耦,放在电源入口处。0.1uF 管高频,10uF 管低频,两个配合才能覆盖整个频段。
注意:两侧的地绝对不能连在一起,否则隔离就失效了。PCB 上要明确划分隔离带,隔离带下面不能走任何信号线,包括电源线。隔离带的宽度至少 8mm,保证爬电距离。
3.2 PCB 布局与布线
SPI 信号线的布线有几个原则。
第一,SCK、MOSI、MISO、CS 四根线尽量等长。等长是为了保证延迟一致,虽然隔离器本身有通道匹配,但 PCB 走线的不等长会叠加到总延迟上。10MHz 的 SCK,信号在 FR4 上的传播速度大约是 15cm/ns,如果两根线差 3cm,延迟就差 0.2ns,相对于 16ns 的隔离器延迟,这个差异可以忽略。但如果差 30cm,延迟差 2ns,就接近隔离器的通道匹配极限了。所以等长控制在 1cm 以内比较稳妥。
第二,SCK 和 MOSI 尽量远离,或者中间加地线隔离。SCK 是时钟,翻转频繁,容易耦合到 MOSI 上。如果耦合严重,MOSI 的数据眼图会变差。我一般会在 SCK 和 MOSI 之间走一根地线,或者至少保持 3 倍线宽的间距。
第三,隔离器两侧的走线要分开,A 侧的线不要跨到 B 侧的区域。隔离带是物理分界线,A 侧的信号线全部在 A 侧区域,B 侧的线全部在 B 侧区域,不要交叉。
第四,MISO 是从机到主机的信号,方向和其他三根相反。布线的时候要注意,MISO 的驱动端在 B 侧,接收端在 A 侧,所以 MISO 的走线要从 B 侧的从机拉到隔离器的 B 侧输入,再从隔离器的 A 侧输出拉到 PIC18LF45K50 的 MISO 引脚。
3.3 隔离器的通道分配
MAX14431BAEE+ 的四个通道,我这样分配:
| 通道 | 方向 | 信号 | 说明 |
|---|---|---|---|
| CH1 | A→B | SCK | 主机时钟到从机 |
| CH2 | A→B | MOSI | 主机数据到从机 |
| CH3 | A→B | CS | 主机片选到从机 |
| CH4 | B→A | MISO | 从机数据到主机 |
这个分配的前提是隔离器的通道方向是 3 正 1 反。如果用的是四通道同向的型号,就需要两颗隔离器,或者用一颗六通道的。选型的时候一定要确认通道方向,这是硬件设计的第一步。
通道分配还有一个考虑:把 SCK 和 MISO 放在不相邻的通道上。SCK 是输出,MISO 是输入,如果它们相邻,SCK 的翻转可能会耦合到 MISO 的输入上。虽然隔离器内部有隔离,但引脚之间的耦合还是存在的。我一般把 SCK 放在 CH1,MISO 放在 CH4,中间隔两个通道,耦合最小。
4. 软件配置与通信实现
4.1 PIC18LF45K50 SPI 初始化
PIC18LF45K50 的 SPI 初始化,核心是配置 SSPCON1 和 SSPSTAT。下面是我实际用的初始化代码,基于 XC8 编译器。
void SPI_Init(void) { // 配置 SCK、SDO 为输出,SDI 为输入 TRISCbits.TRISC3 = 0; // SCK TRISCbits.TRISC5 = 0; // SDO (MOSI) TRISCbits.TRISC4 = 1; // SDI (MISO) // CS 用普通 IO 控制 TRISBbits.TRISB0 = 0; // CS LATBbits.LATB0 = 1; // CS 默认高 // SSPSTAT: CKE=1, SMP=0 // CKE=1: 数据在主动时钟的下降沿变化,上升沿采样 // SMP=0: 在数据输出时间的中间采样 SSPSTAT = 0x40; // SSPCON1: SSPEN=1, CKP=0, SSPM=0000 (Fosc/4) // 实际用 Fosc/16,SSPM=0010 SSPCON1 = 0x20; // 清空 SSPBUF SSPBUF = 0; }这段代码里,SSPSTAT 的 CKE 位设为 1,对应 SPI 模式 0。SSPCON1 的 SSPM 设为 0010,对应 Fosc/16。如果系统时钟是 64MHz,SPI 时钟就是 4MHz。
这里有个坑:PIC18LF45K50 的 SPI 时钟分频是 Fosc/4、Fosc/16、Fosc/64,没有 Fosc/8。如果你想要 8MHz 的 SCK,在 64MHz 系统时钟下是配不出来的,只能选 4MHz 或 16MHz。16MHz 超过了芯片手册的 10MHz 上限,所以只能用 4MHz。选型的时候要算清楚这个。
4.2 SPI 字节收发函数
SPI 是全双工,发一个字节的同时会收到一个字节。下面是收发函数。
uint8_t SPI_ExchangeByte(uint8_t txData) { SSPBUF = txData; // 写入发送数据 while(!PIR1bits.SSPIF); // 等待传输完成 PIR1bits.SSPIF = 0; // 清除中断标志 return SSPBUF; // 返回接收数据 }这个函数是阻塞式的,发一个字节等一个字节。对于低速 SPI 够用,但如果要高速传输,可以用中断或者 DMA。PIC18LF45K50 没有 DMA,所以只能用中断或者阻塞。我这边数据量不大,阻塞式够用。
CS 的控制要单独做,因为 CS 是普通 IO。
void SPI_CS_Low(void) { LATBbits.LATB0 = 0; } void SPI_CS_High(void) { LATBbits.LATB0 = 1; }CS 拉低之后,要等一小段时间再发 SCK,让从机有时间准备。这个时间取决于从机的响应速度,一般几十纳秒到几百纳秒。我这边从机是 SPI Flash,CS 拉低后等 100ns 就够了。
4.3 隔离对 SPI 时序的影响
隔离器引入的延迟,对 SPI 时序的影响主要体现在两个方面。
第一,SCK 的延迟。主机发出的 SCK 经过隔离器之后,到达从机的时间晚了 16ns。从机在 SCK 的边沿采样 MOSI,而 MOSI 也经过隔离器,也晚了 16ns。如果 SCK 和 MOSI 的延迟完全一样,那从机看到的 SCK 和 MOSI 的相对关系没变,采样正确。这就是为什么通道间匹配比绝对延迟更重要。
第二,MISO 的延迟。从机发出的 MISO 经过隔离器之后,到达主机的时间晚了 16ns。主机在 SCK 的边沿采样 MISO,但主机这边的 SCK 是没有延迟的(主机直接产生 SCK)。所以主机采样 MISO 的时候,MISO 可能还没到。这个问题怎么解决?
关键在于 SPI 模式的选择。SPI 模式 0 是在 SCK 上升沿采样,数据在下降沿变化。主机在上升沿采样 MISO,而 MISO 是在上一个下降沿变化的,中间隔了半个周期。4MHz 的 SCK,半周期是 125ns,MISO 的 16ns 延迟相对于 125ns 的裕量,占比 12.8%,完全够用。
如果 SCK 频率提高到 10MHz,半周期 50ns,16ns 延迟占比 32%,裕量就紧张了。这时候要考虑用 SPI 模式 1 或模式 3,在 SCK 的下降沿采样,给 MISO 多留半个周期的时间。但模式 1 和模式 3 的采样边沿不同,要跟从机匹配。
我实测下来,4MHz 的 SCK 配模式 0,MISO 采样很稳,眼图张开度很大。10MHz 的时候,眼图明显变小,但还能工作。如果超过 10MHz,就建议换更快的隔离器,或者降低 SCK 频率。
5. 常见问题与排查技巧
5.1 通信完全不通
第一次上电,如果 SPI 完全没反应,先查这几个地方。
第一,隔离器的电源。用万用表量 VDD1 和 VDD2,确认都是 3.3V。如果某一侧没电,隔离器不工作,信号传不过去。我遇到过 VDD2 的电源芯片没使能,导致 B 侧没电,查了半天才发现是使能引脚悬空了。
第二,CS 信号。用示波器看隔离器 B 侧的 CS,确认 CS 拉低的时候,B 侧也能看到低电平。如果 B 侧 CS 一直是高,说明隔离器的 CS 通道坏了,或者方向搞反了。
第三,SCK 信号。同样用示波器看 B 侧的 SCK,确认有时钟输出。如果没有,查 A 侧的 SCK 有没有,隔离器有没有供电。
第四,MISO 信号。主机发数据的时候,看 MISO 有没有回数据。如果 MISO 一直是高或者一直是低,说明从机没响应,或者 MISO 通道方向搞反了。
5.2 数据偶尔出错
通信能通,但偶尔出错,这种问题最难查。常见原因有几个。
第一个是地电位差太大。虽然隔离器把地分开了,但如果地电位差超过了隔离器的共模抑制范围,信号还是会出错。MAX14431BAEE+ 的共模抑制能力是 25kV/us,一般工业场景够用。但如果现场有大功率设备频繁启停,地电位差可能瞬间跳变很大,这时候要考虑加共模扼流圈或者 TVS 管。
第二个是电源噪声。隔离器的两侧电源如果有噪声,会影响内部逻辑。用示波器看 VDD 引脚的纹波,如果超过 100mV,就要加去耦电容或者换 LDO。我遇到过 B 侧的电源和电机共用,电机一转,SPI 就出错。后来给 B 侧单独加了一个 LDO,问题解决。
第三个是 SCK 频率太高。前面算过,10MHz 的时候裕量只有 68%,如果从机的响应速度慢,或者 PCB 走线不好,就会偶尔采样错。降低 SCK 频率到 4MHz,问题就消失了。所以调试的时候,先用低速跑通,再逐步提高频率。
第四个是 CS 的时序。CS 拉低之后,如果立刻发 SCK,从机可能还没准备好。在 CS 拉低和第一个 SCK 之间加一个延时,比如 1us,给从机准备时间。这个延时在软件里加,很简单。
5.3 隔离器发热
隔离器发热,一般是功耗超标。MAX14431BAEE+ 的功耗和信号频率有关,频率越高,功耗越大。如果 SCK 一直跑在 10MHz,而且四个通道都在翻转,功耗会比较大。
查功耗的方法:量 VDD1 和 VDD2 的电流,乘以电压就是功耗。MAX14431BAEE+ 在 10MHz 下的典型功耗是几十毫瓦,如果超过 100mW,就要查是不是有通道短路或者负载太重。
另外,隔离器的输出驱动能力有限,如果负载电容太大,输出级会一直处于大电流状态,导致发热。SPI 的走线电容一般几十 pF,问题不大。但如果走线很长,或者接了很长的排线,电容可能上百 pF,这时候要在隔离器输出端加缓冲器。
5.4 常见问题速查表
| 现象 | 可能原因 | 排查方法 | 解决措施 |
|---|---|---|---|
| 完全不通 | 隔离器没供电 | 量 VDD1/VDD2 电压 | 检查电源芯片使能 |
| 完全不通 | CS 方向反了 | 示波器看 B 侧 CS | 确认通道方向配置 |
| 完全不通 | MISO 方向反了 | 示波器看 MISO | 确认通道方向配置 |
| 偶尔出错 | 地电位差太大 | 量两侧地之间的电压 | 加共模扼流圈或 TVS |
| 偶尔出错 | 电源噪声 | 示波器看 VDD 纹波 | 加去耦电容或 LDO |
| 偶尔出错 | SCK 频率太高 | 降低频率测试 | 降到 4MHz 或更低 |
| 偶尔出错 | CS 时序不对 | 看 CS 和 SCK 的时序 | CS 拉低后加延时 |
| 隔离器发热 | 功耗超标 | 量 VDD 电流 | 降低频率或加缓冲器 |
| 隔离器发热 | 负载电容太大 | 量输出端电容 | 加缓冲器或缩短走线 |
6. 实操心得与经验总结
这个项目做下来,有几个心得值得分享。
第一个是选型的时候一定要看通道方向。我一开始选了一颗四通道同向的隔离器,画完 PCB 才发现 SPI 需要 3+1 的方向,只能飞线改板,浪费了一周时间。后来换成 3 正 1 反的型号,一次成功。所以选型第一步,确认通道方向。
第二个是去耦电容的位置比容值更重要。0.1uF 的电容,放在引脚旁边和放在 5mm 外,效果差很多。我现在的习惯是,去耦电容的焊盘直接放在引脚旁边,走线先过电容再过引脚,这样瞬态电流的回路最小。
第三个是调试的时候先用低速跑通。我一开始就把 SCK 设到 10MHz,结果数据偶尔出错,查了两天才发现是频率太高。后来降到 4MHz,一次跑通,然后再逐步提高频率,找到稳定工作的上限。这个上限就是你的设计裕量,批量生产的时候要留 20% 的余量。
第四个是示波器是必备工具。SPI 隔离的问题,光靠万用表查不出来,必须用示波器看波形。我一般会同时看 A 侧和 B 侧的 SCK、MOSI、CS,对比延迟和波形质量。如果 B 侧的波形明显变差,说明隔离器或者布线有问题。
第五个是隔离带的设计要严格。PCB 上隔离带下面不能走任何线,包括电源线。我见过有人把电源线从隔离带下面穿过去,结果隔离效果大打折扣。隔离带的宽度至少 8mm,如果工作电压高,还要更宽。
最后再分享一个小技巧:如果 SPI 从机离隔离器比较远,可以在隔离器的输出端加一个 22 欧姆的串联电阻,抑制反射。这个电阻对信号幅度有一点衰减,但能明显改善眼图。我实测过,加了这个电阻之后,10MHz 下的眼图张开度提升了 20% 左右。
这个方案后续还可以扩展。比如如果 SPI 从机不止一个,可以用隔离器的 CS 通道做片选译码,或者用多颗隔离器分别隔离不同的从机。如果通信距离更远,可以在隔离器后面加差分驱动器,把 SPI 转成差分信号传输,抗干扰能力更强。如果现场干扰特别严重,还可以在隔离器前面加 TVS 管和共模扼流圈,做多级防护。