核心传感器接口: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轻松搞定。
二、传感器寄存器读写实战
不管用哪种协议,最终都要落到寄存器读写上。热成像传感器内部通常有几十到几百个寄存器,控制着增益、偏置、帧率、工作模式等。
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];
}
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]);
}
}
}
四、知识体系结构图
下面这张图,帮你理清本章的核心逻辑:
从图中可以看出,整个流程是自上而下的:传感器通过三种接口协议,分别完成寄存器配置、数据读取和调试输出,最终汇聚到非均匀校正这个核心环节。
五、实战中的几个坑
- I2C上拉电阻不能省:SCL和SDA必须接上拉电阻,典型值4.7kΩ。我见过有人直接用MCU的GPIO开漏输出,没接上拉,结果通信时好时坏。
- SPI片选时序要严格:有些传感器要求片选拉低后等几个微秒再开始传时钟。Lepton 3.5就要求至少5μs的建立时间。
- UART波特率要匹配:热成像传感器的UART通常固定波特率,比如921600。别想当然用115200,否则收到的全是乱码。
- 校正参数要校验:EEPROM里的参数可能损坏。我建议读出来后做个CRC校验,如果校验失败,就用默认参数或者重新标定。
好了,接口协议和寄存器读写就聊到这儿。非均匀校正参数配置是热成像系统的“灵魂”,参数不对,图像全废。下一节咱们会深入聊聊图像处理流水线,到时候你会看到这些参数是怎么影响最终画面的。