# 利用 STM32 DCMI 与 DMA 双缓冲实现 OV2640 帧同步采集 ## 一、为什么需要无 CPU 干预采集? 在嵌入式视觉系统中,图像数据量大(如 800x600 分辨率,RGB565 格式,一帧约 960KB),若由 CPU 逐像素搬运,将占用大量主频资源,导致其他任务(如算法处理、通信)无法及时响应。STM32 的 DCMI(Digital Camera Interface)接口专为摄像头设计,配合 DMA 双缓冲机制,可实现数据从摄像头到内存的全硬件搬运,CPU 仅在帧完成中断中切换缓冲区并处理图像,真正实现零拷贝、低延迟采集。 ## 二、硬件原理:DCMI 与 DMA 双缓冲 ### 1. DCMI 接口核心特性 - 支持 8/10/12/14 位并行数据输入,兼容 OV2640 的 8 位 YCbCr/RGB 输出。 - 内置同步信号(VSYNC、HSYNC、PIXCLK)检测,可配置极性。 - 支持帧捕获模式(连续或单帧),通过硬件状态机自动完成帧同步。 ### 2. DMA 双缓冲(乒乓缓冲) DMA 双缓冲使用两个内存缓冲区(Buffer A 和 Buffer B),当 DMA 正在填充 Buffer A 时,CPU 可处理 Buffer B 中的数据;反之亦然。通过硬件自动切换,避免数据覆盖和等待,实现流水线式处理。 ### 3. 帧同步流程 - 摄像头输出 VSYNC 信号,DCMI 检测到帧起始。 - DCMI 将像素数据按行写入 DMA,DMA 将数据搬运至当前缓冲区。 - 一帧传输完成,DMA 产生传输完成中断,硬件自动切换到另一缓冲区。 - CPU 在中断中处理已满的缓冲区,同时新数据写入另一缓冲区。 ## 三、CubeMX 配置步骤 ### 1. 初始化 DCMI - 在 CubeMX 中启用 DCMI,配置数据线(如 D0-D7)、同步信号(VSYNC、HSYNC、PIXCLK)。 - 设置像素时钟极性(通常为上升沿采样)、同步信号极性(低有效)。 - 选择捕获模式为连续模式,数据格式为 RGB565(8 位模式)。 ### 2. 配置 DMA - 添加 DMA 请求,选择 DCMI 的 RX 通道。 - 设置 DMA 模式为循环模式(Circular),数据宽度为半字(16 位),内存地址递增。 - 启用 DMA 双缓冲模式,指定两个内存缓冲区地址。 ### 3. 生成代码并调整 - 在生成的 main.c 中,启动 DCMI 和 DMA 传输。 - 实现 DMA 传输完成中断回调函数,处理帧数据。 ## 四、关键代码实现 ### 1. 缓冲区定义与初始化 ```c #define IMG_WIDTH 800 #define IMG_HEIGHT 600 #define IMG_SIZE (IMG_WIDTH * IMG_HEIGHT * 2) // RGB565 每像素 2 字节 uint8_t buffer_a[IMG_SIZE] __attribute__((aligned(32))); uint8_t buffer_b[IMG_SIZE] __attribute__((aligned(32))); // 在 main 中初始化 HAL_DCMI_Start_DMA(&hdcmi, DMA_MODE_CIRCULAR, (uint32_t)buffer_a, (uint32_t)buffer_b, IMG_SIZE); ``` ### 2. DMA 中断回调处理 ```c // 在 stm32f4xx_it.c 中调用 HAL_DCMI_IRQHandler void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi) { // 判断当前 DMA 使用的缓冲区(通过 DMA 的 NDTR 寄存器或自定义标志) if (dma_current_buffer == buffer_a) { // 处理 buffer_b 中的图像数据 process_image(buffer_b, IMG_SIZE); } else { process_image(buffer_a, IMG_SIZE); } // 切换标志(注意:DMA 已自动切换,此处仅用于逻辑判断) dma_current_buffer = (dma_current_buffer == buffer_a) ? buffer_b : buffer_a; } ``` ### 3. 图像处理函数示例(伪代码) ```c void process_image(uint8_t *buf, uint32_t size) { // 例如:转换为灰度图、边缘检测等 // 注意:处理时间必须小于一帧传输时间,否则会丢帧 // 可在此处添加帧计数、时间戳等 } ``` ## 五、注意事项与常见坑点 - **缓冲区对齐**:DMA 要求缓冲区地址按 4 字节对齐,建议使用 `__attribute__((aligned(32)))` 或 `memalign`。 - **DMA 循环模式**:必须设置为循环模式,否则 DMA 传输一次后停止,无法连续采集。 - **中断优先级**:DCMI 帧中断应设置为高优先级,避免被其他中断阻塞导致丢帧。 - **处理时间控制**:图像处理必须在下一帧传输完成前结束,否则缓冲区会被覆盖。可通过帧率测量或双缓冲状态标志检测。 - **同步信号极性**:OV2640 的 VSYNC/HSYNC 极性可通过寄存器配置,需与 DCMI 设置匹配,否则帧同步失败。 - **像素时钟频率**:确保 PIXCLK 不超过 DCMI 最大频率(如 STM32F4 为 54MHz),否则数据采样错误。 - **内存带宽**:高分辨率高帧率时,DMA 占用总线带宽,可能影响其他外设,需合理规划。 ## 六、总结 通过 DCMI + DMA 双缓冲,STM32 可高效完成图像采集,CPU 负载极低,适用于实时视觉处理、运动检测等场景。本文提供的配置和代码可直接移植到多数 STM32 系列(如 F4/H7),开发者只需根据具体型号调整时钟和引脚即可。掌握该技术,是迈向高性能嵌入式视觉系统的重要一步。