1. 三种串行总线的本质差异与选型逻辑1.1 为什么嵌入式面试总爱问这三种总线UART、I2C、SPI这三个名字但凡做过嵌入式开发的人都不会陌生。但面试官反复追问它们绝不只是想听你背出“全双工”“半双工”“同步”“异步”这几个词。真正想考察的是你有没有在真实项目里做过总线选型的决策有没有被时序问题折磨过有没有在示波器前蹲过半夜抓波形。我面过不少人也被人面过不少次。一个很深的感受是能把这三者的区别讲清楚的人很多但能讲清楚“为什么这个场景选I2C而不是SPI”的人少之又少。前者是知识后者是经验。面试官要的是后者。这三种总线本质上解决的是同一个问题芯片与芯片之间、板级器件之间如何用最少的引脚实现可靠的数据传输。但它们在设计哲学上走了三条完全不同的路。UART追求的是简单和普适。它不需要时钟线双方约定好速率就能通信代价是速率不能太高、距离不能太远、只能点对点。I2C追求的是引脚效率和设备管理能力两根线挂一堆设备每个设备有自己的地址代价是速率受限、协议开销大、总线仲裁复杂。SPI追求的是速度和灵活性四根线全双工跑几十兆赫兹代价是每多一个从设备就多一根片选线没有硬件级的应答机制。理解了这个设计哲学层面的差异后面所有的技术细节都是自然推导出来的结果。面试时如果你能从“设计目标”出发去分析而不是零散地罗列参数面试官对你的评价会完全不一样。1.2 一张表看清三者的核心参数对比先上一张我在复习和面试时反复用的对比表把最核心的参数一次性拉通。这张表建议直接记住面试时随手画出来比说一堆话都管用。对比维度UARTI2CSPI线数2TX/RX2SCL/SDA4SCLK/MOSI/MISO/CS通信方式异步同步同步双工模式全双工半双工全双工拓扑结构点对点多主多从地址寻址一主多从片选寻址典型速率9600bps~1Mbps100k/400k/1M/3.4M1M~50M时钟极性/相位不适用不适用CPOL/CPHA四种模式应答机制无可软件约定有ACK/NACK无总线仲裁不支持支持不支持引脚开销固定2根固定2根4NN为从设备数典型应用调试串口、GPS、蓝牙模块传感器、EEPROM、RTCFlash、LCD、ADC这张表里的每一行背后都有大量可以展开的细节。比如“典型速率”这一行UART为什么通常不超过1Mbps因为它是异步的双方时钟有偏差速率越高一个bit的时间越短时钟偏差导致的采样错误就越容易发生。一般要求收发双方波特率误差在2%以内超过这个值就可能出现帧错误。而SPI是同步的时钟由主机提供从机只是跟着时钟走不存在累积误差的问题所以能跑到几十兆甚至上百兆。再比如“应答机制”这一行I2C的ACK是硬件层面的每传输一个字节接收方必须拉低SDA一个时钟周期表示收到。SPI没有这个机制主机发了数据根本不知道从机有没有收到。所以用SPI驱动Flash时读完状态寄存器确认写入完成是必须的步骤不能省。1.3 选型时真正该问自己的几个问题面试中被问到“这个场景你选哪个总线”很多人第一反应是“看速率”。速率当然重要但远不是唯一因素。我一般会按下面的顺序来思考第一通信距离和板级还是板间如果是同一块PCB上两个芯片之间SPI和I2C都可以。如果要通过排线连到另一块板子甚至通过电缆连到几米外UART或者差分方案更合适。I2C设计初衷就是板内通信走线长了电容增大上升沿变缓400kHz都可能跑不稳。第二需要挂多少个设备如果总线上要挂8个传感器I2C两根线搞定SPI需要8根片选线加4根共用线一共12根。引脚紧张的时候I2C的优势非常明显。但如果只挂一个FlashSPI的4根线完全可以接受而且速率快得多。第三有没有中断和实时性要求I2C的时钟拉伸机制允许从机在忙不过来时拉低SCL强制主机等待。这个特性在传感器数据还没准备好时非常有用。SPI没有这个机制主机发了时钟从机就得跟着走从机没准备好就会丢数据。第四功耗敏感吗I2C的上拉电阻在总线空闲时会有静态电流虽然不大但在超低功耗场景下也需要考虑。SPI的推挽输出在空闲时没有静态电流但片选线多如果每个从设备都要单独控制GPIO的漏电流也要算进去。UART的TX线空闲时是高电平如果对方设备没上电可能会有倒灌电流。第五软件复杂度能接受多少I2C协议栈比SPI复杂得多起始条件、地址帧、ACK、停止条件、时钟拉伸、总线仲裁每一项都要处理。SPI简单粗暴基本上就是“拉低片选、发时钟、收数据、拉高片选”。如果项目时间紧、团队经验不足SPI的调试成本更低。这些问题没有标准答案但面试时你能把思考过程讲出来比直接给一个结论要有说服力得多。2. UART的核心考点与实战细节2.1 波特率、时钟偏差与采样机制UART最核心的概念就是波特率。它定义的是每秒传输的符号数在UART里一个符号就是一个bit所以波特率和比特率数值上相等。常见的波特率有9600、19200、38400、57600、115200、230400、460800、921600等。为什么是这些看起来不太规整的数字因为早期UART的时钟源是1.8432MHz的晶振除以16再除以不同的分频系数就得到了这些标准波特率。1.8432MHz ÷ 16 115200Hz这就是115200的由来。现在虽然晶振频率选择多了但这些标准值一直沿用下来。UART是异步通信接收方没有时钟信号只能靠自己的时钟去采样。它是怎么做的在起始位的下降沿开始计数然后在每个bit时间的中间点采样。具体来说接收方检测到起始位后会等待1.5个bit时间采样第一个数据位然后每隔1个bit时间采样一次直到停止位。这里的关键是采样点必须在bit的中间。如果采样点偏到了bit的边缘稍微有一点噪声就会采错。所以UART对时钟精度的要求是收发双方的波特率误差之和不能超过采样窗口的一半。一个bit有16个采样时钟 oversampling 16x采样窗口在中间允许的误差大概是±5%但实际工程中一般要求控制在±2%以内才可靠。我遇到过一个问题某项目用内部RC振荡器做UART时钟源常温下通信正常一到冬天就偶尔丢数据。后来查出来是RC振荡器的温漂导致波特率偏差超过了3%接收方采样点偏移到了bit边缘。换成外部晶振后问题消失。这个坑在面试时讲出来面试官会知道你真正做过产品。2.2 数据帧格式与常见配置陷阱UART的数据帧格式是1个起始位 5~9个数据位 0~1个校验位 1~2个停止位。最常见的是“8N1”即8个数据位、无校验、1个停止位。起始位是必须的它是一个逻辑低电平用来告诉接收方“数据来了”。停止位是必须的它是一个逻辑高电平用来表示帧结束同时给接收方一个缓冲时间。校验位是可选的可以是奇校验、偶校验、0校验、1校验但校验能力很弱只能检测奇数个bit错误实际项目中很少用。这里有几个容易踩的坑第一个坑数据位长度。有些老设备用7位数据位加1位校验位总共还是8位。如果你用8N1去接就会解析错误。我调试某款老式工控设备时文档写的是“9600, 8, N, 1”但实际设备用的是7位数据位加偶校验。后来用示波器抓波形才发现第8位是校验位不是数据位。所以拿到一个不熟悉的设备先用示波器或者逻辑分析仪抓一帧看看比看文档靠谱。第二个坑停止位长度。大部分设备用1位停止位但有些设备用2位。如果主机用1位停止位从机用2位通常还能通信因为从机在第一个停止位之后就开始找下一个起始位了。但反过来主机用2位停止位从机用1位从机可能会把第二个停止位当成起始位导致帧错误。所以配置要一致。第三个坑流控。硬件流控用RTS/CTS两根线当接收方缓冲区快满时拉高RTS告诉对方暂停发送。软件流控用XON/XOFF字符在数据流中插入特殊字符来控制。如果双方流控配置不一致比如一方开了硬件流控另一方没开数据量大的时候就会丢数据。这个坑在高速通信时特别常见。2.3 中断接收与环形缓冲区的工程实现UART接收最常用的方式是中断。每收到一个字节触发一次中断在中断服务函数里把数据存到缓冲区。但这里有一个经典问题如果中断服务函数里做太多事情比如解析协议、处理数据就会阻塞其他中断导致丢数据。正确的做法是中断服务函数只做一件事——把数据存到环形缓冲区然后设置一个标志位。主循环或者任务去检查标志位从缓冲区里取数据做处理。这就是“生产者-消费者”模型。环形缓冲区的大小怎么定取决于你的数据处理速度和数据量。如果主循环每10ms处理一次波特率115200每10ms最多来115个字节缓冲区至少要128字节才安全。如果主循环可能被其他任务阻塞更久缓冲区就要更大。下面是一个典型的环形缓冲区实现思路#define BUF_SIZE 256 // 必须是2的幂方便用位运算取模 typedef struct { volatile uint16_t head; // 写入位置 volatile uint16_t tail; // 读取位置 uint8_t buf[BUF_SIZE]; } ring_buf_t; // 中断中调用 void ring_buf_put(ring_buf_t *rb, uint8_t data) { uint16_t next (rb-head 1) (BUF_SIZE - 1); if (next ! rb-tail) { // 缓冲区未满 rb-buf[rb-head] data; rb-head next; } // 满了就丢弃或者设置溢出标志 } // 主循环中调用 int ring_buf_get(ring_buf_t *rb, uint8_t *data) { if (rb-head rb-tail) return 0; // 空 *data rb-buf[rb-tail]; rb-tail (rb-tail 1) (BUF_SIZE - 1); return 1; }这里有几个细节值得注意。head和tail要加volatile因为它们在中断和主循环中都会被访问编译器不能优化掉。缓冲区大小用2的幂取模用位与运算代替速度快很多。判断满的条件是next tail而不是head tail因为head tail表示空。这样会浪费一个字节的空间但换来了判断逻辑的简化。注意如果波特率很高比如921600且主循环处理不及时环形缓冲区也可能溢出。这时候要么加大缓冲区要么用DMA接收。DMA是更彻底的方案数据不经过CPU直接存到内存CPU只需要处理“一帧接收完成”的事件。2.4 面试高频问题UART为什么不能多主多从这个问题看起来简单但能答到点子上的人不多。UART不能多主多从的根本原因是它没有地址机制也没有总线仲裁机制。I2C有7位地址每个从设备有自己的地址主机发地址就能选中对应的从设备。SPI有片选线拉低哪个片选就选中哪个从设备。UART什么都没有TX接RXRX接TX一对一。如果多个设备同时发送它们的TX线接在一起一个发高一个发低就会短路甚至烧毁引脚。那能不能用软件实现多机通信可以但很麻烦。常见的方法是所有设备的TX线通过开漏输出接到一起加上拉电阻然后软件定义一套地址协议。但这就不是标准UART了而且效率很低。实际项目中如果需要多机通信直接上RS-485或者CAN不要用UART硬撑。面试时如果被问到这个问题你可以先回答“UART没有地址和仲裁机制”然后补充“实际工程中多机通信用RS-485或CAN”再提一句“如果非要用UART组网可以用开漏加软件协议但可靠性和效率都不如专用总线”。这样回答层次分明面试官会觉得你既有理论又有工程判断。3. I2C的协议精髓与调试经验3.1 起始条件、停止条件与时钟拉伸I2C的协议核心可以概括为两根线、三种信号、四种状态。两根线是SCL和SDA三种信号是起始条件、停止条件、应答信号四种状态是空闲、起始、数据传输、停止。起始条件的定义是SCL为高电平时SDA从高变低。停止条件的定义是SCL为高电平时SDA从低变高。注意数据在传输过程中SDA只能在SCL为低电平时变化SCL为高电平时SDA必须保持稳定。这就是为什么起始和停止条件要用SCL高电平时的SDA跳变来定义——因为它们不是普通数据而是特殊的帧边界信号。时钟拉伸是I2C一个非常独特的设计。从设备如果处理不过来可以在接收完一个字节后把SCL拉低强制主机等待。主机释放SCL后如果SCL还是低就知道从机在拉伸时钟会一直等到SCL变高才继续。这个机制让慢速从设备也能挂在高速总线上非常巧妙。但时钟拉伸也是调试时的常见坑点。有些主机的I2C控制器不支持时钟拉伸或者支持得不好遇到从机拉伸时钟就会超时或者出错。我调试某款传感器时主机配置为400kHz传感器在转换数据时会拉伸时钟结果主机直接报总线错误。后来把速率降到100kHz拉伸时间短了才勉强能用。最终方案是换了一个支持时钟拉伸的主机控制器。3.2 7位地址、10位地址与地址冲突处理I2C的设备地址有7位和10位两种。7位地址是标准理论上可以挂128个设备但实际保留了16个地址0000xxx和1111xxx所以可用地址是112个。10位地址扩展了地址空间但用得少因为大多数场景112个地址足够了。7位地址的传输方式是起始条件后主机发送第一个字节高7位是地址最低位是读写标志0表示写1表示读。从机把自己的地址和收到的地址比较匹配就拉低SDA在第9个时钟周期作为ACK。地址冲突是实际项目中经常遇到的问题。比如两个传感器出厂地址都是0x48挂在一起就冲突了。解决方法有几种第一种用地址选择引脚。很多传感器芯片有ADDR引脚接GND是一个地址接VCC是另一个地址。比如某款温度传感器ADDR接GND时地址是0x48接VCC时是0x49。挂两个就用两个不同的地址。第二种用I2C多路复用器。比如TCA9548A一个主机侧接口八个从机侧通道每个通道可以挂地址相同的设备。主机先写多路复用器选择通道再和对应通道上的设备通信。这个方案适合挂很多个相同型号的传感器。第三种用GPIO控制从设备的电源或使能。同一时刻只使能一个设备其他设备断电或者使能脚拉低就不会冲突。但这种方法不能同时使用多个设备只能分时复用。面试时如果被问到“两个相同地址的I2C设备怎么挂在一起”把上面三种方案讲出来再补充一句“优先用地址选择引脚最省事”就非常完整了。3.3 上拉电阻的计算与总线电容限制I2C的SCL和SDA都是开漏输出必须加上拉电阻才能输出高电平。上拉电阻的阻值选择是一个经典的计算题面试中也经常考。上拉电阻的下限由灌电流能力决定。I2C标准规定标准模式100kHz和快速模式400kHz下器件的灌电流能力至少是3mA。假设VDD是3.3VVOL最大是0.4V那么上拉电阻最小值是R_min (VDD - VOL) / IOL (3.3 - 0.4) / 0.003 967Ω所以上拉电阻不能小于约1kΩ否则灌电流会超过器件的承受能力。上拉电阻的上限由上升时间决定。I2C总线的上升时间由RC充电决定R是上拉电阻C是总线电容。标准模式要求上升时间不超过1000ns快速模式不超过300ns。总线电容包括PCB走线电容、引脚电容、器件电容一般估算为10~20pF每个器件加上走线电容。假设总线电容是200pF快速模式要求上升时间300ns那么R_max t_r / (0.8473 × C) 300ns / (0.8473 × 200pF) ≈ 1.77kΩ所以上拉电阻的范围大概是1.8kΩ到10kΩ之间。实际项目中4.7kΩ是最常用的值兼顾了上升时间和功耗。如果总线电容大或者速率高就减小到2.2kΩ或1.5kΩ。如果设备多、电容大可能要用到1kΩ甚至更小。注意上拉电阻越小上升沿越陡但静态电流越大。3.3V系统用1kΩ上拉总线空闲时每个电阻消耗3.3mA两个电阻就是6.6mA。电池供电的设备要权衡这个功耗。3.4 用逻辑分析仪抓I2C波形的实战技巧调试I2C逻辑分析仪是必备工具。我用的最多的是某款开源逻辑分析仪配合开源软件便宜好用。抓I2C波形时有几个技巧可以让你快速定位问题。第一个技巧触发条件设成起始条件。I2C总线平时都是高电平起始条件是SDA在SCL高时下降。把触发条件设成这个就能抓到每一次通信的开始不会漏掉。第二个技巧解码器选I2C但要注意地址格式。逻辑分析仪的I2C解码器通常会把地址显示成7位或者8位。如果显示的是8位最低位是读写位要自己右移一位才是真正的7位地址。我见过有人把0x90当成设备地址其实是0x48左移一位加上写标志。第三个技巧看ACK位。每传输完一个字节第9个时钟周期是ACK位。如果从机拉低了SDA说明应答了如果SDA保持高说明没有应答。没有应答通常意味着地址不对、从机没上电、从机坏了、上拉电阻没接。先查地址再查硬件。第四个技巧看时钟拉伸。如果SCL在某个时刻被拉低了很久那就是从机在拉伸时钟。如果主机不等直接继续发时钟就会出错。逻辑分析仪上能清楚看到SCL被拉低的异常波形。第五个技巧看重复起始条件。I2C读操作通常是起始、写地址写方向、写寄存器地址、重复起始、写地址读方向、读数据、停止。重复起始条件看起来像起始条件但前面没有停止条件。逻辑分析仪上能清楚区分。我调试某款EEPROM时写数据一直失败。用逻辑分析仪抓波形发现主机发了起始条件和地址后从机没有ACK。查了半天发现是EEPROM的写保护引脚被拉高了。把写保护引脚拉低后一切正常。这个坑告诉我I2C通信失败先查硬件连接和配置引脚再查软件。4. SPI的四种模式与高速传输要点4.1 CPOL/CPHA四种模式的本质SPI的四种模式由CPOL和CPHA两个参数组合而成。CPOL是时钟极性CPHA是时钟相位。很多人背了四种模式的组合但没理解本质面试时一追问就露馅。CPOL决定的是时钟空闲时的电平。CPOL0空闲时SCLK为低CPOL1空闲时SCLK为高。CPHA决定的是数据在哪个时钟边沿采样。CPHA0在第一个边沿采样即SCLK从空闲电平跳变到有效电平的边沿CPHA1在第二个边沿采样即SCLK从有效电平跳变回空闲电平的边沿。组合起来就是四种模式模式CPOLCPHA空闲电平采样边沿移出边沿Mode 000低上升沿下降沿Mode 101低下降沿上升沿Mode 210高下降沿上升沿Mode 311高上升沿下降沿理解这个表的关键是采样边沿和移出边沿总是相反的。数据在移出边沿变化在采样边沿被读取这样数据在采样时是稳定的。Mode 0和Mode 3最常用因为它们的采样边沿都是上升沿符合大多数器件的习惯。面试时如果被问到“SPI四种模式怎么选”标准答案是“看从设备的数据手册”。但你可以补充一句“如果从设备支持多种模式优先选Mode 0因为最直观示波器上看起来最舒服。”这个补充会让面试官觉得你有实际调试经验。4.2 片选信号的管理与多从设备扩展SPI的片选信号是主机控制从设备的关键。每个从设备需要一根独立的CS线主机要通信时拉低对应的CS通信结束后拉高。CS的有效电平通常是低电平有效但也有高电平有效的看器件手册。多从设备扩展时CS线的数量等于从设备的数量。如果从设备很多GPIO不够用可以用译码器扩展。比如3-8译码器3根GPIO可以控制8个CS线。主机输出3位地址译码器选中对应的CS。这样GPIO开销从N根降到log2(N)根。但译码器方案有一个问题同一时刻只能选中一个从设备。如果两个从设备需要同时通信比如一个发一个收译码器就做不到了。这时候要么用更多GPIO要么用菊花链方案。菊花链是另一种扩展方式所有从设备的MOSI和MISO串联数据从主机出发经过第一个从设备再到第二个最后回到主机。每个从设备有一个数据输入和一个数据输出CS可以共用。这种方式适合移位寄存器类的器件比如LED驱动芯片。但菊花链的缺点是延迟大数据要经过所有器件才能到达目标器件。注意CS信号在通信间隙必须拉高让从设备复位内部状态机。如果CS一直拉低从设备可能一直处于选中状态内部逻辑混乱。我见过有人调试SPI Flash时CS忘记拉高结果Flash一直处于忙状态读出来的数据全是0xFF。4.3 SPI Flash读写时序与状态轮询SPI Flash是最常见的SPI从设备之一面试中也经常被问到。它的操作有几个关键点第一写使能。在写数据之前必须先发送写使能命令通常是0x06否则写操作会被忽略。这是Flash的保护机制防止误写。第二页编程。Flash的写入是按页进行的一页通常是256字节。如果写入的数据跨页需要分多次写。而且每次写之前都要发写使能命令。第三状态轮询。Flash写入需要时间典型值是0.7ms到几毫秒。在写入期间Flash处于忙状态不响应其他命令。主机需要不断读取状态寄存器命令0x05检查忙位bit 0是否清零。只有忙位清零后才能进行下一次操作。第四擦除。Flash写入之前必须先擦除擦除是按扇区4KB或块64KB进行的。擦除时间更长典型值是几十毫秒到几百毫秒。擦除后所有位变成1写入只能把1变成0不能把0变成1。所以如果要在同一地址写新数据必须先擦除。我调试SPI Flash时踩过一个坑写完数据后立即读读出来的数据不对。后来查手册发现写入后必须等忙位清零才能读。加了一个状态轮询的循环后问题解决。这个坑在面试时讲出来面试官会知道你真正写过Flash驱动。4.4 高速SPI的PCB布局与信号完整性SPI速率高了之后信号完整性问题就出来了。我做过一个项目SPI时钟跑到50MHzPCB走线没注意结果数据偶尔出错。后来用示波器看波形发现时钟线上有过冲和振铃数据线的建立时间和保持时间都不够。高速SPI的PCB布局有几个要点第一走线尽量短。SPI的走线长度最好控制在几厘米以内超过10厘米就要考虑信号完整性问题。如果必须走长线用差分方案或者降低速率。第二时钟线和数据线等长。虽然SPI是同步的时钟和数据一起走但走线长度差异太大会导致时序偏移。一般要求时钟线和数据线的长度差不超过走线总长的10%。第三远离干扰源。SPI线不要和电源线、电机驱动线平行走。如果必须交叉尽量垂直交叉减少耦合。第四加串联电阻。在时钟线和数据线的源端加22Ω到33Ω的串联电阻可以抑制过冲和振铃。这个电阻要靠近源端放置不能放在接收端。第五地平面完整。高速信号需要完整的参考地平面不要在地平面上开槽。如果走线要换层旁边要加地过孔提供回流路径。这些经验在面试时不一定直接考但如果你能主动提到面试官会觉得你做过高速设计不是只会写代码。5. 面试实战高频问题与回答框架5.1 总线选型类问题的回答模板面试中被问到“这个场景你选哪个总线”不要直接给答案按下面的框架来回答第一步明确需求。先问清楚场景的关键参数速率要求、设备数量、通信距离、功耗限制、实时性要求。比如“这个传感器数据量多大多久读一次总线上要挂几个设备”第二步排除不合适的。比如“如果速率要求超过10MbpsUART和I2C都达不到只能选SPI。”或者“如果要挂8个设备SPI需要8根片选线引脚不够优先考虑I2C。”第三步对比剩下的选项。把符合条件的总线列出来对比优缺点。比如“SPI和I2C都能满足速率要求但SPI需要4根线I2C只需要2根。如果引脚紧张选I2C如果速率要求高选SPI。”第四步给出推荐并说明理由。“综合考虑我推荐I2C因为引脚少、有ACK机制、支持多设备。虽然速率比SPI低但满足这个场景的需求。”这个框架的好处是即使你最后选的不是面试官心里的答案他也会认可你的分析过程。面试考的是思维不是标准答案。5.2 时序调试类问题的排查思路“I2C通信失败怎么排查”这是面试中的高频问题。我一般按下面的顺序来第一步查硬件。用万用表量SCL和SDA的电压空闲时应该是高电平。如果是低电平说明总线被拉死了可能是某个设备故障或者上拉电阻没接。量电源电压确认从设备供电正常。量地址选择引脚确认地址配置正确。第二步查波形。用逻辑分析仪抓波形看起始条件、地址、ACK、数据、停止条件是否完整。如果地址后没有ACK说明从设备没响应查地址和硬件。如果数据不对查时钟极性和相位。第三步查配置。确认主机的I2C速率、地址模式7位还是10位、时钟拉伸支持等配置和从设备匹配。确认从设备的寄存器配置正确有些设备需要先写配置寄存器才能正常工作。第四步查软件。确认读写时序符合从设备手册的要求。比如有些设备要求写寄存器和读寄存器之间要有延时有些设备要求先发特定命令序列。确认没有其他任务在同时访问I2C总线导致冲突。这个排查思路同样适用于SPI和UART。面试时把步骤讲清楚再举一个自己实际遇到的例子就非常有说服力。5.3 协议细节类问题的深度追问面试官如果追问协议细节说明他对你的期望比较高。几个常见的深度追问“I2C的时钟拉伸是怎么实现的”从设备在接收完一个字节后如果需要更多时间处理就把SCL拉低。主机释放SCL后检测到SCL还是低就知道从机在拉伸会等待。从设备处理完后释放SCL通信继续。“SPI的MISO线在从设备未选中时是什么状态”应该是高阻态。如果从设备的MISO不是高阻态多个从设备挂在一起就会冲突。所以SPI从设备必须支持MISO高阻输出。如果从设备不支持需要外加缓冲器或者多路复用器。“UART的帧错误和溢出错误有什么区别”帧错误是停止位不是高电平说明波特率不匹配或者数据位配置错误。溢出错误是接收缓冲区满了新数据来了没地方存说明CPU处理不及时。帧错误通常是配置问题溢出错误通常是软件问题。“I2C的总线仲裁是怎么工作的”多个主机同时发送时每个主机在发送每一位的同时也在监听SDA。如果自己发的是高但检测到低说明另一个主机在发低自己就失去仲裁退出竞争。仲裁是非破坏性的赢得仲裁的主机继续通信输的主机变成从机或者等待下一次。这些问题答得好不好取决于你对协议的理解深度。建议在复习时不要只背结论多问自己“为什么这样设计”“如果不这样会怎样”。5.4 常见问题速查表最后整理一张面试和调试中常见问题的速查表方便快速回顾问题现象可能原因排查方法UART收不到数据TX/RX接反、波特率不匹配、地线没接交换TX/RX、确认波特率、量地线UART数据乱码波特率偏差大、数据位/停止位配置错误换晶振、确认帧格式I2C无ACK地址错误、从机没上电、上拉电阻缺失查地址、量电源、量上拉I2C总线死锁从机拉低SDA不放、时钟拉伸超时重新上电、加总线恢复逻辑SPI数据错位CPOL/CPHA配置错误、CS时序不对确认模式、查CS波形SPI读Flash失败写使能没发、忙位没等、地址越界查命令序列、加状态轮询高速SPI误码走线太长、无串联电阻、地平面不完整缩短走线、加电阻、检查地平面这张表里的每一条都是我在实际项目中踩过的坑。面试时如果能结合具体案例讲比干巴巴地背知识点强十倍。6. 从面试考点到工程能力的跨越6.1 为什么背题解决不了实际问题我见过很多人把UART、I2C、SPI的面试题背得滚瓜烂熟但一到实际项目就抓瞎。原因很简单面试题考的是知识点实际项目考的是系统思维。举个例子面试题会问“I2C的上拉电阻怎么选”答案是“根据总线电容和上升时间计算”。但实际项目中你还要考虑上拉电阻的封装选多大0402还是0603放在板子的哪个位置靠近主机还是靠近从机电源域是3.3V还是1.8V需不需要电平转换这些问题面试题不会问但实际项目中每一个都可能让你调试半天。再比如面试题会问“SPI的四种模式怎么配置”答案是“看从设备手册”。但实际项目中你还要考虑主机的SPI控制器支持哪些模式DMA怎么配置中断优先级怎么设多个从设备怎么分时复用这些问题也没有标准答案只能靠经验积累。所以我的建议是面试前背题是必要的但不要只背题。多动手写驱动多用逻辑分析仪抓波形多踩坑。踩过的坑才是真正属于你的知识。6.2 从驱动开发到系统设计的进阶路径嵌入式工程师的成长路径大概可以分为三个阶段第一阶段能写驱动。能根据手册写出UART、I2C、SPI的收发函数能配置寄存器能处理中断。这个阶段的核心是“能用”。第二阶段能调问题。通信失败了能快速定位是硬件问题还是软件问题能用示波器和逻辑分析仪抓波形能看懂时序图。这个阶段的核心是“能修”。第三阶段能做设计。能在项目初期就规划好总线选型、引脚分配、电源域划分能预判可能的问题并提前规避。这个阶段的核心是“能防”。从第一阶段到第二阶段靠的是多调试、多踩坑。从第二阶段到第三阶段靠的是多总结、多复盘。每次项目结束后把遇到的问题和解决方案整理成文档下次遇到类似场景就能快速反应。6.3 我个人的几条实战建议最后分享几条我在实际工作中总结的建议不一定对但都是真实体会第一条永远不要相信“应该没问题”。硬件连接要量波形要抓配置要核对。我见过太多“应该没问题”最后变成“果然有问题”的案例。第二条逻辑分析仪比万用表有用。万用表只能看静态电平逻辑分析仪能看时序。调试通信问题逻辑分析仪是首选工具。几百块钱的投资能省下几十个小时的调试时间。第三条先查硬件再查软件。通信失败先量电源、量上拉、量连接再查代码。硬件问题占通信故障的七成以上但很多人一上来就改代码浪费时间。第四条留测试点。PCB设计时在SCL、SDA、SCLK、MOSI、MISO、CS上留测试点方便以后抓波形。测试点不用大一个过孔或者一个焊盘就行但调试时能救命。第五条写驱动先写测试。不要等整个系统集成好了再测驱动写一个简单的测试程序单独验证驱动能不能收发数据。驱动没问题了再集成能省很多事。这些建议听起来简单但真正做到的人不多。面试时如果你能结合这些经验来回答面试官会觉得你是一个有工程思维的开发者而不是一个只会背题的应试者。
阅读完成 · 觉得有帮助?