硬汉嵌入式论坛

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

[摄像头] DCMI偶发性地收不到传输过来的前几个字节的数据(混用DMA循环模式和普通模式的教训)

[复制链接]

9

主题

40

回帖

67

积分

初级会员

积分
67
发表于 2025-5-30 12:01:22 | 显示全部楼层 |阅读模式
问题描述
使用主控STM32H750VBT6的DCMI外设与FPGA进行大数据交互。在测试中发现,程序会偶发性地收不到前几个FPGA发过来的字节数据
DCMI外设的配置代码如下
void MX_DCMI_Init(void)
{
​
  /* USER CODE BEGIN DCMI_Init 0 */
​
  /* USER CODE END DCMI_Init 0 */
​
  /* USER CODE BEGIN DCMI_Init 1 */
​
  /* USER CODE END DCMI_Init 1 */
  hdcmi.Instance = DCMI;
  hdcmi.Init.SynchroMode = DCMI_SYNCHRO_HARDWARE;
  hdcmi.Init.PCKPolarity = DCMI_PCKPOLARITY_RISING;
  hdcmi.Init.VSPolarity = DCMI_VSPOLARITY_HIGH;
  hdcmi.Init.HSPolarity = DCMI_HSPOLARITY_HIGH;
  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_OEBS_ODD;
  hdcmi.Init.LineSelectMode = DCMI_LSM_ALL;
  hdcmi.Init.LineSelectStart = DCMI_OELS_ODD;
  if (HAL_DCMI_Init(&hdcmi) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN DCMI_Init 2 */
​
  /* USER CODE END DCMI_Init 2 */
​
}
​
void HAL_DCMI_MspInit(DCMI_HandleTypeDef* dcmiHandle)
{
​
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(dcmiHandle->Instance==DCMI)
  {
  /* USER CODE BEGIN DCMI_MspInit 0 */
​
  /* USER CODE END DCMI_MspInit 0 */
    /* DCMI clock enable */
    __HAL_RCC_DCMI_CLK_ENABLE();
​
    __HAL_RCC_GPIOE_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**DCMI GPIO Configuration
    PE4     ------> DCMI_D4
    PA4     ------> DCMI_HSYNC
    PA6     ------> DCMI_PIXCLK
    PA9     ------> DCMI_D0
    PA10     ------> DCMI_D1
    PD3     ------> DCMI_D5
    PB7     ------> DCMI_VSYNC
    PB8     ------> DCMI_D6
    PB9     ------> DCMI_D7
    PE0     ------> DCMI_D2
    PE1     ------> DCMI_D3
    */
    GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_0|GPIO_PIN_1;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF13_DCMI;
    HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
​
    GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_6|GPIO_PIN_9|GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF13_DCMI;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
​
    GPIO_InitStruct.Pin = GPIO_PIN_3;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF13_DCMI;
    HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
​
    GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF13_DCMI;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
​
    /* DCMI DMA Init */
    /* DCMI Init */
    hdma_dcmi.Instance = DMA2_Stream1;
    hdma_dcmi.Init.Request = DMA_REQUEST_DCMI;
    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_DISABLE;
    if (HAL_DMA_Init(&hdma_dcmi) != HAL_OK)
    {
      Error_Handler();
    }
​
    __HAL_LINKDMA(dcmiHandle,DMA_Handle,hdma_dcmi);
​
    /* DCMI interrupt Init */
    HAL_NVIC_SetPriority(DCMI_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DCMI_IRQn);
  /* USER CODE BEGIN DCMI_MspInit 1 */
​
  /* USER CODE END DCMI_MspInit 1 */
  }
}
​
void HAL_DCMI_MspDeInit(DCMI_HandleTypeDef* dcmiHandle)
{
​
  if(dcmiHandle->Instance==DCMI)
  {
  /* USER CODE BEGIN DCMI_MspDeInit 0 */
​
  /* USER CODE END DCMI_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_DCMI_CLK_DISABLE();
​
    /**DCMI GPIO Configuration
    PE4     ------> DCMI_D4
    PA4     ------> DCMI_HSYNC
    PA6     ------> DCMI_PIXCLK
    PA9     ------> DCMI_D0
    PA10     ------> DCMI_D1
    PD3     ------> DCMI_D5
    PB7     ------> DCMI_VSYNC
    PB8     ------> DCMI_D6
    PB9     ------> DCMI_D7
    PE0     ------> DCMI_D2
    PE1     ------> DCMI_D3
    */
    HAL_GPIO_DeInit(GPIOE, GPIO_PIN_4|GPIO_PIN_0|GPIO_PIN_1);
​
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_4|GPIO_PIN_6|GPIO_PIN_9|GPIO_PIN_10);
​
    HAL_GPIO_DeInit(GPIOD, GPIO_PIN_3);
​
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9);
​
    /* DCMI DMA DeInit */
    HAL_DMA_DeInit(dcmiHandle->DMA_Handle);
​
    /* DCMI interrupt Deinit */
    HAL_NVIC_DisableIRQ(DCMI_IRQn);
  /* USER CODE BEGIN DCMI_MspDeInit 1 */
​
  /* USER CODE END DCMI_MspDeInit 1 */
  }
}
程序使用DCMI的快照模式
HAL_DCMI_Start_DMA(&hdcmi,DCMI_MODE_SNAPSHOT, (uint32_t)DCMI_DataBuffer[idx], DCMI_FRAME_WORDS * DCMI_PACKET_FRAMES);
在DCMI帧完成回调中处理DCMI的数据
void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi)
{
    HAL_DCMI_Stop(&hdcmi);      //停止DCMI接收
   
    /* DCMI数据处理过程 */
   
    HAL_DCMI_Start_DMA(&hdcmi,DCMI_MODE_SNAPSHOT, (uint32_t)DCMI_DataBuffer[idx], DCMI_FRAME_WORDS * DCMI_PACKET_FRAMES);   //重新开启DCMI接收
}
问题是在复位MCU后会偶发性的出现。DCMI缓存里,前几个数据一直是0值
问题查找
MCU这端因为使用的是STM32H7系列芯片,涉及到了DMA。所以在每次处理DCMI的数据前,都用使用Cache无效化缓存函数,避免数据一致性问题。同时测量了整个帧中断周期的耗时。发现是完全满足FPGA传输数据的速率。不会因为中断太占时间而导致数据没有接收到。
FPGA端抓取过时钟和数据信号。发现是没问题的。
硬件端使用示波器勘测过DCMI的时钟信号。也是没有问题。
最后发现是DMA配置这里,应该是有问题的
  hdma_dcmi.Init.Mode = DMA_CIRCULAR;
