☰
I2C、I2S、SPI、UART四种总线选型指南:从原理到实战
2026/9/28 1:20:49 网站建设 项目流程

1. 四种总线到底该怎么选:先搞清楚它们各自在解决什么问题

嵌入式开发干久了,你会发现一个很有意思的现象:新手最爱问“I2C和SPI哪个更快”,老手反而会先问“你这个场景到底需要几根线、挂几个设备、要不要热插拔”。I2C、I2S、SPI、UART这四种总线,几乎覆盖了单片机外围通信的八成场景,但它们之间不是简单的“谁替代谁”的关系,而是各自有明确的势力范围。选错了,轻则多飞几根线、多写几百行驱动,重则通信不稳定、音频出杂音、传感器时不时掉线。

这篇文章我打算把这四种总线放在一起做一次横向拆解,不是照本宣科地列时序图,而是从实际项目选型的角度,把每种总线的核心机制、典型应用、踩坑点讲透。无论你是刚接触STM32 HAL库的新手,还是正在做RK3588这类复杂平台外围设计的老手,都能从中找到可以直接抄作业的判断依据。全文会围绕I2C、I2S、SPI、UART这四个关键词展开,穿插ESP32-C3的I2S输出、FPGA挂SPI ADC、Linux下PHY不走MDIO走I2C、GT911触摸屏I2C通信失败等真实场景,把“为什么这么选”和“怎么落地”讲清楚。

先给一个最粗的结论,方便你建立第一印象:UART是点对点异步串行,最简单但速度有限;I2C是两根线的总线型同步串行,省引脚但速率中等;SPI是四根线的高速同步串行,快但占引脚;I2S是专门为音频数据流设计的同步串行,本质上是SPI的一个“专业化分支”。记住这个框架,后面所有细节都是往里面填肉。

2. 四种总线的核心机制拆解:从电气特性到时序逻辑

2.1 UART:没有时钟线的“约定式”通信

UART的全称是通用异步收发器,关键词在“异步”两个字。它没有时钟线,收发双方靠事先约定好的波特率来对齐每一位数据。你可以把它想象成两个人约好每隔一秒说一个字,谁也没有节拍器,全靠各自的表走得准不准。一旦双方的时钟偏差超过容忍范围,比如超过5%,采样就会错位,收到乱码。

UART的帧结构很固定:1个起始位(低电平)、5到9个数据位(通常8位)、可选的校验位、1到2个停止位(高电平)。空闲时线路保持高电平。起始位的下降沿就是接收方的“闹钟”,触发它开始按波特率采样。这里有个实操细节:接收方通常在起始位下降沿后延迟1.5个位时间开始采样第一位数据,之后每隔1个位时间采一次,这样采样点正好落在每位数据的中间,容错率最高。

UART的典型应用场景是调试串口、GPS模块、蓝牙模块、以及各种“AT指令”类设备。16550是经典的UART控制器行业标准,现在很多MCU内部的UART外设都兼容它的寄存器模型。FT232R、FT231X这类USB转UART芯片,则是PC端调试的常客,驱动安装虽然偶尔折腾,但胜在生态成熟。

注意:UART是点对点的,不能像I2C那样一条总线挂多个设备。如果你需要多个UART设备,要么用多个UART外设,要么加多路复用器。

2.2 I2C:两根线挂一串设备的“总线型”方案

I2C只有两根线:SDA(数据)和SCL(时钟),都是开漏输出,需要外接上拉电阻。开漏的好处是天然支持“线与”逻辑,任何设备拉低总线都能被检测到,这就实现了多设备共享总线和仲裁机制。上拉电阻的取值很讲究,典型值4.7kΩ,但高速模式下可能要用2.2kΩ甚至更小,因为上升沿太慢会导致时序违规。

I2C的通信过程是“主机发起、从机响应”。主机先发一个起始条件(SCL高时SDA由高变低),然后发7位从机地址加1位读写位。从机如果地址匹配,就在第9个时钟拉低SDA作为ACK应答。之后就是数据字节的传输,每传一个字节跟一个ACK。停止条件是SCL高时SDA由低变高。

I2C最让人头疼的地方在于时钟拉伸和总线死锁。时钟拉伸是从机拉低SCL告诉主机“我还没准备好”,主机必须等待。如果主机不支持时钟拉伸,就会读错数据。总线死锁则常见于主机复位时从机还在拉低SDA,导致总线一直忙。解决办法是主机发送9个时钟脉冲,让从机把剩余数据发完释放总线。

I2C的典型应用包括EEPROM读写、传感器(温湿度、加速度计)、触摸屏(GT911就是I2C接口)、以及一些编码器。GT911 I2C通信失败是很多人踩过的坑,常见原因就三个:上拉电阻没焊、地址配错(GT911有0x5D和0x14两个地址,取决于上电时INT引脚电平)、以及复位时序不对。

2.3 SPI:四根线换来的高速与灵活

SPI用四根线:SCLK(时钟)、MOSI(主出从入)、MISO(主入从出)、CS(片选)。它是同步通信,时钟由主机产生,所以速率可以拉得很高,几十MHz很常见。SPI没有地址概念,靠CS片选来选中某个从机。这意味着每多一个从机,就多一根CS线,引脚消耗大。

SPI有四种模式,由CPOL(时钟极性)和CPHA(时钟相位)组合而成。CPOL=0表示空闲时SCLK为低,CPOL=1表示空闲为高;CPHA=0表示第一个边沿采样,CPHA=1表示第二个边沿采样。模式选错是SPI调试中最常见的问题,现象是读出来全是0xFF或0x00。我的经验是:先查从机手册确认模式,然后用逻辑分析仪抓波形,看数据在哪个边沿稳定。

SPI的片选分硬件片选和软件片选。硬件片选由SPI外设自动控制CS引脚,时序精准;软件片选则是用普通GPIO手动拉低拉高,灵活但占用CPU。CS的最小脉宽取决于从机要求,有些ADC要求CS在两次转换之间保持高电平至少几十纳秒,这个参数在手册里叫tCSH或tCS。STM32用CubeMX配置SPI DMA时,硬件片选能省不少CPU开销。

SPI的典型应用包括Flash存储、ADC(FPGA挂SPI ADC很常见)、显示屏、以及无线模块。RK3588的SPI接口性能很强,但要注意引脚复用和电平匹配。

2.4 I2S:为音频而生的“SPI变体”

I2S全称是Inter-IC Sound,专门传音频数据。它至少有三根线:SCK(位时钟)、WS(声道选择,也叫LRCLK)、SD(数据)。有时候还有MCLK(主时钟)。I2S的时序和SPI很像,但有几个关键区别:WS信号在每个声道切换时翻转,决定当前数据属于左声道还是右声道;数据通常在高位对齐或低位对齐,标准I2S是高位对齐且比WS延迟一个SCK。

I2S的采样率、位深、主从模式是三个核心配置。比如ESP32-C3的I2S输出,你要配采样率44.1kHz或48kHz、位深16位或32位、以及是主模式还是从模式。主模式下ESP32-C3产生SCK和WS,从模式下外部提供。用逻辑分析仪看I2S波形时,重点看WS翻转频率是否等于采样率,以及数据是否在SCK边沿稳定。

I2S的典型应用就是音频编解码器、数字麦克风、以及音频功放。如果你只是传普通数据,别用I2S,用SPI更灵活。

3. 横向对比:速率、引脚、拓扑、场景一张表说清

3.1 关键参数对比表

特性UARTI2CSPII2S
线数2(TX/RX)2(SDA/SCL)4(SCLK/MOSI/MISO/CS)3+(SCK/WS/SD)
时钟异步同步同步同步
拓扑点对点总线型(多主多从)一主多从(每从一CS)点对点为主
典型速率9600bps~1Mbps100k~3.4Mbps1M~50Mbps取决于音频采样率
寻址方式无7位/10位地址CS片选WS声道选择
引脚开销低低高中
抗干扰弱中强中
典型场景调试、GPS、蓝牙传感器、EEPROM、触摸Flash、ADC、屏幕音频编解码

