OV7670 外部时钟版 驱动代码例程
一、STM32 平台 — 外部时钟生成 + DVP 采集
1.1 MCO 时钟输出配置(提供 XCLK)
#include "stm32f4xx_hal.h"
void MCO1_Output_24MHz(void) {
GPIO_InitTypeDef GPIO_Init = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
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);
HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_HSE, RCC_MCODIV_1);
}
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();
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);
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 外部时钟版初始化
#include "ov7670_stm32.h" // 复用之前定义的 SCCB 和 DVP 函数
void OV7670_ExtClk_Init(uint32_t xclk_freq_hz) {
GPIO_InitTypeDef GPIO_Init = {0};
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);
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 动态帧率调节
void OV7670_SetFrameRate(uint8_t fps_code) {
static const uint8_t clkrc_table[] = {
0x01,
0x03,
0x07,
0x0F,
};
if (fps_code < sizeof(clkrc_table)) {
OV7670_WriteReg(0x11, clkrc_table[fps_code]);
delay_ms(50); // 等待帧率切换稳定
}
}
二、ESP32 平台 — 外部时钟输出
#include "esp_camera.h"
#include "driver/ledc.h"
#define XCLK_GPIO_NUM 27
void setupXCLK_24MHz(void) {
ledc_timer_config_t timer_conf = {
.speed_mode = LEDC_HIGH_SPEED_MODE,
.duty_resolution = LEDC_TIMER_2_BIT,
.timer_num = LEDC_TIMER_0,
.freq_hz = 24000000,
.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,
.hpoint = 0,
};
ledc_channel_config(&ch_conf);
}
void setup() {
Serial.begin(115200);
Serial.println("OV7670 外部时钟版 初始化...");
setupXCLK_24MHz();
delay(10);
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);
}
三、多传感器同步采集(外部时钟版核心优势)
#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];
void MultiCam_Init(void) {
MCO1_Output_24MHz();
delay_ms(10);
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);
OV7670_WriteReg_Ex(cameras[i].hi2c, 0x12, 0x80); // 软复位
delay_ms(100);
OV7670_ConfigVGA_RGB565_Ex(cameras[i].hi2c);
}
}