硬汉嵌入式论坛

 找回密码
 立即注册
查看: 248|回复: 4
收起左侧

[摄像头] STM32H7 驱动 OV5640出现画面异常错位

[复制链接]

3

主题

10

回帖

19

积分

新手上路

积分
19
发表于 2025-10-16 17:44:53 | 显示全部楼层 |阅读模式
本帖最后由 dreamland 于 2025-10-16 17:47 编辑

楼主使用STM32H743 DCMI接口驱动OV5640。基本信息如下:


  • 驱动使用Github STM32 OV5640
  • 格式RGB565, 图片大小选择640*480,DCMI触发方式是PCLK上升沿极性,HSYNC, VSYNC都是高极性
  • PCLK频率是24MHz,其余配置完全使用驱动代码


DCMI使用DMA1 Channel0来获取数据。开启双缓冲模式,一级缓冲区位于D2 SRAM1.

stage1

stage1



DMA1的传输完成信息传递到MDMA,MDMA将一级缓冲区的信息搬运到SDRAM里。
MDMA使用了多块传输 + 链表项的使用方法


stage2

stage2


开启了DMA,MDMA,DCMI的中断,并且编写了完成回调和错误回调(如果发生了DMA FIFO溢出错误,DCMI Overrun的错误会触发回调)

运行过程正常,没有触发错误回调。成功抓取到画面,但画面出现异常错位,如图所示。

picture

picture


部分代码已经贴出。楼主实在是找不到具体的问题出在哪里了。

DCMI的初始化部分

[C] 纯文本查看 复制代码
    RGB_hdcmi.data.instance.Instance                = DCMI;
    RGB_hdcmi.data.instance.Init.SynchroMode        = DCMI_SYNCHRO_HARDWARE;
    RGB_hdcmi.data.instance.Init.PCKPolarity        = DCMI_PCKPOLARITY_RISING;
    RGB_hdcmi.data.instance.Init.VSPolarity         = DCMI_VSPOLARITY_HIGH;
    RGB_hdcmi.data.instance.Init.HSPolarity         = DCMI_HSPOLARITY_HIGH;
    RGB_hdcmi.data.instance.Init.CaptureRate        = DCMI_CR_ALL_FRAME;
    RGB_hdcmi.data.instance.Init.ExtendedDataMode   = DCMI_EXTEND_DATA_8B;
    RGB_hdcmi.data.instance.Init.JPEGMode           = DCMI_JPEG_DISABLE;
    RGB_hdcmi.data.instance.Init.ByteSelectMode     = DCMI_BSM_ALL;
    RGB_hdcmi.data.instance.Init.ByteSelectStart    = DCMI_OEBS_ODD;
    RGB_hdcmi.data.instance.Init.LineSelectMode     = DCMI_LSM_ALL;
    RGB_hdcmi.data.instance.Init.LineSelectStart    = DCMI_OELS_ODD;


DMA1(FirstStage DMA) 的初始化
MDMA(Second Stage DMA)的初始化