因为使用的是DCMI的快照模式,在每次接收完一帧完成的数据后会停止DCMI.在处理完数据后再打开DMA接收。但是这里DMA的模式是循环模式。意思是在接收完一帧完整的信号后。DMA的指针地址会回到缓存开头的地址。但是此时DCMI的数据信号全是0(这里看过时钟信号。虽然VSYNC和HSYNNC信号无效了,但是PIXEL信号是一直在输出的)。所以DMA会收到PIXEL信号的驱使。继续搬运数据总线上的信号到缓存中。改掉DMA的CIRCULAR模式为NORMAL模式。问题得到解决
时序图:




screenshot_2025-05-30_11-57-04.png

评分

参与人数 1金币 +100 收起 理由
eric2013 + 100 很给力!

查看全部评分

回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
116197
QQ
发表于 6 天前 | 显示全部楼层
谢谢楼主分享。
回复

使用道具 举报

9

主题

40

回帖

67

积分

初级会员

积分
67
 楼主| 发表于 6 天前 | 显示全部楼层
eric2013 发表于 2025-6-3 07:49
谢谢楼主分享。

硬汉哥,这两天我针对这个问题有过更深地思考,感觉这里还是有点问题。在应用手册里讲了“DCMI_HSYNC和DCMI_VSYNC信号类似于消隐信号,因为在DCMI_HSYNC / DCMI_ VSYNC有效期间接收的所有数据都将被忽略。”
那如果是这样的话。DMA就不应该再搬运无效地数据了哇。
针对这种情况我觉得有可能是几个原因:

1.FPGA那边在发送指定个数的数据后,并没有及时改变DCMI_HSYNC和DCMI_VSYNC信号的极性。导致DMA还在搬运一些无效的数据到RAM中
2.DCMI模块这里使用的是单次快照模式。是不是本身就不能和DMA循环模式混用?
DCMI数据极性问题.jpg
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
116197
QQ
发表于 5 天前 | 显示全部楼层
Yhlr 发表于 2025-6-3 09:25
硬汉哥,这两天我针对这个问题有过更深地思考,感觉这里还是有点问题。在应用手册里讲了“DCMI_HSYNC和DC ...

1、我看这个DCMI里面没有类似LCD得边界参数设置,这样得话,那DCMI就完全受到DCMI_HSYNC / DCMI_ VSYNC控制了。如果控制不正常,确实存在搬运无效数据问题。

2、DCMI_MODE_SNAPSHOT下,DCMI得函数HAL_DCMI_Start_DMA,对于大数据传输时循环模式的。
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-6-9 23:38 , Processed in 0.235134 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

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