大家好,我是老张。上一章咱们把电源设计聊透了,板子供电稳了,MCU也跑起来了。接下来该让芯片和芯片之间“说话”(通信)了。
很多做嵌入式软件的兄弟觉得,通信嘛,不就是TX接RX、SCL接SCL,线连上代码写对就行了。可真到调试的时候,串口偶尔乱几个字节、I2C跑着跑着就挂死、SPI一上高速数据就出错、CAN总线换个终端电阻就不行……这些问题你如果只会从软件里调时序、加延时,能把你调到怀疑人生。
因为这些问题的根,一大半不在代码,在物理层——在电平、在走线、在终端电阻、在总线拓扑。这一章老张就带你逐一拆解四种最常用的嵌入式通信接口:UART、I2C、SPI、CAN,把它们的硬件特性和常见坑一次性说清楚。
目录
一、UART——最老牌的串口,坑一点不少
1.1 UART的硬件本质
1.2 TTL串口、RS232、RS485——别混了
1.3 波特率误差与采样时钟——时钟不准,通信必乱
1.4 长线传输的振铃与反射
二、I2C——两根线打天下,但时序是硬伤
2.1 I2C的硬件拓扑
2.2 上拉电阻的选择——不是随便一个电阻就能用
2.3 I2C的死锁问题——总线挂死了怎么办?
2.4 电平转换与总线隔离
三、SPI——高速是优势,高速也是麻烦
3.1 SPI的硬件拓扑
3.2 信号完整性问题——SPI不是线连上就能跑
3.3 多从设备的管理——CS引脚的坑
3.4 长线SPI怎么办?
四、CAN总线——工业通信的扛把子
4.1 CAN的硬件本质
4.2 终端电阻——没有它总线就废了
4.3 总线拓扑与走线
4.4 共模电压范围与隔离
五、四种接口硬核对比
六、本章总结
一、UART——最老牌的串口,坑一点不少
1.1 UART的硬件本质
UART(通用异步收发传输器)是嵌入式世界最简单、最古老的通信方式。两根线:TX(发送)接对方RX(接收),RX接对方TX,再加一根地线。异步通信,没有时钟线,双方约定好波特率,各自按自己的时钟采样。

硬件上的关键点就两个:电平标准和信号质量。
1.2 TTL串口、RS232、RS485——别混了
这是新手最容易搞混的概念。UART是协议,TTL/RS232/RS485是物理层标准,决定信号用什么样的电压来表示0和1。
TTL串口:就是MCU引脚直接出来的3.3V或5V逻辑电平。高电平是逻辑1,低电平是逻辑0。传输距离一般不超过一两米,板间通信够用。直接怼就行,成本为零。
RS232:把TTL电平转换成±12V左右的差分信号(对地),逻辑1是-3V到-15V,逻辑0是+3V到+15V。为什么搞这么高电压?为了抗干扰和长距离,最远能传15米左右。老式电脑后面的DB9串口就是RS232。嵌入式里想用RS232,加一颗MAX3232之类的电平转换芯片就行,注意外围电容值按芯片手册配,配错通信会出错。
RS485:工业上用得最多。它是差分信号,两根线A和B,A比B高0.2V以上是逻辑1,B比A高0.2V以上是逻辑0。因为是差分信号,对共模干扰天然免疫,传输距离能到千米级别,还能在一对线上挂多个设备(多点通信)。嵌入式里加一颗MAX485/SP3485芯片就能用。
翻车点:有兄弟直接用MCU的TTL串口拉出5米长线去通信,结果数据乱飞。TTL是单端信号,对地参考,长线上感应的共模噪声直接叠加在信号上,根本没抵抗力。长距离通信请自觉上RS485或者RS232,别用TTL硬扛。
1.3 波特率误差与采样时钟——时钟不准,通信必乱
UART接收端用自己的时钟去采样数据线,如果收发双方的时钟偏差太大,采样点就会逐渐漂移,最终采错位。
大多数MCU的UART用16倍过采样,即在每个数据位的中间位置采样。整帧的采样容差大约是±5%左右。所以双方波特率偏差不能太大。
两个常见坑:
-
内部RC振荡器跑串口:内部RC精度只有1%左右,常温还勉强,温度一变就可能偏到3%以上,两个设备一个偏正一个偏负,加起来就超过5%了,通信必出错。工业产品跑串口,老老实实上外部晶振。
-
非标准波特率:有些特殊外设要求非标波特率,但你的MCU的UART分频器可能产生不了准确值,实际波特率偏差过大。设计前先算一下分频误差,超过2%就要小心。
1.4 长线传输的振铃与反射
TTL信号在长线上传输,如果线缆特性阻抗不匹配,信号会在端点反射,产生“振铃”——在信号边沿后面跟着一串衰减的振荡。这个振荡如果幅度够大,可能被接收端误判为额外的数据脉冲。
解决办法:在信号线始端或终端串一个几十Ω的小电阻(比如22Ω~100Ω)吸收反射能量,或者在接收端对地并一个小电容滤除毛刺。当然最好的办法还是:别用TTL跑长线,换RS485。
二、I2C——两根线打天下,但时序是硬伤
2.1 I2C的硬件拓扑
I2C是飞利浦搞出来的双线串行总线:SCL(时钟)和SDA(数据),两条线都通过上拉电阻拉到VDD,所有设备开漏输出接在上面,谁都可以拉低,但谁都不能主动拉高,释放后靠上拉电阻恢复高电平。

