当前位置:   article > 正文

学习笔记之STM32的ov7670摄像头实验

ov7670

目录

1. OV7670摄像头模块

1.1 OV7670 传感器内置功能模块

1.2 OV7670模块的引脚

 1.3 OV7670的时序图

1.4 OV7670的分辨率及其计算

2. FIFO模块

2.1 FIFO的简介

2.2 FIFO的信号

2.3 常用的FIFO数据存储器

3. BMP编码  

3.1 BMP文件的组成

3.2 BMP编码步骤

4. 摄像头实验

4.1 工作流程

4.2 主要函数

4.3 部分代码

5. 照相机实验

5.1 设计思路

5.2 主要函数

6. 监视器实验

6.1 设计思路

6.2 部分代码


1. OV7670摄像头模块

        OV7670是由OV(OmniVision)公司生产的一颗1/6寸的CMOS VGA图像传感器。该传感器体积小、工作电压低,提供单片VGA摄像头和影像处理器的所有功能。通过SCCB总线控制,可以输出整帧、子采样、取窗口等方式的各种分辨率8位影像数据。该产品VGA图像最高达到30帧/秒,用户可以完全控制图像质量、数据格式和传输方式。

1.1 OV7670 传感器内置功能模块

   1.感光整列(Image Array ) OV7670 总共有656*488 个像素,其中640*480 个有效(即有效像素为30W )。

    2.时序发生器(Video Timing Generator )

    时序发生器具有的功能包括:整列控制和帧率发生(7 种不同格式输出)、内部信号发生器和分布、帧率时序、自动曝光控制、输出外部时序(VSYNC 、HREF/HSYNC 和PCLK )。

    3.模拟信号处理(Analog Processing )

    模拟信号处理所有模拟功能,并包括:自动增益(AGC )和自动白平衡(AWB )。

    4.A/D  转换(A/D )

    原始的信号经过模拟处理器模块之后 ,分G 和BR 两路进入一个 10  位的A/D转换器, A/D 转换器工作在12M 频率,与像素频率完全同步(转换的频率和帧率有关)。

    除A/D 转换器外,该模块还有以下三个功能:

    l   黑电平校正(BLC)

    l   U/V 通道延迟 

    l   A/D 范围控制 

    A/D 范围乘积和A/D 的范围控制共同设置A/D 的范围和最大值,允许用户根据应用调整图片的亮度。

    5.测试图案发生器(Test Pattern Generator)

    测试图案发生器功能包括:八色彩色条图案、渐变至黑白彩色条图案和输出脚移位“1”。

    6.数字处理器(DSP )

    这个部分控制由原始信号插值到RGB信号的过程,并控制一些图像质量:

         边缘锐化(二维高通滤波器) 

    l   颜色空间转换( 原始信号到RGB或者YUV/YCbYCr)

    l   RGB 色彩矩阵以消除串扰 

    l   色相和饱和度的控制 

    l   黑/ 白点补偿 

    l   降噪 

    l   镜头补偿 

    l   可编程的伽玛 

    l   十位到八位数据转换 

    7.缩放功能(Image Scaler )

    这个模块按照预先设置的要求输出数据格式,能将YUV/RGB 信号从VGA 缩小到CIF以下的任何尺寸。

    8.数字视频接口(Digital Video Port )

    通过寄存器COM2[1:0],调节IOL/IOH 的驱动电流,以适应用户的负载。

    9.SCCB 接口(SCCB Interface)

    SCCB  接口控制图像传感器芯片的运行,详细使用方法参照光盘的《OmniVision Technologies Seril Camera Control Bus(SCCB) Specification》这个文档。

    值得注意的是SCCB接口与IIC很相似,在SCCB_clk高电平期间拉低SCCB_SDA会产生起始信号;在SCCB_clk高电平期间恢复SCCB_SDA高电平会产生起始信号。

    10.LED 和闪光灯的输出控制(LED and Storbe Flash Control Output )

    OV7670 有闪光灯模式,可以控制外接闪光灯或闪光LED 的工作;

    OV7670 的寄存器通过SCCB 时序访问并设置。

1.2 OV7670模块的引脚

