OV7725 外部时钟版本

元器件
传感器
库存 300

介绍

OmniVision OV7725 VGA CMOS传感器外部时钟版本,PLL旁路,XVCLK直接驱动系统时钟。适合FPGA/精准时钟源场景,上电更快、配置更简、帧率精准可控。6.0μm大像素低照度优异,支持60fps@VGA高速输出。

规格参数

参数
像素VGA 30万(640x480)
封装CSP-28
信噪比50dB
供电电压内核1.8V(内部LDO), 模拟3.3V, I/O 1.8V-3.3V
像素尺寸6.0μm x 6.0μm
动态范围72dB
时钟方案外部时钟直驱,PLL旁路
最大帧率60fps@VGA, 120fps@QVGA
特色功能高灵敏度6.0μm大像素、低照度0.5lux、无PLL延迟
输出接口DVP 8/10位并行
传感器尺寸1/4英寸

代码例程

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

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

## 一、平台说明

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

---

## 二、SCCB 基础驱动

```c
#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 方案

```c
// 产生 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 方案(推荐)

```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
```

---

## 四、外部时钟版本初始化

```c
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");
}
```

---

## 五、高速采集示例

```c
#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 |

参考资料

暂无参考文献