一文搞懂 串口、MODBUS、UART、USART、RS232、RS485、SPI、IIC、CAN 总线的区别

一、通信协议基础概念:从底层到应用层的 “金字塔” 结构

在嵌入式开发中,通信协议可分为 底层硬件协议(解决 “如何传数据”)和 应用层协议(解决 “传什么数据”)。本文将深入解析 6 大底层协议(RS232、RS485、SPI、IIC、CAN),并补充 UART/USART/ 串口 的深度对比,最后介绍工业常用的 MODBUS 协议。

1. 通信基础概念详解

(1)双工模式
  • 单工通信:数据仅能单向传输(如遥控器与电视)。
  • 半双工通信:数据可双向传输但不能同时进行(如对讲机)。
  • 全双工通信:数据可同时双向传输(如电话)。
(2)并行与串行传输
特性并行通信串行通信
数据传输多根线同时传输多个数据位单根线逐位传输数据位
速度快(如并口打印机)慢(如 USB)
距离短(米级)长(千米级)
成本高(多根线)低(单根线)
典型应用早期计算机总线(如 ISA)现代接口(如 SPI、IIC、UART)
(3)同步与异步通信
  • 同步通信
    • 需时钟线(如 SPI 的 SCLK),数据传输与时钟同步。
    • 特点:高速(如 SPI 可达 50Mbps+),但硬件复杂。
  • 异步通信
    • 无时钟线,依赖起始位(0)和停止位(1)同步(如 UART)。
    • 特点:硬件简单,适合低速场景。

二、六大底层硬件协议深度对比

1. UART/USART/ 串口:最熟悉的 “异步通信三兄弟”

1.1 基础概念与核心定义
  • 串口(Serial Port)

    • 定义:广义指所有串行通信接口,狭义通常特指基于 UART 硬件的异步串口(如常见的 TTL 电平串口)。
    • 核心作用:实现设备间逐位传输数据,仅需 1~2 根信号线(TX 发送、RX 接收),成本低、易部署,是嵌入式开发中最基础的通信方式。
  • UART(Universal Asynchronous Receiver-Transmitter)

    • 定义:通用异步收发器,硬件电路,实现异步串行通信,不支持同步时钟信号。
    • 核心特性:异步通信,收发双方各自独立时钟,靠约定波特率同步;数据以 “帧” 为单位传输,每帧包含起始位、数据位、校验位、停止位。
  • USART(Universal Synchronous Asynchronous Receiver-Transmitter)

    • 定义:通用同步异步收发器,UART 的 “增强版”,支持异步和同步两种模式。
    • 核心特性:异步模式兼容 UART;同步模式可外接时钟线(SCLK),用于高速同步通信(如配合 SPI 协议),功能更灵活。
1.2 硬件接口与物理层
特性UART / 串口(TTL 电平)USART(同步模式)
信号线TX(发送)、RX(接收)、GND(共地)TX、RX、SCLK(同步时钟)、GND
电平标准3.3V/5V TTL(高电平 1,低电平 0)可兼容 TTL/CMOS,同步模式需额外时钟线
典型接线TX 接对方 RX,RX 接对方 TX,共地异步时同 UART;同步时需 SCLK 同步时钟
1.3 工作原理:从 “帧格式” 到数据收发
1.3.1 异步通信(UART/USART 异步模式)
  • 帧格式(以 8 位数据位为例)

    起始位(1位,0) → 数据位(5~8位,低位先传) → 校验位(0/1位,奇/偶/无) → 停止位(1/1.5/2位,1)  
    
     
    • 示例:发送字符‘A’(ASCII 码 0x41,二进制 01000001),无校验位,1 位停止位:
      帧结构为 0(起始) + 1000001(数据,低位先传即1000001→1000001,注意低位是第一位) + 1(停止)
  • 波特率:每秒传输的比特数(如 9600bps 表示每秒传 9600 位),收发双方必须一致(误差 < 5%),常见值:9600、19200、115200。

  • 工作流程(以 STM32 单片机为例)

    1. 初始化配置(代码示例,注释解释):

      // 步骤1:开启GPIO和USART时钟(假设使用USART1,对应PA9/TX、PA10/RX)
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
      
      // 步骤2:配置TX引脚为复用推挽输出,RX为浮空输入
      GPIO_InitTypeDef GPIO_InitStruct;
      GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;       // TX引脚PA9
      GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出(发送数据)
      GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_Init(GPIOA, &GPIO_InitStruct);
      
      GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;      // RX引脚PA10
      GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入(接收数据)
      GPIO_Init(GPIOA, &GPIO_InitStruct);
      
      // 步骤3:配置USART参数(异步模式)
      USART_InitTypeDef USART_InitStruct;
      USART_InitStruct.USART_BaudRate = 115200;    // 波特率
      USART_InitStruct.USART_WordLength = USART_WordLength_8b; // 8位数据位
      USART_InitStruct.USART_StopBits = USART_StopBits_1;     // 1位停止位
      USART_InitStruct.USART_Parity = USART_Parity_No;        // 无校验位
      USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件流控
      USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; // 收发模式
      USART_Init(USART1, &USART_InitStruct);
      
      // 步骤4:使能USART
      USART_Cmd(USART1, ENABLE);
      
       

      参数解析

      • USART_BaudRate:波特率,决定数据传输速度。
      • USART_WordLength:数据位长度(5~8 位),需与对方一致。
      • USART_StopBits:停止位长度,长停止位可容忍时钟误差(如多机通信)。
      • USART_Parity:校验位(奇 / 偶 / 无),简单错误检测,非必须。
    2. 数据发送

      • 单字符发送:调用 USART_SendData(USART1, data),等待发送完成标志 USART_FLAG_TC(避免覆盖发送)。
        while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); // 等待上一帧发送完成
        USART_SendData(USART1, 'A'); // 发送字符'A'
        
      • 字符串发送:循环调用单字符发送函数,逐字节发送。
    3. 数据接收

      • 轮询方式:检测接收缓冲区非空标志 USART_FLAG_RXNE,读取数据寄存器 USART_ReceiveData(USART1)
      • 中断方式(推荐,适合实时性场景):使能接收中断 USART_IT_RXNE,在中断服务函数中读取数据。
        // 使能接收中断
        USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
        // 中断服务函数
        void USART1_IRQHandler(void) {
            if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
                uint8_t data = USART_ReceiveData(USART1); // 读取接收到的数据
                // 处理数据(如存入缓冲区)
            }
        }
        
1.3.2 同步通信(USART 同步模式)
  • 核心区别:外接同步时钟线(SCLK),收发双方共享同一时钟,数据在时钟边沿同步传输,适合高速、长距离(需时钟稳定)。
  • 应用场景:极少单独使用,常作为 SPI 协议的底层驱动(SPI 本质是同步串行协议,USART 同步模式可配置为 SPI 兼容模式)。
1.4 典型应用场景
  • UART / 串口(TTL 电平)

    • 嵌入式设备与电脑通信(通过 CH340/PL2303 模块转换为 USB),如单片机调试(打印日志、接收指令)。
    • 低速外设连接,如蓝牙模块(HC-05)、WiFi 模块(ESP8266)、传感器(部分串口传感器)。
    • 示例:Arduino 通过 SoftwareSerial 库模拟 UART,与蓝牙模块通信。
  • USART

    • 需同步的高速场景(如配合 SPI),但更常见是作为增强型 UART 使用(异步模式为主)。
    • 多协议兼容:STM32 等单片机的 USART 外设可配置为 UART、LIN、IrDA 等多种模式。
1.5 优缺点对比
优点缺点
硬件简单(仅需 TX/RX/GND)异步模式依赖波特率严格一致(误差敏感)
成本低,适合短距离(TTL 电平≤10m)无硬件流控时易丢包(需软件握手)
协议简单,上手快(帧格式固定)同步模式需额外时钟线,应用较少
支持全双工(TX/RX 独立,可同时收发)数据无应答机制,可靠性依赖上层协议
1.6 与其他协议的核心差异
  • 对比 RS232/RS485:UART/USART 是逻辑层协议(定义数据格式),RS232/RS485 是物理层标准(定义电平、接口、传输距离)。
    • UART(TTL 电平)可通过 MAX232 芯片转换为 RS232 电平(±12V),延长传输距离至 15m。
    • 通过 MAX485 芯片转换为 RS485 差分信号,支持多节点、长距离(1200m)、抗干扰。
  • 对比 SPI/IIC:UART 是异步、全双工;SPI 是同步、全双工(需时钟和片选线);IIC 是同步、半双工(单总线双向)。
  • 对比 CAN:UART 无纠错和仲裁机制,适合简单场景;CAN 是高可靠、多主总线,用于汽车电子等严苛环境。