图1 OV7670的引脚定义

 1.3 OV7670的时序图

 图2 ov7670输出时序图

        可以看出,图像数据在 HREF 为高的时候输出,当 HREF 变高后,每一个 PCLK 时钟,输出一个字节数据。采用 VGA 时序, RGB565 格式输出,每 2 个字节组成一个像素的颜色(高字节在前,低字节在后),这样每行输出总共有 640*2 PCLK 周期,输出 640*2 个字节。

  图3 ov7670帧时序图

        上图清楚的表示了OV7670 VGA 模式下的数据输出,注意,图中的 HSYNC HREF其实是同一个引脚产生的信号,只是在不同场合下面,使用不同的信号方式,我们用到的是HREF。1 个 HREF 周期由 640tp高电平和低电平 144tp组成。对于 YUV/RGB 模式,tP=2×tPCLK,即一个tPCLK对应传输一个字节(RGB565 格式传输一行数据的时间 T=640×2tPCLK)。其中 144tp 是传输一行数据的间隔时间。当传输了 480 个 HREF 周期(480×tLINE)后刚好完成一个 VSYNC(帧)数据传输,等 8 tLINE 后会产生一个 VSYNC 上升沿表示一帧数据传输完成。

1.4 OV7670的分辨率及其计算

        OV7670模块主要支持的分辨率:

        VGA:640*480的输出模式

        QVGA:320*240的输出模式

        QQVGA:160*120的输出模式

        在OV7670模块中,跟分辨率相关的寄存器有以下几个:

寄存器地址寄存器名称功能
0x17HSTART行帧开始高八位(低三位在HREF[2:0])
0x18HSTOP行帧结束高八位(低三位在HREF[5:3])
0x19VSTART场频开始高八位(低两位在VREF[1:0])
0x1AVSTOP场频结束高八位(低两位在VREF[3:2])
0x03VREF位[3:2]VREF结束低两位,位[1:0]VREF开始低两位
0x32HREF位[5:3]HREF结束低三位,位[2:0]VREF开始低三位

        网上一般给出的参数设置方式是:QVGA RGB565 320*240

        {0x17,0x17},

        {0x18,0x05},  

        {0x19,0x02},     

        {0x1A,0x7B}, 

        {0x03,0x0A}

        {0x32,0x80},

HREF的计算 :

        HREF = WEIGHT*2;每个像素点有两个数据

        HSTOP = START + HREF

PCLK的计算:

        VSYNC:510*Line = 3*tLINE+15*tLINE+480*tLINE

        HREF:784*tp = 640*tp+144*tp

        HSYNC:784*tp = 80*tp+45*tp+640*tp+19*tp

        VGA RGB565,YUV 30 fps

        PCLK = 784*510*30*2(byte)=24MHz

       VSTART和VSTOP决定了采样的行数也就是高度,VSTART = HEIGHE*2 +VSTOP

注:QVAG的时序宽度是VGA的两倍,因此若VGA下为640*480,则在QVAG下为320*240。若HSTOP >784,即超出周期,需要进行取余操作。

2. FIFO模块

         因为 OV7670 的像素时钟( PCLK )最高可达 24Mhz ,一般单片机(如 STM32) 的 IO 口直接抓取,是非常困难的,也十分占耗 CPU 所以我们的模块通过采用 FIFO 暂存取自于OV7670 的图像数据,它能完整的存一帧的图像数据 并且可以随时提取。

2.1 FIFO的简介

         FIFO是First In First Out的缩写,意思是先进先出。它是一种数据结构,类似于队列,用于暂存数据。在FPGA中,FIFO通常用作数据缓冲区,可以在数据读写之间提供临时存储。值得注意的是,FIFO只可以由内部数据地址自动+1, 不支持寻址读取

 图4 FIFO的读取示意图

        在使用FIFO使还要注意FIFO的深度和宽度:

        FIFO的宽度:用fifo_data_size表示,也就是每个数据的宽度

        FIFO的深度:用fifo_addr_size表示,也就是地址的大小(可以储存多少个数据)。

2.2 FIFO的信号

 图5 FIFO的示意图

