• 加深对数字电路时序的理解;
• 掌握 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/OV7171_CAMERACHIPTM 图像传感器,体积小、工作电压低,提供单片 VGA 摄像头和影像处理器的所有功能。通过 SCCB 总线控制,可以输出整帧、子采样、取窗口等方式的各种分辨率 8 位影响数据。该产品 VGA 图像最高达到 30 帧/秒。
用户可以完全控制图像质量、数据格式和传输方式。所有图像处理功能过程包括伽玛曲线、白平衡、饱和度、色度等都可以通过 SCCB 接口编程。OmmiVision 图像传感器应用独有的传感器技术,通过减少或消除光学或电子缺陷如固定图案噪声、托尾、浮散等,提高图像质量,得到清晰的稳定的彩色图像。
(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) 降噪自动调
功能模块包括:感光阵列(共有 656x488 个像素,其中在 YUV 的模式中,有效像素为640x480 个) 、模拟信号处理 、A/D 转换 、测试图案发生器 、数字信号处理器 、图像缩放 、时序发生器 、数字视频端口、SCCB 接口 、LED 和闪光灯输出控。
SCCB协议有两线也有三线,两线为SIO_C与SIO_D,三线为SIO_E、SIO_C 与SIO_D。2线的SCCB总线只能是一个主器件对一个从器件控制,但3线SCCB接口可以对多个从器件控制,因此当只有一个从机(slave device)时用两线,有多个从机时用三线。其中SIO_C只能由主机配置(FPGA),SIO_D是一个三态门, 双向数据线,既可以由主机控制,也可以由从机控制。
当写数据到从机被定义为写传输(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)
总结如下:
每一个阶段组成:8位数据+don’t care/NA
如果是主机发送数据,即进行写操作,第九位就为don’t care 如果是从机发送数据,即为读操作,第九位就为NA.
在进行主器件写操作时,全部阶段的最低位均是Don’t care bit
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这一段是能够正常显示图像信息的,也就是屏幕上显示的那一块区间。
OV7670 摄像头接口为 SCCB 接口。SCCB 接口是特殊的 I2C 协议,SCCB 与 I2C 的具体差异如下:
(1) SCCB传输协议中,第9位为不必关心位,而IIC写传输协议为应答位。
(2) SCCB每次传输过程不超过3个阶段,即不能连续读写。
(3) SCCB读传输协议中没有重复开始的概念,在写完寄存器地址后,发起停止信号。
(1) 总线空闲状态:SDA为高电平,SCL为高电平
I2C协议起始位:SCL为高电平时,SDA出现下降沿,产生一个起始位 I2C协议结束位:SCL为高电平时,SDA出现上升沿,产生一个结束位SCL低电平时,SDA线上的数据变化(SCL:100k~400k,最大可达3.4M) SCL高电平时,SDA线上的数据被读取
(2) 应答位
每当数据接收方正确接收一个字节的指令或数据,都会产生一个应答位(ACK) 每当数据发送方发送完一个字节的数据或指令后,应该将SDA信号设置为三态输入 。
由于总线上拉电阻的存在,此时SDA信号线为高电平。数据接收方控制SDA信号线,如果正确接收数据,则将SDA信号线拉为低电平。
每一个I2C器件都有一个器件地址。
有的器件地址出厂即设置好了。有的设置好了几位(如常见的I2C接口的EEPROM存储器,留有3个控制地址的引脚,由用户自己在硬件设计时确定)。
主机向总线发送地址,所有从机接收并与自己地址识别,如果地址匹配,该从机想总线 发送响应信号,主机收到响应信号,开始向总线发送数据。若主机没有收到响应信号,则表 示寻址失败。
摄像头OV7670,其器件地址固定为0x42。
在本次项目设计中,我们只需要用I2C协议对摄像头OV7670进行写操作,初始化
OV7670的寄存器参数以控制其输出格式。
(1) 写时序概述
(单个)写时序:主机---->发生控制字节—>从机应答---->主机传存储器地址---->传数据。
(单个)写时序:起始位—>控制字节(最低位为0)—>应答位—>一个字节的存储器地址—>应答位—一个字节的存储器地址(可选)—应答位(可选)—写入一个字节的数据应答位……停止位
(2) 写时序具体过程:
● 主机设置SDA为输出
● 主机产生起始信号
● 主机传输器件地址字节,其中最低位位0,表明为写操作
● 主机设置SDA为三态门输入,读取从机应答信号
● 读取应答信号成功,主机设置SDA为输出,传输1字节地址数据
● 主机设置SDA为三态门输入,读取从机应答信号
● 读取应答信号成功,对于2字节地址段器件,传输地址数据低字节;对于1字节地 址段器件,主机设置SDA为输出,传输待写入的数据。
● 设置SDA为三态门输入,读取从机应答信号,对于2字节地址段器件,执行下一步 骤;对于1字节地址段器件,直接跳转到最后一步。
● 读取应答信号成功,主机设置SDA为输出,传输待写入的数据
● 设置SDA为三态门输入,读取从机应答信号
● 读取应答信号成功,主机产生STOP位,终止传输。
在系统开始工作之前,I2C驱动模块必须向摄像头OV7670发送初始化信号,对摄像头的 工作方式(寄存器)进行初始化。
本模块主要是在I2C驱动模块发送0V7670器件地址0x42后,将SDA信号线设置为三态输 入,因为的存在,此时SDA为高电平,摄像头响应地址后会将SDA信号线拉为低电平。
每一个支持I2C协议的器件,内部总会有一些可供读/写的寄存器。
OV7670的CMOS摄像头: 内部是一系列编址的可供读/写的寄存器。通过对这些寄存器写入数据来初始化摄像头的功能和工作方式。
//配置寄存器地址与数据
//这里配置的是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个,实际使用过程中,我们可以根据需要对相关寄存器进行初始化。
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
////////////////////////////////////////////////////////////
这部分模块主要通过调用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会把读出来的数据缓存两级才会输出给你想给数据的地方。
VGA扫描显示其实就是两条线,一个行扫描,一个场扫描,在行有效和场有效的时候把 数据发送给VGA显示。
显示屏扫描方式分为逐行扫描和隔行扫描:逐行扫描是扫描从屏幕左上角一点开始,从左像右逐点扫描,每扫描完一行,电子书回到屏幕的左边下一行的起始位置,在这器件,CRT 对电子束进行消隐,每行结束时,用场同步信号进行场同步,并使扫描回到屏幕左上方,同 时进行场消隐,开始下一帧。VGA时序过程前面已有介绍,隔行扫描方式在此也不赘述了。
////////////////////////////////////////////////////////////
//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;
////////////////////////////////////////////////////////////
灰度图显示:
(1) 设计I2C总线接口以及控制器,实现对摄像头的配置;
(2) 设计OV7670输出转简单格式模块;
(3) 利用BRAM搭建图像帧缓冲空间;
(4) 设计VGA显示模块,显示摄像头输入的图像;
(5) 使用双缓冲机制搭建视频通路;
(6) 设计RGB565转灰度图模块,可利用拨码开关选择显示彩图或是灰度图;
在RGB彩色输出切换至灰度图输出时,由于有2个像素时钟的延时,在VGA显示屏上显示的灰度图区域会较彩色图向右平移2个像素点。
这是由于灰度图转换成彩色图需要经过2个像素时钟的计算过程,这2个像素时钟的延时 导致了显示区域的右移。
(1) 彩色图显示的时钟也延时2个像素延时
(2) 给灰度图显示单独分配一个时钟时间有限上述试想未付诸实践。
//有一张蛮有意思的图片(多层画面重叠、画中画??)没保存下来。。。