1.7 避坑指南
  1. 波特率不一致:必现乱码,调试时先用串口助手固定波特率(如 115200),逐步排查。
  2. 电平不匹配:3.3V 设备与 5V 设备直连可能烧芯片,需用电平转换模块(如电阻分压)。
  3. 停止位 / 校验位错误:收发双方帧格式必须完全一致(数据位、停止位、校验位),否则接收错位。
  4. 中断处理不当:接收中断需及时读取数据,避免缓冲区溢出(可配合 DMA 或环形缓冲区)。
1.8 拓展:UART 的 “进阶玩法”
  • 硬件流控(RTS/CTS):通过请求发送(RTS)和清除发送(CTS)信号控制数据流,避免缓冲区溢出,适合高速通信。
  • 多机通信:利用 UART 的空闲总线检测(IDLE 中断),实现主从模式(如 1 个主机带多个从机,通过地址帧寻址)。
  • 与 MODBUS 结合:MODBUS-RTU 协议基于 UART,定义了数据帧格式和功能码(如 0x03 读取寄存器),是工业控制中最常用的串口应用层协议。
总结:一句话分清三兄弟
  • 串口:泛指标称,底层通常是 UART 硬件。
  • UART:纯异步,靠波特率同步,最简串行通信方案。
  • USART:异步为主,支持同步,功能更全(如可配置为 SPI 兼容模式),单片机中最常用。

理解 UART/USART 是掌握其他串行协议的基础,后续将继续对比 RS232/RS485、SPI/IIC/CAN 等协议,帮你构建完整的嵌入式通信知识体系。

2. RS232:PC 串口的 “老大哥”(异步、单端)

2.1 物理层核心特性:电平、距离与信号特性
  • 独特的负逻辑电平体系
    RS232 采用 负逻辑电平标准,与微控制器的 TTL/CMOS 电平完全不同:

    • 逻辑 1:电压范围 -3V ~ -15V(高电平代表逻辑 1,反常识设计)
    • 逻辑 0:电压范围 +3V ~ +15V(低电平代表逻辑 0)
    • 未定义区-3V ~ +3V(信号处于此区间时,接收端可能无法正确解析)

    为什么这样设计? 早期为兼容电话线路的差分传输,通过负逻辑减少噪声干扰,但与现代数字电路(0-3.3V/5V 电平)完全不兼容,必须依赖 MAX232/SP3232 等电平转换芯片

  • 短距离通信的局限性

    • 传输距离:最大有效距离 15 米,实际应用中通常控制在 5 米内以保证稳定性。
    • 信号缺陷:采用 单端信号传输(仅一根信号线,以公共地为参考),易受电磁干扰(EMI)和地电位差影响。距离越长,信号衰减越明显(如 10 米外信号幅值可能下降 30%)。
2.2 硬件连接:PC 与嵌入式设备的 “翻译官”
  • 三要素:电平转换 + 共地 + 引脚对应
    PC(DB9 接口)       MAX232 芯片       单片机  
    ┌───────────────┐   ┌───────────────┐   ┌───────────────┐  
    │ TX(2 脚,-3~-15V)→ R1IN(13 脚)   │   → TXD(TTL 3.3V)│  
    │ RX(3 脚,-3~-15V)← T1OUT(14 脚)  │   ← RXD(TTL 3.3V)│  
    │ GND(5 脚,地)   ── GND(6 脚)─────┼── GND(地)       │  
    └───────────────┘   └───────────────┘   └───────────────┘  
    
  • 关键步骤解析
    1. 电平转换芯片选型
      • 5V 系统选 MAX232(内部电荷泵将 5V 转为 ±12V 满足 RS232 电平)。
      • 3.3V 系统选 MAX3232(3.3V 供电,兼容 3.3V TTL 电平)。
    2. 引脚功能对应
      • PC 的 TX(发送)接 MAX232 的 R1IN(接收输入):PC 发送 RS232 电平,经芯片转为 TTL 电平给单片机。
      • PC 的 RX(接收)接 MAX232 的 T1OUT(发送输出):单片机发送 TTL 电平,经芯片转为 RS232 电平给 PC。
    3. 共地连接:必须将 PC、MAX232、单片机的 GND 短接,否则地电位差会导致信号解析错误(如地电位差 2V 会被误判为逻辑 0)。
2.3 典型应用场景:嵌入式调试黄金搭档
  • 场景 1:单片机串口调试

    • 操作流程
      1. 单片机 TXD 接 MAX232 的 DI,RXD 接 RO。
      2. MAX232 连接 PC 的 USB 转 RS232 接口(如 CH340 芯片)。
      3. 串口助手设置波特率 115200、8 数据位、1 停止位、无校验。
    • 代码示例(STM32 发送数据到 PC)
      // 初始化 USART1 为 TTL 电平模式(默认已为 TTL,无需额外转换)  
      USART_InitTypeDef USART_InitStruct;  
      USART_InitStruct.USART_BaudRate = 115200;  
      USART_InitStruct.USART_WordLength = USART_WordLength_8b;  
      USART_Init(USART1, &USART_InitStruct);  
      USART_Cmd(USART1, ENABLE);  
      
      // 发送字符串  
      void USART_SendString(char *str) {  
          while (*str) {  
              USART_SendData(USART1, *str++);  
              while (!USART_GetFlagStatus(USART1, USART_FLAG_TXE)); // 等待发送缓冲区空  
          }  
      }  
      // 调用:USART_SendString("Hello, RS232!\r\n");  
      
  • 场景 2:工业设备点对点通信

    • 应用案例:老式数控机床通过 RS232 连接上位机,传输加工程序(波特率 9600,需手动配置两端参数一致)。
2.4 避坑指南:三大常见问题解决
问题现象可能原因解决方法
串口助手无数据显示电平转换芯片供电错误测量 MAX232 的 VCC 引脚是否为 5V/3.3V(对应系统电压)
数据乱码波特率 / 数据位 / 停止位不匹配检查串口助手与设备的参数设置(如是否为 8-N-1 格式)
芯片发热烧毁引脚接反(如 TX 接 RX)对照电路图,确保 PC TX 接芯片 R1IN,PC RX 接芯片 T1OUT

3. RS485:工业级 “多节点” 串口(异步、差分)

