当前位置:网站首页>基于FPGA的图像实时采集
基于FPGA的图像实时采集
2022-07-31 01:46:00 【醉意丶千层梦】
文章目录
一、系统框架
首先整个系统由摄像头模块、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*[email protected]参数输出图像,在行场有效区域内通过拉发起请求读取读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
[email protected](*)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
三、源码
边栏推荐
猜你喜欢
随机推荐
TiDB 在多点数字化零售场景下的应用
uniapp uses 3rd party fonts
太阳能板最大面积 od js
MySQL (6)
Xiaohei's leetcode journey: 117. Fill the next right node pointer of each node II
What are the project management tools like MS Project
pc端判断当前使用浏览器类型
My first understanding of MySql, and the basic syntax of DDL and DML and DQL in sql statements
[WeChat applet] This article takes you to understand data binding, event binding, event parameter transfer, and data synchronization
聚簇索引和非聚簇索引到底有什么区别
充电效果模拟
Meta元宇宙部门第二季度亏损28亿 仍要继续押注?元宇宙发展尚未看到出路
Gateway路由的配置方式
Simple confession page
leetcode-399:除法求值
Set the browser scrollbar style
【genius_platform软件平台开发】第七十四讲:window环境下的静态库和动态库的一些使用方法(VC环境)
16、注册中心-consul
C语言小程序 -- 常见经典练习题
加密生活,Web3 项目合伙人的一天