1. 接口协议基础与验证环境搭建:SPI、I2C、UART 协议对比,验证平台架构设计,Python 与 C 混合编程环境配置

各位同学,咱们今天正式开课。做硬件验证和驱动开发,第一件事不是写代码,而是把三个最常用的接口协议搞清楚。SPI、I2C、UART,这三兄弟你几乎天天见。我见过不少工程师,代码写得飞起,结果连 I2C 的起始条件都画错——嗯,这种坑咱们今天一次性填平。

1.1 三大协议核心差异:一张表说清楚

先别急着看波形。我个人习惯,上手之前先做横向对比。你想想看,选协议就像选工具——拧螺丝用扳手,剪电线用钳子,各有各的活法。

特性 UART SPI I2C
信号线数 2(TX、RX) 4(SCLK、MOSI、MISO、CS) 2(SCL、SDA)
通信方式 异步,无时钟线 同步,主设备提供时钟 同步,主设备提供时钟
数据方向 全双工 全双工 半双工
从设备选择 点对点 片选线(CS) 地址寻址(7位或10位)
速度范围 典型 9600~115200 bps 典型 1~50 MHz 标准 100 kHz,快速 400 kHz
抗干扰能力 弱(无时钟同步) 强(差分可选) 中等(开漏输出)
典型应用 调试日志、GPS 模块 ADC、SD 卡、显示屏 传感器、EEPROM、RTC

这张表我建议你存下来。每次选型的时候拿出来对照一下,能省不少事。我在项目中遇到过有人用 UART 接 10 个从设备,结果搞了个复杂的轮询协议——其实换个 I2C 就解决了。

1.2 协议细节与避坑指南

UART:最老实的那个

UART 说白了就是两根线,一发一收。没有时钟线,所以两边得提前约定好波特率。我刚开始做嵌入式的时候,经常遇到乱码——后来发现是波特率对不上。嗯,这里要注意:波特率误差不能超过 ±2%,否则数据就飘了。

我曾经踩过的坑:用 8 MHz 内部振荡器跑 115200 波特率,结果误差 3.5%,数据全错。后来换成外部晶振才稳定。所以,高频 UART 一定要用高精度时钟源。

SPI:快,但线多

SPI 是速度之王。四根线:时钟、主出从入、主入从出、片选。每个从设备独占一根片选线,所以从设备多了,GPIO 就不够用。我见过一个项目接了 8 个 SPI 从设备,光片选就占了 8 个引脚——后来改用 I2C 了。

SPI 有四种模式,由时钟极性(CPOL)和时钟相位(CPHA)组合决定。很多人在这里翻车。我教你一个记忆方法:CPOL=0 时空闲低电平,CPOL=1 时空闲高电平;CPHA=0 时第一个边沿采样,CPHA=1 时第二个边沿采样。记不住?那就直接看数据手册,别猜。

I2C:线少,但协议复杂

I2C 只有两根线,靠地址寻址。起始条件、停止条件、应答信号——这些时序要求很严格。我调试 I2C 的时候,最喜欢用逻辑分析仪抓波形。为什么?因为 I2C 的时序问题用肉眼很难看出来。

小技巧:I2C 总线上拉电阻选 4.7 kΩ 是通用值。如果总线电容大(比如线很长),换成 2.2 kΩ。如果功耗敏感,用 10 kΩ。这个经验值我用了十年,基本没出过问题。

1.3 验证平台架构设计

协议搞清楚了,接下来搭验证平台。我个人习惯把平台分成三层:硬件抽象层、协议驱动层、测试用例层。这样分层的好处是——你换硬件平台,只需要改最下面一层。

验证平台三层架构 测试用例层 SPI 读写测试 | I2C 从设备枚举 | UART 回环测试 协议驱动层 SPI 驱动 | I2C 驱动 | UART 驱动 硬件抽象层 GPIO 控制 | 中断管理 | 定时器配置 上层调用下层接口,下层不依赖上层实现

这个架构的好处是——你写测试用例的时候,根本不用关心底层寄存器怎么配。我见过很多团队把测试代码和驱动代码混在一起,结果换了个芯片,全部重写。你说这得多折腾?

1.4 Python 与 C 混合编程环境配置

做验证测试,C 语言跑在目标板上,Python 跑在 PC 上。Python 负责发指令、收数据、做分析;C 负责执行底层操作。两者通过串口或网络通信。

我推荐的环境配置如下:

  • PC 端:Python 3.8+,安装 pyserial、numpy、matplotlib 用于数据分析和可视化
  • 目标板:C 语言(GCC 或 ARMCC),实现底层驱动和通信协议
  • 通信桥梁:UART 转 USB(如 CH340、FT232),波特率 115200 或更高

核心思路:Python 发命令字符串(如 "SPI_READ 0x10 4"),C 端解析后执行,返回结果。这样你可以在 Python 里写循环、做统计、画波形——C 端只管干活。

下面是一个简单的通信协议示例:

// C 端(目标板)命令解析框架
void process_command(char *cmd) {
    if (strncmp(cmd, "SPI_READ", 8) == 0) {
        uint8_t addr = (uint8_t)atoi(cmd + 9);
        uint8_t len  = (uint8_t)atoi(cmd + 13);
        spi_read(addr, buffer, len);
        uart_send(buffer, len);
    } else if (strncmp(cmd, "I2C_SCAN", 8) == 0) {
        i2c_scan_devices();
    }
}
# Python 端(PC)发送与接收
import serial
ser = serial.Serial('COM3', 115200, timeout=1)
ser.write(b'SPI_READ 0x10 4\n')
data = ser.read(4)
print(f"Received: {data.hex()}")

个人经验:Python 端一定要加超时处理。我遇到过目标板死机,Python 一直阻塞等待,整个测试流程卡死。加个 timeout=1,再配合重试机制,稳得很。

1.5 环境搭建检查清单

最后,给你一个检查清单。每次搭建新环境,按这个走一遍,基本不会漏东西:

  1. Python 环境:确认 pyserial 安装成功,import serial 不报错
  2. C 编译环境:确认交叉编译器路径正确,能编译一个空工程
  3. 串口通信:用串口助手测试 PC 和目标板能否收发数据
  4. 逻辑分析仪:连接 SPI/I2C 信号线,确认波形正确
  5. 电源:确认目标板供电稳定,尤其是 SPI 高速模式时

好了,这一章的内容就到这里。协议对比、平台架构、环境配置——这三块是后面所有章节的基础。你把这些搞扎实了,后面的调试才不会手忙脚乱。