概述
OV5640 是一款 1/4 英寸单芯片图像传感器,其感光阵列达到 25921944(即 500W 像素),能实现最快 15fps QSXVGA(25921944)或者 90fps VGA(640*480)分辨率的图像采集。传感器采用 OmniVision 推出的 OmniBSI(背面照度)技术,使传感器达到更高的性能,如高灵敏度、低串扰和低噪声。传感器内 部集成了图像处理的功能,包括自动曝光控制(AEC)、自动白平衡(AWB)等。同时该传感器支持 LED 补光、MIPI(移动产业处理器接口)输出接口和 DVP(数字视频并行)输出接口选择、ISP(图像信号处理) 以及 AFC(自动聚焦控制)等功能。输出最大为 500 万像素的图像 (2592x1944 分辨率),支持使用 VGA 时序输出图像数据,输出图像的数据格式支持 YUV(422/420)、YCbCr422、 RGB565 以及 JPEG 格式,若直接输出 JPEG 格式的图 像时可大大减少数据量,方便网络传输。
(1) 控制寄存器标号1处的是 OV5640 的控制寄存器,它根据这些寄存器配置的参数来运行,而 这些参数是由外部控制器通过 SIO_C 和 SIO_D 引脚写入的, SIO_C 与 SIO_D 使用的通讯协议跟 I2C 十分类似。
(2) 通信、控制信号及时钟标号2处包含了 OV5640 的通信、控制信号及外部时钟,其中 PCLK、 HREF 及 VSYNC 分别是像素同步时钟、行同步信号以及帧同步信号,这与液晶屏控制中的信号是很类似的。 RESETB 引脚为低电平时,用于复位整个传感器芯片, PWDN 用于控制芯片进入低功耗模式。注意最后的一个 XCLK 引脚,它跟 PCLK 是完全不同的, XCLK 是用于驱动整个传感器芯片的时钟信号,是外部输入到 OV5640 的信号;而 PCLK 是 OV5640 输出数据时的同步信号,它是由 OV5640 输 出的信号。 XCLK 可以外接晶振或由外部控制器提供。
(3) 感光矩阵标号3处的是感光矩阵,光信号在这里转化成电信号,经过各种处理,这些信号存储成由一个个像素点表示的数字图像。
(4) 数据输出信号标号4处包含了 DSP 处理单元,它会根据控制寄存器的配置做一些基本的图像处理运算。这部分还包含了图像格式转换单元及压缩单元,转换出的数据最终通过 Y0-Y9 引脚输出,OV5640 在 RGB565 模式下只有高 8 位数据是有效的即 D[9:2], 因此我们仅使用 Y2-Y9 引脚。
(5) 数据输出信号标号5处为 VCM 处理单元,他会通过图像分析来实现图像的自动对焦功能。要实现自动对焦还需要下载自动对焦固件到模组,后面摄像头实验详细介绍这个功能。
SCCB 时序
外部控制器对 OV5640 寄存器的配置参数是通过 SCCB 总线传输过去的,而 SCCB 总线跟 I2C 十分类似。
SCCB 的起始、停止信号及数据有效性
起始信号: 在 SCL(图中为 SIO_C) 为高电平时, SDA(图中为 SIO_D)出现一 个下降沿,则 SCCB 开始传输。
停止信号:在 SCL 为高电平时, SDA 出现一个上升沿,则 SCCB 停止传输。
数据有效性:除了开始和停止状态, 在数据传输过程中,当 SCL 为高电平时, 必须保证 SDA 上的数据稳定,也就是说, SDA 上的电平变换只能发生在 SCL 为 低电平的时候,SDA 的信号在 SCL 为高电平时被采集。
SCCB 数据读写过程
在 SCCB 协议中定义的读写操作与 I2C 也是一样的,只是换了一种说法。它定义了两种写操作,即三步写操作和两步写操作。三步写操作可向从设备的一个目的寄存器中写入 数据,见图 。在三步写操作中,第一阶段发送从设备的 ID 地址+W 标志(等于 I2C 的 设备地址: 7 位设备地址+读写方向标志),第二阶段发送从设备目标寄存器的 8 位地址, 第三阶段发送要写入寄存器的 8 位数据。图中的“X”数据位可写入 1 或 0,对通讯无影响。
而两步写操作没有第三阶段,即只向从器件传输了设备 ID+W 标志和目的寄存器的地址,见图 。两步写操作是用来配合后面的读寄存器数据操作的,它与读操作一起使 用,实现 I2C 的复合过程。
两步读操作,它用于读取从设备目的寄存器中的数据,见图 。在第一阶段中发送从设备的设备 ID+R 标志(设备地址+读方向标志)和自由位,在第二阶段中读取寄存器中的 8 位数据和写 NA 位(非应答信号)。 由于两步读操作没有确定目的寄存器的地址,所以在 读操作前,必需有一个两步写操作,以提供读操作中的寄存器地址。
OV5640 的寄存器
控制 OV5640 涉及到它很多的寄存器,可直接查询《ov5640datasheet》了解,通过这 些寄存器的配置,可以控制它输出图像的分辨率大小、图像格式及图像方向等。要注意的是 OV5640 寄存器地址为 16 位。
像素数据输出时序
对 OV5640 采用 SCCB 协议进行控制,而它输出图像时则使用 VGA 时序(还可用 SVGA、 UXGA,这些时序都差不多),这跟控制液晶屏输入图像时很类似。 OV5640 输出 图像时,一帧帧地输出,在帧内的数据一般从左到右,从上到下,一个像素一个像素地输 出(也可通过寄存器修改方向)。 若我们使用 Y2-Y9 数据线,图像格式设置为 RGB565,进行数据输出 时, Y2-Y9 数据线会在 1 个像素同步时钟 PCLK 的驱动下发送 1 字节的数据信号,所以 2 个 PCLK 时钟可发送 1 个 RGB565 格式的像素数据。像素数据依次传输,每传输完一行数据时,行同步信号 HREF 会输出一个电平跳变信号,每传输完一帧图像时, VSYNC 会输出一个电平跳变信号。
主要模块代码
图像采集模块
图像采集模块的主要功能是接收并拼接 OV5640 摄像头传入的图像数据
在实际的使用过程中,为了避免采集到摄像头刚初始化完成时的不稳定数据,一般舍弃前10帧图像后,再采集数据。因此通过 摄像头输入的帧同步信号来进行帧数的判断ov5640_vsync
。同时,因为我们仅仅使用摄像头的8跟数据线采集数据,因此两个时钟周期才可以传输一个完整RGB565像素数据,对此,我们需要将采集到的数据进行拼接。
`timescale 1ns / 1ns
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/03/20 17:06:21
// Design Name:
// Module Name: OV5640_data
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module OV5640_data#(
parameter PIC_CNT_MAX = 4'd10 //舍弃前10帧不稳定图像数据
)
(
input wire sys_rst_n , //复位信号
// OV5640
input wire ov5640_pclk , //摄像头像素时钟
input wire ov5640_href , //摄像头行同步信号
input wire ov5640_vsync , //摄像头场同步信号
input wire [ 7:0] ov5640_data , //摄像头图像数据
// 写FIFO
output wire ov5640_wr_en , //图像数据有效使能信号
output wire [15:0] ov5640_data_out //图像数据
);
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//wire define
wire pic_flag ; //帧图像标志信号,每拉高一次,代表一帧完整图像
//reg define
reg ov5640_vsync_dly ; //摄像头输入场同步信号打拍
reg [3:0] cnt_pic ; //图像帧计数器
reg pic_valid ; //帧有效标志信号
reg [7:0] pic_data_reg ; //输入8位图像数据缓存
reg [15:0] data_out_reg ; //输出16位图像数据缓存
reg data_flag ; //输入8位图像数据缓存
reg data_flag_dly1 ; //图像数据拼接标志信号打拍
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//ov5640_vsync_dly:摄像头输入场同步信号打拍
always@(posedge ov5640_pclk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
ov5640_vsync_dly <= 1'b0;
else
ov5640_vsync_dly <= ov5640_vsync;
//pic_flag:帧图像标志信号,每拉高一次,代表一帧完整图像
assign pic_flag = ((ov5640_vsync_dly == 1'b0)
&& (ov5640_vsync == 1'b1)) ? 1'b1 : 1'b0;
//cnt_pic:图像帧计数器
always@(posedge ov5640_pclk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_pic <= 4'd0;
else if(cnt_pic < PIC_CNT_MAX)
cnt_pic <= cnt_pic + 1'b1;
else
cnt_pic <= cnt_pic;
//pic_valid:帧有效标志
always@(posedge ov5640_pclk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pic_valid <= 1'b0;
else if((cnt_pic == PIC_CNT_MAX) && (pic_flag == 1'b1))
pic_valid <= 1'b1;
else
pic_valid <= pic_valid;
//data_out_reg,pic_data_reg,data_flag:输出16位图像数据缓冲
//输入8位图像数据缓存输入8位,图像数据缓存
always@(posedge ov5640_pclk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
data_out_reg <= 16'd0;
pic_data_reg <= 8'd0;
data_flag <= 1'b0;
end
else if(ov5640_href == 1'b1)
begin
data_flag <= ~data_flag;
pic_data_reg <= ov5640_data;
data_out_reg <= data_out_reg;
if(data_flag == 1'b1)
data_out_reg <= {pic_data_reg,ov5640_data};
else
data_out_reg <= data_out_reg;
end
else
begin
data_flag <= 1'b0;
pic_data_reg <= 8'd0;
data_out_reg <= data_out_reg;
end
//data_flag_dly1:图像数据缓存打拍
always@(posedge ov5640_pclk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
data_flag_dly1 <= 1'b0;
else
data_flag_dly1 <= data_flag;
//ov5640_data_out:输出16位图像数据
assign ov5640_data_out = (pic_valid == 1'b1) ? data_out_reg : 16'b0;
//ov5640_wr_en:输出16位图像数据使能
assign ov5640_wr_en = (pic_valid == 1'b1) ? data_flag_dly1 : 1'b0;
endmodule
寄存器配置模块
要想 OV5640 摄像头正常工作,需要先对摄像头进行寄存器配置,即向摄像头寄存器 写入对应指令。
此处需要注意的是,摄像头上电之后,需要等待一段时间之后,才可以开始寄存器的配置。配置则是通过iic总线向寄存器发送命令。
`timescale 1ns / 1ns
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/03/20 20:44:00
// Design Name:
// Module Name: ov5640_cfg
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module ov5640_cfg#(
parameter CFG_NUM = 9'd250, //需配置的寄存器个数
parameter CNT_WAIT_MAX = 15'd20000 //ov640配置寄存器等待时间
)
(
input wire sys_clk , //时钟,IIC时钟
input wire sys_rst_n , //复位
input wire cfg_one_end , //一个寄存器配置完成,IIC传来
output reg cfg_start , //单个寄存器配置信号
output reg [23:0] cfg_data , //寄存器地址+数据
output reg cfg_done //寄存器配置完成
);
//reg define
reg [8:0] cfg_num ; //寄存器配置完成计数器
reg [14:0] wait_cnt; //等待计数
//main code
//cfg_num已配置寄存器计数
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0)
cfg_num <= 1'b0;
else if(cfg_one_end == 1'b1)
cfg_num <= cfg_num + 1'b1;
else
cfg_num <= cfg_num;
end
//wait_cnt ov5640上电后需要等待一段时间,才可以开始配置寄存器
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0)
wait_cnt <= 1'b0;
else if(wait_cnt < CNT_WAIT_MAX )
wait_cnt <= wait_cnt + 1'b1;
else
wait_cnt <= wait_cnt;
end
//cfg_start:单个寄存器配置触发信号
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0)
cfg_start <= 1'b0;
else if((cfg_one_end == 1'b1) && (cfg_num < CFG_NUM))
cfg_start <= 1'b1;
else if(wait_cnt == CNT_WAIT_MAX - 1'b1)
cfg_start <= 1'b1;
else
cfg_start <= 1'b0;
end
//cfg_done
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0)
cfg_done <= 1'b0;
else if(cfg_num == CFG_NUM && cfg_one_end == 1'b1)
cfg_done <= 1'b1;
else
cfg_done <= cfg_done;
end
//----------------------------------------------------
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cfg_data <= 24'b0;
else if(cfg_done == 1'b0)begin
case(cfg_num)
//先对寄存器进行软件复位,使寄存器恢复初始值
//寄存器软件复位后,需要延时1ms才能配置其它寄存器
8'd0 : cfg_data <= {16'h300a,8'h0}; //
8'd1 : cfg_data <= {16'h300b,8'h0}; //
8'd2 : cfg_data <= {16'h3008,8'h82}; //Bit[7]:复位 Bit[6]:电源休眠
8'd3 : cfg_data <= {16'h3008,8'h02}; //正常工作模式
8'd4 : cfg_data <= {16'h3103,8'h02}; //Bit[1]:1 PLL Clock
//引脚输入/输出控制 FREX/VSYNC/HREF/PCLK/D[9:6]
8'd5 : cfg_data <= {8'h30,8'h17,8'hff};
//引脚输入/输出控制 D[5:0]/GPIO1/GPIO0
8'd6 : cfg_data <= {16'h3018,8'hff};
8'd7 : cfg_data <= {16'h3037,8'h13}; //PLL分频控制
8'd8 : cfg_data <= {16'h3108,8'h01}; //系统根分频器
8'd9 : cfg_data <= {16'h3630,8'h36};
8'd10 : cfg_data <= {16'h3631,8'h0e};
8'd11 : cfg_data <= {16'h3632,8'he2};
8'd12 : cfg_data <= {16'h3633,8'h12};
8'd13 : cfg_data <= {16'h3621,8'he0};
8'd14 : cfg_data <= {16'h3704,8'ha0};
8'd15 : cfg_data <= {16'h3703,8'h5a};
8'd16 : cfg_data <= {16'h3715,8'h78};
8'd17 : cfg_data <= {16'h3717,8'h01};
8'd18 : cfg_data <= {16'h370b,8'h60};
8'd19 : cfg_data <= {16'h3705,8'h1a};
8'd20 : cfg_data <= {16'h3905,8'h02};
8'd21 : cfg_data <= {16'h3906,8'h10};
8'd22 : cfg_data <= {16'h3901,8'h0a};
8'd23 : cfg_data <= {16'h3731,8'h12};
8'd24 : cfg_data <= {16'h3600,8'h08}; //VCM控制,用于自动聚焦
8'd25 : cfg_data <= {16'h3601,8'h33}; //VCM控制,用于自动聚焦
8'd26 : cfg_data <= {16'h302d,8'h60}; //系统控制
8'd27 : cfg_data <= {16'h3620,8'h52};
8'd28 : cfg_data <= {16'h371b,8'h20};
8'd29 : cfg_data <= {16'h471c,8'h50};
8'd30 : cfg_data <= {16'h3a13,8'h43}; //AEC(自动曝光控制)
8'd31 : cfg_data <= {16'h3a18,8'h00}; //AEC 增益上限
8'd32 : cfg_data <= {16'h3a19,8'hf8}; //AEC 增益上限
8'd33 : cfg_data <= {16'h3635,8'h13};
8'd34 : cfg_data <= {16'h3636,8'h03};
8'd35 : cfg_data <= {16'h3634,8'h40};
8'd36 : cfg_data <= {16'h3622,8'h01};
8'd37 : cfg_data <= {16'h3c01,8'h34};
8'd38 : cfg_data <= {16'h3c04,8'h28};
8'd39 : cfg_data <= {16'h3c05,8'h98};
8'd40 : cfg_data <= {16'h3c06,8'h00}; //light meter 1 阈值[15:8]
8'd41 : cfg_data <= {16'h3c07,8'h08}; //light meter 1 阈值[7:0]
8'd42 : cfg_data <= {16'h3c08,8'h00}; //light meter 2 阈值[15:8]
8'd43 : cfg_data <= {16'h3c09,8'h1c}; //light meter 2 阈值[7:0]
8'd44 : cfg_data <= {16'h3c0a,8'h9c}; //sample number[15:8]
8'd45 : cfg_data <= {16'h3c0b,8'h40}; //sample number[7:0]
8'd46 : cfg_data <= {16'h3810,8'h00}; //Timing Hoffset[11:8]
8'd47 : cfg_data <= {16'h3811,8'h10}; //Timing Hoffset[7:0]
8'd48 : cfg_data <= {16'h3812,8'h00}; //Timing Voffset[10:8]
8'd49 : cfg_data <= {16'h3708,8'h64};
8'd50 : cfg_data <= {16'h4001,8'h02}; //BLC(黑电平校准)补偿起始行号
8'd51 : cfg_data <= {16'h4005,8'h1a}; //BLC(黑电平校准)补偿始终更新
8'd52 : cfg_data <= {16'h3000,8'h00}; //系统块复位控制
8'd53 : cfg_data <= {16'h3004,8'hff}; //时钟使能控制
8'd54 : cfg_data <= {16'h4300,8'h61}; //格式控制 RGB565
8'd55 : cfg_data <= {16'h501f,8'h01}; //ISP RGB
8'd56 : cfg_data <= {16'h440e,8'h00};
8'd57 : cfg_data <= {16'h5000,8'ha7}; //ISP控制
8'd58 : cfg_data <= {16'h3a0f,8'h30}; //AEC控制;stable range in high
8'd59 : cfg_data <= {16'h3a10,8'h28}; //AEC控制;stable range in low
8'd60 : cfg_data <= {16'h3a1b,8'h30}; //AEC控制;stable range out high
8'd61 : cfg_data <= {16'h3a1e,8'h26}; //AEC控制;stable range out low
8'd62 : cfg_data <= {16'h3a11,8'h60}; //AEC控制; fast zone high
8'd63 : cfg_data <= {16'h3a1f,8'h14}; //AEC控制; fast zone low
//LENC(镜头校正)控制 16'h5800~16'h583d
8'd64 : cfg_data <= {16'h5800,8'h23};
8'd65 : cfg_data <= {16'h5801,8'h14};
8'd66 : cfg_data <= {16'h5802,8'h0f};
8'd67 : cfg_data <= {16'h5803,8'h0f};
8'd68 : cfg_data <= {16'h5804,8'h12};
8'd69 : cfg_data <= {16'h5805,8'h26};
8'd70 : cfg_data <= {16'h5806,8'h0c};
8'd71 : cfg_data <= {16'h5807,8'h08};
8'd72 : cfg_data <= {16'h5808,8'h05};
8'd73 : cfg_data <= {16'h5809,8'h05};
8'd74 : cfg_data <= {16'h580a,8'h08};
8'd75 : cfg_data <= {16'h580b,8'h0d};
8'd76 : cfg_data <= {16'h580c,8'h08};
8'd77 : cfg_data <= {16'h580d,8'h03};
8'd78 : cfg_data <= {16'h580e,8'h00};
8'd79 : cfg_data <= {16'h580f,8'h00};
8'd80 : cfg_data <= {16'h5810,8'h03};
8'd81 : cfg_data <= {16'h5811,8'h09};
8'd82 : cfg_data <= {16'h5812,8'h07};
8'd83 : cfg_data <= {16'h5813,8'h03};
8'd84 : cfg_data <= {16'h5814,8'h00};
8'd85 : cfg_data <= {16'h5815,8'h01};
8'd86 : cfg_data <= {16'h5816,8'h03};
8'd87 : cfg_data <= {16'h5817,8'h08};
8'd88 : cfg_data <= {16'h5818,8'h0d};
8'd89 : cfg_data <= {16'h5819,8'h08};
8'd90 : cfg_data <= {16'h581a,8'h05};
8'd91 : cfg_data <= {16'h581b,8'h06};
8'd92 : cfg_data <= {16'h581c,8'h08};
8'd93 : cfg_data <= {16'h581d,8'h0e};
8'd94 : cfg_data <= {16'h581e,8'h29};
8'd95 : cfg_data <= {16'h581f,8'h17};
8'd96 : cfg_data <= {16'h5820,8'h11};
8'd97 : cfg_data <= {16'h5821,8'h11};
8'd98 : cfg_data <= {16'h5822,8'h15};
8'd99 : cfg_data <= {16'h5823,8'h28};
8'd100: cfg_data <= {16'h5824,8'h46};
8'd101: cfg_data <= {16'h5825,8'h26};
8'd102: cfg_data <= {16'h5826,8'h08};
8'd103: cfg_data <= {16'h5827,8'h26};
8'd104: cfg_data <= {16'h5828,8'h64};
8'd105: cfg_data <= {16'h5829,8'h26};
8'd106: cfg_data <= {16'h582a,8'h24};
8'd107: cfg_data <= {16'h582b,8'h22};
8'd108: cfg_data <= {16'h582c,8'h24};
8'd109: cfg_data <= {16'h582d,8'h24};
8'd110: cfg_data <= {16'h582e,8'h06};
8'd111: cfg_data <= {16'h582f,8'h22};
8'd112: cfg_data <= {16'h5830,8'h40};
8'd113: cfg_data <= {16'h5831,8'h42};
8'd114: cfg_data <= {16'h5832,8'h24};
8'd115: cfg_data <= {16'h5833,8'h26};
8'd116: cfg_data <= {16'h5834,8'h24};
8'd117: cfg_data <= {16'h5835,8'h22};
8'd118: cfg_data <= {16'h5836,8'h22};
8'd119: cfg_data <= {16'h5837,8'h26};
8'd120: cfg_data <= {16'h5838,8'h44};
8'd121: cfg_data <= {16'h5839,8'h24};
8'd122: cfg_data <= {16'h583a,8'h26};
8'd123: cfg_data <= {16'h583b,8'h28};
8'd124: cfg_data <= {16'h583c,8'h42};
8'd125: cfg_data <= {16'h583d,8'hce};
//AWB(自动白平衡控制) 16'h5180~16'h519e
8'd126: cfg_data <= {16'h5180,8'hff};
8'd127: cfg_data <= {16'h5181,8'hf2};
8'd128: cfg_data <= {16'h5182,8'h00};
8'd129: cfg_data <= {16'h5183,8'h14};
8'd130: cfg_data <= {16'h5184,8'h25};
8'd131: cfg_data <= {16'h5185,8'h24};
8'd132: cfg_data <= {16'h5186,8'h09};
8'd133: cfg_data <= {16'h5187,8'h09};
8'd134: cfg_data <= {16'h5188,8'h09};
8'd135: cfg_data <= {16'h5189,8'h75};
8'd136: cfg_data <= {16'h518a,8'h54};
8'd137: cfg_data <= {16'h518b,8'he0};
8'd138: cfg_data <= {16'h518c,8'hb2};
8'd139: cfg_data <= {16'h518d,8'h42};
8'd140: cfg_data <= {16'h518e,8'h3d};
8'd141: cfg_data <= {16'h518f,8'h56};
8'd142: cfg_data <= {16'h5190,8'h46};
8'd143: cfg_data <= {16'h5191,8'hf8};
8'd144: cfg_data <= {16'h5192,8'h04};
8'd145: cfg_data <= {16'h5193,8'h70};
8'd146: cfg_data <= {16'h5194,8'hf0};
8'd147: cfg_data <= {16'h5195,8'hf0};
8'd148: cfg_data <= {16'h5196,8'h03};
8'd149: cfg_data <= {16'h5197,8'h01};
8'd150: cfg_data <= {16'h5198,8'h04};
8'd151: cfg_data <= {16'h5199,8'h12};
8'd152: cfg_data <= {16'h519a,8'h04};
8'd153: cfg_data <= {16'h519b,8'h00};
8'd154: cfg_data <= {16'h519c,8'h06};
8'd155: cfg_data <= {16'h519d,8'h82};
8'd156: cfg_data <= {16'h519e,8'h38};
//Gamma(伽马)控制 16'h5480~16'h5490
8'd157: cfg_data <= {16'h5480,8'h01};
8'd158: cfg_data <= {16'h5481,8'h08};
8'd159: cfg_data <= {16'h5482,8'h14};
8'd160: cfg_data <= {16'h5483,8'h28};
8'd161: cfg_data <= {16'h5484,8'h51};
8'd162: cfg_data <= {16'h5485,8'h65};
8'd163: cfg_data <= {16'h5486,8'h71};
8'd164: cfg_data <= {16'h5487,8'h7d};
8'd165: cfg_data <= {16'h5488,8'h87};
8'd166: cfg_data <= {16'h5489,8'h91};
8'd167: cfg_data <= {16'h548a,8'h9a};
8'd168: cfg_data <= {16'h548b,8'haa};
8'd169: cfg_data <= {16'h548c,8'hb8};
8'd170: cfg_data <= {16'h548d,8'hcd};
8'd171: cfg_data <= {16'h548e,8'hdd};
8'd172: cfg_data <= {16'h548f,8'hea};
8'd173: cfg_data <= {16'h5490,8'h1d};
//CMX(彩色矩阵控制) 16'h5381~16'h538b
8'd174: cfg_data <= {16'h5381,8'h1e};
8'd175: cfg_data <= {16'h5382,8'h5b};
8'd176: cfg_data <= {16'h5383,8'h08};
8'd177: cfg_data <= {16'h5384,8'h0a};
8'd178: cfg_data <= {16'h5385,8'h7e};
8'd179: cfg_data <= {16'h5386,8'h88};
8'd180: cfg_data <= {16'h5387,8'h7c};
8'd181: cfg_data <= {16'h5388,8'h6c};
8'd182: cfg_data <= {16'h5389,8'h10};
8'd183: cfg_data <= {16'h538a,8'h01};
8'd184: cfg_data <= {16'h538b,8'h98};
//SDE(特殊数码效果)控制 16'h5580~16'h558b
8'd185: cfg_data <= {16'h5580,8'h06};
8'd186: cfg_data <= {16'h5583,8'h40};
8'd187: cfg_data <= {16'h5584,8'h10};
8'd188: cfg_data <= {16'h5589,8'h10};
8'd189: cfg_data <= {16'h558a,8'h00};
8'd190: cfg_data <= {16'h558b,8'hf8};
8'd191: cfg_data <= {16'h501d,8'h40}; //ISP MISC
//CIP(颜色插值)控制 (16'h5300~16'h530c)
8'd192: cfg_data <= {16'h5300,8'h08};
8'd193: cfg_data <= {16'h5301,8'h30};
8'd194: cfg_data <= {16'h5302,8'h10};
8'd195: cfg_data <= {16'h5303,8'h00};
8'd196: cfg_data <= {16'h5304,8'h08};
8'd197: cfg_data <= {16'h5305,8'h30};
8'd198: cfg_data <= {16'h5306,8'h08};
8'd199: cfg_data <= {16'h5307,8'h16};
8'd200: cfg_data <= {16'h5309,8'h08};
8'd201: cfg_data <= {16'h530a,8'h30};
8'd202: cfg_data <= {16'h530b,8'h04};
8'd203: cfg_data <= {16'h530c,8'h06};
8'd204: cfg_data <= {16'h5025,8'h00};
//系统时钟分频 Bit[7:4]:系统时钟分频 input clock =24Mhz, PCLK = 48Mhz
8'd205: cfg_data <= {16'h3035,8'h11};
8'd206: cfg_data <= {16'h3036,8'h3c}; //PLL倍频
8'd207: cfg_data <= {16'h3c07,8'h08};
//时序控制 16'h3800~16'h3821
8'd208: cfg_data <= {16'h3820,8'h46};
8'd209: cfg_data <= {16'h3821,8'h01};
8'd210: cfg_data <= {16'h3814,8'h31};
8'd211: cfg_data <= {16'h3815,8'h31};
8'd212: cfg_data <= {16'h3800,8'h00};
8'd213: cfg_data <= {16'h3801,8'h00};
8'd214: cfg_data <= {16'h3802,8'h00};
8'd215: cfg_data <= {16'h3803,8'h04};
8'd216: cfg_data <= {16'h3804,8'h0a};
8'd217: cfg_data <= {16'h3805,8'h3f};
8'd218: cfg_data <= {16'h3806,8'h07};
8'd219: cfg_data <= {16'h3807,8'h9b};
//设置输出像素个数
//DVP 输出水平像素点数高4位
8'd220: cfg_data <= {16'h3808,8'h04};
//DVP 输出水平像素点数低8位
8'd221: cfg_data <= {16'h3809,8'h00};
//DVP 输出垂直像素点数高3位
8'd222: cfg_data <= {16'h380a,8'h02};
//DVP 输出垂直像素点数低8位
8'd223: cfg_data <= {16'h380b,8'h58};
//水平总像素大小高5位
8'd224: cfg_data <= {16'h380c,8'h08};
//水平总像素大小低8位
8'd225: cfg_data <= {16'h380d,8'h98};
//垂直总像素大小高5位
8'd226: cfg_data <= {16'h380e,8'h03};
//垂直总像素大小低8位
8'd227: cfg_data <= {16'h380f,8'hE8};
8'd228: cfg_data <= {16'h3813,8'h06};
8'd229: cfg_data <= {16'h3618,8'h00};
8'd230: cfg_data <= {16'h3612,8'h29};
8'd231: cfg_data <= {16'h3709,8'h52};
8'd232: cfg_data <= {16'h370c,8'h03};
8'd233: cfg_data <= {16'h3a02,8'h17}; //60Hz max exposure
8'd234: cfg_data <= {16'h3a03,8'h10}; //60Hz max exposure
8'd235: cfg_data <= {16'h3a14,8'h17}; //50Hz max exposure
8'd236: cfg_data <= {16'h3a15,8'h10}; //50Hz max exposure
8'd237: cfg_data <= {16'h4004,8'h02}; //BLC(背光) 2 lines
8'd238: cfg_data <= {16'h4713,8'h03}; //JPEG mode 3
8'd239: cfg_data <= {16'h4407,8'h04}; //量化标度
8'd240: cfg_data <= {16'h460c,8'h22};
8'd241: cfg_data <= {16'h4837,8'h22}; //DVP CLK divider
8'd242: cfg_data <= {16'h3824,8'h02}; //DVP CLK divider
8'd243: cfg_data <= {16'h5001,8'ha3}; //ISP 控制
8'd244: cfg_data <= {16'h3b07,8'h0a}; //帧曝光模式
//彩条测试使能
8'd245: cfg_data <= {16'h503d,8'h00}; //8'h00:正常模式 8'h80:彩条显示
//测试闪光灯功能
8'd246: cfg_data <= {16'h3016,8'h02};
8'd247: cfg_data <= {16'h301c,8'h02};
8'd248: cfg_data <= {16'h3019,8'h02}; //打开闪光灯
8'd249: cfg_data <= {16'h3019,8'h00}; //关闭闪光灯
//只读存储器,防止在case中没有列举的情况,之前的寄存器被重复改写
default : cfg_data <= {16'h300a,8'h00}; //器件ID高8位
endcase
end
end
endmodule
iic模块
sccb时序与iic时序几乎一模一样,因此直接使用iic模块来进行摄像头寄存器的配置
`timescale 1ns / 1ns
module i2c_ctrl
#(
parameter DEVICE_ADDR = 7'h3c , //i2c设备地址
parameter SYS_CLK_FREQ = 26'd50_000_000 , //输入系统时钟频率
parameter SCL_FREQ = 18'd250_000 //i2c设备scl时钟频率
)
(
input wire sys_clk , //输入系统时钟,25MHz
input wire sys_rst_n , //输入复位信号,低电平有效
input wire wr_en , //输入写使能信号
input wire rd_en , //输入读使能信号
input wire i2c_start , //输入i2c触发信号
input wire addr_num , //输入i2c字节地址字节数
input wire [15:0] byte_addr , //输入i2c字节地址
input wire [7:0] wr_data , //输入i2c设备数据
output reg i2c_clk , //i2c驱动时钟
output reg i2c_end , //i2c一次读/写操作完成
output reg [7:0] rd_data , //输出i2c设备读取数据
output reg i2c_scl , //输出至i2c设备的串行时钟信号scl
inout wire i2c_sda //输出至i2c设备的串行数据信号sda
);
//************************************************************************//
//******************** Parameter and Internal Signal *********************//
//************************************************************************//
// parameter define
parameter CNT_CLK_MAX = (SYS_CLK_FREQ/SCL_FREQ) >> 2'd3 ; //cnt_clk计数器计数最大值
parameter CNT_START_MAX = 8'd100; //cnt_start计数器计数最大值
parameter IDLE = 4'd00, //初始状态
START_1 = 4'd01, //开始状态1
SEND_D_ADDR = 4'd02, //设备地址写入状态 + 控制写
ACK_1 = 4'd03, //应答状态1
SEND_B_ADDR_H = 4'd04, //字节地址高八位写入状态
ACK_2 = 4'd05, //应答状态2
SEND_B_ADDR_L = 4'd06, //字节地址低八位写入状态
ACK_3 = 4'd07, //应答状态3
WR_DATA = 4'd08, //写数据状态
ACK_4 = 4'd09, //应答状态4
START_2 = 4'd10, //开始状态2
SEND_RD_ADDR = 4'd11, //设备地址写入状态 + 控制读
ACK_5 = 4'd12, //应答状态5
RD_DATA = 4'd13, //读数据状态
N_ACK = 4'd14, //非应答状态
STOP = 4'd15; //结束状态
// wire define
wire sda_in ; //sda输入数据寄存
wire sda_en ; //sda数据写入使能信号
// reg define
reg [7:0] cnt_clk ; //系统时钟计数器,控制生成clk_i2c时钟信号
reg [3:0] state ; //状态机状态
reg cnt_i2c_clk_en ; //cnt_i2c_clk计数器使能信号
reg [1:0] cnt_i2c_clk ; //clk_i2c时钟计数器,控制生成cnt_bit信号
reg [2:0] cnt_bit ; //sda比特计数器
reg ack ; //应答信号
reg i2c_sda_reg ; //sda数据缓存
reg [7:0] rd_data_reg ; //自i2c设备读出数据
//************************************************************************//
//******************************* Main Code ******************************//
//************************************************************************//
// cnt_clk:系统时钟计数器,控制生成clk_i2c时钟信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_clk <= 8'd0;
else if(cnt_clk == CNT_CLK_MAX - 1'b1)
cnt_clk <= 8'd0;
else
cnt_clk <= cnt_clk + 1'b1;
// i2c_clk:i2c驱动时钟
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
i2c_clk <= 1'b1;
else if(cnt_clk == CNT_CLK_MAX - 1'b1)
i2c_clk <= ~i2c_clk;
// cnt_i2c_clk_en:cnt_i2c_clk计数器使能信号
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_i2c_clk_en <= 1'b0;
else if((state == STOP) && (cnt_bit == 3'd3) &&(cnt_i2c_clk == 3))
cnt_i2c_clk_en <= 1'b0;
else if(i2c_start == 1'b1)
cnt_i2c_clk_en <= 1'b1;
// cnt_i2c_clk:i2c_clk时钟计数器,控制生成cnt_bit信号
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_i2c_clk <= 2'd0;
else if(cnt_i2c_clk_en == 1'b1)
cnt_i2c_clk <= cnt_i2c_clk + 1'b1;
// cnt_bit:sda比特计数器
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_bit <= 3'd0;
else if((state == IDLE) || (state == START_1) || (state == START_2)
|| (state == ACK_1) || (state == ACK_2) || (state == ACK_3)
|| (state == ACK_4) || (state == ACK_5) || (state == N_ACK))
cnt_bit <= 3'd0;
else if((cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3))
cnt_bit <= 3'd0;
else if((cnt_i2c_clk == 2'd3) && (state != IDLE))
cnt_bit <= cnt_bit + 1'b1;
// state:状态机状态跳转
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
state <= IDLE;
else case(state)
IDLE:
if(i2c_start == 1'b1)
state <= START_1;
else
state <= state;
START_1:
if(cnt_i2c_clk == 3)
state <= SEND_D_ADDR;
else
state <= state;
SEND_D_ADDR:
if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
state <= ACK_1;
else
state <= state;
ACK_1:
if((cnt_i2c_clk == 3) && (ack == 1'b0))
begin
if(addr_num == 1'b1)
state <= SEND_B_ADDR_H;
else
state <= SEND_B_ADDR_L;
end
else
state <= state;
SEND_B_ADDR_H:
if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
state <= ACK_2;
else
state <= state;
ACK_2:
if((cnt_i2c_clk == 3) && (ack == 1'b0))
state <= SEND_B_ADDR_L;
else
state <= state;
SEND_B_ADDR_L:
if((cnt_bit == 3'd7) && (cnt_i2c_clk == 3))
state <= ACK_3;
else
state <= state;
ACK_3:
if((cnt_i2c_clk == 3) && (ack == 1'b0))
begin
if(wr_en == 1'b1)
state <= WR_DATA;
else if(rd_en == 1'b1)
state <= START_2;
else
state <= state;
end
else
state <= state;
WR_DATA:
if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
state <= ACK_4;
else
state <= state;
ACK_4:
if((cnt_i2c_clk == 3) && (ack == 1'b0))
state <= STOP;
else
state <= state;
START_2:
if(cnt_i2c_clk == 3)
state <= SEND_RD_ADDR;
else
state <= state;
SEND_RD_ADDR:
if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
state <= ACK_5;
else
state <= state;
ACK_5:
if((cnt_i2c_clk == 3) && (ack == 1'b0))
state <= RD_DATA;
else
state <= state;
RD_DATA:
if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
state <= N_ACK;
else
state <= state;
N_ACK:
if(cnt_i2c_clk == 3)
state <= STOP;
else
state <= state;
STOP:
if((cnt_bit == 3'd3) &&(cnt_i2c_clk == 3))
state <= IDLE;
else
state <= state;
default: state <= IDLE;
endcase
// ack:应答信号
always@(*)
case (state)
IDLE,START_1,SEND_D_ADDR,SEND_B_ADDR_H,SEND_B_ADDR_L,
WR_DATA,START_2,SEND_RD_ADDR,RD_DATA,N_ACK:
ack <= 1'b1;
ACK_1,ACK_2,ACK_3,ACK_4,ACK_5:
if(cnt_i2c_clk == 2'd0)
ack <= sda_in ;
else
ack <= ack;
default: ack <= 1'b1;
endcase
// i2c_scl:输出至i2c设备的串行时钟信号scl
always@(*)
case (state)
IDLE:
i2c_scl <= 1'b1;
START_1:
if(cnt_i2c_clk == 2'd3)
i2c_scl <= 1'b0;
else
i2c_scl <= 1'b1;
SEND_D_ADDR,ACK_1,SEND_B_ADDR_H,ACK_2,SEND_B_ADDR_L,
ACK_3,WR_DATA,ACK_4,START_2,SEND_RD_ADDR,ACK_5,RD_DATA,N_ACK:
if((cnt_i2c_clk == 2'd1) || (cnt_i2c_clk == 2'd2))
i2c_scl <= 1'b1;
else
i2c_scl <= 1'b0;
STOP:
if((cnt_bit == 3'd0) &&(cnt_i2c_clk == 2'd0))
i2c_scl <= 1'b0;
else
i2c_scl <= 1'b1;
default: i2c_scl <= 1'b1;
endcase
// i2c_sda_reg:sda数据缓存
always@(*)
case (state)
IDLE:
begin
i2c_sda_reg <= 1'b1;
rd_data_reg <= 8'd0;
end
START_1:
if(cnt_i2c_clk <= 2'd0)
i2c_sda_reg <= 1'b1;
else
i2c_sda_reg <= 1'b0;
SEND_D_ADDR:
if(cnt_bit <= 3'd6)
i2c_sda_reg <= DEVICE_ADDR[6 - cnt_bit];
else
i2c_sda_reg <= 1'b0;
ACK_1:
i2c_sda_reg <= 1'b1;
SEND_B_ADDR_H:
i2c_sda_reg <= byte_addr[15 - cnt_bit];
ACK_2:
i2c_sda_reg <= 1'b1;
SEND_B_ADDR_L:
i2c_sda_reg <= byte_addr[7 - cnt_bit];
ACK_3:
i2c_sda_reg <= 1'b1;
WR_DATA:
i2c_sda_reg <= wr_data[7 - cnt_bit];
ACK_4:
i2c_sda_reg <= 1'b1;
START_2:
if(cnt_i2c_clk <= 2'd1)
i2c_sda_reg <= 1'b1;
else
i2c_sda_reg <= 1'b0;
SEND_RD_ADDR:
if(cnt_bit <= 3'd6)
i2c_sda_reg <= DEVICE_ADDR[6 - cnt_bit];
else
i2c_sda_reg <= 1'b1;
ACK_5:
i2c_sda_reg <= 1'b1;
RD_DATA:
if(cnt_i2c_clk == 2'd2)
rd_data_reg[7 - cnt_bit] <= sda_in;
else
rd_data_reg <= rd_data_reg;
N_ACK:
i2c_sda_reg <= 1'b1;
STOP:
if((cnt_bit == 3'd0) && (cnt_i2c_clk < 2'd3))
i2c_sda_reg <= 1'b0;
else
i2c_sda_reg <= 1'b1;
default:
begin
i2c_sda_reg <= 1'b1;
rd_data_reg <= rd_data_reg;
end
endcase
// rd_data:自i2c设备读出数据
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rd_data <= 8'd0;
else if((state == RD_DATA) && (cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3))
rd_data <= rd_data_reg;
// i2c_end:一次读/写结束信号
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
i2c_end <= 1'b0;
else if((state == STOP) && (cnt_bit == 3'd3) &&(cnt_i2c_clk == 3))
i2c_end <= 1'b1;
else
i2c_end <= 1'b0;
// sda_in:sda输入数据寄存
assign sda_in = i2c_sda;
// sda_en:sda数据写入使能信号
assign sda_en = ((state == RD_DATA) || (state == ACK_1) || (state == ACK_2)
|| (state == ACK_3) || (state == ACK_4) || (state == ACK_5))
? 1'b0 : 1'b1;
// i2c_sda:输出至i2c设备的串行数据信号sda
assign i2c_sda = (sda_en == 1'b1) ? i2c_sda_reg : 1'bz;
endmodule
OV5640 顶层模块
`timescale 1ns / 1ns
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/03/21 17:51:47
// Design Name:
// Module Name: ov5640_top
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module ov5640_top#(
parameter PIC_CNT_MAX = 4'd10 , //舍弃前10帧不稳定图像数据
parameter SLAVE_ADDR = 7'h3c , // 器件地址(SLAVE_ADDR)
parameter BIT_CTRL = 1'b1 , // 字地址位控制参数(16b/8b)
parameter CLK_FREQ = 26'd25_000_000, // i2c_dri 模块的驱动时钟频率(CLK_FREQ)
parameter I2C_FREQ = 18'd250_000 , // I2C 的 SCL 时钟频率
parameter CFG_NUM = 9'd256 , // 需配置的寄存器个数
parameter CNT_WAIT_MAX = 15'd20000 //配置寄存最大等待时间,超过跳过该寄存器配置
)
(
input wire sys_clk , //50Mhz
input wire sys_rst_n ,
input wire ov5640_pclk , //摄像头像素时钟
input wire ov5640_vsync , //摄像头行同步信号
input wire ov5640_href , //摄像头场同步信号
input wire sys_init_done , //系统初始化完成(DDR3 SDRAM + 摄像头)
input wire [ 7:0] ov5640_data , //摄像头图像数据
output wire ov5640_wr_en , //图像数据有效使能信号
output wire [15:0] ov5640_data_out , //图像数据
output wire cfg_done ,
output wire sccb_scl ,
output wire sccb_sda
);
//wire define
wire cfg_one_end ;
wire cfg_start ;
wire [23:0] cfg_data ;
wire cfg_clk ;
i2c_ctrl
#(
.DEVICE_ADDR (SLAVE_ADDR ), //i2c设备地址
.SYS_CLK_FREQ (CLK_FREQ ), //输入系统时钟频率
.SCL_FREQ (I2C_FREQ ) //i2c设备scl时钟频率
)
i2c_ctrl_inst
(
.sys_clk (sys_clk ), //输入25MHz
.sys_rst_n (sys_rst_n ), //输入复位信号,低电平有效
.wr_en (1'b1 ), //输入写使能信号
.rd_en (1'b0 ), //输入读使能信号
.i2c_start (cfg_start ), //输入i2c触发信号
.addr_num (BIT_CTRL ), //输入i2c字节地址字节数
.byte_addr (cfg_data[23:8]), //输入i2c字节地址
.wr_data (cfg_data[7:0] ), //输入i2c设备数据.
.i2c_clk (cfg_clk ), //i2c驱动时钟
.i2c_end (cfg_one_end ), //i2c一次读/写操作完成
.rd_data ( ), //输出i2c设备读取数据
.i2c_scl (sccb_scl ), //输出至i2c设备的串行时钟信号scl
.i2c_sda (sccb_sda ) //输出至i2c设备的串行数据信号sda
);
OV5640_data
#(
.PIC_CNT_MAX (PIC_CNT_MAX) //舍弃前10帧不稳定图像数据
) OV5640_data_inst
(
.ov5640_pclk (ov5640_pclk ), //摄像头工作时钟 25MHz
.sys_rst_n (sys_rst_n & sys_init_done), //复位信号
.ov5640_vsync (ov5640_vsync ), //场同步信号
.ov5640_href (ov5640_href ), //行同步信号
.ov5640_data (ov5640_data ), //图像信息
.ov5640_wr_en (ov5640_wr_en ), //像素数据写入DDR3使能
.ov5640_data_out (ov5640_data_out) //写入DDR3使能的像素数据
);
ov5640_cfg#(
.CFG_NUM (CFG_NUM ), //需配置的寄存器个数
.CNT_WAIT_MAX (CNT_WAIT_MAX) //配置寄存最大等待时间,超过跳过该寄存器配置
)
ov5640_cfg_inst
(
.sys_clk (cfg_clk ), //时钟,IIC时钟
.sys_rst_n (sys_rst_n ), //复位
.cfg_one_end (cfg_one_end), //一个寄存器配置完成,IIC传来
.cfg_start (cfg_start ), //单个寄存器配置信号
.cfg_data (cfg_data ), //寄存器地址+数据
.cfg_done (cfg_done ) //寄存器配置完成
);
endmodule
摄像头各个模块例化
最后为了实现摄像头在LCD屏幕上的显示,又增加了DDR3模块与LCD显示模块。将摄像头数据缓存至ddr3之中,然后显示屏在读取。
显示顶层模块
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/03/23 18:48:44
// Design Name:
// Module Name: ov5640_ddr_lcd
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
// ov5640摄像头采集数据,存储到DDR3中(带乒乓操作),然后显示到LCD上
// Dependencies:
// [email protected]
// https://www.cnblogs.com/fangrunze
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module ov5640_ddr_lcd#(
parameter SLAVE_ADDR = 7'h3c , // ov5640器件地址(SLAVE_ADDR)
parameter BIT_CTRL = 1'b1 , // 字地址位控制参数(16b/8b)
parameter CLK_FREQ = 26'd50_000_000, // i2c_dri 模块的驱动时钟频率(CLK_FREQ)
parameter I2C_FREQ = 18'd250_000 , // I2C 的 SCL 时钟频率
parameter CFG_NUM = 9'd250 , // 需配置的寄存器个数
parameter CNT_WAIT_MAX = 15'd20000 , //配置ov5640寄存器等待时间
parameter PIC_CNT_MAX = 4'd10 , //舍弃前10帧不稳定图像数据
parameter H_PIC = 11'd1024 , //图片长度
parameter W_PIC = 10'd600 , //图片宽度
parameter PIC_SIZE = 20'd614400 , //图片像素个数
parameter baby_blue = 24'hADD8E6 //空余位置图片颜色 浅蓝色
)
(
input wire sys_clk,
input wire sys_rst_n,
//OV5640
input wire ov5640_pclk , //摄像头像素时钟
input wire ov5640_vsync , //摄像头行同步信号
input wire ov5640_href , //摄像头场同步信号
input wire [ 7:0] ov5640_data , //摄像头图像数据
output wire ov5640_rst_n , //摄像头复位
output wire ov5640_pwdn , //摄像头电源控制
output wire sccb_scl , //sccb_scl
output wire sccb_sda , //sccb_sda
//RGB-LCD 接口
output wire lcd_de , //LCD 数据使能信号
output wire lcd_hs , //LCD 行同步信号
output wire lcd_vs , //LCD 场同步信号
output wire lcd_bl , //LCD 背光控制信号
output wire lcd_clk , //LCD 像素时钟
output wire lcd_rst , //LCD 复位
output wire [23:0] lcd_rgb , //LCD RGB数据
//DDR3接口
inout wire [15:0] ddr3_dq , //DDR3 数据
inout wire [1:0] ddr3_dqs_n , //DDR3 dqs负
inout wire [1:0] ddr3_dqs_p , //DDR3 dqs正
output wire [13:0] ddr3_addr , //DDR3 地址
output wire [2:0] ddr3_ba , //DDR3 banck 选择
output wire ddr3_ras_n , //DDR3 行选择
output wire ddr3_cas_n , //DDR3 列选择
output wire ddr3_we_n , //DDR3 读写选择
output wire ddr3_reset_n , //DDR3 复位
output wire [0:0] ddr3_ck_p , //DDR3 时钟正
output wire [0:0] ddr3_ck_n , //DDR3 时钟负
output wire [0:0] ddr3_cke , //DDR3 时钟使能
output wire [0:0] ddr3_cs_n , //DDR3 片选
output wire [1:0] ddr3_dm , //DDR3_dm
output wire [0:0] ddr3_odt //DDR3_odt
);
//wire define==============================================================
wire locked ; //锁相环锁定
wire rst_n ; //系统复位
wire clk_50m ; //ov5640输入时钟 50Mhz
wire sys_init_done ; //系统初始化完成(DDR3 SDRAM + 摄像头)
wire ov5640_wr_en ; //图像数据有效使能信号
wire [15:0] ov5640_data_out ; //ov5640输出的图像数据
wire cfg_done ; //ov5640寄存器配置完成
wire ddr3_clk ; //DDR3时钟
wire data_req ; //ddr3读使能
wire [15:0] rd_data ; //FIFO读数据
wire calib_done ; //DDR3 控制器初始化完成标,高有效
assign sys_init_done = (calib_done)&(cfg_done); //系统初始化完成(DDR3 SDRAM + 摄像头)
assign rst_n = sys_rst_n & calib_done & locked ; //系统复位
//ov5640_rst_n:摄像头复位,固定高电平
assign ov5640_rst_n = 1'b1;
assign ov5640_pwdn = 1'b0;
//=========================================================================
//===时钟模块
//=========================================================================
clk_gen clk_gen_inst
(
.clk_in1 (sys_clk ) ,
.reset (~sys_rst_n ) ,
.clk_out1 (clk_50m ) , //50Mhz
.clk_out2 (ddr3_clk ) , //200Mhz
.locked (locked )
);
//=========================================================================
//===ov5640模块
//=========================================================================
ov5640_top#(
.SLAVE_ADDR (SLAVE_ADDR ), // 器件地址
.BIT_CTRL (BIT_CTRL ), // 字地址位控制参数
.CLK_FREQ (CLK_FREQ ), // i2c_dri 模块的驱动时钟频率
.I2C_FREQ (I2C_FREQ ), // I2C 的 SCL 时钟频率
.CFG_NUM (CFG_NUM ), // 需配置的寄存器个数
.CNT_WAIT_MAX (CNT_WAIT_MAX ) //配置ov5640寄存器等待时间
)
ov5640_top_inst
(
.sys_clk (clk_50m ) , //25Mhz
.sys_rst_n (rst_n ) ,
.ov5640_pclk (ov5640_pclk ) , //摄像头像素时钟
.ov5640_vsync (ov5640_vsync ) , //摄像头行同步信号
.ov5640_href (ov5640_href ) , //摄像头场同步信号
.sys_init_done (sys_init_done ) , //系统初始化完成(DDR3 SDRAM + 摄像头)
.ov5640_data (ov5640_data ) , //摄像头图像数据
.ov5640_wr_en (ov5640_wr_en ) , //图像数据有效使能信号
.ov5640_data_out (ov5640_data_out) , //图像数据
.cfg_done (cfg_done ) , //ov5640寄存器配置完成
.sccb_scl (sccb_scl ) , //sccb_scl
.sccb_sda (sccb_sda ) //sccb_sda
);
//=========================================================================
//==DDR读写控制部分
//=========================================================================
ddr3_ctrl_2port #(
.FIFO_DW (16 ), //模块写 FIFO 的写数据和读 FIFO 的读数据位宽
.PINGPANG (1 ), //PingPang 模式选择,0:PingPang 模式,1:PingPang 模式
.WR_BYTE_ADDR_BEGIN (0 ), //写数据存储空间的起始地址,
.WR_BYTE_ADDR_END ( PIC_SIZE*2 ), //写数据存储空间的终止地址,一个地址对应一个字节
.RD_BYTE_ADDR_BEGIN (0 ), //取数据存储空间的起始地址,
.RD_BYTE_ADDR_END ( PIC_SIZE*2 ) //取数据存储空间的终止地址,
)
inst_ddr3_ctrl_2port(
.ddr3_clk200m (ddr3_clk ), //DDR3时钟
.ddr3_rst_n (sys_rst_n&locked ), //DDR3复位
.ddr3_init_done (calib_done ), //DDR3 控制器初始化完成标,高有效
.wrfifo_clr ( ~(sys_rst_n&locked) ), //写 FIFO 清空控制信号,给高电平
.wrfifo_clk (ov5640_pclk ), //FIFO写时钟
.wrfifo_wren (ov5640_wr_en ), //fifo写使能
.wrfifo_din (ov5640_data_out ), //fifo写数据
.wrfifo_full ( ), //写 FIFO 的写满标识信号
.wrfifo_wr_cnt ( ), //写 FIFO 的写数据计数
.rdfifo_clr ( ~(sys_rst_n&locked) ), //读 FIFO 清空控制信号,给高电平
.rdfifo_clk (lcd_clk ), //FIFO读时钟
.rdfifo_rden (data_req ), //FIFO读使能
.rdfifo_dout (rd_data ), //FIFO读数据
.rdfifo_empty ( ), //读FIFO空标志
.rdfifo_rd_cnt ( ), //读FIFO读数据计数
//ddr3接口
.ddr3_dq (ddr3_dq ), //DDR3 数据
.ddr3_dqs_n (ddr3_dqs_n ), //DDR3 dqs负
.ddr3_dqs_p (ddr3_dqs_p ), //DDR3 dqs正
.ddr3_addr (ddr3_addr ), //DDR3 地址
.ddr3_ba (ddr3_ba ), //DDR3 banck 选择
.ddr3_ras_n (ddr3_ras_n ), //DDR3 行选择
.ddr3_cas_n (ddr3_cas_n ), //DDR3 列选择
.ddr3_we_n (ddr3_we_n ), //DDR3 读写选择
.ddr3_reset_n (ddr3_reset_n ), //DDR3 复位
.ddr3_ck_p (ddr3_ck_p ), //DDR3 时钟正
.ddr3_ck_n (ddr3_ck_n ), //DDR3 时钟负
.ddr3_cke (ddr3_cke ), //DDR3 时钟使能
.ddr3_cs_n (ddr3_cs_n ), //DDR3 片选
.ddr3_dm (ddr3_dm ), //DDR3_dm
.ddr3_odt (ddr3_odt ) //DDR3_odt
);
//=========================================================================
//===LCD控制部分
//=========================================================================
top_rgb#(
.H_PIC (H_PIC ), //图片长度
.W_PIC (W_PIC ), //图片宽度
.PIC_SIZE (PIC_SIZE ), //图片像素个数
.baby_blue (baby_blue) //空余位置图片颜色 浅蓝色
)
top_rgb_inst
(
.sys_clk (clk_50m ), //系统时钟
.sys_rst_n (rst_n &sys_init_done ), //系统复位
.lcd_pclk (clk_50m ), //lcd 时钟
.pi_data (rd_data ), //FIFO读数据
.data_req (data_req ), //ddr3读使能
.lcd_de (lcd_de ), //LCD 数据使能信号
.lcd_hs (lcd_hs ), //LCD 行同步信号
.lcd_vs (lcd_vs ), //LCD 场同步信号
.lcd_bl (lcd_bl ), //LCD 背光控制信号
.lcd_clk (lcd_clk ), //LCD 像素时钟
.lcd_rst (lcd_rst ), //LCD 复位
.lcd_rgb (lcd_rgb ) //LCD RGB数据
);
endmodule
顶层模块例化了3个小模块,其中时钟模块产生的50MHz时钟,用于系统以及摄像头使用,200MHz则提供给DDR3使用。
注意:这里的ddr3使用的是小梅哥的程序,但是经过修改,支持乒乓操作,如果不使用乒乓操作,则会使得的显示的摄像头图像有明显的撕裂感!!!!!!!!!(具体修改见代码)
其他模块就不再介绍,代码,下边上传的工程代码之中,会有详细的代码注释。
上机效果
使用屏幕:1024*600 屏幕
使用开发板:正点原子达芬奇FPGA开发板
源代码
链接: https://pan.baidu.com/s/1N1QqlSDH85mqPFGE5NXXPg?pwd=uwfd 提取码: uwfd 复制这段内容后打开百度网盘手机App,操作更方便哦
参考文献
[1] 正点原子. 达芬奇之FPGA开发指南
[2] 野火. FPGA+Verilog开发实战指南——基于Xilinx+Spartan6
[3] 小梅哥.小梅哥Xilinx ACX720 FPGA开发板自学教程V3.1.2