2019-08-28 01:52 AM
Hi Community,
I'm trying to get an image from a camera with a resolution of 752x480.
In this case, the size (752 * 480) / 4 = 90240 exceeds the maximum DMA transfer length 65535.
And I get a broken image:
1/4 of the image is duplicated.
Original image:
DMA MemToMem initialization:
void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMAMUX1_CLK_ENABLE();
__HAL_RCC_DMA1_CLK_ENABLE();
__HAL_RCC_DMA2_CLK_ENABLE();
/* Configure DMA request hdma_memtomem_dma2_channel1 on DMA2_Channel1 */
hdma_memtomem_dma2_channel1.Instance = DMA2_Channel1;
hdma_memtomem_dma2_channel1.Init.Request = DMA_REQUEST_MEM2MEM;
hdma_memtomem_dma2_channel1.Init.Direction = DMA_MEMORY_TO_MEMORY;
hdma_memtomem_dma2_channel1.Init.PeriphInc = DMA_PINC_ENABLE;
hdma_memtomem_dma2_channel1.Init.MemInc = DMA_MINC_ENABLE;
hdma_memtomem_dma2_channel1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_memtomem_dma2_channel1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_memtomem_dma2_channel1.Init.Mode = DMA_NORMAL;
hdma_memtomem_dma2_channel1.Init.Priority = DMA_PRIORITY_HIGH;
if (HAL_DMA_Init(&hdma_memtomem_dma2_channel1) != HAL_OK)
{
Error_Handler();
}
/* DMA interrupt init */
/* DMA1_Channel5_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);
/* DMA2_Channel1_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA2_Channel1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA2_Channel1_IRQn);
}
DCMI initialization:
void MX_DCMI_Init(void)
{
hdcmi.Instance = DCMI;
hdcmi.Init.SynchroMode = DCMI_SYNCHRO_HARDWARE;
hdcmi.Init.PCKPolarity = DCMI_PCKPOLARITY_FALLING;
hdcmi.Init.VSPolarity = DCMI_VSPOLARITY_LOW;
hdcmi.Init.HSPolarity = DCMI_HSPOLARITY_LOW;
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();
}
}
void HAL_DCMI_MspInit(DCMI_HandleTypeDef* dcmiHandle)
{
__HAL_RCC_DCMI_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
.......
Pins initialization....
.....
/* DCMI DMA Init */
/* DCMI Init */
hdma_dcmi.Instance = DMA1_Channel5;
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_HIGH;
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, 1, 0);
HAL_NVIC_EnableIRQ(DCMI_IRQn);
/* USER CODE BEGIN DCMI_MspInit 1 */
/* USER CODE END DCMI_MspInit 1 */
}
}
In main:
extern DMA_HandleTypeDef hdma_memtomem_dma2_channel1;
extern DCMI_HandleTypeDef hdcmi;
hdcmi.DMAM2M_Handle = &hdma_memtomem_dma2_channel1;
uint8_t buf[ 752 * 480 ];
HAL_DCMI_DisableCrop( &hdcmi );
HAL_DCMI_Start_DMA( &hdcmi, DCMI_MODE_SNAPSHOT, (uint32_t)buf, 752 * 480 / 4 );
Does DCMI in STM32L4R7 support this resolution?
If so, what could be the problem?
2019-08-28 03:53 AM
Hi,
I think you need to implement double buffer mode.
Take a look in stm32f7xx_hal_dcmi in function HAL_DCMI_Start_DMA, there is a double buffer implementation.
Regards
2019-08-28 06:37 AM
eng23, thanks for your reply.
I compared the files stm32f7xx_hal_dcmi.c and stm32L4xx_hal_dcmi.c, it looks like there is no DMA double buffer mode in STM32L4.
However, the description and source code of HAL_ADC_Start_DMA handle the situation when the dma transfer length is greater than 65535:
* @note In case of length larger than 65535 (0xFFFF is the DMA maximum transfer length),
* the API uses the end of the destination buffer as a work area: HAL_DCMI_Start_DMA()
* initiates a circular DMA transfer from DCMI DR to the ad-hoc work buffer and each
* half and complete transfer interrupt triggers a copy from the work buffer to
* the final destination pData thru a second DMA channel.
* @note Following HAL_DCMI_Init() call, all interruptions are enabled (line end,
* frame end, overrun, VSYNC and embedded synchronization error interrupts).
* User can disable unwanted interrupts thru __HAL_DCMI_DISABLE_IT() macro
* before invoking HAL_DCMI_Start_DMA().
* @note For length less than 0xFFFF (DMA maximum transfer length) and in snapshot mode,
* frame interrupt is disabled before DMA transfer. FRAME capture flag is checked
* in DCMI_DMAXferCplt callback at the end of the DMA transfer. If flag is set,
* HAL_DCMI_FrameEventCallback() API is called.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_DCMI_Start_DMA(DCMI_HandleTypeDef* hdcmi, uint32_t DCMI_Mode, uint32_t pData, uint32_t Length)
{
uint32_t circular_copy_length;
............
if(Length <= 0xFFFFU)
{
...........
}
else /* Capture length is longer than DMA maximum transfer size */
{
/* Set DMA in circular mode */
hdcmi->DMA_Handle->Init.Mode = DMA_CIRCULAR;
/* Set the DMA half transfer complete callback */
hdcmi->DMA_Handle->XferHalfCpltCallback = DCMI_DMAHalfXferCplt;
/* Initialize transfer parameters */
hdcmi->XferSize = Length; /* Store the complete transfer length in DCMI handle */
hdcmi->pBuffPtr = pData; /* Final destination buffer pointer */
circular_copy_length = DCMI_TransferSize(Length);
/* Check if issue in intermediate length computation */
if (circular_copy_length == 0U)
{
/* Set state back to Ready */
hdcmi->State = HAL_DCMI_STATE_READY;
/* Process Unlocked */
__HAL_UNLOCK(hdcmi);
return HAL_ERROR;
}
/* Store the number of half - intermediate buffer copies needed */
hdcmi->XferCount = 2U * ((Length / circular_copy_length) - 1U);
/* Store the half-buffer copy length */
hdcmi->HalfCopyLength = circular_copy_length / 2U;
/* DCMI DR samples in circular mode will be copied
at the end of the final buffer.
Now compute the circular buffer start address. */
/* Start by pointing at the final buffer */
hdcmi->pCircularBuffer = pData;
/* Update pCircularBuffer in "moving" at the end of the final
buffer, don't forger to convert in bytes to compute exact address */
hdcmi->pCircularBuffer += 4U * (((Length / circular_copy_length) - 1U) * circular_copy_length);
/* Initiate the circular DMA transfer from DCMI IP to final buffer end */
if ( HAL_DMA_Start_IT(hdcmi->DMA_Handle, (uint32_t)&hdcmi->Instance->DR, (uint32_t)hdcmi->pCircularBuffer, circular_copy_length) != HAL_OK)
{
/* Update error code */
hdcmi->ErrorCode |= HAL_DCMI_ERROR_DMA;
/* Set state back to Ready */
hdcmi->State = HAL_DCMI_STATE_READY;
/* Process Unlocked */
__HAL_UNLOCK(hdcmi);
return HAL_ERROR;
}
}
.............
/* Return function status */
return HAL_OK;
}
This is very confusing.
2020-02-06 02:14 AM
Hi @MMele ,
I'm suffering some issues as well with a sensor generating 300kB images.
The symptom on my side is that the DCMI DMA ends up in Overrun error after the first transfer is complete.
Have you got some success with your implementation in the end?
If so, can you explain what solved the issue?
Thanks in advance,
JC
2020-02-07 02:01 AM
Doing my own response, I have found that the DMA channel used for DCMI should be set in circular mode in CubeMX.
I was focusing on the generated code and it was looking like everything was set properly. That was a wrong assumption.