ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

嵌入式必知:UART、I2C、SPI、I2S串行协议实战对比

嵌入式必知:UART、I2C、SPI、I2S串行协议实战对比 搞嵌入式的迟早要跟这几种串行协议打交道。UART、I2C、SPI、I2S这四个缩写基本是每一个硬件工程师、嵌入式开发者的必修课尤其是当你拿到一块新板子、一颗新传感器、一块新屏幕的时候大概率要跟其中一个纠缠几天。这篇文章不打算按教科书方式念一遍而是结合我自己实际调板、抓波形、修驱动的经验把这四个协议放在一起做一次横向拆解把时序、选型、踩坑和逻辑分析仪上的波形都说清楚希望能帮你在下次被某个协议卡住的时候少走一段弯路。1. 四种协议横着看长得都像性格完全不同1.1 先记住一句话同步看时钟异步看波特率我见过很多刚入门的朋友一上来就去背I2C的起始停止条件、SPI的四种模式结果背完就忘因为没抓住主干。这四种协议最根本的分水岭就一条有没有一根独立的时钟线。有专门时钟线的叫同步通信主机什么时候发数据、什么时候采集数据全部跟着时钟沿走。I2C有SCLSPI有SCLKI2S有BCLK它们都是同步协议。没有时钟线、双方各自按约定速率收发的是异步通信典型代表就是UART。双方得提前约定波特率比如115200然后靠起始位和停止位把每一帧数据框出来。打个比方同步通信像两个人踩着同一个节拍跳舞动作整齐划一异步通信更像打电话先喊一声“喂”起始位说完了说“拜拜”停止位双方靠约定好的语速来保证不抢话。想通了这一点后面所有时序细节都可以在这个框架里去理解。1.2 一张表快速建立全貌协议引脚数通信方式方向速度范围拓扑结构最典型场景UART2TX/RX异步全双工一般≤1.5Mbps点对点调试日志、GPS模块、蓝牙模块I2C2SDA/SCL同步半双工一般≤3.4Mbps多主多从传感器、EEPROM、PMICSPI4MOSI/MISO/SCLK/CS同步全双工可达几十Mbps一主多从Flash、ADC、屏幕、SD卡I2S3BCLK/WS/SD同步半双工/全双工取决于音频采样率一主多从DAC/ADC、音频编解码器这张表只是热身真正要理解的是“为什么会有这四个协议”以及“厂商为什么给这个器件选了I2C而不是SPI”。1.3 为什么一个板子上能同时看到它们拿一块常见的开发板来说调试串口走UART姿态传感器走I2CFlash和屏幕走SPI音频Codec走I2S。这其实不是随便安排的而是每种协议对应了一类需求的“最优解”UART负责“简单可靠的点对点”例如调试程序、接收GPS NMEA报文I2C负责“少引脚挂很多设备”一条总线上几十个从机只需要两根线SPI负责“高速搬数据”跑Flash、跑DMA、跑大吞吐量外设I2S负责“音频流的连续搬运”它连寻址、应答都不做只专注把采样值按节奏送进DAC。所以不要问“哪种协议最强”要问“我这颗外设最适合用哪种总线连接”。下面我从最基础的UART开始逐个拆。2. UART最老牌也最不会过时的串行协议2.1 一帧数据的结构起始位、数据位、停止位UART通信里空闲时TX线保持高电平要发数据时先拉低一个位时间这个低电平就是起始位。接收端看到这个下降沿就知道“数据要来了”然后按约定好的波特率一位一位采样收完数据位后TX线再拉回高电平至少一个位时间这就是停止位。这中间还能选择插入一个校验位常见的有奇校验Odd、偶校验Even也可以直接不要校验None。一帧典型的数据就是起始位1位 数据位8位 校验位0或1位 停止位1位或2位。比如最常用的8N1就是8个数据位、无校验、1个停止位。我调板时经常遇到一个问题两边都写了“115200 8N1”但就是乱码。这时候我第一步不会去怀疑芯片坏了而是先看两边的时钟误差。UART虽然简单但它对波特率误差很敏感收发双方的波特率偏差超过3%左右长期跑就会出现位错位、数据乱码。具体原因不复杂接收端在起始位下降沿开始计时然后分别在每个位的中间点采样如果时钟偏得太厉害采到的位就跟发送端对不上。2.2 波特率分频与16550标准的那些事UART波特率怎么来的一般单片机内部有一个UART外设时钟比如72MHz或者80MHz然后通过一个分频寄存器把它降到一个指定的波特率。STM32的HAL库里有HAL_UART_Init()它会根据结构体里的波特率自动计算分频值但你要是用了古老的8051内核串口分频方式就不一样了经常算出来的实际波特率和理论值有微小偏差。这里必须提一下16550这个行业标准UART。PC机历史上用过的串口芯片从8250到16550一路演进16550最重要的贡献就是引入了16字节FIFO。在老版8250里每收一个字节就要产生一次中断CPU在高频串口数据下很容易被打爆16550把收到的数据先攒进FIFO攒够设定的阈值比如8字节再中断一次CPU这样CPU压力大幅下降。现在很多USB转串口芯片、FPGA里的UART IP核内部FIFO设计依然延续这套思路。2.3 USB转串口芯片的驱动坑FT231X和FT232R现在笔记本没有串口大家基本靠USB转串口芯片最常见的就是FTDI家的FT232R和FT231X。这两个芯片都很稳但我在Windows和Linux上都踩过驱动坑。FT232R是老芯片主驱动兼容性好但新版操作系统有时候会出现“插上后认成未知设备”的情况。最靠谱的办法是去FTDI官网下载对应版本的驱动而不是直接依赖Windows自动更新。FT231X是相对新的型号它跟FT232R的驱动基本通用但有些低价模块用的是冒牌芯片插上后Windows会直接报错这时候就不是驱动问题了而是芯片本身要换。另外提醒一句FPGA调试时经常要往UART发大量数据这时候Fluke、ftdi的芯片FIFO做得大丢包概率低普通国产CH340在低速下完全够用但上到1.5Mbps以上时就要谨慎了。我做过一次实测同样的程序FT232R在3Mbps下稳定收数据CH340到2Mbps已经开始偶尔丢字节。2.4 什么时候你会需要UART转其他东西串口这个形态很老但生命力极强。很多仪器仪表、工业设备至今还在用串口对外通信所以衍生出了各种转换器UART转GPIB、UART转CAN、USB转GPIB等等。我之前调试一台老式电源它对外只有GPIB接口而电脑没有GPIB卡最后就是用UART转GPIB模块通过串口命令去控制硬件原理其实还是那套串口把协议帧发给转换器转换器再用GPIB时序跟仪表握手。3. I2C两线制多设备总线的设计智慧3.1 为什么SDA和SCL要开漏加外部上拉I2C最大的特色就是只用两根线SDA数据线和SCL时钟线。但这两根线不能像普通GPIO那样推挽输出必须做成开漏结构然后在外部接上拉电阻。为什么要这么干因为I2C总线上可以挂多个设备谁都可以拉低总线但谁都不能强行拉高否则两个设备一个输出高一个输出低直接短路烧芯片。开漏结构加上上拉电阻就实现了“线与”逻辑任何设备都可以把总线拉低总线空闲时靠上拉电阻恢复高电平。这就是I2C仲裁机制的物理基础——当两个主机同时在总线上发数据时它们都会监视SDA电平如果发现自己想发高电平但总线上却是低电平就知道有人抢线主动退出。上拉电阻的取值也很有讲究。电阻太小灌电流太大推不动低电平电阻太大总线电容充放电太慢速度上不去。常见的取值是4.7kΩ对应100kbps标准模式没问题如果跑400kbps快速模式可以换2.2kΩ甚至1kΩ具体要根据总线长度和挂载设备数量来算。3.2 I2C时序核心起始、停止、ACK、NACKI2C时序归纳起来就四个关键动作起始条件SCL为高时SDA产生一个高到低的跳变停止条件SCL为高时SDA产生一个低到高的跳变ACK应答接收方在第9个时钟周期把SDA拉低表示“收到继续”NACK不应答接收方在第9个时钟周期让SDA保持高表示“不要了”一般用于主机结束读取。所以I2C一个字节的传输其实是9个时钟周期8位数据 1位应答。I2C设备地址通常7位加上方向位读1写0组成8位把起始条件后的第一字节称为地址字节。7位地址意味着理论上一条总线最多挂128个设备但实际受总线电容和地址冲突限制远到不了这个数。这里有个初学者容易晕的点I2C的时序图上SDA变化都发生在SCL低电平期间SCL高电平期间SDA必须保持稳定。因为接收方是在SCL高电平期间采样SDA的如果你在SCL为高时乱跳SDA会被当成起始或停止条件直接破坏通信。3.3 寄存器读写与数据帧格式大多数I2C设备都是靠“寄存器地址”来访问内部数据的。以向传感器写配置为例一帧数据通常是起始条件 从机地址写方向位 寄存器地址 要写入的数据 停止条件。读数据稍微多一步起始条件 从机地址写方向位 寄存器地址 重复起始条件 从机地址读方向位 读取N个字节数据 主机发NACK 停止条件。这里多了一个重复起始条件Restart意思是总线不释放主机在读完寄存器地址后紧接着重新发一个起始条件再切到读模式。为什么不直接发停止再重新开始因为如果先发停止总线就可能被其他主机抢走没法保证“读同一个寄存器”这个操作的原子性。关于数据帧我踩过一次很典型的坑RTD温度传感器芯片规格书上写寄存器地址是16位我按8位发了半天数据全是0xFF。翻手册才发现这款芯片的寄存器地址是两字节必须先发高字节再发低字节。所以用任何I2C器件之前第一件事是确认它的寄存器地址宽度。3.4 自由数据模式、从机主动更新和I2C扩展不是所有I2C设备都按寄存器模型工作。有些芯片有“自由数据模式”比如某些触摸控制器它会把触摸坐标以事件流的方式持续发出来主机不做寄存器寻址直接读事件队列就行。这种模式下时序更简单但要注意读空以后设备会返回0xFF之类的无效数据。另外有个需求经常被问到I2C从机能不能主动更新主机寄存器I2C标准里从机是不能主动发起通信的因为所有传输都由主机产生时钟。但实际设计中从机可以把某个“事件标志位”置位主机下次轮询时读出这个标志就知道从机有更新。我做过一个方案从机内部有新的测量结果时会拉低一根独立的INT引脚主机检测到INT后立刻发起I2C读操作这样比纯轮询省电多了。I2C要扩展设备数量有两条路一是端口扩展芯片比如PCF8574用I2C控制8个GPIO的输出相当于把GPIO数量扩展二是总线复用器比如TCA9548A它可以把一条I2C总线分成8路让多个相同地址的从机挂在不同通道上避免地址冲突。3.5 实际项目里的I2C坑GT911、软件模拟、PMBus我在一个触摸屏项目里被GT911折腾过两天。现象是I2C通信经常失败读回的全是0xFF或者第一次握手成功运行几分钟后掉线。排查到最后发现两个原因一是GT911的上拉电阻焊接虚焊导致总线一直处于半拉不拉的电平状态二是我在初始化时序里漏了等待它内部固件启动完成芯片上电后立刻被读写它根本不响应。解决办法很简单复位后延时至少50ms再访问。另一个高频场景是软件模拟I2C。很多单片机I2C硬件外设引脚固定但PCB布线已经定死了只能换IO口软件模拟。我写过RDA5807收音机芯片的软件I2C驱动核心就是GPIO翻转加延时。软件模拟的坑在于延时要准尤其是起始、停止条件和每个时钟周期的SDA建立时间要留够。我当时用逻辑分析仪抓完波形发现SDA在SCL高电平期间发生跳变就是延时写得太短导致的。最后必须提PMBus。PMBus是基于I2C的电源管理总线很多电源芯片如TI的TPS系列数字电源用它来配置输出电压、读取电流电压。PMBus和普通I2C的区别是它定义了一套标准命令集比如VOUT_COMMAND、READ_IOUT芯片有一套标准的写读流程。所以你在代码里看到的还是熟悉的I2C底层但上面跑的是PMBus协议帧。还有一类网络PHY芯片的配置管理规范上要求用MDIO接口但Linux下有些驱动为了绕开MDIO的硬件限制会通过I2C去读写PHY寄存器这在项目里也确实是存在的做法。4. SPI高速全双工却是四个里最容易“野”的4.1 四线制的分工与无应答机制SPI用四根线SCLK时钟、MOSI主机输出从机输入、MISO主机输入从机输出、CS片选。跟I2C相比SPI最大的特点是全双工主机在SCLK的某个边沿发送一位同时在另一个边沿采样一位一个时钟周期就能同时收发一位数据所以一个时钟周期等于交换了一位数据。但它有一个“软肋”SPI没有统一的应答机制。I2C每个字节都有ACK位从机不响应主机马上知道SPI从机不响应时主机只会读到一堆没意义的0xFF或者随机电平。所以用SPI调一个不熟悉的器件时我第一反应是抓波形看MISO有没有数据而不是傻傻地在驱动里翻寄存器。这在调试阶段特别重要。SPI的另一个特点是CS片选线拉低CS表示选中某个从机拉高CS表示释放从机。片选是SPI多设备拓扑里区分“跟谁说话”的唯一手段所以CS的控制必须非常规范不能有毛刺。4.2 四种时序模式CPOL和CPHA到底怎么组合SPI时序模式大家应该都背过CPOL决定时钟空闲电平CPHA决定采样边沿。组合起来四种模式模式CPOLCPHA空闲时钟采样边沿常见器件Mode 000低上升沿W25Q系列Flash、SD卡Mode 101低下降沿部分ADCMode 210高下降沿部分LCD屏Mode 311高上升沿部分EEPROM我在调W25Q64 Flash时用的就是Mode 0。这里有个经验读不到正确数据时先把CPOL和CPHA各翻转一次试试很多芯片手册对模式的描述不直观用逻辑分析仪抓完波形对照数据手册很快就会锁定正确模式。另一个容易忽略的参数是复位信号的时序顺序。SPI器件上电后主机最好先把CS拉高再初始化SCLK极性最后再发起访问。如果CS在SCLK还没稳定时就先拉低了有些从机内部状态机会被异常复位返回的数据全是乱的。4.3 硬件片选与软件片选怎么选SPI的CS有两种实现方式一是SPI外设自带的硬件CS引脚NSS二是用普通GPIO去软件控制CS。STM32的HAL库中配置SPI_InitTypeDef时可以设置NSS为硬件还是软件模式。我自己的经验是如果SPI总线上只挂一个从机直接硬件CS省心如果挂多个从机要用软件CS。因为硬件CS在切换从机时经常需要额外的配置和时序等待而且有些MCU的硬件NSS在从模式上电瞬间行为很诡异。软件片选的好处是灵活但要注意切换CS的顺序先拉低目标从机的CS再发数据发完最后一个字节后等SPI完全移位结束再拉高CS。如果你在SPI还在发送时就把CS拉高某些从机会把当前字节丢掉或者产生一个“CS异常终止”的标志。这里顺带说一个量产的坑CS引脚和其他信号线之间如果布线太近CS线上的毛刺会误触发从机的选中。我见过一次偶发性的Flash读写故障排查到最后是CS走线跟SCLK相邻时钟边沿串扰把CS拉低了一下。解决方式是让CS远离SCLK或者给CS加上下拉电阻。4.4 SPI的CS最小间隔问题CS在高电平时从机要完成内部数据的整理与准备所以两次片选之间必须留一个最小间隔。这个时间在Flash芯片手册里通常叫tCSH或者片选高电平时间常见值是几十纳秒。但如果你用的是DMA连续搬运中间CS拉高时间可能只有一两百纳秒这在高速下容易触发错误。我之前测过普通W25Q系列Flash主频设到40MHz两个传输间的CS高电平必须大于100ns左右否则读取的数据偶尔出现跳变。这个问题在FPGA里出得更多因为FPGA的SPI控制器是纯逻辑实现的程序员很容易把CS释放时机写得太激进。所以做SPI控制时别忘了在最后一个字节的最后一个时钟边沿之后再插入几个周期的延时再拉高CS。4.5 SPI的高性能玩法DMA、FPGAADC、RK3588引导SPI的数据吞吐量一上来就会用到DMA。STM32的CubeMX里配SPI DMA很简单初始化SPI外设后再配置一个DMA通道把HAL_SPI_Transmit_DMA()挂进去即可。但用DMA有几个注意点DMA传输期间CPU不要碰SPI的数据寄存器如果同时收发要用双缓冲模式DMA中断里记得清理标志位否则第二次传输可能不触发。在FPGA端SPI最经典的应用就是接高速ADC比如AD7606。FPGA做SPI主机的时钟可以自由设得很高比如25MHz、50MHz关键是采样数据的处理要跟上节奏。这类设计常见的毛病是FPGA的SPI状态机在时钟很高时MISO采样点刚好落在信号翻转沿上导致采到的数据位时对时错。解决办法是把采样点移到时钟沿的中间位置比如在SCLK下降沿采MISO的数据而不是在上升沿采。还有一个我最近在RK3588平台项目里遇到的组合SPI NOR Flash放引导程序系统盘用PCIe NVMe SSD。这种混合存储方案的好处是上电时BootROM从SPI NOR加载极小的引导代码引导代码再初始化PCIe控制器去加载SSD里的系统兼顾了NOR Flash的简单可靠和NVMe的大容量。但坑也很明显SPI NOR的启动速度太慢如果引导代码超过一定大小上电时间会飙得很高。我当时是在SPI NOR里只放了一个迷你引导把真正的BootLoader压缩到SSD里启动时间从8秒降到了3秒多。另外提一句Python调USB模拟SPI接口这种方案一般用FT232H之类的芯片在电脑上跑pyftdi库直接对着SPI总线上挂的器件读写。我在调试新器件时经常这么干因为不用写MCU固件直接在PC上敲命令试波形效率高很多。这几个场景加起来说明SPI是一把“性能上的好刀”但也是一把“容易割手”的刀。5. I2S为音频而生的专用总线5.1 I2S的三根线BCLK、WS、SDI2S跟前面三个都不太一样它从设计之初就是为音频流服务的只关心一件事把采样数据按节奏送给DAC或者从ADC收回来。它通常有三根线BCLK位时钟每个时钟周期移一位音频数据WS声道选择也叫LRCLK高电平代表右声道低电平代表左声道不同芯片可能相反SD串行数据音频数据线。以常见的48kHz采样率、16位精度双声道为例BCLK频率大概就是48kHz×16×21.536MHz。这个数字是固定的由采样率和位宽计算出来的所以I2S的节奏其实是“数据跟着时钟自然流动”不需要像UART那样去对波特率。I2S的标准时序是WS变化后数据在BCLK的第二个上升沿开始送最高位MSB而且MSB比WS变化晚一个时钟周期。这种设计保证了发送方和接收方不需要知道具体每帧有多少位接收方只要跟着WS找左右声道跟着BCLK移数据就行了。5.2 I2S的主从角色和常见变体I2S总线也有主从关系主机产生BCLK和WS从机比如DAC芯片被动接收。一个系统里MCU和Codec都可以当主机我一般习惯让MCU当I2S主机因为Codec从机的频率跟随比较简单。但也见过高精度音频系统让独立晶振的DAC当主机MCU做从机这样能避免抖动。I2S的变体很多主要区别在“对齐方式”标准I2SMSB在WS变化后的第二个BCLK上升沿开始左对齐Left JustifiedMSB在WS变化后的第一个BCLK上升沿就开始跟WS同步右对齐Right Justified数据靠帧末尾对齐常用于部分DACTDM模式一根SD线上分时隙传多个声道的采样数据比如8路麦克风阵列。选编码器或者DAC之前我建议先确认它用的是哪种对齐模式否则时序对不上出来的声音要么是噪声要么左右声道对调。除了I2S还有PCM接口这种类似的东西很多Codec把I2S和PCM概念混着用真要说区别PCM接口支持更灵活的时隙配置和更长的帧长本质上还是同一套采样时钟体系。5.3 实操记录ESP32-C3的I2S输出和逻辑分析仪波形ESP32-C3自带的I2S外设用起来还算顺手。我之前用它接了一片I2S DAC把MCU里解码好的音频数据送出去播放。ESP32-C3的I2S配置里有一个关键参数就是WS和BCLK的极性跟标准I2S可能差半拍如果你直接套用标准I2S的初始化代码输出时DAC偶尔会发出沙沙声我查了半天最后是在i2s_config_t里把ws_pol翻了一下才正常。用逻辑分析仪抓I2S波形时我习惯同时抓BCLK、WS、SD三根线然后把采样率设到BCLK频率的4倍以上。看图时重点确认三件事WS的翻转频率是否等于采样率、SD上的数据是否从MSB开始、BCLK的占空比是否接近50%。我曾经用某款逻辑分析仪解I2S协议发现解出来的音频数据全是乱的后来发现是分析仪的I2S解码器把左右声道反了这种时候不要盲目信工具自己对照芯片手册数一下位顺序更可靠。6. 横向对比到底怎么选6.1 一张大表算总账维度UARTI2CSPII2S引脚数2243时钟无异步SCLSCLKBCLK工作方式全双工半双工全双工点对点音频寻址方式无7/10位地址片选线无应答机制无ACK/NACK无一般无多设备不支持点对点支持一总多挂支持多从靠CS一般一对多最大速率较低受波特率误差限制标准100k/快速400k/高速3.4M可轻松上几十M跟采样率和位宽走协议复杂度低中中低但变体多典型外设GPS、蓝牙、调试口传感器、EEPROM、PMICFlash、ADC、屏幕DAC、ADC、音频Codec调试难度低中地址、时序都要看中采样点敏感中对齐方式需确认这张表不是用来背的而是选型时的参考外设只需要几百比特每秒的配置数据时我会默认走I2C需要高速搬大块数据时我会默认走SPI只跟另外一块板子或设备通信时我会默认走UART传输的是音频采样数据时直接上I2S。6.2 跟CAN、GPIO、SDIO、MDIO的边界很多人会把串行协议混在一起聊其实边界很清楚CAN也是串行总线但它最大的价值是多主、长距离、抗干扰和仲裁机制常用于汽车和工业总线。CAN跟UART、I2C在物理层和帧结构上完全不同它用的是差分信号可以在一条线上跑几十米而I2C和SPI一般只适合板内短距离。GPIO不是通信协议它就是最原始的输入输出引脚但很多软件模拟协议比如软件I2C、软件SPI本质上是GPIO的时序模拟所以大家经常把它们放在一起比较。SDIO是SD卡用的并行/串行接口用来跑SD卡或者WiFi模组它跟SPI有交集SD卡支持SPI模式但SDIO的协议栈更复杂速率也更高。MDIO是管理PHY芯片寄存器的专用两线接口跟I2C地位类似但不是电气兼容的关系所以Linux PHY驱动默认走MDIO如果硬件上非要“不使用MDIO、使用I2C”去读寄存器就要靠驱动代码在MDIO总线上套一个I2C的适配层相当于软件上“挂羊头”实现寄存器管理。6.3 选型决策树和混合使用经验我自己总结了一个比较务实的选型流程先看引脚数量预算只有两根线富余基本只能选I2C或UART再看数据量配置类低速交互选I2C流式高速数据选SPI接着看是否需要多设备需要多设备又希望省引脚选I2C总线的同时可以挂I2C多路复用器最后看距离和干扰板间通信优先UART或CAN板内短距离优先I2C/SPI/I2S。混合使用的经验也分享一下我做过一个音频采集系统主控和Codec用I2S跑音频数据主控和传感器用I2C跑配置主控和外部模块用UART对接另外Flash用SPI存音频样本。四套协议在同一个芯片上同时跑关键就是中断优先级和DMA的规划。I2S和SPI的数据流尽量走DMAI2C和UART的配置消息频率低可以靠中断处理否则高频率中断会互相抢占导致I2S的DMA下溢声音就会断断续续。7. 常见问题与排查技巧实录7.1 问题现象速查表现象最可能的原因排查方向UART乱码波特率不匹配、双方时钟误差大检查初始化参数和时钟树分频I2C读全是0xFF地址错误、器件未上电、寄存器宽度不对用逻辑分析仪抓起始条件后的地址字节I2C卡死在等待ACK从机地址不存在或总线被拉死量SDA和SCL电平检查上拉SPI读回的MISO恒为1MOSI方向配反、从机没被CS选中查CS时序和MOSI/MISO是否接反SPI数据偶尔错位采样边沿与器件不匹配、CS间隔不足翻转CPOL/CPHA加CS高电平延时I2S播放有噪声对齐方式不匹配、WS极性反左右对齐切换、翻转ws_polWindows提示“I2C HID设备找不到足够资源代码12”I2C控制器资源被占用或驱动冲突检查设备管理器里的I2C控制器禁用再启用USB转串口插上没反应驱动问题或芯片造假换官网驱动确认芯片型号7.2 逻辑分析仪永远是你的第一取证工具排查通信协议问题时我永远建议先抓波形再改代码。一个逻辑分析仪花不了多少钱但比你在万用表前面猜半天强得多。特别是I2C和SPI这种有明确时钟的协议抓到波形后对照数据手册去check问题往往一眼就能看出来是地址对不上、数据位顺序反了还是没有ACK。抓波形有几个关键习惯要养成采样率至少设成信号最高频率的4倍以上否则边沿位置不准抓I2C时把SDA和SCL同时接上推荐用10kΩ左右的电阻做外部上拉保证无设备驱动时是高电平抓SPI时把CS、SCLK、MOSI、MISO四根线全接上这样才能看到主从交互完整过程抓I2S时重点抓WS和BCLK确认声道切换和数据位顺序。7.3 我踩过的几个坑直接说结论最后说几条个人经验都是交过学费的一是UART接反TX/RX几乎每个新手都会遇到但反过来也暴露了一个问题很多廉价调试模块没有做交叉处理你按“TX接RX”的规则接好之后还是有数据乱象多半是模块内部已经做了一组交叉你又交叉了一次。接反的典型现象是“能发不能收”排查时先把TX和RX互换一次试。二是I2C总线被某个从机拉死的现象是SDA、SCL电平卡在0.5V左右而不是0V或3.3V这时候挨个拔设备拔到哪个总线恢复就是哪个设备的毛病。I2C设备在没有电源时如果它的I2C引脚恰好是开漏上拉到自己的VDD那这个VDD没起来就会把整条总线钳制住。三是SPI模式下从机驱动能力不够时MISO线上的上升沿会特别缓如果SCLK频率很高主机采样的电平可能还在翻转中这个问题的解决办法是降低SCLK、改用边沿更陡峭的从机或者给MISO串一个小电阻、加大驱动。四是I2S对齐模式搞错了不一定是纯噪声很多时候是数据整体延迟了一位或几位导致音频听起来像“金属声”或者“轻飘飘”。遇到这种情况删除逻辑分析仪的解码对照数据手册数“从WS变化到MSB之间隔了几个时钟”是最靠谱的做法。我个人在实际项目里曾因为I2S数据延迟一位整整排查了一下午最后发现就是少配置了一个ws_pol的极性参数。这四个协议说到底是工程师手里的一组工具。它们没有谁替代谁的关系只有谁更适配当前任务的关系。希望这篇对比能把你在选型和调试时的思路理得更顺下次再拿到一颗新芯片不管它是I2C、SPI还是I2S你都能少走一些弯路早点把波形抓出来把设备跑起来。
返回列表