3.2 选型决策树:三步锁定目标总线

第一步,问自己“传的是什么”。如果是音频流,直接I2S,别犹豫。如果是传感器数据、配置寄存器,优先I2C。如果是高速数据块,比如图像、大量ADC采样,选SPI。如果只是调试打印或跟PC通信,UART最省事。

第二步,问“挂几个设备”。一个设备,UART或SPI都行。多个设备且引脚紧张,I2C是首选。多个设备但要求高速,SPI加多CS,或者用I2C加多路复用器(I2C控制的多路复用器很常见,比如TCA9548A)。

第三步,问“速率要求”。低于1Mbps,I2C和UART都能胜任。1M到10Mbps,SPI开始显现优势。高于10Mbps,基本只有SPI和I2S(音频专用)可选。

提示:PMBus和I2C的区别主要在协议层,PMBus是I2C的一个子集,增加了电源管理相关的命令格式。硬件上完全兼容,所以如果你在调电源芯片,看到PMBus别慌,按I2C调就行。

4. 实操落地:从STM32 HAL库到Linux平台的配置要点

4.1 STM32 HAL库下四种外设的初始化与API调用

STM32CubeMX生成代码后,每种外设的初始化函数和API调用方式差异很大。UART用HAL_UART_Init,发送用HAL_UART_Transmit,接收用HAL_UART_Receive或中断/DMA模式。I2C用HAL_I2C_Init,读写用HAL_I2C_Mem_Read和HAL_I2C_Mem_Write,注意这两个函数需要指定设备地址和寄存器地址。SPI用HAL_SPI_Init,收发用HAL_SPI_TransmitReceive,DMA模式下要配HAL_SPI_TransmitReceive_DMA。I2S在STM32里通常和SPI共用外设,用HAL_I2S_Init配置。

这里有个实操心得:HAL库的I2C函数在遇到从机不响应时会阻塞很久,默认超时是1000ms,调试时很浪费时间。建议把超时改短,比如100ms,或者用中断模式。另外,STM32的硬件I2C在某些型号上有已知的时序问题,如果调不通,可以先用软件I2C(GPIO模拟)验证从机是否正常,再切回硬件I2C。

4.2 ESP32-C3的I2S输出配置实例

ESP32-C3的I2S外设配置需要关注几个参数:采样率、位深、声道数、以及DMA缓冲区大小。用ESP-IDF的话,调用i2s_driver_install和i2s_set_clk。主模式下,ESP32-C3产生SCK和WS,从模式下外部提供。如果你用ESP32-C3做蓝牙音频接收,I2S输出到DAC,采样率通常设44.1kHz或48kHz,位深16位。

实测下来,ESP32-C3的I2S在48kHz/16位下很稳,但DMA缓冲区别设太小,否则容易断音。建议至少设1024字节。另外,MCLK如果不需要就别开,省引脚。

4.3 Linux平台下I2C和SPI的设备树配置

在RK3588这类Linux平台上,I2C和SPI设备通过设备树描述。I2C设备节点里要写compatible、reg(设备地址)、以及具体驱动需要的属性。SPI设备节点要写compatible、reg(片选号)、spi-max-frequency。有个坑是:Linux的I2C驱动有时会尝试用MDIO访问PHY,但有些PHY只支持I2C,这时候要在设备树里明确指定用I2C,别让内核自动探测。

RK3588的SPI接口在设备树里配置时,注意pinctrl要选对引脚组,spi-max-frequency别超过从机手册的最大值。如果从机是SPI NOR Flash,还要配spi-tx-bus-width和spi-rx-bus-width。

4.4 用Python模拟SPI接口的可行性分析

有人问能不能用Python调用USB模拟SPI接口。答案是:可以,但有限制。FT232H这类芯片支持MPSSE模式,能模拟SPI、I2C、JTAG。Python用pyftdi库可以调用。但模拟SPI的速率远低于硬件SPI,通常只有几百kHz到几MHz,而且时序抖动大。适合调试和低速设备,不适合高速ADC或Flash。

