文章目录
一、系统框架 1.摄像头模块 摄像头配置 摄像头数据处理 2.SDRAM模块 SDRAM控制模块 SDRAM读写仲裁 SDRAM接口 读写FIFO 3.vga显示模块 4.PLL时钟模块 二、部分模块实现代码 1.摄像头配置 I2C接口 摄像头配置 摄像头数据处理 2.SDRAM控制模块 3.vga模块 4.顶层文件 三、源码一、系统框架
首先整个系统由摄像头模块、SDRAM数据缓存模块、vga显示模块、PLL时钟模块以及图像处理模块组成,这里先不用图像处理模块。
1.摄像头模块
摄像头配置
摄像头模块里面负责处理摄像头采集的数据,根据ov5640摄像头手册说明,我们需要先通过I2C协议去配置摄像头相关寄存器的参数。在摄像头上电后需要等待20ms。然后再通过I2C发送设备ID、写地址和数据,其中地址先发送高8位再发送低8位。这里包含摄像头时钟、图像大小、帧率以及其他和图像相关的参数。这里最重要的配置参数就是摄像头的图像分辨率和图像的色彩格式,这里通过配置的分辨率为1280*720,RGB565格式。
摄像头数据处理
根据时序图可以看到,当场同步信号到来后,会跟随着许多HREF数据有效信号,我们在HREF高电平的时候去采集数据即可。
但是摄像头的数据是把16位RGB拆分为高八位和低八位发送的,我们需要通过移位+位拼接的方式把两个8bit数据合并成16bit数据输出。同时为了SDRAM模块更好的识别帧头和帧尾,在图像的第一个像素点以及最后一个像素点的时候分别拉高sop和eop信号,其余像素点这拉低。
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
data <= 0;
end
else begin
data <= {data[7:0],din};//左移
end
end
assign pixel = data;
assign sop = cnt_h == 1 && cnt_v == 0;
assign eop = data_eop;
assign vld = cnt_h[0];
2.SDRAM模块
SDRAM控制模块
由于摄像头数据时钟(84M)和vga时钟(75M)不一样,为了避免读写数据速度不一致就需要把摄像头的数据进行缓存,常见的缓存可以通过fifo,但是这里的数据量十分庞大,故需要通过SDRAM进行缓存。缓存的方式则是通过乒乓缓存,把SDRAM的两个blank单独作为数据的写入和读出,并在读写完成后切换读写blank,并且需要通过丢帧和复读的操作来保证读写图像的完整性。
SDRAM读写仲裁
由于摄像头的数据输出和vga的数据请求都是源源不断的,此时到底是读还是写SDRAM就需要一个规则约束。首先为了保证写FIFO数据过多或者读FIFO数据过少。当写FIFO数据大于阈值后就会发起一个写请求,当读FIFO的数据低于阈值后就会发起读请求。仲裁机制根据读写请求进行仲裁,如果同一时间只有单独的读或者写请求直接执行即可,如果同时存在两种请求,则会根据上次执行的是读或者写来进行相反的请求。
//flag_sel ;//标记上一次操作
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
flag_sel <= 0;
end
else if(read2done)begin
flag_sel <= 1;
end
else if(write2done)begin
flag_sel <= 0;
end
end
//prior_flag ;//优先级标志 0:写优先级高 1:读优先级高 仲裁读、写的优先级
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
prior_flag <= 0;
end
else if(wr_flag && (flag_sel || (~flag_sel && ~rd_flag)))begin //突发写优先级高
prior_flag <= 1'b0;
end
else if(rd_flag && (~flag_sel || (flag_sel && ~wr_flag)))begin //突发读优先级高
prior_flag <= 1'b1;
end
end
SDRAM接口
这里直接使用quartus自带的SDRAM接口ip。
需要注意的是SDRAM的blank数据是数据位的最高位和第10位,行地址为第23位到11位,列地址则为低9位当然前提是这里的为4blank13行9列。如图为ip接口部分源码
所以地址需要这样进行拼接
assign avm_addr = (state_c == WRITE)?{wr_bank[1],wr_addr[21:9],wr_bank[0],wr_addr[8:0]}
:((state_c == READ)?{rd_bank[1],rd_addr[21:9],rd_bank[0],rd_addr[8:0]}
:0);
读写FIFO
整个SDRAM模块涉及到摄像头模块到SDRAM模块(慢时钟域到快时钟域),SDRAM数据到vga模块(快时钟域到慢时钟域)的跨时钟域数据同步的问题,这里使用两个异步FIFO来缓存两个跨时钟域的数据。
首先是写FIFO(这里的读写相对于SDRAM)
写FIFO的写使能条件是FIFO非满、输入数据有效、旧的一帧被vga读完且新的一帧到来或者是当前帧数据。
写FIFO的读使能条件是当前需要往SDRAM写数据且FIFO非空,这里通过SDRAM控制模块的仲裁机制决定。
assign wfifo_wrreq = ~wfifo_full & din_vld & ((~wr_finish_r[1] & din_sop) ||wr_data_flag);
assign wfifo_rdreq = state_c == WRITE && ~avs_waitrequest;
然后是读FIFO
读FIFO的写使能是当前把SDRAM读出来的数据有效且FIFO非满
读FIFO的读使能为当前FIFO非空且vga发起数据请求。
assign rfifo_wrreq = ~rfifo_full & avs_rddata_vld;
assign rfifo_rdreq = ~rfifo_empty & rdreq;
3.vga显示模块
vga显示这边使用1280*780@60HZ参数输出图像,在行场有效区域内通过拉发起请求读取读FIFO的数据内的数据,再输出到屏幕上面。可以参考前面的博客基于FPGA的VGA显示彩条、字符、图片,这里不多描述。
4.PLL时钟模块
整个系统由50M的基准时钟驱动,并且通过PLL生成配置摄像头的驱动时钟(24M)、SDRAM接口驱动时钟(100M),vga驱动时钟(75M)还有输出到SDRAM外设的100M带相位偏移的时钟。
二、部分模块实现代码
1.摄像头配置
I2C接口
`include "param.v"
module i2c_intf(
input clk ,
input rst_n ,
input req ,
input [3:0] cmd ,
input [7:0] din ,
output [7:0] dout ,
output done ,
output slave_ack ,
output i2c_scl ,
input i2c_sda_i ,
output i2c_sda_o ,
output i2c_sda_oe
);
//状态机参数定义
localparam IDLE = 7'b000_0001,
START = 7'b000_0010,
WRITE = 7'b000_0100,
RACK = 7'b000_1000,
READ = 7'b001_0000,
SACK = 7'b010_0000,
STOP = 7'b100_0000;
//信号定义
reg [6:0] state_c ;
reg [6:0] state_n ;
reg [8:0] cnt_scl ;//产生i2c时钟
wire add_cnt_scl ;
wire end_cnt_scl ;
reg [3:0] cnt_bit ;//传输数据 bit计数器
wire add_cnt_bit ;
wire end_cnt_bit ;
reg [3:0] bit_num ;
reg scl ;//输出寄存器
reg sda_out ;
reg sda_out_en ;
reg [7:0] rx_data ;
reg rx_ack ;
reg [3:0] command ;
reg [7:0] tx_data ;//发送数据
wire idle2start ;
wire idle2write ;
wire idle2read ;
wire start2write ;
wire start2read ;
wire write2rack ;
wire read2sack ;
wire rack2stop ;
wire sack2stop ;
wire rack2idle ;
wire sack2idle ;
wire stop2idle ;
//状态机
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
state_c <= IDLE ;
end
else begin
state_c <= state_n;
end
end
always @(*) begin
case(state_c)
IDLE :begin
if(idle2start)
state_n = START ;
else if(idle2write)
state_n = WRITE ;
else if(idle2read)
state_n = READ ;
else
state_n = state_c ;
end
START :begin
if(start2write)
state_n = WRITE ;
else if(start2read)
state_n = READ ;
else
state_n = state_c ;
end
WRITE :begin
if(write2rack)
state_n = RACK ;
else
state_n = state_c ;
end
RACK :begin
if(rack2stop)
state_n = STOP ;
else if(rack2idle)
state_n = IDLE ;
else
state_n = state_c ;
end
READ :begin
if(read2sack)
state_n = SACK ;
else
state_n = state_c ;
end
SACK :begin
if(sack2stop)
state_n = STOP ;
else if(sack2idle)
state_n = IDLE ;
else
state_n = state_c ;
end
STOP :begin
if(stop2idle)
state_n = IDLE ;
else
state_n = state_c ;
end
default : state_n = IDLE ;
endcase
end
assign idle2start = state_c==IDLE && (req && (cmd&`CMD_START));
assign idle2write = state_c==IDLE && (req && (cmd&`CMD_WRITE));
assign idle2read = state_c==IDLE && (req && (cmd&`CMD_READ ));
assign start2write = state_c==START && (end_cnt_bit && (command&`CMD_WRITE));
assign start2read = state_c==START && (end_cnt_bit && (command&`CMD_READ ));
assign write2rack = state_c==WRITE && (end_cnt_bit);
assign read2sack = state_c==READ && (end_cnt_bit);
assign rack2stop = state_c==RACK && (end_cnt_bit && (command&`CMD_STOP ));
assign sack2stop = state_c==SACK && (end_cnt_bit && (command&`CMD_STOP ));
assign rack2idle = state_c==RACK && (end_cnt_bit && (command&`CMD_STOP ) == 0);
assign sack2idle = state_c==SACK && (end_cnt_bit && (command&`CMD_STOP ) == 0);
assign stop2idle = state_c==STOP && (end_cnt_bit );
//计数器
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
cnt_scl <= 0;
end
else if(add_cnt_scl) begin
if(end_cnt_scl)
cnt_scl <= 0;
else
cnt_scl <= cnt_scl+1 ;
end
end
assign add_cnt_scl = (state_c != IDLE);
assign end_cnt_scl = add_cnt_scl && cnt_scl == (`SCL_PERIOD)-1 ;
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
cnt_bit <= 0;
end
else if(add_cnt_bit) begin
if(end_cnt_bit)
cnt_bit <= 0;
else
cnt_bit <= cnt_bit+1 ;
end
end
assign add_cnt_bit = (end_cnt_scl);
assign end_cnt_bit = add_cnt_bit && cnt_bit == (bit_num)-1 ;
always @(*)begin
if(state_c == WRITE | state_c == READ) begin
bit_num = 8;
end
else begin
bit_num = 1;
end
end
//command
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
command <= 0;
end
else if(req)begin
command <= cmd;
end
end
//tx_data
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
tx_data <= 0;
end
else if(req)begin
tx_data <= din;
end
end
//scl
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
scl <= 1'b1;
end
else if(idle2start | idle2write | idle2read)begin//开始发送时,拉低
scl <= 1'b0;
end
else if(add_cnt_scl && cnt_scl == `SCL_HALF-1)begin
scl <= 1'b1;
end
else if(end_cnt_scl && ~stop2idle)begin
scl <= 1'b0;
end
end
//sda_out
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
sda_out <= 1'b1;
end
else if(state_c == START)begin //发起始位
if(cnt_scl == `LOW_HLAF)begin //时钟低电平时拉高sda总线
sda_out <= 1'b1;
end
else if(cnt_scl == `HIGH_HALF)begin //时钟高电平时拉低sda总线
sda_out <= 1'b0; //保证从机能检测到起始位
end
end
else if(state_c == WRITE && cnt_scl == `LOW_HLAF)begin //scl低电平时发送数据 并串转换
sda_out <= tx_data[7-cnt_bit];
end
else if(state_c == SACK && cnt_scl == `LOW_HLAF)begin //发应答位
sda_out <= (command&`CMD_STOP)?1'b1:1'b0;
end
else if(state_c == STOP)begin //发停止位
if(cnt_scl == `LOW_HLAF)begin //时钟低电平时拉低sda总线
sda_out <= 1'b0;
end
else if(cnt_scl == `HIGH_HALF)begin //时钟高电平时拉高sda总线
sda_out <= 1'b1; //保证从机能检测到停止位
end
end
end
//sda_out_en 总线输出数据使能
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
sda_out_en <= 1'b0;
end
else if(idle2start | idle2write | read2sack | rack2stop)begin
sda_out_en <= 1'b1;
end
else if(idle2read | start2read | write2rack | stop2idle)begin
sda_out_en <= 1'b0;
end
end
//rx_data 接收读入的数据
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
rx_data <= 0;
end
else if(state_c == READ && cnt_scl == `HIGH_HALF)begin
rx_data[7-cnt_bit] <= i2c_sda_i; //串并转换
end
end
//rx_ack
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
rx_ack <= 1'b1;
end
else if(state_c == RACK && cnt_scl == `HIGH_HALF)begin
rx_ack <= i2c_sda_i;
end
end
//输出信号
assign i2c_scl = scl ;
assign i2c_sda_o = sda_out ;
assign i2c_sda_oe = sda_out_en ;
assign dout = rx_data;
assign done = rack2idle | sack2idle | stop2idle;
assign slave_ack = rx_ack;
endmodule
摄像头配置
`include "param.v"
module cmos_config(
input clk ,
input rst_n ,
//i2c_master
output req ,
output [3:0] cmd ,
output [7:0] dout ,
input done ,
output config_done
);
//定义参数
localparam WAIT = 4'b0001,//上电等待20ms
IDLE = 4'b0010,
WREQ = 4'b0100,//发写请求
WRITE = 4'b1000;//等待一个字节写完
parameter DELAY = 1000_000;//上电延时20ms开始配置
//信号定义
reg [3:0] state_c ;
reg [3:0] state_n ;
reg [19:0] cnt0 ;
wire add_cnt0/* synthesis syn_keep*/ ;
wire end_cnt0/* synthesis syn_keep*/ ;
reg [1:0] cnt1 ;
wire add_cnt1/* synthesis syn_keep*/ ;
wire end_cnt1/* synthesis syn_keep*/ ;
reg config_flag ;//1:表示在配置摄像头 0:表示配置完成
reg [23:0] lut_data ;
reg tran_req ;
reg [3:0] tran_cmd ;
reg [7:0] tran_dout ;
wire wait2idle ;
wire idle2wreq ;
wire write2wreq ;
wire write2idle ;
//状态机
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
state_c <= WAIT;
end
else begin
state_c <= state_n;
end
end
always @(*)begin
case(state_c)
WAIT :begin
if(wait2idle)
state_n = IDLE;
else
state_n = state_c;
end
IDLE :begin
if(idle2wreq)
state_n = WREQ;
else
state_n = state_c;
end
WREQ :state_n = WRITE;
WRITE :begin
if(write2wreq)
state_n = WREQ;
else if(write2idle)
state_n = IDLE;
else
state_n = state_c;
end
default:state_n = IDLE;
endcase
end
assign wait2idle = state_c == WAIT && end_cnt0;
assign idle2wreq = state_c == IDLE && config_flag;
assign write2wreq = state_c == WRITE && done && ~end_cnt1;
assign write2idle = state_c == WRITE && end_cnt1;
//计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt0 <= 0;
end
else if(add_cnt0)begin
if(end_cnt0)
cnt0 <= 0;
else
cnt0 <= cnt0 + 1;
end
end
assign add_cnt0 = state_c == WAIT || state_c == WRITE && end_cnt1;
assign end_cnt0 = add_cnt0 && cnt0 == ((state_c == WAIT)?(DELAY-1):(`REG_NUM-1));
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt1 <= 0;
end
else if(add_cnt1)begin
if(end_cnt1)
cnt1 <= 0;
else
cnt1 <= cnt1 + 1;
end
end
assign add_cnt1 = state_c == WRITE && done;
assign end_cnt1 = add_cnt1 && cnt1 == 4-1;
//config_flag
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
config_flag <= 1'b1;
end
else if(config_flag & end_cnt0 & state_c != WAIT)begin //所有寄存器配置完,flag拉低
config_flag <= 1'b0;
end
end
//输出寄存器
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
tran_req <= 0;
tran_cmd <= 0;
tran_dout <= 0;
end
else if(state_c == WREQ)begin
case(cnt1)
0:begin
tran_req <= 1;
tran_cmd <= {`CMD_START | `CMD_WRITE};
tran_dout <= `WR_ID;
end
1:begin
tran_req <= 1;
tran_cmd <= `CMD_WRITE;
tran_dout <= lut_data[23:16];
end
2:begin
tran_req <= 1;
tran_cmd <= `CMD_WRITE;
tran_dout <= lut_data[15:8];
end
3:begin
tran_req <= 1;
tran_cmd <= {`CMD_STOP | `CMD_WRITE};
tran_dout <= lut_data[7:0];
end
default:tran_req <= 0;
endcase
end
else begin
tran_req <= 0;
tran_cmd <= 0;
tran_dout <= 0;
end
end
//输出
assign config_done = ~config_flag;
assign req = tran_req;
assign cmd = tran_cmd;
assign dout = tran_dout;
//lut_data
always@(*)begin
case(cnt0)
//15fps VGA YUV output
// 24MHz input clock, 84MHz PCLK
0 :lut_data = {24'h3103_11}; // system clock from pad, bit[1]
1 :lut_data = {24'h3008_82}; // software reset, bit[7]
2 :lut_data = {24'h3008_42}; // software power down, bit[6]
3 :lut_data = {24'h3103_03}; // system clock from PLL, bit[1]
4 :lut_data = {24'h3017_ff}; // FREX, Vsync, HREF, PCLK, D[9:6] output enable
5 :lut_data = {24'h3018_ff}; // D[5:0], GPIO[1:0] output enable
6 :lut_data = {24'h3034_1a}; // MIPI 10-bit
7 :lut_data = {24'h3037_13}; // PLL root divider, bit[4], PLL pre-divider, bit[3:0]
8 :lut_data = {24'h3108_01}; // PCLK root divider, bit[5:4], SCLK2x root divider, bit[3:2]
9 :lut_data = {24'h3630_36};//SCLK root divider, bit[1:0]
10 :lut_data = {24'h3631_0e};
11 :lut_data = {24'h3632_e2};
12 :lut_data = {24'h3633_12};
13 :lut_data = {24'h3621_e0};
14 :lut_data = {24'h3704_a0};
15 :lut_data = {24'h3703_5a};
16 :lut_data = {24'h3715_78};
17 :lut_data = {24'h3717_01};
18 :lut_data = {24'h370b_60};
19 :lut_data = {24'h3705_1a};
20 :lut_data = {24'h3905_02};
21 :lut_data = {24'h3906_10};
22 :lut_data = {24'h3901_0a};
23 :lut_data = {24'h3731_12};
24 :lut_data = {24'h3600_08}; // VCM control
25 :lut_data = {24'h3601_33}; // VCM control
26 :lut_data = {24'h302d_60}; // system control
27 :lut_data = {24'h3620_52};
28 :lut_data = {24'h371b_20};
29 :lut_data = {24'h471c_50};
30 :lut_data = {24'h3a13_43}; // pre-gain = 1.047x
31 :lut_data = {24'h3a18_00}; // gain ceiling
32 :lut_data = {24'h3a19_f8}; // gain ceiling = 15.5x
33 :lut_data = {24'h3635_13};
34 :lut_data = {24'h3636_03};
35 :lut_data = {24'h3634_40};
36 :lut_data = {24'h3622_01};
// 50/60Hz detection 50/60Hz 灯光条纹过滤
37 :lut_data = {24'h3c01_34}; // Band auto, bit[7]
38 :lut_data = {24'h3c04_28}; // threshold low sum
39 :lut_data = {24'h3c05_98}; // threshold high sum
40 :lut_data = {24'h3c06_00}; // light meter 1 threshold[15:8]
41 :lut_data = {24'h3c07_08}; // light meter 1 threshold[7:0]
42 :lut_data = {24'h3c08_00}; // light meter 2 threshold[15:8]
43 :lut_data = {24'h3c09_1c}; // light meter 2 threshold[7:0]
44 :lut_data = {24'h3c0a_9c}; // sample number[15:8]
45 :lut_data = {24'h3c0b_40}; // sample number[7:0]
46 :lut_data = {24'h3810_00}; // Timing Hoffset[11:8]
47 :lut_data = {24'h3811_10}; // Timing Hoffset[7:0]
48 :lut_data = {24'h3812_00}; // Timing Voffset[10:8]
49 :lut_data = {24'h3708_64};
50 :lut_data = {24'h4001_02}; // BLC start from line 2
51 :lut_data = {24'h4005_1a}; // BLC always update
52 :lut_data = {24'h3000_00}; // enable blocks
53 :lut_data = {24'h3004_ff}; // enable clocks
54 :lut_data = {24'h300e_58}; //MIPI power down,DVP enable
55 :lut_data = {24'h302e_00};
56 :lut_data = {24'h4300_61}; // RGB,
57 :lut_data = {24'h501f_01}; // ISP RGB
58 :lut_data = {24'h440e_00};
59 :lut_data = {24'h5000_a7}; // Lenc on, raw gamma on, BPC on, WPC on, CIP on
// AEC target 自动曝光控制
60 :lut_data = {24'h3a0f_30}; // stable range in high
61 :lut_data = {24'h3a10_28}; // stable range in low
62 :lut_data = {24'h3a1b_30}; // stable range out high
63 :lut_data = {24'h3a1e_26}; // stable range out low
64 :lut_data = {24'h3a11_60}; // fast zone high
65 :lut_data = {24'h3a1f_14}; // fast zone low
// Lens correction for ? 镜头补偿
66 :lut_data = {24'h5800_23};
67 :lut_data = {24'h5801_14};
68 :lut_data = {24'h5802_0f};
69 :lut_data = {24'h5803_0f};
70 :lut_data = {24'h5804_12};
71 :lut_data = {24'h5805_26};
72 :lut_data = {24'h5806_0c};
73 :lut_data = {24'h5807_08};
74 :lut_data = {24'h5808_05};
75 :lut_data = {24'h5809_05};
76 :lut_data = {24'h580a_08};
77 :lut_data = {24'h580b_0d};
78 :lut_data = {24'h580c_08};
79 :lut_data = {24'h580d_03};
80 :lut_data = {24'h580e_00};
81 :lut_data = {24'h580f_00};
82 :lut_data = {24'h5810_03};
83 :lut_data = {24'h5811_09};
84 :lut_data = {24'h5812_07};
85 :lut_data = {24'h5813_03};
86 :lut_data = {24'h5814_00};
87 :lut_data = {24'h5815_01};
88 :lut_data = {24'h5816_03};
89 :lut_data = {24'h5817_08};
90 :lut_data = {24'h5818_0d};
91 :lut_data = {24'h5819_08};
92 :lut_data = {24'h581a_05};
93 :lut_data = {24'h581b_06};
94 :lut_data = {24'h581c_08};
95 :lut_data = {24'h581d_0e};
96 :lut_data = {24'h581e_29};
97 :lut_data = {24'h581f_17};
98 :lut_data = {24'h5820_11};
99 :lut_data = {24'h5821_11};
100:lut_data = {24'h5822_15};
101:lut_data = {24'h5823_28};
102:lut_data = {24'h5824_46};
103:lut_data = {24'h5825_26};
104:lut_data = {24'h5826_08};
105:lut_data = {24'h5827_26};
106:lut_data = {24'h5828_64};
107:lut_data = {24'h5829_26};
108:lut_data = {24'h582a_24};
109:lut_data = {24'h582b_22};
110:lut_data = {24'h582c_24};
111:lut_data = {24'h582d_24};
112:lut_data = {24'h582e_06};
113:lut_data = {24'h582f_22};
114:lut_data = {24'h5830_40};
115:lut_data = {24'h5831_42};
116:lut_data = {24'h5832_24};
117:lut_data = {24'h5833_26};
118:lut_data = {24'h5834_24};
119:lut_data = {24'h5835_22};
120:lut_data = {24'h5836_22};
121:lut_data = {24'h5837_26};
122:lut_data = {24'h5838_44};
123:lut_data = {24'h5839_24};
124:lut_data = {24'h583a_26};
125:lut_data = {24'h583b_28};
126:lut_data = {24'h583c_42};
127:lut_data = {24'h583d_ce}; // lenc BR offset
// AWB 自动白平衡
128:lut_data = {24'h5180_ff}; // AWB B block
129:lut_data = {24'h5181_f2}; // AWB control
130:lut_data = {24'h5182_00}; // [7:4] max local counter, [3:0] max fast counter
131:lut_data = {24'h5183_14}; // AWB advanced
132:lut_data = {24'h5184_25};
133:lut_data = {24'h5185_24};
134:lut_data = {24'h5186_09};
135:lut_data = {24'h5187_09};
136:lut_data = {24'h5188_09};
137:lut_data = {24'h5189_75};
138:lut_data = {24'h518a_54};
139:lut_data = {24'h518b_e0};
140:lut_data = {24'h518c_b2};
141:lut_data = {24'h518d_42};
142:lut_data = {24'h518e_3d};
143:lut_data = {24'h518f_56};
144:lut_data = {24'h5190_46};
145:lut_data = {24'h5191_f8}; // AWB top limit
146:lut_data = {24'h5192_04}; // AWB bottom limit
147:lut_data = {24'h5193_70}; // red limit
148:lut_data = {24'h5194_f0}; // green limit
149:lut_data = {24'h5195_f0}; // blue limit
150:lut_data = {24'h5196_03}; // AWB control
151:lut_data = {24'h5197_01}; // local limit
152:lut_data = {24'h5198_04};
153:lut_data = {24'h5199_12};
154:lut_data = {24'h519a_04};
155:lut_data = {24'h519b_00};
156:lut_data = {24'h519c_06};
157:lut_data = {24'h519d_82};
158:lut_data = {24'h519e_38}; // AWB control
// Gamma 伽玛曲线
159:lut_data = {24'h5480_01}; //Gamma bias plus on, bit[0]
160:lut_data = {24'h5481_08};
161:lut_data = {24'h5482_14};
162:lut_data = {24'h5483_28};
163:lut_data = {24'h5484_51};
164:lut_data = {24'h5485_65};
165:lut_data = {24'h5486_71};
166:lut_data = {24'h5487_7d};
167:lut_data = {24'h5488_87};
168:lut_data = {24'h5489_91};
169:lut_data = {24'h548a_9a};
170:lut_data = {24'h548b_aa};
171:lut_data = {24'h548c_b8};
172:lut_data = {24'h548d_cd};
173:lut_data = {24'h548e_dd};
174:lut_data = {24'h548f_ea};
175:lut_data = {24'h5490_1d};
// color matrix 色彩矩阵
176:lut_data = {24'h5381_1e}; // CMX1 for Y
177:lut_data = {24'h5382_5b}; // CMX2 for Y
178:lut_data = {24'h5383_08}; // CMX3 for Y
179:lut_data = {24'h5384_0a}; // CMX4 for U
180:lut_data = {24'h5385_7e}; // CMX5 for U
181:lut_data = {24'h5386_88}; // CMX6 for U
182:lut_data = {24'h5387_7c}; // CMX7 for V
183:lut_data = {24'h5388_6c}; // CMX8 for V
184:lut_data = {24'h5389_10}; // CMX9 for V
185:lut_data = {24'h538a_01}; // sign[9]
186:lut_data = {24'h538b_98}; // sign[8:1]
// UV adjust UV 色彩饱和度调整
187:lut_data = {24'h5580_06}; // saturation on, bit[1]
188:lut_data = {24'h5583_40};
189:lut_data = {24'h5584_10};
190:lut_data = {24'h5589_10};
191:lut_data = {24'h558a_00};
192:lut_data = {24'h558b_f8};
193:lut_data = {24'h501d_40}; // enable manual offset of contrast
// CIP 锐化和降噪
194:lut_data = {24'h5300_08}; //CIP sharpen MT threshold 1
195:lut_data = {24'h5301_30}; //CIP sharpen MT threshold 2
196:lut_data = {24'h5302_10}; // CIP sharpen MT offset 1
197:lut_data = {24'h5303_00}; // CIP sharpen MT offset 2
198:lut_data = {24'h5304_08}; // CIP DNS threshold 1
199:lut_data = {24'h5305_30}; // CIP DNS threshold 2
200:lut_data = {24'h5306_08}; // CIP DNS offset 1
201:lut_data = {24'h5307_16}; // CIP DNS offset 2
202:lut_data = {24'h5309_08}; //CIP sharpen TH threshold 1
203:lut_data = {24'h530a_30}; //CIP sharpen TH threshold 2
204:lut_data = {24'h530b_04}; //CIP sharpen TH offset 1
205:lut_data = {24'h530c_06}; //CIP sharpen TH offset 2
206:lut_data = {24'h5025_00};
207:lut_data = {24'h3008_02}; //wake up from standby,bit[6]
// input clock 24Mhz, PCLK 84Mhz
208:lut_data = {24'h3035_21}; // PLL
209:lut_data = {24'h3036_69}; // PLL
210:lut_data = {24'h3c07_07}; // lightmeter 1 threshold[7:0]
211:lut_data = {24'h3820_47}; // flip
212:lut_data = {24'h3821_01}; // no mirror
213:lut_data = {24'h3814_31}; // timing X inc
214:lut_data = {24'h3815_31}; // timing Y inc
215:lut_data = {24'h3800_00}; // HS
216:lut_data = {24'h3801_00}; // HS
217:lut_data = {24'h3802_00}; // VS
218:lut_data = {24'h3803_fa}; // VS
219:lut_data = {24'h3804_0a}; // HW :
220:lut_data = {24'h3805_3f}; // HW :
221:lut_data = {24'h3806_06}; // VH :
222:lut_data = {24'h3807_a9}; // VH :
223:lut_data = {24'h3808_05}; // DVPHO 1280
224:lut_data = {24'h3809_00}; // DVPHO
225:lut_data = {24'h380a_02}; // DVPVO 720
226:lut_data = {24'h380b_d0}; // DVPVO
227:lut_data = {24'h380c_07}; // HTS
228:lut_data = {24'h380d_64}; // HTS
229:lut_data = {24'h380e_02}; // VTS
230:lut_data = {24'h380f_e4}; // VTS
231:lut_data = {24'h3813_04}; // timing V offset
232:lut_data = {24'h3618_00};
233:lut_data = {24'h3612_29};
234:lut_data = {24'h3709_52};
235:lut_data = {24'h370c_03};
236:lut_data = {24'h3a02_02}; // 60Hz max exposure
237:lut_data = {24'h3a03_e0}; // 60Hz max exposure
238:lut_data = {24'h3a14_02}; // 50Hz max exposure
239:lut_data = {24'h3a15_e0}; // 50Hz max exposure
240:lut_data = {24'h4004_02}; // BLC line number
241:lut_data = {24'h3002_1c}; // reset JFIFO, SFIFO, JPG
242:lut_data = {24'h3006_c3}; // disable clock of JPEG2x, JPEG
243:lut_data = {24'h4713_03}; // JPEG mode 3
244:lut_data = {24'h4407_04}; // Quantization scale
245:lut_data = {24'h460b_37};
246:lut_data = {24'h460c_20};
247:lut_data = {24'h4837_16}; // MIPI global timing
248:lut_data = {24'h3824_04}; // PCLK manual divider
249:lut_data = {24'h5001_83}; // SDE on, CMX on, AWB on
250:lut_data = {24'h3503_00}; // AEC/AGC on
251:lut_data = {24'h4740_20}; // VS 1
252:lut_data = {24'h503d_00}; // color bar
253:lut_data = {24'h4741_00}; //
default:lut_data = 0;
endcase
end
endmodule
摄像头数据处理
`include "param.v"
module capture (
input wire clk,
input wire rst_n,
input wire vsync,
input wire href,
input wire [ 7:0 ] din,
input wire din_vld,
output wire sop,
output wire eop,
output wire vld,
output wire [ 15:0 ] pixel
);
// localparam red = 16'd63488;
// localparam orange = 16'd64384;
// localparam yellow = 16'd65472;
// localparam green = 16'd1024;
// localparam blue = 16'd31;
// localparam indigo = 16'd18448;
// localparam purple = 16'd32784;
// localparam white = 16'd65503;
// localparam black = 16'd0;
//信号定义
reg [11:0] cnt_h ;
wire add_cnt_h ;
wire end_cnt_h ;
reg [9:0] cnt_v ;
wire add_cnt_v ;
wire end_cnt_v ;
reg [1:0] vsync_r ;//同步打拍
wire vsync_nedge ;//下降沿
reg flag ;//串并转换标志
reg [15:0] data ;
reg data_vld ;
reg data_sop ;
reg data_eop ;
//计数器
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
cnt_h <= 0;
end
else if(add_cnt_h) begin
if(end_cnt_h)
cnt_h <= 0;
else
cnt_h <= cnt_h+1 ;
end
end
assign add_cnt_h = flag & href;
assign end_cnt_h = add_cnt_h && cnt_h == (`H_AP << 1)-1;
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
cnt_v <= 0;
end
else if(add_cnt_v) begin
if(end_cnt_v)
cnt_v <= 0;
else
cnt_v <= cnt_v+1 ;
end
end
assign add_cnt_v = end_cnt_h;
assign end_cnt_v = add_cnt_v && cnt_v == `V_AP-1 ;
//vsync同步打拍
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
vsync_r <= 2'b00;
end
else begin
vsync_r <= {vsync_r[0],vsync};
end
end
assign vsync_nedge = vsync_r[1] & ~vsync_r[0];
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
flag <= 1'b0;
end
else if(din_vld & vsync_nedge)begin //摄像头配置完成且场同步信号拉低之后开始采集有效数据
flag <= 1'b1;
end
else if(end_cnt_v)begin //一帧数据采集完拉低
flag <= 1'b0;
end
end
//data
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
data <= 0;
end
else begin
data <= {data[7:0],din};//左移
end
end
//data_sop
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
data_sop <= 1'b0;
data_eop <= 1'b0;
data_vld <= 1'b0;
end
else begin
data_sop <= add_cnt_h && cnt_h == 2-1 && cnt_v == 0;
data_eop <= end_cnt_v;
data_vld <= add_cnt_h && cnt_h[0] == 1'b0 ;
end
end
assign pixel = data;
assign sop = cnt_h == 1 && cnt_v == 0;
assign eop = data_eop;
assign vld = cnt_h[0];
endmodule //capture
2.SDRAM控制模块
`include"param.v"
module sdram_ctrl (
input clk ,
input clk_in ,
input clk_out ,
input rst_n ,
//数据输入
input [15:0] din ,//摄像头输入像素数据
input din_sop ,
input din_eop ,
input din_vld ,
//数据输出
input rdreq ,//vga的读数据请求
output [15:0] dout ,//输出给vga的数据
output dout_vld ,//输出给vga的数据有效标志
//sdram_interface
output avm_write ,//输出给sdram 接口 IP 的写请求
output avm_read ,//输出给sdram 接口 IP 的读请求
output [23:0] avm_addr ,//输出给sdram 接口 IP 的读写地址
output [15:0] avm_wrdata ,//输出给sdram 接口 IP 的写数据
input [15:0] avs_rddata ,//sdram 接口 IP 输入的读数据
input avs_rddata_vld ,
input avs_waitrequest
);
//参数定义
localparam IDLE = 4'b0001,
WRITE = 4'b0010,
READ = 4'b0100,
DONE = 4'b1000;
//信号定义
reg [3:0] state_c ;
reg [3:0] state_n ;
reg [8:0] cnt ;//突发读写计数器
wire add_cnt ;
wire end_cnt ;
reg [1:0] wr_bank ;//写bank
reg [1:0] rd_bank ;//读bank
reg [21:0] wr_addr ;//写地址 行地址 + 列地址
wire add_wr_addr ;
wire end_wr_addr ;
reg [21:0] rd_addr ;//读地址 行地址 + 列地址
wire add_rd_addr ;
wire end_rd_addr ;
reg change_bank ;//切换bank
reg wr_finish ;//一帧数据写完
reg [1:0] wr_finish_r ;//同步到写侧
reg wr_data_flag;//wrfifo写数据的标志
reg wr_flag ;
reg rd_flag ;
reg flag_sel ;
reg prior_flag ;
wire idle2write ;
wire idle2read ;
wire write2done ;
wire read2done ;
reg [15:0] rd_data ;//rfifo读数据输出
reg rd_data_vld ;
wire [17:0] wfifo_data ;
wire wfifo_rdreq ;
wire wfifo_wrreq ;
wire [17:0] wfifo_q ;
wire wfifo_empty ;
wire [10:0] wfifo_usedw ;
wire wfifo_full ;
wire [15:0] rfifo_data ;
wire rfifo_rdreq ;
wire rfifo_wrreq ;
wire [15:0] rfifo_q ;
wire rfifo_empty ;
wire rfifo_full ;
wire [10:0] rfifo_usedw ;
//状态机
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
state_c <= IDLE;
end
else begin
state_c <= state_n;
end
end
always @(*)begin
case(state_c)
IDLE :begin
if(idle2write)
state_n = WRITE;
else if(idle2read)
state_n = READ;
else
state_n = state_c;
end
WRITE :begin
if(write2done)
state_n = DONE;
else
state_n = state_c;
end
READ :begin
if(read2done)
state_n = DONE;
else
state_n = state_c;
end
DONE :state_n = IDLE;
default:state_n = IDLE;
endcase
end
assign idle2write = state_c == IDLE && (~prior_flag && wfifo_usedw >= `USER_BL);
assign idle2read = state_c == IDLE && prior_flag && rfifo_usedw <= `RD_UT;
assign write2done = state_c == WRITE && end_cnt;
assign read2done = state_c == READ && end_cnt;
//计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 0;
end
else if(add_cnt)begin
if(end_cnt)
cnt <= 0;
else
cnt <= cnt + 1;
end
end
assign add_cnt = (state_c == WRITE | state_c == READ) & ~avs_waitrequest;
assign end_cnt = add_cnt && cnt== `USER_BL-1;
/************************读写优先级仲裁*****************************/
//rd_flag ;//读请求标志
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rd_flag <= 0;
end
else if(rfifo_usedw <= `RD_LT)begin
rd_flag <= 1'b1;
end
else if(rfifo_usedw > `RD_UT)begin
rd_flag <= 1'b0;
end
end
//wr_flag ;//写请求标志
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
wr_flag <= 0;
end
else if(wfifo_usedw >= `USER_BL)begin
wr_flag <= 1'b1;
end
else begin
wr_flag <= 1'b0;
end
end
//flag_sel ;//标记上一次操作
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
flag_sel <= 0;
end
else if(read2done)begin
flag_sel <= 1;
end
else if(write2done)begin
flag_sel <= 0;
end
end
//prior_flag ;//优先级标志 0:写优先级高 1:读优先级高 仲裁读、写的优先级
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
prior_flag <= 0;
end
else if(wr_flag && (flag_sel || (~flag_sel && ~rd_flag)))begin //突发写优先级高
prior_flag <= 1'b0;
end
else if(rd_flag && (~flag_sel || (flag_sel && ~wr_flag)))begin //突发读优先级高
prior_flag <= 1'b1;
end
end
/******************************************************************/
/******************** 地址设计 ****************************/
//wr_bank rd_bank
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
wr_bank <= 2'b00;
rd_bank <= 2'b11;
end
else if(change_bank)begin
wr_bank <= ~wr_bank;
rd_bank <= ~rd_bank;
end
end
// wr_addr rd_addr
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
wr_addr <= 0;
end
else if(add_wr_addr) begin
if(end_wr_addr)
wr_addr <= 0;
else
wr_addr <= wr_addr+1 ;
end
end
assign add_wr_addr = (state_c == WRITE) && ~avs_waitrequest;
assign end_wr_addr = add_wr_addr && wr_addr == `BURST_MAX-1 ;
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
rd_addr <= 0;
end
else if(add_rd_addr) begin
if(end_rd_addr)
rd_addr <= 0;
else
rd_addr <= rd_addr+1 ;
end
end
assign add_rd_addr = (state_c == READ) && ~avs_waitrequest;
assign end_rd_addr = add_rd_addr && rd_addr == `BURST_MAX-1;
//wr_finish 一帧数据全部写到SDRAM
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
wr_finish <= 1'b0;
end
else if(~wr_finish & end_wr_addr)begin //写完 从wrfifo读出eop
wr_finish <= 1'b1;
end
else if(wr_finish && end_rd_addr)begin //读完
wr_finish <= 1'b0;
end
end
//change_bank ;//切换bank
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
change_bank <= 1'b0;
end
else begin
change_bank <= wr_finish && end_rd_addr;
end
end
/****************************************************************/
/*********************** wrfifo 写数据 ************************/
//控制像素数据帧 写入 或 丢帧
always @(posedge clk_in or negedge rst_n)begin
if(~rst_n)begin
wr_data_flag <= 1'b0;
end
else if(~wr_data_flag & ~wr_finish_r[1] & din_sop)begin//可以向wrfifo写数据
wr_data_flag <= 1'b1;
end
else if(/*wr_finish_r[1] && din_sop*/wr_data_flag & din_eop)begin//不可以向wrfifo写入数据
wr_data_flag <= 1'b0;
end
end
always @(posedge clk_in or negedge rst_n)begin //把wr_finish从wrfifo的读侧同步到写侧
if(~rst_n)begin
wr_finish_r <= 0;
end
else begin
wr_finish_r <= {wr_finish_r[0],wr_finish};
end
end
/****************************************************************/
always @(posedge clk_out or negedge rst_n)begin
if(~rst_n)begin
rd_data <= 0;
rd_data_vld <= 1'b0;
end
else begin
rd_data <= rfifo_q;
rd_data_vld <= rfifo_rdreq;
end
end
wfifo wrfifo_inst (
.aclr (~rst_n ),
.data (wfifo_data ),
.rdclk (clk ),
.rdreq (wfifo_rdreq),
.wrclk (clk_in ),
.wrreq (wfifo_wrreq),
.q (wfifo_q ),
.rdempty(wfifo_empty),
.rdusedw(wfifo_usedw),
.wrfull (wfifo_full )
);
assign wfifo_data = {din_eop,din_sop,din};
assign wfifo_wrreq = ~wfifo_full & din_vld & ((~wr_finish_r[1] & din_sop) ||wr_data_flag);
assign wfifo_rdreq = state_c == WRITE && ~avs_waitrequest;
rfifo u_rdfifo(
.aclr (~rst_n ),
.data (rfifo_data ),
.rdclk (clk_out ),
.rdreq (rfifo_rdreq),
.wrclk (clk ),
.wrreq (rfifo_wrreq),
.q (rfifo_q ),
.rdempty (rfifo_empty),
.wrfull (rfifo_full ),
.wrusedw (rfifo_usedw)
);
assign rfifo_data = avs_rddata;
assign rfifo_wrreq = ~rfifo_full & avs_rddata_vld;
assign rfifo_rdreq = ~rfifo_empty & rdreq;
//输出
assign dout = rd_data;
assign dout_vld = rd_data_vld;
assign avm_wrdata = wfifo_q[15:0];
assign avm_write = ~(state_c == WRITE && ~avs_waitrequest);
assign avm_read = ~(state_c == READ && ~avs_waitrequest);
assign avm_addr = (state_c == WRITE)?{wr_bank[1],wr_addr[21:9],wr_bank[0],wr_addr[8:0]}
:((state_c == READ)?{rd_bank[1],rd_addr[21:9],rd_bank[0],rd_addr[8:0]}
:0);
endmodule
3.vga模块
module vga_dirve (input wire clk, //系统时钟
input wire rst_n, //复位
input wire [ 15:0 ] rgb_data, //16位RGB对应值
output reg h_sync, //行同步信号
output reg v_sync, //场同步信号
output reg [ 11:0 ] addr_h, //行地址
output reg [ 11:0 ] addr_v, //列地址
output wire [ 4:0 ] rgb_r, //红基色
output wire [ 5:0 ] rgb_g, //绿基色
output wire [ 4:0 ] rgb_b //蓝基色
);
//1280 * 640 60HZ
localparam H_FRONT = 110; // 行同步前沿信号周期长
localparam H_SYNC = 40; // 行同步信号周期长
localparam H_BLACK = 220; // 行同步后沿信号周期长
localparam H_ACT = 1280; // 行显示周期长
localparam V_FRONT = 5; // 场同步前沿信号周期长
localparam V_SYNC = 5; // 场同步信号周期长
localparam V_BLACK = 20; // 场同步后沿信号周期长
localparam V_ACT = 720; // 场显示周期长
// 640 * 480 60HZ
// localparam H_FRONT = 16; // 行同步前沿信号周期长
// localparam H_SYNC = 96; // 行同步信号周期长
// localparam H_BLACK = 48; // 行同步后沿信号周期长
// localparam H_ACT = 640; // 行显示周期长
// localparam V_FRONT = 11; // 场同步前沿信号周期长
// localparam V_SYNC = 2; // 场同步信号周期长
// localparam V_BLACK = 31; // 场同步后沿信号周期长
// localparam V_ACT = 480; // 场显示周期长
// 800 * 600 72HZ
// localparam H_FRONT = 40; // 行同步前沿信号周期长
// localparam H_SYNC = 120; // 行同步信号周期长
// localparam H_BLACK = 88; // 行同步后沿信号周期长
// localparam H_ACT = 800; // 行显示周期长
// localparam V_FRONT = 37; // 场同步前沿信号周期长
// localparam V_SYNC = 6; // 场同步信号周期长
// localparam V_BLACK = 23; // 场同步后沿信号周期长
// localparam V_ACT = 600; // 场显示周期长
localparam H_TOTAL = H_FRONT + H_SYNC + H_BLACK + H_ACT; // 行周期
localparam V_TOTAL = V_FRONT + V_SYNC + V_BLACK + V_ACT; // 列周期
reg [ 11:0 ] cnt_h ; // 行计数器
reg [ 11:0 ] cnt_v ; // 场计数器
reg [ 15:0 ] rgb ; // 对应显示颜色值
// 对应计数器开始、结束、计数信号
wire flag_enable_cnt_h ;
wire flag_clear_cnt_h ;
wire flag_enable_cnt_v ;
wire flag_clear_cnt_v ;
wire flag_add_cnt_v ;
wire valid_area ;
// 行计数
always @( posedge clk or negedge rst_n ) begin
if ( !rst_n ) begin
cnt_h <= 0;
end
else if ( flag_enable_cnt_h ) begin
if ( flag_clear_cnt_h ) begin
cnt_h <= 0;
end
else begin
cnt_h <= cnt_h + 1;
end
end
else begin
cnt_h <= 0;
end
end
assign flag_enable_cnt_h = 1;
assign flag_clear_cnt_h = cnt_h == H_TOTAL - 1;
// 行同步信号
always @( posedge clk or negedge rst_n ) begin
if ( !rst_n ) begin
h_sync <= 1;
end
else if ( cnt_h == H_SYNC - 1 ) begin // 同步周期时为1
h_sync <= 0;
end
else if ( flag_clear_cnt_h ) begin // 其余为0
h_sync <= 1;
end
else begin
h_sync <= h_sync;
end
end
// 场计数
always @( posedge clk or negedge rst_n ) begin
if ( !rst_n ) begin
cnt_v <= 0;
end
else if ( flag_enable_cnt_v ) begin
if ( flag_clear_cnt_v ) begin
cnt_v <= 0;
end
else if ( flag_add_cnt_v ) begin
cnt_v <= cnt_v + 1;
end
else begin
cnt_v <= cnt_v;
end
end
else begin
cnt_v <= 0;
end
end
assign flag_enable_cnt_v = flag_enable_cnt_h;
assign flag_clear_cnt_v = cnt_v == V_TOTAL - 1;
assign flag_add_cnt_v = flag_clear_cnt_h;
// 场同步信号
always @( posedge clk or negedge rst_n ) begin
if ( !rst_n ) begin
v_sync <= 1;
end
else if ( cnt_v == V_SYNC - 1 ) begin
v_sync <= 0;
end
else if ( flag_clear_cnt_v ) begin
v_sync <= 1;
end
end
// 对应有效区域行地址 1-640
always @( posedge clk or negedge rst_n ) begin
if ( !rst_n ) begin
addr_h <= 0;
end
else if ( valid_area ) begin
addr_h <= cnt_h - H_SYNC - H_BLACK + 1;
end
else begin
addr_h <= 0;
end
end
// 对应有效区域列地址 1-480
always @( posedge clk or negedge rst_n ) begin
if ( !rst_n ) begin
addr_v <= 0;
end
else if ( valid_area ) begin
addr_v <= cnt_v -V_SYNC - V_BLACK + 1;
end
else begin
addr_v <= 0;
end
end
// 有效显示区域
assign valid_area = cnt_h >= H_SYNC + H_BLACK && cnt_h < H_SYNC + H_BLACK + H_ACT && cnt_v >= V_SYNC + V_BLACK && cnt_v < V_SYNC + V_BLACK + V_ACT;
// 显示颜色
always @( posedge clk or negedge rst_n ) begin
if ( !rst_n ) begin
rgb <= 16'h0;
end
else if ( valid_area ) begin
rgb <= rgb_data;
end
else begin
rgb <= 16'b0;
end
end
assign rgb_r = rgb[ 15:11 ];
assign rgb_g = rgb[ 10:5 ];
assign rgb_b = rgb[ 4:0 ];
endmodule // vga_dirve
4.顶层文件
module camera_top (
input wire clk,
input wire rst_n,
/* 配置寄存器 */
output wire cmos_pwdn,
output wire cmos_reset,
output wire cmos_sioc,
output wire cmos_siod,
output wire cmos_xclk,
input wire cmos_pclk,
input wire cmos_vsync,
input wire cmos_href,
input wire [7:0] cmos_din,
output sdram_clk ,
output sdram_cke ,
output sdram_csn ,
output sdram_rasn ,
output sdram_casn ,
output sdram_wen ,
output [1:0 ] sdram_bank ,
output [12:0] sdram_addr ,
inout [15:0] sdram_dq ,
output [1: 0] sdram_dqm ,
//vga
output wire h_sync,
output wire v_sync,
output wire [15:0] vga_rgb
);
wire clk_24 ;
wire clk_50 ;
wire clk_75 ;
wire clk_100 ;
wire clk_84 ;
wire clk_100_s ;
wire clk_150 ;
wire clk_200 ;
wire pclk ;
wire has_config ;
wire rd_req ;
wire [ 15:0 ] dout ;
wire sop ;
wire eop ;
wire vld ;
wire [ 15:0 ] data ;
wire dout_vld ;
wire [ 11:0 ] addr_h ;
wire [ 11:0 ] addr_v ;
wire [ 15:0 ] rgb_data ;
//PLL
pll pll_inst (
.areset ( ~rst_n ),
.inclk0 ( clk ),
.c0 ( clk_50 ),//50M
.c1 ( clk_24 ),//24M
.c2 ( clk_75 ),//75M
.c3 ( clk_100 ),//100M
.c4 ( clk_84 )
);
pll1 pll1_inst (
.areset ( ~rst_n ),
.inclk0 ( clk ),
.c0 ( clk_100_s ),
.c1 ( clk_150 ),
.c2 ( clk_200 )
);
iobuf u_iobuf(
.datain (cmos_pclk ),
.dataout (pclk )
);
assign sdram_clk = clk_100_s;
assign cmos_xclk = clk_24;
cmos_top u_cmos_top(
.clk ( clk ),
.rst_n ( rst_n ),
.scl ( cmos_sioc ),
.sda ( cmos_siod ),
.pwdn ( cmos_pwdn ),
.reset ( cmos_reset ),
.cfg_done ( has_config )
);
// camera_config_drive u_camera_config_drive(
// .clk ( clk ),
// .rst_n ( rst_n ),
// .pwdn ( cmos_pwdn ),
// .reset ( cmos_reset ),
// .sioc ( cmos_sioc ),
// .siod ( cmos_siod ),
// .done ( has_config )
// );
capture u_capture(
.clk ( pclk ),
.rst_n ( rst_n ),
.vsync ( cmos_vsync ),
.href ( cmos_href ),
.din ( cmos_din ),
.din_vld(has_config),
.sop ( sop ),
.eop ( eop ),
.vld ( vld ),
.pixel ( data )
);
sdram_controller u_sdram_controller(
.clk ( clk_100 ),
.clk_in ( pclk ),
.clk_out ( clk_75 ),
.rst_n ( rst_n ),
.sop ( sop ),
.eop ( eop ),
.din ( data ),
.din_vld ( vld ),
.rd_req ( rd_req ),
.dout ( dout ),
.dout_vld ( dout_vld ),
.mem_cke (sdram_cke ),
.mem_csn (sdram_csn ),
.mem_rasn (sdram_rasn ),
.mem_casn (sdram_casn ),
.mem_wen (sdram_wen ),
.mem_bank (sdram_bank ),
.mem_addr (sdram_addr ),
.mem_dq (sdram_dq ),
.mem_dqm (sdram_dqm )
);
vga_control u_vga_control(
.clk ( clk_75 ),
.rst_n ( rst_n ),
.din ( dout ),
.din_vld ( dout_vld ),
.addr_h ( addr_h ),
.addr_v ( addr_v ),
.rd_req ( rd_req ),
.rgb_data ( rgb_data )
);
vga_dirve u_vga_dirve(
.clk ( clk_75 ),
.rst_n ( rst_n ),
.rgb_data ( rgb_data ),
.h_sync ( h_sync ),
.v_sync ( v_sync ),
.addr_h ( addr_h ),
.addr_v ( addr_v ),
.rgb_r ( rgb_r ),
.rgb_g ( rgb_g ),
.rgb_b ( rgb_b )
);
// vga_interface u_vga(
// /*input */.clk (clk_75 ),
// /*input */.rst_n (rst_n ),
// /*input [15:0] */.din (dout ),
// /*input */.din_vld (dout_vld ),
// /*output */.rdy (rd_req ),
// /*output [15:0] */.vga_rgb (rgb_data ),
// /*output */.vga_hsync(h_sync ),
// /*output */.vga_vsync(v_sync )
// );
assign vga_rgb = rgb_data;
endmodule //camera_top
三、源码
https://github.com/TangtangSix/ov5640