news 2026/9/8 5:07:57

嵌入式通讯协议核心解析:UART、I2C、SPI、CAN面试与工程实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
嵌入式通讯协议核心解析:UART、I2C、SPI、CAN面试与工程实战

各位做嵌入式的朋友应该都有体会,通讯协议是嵌入式开发中绕不开的核心知识。无论是驱动一个传感器、连接一块屏幕,还是搭建一整套控制系统,MCU 与外设之间、板卡与板卡之间都要靠各种通讯协议来完成数据传输。更重要的是,通讯协议几乎是嵌入式面试的必考内容,很多公司在技术面时都会围绕 UART、I2C、SPI、CAN 这些协议反复提问,从基本原理问到项目中的实际应用,再问到异常排查。

本文是“嵌入式八股”系列的第三篇,围绕通讯协议这个主题做一次系统梳理。我会从通讯协议的基本概念讲起,逐一拆解嵌入式开发中最常用的几种协议,包括它们的物理层特点、数据传输机制、优缺点和适用场景,然后整理一批面试中高频出现的通讯协议问题与回答思路,最后给出一些实用的工程建议。不管是正在准备嵌入式面试,还是想在项目中选型通讯方案,这篇文章都可以作为一份比较完整的参考资料。

1. 通讯协议到底在解决什么问题

1.1 通讯协议的本质

通讯协议的本质,是通讯双方约定好的一套“语言规则”。单片机本身只知道高低电平,而高低电平本身没有意义,必须通过协议把它们组织成有意义的“帧”“字节”“位”。协议里至少要约定以下几件事:

  • 电平标准:多高的电压算高电平,多低的电压算低电平,还是用差分信号传输。
  • 时序关系:时钟线何时采样,数据线何时变化,起始位、停止位、应答位如何排列。
  • 数据格式:一个字节包含多少位,先传高位还是低位,有没有校验位。
  • 通讯模式:是全双工、半双工,还是单工。
  • 寻址方式:如果总线上挂着多个设备,如何区分数据是发给谁的。

可以说,通讯协议是嵌入式系统内部各模块之间、系统与外部设备之间交换信息的“共同语言”。没有协议,硬件上即使连接在一起,数据也无法被正确理解。

1.2 为什么嵌入式开发必须掌握通讯协议

嵌入式开发几乎不可能脱离通讯协议单独存在。常见场景包括:

  • 读取传感器数据:温湿度传感器 SHT30 用 I2C 通讯,陀螺仪 MPU6050 用 I2C 或 SPI,气压计 BMP280 用 I2C/SPI。
  • 驱动显示与存储设备:LCD 屏幕用 SPI 或 RGB 接口,Flash 芯片用 SPI,EEPROM 用 I2C。
  • 板间通讯:设备之间长距离传输数据时,常用 CAN 总线或 RS485 总线。
  • 与上位机通讯:调试时通过 USART 转 USB 与 PC 交互,工业场景中通过 Modbus 协议与 PLC、触摸屏通讯。

而这些场景中的协议,恰恰是面试官最喜欢深入追问的内容。因为通讯协议能同时考察一个人的硬件基础、C 语言功底、时序理解能力和项目实战经验。

1.3 通讯方式的分类

从不同维度看,通讯方式可以分成几组:

分类维度类型典型代表
传输方向单工、半双工、全双工单工:红外遥控;半双工:I2C、RS485;全双工:UART、SPI
时钟方式同步、异步同步:I2C、SPI;异步:UART
数据传输方式串行、并行串行:绝大多数总线;并行:并口屏、并口 Flash
信号形式单端、差分单端:UART、I2C、SPI;差分:CAN、RS485、USB

理解这些分类非常重要,因为在面试中经常会出现“I2C 是什么类型的通讯”这类问题,如果你能直接说出“同步、半双工、串行通讯”,就能给面试官一个专业的第一印象。

2. 串行通讯的基础知识点

2.1 串行与并行的区别

串行通讯指数据按位依次传输,每次只传输一位,因此只需要较少的数据线。并行通讯则一次传输多位数据,需要多根数据线同时传输。

串行通讯的优点是引脚少、布线简单、成本低、抗干扰能力容易做得好,缺点是相同时钟频率下传输速率比并行低。并行通讯的优点是速度快,但占用引脚多,布线复杂,高速时容易产生信号串扰。

