介绍
OV7670 VGA图像传感器外部时钟版本,30万像素(640×480),支持RGB/YCbCr/Raw输出格式,需外部提供12-48MHz时钟源,SCCB控制接口,最高30fps VGA输出,3.3V供电,适合需要精确时钟同步的应用场景
OV7670 VGA图像传感器外部时钟版本,30万像素(640×480),支持RGB/YCbCr/Raw输出格式,需外部提供12-48MHz时钟源,SCCB控制接口,最高30fps VGA输出,3.3V供电,适合需要精确时钟同步的应用场景
| 参数 | 值 |
|---|---|
| 像素 | 30万 |
| 功耗 | 60mW(典型) |
| 封装 | 28-pin CSP |
| 帧率 | 30fps@VGA |
| 接口 | 8位并行DVP + SCCB |
| 时钟 | 外部XCLK输入 10-48MHz |
| 信噪比 | 36dB |
| 分辨率 | 640×480 VGA |
| 供电电压 | 3.3V(内核1.8V内置LDO) |
| 像素尺寸 | 3.6μm×3.6μm |
| 动态范围 | 60dB |
| 输出格式 | RGB565/555/444, YUV422, Raw Bayer |
| 镜头接口 | M12/CS可选 |
| XCLK输入范围 | 10MHz ~ 48MHz |
# OV7670 外部时钟版 驱动代码例程
## 一、STM32 平台 — 外部时钟生成 + DVP 采集
### 1.1 MCO 时钟输出配置(提供 XCLK)
```c
// ov7670_ext_clk_stm32.c
// 使用 STM32F4 MCO1 输出 24MHz 时钟给 OV7670 XCLK
#include "stm32f4xx_hal.h"
/**
* 配置 MCO1 (PA8) 输出 24MHz 供 OV7670 外部时钟版使用
* 需先配置系统时钟为 HSE/PLL 提供时钟源
*/
void MCO1_Output_24MHz(void) {
GPIO_InitTypeDef GPIO_Init = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
/* PA8 = MCO1, 推挽输出, 高速 */
GPIO_Init.Pin = GPIO_PIN_8;
GPIO_Init.Mode = GPIO_MODE_AF_PP;
GPIO_Init.Pull = GPIO_NOPULL;
GPIO_Init.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_Init.Alternate = GPIO_AF0_MCO;
HAL_GPIO_Init(GPIOA, &GPIO_Init);
/*
* MCO1 时钟源选择:
* - 若系统时钟 168MHz,用 HSE 24MHz → 直接输出 24MHz
* - HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_HSE, RCC_MCODIV_1);
*/
HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_HSE, RCC_MCODIV_1);
// 输出: HSE 24MHz ÷ 1 = 24MHz XCLK
}
/**
* 备选方案: 使用 TIM1 CH1 输出 PWM 产生 24MHz
* 适合没有 MCO 的 MCU(如 STM32F1)
*/
void TIM1_PWM_24MHz_Init(void) {
TIM_OC_InitTypeDef sConfigOC = {0};
GPIO_InitTypeDef GPIO_Init = {0};
__HAL_RCC_TIM1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/* PA8 = TIM1_CH1 */
GPIO_Init.Pin = GPIO_PIN_8;
GPIO_Init.Mode = GPIO_MODE_AF_PP;
GPIO_Init.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_Init.Alternate = GPIO_AF1_TIM1;
HAL_GPIO_Init(GPIOA, &GPIO_Init);
/* TIM1: 72MHz → 24MHz = 每周期3个计数 */
TIM_HandleTypeDef htim1;
htim1.Instance = TIM1;
htim1.Init.Prescaler = 0; // 不分频
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 2; // ARR=2, 0-1-2 = 3拍 = 24MHz
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
HAL_TIM_PWM_Init(&htim1);
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 1; // 50%占空比 (1/2)
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
}
```
### 1.2 外部时钟版初始化
```c
// ov7670_ext_init.c
#include "ov7670_stm32.h" // 复用之前定义的 SCCB 和 DVP 函数
/* 外部时钟版初始化 */
void OV7670_ExtClk_Init(uint32_t xclk_freq_hz) {
GPIO_InitTypeDef GPIO_Init = {0};
// ... GPIO初始化同内部时钟版 ...
/* 关键:先启动外部时钟,再复位传感器 */
if (xclk_freq_hz == 24000000) {
MCO1_Output_24MHz();
}
delay_ms(10); // 等待时钟稳定
/* 硬件复位 */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_RESET); // PWDN=0
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET); // RESET=0
delay_ms(10);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET); // RESET=1
delay_ms(100);
/* 软复位 */
OV7670_WriteReg(0x12, 0x80);
delay_ms(100);
/* 根据XCLK频率配置 */
uint8_t clkrc_val;
switch (xclk_freq_hz) {
case 12000000:
clkrc_val = 0x01; // PCLK = 24MHz
break;
case 24000000:
clkrc_val = 0x01; // PCLK = 48MHz
break;
case 48000000:
clkrc_val = 0x03; // PCLK = 48MHz (÷4)
break;
default:
clkrc_val = 0x01;
}
/* 外部时钟模式配置 */
OV7670_WriteReg(0x11, clkrc_val); // 时钟分频
OV7670_WriteReg(0x6B, 0x00); // 外部时钟模式,不用内部LDO
/* 后续配置同内部时钟版... */
OV7670_ConfigVGA_RGB565();
}
```
### 1.3 动态帧率调节
```c
/**
* 外部时钟版的优势:可动态调整帧率
* 通过修改寄存器0x11改变PCLK分频比
*/
void OV7670_SetFrameRate(uint8_t fps_code) {
static const uint8_t clkrc_table[] = {
0x01, // ~30fps (XCLK=24MHz, PCLK=48MHz)
0x03, // ~15fps (XCLK=24MHz, PCLK=24MHz)
0x07, // ~7.5fps
0x0F, // ~3.75fps
};
if (fps_code < sizeof(clkrc_table)) {
OV7670_WriteReg(0x11, clkrc_table[fps_code]);
delay_ms(50); // 等待帧率切换稳定
}
}
```
---
## 二、ESP32 平台 — 外部时钟输出
```cpp
// ov7670_ext_esp32.ino
// ESP32 为 OV7670 外部时钟版提供 XCLK
#include "esp_camera.h"
#include "driver/ledc.h"
// XCLK 输出引脚
#define XCLK_GPIO_NUM 27
/**
* 使用 ESP32 LEDC 生成 24MHz XCLK
* ESP32 外设限制:最高约 40MHz(占空比可能不够精确)
* 推荐使用 20MHz 以下以确保稳定性
*/
void setupXCLK_24MHz(void) {
ledc_timer_config_t timer_conf = {
.speed_mode = LEDC_HIGH_SPEED_MODE,
.duty_resolution = LEDC_TIMER_2_BIT, // 2位分辨率 для 高速
.timer_num = LEDC_TIMER_0,
.freq_hz = 24000000, // 24MHz
.clk_cfg = LEDC_AUTO_CLK,
};
ledc_timer_config(&timer_conf);
ledc_channel_config_t ch_conf = {
.gpio_num = XCLK_GPIO_NUM,
.speed_mode = LEDC_HIGH_SPEED_MODE,
.channel = LEDC_CHANNEL_0,
.timer_sel = LEDC_TIMER_0,
.duty = 2, // 50%占空比 (2^2 / 2 = 2)
.hpoint = 0,
};
ledc_channel_config(&ch_conf);
}
void setup() {
Serial.begin(115200);
Serial.println("OV7670 外部时钟版 初始化...");
// 1. 先启动 XCLK
setupXCLK_24MHz();
delay(10);
// 2. 初始化摄像头
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = 32; // Y2
config.pin_d1 = 35; // Y3
config.pin_d2 = 34; // Y4
config.pin_d3 = 5; // Y5
config.pin_d4 = 39; // Y6
config.pin_d5 = 18; // Y7
config.pin_d6 = 36; // Y8
config.pin_d7 = 19; // Y9
config.pin_xclk = XCLK_GPIO_NUM; // 关键:XCLK由ESP32输出
config.pin_pclk = 21; // PCLK
config.pin_vsync = 22; // VSYNC
config.pin_href = 26; // HREF
config.pin_sscb_sda = 25; // SIOD
config.pin_sscb_scl = 23; // SIOC
config.pin_pwdn = -1;
config.pin_reset = 15;
config.xclk_freq_hz = 24000000; // 外部时钟版:24MHz
config.pixel_format = PIXFORMAT_RGB565;
config.frame_size = FRAMESIZE_VGA;
config.jpeg_quality = 12;
config.fb_count = 2;
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("初始化失败: 0x%x\n", err);
return;
}
Serial.println("OV7670 外部时钟版 就绪!");
}
void loop() {
camera_fb_t *fb = esp_camera_fb_get();
if (fb) {
Serial.printf("帧: %u字节\n", fb->len);
esp_camera_fb_return(fb);
}
delay(50);
}
```
---
## 三、多传感器同步采集(外部时钟版核心优势)
```c
// ov7670_multi_sync.c
// 3个OV7670外部时钟版同步采集方案
#define CAM_COUNT 3
typedef struct {
I2C_HandleTypeDef *hi2c; // 独立I2C总线
uint8_t frameBuffer[640*480*2];
uint8_t frameReady;
} OV7670_Camera;
OV7670_Camera cameras[CAM_COUNT];
/**
* 多摄像头同步初始化
* 所有摄像头共用 XCLK,各自独立 SCCB 和 DVP
*/
void MultiCam_Init(void) {
// 1. 启动共用的 24MHz XCLK(连接至所有摄像头)
MCO1_Output_24MHz();
delay_ms(10);
// 2. 依次初始化每个摄像头
for (int i = 0; i < CAM_COUNT; i++) {
cameras[i].hi2c = &hi2c_list[i]; // 各自独立的 I2C
cameras[i].frameReady = 0;
// 单独复位
HAL_GPIO_WritePin(reset_ports[i], reset_pins[i], GPIO_PIN_RESET);
delay_ms(5);
HAL_GPIO_WritePin(reset_ports[i], reset_pins[i], GPIO_PIN_SET);
delay_ms(100);
// 配置(可用同一配置,通过独立SCCB写入)
OV7670_WriteReg_Ex(cameras[i].hi2c, 0x12, 0x80); // 软复位
delay_ms(100);
OV7670_ConfigVGA_RGB565_Ex(cameras[i].hi2c);
}
// 3. 所有摄像头已同步(共用XCLK确保帧起始对齐)
// 误差 < 1 pixel clock cycle
}
```
暂无参考文献