OV5640 内部时钟版本

元器件
传感器
库存 150

介绍

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 内部时钟版本驱动代码例程.md

# 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 |

参考资料

暂无参考文献