如题开启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])。调试了好几天找不出原因,因此前来求助各位大神,先谢谢了。
附一张拍出来的图片
一周热门 更多>