4、CPU侧控制逻辑:I2C/SPI驱动配置、寄存器读写、GPIO触发同步、电源管理序列

CPU 侧的控制逻辑,说白了就是嵌入式大脑怎么跟相机模组「对话」。我做了这么多年嵌入式视觉,发现很多新手把精力全放在图像算法上,结果板子一上电,传感器压根不工作——原因往往是 I2C 时序不对,或者电源序列搞反了。

这一章,我们就来拆解 CPU 侧最核心的四个控制动作:I2C/SPI 驱动配置、寄存器读写、GPIO 触发同步、电源管理序列。你想想看,相机模组本质上就是个复杂的模拟+数字混合器件,CPU 要让它干活,就得通过这几条「神经」去下指令。

4.1 I2C/SPI 驱动配置:选对总线,调好时序

相机传感器最常用的控制总线就两种:I2C 和 SPI。我个人的习惯是:帧率要求不高、引脚紧张的项目用 I2C;需要快速配置大量寄存器、或者传感器本身支持 SPI 时,优先用 SPI。

特性 I2C SPI
引脚数 2 根(SCL, SDA) 4 根(SCLK, MOSI, MISO, CS)
速度 标准 100kHz~400kHz,快速 1MHz 通常 10MHz~50MHz
寻址方式 7 位/10 位地址,多设备共用 片选 CS 独立,一主多从
典型场景 OV2640、MT9V034 等中低速传感器 AR0234、IMX 系列高速传感器
我的经验:I2C 上拉电阻别乱选。4.7kΩ 是通用值,但总线电容大时(比如线长超过 10cm),换成 2.2kΩ 能明显改善信号边沿。我曾经在一个项目里被 I2C 时钟拉伸坑了三天,后来发现是传感器从设备拉低时钟时,主设备没做超时处理。

4.1.1 I2C 驱动配置要点

配置 I2C 驱动时,我建议你关注这几个参数:

  • 时钟频率:不要超过传感器 datasheet 标称最大值。有些传感器对时序抖动敏感,频率设太高反而容易丢数据。
  • 从设备地址:注意 7 位地址和 8 位地址的区别。很多 Linux 驱动里用的是 7 位左移 1 位的格式,跟裸机驱动不一样。
  • 传输模式:大部分传感器用标准模式(SMBus 兼容),但有些需要重复起始条件(Restart)才能连续读写。
// 以 STM32 HAL 库为例,配置 I2C 句柄
I2C_HandleTypeDef hi2c1;
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 400000;       // 400kHz 快速模式
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
HAL_I2C_Init(&hi2c1);

4.1.2 SPI 驱动配置要点

SPI 配置比 I2C 稍微复杂一点,主要是极性和相位。我见过太多人 SPI 通信失败,就是因为 CPOL 和 CPHA 没配对。

  • CPOL(时钟极性):空闲时时钟是高电平还是低电平。
  • CPHA(时钟相位):数据是在第一个边沿采样,还是第二个边沿采样。
  • 数据位宽:大部分传感器用 8 位,但也有 16 位或 32 位模式。
避坑指南:我曾经在调试一款全局快门传感器时,SPI 读寄存器总是返回 0xFF。折腾了两天,最后发现是传感器要求 MSB 在前,而我的 SPI 控制器默认配置成了 LSB 在前。所以,务必确认传感器的 SPI 传输顺序。

4.2 寄存器读写:传感器的「控制面板」

相机传感器内部有几十到几百个寄存器,控制着曝光时间、增益、帧率、窗口大小、输出格式等等。CPU 通过 I2C/SPI 读写这些寄存器,就像在操作一个远程控制面板。

我个人的习惯是:把寄存器操作封装成两层。底层是总线读写函数,上层是传感器专用 API。这样换传感器时,只需要改上层,底层不用动。

// 底层:I2C 寄存器读写(以 16 位地址为例)
int sensor_write_reg(uint16_t reg_addr, uint8_t value) {
    uint8_t buf[3] = { (reg_addr >> 8) & 0xFF, reg_addr & 0xFF, value };
    return HAL_I2C_Master_Transmit(&hi2c1, SENSOR_ADDR, buf, 3, 100);
}

int sensor_read_reg(uint16_t reg_addr, uint8_t *value) {
    uint8_t addr_buf[2] = { (reg_addr >> 8) & 0xFF, reg_addr & 0xFF };
    HAL_I2C_Master_Transmit(&hi2c1, SENSOR_ADDR, addr_buf, 2, 100);
    return HAL_I2C_Master_Receive(&hi2c1, SENSOR_ADDR, value, 1, 100);
}

