STM32F407+OV2640 DCMI+DMA双缓冲方式采集图像帧数据时数据丢失

2019-07-20 22:28发布

如题开启DCMI+DMA双缓冲方式采集图像(JPG格式),在DMA传输完成中断里将已存储数据的缓冲区的数据复制到CCM RAM(整张JPG数据的缓冲区)里,然后DCMI帧中断时再将最后一个缓冲区里的数据复制到CCM RAM。部分程序如下:
[mw_shl_code=c,true]#define DMA_DBM_BUF_SIZE 1024 // DMA单通道数据缓冲区大小,以字为单位, 双缓冲时使用 #define JPEG_BUF_WORD_SIZE (1024*16) // JPEG数据缓冲通道大小,以字为单位, __align(32) uint32_t jpegbuf[JPEG_BUF_WORD_SIZE] __attribute__((at(0x10000000))); // CCM RAM全部用来存放JPG数据 typedef struct { uint32_t* JpegBuf; //JPEG数据缓存 uint32_t JpegDataLength; JPEG_COLLECT_STATE JpegState; }JpegInfoStruct; JpegInfoStruct JpegInfo; JpegInfo.JpegBuf = jpegbuf; static uint32_t buf0[DMA_DBM_BUF_SIZE]; static uint32_t buf1[DMA_DBM_BUF_SIZE];[/mw_shl_code] [mw_shl_code=c,true]u8 dma_buf_sw[2] = {0}; void ov2640_dcmi_callback(void) { u32 i,j; u32 *p = &JpegInfo.JpegBuf[JpegInfo.JpegDataLength]; if(JpegInfo.JpegState == JPEG_COLLECT_STOPED) { j = DMA_DBM_BUF_SIZE - DMA_GetCurrDataCounter(DMA2_Stream1); if (DMA2_Stream1->CR & DMA_Memory_1) { for(i=0; i<j; i++) p = buf1; } else { for(i=0; i<j; i++) p = buf0; } JpegInfo.JpegDataLength += j; JpegInfo.JpegState = JPEG_COLLECT_FINISH; } } void ov2640_dma_callback(void) { u32 i; u32 *p = &JpegInfo.JpegBuf[JpegInfo.JpegDataLength]; if(JpegInfo.JpegDataLength < JPEG_BUF_WORD_SIZE) { if (DMA2_Stream1->CR & DMA_Memory_1) // buf0传输完成 {[/mw_shl_code] [mw_shl_code=c,true]            dma_buf_sw[0]++; for(i=0; i<DMA_DBM_BUF_SIZE; i++) { p = buf0; buf0 = 0; } } else // buffer1传输完成 {[/mw_shl_code] [mw_shl_code=c,true]            dma_buf_sw[1]++; for(i=0; i<DMA_DBM_BUF_SIZE; i++) { p = buf1; buf1 = 0; } } JpegInfo.JpegDataLength += DMA_DBM_BUF_SIZE; } else { JpegInfo.JpegState = JEPG_COLLECT_ERROR; printf("jpg buffer overflow!!! "); } } void OV2640_PHOTO_START() { JpegInfoReset();[/mw_shl_code] [mw_shl_code=c,true]    dma_buf_sw[0] = 0;     dma_buf_sw[1] = 0; //DCMI_Cmd(ENABLE); DMA2_Stream1->NDTR = (uint16_t)DMA_DBM_BUF_SIZE; DMA_Cmd(DMA2_Stream1, ENABLE); DCMI_CaptureCmd(ENABLE); JpegInfo.JpegState = JPEG_IS_COLLECTING; } void OV2640_PHOTO_END() { //DCMI_Cmd(DISABLE); DMA_Cmd(DMA2_Stream1, DISABLE); while(DMA_GetCmdStatus(DMA2_Stream1) != DISABLE); DCMI_CaptureCmd(DISABLE); JpegInfo.JpegState = JPEG_COLLECT_STOPED; } void DMA2_Stream1_IRQHandler(void) { if(DMA_GetFlagStatus(DMA2_Stream1,DMA_FLAG_TCIF1)!=RESET) // DMA2_Steam1,传输完成标志 { DMA_ClearFlag(DMA2_Stream1,DMA_FLAG_TCIF1); // 清除传输完成中断 ov2640_dma_callback(); // 读DMA通道数据 } } void DCMI_IRQHandler(void) { if (DCMI_GetITStatus(DCMI_IT_OVF) != RESET) // 置位 { DCMI_ClearITPendingBit(DCMI_IT_OVF); OV2640_PHOTO_END(); printf("there is a DCMI overflow interrupt. "); } else if (DCMI_GetITStatus(DCMI_IT_FRAME) != RESET) { DCMI_ClearITPendingBit(DCMI_IT_FRAME); OV2640_PHOTO_END(); ov2640_dcmi_callback(); } } // 返回0:OK,返回1:超时,返回2:文件头错误,返回3:文件尾错误 u8 wait_frame_data_ready(u32 timeout_ms) { u8 *pbuf; vu32 i,j,size; while(JpegInfo.JpegState != JPEG_COLLECT_FINISH); // 等待图像接收完成     printf("dma_buf_sw[0] = %d, dma_buf_sw[1] = %d ", dma_buf_sw[0], dma_buf_sw[1]); { pbuf=(u8*)JpegInfo.JpegBuf; size = JpegInfo.JpegDataLength*4; for(i=0;i<size;i++)//查找 0XFF,0XD8 { if((pbuf==0XFF)&&(pbuf[i+1]==0XD8)) { j=i; break; } } if(i==size) { return 2;//没找到 0XFF,0XD8 } else { for(;i<size;i++) //查找FFD9 { if((pbuf==0XFF)&&(pbuf[i+1]==0XD9)) break; } if(i==size) { return 3; } else { JpegInfo.JpegDataLength = i + 2 - j; // 将图片数据长度转换成实际的字节长度 JpegInfo.JpegState = JPEG_DATA_VALID; return 0; } } } } void OV2640_DCMI_INIT(void) { DCMI_InitTypeDef DCMI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; camera_dcmi_io_init(); camera_power_control(DISABLE); delay_ms(5); camera_power_control(ENABLE); delay_ms(10); /* Enable DCMI clock */ RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_DCMI, ENABLE); /* DCMI configuration *******************************************************/ DCMI_DeInit(); DCMI_InitStructure.DCMI_CaptureMode = DCMI_CaptureMode_Continuous;//连续模式 ===================== DCMI_InitStructure.DCMI_CaptureRate = DCMI_CaptureRate_All_Frame;//全帧捕获 DCMI_InitStructure.DCMI_ExtendedDataMode = DCMI_ExtendedDataMode_8b;//8位数据格式 DCMI_InitStructure.DCMI_HSPolarity = DCMI_HSPolarity_Low;// HSYNC 低电平有效 DCMI_InitStructure.DCMI_PCKPolarity = DCMI_PCKPolarity_Rising;//PCLK 上升沿有效 DCMI_InitStructure.DCMI_SynchroMode = DCMI_SynchroMode_Hardware;//硬件同步HSYNC,VSYNC DCMI_InitStructure.DCMI_VSPolarity = DCMI_VSPolarity_Low;//VSYNC 低电平有效 DCMI_Init(&DCMI_InitStructure); DCMI_JPEGCmd(ENABLE); NVIC_InitStructure.NVIC_IRQChannel = DCMI_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0xF; //抢占优先级1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化NVIC寄存器 /* DCMI Interrupts config ***************************************************/ DCMI_ITConfig(DCMI_IT_FRAME, ENABLE); DCMI_ITConfig(DCMI_IT_OVF, ENABLE); DCMI_ITConfig(DCMI_IT_ERR, ENABLE); DCMI_Cmd(ENABLE); // 可以始终保持使能状态 } void OV2640_DMA_INIT(uint32_t DmaMemBaseAddr0, uint32_t DmaMemBaseAddr1, uint32_t DmaMemSize) { DMA_InitTypeDef DMA_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; /* Enable DMA2 clock */ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); /* DMA2 Stream1 Configuration */ DMA_DeInit(DMA2_Stream1); while (DMA_GetCmdStatus(DMA2_Stream1) != DISABLE) {} DMA_InitStructure.DMA_Channel = DMA_Channel_1; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&DCMI->DR); DMA_InitStructure.DMA_Memory0BaseAddr = DmaMemBaseAddr0; // 目标地址0 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; DMA_InitStructure.DMA_BufferSize = DmaMemSize; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; // 以字为传输长度,存在字节数组中。 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(DMA2_Stream1, &DMA_InitStructure); if (DmaMemBaseAddr1) // 是否开启双缓冲 { // 通道2基地址 第一个被使用的缓冲通道 DMA_DoubleBufferModeConfig(DMA2_Stream1, DmaMemBaseAddr1, DMA_Memory_0); // 下一个缓冲通道基地址 下一个要使用的缓冲通道号 DMA_MemoryTargetConfig(DMA2_Stream1, DmaMemBaseAddr1, DMA_Memory_1); DMA_DoubleBufferModeCmd(DMA2_Stream1, ENABLE); // 双缓冲模式 DMA_ITConfig(DMA2_Stream1, DMA_IT_TC, ENABLE); // 开启传输完成中断 NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0xD; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } } OV2640_DMA_INIT((uint32_t)buf0, (uint32_t)buf1, DMA_DBM_BUF_SIZE); [/mw_shl_code]
观察接收到的图片发现在图片底部有一条不正常的 {MOD}带,调试发现在帧中断之前的最后一次DMA传输完成中断里应该复制进图片缓冲区的数据全为0,而且大小刚好是一个缓冲区的大小数据如下图




