
嵌入式工程师面试中通讯协议是绕不开的高频考点。不管是校招还是社招简历上写了 UART、I2C、SPI、CAN 这些关键词面试官就默认你懂时序、懂帧格式、懂实际调试。很多候选人能把协议特性背得很熟一追问“RS485 收发切换怎么处理”“Modbus 报文里 CRC 怎么算”“多设备挂总线为什么需要仲裁”就答不上来。这篇是嵌入式八股系列第 3 篇聚焦通讯协议。内容不是把《嵌入式八股文》里每个协议的简介抄一遍而是按面试提问和工程落地的顺序把最常考的协议、最常踩的坑、最需要背下来的代码片段整理成一套可以直接用的复习材料。看完之后你应该能回答这几类问题协议选型怎么答、UART/RS485/Modbus 的帧格式和工程细节、I2C 和 SPI 的时序差异、CAN 的仲裁机制以及通讯协议在无 OS 和 RTOS 下的接口设计思路。1. 嵌入式通讯协议考点速览协议物理层特征面试高频问题工程关键点UART异步串行TTL 电平起始位/停止位/波特率误差帧格式、丢字节处理RS485差分信号半双工收发切换、终端电阻方向控制 GPIO、120Ω 匹配Modbus应用层协议RTU/ASCII报文格式、CRC16 校验主从模型、功能码I2C两线制开漏上拉起始/停止条件、应答7 位/10 位地址、时钟拉伸SPI四线制全双工四种模式 CPOL/CPHA主从配置、片选管理CAN差分总线多主仲裁机制、位填充过滤器配置、错误状态这个表格几乎就是一张面试地图。面试官问“你用过哪些通讯协议”时不需要把协议名称全部报一遍而是按这张表的方向挑两三款真正写过程序的协议展开物理层是什么样的帧格式怎么组织的调试时用示波器或逻辑分析仪看到了什么波形遇到不稳定时的排查思路是什么。更深一层多数通讯协议题背后都在考察一个核心概念同步与握手。UART 靠波特率约定节奏I2C 靠时钟线同步数据SPI 以主机时钟为准CAN 靠位同步和仲裁。理解了这一点背协议细节就不容易乱。2. 通讯协议选型与使用边界面试里常有一种问法“有一个传感器数据量不大距离 3 米要在 MCU 上读它你选什么协议”这种题没有唯一答案重点考察你的边界判断。下面是我建议的回答框架。先判距离和速率。板内通信I2C 或 SPI 是首选。I2C 线少、适合接大量低速外设SPI 速率高、适合 Flash、显示屏这类吞吐要求高的器件。板间短距离、速率不高选 UART简单可靠。距离超过 1 米尤其有电机、电源等干扰源的工业环境直接考虑 RS485 或 CAN。RS485 本质是 UART 的差分物理层还是半双工CAN 是多主、带优先级仲裁、抗干扰更强适合多个节点需要自主上报的场景。再判节点数和主从关系。I2C 靠地址区分设备但总线上设备多、线长之后可靠性会降。SPI 每个从机都要一根片选线设备多了 PCB 走线难受。Modbus RTU 跑在 RS485 上组网数量一般建议不超过 32 个节点具体受收发器驱动能力影响。CAN 2.0 标准帧有 11 位标识符实际工程中组几十个节点很常见。注意使用边界。UART 是异步协议对波特率误差敏感长线传输时尽量降速、加校验。RS485 半双工会带来收发切换延时主从轮询模式下要留足响应超时时间。SPI 没有标准应答机制从机出错主机很难感知。I2C 开漏结构决定总线上必须有上拉电阻而且不能两个主机同时抢占总线除非做多主仲裁。Modbus 的应用层非常简洁但功能码覆盖不了所有需求很多人会自定义寄存器区这也是面试官喜欢追问的点。3. UART 与 RS485 基础与 Modbus 实战3.1 UART 帧格式UART 是嵌入式最常用的异步串行通讯协议面试必问。常见题是“UART 一帧数据由什么组成”。标准答案空闲位、起始位、数据位通常 8 位、校验位可选、停止位。低位先发。波特率就是每秒传输的位数双方要约定一致。帧格式里最容易忽略的是波特率误差实际工程里晶振偏差、分频误差累计会让通信偶发错码。判断标准是接收端采样点尽量落在数据位中间误差一般要控制在 2% 以内越高速越严格。以 STM32 标准库风格为例UART 初始化的完整配置需要关注以下几个参数void UART_InitConfig(uint32_t baudrate) { // 1. 打开时钟配置 GPIO 复用为串口功能 // 2. 配置串口参数波特率、数据位、停止位、校验位 // 3. 使能接收中断或 DMA // 4. 使能串口 }这里每个步骤都有对应寄存器面试时不会被要求默写寄存器但需要把“时钟→引脚→串口参数→中断/DMA”这条链路讲清楚。3.2 RS485 收发切换RS485 物理层是差分信号A、B 两线电压差表示 0 和 1抗共模干扰能力强支持多节点挂总线。但 RS485 收发器通常只有一个差分对半双工工作所以同一个时刻只能发送或只能接收。这就引出了高频题“RS485 怎么切换收发方向”工程上常用一个 GPIO 控制收发器的 DE/RE 引脚。发送时拉高让驱动器使能发送完成后必须拉低恢复接收。切换时序是一个大坑。发送完最后一个字节不能立刻切换方向因为移位寄存器可能还没把数据完全发出去。正确的做法是等待发送完成标志位再拉低方向引脚。以下是典型的工程代码片段void RS485_SendData(uint8_t *buf, uint16_t len) { RS485_DE_HIGH(); // 切换到发送模式 UART_SendBytes(buf, len); // 发送数据 while (!UART_TX_COMPLETE); // 等待发送完成确保移位寄存器清空 RS485_DE_LOW(); // 切换回接收模式 }如果忽略了这个等待就会出现数据末尾被截断、对端收到乱码的问题。另外长线 RS485 组网还要在总线两端各接一个 120Ω 终端电阻减少信号反射。面试问“为什么 RS485 要接 120Ω 电阻”时可以从传输线阻抗匹配和反射的角度答。3.3 Modbus RTU 报文格式Modbus 是应用层协议通常跑在 RS485 物理层上是工业设备通讯事实标准。面试重点有两个报文格式、CRC16 校验。Modbus RTU 报文结构是“地址码 功能码 数据区 CRC16 校验”。地址码表示从机地址功能码表示操作类型比如 03 读保持寄存器、06 写单个寄存器、16 写多个寄存器数据区存放寄存器地址和数量CRC16 校验低字节在前。下面是一个典型读保持寄存器请求帧从机地址: 0x01 功能码: 0x03 起始地址: 0x00 0x6B 寄存器数: 0x00 0x03 CRC16: 低字节在前Modbus 主从模型还有一个关键点同一时间只能有一个主机发起请求从机不能主动上报数据。所以 RS485 半双工与 Modbus 主从轮询是天然配合。面试要能解释为什么 Modbus 从机不做事件上报如果需求就是需要主动上报可以改用 CAN 或自定义应用层协议。CRC16 校验的软件实现是面试手撕代码高概率题下面这个标准查表法可以直接背下来uint16_t Modbus_CRC16(uint8_t *buf, uint16_t len) { uint16_t crc 0xFFFF; for (uint16_t i 0; i len; i) { crc ^ buf[i]; for (uint8_t j 0; j 8; j) { if (crc 0x0001) { crc (crc 1) ^ 0xA001; } else { crc 1; } } } return crc; }这里记住两点初始值是0xFFFF多项式反转是0xA001。发送时 CRC 低字节在前、高字节在后。4. I2C 与 SPI 面试要点4.1 I2C 时序与高频追问I2C 的面试考点集中在时序图和总线特性。总线空闲时SCL 和 SDA 都被上拉电阻拉高。主机要发起通信先让 SDA 从高变低此时 SCL 保持高这就是起始条件。停止条件相反SDA 在 SCL 为高时从低变高。数据位传输规则是SDA 在 SCL 高电平期间保持稳定在 SCL 低电平期间变化。每个字节传输完接收方要回一个 ACK主机释放 SDA 并检测第九个时钟周期上 SDA 是否被拉低。面试常见追问“为什么 I2C 需要上拉电阻”因为 I2C 引脚是开漏结构只能主动拉低不能主动输出高电平高电平靠外部上拉电阻实现。这也是 I2C 支持线与特性的原因多个设备挂在同一根线上任何一个设备拉低总线就是低电平。多主机仲裁是进阶考点。两个主机同时发起传输时时钟同步靠线与特性数据仲裁靠“发送高电平却读到低电平即退出”。如果面试官问“I2C 最多能挂多少个设备”不要背死数字答“受总线电容、地址空间和从机驱动能力限制标准模式通常推荐不超过几十个实际以数据手册为准”更稳妥。以下是一个模拟 I2C 起始条件的参考写法考的是对时序的理解void I2C_Start(void) { SDA_HIGH(); SCL_HIGH(); delay_us(5); SDA_LOW(); // SCL 高电平期间 SDA 拉低产生起始条件 delay_us(5); SCL_LOW(); }4.2 SPI 四种模式SPI 是同步全双工协议四根线SCLK、MOSI、MISO、CS。面试高频题是“SPI 的四种模式是什么”。四种模式由 CPOL时钟极性和 CPHA时钟相位组合决定。CPOL 决定空闲时时钟电平CPHA 决定数据在时钟上升沿还是下降沿采样。面试时画时序图即可记住下面这个对应表模式CPOLCPHA空闲时钟采样沿Mode 000低电平上升沿Mode 101低电平下降沿Mode 210高电平下降沿Mode 311高电平上升沿工程上最多见的是 Mode 0但具体以从机数据手册为准主从配置不一致时通信会错位读回来全是 0xFF 或 0x00。SPI 没有 ACK 机制主机无法直接知道从机是否收到所以调试 SPI 时一般会配合从机状态寄存器或读回比较。SPI 和 I2C 的选型对比也是一个经典题SPI 速率高、全双工、一根线一个片选适合大吞吐I2C 两根线就能带多个设备适合小数据量、慢速外设。但 I2C 的速率上限比 SPI 低传输大量数据时效率不如 SPI。面试回答要落到“需求驱动选型”不能只说“SPI 快”。5. CAN 通讯协议与工程化考题CAN 是车载和工业控制里最重要的多主通讯协议近两年嵌入式岗位问 CAN 的频率明显上升。面试考点主要有三块报文格式、仲裁机制、过滤器配置。5.1 报文格式CAN 2.0B 有两种帧格式标准帧和扩展帧。标准帧标识符 11 位扩展帧标识符 29 位。数据帧结构包括帧起始、仲裁场、控制场、数据场、CRC 场、应答场、帧结束。数据场最多 8 字节。相比其他协议CAN 的 CRC、ACK 和位填充机制让链路层可靠性更高。面试常问“CAN 数据场为什么是 8 字节”这和历史设计有关也受实时性和总线利用率约束。实际工程中也可以使用 CAN FD数据场最长 64 字节但需要节点都支持。5.2 仲裁机制CAN 是多主总线多个节点同时发送时靠标识符仲裁。标识符数值越小优先级越高。仲裁发生在显性位逻辑 0和隐性位逻辑 1同时出现时显性位覆盖隐性位发送隐性位的节点检测到总线为显性就知道自己仲裁失败立即转为接收。需要强调一点CAN 仲裁不破坏数据失败节点自动退出胜利节点继续完整发送。这个机制是 CAN 高实时性的基础。如果面试题问“CAN 和 RS485 区别”可以从多主、仲裁、错误处理、传输距离几个维度答RS485 是一主多从、半双工、链路层没有仲裁CAN 是多主、带优先级仲裁、错误检测更完备。5.3 过滤器与收发代码思路CAN 控制器比如 STM32 的 bxCAN有硬件过滤器可以只让符合条件的报文进入接收 FIFO降低 MCU 中断频率。过滤模式有掩码模式和列表模式。掩码模式下掩码位为 1 表示必须匹配为 0 表示不关心。配置需要看具体芯片参考手册面试不要求背寄存器但要能说清楚“我通过 ID 掩码过滤只接收某几个方向的报文”。CAN 接收中断服务函数里要做的事很简单读 FIFO、保存报文、清标志位。不要在中断里做大数据处理或打印。下面是一个典型的接收处理框架void CAN_RX_IRQHandler(void) { uint8_t data[8]; uint32_t id; if (CAN_Receive(id, data) CAN_OK) { // 快速拷贝到接收队列由主循环或任务处理 Queue_Push(can_rx_queue, id, data); } }这个框架可以延伸出高频追问“接收队列满了怎么办”建议回答丢最旧数据、丢最新数据还是阻塞覆盖取决于业务优先级对控制类报文一般丢弃旧数据保新数据因为控制指令更看重时效性。这个回答能体现工程思维比“一直存到不丢为止”靠谱得多。6. 通讯协议栈接口设计与批量任务通路通讯协议往上层走就是接口设计问题。很多嵌入式项目不是简单读写寄存器而是要构建一套能支撑批量上报、多帧缓冲、失败重传的数据通路。面试中问“你怎么设计一个通讯模块”本质就是在考察这个。6.1 分层设计推荐按三层拆物理层驱动、数据链路层/协议层、应用层接口。物理层驱动直接操作 UART/SPI/CAN 外设寄存器向上提供 send/receive 原语。协议层负责组帧、解帧、校验、超时重传。应用层只关心业务数据比如一个温度值、一个开关状态。这样上层换协议时下层不需要动。6.2 环形缓冲区与批量接收串口接收数据时字节是逐个到达的但协议帧可能几十个字节。最常用的方案是“串口中断收字节 环形缓冲区存放 主循环解析帧”。环形缓冲区可以用数组加读指针和写指针实现#define RX_BUF_SIZE 256 typedef struct { uint8_t buf[RX_BUF_SIZE]; uint16_t head; uint16_t tail; } ring_buffer_t; int8_t RingBuf_Push(ring_buffer_t *rb, uint8_t data) { uint16_t next (rb-head 1) % RX_BUF_SIZE; if (next rb-tail) { return -1; // 缓冲区满 } rb-buf[rb-head] data; rb-head next; return 0; } int8_t RingBuf_Pop(ring_buffer_t *rb, uint8_t *data) { if (rb-head rb-tail) { return -1; // 缓冲区空 } *data rb-buf[rb-tail]; rb-tail (rb-tail 1) % RX_BUF_SIZE; return 0; }有了环形缓冲区批量数据就不会在中断里乱序丢失。中断只负责 push应用层在空闲时间 pop 并解析协议帧这个模式在 Modbus、自定义私有协议里都适用。6.3 批量任务与超时重传批量任务指一次需要下发或读取多帧数据。设计时要考虑批量队列、逐帧超时、失败重试次数上限。一个简单的流程是上层把 N 帧任务放入发送队列协议层逐帧发送每帧发送后进入等待应答状态启动超时定时器超时未收到应答则重传重传超过 3 次就标记该帧失败并继续下一帧。面试官很看重你是否有这种失败的隔离意识而不是一帧失败整批终止。如果使用 RTOS可以用消息队列和信号量替代裸机标志位。发送任务阻塞在应答信号量上接收中断解析到正确应答后释放信号量这样天然实现了等待和超时管理。7. 资源占用与性能观察通讯协议的代码看似简单性能和可靠性隐藏在细节里。CPU 占用。轮询接收比中断接收更占 CPU高速率下容易丢数据。批量收发大帧时尽量用 DMA 或 FIFO。UART 的 DMA 空闲中断是接收不定长帧的常见方案但 DMA 不会主动告诉 CPU“数据收完了”要靠串口空闲中断配合判断一帧结束。SPI 发数据时如果频繁在中断里等待会影响其他实时任务。缓冲区大小。环形缓冲区大小不是随便定的。要按“最大帧长 × 可能积压的帧数”算并留出余量。缓冲区太小会丢帧太大浪费 RAM尤其 MCU 内部 RAM 只有几十 KB 时。波特率和时序。波特率越高单位时间数据量越大对缓冲区、中断响应速度要求越高。I2C 速率和上拉电阻有关上拉电阻太大上升沿变缓通信速率受限上拉电阻太小灌电流过大可能损坏引脚。CAN 总线长度和波特率相关常用组合是 500kbps 时总线长度控制在几十米以内低速时才能更长实际以收发器数据手册为准。多任务下的优先级。在 FreeRTOS 中通讯接收任务一般要高于普通计算任务但低于硬实时控制任务。中断里不能做耗时操作比如不建议在串口中断里直接解析 JSON 或 Modbus 组包只做存字节和置标志。8. 通讯协议调试与面试排查清单通讯协议出问题排查思路比背出一条命令重要。下面这个表格是实际开发和面试中都可以用的排查路径。问题现象可能原因排查方式解决方案收到数据全是 0xFF引脚配置错误、共地问题、波特率不匹配示波器看波形检查 TX/RX 是否交叉修正 GPIO 配置确认收发双方波特率一致偶尔乱码波特率误差偏大、干扰、电源纹波逻辑分析仪抓帧降低波特率测试调整分频系数使用外部晶振增加滤波电容RS485 数据末尾被截断发送完成前过早切换方向确认发送完成标志后再拉低 DE等待 TX 移位寄存器清空I2C 总线卡死SDA 被从机拉低未产生停止条件示波器看 SCL/SDA 电平主机产生 9 个时钟脉冲复位从机SPI 读回全 0模式不匹配、片选时序不对对比从机数据手册时序图调整 CPOL/CPHA检查 CS 拉低时点CAN 接收不到报文过滤器配置错误、波特率不一致检查错误计数器对比位时序参数重新配置过滤器校准波特率Modbus 返回超时从机地址或 CRC 错误、从机未及时上电抓串口报文逐字节比对核对地址、CRC延长超时时间批量任务中途卡住重试机制缺失、缓冲区满增加日志输出帧序号加入逐帧超时和失败重试面试时被问到“你调试通讯协议遇到最棘手的问题是什么”不要只回答现象要按“现象→猜测→用工具验证→定位根因→解决→预防”这个顺序讲。哪怕问题本身不复杂这个回答结构也能体现工程方法。9. 高频八股题目参考答案速记下面按“题目→一句话答案→可扩展点”收拢一批今年常被问到的通讯协议八股UART、I2C、SPI 有什么区别一句话版本UART 异步走波特率约定I2C 同步两线多设备靠地址SPI 同步四线全双工速率高。扩展点线数、速率、多设备支持、是否全双工。为什么 RS485 比 RS232 传输距离远RS485 用差分信号共模抑制能力强抗干扰好且适合挂多节点。RS232 是单端信号参考地容易受干扰。扩展点A/B 电平定义、终端电阻。Modbus RTU 一帧怎么组成地址码 功能码 数据 CRC16。CRC 初始值 0xFFFF多项式 0xA001低字节在前。CAN 怎么保证高优先级报文先发标识符越小优先级越高显性位覆盖隐性位低优先级节点仲裁失败自动退出发送。扩展点仲裁不破坏数据、位填充的同步作用。I2C 起始条件、停止条件是什么SCL 高电平期间 SDA 从高到低是起始SCL 高电平期间 SDA 从低到高是停止。扩展点重复起始条件、多主机仲裁。SPI 从机为什么要片选信号SPI 没有地址概念所有从机共享 MOSI/MISO 和时钟线主机通过片选信号选中一个从机避免多个从机同时驱动 MISO 造成总线冲突。CAN 数据帧最多少字节标准 CAN 2.0 的数据场是 0 到 8 字节CAN FD 最多 64 字节。扩展点CAN FD 需要节点都支持。嵌入式项目里通讯协议怎么保证可靠性分层校验 应答 超时重传。链路层有 CRC/奇偶校验应用层定义 ACK/NAK发送端设置超时和重试次数同时用状态机解析帧避免粘包。10. 总结与下一步通讯协议这块八股背表格只是第一步真正要练的是三件事。第一自己动手把 UART、I2C、SPI 的初始化、收发和调试跑通手里有逻辑分析仪更好能看到时序和代码对不上时哪里出了问题。第二把 Modbus CRC16、环形缓冲区这两段代码写到能默写的程度面试手撕代码时很占便宜。第三准备一个自己调通 RS485/CAN 的真实案例能把现象、排查过程、最终根因讲完整这在面试时比背二十条概念都有说服力。下一步建议按顺序做先把 UARTRS485Modbus 这个链路一套跑通因为这类面试题出现频率最高再补 I2C 和 SPI 的时序对比把四种模式的实际波形看一遍最后在需要多主上报的板子上体验一把 CAN 过滤器和仲裁的效果。通讯协议没有太多玄学把物理层、数据链路层、应用层的分工理清楚面试和项目都不会慌。建议收藏备用后面连载补上嵌入式八股第 4 篇。