VerilogFPGA驱动Ov7670/Ov7725搭建视频通路(RGB565灰度图)

Posted 阿汪先生

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了VerilogFPGA驱动Ov7670/Ov7725搭建视频通路(RGB565灰度图)相关的知识,希望对你有一定的参考价值。

一、课题功能指标要求

(一)课程目的

• 加深对数字电路时序的理解;
• 掌握 OV 系列摄像头输出时序;
• 掌握 I2C 总线时序,以及使用 verilog 驱动三态门的方法;
• 掌握数字系统设计的方法;

(二)设计任务

o 设计并利用 FPGA 实现 OV7670(Ov7725)~VGA(320*240)显示器的视频通路;
o (基本要求)设计 I2C 总线接口以及控制器,实现对摄像头的配置;
o (基本要求)设计 OV7670(Ov7725) 输出转简单格式模块;
o (基本要求)利用 BRAM 搭建图像帧缓冲空间;
o (基本要求)设计 VGA 显示模块,显示摄像头输入的图像;
o (提高要求)使用双缓冲机制搭建视频通路;
o (提高要求)设计 RGB565 转灰度图模块,可利用拨码开关选择显示彩图或是灰度图;

(三)验收功能指标

1、输入时序的仿真波形;
2、图像在输入情况良好的情况下不撕裂,无歪斜或平移;
3、所有电路均采用同步电路的设计方法,除输入模块以外均采用同一个时钟驱动。


二、系统硬件描述

(一)系统硬件框图


其中,OV76760和显示屏为单独外设,FPGA主控模块和SRAM模块均为EGO1FPGA板板载资源。

(二)OV7670概述

1、功能描述

OV7670/OV7171_CAMERACHIPTM 图像传感器,体积小、工作电压低,提供单片 VGA 摄像头和影像处理器的所有功能。通过 SCCB 总线控制,可以输出整帧、子采样、取窗口等方式的各种分辨率 8 位影响数据。该产品 VGA 图像最高达到 30 帧/秒。
用户可以完全控制图像质量、数据格式和传输方式。所有图像处理功能过程包括伽玛曲线、白平衡、饱和度、色度等都可以通过 SCCB 接口编程。OmmiVision 图像传感器应用独有的传感器技术,通过减少或消除光学或电子缺陷如固定图案噪声、托尾、浮散等,提高图像质量,得到清晰的稳定的彩色图像。

2、性能参数

(1) 高灵敏度适合低照度应用
(2) 低电压适合嵌入式应用
(3) 标准的 SCCB 接口,兼容 I2C 接口
(4)RawRGB,RGB(GRB4:2:2,RGB565/555/444),YUV (4:2:2)和 YCbCr(4:2:2)输出格式
(5) 支持 VGA,CIF,和从 CIF 到 40x30 的各种尺寸
(6) VarioPixel 子采样方式
(7) 自动影响控制功能包括:自动曝光控制、自动增益控制、自动白平衡,自动消除灯 光条纹、自动黑电平校准、图像质量控制
(8) ISP 具有消除噪声和坏点补偿功能
(9) 支持闪光灯:LED 灯和氙灯
(10) 支持图像缩放
(11) 镜头失光补偿
(12)50/60Hz 自动检测
(13) 饱和度自动调节(UV 调整)
(14) 边缘增强自动调节
(15) 降噪自动调

3、功能模块

功能模块包括:感光阵列(共有 656x488 个像素,其中在 YUV 的模式中,有效像素为640x480 个) 、模拟信号处理 、A/D 转换 、测试图案发生器 、数字信号处理器 、图像缩放 、时序发生器 、数字视频端口、SCCB 接口 、LED 和闪光灯输出控。

4、功能框

OV7670内部功能框图:

5、时序框图

OV7670摄像头的接口为SCCB接口,其时序图如下。

一行图像数据获取时序图如下:

一帧图像数据获取时序图如下:

(1) SCCB简介

SCCB协议有两线也有三线,两线为SIO_C与SIO_D,三线为SIO_E、SIO_C 与SIO_D。2线的SCCB总线只能是一个主器件对一个从器件控制,但3线SCCB接口可以对多个从器件控制,因此当只有一个从机(slave device)时用两线,有多个从机时用三线。其中SIO_C只能由主机配置(FPGA),SIO_D是一个三态门, 双向数据线,既可以由主机控制,也可以由从机控制。

