4. 通信协议基础:I2C、SPI、UART协议详解

好,咱们进入正题。光照传感器和主控之间怎么「说话」?说白了,就是靠通信协议。我见过不少新手,传感器买回来,线一接,发现读不到数据,急得团团转。其实啊,多半是协议没搞对。

今天咱们就掰开揉碎,把 I2C、SPI、UART 这三个最常用的协议讲清楚。你想想看,搞懂了它们,光照传感器和主控之间的通信,基本就稳了。

4.1 为什么需要通信协议?

传感器采集到的是模拟信号,比如光敏电阻的电压变化。但主控芯片(比如 ESP32、STM32)只认数字信号——0 和 1。所以,中间需要一个「翻译官」,把模拟量转成数字量,再按照约定好的规则传过去。

这个「约定好的规则」,就是通信协议。它规定了:

  • 数据怎么开始、怎么结束
  • 谁先说话、谁后说话
  • 数据格式是几位、校验怎么算

嗯,这里要注意:不同的传感器,支持的协议不一样。买之前一定要看数据手册,别买回来才发现不匹配。

4.2 I2C 协议详解

I2C 是我个人最喜欢用的协议之一。为什么?因为它只需要两根线:SDA(数据线)和 SCL(时钟线)。你想想看,两根线就能挂一堆传感器,多省事。

我在项目中遇到过,一个 ESP32 上挂了 5 个 I2C 传感器:光照、温湿度、气压、距离、还有一个人体红外。只要地址不冲突,一根总线全搞定。

4.2.1 I2C 的工作原理

I2C 是主从结构。主控是「主」,传感器是「从」。主控负责发时钟信号,从设备只能被动响应。

  • 起始条件:SCL 高电平时,SDA 从高变低
  • 停止条件:SCL 高电平时,SDA 从低变高
  • 数据传输:每个字节 8 位,后面跟一个应答位(ACK)
  • 地址:每个从设备有唯一 7 位或 10 位地址

重要:I2C 的速率有标准模式(100kHz)、快速模式(400kHz)和高速模式(3.4MHz)。光照传感器一般用 100kHz 或 400kHz 就够了。

4.2.2 光照传感器的 I2C 通信示例

以 BH1750 光照传感器为例,它的 I2C 地址是 0x23(ADDR 引脚接地时)。

// 伪代码示例:读取 BH1750 光照值
I2C_Start();                    // 发送起始条件
I2C_SendByte(0x46);             // 地址 0x23 + 写位 (0x23 << 1 | 0)
I2C_WaitAck();                  // 等待应答
I2C_SendByte(0x10);             // 发送命令:连续高分辨率模式
I2C_WaitAck();
I2C_Stop();                     // 发送停止条件

delay(120);                     // 等待测量完成(约120ms)

I2C_Start();
I2C_SendByte(0x47);             // 地址 0x23 + 读位 (0x23 << 1 | 1)
I2C_WaitAck();
high_byte = I2C_ReadByte();     // 读取高字节
I2C_SendAck(0);                 // 发送应答
low_byte = I2C_ReadByte();      // 读取低字节
I2C_SendAck(1);                 // 发送非应答,表示结束
I2C_Stop();

lux = (high_byte << 8) | low_byte;  // 合并为16位数据
lux = lux / 1.2;                // 根据手册换算为勒克斯

我的经验:BH1750 的测量时间不能太短。我曾经为了省时间,只等了 100ms,结果读出来的数据全是乱的。后来老老实实等了 120ms,数据就稳了。有些坑,踩过才知道。

4.3 SPI 协议详解

SPI 比 I2C 快,但线也多。它需要四根线:MOSI(主出从入)、MISO(主入从出)、SCLK(时钟)、CS(片选)。

你想想看,SPI 是全双工的——主控发数据的同时也能收数据。这一点比 I2C 强。但代价就是占用的引脚多。

4.3.1 SPI 的工作模式

SPI 有四种模式,由时钟极性(CPOL)和时钟相位(CPHA)决定:

模式 CPOL CPHA 说明
模式 0 0 0 空闲时时钟低电平,第一个边沿采样
模式 1 0 1 空闲时时钟低电平,第二个边沿采样
模式 2 1 0 空闲时时钟高电平,第一个边沿采样
模式 3 1 1 空闲时时钟高电平,第二个边沿采样