3.1 物理层升级:差分信号如何实现抗干扰?
  • 差分传输核心原理

    • 信号定义:通过两根信号线(A 线、B 线)的电压差表示逻辑值:
      • 逻辑 1:A - B ≥ +200mV(A 线电压高于 B 线)
      • 逻辑 0:A - B ≤ -200mV(B 线电压高于 A 线)
    • 抗干扰优势:共模干扰(如地噪声、电磁辐射)同时影响 A、B 线,差分接收器仅检测压差。
    • 长距离传输能力

      • 波特率与距离关系
        波特率1200bps9600bps115200bps10Mbps
        最大距离1200 米1200 米400 米15 米
      • 工业场景首选:煤矿、工厂等恶劣环境,距离 500 米以上时优先选择 RS485。
    • 3.2 多节点组网:从硬件到协议的完整方案
    • 硬件组网三要素

      1. 半双工控制(DE/RE 引脚)
        • DE(Driver Enable):高电平使能发送器(A/B 线输出差分信号),低电平进入高阻态。
        • RE(Receiver Enable):低电平使能接收器(读取 A/B 线信号),高电平禁止接收(部分芯片与 DE 共用引脚)。
      2. 终端电阻(120Ω 关键作用)
        • 作用:匹配传输线特性阻抗,消除信号反射(长距离传输时,未匹配的信号会在末端反射,导致数据错误)。
        • 安装位置:总线两端各接一个 120Ω 电阻(并联在 A、B 线之间),短距离(<300 米)可省略。
      3. 线缆选择:使用 屏蔽双绞线(减少电磁干扰),A 线用红色,B 线用黑色(行业通用配色)。
    • 典型网络拓扑

      主机(PLC)────A/B────从机 1────A/B────从机 2────A/B────终端电阻(120Ω)  
                         ↓  
                     从机 3(T 型连接,支线 <10米)  
      
    • 3.3 代码实现:STM32 控制 RS485 收发器(带收发切换)
      #include "stm32f10x.h"  
      
      // 定义 RS485 控制引脚(DE 高电平发送,低电平接收)  
      #define RS485_DE_GPIO_PORT GPIOA  
      #define RS485_DE_PIN GPIO_Pin_11  
      
      // 初始化 RS485 接口  
      void RS485_Init(void) {  
        // 使能 GPIO 和 USART 时钟  
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);  
      
        // 配置 TX 引脚(PA9)为复用推挽输出  
        GPIO_InitTypeDef GPIO_InitStruct;  
        GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;  
        GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;  // 复用推挽,由 USART 控制发送  
        GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;  
        GPIO_Init(GPIOA, &GPIO_InitStruct);  
      
        // 配置 RX 引脚(PA10)为浮空输入  
        GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;  
        GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;  // 浮空输入,高阻态接收  
        GPIO_Init(GPIOA, &GPIO_InitStruct);  
      
        // 配置 DE 控制引脚(PA11)为推挽输出  
        GPIO_InitStruct.GPIO_Pin = RS485_DE_PIN;  
        GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;  // 推挽输出,控制收发模式  
        GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;  
        GPIO_Init(GPIOA, &GPIO_InitStruct);  
        GPIO_ResetBits(RS485_DE_GPIO_PORT, RS485_DE_PIN);  // 初始化为接收模式  
      
        // 初始化 USART1(与从机参数一致)  
        USART_InitTypeDef USART_InitStruct;  
        USART_InitStruct.USART_BaudRate = 9600;          // 波特率  
        USART_InitStruct.USART_WordLength = USART_WordLength_8b;  // 8 位数据位  
        USART_InitStruct.USART_StopBits = USART_StopBits_1;       // 1 位停止位  
        USART_InitStruct.USART_Parity = USART_Parity_No;          // 无校验位  
        USART_Init(USART1, &USART_InitStruct);  
        USART_Cmd(USART1, ENABLE);  
      }  
      
      // 发送数据函数(自动切换收发模式)  
      void RS485_SendData(uint8_t *data, uint16_t len) {  
        GPIO_SetBits(RS485_DE_GPIO_PORT, RS485_DE_PIN);  // 置高 DE,进入发送模式  
        for (uint16_t i = 0; i < len; i++) {  
          USART_SendData(USART1, data[i]);  // 发送数据  
          while (!USART_GetFlagStatus(USART1, USART_FLAG_TXE));  // 等待发送缓冲区空  
        }  
        while (!USART_GetFlagStatus(USART1, USART_FLAG_TC));  // 等待发送完成(避免多帧重叠)  
        GPIO_ResetBits(RS485_DE_GPIO_PORT, RS485_DE_PIN);  // 置低 DE,恢复接收模式  
      }  
      
      // 接收数据(中断方式,需配置 NVIC)  
      void USART1_IRQHandler(void) {  
        if (USART_GetITStatus(USART1, USART_IT_RXNE)) {  // 接收缓冲区非空中断  
          uint8_t data = USART_ReceiveData(USART1);  // 读取数据  
          // 存入接收缓冲区(如环形缓冲区)或处理数据  
        }  
      }  
      
    • 代码核心逻辑
      • 收发切换:发送前置高 DE,使能发送器;发送完成后置低 DE,避免占用总线。
      • 状态标志检测USART_FLAG_TXE 表示发送缓冲区空,USART_FLAG_TC 表示整帧发送完成,确保数据不丢失。
    • 3.4 应用层协议:Modbus-RTU 与 RS485 的黄金组合
    • Modbus-RTU 帧格式(基于 RS485)

      [设备地址(1B)] [功能码(1B)] [数据起始地址(2B)] [数据长度(2B)] [数据( nB)] [CRC 校验(2B)]  
      
       
      • 设备地址:0x01~0x7F(唯一标识从机,0x00 为广播地址)。
      • 功能码:0x03 读寄存器,0x06 写单个寄存器(具体见 Modbus 协议文档)。
    • 通信流程(主机读取从机寄存器)

      1. 主机发送查询帧:01 03 00 00 00 01 CRC_H CRC_L(读取从机 01 的 0x0000 寄存器)。
      2. 从机响应:01 03 02 DATA1 DATA2 CRC_H CRC_L(返回 2 字节数据)。
    • 3.5 工程实践:三大核心问题解决方案
    • 节点冲突:同一时间多设备发送
      • 解决:采用 单主多从模式(主机发起,从机响应),从机发送前检测总线空闲(如通过监测 A/B 线压差)。
    • 信号反射:数据乱码或丢失
      • 解决:长距离(>300 米)必须安装终端电阻,使用示波器测量信号波形,调整电阻值(典型 120Ω,根据线缆特性阻抗调整)。
    • 地电位差:烧毁芯片或通信中断
      • 解决:添加 信号隔离模块(如光耦隔离 + DC-DC 隔离电源),切断地环路,避免共模电压超标(RS485 允许共模电压 -7V~+12V)。
    • 总结:RS232 vs RS485,选型决策表

      场景RS232RS485
      传输距离<15 米(短距离调试)1200 米(长距离工业场景)
      节点数2 个(点对点)32+ 个(单主多从)
      抗干扰能力弱(单端信号)强(差分信号)
      典型应用单片机与 PC 调试、老式设备连接工业仪表组网、传感器集群
      必备硬件MAX232 电平转换芯片RS485 收发器(如 SP485)、终端电阻
      软件要点无需收发切换,直接透传DE/RE 引脚控制收发模式
       

      通过理解 RS232 和 RS485 的物理层差异、硬件连接和应用场景,新手可快速掌握工业通信的基础,后续结合 Modbus 等协议可实现更复杂的设备互联。记住:RS232 是入门调试的桥梁,RS485 是工业组网的核心,两者相辅相成,共同构成嵌入式通信的基石。

4. SPI:高速外设的 “专属通道”(同步、全双工)

4.1 物理层:四根线构建高速通道
  • 核心信号线(必知必会)

    信号名全称方向功能描述
    SCLK同步时钟线主→从由主机生成时钟信号,控制数据传输节奏(频率可达 50Mbps+)。
    MOSI主出从入主→从主机发送数据,从机接收(Master Out, Slave In)。
    MISO主入从出从→主从机发送数据,主机接收(Master In, Slave Out)。
    CS(NSS)片选线主→从低电平有效,主机通过拉低对应从机的 CS 线选中目标设备(每个从机独立 CS)。
     

    硬件连接示例(单主机双从机)

    plaintext

    主机(STM32)       从机1(W25Q64)       从机2(SD卡)  
    SCLK ────────────── SCLK                 SCLK  
    MOSI ────────────── MOSI                 MOSI  
    MISO ────────────── MISO                 MISO  
    CS1  ────────────── CS                   —  
    CS2  ────────────── —                    CS  
    
     
    • 关键:每个从机必须有独立的 CS 线,主机通过切换 CS 电平选择通信对象。
  • 传输特性

    • 全双工:主机和从机可同时发送 / 接收数据(MOSI 和 MISO 独立传输)。
    • 高速率:波特率可达系统时钟的 1/2(如 STM32 时钟 72MHz 时,SPI 速率可达 36MHz),适合传输大量数据(如图像、音频)。
4.2 通信模式:时钟极性与相位的组合魔法

4.3.2 数据收发:同步交换机制

4.4 典型应用场景

4.5 优缺点与适用场景
优点缺点最佳场景
高速率(50Mbps+)无硬件应答机制(依赖软件确认)高速数据传输(存储、图像)
全双工同步通信多从机需独立 CS 线(引脚资源紧张)点对点或少量从机组网
协议简单(无复杂握手)无总线仲裁(多主机易冲突)主从架构明确的系统(如单片机 + 外设)
4.6 新手避坑与拓展

4.7 与其他协议对比(核心差异)
特性SPII2CUART
同步 / 异步同步(需时钟线)同步(SCL 时钟)异步(波特率同步)
双工模式全双工半双工全双工(TX/RX 独立)
从机选择独立 CS 线7/10 位地址(SDA)无(点对点)
典型速率10Mbps~50Mbps400kHz~3.4MHz115200bps~1Mbps
总线仲裁无(依赖主机控制 CS)有(SDA 竞争检测)

