跳转至

串口与I2C/SPI

概述

UART(串口)、I2C和SPI是嵌入式系统中最基础的三种通信协议,几乎所有机器人都会用到。它们各自适用于不同的场景:UART用于板间点对点通信,I2C用于低速多设备挂载,SPI用于高速外设读写。

UART(通用异步收发器)

基本原理

UART(Universal Asynchronous Receiver/Transmitter)是最简单的串行通信协议:

  • 异步:无时钟线,收发双方约定相同波特率
  • 全双工:TX和RX独立,可同时收发
  • 点对点:标准UART只连接两个设备

帧格式

空闲(HIGH) ─┐  ┌─────────────────┐  ┌──┐  ┌─ 空闲(HIGH)
            └──┤ D0 D1 ... D7    ├──┤P ├──┤
          起始位    数据位(8bit)    校验  停止位
           (LOW)                  (可选) (HIGH)
参数 说明
起始位 1位,固定为LOW
数据位 通常8位(也可5/6/7/9位)
校验位 无/奇校验/偶校验(可选)
停止位 1位或2位,固定为HIGH

波特率

常用波特率:

波特率 字节速率 典型应用
9600 960 B/s GPS(NMEA)
115200 11.5 KB/s 调试串口、通用
256000 25.6 KB/s 快速数据传输
1000000 100 KB/s 总线舵机(Feetech STS)
2000000 200 KB/s 激光雷达

波特率匹配

收发两端波特率必须一致,误差不超过3-5%,否则数据乱码。晶振精度很重要。

电平标准

标准 电平 传输距离 应用
TTL 0V/3.3V 或 0V/5V <1m MCU间直连
RS-232 ±3~15V <15m PC串口(DB9)
RS-485 差分±1.5V <1200m 工业设备、Dynamixel

RS-485详解

RS-485使用差分信号,抗干扰能力强:

     A线 ───────────────────────
            差分信号              
     B线 ───────────────────────

     逻辑1: A > B (A-B > +200mV)
     逻辑0: A < B (A-B < -200mV)

特点:

  • 半双工(两线)或全双工(四线)
  • 多点通信:一条总线上可挂载32/128/256个节点
  • 传输距离长:1200m@100kbps
  • 需要终端电阻(120Ω)

Arduino/ESP32 UART代码

// ESP32 双串口示例
// Serial  - USB调试
// Serial1 - 连接GPS
// Serial2 - 连接激光雷达

void setup() {
    Serial.begin(115200);                          // USB调试
    Serial1.begin(9600, SERIAL_8N1, 16, 17);       // GPS: RX=16, TX=17
    Serial2.begin(230400, SERIAL_8N1, 18, 19);     // 激光雷达: RX=18, TX=19
}

void loop() {
    // 读取GPS数据
    while (Serial1.available()) {
        char c = Serial1.read();
        if (c == '$') {
            String nmea = Serial1.readStringUntil('\n');
            Serial.println("GPS: " + nmea);
        }
    }

    // 读取激光雷达数据包
    if (Serial2.available() >= 47) {  // 完整数据包长度
        uint8_t header = Serial2.read();
        if (header == 0x54) {
            // 解析数据包...
        }
    }
}

I2C(内部集成电路总线)

基本原理

I2C(Inter-Integrated Circuit)由Philips开发,两线制同步串行总线:

  • SDA(Serial Data):数据线
  • SCL(Serial Clock):时钟线
  • 开漏输出:需要外部上拉电阻

总线结构

VCC ───┬────────┬────────┬────────┐
       R(4.7k)  R(4.7k)  │        │
       │        │        │        │
SDA ───┼────────┼────────┼────────┤
SCL ───┼────────┼────────┼────────┤
       │        │        │        │
     [MCU]   [IMU]    [OLED]  [EEPROM]
    (Master) (0x68)   (0x3C)  (0x50)

地址机制

  • 7位地址:理论128个设备,实际约112个可用
  • 10位地址:扩展模式,很少使用
  • 每个设备有固定或可配置的地址

常见设备地址:

设备 地址
MPU6050 IMU 0x68 / 0x69
BME280气压计 0x76 / 0x77
SSD1306 OLED 0x3C / 0x3D
AS5600编码器 0x36
PCA9685 PWM 0x40-0x7F
EEPROM AT24C 0x50-0x57

通信协议

起始   地址(7bit) R/W ACK  数据(8bit) ACK  数据(8bit) ACK  停止
 S    [A6...A0]  [W]  [A] [D7...D0]  [A]  [D7...D0]  [A]   P
 ↓        ↓       ↓    ↓      ↓       ↓      ↓       ↓    ↓
SDA:  ─┐ XXXXXXX  X    X  XXXXXXXX    X  XXXXXXXX    X  ┌─
SCL:  ─┘ ^^^^^^^^  ^    ^  ^^^^^^^^    ^  ^^^^^^^^    ^  └─
  • S(Start):SDA在SCL为高时拉低
  • P(Stop):SDA在SCL为高时拉高
  • ACK:接收方拉低SDA表示确认

I2C速率模式

模式 速率 应用
标准模式 100 kHz 一般传感器
快速模式 400 kHz IMU、编码器
快速+模式 1 MHz 高速外设
高速模式 3.4 MHz 特殊应用

上拉电阻选择

\[ R_{pull-up} = \frac{V_{CC}}{I_{sink}} \approx \frac{V_{CC}}{3mA} \approx 1.1k\Omega \sim 10k\Omega \]
  • 速率越高,上拉电阻越小(充电更快)
  • 典型值:100kHz用4.7kΩ,400kHz用2.2kΩ