避坑指南:我曾经在项目里用 SPI 连接一个光照传感器,结果死活读不到数据。查了半天,发现是 SPI 模式没对上。传感器要求模式 1,我默认用了模式 0。所以,一定要先看数据手册里的 SPI 模式说明。

4.3.2 SPI 通信示例

以 TSL2591 光照传感器为例,它支持 SPI 接口。

// 伪代码示例:读取 TSL2591 的 ID 寄存器
CS_LOW();                       // 拉低片选,选中设备
SPI_TransferByte(0x12);         // 发送寄存器地址(读命令)
device_id = SPI_TransferByte(0x00);  // 发送任意数据,同时读取返回数据
CS_HIGH();                      // 拉高片选,释放设备

if (device_id == 0x50) {
    // 设备正常
} else {
    // 设备异常,检查接线或地址
}

我的建议:SPI 的速率可以很高,但光照传感器一般不需要太快。我通常把 SPI 时钟设在 1MHz 左右,既稳定又够用。速率太高反而容易受干扰。

4.4 UART 协议详解

UART 是最简单的协议,只需要两根线:TX(发送)和 RX(接收)。但它是一对一的,不能像 I2C 那样挂多个设备。

UART 是异步的,没有时钟线。所以通信双方必须约定好相同的波特率——说白了就是每秒传多少位。常见的波特率有 9600、115200 等。

4.4.1 UART 的数据帧格式

一个 UART 数据帧包含:

  • 起始位:1 位,固定为低电平
  • 数据位:5~9 位,常用 8 位
  • 校验位:可选,奇校验或偶校验
  • 停止位:1 位或 2 位,固定为高电平

4.4.2 光照传感器的 UART 通信示例

有些光照传感器(比如一些串口输出的模块)直接用 UART 输出数据。

// 伪代码示例:读取 UART 光照传感器数据
// 假设传感器每秒输出一次数据,格式:$LUX,1234.5\r\n

char buffer[32];
int index = 0;

while (1) {
    char c = UART_ReadByte();   // 读取一个字节
    if (c == '\n') {            // 遇到换行符,表示一帧结束
        buffer[index] = '\0';   // 字符串结束符
        if (strncmp(buffer, "$LUX,", 5) == 0) {
            float lux = atof(&buffer[5]);  // 提取光照值
            // 处理光照值
        }
        index = 0;
    } else {
        buffer[index++] = c;
    }
}

避坑指南:UART 通信最容易出问题的是波特率不匹配。我曾经调试一个传感器,数据全是乱码,折腾了半小时才发现传感器默认是 9600,而我代码里配的是 115200。所以,上电后先确认波特率。

4.5 如何选择通信协议?

好,三种协议都讲完了。你可能会问:那我到底该用哪个?

我个人习惯这样选:

  • I2C:适合挂多个传感器,引脚少,速度要求不高。光照传感器大多用 I2C。
  • SPI:适合高速数据传输,比如摄像头、显示屏。光照传感器用 SPI 有点大材小用。
  • UART:最简单,一对一,适合和电脑串口调试。但光照传感器用 UART 的较少。

我的建议:对于智能窗帘的光照传感器,我推荐用 I2C。原因很简单:引脚少、传感器选择多、代码库成熟。你想想看,一个 ESP32 用 I2C 挂一个 BH1750,总共就 4 根线(VCC、GND、SDA、SCL),多清爽。

4.6 建立通信的完整流程

最后,咱们总结一下光照传感器和主控建立通信的步骤:

  1. 确认传感器支持的协议:看数据手册,找到通信接口说明
  2. 硬件连接:按照协议要求接线,注意电平匹配(3.3V vs 5V)
  3. 初始化主控的通信外设:配置 I2C/SPI/UART 的引脚、速率、模式
  4. 发送命令:按照传感器手册,发送初始化命令或读取命令
  5. 读取数据:解析返回的数据,换算为实际物理量
  6. 验证:用手遮挡传感器,看数值是否变化

我的经验:第 6 步特别重要。我见过有人代码写完了,数据也读出来了,但数值一直不变。后来发现是传感器地址写错了,读的是另一个不存在的设备。所以,一定要用手挡一下传感器,确认数据有变化。

好了,通信协议这块就讲到这里。下一章咱们会讲具体的硬件选型和电路设计。到时候,我会把 BH1750 和 ESP32 的接线图、代码都贴出来,手把手带你调通。

记住一句话:协议是死的,人是活的。搞懂了原理,换什么传感器都不怕。