[C] 纯文本查看 复制代码
    camera_first_stage_dma.Instance                 = DMA1_Stream0;
    camera_first_stage_dma.Init.Request             = DMA_REQUEST_DCMI;
    camera_first_stage_dma.Init.Direction           = DMA_PERIPH_TO_MEMORY;
    camera_first_stage_dma.Init.PeriphInc           = DMA_PINC_DISABLE;
    camera_first_stage_dma.Init.MemInc              = DMA_MINC_ENABLE;
    camera_first_stage_dma.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    camera_first_stage_dma.Init.MemDataAlignment    = DMA_MDATAALIGN_HALFWORD;
    camera_first_stage_dma.Init.Mode                = DMA_CIRCULAR;
    camera_first_stage_dma.Init.Priority            = DMA_PRIORITY_HIGH;
    camera_first_stage_dma.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;
    camera_first_stage_dma.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
    camera_first_stage_dma.Init.MemBurst            = DMA_MBURST_SINGLE;
    camera_first_stage_dma.Init.PeriphBurst         = DMA_PBURST_SINGLE;
    if (HAL_DMA_Init(&camera_first_stage_dma) != HAL_OK) {
        Error_Handler();
    }

    __HAL_LINKDMA(dcmiHandle, DMA_Handle, camera_first_stage_dma);

    HAL_NVIC_SetPriority(DCMI_IRQn, 6, 0);
    HAL_NVIC_EnableIRQ(DCMI_IRQn);

    // MDMA
    camera_second_stage_dma.Instance                      = MDMA_Channel2;
    camera_second_stage_dma.Init.Request                  = MDMA_REQUEST_DMA1_Stream0_TC;
    camera_second_stage_dma.Init.TransferTriggerMode      = MDMA_REPEAT_BLOCK_TRANSFER;
    camera_second_stage_dma.Init.Priority                 = MDMA_PRIORITY_VERY_HIGH;
    camera_second_stage_dma.Init.Endianness               = MDMA_LITTLE_ENDIANNESS_PRESERVE;
    camera_second_stage_dma.Init.SourceInc                = MDMA_SRC_INC_WORD;
    camera_second_stage_dma.Init.DestinationInc           = MDMA_DEST_INC_WORD;
    camera_second_stage_dma.Init.SourceDataSize           = MDMA_SRC_DATASIZE_WORD;
    camera_second_stage_dma.Init.DestDataSize             = MDMA_DEST_DATASIZE_WORD;
    camera_second_stage_dma.Init.DataAlignment            = MDMA_DATAALIGN_PACKENABLE;
    camera_second_stage_dma.Init.BufferTransferLength     = 32;
    camera_second_stage_dma.Init.SourceBurst              = MDMA_SOURCE_BURST_8BEATS;
    camera_second_stage_dma.Init.DestBurst                = MDMA_DEST_BURST_8BEATS;
    camera_second_stage_dma.Init.SourceBlockAddressOffset = 0;
    camera_second_stage_dma.Init.DestBlockAddressOffset   = 0;


摄像头初始化部分
这里可以忽略 indicator_operate 函数,它只是我在屏幕上显示的一串字符串。

[C] 纯文本查看 复制代码
            indicator_operate("Initialize DCMI Interface...");
            if (HAL_DCMI_Init(&(target_dcmi->data.instance)) != HAL_OK) {
                indicator_operate("Initialize DCMI Interface Failed!");
                can_catch_scene = false;
                continue;
            }

            indicator_operate("Initialize Camera...");
            (void)OV5640_RegisterBusIO(&ov5640, &ov5640_io);
            if (OV5640_Init(&ov5640, resolution, format) != OV5640_OK) {
                indicator_operate("Initialize Camera Failed!");
                can_catch_scene = false;
                continue;
            }
            OV5640_SetPCLK(&ov5640, OV5640_PCLK_24M);

            indicator_operate("Start Camera...");
            OV5640_Start(&ov5640);