总结:SPI 为何是高速外设首选?

掌握 SPI 的核心在于理解时钟时序(CPOL/CPHA)和片选逻辑,从简单的 Flash 读写开始,逐步拓展到多设备组网,是进入高速串行通信的关键一步。

编辑

分享

SPI总线的优缺点有哪些?

SPI与IIC总线的区别是什么?

详细介绍一下SPI总线的拓展知识

  • 四大模式(由 CPOL 和 CPHA 定义)

    模式CPOL(空闲电平)CPHA(采样边沿)数据采样时机典型应用(示例)
    00(低电平)0(第一个边沿)时钟上升沿W25Q64(默认模式)
    10(低电平)1(第二个边沿)时钟下降沿部分传感器(需匹配从机时序)
    21(高电平)0(第一个边沿)时钟下降沿少数高速外设
    31(高电平)1(第二个边沿)时钟上升沿兼容模式 0 的从设备
     

    如何选择? 查看从设备数据手册(如 W25Q64 要求 CPOL=0/1 且 CPHA=0/1,故模式 0 或 3 可用)。

  • 时序图解析(以模式 0 为例)

  • 空闲时 SCLK=0,数据在第一个上升沿(时钟从 0→1)时被从机采样,主机在下降沿(1→0)时更新数据。
  • 4.3 代码实现:STM32 驱动 SPI 外设(以 W25Q64 为例)

  • 4.3.1 初始化步骤(分三步走)
  • GPIO 配置(设置复用功能)

    void SPI_GPIO_Init(void) {  
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能GPIOA时钟  
        GPIO_InitTypeDef GPIO_InitStruct;  
        // SCK(PA5)和MOSI(PA7)设为复用推挽输出  
        GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;  
        GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;  // 复用推挽,由SPI控制  
        GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;  
        GPIO_Init(GPIOA, &GPIO_InitStruct);  
        // MISO(PA6)设为浮空输入  
        GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;  
        GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;  // 高阻态接收从机数据  
        GPIO_Init(GPIOA, &GPIO_InitStruct);  
        // 片选线(如PC7)设为普通输出(软件控制)  
        GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;  
        GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT_PP;  
        GPIO_Init(GPIOC, &GPIO_InitStruct);  
        GPIO_SetBits(GPIOC, GPIO_Pin_7);  // 初始拉高,不选中从机  
    }  
    
     

    参数解释

    • GPIO_Mode_AF_PP:复用推挽,允许 SPI 外设控制引脚输出时钟和数据。
    • GPIO_Mode_IN_FLOATING:浮空输入,避免固定电平干扰从机数据读取。
  • SPI 外设配置(核心参数)

    void SPI_Init_Config(void) {  
        SPI_InitTypeDef SPI_InitStruct;  
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); // 使能SPI1时钟  
        SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  // 全双工模式  
        SPI_InitStruct.SPI_Mode = SPI_Mode_Master;  // 主机模式  
        SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;  // 8位数据帧  
        SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;  // 时钟空闲低电平(模式0/1)  
        SPI_InitStruct.SPI_CPHA = SPI_PHASE_1EDGE;  // 第一个边沿采样(模式0/2)  
        SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;  // 软件片选(通过GPIO控制CS)  
        SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;  // 波特率=系统时钟/4(如72MHz→18MHz)  
        SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;  // 高位先行(如0x55先传最高位1)  
        SPI_Init(SPI1, &SPI_InitStruct);  
        SPI_Cmd(SPI1, ENABLE);  // 使能SPI外设  
    }  
    
     

    关键参数

    • SPI_NSS_Soft:不使用硬件片选,通过软件控制 GPIO 作为 CS 线(灵活适配多从机)。
    • SPI_BaudRatePrescaler:分频系数越大,波特率越低(高速设备选小系数,如_2;低速设备选大系数,如_256)。
  • 片选控制(软件管理从机)

    #define W25Q64_CS_GPIO GPIOC  
    #define W25Q64_CS_PIN GPIO_Pin_7  
    #define SELECT_SLAVE() GPIO_ResetBits(W25Q64_CS_GPIO, W25Q64_CS_PIN)  // 拉低CS选中从机  
    #define DESELECT_SLAVE() GPIO_SetBits(W25Q64_CS_GPIO, W25Q64_CS_PIN)  // 拉高CS取消选中  
    
  • 单字节读写(核心函数)

    uint8_t SPI_TransmitReceive(uint8_t data) {  
        while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);  // 等待发送缓冲区空  
        SPI_I2S_SendData(SPI1, data);  // 发送数据到缓冲区  
        while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);  // 等待接收缓冲区非空  
        return SPI_I2S_ReceiveData(SPI1);  // 返回从机发送的数据  
    }  
    
     

    原理:SPI 是 “数据交换协议”,主机发送数据时,从机同时返回数据(即使从机无需响应,也需发送 dummy 数据以维持时钟)。

  • 页编程示例(向 W25Q64 写入 256 字节)

    void W25Q64_PageProgram(uint32_t addr, uint8_t *data, uint8_t len) {  
        SELECT_SLAVE();  // 选中从机  
        SPI_TransmitReceive(W25Q64_PAGE_PROGRAM);  // 发送页编程指令(0x02)  
        SPI_TransmitReceive((addr >> 16) & 0xFF);  // 发送24位地址高8位  
        SPI_TransmitReceive((addr >> 8) & 0xFF);   // 中8位  
        SPI_TransmitReceive(addr & 0xFF);           // 低8位  
        for (int i=0; i<len; i++) {  
            SPI_TransmitReceive(data[i]);  // 发送数据字节  
        }  
        DESELECT_SLAVE();  // 取消选中  
        W25Q64_WaitBusy();  // 等待写入完成(检测状态寄存器)  
    }  
    
  • 高速存储设备
    • FLASH 芯片(如 W25Q64):通过 SPI 读取 / 写入固件,利用高速率减少等待时间。
    • SD 卡:使用 SPI 模式(非 SDIO 模式)初始化,适合低速单片机控制。
  • 传感器通信
    • 加速度计 / 陀螺仪(如 ADXL345):实时传输姿态数据,全双工模式确保双向配置与数据读取。
  • 显示驱动
    • OLED/LCD 控制器(如 SSD1306):通过 SPI 发送像素数据,高位先行格式适配屏幕缓存。
  • 常见问题解决

    1. 数据错位:检查 CPOL/CPHA 是否与从机匹配(用逻辑分析仪抓取时钟与数据波形)。
    2. 片选未释放:确保每次通信后拉高 CS,避免多从机同时响应导致总线冲突。
    3. 波特率过高:高速通信时需匹配从机支持的最大速率(查看数据手册,如 W25Q64 最大 50MHz)。
  • 拓展知识:SPI 变种

    • Dual SPI:使用 MOSI 和 MISO 同时传输 2 位数据,速率翻倍(如 QSPI Flash)。
    • Quad SPI:支持 4 位数据传输(IO0~IO3),进一步提升带宽(适用于大容量存储)。
    • 半双工模式:关闭 MISO 或 MOSI,用于单工通信(如只发送不接收的传感器)。
  • 速度为王:同步时钟驱动,支持超高波特率,适合 Flash、SD 卡等海量数据传输。
  • 简单直接:无需复杂协议,四根线实现全双工,主机完全掌控通信节奏。
  • 灵活适配:通过 CS 线轻松扩展多从机,软件配置四种模式兼容不同外设。

5. IIC(I²C):低速外设的 “双线极简方案”(同步、开漏)

5.1 物理层:两根线撑起极简通信
  • 核心信号线(开漏结构是关键)

    信号名全称方向特性描述
    SCL同步时钟线双向主机生成时钟,从机同步数据;开漏输出,需外接上拉电阻(3.3V/5V,阻值 4.7kΩ~10kΩ)。
    SDA数据线双向传输数据,开漏输出,支持 “线与” 特性(多主机冲突时,低电平优先)。
     

    硬件连接(以 SHT30 为例)

    STM32 (软件模拟IIC)       SHT30  
    PB6(SCL) ───────────── SCL(上拉至3.3V)  
    PB7(SDA) ───────────── SDA(上拉至3.3V)  
    GND   ──────────────── VSS(共地)  
    
     
    • 上拉电阻作用:确保总线空闲时为高电平,开漏输出只能拉低,拉高需依赖上拉电阻。
  • 传输特性

    • 速度模式:标准模式 100kHz(低速外设,如 RTC)、快速模式 400kHz(传感器)、高速模式 3.4MHz(少用)。
    • 总线负载:总电容≤400pF,超过会导致信号上升沿变缓,需缩短线缆或减少从机数量。
5.2 通信时序:从 “起始” 到 “停止” 的完整流程
  • 四大核心信号(时序图是关键)
    1. 起始信号(Start)

      • 条件:SCL 为高电平时,SDA 由高→低跳变。
      • 作用:通知从机 “开始通信”,总线从空闲状态(SCL=SDA=1)转入忙状态。
      • 代码实现(软件模拟):
        void IIC_Start() {  
            SDA_OUT();  // 设置SDA为输出  
            SCL_H; SDA_H;  // 先拉高双线  
            delay_us(5);  
            SDA_L;  // SCL高电平时拉低SDA,产生起始信号  
            delay_us(5);  
            SCL_L;  // 拉低SCL,准备发送数据  
        }  
        
    2. 停止信号(Stop)

      • 条件:SCL 为高电平时,SDA 由低→高跳变。
      • 作用:通知从机 “通信结束”,总线返回空闲状态。
      • 代码实现:
        void IIC_Stop() {  
            SDA_OUT();  
            SCL_L; SDA_L;  // 先拉低双线  
            delay_us(5);  
            SCL_H;  // 拉高SCL  
            delay_us(5);  
            SDA_H;  // SCL高电平时拉高SDA,产生停止信号  
            delay_us(5);  
        }  
        
    3. 应答信号(ACK)

      • 定义:接收方在接收到 8 位数据后,第 9 个时钟周期拉低 SDA(ACK)或保持高电平(NACK)。
      • 主机接收时需释放 SDA(设置为输入),检测从机是否拉低 SDA:
        uint8_t IIC_WaitACK() {  
            SDA_IN();  // SDA设为输入  
            SCL_H; delay_us(1);  
            uint8_t ack = !SDA_READ;  // 读取SDA电平,0=ACK,1=NACK  
            SCL_L;  
            return ack;  
        }  
        
    4. 数据传输

      • 每个字节先发最高位(MSB),SDA 在 SCL 低电平期间变化,高电平期间保持稳定。
      • 发送字节示例:
        void IIC_SendByte(uint8_t data) {  
            SDA_OUT();  
            for (int i=7; i>=0; i--) {  
                SDA_WRITE = (data >> i) & 1;  // 从最高位开始发送  
                SCL_H; delay_us(1);  // 高电平期间数据有效  
                SCL_L; delay_us(1);  // 低电平期间允许数据变化  
            }  
        }  
        
5.3 数据帧格式:从机地址 + 读写控制 + 数据 + 应答
  • 完整传输流程(以 SHT30 读温度为例)

    1. 主机发送起始信号 → 2. 发送从机地址(7 位)+ 写位(0) → 3. 等待 ACK →
    2. 发送寄存器地址(如 0x2C) → 5. 等待 ACK → 6. 发送起始信号(重复起始) →
    3. 发送从机地址(7 位)+ 读位(1) → 8. 等待 ACK → 9. 接收 16 位温度数据 → 10. 发送 NACK → 11. 发送停止信号

    从机地址计算

    • SHT30 的 ADDR 引脚接地时,7 位地址为 0x44,写操作地址为0x44<<1 | 0=0x88,读操作地址为0x44<<1 | 1=0x89
  • 多主机仲裁

    • 当多个主机同时发送数据时,SDA 线会根据 “线与” 原则仲裁:若主机 1 发 1,主机 2 发 0,最终 SDA 为 0,主机 1 检测到冲突后自动退出发送,确保唯一主机控制总线。
5.4 典型应用:软件模拟 IIC 驱动 SHT30(避坑指南)
  • 步骤 1:GPIO 初始化(软件模拟,STM32 为例)

    void IIC_GPIO_Init() {  
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);  
        GPIO_InitTypeDef GPIO_InitStruct;  
        GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;  
        GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;  // 开漏输出需软件模拟  
        GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;  
        GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;  // 内部上拉(也可外接上拉电阻)  
        GPIO_Init(GPIOB, &GPIO_InitStruct);  
        SCL_H; SDA_H;  // 初始化为高电平  
    }  
    
  • 步骤 2:发送单字节并等待 ACK(核心函数)

    uint8_t IIC_WriteByte(uint8_t data) {  
        IIC_SendByte(data);  // 发送8位数据  
        return IIC_WaitACK();  // 返回应答状态(0=成功,1=失败)  
    }  
    
  • 步骤 3:读取温湿度数据(SHT30 为例)

    void SHT30_ReadData(float *temp, float *humidity) {  
        uint8_t data[6];  
        IIC_Start();  
        if (!IIC_WriteByte(0x88)) {  // 写地址0x88(0x44<<1+0)  
            IIC_WriteByte(0x2C);  // 发送高重复性测量命令0x2C  
            IIC_WriteByte(0x06);  
            IIC_Stop();  
            delay_ms(15);  // 等待转换完成  
            IIC_Start();  
            if (!IIC_WriteByte(0x89)) {  // 读地址0x89(0x44<<1+1)  
                data[0] = IIC_ReadByte(1);  // 读温度MSB,发送NACK  
                data[1] = IIC_ReadByte(1);  // 读温度LSB  
                data[2] = IIC_ReadByte(1);  // 读CRC(可忽略)  
                data[3] = IIC_ReadByte(0);  // 读湿度MSB,发送ACK  
                data[4] = IIC_ReadByte(0);  // 读湿度LSB  
                data[5] = IIC_ReadByte(1);  // 读CRC,发送NACK  
                IIC_Stop();  
                // 数据转换公式(参考SHT30手册)  
                *temp = ((((uint16_t)data[0] << 8) | data[1]) / 65535.0) * 175 - 45;  
                *humidity = ((((uint16_t)data[3] << 8) | data[4]) / 65535.0) * 100;  
            }  
        }  
    }  
    
5.5 优缺点与适用场景
优点缺点最佳场景
极简硬件(仅两根线)速度较慢(最高 3.4MHz,常用 400kHz)低速传感器(SHT30、MPU6050)、RTC、EEPROM
支持多主机仲裁总线负载敏感(电容≤400pF)少量外设组网(≤8 个从机)
地址自动分配(7 位地址)软件模拟时序复杂(需精准延时)对引脚资源敏感的场景(如单片机)
5.6 新手避坑与进阶技巧
  • 常见问题解决

    1. 应答失败(ACK=1)
      • 检查从机地址是否正确(是否左移一位 + 读写位)。
      • 确认从机供电正常,SDA/SCL 上拉电阻是否接入(硬件模拟时必须外接)。
    2. 数据错位
      • 确保软件模拟时延时精准(参考从机时序要求,如 SHT30 要求 SCL 高 / 低电平时间≥1μs)。
      • 使用逻辑分析仪抓取时序,对比从机数据手册。
  • 进阶技巧

    • 硬件 IIC vs 软件模拟
      • 硬件 IIC(如 STM32 的 I2C1)适合高速场景,但需注意兼容性问题(部分单片机硬件 IIC 有 BUG)。
      • 软件模拟灵活可靠,适合低速设备(如传感器),但需用__nop()或精准延时函数保证时序。
    • 多从机地址冲突
      • 确保每个从机地址唯一(通过 ADDR 引脚设置,如 SHT30 的 ADDR 接 VDD/VSS 选择 0x44/0x45)。
5.7 与其他协议对比(核心差异)
特性IICSPIUART
信号线2 根(SCL+SDA)4 根(SCK+MOSI+MISO+CS)2 根(TX+RX)
同步方式同步(SCL 时钟)同步(SCK 时钟)异步(波特率同步)
双工模式半双工(SDA 双向)全双工全双工
地址机制7/10 位地址(自动仲裁)独立 CS 线(主机控制)无(点对点)
典型应用低速传感器、RTC高速存储、SD 卡调试串口、模块通信