// 上层:设置曝光时间
void sensor_set_exposure(uint32_t exposure_lines) {
    sensor_write_reg(0x3501, (exposure_lines >> 8) & 0xFF);
    sensor_write_reg(0x3502, exposure_lines & 0xFF);
}
关键点:很多传感器在写入某些关键寄存器后,需要等待几个帧周期才能生效。比如修改曝光时间,通常要等到下一个 VSYNC 信号到来后才会加载新值。如果你连续写入多个寄存器,最好在每两次写入之间加一个短暂延时,或者等待 VSYNC 中断。

4.3 GPIO 触发同步:让多个传感器「齐步走」

在多目相机系统中,GPIO 触发同步是保证图像时间对齐的关键。我做过一个双目深度相机项目,两个传感器如果不同步,误差哪怕只有 1 毫秒,生成的深度图都会出现明显的边缘错位。

常用的同步方式有两种:

  • 硬件触发(Hardware Trigger):CPU 输出一个 GPIO 脉冲,传感器检测到上升沿或下降沿后开始曝光。这种方式延迟最小,抖动通常在微秒级。
  • 软件触发(Software Trigger):CPU 通过 I2C/SPI 写寄存器来触发曝光。这种方式简单,但延迟和抖动都比较大,不适合高精度同步。

我个人强烈建议:只要硬件支持,就用硬件触发。下面是一个典型的硬件触发时序:

// GPIO 触发脉冲生成(以 STM32 为例)
void trigger_sensor(void) {
    HAL_GPIO_WritePin(TRIGGER_GPIO_Port, TRIGGER_Pin, GPIO_PIN_SET);
    delay_us(10);  // 脉冲宽度 10 微秒,具体值看传感器要求
    HAL_GPIO_WritePin(TRIGGER_GPIO_Port, TRIGGER_Pin, GPIO_PIN_RESET);
}
我的经验:如果多个传感器共用同一个触发信号,要注意 GPIO 的驱动能力。我曾经用 STM32 的一个 GPIO 同时触发 4 个传感器,结果信号边沿变缓,触发抖动变大。后来加了一个 74HC14 施密特触发器做缓冲,问题就解决了。

4.4 电源管理序列:上电下电,顺序不能乱

相机传感器对电源序列非常敏感。上电时,如果核心电压(VDD)先于 I/O 电压(VDDIO)到达,或者模拟电压(AVDD)跟数字电压(DVDD)时序不对,轻则传感器不工作,重则直接烧毁。

我见过最惨的一次:一个同事把传感器上电顺序搞反了,结果芯片内部 ESD 保护二极管导通,大电流灌进去,传感器当场冒烟。嗯,从那以后,我每次画板子都会在电源序列上多花一倍时间。

典型的电源序列要求如下(以某款 500 万像素传感器为例):

步骤 动作 延时要求 说明
1 VDDIO(1.8V)上电 0ms I/O 电源先稳定
2 AVDD(3.3V)上电 ≥ 1ms 模拟电源随后
3 DVDD(1.2V)上电 ≥ 1ms 数字电源最后
4 MCLK 提供时钟 ≥ 5ms 所有电源稳定后
5 复位释放(XSHUT 拉高) ≥ 10ms 时钟稳定后
6 I2C/SPI 初始化 ≥ 1ms 复位释放后
警告:下电顺序通常与上电顺序相反。先关 DVDD,再关 AVDD,最后关 VDDIO。如果下电顺序不对,传感器内部的电荷可能无法正常泄放,导致下次上电时出现异常。我建议在电源管理芯片上选择支持可编程时序的型号,比如 TPS65023 或类似 PMIC。

4.5 知识体系总览

下面这张图把 CPU 侧控制逻辑的四个核心模块串起来了。你可以把它当作一个检查清单:做新项目时,对着这张图一项一项确认,基本不会漏东西。

CPU侧控制逻辑知识体系 CPU侧控制逻辑知识体系 CPU 主控 I2C/SPI 驱动配置 时钟频率 / 极性 / 地址 寄存器读写 封装层 / 延时等待 GPIO 触发同步 硬件触发 / 软件触发 电源管理序列 VDDIO → AVDD → DVDD → MCLK → 复位 → 初始化 💡 核心原则 1. 先配总线,再读写寄存器,最后触发同步。 2. 电源序列是硬约束,时序错了传感器可能直接损坏。 3. 多传感器同步时,硬件触发比软件触发可靠得多。

好了,这一章的内容就是这些。从 I2C/SPI 驱动配置,到寄存器读写,再到 GPIO 触发同步和电源管理序列,每一步都是 CPU 跟相机传感器「沟通」的关键环节。你在实际项目中遇到问题时,不妨回头看看这张图,大概率能找到排查方向。