SPI vs I2C:嵌入式总线选型与调试实战指南

发布时间:2026/10/25 1:12:50
SPI vs I2C:嵌入式总线选型与调试实战指南 说实话第一次看到“从沙子到车辙”这个系列名的时候我以为是讲硬件从零开始DIY的结果读了几篇才发现这是讲嵌入式底层通信的从“沙子”里的硅片讲到“车辙”一样清晰的信号路径脑洞确实大。这一篇聊的是“从片内到片间”也就是芯片和芯片之间、模块和模块之间怎么把数据说清楚。而说到这个绕不开的两大主角就是SPI和I2C。做嵌入式这么多年和这两种总线打的交道实在太多了。很多刚入门的朋友一上来就背协议时序图背完就忘遇到波形不对还是不会调。这篇我打算换个角度用做事踩坑的经验来讲清楚SPI和I2C到底是什么它们各自的性格、脾气以及哪种场景选谁更合适。顺便把搜索热词里大家最关心的几个问题——比如I2C开漏模式、SPI硬件片选和软件片选怎么选、软件SPI和硬件SPI的差距、SPI接DMA要怎么配——一并梳理清楚。1. 内容整体设计与思路拆解1.1 两种总线其实是两种世界观先聊个最基础但很多人没深想的问题为什么有了UART还要搞SPI和I2CUART是异步的双方各用各的时钟靠起始位和停止位把数据框起来。它的好处是简单两根线就能跑但缺点是速度上不去也不适合一拖多。而SPI和I2C解决的核心问题都是“同步通信”就是收和发共用同一个时钟节拍时序精确可控。这两兄弟在大学教材里经常挨着讲但实际上是两种完全不同的设计思路。SPI的全称是Serial Peripheral Interface串行外设接口。它是一个主子架构的总线主设备提供时钟SCLK数据在MOSI和MISO两根线上各走各的全双工想多挂设备就靠片选信号CS一个个点名。它的风格是“我给你时钟你就得立刻回话”。I2C的全称是Inter-Integrated Circuit集成电路间总线。它只有两根线一根时钟SCL一根数据SDA半双工。它带设备地址总线上挂一堆设备靠地址区分谁应答。它的风格是“我叫你你应一声我才继续说”。用生活化的类比来说SPI像是上课点名老师挨个儿被点到谁谁就回答问题没点到的不要插嘴效率高但人多的时候老师点名点到手软。I2C更像对讲机群聊大家一起听一个频道但每条消息带个呼号只有呼号对得上的那台才回应线少但规矩多。这个本质差异决定了后续的选型。SPI适合量大、要求速度、主从关系明确的场景比如Flash存储、SD卡、ADC采样、屏幕刷新。I2C适合数量多、速率要求不高的传感器、RTC、EEPROM这类设备。1.2 选型思路速度之外还要看什么很多人选总线只看速度SPI几十MHzI2C通常几百kHz到几MHz那是不是无脑上SPI就行了不是这样的。我见过有人用I2C读一个加速度计每秒采样几百次完全跑得动但他偏要换SPI结果改动了一大堆驱动代码把CS引脚接错折腾了两天才好。反过来也有朋友在SPI上挂了一个温湿度传感器非要按I2C的时序去读卡的最后连设备地址都找不到。选型的时候速度只是一个维度。还要看引脚数量够不够。SPI最少四根线I2C只要两根。在引脚紧张的板子上I2C的复用优势就很明显。还要看设备数量和寻址方式。I2C天然支持一总线多设备SPI每多挂一个设备就要多占一个片选引脚。还有实时性的要求SPI的时序更确定适合硬实时场景。我把这两种总线的关键属性整理成一个对照表大家在选型的时候可以对着看特性SPII2C线数4根SCLK、MOSI、MISO、CS2根SCL、SDA通信模式全双工半双工速率范围通常数十MHz通常数百kHz到数MHz设备寻址靠硬件片选CS靠7位或10位软件地址拓扑结构主从点对点为主多主多从总线型电气特性推挽输出开漏输出加上拉适合场景高速数据、存储、采样低速传感器、RTC、EEPROM、配置芯片拿做项目来说我常挂在嘴边的一句话是SPI干的是累活I2C干的是杂活。累活要的是力气杂活要的是人脉。理解了这个比喻选型思路就清晰了一大半。2. 核心细节解析与实操要点2.1 SPI的四种模式你真的会配吗很多朋友在SPI上踩的第一个坑就是模式配错。SPI有四种模式由CPOL时钟极性和CPHA时钟相位共同决定。CPOL决定空闲时时钟是高还是低CPHA决定数据是在第一个跳变沿还是第二个跳变沿采样。具体来说模式0CPOL0CPHA0空闲时钟低第一个沿采样模式1CPOL0CPHA1空闲时钟低第二个沿采样模式2CPOL1CPHA0空闲时钟高第一个沿采样模式3CPOL1CPHA1空闲时钟高第二个沿采样绝大多数Flash芯片、SD卡、显示屏都支持模式0和模式3你在网上搜别人的代码很多默认配的也是模式0。但这不是想当然的我遇到过一款传感器手册里写得明明白白是模式1有人没看手册直接用模式0去读出来的数据全是歪的波形看着好像有数据但整理出来基本是乱码。配模式这件事唯一的正确做法就是翻芯片手册找到SPI章节的时序图看清楚CPOL和CPHA然后和你代码里的配置对应上。没有第二种捷径。如果条件允许先用示波器或者逻辑分析仪抓一下主设备发出的时钟和数据波形对照手册觉得没问题再往深处调试。2.2 I2C的开漏模式与上拉电阻搜索热词里有一句“I2C的推挽模式和开漏模式”这个值得好好展开。I2C的SDA和SCL必须是开漏输出这是协议层面规定的不是硬件设计时随随便便定的。开漏的意思是芯片内部不主动输出高电平只能把引脚拉低或者释放。释放之后引脚的电平由外部上拉电阻决定。这个设计精妙在哪在于它天然支持“线与”逻辑。任何一台设备想拉低总线都能把总线拉低多个设备同时驱动也不会出现短路冲突。这在多主设备场景下特别重要因为总线仲裁就是靠“看谁先把总线拉低”来判定的。但开漏也带来一个非常实际的问题速度上不去。因为释放引脚之后高电平要靠上拉电阻慢慢把寄生电容充满电阻越大上升沿越缓速率越高越容易出错。所以上拉电阻的取值是个权衡常见的取值范围在1k到10k欧姆之间。具体怎么选我之前做过一个项目I2C总线上挂了三个从设备总线长度大概10厘米我用的是4.7k的上拉跑400kHz标准模式很稳定。后来加了一块距离稍远的扩展板总线长了之后4.7k就偶尔出现ACK丢失换成了2.2k才好。一般来说低速模式100kHz总线短可以用10k或4.7k省电标准模式400kHz推荐4.7k高速模式1MHz以上总线短用2.2k或1k更稳还有一件事容易被忽略就是上拉电阻接的电源电压要和器件电平匹配。比如MCU是3.3V但I2C从设备是5V的这种场景电平转换不是简单串联电阻能搞定的。最可靠的办法是用电平转换芯片或者用支持混合电压的开漏结构自己搭离散MOSFET转换。网上热词里“I2C需要电平转换吗”问的就是这个我说句经验之谈如果两边电平不一致接口处标称又没写“电平兼容”那大概率需要做转换。2.3 硬件片选与软件片选之争SPI的片选看起来最简单就一根线拉低选通、拉高释放。但这里藏着一个老生常谈的问题硬件片选硬件NSS自动控制和软件片选GPIO手动控制到底用哪个不少MCU的SPI外设自带NSS引脚启用硬件片选后外设会自动管理片选的时序。听起来很智能但实际用起来反而容易出事。因为硬件片选在一些MCU上的行为和预期不一样尤其在连续传输多字节数据时NSS可能在每个字节之间跳变一次导致从设备以为传输结束了直接把数据丢掉。我调过的某个STM32型号就有这种表现当年排查了好几个小时最后把CS改成软件控制立刻就好了。软件片选的好处是控制权完全在你手里。你可以在一次事务开始之前把CS拉低传输结束后手动拉高中间不管发多少个字节都不会中断。对多数从设备来说这是最稳的。那什么情况用硬件片选有的MCU结合DMA做数据流传输时硬件NSS的翻转和DMA搬运配合得非常好能省CPU干预。但现在很多工程师的做法是默认软件片选除非你对这颗MCU的硬件NSS行为非常了解并且验证过它不会中途乱跳。还有个小坑是片选引脚的电平极性。绝大多数器件是低有效但有些屏幕模块、射频芯片是高有效片选。代码里如果默认拉低选通遇到高有效器件就永远选不中。解决办法还是看手册看清CS的active level然后代码里配置好。3. 实操过程与核心环节实现3.1 用硬件SPI驱动一款Flash存储芯片纸上谈兵没意思来点实际的。我之前做过一个数据记录器的小板子MCU要把采集的数据存进一颗SPI接口的NOR Flash。当时用的具体型号不说了反正时序是标准SPI模式0。整个配置流程可以分成几步第一步是把SPI外设和GPIO初始化好第二步是CS引脚拉高Flash默认不选中第三步是写使能第四步是发写指令和地址第五步是把数据发出去最后轮询状态寄存器等忙标志清掉。初始化SPI的时候时钟速率我当时设的是系统主频的四分之一分频。选四分之一不是拍脑袋的因为Flash手册里写的最大时钟是某一档我的分频之后约等于那个值的一半留了余量。如果分频太狠速度上不去写入很慢如果分频太激进时序余量不足遇到批次不太一样的芯片就容易偶发错误。我曾经图快把SPI时钟调到和手册极限值差不多的位置板子单独跑测试没问题但装上整机之后因为电源纹波变大偶尔写出来的数据校验不过把时钟降回来之后就再也没出过事。所以SPI时钟设置的第一个原则是别贴着极限跑。代码层面的核心逻辑大致是flash_cs_low(); spi_write_byte(CMD_READ); // 读指令 spi_write_byte(addr 16); // 地址高字节 spi_write_byte(addr 8); // 地址中字节 spi_write_byte(addr 0xFF); // 地址低字节 for (i 0; i len; i) { buf[i] spi_read_write_byte(0x00); } flash_cs_high();这段代码有一个容易被忽略的点读数据时主设备必须不停地给从设备提供一个时钟才能让数据出来而这个“喂”时钟的动作在SPI里就是发送任意字节。我这里发的0x00有些驱动里写成0xFF也没问题只要保证时钟在跑就行。有的同学以为读数据不用发东西结果时序出来对不上就是这个原理没理解透。这个案例里最重要的是理解CS和指令的配合。在一个片选周期内你可以连续发“读指令地址若干数据”作为一个完整的事务。同理写操作也要把“写使能指令”和“页编程指令”分开两个片选周期完成。很多新手把指令全部串在一起没有单独做写使能结果写操作永远不生效查半天找不到原因。3.2 软件SPI和硬件SPI到底差在哪搜索热词里有“软件SPI和硬件SPI的区别”这个问题问得特别好。我说的软件SPI就是不用外设直接用GPIO翻转时钟和数据线来模拟SPI时序。硬件SPI则是MCU内部集成的SPI外设自动完成移位和电平翻转。软件SPI和硬件SPI的最核心区别是速度。硬件SPI的时钟可以跑到几十MHz软件SPI我实测在普通8位MCU上能稳定跑的模拟速率通常也就是几百kHz到一两MHz。因为每一位数据都要靠CPU去翻转引脚再怎么优化也甩不开这个瓶颈。但软件SPI的价值在于引脚任意、模式随便改、调试起来直观。我自己的经验是如果在做原型验证或者临时替代方案软件SPI完全可以顶上。比如某个模块的硬件SPI引脚被别的功能占用了我就用两个普通GPIO模拟SPI跑个1MHz给一颗温湿度传感器做采样完全够用。但如果做产品量产或者数据量很大那还是老老实实硬件SPI加DMACPU的负担差远了。软件SPI实现的时候要注意时钟极性和相位的模拟尤其要保证数据的建立时间和保持时间。我写模拟SPI时习惯先在时钟边沿之前把数据线拉好然后再翻转时钟。这个顺序不能反反了会让从设备采样到不稳定数据。3.3 I2C读写EEPROM的完整流程I2C总线最经典的练手项目就是读写EEPROM。搜索热词里有一条“I2C读写EEPROM代码 Verilog”看来硬件描述语言的场景也有人在搞我先说通用流程再补充一点针对性的建议。I2C写EEPROM的标准流程是发出起始条件然后发设备地址加写位等待从设备ACK接着发寄存器地址再等ACK然后发送要写的数据等待ACK最后发停止条件。读的流程稍有不同要把读写位从写切到读发送一个重复起始条件然后再读字节读完要回NACK表示“我没有更多数据要读了”最后停止。实际调I2C时最常见的三个问题我在这里集中说一下。第一个问题是ACK检测。很多I2C控制器在发送完地址字节后会自动检查从机的ACK位但如果用GPIO模拟I2C就需要手动检测SDA在第九个时钟里是否为低。漏掉这一环节可能导致你在写一个不存在的地址时代码还继续往下发白白浪费时间。第二个问题是重复起始条件。有些简化版驱动读EEPROM时发完地址后直接用停止条件再重新启动一次这在很多情况下也能跑通但严格按I2C规范来说应该用重复起始条件。少数器件对这两个操作的容忍度不一样为了保险按规范来实现不会错。第三个问题是上拉电阻。前面说过开漏模式如果上拉电阻太大总线的上升沿变慢在高速模式下就会读到不该读到的电平变化。EEPROM本身速度通常不快100kHz和400kHz比较多但总线电容一大照样会出问题。如果是Verilog方向想实现I2C控制器思路其实也很简单核心是一个状态机。状态机里有起始、地址发送、ACK检测、数据处理、停止这几个状态。难点反而在时钟分频和SDA方向控制上因为双向口在FPGA里要用inout类型要控制好什么时候当输入、什么时候当输出。网上很多开源例程的方向控制写得混乱建议自己动手画个时序图把方向和SCL的时序对起来就容易理解了。I2C上传数据的速率我并不特别推荐所有人先去追求高性能我见过很多项目跑400kHz跑得很欢但系统其他中断一多I2C时序被拉长偶尔就废了。先稳后快先把100kHz调通再去追求高速这条路最踏实。3.4 SPI与DMA解放CPU的正确姿势热词里“SPI DMA”反复出现这确实是SPI使用里的一个高级用法。SPI每次发一个字节如果数据量上千CPU就一直在循环里转悠别的活儿全被耽误了。DMA就是把这个搬运的工作接管过去CPU只需要告诉DMA“从哪搬、搬到哪、搬多少”然后就去忙别的搬完DMA会拉一个中断告诉CPU完事了。我在GD32F303上做过一个SPI读取ADC数据流的系统8通道同步采样的ADC数据量很大。直接CPU轮询方式读SPI中断响应稍微慢一点就会丢数据。后来改成SPI加DMA读取由DMA自动完成数据搬到一个环形缓冲区里主程序只有缓冲区半满时才会被叫去处理数据。同样的CPU频率处理能力一下子宽裕很多。SPI DMA的配置有一个特别要注意的点接收DMA和发送DMA的触发源要匹配好。SPI是全双工的接收DMA的触发条件是RX缓冲区非空发送DMA的触发条件是TX缓冲区空。有些MCU里这两个DMA通道是可以独立配置的你可能会遇到“只收数据不需要发数据”的操作但为了产生接收时钟SPI外设还是要被动地发送一些字节去推动时钟这时候接收DMA单独工作就无法持续触发必须辅以一个发送DMA或者手动发送逻辑。打个比方SPI读数据这件事就像坐手摇船你想要对岸的货物可你的船必须有人摇船桨才能前进。接收端收得再快没有发送端在摇桨数据也不会自己从对岸飞过来。实际调的时候可以先测发送侧用逻辑分析仪确认时钟和数据正常再开接收DMA这样问题容易定位。DMA中断里有不少小坑最常见的两个一个是传输完成中断触发条件要比对寄存器版本另一点是循环模式和正常模式的差异。循环模式下DMA会在传输结束后自动重新装载地址适合连续采样正常模式则止步。如果你的代码里用了循环模式但初始化时DMA中断标志没有及时清理再次启动时可能触发一次假中断造成缓冲区错位。这些都是真实踩过的坑写在这里提醒大家。3.5 关于TF卡SPI和AD7606、编码器这些实际应用盘点这些热词“TF卡SPI电路”也是很多人的痛点。TF卡跑SPI模式是比较经典的玩法。TF卡默认应该是SD模式切到SPI模式需要在初始化时先给卡发至少74个时钟脉冲然后发CMD0进入SPI模式再发CMD8、ACMD41这些初始化指令。硬件电路上最常见的坑是TF卡的DAT1、DAT2、CD引脚在SPI模式下要接上拉否则卡可能识别不稳定。“安富莱AD7606”这个热词说明有人在用AD7606做高速同步采样。AD7606支持SPI接口也支持并行接口如果用SPI模式要特别注意它的BUSY引脚和FRSTDATA引脚的时序关系。我调试时最喜欢的做法是用逻辑分析仪同时抓CS、SCLK、BUSY三根线确认每次转换完成后数据输出的帧格式。AD7606可以配置成过采样过采样倍数越高输出数据的有效位数越好但输出速率会降低两者要权衡。“I2C编码器”这个热词也挺有意思。旋转编码器通常是AB相脉冲直接接GPIO中断但也有带I2C接口的编码器内部带寄存器可以读到绝对位置。这种I2C编码器对时序要求不严格但通信频率一般不高挂总线上注意地址别冲突就行。“SPI引擎”这个词如果我没理解错应该是指SPI接口的加密引擎或者硬件加速模块。这类模块一般在网络安全芯片和存储加密上用得比较多实现原理大多是内置AES等算法的硬件电路通过SPI接口与主控交互。这种场景对SPI时序要求非常苛刻因为加密数据一旦因为时序问题中断整个会话就废了所以这类应用我强烈建议用硬件片选和DMA配合。4. 常见问题与排查技巧实录4.1 有波形但读不出正确数据问题出在哪我在调试SPI和I2C时最怕的不是完全没波形而是波形看着正常但数据不对。这种情况最容易把人绕晕因为你会怀疑是软件逻辑不对又怀疑是硬件连接不对两边反复查。对于SPI第一件事就是确认时钟极性和相位。拿出逻辑分析仪抓一次主设备发指令的波形对照手册的时序图一格一格看空闲电平、采样沿。数据错误绝大多数都是模式不对尤其是用别人现成代码的时候更容易被默认的模式带偏。确认模式没问题之后再检查片选时序。CS拉低之后是不是马上就发数据了中间有没有留一点点时间让从设备准备好有的从设备对CS建立时间有最低要求如果主设备在CS拉低的同一瞬间就发第一个时钟某些慢速设备会响应不过来。对于I2C同样用抓波形的办法重点看起始条件、地址字节和ACK位。我经常遇到一种情况地址不对总线上的设备不ACK波形里SDA在第九个时钟一直是高代码却还硬撑着继续读。这种情况要回头查设备地址到底是多少。7位地址和8位地址经常有人搞混很多器件手册写上的是8位含读写位的代码里写的却是7位的结果差一位对不上。4.2 总线挂死和时钟拉伸I2C专属的麻烦I2C有一个让很多人头疼的现象总线挂死SDA一直为低时钟还在跳或者干脆也不动了。这通常是某个从设备把SDA钳在低电平等它释放却一直不释放。最简单的解救办法是把SCL多翻转几次有些从设备在时钟翻转几次后会重新同步释放总线。如果这招不行就只能把总线的电源断一下或者把主设备复位一下。另外还有一个概念叫时钟拉伸就是某些从设备处理不过来的时候会把SCL拉低让主设备等一等。这在很多传感器上很常见。如果你的控制器不支持时钟拉伸可能会因为等不到回应而超时报错。我建议在选用I2C器件时留意一下它是否有时钟拉伸需求特别是做高速传输的时候。4.3 上拉电阻反复调就是不行换思路上拉电阻的问题前面已经说了不少但实际调试中还有一种情况阻值怎么调都调不好。这时要冷静怀疑是不是I2C总线上挂的某个设备本身有问题比如某个引脚的漏电流特别大把总线电平拖垮了。我就遇到过一颗器件本身有内部上拉和外部上拉并联之后总阻值太小低电平都拉不下去导致整个总线工作异常。排查这种问题只能一个一个挂载设备试把可疑的器件先摘下来看总线状态有没有恢复。另外多从设备的总线每个从设备的输入引脚都带有电容数量一多总线等效电容就变大即便你换更小的上拉电阻上升沿还是不够陡峭。这时候要考虑降速或者减少总线上挂的设备数。I2C总线扩展器就是干这个的热词里也有“I2C扩展”本质是把一条总线扩成多条或者把I2C信号转成别的接口再接多个设备。扩展之后要注意延时会变大速率不宜调高。4.4 常见问题速查表下面这张表是这几类问题的高频排查要点我在项目里基本每次都参照这个顺序来排除分享出来给大家当个参考。现象可能原因排查顺序读到0xFF或全0SPI模式不对、CS未选通、从设备未上电1. 查模式 2. 查CS电平 3. 查供电数据随机跳变时钟过快、线路过长、干扰1. 降速 2. 查波形 3. 缩短走线I2C无ACK地址错误、总线电平不对、上拉失效1. 查地址 2. 量SDA电平 3. 查上拉I2C总线挂死从设备锁死、主从不同步1. 翻转SCL 2. 断电复位 3. 查时钟拉伸偶尔通信失败片选时序不达标、中断打断1. 加延时 2. 关中断 3. 加DMADMA数据错位标志位未清、循环模式配置错误1. 查中断初始化 2. 查缓冲区指针 3. 单步调试看着这张表再回头看问题其实很多难调的bug都不是玄学而是时序关系没有完全对上。理解每一种现象背后的协议细节再配合示波器或逻辑分析仪去验证问题往往很快就能锁定。5. 实践中的选型心得与学习路径建议5.1 我的选型决策习惯做了这么多项目我慢慢养成了一个选型决策习惯。第一步永远是列接口需求把要接的器件数量、数据量、速率要求、引脚余量全部列出来。第二步才是看速度参数但不会只盯着上限而是看典型值和工作电压下的可靠值。第三步是画原理图时就把上拉、下拉和去耦电容的位置预留好就算最后没用上也比板子回来之后再飞线强。之前有个模拟项目我印象很深要接三颗I2C传感器加一颗I2C EEPROMMCU引脚刚好够用但I2C总线上四颗器件如果全部直接挂上去总线电容变大可能不稳。我就干脆加了一个I2C总线扩展器把三颗传感器分到另一条总线上EEPROM和主控留在原始总线。这样每条总线都是轻载运行非常稳而且扩展器本身也增加了系统的灵活性。另一个项目中需要接一颗SPI接口的SD卡数据量比较大。我选了硬件SPI加DMA方案并且用硬件片选。为什么这时候用硬件片选因为我在那款MCU上验证过硬件NSS在大批量DMA传输时行为正常不会中途跳变同时软件片选在DMA连续传输中可能需要专门配合开发量反而大。选型没有绝对的对错只有对自己硬件外设特性的把握程度。5.2 软件模拟和硬件外设的配合策略很多项目初期调试阶段硬件外设的引脚可能被调试器占用或者芯片封装引脚有限。这种情况下我的策略是先用软件模拟验证逻辑确认器件能正常通信然后再切到硬件外设把速率拉起来。这个策略有一个明显好处软件的时序完全可控出问题容易定位硬件外设一旦配好性能又远超软件模拟。切换到硬件外设时记得把软件模拟的延时逻辑全部去掉因为硬件外设自动管理时钟和数据不需要你在代码里手动翻转。如果切过去之后数据不对不要怀疑硬件外设坏了先检查外设的配置参数尤其是模式、位数、分频、片选极性大概率是配置里某一位和软件模拟时有差异。软件模拟SPI和I2C还有一层价值当你需要把一个老设备接到新MCU上而新MCU的外设寄存器细节你还不太熟时可以先拿软件模拟把器件驱动起来等验证完器件没问题再去啃外设驱动压力会小很多。5.3 面向新手的学习路径建议新手学SPI和I2C我不建议一上来就啃协议手册或者看一堆例程。我建议按这个顺序来。第一步用逻辑分析仪抓现成设备通信的波形。随便找一个开发板上的加速度计或EEPROM例程抓SCL、SDA或者SCLK、MOSI、MISO的波形对照时序图看明白每个字节是怎么被发出去的。第二步动手用GPIO模拟一遍最简单的协议。不需要接真实设备先把起始条件、停止条件、字节发送的波形模拟出来可以用逻辑分析仪验证确认自己完全理解协议时序。这一步做完你会对协议有真正的“手感”而不是只会抄代码。第三步再切换到硬件外设。用同一颗器件配置好硬件外设读数据对比软件模拟的结果确认两边行为一致。此时你已经理解时序本质了硬件外设的寄存器配置对你来说只是换了一种实现方式。第四步进阶到DMA和中断的结合把CPU从通信里解放出来这时候你才算真正用一个产品的思路在做这件事。5.4 几个容易忽略的细节彩蛋最后讲几个容易被忽略的细节都是我实际用过的经验。第一个是SPI在高速模式下MOSI和MISO之间的串扰问题。如果板子上这两根线走在一起而且靠得很近高速翻转的时候可能会串扰轻则丢位重则整个数据帧乱套。解决方法是拉开间距或者在关键信号之间加地线隔离。我做过一次板子SPI Clock 20MHzMISO数据偶发错误查了半天的软件最后发现是布线平行的锅。第二个细节是I2C的地址冲突。有些器件支持多个地址引脚比如A0、A1、A2设计板子的时候要预留跳线或电阻配置避免多颗同型号器件地址一样导致冲突。我见过有人偷懒把三颗一样的传感器A0、A1、A2全部接地结果三颗地址完全一样总线上永远只能识别到一颗剩下两颗直接失联。第三个细节是中断优先级。SPI和I2C通信过程被高优先级中断打断可能导致时序被拉伸尤其在严格的主从同步场景下会出问题。如果系统里有硬实时中断比如电机控制、PWM斩波要给通信中断设置合理的优先级或者干脆用DMA把这些通信从CPU中断里摘出去。这点在之前那个AD7606采样系统里体现得最明显DMA介入之后无论中断多频繁数据流都稳稳的。第四个细节是关于热词里的“不拆SPI Flash芯片可以刷BIOS吗”。这其实是个很有意思的工具场景很多主板上的SPI Flash可以通过专用的编程器夹具用SPI协议直接在线刷写不需要拆芯片。原理就是编程器通过夹具夹住Flash芯片的引脚用SPI时序去读写内部数据。对于不想拆焊的人来说这个场景本身就是SPI协议价值的又一个体现。不过操作的时候务必断开Flash芯片和主板其他部分的电气连接至少要把主板的电源断开不然编程器和主板上的电路同时驱动同一根信号线轻则烧引脚重则损坏器件。这些零零散散的细节单独拿出来看好像都很小但每一个都有可能在项目关键时刻把人卡住几个小时甚至几天。通信总线的调试就是这样基础原理全懂细节才见真功夫。做嵌入式的路上大家都是从每一次波形乱掉、每一次ACK丢失、每一次SPI数据错位里爬出来的。SPI和I2C只是这个大千世界里最常见的两个接口但把这两个接口吃透了其他总线的学习路径也会清晰很多。希望这篇经验分享能帮你少走几步弯路把片内到片间的这趟旅程走得顺畅一些。