(2) 数据传输

当写数据到从机被定义为写传输(write transmission),当从机中读数据被定义为读传输 (read transmission),每一个传输都要有开始和结束来释放总线(start + sotp)
完整的数据传输包括两个或三个阶段,每一个阶段包含9位数据,其中高8位为所要传输 的数据,最低位根据器件所处情况有不同的取值。

规律如下:

  • 每一个阶段组成:8位数据+don’t care/NA
    如果是主机发送数据,即进行写操作,第九位就为don’t care 如果是从机发送数据,即为读操作,第九位就为NA。

  • 在进行主器件写操作时,全部阶段的最低位均是Don’t care bit 写操作:三个阶段构成传输的写,每个阶段都为9位
    ID地址(7位ID地址+1位读写控制+don’t care) + 要写的寄存器地址(8位寄存器地址+don’t care) +要写入的数据(8位数据+don’t care)

ID地址组成:

  • 写操作有: start + ID地址(地址位42)+ 寄存器地址 + 数据 + stop
  • 读操作:根据SCCB接口的读操作时序有两个阶段传输组成。2个阶段写传输 + 2个阶段读传输,每一相都是9位,具体如下:
    • ID地址(8位ID地址+1位读写控制+don’t care)+ FPGA要向从机写入即将要读的寄存器地址(8位寄存器地址+don’t care)
    • ID地址(8位ID地址+1位读写控制+don’t care) +从机向FPGA发送被指定寄存器里面的数据的数据(8位数据+NA)
  • 读操作为:
    • start1+ ID地址(42)+ 寄存器地址 +stop1+start 2 +ID地址(43)+ 数据 + stop2

总结如下:
每一个阶段组成:8位数据+don’t care/NA
如果是主机发送数据,即进行写操作,第九位就为don’t care 如果是从机发送数据,即为读操作,第九位就为NA.
在进行主器件写操作时,全部阶段的最低位均是Don’t care bit

(三)VGA显示模块

VGA接口是一种D型接口,上面共有15针孔,分成三排,每排五个,其中比较重要的是 3根RGB彩色分量信号和2根扫描同步信号HSYNC和VSYNC针,其引脚编号图如下所示:

VGA 显示器扫描方式从屏幕左上角一点开始,从左向右逐点扫描,每扫描完一行,电子束回到屏幕的左边下一行的起始位置,在这期间,CRT 对电子束进行消隐,每行结束时, 用行同步信号进行同步;

当扫描完所有的行,形成一帧,用场同步信号进行场同步,并使扫描回到屏幕左上方,同时进行场消隐,开始下一帧。

完成一行扫描的时间称为水平扫描时间,其倒数称为行频率;完成一帧(整屏)扫描的 时间称为垂直扫描时间,其倒数称为场频率,即屏幕的刷新频率。

行扫描:Hor Sync 、Hor Back Porch 、Hor Active Video和Hor Front Porch

Hor Scan Time是一个扫描周期,它会先扫描到Hor Sync、再扫描Hor Back Porch,然后才进入有效显示区Hor Active Video,最后是一段Hor Front Porch;

可以看出来,四段区间只有Hor Active Video这一段是能够正常显示图像信息的,也就是屏幕上显示的那一块区间。

列扫描:Ver Sync 、Ver Back Porch 、Ver Active Video和Ver Front Porch

Ver Scan Time是一个扫描周期,它会先扫描到Ver Sync、再扫描Ver Back Porch,然后才进入有效显示区Ver Active Video,最后是一段Ver Front Porch;可以看出来,四段区间只有Ver Active Video这一段是能够正常显示图像信息的,也就是屏幕上显示的那一块区间。

VGA常用的分辨率时序参数如下图所示:

VGA在显示屏上的显示范围如下图所示:


三、软件设计描述

(一)软件设计框图

(二)I2C驱动模块

1、 SCCB 接口

OV7670 摄像头接口为 SCCB 接口。SCCB 接口是特殊的 I2C 协议,SCCB 与 I2C 的具体差异如下:
(1) SCCB传输协议中,第9位为不必关心位,而IIC写传输协议为应答位。
(2) SCCB每次传输过程不超过3个阶段,即不能连续读写。
(3) SCCB读传输协议中没有重复开始的概念,在写完寄存器地址后,发起停止信号。

2、I2C协议概述

