一、OV2640简介
OV2640是OmniVision公司生产的一颗1/4寸的CMOS UXGA(1632 * 1232)图像传感器。该传感器体积小、工作电压低,提供单片UXGA摄像头和影像处理器的所有功能。通过SCCB 总线控制,可以输出整帧、子采样、缩放和取窗口等方式的各种分辨率8/10位影像数据。UXGA最高15帧/秒(SVGA可达30帧,CIF可达60帧)。用户可以完全控制图像质量、数据格式和传输方式。所有图像处理功能过程包括伽玛曲线、白平衡、对比度、色度等都可以通过SCCB接口编程。 特点
- ①高灵敏度、低电压适合嵌入式应用。
- ②标准的SCCB接口,兼容IIC接口。
- ③支持RawRGB、RGB(RGB565/RGB555)、GRB422、YUV(422/420)和YCbCr(422)输出格式。
- ④支持UXGA、SXGA、SVGA以及按比例缩小到从SXGA到40*30的任何尺寸 。
- ⑤支持自动曝光控制、自动增益控制、自动白平衡、自动消除灯光条纹、自动黑电平校准等自动控制功能。同时支持色饱和度、色相、伽马、锐度等设置。
- ⑥支持图像缩放和闪光灯。
- ⑦支持图像压缩,即可输出JPEG图像数据。
二、OV2640时序
OV2640行输出时序 其中:PCLK最高为36MHz。图像数据在HREF为高的时候输出,当HREF变高后,每一个PCLK时钟,输出一个字节数据。比如我们采用UXGA时序,RGB565格式输出,每2个字节组成一个像素的颜色(低字节在前,高字节在后),这样每行输出总共有16002个PCLK周期,输出16002个字节。 OV2640帧输出时序 注意:OV2640支持RGB565或JPEG输出。RGB565输出时,时序如图所示。JPEG输出时,PCLK大大减少,且HREF不连续,数据流以0XFF,0XD8开头,以0XFF,0XD9结束,将此间数据保存为.jpg即可在电脑打开查看。
三、输出图像窗口设置
其中: 1、最外层为传感器窗口设置(OV2640_Window_Set),传感器窗口设置允许用户设置整个传感器区域,即在传感器里进行开窗,范围是2 * 2 ~ 1632 * 1220都可以设置。要求传感器尺寸大于图像尺寸。 2、图像尺寸设置(OV2640_ImageSize_Set)即DSP输出图像的最大尺寸。通过0XC0、0XC1、0X8C等寄存器设置。 3、图像窗口设置(OV2640_ImageWin_Set)图像窗口设置其实和前面的传感器窗口设置类似,只是这个窗口是在我们前面设置的图像尺寸里面,再一次设置窗口大小,该窗口必须小于等于前面设置的图像尺寸。该窗口设置后的图像范围,将用于输出到外部。图像窗口设置通过:0X51、0X52、0X53、0X54、0X55、0X57等寄存器设置。 4、图像输出大小设置(OV2640_OutSize_Set) 图像输出大小设置,控制最终输出到外部的图像尺寸。该设置将图像窗口设置所决定的窗口大小,通过内部DSP处理,缩放成我们输出到外部的图像大小。该设置将会对图像进行缩放处理,如果设置的图像输出大小不等于图像窗口设置图像大小,那么图像就会被缩放处理,只有这两者设置一样大的时候,输出比例才是1:1的。图像输出大小通过:0X5A/0X5B/0X5C等寄存器设置。
四、引脚说明以及操作流程
OV2640初始化流程 单片机读取OV2640模块图像数据过程
五、STM32F4的DCMI
DCMI信号说明 1,数据据输入(D[0:13]),接摄像头的数据输出。 2,水平同步(行同步)输入(HSYNC),接摄像头的HSYNC/HREF信号。 3,垂直同步(场同步)输入(VSYNC),接摄像头的VSYNC信号。 4,像素时钟输入(PIXCLK),接摄像头的PCLK信号。 DCMI接口的数据与PIXCLK(即PCLK)保持同步,并根据像素时钟的极性在像素时钟上升沿/下降沿发生变化。HSYNC(HREF)信号指示行的开始/结束,VSYNC信号指示帧的开始/结束。 图中对应设置为:DCMI_PIXCLK的捕获沿为下降沿,DCMI_HSYNC和DCMI_VSYNC的有效状态为1。 注意:这里的有效状态实际上对应的是指示数据在并行接口上无效时,HSYNC/VSYNC引脚上面的引脚电平。 DCMI数据说明 DCMI接收到的数据,存储在DCMI_DR寄存器(32位)里面,我们接ATK-OV2640采用8位数据宽度,所以每4个像素时钟,才会捕获完32位数据,第一个字节存放在LSB位置,第四个字节存放在MSB位置,如下图所示: 注意:低字节在前,高字节在后。 DCMI之DMA说明 DCMI支持DMA传输,当DCMI_CR寄存器中的CAPTURE位置1时,激活DMA接口。 摄像头接口每次在其寄存器(DCMI_DR)中收到一个完整的32位数据块时,都将发一个DMA请求,由DMA将DCMI_DR寄存器的值搬运到目的地址(比如LCD/SRAM)。 DCMI的DMA请求是映射在DMA2通道1的数据流1上面的,所以配置DMA时,应该配置这个。另外,如果是直接DCMI -> DMA -> LCD的传输方式,因为LCD是16位宽(RGB565),而DCMI_DR是32位宽。所以,一次DCMI引起的DMA传输,将引发往LCD写2次数据。
六、DCMI寄存器说明
1、DCMI控制寄存器(DCMI_CR)
- ENABLE,用于设置是否使能DCMI。在使能之前,必须将其他配置设置好。
- FCRC[1:0],这两个位用于帧率控制,我们捕获所有帧,所以设置为00即可。
- VSPOL,用于设置垂直同步极性,也就是VSYNC引脚上面,数据无效时的电平状态,根据前面说所,我们应该设置为0。
- HSPOL,用于设置水平同步极性,也就是HSYNC引脚上面,数据无效时的电平状态,同样应该设置为0。
- PCKPOL,用于设置像素时钟极性,我们用上升沿捕获,所以设置为1。
- CM,用于设置捕获模式,我们用连续采集模式,所以设置为0即可。
- CAPTURE,用于使能捕获,我们设置为1。该位使能后,将激活DMA,
DCMI等待第一帧开始,然后生成DMA请求将收到的数据传输到目标存储器中。 注意:CAPTURE位必须在其他所有配置(包括DMA)完成后,才使能! 2、DCMI中断使能寄存器(DCMI_IER) - FRAME_IE,捕获完成中断使能位。实际上就是帧中断。
七、初始化流程
- ①配置相关引脚的复用功能,使能DCMI时钟。要用DCMI,先要使能DCMI的时钟。其次要设置DCMI的相关引脚为复用输出,以便连接OV2640模块。
- ②设置DCMI工作模式及PCLK/HSYNC/VSYNC等参数。DCMI接口,使用8位接口,连续模式,根据OV2640模块的输出时序图,设置:PCLK为上升沿有效,HSYNC和VSYNC为低电平有效。同时还要设置帧中断(jpeg数据采集用)等参数。
- ③设置DMA。DCMI数据,一般采用DMA来搬运,所以,设置好DCMI相关参数后,需要设置DMA,以便采集数据。
- ④启动DCMI传输。最后,设置DCMI->CR的最低位为1,即可启动DCMI捕获图像数据。
八、代码流程
1、初始化OV2640
u8 OV2640_Init(void)
{
u16 i=0;
u16 reg;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOG, &GPIO_InitStructure);
OV2640_PWDN=0;
delay_ms(10);
OV2640_RST=0;
delay_ms(10);
OV2640_RST=1;
SCCB_Init();
SCCB_WR_Reg(OV2640_DSP_RA_DLMT, 0x01);
SCCB_WR_Reg(OV2640_SENSOR_COM7, 0x80);
delay_ms(50);
reg=SCCB_RD_Reg(OV2640_SENSOR_MIDH);
reg<<=8;
reg|=SCCB_RD_Reg(OV2640_SENSOR_MIDL);
if(reg!=OV2640_MID)
{
printf("MID:%d\r\n",reg);
return 1;
}
reg=SCCB_RD_Reg(OV2640_SENSOR_PIDH);
reg<<=8;
reg|=SCCB_RD_Reg(OV2640_SENSOR_PIDL);
if(reg!=OV2640_PID)
{
printf("HID:%d\r\n",reg);
return 2;
}
for(i=0;i<sizeof(ov2640_sxga_init_reg_tbl)/2;i++)
{
SCCB_WR_Reg(ov2640_sxga_init_reg_tbl[i][0],ov2640_sxga_init_reg_tbl[i][1]);
}
return 0x00;
}
2、JPEG模式
void jpeg_test(void)
{
u32 i;
u8 *p;
u8 key;
u8 effect=0,saturation=2,contrast=2;
u8 size=3;
u8 msgbuf[15];
LCD_Clear(WHITE);
POINT_COLOR=RED;
LCD_ShowString(30,50,200,16,16,"ALIENTEK STM32F4");
LCD_ShowString(30,70,200,16,16,"OV2640 JPEG Mode");
LCD_ShowString(30,100,200,16,16,"KEY0:Contrast");
LCD_ShowString(30,120,200,16,16,"KEY1:Saturation");
LCD_ShowString(30,140,200,16,16,"KEY2:Effects");
LCD_ShowString(30,160,200,16,16,"KEY_UP:Size");
sprintf((char*)msgbuf,"JPEG Size:%s",JPEG_SIZE_TBL[size]);
LCD_ShowString(30,180,200,16,16,msgbuf);
OV2640_JPEG_Mode();
My_DCMI_Init();
DCMI_DMA_Init((u32)&jpeg_buf,jpeg_buf_size,DMA_MemoryDataSize_Word,DMA_MemoryInc_Enable);
OV2640_OutSize_Set(jpeg_img_size_tbl[size][0],jpeg_img_size_tbl[size][1]);
DCMI_Start();
while(1)
{
if(jpeg_data_ok==1)
{
p=(u8*)jpeg_buf;
LCD_ShowString(30,210,210,16,16,"Sending JPEG data...");
for(i=0;i<jpeg_data_len*4;i++)
{
while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET);
USART_SendData(USART2,p[i]);
key=KEY_Scan(0);
if(key)break;
}
if(key)
{
LCD_ShowString(30,210,210,16,16,"Quit Sending data ");
switch(key)
{
case KEY0_PRES:
contrast++;
if(contrast>4)contrast=0;
OV2640_Contrast(contrast);
sprintf((char*)msgbuf,"Contrast:%d",(signed char)contrast-2);
break;
case KEY1_PRES:
saturation++;
if(saturation>4)saturation=0;
OV2640_Color_Saturation(saturation);
sprintf((char*)msgbuf,"Saturation:%d",(signed char)saturation-2);
break;
case KEY2_PRES:
effect++;
if(effect>6)effect=0;
OV2640_Special_Effects(effect);
sprintf((char*)msgbuf,"%s",EFFECTS_TBL[effect]);
break;
case WKUP_PRES:
size++;
if(size>8)size=0;
OV2640_OutSize_Set(jpeg_img_size_tbl[size][0],jpeg_img_size_tbl[size][1]);
sprintf((char*)msgbuf,"JPEG Size:%s",JPEG_SIZE_TBL[size]);
break;
}
LCD_Fill(30,180,239,190+16,WHITE);
LCD_ShowString(30,180,210,16,16,msgbuf);
delay_ms(800);
}else LCD_ShowString(30,210,210,16,16,"Send data complete!!");
jpeg_data_ok=2;
}
}
}
3、RGB565模式
void rgb565_test(void)
{
u8 key;
u8 effect=0,saturation=2,contrast=2;
u8 scale=1;
u8 msgbuf[15];
LCD_Clear(WHITE);
POINT_COLOR=RED;
LCD_ShowString(30,50,200,16,16,"ALIENTEK STM32F4");
LCD_ShowString(30,70,200,16,16,"OV2640 RGB565 Mode");
LCD_ShowString(30,100,200,16,16,"KEY0:Contrast");
LCD_ShowString(30,130,200,16,16,"KEY1:Saturation");
LCD_ShowString(30,150,200,16,16,"KEY2:Effects");
LCD_ShowString(30,170,200,16,16,"KEY_UP:FullSize/Scale");
OV2640_RGB565_Mode();
My_DCMI_Init();
DCMI_DMA_Init((u32)&LCD->LCD_RAM,1,DMA_MemoryDataSize_HalfWord,DMA_MemoryInc_Disable);
OV2640_OutSize_Set(lcddev.width,lcddev.height);
DCMI_Start();
while(1)
{
key=KEY_Scan(0);
if(key)
{
DCMI_Stop();
switch(key)
{
case KEY0_PRES:
contrast++;
if(contrast>4)contrast=0;
OV2640_Contrast(contrast);
sprintf((char*)msgbuf,"Contrast:%d",(signed char)contrast-2);
break;
case KEY1_PRES:
saturation++;
if(saturation>4)saturation=0;
OV2640_Color_Saturation(saturation);
sprintf((char*)msgbuf,"Saturation:%d",(signed char)saturation-2);
break;
case KEY2_PRES:
effect++;
if(effect>6)effect=0;
OV2640_Special_Effects(effect);
sprintf((char*)msgbuf,"%s",EFFECTS_TBL[effect]);
break;
case WKUP_PRES:
scale=!scale;
if(scale==0)
{
OV2640_ImageWin_Set((1600-lcddev.width)/2,(1200-lcddev.height)/2,lcddev.width,lcddev.height);
OV2640_OutSize_Set(lcddev.width,lcddev.height);
sprintf((char*)msgbuf,"Full Size 1:1");
}else
{
OV2640_ImageWin_Set(0,0,1600,1200);
OV2640_OutSize_Set(lcddev.width,lcddev.height);
sprintf((char*)msgbuf,"Scale");
}
break;
}
LCD_ShowString(30,50,210,16,16,msgbuf);
delay_ms(800);
DCMI_Start();
}
delay_ms(10);
}
}
4、DCMI初始化
void My_DCMI_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOC|RCC_AHB1Periph_GPIOE, ENABLE);
RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_DCMI,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_6;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_11;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6;
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource4,GPIO_AF_DCMI);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_DCMI);
GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_DCMI);
GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_DCMI);
GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_DCMI);
GPIO_PinAFConfig(GPIOC,GPIO_PinSource8,GPIO_AF_DCMI);
GPIO_PinAFConfig(GPIOC,GPIO_PinSource9,GPIO_AF_DCMI);
GPIO_PinAFConfig(GPIOC,GPIO_PinSource11,GPIO_AF_DCMI);
GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_DCMI);
GPIO_PinAFConfig(GPIOE,GPIO_PinSource5,GPIO_AF_DCMI);
GPIO_PinAFConfig(GPIOE,GPIO_PinSource6,GPIO_AF_DCMI);
DCMI_DeInit();
DCMI_InitStructure.DCMI_CaptureMode=DCMI_CaptureMode_Continuous;
DCMI_InitStructure.DCMI_CaptureRate=DCMI_CaptureRate_All_Frame;
DCMI_InitStructure.DCMI_ExtendedDataMode= DCMI_ExtendedDataMode_8b;
DCMI_InitStructure.DCMI_HSPolarity= DCMI_HSPolarity_Low;
DCMI_InitStructure.DCMI_PCKPolarity= DCMI_PCKPolarity_Rising;
DCMI_InitStructure.DCMI_SynchroMode= DCMI_SynchroMode_Hardware;
DCMI_InitStructure.DCMI_VSPolarity=DCMI_VSPolarity_Low;
DCMI_Init(&DCMI_InitStructure);
DCMI_ITConfig(DCMI_IT_FRAME,ENABLE);
DCMI_Cmd(ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = DCMI_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
5、DCMI配置
void DCMI_DMA_Init(u32 DMA_Memory0BaseAddr,u16 DMA_BufferSize,u32 DMA_MemoryDataSize,u32 DMA_MemoryInc)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);
DMA_DeInit(DMA2_Stream1);
while (DMA_GetCmdStatus(DMA2_Stream1) != DISABLE){}
DMA_InitStructure.DMA_Channel = DMA_Channel_1;
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&DCMI->DR;
DMA_InitStructure.DMA_Memory0BaseAddr = DMA_Memory0BaseAddr;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = DMA_BufferSize;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
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);
}
6、DCMI开始传输
void DCMI_Start(void)
{
LCD_SetCursor(0,0);
LCD_WriteRAM_Prepare();
DMA_Cmd(DMA2_Stream1, ENABLE);
DCMI_CaptureCmd(ENABLE);
}
7、DCMI停止传输
void DCMI_Stop(void)
{
DCMI_CaptureCmd(DISABLE);
while(DCMI->CR&0X01);
DMA_Cmd(DMA2_Stream1,DISABLE);
}
8、DCMI中断服务函数
void DCMI_IRQHandler(void)
{
if(DCMI_GetITStatus(DCMI_IT_FRAME)==SET)
{
jpeg_data_process();
DCMI_ClearITPendingBit(DCMI_IT_FRAME);
LED1=!LED1;
ov_frame++;
}
}
|