尧图精选

嵌入式必知:I2C、SPI、UART、I2S四大通信协议全解析

🕒 发布时间:2026/9/28 2:06:32 📁 来源:尧图网络
做嵌入式开发这几年被新手问得最多的问题就是I2C、I2S、SPI、UART到底有什么区别这几个缩写几乎天天出现在原理图上但真到选型接口、调试时序的时候又经常翻车。这篇文章我把四个协议放在一起从同步异步、总线拓扑、速率波形、实际调试和踩坑经验五个维度一次讲透。适合刚入门单片机的同学也适合干了几年依然在I2C上卡壳的工程师。1. 整体思路拆解四种协议到底在解决什么问题先别急着记时序图。把这个问题想通了后面全是水到渠成通信协议的本质就是在一组物理线上按照约定好的规则把0和1按时序送出去。之所以发展出这么多协议是因为不同场景对速率、距离、引脚数量、可靠性的需求完全不一样。1.1 同步与异步通信世界的第一道分水岭第一个判断维度是同步还是异步。UART是典型的异步它没有额外时钟线发送方和接收方各自用本地时钟去采样电平。只要两边波特率一致就能按约定时间节奏把数据解出来。而SPI、I2C、I2S都带时钟线主机提供时钟从机跟着时钟边沿去采样不需要“约节奏”。这个区别带来的实际影响非常大。异步通信对时钟精度有要求如果两边波特率误差超过大约3%数据就会在采样窗口边缘飘移轻则乱码重则丢帧。同步通信则宽松得多时钟沿由主机决定只要主从双方都遵守同一个相位约定速率甚至可以中途变化。这也是为什么SPI的SCLK能跑到几十兆赫兹而UART很难在普通TTL电平下长时间跑高速。你可以把异步想象成两个人各自戴表约定十一点见面只要表差不多准就行同步则更像一个人喊口令大家一起听从指挥节奏完全由喊口令的人控制。在实际项目中UART常用来接PC、GPS模块、蓝牙模块SPI/I2C则更多在板内连传感器、Flash、触摸屏。1.2 引脚数量与总线规模从拓扑结构看适用场景第二个维度是电气拓扑。UART最少只要TX、RX两根线适合点对点SPI至少需要SCLK、MOSI、MISO外加片选CS每个从机独占一根CS所以从机多了IO吃不消I2C只要SDA、SCL两根线靠设备地址区分多个从机挂十来个传感器不成问题I2S专门服务音频至少BCLK、LRCK、DATA三根线实际使用往往还要加MCLK给codec做主时钟。选型时先数IO资源是基本素养。如果你的主控只剩两个引脚却要挂三个不同型号的传感器I2C基本是唯一选择。如果要做高速数据采集从机又不多SPI的独立CS和全双工能力显然更合适。I2S则基本不用想别的它天生就是为音频流准备的。1.3 速率与距离一个表格看清性能边界不同协议的速率和距离差异极大这也是很多同学最不容易记住的地方。我经常用下面这个表做快速判断实战中比较可靠协议典型速率常用电平实用距离UART9600bps ~ 几MbpsTTL、RS232、RS485TTL几十厘米RS485可达上千米SPI10Mbps ~ 50MbpsTTL/CMOS通常小于20cmI2C100kbps / 400kbps / 1Mbps / 3.4Mbps开漏加外部上拉通常小于1m受容性负载限制I2S1.5Mbps ~ 6Mbps以上TTL/CMOS板级走线通常小于10cmUART能远距离传输靠的是RS485差分电平而不是TTL。SPI和I2S是强推挽驱动信号完整性和电磁干扰限制了传输距离。I2C因为开漏结构上拉电阻和总线电容决定了边沿速度距离稍长波形就会变圆。I2S则是典型的板内音频总线很少有人把它拉出PCB设计。2. 四个协议逐个拆解从原理到实操这一趴把每个协议的关键细节过一遍。重点不是背定义而是理解每一根线为什么存在以及实际使用时哪些配置最容易翻车。2.1 UART最朴素的异步串口UART就是通用异步收发器传输单位叫“帧”。一帧由空闲状态开始空闲时TX线保持高电平发送端先拉低一个bit时间作为起始位接着按顺序输出数据位一般是LSB first最后拉高一个bit时间作为停止位。常见配置“115200-8-N-1”意思是波特率115200、8位数据、无校验、1位停止位。要注意一个常见误区波特率不等于字节速率。以115200bps、8N1为例每传一字节实际要占用1个起始位8个数据位1个停止位10个bit所以理论有效吞吐率是11520字节/秒。如果开启校验位就变成11个bit吞吐率降到约10472字节/秒。计算这个有助于估算日志或大文件传输时间很多人直接把波特率当字节数算差距很大。电平方面MCU引脚是TTL/CMOS电平电脑串口一般是RS232电平直接接上去轻则乱码重则烧IO。需要转换芯片或板载USB转UART。接线时必须TX接对方RX、RX接对方TX而且TTL设备之间要共地否则数据根本跑不起来。调试时我最喜欢先用回环测试把本机TX和RX短接看自发自收是否正常这能快速排除接线问题。2.2 SPI速率优先的同步全双工SPI是四线制同步全双工接口主机通过SCLK输出时钟在时钟沿同步发送和接收数据。MOSI是主机输出从机输入MISO是主机输入从机输出CS用来片选从机。每个CS对应一个从机通信前把CS拉低通信结束后拉高。SPI最大的优点是没有应答机制底层非常轻因此可以把时钟做得很快。这也是很多高速ADC、Flash、LCD屏选SPI的原因。但缺点也很明显主从之间没有标准错误反馈通信是否成功要靠数据内容或软件自己定义校验。读W25Q128这类SPI Flash时先发命令字节接着读数据CS必须在整个读序列中保持低电平提前拉高会让Flash内部状态错乱。从机侧也要注意MISO在从机未被选中时应该是高阻态否则多从机挂一条MISO会数据冲突。有些廉价模块没有做CS控制直接导致总线异常。SPI的四种模式我后面会专门展开这里先记住一句话两个设备必须工作在同一个CPOL和CPHA组合下否则逻辑分析仪上看到的波形虽然完整数据却全是错的。2.3 I2C双线制半双工的“总线”思维I2C只有SDA和SCL两根线却是所有协议里逻辑最讲究的一个。它是开漏结构低电平由设备主动驱动高电平靠外部上拉电阻所以天生支持“线与”多个设备同时输出低电平不会短路。这就实现了真正的多主多从总线设备靠7位或10位地址区分。I2C通信以起始条件开始SCL为高时SDA由高变低然后发送设备地址和读写位每个字节传输后接收方需要在第9个时钟周期拉低SDA作为ACK结束时SCL为高时SDA由低变高产生停止条件。数据线只能在SCL为低电平时变化SCL高电平时必须保持稳定这个时序约束决定了I2C很难跑快。标准模式100kHz快速模式400kHz快速模式1MHz高速模式3.4MHz。速率不是想跑多高就能跑多高它受到上拉电阻阻值、总线电容、从机数量和线缆长度的综合影响。我有一条I2C总线挂了8个传感器本来跑1MHz最多只能访问到前几个设备直接把速率降到400k后就全部稳定了。I2C还有一个“时钟拉伸”特性从机可以拉低SCL要求主机暂停等待传感器类芯片很常见。如果是GPIO模拟I2C必须支持检测SCL被拉低的状态否则通信会随机失败。2.4 I2S专为音频流设计的同步串行接口I2S实际是一个音频设备之间的串行总线标准与I2C、SPI不是同一类用途。它至少有三根线BCLK位时钟、LRCK左右声道选择、DATA串行数据。为了给音频codec提供主时钟通常还要接一根MCLK。MCLK一般是采样率的整数倍比如256倍或512倍。I2S的时序核心是一个音频帧分为左声道和右声道两个部分。LRCK的频率等于采样率例如48kHz当LRCK为高时对应右声道为低时对应左声道不同芯片可能有反极性。数据位采用MSB first但标准I2S下数据相对于LRCK边界通常会延迟一个BCLK周期这是协议规格决定的不是bug。计算BCLK频率的公式特别简单BCLK 采样率fs × 位宽 × 声道数。比如48kHz、16bit、双声道立体声BCLK 48000 × 16 × 2 1.536MHz。如果你的I2S配置成32bit槽位那么BCLK就是 48000 × 32 × 2 3.072MHz。很多codec还有左对齐、右对齐等格式配置错就会产生爆音或无声。使用ESP32-C3这类芯片做I2S输出时尤其要注意内部外设映射和DMA配置不是随便初始化一下就一定能出声的。3. 实测与调试逻辑分析仪才是照妖镜纸上谈兵没用通信波形一抓便知。逻辑分析仪是嵌入式调试串行协议最划算的工具几十块钱的百兆采样率逻辑分析仪配合免费软件就能直接解码UART、SPI、I2C、I2S比示波器直观太多。3.1 逻辑分析仪看时序的通用方法采集前最重要的事情是共地。逻辑分析仪的GND不接被测板的GND波形必然一团乱。通道分配也有讲究把时钟线和数据线挨着放方便观察对齐关系。采样率一定要留足裕量至少为目标信号最高频率的5到10倍。比如被测SPI时钟是20MHz那至少要200MHz采样率的设备如果手里分析仪只有24MHz就别拿去测几兆赫兹的SPI老老实实把通信速率降到几百千赫兹再抓。触发设置可以选起始条件或CS下降沿然后设好采集时长。采集结束后直接开启协议解码器填好通道、极性和设备地址比人工看波形省一半时间。我一般是先看解码结果如果数据不对再回头看原始波形定位物理层问题。3.2 UART波形怎么读起始位、数据、停止位UART波形是四个协议里最好认的。默认空闲是高电平然后一个明显的低电平脉冲是起始位接着是低位在前的数据位最后回到高电平的停止位。用逻辑分析仪解码时只要配置波特率、数据位、校验位、停止位软件会直接解出ASCII字符。举个例子发送十六进制0x55二进制就是01010101由于LSB first从低到高输出顺序是1、0、1、0、1、0、1、0波形会呈现规则的方波时钟样式。如果逻辑分析仪解出来是乱码第一步不是怀疑解码配置而是量一下实际波特率在波形上测量一个bit的时长取倒数就是真实波特率。很多时候芯片内置RC振荡器偏差超过3%标称9600实际只有9216直接乱码。解决办法是换外部晶振或改主机波特率去迁就。3.3 SPI时序里最容易懵的四个模式SPI四种模式由CPOL和CPHA共同决定。CPOL决定时钟空闲电平CPHA决定数据采样发生在第一个时钟沿还是第二个时钟沿。很多新手看到模块手册写“SPI Mode 0”就照着配置结果还是不对基本是没搞懂CPOL和CPHA的具体含义。SPI模式CPOLCPHA空闲时钟数据采样沿Mode 000低电平第一个沿上升沿Mode 101低电平第二个沿下降沿Mode 210高电平第一个沿下降沿Mode 311高电平第二个沿上升沿调试SPI时如果读回全FF或全00先别急着怀疑接线重查Mode匹配。W25Q128这类Flash同时支持Mode 0和Mode 3但某些ADC就只认一种模式比如ADS1256配置Mode 1时用Mode 0读出来的数全是乱的。逻辑分析仪解码SPI时还要正确设置CS通道和Mode否则解出的字节错位一个bit都很正常。3.4 I2C时序起始、停止、ACK与总线仲裁I2C波形有两个最关键的边界起始条件是SCL高电平时SDA由高变低停止条件是SCL高电平时SDA由低变高。在数据传输过程中SDA只允许在SCL低电平时变化SCL高电平时SDA必须保持稳定。逻辑分析仪解码I2C时会自动把起始、停止、地址、数据、ACK/NACK都标出来。看I2C波形时我最常提醒自己的是地址格式。7位地址模式下逻辑分析仪有时会显示8位地址也就是把读/写位算进去了。比如设备地址0x50写模式是0xA0读模式是0xA1很多人看到0xA0就疑惑怎么地址对不上。只要理解这个R/W位就能立刻看明白。自由数据模式也一样某些控制器允许不发送设备地址纯粹按起始、数据、停止的裸序列发送适合那些没有标准I2C地址的特殊设备。3.5 I2S的BCLK/LRCK与音频帧对齐抓I2S波形核心是看LRCK与数据位的对齐关系。LRCK跳变代表左右声道切换数据应该在BCLK的某个边沿后稳定并持续一个bit周期。标准I2S下第一个数据位比LRCK边界晚一个BCLK周期而不是正好对齐。如果解码时发现左右声道数据完全颠倒通常是把LRCK极性配反了。音频问题听起来玄实际看波形非常直观。如果播放出来的声音有严重的“沙沙”声但解码数据看起来每个字节又正确绝大多数情况是位宽或槽位配置问题。例如16bit数据放到32bit槽位时如果低位没有按0填充codec会把填充的垃圾bit当作数据噪声就会产生。用逻辑分析仪对比BCLK频率和设定值能快速发现是时钟频率计算错误还是DMA数据搬运错误。4. 实操记录与常见坑速查最后分享一些项目里面沉淀下来的选型习惯和排障经验。这几条不是书上的理论是我实际踩过的坑。4.1 真实项目中的选型对照表不同设备类型往往已经有约定俗成的接口选择我总结了一个比较好用的对照表设备/场景推荐接口原因环境传感器、RTC、EEPROMI2C引脚少设备多速率足够SPI Flash、SD卡、高速ADC、显示屏SPI吞吐率高全双工简单可靠调试日志、GPS、蓝牙/WiFi模块UART异步简单电脑端普及率高音频codec、DAC/ADC音频通路I2S专为音频流设计自带声道区分RS232/RS485工业设备UART可差分远距离传输这个对照表不是死的。同一个器件也可能同时支持多种接口比如部分温湿度传感器SHT3x系列同时支持I2C和SPI。选型时我会先看IO资源再看通信量。IO紧张、通信量小的选I2C通信量大、从机少的选SPI要和外部设备长距离通信就必须用UART转RS485。4.2 IO模拟与硬件外设什么时候选哪个当主控硬件外设不够时用GPIO模拟I2C和SPI是常见方案。但模拟最大的问题不是慢而是时序容易被系统调度打断。尤其在RTOS任务里如果某个优先级更高的任务抢占CPU延时函数就会偏离预期bit时序跟着漂移。稳妥做法是模拟I2C/SPI时临时关中断或者直接改用带超时机制的硬件外设。硬件外设的优势是内置移位寄存器、FIFO和DMA。STM32的HAL库例程已经比较完善初始化结构体配好速率、模式、地址后调用收发函数即可。配合DMA传输大块数据搬运完全不用CPU参与。但硬件外设也有自己的脾气比如某些MCU的硬件I2C对引脚复用和上拉配置非常敏感初始化顺序不对就会卡在忙状态。如果是在电脑上调试传感器可以使用USB转SPI/I2C适配器再配合Python脚本做成产线测试工具非常方便。以SPI Flash为例使用spidev库读JEDEC ID的代码很短import spidev spi spidev.SpiDev() spi.open(0, 0) # 打开SPI总线0从机0 spi.max_speed_hz 1000000 # 读JEDEC ID命令 0x9F后续三字节为厂商和器件ID data spi.xfer2([0x9F, 0x00, 0x00, 0x00]) print(Manufacturer:, hex(data[1]), Device:, hex(data[2]), hex(data[3]))注意xfer2是全双工发一个字节的同时会收到一个字节。想读数据就得在发送命令后补发空字节空字节的填充值通常是0x00。很多刚开始用Python调SPI的人忘了这个机制只发一个0x9F就指望返回数据结果什么也读不到。4.3 常见问题与排查技巧速查通信问题最容易让人心态崩但其实多数都是初级的物理连接问题。我把高频问题整理成一张速查表遇到问题直接对号入座现象可能原因排查方法UART乱码波特率偏差、电平不匹配、TX/RX接反逻辑分析仪量波特率测量bit时长和理论值对比UART收不到双方没有共地、模块供电不足万用表量模块电压确保GND连接SPI读回全FFMOSI/MISO接反、Mode不匹配、CS悬空检查接线调Mode确保CS有明确高阻态控制SPI读回全00从机没上电、MISO引脚没配置为输入量从机电源和信号引脚电压I2C SDA一直被拉低有从机锁死总线、上拉电阻缺失、地址错误断开所有从机逐个枚举重新上电I2C超时/卡死从机时钟拉伸软件模拟没等待改用硬件I2C或增加超时重试I2S无声MCLK未配置、LRCK/BCLK速率错、codec复位脚没拉高先量BCLK频率再查codec寄存器设备管理器报代码12I2C HID设备资源不足或驱动冲突更新主板BIOS和HID驱动禁用再启用设备遇到具体报错时比如Windows上I2C HID设备显示“该设备找不到足够资源可以使用代码12”不要先怀疑传感器坏了大概率是驱动冲突或BIOS里I2C控制器资源分配问题。更新驱动、改变BIOS设置、重新扫描硬件多数能解决。还有一个排查顺序我已经坚持很多年先用逻辑分析仪抓波形确认物理层对再检查初始化和寄存器。因为八成通信失败都是时序和接线问题并不是HAL库用法多难。波形对了代码几乎不用怎么找波形不对寄存器写得再漂亮也没用。最后说点个人体会。四个协议各有各的脾气UART是异步里的大众脸SPI是同步里的速度狂I2C是双线里的多面手I2S是为音频而生。新手阶段不用贪多可以分别做四个最小实验UART回环、SPI读Flash ID、I2C读EEPROM、I2S输出方波先跑通再深入。我自己的选型习惯是板内小数据量优先I2C批量数据交换优先SPI应用日志和无线模块一律UART音频通通走I2S。这套组合帮我少走了很多弯路也希望能给你一个明确的起点。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →