F FisherHub Docs

通信协议

为什么需要板级通信协议

MCU 需要和传感器、存储器、显示屏等多种外设通信。不同外设对速度、引脚数、距离、从机数量的要求不同,催生了多种通信协议。理解它们的区别是嵌入式开发的核心技能。

UART (Universal Asynchronous Receiver Transmitter)

UART 是最简单的异步串行通信,只需要 TX 和 RX 两根线。

特点

  • 异步,无需时钟线
  • 全双工
  • 点对点通信(只有两个设备)
  • 需要双方约定波特率

常见波特率: 9600, 115200, 921600

// UART 初始化示例 (STM32 HAL)
UART_HandleTypeDef huart1;

void MX_USART1_UART_Init(void) {
    huart1.Instance = USART1;
    huart1.Init.BaudRate = 115200;
    huart1.Init.WordLength = UART_WORDLENGTH_8B;
    huart1.Init.StopBits = UART_STOPBITS_1;
    huart1.Init.Parity = UART_PARITY_NONE;
    huart1.Init.Mode = UART_MODE_TX_RX;
    HAL_UART_Init(&huart1);
}

// 发送和接收
HAL_UART_Transmit(&huart1, (uint8_t*)"Hello\r\n", 8, 100);
HAL_UART_Receive(&huart1, rx_buffer, 1, 1000);

调试技巧:UART 的时序判断最简单。如果抓到乱码,先检查波特率是否一致。用逻辑分析仪抓取一个字节的波形,验证起始位(低)+数据位+停止位(高)是否符合预期。

I2C (Inter-Integrated Circuit)

I2C 使用 SCL (时钟) 和 SDA (数据) 两根线,支持一主多从通信。

特点

  • 同步,有时钟线
  • 半双工
  • 多从机(通过 7 位或 10 位地址区分)
  • 标准速度 100kHz,快速 400kHz,高速 3.4MHz

通信流程

  1. 主机发送 START 条件 (SCL 高时 SDA 下降沿)
  2. 主机发送 7 位从机地址 + 读写位
  3. 从机发送 ACK 应答
  4. 传输数据字节,每字节后跟 ACK/NACK
  5. 主机发送 STOP 条件 (SCL 高时 SDA 上升沿)
# I2C 扫描从机地址 (MicroPython)
from machine import Pin, I2C

i2c = I2C(0, scl=Pin(22), sda=Pin(21), freq=400000)
devices = i2c.scan()

if devices:
    print("发现 I2C 设备:")
    for addr in devices:
        print(f"  0x{addr:02X}")
else:
    print("未检测到 I2C 设备")
// I2C 读取温湿度传感器 (Arduino)
#include <Wire.h>

void setup() {
    Wire.begin();  // 作为主机
    Serial.begin(115200);
    
    Wire.beginTransmission(0x44);  // SHT30地址
    Wire.write(0x2C);
    Wire.write(0x06);             // 发送测量命令
    Wire.endTransmission();
    delay(100);
    
    Wire.requestFrom(0x44, 6);
    while (Wire.available()) {
        byte data[6];
        for (int i = 0; i < 6; i++) {
            data[i] = Wire.read();
        }
        // 处理数据...
    }
}

常见问题:总线卡死(SDA 被拉低)。解决方法是给 SCL 发送 9 个脉冲让从机复位,或者检查上拉电阻是否正确(4.7kΩ 典型值)。

SPI (Serial Peripheral Interface)

SPI 使用四线(MOSI, MISO, SCLK, CS)实现高速全双工通信。

特点

  • 同步,全双工
  • 一主多从(每个从机一根 CS 片选线)
  • 速度可达数十 MHz
  • 不需要地址,靠片选信号选择从机

四种模式(CPOL 时钟极性 + CPHA 时钟相位):

模式CPOLCPHA采样沿说明
000上升沿最常用
101下降沿
210下降沿
311上升沿
// SPI 读写示例 (ESP-IDF)
#include "driver/spi_master.h"

spi_device_handle_t spi_handle;

void spi_init(void) {
    spi_bus_config_t bus_cfg = {
        .mosi_io_num = 23,
        .miso_io_num = 19,
        .sclk_io_num = 18,
        .max_transfer_sz = 4096
    };
    spi_bus_initialize(SPI2_HOST, &bus_cfg, SPI_DMA_CH_AUTO);
    
    spi_device_interface_config_t dev_cfg = {
        .mode = 0,
        .clock_speed_hz = 5 * 1000 * 1000,  // 5MHz
        .spics_io_num = 5,
        .queue_size = 1
    };
    spi_bus_add_device(SPI2_HOST, &dev_cfg, &spi_handle);
}

void spi_read_sensor(uint8_t reg, uint8_t *data, size_t len) {
    spi_transaction_t t = {
        .length = 8 * (1 + len),
        .tx_buffer = (uint8_t[]){reg | 0x80, 0},  // 读命令+虚拟字节
        .rx_buffer = NULL
    };
    // SPI 是同时收发,需要从rx数据中提取
    spi_device_transmit(spi_handle, &t);
}

三种协议对比

特性UARTI2CSPI
线数223+n (每从机一根CS)
传输方式异步同步同步
双工全双工半双工全双工
速度最高 ~1Mbps最高 ~3.4Mbps最高 ~80Mbps
从机数量1:11:多 (127个)1:多 (受限于CS引脚)
抗噪能力好(差分SPI更好)
调试难度

调试方法总结

必备工具:逻辑分析仪(20MHz以上采样率即可)。

调试步骤:

  1. 确认电气连接正常(万用表测通断)
  2. 用逻辑分析仪抓取波形
  3. 验证起始条件、地址/命令、数据、应答位是否符合协议规范
  4. 检查时序参数(时钟频率、建立时间、保持时间)是否满足外设数据手册要求
# 逻辑分析仪捕获数据处理(示例)
# 假设已从 PulseView/Saleae 导出 CSV
import csv

def parse_i2c_transaction(csv_file):
    with open(csv_file, 'r') as f:
        reader = csv.reader(f)
        for row in reader:
            time_ns, scl, sda = float(row[0]), int(row[1]), int(row[2])
            # 根据SCL和SDA的边沿变化解码
            # 这里简化处理,实际使用需要完整的协议解析
            process_edge(time_ns, scl, sda)

小结

UART 适合双向简单通信、调试输出;I2C 适合连接大量低速传感器(占用引脚少);SPI 适合高速数据采集(显示屏、SD卡、ADC)。选型时先看外设支持的接口,再看 MCU 的外设资源是否够用,最后考虑引脚布局是否方便。