写数据端口:

  • w_clk   :  写数据时钟信号
  • w_req   :  写请求信号
  • w_data :  要写入的数据

 读数据端口:

  • r_clk    :  读数据时钟信号
  • r_data :   读出的数据

        此外,FIFO还根据w_clk  和 r_clk 是否一致划分为SCFIFO (同步FIFO) 和DCFIFO (异步FIFO) 。同步FIFO是指读时钟和写时钟为同一个时钟,在时钟沿来临时同时发生读写操作。异步FIFO是指读写时钟不一致,读写时钟是互相独立的。

2.3 常用的FIFO数据存储器

        AL422内置了3M的DRAM,提供了50%以上的内存以支持数字PC图形或视频应用程序的高分辨率。目前常用的主要有以下三种型号:

        这里我们选用的是AL422B缓存芯片,如下图所示。

3. BMP编码  

        BMP(全称Bitmap)是Windows操作系统中的标准图像文件格式,可以分成两类:设备有向量相关位图(DDB)和设备无向量关位图(DIB),使用非常广。它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此,BMP文件所占用的空间很大。BMP文件的图像深度可选1bit、4bit、8bit及24bit。BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。由于BMP文件格式是Windows环境中交换与图有关的数据的一种标准,因此在Windows环境中运行的图形图像软件都支持BMP图像格式。

3.1 BMP文件的组成

       BMP是最简单的图片编码,是由文件头、位图信息头、颜色信息和图形数据等四部分组成。我们先来了解下这几个部分。

1、BMP 文件头(14 字节):BMP 文件头数据结构含有 BMP 文件的类型、文件大小和位图起始位置等信息。

  1. //BMP 文件头
  2. typedef __packed struct
  3. {
  4. u16 bfType ; //文件标志.只对'BM',用来识别 BMP 位图类型
  5. u32 bfSize ; //文件大小,占四个字节
  6. u16 bfReserved1 ; //保留
  7. u16 bfReserved2 ; //保留
  8. u32 bfOffBits ; //从文件开始到位图数据(bitmap data)开始之间的偏移量
  9. }BITMAPFILEHEADER ;

2、位图信息头(40 字节):BMP 位图信息头数据用于说明位图的尺寸等信息。

  1. typedef __packed struct
  2. {
  3. u32 biSize ; //说明 BITMAPINFOHEADER 结构所需要的字数。
  4. long biWidth ; //说明图象的宽度,以象素为单位
  5. long biHeight ; //说明图象的高度,以象素为单位
  6. u16 biPlanes ; //为目标设备说明位面数,其值将总是被设为 1
  7. u16 biBitCount ; //说明比特数/象素,其值为 1481624、或 32
  8. u32 biCompression ; //说明图象数据压缩的类型。其值可以是下述值之一:
  9. //BI_RGB:没有压缩;
  10. //BI_RLE8:每个象素 8 比特的 RLE 压缩编码,压缩格式由 2 字节组成
  11. //BI_RLE4:每个象素 4 比特的 RLE 压缩编码,压缩格式由 2 字节组成
  12. //BI_BITFIELDS:每个象素的比特由指定的掩码决定。
  13. u32 biSizeImage ;//说明图象的大小,以字节为单位。当用 BI_RGB 格式时,可设置为 0
  14. long biXPelsPerMeter ;//说明水平分辨率,用象素/米表示
  15. long biYPelsPerMeter ;//说明垂直分辨率,用象素/米表示
  16. u32 biClrUsed ; //说明位图实际使用的彩色表中的颜色索引数
  17. u32 biClrImportant ; //说明对图象显示有重要影响的颜色索引的数目,
  18. //如果是 0,表示都重要。
  19. }BITMAPINFOHEADER ;

3、颜色表(调色板):颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD 类型的结构,定义一种颜色。

  1. //颜色表
  2. typedef __packed struct
  3. {
  4. u8 rgbBlue ; //指定蓝色强度
  5. u8 rgbGreen ; //指定绿色强度
  6. u8 rgbRed ; //指定红色强度
  7. u8 rgbReserved ; //保留,设置为 0
  8. }RGBQUAD
  9. //BMP 文件头、位图信息头和颜色表组成位图信息
  10. typedef __packed struct
  11. {
  12. BITMAPFILEHEADER bmfHeader;
  13. BITMAPINFOHEADER bmiHeader;
  14. RGBQUAD bmiColors[1];
  15. }BITMAPINFO;