在实际项目中,MCU 与外设的长距离通讯几乎全部采用串行方式。并行通讯多用于内部总线或者对速度要求极高的存储接口,例如并口 NOR Flash。

2.2 同步通讯与异步通讯

同步通讯会单独提供一根时钟线。发送方和接收方都按照这根时钟线的节拍来传输数据,接收方在时钟的上升沿或下降沿采样数据线。典型的同步协议有 I2C、SPI、I2S。

异步通讯没有时钟线。通讯双方必须约定相同的波特率(即每秒传输多少位),接收方靠起始位来对齐数据的边界。典型的异步协议是 UART。

同步通讯的优点是时序明确、速率可以做得更高,缺点是需要额外的时钟线。异步通讯的优点是不需要时钟线、接线少,缺点是通讯双方必须保证波特率一致,且波特率误差过大时数据会出错。

2.3 单端信号与差分信号

单端信号使用一根信号线,电平参考 GND。UART、I2C、SPI 都属于单端信号。单端信号实现简单,但抗干扰能力相对较弱,距离长了容易受到共模干扰。

差分信号使用两根线传输一路信号,接收端比较两根线的电压差来判断电平。CAN、RS485、USB 都属于差分信号。差分信号抗干扰能力强,适合长距离、高可靠的工业现场。

面试中如果被问到“RS485 为什么能传上千米”,核心答案就是差分信号带来的抗干扰能力。

3. 常用通讯协议逐个拆解

3.1 UART:最基础的异步串行通讯

UART 全称 Universal Asynchronous Receiver/Transmitter,通用异步收发器。它是嵌入式开发中最常用、最简单的通讯协议之一。

硬件上只需要两根线:TX(发送)和 RX(接收)。两个设备之间的 TX 接对方的 RX。通讯双方共地。

UART 的数据帧格式为:

起始位数据位校验位(可选)停止位
1 位,低电平5~8 位,常用 8 位奇校验/偶校验/无校验1 位或 2 位,高电平

发送开始时,总线先拉低一个位时间,这就是起始位。接收方检测到这个下降沿后,就知道数据开始了。之后按约定的波特率依次采样数据位,最后是停止位。

面试中与 UART 相关的常见知识点:

  • 波特率:比如 9600、115200,表示每秒传输的位数。
  • 波特率误差:通讯双方的波特率必须足够接近,一般误差不能超过 2%~3%。
  • 起始位、停止位的作用:起始位用于对齐数据,停止位用于保证帧间隔。
  • 流控:有硬件流控(RTS/CTS)和软件流控(XON/XOFF),但裸机开发中常用无流控模式。

UART 本身只定义了数据帧格式,并没有定义电平标准。我们通常说的 TTL 电平 UART、RS232、RS485,本质上是 UART 数据在不同物理层上的传输方式。

3.2 I2C:两线制同步半双工通讯

I2C 全称 Inter-Integrated Circuit,由飞利浦公司(现恩智浦)提出,是一种广泛用于连接低速外设的两线制同步串行总线。

I2C 只需要两根线:

  • SCL(Serial Clock):时钟线,由主机产生。
  • SDA(Serial Data):数据线,双向传输数据。

I2C 支持一主多从或多主通讯,每个从设备都有一个唯一的地址。主机通过地址来选择与哪个从设备通讯。

I2C 的关键机制:

  • 起始条件:SCL 为高电平时,SDA 产生一个下降沿。
  • 停止条件:SCL 为高电平时,SDA 产生一个上升沿。
  • 应答机制:每传输 8 位数据后,接收方需要拉低 SDA 表示应答(ACK),不应答则释放 SDA(NACK)。
  • 地址帧:通常主机先发送 7 位从机地址加 1 位读写标志位。
  • 速率模式:标准模式 100kbit/s,快速模式 400kbit/s,高速模式 3.4Mbit/s。

I2C 的上拉电阻问题也是面试常考点。因为 I2C 总线使用开漏输出,所以 SCL 和 SDA 上必须接上拉电阻。上拉电阻的取值需要平衡功耗和上升沿速度。

I2C 的优点是引脚少、支持多设备挂载、接线简单,非常适合连接传感器、EEPROM、RTC 等低速设备。缺点是速率相对 SPI 较低,且没有类似 SPI 的片选信号,多设备靠地址区分。

3.3 SPI:高速全双工同步通讯

SPI 全称 Serial Peripheral Interface,串行外设接口,由摩托罗拉公司提出。

