# STM32F4 DCMI 与 OV5640 寄存器级初始化时序避坑指南 ## 一、DCMI 与 OV5640 基础原理 STM32F4 的 DCMI(Digital Camera Interface)是一个同步并行接口,支持 8/10/12/14 位数据宽度,通过 HSYNC、VSYNC 和 PIXCLK 信号与外部摄像头同步。OV5640 是一款 500 万像素 CMOS 传感器,支持 SCCB(兼容 I2C)接口配置寄存器,输出格式可设置为 RGB565、YUV422 等。 关键时序参数: - PIXCLK:像素时钟,由 OV5640 内部 PLL 产生,典型值 24MHz(VGA 模式)。 - VSYNC:帧同步,高电平有效或低电平有效可配置。 - HSYNC:行同步,同样极性可配。 - 数据在 PIXCLK 上升沿或下降沿有效,需与 DCMI 的捕获沿匹配。 ## 二、初始化时序避坑要点 ### 1. 上电顺序与复位 OV5640 对电源时序敏感:先提供 AVDD(模拟电源)和 DOVDD(数字 IO 电源),再提供 DVDD(核心数字电源)。复位引脚(RESETB)需保持至少 1ms 低电平,然后拉高。若使用 GPIO 控制,务必在初始化代码中先配置 GPIO,再延时。 ```c // 复位 OV5640 void OV5640_Reset(void) { GPIO_ResetBits(GPIOE, GPIO_Pin_1); // RESETB 低 delay_ms(10); GPIO_SetBits(GPIOE, GPIO_Pin_1); // 拉高 delay_ms(20); // 等待内部 PLL 稳定 } ``` ### 2. SCCB 初始化时序 SCCB 协议类似 I2C,但起始/停止条件有细微差别。OV5640 的 SCCB 地址为 0x3C(写)/0x3D(读)。初始化时,需先发送软复位命令(寄存器 0x3103 = 0x03),然后延时 5ms。注意:SCCB 时钟频率不要超过 400kHz,否则可能通信失败。 ```c // 写寄存器函数(SCCB) void OV5640_WriteReg(uint8_t reg, uint8_t val) { I2C_Start(); I2C_SendByte(0x3C); // 写地址 I2C_WaitAck(); I2C_SendByte(reg); I2C_WaitAck(); I2C_SendByte(val); I2C_WaitAck(); I2C_Stop(); } ``` ### 3. DCMI 配置时序 DCMI 必须在摄像头输出数据之前配置完成。配置步骤: 1. 使能 DCMI 时钟和 GPIO 时钟。 2. 配置 GPIO 复用功能(AF13)。 3. 配置 DCMI 同步模式、数据宽度、极性等。 4. 使能 DCMI 捕获。 **避坑点**:DCMI 的 VSYNC 和 HSYNC 极性必须与 OV5640 输出匹配。OV5640 默认 VSYNC 高有效、HSYNC 高有效,但可通过寄存器 0x3106 修改。若极性不匹配,会导致帧错乱。 ```c void DCMI_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; DCMI_InitTypeDef DCMI_InitStruct; // 使能时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOC, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_DCMI, ENABLE); // 配置引脚:PE0-D0, PE1-D1, ... PE7-D7, PC6-VSYNC, PC7-HSYNC, PC8-PIXCLK GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | ... | GPIO_Pin_7; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz; GPIO_Init(GPIOE, &GPIO_InitStruct); GPIO_PinAFConfig(GPIOE, GPIO_PinSource0, GPIO_AF_13); // 类似配置其他引脚... // DCMI 配置 DCMI_InitStruct.DCMI_CaptureMode = DCMI_CaptureMode_Continuous; DCMI_InitStruct.DCMI_SynchroMode = DCMI_SynchroMode_Hardware; DCMI_InitStruct.DCMI_PCKPolarity = DCMI_PCKPolarity_Rising; // 上升沿捕获 DCMI_InitStruct.DCMI_VSPolarity = DCMI_VSPolarity_High; DCMI_InitStruct.DCMI_HSPolarity = DCMI_HSPolarity_High; DCMI_InitStruct.DCMI_CaptureRate = DCMI_CaptureRate_All_Frame; DCMI_InitStruct.DCMI_ExtendedDataMode = DCMI_ExtendedDataMode_8b; DCMI_Init(&DCMI_InitStruct); DCMI_Cmd(ENABLE); } ``` ### 4. 初始化顺序的坑 很多开发者先配置 DCMI 再初始化 OV5640,这会导致 DCMI 在摄像头未就绪时收到无效信号,可能进入错误状态。正确顺序: 1. 复位 OV5640。 2. 通过 SCCB 写入 OV5640 寄存器(设置输出格式、分辨率、时钟分频)。 3. 等待 OV5640 输出稳定(至少 100ms)。 4. 配置并启动 DCMI。 ## 三、完整代码示例 以下是一个简化的初始化流程,包含关键步骤。 ```c void Camera_Init(void) { // 1. 复位 OV5640 OV5640_Reset(); // 2. 初始化 SCCB(I2C) I2C_Init(); // 3. 写入 OV5640 寄存器序列(部分关键寄存器) OV5640_WriteReg(0x3103, 0x03); // 软复位 delay_ms(5); OV5640_WriteReg(0x3107, 0x23); // 关闭内部 PLL 旁路 OV5640_WriteReg(0x3008, 0x82); // 关闭输出 // 设置输出格式为 RGB565,分辨率 640x480 OV5640_WriteReg(0x3800, 0x00); // 水平起始高字节 OV5640_WriteReg(0x3801, 0x00); OV5640_WriteReg(0x3802, 0x00); // 垂直起始高字节 OV5640_WriteReg(0x3803, 0x00); OV5640_WriteReg(0x3804, 0x0A); // 水平结束高字节 OV5640_WriteReg(0x3805, 0x3F); OV5640_WriteReg(0x3806, 0x07); // 垂直结束高字节 OV5640_WriteReg(0x3807, 0x9F); OV5640_WriteReg(0x3808, 0x02); // 输出宽度高字节 OV5640_WriteReg(0x3809, 0x80); // 640 OV5640_WriteReg(0x380A, 0x01); // 输出高度高字节 OV5640_WriteReg(0x380B, 0xE0); // 480 // 设置 PCLK 分频等... OV5640_WriteReg(0x3008, 0x02); // 开启输出 // 4. 等待稳定 delay_ms(100); // 5. 初始化 DCMI DCMI_Init(); // 6. 使能 DCMI 捕获中断(可选) DCMI_ITConfig(DCMI_IT_FRAME, ENABLE); NVIC_Init(&NVIC_InitStructure); } ``` ## 四、常见问题与调试技巧 - **图像花屏**:检查 PIXCLK 极性是否匹配。尝试切换 DCMI_PCKPolarity_Rising 为 Falling。 - **帧不同步**:检查 VSYNC/HSYNC 极性,以及 OV5640 的同步信号输出是否使能(寄存器 0x300E)。 - **数据错位**:确认数据线连接顺序,DCMI 的 D0-D7 对应 OV5640 的 D0-D7,不要交叉。 - **DCMI 无中断**:检查 DMA 配置(若使用),以及 DCMI 捕获使能位。 调试建议: - 用逻辑分析仪抓取 VSYNC、HSYNC、PIXCLK 波形,确认时序。 - 先使用简单的测试模式(OV5640 寄存器 0x503D 可输出彩条)验证 DCMI 通路。 ## 五、总结 寄存器级初始化 DCMI 与 OV5640 的关键在于时序匹配:上电顺序、SCCB 通信、DCMI 极性配置、初始化顺序缺一不可。遵循本文的避坑指南,可大幅减少调试时间。记住:先摄像头后 DCMI,先稳定后捕获。