OV7725 外部时钟版本驱动代码例程

知识库
知识库文档
/firmware/传感器/OV7725 外部时钟版本/OV7725 外部时钟版本驱动代码例程.md

文档

OV7725 外部时钟版本驱动代码例程

一、平台说明

  • 主控:FPGA (Xilinx) + STM32 协处理 / STM32F407
  • 时钟方案:外部精准时钟直驱 XVCLK(PLL旁路)
  • SCCB地址:0x42

二、SCCB 基础驱动

#include "stm32f4xx_hal.h"

#define OV7725_I2C_ADDR  0x42

I2C_HandleTypeDef hi2c1;

void OV7725_I2C_Init(void)
{
    __HAL_RCC_I2C1_CLK_ENABLE();
    hi2c1.Instance = I2C1;
    hi2c1.Init.ClockSpeed = 100000;
    hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
    hi2c1.Init.OwnAddress1 = 0;
    hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
    HAL_I2C_Init(&hi2c1);
}

uint8_t OV7725_WriteReg(uint8_t reg, uint8_t data)
{
    uint8_t buf[2] = {reg, data};
    return HAL_I2C_Master_Transmit(&hi2c1, OV7725_I2C_ADDR, buf, 2, 100);
}

三、外部时钟版本 XVCLK 生成

STM32 TIM1 方案

// 产生 72MHz XVCLK (168MHz / 2 ≈ 84MHz, 取近似值)
TIM_HandleTypeDef htim1;

void XVCLK_ExtClk_72MHz_Init(void)
{
    __HAL_RCC_TIM1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();

    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = GPIO_PIN_8;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    htim1.Instance = TIM1;
    htim1.Init.Prescaler = 0;
    htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim1.Init.Period = 1;              // 168/2=84MHz (~72MHz)
    htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_PWM_Init(&htim1);

    TIM_OC_InitTypeDef sConfigOC = {0};
    sConfigOC.OCMode = TIM_OCMODE_PWM1;
    sConfigOC.Pulse = 1;
    sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
    sConfigOC.OCFastMode = TIM_OCFAST_ENABLE;
    HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1);

    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
    printf("XVCLK=84MHz 已启动 [OV7725外部时钟版本]\r\n");
}

FPGA Verilog 方案(推荐)

module ov7725_xclk_gen (
    input  wire       clk_100mhz,
    input  wire       rst_n,
    output wire       ov7725_xvclk_72mhz
);
    // MMCM: 100MHz → 72MHz
    clk_wiz_0 mmcm_inst (
        .clk_in1   (clk_100mhz),
        .clk_out1  (ov7725_xvclk_72mhz),
        .reset     (~rst_n),
        .locked    ()
    );
endmodule

四、外部时钟版本初始化

void OV7725_ExtClk_HardwareInit(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();

    GPIO_InitStruct.Pin = GPIO_PIN_8;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = GPIO_PIN_0;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
    HAL_Delay(5);

    // ⚠ 先启动XVCLK
    XVCLK_ExtClk_72MHz_Init();
    HAL_Delay(1);

    // 外部版本仅需1ms
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
    HAL_Delay(1);

    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
    HAL_Delay(20);
}

