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 方案
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
);
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_ExtClk_72MHz_Init();
HAL_Delay(1);
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);
}
void OV7725_ExtClk_RegInit(void)
{
printf("OV7725 外部时钟版本 寄存器初始化...\r\n");
OV7725_WriteReg(0x12, 0x80);
HAL_Delay(100);
OV7725_WriteReg(0x0D, 0x01); // COM4: PLL禁用! bit[6]=0
OV7725_WriteReg(0x11, 0x02); // CLKRC: 外部时钟直通, PCLK=SCLK/3
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 |