介绍
OmniVision OV7725 VGA CMOS传感器外部时钟版本,PLL旁路,XVCLK直接驱动系统时钟。适合FPGA/精准时钟源场景,上电更快、配置更简、帧率精准可控。6.0μm大像素低照度优异,支持60fps@VGA高速输出。
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 外部时钟版本驱动代码例程
## 一、平台说明
- **主控**: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 |
暂无参考文献