(1) 总线空闲状态:SDA为高电平,SCL为高电平
I2C协议起始位:SCL为高电平时,SDA出现下降沿,产生一个起始位 I2C协议结束位:SCL为高电平时,SDA出现上升沿,产生一个结束位SCL低电平时,SDA线上的数据变化(SCL:100k~400k,最大可达3.4M) SCL高电平时,SDA线上的数据被读取

(2) 应答位
每当数据接收方正确接收一个字节的指令或数据,都会产生一个应答位(ACK) 每当数据发送方发送完一个字节的数据或指令后,应该将SDA信号设置为三态输入 。
由于总线上拉电阻的存在,此时SDA信号线为高电平。数据接收方控制SDA信号线,如果正确接收数据,则将SDA信号线拉为低电平。

3、I2C器件地址

每一个I2C器件都有一个器件地址。
有的器件地址出厂即设置好了。有的设置好了几位(如常见的I2C接口的EEPROM存储器,留有3个控制地址的引脚,由用户自己在硬件设计时确定)。

主机向总线发送地址,所有从机接收并与自己地址识别,如果地址匹配,该从机想总线 发送响应信号,主机收到响应信号,开始向总线发送数据。若主机没有收到响应信号,则表 示寻址失败。

摄像头OV7670,其器件地址固定为0x42。

4、工作状态

在本次项目设计中,我们只需要用I2C协议对摄像头OV7670进行写操作,初始化
OV7670的寄存器参数以控制其输出格式。

(1) 写时序概述
(单个)写时序:主机---->发生控制字节—>从机应答---->主机传存储器地址---->传数据。
(单个)写时序:起始位—>控制字节(最低位为0)—>应答位—>一个字节的存储器地址—>应答位—一个字节的存储器地址(可选)—应答位(可选)—写入一个字节的数据应答位……停止位

(2) 写时序具体过程:
● 主机设置SDA为输出
● 主机产生起始信号
● 主机传输器件地址字节,其中最低位位0,表明为写操作
● 主机设置SDA为三态门输入,读取从机应答信号
● 读取应答信号成功,主机设置SDA为输出,传输1字节地址数据
● 主机设置SDA为三态门输入,读取从机应答信号
● 读取应答信号成功,对于2字节地址段器件,传输地址数据低字节;对于1字节地 址段器件,主机设置SDA为输出,传输待写入的数据。
● 设置SDA为三态门输入,读取从机应答信号,对于2字节地址段器件,执行下一步 骤;对于1字节地址段器件,直接跳转到最后一步。
● 读取应答信号成功,主机设置SDA为输出,传输待写入的数据
● 设置SDA为三态门输入,读取从机应答信号
● 读取应答信号成功,主机产生STOP位,终止传输。

5、vivado中的电路原理图

(三)I2C配置模块

1、模块概述

在系统开始工作之前,I2C驱动模块必须向摄像头OV7670发送初始化信号,对摄像头的 工作方式(寄存器)进行初始化。

本模块主要是在I2C驱动模块发送0V7670器件地址0x42后,将SDA信号线设置为三态输 入,因为的存在,此时SDA为高电平,摄像头响应地址后会将SDA信号线拉为低电平。
每一个支持I2C协议的器件,内部总会有一些可供读/写的寄存器。
OV7670的CMOS摄像头: 内部是一系列编址的可供读/写的寄存器。通过对这些寄存器写入数据来初始化摄像头的功能和工作方式。