总结:IIC 为何是低速外设首选?

  • 极简设计:两根线实现多设备通信,节省引脚资源,适合传感器、存储等低速场景。
  • 总线仲裁:自动解决多主机冲突,无需额外硬件逻辑。
  • 灵活适配:软件模拟兼容所有单片机,硬件 IIC 提升速度,满足不同需求。

掌握 IIC 的关键在于理解开漏结构、时序细节和应答机制,从简单的传感器读取开始,逐步尝试多从机通信,是进入嵌入式设备互联的重要一步。

6. CAN:汽车级 “高可靠” 总线(异步、差分)

6.1 物理层:差分传输构建抗干扰基石
6.1.1 差分信号与总线结构

CAN 总线采用两根差分信号线 CAN_H 和 CAN_L,通过电压差传输信号,抗干扰能力极强,适合恶劣工业环境和汽车电子场景。

  • 显性电平(逻辑 0):CAN_H ≈ 3.5V,CAN_L ≈ 1.5V,压差 2V(总线被主动驱动)。
  • 隐性电平(逻辑 1):CAN_H ≈ CAN_L ≈ 2.5V,压差 0V(总线由上拉电阻保持高阻态)。

两种网络结构

  1. 闭环结构(ISO 11898,高速短距离)
    • 两端接 120Ω 终端电阻,消除信号反射,支持最高 1Mbps,最远 40 米(如汽车动力系统)。
    • 典型硬件:STM32 + TJA1040 收发器,适用于高速实时控制。
  2. 开环结构(ISO 11519-2,低速长距离)
    • 两端接 2.2kΩ 电阻,支持最高 125kbps,最远 10km(如工业传感器网络)。
6.1.2 硬件连接三要素
主机(STM32)       CAN收发器(TJA1040)       从机(传感器)  
PA11(RX) ────── Rx引脚                CAN_L  
PA12(TX) ────── Tx引脚                CAN_H  
GND   ──────────── GND                   GND  

  • CAN 控制器:MCU 片上外设(如 STM32 CAN1),处理协议逻辑。
  • CAN 收发器:转换 TTL 电平与 CAN 差分电平,如 TJA1040(闭环)、SN65HVD230(开环)。
  • 终端电阻:长距离必须接入,短距离(<10 米)可临时省略但需测试稳定性。
6.2 核心特性:多主仲裁与可靠传输
6.2.1 多主仲裁:ID 越小优先级越高
  • 非破坏性仲裁:多个节点同时发送时,逐位比较 ID,显性电平(0)优先,隐性电平(1)退出发送。
    • 示例:节点 A(ID=0x123)和节点 B(ID=0x456)同时发送,前 11 位中某一位 A 发 0、B 发 1,B 检测到总线状态与自身发送不一致,立即停止,A 继续发送。
  • 数据帧 vs 遥控帧:同 ID 时,数据帧的 RTR 位为显性(0),优先级高于遥控帧(RTR=1)。
  • 标准帧 vs 扩展帧:标准帧(11 位 ID)的 IDE 位为显性(0),比扩展帧(29 位 ID,IDE=1)优先级高。
6.2.2 错误检测与自动重发
  • 5 种错误类型:位错误、填充错误、CRC 错误、格式错误、ACK 错误。
  • 错误处理
    1. 检测到错误的节点发送 错误帧(主动错误标志 = 6 个显性位,被动错误标志 = 6 个隐性位)。
    2. 所有节点接收错误帧后,发送节点自动重发数据,确保可靠传输(如汽车刹车信号必须正确到达)。
6.3 帧结构:数据帧详解(最常用场景)
6.3.1 数据帧 7 大段(标准格式为例)
[帧起始(1位显性)] → [仲裁段(11位ID+RTR位)] → [控制段(6位)] → [数据段(0-8字节)] → [CRC段(16位)] → [ACK段(2位)] → [帧结束(7位隐性)]  

  • 仲裁段:11 位 ID 决定优先级,ID 越小优先级越高(如刹车信号 ID=0x001 优先于温度信号 ID=0x100)。
  • 控制段:DLC 字段表示数据长度(0-8 字节,CAN 单次最多传 8 字节,确保实时性)。
  • ACK 段:接收方在 ACK 槽发送显性位(0)表示成功接收,发送方检测到 ACK 后继续传输。
6.3.2 扩展帧 vs 标准帧
特性标准帧扩展帧
ID 长度11 位29 位
适用场景中小规模网络(≤30 节点)复杂系统(如汽车多 ECU)
优先级高于同 ID 扩展帧低于同 ID 标准帧
6.4 代码实现:STM32 驱动 CAN 总线(从初始化到数据发送)
6.4.1 硬件准备(STM32F103 为例)
  • 所需工具:STM32CubeMX(辅助配置)、CANalyzer(总线抓包调试)。
  • 关键引脚
    • PA12(CAN_TX):复用推挽输出,连接收发器 Tx 引脚。
    • PA11(CAN_RX):浮空输入,连接收发器 Rx 引脚。
6.4.2 初始化步骤(逐行解析)
#include "stm32f10x.h"  
#define CAN_TX_PIN GPIO_Pin_12  
#define CAN_RX_PIN GPIO_Pin_11  
#define CAN_PERIPH CAN1  

void CAN_InitSetup(void) {  
    GPIO_InitTypeDef GPIO_InitStruct;  
    CAN_InitTypeDef CAN_InitStruct;  
    CAN_FilterInitTypeDef CAN_FilterStruct;  

    // 1. 使能时钟  
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);       // 使能GPIOA时钟  
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);       // 使能CAN1时钟  

    // 2. 配置GPIO为CAN复用功能  
    // TX引脚:复用推挽输出  
    GPIO_InitStruct.GPIO_Pin = CAN_TX_PIN;  
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;                // 复用推挽,由CAN控制器驱动  
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;  
    GPIO_Init(GPIOA, &GPIO_InitStruct);  
    // RX引脚:浮空输入  
    GPIO_InitStruct.GPIO_Pin = CAN_RX_PIN;  
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;         // 高阻态接收差分信号  
    GPIO_Init(GPIOA, &GPIO_InitStruct);  

    // 3. 配置CAN核心参数(波特率1Mbps)  
    CAN_InitStruct.CAN_Mode = CAN_Mode_Normal;                 // 正常模式(非测试模式)  
    CAN_InitStruct.CAN_SJW = CAN_SJW_1tq;                     // 同步跳转宽度:1个时间量子  
    CAN_InitStruct.CAN_BS1 = CAN_BS1_6tq;                    // 时间段1:6个时间量子  
    CAN_InitStruct.CAN_BS2 = CAN_BS2_3tq;                    // 时间段2:3个时间量子  
    CAN_InitStruct.CAN_Prescaler = 6;                          // 预分频器:72MHz/(6*(6+3+1))=1Mbps  
    CAN_Init(CAN_PERIPH, &CAN_InitStruct);  

    // 4. 配置滤波器(接收所有ID,简化示例)  
    CAN_FilterStruct.CAN_FilterNumber = 0;                     // 滤波器0  
    CAN_FilterStruct.CAN_FilterMode = CAN_FilterMode_IdMask;    // 掩码模式,接收所有ID  
    CAN_FilterStruct.CAN_FilterScale = CAN_FilterScale_32bit;   // 32位过滤(ID+掩码各16位,全0表示不过滤)  
    CAN_FilterInit(&CAN_FilterStruct);  
    CAN_FilterCmd(0, ENABLE);                                 // 使能滤波器0  

    // 5. 使能CAN控制器  
    CAN_Cmd(CAN_PERIPH, ENABLE);  
}  
6.4.3 发送数据帧(标准帧,ID=0x123,数据 = 0x55 0xAA)
void CAN_SendDataFrame(uint16_t std_id, uint8_t* data, uint8_t len) {  
    CanTxMsg TxMsg;  
    uint8_t mailbox;  

    // 配置消息参数  
    TxMsg.StdId = std_id;                // 标准ID:0x123(11位有效,实际取低11位)  
    TxMsg.IDE = CAN_ID_STD;              // 标准帧  
    TxMsg.RTR = CAN_RTR_DATA;            // 数据帧(非遥控帧)  
    TxMsg.DLC = len;                     // 数据长度:2字节  
    for (int i=0; i<len; i++) {  
        TxMsg.Data[i] = data[i];          // 填充数据  
    }  

    // 发送并等待成功  
    mailbox = CAN_Transmit(CAN_PERIPH, &TxMsg);  
    while (CAN_TransmitStatus(CAN_PERIPH, mailbox) != CAN_TxStatus_Ok);  // 阻塞直到发送完成  
}  

