OV5640 内部时钟版本驱动代码例程
一、平台说明
- MCU平台:STM32F407(DCMI接口) / ESP32-S3(并行接口模拟)
- 接口类型:DVP 8位并行接口
- 通信协议:SCCB(兼容I²C)用于寄存器配置
- 开发环境:STM32CubeIDE / ESP-IDF
二、GPIO初始化与SCCB驱动
2.1 STM32平台 I²C/SCCB 初始化
#include "stm32f4xx_hal.h"
I2C_HandleTypeDef hi2c1;
#define SCCB_SCL_PIN GPIO_PIN_6 // PB6
#define SCCB_SDA_PIN GPIO_PIN_7 // PB7
#define SCCB_GPIO_PORT GPIOB
#define OV5640_I2C_ADDR 0x78 // SCCB写地址
void SCCB_Init(void)
{
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_I2C1_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = SCCB_SCL_PIN | SCCB_SDA_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
HAL_GPIO_Init(SCCB_GPIO_PORT, &GPIO_InitStruct);
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 100000; // SCCB标准100kHz
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
HAL_I2C_Init(&hi2c1);
}
uint8_t OV5640_WriteReg(uint16_t reg, uint8_t data)
{
uint8_t buf[3];
buf[0] = (uint8_t)(reg >> 8); // 寄存器地址高8位
buf[1] = (uint8_t)(reg & 0xFF); // 寄存器地址低8位
buf[2] = data; // 数据
return HAL_I2C_Master_Transmit(&hi2c1, OV5640_I2C_ADDR, buf, 3, 100);
}
uint8_t OV5640_ReadReg(uint16_t reg)
{
uint8_t addr_buf[2];
uint8_t data = 0;
addr_buf[0] = (uint8_t)(reg >> 8);
addr_buf[1] = (uint8_t)(reg & 0xFF);
HAL_I2C_Master_Transmit(&hi2c1, OV5640_I2C_ADDR, addr_buf, 2, 100);
HAL_I2C_Master_Receive(&hi2c1, OV5640_I2C_ADDR | 0x01, &data, 1, 100);
return data;
}
三、DCMI初始化(STM32F4平台)
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;
hdcmi.Init.ByteSelectMode = DCMI_BSM_ALL;
hdcmi.Init.ByteSelectStart = DCMI_BSM_ALL;
hdcmi.Init.LineSelectMode = DCMI_LSM_ALL;
hdcmi.Init.LineSelectStart = DCMI_LSM_ALL;
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;
hdma_dcmi.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
hdma_dcmi.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
hdma_dcmi.Init.MemBurst = DMA_MBURST_SINGLE;
hdma_dcmi.Init.PeriphBurst = DMA_PBURST_SINGLE;
HAL_DMA_Init(&hdma_dcmi);
__HAL_LINKDMA(&hdcmi, DMA_Handle, hdma_dcmi);
}
四、OV5640内部时钟PLL配置
void OV5640_ConfigPLL_InternalClock(void)
{
OV5640_WriteReg(0x3008, 0x82); // 软件复位
HAL_Delay(10);
OV5640_WriteReg(0x3034, 0x1A); // PLL控制:charge pump 0x1A
OV5640_WriteReg(0x3035, 0x21); // SC PLL控制1:系统分频 /1, MIPI分频 /2
OV5640_WriteReg(0x3036, 0x46); // SC PLL控制2:倍频系数 N=70 (0x46, 含高位)
OV5640_WriteReg(0x3037, 0x13); // PCLK root divider
OV5640_WriteReg(0x3038, 0x00); // MIPI bit rate控制
OV5640_WriteReg(0x3017, 0x7F); // PAD IO控制1
OV5640_WriteReg(0x3018, 0xFC); // PAD IO控制2
printf("OV5640 内部PLL配置完成,XVCLK=24MHz → PCLK=84MHz\r\n");
}
五、传感器完整初始化序列
void OV5640_Hardware_Init(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;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
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); // PWDN=0
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); // RESET=0
HAL_Delay(10);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET); // PWDN=1
HAL_Delay(5); // 等待PLL锁定(内部时钟版本此步很重要!)
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); // RESET=1
HAL_Delay(20); // 等待传感器稳定
}
void OV5640_Init_Registers(void)
{
OV5640_WriteReg(0x3008, 0x82); // 软件复位
HAL_Delay(5);
OV5640_WriteReg(0x3008, 0x42); // 退出复位,进入正常工作模式
OV5640_WriteReg(0x3017, 0xFF); // IO控制寄存器
OV5640_WriteReg(0x3018, 0xFF); // IO控制寄存器
OV5640_WriteReg(0x302C, 0x02); // IO驱动能力
OV5640_WriteReg(0x3034, 0x1A); // PLL charge pump
OV5640_WriteReg(0x3035, 0x21); // 系统时钟分频
OV5640_WriteReg(0x3036, 0x46); // PLL倍频
OV5640_WriteReg(0x3037, 0x13); // PCLK分频
OV5640_WriteReg(0x3108, 0x01); // 系统时钟根分频
OV5640_WriteReg(0x3824, 0x04); // PCLK分频
OV5640_WriteReg(0x4600, 0x02); // VFIFO控制
OV5640_WriteReg(0x4601, 0x04); // VFIFO控制
OV5640_WriteReg(0x4300, 0x61); // 格式控制:RGB565
OV5640_WriteReg(0x3800, 0x00); // X起始高8位
OV5640_WriteReg(0x3801, 0x00); // X起始低8位
OV5640_WriteReg(0x3802, 0x00); // Y起始高8位
OV5640_WriteReg(0x3803, 0x00); // Y起始低8位
OV5640_WriteReg(0x3804, 0x0A); // X结束高8位
OV5640_WriteReg(0x3805, 0x3F); // X结束低8位 → 2623
OV5640_WriteReg(0x3806, 0x07); // Y结束高8位
OV5640_WriteReg(0x3807, 0x9F); // Y结束低8位 → 1951
OV5640_WriteReg(0x3808, 0x02); // 输出宽度高8位
OV5640_WriteReg(0x3809, 0x80); // 输出宽度低8位 → 640
OV5640_WriteReg(0x380A, 0x01); // 输出高度高8位
OV5640_WriteReg(0x380B, 0xE0); // 输出高度低8位 → 480
OV5640_WriteReg(0x380C, 0x07); // HTS高8位
OV5640_WriteReg(0x380D, 0x58); // HTS低8位
OV5640_WriteReg(0x380E, 0x01); // VTS高8位
OV5640_WriteReg(0x380F, 0xF0); // VTS低8位
OV5640_WriteReg(0x3A0F, 0x38); // 自动曝光控制
OV5640_WriteReg(0x3A10, 0x30); // 自动曝光控制
OV5640_WriteReg(0x3A11, 0x90); // 自动曝光控制
OV5640_WriteReg(0x3A1B, 0x30); // 自动曝光控制
OV5640_WriteReg(0x3A1E, 0x28); // 自动曝光控制
OV5640_WriteReg(0x3A1F, 0x10); // 自动曝光控制
OV5640_WriteReg(0x3406, 0x00); // AWB控制
OV5640_WriteReg(0x5180, 0xFF); // AWB增益
OV5640_WriteReg(0x5181, 0xF2); // AWB增益
OV5640_WriteReg(0x5182, 0x00); // AWB增益
OV5640_WriteReg(0x5183, 0x14); // AWB增益
OV5640_WriteReg(0x5184, 0x25); // AWB增益
OV5640_WriteReg(0x5185, 0x24); // AWB增益
OV5640_WriteReg(0x5000, 0x06); // 颜色饱和度
OV5640_WriteReg(0x5001, 0x01); // 锐度
OV5640_WriteReg(0x501F, 0x00); // ISP格式控制
OV5640_WriteReg(0x5580, 0x06); // 饱和度U
OV5640_WriteReg(0x5583, 0x40); // 饱和度V
OV5640_WriteReg(0x5584, 0x10); // 饱和度控制
OV5640_WriteReg(0x5588, 0x06); // 亮度控制 threshold
OV5640_WriteReg(0x5589, 0x10); // 亮度控制 slope
OV5640_WriteReg(0x558A, 0x01); // 亮度控制 limit
OV5640_WriteReg(0x558B, 0x80); // 亮度控制
printf("OV5640 寄存器初始化完成\r\n");
}
六、完整图像采集示例
#include <stdio.h>
#include <string.h>
#define FRAME_WIDTH 640
#define FRAME_HEIGHT 480
#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT * 2) // RGB565每像素2字节
__attribute__((aligned(4))) uint8_t frame_buffer[FRAME_SIZE];
volatile uint8_t frame_ready = 0;
void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi)
{
frame_ready = 1;
}
void OV5640_Capture_Init(void)
{
printf("=== OV5640 内部时钟版本 图像采集初始化 ===\r\n");
SCCB_Init();
OV5640_Hardware_Init();
OV5640_ConfigPLL_InternalClock(); // 内部PLL配置
OV5640_Init_Registers();
DCMI_Init();
HAL_DCMI_Start_DMA(&hdcmi, DCMI_MODE_CONTINUOUS,
(uint32_t)frame_buffer, FRAME_SIZE / 4);
printf("OV5640 初始化完成,开始采集...\r\n");
}
int main(void)
{
HAL_Init();
SystemClock_Config();
UART_Init();
OV5640_Capture_Init();
while (1)
{
if (frame_ready)
{
frame_ready = 0;
printf("Frame captured! Size: %d bytes\r\n", FRAME_SIZE);
printf("First 16 pixels (RGB565): ");
for (int i = 0; i < 32; i += 2)
{
uint16_t pixel = (frame_buffer[i] << 8) | frame_buffer[i + 1];
printf("%04X ", pixel);
}
printf("\r\n");
}
}
}
七、ESP32-S3平台驱动(Arduino/ESP-IDF)
#include "esp_camera.h"
static camera_config_t camera_config = {
.pin_pwdn = 47,
.pin_reset = 48,
.pin_xclk = 43,
.pin_sccb_sda = 40,
.pin_sccb_scl = 41,
.pin_d7 = 15,
.pin_d6 = 14,
.pin_d5 = 13,
.pin_d4 = 12,
.pin_d3 = 11,
.pin_d2 = 10,
.pin_d1 = 9,
.pin_d0 = 8,
.pin_vsync = 4,
.pin_href = 5,
.pin_pclk = 6,
.xclk_freq_hz = 24000000,
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,
.pixel_format = PIXFORMAT_RGB565,
.frame_size = FRAMESIZE_VGA,
.jpeg_quality = 12,
.fb_count = 2,
.fb_location = CAMERA_FB_IN_PSRAM,
.grab_mode = CAMERA_GRAB_WHEN_EMPTY,
};
void OV5640_ESP32_Init(void)
{
esp_err_t err = esp_camera_init(&camera_config);
if (err != ESP_OK)
{
printf("OV5640初始化失败: 0x%x\r\n", err);
return;
}
sensor_t *s = esp_camera_sensor_get();
if (s != NULL)
{
s->set_pll(s, 1); // 使能PLL
s->set_xclk(s, 24000000); // 设置参考时钟
s->set_framesize(s, FRAMESIZE_VGA);
s->set_pixformat(s, PIXFORMAT_RGB565);
}
printf("OV5640 (内部时钟版本) ESP32-S3 初始化完成\r\n");
}
void OV5640_ESP32_Capture(void)
{
camera_fb_t *fb = esp_camera_fb_get();
if (!fb)
{
printf("获取帧失败\r\n");
return;
}
printf("帧大小: %zu 字节, 分辨率: %dx%d\r\n",
fb->len, fb->width, fb->height);
esp_camera_fb_return(fb);
}
八、内部时钟版本 vs 外部时钟版本切换
void OV5640_SetClockMode(uint8_t internal_pll)
{
if (internal_pll)
{
OV5640_WriteReg(0x3103, 0x13); // 系统时钟来自PLL
OV5640_WriteReg(0x3034, 0x1A); // 使能PLL charge pump
OV5640_WriteReg(0x3035, 0x21); // PLL配置
OV5640_WriteReg(0x3036, 0x46); // PLL倍频
printf("OV5640: 内部时钟(PLL)模式\r\n");
}
else
{
OV5640_WriteReg(0x3103, 0x03); // 系统时钟来自XVCLK
OV5640_WriteReg(0x3034, 0x1A); // 关闭PLL
OV5640_WriteReg(0x3035, 0x01); // 直通模式
printf("OV5640: 外部时钟模式\r\n");
}
}
九、常见问题排查
| 问题 |
可能原因 |
解决方案 |
| 无图像输出 |
PLL未正确配置 |
检查0x3034-0x3037寄存器 |
| 花屏/条纹 |
PCLK极性错误 |
调整DCMI PCKPolarity |
| 图像颜色异常 |
像素格式不匹配 |
确认RGB565/YUV设置一致 |
| SCCB无应答 |
I²C地址错误或时序问题 |
扫描I²C总线确认0x3C地址 |
| 上电后无响应 |
PLL锁定时间不足 |
增加PWDN释放后延迟≥10ms |