(40)STM32——OV2640摄像头实验
目录
学习目标
运行结果
内容
OV2640
特点
时序
帧输出时序
配置
DCMI
特点
信号
DMA
寄存器
配置
硬件连接
代码
总结
学习目标
今天我们要学习的是OV2640摄像头实验,采用的是DCMI接口,进行传输。个人觉得难度较大,加上没有相应的串口线,导致部分实验无法进行,所以就先讲解理论知识,等串口线到了再把电脑端的实验补上。
运行结果
摄像头实验
内容
OV2640
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厂家做好的,我们就不需要重新写过了。
读取图像数据
DCMI
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 位字中的排布如表所示:
DMA
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->DRDMA_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;//100MHzGPIO_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_HSYNCGPIO_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_D2GPIO_PinAFConfig(GPIOC,GPIO_PinSource9,GPIO_AF_DCMI); //PC9,AF13 DCMI_D3GPIO_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_D6GPIO_PinAFConfig(GPIOE,GPIO_PinSource6,GPIO_AF_DCMI); //PE6,AF13 DCMI_D7DCMI_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,VSYNCDCMI_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;//抢占优先级1NVIC_InitStructure.NVIC_IRQChannelSubPriority =0; //子优先级3NVIC_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(); //开始写入GRAMDMA_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,VSYNCDCMI_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;//100MHzGPIO_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发送失败,返回1else res=0; //SDA=0发送成功,返回0SCCB_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;//100MHzGPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化OV2640_PWDN=0; //POWER ONdelay_ms(10);OV2640_RST=0; //复位OV2640delay_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); //软复位OV2640delay_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://cloudyregccval=0X65;regcdval=0X41;regceval=0X4F;break; case 3://officeregccval=0X52;regcdval=0X41;regceval=0X66;break; case 4://homeregccval=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://-2reg7d0val=0X18; reg7d1val=0X34; break; case 1://-1reg7d0val=0X1C; reg7d1val=0X2A; break; case 3://1reg7d0val=0X24; reg7d1val=0X16; break; case 4://2reg7d0val=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*2endy=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, //QCIF160,120, //QQVGA352,288, //CIF320,240, //QVGA640,480, //VGA800,600, //SVGA1024,768, //XGA1280,1024, //SXGA1600,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; //默认是UXGAu8 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<jpeg_data_len*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: //饱和度Saturationsaturation++;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);//设置系统中断优先级分组2delay_init(168); //初始化延时函数uart_init(115200); //初始化串口波特率为115200usart2_init(42,115200); //初始化串口2波特率为115200LED_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摄像头的知识点比较的多,而且目前只有显示屏,所以不能在电脑端显示图像,后期串口线到了之后再补上。
(40)STM32——OV2640摄像头实验相关推荐
- STM32——OV2640摄像头实验
一.硬件连接原理图 实物图: 二.OV2640驱动代码 1.SCCB_Init函数 //初始化SCCB接口 void SCCB_Init(void) {GPIO_InitTypeDef GPIO_In ...
- STM32摄像头实验相关源码分享[一、颜色识别]
要每天养成写博客的习惯,可是,苦于最近学习没有进展啊. 无奈把自己去年学习STM32以及做相关项目时的代码发出来水水博客吧! 本代码在原子哥摄像头实验基础上做了相关更改. color.c部分[用于设定 ...
- STM32 F4 OV2640摄像头学习笔记(一)
//尝试写作,记录学习过程. 一.OV2640摄像头简介 本节将主要讲解OV2640摄像头基础部分和一些引脚配置. OV2640传感器简介 OV2640是Omni Vision公司生产的一颗1/4寸的 ...
- 【STM32】OV2640摄像头学习笔记 转
[STM32]OV2640摄像头学习笔记 2019年03月03日 13:01:35 淹死的大白鲨 阅读数 4736更多 分类专栏: [STM32] 版权声明:本文为博主原创文章,遵循 CC 4.0 B ...
- STM32摄像头实验
探索者 STM32F4 开发板的摄像头接口与 ALIENTEK OV2640 摄像头模块 的连接.在开发板的左下角的 2*9 的 P8 排座,是摄像头模块/OLED 模块共用接口, 特别注意:DCMI ...
- 基于STM32F407摄像头实验(有代码)
1.OV2640 简介 OV2640 是 OV(OmniVision)公司生产的一颗 1/4 寸的 CMOS UXGA(16321232)图像 传感器.该传感器体积小.工作电压低,提供单片 UXGA ...
- STM32F407获取OV2640摄像头图像及上位机解码(一维码二维码)
STM32F407获取OV2640摄像头图像及上位机解码(一维码&二维码) 1. 目的 针对静止拍摄图像场景,实现STM32F407对200万像素OV2640摄像头进行图像捕获,并通过串口将数 ...
- STM32H750获取OV2640摄像头图像及上位机解码(一维码二维码)
STM32H750获取OV2640摄像头图像及上位机解码(一维码&二维码) 1. 目的 针对静止拍摄图像场景,实现STM32H750对200万像素OV2640摄像头进行图像捕获,并通过串口将数 ...
- STM32 OV7725摄像头模块的颜色处理和简单物体识别(串口输出图片)
目录 前言 一.摄像头采集数据流程 二.如何将图像显示到电脑上 三.图像二值化 1.什么是RGB? 2.RGB565转RGB888 I.RGB565和RGB888的区别 II.代码 3.RGB转HSL ...
最新文章
- Android 开发中的多线程编程技术
- android 减少布局层级,Android 布局优化
- Opencv判断是否加载图片的两种方法
- python爬虫天气预报_Python爬虫实例扒取2345天气预报
- Java:数列排序 给定一个长度为n的数列,将这个数列按从小到大的顺序排列。1<=n<=200
- 1452.接水问题(思维)
- SpringSecurity分布式整合之认证服务配置文件编写和测试
- 自己遇到oracle的错误记录
- C#实现软键盘的几个关键技术实现方法
- iphone最新款手机_苹果罕见“跌停”!遭遇6年来最惨淡一夜,2019年全球股市第一颗雷引爆...
- am82.top 1.php,Droppy v2.1.3 – PHP在线网盘系统
- java socket 浏览器_Socket实现Java和浏览器交互。
- 学习python课程_想学习Python吗? 这是我们的免费4小时互动课程
- java编码native2ascii下载_使用native2ascii 中文字符与Unicode编码相互转换
- 基于智能电网的电力线载波通信研究
- Python查询快递订单信息
- 初创企业选择阿里云服务器与传统自建服务器的对比与选择
- React源码解毒 - 检测开发者是否错误的使用了props属性
- 电脑白屏或黑屏解决方法
- 发现了一个好用的WEB项目打印控件--四方打印