ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

UART、I2C、SPI、I2S四大嵌入式通信协议对比与选型实战

UART、I2C、SPI、I2S四大嵌入式通信协议对比与选型实战 刚入行那会儿每次拿到一块新板子我第一件事就是翻原理图看接口标号。I2C、I2S、SPI、UART这四个缩写几乎出现在每一块嵌入式板卡上引脚数量不同、名字相似稍不留神就会把SDA当成MOSI接进去然后花一整天在逻辑分析仪上找波形。直到我把四种协议从头到尾理了一遍才发现它们之间不只是“快慢不同”这么简单——每个协议从诞生那天起就是为某一种特定场景而设计的。这篇内容我就围绕这四个协议的时序特征、物理层差异、选型逻辑和实际调试中的坑认认真真做一次横向对比帮你在自己项目里少走弯路。这篇文章适合刚接触嵌入式通信协议的同学也适合已经在用但没深究过“为什么这样设计”的开发者。我会把原理讲清楚也会给出实际可抄的配置思路和调试经验。1. 四种协议的身份定位它们各自解决什么问题很多教程喜欢直接罗列引脚定义和帧格式但我觉得理解协议最好的切入点是先搞清楚“它生下来是为了干什么的”。1.1 UART给“两个设备聊天”设计的异步串行协议UARTUniversal Asynchronous Receiver/Transmitter通用异步收发器的历史可以追溯到早期计算机串口设备它解决的问题其实非常朴素让两个设备之间只靠一根发送线、一根接收线就能互相传数据。它叫“异步”是因为收发双方不需要共享时钟线。发送方按照约定的波特率比如115200bps把数据一位一位地发出去接收方用自己的时钟在同样的波特率下去采样。这就好比两个人打电话不需要一根额外的“节拍器”线只要双方都说话语速一致就行。UART的典型特征是点对点一收一发。虽然也有一主多从的RS-485变体但那是靠地址和收发使能来控制的方向切换本质上还是一对一通信在工作。它的帧结构也很简单空闲时拉高起始位拉低一个位时间然后传5到8个数据位最后是校验位可选和停止位。这也是为什么UART在调试中最常见——往TX脚扔几个字节串口助手就能看到内容不需要考虑总线仲裁、设备寻址这些复杂机制。所以UART最适合的场景是两个设备之间低速、简单、双向的数据交换尤其是调试日志、AT指令、GPS定位信息这类流式文本数据。1.2 I2C给“一总线上挂很多小器件”设计的同步协议I2CInter-Integrated Circuit集成电路间总线是Philips后来是NXP在1982年为了连接电视、音响里的各种芯片发明的。它解决的核心问题是板子上器件越来越多每颗芯片都单独拉几根控制线PCB布线会爆炸。I2C只用了两根线SDA数据线和SCL时钟线。所有器件挂在这两根线上每个器件有唯一的7位或10位地址主机通过地址寻址来和某个从机通信。因为线少所以速度快不起来标准模式100kbps快速模式400kbps高速模式3.4Mbps实际嵌入式项目里100k和400k用得最多。它的设计哲学是“够用就行”——大多数传感器、EEPROM、RTC实时时钟、温度监控芯片数据量都很小不需要高带宽但要求接线简单、挂载方便。我还见过一个I2C总线上挂了8个设备的设计从气压计到触摸屏控制器全在上面每个设备地址不一样互不干扰。I2C最特别的地方在于开漏结构和上拉电阻。SDA和SCL都是开漏输出只能拉低不能主动拉高靠外部上拉电阻把线拉回高电平。这种设计天然支持多设备“线与”——只要有一个设备拉低总线就是低从而实现了最原始的仲裁机制。这也是为什么I2C时序图里总有那些看起来很“圆润”的上升沿——那是RC充电曲线。1.3 SPI给“高速传输数据块”设计的同步协议SPISerial Peripheral Interface串行外设接口是Motorola在80年代提出的定位和I2C正好相反我不管你能挂多少设备我就是要快要简单粗暴地把数据倒进来倒出去。SPI最少需要四根线SCLK时钟、MOSI主出从入、MISO主入从出、CS片选。主机产生时钟时钟一跑双方同步移位一个时钟周期交换一个bit。因为是全双工而且时钟可以由主机无线调速SPI可以轻松跑到几十MHz在嵌入式领域做到数十Mbps甚至上百Mbps的吞吐量不是什么难事。代价就是每挂一个从设备就要独占一个片选脚。如果你的主控GPIO充裕、设备数量在2到3个以内SPI是效率最高的选择。Flash存储、SD卡SD卡早期就是SPI模式起步、显示屏控制器、ADC采样芯片这些需要批量搬数据的场景几乎是SPI的主场。SPI没有标准帧格式一说它只有“时序约定”。CPOL时钟极性和CPHA时钟相位两个参数组合出四种模式这也是初学者最容易踩坑的地方——后面我会专门展开讲。1.4 I2S给“音频采样数据连续流动”设计的同步协议I2SInter-IC Sound集成电路间音频总线是Philips在1986年为数字音频制定的协议它干的事和上面三个完全不同它专门传输连续的音频采样流PCM数据。音频数据有一个特点它是实时产生的左右声道交替采样而且对时序连续性要求极高。I2S为此专门设计了三个信号BCK位时钟、LRCLK左右声道选择时钟/帧同步信号、SD串行数据。有的系统还会加一根MCLK主时钟给音频DAC芯片做内部时钟基准。I2S的典型配置是BCK频率 采样率 × 位深 × 通道数。比如44.1kHz采样率、16bit、双声道BCK就是44.1k × 16 × 2 1.4112MHz。LRCLK的频率等于采样率低电平期间发左声道数据高电平期间发右声道数据。你可以把I2S理解成一个永不停歇的流水线——主机不断产生时钟数据一位一位被推送到DAC里中间停了就会产生爆音或卡顿。所以I2S不适合传控制指令也不适合做通用数据传输它就是为音频单行道量身定做的。谁要是试图用I2S去传一堆非音频的随机数据那是真的选错了工具。2. 时序细节与波形特征从逻辑分析仪视角看差异协议对比不能只停留在“引脚功能表”上拉出真实波形看才是硬功夫。我从波形特征入手逐个拆解因为调试时你就是在示波器或逻辑分析仪上认这些波形的。2.1 UART波形最简单的电平翻转但波特率误差是隐形杀手UART的波形一眼就能认出来空闲状态是持续的高电平通常3.3V或5V起始位是一个明显的下降沿然后数据位逐个出现最后停止位拉高。抓一个115200、8N18数据位、无校验、1停止位的波形逻辑分析仪上你会看到类似这样的结构一个低电平段起始位跟着8个宽窄不一的电平段数据位LSB先行最后是一个高电平段停止位。关键点是UART没有时钟线接收方靠什么保证同步答案是“起止位波特率误差容忍”。接收方在检测到下降沿起始位后会开始用内部时钟计时然后在每个位时间的中间点采样。这个机制有一个容忍范围通常要求收发双方波特率误差不超过±2%到±3%。超过这个范围位采样点就会偏移到数据位边缘出现乱码或丢字节。我实际测过主板用晶振分频出的115200和USB转串口芯片内部PLL出来的115200两者误差一般在±1%以内问题不大。但如果你用软件模拟UART在忙乱中被中断打断时序漂移就会积累起来。尤其是一些主频不高的MCU定时器重装值的四舍五入会让实际波特率偏得离谱。2.2 I2C波形起始停止条件和应答位是灵魂I2C波形比UART复杂但特征非常鲜明。你抓一次EEPROM写入操作会看到反复出现的两种“特殊电平变化”起始条件STARTSCL为高电平时SDA产生一个下降沿。 停止条件STOPSCL为高电平时SDA产生一个上升沿。这是I2C协议的符号学基础。在数据阶段规定SDA只能在SCL为低时变化SCL为高时必须保持稳定接收方在SCL上升沿采样SDA。每个字节传输9个时钟前8个时钟传数据高位先行第9个时钟传应答位。主机发送完8位地址读写位后释放SDA从机如果存在且地址匹配会把SDA拉低一个时钟周期作为ACK。如果没有ACK从机根本不在线波形上SDA在第9个时钟继续保持高电平——这是排查I2C设备不通信时最常见的信号。这里有个细节7位地址读写位正好凑成一个字节。比如RDA5807那个经典收音机芯片7位地址是0x3C那么写操作字节是0x78左移一位补0读操作字节是0x79左移一位补1。很多初学者在代码里写错设备地址就是因为没搞清楚“7位地址”和“总线字节”之间的换算关系。这也是那些“i2c通信失败”问题里最高频的根因之一。除了标准帧I2C还支持重复起始条件在主从通信中途重新发起START而不发STOP用于在读操作前切换传输方向以及10位地址扩展、自由数据模式比如访问大容量EEPROM时连续读多个字节不需要重新寻址等变体。但这些都是在基础时序上做加法先把起始停止、ACK、字节格式看明白后面都好说。2.3 SPI波形看边沿采样关键是CPOL/CPHASPI波形结构上是四种协议里最直观的CS拉低表示一次传输开始SCLK在那边连续翻转MOSI在时钟的某个边沿输出数据MISO在同一或另一个边沿被采样。但“哪个边沿写、哪个边沿读”就分出了四种模式。CPOL决定空闲时SCLK电平CPOL0空闲低CPOL1空闲高。 CPHA决定采样发生在哪个边沿CPHA0在第一个边沿采样CPHA1在第二个边沿采样。以模式0CPOL0CPHA0为例空闲时时钟线为低第一个边沿上升沿采样第二个边沿下降沿改变数据。模式1则是第一个边沿改变数据第二个边沿采样。实际工作中Flash芯片比如W25Q系列几乎都在模式0或模式3工作SD卡SPI模式通常用模式0部分ADC芯片要求模式1或模式2。调试SPI通信时逻辑分析仪抓波形看到数据出现“错一位”或者“第一位丢失”十有八九就是CPHA设置反了。关于片选很多人都踩过“硬件片选”和“软件片选”的坑。软件片选就是用普通GPIO拉低拉高时序上完全受控想延时多久就多久缺点是每次片选切换需要CPU参与。而有些MCU的专用硬件片选如STM32的NSS会在SPI传输自动结束时提前拉高如果从设备要求CS保持低电平直到最后一个位采样完成硬件片选配上SPI DMA就会偶发性丢最后一个字节。我在调试RK3588SPI接口时就遇到过类似问题硬件片选默认行为在传输结束立刻释放导致后级设备收尾数据出错最后改用软件片选才稳定。2.4 I2S波形数据始终在跑对齐不齐一眼就看出来I2S波形和前面三种完全不同。你用逻辑分析仪抓I2S会看到BCK是一个连续的方波脉冲串LRCLK是一个方波频率等于采样率SD线上则是持续不断的bit流。I2S标准定义SD数据在LRCLK下降沿之后延迟一个BCK周期开始也就是“数据比帧信号延迟一位”。左声道数据在LRCLK为低期间发送右声道在LRCLK为高期间发送。发送方在BCK下降沿改变数据接收方在BCK上升沿采样。不过市面上还有左对齐Left Justified和右对齐Right Justified两种变体。左对齐模式数据紧跟着LRCLK翻转无延迟右对齐模式数据对齐到帧结尾。如果主控的I2S控制器和音频编解码芯片配置不一致你听到的声音就是尖锐的噪声或明显的节奏错乱——这在波形上很难看出来只能靠对照芯片手册的时序图和控制器寄存器设置来排查。MCLK是另一个高频坑。很多现代DAC比如ES8388、WM8960这类编解码芯片要求主机提供MCLK或称SYSCLK用来驱动内部滤波器时钟。MCLK频率通常是采样率的256倍或512倍比如44.1kHz对应11.2896MHz或22.5792MHz。有些主控片上PLL不一定能分频出这个精确值系统就得改采样率48kHz对应12.288MHz来迁就时钟配置。这就是为什么很多音频板子默认用48kHz而不是44.1kHz——纯粹是MCLK生成更容易。3. 物理层与拓扑结构对比引脚、速率和能挂几个设备下面这张表是四种协议最核心的物理层差异建议截图保存选型时直接对照着看。对比项UARTI2CSPII2S信号线数量最少2根TX/RX2根SDA/SCL最少4根SCLK/MOSI/MISO/CS最少3根BCK/LRCLK/SD常用4根加MCLK时钟方式异步无时钟线同步SCL由主机产生同步SCLK由主机产生同步BCK和LRCLK由主机产生全双工是否半双工一根数据线双向是否数据单方向为主标准I2S单向从一个设备到另一个设备最大设备数点对点理论上挂128个7位地址受限于主机片选GPIO数量通常点对点可串联但很少用常用速率范围9600bps~2Mbps100kbps~1Mbps常用快速模式3.4Mbps1MHz~几十MHz取决于音频配置BCK约1~6MHzMCLK可达22~49MHz总线仲裁无有线与仲裁无主机独占无主机独占抗干扰能力中长线应用要靠RS-232/RS-485电平弱开漏高阻适合板内短距离中推挽输出可较快传输弱不适合长距离流控硬件流控RTS/CTS和软件流控N/AN/AN/A3.1 I2C上拉电阻怎么选一个影响全总线稳定性的细节I2C虽然只有两根线但这两根线上的上拉电阻选错整条总线都会出问题。阻值太小灌电流过大低电平可能压不下去阻值太大RC上升沿太慢高电平来不及建立高速模式根本跑不稳。常用做法是400kHz快速模式配2.2kΩ上拉100kHz标准模式配4.7kΩ上拉总线电容大的时候选小一点。一条经验公式是上升沿时间约等于0.7倍R×C如果你不知道总线电容可以先用4.7kΩ起手逻辑分析仪看上升沿如果沿变得太缓再换2.2kΩ。我遇到过一块板子设计时忘了放上拉电阻I2C扫描一个设备都扫不到后来飞线焊了俩4.7kΩ才恢复。还有一个很容易忽视的问题如果板上有多个I2C器件而且每个器件子卡都自带4.7kΩ上拉并联起来等效电阻可能只有1kΩ不到此时低电平灌电流过大主机GPIO可能拉不下去通信时好时坏。做法是多板互联时只保留主机侧一组上拉。3.2 SPI为什么可以跑很高推挽输出和专用片选SPI之所以能上几十MHz物理层面有三个原因推挽输出高电平和低电平都是主动驱动、时钟和数据由主机同步产生、片选隔离了无关设备。相比之下I2C的开漏结构和仲裁机制天生限制了速率要上升沿靠外部电阻慢慢充电想跑快就得加大电流功耗和EMI都上去。SPI的速率上限通常由两方面决定主控SPI外设支持的最大频率以及从设备的数据手册标称最大值。比如W25Q128JV手册写可以支持到133MHz DTR模式但STM32F103的SPI最高只有18MHz主频72MHz/4所以瓶颈反而在主控这边。多设备SPI还有一个做法是“菊花链”Daisy Chain多个从设备的MISO和MOSI串联起来数据像移位寄存器一样级联传下去。好处是省片选脚坏处是延迟叠加适合对实时性要求不高但设备多的场景。但在绝大多数嵌入式项目里老老实实每设备一个CS脚最稳。3.3 UART为什么看起来“慢”波特率不是带宽很多人拿UART和SPI比速率觉得UART太慢。但注意UART在115200bps下有效数据率要打折扣每传1字节实际占用10个位时间1起始位8数据位1停止位大概1.152Mbps的线速率只能跑出115KB/s的有效吞吐。如果开了校验和双停止位还要更低。所以UART不是用来搬大量数据的。它的优势是简单、长距离版本成熟RS-485能跑1200米、协议可以自定义加帧头校验帧尾。16550这个经典UART芯片定义了现代串口的寄存器规范一直沿用到今天也侧面说明这个协议的生命力有多长。4. 项目选型决策指南什么场景选哪个协议理论上讲清楚之后落到实际项目里怎么选型我给出几个具体的决策路径和案例参考。4.1 选型决策树三步帮你定方向先看数据量如果你每个事务只是交换几个字节传感器读数、寄存器配置I2C通常最合适。如果你要连续搬移几KB到几MB的数据固件更新、图像帧、音频流考虑SPI或I2S。再看方向如果两个设备互相对发数据且都主动发起通信UART最宽松双方都是主机没有从机概念SPI则要设计好从机中断机制来模拟双向主动通信。I2C的主从模型决定了它适合“主机主动轮询从机”。再看设备数量一板上挂超过3个同总线外设I2C最省引脚。设备数量少但追求速率SPI。音频数据最后一定要落到I2S不要再拿SPI去模拟曼彻斯特编码传音频采样那是给自己找麻烦。4.2 案例拆解1环境监测节点我之前做一个环境监测节点板载主控STM32。温湿度传感器SHT40用I2C接因为就是周期性读几字节温湿度数据气压传感器BMP390也在同一条I2C总线上地址不同扫描一次就能发现。日志和调试接口走UART接到USB转串口模块FT232R这类上位机读串口助手看输出。板载Flash W25Q32用于存储历史数据用SPI接因为写入几百条日志要批量搬数据SPI比I2C快一个数量级。这个案例里四个协议用了三个各司其职。音频芯片压根没出现所以I2S没被用到——但项目里如果加上音频播报功能话题就完全不同了。4.3 案例拆解2带触摸屏的音频播放器这是我做过比较复杂的一个嵌入式设备。主控是ESP32-C3。音频输出编解码芯片ES8388通过I2S接收主控发来的PCM数据同时通过I2C配置寄存器耳机音量、采样率、通路切换。同一个芯片上出现了I2S传数据、I2C传控制字这个组合非常经典。触摸屏GT911电容触摸屏走I2C地址是0x14/0x5D可选。初期老是通信失败后来发现就是上拉电阻阻值不对加上I2C地址模式判断错误改完就好了。显示屏初始化部分屏初始化参数通过SPI写入或者有些屏幕原生就是SPI接口。主控和另一颗MCU之间用UART交互命令格式化成JSON字符串。这个项目的经验就是一颗音频编解码芯片上I2C和I2S并存是标准玩法I2C管“配置”I2S管“数据”两套协议分工明确。配置和数据分离这是一个很重要的设计思路——不要在音频数据流里嵌控制信息。4.4 特殊场景FPGASPI ADC的高速采样FPGA项目里SPI几乎是绝对主角。比如要通过ADC采集高速信号送到FPGA处理一颗20MHz采样率的ADC通常就是SPI接口FPGA把SPI的SCLK当作采样时钟的一部分来设计时钟连续翻转数据不断涌入。这时候SPI的“同步、连续、可高带宽”特征全部发挥出来了。在这种场景下I2C就完全不适合——20MHz的采样率意味着每秒要传几百万字节I2C在400kHz下每秒最多50KB差了两个数量级。而UART即使跑到2Mbps也只有250KB/s同样不够用。所以FPGA高速ADC选SPI是唯一合理选项。4.5 混合系统中协议并存管理面与数据面分离很多复杂系统里你会看到同样的芯片同时用I2C和SPI甚至UART接出不同功能。比如一款交换芯片或PHY调试接口是UART寄存器管理口是MDIO类似I2C的管理总线或I2C固件启动则挂在SPI NOR Flash上。这就是“管理面与数据面分离”的经典架构——SPI快速搬数据I2C管配置UART做人机调试。如果你的系统里也有多协议并存的需求记住一个原则数据流走SPI或I2S控制流走I2C调试流走UART。按这个原则分配接口后期维护会省心很多。5. 实战调试经验我在调这四个协议时踩过的坑最后这部分是干货中的干货。以下每一个问题都是我在实际项目中真实遇到过、并且花时间排查过的希望你能绕过。5.1 I2C总线锁死和上拉电阻的连锁反应I2C有一个特别恶心的故障总线锁死。现象是SDA一直为低所有通信都失败。常见原因是某次通信异常时主机或从机在发送数据中途释放了时钟从机状态机卡在“等待时钟”的中间状态SDA上的低电平被锁死。解决办法很简单——把SCL手动翻转9个时钟从机状态机就能复位。我自己就遇到过GT911触摸屏I2C通信失败的问题上电后触摸屏偶尔能识别、偶尔死锁排查到最后是上拉电阻选择不当导致高电平建立太慢再加上电源上电时序中复位信号和I2C初始化竞争。对策是先硬件复位触摸屏延时100ms再配置I2C并且把上拉电阻从10kΩ换成4.7kΩ问题彻底消失。5.2 SPICPOL/CPHA配反让数据“全对又全错”调试SPI Flash时最迷惑的现象是读出来的芯片ID前几个字节正确后面全错。这通常不是驱动的问题而是模式不匹配。W25Q系列支持模式0和模式3很多主控默认用模式0但如果之前别人把模式配置成了3读回来的数据会有细微差异。我的排查步骤固定在五步先用逻辑分析仪抓CS、SCLK、MOSI、MISO四根线和从设备手册的时序图对照确认CPOL空闲电平和手册一致确认CPHA采样边沿和手册一致检查CS有效期间是否覆盖了整个传输过程最后用只读命令比如读JEDEC ID验证因为读ID不改变芯片状态反复试模式安全。有一次我在STM32上用SPI DMA读ADC数据偶尔跳变根因是SPI开启DMA后DMA传输完成中断比最后一个字节采样完成早半拍我在中断里立刻读了SPI_DR寄存器拿到的是旧数据。解决方案是等待SPI空闲标志BSY位清零后再读数据或者把DMA的传输宽度改成半字。5.3 UART波特率误差和USB转串口芯片的坑UART调试时如果出现“能发不能收”或“收的数据有规律性错误”大概率是波特率误差问题。比如用8MHz内部RC振荡器的MCU硬跑115200分频系数不是整数实际波特率可能偏了3%以上接收方向就会出现数据低位错误。另外一类高频问题是USB转串口芯片的驱动选择。FT232R、FT231X这类芯片在Windows下驱动装不对会识别成未知设备或端口不工作。调试时先确认设备管理器里识别的是“USB Serial Port”再检查驱动版本。国产兼容芯片另说它们偶尔会出现掉线这是芯片本身的问题不是协议的事。UART还有一个隐蔽坑如果只接了TX和RX但设备端开着硬件流控RTS/CTS接收方可能因为CTS被拉低而拒绝发送。我接过一个GPS模块怎么发命令都不回应后来发现它默认开启了RTS/CTS流控把主控空余的两个GPIO接过去模拟RTS/CTS信号才恢复。5.4 I2SDMA缓冲和左右声道对齐I2S调试最讨厌的是无声或噪声。我遇到过ESP32-C3 I2S输出到外部DAC后没有声音的情况逻辑分析仪看BCK和LRCLK都有波形SD线上也有数据但DAC没有输出。排查到最后发现两个问题叠加一是LRCLK的极性配反本该低电平为左声道配置成了高电平为左声道二是DMA缓冲区的PCM数据字节序和DAC期望的格式不一致。16bit音频数据DAC期望的是小端序我代码里按大端序填充导致每个采样值高低字节颠倒解出来的就是噪声。调试I2S的基本功是把MCLK/BCK/LRCLK的频率测准三个频率关系必须满足MCLK 256×采样率或512×采样率BCK 采样率×位深×通道数LRCLK 采样率。如果测量结果偏离这个比例超过1%音频一定出问题。5.5 调试工具和通用方法说一千道一万调这四种协议逻辑分析仪是我最推荐的工具。现在市面上的廉价逻辑分析仪配合上位机软件就可以同时抓十几路信号I2C和SPI还能直接解码成十六进制数据大大缩短排查时间。示波器更适合看模拟细节上升沿快慢、电平幅度逻辑分析仪适合看时序关系谁先谁后、采样边沿对不对。我自己的习惯是每一种新协议先让硬件工程师拉一根测试点把关键信号都引出来然后跑一个最简单的loopback或读器件ID的demo抓波形确认再进入业务逻辑开发。这一步看起来多花了半小时实际上省下的查错时间是以天计的。写在最后的一点个人体会四种协议放在一起对比最容易得到的结论是“SPI最快、I2C最省线、UART最简单、I2S最专一”但实际项目里更重要的不是背这个结论而是理解它们各自的设计哲学。I2C的地址仲裁、SPI的片选和DMA结合、UART的起止位同步、I2S的帧对齐连续性每个细节背后都是几十年工程实践的沉淀。我个人实际体验最深刻的一点是不要试图用一个协议包打天下。之前在某个项目里有人试图用SPI去接一个本来应该I2C的传感器理由是“SPI快”结果为了模拟I2C的应答位和总线释放写了上百行的状态机最后还是不稳定。后来换回I2C二十行代码解决战斗。协议选型的第一原则是匹配场景而不是追参数。最后分享一个小技巧无论调哪个协议先在纸上把时序图画出来标清楚哪个边沿采样、哪个电平有效、地址字节是多少再写代码。我见过太多人代码写完才发现连从机的设备地址都算错了。把这四种协议的时序图打印出来贴在工位前比记住所有寄存器配置更有用。
返回列表