4、位图数据:位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。

        biBitCount=16 表示位图最多有 65536 种颜色,每个像素用 16 位(2 个字节)表示。当 biCompression=BI_RGB(0) 时,采用RGB555格式,最高位恒为0;当biCompression=BI_BITFIELDS(3) 时, 红、绿、蓝的掩码分别是:0x7C000x03E00x001F。我们需要设置 biBitCount 的值为 16,这样得到新的位图信息(BITMAPINFO)结构体:

  1. typedef __packed struct
  2. {
  3. BITMAPFILEHEADER bmfHeader;
  4. BITMAPINFOHEADER bmiHeader;
  5. u32 RGB_MASK[3]; //调色板用于存放 RGB 掩码.
  6. }BITMAPINFO;

3.2 BMP编码步骤

  1.         创建BMP位图信息,初始化各个相关部分
  2.         创建新的BMP文件,写入BMP位图信息
  3.         保存位图数据(从左到右,从下到上)
  4.         关闭文件

4. 摄像头实验

4.1 工作流程

摄像头模块存储图像数据的过程为

        等待 OV7725 帧同步信号(利用外部中断)FIFO 写指针复位FIFO写使能→等待第二个 OV7725 帧同步信号FIFO 写禁止。

注意:FIFO 写禁止操作不是必须的,只有当你想将一帧图片数据存储在 FIFO,并在外部 MCU 读取完这帧图片数据之前,不再采集新的图片数据的时候,才需要进行 FIFO 写禁止。

摄像头模块读取图像数据的过程为

        FIFO 读指针复位(FIFO_RRST=0)→给FIFO 读时钟(FIFO_RCLK读取第一个像素高字节FIFO 读时钟读取第一个像素低字节→FIFO 读时钟读取第二个像素高字节循环读取剩余像素结束。

4.2 主要函数

名称功能其他
OV7670_Init();定义I/O口
SCCB_Init();定义SCCB的时钟线和数据线
OV7670_Light_Mode(u8 mode);设置色彩白平衡
OV7670_Color_Saturation(u8 sat);设置色彩饱和度
OV7670_Brightness(u8 bright);设置色彩亮度
OV7670_Contrast(u8 contrast);设置色对比度
OV7670_Window_Set(u16 s_x,u16 s_y,u16 width,u16 height);设置图像显示范围设置QVAG
EXTIx_IRQHandler();帧中断标记,提醒MCU从FIFO取数据EXTI5-EXTI9共用一个中断程序
camer_refresh();

读取FIFO的数据并显示在LCD上

一帧要读取76800字节

4.3 部分代码

  1. u8 OV7670_Init(void)
  2. {
  3. u8 temp;
  4. u16 i=0;
  5. GPIO_InitTypeDef GPIO_InitStructure;
  6. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOG|RCC_APB2Periph_AFIO, ENABLE); //使能相关时钟
  7. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
  8. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
  9. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  10. GPIO_Init(GPIOA, &GPIO_InitStructure);
  11. GPIO_SetBits(GPIOA,GPIO_Pin_8);
  12. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4;
  13. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  14. GPIO_Init(GPIOB, &GPIO_InitStructure);
  15. GPIO_SetBits(GPIOB,GPIO_Pin_3|GPIO_Pin_4);
  16. GPIO_InitStructure.GPIO_Pin = 0xff;
  17. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
  18. GPIO_Init(GPIOC, &GPIO_InitStructure);
  19. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
  20. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  21. GPIO_Init(GPIOD, &GPIO_InitStructure);
  22. GPIO_SetBits(GPIOD,GPIO_Pin_6);
  23. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14|GPIO_Pin_15;
  24. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  25. GPIO_Init(GPIOG, &GPIO_InitStructure);
  26. GPIO_SetBits(GPIOG,GPIO_Pin_14|GPIO_Pin_15);
  27. GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); //SWD
  28. SCCB_Init(); //初始化SCCB的I/O口
  29. if(SCCB_WR_Reg(0x12,0x80))return 1; //复位SCCB
  30. delay_ms(50);
  31. //读取型号
  32. temp=SCCB_RD_Reg(0x0b);
  33. if(temp!=0x73)return 2;
  34. temp=SCCB_RD_Reg(0x0a);
  35. if(temp!=0x76)return 2;
  36. //初始化序列
  37. for(i=0;i<sizeof(ov7670_init_reg_tbl)/sizeof(ov7670_init_reg_tbl[0]);i++)
  38. {
  39. SCCB_WR_Reg(ov7670_init_reg_tbl[i][0],ov7670_init_reg_tbl[i][1]);
  40. }
  41. return 0x00; //ok
  42. }
  43. void OV7670_Window_Set(u16 sx,u16 sy,u16 width,u16 height) //设置输出窗口
  44. {
  45. u16 endx;
  46. u16 endy;
  47. u8 temp;
  48. endx=sx+width*2; //V*2
  49. endy=sy+height*2;
  50. if(endy>784)endy-=784;
  51. temp=SCCB_RD_Reg(0X03); //读取Vref之前的值
  52. temp&=0XF0;
  53. temp|=((endx&0X03)<<2)|(sx&0X03);
  54. SCCB_WR_Reg(0X03,temp); //设置Vref的startend的最低两位
  55. SCCB_WR_Reg(0X19,sx>>2); //设置Vref的start的高八位
  56. SCCB_WR_Reg(0X1A,endx>>2); //设置Vref的end的高八位
  57. temp=SCCB_RD_Reg(0X32); //读取Href的值
  58. temp&=0XC0;
  59. temp|=((endy&0X07)<<3)|(sy&0X07);
  60. SCCB_WR_Reg(0X17,sy>>3); //设置Href的start的高八位
  61. SCCB_WR_Reg(0X18,endy>>3); //设置Href的end的高八位
  62. }

