2. 开发环境与工具链搭建

做嵌入式这些年,我接触过的传感器少说也有上百种。选接口这件事,说白了就是选通信方式。每种接口都有自己的脾气,选对了事半功倍,选错了……嗯,我见过不少项目因为接口选型翻车,最后改板子改到崩溃。

这一章咱们就把五种最常见的传感器接口掰开揉碎讲清楚。我会结合自己的项目经验,告诉你每种接口什么时候该用,什么时候千万别用。

接口特性对比:一张表看懂全局

先给个全景图。这张表我整理了好几年,每次选型都会拿出来对照一下。

接口 通信方式 引脚数 速度 功耗 典型传感器
I2C 同步、半双工 2 (SCL, SDA) 100kHz~3.4MHz 低 温湿度、气压计
SPI 同步、全双工 4 (SCK, MOSI, MISO, CS) 最高 80MHz 中 加速度计、陀螺仪
UART 异步、全双工 2 (TX, RX) 最高 10Mbps 中 GPS、蓝牙模块
GPIO 并行、电平 1~N 取决于MCU 极低 按键、中断触发
ADC 模拟量 1 采样率决定 低~中 光敏、电位器

核心原则:接口选型不是越快越好,而是匹配就好。低功耗场景下,I2C 往往是首选,因为它的总线空闲时几乎不耗电。

I2C 接口详解

I2C 是我用得最多的接口。为什么?两根线就能挂一堆传感器,省引脚啊!

时序要求

I2C 的时序其实不复杂。SCL 时钟线,SDA 数据线。起始条件:SCL 高电平时 SDA 从高变低。停止条件:SCL 高电平时 SDA 从低变高。就这么简单。

但有个坑——上拉电阻。我刚开始做项目时,I2C 死活不通,查了半天发现是上拉电阻没焊。标准值是 4.7kΩ,但如果你总线电容大,得换成 2.2kΩ。

驱动开发要点

// I2C 读取温湿度传感器示例
uint8_t i2c_read_byte(uint8_t dev_addr, uint8_t reg_addr) {
    uint8_t data;
    i2c_start();
    i2c_write(dev_addr << 1 | 0);  // 写地址
    i2c_write(reg_addr);            // 写寄存器
    i2c_stop();
    i2c_start();
    i2c_write(dev_addr << 1 | 1);  // 读地址
    data = i2c_read_nack();
    i2c_stop();
    return data;
}

我的经验:I2C 驱动里一定要加超时处理。我曾经遇到过传感器死锁,总线一直拉低,整个系统卡死。加个 100ms 超时,遇到问题就复位总线,稳得很。

SPI 接口详解

SPI 比 I2C 快得多,但引脚也多。如果你需要高速采集加速度计数据,SPI 是首选。

时序要求

SPI 有四种模式,由时钟极性(CPOL)和时钟相位(CPHA)决定。大多数传感器用模式 0 或模式 3。我习惯用模式 0:空闲时 SCK 低电平,第一个边沿采样。

这里有个容易踩的坑——片选信号。CS 必须在传输开始前拉低,结束后拉高。有些传感器对 CS 的时序要求很严格,拉高太快会丢数据。

驱动开发要点

// SPI 读取加速度计示例
uint16_t spi_read_accel(uint8_t reg) {
    uint8_t tx_buf[2] = {reg | 0x80, 0x00};  // 读命令
    uint8_t rx_buf[2] = {0};
    CS_LOW();
    spi_transfer(tx_buf, rx_buf, 2);
    CS_HIGH();
    return (rx_buf[0] << 8) | rx_buf[1];
}

注意:SPI 的从设备不能主动发起通信。如果你需要传感器主动上报数据,得用中断引脚配合。我做过一个项目,加速度计检测到震动后通过 GPIO 中断唤醒 MCU,再用 SPI 读取数据,这样功耗控制得很好。

UART 接口详解

UART 是异步通信,不需要时钟线。但正因为异步,双方得约定好波特率。

时序要求

UART 的时序就是波特率。常见的 9600、115200。但要注意,波特率误差不能超过 2%。MCU 的时钟精度不够时,高速通信容易出错。

我记得有一次用内部 RC 振荡器跑 115200,结果丢包率 30%。换成外部晶振就好了。

驱动开发要点

// UART 接收一帧数据
void uart_rx_handler(uint8_t byte) {
    static uint8_t buffer[64];
    static uint8_t index = 0;
    buffer[index++] = byte;
    if (byte == '\n') {
        process_frame(buffer, index);
        index = 0;
    }
}

避坑指南:UART 的接收最好用中断或 DMA,别用轮询。轮询会浪费大量 CPU 时间,在低功耗场景下尤其致命。

GPIO 与 ADC 接口

这两个接口虽然简单,但用好了能解决大问题。

GPIO 的妙用

GPIO 不只是用来点灯的。它可以做中断触发、模拟时序、甚至模拟 I2C。我做过一个项目,MCU 引脚不够用,就用两个 GPIO 模拟 I2C 挂传感器,虽然慢了点,但功能完全正常。

ADC 的注意事项

ADC 采集模拟信号,精度取决于参考电压和分辨率。12 位 ADC 的理论精度是 4096 级,但实际能到 10 位就不错了。

为什么?噪声。我遇到过 ADC 采集值跳来跳去,最后发现是电源纹波太大。加个 100nF 电容在传感器电源脚上,问题解决。

// ADC 采集光敏电阻示例
uint16_t adc_read_light(void) {
    ADC_StartConversion();
    while(!ADC_IsConversionDone());
    return ADC_GetResult();
}

常见传感器的接口选择

讲完接口,咱们看看实际传感器怎么选。

传感器类型 推荐接口 理由
温湿度 (SHT30, DHT22) I2C 数据量小,功耗低,两根线搞定
加速度计 (MPU6050, LIS3DH) SPI / I2C 高速采集用 SPI,低功耗用 I2C
气压计 (BMP280, MS5611) I2C 数据更新慢,I2C 足够
光敏电阻 ADC 模拟量输出,直接 ADC 采集
GPS 模块 UART 数据量大,异步通信方便

我的建议:如果传感器同时支持 I2C 和 SPI,优先选 I2C。除非你需要高速采集(比如加速度计 1kHz 以上采样率),否则 I2C 的功耗和引脚优势更明显。

接口选择决策流程

下面这张图是我自己总结的接口选型流程,每次做新项目都会走一遍。

传感器接口选型决策流程 传感器选型开始 模拟/数字? ADC 数字接口 高速? SPI 引脚够? I2C UART 选型要点 • 模拟信号 → ADC(光敏、电位器、麦克风) • 数字信号 → 高速选 SPI,低速选 I2C,引脚不够选 UART

实战中的避坑指南

最后分享几个我踩过的坑,希望能帮你少走弯路。

  • I2C 总线电容:线太长或者挂的设备太多,总线电容会变大,波形变差。我建议总线上挂的设备不要超过 8 个,线长控制在 30cm 以内。
  • SPI 的 MISO 冲突:多个 SPI 设备共用总线时,一定要确保片选信号正确。我曾经因为 CS 没拉高,两个设备同时抢 MISO,数据全乱了。
  • UART 的波特率误差:用内部振荡器时,别跑 115200 以上。我吃过亏,后来一律用外部晶振。
  • ADC 的参考电压:参考电压一定要稳定。我用过 MCU 内部参考,结果温度一变,采集值就飘。后来换成外部基准,稳如老狗。

重要提醒:低功耗场景下,传感器在不工作时一定要进入休眠模式。很多传感器都有休眠引脚或者寄存器配置。我见过有人让传感器一直工作,电池两天就耗光了。