SPI 需要四根线:

  • SCLK:时钟线,由主机产生。
  • MOSI(Master Out Slave In):主机输出、从机输入。
  • MISO(Master In Slave Out):主机输入、从机输出。
  • CS/SS(Chip Select / Slave Select):片选线,低电平有效。

SPI 是真正意义上的全双工通讯,主机和从机可以同时发送和接收数据。它的核心机制是移位寄存器:每产生一个时钟脉冲,主机和从机各移出一位数据,同时各移入一位数据。

SPI 没有地址机制,依靠 CS 片选线来选择从机。这也是 SPI 和 I2C 最直观的区别之一。

SPI 的四种工作模式由 CPOL(时钟极性)和 CPHA(时钟相位)决定:

模式CPOLCPHA数据采样时机
模式 000上升沿采样
模式 101下降沿采样
模式 210下降沿采样
模式 311上升沿采样

其中模式 0 是最常用的。

如果通讯双方配置的 SPI 模式不一致,接收到的数据就会错位。所以在写 SPI 驱动时,必须先查阅从设备数据手册,确认设备要求的是哪种模式。

SPI 的优点是速率高、全双工、数据吞吐量大,适合 Flash、屏幕、SD 卡等需要用大量数据交互的场景。缺点是需要至少 4 根线,且每个从机都需要单独的 CS 引脚,设备多了以后布线压力比较大。

3.4 CAN:高可靠多主差分通讯

CAN 全称 Controller Area Network,控制器局域网,由博世公司开发,最早用于汽车电子,现在已经是工业控制、机器人、医疗设备中主流的现场总线之一。

CAN 使用两根差分线:CAN_H 和 CAN_L。逻辑电平分为显性电平(Dominant)和隐性电平(Recessive)。显性电平对应逻辑 0,隐性电平对应逻辑 1。当总线上多个节点同时发送时,显性电平会覆盖隐性电平,这个特性是 CAN 总线仲裁的基础。

CAN 总线的物理层要求两端各接一个 120Ω 终端电阻,用于消除信号反射。

CAN 的关键机制:

  • 多主通讯:总线上任何节点都可以主动发起通讯。
  • 报文仲裁:多个节点同时发送时,ID 小的报文优先发送,也就是显性位优先。
  • 帧类型:数据帧、远程帧、错误帧、过载帧,最常用的是数据帧。
  • 标准帧与扩展帧:标准帧 ID 为 11 位,扩展帧 ID 为 29 位。
  • 错误处理:CAN 节点具备完善的错误检测和处理机制,包括位错误、填充错误、CRC 错误、格式错误、应答错误。

CAN 的波特率与位时序相关。每个位时间由同步段、传播段、相位缓冲段 1、相位缓冲段 2 组成,采样点就位于相位缓冲段 1 和 2 的交界处。实际项目中,采样点通常设置在 75%~87.5% 左右,具体取决于总线速率和线缆长度。

CAN 的主要优点:

  • 可靠性高,抗干扰能力强。
  • 支持多主,节点之间可以自由通讯。
  • 总线利用率高,仲裁机制避免了数据碰撞。
  • 支持错误检测和自动重发。

CAN 的缺点是协议相对复杂,配置难度比 UART、SPI 高,普通 MCU 通常需要外接 CAN 收发器芯片(如 TJA1050、SN65HVD230)。

3.5 RS485 与 Modbus:工业现场的经典组合

RS485 是电气层标准,定义的是差分信号的电气特性,不规定具体的数据帧格式。因此 RS485 可以承载 UART 协议数据,也可以承载 Modbus 协议数据。

RS485 的特点:

  • 差分信号,抗干扰能力强。
  • 支持多点通讯,一条总线上最多可以挂接 32 个节点(标准负载条件下)。
  • 半双工工作,同一时刻只能有一个节点发送。
  • 传输距离远,1200m 以上仍可正常工作(与波特率相关)。
  • 需要加终端电阻,通常在总线两端各接一个 120Ω 匹配电阻。

Modbus 是应用层协议,在很多工业设备中,物理层采用 RS485,数据链路层和应用层采用 Modbus RTU。

Modbus RTU 的报文格式为:

从站地址功能码数据区CRC 校验
1 字节1 字节N 字节2 字节

常用的功能码包括:

  • 0x01:读线圈
  • 0x02:读离散输入
  • 0x03:读保持寄存器
  • 0x04:读输入寄存器
  • 0x05:写单个线圈
  • 0x06:写单个寄存器
  • 0x0F:写多个线圈
  • 0x10:写多个寄存器

