核心传感器接口:I2C/SPI/UART协议对比、传感器寄存器读写、非均匀校正参数配置

做热成像系统,说白了就是跟传感器打交道。而传感器怎么跟你“说话”?全靠接口协议。今天咱们就聊聊最常用的三种:I2C、SPI、UART。别小看这些基础协议,我在项目里见过太多因为接口选型失误,导致整个系统推倒重来的案例。

一、三种协议,谁更适合热成像?

先看一张对比表,心里有个底:

特性 I2C SPI UART
信号线数量 2(SCL, SDA) 4(SCLK, MOSI, MISO, CS) 2(TX, RX)
通信方式 半双工 全双工 全双工
速率 100k~3.4Mbps 10M~80Mbps 115.2k~3Mbps
从机数量 多从机(地址寻址) 多从机(片选) 点对点
热成像适用场景 配置寄存器、读取校正参数 高速传输原始像素数据 调试日志、低带宽控制

我个人习惯是:配置用I2C,数据用SPI,调试用UART。为什么?你想想看,热成像传感器的原始数据量有多大?以常见的80×60分辨率、16位像素深度为例,一帧就是9600字节。如果帧率30Hz,每秒要传288KB。I2C的速率根本扛不住,而SPI轻松搞定。

核心原则: 寄存器读写用I2C(简单、线少),像素数据流用SPI(高速、可靠),调试信息用UART(方便、通用)。

二、传感器寄存器读写实战

不管用哪种协议,最终都要落到寄存器读写上。热成像传感器内部通常有几十到几百个寄存器,控制着增益、偏置、帧率、工作模式等。

2.1 I2C读写示例(以MLX90640为例)

我记得第一次调MLX90640时,卡在寄存器地址上半天。它的寄存器是16位地址、16位数据。看代码:

// I2C写寄存器:addr为16位寄存器地址,data为16位数据
int mlx_write_reg(uint16_t addr, uint16_t data) {
    uint8_t buf[4];
    buf[0] = (addr >> 8) & 0xFF;  // 高字节
    buf[1] = addr & 0xFF;         // 低字节
    buf[2] = (data >> 8) & 0xFF; // 数据高字节
    buf[3] = data & 0xFF;         // 数据低字节
    return i2c_write(MLX_ADDR, buf, 4);
}

// I2C读寄存器
uint16_t mlx_read_reg(uint16_t addr) {
    uint8_t wbuf[2] = {(addr >> 8) & 0xFF, addr & 0xFF};
    uint8_t rbuf[2];
    i2c_write(MLX_ADDR, wbuf, 2);  // 先写地址
    i2c_read(MLX_ADDR, rbuf, 2);   // 再读数据
    return ((uint16_t)rbuf[0] << 8) | rbuf[1];
}
避坑指南: 我曾经犯过一个低级错误——读寄存器时忘了先写地址。I2C从机需要你先告诉它“我要读哪个地址”,然后才能读数据。很多新手会直接调用读函数,结果读回来的全是0xFF。记住:写地址 + 读数据,两步缺一不可。

2.2 SPI高速读取像素数据

SPI读取通常是一次性读完一整帧。以Lepton 3.5为例,它的SPI读取时序是这样的:

// SPI读取一帧数据(164行 × 每行164像素 × 2字节)
void lepton_read_frame(uint16_t *buffer) {
    uint8_t tx_dummy = 0x00;
    uint8_t rx_byte;
    
    // 拉低片选
    CS_LOW();
    
    for(int i = 0; i < 164*164*2; i++) {
        spi_transfer(&tx_dummy, &rx_byte, 1);
        // 注意:Lepton的数据是MSB先传,大端模式
        if(i % 2 == 0) {
            buffer[i/2] = ((uint16_t)rx_byte) << 8;
        } else {
            buffer[i/2] |= rx_byte;
        }
    }
    
    // 拉高片选
    CS_HIGH();
}

这里有个细节:SPI时钟极性(CPOL)和相位(CPHA)必须跟传感器匹配。Lepton 3.5要求CPOL=0、CPHA=0,也就是模式0。我见过有人用模式3去读,读回来的数据全是乱的。

三、非均匀校正参数配置

热成像传感器有个“通病”——每个像素的响应都不完全一样。有的像素对温度敏感,有的迟钝。这就叫非均匀性。不做校正,你看到的图像就是花屏。

校正参数通常存储在传感器的EEPROM里,上电后需要读出来,然后配置到对应的寄存器中。流程如下:

// 非均匀校正参数加载流程
void load_nuc_params() {
    uint16_t gain[64*64];   // 增益校正系数
    uint16_t offset[64*64]; // 偏移校正系数
    uint16_t bad_pixel[64]; // 坏点列表
    
    // 1. 从EEPROM读取校正参数(I2C读取)
    for(int i = 0; i < 64*64; i++) {
        gain[i] = mlx_read_reg(EEPROM_BASE + i*2);
        offset[i] = mlx_read_reg(EEPROM_BASE + 0x2000 + i*2);
    }
    
    // 2. 写入传感器内部寄存器
    for(int i = 0; i < 64*64; i++) {
        mlx_write_reg(GAIN_REG_BASE + i*2, gain[i]);
        mlx_write_reg(OFFSET_REG_BASE + i*2, offset[i]);
    }
    
    // 3. 配置坏点替换(用相邻像素平均值替代)
    for(int i = 0; i < 64; i++) {
        if(bad_pixel[i] != 0xFFFF) {
            mlx_write_reg(BAD_PIXEL_REG, bad_pixel[i]);
        }
    }
}
注意: 校正参数是每个传感器独有的,不能通用!我曾经图省事,把A传感器的参数直接写到B传感器上,结果图像出现大面积条纹。每个传感器出厂时都会做标定,参数存储在内部EEPROM里。一定要从自己的传感器读取,不要复用。

四、知识体系结构图

下面这张图,帮你理清本章的核心逻辑:

热成像传感器接口与配置知识体系 热成像传感器 I2C 协议 SPI 协议 UART 协议 寄存器读写 像素数据流 调试日志 配置增益/偏置/帧率 读取原始像素数据 打印传感器状态 非均匀校正参数配置

从图中可以看出,整个流程是自上而下的:传感器通过三种接口协议,分别完成寄存器配置、数据读取和调试输出,最终汇聚到非均匀校正这个核心环节。

五、实战中的几个坑

  1. I2C上拉电阻不能省:SCL和SDA必须接上拉电阻,典型值4.7kΩ。我见过有人直接用MCU的GPIO开漏输出,没接上拉,结果通信时好时坏。
  2. SPI片选时序要严格:有些传感器要求片选拉低后等几个微秒再开始传时钟。Lepton 3.5就要求至少5μs的建立时间。
  3. UART波特率要匹配:热成像传感器的UART通常固定波特率,比如921600。别想当然用115200,否则收到的全是乱码。
  4. 校正参数要校验:EEPROM里的参数可能损坏。我建议读出来后做个CRC校验,如果校验失败,就用默认参数或者重新标定。
我的习惯: 每次上电后,先读传感器的ID寄存器,确认通信正常,再加载校正参数。如果ID读不对,直接报错,不要继续往下走。这能省下大量调试时间。

好了,接口协议和寄存器读写就聊到这儿。非均匀校正参数配置是热成像系统的“灵魂”,参数不对,图像全废。下一节咱们会深入聊聊图像处理流水线,到时候你会看到这些参数是怎么影响最终画面的。


专注资料整理