本帖最后由 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
DMA1的传输完成信息传递到MDMA,MDMA将一级缓冲区的信息搬运到SDRAM里。
MDMA使用了多块传输 + 链表项的使用方法
stage2
开启了DMA,MDMA,DCMI的中断,并且编写了完成回调和错误回调(如果发生了DMA FIFO溢出错误,DCMI Overrun的错误会触发回调)
运行过程正常,没有触发错误回调。成功抓取到画面,但画面出现异常错位,如图所示。
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));
|