Modbus 是主从协议,主机发起请求,从机响应。从机不能主动向主机发送数据。

面试中如果被问到 Modbus 和 RS485 的关系,回答思路是:RS485 解决的是“电信号怎么传”,Modbus 解决的是“数据怎么组织、指令怎么定义”,两者可以组合使用。

3.6 其他常见协议简述

除了上面几种协议,嵌入式开发中还经常接触到:

  • USB:差分信号,支持热插拔、枚举、多种传输类型。复杂但通用性极强。
  • 以太网:工业以太网、EtherCAT、PROFINET 等衍生协议覆盖面很广。
  • SDIO:用于 SD 卡、WiFi 模块等设备。
  • 1-Wire:单总线协议,典型应用是 DS18B20 温度传感器。
  • LIN:汽车领域低成本的局域互联网络,CAN 的补充。

面试中通常会先考察 UART、I2C、SPI、CAN 这四大协议,因为这些协议覆盖面最广,项目中出现概率最高。

4. 常用协议特性对比

面试中经常要求横向对比不同协议,下面这张表建议熟练掌握:

特性UARTI2CSPICAN
信号线2 根(TX/RX)2 根(SCL/SDA)4 根(SCLK/MOSI/MISO/CS)2 根(CAN_H/CAN_L)
通讯模式全双工半双工全双工半双工
同步/异步异步同步同步同步
信号形式单端单端单端差分
多设备支持不支持(点对点)支持,多从机靠地址支持,多从机靠片选支持,多主
速率一般 ≤ 几 Mbit/s最高 3.4Mbit/s(高速模式)几十 Mbit/s最高 1Mbit/s(经典 CAN)
传输距离较短较短较短很长
抗干扰能力
典型应用调试、GPS、蓝牙模块传感器、EEPROM、RTCFlash、屏幕、SD 卡汽车、工业控制

这个表不需要死记硬背,而是要通过理解协议的工作原理自然推导出来。比如“SPI 为什么是全双工?”因为 MOSI 和 MISO 是独立的两根线,可以同时数据双向传输。“I2C 为什么是半双工?”因为 SDA 是双向数据线,同一时刻只能一个方向传输数据。

5. 通讯协议面试高频题目与回答思路

5.1 UART 面试题

问题 1:UART 通讯中起始位和停止位的作用是什么?

回答思路:起始位用于标识一帧数据的开始,接收方在检测到起始位(高电平变为低电平的下降沿)后开始采样;停止位用于标识一帧数据的结束,同时也保证了两个连续数据帧之间有一段高电平间隔,避免起始位检测混乱。

问题 2:两个设备 UART 通讯,波特率不一致会怎样?

回答思路:接收方采样时机和数据实际位置出现偏差,采到的数据位不对,最终出现乱码。波特率误差较小时(一般 2% 以内)可能勉强可以通讯,误差大了必然出错。

问题 3:为什么有时候 UART 传输距离很短?

回答思路:UART 属于单端信号,参考地是 GND,在长距离传输时容易受到共模干扰,且信号电平摆幅固定,线缆的分布电容也会导致波形变差。因此 UART 适合板级短距离通讯,远距离传输应改用 RS232、RS485 或者 CAN 等物理层方案。

5.2 I2C 面试题

问题 1:I2C 为什么需要上拉电阻?

回答思路:I2C 的 SCL 和 SDA 是开漏输出结构,只能主动拉低电平,不能主动输出高电平。所以必须通过上拉电阻将电平拉高。上拉电阻的阻值会影响功耗和通讯速率,阻值太小功耗大,阻值太大上升沿变缓,高速通讯容易出错。

问题 2:I2C 一主多从时如何区分不同设备?

回答思路:每个 I2C 从设备都有唯一的器件地址。主机在起始条件后先发送从机地址加读写标志位,只有地址匹配的从机会产生应答,其他从机则不参与后续通讯。

问题 3:如果 I2C 总线上某个从机不回应答,可能是什么原因?

回答思路:地址错误、从机供电异常、SDA 或 SCL 线序接反、上拉电阻缺失、从机地址冲突,或者从机正处于复位状态。排查时先用示波器或逻辑分析仪抓取总线波形,确认起始条件、地址帧和应答位是否存在。

5.3 SPI 面试题

问题 1:SPI 和 I2C 的区别有哪些?