如果你只是偶尔用一下,pyftdi够用。如果要稳定高速,还是用硬件SPI。

5. 常见问题与排查技巧实录

5.1 I2C通信失败排查清单

现象可能原因排查方法
完全无响应上拉电阻缺失用万用表测SDA/SCL空闲时是否高电平
地址无应答地址配错查手册确认7位地址,注意左移一位
偶发NACK总线电容过大减小上拉电阻或降低速率
总线死锁从机拉低SDA发9个时钟脉冲释放
GT911失败复位时序不对按手册控制INT和RST引脚

5.2 SPI模式选错与CS时序问题

SPI调不通,先查模式。用逻辑分析仪抓SCLK和MOSI,看数据在哪个边沿变化。如果数据在第一个边沿变化,那是CPHA=0;在第二个边沿变化,CPHA=1。CPOL看空闲电平。CS的最小脉宽如果不够,从机会忽略命令。比如有些ADC要求CS高电平至少50ns,你如果连续操作没留间隔,就会丢数据。解决办法是在两次传输之间加延时,或者用硬件片选让外设自动处理。

5.3 UART乱码与波特率误差计算

UART乱码九成是波特率不对。波特率误差计算公式是:误差 = (实际波特率 - 目标波特率) / 目标波特率。一般要求误差小于2%,最好小于1%。比如你晶振是8MHz,要115200波特率,分频系数是8M/115200=69.44,取整69,实际波特率是8M/69=115942,误差0.64%,可以接受。如果误差超过2%,就要换晶振或改用更高精度的时钟源。

5.4 I2S波形异常与逻辑分析仪使用

I2S出问题,先用逻辑分析仪看三根线。WS频率应该等于采样率,SCK频率等于采样率×位深×2。如果WS不翻转,可能是主从模式配错。如果数据全是0,可能是DMA没启动或缓冲区没填。ESP32-C3的I2S输出如果断断续续,检查DMA缓冲区是否太小,或者任务优先级是否被其他任务抢占。

6. 进阶话题:I2C自由数据模式与SPI硬件测试用例

6.1 I2C自由数据模式的应用

I2C自由数据模式是指不遵循标准寄存器读写格式,直接发原始字节。有些设备比如某些编码器或自定义协议,就是用这种模式。实现方法是直接用HAL_I2C_Master_Transmit发原始数据,不用HAL_I2C_Mem_Write。注意这种模式下没有寄存器地址概念,从机怎么解析完全看它的固件。

6.2 SPI硬件测试用例设计

SPI硬件测试通常包括:回环测试(MOSI短接MISO)、片选测试(测CS能否正常拉低拉高)、速率测试(逐步提高SCLK看何时出错)、以及模式测试(四种模式都试一遍)。回环测试最简单,发什么收什么就说明硬件通路正常。速率测试能帮你找到从机的最大承受频率。

6.3 从机主动更新主机寄存器的实现思路

I2C从机主动更新主机寄存器,标准I2C不支持,因为I2C是主机主导。但可以用“从机拉低一个中断引脚,主机收到中断后去读从机”的方式模拟。有些MCU的I2C外设支持从机发送模式,但需要主机配合。更常见的做法是用SPI,因为SPI从机可以在主机发时钟时把数据推出去。

7. 我个人在实际项目中的选型体会

干了这么多年,我选总线的顺序基本固定:先看有没有现成驱动,再看引脚够不够,最后才看速率。因为调试成本往往比硬件成本高。I2C虽然速率不高,但两根线挂一堆传感器,布线清爽,驱动也成熟。SPI快是快,但每加一个设备就多一根CS,PCB走线麻烦。UART最简单,但点对点限制大。I2S专用性强,非音频场景别碰。

最后分享一个小技巧:如果你不确定选哪个,先用UART或软件I2C把功能跑通,再根据速率需求决定要不要换硬件总线。这样能最快验证核心逻辑,避免一开始就陷在时序调试里。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询