这根总线可以挂多个主设备和多个从设备,每个从设备有唯一的7位或10位地址。
2.2 上拉电阻的选择——不是随便一个电阻就能用
上一章提过一嘴,这里展开说透。I2C的上拉阻值直接决定了通信的可靠性和速度。
阻值太小:总线拉低时,上拉电阻会流过电流 VDD/R。3.3V/1kΩ=3.3mA,单个器件可能承受得住,但如果总线上有多个器件同时拉低,或者器件驱动能力弱,低电平就拉不实。低电平不够低(比如高于0.4V),接收端可能认不到逻辑0。
阻值太大:总线释放后,上拉电阻给总线寄生电容充电,上升沿会变缓。I2C规范要求上升时间在标准模式(100kHz)小于1000ns,快速模式(400kHz)小于300ns。用10kΩ上拉,总线电容100pF,RC时间常数1μs,上升沿超标,数据采样会出错。
实战选值:我用得最多的是3.3kΩ~4.7kΩ。100kHz随便跑,400kHz也从容。总线上设备多、线缆长、寄生电容大时,选小一点;追求低功耗时,选大一点降速用。
另外,上升沿太慢还会导致从机误触发。如果上升沿慢吞吞地爬过逻辑门限区域,线上的一点小干扰就可能产生一个毛刺,从机会把这个毛刺当成额外的时钟脉冲,导致状态机错乱,总线死锁。
2.3 I2C的死锁问题——总线挂死了怎么办?
I2C有一个经典故障:主设备正在读数据,突然一个干扰或复位,导致从设备还在等着输出下一个数据位,死死地把SDA拉低不释放,主设备想发停止条件却发现SDA一直被从设备拉着,总线彻底卡死。
解决方法:软件上,主设备检测到SDA被长时间拉低,可以手动翻转SCL,产生额外的时钟脉冲,让从设备把剩下的数据位“吐完”,释放SDA。硬件上,在SDA和SCL上各对地并一个几pF到十几pF的小电容,可以吸收毛刺减少误触发。
还有一种死锁场景:上电时序问题。如果MCU上电速度比外设快,MCU已经在操作I2C了,外设还没初始化完,外设可能会把SDA拉住。可以在MCU启动代码里加一段延时,等外设准备好。
2.4 电平转换与总线隔离
3.3V主控挂5V外设,或者3.3V主控挂1.8V传感器,就需要电平转换。I2C电平转换最经典的方案是老张在第3章讲过的双MOS管方案(两个N沟道MOS管加四个上拉电阻),成本几毛钱,双向自动转换,稳定可靠。
长距离I2C(比如超过一米)是作死行为。如果一定要远距离,上I2C缓冲器/延长器芯片(如PCA9515、P82B715),或者干脆换CAN/RS485。
三、SPI——高速是优势,高速也是麻烦
3.1 SPI的硬件拓扑
SPI是四线全双工总线:SCK(时钟)、MOSI(主出从入)、MISO(主入从出)、CS(片选)。主设备产生时钟,从设备响应。速度轻松上10MHz,快的能到50MHz以上。