回答思路:物理层上 SPI 需要 4 根线,I2C 只需要 2 根线;SPI 全双工,I2C 半双工;SPI 依靠 CS 选择从机,I2C 依靠地址选择从机;SPI 速率通常高于 I2C;SPI 没有应答机制,I2C 有 ACK/NACK 应答。

问题 2:SPI 的四种模式怎么区分?

回答思路:由 CPOL 和 CPHA 两个参数决定。CPOL 决定空闲时时钟电平为高还是低,CPHA 决定数据在第几个边沿采样。两个设备必须工作在相同的模式下才能正常通讯。

问题 3:SPI 模式配置错了会出现什么现象?

回答思路:主机和从机的采样边沿不匹配,导致数据移位错乱,读到的数据出现位错位或字节错位,甚至完全无法通讯。一般表现为读寄存器值异常,或者设备完全不应答。

5.4 CAN 面试题

问题 1:CAN 总线为什么需要终端电阻?

回答思路:CAN 属于高速差分总线,信号在总线末端会发生反射。终端电阻可以吸收反射能量,保证信号质量。标准要求总线两端各接一个 120Ω 电阻,总等效电阻约 60Ω。

问题 2:CAN 的仲裁机制是怎样的?

回答思路:CAN 总线上的显性电平(逻辑 0)可以覆盖隐性电平(逻辑 1)。多个节点同时发送时,每个节点在发送过程中监督总线电平,如果自己发送隐性位但总线上是显性位,就说明有更高优先级(ID 更小)的报文在发送,该节点立即停止发送,转为接收状态。

问题 3:CAN 标准帧和扩展帧有什么区别?

回答思路:标准帧 ID 是 11 位,扩展帧 ID 是 29 位。扩展帧 ID 是 11 位基础 ID 加 18 位扩展 ID。两者在帧格式上不同,但仲裁机制和错误机制相同。

问题 4:CAN 能传输多远的距离?

回答思路:与波特率有关。波特率越低,传输距离越长。比如 1Mbit/s 时可以达到约 40m,125kbit/s 时可以达到约 500m 以上,50kbit/s 时可以达到 1000m 左右。具体还需要考虑线缆质量、总线节点数和终端电阻配置。

5.5 RS485/Modbus 面试题

问题 1:RS485 为什么传输距离远、抗干扰能力强?

回答思路:RS485 采用差分信号传输,接收端比较 A/B 两线电压差来判断逻辑值,共模干扰对差分信号的干扰会被抵消。因此抗干扰能力强,传输距离远。

问题 2:RS485 通讯中 A/B 线接反会怎样?

回答思路:逻辑电平反相,接收到的数据取反,上位机或 MCU 收到的数据乱码或完全不对。很多 RS485 收发器也有 A/B 接反保护,但更稳妥的做法是严格按照 A 接 A、B 接 B 来接线。

问题 3:Modbus RTU 的一帧报文由哪些部分组成?

回答思路:从站地址(1 字节)、功能码(1 字节)、数据区(N 字节)、CRC16 校验(2 字节)。CRC 校验低位在前,高位在后。

5.6 综合性设计题

这一类问题是面试中难度较高的,通常没有标准答案,考察的是综合能力。

问题 1:如果让你设计一个多设备通讯系统,你会怎么选型?

回答思路:先分析需求:通讯距离、数据量、实时性、节点数量、成本、可靠性和抗干扰要求。短距离小数据量选 I2C,大数据量高速率选 SPI,长距离多节点选 RS485 或 CAN,汽车或工业现场选 CAN,低成本点对点选 UART。

问题 2:如何判断一个新的通讯协议芯片是否工作正常?

回答思路:先用示波器或逻辑分析仪确认时钟线是否有时钟输出,确认/CS 片选电平是否正确拉低,确认 MOSI 发送的数据是否与寄存器配置一致。再检查 MISO 返回数据,必要时先用已知设备验证主机配置是否正确。

问题 3:通讯数据出现乱码,一般怎么排查?

回答思路:从物理层到协议层逐步排查。先确认波特率或时钟极性配置;再测量波形,看电平幅值是否正常,是否有毛刺;然后检查数据帧格式、地址、校验;最后检查软件逻辑,比如缓冲区处理、中断优先级等。

6. 实战代码示例

下面用 STM32 + HAL 库给出几个常见协议的初始化与读写示例。代码以思路演示为主,具体引脚以实际板卡为准。