2、主要代码摘录
//配置寄存器地址与数据
//这里配置的是Ov7725的寄存器,其它摄像头的配置根据相关数据手册即可
always @(posedge clk or negedge rst_n) begin if(!rst_n)
i2c_data <= 16'b0; else begin
case(init_reg_cnt)
//先对寄存器进行软件复位,使寄存器恢复初始值
//寄存器软件复位后,需要延时1ms才能配置其它寄存器
7'd0 : i2c_data <= 8'h12, 8'h80; //COM7 BIT[7]:复位所有的寄存器
7'd1 : i2c_data <= 8'h3d, 8'h03; //COM12 模拟过程直流补偿
7'd2 : i2c_data <= 8'h15, 8'h00; //COM10 href/vsync/pclk/data信号控制7'd3 : i2c_data <= 8'h17, 8'h3f;//8'h17, 8'h26; //HSTART 水平起始位置
7'd4 : i2c_data <= 8'h18, 8'h50;//8'h18, 8'ha0; //HSIZE 水平尺寸
7'd5 : i2c_data <= 8'h19, 8'h03;//8'h19, 8'h07; //VSTRT 垂直起始位置
7'd6 : i2c_data <= 8'h1a, 8'h78;//8'h1a, 8'hf0; //VSIZE 垂直尺寸
7'd7 : i2c_data <= 8'h32, 8'h00; //HREF 图像开始和尺寸控制,控制低位
7'd8 : i2c_data <= 8'h29, 8'h50;//8'h29, 8'ha0; //HOutSize 水平输出尺寸
7'd9 : i2c_data <= 8'h2a, 8'h00; //EXHCH 虚拟像素MSB
7'd10 : i2c_data <= 8'h2b, 8'h9e;//8'h2b, 8'h00; //EXHCL 虚 拟 像 素 LSB 7'd11 : i2c_data <= 8'h2c, 8'h78;//8'h2c, 8'hf0; //VOutSize 垂直输出尺寸7'd12 : i2c_data <= 8'h0d, 8'h41; //COM4 PLL倍频设置(multiplier)
7'd13 : i2c_data <= 8'h11, 8'h00; //CLKRC 内部时钟配置
//Freq=multiplier/[(CLKRC[5:0]+1)*2]
7'd14 : i2c_data <= 8'h12, 8'h46;//8'h12, 8'h06; //COM7 输出QVGA(320*240)
7'd15 : i2c_data <= 8'h0c, 8'h90;//8'h0c, 8'h10; //COM3 Bit[0]: 0:图像数据 1:
 

这里列举出一部分初始化寄存器的代码,OV7670的内部寄存器总共有201个,实际使用过程中,我们可以根据需要对相关寄存器进行初始化。

3、 vivado中的电路原理图

4、程序流程图

(四)CMOS图像数据采集模块

CMOS图像数据采集模块,需等待I2C协议对摄像头内部寄存器进行初始化。寄存器全部配置完成后,还需等待10帧数据,此等待10帧数据的目的是等待摄像头工作状态稳定
待寄存器配置生效、摄像头工作状态稳定后再开始采集图像。
具体地,待等待帧数等于10时,会使能标志信号frame_val_flag,使得OV77670开始对采集的图像数据中相应范围0~76800(340x240)的8位像素点数据经拼接操作,转存到寄存器data2ram中,data2ram将输出拼接后的16位数据到SRAM模块的输入引脚dina。
不同的摄像头的初始化,主要是根据性能参数、工作需要以及手册,初始化不同的寄存器参数。
代码摘录:


//将cmos_frame_valid转为ram地址
always @(posedge cam_pclk or negedge rst_n) begin if (!rst_n) begin	//复位
addr<=0; data2ram<=0;
end
else if (addr>=76800) begin	//320*240=76800 addr<=0;	//像素地址回到初始位置
end
else if (cmos_frame_valid) begin addr<=addr+1;
data2ram<=cmos_frame_data;//使其和使能一样延迟一个时钟
end
end


//8位数据转16位RGB565数据
always @(posedge cam_pclk or negedge rst_n) begin
if(!rst_n) begin	//复位
cmos_data_t <= 16'd0; cam_data_d0 <= 8'd0; byte_flag <= 1'b0;
end
else if(cam_href) begin
byte_flag <= ~byte_flag;	//翻转标志位cam_data_d0 <= cam_data;
end
 
if(byte_flag)
cmos_data_t <= cam_data_d0,cam_data;	//8位拼接16位
 
else begin
byte_flag <= 1'b0; cam_data_d0 <= 8'b0;
end
end

摄像头像素点数据输出:

2、Vivado中的电路原理图

(五)SRAM存储模块

1、概述

这部分模块主要通过调用ROM IP Core实现。点击IP Catalog,在搜索框中搜索block。选择Single Port ROM,图片位宽为16bit,深度为340x240=76800,表示有76800个16bit的RGB565像素。

图3.5.1. 调用配置IP核

RAM.ROM读取有延时,要在扫描第一个点的前两个时钟周期读取RAM/ROM,我在这里用的 是双口RAM,在Vivado这里显示的是有两个时钟的周期的延时,也就是当你给读命令时,RAM会把读出来的数据缓存两级才会输出给你想给数据的地方

