介绍
OmniVision OV5640 500万像素CMOS图像传感器,支持自动对焦,最高2592x1944分辨率,支持DVP/MIPI接口,内部集成PLL时钟发生器,无需外部晶振即可工作。适用于嵌入式视觉、工业检测、智能家居等场景。
OmniVision OV5640 500万像素CMOS图像传感器,支持自动对焦,最高2592x1944分辨率,支持DVP/MIPI接口,内部集成PLL时钟发生器,无需外部晶振即可工作。适用于嵌入式视觉、工业检测、智能家居等场景。
| 参数 | 值 |
|---|---|
| 像素 | 500万(2592x1944) |
| 封装 | CSP-40 |
| 信噪比 | 36dB |
| 供电电压 | 内核1.5V, 模拟2.8V, I/O 1.8V/2.8V |
| 像素尺寸 | 1.4μm x 1.4μm |
| 动态范围 | 68dB |
| 时钟方案 | 内部PLL时钟发生器,12-50MHz参考时钟 |
| 最大帧率 | 15fps@2592x1944, 30fps@1080p, 60fps@720p, 120fps@VGA |
| 自动对焦 | 支持嵌入式AF引擎 |
| 输出接口 | DVP/MIPI CSI-2 |
| 传感器尺寸 | 1/4英寸 |
# OV5640 内部时钟版本驱动代码例程
## 一、平台说明
- **MCU平台**:STM32F407(DCMI接口) / ESP32-S3(并行接口模拟)
- **接口类型**:DVP 8位并行接口
- **通信协议**:SCCB(兼容I²C)用于寄存器配置
- **开发环境**:STM32CubeIDE / ESP-IDF
---
## 二、GPIO初始化与SCCB驱动
### 2.1 STM32平台 I²C/SCCB 初始化
```c
#include "stm32f4xx_hal.h"
I2C_HandleTypeDef hi2c1;
// SCCB引脚定义(使用I2C1)
#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);
}
// OV5640 SCCB 寄存器写入(16位寄存器地址,8位数据)
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);
}
// OV5640 SCCB 寄存器读取
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平台)
```c
DCMI_HandleTypeDef hdcmi;
DMA_HandleTypeDef hdma_dcmi;
// DCMI引脚配置:PC6(D0), PC7(D1), PC8(D2), PC9(D3), PC11(D4),
// PB6(D5), PB7(D6), PE5(D7), PA4(HSYNC), PB7(VSYNC), PA6(PCLK)
void DCMI_Init(void)
{
__HAL_RCC_DCMI_CLK_ENABLE();
__HAL_RCC_DMA2_CLK_ENABLE();
// GPIO配置(略,根据实际硬件连接配置DCMI复用引脚)
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);
// DMA配置:DCMI数据 → 内存帧缓冲
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配置
```c
// =============================================
// 内部时钟版本 PLL 配置(核心!)
// XVCLK=24MHz → PCLK=84MHz
// =============================================
void OV5640_ConfigPLL_InternalClock(void)
{
// --- 系统复位 ---
OV5640_WriteReg(0x3008, 0x82); // 软件复位
HAL_Delay(10);
// --- PLL配置 ---
// 预分频 M=3 → f_in = 24MHz/3 = 8MHz
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, 含高位)
// PLL输出 = 24MHz / M * N / P = 24/3*70/2 = 280MHz (VCO)
// SCLK = VCO/2/1 = 140MHz
// PCLK = SCLK / 配置分频
// PCLK分频设置
OV5640_WriteReg(0x3037, 0x13); // PCLK root divider
// MIPI配置(如使用DVP可跳过)
OV5640_WriteReg(0x3038, 0x00); // MIPI bit rate控制
// --- IO控制 ---
OV5640_WriteReg(0x3017, 0x7F); // PAD IO控制1
OV5640_WriteReg(0x3018, 0xFC); // PAD IO控制2
printf("OV5640 内部PLL配置完成,XVCLK=24MHz → PCLK=84MHz\r\n");
}
```
---
## 五、传感器完整初始化序列
```c
// OV5640 上电与GPIO控制初始化
void OV5640_Hardware_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 使能GPIO时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
// PWDN引脚 PA8
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);
// RESET引脚 PB0
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);
// 释放PWDN
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET); // PWDN=1
HAL_Delay(5); // 等待PLL锁定(内部时钟版本此步很重要!)
// 释放RESET
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); // RESET=1
HAL_Delay(20); // 等待传感器稳定
}
// OV5640 完整寄存器初始化(简化版,关键寄存器)
void OV5640_Init_Registers(void)
{
// ---- 系统控制 ----
OV5640_WriteReg(0x3008, 0x82); // 软件复位
HAL_Delay(5);
OV5640_WriteReg(0x3008, 0x42); // 退出复位,进入正常工作模式
// ---- IO方向控制 ----
OV5640_WriteReg(0x3017, 0xFF); // IO控制寄存器
OV5640_WriteReg(0x3018, 0xFF); // IO控制寄存器
OV5640_WriteReg(0x302C, 0x02); // IO驱动能力
// ---- 系统时钟/PLL(内部时钟版本关键配置)----
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控制
// ---- 输出格式:RGB565 ----
OV5640_WriteReg(0x4300, 0x61); // 格式控制:RGB565
// ---- 输出分辨率:640x480(预览模式)----
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位
// ---- AEC/AGC自动控制 ----
OV5640_WriteReg(0x3A0F, 0x38); // 自动曝光控制
OV5640_WriteReg(0x3A10, 0x30); // 自动曝光控制
OV5640_WriteReg(0x3A11, 0x90); // 自动曝光控制
OV5640_WriteReg(0x3A1B, 0x30); // 自动曝光控制
OV5640_WriteReg(0x3A1E, 0x28); // 自动曝光控制
OV5640_WriteReg(0x3A1F, 0x10); // 自动曝光控制
// ---- AWB自动白平衡 ----
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");
}
```
---
## 六、完整图像采集示例
```c
#include <stdio.h>
#include <string.h>
// 帧缓冲区(QVGA: 320x240 RGB565 = 153600字节)
#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;
// DCMI帧完成中断回调
void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi)
{
frame_ready = 1;
}
void OV5640_Capture_Init(void)
{
printf("=== OV5640 内部时钟版本 图像采集初始化 ===\r\n");
// 1. 硬件初始化
SCCB_Init();
OV5640_Hardware_Init();
// 2. 寄存器配置
OV5640_ConfigPLL_InternalClock(); // 内部PLL配置
OV5640_Init_Registers();
// 3. DCMI初始化
DCMI_Init();
// 4. 启动DCMI捕获
HAL_DCMI_Start_DMA(&hdcmi, DCMI_MODE_CONTINUOUS,
(uint32_t)frame_buffer, FRAME_SIZE / 4);
// 注意:DMA传输大小为Word(4字节),所以除以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);
// 示例:将第一行前16个像素通过串口输出
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)
```c
// ESP32-S3 使用I2C + 并行GPIO读取(无DCMI硬件)
#include "esp_camera.h"
// ESP32-S3摄像头引脚配置
static camera_config_t camera_config = {
.pin_pwdn = 47,
.pin_reset = 48,
.pin_xclk = 43, // 提供给OV5640的XVCLK(内部时钟版本仍需参考时钟)
.pin_sccb_sda = 40, // SCCB SDA
.pin_sccb_scl = 41, // SCCB SCL
.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, // 24MHz参考时钟
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,
.pixel_format = PIXFORMAT_RGB565,
.frame_size = FRAMESIZE_VGA, // 640x480
.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;
}
// 通过SCCB配置内部PLL(ESP32-CAM驱动默认已配置)
sensor_t *s = esp_camera_sensor_get();
if (s != NULL)
{
// 确认使用内部时钟PLL
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);
// 处理图像数据 fb->buf ...
esp_camera_fb_return(fb);
}
```
---
## 八、内部时钟版本 vs 外部时钟版本切换
```c
/*
* 内部时钟版本与外部时钟版本的关键区别:
*
* 内部时钟版本:
* - PLL使能(0x3034-0x3037需配置)
* - XVCLK仅作为参考时钟(12-50MHz)
* - 上电后需等待PLL锁定(≥5ms)
*
* 外部时钟版本:
* - PLL可旁路或不使用
* - XVCLK直接或经简单分频后作为系统时钟
* - 对XVCLK频率精度要求更高
*/
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 |
暂无参考文献