二、硬件触发同步基础:GPIO中断与外部触发信号设计

各位同学,今天我们聊一个硬核话题——硬件触发同步。

说白了,就是让多个摄像头在同一时刻按下快门。你想想看,如果每个摄像头各自为政,拍出来的画面时间戳对不上,那后面的图像拼接、3D重建全得崩。我在项目中遇到过最惨的一次,四个摄像头拍运动物体,结果时间差最大到了50毫秒,画面里的小球位置差了半个身位——嗯,那感觉就像看了一场音画不同步的电影。

2.1 为什么需要硬件触发?

软件触发不行吗?当然可以,但精度不够。

我习惯把触发方式分成三个等级:

  • 软件触发:通过UART、TCP/IP发指令。延迟在毫秒到几十毫秒级别,不可控。
  • 硬件触发(电平触发):用GPIO高低电平控制。延迟在微秒级,但容易受噪声干扰。
  • 硬件触发(边沿触发):用上升沿或下降沿触发。延迟在纳秒级,稳定可靠。

多摄像头同步,至少要用边沿触发。我个人的经验是,如果摄像头数量超过3个,直接上差分信号(比如RS-422或LVDS),别用单端TTL——长线传输时信号反射会让你怀疑人生。

核心结论:硬件触发同步的精度,决定了多摄像头系统能跑多快、拍多准。这是整个架构的基石。

2.2 GPIO中断的工作原理

GPIO中断,说白了就是让CPU在检测到引脚电平变化时,暂停当前任务,去执行一段紧急代码。

以STM32为例,它的EXTI(外部中断/事件控制器)可以配置为:

  • 上升沿触发
  • 下降沿触发
  • 双边沿触发
  • 软件触发

我建议你使用上升沿触发。为什么?因为下降沿触发在信号线从高到低跳变时,容易受到地弹噪声的影响。我曾经在一个电机驱动项目里吃过这个亏——电机一启动,下降沿误触发了一堆中断,系统直接卡死。

来看一段STM32的GPIO中断配置代码:

// STM32 HAL库 - GPIO外部中断配置示例
void EXTI_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    // 1. 使能GPIO时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_SYSCFG_CLK_ENABLE();

    // 2. 配置PA0为输入模式,上拉
    GPIO_InitStruct.Pin = GPIO_PIN_0;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;  // 上升沿触发
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    // 3. 配置中断优先级
    HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}

// 中断服务函数
void EXTI0_IRQHandler(void)
{
    // 检查中断标志位
    if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0) != RESET)
    {
        // 触发摄像头采集
        camera_trigger_all();

        // 清除中断标志位
        __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);
    }
}

个人技巧:中断服务函数里尽量只做标志位设置,不要做耗时操作。我习惯在中断里只置一个volatile变量,然后在主循环里轮询处理。这样能避免中断嵌套导致的时序混乱。

2.3 外部触发信号设计要点

触发信号的设计,其实就三个问题:电压、时序、抗干扰。

2.3.1 电压匹配

STM32的GPIO是3.3V逻辑,树莓派也是3.3V。但有些工业相机用的是5V或12V触发信号。这时候就需要电平转换。

我常用的方案:

场景 推荐方案 注意事项
3.3V ↔ 3.3V 直连 线长不超过30cm
3.3V ↔ 5V 电平转换模块(如TXS0108) 注意方向控制
3.3V ↔ 12V 光耦隔离(如PC817) 速度受限,最高约100kHz
长距离(>5m) RS-422差分传输 抗干扰能力强

避坑指南:我曾经直接用3.3V GPIO去驱动一个5V输入的工业相机,结果相机死活不触发。后来发现是电压阈值不够——5V TTL的高电平阈值是2.0V,但3.3V输出经过长线衰减后只剩2.8V,刚好卡在临界区。从那以后,我所有跨电压的触发信号都加了一级施密特触发器(比如74HC14)做整形。

2.3.2 时序要求

触发信号的脉宽、频率、抖动,都有讲究。

  • 最小脉宽:一般工业相机要求触发信号高电平至少持续50μs。我习惯留余量,设到200μs。
  • 触发频率:取决于相机的帧率。比如30fps的相机,触发周期就是33.3ms。
  • 抖动(Jitter):GPIO中断的响应时间受CPU负载影响。我实测过,STM32F4在72MHz主频下,中断响应抖动约±3个时钟周期,也就是约±42ns。这个精度对于大多数视觉应用足够了。