6.1 UART 收发示例

// 文件路径:uart_demo.c #include "main.h" extern UART_HandleTypeDef huart1; uint8_t rx_buffer[1]; uint8_t tx_data[] = "Hello UART\r\n"; void UART_Demo_Init(void) { // 使能串口接收中断,单字节接收 HAL_UART_Receive_IT(&huart1, rx_buffer, 1); } void UART_Demo_Send(void) { HAL_UART_Transmit(&huart1, tx_data, sizeof(tx_data) - 1, 1000); } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART1) { // 收到一字节数据,原样回显 HAL_UART_Transmit(&huart1, rx_buffer, 1, 100); // 重新使能接收 HAL_UART_Receive_IT(&huart1, rx_buffer, 1); } }

关键点:UART 接收中断必须在每次接收完一帧或一字节后重新使能,否则后续数据无法触发中断。实际项目中建议使用空闲中断加 DMA 的方式处理不定长数据。

6.2 I2C 读传感器示例

// 文件路径:i2c_demo.c #include "main.h" extern I2C_HandleTypeDef hi2c1; // 以 SHT30 温湿度传感器为例,设备地址 0x44 << 1 #define SHT30_ADDR (0x44 << 1) #define SHT30_CMD_MEASURE 0x2C06 uint8_t recv_data[6]; void SHT30_Read(float *temperature, float *humidity) { uint8_t cmd[2]; cmd[0] = (SHT30_CMD_MEASURE >> 8) & 0xFF; cmd[1] = SHT30_CMD_MEASURE & 0xFF; // 发送测量命令 HAL_I2C_Master_Transmit(&hi2c1, SHT30_ADDR, cmd, 2, 1000); // 等待测量完成 HAL_Delay(20); // 读取 6 字节数据 HAL_I2C_Master_Receive(&hi2c1, SHT30_ADDR, recv_data, 6, 1000); // 温度:前两个字节为数据,第三个字节为 CRC uint16_t temp_raw = (recv_data[0] << 8) | recv_data[1]; *temperature = -45.0f + 175.0f * (float)temp_raw / 65535.0f; // 湿度:后两个字节为数据 uint16_t hum_raw = (recv_data[3] << 8) | recv_data[4]; *humidity = 100.0f * (float)hum_raw / 65535.0f; }

关键点:I2C 读设备时,地址最后一位是读写标志。HAL 库的地址参数已经帮我们处理了移位,注意传入时地址要左移一位。从设备收到命令后可能需要延时等待处理完成,不能立刻读数据。

6.3 SPI 读写 Flash 示例

// 文件路径:spi_demo.c #include "main.h" extern SPI_HandleTypeDef hspi1; #define FLASH_CS_LOW() HAL_GPIO_WritePin(FLASH_CS_GPIO_Port, FLASH_CS_Pin, GPIO_PIN_RESET) #define FLASH_CS_HIGH() HAL_GPIO_WritePin(FLASH_CS_GPIO_Port, FLASH_CS_Pin, GPIO_PIN_SET) uint8_t spi_tx_buf[8]; uint8_t spi_rx_buf[8]; // 读取 Flash 的 JEDEC ID,用于验证 SPI 通讯是否正常 void Flash_Read_JEDEC_ID(void) { uint8_t tx_cmd[4] = {0x9F, 0x00, 0x00, 0x00}; uint8_t rx_data[4] = {0}; FLASH_CS_LOW(); HAL_SPI_TransmitReceive(&hspi1, tx_cmd, rx_data, 4, 1000); FLASH_CS_HIGH(); // rx_data[1] 为 Manufacturer ID,rx_data[2] 为 Memory Type,rx_data[3] 为 Capacity }

关键点:SPI 时序中,CS 片选必须在整个读写过程中保持拉低,操作完成后拉高。主机发送数据的同时必须接收数据,因为 SPI 是全双工发送,即使只读数据,也要发送填充字节来产生时钟。

6.4 CAN 发送示例

