目录
今天我们要学习的是OV2640摄像头实验,采用的是DCMI接口,进行传输。个人觉得难度较大,加上没有相应的串口线,导致部分实验无法进行,所以就先讲解理论知识,等串口线到了再把电脑端的实验补上。
摄像头实验
OV2640 是 OV(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 图像数据
- 自带嵌入式微处理器
- UXGA , 即 分 辨 率 位 1600*1200 的输出格式,类似的还有: SXGA(1280*1024) 、 WXGA+(1440*900)、XVGA(1280*960)、WXGA(1280*800)、XGA(1024*768)、SVGA(800*600)、 VGA(640*480)、CIF(352*288)、WQVGA(400*240)、QCIF(176*144)和 QQVGA(160*120)等。
- PCLK,即像素时钟,一个 PCLK 时钟,输出一个像素(或半个像素)。
- VSYNC,即帧同步信号。
- HREF /HSYNC,即行同步信号。
我们的数据输出(通过 Y[9:0]),就是通过PCLK,VSYNC和HREF /HSYNC来实现的,接下来我们就来讲解一下OV2640的时序部分。
图像数据在 HREF 为高的时候输出,当 HREF 变高后,每一个 PCLK 时钟,输出一个 8 位/10 位数据。我们采用 8 位接口,所以每个 PCLK 输出 1 个字节,且在 RGB/YUV 输出格式下,每个 tp=2 个 Tpclk,如果是 Raw 格式,则一个 tp=1 个 Tpclk。比如我们采用 UXGA 时序,RGB565 格式输出,每 2 个字节组成一个像素的颜色(高低字节顺序可通过 0XDA 寄存器设置),这样每行输出总共有 1600*2 个 PCLK 周期,输出 1600*2 个字节。
我们以UXGA举例, 首先VSYNC发送一个高电平数据表示起始信号,然后在HREF为高电平期间采集有效数据,一共1600条,然后重复1200次,就达到了输出的效果。
窗口设置
传感器窗口设置,该功能允许用户设置整个传感器区域(1632*1220)的感兴趣部分,也就是在传感器里面开窗,开窗范围从 2*2~1632*1220 都可以设置,不过要求这个窗口必须大于等于随后设置的图像尺寸。传感器窗口设置,通过:0X03/0X19/0X1A/0X07/0X17/0X18 等寄存器设置,使用的函数是OV2640_Window_Set。
图像尺寸设置,也就是 DSP 输出(最终输出到 LCD 的)图像的最大尺寸,该尺寸要小于等于前面我们传感器窗口设置所设定的窗口尺寸。图像尺寸通过:0XC0/0XC1/0X8C 等寄存器设置。使用的函数是OV2640_ImageSize_Set。
图像窗口设置,这里起始和前面的传感器窗口设置类似,只是这个窗口是在我们前面设置的图像尺寸里面,再一次设置窗口大小,该窗口必须小于等于前面设置的图像尺寸。该窗口设置后的图像范围,将用于输出到外部。图像窗口设置通过:0X51/0X52/0X53/0X54/0X55/0X57等寄存器设置。使用的函数是OV2640_ImageWin_Set。
图像输出大小设置,这是最终输出到外部的图像尺寸。该设置将图像窗口设置所决定的窗口大小,通过内部 DSP 处理,缩放成我们输出到外部的图像大小。该设置将会对图像进行缩放处理,如果设置的图像输出大小不等于图像窗口设置图像大小,那么图像就会被缩放处理,只有这两者设置一样大的时候,输出比例才是 1:1 的。使用的函数是OV2640_OutSize_Set。
整体效果如下所示
初始化
这是初始化的流程,首先初始化好IO口,然后上电复位,读取ID,执行初始化序列,这个是OV2640厂家做好的,我们就不需要重新写过了。
读取图像数据
STM32F4 自带了一个数字摄像头(DCMI)接口,该接口是一个同步并行接口,能够接收外部 8 位、10 位、12 位或 14 位 CMOS 摄像头模块发出的高速数据流。可支持不同的数据格式:YCbCr4:2:2/RGB565 逐行视频和压缩数据 (JPEG)。
DCMI 接口是一个同步并行接口,可接收高速(可达 54 MB/s)数据流。该接口包含多达14 条数据线(D13-D0)和一条像素时钟线(PIXCLK)。像素时钟的极性可以编程,因此可以在像素时钟的上升沿或下降沿捕获数据。
- 8 位、10 位、12 位或 14 位并行接口
- 内嵌码/外部行同步和帧同步
- 连续模式或快照模式
- 裁剪功能
- 支持以下数据格式:
- 8/10/12/14 位逐行视频:单色或原始拜尔(Bayer)格式
- YCbCr 4:2:2 逐行视频
- RGB 565 逐行视频
- 压缩数据:JPEG
- 数据输入(D[0:13]),用于接摄像头的数据输出,接 OV2640 我们只用了 8 位数据。
- 水平同步(行同步)输入(HSYNC),用于接摄像头的 HSYNC/HREF 信号。
- 垂直同步(场同步)输入(VSYNC),用于接摄像头的 VSYNC 信号。
- 像素时钟输入(PIXCLK),用于接摄像头的 PCLK 信号。
DCMI 接口的数据与 PIXCLK(即 PCLK)保持同步,并根据像素时钟的极性在像素时钟上升沿/下降沿发生变化。HSYNC(HREF)信号指示行的开始/结束,VSYNC 信号指示帧的开始/结束。DCMI 信号波形如图所示:
上图中,对应设置为:DCMI_PIXCLK 的捕获沿为下降沿,DCMI_HSYNC 和 DCMI_VSYNC 的有效状态为 1,注意,这里的有效状态实际上对应的是指示数据在并行接口上无效时, HSYNC/VSYNC 引脚上面的引脚电平。
我们用到 DCMI 的 8 位数据宽度,通过设置 DCMI_CR 中的 EDM[1:0]=00 设置。此时 DCMI_D0~D7 有效,DCMI_D8~D13 上的数据则忽略,这个时候,每次需要 4 个像素时钟来捕获一个 32 位数据。捕获的第一个数据存放在 32 位字的 LSB 位置,第四个数据存放在 32 位字的 MSB 位置 ,捕获数据字节在 32 位字中的排布如表所示:
DCMI 接口支持 DMA 传输,当 DCMI_CR 寄存器中的 CAPTURE 位置 1 时,激活 DMA 接口。摄像头接口每次在其寄存器中收到一个完整的 32 位数据块时,都将触发一个 DMA请求。
寄存器就不详细介绍了。
- 配置 OV2640 控制引脚,并配置 OV2640 工作模式。
- 配置相关引脚的模式和复用功能(AF13),使能时钟。
- 配置 DCMI 相关设置。
- 配置 DMA。
- 设置 OV2640 的图像输出大小,使能 DCMI 捕获。
- //dcmi.c
- #include "sys.h"
- #include "dcmi.h"
- #include "led.h"
- #include "ov2640.h"
-
- u8 ov_frame=0; //帧率
- extern void jpeg_data_process(void); //JPEG数据处理函数
-
- DCMI_InitTypeDef DCMI_InitStructure;
-
- //DCMI中断服务函数
- void DCMI_IRQHandler(void)
- {
- if(DCMI_GetITStatus(DCMI_IT_FRAME)==SET)//捕获到一帧图像
- {
- jpeg_data_process(); //jpeg数据处理
- DCMI_ClearITPendingBit(DCMI_IT_FRAME);//清除帧中断
- LED1=!LED1;
- ov_frame++;
- }
- }
- //DCMI DMA配置
- //DMA_Memory0BaseAddr:存储器地址 将要存储摄像头数据的内存地址(也可以是外设地址)
- //DMA_BufferSize:存储器长度 0~65535
- //DMA_MemoryDataSize:存储器位宽
- //DMA_MemoryDataSize:存储器位宽 @defgroup DMA_memory_data_size :DMA_MemoryDataSize_Byte/DMA_MemoryDataSize_HalfWord/DMA_MemoryDataSize_Word
- //DMA_MemoryInc:存储器增长方式 @defgroup DMA_memory_incremented_mode /** @defgroup DMA_memory_incremented_mode : DMA_MemoryInc_Enable/DMA_MemoryInc_Disable
- 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);//DMA2时钟使能
- DMA_DeInit(DMA2_Stream1);
- while (DMA_GetCmdStatus(DMA2_Stream1) != DISABLE){}//等待DMA2_Stream1可配置
-
- /* 配置 DMA Stream */
- DMA_InitStructure.DMA_Channel = DMA_Channel_1; //通道1 DCMI通道
- DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&DCMI->DR;//外设地址为:DCMI->DR
- DMA_InitStructure.DMA_Memory0BaseAddr = DMA_Memory0BaseAddr;//DMA 存储器0地址
- 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;//外设数据长度:32位
- 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; //FIFO模式
- DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;//使用全FIFO
- DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//外设突发单次传输
- DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//存储器突发单次传输
- DMA_Init(DMA2_Stream1, &DMA_InitStructure);//初始化DMA Stream
-
- }
- //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);//使能GPIOA B C E 时钟
- RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_DCMI,ENABLE);//使能DCMI时钟
- //PA4/6初始化设置
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_6;//PA4/6 复用功能输出
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能输出
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
- GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化
-
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_6;// PB6/7 复用功能输出
- GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
-
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_11;//PC6/7/8/9/11 复用功能输出
- GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化
-
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6;//PE5/6 复用功能输出
- GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化
-
- GPIO_PinAFConfig(GPIOA,GPIO_PinSource4,GPIO_AF_DCMI); //PA4,AF13 DCMI_HSYNC
- GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_DCMI); //PA6,AF13 DCMI_PCLK
- GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_DCMI); //PB7,AF13 DCMI_VSYNC
- GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_DCMI); //PC6,AF13 DCMI_D0
- GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_DCMI); //PC7,AF13 DCMI_D1
- GPIO_PinAFConfig(GPIOC,GPIO_PinSource8,GPIO_AF_DCMI); //PC8,AF13 DCMI_D2
- GPIO_PinAFConfig(GPIOC,GPIO_PinSource9,GPIO_AF_DCMI); //PC9,AF13 DCMI_D3
- GPIO_PinAFConfig(GPIOC,GPIO_PinSource11,GPIO_AF_DCMI);//PC11,AF13 DCMI_D4
- GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_DCMI); //PB6,AF13 DCMI_D5
- GPIO_PinAFConfig(GPIOE,GPIO_PinSource5,GPIO_AF_DCMI); //PE5,AF13 DCMI_D6
- GPIO_PinAFConfig(GPIOE,GPIO_PinSource6,GPIO_AF_DCMI); //PE6,AF13 DCMI_D7
-
-
- 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_ITConfig(DCMI_IT_FRAME,ENABLE);//开启帧中断
-
- DCMI_Cmd(ENABLE); //DCMI使能
-
- NVIC_InitStructure.NVIC_IRQChannel = DCMI_IRQn;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//抢占优先级1
- NVIC_InitStructure.NVIC_IRQChannelSubPriority =0; //子优先级3
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
- NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
-
- }
- //DCMI,启动传输
- void DCMI_Start(void)
- {
- DMA_Cmd(DMA2_Stream1, ENABLE);//开启DMA2,Stream1
- DCMI_CaptureCmd(ENABLE);//DCMI捕获使能
- }
- //DCMI,关闭传输
- void DCMI_Stop(void)
- {
- DCMI_CaptureCmd(DISABLE);//DCMI捕获使关闭
-
- while(DCMI->CR&0X01); //等待传输结束
-
- DMA_Cmd(DMA2_Stream1,DISABLE);//关闭DMA2,Stream1
- }
-
- //以下两个函数,供usmart调用,用于调试代码
-
- //DCMI设置显示窗口
- //sx,sy;LCD的起始坐标
- //width,height:LCD显示范围.
- void DCMI_Set_Window(u16 sx,u16 sy,u16 width,u16 height)
- {
- DCMI_Stop();
- // LCD_Clear(WHITE);
- // LCD_Set_Window(sx,sy,width,height);
- OV2640_OutSize_Set(width,height);
- // LCD_SetCursor(0,0);
- // LCD_WriteRAM_Prepare(); //开始写入GRAM
-
- DMA_Cmd(DMA2_Stream1,ENABLE); //开启DMA2,Stream1
-
- DCMI_CaptureCmd(ENABLE);//DCMI捕获使能
-
- }
-
- //通过usmart调试,辅助测试用.
- //pclk/hsync/vsync:三个信号的有限电平设置
- void DCMI_CR_Set(u8 pclk,u8 hsync,u8 vsync)
- {
- 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= hsync<<6;//HSYNC 低电平有效
- DCMI_InitStructure.DCMI_PCKPolarity= pclk<<5;//PCLK 上升沿有效
- DCMI_InitStructure.DCMI_SynchroMode= DCMI_SynchroMode_Hardware;//硬件同步HSYNC,VSYNC
- DCMI_InitStructure.DCMI_VSPolarity=vsync<<7;//VSYNC 低电平有效
- DCMI_Init(&DCMI_InitStructure);
-
- DCMI_CaptureCmd(ENABLE);//DCMI捕获使能
- DCMI_Cmd(ENABLE); //DCMI使能
-
- }
- // sccb.c
- #include "sys.h"
- #include "sccb.h"
- #include "delay.h"
-
- //初始化SCCB接口
- void SCCB_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
-
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);//使能GPIOD时钟
- //GPIOF9,F10初始化设置
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;//PD6,7 推挽输出
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //PD6,7 推挽输出
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//100MHz
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
- GPIO_Init(GPIOD, &GPIO_InitStructure);//初始化
-
- GPIO_SetBits(GPIOD,GPIO_Pin_6|GPIO_Pin_7);
- SCCB_SDA_OUT();
- }
-
- //SCCB起始信号
- //当时钟为高的时候,数据线的高到低,为SCCB起始信号
- //在激活状态下,SDA和SCL均为低电平
- void SCCB_Start(void)
- {
- SCCB_SDA=1; //数据线高电平
- SCCB_SCL=1; //在时钟线高的时候数据线由高至低
- delay_us(50);
- SCCB_SDA=0;
- delay_us(50);
- SCCB_SCL=0; //数据线恢复低电平,单操作函数必要
- }
-
- //SCCB停止信号
- //当时钟为高的时候,数据线的低到高,为SCCB停止信号
- //空闲状况下,SDA,SCL均为高电平
- void SCCB_Stop(void)
- {
- SCCB_SDA=0;
- delay_us(50);
- SCCB_SCL=1;
- delay_us(50);
- SCCB_SDA=1;
- delay_us(50);
- }
- //产生NA信号
- void SCCB_No_Ack(void)
- {
- delay_us(50);
- SCCB_SDA=1;
- SCCB_SCL=1;
- delay_us(50);
- SCCB_SCL=0;
- delay_us(50);
- SCCB_SDA=0;
- delay_us(50);
- }
- //SCCB,写入一个字节
- //返回值:0,成功;1,失败.
- u8 SCCB_WR_Byte(u8 dat)
- {
- u8 j,res;
- for(j=0;j<8;j++) //循环8次发送数据
- {
- if(dat&0x80)SCCB_SDA=1;
- else SCCB_SDA=0;
- dat<<=1;
- delay_us(50);
- SCCB_SCL=1;
- delay_us(50);
- SCCB_SCL=0;
- }
- SCCB_SDA_IN(); //设置SDA为输入
- delay_us(50);
- SCCB_SCL=1; //接收第九位,以判断是否发送成功
- delay_us(50);
- if(SCCB_READ_SDA)res=1; //SDA=1发送失败,返回1
- else res=0; //SDA=0发送成功,返回0
- SCCB_SCL=0;
- SCCB_SDA_OUT(); //设置SDA为输出
- return res;
- }
- //SCCB 读取一个字节
- //在SCL的上升沿,数据锁存
- //返回值:读到的数据
- u8 SCCB_RD_Byte(void)
- {
- u8 temp=0,j;
- SCCB_SDA_IN(); //设置SDA为输入
- for(j=8;j>0;j--) //循环8次接收数据
- {
- delay_us(50);
- SCCB_SCL=1;
- temp=temp<<1;
- if(SCCB_READ_SDA)temp++;
- delay_us(50);
- SCCB_SCL=0;
- }
- SCCB_SDA_OUT(); //设置SDA为输出
- return temp;
- }
- //写寄存器
- //返回值:0,成功;1,失败.
- u8 SCCB_WR_Reg(u8 reg,u8 data)
- {
- u8 res=0;
- SCCB_Start(); //启动SCCB传输
- if(SCCB_WR_Byte(SCCB_ID))res=1; //写器件ID
- delay_us(100);
- if(SCCB_WR_Byte(reg))res=1; //写寄存器地址
- delay_us(100);
- if(SCCB_WR_Byte(data))res=1; //写数据
- SCCB_Stop();
- return res;
- }
- //读寄存器
- //返回值:读到的寄存器值
- u8 SCCB_RD_Reg(u8 reg)
- {
- u8 val=0;
- SCCB_Start(); //启动SCCB传输
- SCCB_WR_Byte(SCCB_ID); //写器件ID
- delay_us(100);
- SCCB_WR_Byte(reg); //写寄存器地址
- delay_us(100);
- SCCB_Stop();
- delay_us(100);
- //设置寄存器地址后,才是读
- SCCB_Start();
- SCCB_WR_Byte(SCCB_ID|0X01); //发送读命令
- delay_us(100);
- val=SCCB_RD_Byte(); //读取数据
- SCCB_No_Ack();
- SCCB_Stop();
- return val;
- }
- //ov2640.c
- #include "sys.h"
- #include "ov2640.h"
- #include "ov2640cfg.h"
- #include "timer.h"
- #include "delay.h"
- #include "usart.h"
- #include "sccb.h"
-
- //初始化OV2640
- //配置完以后,默认输出是1600*1200尺寸的图片!!
- //返回值:0,成功
- // 其他,错误代码
- u8 OV2640_Init(void)
- {
- u16 i=0;
- u16 reg;
- //设置IO
- GPIO_InitTypeDef GPIO_InitStructure;
-
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);
- //GPIOG9,15初始化设置
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_15;//PG9,15推挽输出
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //推挽输出
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//100MHz
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
- GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化
-
- OV2640_PWDN=0; //POWER ON
- delay_ms(10);
- OV2640_RST=0; //复位OV2640
- delay_ms(10);
- OV2640_RST=1; //结束复位
- SCCB_Init(); //初始化SCCB 的IO口
- SCCB_WR_Reg(OV2640_DSP_RA_DLMT, 0x01); //操作sensor寄存器
- SCCB_WR_Reg(OV2640_SENSOR_COM7, 0x80); //软复位OV2640
- delay_ms(50);
- reg=SCCB_RD_Reg(OV2640_SENSOR_MIDH); //读取厂家ID 高八位
- reg<<=8;
- reg|=SCCB_RD_Reg(OV2640_SENSOR_MIDL); //读取厂家ID 低八位
- if(reg!=OV2640_MID)
- {
- printf("MID:%d\r\n",reg);
- return 1;
- }
- reg=SCCB_RD_Reg(OV2640_SENSOR_PIDH); //读取厂家ID 高八位
- reg<<=8;
- reg|=SCCB_RD_Reg(OV2640_SENSOR_PIDL); //读取厂家ID 低八位
- if(reg!=OV2640_PID)
- {
- printf("HID:%d\r\n",reg);
- return 2;
- }
- //初始化 OV2640,采用UXGA分辨率(1600*1200)
- for(i=0;i<sizeof(ov2640_uxga_init_reg_tbl)/2;i++)
- {
- SCCB_WR_Reg(ov2640_uxga_init_reg_tbl[i][0],ov2640_uxga_init_reg_tbl[i][1]);
- }
- return 0x00; //ok
- }
- //OV2640切换为JPEG模式
- void OV2640_JPEG_Mode(void)
- {
- u16 i=0;
- //设置:YUV422格式
- for(i=0;i<(sizeof(ov2640_yuv422_reg_tbl)/2);i++)
- {
- SCCB_WR_Reg(ov2640_yuv422_reg_tbl[i][0],ov2640_yuv422_reg_tbl[i][1]);
- }
-
- //设置:输出JPEG数据
- for(i=0;i<(sizeof(ov2640_jpeg_reg_tbl)/2);i++)
- {
- SCCB_WR_Reg(ov2640_jpeg_reg_tbl[i][0],ov2640_jpeg_reg_tbl[i][1]);
- }
- }
- //OV2640切换为RGB565模式
- void OV2640_RGB565_Mode(void)
- {
- u16 i=0;
- //设置:RGB565输出
- for(i=0;i<(sizeof(ov2640_rgb565_reg_tbl)/2);i++)
- {
- SCCB_WR_Reg(ov2640_rgb565_reg_tbl[i][0],ov2640_rgb565_reg_tbl[i][1]);
- }
- }
- //自动曝光设置参数表,支持5个等级
- const static u8 OV2640_AUTOEXPOSURE_LEVEL[5][8]=
- {
- {
- 0xFF,0x01,
- 0x24,0x20,
- 0x25,0x18,
- 0x26,0x60,
- },
- {
- 0xFF,0x01,
- 0x24,0x34,
- 0x25,0x1c,
- 0x26,0x00,
- },
- {
- 0xFF,0x01,
- 0x24,0x3e,
- 0x25,0x38,
- 0x26,0x81,
- },
- {
- 0xFF,0x01,
- 0x24,0x48,
- 0x25,0x40,
- 0x26,0x81,
- },
- {
- 0xFF,0x01,
- 0x24,0x58,
- 0x25,0x50,
- 0x26,0x92,
- },
- };
- //OV2640自动曝光等级设置
- //level:0~4
- void OV2640_Auto_Exposure(u8 level)
- {
- u8 i;
- u8 *p=(u8*)OV2640_AUTOEXPOSURE_LEVEL[level];
- for(i=0;i<4;i++)
- {
- SCCB_WR_Reg(p[i*2],p[i*2+1]);
- }
- }
- //白平衡设置
- //0:自动
- //1:太阳sunny
- //2,阴天cloudy
- //3,办公室office
- //4,家里home
- void OV2640_Light_Mode(u8 mode)
- {
- u8 regccval=0X5E;//Sunny
- u8 regcdval=0X41;
- u8 regceval=0X54;
- switch(mode)
- {
- case 0://auto
- SCCB_WR_Reg(0XFF,0X00);
- SCCB_WR_Reg(0XC7,0X10);//AWB ON
- return;
- case 2://cloudy
- regccval=0X65;
- regcdval=0X41;
- regceval=0X4F;
- break;
- case 3://office
- regccval=0X52;
- regcdval=0X41;
- regceval=0X66;
- break;
- case 4://home
- regccval=0X42;
- regcdval=0X3F;
- regceval=0X71;
- break;
- }
- SCCB_WR_Reg(0XFF,0X00);
- SCCB_WR_Reg(0XC7,0X40); //AWB OFF
- SCCB_WR_Reg(0XCC,regccval);
- SCCB_WR_Reg(0XCD,regcdval);
- SCCB_WR_Reg(0XCE,regceval);
- }
- //色度设置
- //0:-2
- //1:-1
- //2,0
- //3,+1
- //4,+2
- void OV2640_Color_Saturation(u8 sat)
- {
- u8 reg7dval=((sat+2)<<4)|0X08;
- SCCB_WR_Reg(0XFF,0X00);
- SCCB_WR_Reg(0X7C,0X00);
- SCCB_WR_Reg(0X7D,0X02);
- SCCB_WR_Reg(0X7C,0X03);
- SCCB_WR_Reg(0X7D,reg7dval);
- SCCB_WR_Reg(0X7D,reg7dval);
- }
- //亮度设置
- //0:(0X00)-2
- //1:(0X10)-1
- //2,(0X20) 0
- //3,(0X30)+1
- //4,(0X40)+2
- void OV2640_Brightness(u8 bright)
- {
- SCCB_WR_Reg(0xff, 0x00);
- SCCB_WR_Reg(0x7c, 0x00);
- SCCB_WR_Reg(0x7d, 0x04);
- SCCB_WR_Reg(0x7c, 0x09);
- SCCB_WR_Reg(0x7d, bright<<4);
- SCCB_WR_Reg(0x7d, 0x00);
- }
- //对比度设置
- //0:-2
- //1:-1
- //2,0
- //3,+1
- //4,+2
- void OV2640_Contrast(u8 contrast)
- {
- u8 reg7d0val=0X20;//默认为普通模式
- u8 reg7d1val=0X20;
- switch(contrast)
- {
- case 0://-2
- reg7d0val=0X18;
- reg7d1val=0X34;
- break;
- case 1://-1
- reg7d0val=0X1C;
- reg7d1val=0X2A;
- break;
- case 3://1
- reg7d0val=0X24;
- reg7d1val=0X16;
- break;
- case 4://2
- reg7d0val=0X28;
- reg7d1val=0X0C;
- break;
- }
- SCCB_WR_Reg(0xff,0x00);
- SCCB_WR_Reg(0x7c,0x00);
- SCCB_WR_Reg(0x7d,0x04);
- SCCB_WR_Reg(0x7c,0x07);
- SCCB_WR_Reg(0x7d,0x20);
- SCCB_WR_Reg(0x7d,reg7d0val);
- SCCB_WR_Reg(0x7d,reg7d1val);
- SCCB_WR_Reg(0x7d,0x06);
- }
- //特效设置
- //0:普通模式
- //1,负片
- //2,黑白
- //3,偏红色
- //4,偏绿色
- //5,偏蓝色
- //6,复古
- void OV2640_Special_Effects(u8 eft)
- {
- u8 reg7d0val=0X00;//默认为普通模式
- u8 reg7d1val=0X80;
- u8 reg7d2val=0X80;
- switch(eft)
- {
- case 1://负片
- reg7d0val=0X40;
- break;
- case 2://黑白
- reg7d0val=0X18;
- break;
- case 3://偏红色
- reg7d0val=0X18;
- reg7d1val=0X40;
- reg7d2val=0XC0;
- break;
- case 4://偏绿色
- reg7d0val=0X18;
- reg7d1val=0X40;
- reg7d2val=0X40;
- break;
- case 5://偏蓝色
- reg7d0val=0X18;
- reg7d1val=0XA0;
- reg7d2val=0X40;
- break;
- case 6://复古
- reg7d0val=0X18;
- reg7d1val=0X40;
- reg7d2val=0XA6;
- break;
- }
- SCCB_WR_Reg(0xff,0x00);
- SCCB_WR_Reg(0x7c,0x00);
- SCCB_WR_Reg(0x7d,reg7d0val);
- SCCB_WR_Reg(0x7c,0x05);
- SCCB_WR_Reg(0x7d,reg7d1val);
- SCCB_WR_Reg(0x7d,reg7d2val);
- }
- //彩条测试
- //sw:0,关闭彩条
- // 1,开启彩条(注意OV2640的彩条是叠加在图像上面的)
- void OV2640_Color_Bar(u8 sw)
- {
- u8 reg;
- SCCB_WR_Reg(0XFF,0X01);
- reg=SCCB_RD_Reg(0X12);
- reg&=~(1<<1);
- if(sw)reg|=1<<1;
- SCCB_WR_Reg(0X12,reg);
- }
- //设置图像输出窗口
- //sx,sy,起始地址
- //width,height:宽度(对应:horizontal)和高度(对应:vertical)
- void OV2640_Window_Set(u16 sx,u16 sy,u16 width,u16 height)
- {
- u16 endx;
- u16 endy;
- u8 temp;
- endx=sx+width/2; //V*2
- endy=sy+height/2;
-
- SCCB_WR_Reg(0XFF,0X01);
- temp=SCCB_RD_Reg(0X03); //读取Vref之前的值
- temp&=0XF0;
- temp|=((endy&0X03)<<2)|(sy&0X03);
- SCCB_WR_Reg(0X03,temp); //设置Vref的start和end的最低2位
- SCCB_WR_Reg(0X19,sy>>2); //设置Vref的start高8位
- SCCB_WR_Reg(0X1A,endy>>2); //设置Vref的end的高8位
-
- temp=SCCB_RD_Reg(0X32); //读取Href之前的值
- temp&=0XC0;
- temp|=((endx&0X07)<<3)|(sx&0X07);
- SCCB_WR_Reg(0X32,temp); //设置Href的start和end的最低3位
- SCCB_WR_Reg(0X17,sx>>3); //设置Href的start高8位
- SCCB_WR_Reg(0X18,endx>>3); //设置Href的end的高8位
- }
- //设置图像输出大小
- //OV2640输出图像的大小(分辨率),完全由改函数确定
- //width,height:宽度(对应:horizontal)和高度(对应:vertical),width和height必须是4的倍数
- //返回值:0,设置成功
- // 其他,设置失败
- u8 OV2640_OutSize_Set(u16 width,u16 height)
- {
- u16 outh;
- u16 outw;
- u8 temp;
- if(width%4)return 1;
- if(height%4)return 2;
- outw=width/4;
- outh=height/4;
- SCCB_WR_Reg(0XFF,0X00);
- SCCB_WR_Reg(0XE0,0X04);
- SCCB_WR_Reg(0X5A,outw&0XFF); //设置OUTW的低八位
- SCCB_WR_Reg(0X5B,outh&0XFF); //设置OUTH的低八位
- temp=(outw>>8)&0X03;
- temp|=(outh>>6)&0X04;
- SCCB_WR_Reg(0X5C,temp); //设置OUTH/OUTW的高位
- SCCB_WR_Reg(0XE0,0X00);
- return 0;
- }
- //设置图像开窗大小
- //由:OV2640_ImageSize_Set确定传感器输出分辨率从大小.
- //该函数则在这个范围上面进行开窗,用于OV2640_OutSize_Set的输出
- //注意:本函数的宽度和高度,必须大于等于OV2640_OutSize_Set函数的宽度和高度
- // OV2640_OutSize_Set设置的宽度和高度,根据本函数设置的宽度和高度,由DSP
- // 自动计算缩放比例,输出给外部设备.
- //width,height:宽度(对应:horizontal)和高度(对应:vertical),width和height必须是4的倍数
- //返回值:0,设置成功
- // 其他,设置失败
- u8 OV2640_ImageWin_Set(u16 offx,u16 offy,u16 width,u16 height)
- {
- u16 hsize;
- u16 vsize;
- u8 temp;
- if(width%4)return 1;
- if(height%4)return 2;
- hsize=width/4;
- vsize=height/4;
- SCCB_WR_Reg(0XFF,0X00);
- SCCB_WR_Reg(0XE0,0X04);
- SCCB_WR_Reg(0X51,hsize&0XFF); //设置H_SIZE的低八位
- SCCB_WR_Reg(0X52,vsize&0XFF); //设置V_SIZE的低八位
- SCCB_WR_Reg(0X53,offx&0XFF); //设置offx的低八位
- SCCB_WR_Reg(0X54,offy&0XFF); //设置offy的低八位
- temp=(vsize>>1)&0X80;
- temp|=(offy>>4)&0X70;
- temp|=(hsize>>5)&0X08;
- temp|=(offx>>8)&0X07;
- SCCB_WR_Reg(0X55,temp); //设置H_SIZE/V_SIZE/OFFX,OFFY的高位
- SCCB_WR_Reg(0X57,(hsize>>2)&0X80); //设置H_SIZE/V_SIZE/OFFX,OFFY的高位
- SCCB_WR_Reg(0XE0,0X00);
- return 0;
- }
- //该函数设置图像尺寸大小,也就是所选格式的输出分辨率
- //UXGA:1600*1200,SVGA:800*600,CIF:352*288
- //width,height:图像宽度和图像高度
- //返回值:0,设置成功
- // 其他,设置失败
- u8 OV2640_ImageSize_Set(u16 width,u16 height)
- {
- u8 temp;
- SCCB_WR_Reg(0XFF,0X00);
- SCCB_WR_Reg(0XE0,0X04);
- SCCB_WR_Reg(0XC0,(width)>>3&0XFF); //设置HSIZE的10:3位
- SCCB_WR_Reg(0XC1,(height)>>3&0XFF); //设置VSIZE的10:3位
- temp=(width&0X07)<<3;
- temp|=height&0X07;
- temp|=(width>>4)&0X80;
- SCCB_WR_Reg(0X8C,temp);
- SCCB_WR_Reg(0XE0,0X00);
- return 0;
- }
- // main.c
- #include "sys.h"
- #include "delay.h"
- #include "usart.h"
- #include "led.h"
- #include "key.h"
- #include "usmart.h"
- #include "usart2.h"
- #include "timer.h"
- #include "ov2640.h"
- #include "dcmi.h"
-
-
-
-
- u8 ov2640_mode=0; //工作模式:0,RGB565模式;1,JPEG模式
-
- #define jpeg_buf_size 31*1024 //定义JPEG数据缓存jpeg_buf的大小(*4字节)
- __align(4) u32 jpeg_buf[jpeg_buf_size]; //JPEG数据缓存buf
- volatile u32 jpeg_data_len=0; //buf中的JPEG有效数据长度
- volatile u8 jpeg_data_ok=0; //JPEG数据采集完成标志
- //0,数据没有采集完;
- //1,数据采集完了,但是还没处理;
- //2,数据已经处理完成了,可以开始下一帧接收
- //JPEG尺寸支持列表
- const u16 jpeg_img_size_tbl[][2]=
- {
- 176,144, //QCIF
- 160,120, //QQVGA
- 352,288, //CIF
- 320,240, //QVGA
- 640,480, //VGA
- 800,600, //SVGA
- 1024,768, //XGA
- 1280,1024, //SXGA
- 1600,1200, //UXGA
- };
- const u8*EFFECTS_TBL[7]={"Normal","Negative","B&W","Redish","Greenish","Bluish","Antique"}; //7种特效
- const u8*JPEG_SIZE_TBL[9]={"QCIF","QQVGA","CIF","QVGA","VGA","SVGA","XGA","SXGA","UXGA"}; //JPEG图片 9种尺寸
-
-
- //处理JPEG数据
- //当采集完一帧JPEG数据后,调用此函数,切换JPEG BUF.开始下一帧采集.
- void jpeg_data_process(void)
- {
- if(ov2640_mode)//只有在JPEG格式下,才需要做处理.
- {
- if(jpeg_data_ok==0) //jpeg数据还未采集完?
- {
- DMA_Cmd(DMA2_Stream1, DISABLE);//停止当前传输
- while (DMA_GetCmdStatus(DMA2_Stream1) != DISABLE){}//等待DMA2_Stream1可配置
- jpeg_data_len=jpeg_buf_size-DMA_GetCurrDataCounter(DMA2_Stream1);//得到此次数据传输的长度
-
- jpeg_data_ok=1; //标记JPEG数据采集完按成,等待其他函数处理
- }
- if(jpeg_data_ok==2) //上一次的jpeg数据已经被处理了
- {
- DMA2_Stream1->NDTR=jpeg_buf_size;
- DMA_SetCurrDataCounter(DMA2_Stream1,jpeg_buf_size);//传输长度为jpeg_buf_size*4字节
- DMA_Cmd(DMA2_Stream1, ENABLE); //重新传输
- jpeg_data_ok=0; //标记数据未采集
- }
- }
- }
- //JPEG测试
- //JPEG数据,通过串口2发送给电脑.
- void jpeg_test(void)
- {
- u32 i;
- u8 *p;
- u8 key;
- u8 effect=0,saturation=2,contrast=2;
- u8 size=8; //默认是UXGA
- u8 msgbuf[15]; //消息缓存区
- printf ("OV2640 JPEG Mode");
- printf ("\r\n\r\n");
- printf("KEY0:Contrast"); //对比度
- printf ("\r\n\r\n");
- printf("KEY1:Saturation"); //色彩饱和度
- printf ("\r\n\r\n");
- printf("KEY2:Effects"); //特效
- printf ("\r\n\r\n");
- printf("KEY_UP:Size"); //分辨率设置
- printf ("\r\n\r\n");
- sprintf((char*)msgbuf,"JPEG Size:%s",JPEG_SIZE_TBL[size]);
- printf ("%d",msgbuf); //显示当前JPEG分辨率
- printf ("\r\n\r\n");
-
- OV2640_JPEG_Mode(); //JPEG模式
- My_DCMI_Init(); //DCMI配置
- DCMI_DMA_Init((u32)&jpeg_buf,jpeg_buf_size,DMA_MemoryDataSize_Word,DMA_MemoryInc_Enable);//DCMI DMA配置
- 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;
- printf ("Sending JPEG data..."); //提示正在传输数据
- printf ("\r\n\r\n");
- for(i=0;i
4;i++) //dma传输1次等于4字节,所以乘以4. - {
- while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET); //循环发送,直到发送完毕
- USART_SendData(USART2,p[i]);
- key=KEY_Scan(0);
- if(key)break;
- }
- if(key) //有按键按下,需要处理
- {
- printf ("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);
- printf ("\r\n\r\n");
- break;
- case KEY1_PRES: //饱和度Saturation
- saturation++;
- if(saturation>4)saturation=0;
- OV2640_Color_Saturation(saturation);
- sprintf((char*)msgbuf,"Saturation:%d",(signed char)saturation-2);
- printf ("\r\n\r\n");
- break;
- case KEY2_PRES: //特效设置
- effect++;
- if(effect>6)effect=0;
- OV2640_Special_Effects(effect);//设置特效
- sprintf((char*)msgbuf,"%s",EFFECTS_TBL[effect]);
- printf ("\r\n\r\n");
- break;
- case WKUP_PRES: //JPEG输出尺寸设置
- 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]);
- printf ("\r\n\r\n");
- break;
- }
- printf ("%d",msgbuf);//显示提示内容
- printf ("\r\n\r\n");
- delay_ms(800);
- }else printf ("Send data complete!!");//提示传输结束设置
- jpeg_data_ok=2; //标记jpeg数据处理完了,可以让DMA去采集下一帧了.
- }
- }
- }
-
- int main(void)
- {
- u8 key;
- u8 t;
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
- delay_init(168); //初始化延时函数
- uart_init(115200); //初始化串口波特率为115200
- usart2_init(42,115200); //初始化串口2波特率为115200
- LED_Init(); //初始化LED
- KEY_Init(); //按键初始化
- TIM3_Int_Init(10000-1,8400-1);//10Khz计数,1秒钟中断一次
-
- usmart_dev.init(84); //初始化USMART
- while(OV2640_Init())//初始化OV2640
- {
- printf ("OV2640 ERR");
- printf ("\r\n\r\n");
- delay_ms(200);
- }
- printf("OV2640 OK");
- printf ("\r\n\r\n");
- printf ("KEY0:RGB565 KEY1:JPEG");
- printf ("\r\n\r\n");
- while(1)
- {
-
- key=KEY_Scan(0);
- if(key==KEY0_PRES) //RGB565模式
- {
- ov2640_mode=0;
- break;
- }else if(key==KEY1_PRES) //JPEG模式
- {
- ov2640_mode=1;
- break;
- }
- t++;
- delay_ms(5);
- }
- if(ov2640_mode)jpeg_test();
- // else rgb565_test();
- }
OV2640摄像头的知识点比较的多,而且目前只有显示屏,所以不能在电脑端显示图像,后期串口线到了之后再补上。