硬件上看着简单,但高速就带来了高速的麻烦。
3.2 信号完整性问题——SPI不是线连上就能跑
SPI跑低速(几MHz以下),杜邦线随便飞都没问题。一上10MHz,信号质量问题全冒出来了。
时钟反射与振铃:SCK是高频方波,如果走线长且没有阻抗匹配,信号边沿会产生严重的振铃。从设备可能把一个振铃的毛刺当成额外的时钟沿,数据就被多移了一位,全乱套。
数据与时钟的偏移:MOSI和MISO的数据需要在SCK的采样沿之前稳定下来。高速时如果数据线走线比时钟线长太多,或者经过的过孔数不一样,数据到达的时间就会偏移,导致建立/保持时间不够,采样出错。做高速SPI的PCB,SCK、MOSI、MISO三根线最好做等长处理,走线差距控制在几十mil内。
串扰:SPI几根线并排走,一根线上的高速跳变会通过寄生电容耦合到旁边的线上。如果CS线被SCK串扰干扰,可能产生假动作,从设备莫名其妙被选中或释放。高速SPI走线时,线间距尽量拉大(至少2倍线宽),中间用地线隔离更好。
3.3 多从设备的管理——CS引脚的坑
SPI挂多个从设备,主控用不同的CS片选引脚来选中对应的从设备。看似简单,但有两个坑:
-
CS悬空:未被选中的从设备的CS引脚必须被拉到确定的高电平。如果悬空,干扰会让它随机被选中,跟正在通信的那个从设备在MISO上打架。给每个CS脚加一个10kΩ上拉电阻,选中时拉低,不选时上拉保持高,简单可靠。
-
MISO冲突:多个从设备的MISO都连在一起接主设备的MISO。如果两个从设备同时被选中(比如CS线被干扰),它们会同时在MISO上驱动输出,造成总线冲突,严重时烧IO口。所以CS的上拉和去干扰非常重要。
3.4 长线SPI怎么办?
SPI设计上就是板内通信,不适合拉长线。如果必须拉远(比如半米以上),几个保命招:
-
降速,降到1MHz以下甚至几百kHz。
-
串阻尼电阻,在SCK和MOSI/MISO的源端各串一个22Ω~100Ω的小电阻,吸收反射。
-
用差分转换芯片,把SPI信号转成LVDS差分信号传输,接收端再转回来。比如用SN65LVDS系列。
四、CAN总线——工业通信的扛把子
4.1 CAN的硬件本质
CAN(控制器局域网)是博世搞的差分总线,专为恶劣环境设计。两根线CAN_H和CAN_L,隐性电平(逻辑1)时两根线电压都在2.5V左右,差分电压接近0V;显性电平(逻辑0)时CAN_H约3.5V、CAN_L约1.5V,差分电压约2V。
因为是差分信号,对共模干扰天生免疫;因为用差分电压判0/1而不是绝对电压,抗干扰能力极强。嵌入式里加一颗CAN收发器(如TJA1050、SN65HVD230)就能用。
4.2 终端电阻——没有它总线就废了
CAN总线的两端必须各接一个120Ω的终端电阻,并联后总线等效阻抗60Ω。这个终端电阻的作用是吸收信号反射。
CAN信号在总线上是以电磁波形式传播的,传到总线端点时如果没有匹配的负载,信号会反射回来,跟正常信号叠加,造成振铃和误码。120Ω刚好匹配双绞线电缆的特性阻抗。
翻车实录:有次我调两块CAN通信,偶尔能通偶尔不通,数据错误率奇高。查代码查半天,最后发现是只焊了一端的终端电阻,忘记另一端也要焊。补上后瞬间稳定。CAN总线一定要两端各120Ω,缺一不可。
4.3 总线拓扑与走线
CAN要求总线拓扑是直线型,一根主线从头到尾,设备通过短支线挂在主线上。支线越短越好,一般不超过30cm(高速CAN)。不要搞成星型或者树形,各种反射叠加起来信号质量一塌糊涂。
线缆用双绞线,CAN_H和CAN_L绞在一起,共模干扰同时在两根线上感应相同的电压,差分接收时自动抵消。不要两根线分开走,共模抑制效果大打折扣。
4.4 共模电压范围与隔离
CAN节点之间的地电位可能相差几十伏(工业现场),所以CAN收发器有较宽的共模输入电压范围(如-7V到+12V)。如果地电位差可能更大,或者需要保护MCU,就要加隔离CAN方案:在MCU和CAN收发器之间加数字隔离器,收发器侧用隔离电源供电。
隔离CAN芯片如ISO1050、ADM3053,内部集成了隔离和收发器,用起来跟普通CAN一样,成本高一些,工业产品必备。
五、四种接口硬核对比
| UART | 2+地 | 几十k~几Mbps | 米级(TTL)~千米(RS485) | 点对点(可多点485) | TTL/RS232/RS485 | 波特率误差、长线振铃 |
| I2C | 2 | 100k/400k/1Mbps | 板内,最长1-2米 | 多主多从总线 | 开漏+上拉 | 上拉阻值、死锁、上升沿慢 |
| SPI | 4+片选 | 几M~50Mbps | 板内,最长几十cm | 一主多从星形 | 推挽 | 高速信号完整性、CS悬空 |
| CAN | 2 | 最高1Mbps | 几十米到几千米 | 直线型总线 | 差分 | 终端电阻缺失、拓扑错误 |
六、本章总结
这一章老张把嵌入式最常用的四种通信接口的硬件特性过了一遍。记住几个核心要点:
-
UART:电平标准要分清(TTL/RS232/RS485),长距离别用TTL硬扛,波特率误差要从时钟源头控制。
-
I2C:上拉电阻不是随便选的,要算上升时间;死锁了别急着断电,试试手动翻SCL;上升沿太慢容易被干扰误触发。
-
SPI:低速随便飞,高速要讲究信号完整性——走线等长、阻抗匹配、远离干扰源。CS别悬空。
-
CAN:终端电阻两端各120Ω(总线上很多设备点,只接首尾两个节点),一个不能少;总线用双绞线走直线拓扑,工业场景上隔离。
通信硬件层的坑,调试软件解决不了,只能在画原理图和Layout的时候提前预防。