DMA和MDMA启动流程
这里忽略了部分细节代码(例如地址计算,Cache刷新和地址对齐等),细节代码楼主已经仔细调试过并输出了,运算逻辑和结果是没有问题的。
[C] 纯文本查看 复制代码
    __HAL_LOCK(&Camera_DCMI->data.instance);

    Camera_DCMI->data.instance.State = HAL_DCMI_STATE_BUSY;

    __HAL_DCMI_ENABLE(&Camera_DCMI->data.instance);

    Camera_DCMI->data.instance.Instance->CR &= ~(DCMI_CR_CM);
    Camera_DCMI->data.instance.Instance->CR |= (uint32_t)(DCMI_MODE_SNAPSHOT);

    ... 计算MDMA的链表项数目,以及重复块传输的数目,每次块传输的大小(字节),以及创建它们的链表节点

    HAL_MDMA_DeInit(&Camera_DCMI->data.second_stage_dma);
    HAL_MDMA_Init(&Camera_DCMI->data.second_stage_dma);

    for(...)
        HAL_MDMA_LinkedList_CreateNode(...)
        HAL_MDMA_LinkedList_AddNode(...)

    ...

    HAL_MDMA_ConfigPostRequestMask(&Camera_DCMI->data.second_stage_dma,
                                           uint32_t(&(((DMA_TypeDef *)(Camera_DCMI->data.first_stage_dma.Instance))->LIFCR)),
                                           0b10000u);

    ...

    HAL_MDMA_Start_IT... 启动第一个重复块传输

    ... 计算第一个缓冲区和第二个缓冲区的位置,以启动DMA双缓冲

    HAL_DMA_DeInit(&(Camera_DCMI->data.first_stage_dma));
    HAL_DMA_Init(&(Camera_DCMI->data.first_stage_dma));

    ...

    HAL_DMAEx_MultiBufferStart_IT... 从第一个缓冲区,到第二个缓冲区

    ...

    Camera_DCMI->data.instance.State = HAL_DCMI_STATE_READY;

    __HAL_DCMI_ENABLE_IT(&Camera_DCMI->data.instance, DCMI_IT_FRAME);
    Camera_DCMI->data.instance.Instance->CR |= DCMI_CR_CAPTURE;
    __HAL_UNLOCK(&(Camera_DCMI->data.instance));


回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
119430
QQ
发表于 2025-10-17 08:48:26 | 显示全部楼层
我认为没必要用MDMA,直接使用这个配套好的API更方便,降低程序设计复杂度

STM32H7的摄像头接口DCMI配套的函数HAL_DCMI_Start_DMA非常不错,支持大分辨率直接传输
https://forum.anfulai.cn/forum.p ... 1577&fromuid=58
(出处: 硬汉嵌入式论坛)
回复

使用道具 举报

3

主题

10

回帖

19

积分

新手上路

积分
19
 楼主| 发表于 2025-10-18 00:34:16 | 显示全部楼层
eric2013 发表于 2025-10-17 08:48
我认为没必要用MDMA,直接使用这个配套好的API更方便,降低程序设计复杂度

STM32H7的摄像头接口DCMI配套 ...

感谢回复,我解决这个问题了。是因为我的MDMA的传输起始地址配错了导致的。

直接用DMA传输大分辨率的方案,这是我第一次尝试的。但遇到了DMA FIFO、DCMI FIFO爆了的问题。
我尝试用DMA直接向SDRAM里面写数据,640*480*2 Byte,PCLK也是24M。我跑了一个LVGL,也是用SDRAM存储的显存数据。可能是因为D2 - D1域总线仲裁慢吧,DMA向SDRAM搬数据总是出现FIFO ERROR,有的时候甚至还有DCMI Overrun。

所以我才打算用D2 SRAM1作为跳板,这个方案经过验证确实可行。并且速度也挺快的。

祝安!
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
119430
QQ
发表于 2025-10-18 08:37:09 | 显示全部楼层
dreamland 发表于 2025-10-18 00:34
感谢回复,我解决这个问题了。是因为我的MDMA的传输起始地址配错了导致的。

直接用DMA传输大分辨率的 ...

可能sdram速度没跟上,推荐用32bit带宽,多个总线同时对其发起操作,处理不过来。该用mdma总线优先高些,级我们v7板子摄像头用的我分享的方案,emwin和guix下的摄像头案例都没问题。动态内存,显存,摄像头dma缓冲都是用的sdram
回复

使用道具 举报

3

主题

10

回帖

19

积分

新手上路

积分
19
 楼主| 发表于 2025-10-19 01:55:44 | 显示全部楼层
eric2013 发表于 2025-10-18 08:37
可能sdram速度没跟上,推荐用32bit带宽,多个总线同时对其发起操作,处理不过来。该用mdma总线优先高些, ...

我用的正点的板子,它的SDRAM只有16宽度的数据线。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2025-11-22 04:11 , Processed in 0.042898 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表