来看一个树莓派生成触发信号的Python代码:

# 树莓派 - 生成精确的触发脉冲
import RPi.GPIO as GPIO
import time

TRIGGER_PIN = 18
PULSE_WIDTH_US = 200  # 200微秒脉宽

GPIO.setmode(GPIO.BCM)
GPIO.setup(TRIGGER_PIN, GPIO.OUT)
GPIO.output(TRIGGER_PIN, GPIO.LOW)

def send_trigger():
    """发送一个触发脉冲"""
    GPIO.output(TRIGGER_PIN, GPIO.HIGH)
    # 注意:time.sleep()精度有限,高精度场景用硬件PWM
    time.sleep(PULSE_WIDTH_US / 1_000_000)
    GPIO.output(TRIGGER_PIN, GPIO.LOW)

# 以30fps连续触发
try:
    while True:
        send_trigger()
        time.sleep(1/30)  # 33.3ms
except KeyboardInterrupt:
    GPIO.cleanup()

经验之谈:树莓派的GPIO输出精度其实一般。如果你需要纳秒级的脉冲控制,我建议用STM32的定时器PWM模式来生成触发信号。树莓派只负责发指令,STM32负责精确执行——这种主从架构我在多个项目中验证过,非常稳定。

2.4 STM32与树莓派的硬件接线方案

终于到了大家最关心的部分——怎么把STM32和树莓派连起来?

我推荐两种方案,根据你的场景选择:

方案一:树莓派做主控,STM32做触发从机

适合树莓派已经承担了图像处理任务,需要STM32来精确控制多个摄像头触发的情况。

接线方式:

树莓派 GPIO17 (输出)  →  STM32 PA0 (EXTI0输入)
树莓派 GND             →  STM32 GND
STM32 PB0 (触发输出1)  →  摄像头1 触发输入
STM32 PB1 (触发输出2)  →  摄像头2 触发输入
STM32 PB2 (触发输出3)  →  摄像头3 触发输入

工作流程:

  1. 树莓派通过GPIO17发送一个上升沿
  2. STM32检测到中断,进入EXTI0_IRQHandler
  3. STM32在中断中同时拉高PB0、PB1、PB2
  4. 三个摄像头同时开始曝光

这个方案的好处是,STM32可以保证多个触发输出之间的延迟在纳秒级别。我在项目中实测过,三个输出之间的偏差小于10ns——这比任何软件方案都靠谱。

方案二:STM32做主控,树莓派做图像处理

适合需要高帧率、低延迟的场景。STM32负责所有时序控制,树莓派只负责接收图像数据。

接线方式:

STM32 PA0 (触发输出)   →  摄像头1 触发输入
STM32 PA1 (触发输出)   →  摄像头2 触发输入
STM32 PA2 (触发输出)   →  摄像头3 触发输入
STM32 USART1_TX       →  树莓派 UART_RX (状态通知)
树莓派 UART_TX        →  STM32 USART1_RX (参数配置)

这个方案我更喜欢。为什么?因为STM32的定时器可以做到硬件级别的精确触发,不受操作系统调度影响。树莓派跑的是Linux,哪怕你用了实时内核,中断响应也有几十微秒的不可控抖动。

我的建议:如果摄像头数量超过2个,或者要求帧率超过60fps,直接上方案二。别犹豫。

2.5 实际项目中的避坑指南

最后,分享几个我踩过的坑:

  1. 共地问题:所有设备的GND必须连在一起。我曾经偷懒没接共地,结果触发信号飘了2V,摄像头随机触发。嗯,查了整整一天。
  2. 上拉电阻:GPIO输入引脚默认可能是浮空状态。我习惯在外部加一个10kΩ上拉电阻到3.3V,防止引脚悬空时误触发。
  3. 去耦电容:在触发信号线上靠近STM32引脚处加一个100pF电容到GND。可以滤除高频噪声,但注意电容太大会导致边沿变缓,影响触发精度。
  4. 线缆选择:触发信号线用双绞线,和电源线分开走。我见过有人把触发线和24V电源线绑在一起,结果触发信号上感应出了5V的尖峰——摄像头直接烧了输入口。

好了,这一章的内容就到这里。下一章我们会聊如何用硬件触发信号来同步多个摄像头的曝光时间,以及如何测量和校准同步精度。到时候我会分享一个我亲手调过的四目同步系统案例,保证干货满满。