ARTICLE DETAIL

资讯详情

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

I2C、SPI、UART与I2S对比:嵌入式串行总线选型与调试指南

I2C、SPI、UART与I2S对比:嵌入式串行总线选型与调试指南 1. 先盘底层逻辑:它们并不是同一维度的东西如果你正在搜索“i2c i2s spi uart对比”,大概率是遇到了一个现实中很具体的问题:一个嵌入式项目里,板子上要接传感器、Flash、音频Codec和调试串口,打开数据手册一看全是这些缩写的引脚,瞬间不知道该怎么规划接线。这四个词之所以总被放在一起比较,是因为在MCU/FPGA/嵌入式Linux开发里,它们都是最常见的板级串行通信方式,但残酷的现实是:它们并不完全是同一类事物,硬塞进同一个对比表格里,反而容易让你把方案选偏。先说结论式的一句话:I2C、SPI、UART是三种“通用串行通信总线/协议”,而I2S是“数字音频专用接口”。I2C和SPI是同步通信,由主机提供时钟;UART是异步通信,双方各按约定的波特率收发;I2S本质上也是同步的,但它带着明确的声音数据左右声道划分,不是为了传输通用数据设计的。很多初学者把I2S和I2C搞混,光看缩写觉得它们是亲戚,其实前者是给DAC/ADC/音频Codec用的,后者是给温度传感器、EEPROM、触摸屏这类小数据量设备用的。我给它们的定位是这么四句话:I2C:低成本、低速率、多设备挂在两根线上,靠地址寻址,半双工。SPI:高吞吐、全双工、主设备通过片选信号挨个点名从设备,速度快但线多。UART:点对点、全双工、异步收发,不需要时钟线,电平转换后还能跑很长距离。I2S:连续音频流的专用管道,不关心你是谁,只关心左右声道数据和位时钟对不对。把它们比作日常通信场景会更好理解。I2C像一条楼道里的公共电话线,每个房间有一个分机号,说话前要确保线上没人正在讲,还要带确认回复;SPI像公司里的内部专线,领导要联系哪个部门就按下对应的按键(片选),连接建立后数据哗哗地传,不需要对方逐一回“收到”;UART像两个人约好了语速后互相发消息,不用同一根时钟线,只要双方节奏一致就行;I2S更像广播电台的直播音频通道,前端把采样数据按帧送出去,后端一边收一边还原成声音,强调的是连续性和声道对齐。这么说下来,你大概能明白:真正做选型时,不是把四种协议放一起跑分看谁快,而是要先想清楚“这条链路上跑的到底是什么数据”。不同通信模型决定了它们各自的引脚成本、速度上限、多设备能力和调试方式。所以我这篇不打算只给一张参数表,而是先拆每个协议的物理层和时序特征,再给横向对比和选型路径,最后聊聊用逻辑分析仪抓波形时这四个协议各自的“脾气”。不管你是用STM32做裸机、用ZYNQ做Verilog,还是在Linux下写设备树,这套方法论都通用。1.1 为什么它们总被拿来对比,却经常对比错最大的误区是把I2S和I2C当成同一层级的对手。我在很多交流群里看到有人问“I2S和SPI哪个快,能不能用SPI代替I2S”,这个问题本身就不成立。SPI是个通用全双工总线,你想用SPI传音频当然可以,但那意味着你要自己在软件里定义左右声道帧格式、处理声道切换、还要保证数据流不中断,代码量多一倍不说,稳定性也远不如I2S那颗专门为音频设计的WS(Word Select)信号。反过来,如果你只是读一个温度传感器,用I2S就非常荒唐,它压根没有地址和应答机制。另一个误区是用最高速率去衡量一切。I2C在标准规范里有100kbps、400kbps、1Mbps甚至更高,但实际跑400kbps时如果总线电容大、上拉电阻选得不合适,波形上升沿会变得很缓,通信很容易出错;SPI看起来能跑到几十Mbps,可如果你的从设备本身只能支持2MHz,主设备再快也没意义;UART在MCU上常见的115200比特率不高,但胜在实现简单,而且变成RS485后能传几百米,这是板级I2C/SPI不具备的优势。所以对比这四个东西,核心不是背数字,而是要理解每个协议背后的设计取舍:谁省引脚,谁抗干扰,谁适合大批量数据,谁天生带声道信息,谁又能点对点长距离。下面我一个个拆。2. 四种协议逐个拆解:引脚、时序、速率和应用意图2.1 I2C:两根线挂一票设备,靠地址和确认过日子I2C全称Inter-Integrated Circuit,总线由两根线组成:SDA是数据线,SCL是时钟线。它采用开漏结构,外部必须接上拉电阻,这就是为什么I2C天生是半双工——线上同一时刻只能由一个设备驱动。通信开始时主机先把SCL拉高、SDA从高拉低,这是一个起始位;结束时反过来,SCL高电平期间SDA从低拉高,这是停止位。每个字节8位,第9个时钟周期传送ACK应答位:从机拉低SDA代表“收到了”,保持高电平代表“不响应”。地址机制是I2C最核心的特征。7位地址模式下,总线最多挂127个设备,实际兼顾总线上拉电阻和电容后,挂十几二十个已经很夸张。一次传输的第一个字节是“从机地址读写位”,比如0xA0写代表发EEPROM命令,0xA1是读。这里有一个入门必踩的坑:很多器件数据手册写的是7位地址比如“地址0x50”而发送字节时要左移一位变成0xA0,逻辑分析仪上看到的SDA头字节也是0xA0,初学者最容易在这里被绕晕。I2C的时序细节还包括时钟拉伸(Clock Stretching):从机如果没准备好接收下一个字节,可以把SCL拉低,让主机暂停等待。这个特性在做一些低速传感器时经常遇见,比如某些温湿度芯片转换期间会拉低SCL,主机如果没做相应处理,通信就会卡死。此外I2C常见速率如下:标准模式:100kbps快速模式:400kbps快速模式:1Mbps高速模式:3.4Mbps(实际板级用得少)因为两线结构省引脚,又能挂多个设备,I2C几乎统治了低速小数据量外设:EEPROM、温度传感器、RTC时钟、触摸控制器、PMBus电源管理芯片等等。但它的速度上限和总线电容问题决定了它不适合大吞吐场景。如果数据量超过几KB每秒,或者对延迟很敏感,I2C就有点力不从心了。2.2 SPI:四根线全双工高速跑,片选决定我是谁SPI是Serial Peripheral Interface,典型四线:SCK时钟、MOSI主出从入、MISO主入从出、CS片选。它的全双工特性来自数据线和时钟线分开、独立方向,主机在SCK每个边沿同时发送和接收一位。由于没有像I2C那样的地址帧,SPI靠CS线区分从设备:主机把某个从机的CS拉低,这个从机才参与通信;CS高电平意味着从机被释放。一主多从时,每个从机都需要一根独立的CS,所以从设备一多,GPIO开销就上来了。SPI的时序参数很多文章提CPOL/CPHA,但新手往往理解成“四组可随便试的组合”。其实CPOL决定SCK空闲时的电平极性,0是空闲低,1是空闲高;CPHA决定数据在哪个边沿被采样,0是第一个边沿,1是第二个边沿。主从双方必须约定同一组模式,否则接收方会采到错误的数据。实际工程里,如果从设备数据手册给了时序图,一定要看清楚它是“SCK空闲低、第一个上升沿采样数据”还是“SCK空闲高、第二个下降沿采样数据”,然后选对应的模式。SPI的速率极限通常远高于I2C,常见8MHz、16MHz、36MHz、甚至50MHz以上。它使用推挽输出,不像I2C要开漏上拉,信号边沿更陡,传输更快。全双工加高速的优势让SPI成为高吞吐外设的首选:SPI NOR Flash、SD卡、LCD显示屏、高速ADC、磁编码器等都爱用SPI。比如热词里提到的MT6701磁编码器,通过SPI读取绝对角度,一次传输32位数据,在电机控制场景下能毫秒级获得高精度角度,这种实时性I2C很难做到。但它也有明显短板:无应答机制。主机发一个字节,SPI从机到底收到了没,协议层面没有强制通知,只能靠数据内容或额外的状态引脚确认。这意味着SPI的调试比I2C更要依赖逻辑分析仪,发出去没反应时,你甚至不知道是时序模式错了、片选没拉对、还是从机根本没初始化成功。2.3 UART:两线异步收发,靠波特率默契配合UART是Universal Asynchronous Receiver/Transmitter,它的物理层极其简单:TX发送、RX接收,双方交叉连接。因为没有SCL这类时钟线,收发双方必须提前约定波特率,比如9600、115200、921600。一次数据帧由起始位、数据位、校验位(可选)、停止位组成。空闲状态下TX线是高位,发送一个字节时先拉低一个bit时间作为起始位,然后按LSB先行的顺序输出数据位,最后拉高停止位。衡量UART速度的核心是波特率,而不是“数据位速率”。波特率指的是每秒传输的符号数,一个符号在这里就是1bit。115200波特率意味着1bit持续约8.68微秒,一帧10bit(1起始8数据1停止)差不多86.8微秒。理论上UART没有绝对的上限速度,见过很多MCU跑1Mbps甚至3Mbps,但距离越长、波特率越高,信号畸变越严重。所以实际项目里,板级短距离可以跑高波特率,跨机箱通过RS232/RS485时一般降到115200甚至更低。UART最大的生存优势是“哪些器件都愿意留一个串口”:GPS模块、蓝牙模组、Wi-Fi模组、指纹模块、4G通信模块,几乎所有无线/通信模组都带UART。原因在于它实现简单、不需要时钟线、点对点天然适合两个系统之间的异步消息交换,而且配合电平转换芯片可以转换为RS232/RS485,传到几十米甚至几百米。这也是为什么很多工程师调试时还会留一个UART当log输出——三根线(GND/TX/RX)就能解决所有调试信息输出。热词里提到“uart阻塞和非阻塞”,这里多说一句:阻塞和非阻塞并不是UART协议本身的概念,而是MCU里串口驱动代码的写法。阻塞方式就是轮询等待一个字节发送完成或接收中断标志,CPU被占住;非阻塞方式则通常用FIFO队列加中断/DMA,把收发操作放到后台,主循环可以继续干活。实际产品里几乎不会用阻塞收一整包数据,因为只要数据一多、中断嵌套一深,丢数据和粘包是必然的。2.4 I2S:天生为数字音频准备的连续流接口I2S是Inter-IC Sound,别看名字里带个I2,它与I2C一点关系没有。标准I2S最少三根线:BCK(位时钟,也叫SCK)、WS(左右时钟,也叫LRCK)、SD(串行数据)。有些芯片还会额外引出一根MCLK主时钟,给源端提供更好的时钟精度。数据以帧为单位连续流动,WS状态决定当前发的是左声道还是右声道,BCK的每个边沿同步一位数据。标准的Philips I2S格式里,数据会比WS边沿延迟一个BCK周期,这些时序规则由音频Codec和主控的I2S控制器共同决定。I2S的速率是跟着音频采样率走的,不是随意设一个波特率。比如采样率48kHz、16bit、双声道,那么一个采样帧是左右声道各16bit,Bit Clock频率至少是48kHz×16×21.536MHz。如果是24bit、192kHz、双声道,那就需要192k×24×29.216MHz的BCK。音频数据讲究连续性和低抖动,一旦BCK频率不稳或者数据流中断,后果就是爆音、杂音、声音断续。所以I2S控制器通常需要主时钟MCLK来做精确定时,而且驱动层要保证DMA缓冲不断流。为什么不用SPI代替I2S?你可以用软件在SPI的MOSI线上一帧帧地发送音频采样,自己用GPIO模拟WS切换左右声道,但SPI没有设计声道概念,I2S的WS信号天然解决了左右声道对不齐的问题;同时I2S的数据延迟和位宽对齐是经过标准化定义的,各个音频Codec和DSP之间能直接互通。所以只要设计里带音频Codec、DAC/ADC、蓝牙音频或数字功放,I2S就是最合理的选择,省去大量拼帧代码。3. 一张表看懂横向参数:速度、引脚、模式、距离和用途真正需要对比时,先看这张表,再回到通信模型去理解差异。表格里的数字是典型工程值,不是极限值,别拿极限值去套产品。对比项I2CSPIUARTI2S需要引脚SCL/SDA,共2根MOSI/MISO/SCK/CS,3~4根,一主多从每从一根CSTX/RX,共2根(可加流控RTS/CTS)BCK/WS/SD,3根,部分要MCLK同步方式同步,SCL由主机驱动同步,SCK由主机驱动异步,双方按波特率各自采样同步,BCK由主机或Codec驱动工作模式半双工全双工全双工(TX/RX独立)单向为主,可扩展双向SDI/SDO多设备支持地址寻址,可挂多从机片选寻址,每从机一根CS点对点,很少直接多挂点对点/链式,但无通用地址概念典型速率100k/400k/1M bps1M~80M bps常见0.1M~数M bps由采样率决定,如48kHz×16×2≈1.5M bps总线距离板级,受总线电容限制板级,通常20cm板级或RS485长距离板级,20cm为宜应答/确认有ACK/NACK,支持时钟拉伸无协议应答无协议应答,靠帧格式和CRC(软件层)无应答,数据流要求连续典型外设EEPROM/传感器/RTC/触摸ICFlash/ADC/LCD/SD卡/编码器调试串口/GPS/蓝牙/WiFi模块音频Codec/Microphone/DAC这张表一出来,很多人会立刻抓住两个关键点:一个是SPI在速率和全双工上全面占优,那为什么不是所有设备都用SPI?问题在于引脚成本和对时钟线的依赖。SPI要SCK/MOSI/MISO,再加上每个从设备的CS,接到第四个从设备时就是7根线;而I2C始终两线,对引脚紧张的MCU和需要压低BOM成本的板子来说,I2C优势很大。另一个点是UART居然能跑很长距离,这是它的物理层灵活决定的,但标准UART电平在3.3V下抗干扰很差,所以真正远距离通信都会外接RS485收发器,把UART的TX/RX转成差分信号。I2S在表格里为什么没有列入“多设备支持”?因为它本来就不是寻址式总线。一条I2S链路通常只服务一个音频设备,如果有多个音频源,更多是用I2S的TDM模式去做时分复用,而不是像I2C那样通过地址区分。TDM模式下一条SD线上能塞多个声道的音频数据,但这是另一个复杂话题,选型时可以暂时理解为“音频专用”。再补充一个容易误解的点:很多SOC的引脚是复用型的,比如同一组引脚既可以配置成SPI也可以配置成I2C,甚至UART。这时候你看到引脚下方的复用编号是AF1、AF5之类,千万不要以为这些协议是“同一个串行外设换模式而已”。它们的外设寄存器、DMA请求映射、中断号全都不同,配置错了就是设备无响应。真正决定用哪个接口,除了看外设支持什么,还要看MCU引脚冲突和驱动栈成熟度。4. 选型决策路径:照着这条思路走,基本不会选错4.1 第一步先问:这条链路上传输的数据到底是什么形状的我个人的选型思路从来不是“哪个接口最高级就用哪个”,而是先看数据的形状。数据形状分三类:小量寄存器/状态数据、大批量流式数据、连续音频数据。小量寄存器/状态数据,比如传感器数值、EEPROM内容、触摸坐标、电源管理寄存器,每个事务通常只有几字节到几十字节,传输频率也不高,优先I2C。两线省引脚,挂多个设备也方便。大批量流式数据,比如固件烧录、图像帧、ADC连续采样、非易失存储,几十KB甚至几MB起步,优先SPI。SPI的高时钟和全双工在这里能把I2C远远甩开。连续音频数据,比如麦克风采集、音频播放、蓝牙语音,直接选I2S,特别是Codec接口已经给了I2S引脚的情况下,别用SPI硬顶。点对点系统间通信,比如主板和无线模组之间的数据传输、调试log输出、甚至两个不同主控之间交换命令,优先UART。实现成本最低,模块生态最成熟。这套路径解决了一大半选型问题。剩下的场景看器件本身:如果芯片手册只给了I2C接口,那你还纠结什么SPI;如果Flash芯片只支持SPI/QSPI,那也轮不到I2C。接口选型永远是“器件支持什么系统需要什么”的交集。4.2 第二步再看设备数量和引脚预算挂多个从机时,I2C和SPI的差异非常明显。I2C无论挂几个从机,都只是SDA和SCL两根线,靠地址区分;SPI在从机数量上升时,每增加一个从设备就要多拉一根CS线,从机一多引脚开销成倍增加。所以那种“一块主板上挂6个传感器1个RTC1个EEPROM”的典型物联网节点,用I2C是合理的,因为I2C地址足够、引脚省、速率也够用。但反过来,如果这6个传感器里有高速ADC需要连续采样,或者有一个4Mbit的Flash要频繁读写日志,你就不能全塞在I2C总线上。比较务实的分法:低速小数据量设备挂I2C,高速大数据量设备走SPI,audio用I2S,调试口留UART。很多成熟开发板就是这么布的线。4.3 特殊情况:PMBus、RK3588、FPGA与SPI ADC的集成有些场景表面看是I2C/SPI,实际还要考虑协议栈。热词里的PMBus和I2C区别就是一个经典例子。PMBus物理层完全基于I2C,但上面的命令集是电源管理专用的,比如读电压、读电流、设置输出电压等,地址分配也遵循PMBus规范。如果你把它当成普通I2C去裸读寄存器,往往读出来的数据含义对不上。所以做电源芯片通信时,最好用带PMBus语义的驱动或库,而不是自己拼I2C帧去瞎猜寄存器地址。再比如RK3588这类应用处理器,虽然自带多个SPI控制器,但Linux下能不能用、用SPIDEV还是内核驱动、引脚是否被别的功能复用,都是选型前置条件。有些SPI Flash要挂在BOOT ROM直接启动,这种场景必须在硬件设计阶段就确认SoC的启动引脚和SPI控制器编号,不是软件随便改就能解决的。至于FPGA接SPI ADC,常见问题是FPGA的IO电平可能与ADC输出的LVCMOS电平不匹配,或者是ADC数据并不是标准SPI帧,而是转换完成后通过SDO引脚连续输出的串行位流,这种情况下你写Verilog要关心的不是“SPI协议正确”,而是采样时钟和数据的时序对齐关系。遇到这些非典型场景,建议直接看器件的官方参考设计和Linux内核里的驱动适配情况,别自己从零造轮子。5. 调试实战:逻辑分析仪抓包,四个协议到底各有哪些脾气5.1 工具选型和采样率设置无论哪个协议,调板子最常用的工具都是逻辑分析仪和示波器。逻辑分析仪胜在通道多、解码方便,示波器胜在能看电平幅度和信号质量。我的习惯是:先叠逻辑分析仪抓通信帧,确认协议层逻辑正确;再用示波器探头看上升沿、下冲、电平阈值,排查物理层问题。采样率选择有个基本原则:至少是信号实际频率的4倍,建议8~10倍。比如400kHz的I2C,50MHz采样率的设备毫无压力;如果SPI跑到了50MHz,普通1GHz采样率的百元级逻辑分析仪会吃力,需要叠加“等时采样”或换示波器。UART 115200的采样率需求很低,但抓包时很容易因为触发条件设置不对漏掉起始位。I2S的BCK如果到9.216MHz,逻辑分析仪采样率建议至少50MHz以上,否则BCK边沿可能被采样抖动掩盖,解码出来的声道数据全是乱的。5.2 I2C抓包:先找起始位、ACK和第九个时钟I2C的波形是最好认的:SCL一串小方波,SDA在SCL高电平时不动、在SCL低电平时跳变。抓包时第一眼要找到起始位——SCL高电平期间SDA从高拉低,然后SCL开始产生时钟。接着看第一个字节的高7位是从机地址,最低位是读写标志。如果SDA在第9个时钟高电平期间被从机拉低,说明ACK正常;如果一直是高电平,那就是NACK,答案基本是:地址不对、从机没上电、从机还没准备好。我遇到过两个高发问题。一个是7位地址和8位地址混用。比如GT911触摸芯片的I2C地址,数据手册写的是0x5D或0x14,实际发送时要左移一位加读写位。很多人直接在代码里填0x5D作为寄存器地址,自然全是NACK。另一个是上拉电阻不合适,400kbps时总线电容一大,上升沿就变缓,从机采样点在边沿附近会读出错误电平。这种情况下逻辑分析仪经常抓到乱码地址,但示波器一眼就看到波形上升沿斜率不对。解决方法是降低速率试跑,或者换上更小的上拉电阻(常见2.2k~4.7k)。5.3 SPI抓包:最怕CS时序和CPOL/CPHA不匹配SPI抓包首先要确定CS。每个事务都是从CS下降沿开始,上升沿结束。如果主机软件用普通GPIO模拟CS,而GPIO操作太慢,CS拉低后SCK却迟迟不来,某些从设备可能已经在等超时了。这种情况在数据手册里叫CS Setup Time,一般是ns数量级,软件GPIO翻转动辄几百纳秒,所以高速SPI最好用硬件片选或提前把CS拉低再等几个NOP。CPOL/CPHA不匹配是最常见的“SPI无响应”原因。逻辑分析仪解码时选错模式,解出来的数据可能每个字节都差一位。我常用的验证方法是:让主机发一个已知字节如0x55或0xA5,看MISO上从设备返回的内容能不能对上;对不上就切换四种模式组合再试。另一个细节是MSB/LSB顺序,有些器件默认MSB First,有些寄存器配置里允许切LSB First,如果两边不一致,读出来的数据是字节内位反转,表现非常诡异。还有一种典型的“CS连续性问题”。比如用软件片选读Flash ID时,如果每个字节都先CS拉低再发送再CS拉高,超过Flash允许的CS高电平时间,状态机就复位了,读出来全0xFF或者全0x00。特别是在DMA搬运数据时,CS时序和SPI外设的FIFO深度必须配合好,如果DMA还没填满数据SCK就停了,很多从机就直接判定事务结束。5.4 UART抓包:看空闲高电平、起始位、波特率误差UART波形比同步协议“丑”,但更好判断。空闲状态是高电平,数据以低电平起始位开始,接着8个数据位,最后停止位回高。逻辑分析仪解码UART时要填对波特率、数据位、校验和停止位。最常见的乱码场景就是两边波特率不一致:比如主机实际输出115200,但接收端按9600解,基本上每个字节都会错位。还有一个容易忽略的问题:波特率误差积累。大部分MCU内部时钟不是精确的整数分频,当系统主频为16MHz时产生115200波特率,分频系数会带来一定误差。误差超过2%后,一个字节10bit就可能错位。实际调板子时不要只看逻辑分析仪设置的波特率,要实测TX引脚上最窄脉冲宽度,换算成实际波特率。比如示波器量到一个bit宽度8.7微秒,那实际波特率就是114942,基本正常;如果量出来是10微秒,那就是90000多,肯定会乱。UART在阻塞和非阻塞上的坑,热词里也提到了。很多新手用轮询方式接一包数据时,主循环一边做其他事一边查询接收标志,结果高字节率和长数据时很容易丢字节,看起来像“偶发乱码”。正确做法是开RXNE中断,甚至用DMA空闲中断做不定长接收。我实际写STM32代码时,一般给串口挂一个环形缓冲区,中断里只管读数据放到缓冲区,主循环需要时再从中取,这样即使一包数据来得再急也不可能丢字节。5.5 I2S抓包:重点看BCK与WS的相位关系I2S的抓包思路和其他协议不同,你要关心的是“声道对不对、数据有没有延迟”。标准Philips I2S格式下,WS为低电平期间是左声道,高电平是右声道(不同Codec也可能相反),数据位一般从WS边沿后的第一个BCK上升沿开始采样,但实际延迟可能是一拍或两拍。逻辑分析仪解码I2S时如果不了解Codec的格式配置,解出来的音频数据也只是比特流,完全看不出“声音”内容。调试I2S时的常见症状是:能出来声音但有杂音或明显爆音。原因通常不是协议错了,而是BCK频率和采样率不匹配,比如mclk没配置成BCK的整数倍;或者是DMA中断处理不及时导致缓冲区下溢/上溢,数据流中断。我在音频项目里加过一条经验:先用逻辑分析仪确认BCK频率等于期望的采样率×位宽×声道数,再用示波器看MCLK和BCK是否有稳定的分频关系,最后才谈软件缓冲。硬件时钟不对,调再久的驱动都没用。6. 几个值得单独拿出来说的工程细节,顺便回答一些常见疑问6.1 I2C上拉电阻怎么算,为什么要“开漏上拉”I2C设备引脚内部是开漏输出,只能拉低,不能主动拉高,所以必须在外部接上拉电阻把总线“拉”到高电平。这个设计和“线与”仲裁有关:多个设备同时拉低总线时不会短路,而且可以通过SDA的释放情况做仲裁。上拉电阻小了,总线上升沿变陡、能跑高速,但设备拉低时要灌入更大电流;大了,功耗低但上升沿变缓,高速时就容易误码。估算上拉电阻的经验范围是1kΩ到10kΩ,具体看总线电容和通信速率。总线电容包括导线、焊盘、芯片引脚寄生电容,一般板级I2C总线100pF到400pF。标准模式下允许的最长上升时间是1000ns,用公式Rmax约等于上升时间/(0.8473×总线电容),拿400pF算下来约3kΩ,所以400kHz场景建议上拉电阻别超过3.3kΩ;如果只跑100kHz,用10kΩ也问题不大。低速调试阶段想省功耗,用10kΩ;高速或长总线,换4.7kΩ、2.2kΩ再试。6.2 SPI硬件片选和软件片选到底怎么选这个问题热词里出现频率很高,说明很多人被CS时序坑过。软件片选就是用普通GPIO手动拉低/拉高CS,优点是灵活:你可以控制CS拉低的时机,也可以在每次事务之间插入延时,配合各种奇奇怪怪的从设备时序。缺点是GPIO翻转速度有限,对SCLK时序有不确定的扰动,尤其是在DMA高速传输时,GPIO和控制外设之间的同步很容易产生毛刺。硬件片选由SPI外设内部自动管理:你配置好CS引脚为硬件片选后,向数据寄存器写第一个字节时SCK开始前CS自动拉低,发送完最后一个字节后CS自动拉高。硬件片选适合高速、连续的数据流,可靠性高。但它也有坑:如果从设备要求“CS拉低后等待若干微秒再开始SCK”,或者要求“CS高电平时间不小于某个值”,硬件片选寄存器里的时序参数没配好就会出错。所以我的建议是:低速和调式阶段用软件片选,方便灵活修改;确认时序稳定、量产后追求吞吐率,再切硬件片选。6.3 UART阻塞与非阻塞:为什么总有人在这上面栽跟头很多刚做单片机项目的人,习惯直接用HAL_UART_Receive(huart, buf, len, timeout)这种阻塞接收,代码写起来简单,但一旦要从模块接收不定长数据,比如GPS的NMEA语句、蓝牙模块的AT回复,阻塞等待就会把主循环卡死。非阻塞的核心是把数据的产生和消费解耦:中断/DMA负责把字节丢进环形缓冲区,主循环按帧解析缓冲区里的数据。这样即使数据来得比主循环快,也不会因为处理不及时而丢数据。还有一个热词是“uart阻塞和非阻塞”常伴随DMA一起出现。用DMA接收时要注意不定长数据的结束判定,常见做法是开启DMA接收加空闲中断(Idle Line),数据一停就认为一包结束。我个人的经验是:协议一定要有帧头、长度、校验,别指望“知道什么时候发完”来保证数据完整,否则串口线上任何一个噪声字节都能让解析状态机跑飞。6.4 “I2C从机主动更新主机寄存器”这事,协议本身做不到怎么办经常会有人问:I2C从机可以主动把新数据推给主机吗?答案是否定的,因为在I2C协议里所有传输都由主机发起,从机永远是被动响应。你想让主机及时感知从机状态变化,通常三条路:一是主机以较高频率轮询从机的状态寄存器;二是从机引出一个INT引脚,状态变化时拉电平通知主机来读;三是用主机的外设事件机制,比如数据准备好时由DMA直接搬运。SPI也一样,没有“从机主动”的概念。UART就不一样,它是全双工点对点,从机随时可以往TX线发数据,所以很多异步通知场景用UART更自然。设计系统时,如果你特别在意“从机主动上报”,要么加一根中断/事件线,要么换UART。6.5 大平台集成时的电平、驱动与引脚复用最后提醒的就是不要只盯着协议层而忽略物理层。3.3V的SPI主控制器去接5V的ADC,逻辑电平不一定兼容;I2S的MCLK如果被复用成其他功能,Codec可能直接静音;Linux平台还要考虑SPI设备树节点里cs-gpios和linux,spidev设备名的对应关系,配错了连设备节点都枚举不出来。我接触过不少用RK3588做边缘网关的项目,SPI口常用来接高速采集模块。看起来很简单,实际一配设备树就发现:同一组引脚可能同时被UART、I2C、PWM复用,想要SPI工作,先要确认pinmux默认状态、控制器时钟和片选资源。这种时候的排查思路是:先用内核的pinctrl调试接口看引脚复用状态,再用spidev应用层发起一次读写,最后才怀疑芯片硬件连接。热词里还有“fpga spi adc”的案例,FPGA的优势就是可以用Verilog自由控制时序,但反面是标准协议外的非标时序往往比标准SPI更容易出问题,写逻辑时务必给自己留几个debug寄存器,让上层能通过寄存器看到当前状态机的停留位置。我在这块摸爬滚打这么多年,最大的体会是:接口选型从来没有“最好”,只有“匹配”。低功耗物联网设备里I2C是性价比之王,高性能数据采集里SPI几乎无法替代,模块互联和调试离不开UART,音频系统绕不开I2S。你可以背下所有参数表,但真正到了画原理图的时候,最该问的还是那句——线上的数据是什么形状、对时间有多敏感、器件面板上到底有哪些引脚。从这个角度出发,再去看“i2c i2s spi uart对比”这个问题,答案其实已经清晰了。
返回列表