从图中可以看到最后一次DMA传输完成中断里面复制的数据全是0,图像尾数据正常。通过上面程序里面printf("dma_buf_sw[0] = %d, dma_buf_sw[1] = %d ", dma_buf_sw[0], dma_buf_sw[1]);这句打印出来dma_buf_sw[1]比dma_buf_sw[0]大2(理论上dma_buf_sw[1]应该小于等于dma_buf_sw[0])。调试了好几天找不出原因,因此前来求助各位大神,先谢谢了。
附一张拍出来的图片
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
12条回答
xijiele
1楼-- · 2019-07-21 00:54
 精彩回答 2  元偷偷看……
登云钓月
2楼-- · 2019-07-21 05:47
我调试也也是这个结果,只有一小部分图像,其他的都是灰 {MOD}。
正点原子
3楼-- · 2019-07-21 08:56
说明你中间处理的时候,右一次数据处理慢了,丢失了一次数据。
xijiele
4楼-- · 2019-07-21 14:46
 精彩回答 2  元偷偷看……
xijiele
5楼-- · 2019-07-21 18:31
测试将DMA缓冲区大小改为1KB,即 "#define DMA_DBM_BUF_SIZE    256" 后采集图像正常,再没有看到一大片0。而除此之外其他的大小一律会有前面的问题。虽然能这样能将就用,不过一是没找到问题的根源,二是缓冲区太小DMA频繁中断影响系统性能,因此还是希望有人能帮忙找出问题所在。
正点原子
6楼-- · 2019-07-21 22:49
 精彩回答 2  元偷偷看……

一周热门 更多>