// 文件路径:can_demo.c #include "main.h" extern CAN_HandleTypeDef hcan1; void CAN_SendMessage(uint32_t id, uint8_t *data, uint8_t len) { CAN_TxHeaderTypeDef tx_header; uint32_t tx_mailbox; tx_header.IDE = CAN_ID_STD; // 标准帧 tx_header.RTR = CAN_RTR_DATA; // 数据帧 tx_header.DLC = len; // 数据长度 tx_header.StdId = id; // 标准帧 ID HAL_CAN_AddTxMessage(&hcan1, &tx_header, data, &tx_mailbox); } void CAN_Init_Filter(void) { CAN_FilterTypeDef filter; filter.FilterBank = 0; filter.FilterMode = CAN_FILTERMODE_IDMASK; filter.FilterScale = CAN_FILTERSCALE_32BIT; filter.FilterIdHigh = 0x0000; filter.FilterIdLow = 0x0000; filter.FilterMaskIdHigh = 0x0000; filter.FilterMaskIdLow = 0x0000; filter.FilterFIFOAssignment = CAN_RX_FIFO0; filter.FilterActivation = ENABLE; HAL_CAN_ConfigFilter(&hcan1, &filter); }

关键点:CAN 发送前必须调用HAL_CAN_Start启动 CAN 外设,接收报文前必须先配置滤波器,否则中断不会触发。每发送一帧数据后要检查邮箱发送完成状态。

7. 通讯协议调试经验与工具推荐

7.1 常见调试工具

工具用途
逻辑分析仪抓取 I2C、SPI、UART 时序,分析波形和数据帧
示波器测量电平、毛刺、上升沿时间,检查物理层信号质量
USB 转 TTL 串口模块调试 UART,查看嵌入式设备打印日志
USB 转 RS485 模块调试 RS485/Modbus 通讯
CAN 分析仪抓取 CAN 报文,分析 ID、数据、错误帧
多功能协议分析工具部分工具支持 UART/I2C/SPI/CAN 自动解码

7.2 调试建议

首先确认物理连接,TX/RX 是否交叉、I2C 上拉电阻是否焊接、RS485 A/B 是否正确、CAN 终端电阻是否匹配。很多通讯问题都不是软件问题,而是硬件连接问题。

然后确认配置参数,UART 的波特率、I2C 的地址、SPI 的模式和速率、CAN 的波特率,这些参数任何一个不匹配通讯都会失败。

再用逻辑分析仪抓波形。通过波形可以直接看到起始位、数据位、应答位、片选信号是否正常,比在代码里加打印日志高效得多。

最后做数据校验。先发送固定字节或者 Read ID 命令,确认链路是否通,再处理复杂业务数据。

7.3 一个代表性的排查案例

场景:I2C 读取温湿度传感器,始终返回错误数据或超时。

排查过程:

  1. 先用万用表测量 SCL 和 SDA 电压,正常待机时应该被上拉到 3.3V。
  2. 如果电压为 0,检查上拉电阻是否焊接、传感器是否供电。
  3. 用逻辑分析仪抓取起始条件、设备地址、ACK 位。
  4. 发现 SDA 上完全没有 ACK 信号,进一步确认是设备地址左移/合并错误,修正后正常。

这个案例说明,排查通讯问题时要按照“电平 → 波形 → 协议 → 软件”的顺序逐层缩小范围,不要一开始就翻代码。

8. 通讯协议工程建议与注意事项

8.1 协议选型建议

选型时不要盲目追求“高速”或“功能强”,而要从项目需求反推:

  • 节点数量:只有两三个设备且距离很近,UART 或 I2C 足够。
  • 数据量:大量数据块传输,优先考虑 SPI 或并口。
  • 节点数量多且距离远:考虑 RS485 或 CAN。
  • 环境恶劣、抗干扰要求高:RS485 或 CAN 优于 UART/I2C/SPI。
  • 需要多主机同时通讯:CAN 是最合理的方案。
  • 成本敏感:UART 硬件成本最低,I2C 外设成本低。

8.2 驱动代码编写建议

通讯驱动是嵌入式系统中频繁使用的模块,建议封装成独立接口层。将协议驱动和业务逻辑解耦,后续更换芯片或调整引脚时,只需要修改底层实现,不需要改动上层代码。

// 抽象通讯接口示例 typedef struct { int (*init)(void); int (*read)(uint8_t reg, uint8_t *buf, uint16_t len); int (*write)(uint8_t reg, uint8_t *buf, uint16_t len); } bus_ops_t;

将 UART、I2C、SPI 各自的读写函数封装成统一接口,上层应用面向接口编程,这样代码的可维护性和可移植性都会好很多。

8.3 需要注意的坑