// =============================================
// OV7725 外部时钟版本 — PLL完全旁路
// =============================================
void OV7725_ExtClk_RegInit(void)
{
    printf("OV7725 外部时钟版本 寄存器初始化...\r\n");

    // 软件复位
    OV7725_WriteReg(0x12, 0x80);
    HAL_Delay(100);

    // ---- ⚠ 核心:PLL旁路 ----
    OV7725_WriteReg(0x0D, 0x01);  // COM4: PLL禁用! bit[6]=0
    // bit[6]=0 → PLL旁路
    // bit[5:0]=000001

    OV7725_WriteReg(0x11, 0x02);  // CLKRC: 外部时钟直通, PCLK=SCLK/3
    // bit[6]=0: 保留
    // bit[5]=0: PLL未使用
    // bit[4:0]=2: PCLK分频 = /3
    // PCLK = 84MHz / 3 = 28MHz (VGA 60fps可用)

    // ---- VGA RGB565 ----
    OV7725_WriteReg(0x12, 0x06);

    // ---- 窗口配置 ----(同内部版本)
    OV7725_WriteReg(0x17, 0x21); OV7725_WriteReg(0x18, 0xA1);
    OV7725_WriteReg(0x19, 0x01); OV7725_WriteReg(0x1A, 0xE1);
    OV7725_WriteReg(0x32, 0x21); OV7725_WriteReg(0x33, 0xA1);
    OV7725_WriteReg(0x03, 0x01); OV7725_WriteReg(0x04, 0xE1);
    OV7725_WriteReg(0x51, 0x02); OV7725_WriteReg(0x52, 0x80);
    OV7725_WriteReg(0x53, 0x01); OV7725_WriteReg(0x54, 0xE0);

    // ---- 时序 ----
    OV7725_WriteReg(0x55, 0x00);
    OV7725_WriteReg(0x56, 0x00);
    OV7725_WriteReg(0x57, 0x00);

    // ---- 自动控制 ----
    OV7725_WriteReg(0x13, 0x07);  // AEC/AGC/AWB
    OV7725_WriteReg(0x14, 0x08);
    OV7725_WriteReg(0x22, 0x00);
    OV7725_WriteReg(0x24, 0x78);
    OV7725_WriteReg(0x25, 0x68);
    OV7725_WriteReg(0x26, 0xD0);

    // ---- 图像 ----
    OV7725_WriteReg(0x41, 0x00);
    OV7725_WriteReg(0x4F, 0x80);
    OV7725_WriteReg(0x50, 0x80);

    // ---- 启动 ----
    OV7725_WriteReg(0x0C, 0x00);
    OV7725_WriteReg(0x12, 0x06);

    printf("OV7725 外部时钟版本就绪 (PLL旁路, XVCLK直驱)\r\n");
}

五、高速采集示例

#define FRAME_SIZE  (640 * 480 * 2)
__attribute__((aligned(4))) uint8_t frame_buf[FRAME_SIZE];
volatile uint8_t frame_ready = 0;

DCMI_HandleTypeDef hdcmi;
DMA_HandleTypeDef hdma_dcmi;

void DCMI_Init(void)
{
    __HAL_RCC_DCMI_CLK_ENABLE();
    __HAL_RCC_DMA2_CLK_ENABLE();

    hdcmi.Instance = DCMI;
    hdcmi.Init.SynchroMode = DCMI_SYNCHRO_HARDWARE;
    hdcmi.Init.PCKPolarity = DCMI_PCKPOLARITY_FALLING;
    hdcmi.Init.VSPolarity = DCMI_VSPOLARITY_LOW;
    hdcmi.Init.HSPolarity = DCMI_HSPOLARITY_LOW;
    hdcmi.Init.CaptureRate = DCMI_CR_ALL_FRAME;
    hdcmi.Init.ExtendedDataMode = DCMI_EXTEND_DATA_8B;
    hdcmi.Init.JPEGMode = DCMI_JPEG_DISABLE;
    HAL_DCMI_Init(&hdcmi);

    hdma_dcmi.Instance = DMA2_Stream1;
    hdma_dcmi.Init.Channel = DMA_CHANNEL_1;
    hdma_dcmi.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_dcmi.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_dcmi.Init.MemInc = DMA_MINC_ENABLE;
    hdma_dcmi.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    hdma_dcmi.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_dcmi.Init.Mode = DMA_CIRCULAR;
    hdma_dcmi.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    HAL_DMA_Init(&hdma_dcmi);
    __HAL_LINKDMA(&hdcmi, DMA_Handle, hdma_dcmi);
}

void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi)
{
    frame_ready = 1;
}

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    UART_Init();

    printf("=== OV7725 外部时钟版本 ===\r\n");
    OV7725_I2C_Init();
    OV7725_ExtClk_HardwareInit();
    OV7725_ExtClk_RegInit();

    DCMI_Init();
    HAL_DCMI_Start_DMA(&hdcmi, DCMI_MODE_CONTINUOUS,
                       (uint32_t)frame_buf, FRAME_SIZE / 4);

    uint32_t count = 0, last = HAL_GetTick();
    while (1)
    {
        if (frame_ready)
        {
            frame_ready = 0;
            count++;
            if (HAL_GetTick() - last >= 1000)
            {
                printf("FPS: %lu\r\n", count);
                count = 0; last = HAL_GetTick();
            }
        }
    }
}

六、常见问题

问题 解决
无图像 检查0x0D bit[6]=0, XVCLK已提供
帧率偏低 提高XVCLK或降低0x11分频
图像条纹 XVCLK抖动大,改用有源晶振/FPGA
SCCB无应答 确认I2C地址0x42

信息

路径
/firmware/传感器/OV7725 外部时钟版本/OV7725 外部时钟版本驱动代码例程.md
更新时间
2026/5/26