// 主函数调用示例  
int main() {  
    CAN_InitSetup();  
    uint8_t send_data[] = {0x55, 0xAA};  
    CAN_SendDataFrame(0x123, send_data, 2);  
    while(1);  
}  
6.5 典型应用场景
6.5.1 汽车电子(核心场景)
  • 场景:发动机 ECU、刹车系统、仪表盘通过 CAN 总线互联。
  • 示例:刹车踏板被踩下时,刹车 ECU 发送 ID=0x001 的紧急信号(低 ID 高优先级),所有节点立即响应,确保刹车指令优先传输。
6.5.2 工业自动化(长距离抗干扰)
  • 场景:工厂传感器(温度、压力)组网,距离 1000 米,波特率 50kbps。
  • 优势:差分信号抗电磁干扰,自动重发确保数据不丢失,适合钢铁厂、煤矿等恶劣环境。
6.5.3 医疗设备(高精度同步)
  • 场景:多参数监护仪,各模块(心率、血氧)通过 CAN 总线同步数据。
  • 特点:8 字节数据段刚好容纳实时生理参数,CRC 校验确保数据准确,适合医疗设备对可靠性的严苛要求。
6.6 新手避坑与优化技巧
6.6.1 常见问题排查
问题现象可能原因解决方法
数据乱码 / 丢帧终端电阻缺失或阻值错误总线两端添加 120Ω 电阻(闭环)或 2.2kΩ 电阻(开环)
节点无法通信波特率不匹配检查所有节点的CAN_PrescalerBS1BS2参数,确保一致
多个节点同时发送失败ID 冲突(同 ID 不同帧类型)分配唯一 ID,数据帧 ID 避免与遥控帧重复
6.6.2 波特率计算(关键公式)
波特率 = 系统时钟 / (预分频器 × (BS1 + BS2 + 1))  
示例(STM32 72MHz):  
预分频器=6,BS1=6tq,BS2=3tq  
波特率 = 72MHz / (6 × (6+3+1)) = 1Mbps  
6.6.3 硬件优化
  • 线缆选择:高速场景用屏蔽双绞线(减少 EMI),低速长距离用非屏蔽双绞线。
  • 收发器选型:闭环选 TJA1040(高速),开环选 SN65HVD230(低速),确保与网络结构匹配。
6.7 与其他协议对比(核心差异)
特性CANRS485SPII2C
信号类型差分(抗干扰最强)差分(半双工)单端(同步时钟)单端(开漏上拉)
多主支持支持(仲裁机制)单主多从单主多从(CS 线)多主(仲裁通过 SDA)
数据长度0-8 字节(固定)任意(需自定义)任意(同步传输)任意(单字节应答)
典型场景汽车、工业控制仪表组网高速存储(Flash)低速传感器(SHT30)
错误处理自动重发 + CRC 校验无(依赖上层协议)

总结:为什么 CAN 是工业级首选?

  • 可靠性:差分传输 + 仲裁机制 + 错误重发,适合高容错场景(如汽车刹车系统)。
  • 灵活性:支持多主架构,适配不同规模网络(从小型传感器到汽车多 ECU 集群)。
  • 实时性:ID 优先级确保关键数据优先传输,满足工业控制对延迟的严格要求。

掌握 CAN 总线的关键在于理解差分信号原理、仲裁机制和帧结构,从简单的单节点通信开始,逐步尝试多节点组网,结合逻辑分析仪调试,可快速掌握工业级通信设计的核心能力。

三、应用层协议:MODBUS—— 工业设备的 “通用语言”

MODBUS 是运行在底层协议上的应用层协议,定义了数据格式和功能码,常见于工业场景。它通过统一的数据交互规则,实现不同厂商设备的互联互通。

3.1 MODBUS 与 RS 系列协议的本质区别

3.1.1 物理层与协议层的定位差异
维度RS232/RS485MODBUS
协议层级物理层(定义电气特性、传输介质)应用层(定义数据格式、通信规则)
核心功能实现信号传输(如电平转换、抗干扰)实现数据交互(如寄存器读写、设备控制)
典型场景硬件连接(如 PC 串口、工业总线)设备通信(如 PLC 与传感器、仪表)
兼容性仅支持特定物理接口(如 RS485 差分信号)可运行于 RS485/RS232/Ethernet 等多种物理层

示例

  • RS485 负责将数据以差分信号形式传输(如 A/B 线压差 ±2V)。
  • MODBUS 则定义数据帧格式(如从机地址、功能码、校验),确保主从设备理解数据含义。
3.1.2 数据传输方式对比
特性RS232RS485MODBUS RTU
信号类型单端信号(依赖 GND 参考)差分信号(A/B 线压差)基于 RS485/RS232 的二进制数据帧
传输距离15 米内(抗干扰弱)1200 米 @100Kbps(抗干扰强)受限于物理层(如 RS485 距离)
节点数量1 对 1(点对点)32 个从机(需终端电阻)主从模式(1 主 + N 从)
典型应用设备调试(如单片机串口)工业组网(如 PLC 与仪表)工业自动化(如数据采集、控制)

示例

  • RS485 网络中,MODBUS 主站通过地址 0x01 向从站发送读寄存器命令(功能码 0x03),从站返回数据并附加 CRC 校验。

3.2 MODBUS 核心知识点详解

3.2.1 两种通信模式
  1. MODBUS-RTU(基于 RS485/TTL-UART)

    • 传输效率:二进制数据,1 帧可携带 256 字节,适合高速场景。
    • 帧结构[从机地址(1B)][功能码(1B)][数据长度(2B)][数据内容(nB)][CRC校验(2B)]
    • 应用:工业设备本地通信(如 PLC 与变频器)。
  2. MODBUS-TCP(基于以太网)

    • 传输效率:数据封装在 TCP 包中,支持远程监控。
    • 帧结构[MBAP头(7B)][功能码(1B)][数据内容(nB)]
    • 应用:跨网络通信(如工厂上位机与远程设备)。

示例

  • MODBUS-RTU 读寄存器命令:01 03 00 00 00 03 05 CB(从机地址 1,读 3 个寄存器,CRC 校验 0x05CB)。
  • MODBUS-TCP 读寄存器命令:00 01 00 00 00 06 01 03 00 00 00 03(MBAP 头 + 功能码 + 地址)。
3.2.2 功能码体系(核心操作指令)
功能码名称描述示例
0x01读线圈状态读取开关量输出(如继电器状态)读取从机地址 2 的 00001-00008 线圈状态
0x03读保持寄存器读取可写寄存器(如传感器测量值)读取从机地址 3 的 40001-40003 寄存器(共 3 个)
0x06写单个寄存器写入单个寄存器(如设置参数)向从机地址 1 的 40002 寄存器写入 0x1234
0x10写多个寄存器写入连续寄存器块(如批量设置参数)向从机地址 5 的 40001-40005 寄存器写入 5 个数据

代码示例

# 使用 minimalmodbus 库读取保持寄存器
import minimalmodbus

instrument = minimalmodbus.Instrument('/dev/ttyUSB0', 1)  # 端口、从机地址
instrument.serial.baudrate = 9600  # 波特率
instrument.serial.bytesize = 8    # 数据位
instrument.serial.parity = 'N'    # 校验位
instrument.serial.stopbits = 1    # 停止位

data = instrument.read_registers(0, 3)  # 读取 40001-40003 寄存器
print("读取结果:", data)

3.3 MODBUS 与 RS485 协同工作机制

3.3.1 硬件连接规范
  1. 线缆选择

    • 推荐使用双绞屏蔽线(如 Belden 3107A),屏蔽层单端接地。
    • 长距离(>500 米)需加 120Ω 终端电阻(跨接 A/B 线)。
  2. 组网拓扑

    • 总线型拓扑(菊花链连接),避免星型结构。
    • 节点间距 ≤10 米,分支长度 ≤0.5 米。

示例

  • 工业现场中,PLC 通过 RS485 总线连接 10 台仪表,每台仪表地址唯一(1-10),终端电阻接在总线两端。