时钟拉伸(Clock Stretching)

从设备可以通过拉低SCL来暂停通信("请等一下,我还没准备好")。主设备必须检测并等待SCL释放。

I2C调试技巧

使用I2C扫描程序可以检测总线上所有设备的地址,快速定位连接问题。

I2C代码示例

# MicroPython I2C 示例:读取MPU6050
from machine import I2C, Pin
import struct

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

# 扫描总线
devices = i2c.scan()
print(f"发现设备: {[hex(d) for d in devices]}")

MPU6050_ADDR = 0x68

# 唤醒MPU6050(写PWR_MGMT_1寄存器)
i2c.writeto_mem(MPU6050_ADDR, 0x6B, bytes([0x00]))

def read_accel():
    # 读取6字节加速度数据(X/Y/Z各2字节)
    data = i2c.readfrom_mem(MPU6050_ADDR, 0x3B, 6)
    ax, ay, az = struct.unpack('>hhh', data)
    # 转换为g(±2g量程,灵敏度16384 LSB/g)
    return ax/16384.0, ay/16384.0, az/16384.0

ax, ay, az = read_accel()
print(f"加速度: X={ax:.2f}g, Y={ay:.2f}g, Z={az:.2f}g")

SPI(串行外设接口)

基本原理

SPI(Serial Peripheral Interface)由Motorola开发,高速同步全双工通信:

  • SCK:时钟(主设备产生)
  • MOSI:主出从入(Master Out Slave In)
  • MISO:主入从出(Master In Slave Out)
  • CS/SS:片选(每个从设备一根,低有效)

总线结构

         SCK  ──→ ┌──────┐  ┌──────┐  ┌──────┐
         MOSI ──→ │Slave1│  │Slave2│  │Slave3│
         MISO ←── │(IMU) │  │(Flash)│ │(显示) │
         CS1  ──→ └──────┘  └──────┘  └──────┘
         CS2  ──────────────→ ↑
         CS3  ──────────────────────→ ↑

SPI模式

SPI有4种时钟模式,由CPOL和CPHA决定:

模式 CPOL CPHA 说明
Mode 0 0 0 空闲低电平,上升沿采样
Mode 1 0 1 空闲低电平,下降沿采样
Mode 2 1 0 空闲高电平,下降沿采样
Mode 3 1 1 空闲高电平,上升沿采样

最常用模式

Mode 0和Mode 3最常用。查看外设数据手册确定正确模式。

SPI特点

特性
速率 通常1-50MHz,最高可达100MHz+
全双工 是(MOSI和MISO同时传输)
主从 一个主设备,多个从设备
地址 无地址机制,靠CS片选
开销 无起始/停止/应答位

SPI代码示例

// Arduino SPI 读取ICM-42688 IMU
#include <SPI.h>

#define IMU_CS 10
#define SPI_SPEED 8000000  // 8MHz

void setup() {
    Serial.begin(115200);
    SPI.begin();
    pinMode(IMU_CS, OUTPUT);
    digitalWrite(IMU_CS, HIGH);

    // 读取WHO_AM_I寄存器验证连接
    uint8_t whoami = spiRead(0x75);
    Serial.print("WHO_AM_I: 0x");
    Serial.println(whoami, HEX);  // 应返回0x47
}

uint8_t spiRead(uint8_t reg) {
    uint8_t val;
    digitalWrite(IMU_CS, LOW);
    SPI.beginTransaction(SPISettings(SPI_SPEED, MSBFIRST, SPI_MODE0));
    SPI.transfer(reg | 0x80);  // 最高位=1表示读
    val = SPI.transfer(0x00);  // 发送dummy byte,接收数据
    SPI.endTransaction();
    digitalWrite(IMU_CS, HIGH);
    return val;
}

void spiWrite(uint8_t reg, uint8_t data) {
    digitalWrite(IMU_CS, LOW);
    SPI.beginTransaction(SPISettings(SPI_SPEED, MSBFIRST, SPI_MODE0));
    SPI.transfer(reg & 0x7F);  // 最高位=0表示写
    SPI.transfer(data);
    SPI.endTransaction();
    digitalWrite(IMU_CS, HIGH);
}

三种协议对比

特性 UART I2C SPI
线数 2 (TX/RX) 2 (SDA/SCL) 4+ (SCK/MOSI/MISO/CS)
时钟 无(异步) 有(同步) 有(同步)
速率 <2Mbps <3.4MHz <100MHz
双工 全双工 半双工 全双工
设备数 2(点对点) 128(7位地址) 受CS引脚限制
距离 中(可转RS-485延长) 短(<1m) 短(<1m)
复杂度
功耗 低(上拉电阻)

机器人中的典型分配

外设 推荐总线 原因
IMU(MPU6050/ICM42688) I2C / SPI I2C简单,SPI更快
磁编码器(AS5600) I2C 低速足够
OLED显示屏 I2C / SPI I2C省线,SPI刷新快
GPS模块 UART NMEA协议基于串口
总线舵机 UART (TTL/RS-485) 标准接口
激光雷达 UART / USB 数据量大用USB
Flash存储 SPI 高速读写
ADC(模数转换) SPI 高速采样
气压计/温湿度 I2C 低速传感器

小结

  • UART是最简单的异步通信,适合板间点对点连接
  • I2C两线制多设备挂载,适合低速传感器和显示屏
  • SPI高速全双工,适合IMU、Flash等需要快速数据传输的外设
  • RS-485是UART的工业级延伸,差分信号抗干扰、传输远
  • 实际机器人项目中三种协议通常并存,各司其职

评论 #