引言

在嵌入式视觉系统中,STM32F4 的 DCMI(Digital Camera Interface)与 OV5640 传感器组合广泛使用。然而,开发者常遇到帧同步丢失(Frame Sync Loss)问题,表现为图像撕裂、花屏或采集中断。究其原因,多为 DCMI 与传感器之间的时序失配,或外部干扰导致 VSYNC/HSYNC 信号抖动。本文提出一种硬件中断驱动的时序补偿方案,利用 STM32 的 EXTI 捕获同步信号,动态调整 DCMI 启动时机,从而根治同步丢失。

原理分析

DCMI 同步机制

DCMI 依赖 VSYNC(帧同步)和 HSYNC(行同步)信号来界定数据有效性。在硬件模式下,DCMI 自动检测这些信号,但若信号出现毛刺或相位偏移,内部状态机可能误判,导致帧丢失。OV5640 默认输出 VSYNC 高电平有效,HSYNC 低电平有效,且像素时钟 PCLK 与数据同步。

同步丢失根因

  • 信号抖动:电源噪声或 PCB 布线不当导致 VSYNC/HSYNC 边沿抖动,DCMI 采样点偏移。
  • 帧率波动:OV5640 在自动曝光调整时,帧周期变化,而 DCMI 配置的帧大小固定,造成失配。
  • DMA 延迟:DCMI 数据写入 DMA 时,若带宽不足,FIFO 溢出,导致同步错误。

补偿方案思路

利用 STM32 的 EXTI 引脚连接 VSYNC 和 HSYNC,在中断服务函数中记录时间戳,并计算实际帧周期。若检测到周期偏差,则调整 DCMI 的 CAPTURE 位或重启采集,同时可微调 PCLK 分频。此方案实时性强,不占用 CPU 轮询资源。

硬件配置

  • MCU:STM32F407VET6(或类似 F4 系列)
  • 摄像头:OV5640,DCMI 接口,8 位数据线
  • 连接
    • DCMI_VSYNC -> PA4(EXTI4)
    • DCMI_HSYNC -> PA6(EXTI6)
    • DCMI_PCLK -> PA8(定时器输入捕获可选)
    • DCMI_D0-D7 -> PB8-PB15

配置步骤

1. GPIO 与 EXTI 初始化

将 VSYNC 和 HSYNC 引脚配置为上升沿/下降沿触发中断,并启用 EXTI。

void EXTI_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    EXTI_InitTypeDef EXTI_InitStruct = {0};

    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_SYSCFG_CLK_ENABLE();

    GPIO_InitStruct.Pin = GPIO_PIN_4 | GPIO_PIN_6;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; // VSYNC 下降沿,HSYNC 下降沿
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    EXTI_InitStruct.Line = EXTI_LINE_4 | EXTI_LINE_6;
    EXTI_InitStruct.Mode = EXTI_MODE_INTERRUPT;
    EXTI_InitStruct.Trigger = EXTI_TRIGGER_FALLING;
    HAL_EXTI_Init(&EXTI_InitStruct);

    HAL_NVIC_SetPriority(EXTI4_IRQn, 2, 0);
    HAL_NVIC_EnableIRQ(EXTI4_IRQn);
    HAL_NVIC_SetPriority(EXTI9_5_IRQn, 2, 0);
    HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
}

2. DCMI 初始化

配置 DCMI 为硬件同步,设置帧大小和捕获模式。

void DCMI_Init(void) {
    DCMI_HandleTypeDef hdcmi;
    hdcmi.Instance = DCMI;
    hdcmi.Init.SynchroMode = DCMI_SYNC_HARDWARE;
    hdcmi.Init.PCKPolarity = DCMI_PCKPOLARITY_RISING;
    hdcmi.Init.VSPolarity = DCMI_VSPOLARITY_HIGH;
    hdcmi.Init.HSPolarity = DCMI_HSPOLARITY_LOW;
    hdcmi.Init.CaptureRate = DCMI_CR_ALL_FRAME;
    hdcmi.Init.ExtendedDataMode = DCMI_EXTEND_DATA_8B;
    HAL_DCMI_Init(&hdcmi);
}

3. 中断服务函数实现时序补偿

在 VSYNC 中断中记录帧起始时间,在 HSYNC 中断中统计行数,并计算帧周期。若周期偏差超过阈值,则重启 DCMI 捕获。

volatile uint32_t vsync_timestamp = 0;
volatile uint32_t frame_period = 0;
volatile uint8_t frame_sync_error = 0;

void EXTI4_IRQHandler(void) {
    if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_4)) {
        __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_4);
        uint32_t now = HAL_GetTick();
        if (vsync_timestamp != 0) {
            frame_period = now - vsync_timestamp;
            if (frame_period > 50) { // 假设正常帧周期 33ms,允许偏差 50%
                frame_sync_error = 1;
            }
        }
        vsync_timestamp = now;
    }
}

void EXTI9_5_IRQHandler(void) {
    if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_6)) {
        __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_6);
        // 可在此统计行数,用于调试
    }
}

在主循环中,若检测到同步错误,则重新初始化 DCMI 并重启 DMA 传输。

if (frame_sync_error) {
    frame_sync_error = 0;
    HAL_DCMI_Stop(&hdcmi);
    HAL_DCMI_Start_DMA(&hdcmi, DCMI_MODE_CONTINUOUS, (uint32_t)frame_buffer, 320*240*2);
}

完整代码示例

以下为集成上述功能的简化示例,包含初始化与主循环。

#include "stm32f4xx_hal.h"

DCMI_HandleTypeDef hdcmi;
DMA_HandleTypeDef hdma_dcmi;
uint8_t frame_buffer[320*240*2];

void SystemClock_Config(void);
void GPIO_Init(void);
void DMA_Init(void);
void DCMI_Init(void);
void EXTI_Init(void);

int main(void) {
    HAL_Init();
    SystemClock_Config();
    GPIO_Init();
    DMA_Init();
    DCMI_Init();
    EXTI_Init();

    HAL_DCMI_Start_DMA(&hdcmi, DCMI_MODE_CONTINUOUS, (uint32_t)frame_buffer, sizeof(frame_buffer));

    while (1) {
        if (frame_sync_error) {
            frame_sync_error = 0;
            HAL_DCMI_Stop(&hdcmi);
            HAL_DCMI_Start_DMA(&hdcmi, DCMI_MODE_CONTINUOUS, (uint32_t)frame_buffer, sizeof(frame_buffer));
        }
    }
}

void EXTI4_IRQHandler(void) {
    // 如上实现
}

void EXTI9_5_IRQHandler(void) {
    // 如上实现
}

注意事项

  • 中断优先级:EXTI 中断优先级应高于 DCMI 中断,确保同步信号及时处理。
  • 滤波:在 GPIO 配置中启用施密特触发器,或外接 RC 滤波,减少毛刺。
  • DMA 缓冲:确保 DMA 缓冲区大小与帧大小匹配,避免溢出。
  • 调试:使用逻辑分析仪观察 VSYNC/HSYNC 波形,验证时序补偿效果。
  • 功耗:频繁重启 DCMI 会增加功耗,可适当调整补偿阈值。

结语

通过硬件中断捕获同步信号并动态补偿,STM32F4 与 OV5640 的帧同步丢失问题得到有效解决。此方案不仅适用于 DCMI,也可推广至其他并行摄像头接口。在实际项目中,结合信号完整性和固件优化,可显著提升系统稳定性。希望本文能为你的嵌入式开发提供实用参考。