5. 照相机实验

5.1 设计思路

        本次的照相机实验是基于上一部分的摄像头实验,并在其基础上增加了SD卡和FATFS新建/存储文件的部分代码,对摄像头、外部中断和部分输出引脚的初始化不再赘述。

        采集到图像→按键检测命令→利用f_open检测是否有命名,若没有创造路径→图像编码→图像数据保存。

5.2 主要函数

函数名称功能其他
bmp_encodeBMP编码截屏大小不能超过分辨率
camera_new_pathname创建一个新的图片文件
camera_refresh更新图像
f_open 打开/创建一个文件
f_write写文件

        这里的对SD卡操作主要是利用FATFS文件系统进行的,具体可以参考往期的笔记。

6. 监视器实验

6.1 设计思路

        通过上述的学习,笔者想到了利用超声波模块HC_SR04与OV7670进行结合,当有物体靠近时会自动进行拍照记录,后续也可以利用FATFS进行图片查看,从而达到监视器的作用。在工作流程上只比上例多一个距离检测及判断,整体的工程量并不是很大。

注:HC-SR04的使用方法不难,网上的例程很多便不再赘述了,同样的也可以使用红外模块等进行检测。

6.2 部分代码

  1. uint32_t Get_Timer() //获取传递时间
  2. {
  3. uint32_t t = 0;
  4. t = count*1000;
  5. t += TIM_GetCounter(TIM2);
  6. TIM2->CNT = 0;
  7. delay_ms(50);
  8. return t;
  9. }
  10. float Get_Length(){ //获取目标距离
  11. float t = 0,length = 0;
  12. GPIO_SetBits(GPIOx,trigGPIO);
  13. delay_us(20);
  14. GPIO_ResetBits(GPIOx,trigGPIO);
  15. while (GPIO_ReadInputDataBit(GPIOx,echoGPIO) == RESET){
  16. };
  17. OpenTIM2();
  18. while (GPIO_ReadInputDataBit(GPIOx,echoGPIO) == SET);
  19. CloseTIM2();
  20. t = Get_Timer() / 1000.0; //得出总传递时间
  21. length = t*34.0; //得出总传递路程
  22. return length/2; //返回实际距离
  23. }

具体代码可以点击链接:https://download.csdn.net/download/sincerelover/88760703

免责声明:本文所引用的各种资料均用于自己学习使用,这里感谢正点原子和野火官方的资料以及各位优秀的创作者。 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/526422
推荐阅读
相关标签
  

闽ICP备14008679号