3.3.2 通信流程与时序
  1. 主从通信流程

  2. 时序要求

    • 波特率匹配:主从设备波特率偏差需 <0.5%。
    • 帧间隔:需满足 3.5 字符周期的空闲时间(如 9600bps 时约 3.6ms)。

示例

  • 主站发送命令后,若 100ms 内未收到响应,则判定超时并重新发送。

3.4 常见误区与排错指南

3.4.1 常见误区
  1. MODBUS 只能用 RS485 传输

    • 实际支持 RS232/RS485/Ethernet 等 7 种物理层。
    • 示例:MODBUS-TCP 可通过以太网实现跨网段通信。
  2. RS485 只能跑 MODBUS 协议

    • RS485 仅是物理层,可自定义协议(如自定义数据帧格式)。
    • 示例:某些工业设备使用私有协议通过 RS485 传输。
3.4.2 故障排查步骤
  1. 硬件层

    • 检查线缆通断(万用表测 A/B 线电阻,正常应为 120Ω)。
    • 测量静态电压(A/B 线压差约 2-3V)。
  2. 协议层

    • 确认功能码是否正确(如写线圈用 0x05,而非 0x06)。
    • 校验 CRC 或 LRC 校验值(工具:ModbusPal、ModbusPoll)。

示例

  • 主站发送命令后从站无响应,排查发现从站地址设置错误(主站发 0x01,从站实际地址 0x02)。

3.5 实际应用场景对比

场景推荐方案优势
短距离调试(如单片机与 PC)RS232 + MODBUS-RTU接线简单(3 根线),成本低
工业多设备组网RS485 + MODBUS-RTU抗干扰强,支持 32 个从机
远程监控(跨网段)MODBUS-TCP支持 IP 网络,传输距离无限制
高可靠性汽车电子CAN 总线 + 自定义协议多主仲裁,抗干扰能力极强

示例

  • 智能建筑中,空调控制器通过 RS485 + MODBUS-RTU 连接到 PLC,实现集中监控。

3.6 总结:MODBUS 与 RS 系列的协同关系

层级RS232/RS485MODBUS
物理层提供电气标准(如差分信号)无(依赖底层物理层)
协议层无(仅传输原始数据)定义数据格式、功能码、校验规则
应用层无(仅硬件连接)实现设备通信(如寄存器读写)

关键结论

  • RS232/RS485 是 “高速公路”,MODBUS 是 “交通规则”。
  • 选择 RS485 + MODBUS-RTU 可兼顾长距离、抗干扰和协议通用性。
  • MODBUS-TCP 适合需要远程访问的复杂系统。

通过以上对比,可快速掌握 MODBUS 与 RS 系列协议的核心差异及实际应用技巧。

四、七大协议核心对比表(含 UART/USART)

特性UARTUSARTRS232RS485SPIIICCANMODBUS
同步方式异步异步 + 同步异步异步同步同步异步应用层协议
物理层TTLTTL单端信号差分信号单端信号开漏输出差分信号多样(RS485/RS232 / 以太网等)
线缆数量2(TX/RX)3(TX/RX/CTS/RTS)3(TX/RX/GND)2(A/B)4(CLK/MOSI/MISO/CS)2(SDA/SCL)2(CAN_H/CAN_L)依底层协议
传输距离短(cm 级)短(cm 级)15 米1200 米 @100Kbpscm 级10 米10km@5Kbps依底层协议
多设备点对点点对点点对点1 主多从1 主多从多主多从多主多从1 主多从
典型速度115200bps1Mbps+115200bps10Mbps@100 米50Mbps+400kHz1Mbps@40 米依底层协议
抗干扰中等极强依底层协议
应用场景调试高速外设串口设备工业仪表高速外设低速外设汽车电子工业通信

对比表核心参数解析

1. 同步方式
  • UART/USART/RS232/RS485/CAN:异步通信,无需时钟线,依赖波特率同步。
  • SPI/IIC:同步通信,需时钟线(CLK/SCL)。
  • MODBUS:应用层协议,依赖底层物理层同步方式。
2. 物理层
  • TTL:3.3V/5V 电平(如单片机串口)。
  • 差分信号:RS485/CAN 通过 A/B 线压差传输,抗干扰强。
  • 开漏输出:IIC 需上拉电阻,支持多主设备。
3. 传输距离
  • RS485:1200 米 @100Kbps(工业场景首选)。
  • CAN:10km@5Kbps(汽车电子长距离通信)。
  • SPI:cm 级(高速但抗干扰弱)。
4. 多设备支持
  • RS485:1 主 32 从(需终端电阻)。
  • IIC:多主多从(通过地址仲裁)。
  • CAN:多主多从(非破坏性仲裁)。
5. 典型速度
  • SPI:50Mbps+(如高速 Flash 通信)。
  • USART:1Mbps+(同步模式下)。
  • IIC:400kHz(低速外设控制)。

五、如何选择?

1. 按 “距离 + 设备数量” 选底层协议

  • 短距离(cm 级)、高速外设:SPI(如连接 Flash)。
  • 短距离(米级)、低速多设备:IIC(如传感器组网)。
  • 中距离(100 米级)、多设备:RS485+MODBUS(如工业仪表)。
  • 长距离(km 级)、高可靠:CAN 总线(如汽车 ECU)。
  • PC 调试、点对点:UART+RS232/TTL(如单片机连电脑)。

2. 按 “同步需求” 选 UART/USART

  • 纯异步场景:选 UART(或 USART 配置为 UART 模式)。
  • 需同步高速传输:选 USART 的同步模式(加 CLK 线)。

3. 按 “行业场景” 选协议

  • 工业自动化:RS485+MODBUS(性价比高)或 CAN(高可靠)。
  • 智能家居:IIC(传感器)+UART(控制面板)。
  • 汽车电子:CAN 总线(ECU 控制)。
  • 5G 工业通信:DTU 模块(如腾讯云 TD210)实现串口数据与 IP 协议转换。

六、关键技术深度解析

1. 双工模式的工程实现

  • 半双工
    • RS485:通过 DE/RE 引脚切换收发状态(如 MAX485 芯片)。
    • 代码示例
      // 发送数据时  
      DE = 1; // 使能发送  
      USART_SendData(USART1, data);  
      while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);  
      DE = 0; // 禁止发送,切换为接收  
      
  • 全双工
    • SPI:主机和从机同时收发数据(如 STM32 的 SPI 外设)。
    • 特性:发送一个字节必接收一个字节,硬件自动完成同步。

2. 差分信号与开漏输出的原理

  • 差分信号
    • RS485/CAN:通过 A/B 线压差传输数据,抗共模干扰。
    • 优势:长距离传输(如 RS485 达 1200 米),抗干扰能力强。
  • 开漏输出
    • IIC:SDA/SCL 需外接上拉电阻,实现 “线与” 仲裁。
    • 原理:高阻态表示 1,低电平表示 0,多主机竞争时自动降权。

3. 仲裁机制的工程实现

  • CAN 总线
    • 非破坏性仲裁:ID 越小优先级越高,总线冲突时低 ID 帧优先发送。
    • 示例:节点 A(ID=0x123)与节点 B(ID=0x456)同时发送,ID 逐位比较,0x123 优先级更高。
  • IIC 总线
    • 线与仲裁:多主机竞争时,先拉低 SDA 的主机获得总线控制权。
    • 示例:主机 A 发送 “101”,主机 B 发送 “100”,仲裁时主机 B 获胜。

七、总结:从 “接线” 到 “协议” 的核心逻辑

  1. UART/USART 是逻辑协议,定义数据格式(异步 / 同步),需搭配物理层(如 RS232/TTL)。
  2. RS232/RS485 是物理层标准,解决 “如何传”(电平、线缆),常与 UART 结合。
  3. SPI/IIC 是单片机外设常用的同步总线,适合短距离高速 / 低速设备。
  4. CAN 是工业级高可靠总线,适合多主多从、长距离场景。
  5. MODBUS 是应用层协议,让不同设备 “说同一种语言”,常跑在 RS485/CAN/TCP 上。

掌握这些协议的核心差异,就能在嵌入式开发中根据需求灵活选择,从硬件接线到软件协议实现一气呵成!建议从单片机外设(如 SPI Flash、IIC 传感器)开始实践,逐步深入工业级总线(RS485/CAN)和 MODBUS 协议,实现从入门到进阶的跨越。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值