以下问题在嵌入式通讯开发中非常常见:

  • 未处理总线上无应答、超时等异常情况,导致程序卡死在等待状态。
  • 中断优先级配置不合理,导致通讯数据丢失。
  • 波特率误差过大,尤其在高波特率下容易偶发乱码。
  • I2C 上没有上拉电阻或上拉电阻阻值不合适。
  • SPI 模式和速率不匹配,通讯出现偶发错误。
  • CAN 过滤器配置错误,导致接收不到报文。
  • RS485 半双工收发切换不及时,导致最后一个字节被截断。

写代码时建议给所有通讯接口增加超时机制和错误处理,避免在无应答时无限等待。

8.4 从面试角度准备的建议

准备通讯协议相关面试题时,不要只背概念。面试官更看重你能不能结合项目经验说明问题。比如你说自己用过 I2C,就要能说清楚设备地址怎么确定、上拉电阻阻值选多少、实测波形什么样、有没有遇到过总线锁死的问题,以及你是如何解决的。

以下复习方向建议优先准备:

  • 每个协议的物理层电平特性、引脚定义。
  • 每个协议的时序图,能自己画出来并解释每一步。
  • 每个协议的数据帧格式,能写出关键字段。
  • 每个协议的优缺点和适用场景。
  • 项目中实际用过哪些协议,遇到了什么坑。
  • 经典对比题,如 I2C 与 SPI、RS485 与 CAN、UART 与 RS485。

嵌入式面试中,通讯协议是最容易体现项目深度的话题之一。基础原理大家都懂,但能把原理和项目经验结合起来讲清楚的人并不多。建议你在准备面试时,把自己做过的每一个用到通讯协议的项目单独整理成“项目背景 + 协议选型原因 + 关键配置参数 + 遇到的坑 + 解决方案”的结构,这样不管面试官问到哪个层次,你都能有条理地回应。

通讯协议的知识体系比较庞杂,建议以 UART、I2C、SPI、CAN 四个协议为核心深入学习,RS485/Modbus 作为补充,USB 和以太网按需了解。先把四类主流协议吃透,再结合实际项目反复调试和复盘,这些知识就会逐渐变成自己的工程经验。本文整理的内容可以作为一份复习资料,建议收藏备用,面试前过一遍会有很大帮助。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 5:05:46

用MCP构建数据血缘追踪Server:让AI成为企业数据的寻根大师

别再问数据对不对了&#xff1a;实战 MCP 数据血缘追踪 Server&#xff0c;让 AI 成为企业数据的「寻根大师」早上十点&#xff0c;业务负责人拿着日报找到我&#xff1a;「日报里 GMV 怎么和财务差了两千万&#xff1f;这个数到底哪来的&#xff1f;」我打开调度平台&#xff…

作者头像 李华
网站建设 2026/9/8 5:05:34

滚动渐变导航栏实现:基于scroll事件与CSS过渡的完整方案

简介&#xff1a;这是一份面向前端初学者的HTML5CSS3JS小实例&#xff0c;解决页面滚动时固定导航栏因透明背景导致内容遮挡、可读性下降的问题&#xff0c;通过监听滚动距离让导航栏背景从透明渐变为半透明或纯色&#xff0c;同时协调文字颜色与阴影变化&#xff0c;适合网页设…

作者头像 李华
网站建设 2026/9/8 5:04:09

独立开发者AI编程工具选型与实战:从补全到提示词的高效组合

最近好多独立开发者在群里问同一个问题&#xff1a;AI编程工具现在这么多&#xff0c;到底该用哪个&#xff1f;说实话&#xff0c;我自己的体会是&#xff0c;这问题没有标准答案&#xff0c;但有一套方法论可循。今天的分享就围绕“怎么选”和“怎么用”展开&#xff0c;先讲…

作者头像 李华
网站建设 2026/9/8 5:04:07

OLED显示器选购全攻略:从自发光原理到避坑实操

1. 先搞清楚&#xff1a;OLED凭什么比普通显示器贵这么多1.1 自发光才是OLED的立身之本很多人第一次接触OLED这个词&#xff0c;是在手机上。曲面屏、折叠屏、屏下指纹&#xff0c;这些技术能落地&#xff0c;靠的都是OLED可以做得又薄又柔。但到了桌面显示器上&#xff0c;OLE…

作者头像 李华
网站建设 2026/9/8 5:03:41

Dify工作流实战:从节点编排到生产级AI应用设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/8 5:01:49

Codex 启动失败排查:config.toml 与 CLI 路径修复指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华