2、模块单独仿真时的时序图如下。


图3.5.2. SRAM传输数据时序图

3、Vivado中的电路原理图

(六)VGA驱动模块

VGA扫描显示其实就是两条线,一个行扫描,一个场扫描,在行有效和场有效的时候把 数据发送给VGA显示。
显示屏扫描方式分为逐行扫描和隔行扫描:逐行扫描是扫描从屏幕左上角一点开始,从左像右逐点扫描,每扫描完一行,电子书回到屏幕的左边下一行的起始位置,在这器件,CRT 对电子束进行消隐,每行结束时,用场同步信号进行场同步,并使扫描回到屏幕左上方,同 时进行场消隐,开始下一帧。VGA时序过程前面已有介绍,隔行扫描方式在此也不赘述了。

1、主要代码摘录

//RGB565数据输出
assign vga_rgb = vga_en ? pixel_data : 16'd0; 
assign vga_gray = vga_en ? Y : 16'd0;

//扩展8位
always @(posedge vga_clk or negedge sys_rst_n) begin 
if (!sys_rst_n) begin
r1<=0; g1<=0; b1<=0;
end
else begin 
r1<=pixel_data[15:11],pixel_data[15:13];
g1<=pixel_data[10:5],pixel_data[10:9];
b1<=pixel_data[4:0],pixel_data[4:2];
end
end

 
//生成灰度图
always @(posedge vga_clk or negedge sys_rst_n)begin 
 if (!sys_rst_n) begin
	R_mult<=0; G_mult<=0; B_mult<=0;
end
else begin
R_mult <= r1 * 76; 
G_mult <= g1 * 150; 
B_mult <= b1 * 30;
end
end

always @(posedge vga_clk or negedge sys_rst_n) begin if (!sys_rst_n) 
   begin
	Y<=0;
end
	else begin 
	Y<=(R_mult+G_mult+B_mult)>>8; 
	end
end


//选择输出格式
//vga_en为像素有效标志
//switch为拨码开关,选择RGB/灰度图显示
assign vga_rgb0=(vga_en==1)?vga_rgb[15:12],vga_rgb[10:7],vga_rgb[4:1]:12'hfff; 
assign vga_rgb1=(vga_en==1)?vga_gray[7:4],vga_gray[7:4],vga_gray[7:4]:12'hfff; 
assign vga_rgb_444 = vga_en0 ? (swtich ? vga_rgb0:vga_rgb1 ) :12'h000;

2、Vivado中的电路原理图


四、测试与总结

(一)测试结果分析

灰度图显示:


彩色图显示:

1、基本完成所有的设计要求:

(1) 设计I2C总线接口以及控制器,实现对摄像头的配置;
(2) 设计OV7670输出转简单格式模块;
(3) 利用BRAM搭建图像帧缓冲空间;
(4) 设计VGA显示模块,显示摄像头输入的图像;
(5) 使用双缓冲机制搭建视频通路;
(6) 设计RGB565转灰度图模块,可利用拨码开关选择显示彩图或是灰度图;

2、还存在的问题

在RGB彩色输出切换至灰度图输出时,由于有2个像素时钟的延时,在VGA显示屏上显示的灰度图区域会较彩色图向右平移2个像素点
这是由于灰度图转换成彩色图需要经过2个像素时钟的计算过程,这2个像素时钟的延时 导致了显示区域的右移。

3、解决方案试想

(1) 彩色图显示的时钟也延时2个像素延时
(2) 给灰度图显示单独分配一个时钟时间有限上述试想未付诸实践。

(二)最终实现效果。

阿汪先生的演示视频.

(三)附录一张失败画面。

//有一张蛮有意思的图片(多层画面重叠、画中画??)没保存下来。。。

以上是关于VerilogFPGA驱动Ov7670/Ov7725搭建视频通路(RGB565灰度图)的主要内容,如果未能解决你的问题,请参考以下文章

VerilogFPGA驱动Ov7670/Ov7725搭建视频通路(RGB565灰度图)

iSensor APP 之 摄像头调试 OV5642

iSensor APP 之 摄像头调试 OV3640 OV2640 MT9d112

iSensor APP 之 摄像头调试 MT9D001 MT9P031 测试小结 200万像素和500万像素摄像头

MTK Camera(OV13850) 驱动移植

单片机开发OV2640在没有DCMI接口的情况下的STM32驱动