一、FPGA Cyclone Ⅳ OV5640图像实时采集系统设计
1、系统框架
2、摄像头配置模块
使用SCCB协议配置摄像头
/**************************************功能介绍***********************************
Copyright:
Date :
Author :厉长川
Version :2022.10.10 v1
Description:摄像头配置模块
*********************************************************************************/
`include "param.v"
module sccb(
input clk ,//系统时钟50MHz
input rst_n ,//复位信号
output reg done ,//配置完成标志
output cmos_rst_n ,//摄像头复位信号
output cmos_pwdn ,//摄像头掉电使能信号
output reg cmos_scl ,//时钟线
inout cmos_sda //数据线
);
//参数定义
localparam IDLE = 3'd0 ,//初始化
START = 3'd1 ,//起始
D_ADD = 3'd2 ,//写入器件地址
DC = 3'd3 ,//响应(don't care)
H_ADD = 3'd4 ,//写入寄存器地址高八位
L_ADD = 3'd5 ,//写入寄存器地址低八位
WR = 3'd6 ,//写数据
STOP = 3'd7 ;//停止
//中间信号定义
wire idle2start ;//状态跳转条件
wire start2d_add ;
wire d_add2dc ;
wire dc2h_add ;
wire h_add2dc ;
wire dc2l_add ;
wire l_add2dc ;
wire dc2wr ;
wire wr2dc ;
wire dc2stop ;
wire stop2idle ;
reg [2:0] state_c ;//现态
reg [2:0] state_n ;//次态
reg [5:0] cnt_1m ;//50Mhz时钟做50分频产生1Mhz时钟
wire add_cnt_1m ;
wire end_cnt_1m ;
reg clk_1m ;//1Mhz时钟
reg [1:0] cnt_250k ;//1Mhz时钟做4分频产生250Khz时钟
reg [3:0] cnt_bit ;//bit计数
wire add_cnt_bit ;
reg [7:0] num ;//配置寄存器个数
reg [20:0] cnt_ms ;//20ms计数
wire add_cnt_ms ;
wire end_cnt_ms ;
reg en ;//sccb使能
reg [31:0] lut_data ;//写入数据(分别是器件地址,寄存器地址和寄存器数据,各8bit)
reg [1:0] flag_dc ;//计数进入DC状态次数
reg sda ;//sccb写数据寄存
//cmos_sda:sccb写数据赋值
assign cmos_sda = (state_c != DC)?sda:1'bz;
//cnt_ms:20ms计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_ms <= 21'b0;
end
else if(add_cnt_ms)begin
if(end_cnt_ms)begin
cnt_ms <= 21'b0;
end
else begin
cnt_ms <= cnt_ms + 1'b1;
end
end
end
assign add_cnt_ms = cnt_ms != `CNT_MS;
assign end_cnt_ms = add_cnt_ms && cnt_ms == `CNT_MS + 1'b1;
//cmos_rst_n:摄像头复位信号
assign cmos_rst_n = cnt_ms > `RST_CMOS;
//cmos_pwdn:摄像头掉电使能信号
assign cmos_pwdn = cnt_ms < `PWDN;
//en:sccb使能
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
en <= 1'b0;
end
else if(cnt_ms == `CNT_MS - 1'b1)begin
en <= 1'b1;
end
else if(num == `NUM_CFG)begin
en <= 1'b0;
end
end
//cnt_1m:产生1Mhz时钟计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_1m <= 6'b0;
end
else if(add_cnt_1m)begin
if(end_cnt_1m)begin
cnt_1m <= 6'b0;
end
else begin
cnt_1m <= cnt_1m + 1'b1;
end
end
end
assign add_cnt_1m = (state_c != IDLE);
assign end_cnt_1m = add_cnt_1m && cnt_1m == `CNT_1M - 1'b1;
//clk_1m:1Mhz时钟
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
clk_1m <= 1'b1;
end
else if((cnt_1m == (`CNT_1M >> 1) - 1'b1) || end_cnt_1m)begin
clk_1m <= ~clk_1m;
end
end
//cnt_250k:产生250Khz时钟计数
always @(posedge clk_1m or negedge rst_n)begin
if(!rst_n)begin
cnt_250k <= 2'b0;
end
else begin
cnt_250k <= cnt_250k + 1'b1;
end
end
//cnt_bit:bit计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bit <= 4'b0;
end
else if(add_cnt_bit)begin
cnt_bit <= cnt_bit + 1'b1;
end
else if(state_c == DC)begin
cnt_bit <= 4'b0;
end
end
assign add_cnt_bit = ((state_c == D_ADD) || (state_c == H_ADD) || (state_c == L_ADD) || (state_c == WR))
&& (cnt_250k == 2'd3) && (cnt_1m == 1'b1);
//flag_dc:计数进入DC状态次数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
flag_dc <= 2'b0;
end
else if((cnt_250k == 2'd2) && end_cnt_1m && (state_c == DC))begin
flag_dc <= flag_dc + 1'b1;
end
end
//状态机
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 : if(idle2start)begin
state_n = START;
end
else begin
state_n = state_c;
end
START : if(start2d_add)begin
state_n = D_ADD;
end
else begin
state_n = state_c;
end
D_ADD : if(d_add2dc)begin
state_n = DC;
end
else begin
state_n = state_c;
end
DC : if(dc2h_add)begin
state_n = H_ADD;
end
else if(dc2l_add)begin
state_n = L_ADD;
end
else if(dc2wr)begin
state_n = WR;
end
else if(dc2stop)begin
state_n = STOP;
end
else begin
state_n = state_c;
end
H_ADD : if(h_add2dc)begin
state_n = DC;
end
else begin
state_n = state_c;
end
L_ADD : if(l_add2dc)begin
state_n = DC;
end
else begin
state_n = state_c;
end
WR : if(wr2dc)begin
state_n = DC;
end
else begin
state_n = state_c;
end
STOP : if(stop2idle)begin
state_n = IDLE;
end
else begin
state_n = state_c;
end
default: state_n = state_c;
endcase
end
//给定状态跳转条件
assign idle2start = (state_c == IDLE ) && en ;
assign start2d_add = (state_c == START) && (cnt_250k == 2'd2) && end_cnt_1m ;
assign d_add2dc = (state_c == D_ADD) && (cnt_250k == 2'd2) && end_cnt_1m && (cnt_bit == 4'd8) ;
assign dc2h_add = (state_c == DC ) && (cnt_250k == 2'd2) && end_cnt_1m && (flag_dc == 1'b0) ;
assign h_add2dc = (state_c == H_ADD) && (cnt_250k == 2'd2) && end_cnt_1m && (cnt_bit == 4'd8) ;
assign dc2l_add = (state_c == DC ) && (cnt_250k == 2'd2) && end_cnt_1m && (flag_dc == 1'b1) ;
assign l_add2dc = (state_c == L_ADD) && (cnt_250k == 2'd2) && end_cnt_1m && (cnt_bit == 4'd8) ;
assign dc2wr = (state_c == DC ) && (cnt_250k == 2'd2) && end_cnt_1m && (flag_dc == 2'd2) ;
assign wr2dc = (state_c == WR ) && (cnt_250k == 2'd2) && end_cnt_1m && (cnt_bit == 4'd8) ;
assign dc2stop = (state_c == DC ) && (cnt_250k == 2'd2) && end_cnt_1m && (flag_dc == 2'd3) ;
assign stop2idle = (state_c == STOP ) && (cnt_250k == 2'd2) && end_cnt_1m ;
//cmos_scl:时钟总线
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cmos_scl <= 1'b1;
end
else if((state_c == STOP))begin
if((cnt_250k == 2'd3) && end_cnt_1m)begin
cmos_scl <= 1'b1;
end
end
else if(state_c != IDLE)begin
if((cnt_250k == 1'b1) && end_cnt_1m)begin
cmos_scl <= 1'b0;
end
else if((cnt_250k == 2'd3) && end_cnt_1m)begin
cmos_scl <= 1'b1;
end
end
else begin
cmos_scl <= 1'b1;
end
end
//sda:数据总线
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
sda <= 1'b1;
end
else begin
case (state_c)
START:if((cnt_250k == 1'b0) && end_cnt_1m)begin
sda <= 1'b0;
end
D_ADD:if((cnt_250k == 2'd3) && (cnt_1m == 2'd2))begin
sda <= lut_data[32 - cnt_bit];
end
DC :if((cnt_250k == 2'd3) && (cnt_1m == 2'd2))begin
sda <= 1'b0;
end
H_ADD:if((cnt_250k == 2'd3) && (cnt_1m == 2'd2))begin
sda <= lut_data[24 - cnt_bit];
end
L_ADD:if((cnt_250k == 2'd3) && (cnt_1m == 2'd2))begin
sda <= lut_data[16 - cnt_bit];
end
WR :if((cnt_250k == 2'd3) && (cnt_1m == 2'd2))begin
sda <= lut_data[8 - cnt_bit];
end
STOP :if((cnt_250k == 1'b0) && end_cnt_1m)begin
sda <= 1'b1;
end
default: sda <= 1'b1;
endcase
end
end
//num:配置寄存器个数计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
num <= 1'b0;
end
else if(dc2stop)begin
num <= num + 1'b1;
end
end
//done:配置完成标志
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
done <= 1'b0;
end
else if((num == `NUM_CFG) && stop2idle)begin
done <= 1'b1;
end
end
//lut_data:数据
always@(*)begin
case(num)
8'd0 : lut_data <= {8'h78,16'h3008,8'h82}; //Bit[7]:复位 Bit[6]:电源休眠
8'd1 : lut_data <= {8'h78,16'h3008,8'h02}; //正常工作模式
8'd2 : lut_data <= {8'h78,16'h3103,8'h02}; //Bit[1]:1 PLL Clock
//引脚输入/输出控制 FREX/VSYNC/HREF/PCLK/D[9:6]
8'd3 : lut_data <= {8'h78,8'h30,8'h17,8'hff};
//引脚输入/输出控制 D[5:0]/GPIO1/GPIO0
8'd4 : lut_data <= {8'h78,16'h3018,8'hff};
8'd5 : lut_data <= {8'h78,16'h3037,8'h13}; //PLL分频控制
8'd6 : lut_data <= {8'h78,16'h3108,8'h01}; //系统根分频器
8'd7 : lut_data <= {8'h78,16'h3630,8'h36};
8'd8 : lut_data <= {8'h78,16'h3631,8'h0e};
8'd9 : lut_data <= {8'h78,16'h3632,8'he2};
8'd10 : lut_data <= {8'h78,16'h3633,8'h12};
8'd11 : lut_data <= {8'h78,16'h3621,8'he0};
8'd12 : lut_data <= {8'h78,16'h3704,8'ha0};
8'd13 : lut_data <= {8'h78,16'h3703,8'h5a};
8'd14 : lut_data <= {8'h78,16'h3715,8'h78};
8'd15 : lut_data <= {8'h78,16'h3717,8'h01};
8'd16 : lut_data <= {8'h78,16'h370b,8'h60};
8'd17 : lut_data <= {8'h78,16'h3705,8'h1a};
8'd18 : lut_data <= {8'h78,16'h3905,8'h02};
8'd19 : lut_data <= {8'h78,16'h3906,8'h10};
8'd20 : lut_data <= {8'h78,16'h3901,8'h0a};
8'd21 : lut_data <= {8'h78,16'h3731,8'h12};
8'd22 : lut_data <= {8'h78,16'h3600,8'h08}; //VCM控制,用于自动聚焦
8'd23 : lut_data <= {8'h78,16'h3601,8'h33}; //VCM控制,用于自动聚焦
8'd24 : lut_data <= {8'h78,16'h302d,8'h60}; //系统控制
8'd25 : lut_data <= {8'h78,16'h3620,8'h52};
8'd26 : lut_data <= {8'h78,16'h371b,8'h20};
8'd27 : lut_data <= {8'h78,16'h471c,8'h50};
8'd28 : lut_data <= {8'h78,16'h3a13,8'h43}; //AEC(自动曝光控制)
8'd29 : lut_data <= {8'h78,16'h3a18,8'h00}; //AEC 增益上限
8'd30 : lut_data <= {8'h78,16'h3a19,8'hf8}; //AEC 增益上限
8'd31 : lut_data <= {8'h78,16'h3635,8'h13};
8'd32 : lut_data <= {8'h78,16'h3636,8'h03};
8'd33 : lut_data <= {8'h78,16'h3634,8'h40};
8'd34 : lut_data <= {8'h78,16'h3622,8'h01};
8'd35 : lut_data <= {8'h78,16'h3c01,8'h34};
8'd36 : lut_data <= {8'h78,16'h3c04,8'h28};
8'd37 : lut_data <= {8'h78,16'h3c05,8'h98};
8'd38 : lut_data <= {8'h78,16'h3c06,8'h00}; //light meter 1 阈值[15:8]
8'd39 : lut_data <= {8'h78,16'h3c07,8'h08}; //light meter 1 阈值[7:0]
8'd40 : lut_data <= {8'h78,16'h3c08,8'h00}; //light meter 2 阈值[15:8]
8'd41 : lut_data <= {8'h78,16'h3c09,8'h1c}; //light meter 2 阈值[7:0]
8'd42 : lut_data <= {8'h78,16'h3c0a,8'h9c}; //sample number[15:8]
8'd43 : lut_data <= {8'h78,16'h3c0b,8'h40}; //sample number[7:0]
8'd44 : lut_data <= {8'h78,16'h3810,8'h00}; //Timing Hoffset[11:8]
8'd45 : lut_data <= {8'h78,16'h3811,8'h10}; //Timing Hoffset[7:0]
8'd46 : lut_data <= {8'h78,16'h3812,8'h00}; //Timing Voffset[10:8]
8'd47 : lut_data <= {8'h78,16'h3708,8'h64};
8'd48 : lut_data <= {8'h78,16'h4001,8'h02}; //BLC(黑电平校准)补偿起始行号
8'd49 : lut_data <= {8'h78,16'h4005,8'h1a}; //BLC(黑电平校准)补偿始终更新
8'd50 : lut_data <= {8'h78,16'h3000,8'h00}; //系统块复位控制
8'd51 : lut_data <= {8'h78,16'h3004,8'hff}; //时钟使能控制
8'd52 : lut_data <= {8'h78,16'h4300,8'h61}; //格式控制 RGB565
8'd53 : lut_data <= {8'h78,16'h501f,8'h01}; //ISP RGB
8'd54 : lut_data <= {8'h78,16'h440e,8'h00};
8'd55 : lut_data <= {8'h78,16'h5000,8'ha7}; //ISP控制
8'd56 : lut_data <= {8'h78,16'h3a0f,8'h30}; //AEC控制;stable range in high
8'd57 : lut_data <= {8'h78,16'h3a10,8'h28}; //AEC控制;stable range in low
8'd58 : lut_data <= {8'h78,16'h3a1b,8'h30}; //AEC控制;stable range out high
8'd59 : lut_data <= {8'h78,16'h3a1e,8'h26}; //AEC控制;stable range out low
8'd60 : lut_data <= {8'h78,16'h3a11,8'h60}; //AEC控制; fast zone high
8'd61 : lut_data <= {8'h78,16'h3a1f,8'h14}; //AEC控制; fast zone low
//LENC(镜头校正)控制 16'h5800~16'h583d
8'd62 : lut_data <= {8'h78,16'h5800,8'h23};
8'd63 : lut_data <= {8'h78,16'h5801,8'h14};
8'd64 : lut_data <= {8'h78,16'h5802,8'h0f};
8'd65 : lut_data <= {8'h78,16'h5803,8'h0f};
8'd66 : lut_data <= {8'h78,16'h5804,8'h12};
8'd67 : lut_data <= {8'h78,16'h5805,8'h26};
8'd68 : lut_data <= {8'h78,16'h5806,8'h0c};
8'd69 : lut_data <= {8'h78,16'h5807,8'h08};
8'd70 : lut_data <= {8'h78,16'h5808,8'h05};
8'd71 : lut_data <= {8'h78,16'h5809,8'h05};
8'd72 : lut_data <= {8'h78,16'h580a,8'h08};
8'd73 : lut_data <= {8'h78,16'h580b,8'h0d};
8'd74 : lut_data <= {8'h78,16'h580c,8'h08};
8'd75 : lut_data <= {8'h78,16'h580d,8'h03};
8'd76 : lut_data <= {8'h78,16'h580e,8'h00};
8'd77 : lut_data <= {8'h78,16'h580f,8'h00};
8'd78 : lut_data <= {8'h78,16'h5810,8'h03};
8'd79 : lut_data <= {8'h78,16'h5811,8'h09};
8'd80 : lut_data <= {8'h78,16'h5812,8'h07};
8'd81 : lut_data <= {8'h78,16'h5813,8'h03};
8'd82 : lut_data <= {8'h78,16'h5814,8'h00};
8'd83 : lut_data <= {8'h78,16'h5815,8'h01};
8'd84 : lut_data <= {8'h78,16'h5816,8'h03};
8'd85 : lut_data <= {8'h78,16'h5817,8'h08};
8'd86 : lut_data <= {8'h78,16'h5818,8'h0d};
8'd87 : lut_data <= {8'h78,16'h5819,8'h08};
8'd88 : lut_data <= {8'h78,16'h581a,8'h05};
8'd89 : lut_data <= {8'h78,16'h581b,8'h06};
8'd90 : lut_data <= {8'h78,16'h581c,8'h08};
8'd91 : lut_data <= {8'h78,16'h581d,8'h0e};
8'd92 : lut_data <= {8'h78,16'h581e,8'h29};
8'd93 : lut_data <= {8'h78,16'h581f,8'h17};
8'd94 : lut_data <= {8'h78,16'h5820,8'h11};
8'd95 : lut_data <= {8'h78,16'h5821,8'h11};
8'd96 : lut_data <= {8'h78,16'h5822,8'h15};
8'd97 : lut_data <= {8'h78,16'h5823,8'h28};
8'd98 : lut_data <= {8'h78,16'h5824,8'h46};
8'd99 : lut_data <= {8'h78,16'h5825,8'h26};
8'd100: lut_data <= {8'h78,16'h5826,8'h08};
8'd101: lut_data <= {8'h78,16'h5827,8'h26};
8'd102: lut_data <= {8'h78,16'h5828,8'h64};
8'd103: lut_data <= {8'h78,16'h5829,8'h26};
8'd104: lut_data <= {8'h78,16'h582a,8'h24};
8'd105: lut_data <= {8'h78,16'h582b,8'h22};
8'd106: lut_data <= {8'h78,16'h582c,8'h24};
8'd107: lut_data <= {8'h78,16'h582d,8'h24};
8'd108: lut_data <= {8'h78,16'h582e,8'h06};
8'd109: lut_data <= {8'h78,16'h582f,8'h22};
8'd110: lut_data <= {8'h78,16'h5830,8'h40};
8'd111: lut_data <= {8'h78,16'h5831,8'h42};
8'd112: lut_data <= {8'h78,16'h5832,8'h24};
8'd113: lut_data <= {8'h78,16'h5833,8'h26};
8'd114: lut_data <= {8'h78,16'h5834,8'h24};
8'd115: lut_data <= {8'h78,16'h5835,8'h22};
8'd116: lut_data <= {8'h78,16'h5836,8'h22};
8'd117: lut_data <= {8'h78,16'h5837,8'h26};
8'd118: lut_data <= {8'h78,16'h5838,8'h44};
8'd119: lut_data <= {8'h78,16'h5839,8'h24};
8'd120: lut_data <= {8'h78,16'h583a,8'h26};
8'd121: lut_data <= {8'h78,16'h583b,8'h28};
8'd122: lut_data <= {8'h78,16'h583c,8'h42};
8'd123: lut_data <= {8'h78,16'h583d,8'hce};
//AWB(自动白平衡控制) 16'h5180~16'h519e
8'd124: lut_data <= {8'h78,16'h5180,8'hff};
8'd125: lut_data <= {8'h78,16'h5181,8'hf2};
8'd126: lut_data <= {8'h78,16'h5182,8'h00};
8'd127: lut_data <= {8'h78,16'h5183,8'h14};
8'd128: lut_data <= {8'h78,16'h5184,8'h25};
8'd129: lut_data <= {8'h78,16'h5185,8'h24};
8'd130: lut_data <= {8'h78,16'h5186,8'h09};
8'd131: lut_data <= {8'h78,16'h5187,8'h09};
8'd132: lut_data <= {8'h78,16'h5188,8'h09};
8'd133: lut_data <= {8'h78,16'h5189,8'h75};
8'd134: lut_data <= {8'h78,16'h518a,8'h54};
8'd135: lut_data <= {8'h78,16'h518b,8'he0};
8'd136: lut_data <= {8'h78,16'h518c,8'hb2};
8'd137: lut_data <= {8'h78,16'h518d,8'h42};
8'd138: lut_data <= {8'h78,16'h518e,8'h3d};
8'd139: lut_data <= {8'h78,16'h518f,8'h56};
8'd140: lut_data <= {8'h78,16'h5190,8'h46};
8'd141: lut_data <= {8'h78,16'h5191,8'hf8};
8'd142: lut_data <= {8'h78,16'h5192,8'h04};
8'd143: lut_data <= {8'h78,16'h5193,8'h70};
8'd144: lut_data <= {8'h78,16'h5194,8'hf0};
8'd145: lut_data <= {8'h78,16'h5195,8'hf0};
8'd146: lut_data <= {8'h78,16'h5196,8'h03};
8'd147: lut_data <= {8'h78,16'h5197,8'h01};
8'd148: lut_data <= {8'h78,16'h5198,8'h04};
8'd149: lut_data <= {8'h78,16'h5199,8'h12};
8'd150: lut_data <= {8'h78,16'h519a,8'h04};
8'd151: lut_data <= {8'h78,16'h519b,8'h00};
8'd152: lut_data <= {8'h78,16'h519c,8'h06};
8'd153: lut_data <= {8'h78,16'h519d,8'h82};
8'd154: lut_data <= {8'h78,16'h519e,8'h38};
//Gamma(伽马)控制 16'h5480~16'h5490
8'd155: lut_data <= {8'h78,16'h5480,8'h01};
8'd156: lut_data <= {8'h78,16'h5481,8'h08};
8'd157: lut_data <= {8'h78,16'h5482,8'h14};
8'd158: lut_data <= {8'h78,16'h5483,8'h28};
8'd159: lut_data <= {8'h78,16'h5484,8'h51};
8'd160: lut_data <= {8'h78,16'h5485,8'h65};
8'd161: lut_data <= {8'h78,16'h5486,8'h71};
8'd162: lut_data <= {8'h78,16'h5487,8'h7d};
8'd163: lut_data <= {8'h78,16'h5488,8'h87};
8'd164: lut_data <= {8'h78,16'h5489,8'h91};
8'd165: lut_data <= {8'h78,16'h548a,8'h9a};
8'd166: lut_data <= {8'h78,16'h548b,8'haa};
8'd167: lut_data <= {8'h78,16'h548c,8'hb8};
8'd168: lut_data <= {8'h78,16'h548d,8'hcd};
8'd169: lut_data <= {8'h78,16'h548e,8'hdd};
8'd170: lut_data <= {8'h78,16'h548f,8'hea};
8'd171: lut_data <= {8'h78,16'h5490,8'h1d};
//CMX(彩色矩阵控制) 16'h5381~16'h538b
8'd172: lut_data <= {8'h78,16'h5381,8'h1e};
8'd173: lut_data <= {8'h78,16'h5382,8'h5b};
8'd174: lut_data <= {8'h78,16'h5383,8'h08};
8'd175: lut_data <= {8'h78,16'h5384,8'h0a};
8'd176: lut_data <= {8'h78,16'h5385,8'h7e};
8'd177: lut_data <= {8'h78,16'h5386,8'h88};
8'd178: lut_data <= {8'h78,16'h5387,8'h7c};
8'd179: lut_data <= {8'h78,16'h5388,8'h6c};
8'd180: lut_data <= {8'h78,16'h5389,8'h10};
8'd181: lut_data <= {8'h78,16'h538a,8'h01};
8'd182: lut_data <= {8'h78,16'h538b,8'h98};
//SDE(特殊数码效果)控制 16'h5580~16'h558b
8'd183: lut_data <= {8'h78,16'h5580,8'h06};
8'd184: lut_data <= {8'h78,16'h5583,8'h40};
8'd185: lut_data <= {8'h78,16'h5584,8'h10};
8'd186: lut_data <= {8'h78,16'h5589,8'h10};
8'd187: lut_data <= {8'h78,16'h558a,8'h00};
8'd188: lut_data <= {8'h78,16'h558b,8'hf8};
8'd189: lut_data <= {8'h78,16'h501d,8'h40}; //ISP MISC
//CIP(颜色插值)控制 (16'h5300~16'h530c)
8'd190: lut_data <= {8'h78,16'h5300,8'h08};
8'd191: lut_data <= {8'h78,16'h5301,8'h30};
8'd192: lut_data <= {8'h78,16'h5302,8'h10};
8'd193: lut_data <= {8'h78,16'h5303,8'h00};
8'd194: lut_data <= {8'h78,16'h5304,8'h08};
8'd195: lut_data <= {8'h78,16'h5305,8'h30};
8'd196: lut_data <= {8'h78,16'h5306,8'h08};
8'd197: lut_data <= {8'h78,16'h5307,8'h16};
8'd198: lut_data <= {8'h78,16'h5309,8'h08};
8'd199: lut_data <= {8'h78,16'h530a,8'h30};
8'd200: lut_data <= {8'h78,16'h530b,8'h04};
8'd201: lut_data <= {8'h78,16'h530c,8'h06};
8'd202: lut_data <= {8'h78,16'h5025,8'h00};
//系统时钟分频 Bit[7:4]:系统时钟分频 input clock =24Mhz, PCLK = 48Mhz
8'd203: lut_data <= {8'h78,16'h3035,8'h11};
8'd204: lut_data <= {8'h78,16'h3036,8'h3c}; //PLL倍频
8'd205: lut_data <= {8'h78,16'h3c07,8'h08};
//时序控制 16'h3800~16'h3821
8'd206: lut_data <= {8'h78,16'h3820,8'h46};
8'd207: lut_data <= {8'h78,16'h3821,8'h01};
8'd208: lut_data <= {8'h78,16'h3814,8'h31};
8'd209: lut_data <= {8'h78,16'h3815,8'h31};
8'd210: lut_data <= {8'h78,16'h3800,8'h00};
8'd211: lut_data <= {8'h78,16'h3801,8'h00};
8'd212: lut_data <= {8'h78,16'h3802,8'h00};
8'd213: lut_data <= {8'h78,16'h3803,8'h04};
8'd214: lut_data <= {8'h78,16'h3804,8'h0a};
8'd215: lut_data <= {8'h78,16'h3805,8'h3f};
8'd216: lut_data <= {8'h78,16'h3806,8'h07};
8'd217: lut_data <= {8'h78,16'h3807,8'h9b};
//设置输出像素个数
//DVP 输出水平像素点数高4位
8'd218: lut_data <= {8'h78,16'h3808,8'h04};
//DVP 输出水平像素点数低8位
8'd219: lut_data <= {8'h78,16'h3809,8'h00};
//DVP 输出垂直像素点数高3位
8'd220: lut_data <= {8'h78,16'h380a,8'h03};
//DVP 输出垂直像素点数低8位
8'd221: lut_data <= {8'h78,16'h380b,8'h00};
//水平总像素大小高5位
8'd222: lut_data <= {8'h78,16'h380c,8'h08};
//水平总像素大小低8位
8'd223: lut_data <= {8'h78,16'h380d,8'hc0};
//垂直总像素大小高5位
8'd224: lut_data <= {8'h78,16'h380e,8'h04};
//垂直总像素大小低8位
8'd225: lut_data <= {8'h78,16'h380f,8'hf8};
8'd226: lut_data <= {8'h78,16'h3813,8'h06};
8'd227: lut_data <= {8'h78,16'h3618,8'h00};
8'd228: lut_data <= {8'h78,16'h3612,8'h29};
8'd229: lut_data <= {8'h78,16'h3709,8'h52};
8'd230: lut_data <= {8'h78,16'h370c,8'h03};
8'd231: lut_data <= {8'h78,16'h3a02,8'h17}; //60Hz max exposure
8'd232: lut_data <= {8'h78,16'h3a03,8'h10}; //60Hz max exposure
8'd233: lut_data <= {8'h78,16'h3a14,8'h17}; //50Hz max exposure
8'd234: lut_data <= {8'h78,16'h3a15,8'h10}; //50Hz max exposure
8'd235: lut_data <= {8'h78,16'h4004,8'h02}; //BLC(背光) 2 lines
8'd236: lut_data <= {8'h78,16'h4713,8'h03}; //JPEG mode 3
8'd237: lut_data <= {8'h78,16'h4407,8'h04}; //量化标度
8'd238: lut_data <= {8'h78,16'h460c,8'h22};
8'd239: lut_data <= {8'h78,16'h4837,8'h22}; //DVP CLK divider
8'd240: lut_data <= {8'h78,16'h3824,8'h02}; //DVP CLK divider
8'd241: lut_data <= {8'h78,16'h5001,8'ha3}; //ISP 控制
8'd242: lut_data <= {8'h78,16'h3b07,8'h0a}; //帧曝光模式
//彩条测试使能
8'd243: lut_data <= {8'h78,16'h503d,8'h00}; //8'h00:正常模式 8'h80:彩条显示
//测试闪光灯功能
8'd244: lut_data <= {8'h78,16'h3016,8'h02};
8'd245: lut_data <= {8'h78,16'h301c,8'h02};
8'd246: lut_data <= {8'h78,16'h3019,8'h02}; //打开闪光灯
8'd247: lut_data <= {8'h78,16'h3019,8'h00}; //关闭闪光灯
//只读存储器,防止在case中没有列举的情况,之前的寄存器被重复改写
default : lut_data <= {8'h78,16'h300a,8'h00}; //器件ID高8位
endcase
end
endmodule
3、图像数据拼接模块
将摄像头采集的高八位和低八位图像数据进行拼接
/**************************************功能介绍***********************************
Copyright:
Date :
Author :厉长川
Version :2022.10.10 v1
Description:图像数据采集模块
*********************************************************************************/
`include "param.v"
module pixel_sampling(
input clk ,//像素采集工作时钟,pclk
input rst_n ,//复位信号
input [7:0] din ,//摄像头采集像素
input vsync ,//场同步信号
input href ,//行有效信号
output reg dout_valid ,//输出数据有效标志
output reg [15:0] dout //输出拼接完成的像素
);
//中间信号定义
reg [7:0] din_r ;//像素缓存
reg [1:0] v_r ;//场同步信号同步打拍,检测上升沿
reg flag ;//像素拼接标志,高电平拼接,低电平缓存
reg pixel_valid ;//舍弃10帧完成
reg [3:0] cnt_frame ;//像素帧计数
wire add_cnt_frame ;
wire end_cnt_frame ;
//v_r:场同步信号同步打拍
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
v_r <= 2'b0;
end
else begin
v_r <= {v_r[0], vsync};
end
end
//cnt_frame:像素帧计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_frame <= 4'b0;
end
else if(add_cnt_frame)begin
if(end_cnt_frame)begin
cnt_frame <= 4'b0;
end
else begin
cnt_frame <= cnt_frame + 1'b1;
end
end
end
assign add_cnt_frame = (v_r[0] & ~v_r[1]) && (cnt_frame <= `PIXEL);
assign end_cnt_frame = add_cnt_frame && cnt_frame == `PIXEL + 1'b1;
//pixel_valid:舍弃10帧完成
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
pixel_valid <= 1'b0;
end
else if(cnt_frame == `PIXEL)begin
pixel_valid <= 1'b1;
end
end
//flag:像素拼接标志
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
flag <= 1'b0;
end
else if(href && pixel_valid)begin
flag <= ~flag;
end
else begin
flag <= 1'b0;
end
end
//din_r:像素缓存
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
din_r <= 8'b0;
end
else if(href && pixel_valid)begin
din_r <= din;
end
end
//dout:像素拼接
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dout <= 16'b0;
end
else if(href && pixel_valid && flag)begin
dout <= {din_r, din};
end
end
//dout_valid:输出数据有效标志
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dout_valid <= 1'b0;
end
else if(href && pixel_valid)begin
dout_valid <= flag;
end
else begin
dout_valid <= 1'b0;
end
end
endmodule
4、SDRAM操作模块
SDRAM初始化、自刷新、读和写操作使用一个状态机完成
/**************************************功能介绍***********************************
Copyright:
Date :
Author :厉长川
Version :2022.10.10 v1
Description:SDRAM模块
*********************************************************************************/
`include "param.v"
module sdram(
input clk ,//100Mhz
input rst_n ,//复位,低电平
input w_req ,//写请求
input r_req ,//读请求
input [23:0] w_addr ,//写地址
input [23:0] r_addr ,//读地址
input [15:0] w_data ,//写数据
output [15:0] r_data ,//读数据
output reg [12:0] sdram_addr ,//地址总线
output reg [1:0] sdram_ba ,//bank选择
inout [15:0] sdram_dq ,//数据总线
output reg [3:0] cmd ,//命令
output r_ack ,//写操作响应
output wr_ack ,//读操作响应
output reg init_done ,//初始化结束
output w_done ,//写操作完成标志
output r_done //读操作完成标志
);
//参数定义
parameter IDLE = 4'd0 ,//初始化
PRE = 4'd1 ,//预充电状态
AREF = 4'd2 ,//自动刷新状态
REGIST = 4'd3 ,//模式寄存器配置
W_ACT = 4'd4 ,//写行激活
R_ACT = 4'd5 ,//读行激活
WRITE = 4'd6 ,//写状态
READ = 4'd7 ,//读状态
BURST = 4'd8 ;//突发停止
//状态跳转条件
wire idle2pre ;
wire pre2aref ;
wire aref2regist ;
wire regist2idle ;
wire aref2idle ;
wire idle2w_act ;
wire idle2r_act ;
wire w_act2write ;
wire write2burst ;
wire r_act2read ;
wire read2burst ;
wire burst2pre ;
wire pre2idle ;
//中间信号
reg [3:0] state_c ;//现态
reg [3:0] state_n ;//次态
reg waite_done ;//200us上电等待结束
reg [1:0] atref_time ;//自动刷新次数
reg [13:0] cnt_init ;//上电初始化计数
wire add_cnt_init;
wire end_cnt_init;
reg [9:0] cnt_aref ;//自动刷新计数
wire add_cnt_aref;
wire end_cnt_aref;
reg [1:0] cnt_4clk ;//四周期时钟计数器
wire add_cnt_4clk;
reg [2:0] cnt_7clk ;//7周期时钟计数器
wire add_cnt_7clk;
reg [9:0] cnt_bit ;//读写计数
wire add_cnt_bit ;
wire end_cnt_bit ;
wire rd_ack ;//读响应赋值
reg [2:0] r_ack_r ;//读响应赋值打四拍
reg wr ;//写操作标志
reg rd ;//读操作标志
reg aref_req ;//自动刷新请求
reg ack_r1 ;//写响应打拍
reg ack_r2 ;//读响应打拍
reg [15:0] dq_r ;//sdram读数据打拍
reg w_ack ;//写响应打拍
reg w_en ;//写使能
reg r_en ;//读使能
// w_en:写使能
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
w_en <= 1'b0;
end
else if(w_req)begin
w_en <= 1'b1;
end
else if(wr && pre2idle)begin
w_en <= 1'b0;
end
end
// r_en:读使能
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
r_en <= 1'b0;
end
else if(r_req)begin
r_en <= 1'b1;
end
else if(rd && pre2idle)begin
r_en <= 1'b0;
end
end
// dq_r:sdram读数据打拍
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dq_r <= 16'b0;
end
else begin
dq_r <= sdram_dq;
end
end
// cnt_init:上电等待200us计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_init <= 14'b0;
end
else if(add_cnt_init)begin
if(end_cnt_init)begin
cnt_init <= 14'b0;
end
else begin
cnt_init <= cnt_init + 1'b1;
end
end
end
assign add_cnt_init = cnt_init <= `INIT_TIME;
assign end_cnt_init = add_cnt_init && cnt_init == `INIT_TIME + 1'b1;
// waite_done:上电等待200us结束标志
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
waite_done <= 1'b0;
end
else if(cnt_init == `INIT_TIME - 1'b1)begin
waite_done <= 1'b1;
end
else begin
waite_done <= 1'b0;
end
end
// cnt_aref:自动刷新7us计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_aref <= 10'b0;
end
else if(add_cnt_aref)begin
if(end_cnt_aref)begin
cnt_aref <= 10'b0;
end
else begin
cnt_aref <= cnt_aref + 1'b1;
end
end
end
assign add_cnt_aref = init_done;
assign end_cnt_aref = add_cnt_aref && cnt_aref == `AREF_TIME - 1'b1;
// cnt_4clk:四周期时钟计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_4clk <= 2'b0;
end
else if(add_cnt_4clk)begin
cnt_4clk <= cnt_4clk + 1'b1;
end
end
assign add_cnt_4clk = (state_c == PRE) || (state_c == REGIST) || (state_c == W_ACT)
|| (state_c == R_ACT) || (state_c == BURST);
// cnt_7clk:七周期时钟计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_7clk <= 3'b0;
end
else if(add_cnt_7clk)begin
cnt_7clk <= cnt_7clk + 1'b1;
end
end
assign add_cnt_7clk = (state_c == AREF);
// atref_time:自动刷新次数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
atref_time <= 2'b0;
end
else if(cnt_7clk == 3'd7)begin
atref_time <= atref_time + 1'b1;
end
end
// init_done:SDRAM初始化结束
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
init_done <= 1'b0;
end
else if(regist2idle)begin
init_done <= 1'b1;
end
end
// cnt_bit:读写计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bit <= 10'b0;
end
else if(add_cnt_bit)begin
if(end_cnt_bit)begin
cnt_bit <= 10'b0;
end
else begin
cnt_bit <= cnt_bit + 1'b1;
end
end
end
assign add_cnt_bit = (state_c == WRITE) || (state_c == READ);
assign end_cnt_bit = add_cnt_bit && (((state_c == WRITE) &&
(cnt_bit == `BURST_LEN - 1'b1)) || ((state_c == READ) && (cnt_bit == `BURST_LEN - 1'b1)));
// 写操作标志
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
wr <= 1'b0;
end
else if(write2burst)begin
wr <= 1'b1;
end
else if(pre2idle)begin
wr <= 1'b0;
end
end
// 读操作标志
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rd <= 1'b0;
end
else if(read2burst)begin
rd <= 1'b1;
end
else if(pre2idle)begin
rd <= 1'b0;
end
end
// aref_req:自动刷新请求
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
aref_req <= 1'b0;
end
else if(end_cnt_aref)begin
aref_req <= 1'b1;
end
else if(aref2idle)begin
aref_req <= 1'b0;
end
end
// 状态转移
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 : if(idle2pre)begin
state_n = PRE;
end
else if(idle2w_act)begin
state_n = W_ACT;
end
else if(idle2r_act)begin
state_n = R_ACT;
end
else begin
state_n = state_c;
end
PRE : if(pre2idle)begin
state_n = IDLE;
end
else if(pre2aref)begin
state_n = AREF;
end
else begin
state_n = state_c;
end
AREF : if(aref2idle)begin
state_n = IDLE;
end
else if(aref2regist)begin
state_n = REGIST;
end
else begin
state_n = state_c;
end
REGIST : if(regist2idle)begin
state_n = IDLE;
end
else begin
state_n = state_c;
end
W_ACT : if(w_act2write)begin
state_n = WRITE;
end
else begin
state_n = state_c;
end
R_ACT : if(r_act2read)begin
state_n = READ;
end
else begin
state_n = state_c;
end
WRITE : if(write2burst)begin
state_n = BURST;
end
else begin
state_n = state_c;
end
READ : if(read2burst)begin
state_n = BURST;
end
else begin
state_n = state_c;
end
BURST : if(burst2pre)begin
state_n = PRE;
end
else begin
state_n = state_c;
end
default: state_n = state_c;
endcase
end
// 状态转移条件赋值
assign idle2pre = (state_c == IDLE ) && (waite_done || aref_req) ;//仲裁,进入自动刷新条件
assign pre2aref = (state_c == PRE ) && (cnt_4clk == 2'd3) ;
assign aref2regist = (state_c == AREF ) && (cnt_7clk == 3'd7) && (atref_time == 2'd3) ;
assign regist2idle = (state_c == REGIST) && (cnt_4clk == 2'd3) ;
assign aref2idle = (state_c == AREF ) && (cnt_7clk == 3'd7) && (atref_time == 2'd3) && init_done ;
assign idle2w_act = (state_c == IDLE ) && !aref_req && w_en && init_done ;//仲裁,进入写操作条件
assign idle2r_act = (state_c == IDLE ) && !aref_req && r_en && init_done ;//仲裁,进入读操作条件
assign w_act2write = (state_c == W_ACT ) && (cnt_4clk == 2'd3) ;
assign write2burst = (state_c == WRITE ) && end_cnt_bit ;
assign r_act2read = (state_c == R_ACT ) && (cnt_4clk == 2'd3) ;
assign read2burst = (state_c == READ ) && end_cnt_bit ;
assign burst2pre = (state_c == BURST ) && (cnt_4clk == 2'd3) ;
assign pre2idle = (state_c == PRE ) && (cnt_4clk == 2'd3) && (wr || rd) ;
always @(*)begin
if(!rst_n)begin
cmd = `CMD_NOOP;
sdram_addr = 13'b0;
sdram_ba = 2'b0;
end
else begin
case (state_c)
PRE : if(cnt_4clk == 1'b0)begin
cmd = `CMD_PRE;
sdram_addr = 13'b001_0_00_000_0_000;//选中所有bank进行预充电
sdram_ba = 2'b00;
end
else begin
cmd = `CMD_NOOP;
sdram_addr = 13'b0;
sdram_ba = 2'b0;
end
AREF : if(cnt_7clk == 1'b0)begin
cmd = `CMD_AREF;
sdram_addr = 13'b0;
sdram_ba = 2'b0;
end
else begin
cmd = `CMD_NOOP;
sdram_addr = 13'b0;
sdram_ba = 2'b0;
end
REGIST : if(cnt_4clk == 1'b0)begin
cmd = `CMD_MOD;
sdram_addr = 13'b000_0_00_010_0_111;//配置模式寄存器
sdram_ba = 2'b0;
end
else begin
cmd = `CMD_NOOP;
sdram_addr = 13'b0;
sdram_ba = 2'b0;
end
W_ACT : if(cnt_4clk == 1'b0)begin
cmd = `CMD_ACT;
sdram_addr = w_addr[21:9];
sdram_ba = w_addr[23:22];
end
else begin
cmd = `CMD_NOOP;
sdram_addr = 13'b0;
sdram_ba = 2'b0;
end
R_ACT : if(cnt_4clk == 1'b0)begin
cmd = `CMD_ACT;
sdram_addr = r_addr[21:9];
sdram_ba = r_addr[23:22];
end
else begin
cmd = `CMD_NOOP;
sdram_addr = 13'b0;
sdram_ba = 2'b0;
end
WRITE : if(cnt_bit == 1'b0)begin
cmd = `CMD_WR;
sdram_addr = {4'b0000, w_addr[8:0]};
sdram_ba = w_addr[23:22];
end
else begin
cmd = `CMD_NOOP;
sdram_addr = 13'b0;
sdram_ba = 2'b0;
end
READ : if(cnt_bit == 1'b0)begin
cmd = `CMD_RD;
sdram_addr = {4'b0000, r_addr[8:0]};
sdram_ba = r_addr[23:22];
end
else begin
cmd = `CMD_NOOP;
sdram_addr = 13'b0;
sdram_ba = 2'b0;
end
BURST : if(cnt_4clk == 1'b0)begin
cmd = `CMD_BR;
sdram_addr = 13'b0;
sdram_ba = 2'b0;
end
else begin
cmd = `CMD_NOOP;
sdram_addr = 13'b0;
sdram_ba = 2'b0;
end
default : begin
cmd = `CMD_NOOP;
sdram_addr = 13'b0;
sdram_ba = 2'b0;
end
endcase
end
end
// wr_ack:写响应
assign wr_ack = (state_c == WRITE );
// rd_ack:读响应
assign rd_ack = (state_c == READ );
// r_ack_r:读响应打两拍
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
r_ack_r <= 3'b0;
end
else begin
r_ack_r <= {r_ack_r[1:0], rd_ack};
end
end
// r_ack:输出的读响应
assign r_ack = r_ack_r[2];
// w_ack:写响应打第一拍
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
w_ack <= 1'b0;
end
else begin
w_ack <= wr_ack;
end
end
// ack_r1:写响应打第二拍
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
ack_r1 <= 1'b0;
end
else begin
ack_r1 <= w_ack;
end
end
// ack_r2:输出的读响应打拍
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
ack_r2 <= 1'b0;
end
else begin
ack_r2 <= r_ack;
end
end
// 读写操作完成标志
assign w_done = ack_r1 & ~w_ack;
assign r_done = ack_r2 & ~r_ack;
// 数据:sdram_dq
assign sdram_dq = wr_ack?w_data:16'bz;
// r_data:sdram读出数据
assign r_data = r_ack?dq_r:16'd0;
endmodule
5、乒乓缓存模块
/**************************************功能介绍***********************************
Copyright:
Date :
Author :厉长川
Version :2022.10.10 v1
Description:数据缓存模块
*********************************************************************************/
`include "param.v"
module data_cache(
input clk ,//100MHz工作时钟
input rst_n ,//复位信号
input w_done ,//写完成
input r_done ,//读完成
input init_done ,//sdram初始化结束
input [15:0] r_data ,//sdram数据接收fifo,输入数据
input r_wrclk ,//sdram数据接收fifo,写时钟
input r_wrreq ,//sdram数据接收fifo,写请求
input [15:0] t_data ,//sdram数据发送fifo,输入数据
input t_rdclk ,//sdram数据发送fifo,读时钟
input t_rdreq ,//sdram数据发送fifo,读请求
input w_ack ,//写响应
input r_ack ,//读响应
output [15:0] r_q ,//sdram数据接收fifo,输出数据
output reg [23:0] w_addr ,//sdram写地址
output reg [23:0] r_addr ,//sdram读地址
output [15:0] t_q ,//sdram数据发送fifo,输出数据
output reg sdram_rdreq ,//sdram数据接收fifo,读请求
output reg sdram_wrreq //sdram数据发送fifo,写请求
);
//中间信号定义
wire [9:0] r_rdusedw ;//sdram数据接收fifo,读数据计数
wire [9:0] t_wrusedw ;//sdram数据发送fifo,写数据计数
reg bank_flag ;//bank切换标志
reg bank_en ;//bank切换使能
// bank_flag、bank_en、w_addr:bank切换标志、bank切换使能和写地址
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
bank_en <= 1'b0;
bank_flag <= 1'b0;
w_addr <= 24'b0;
end
else if(w_done)begin
if(w_addr[22:0] >= `ADDR_END)begin
bank_en <= 1'b1;
bank_flag <= ~bank_flag;
end
else begin
w_addr <= w_addr + `BURST_LEN;
end
end
else if(bank_en)begin
bank_en <= 1'b0;
if(bank_flag)begin
w_addr <= {1'b1, 23'b0};
end
else begin
w_addr <= {1'b0, 23'b0};
end
end
end
//r_addr:读地址
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
r_addr <= 24'b0;
end
else if(r_done)begin
if(r_addr[22:0] >= `ADDR_END)begin
if(!bank_flag)begin
r_addr <= {1'b1, 23'b0};
end
else begin
r_addr <= {1'b0, 23'b0};
end
end
else begin
r_addr <= r_addr + `BURST_LEN;
end
end
end
//sdram_rdreq、sdram_wrreq:sdram读写请求
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
sdram_rdreq <= 1'b0;
sdram_wrreq <= 1'b0;
end
else if(init_done)begin
if(r_rdusedw >= `BURST_LEN)begin
sdram_rdreq <= 1'b0;
sdram_wrreq <= 1'b1;
end
else if(t_wrusedw < `BURST_LEN)begin
sdram_rdreq <= 1'b1;
sdram_wrreq <= 1'b0;
end
else begin
sdram_rdreq <= 1'b0;
sdram_wrreq <= 1'b0;
end
end
else begin
sdram_rdreq <= 1'b0;
sdram_wrreq <= 1'b0;
end
end
fifo_r u_fifo_r(
.aclr ( ~rst_n ),
.data (r_data ),
.rdclk (clk ),//sdram数据接收fifo,读时钟
.rdreq (w_ack ),
.wrclk (r_wrclk ),//sdram数据接收fifo,写时钟
.wrreq (r_wrreq ),
.q (r_q ),
.rdusedw (r_rdusedw )
);
fifo_t u_fifo_t(
.aclr ( ~rst_n ),
.data (t_data ),
.rdclk (t_rdclk ),//sdram数据发送fifo,读时钟
.rdreq (t_rdreq ),
.wrclk (clk ),//sdram数据发送fifo,写时钟
.wrreq (r_ack ),
.q (t_q ),
.wrusedw (t_wrusedw )
);
endmodule
6、VGA驱动模块
/**************************************功能介绍***********************************
Copyright:
Date :
Author :厉长川
Version :2022.10.10 v1
Description:VGA驱动模块1024*768
*********************************************************************************/
`include "param.v"
module vga(
input clk ,//65Mhz
input rst_n ,//复位信号
input [15:0] din ,//vga输入数据
output vga_out_hs ,//行同步
output vga_out_vs ,//场同步
output [15:0] vga_dout ,//rgb像素数据
output data_req //像素数据请求信号
);
//中间信号定义
wire data_valid ;//输入像素有效标志
reg [10:0] cnt_h ;//行同步计数器
wire add_cnt_h ;
wire end_cnt_h ;
reg [10:0] cnt_v ;//场同步计数器
wire add_cnt_v ;
wire end_cnt_v ;
//cnt_h:行同步计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_h <= 11'b0;
end
else if(add_cnt_h)begin
if(end_cnt_h)begin
cnt_h <= 11'b0;
end
else begin
cnt_h <= cnt_h + 1'b1;
end
end
end
assign add_cnt_h = 1'b1;
assign end_cnt_h = add_cnt_h && cnt_h == `H_TO - 1'b1;
//cnt_v:场同步计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_v <= 11'b0;
end
else if(add_cnt_v)begin
if(end_cnt_v)begin
cnt_v <= 11'b0;
end
else begin
cnt_v <= cnt_v + 1'b1;
end
end
end
assign add_cnt_v = end_cnt_h;
assign end_cnt_v = add_cnt_v && cnt_v == `V_TO - 1'b1;
//vga_out_hs:行同步
assign vga_out_hs = (cnt_h <= `H_SYNC - 1'b1) ;
//vga_out_vs:场同步
assign vga_out_vs = (cnt_v <= `V_SYNC - 1'b1) ;
//data_req:像素数据请求信号
assign data_req = (`H_SYNC + `H_BP - 2'd2 < cnt_h) && (cnt_h <= `H_SYNC + `H_BP + `H_ACTIVE - 2'd2) &&
(`V_SYNC + `V_BP < cnt_v) && (cnt_v <= `V_SYNC + `V_BP + `V_ACTIVE);
//data_valid
assign data_valid = (`H_SYNC + `H_BP < cnt_h) && (cnt_h <= `H_SYNC + `H_BP + `H_ACTIVE) &&
(`V_SYNC + `V_BP < cnt_v) && (cnt_v <= `V_SYNC + `V_BP + `V_ACTIVE);
//vga_dout:rgb像素数据
assign vga_dout = data_valid ? din : 16'b0;
endmodule
7、顶层模块
/**************************************功能介绍***********************************
Copyright:
Date :
Author :厉长川
Version :2022.10.10 v1
Description:顶层模块
*********************************************************************************/
module img_pro(
input clk ,//系统时钟50Mhz
input rst_n ,//复位信号,低电平有效
input [7:0] cmos_db ,//摄像头采集数据
input cmos_pclk ,//摄像头pclk时钟48MHz
input cmos_vsync ,//摄像头场同步信号
input cmos_href ,//摄像头行有效信号
output cmos_rst_n ,//摄像头复位信号
output cmos_xclk ,//摄像头xclk时钟24Mhz
output cmos_pwdn ,//摄像头掉电使能信号
output cmos_scl ,//摄像头配置时钟信号
inout cmos_sda ,//摄像头配置数据信号
output sdram_clk ,//sdram工作时钟
output sdram_cke ,//sdram使能
output [12:0] sdram_addr ,//sdram地址总线
output [1:0] sdram_ba ,//sdram bank地址
inout [15:0] sdram_dq ,//sdram数据总线
output [1:0] sdram_dqm ,//数据掩码
output [3:0] cmd ,//sdram操作命令
output vga_out_hs ,//vga行同步信号
output vga_out_vs ,//vga场同步信号
output [15:0] vga_dout //vga输出的RGB数据
);
//中间信号定义
wire vga_clk ;//vga时钟
wire clk_100MHz ;//100MHz工作时钟
wire done ;//摄像头配置完成标志
wire [15:0] pixel_dout ;//采集的像素数据
wire dout_valid ;//采集数据有效标志
wire [15:0] w_data ;//sdram写入数据
wire [15:0] r_data ;//sdram读出数据
wire [15:0] vga_din ;//vga输入数据
wire data_req ;//vga数据请求
wire [23:0] w_addr ;//sdram写地址
wire [23:0] r_addr ;//sdram读地址
wire sdram_rdreq ;//sdram数据接收fifo,读请求
wire sdram_wrreq ;//sdram数据发送fifo,写请求
wire r_ack ;//读响应
wire wr_ack ;//写响应
wire locked_1 ;//pll_1输出稳定标志
wire locked_2 ;//pll_2输出稳定标志
wire reset ;//sccb、data_cache、sdram和vga模块复位信号
wire init_done ;//sdram初始化结束
wire pixel_rst ;//pixel_sampling模块复位信号
wire w_done ;//写完成标志
wire r_done ;//读完成标志
//sdram_cke:sdram使能
assign sdram_cke = 1'b1;
//sdram_dqm:数据掩码
assign sdram_dqm = 2'b00;
//reset:sccb、data_cache、sdram和vga模块复位信号
assign reset = locked_1 & locked_2 & rst_n;
//pixel_rst:pixel_sampling模块复位信号
assign pixel_rst = done & init_done & reset;
sccb u_sccb(
.clk (clk ),
.rst_n (reset ),
.done (done ),
.cmos_rst_n (cmos_rst_n ),
.cmos_pwdn (cmos_pwdn ),
.cmos_scl (cmos_scl ),
.cmos_sda (cmos_sda )
);
pixel_sampling u_pixel_sampling(
.clk (cmos_pclk ),//摄像头pclk时钟48MHz
.rst_n (pixel_rst ),//pixel_sampling模块复位信号
.din (cmos_db ),//摄像头采集数据
.vsync (cmos_vsync ),//摄像头场同步信号
.href (cmos_href ),//摄像头行有效信号
.dout_valid (dout_valid ),//输出数据有效标志
.dout (pixel_dout ) //输出拼接完成的像素
);
data_cache u_data_cache(
.clk (clk_100MHz ),
.rst_n (reset ),
.w_done (w_done ),
.r_done (r_done ),
.init_done (init_done ),
.r_data (pixel_dout ),
.r_wrclk (cmos_pclk ),
.r_wrreq (dout_valid ),
.t_data (r_data ),
.t_rdclk (vga_clk ),
.t_rdreq (data_req ),
.w_ack (wr_ack ),
.r_ack (r_ack ),
.r_q (w_data ),
.w_addr (w_addr ),
.r_addr (r_addr ),
.t_q (vga_din ),
.sdram_rdreq (sdram_rdreq ),//sdram数据接收fifo,读请求
.sdram_wrreq (sdram_wrreq ) //sdram数据发送fifo,写请求
);
sdram u_sdram(
.clk (clk_100MHz ),
.rst_n (reset ),
.w_req (sdram_wrreq ),
.r_req (sdram_rdreq ),
.w_addr (w_addr ),
.r_addr (r_addr ),
.w_data (w_data ),
.r_data (r_data ),
.sdram_addr (sdram_addr ),
.sdram_ba (sdram_ba ),
.sdram_dq (sdram_dq ),
.cmd (cmd ),
.init_done (init_done ),
.r_ack (r_ack ),
.wr_ack (wr_ack ),
.w_done (w_done ),//写操作完成标志
.r_done (r_done ) //读操作完成标志
);
vga u_vga(
.clk (vga_clk ),
.rst_n (reset ),
.din (vga_din ),
.vga_out_hs (vga_out_hs ),
.vga_out_vs (vga_out_vs ),
.vga_dout (vga_dout ),
.data_req (data_req )
);
pll_1 pll_1_inst (
.areset (~rst_n ),
.inclk0 (clk ),//50MHz
.c0 (vga_clk ),//65MHz
.c1 (clk_100MHz ),//100MHz
.c2 (sdram_clk ),//100MHz,-75deg
.locked (locked_1 )
);
pll_2 pll_2_inst (
.areset (~rst_n ),
.inclk0 (clk ),//50MHz系统时钟
.c0 (cmos_xclk ),//24Mhz
.locked (locked_2 )
);
endmodule
8、参数定义
仿真时可以修改参数节省仿真时间
/**************************************功能介绍***********************************
Copyright:
Date :
Author :厉长川
Version :2022.10.10 v1
Description:参数定义
*********************************************************************************/
//sccb参数定义
`define CNT_1M 6'd50 //1M时钟计数
`define CNT_MS 21'd1315000 //26.3ms时间计数
`define PWDN 18'd255000 //5.1ms计时
`define RST_CMOS 19'd310000 //1.1ms复位等待时间
`define NUM_CFG 8'd248 //寄存器配置个数
//pixel_sampling参数定义
`define PIXEL 4'd10 //舍弃帧数
//vga接口参数
// 1024*768 65Mhz
`define H_ACTIVE 11'd1024 //行有效
`define H_FP 11'd24 //行前沿
`define H_SYNC 11'd136 //行同步
`define H_BP 11'd160 //行后沿
`define H_TO 11'd1344 //行周期
`define V_ACTIVE 11'd768 //场有效
`define V_FP 11'd3 //场前沿
`define V_SYNC 11'd6 //场同步
`define V_BP 11'd29 //场后沿
`define V_TO 11'd806 //场周期
//sdram模块参数
`define CMD_NOOP 4'b0111 //空指令
`define CMD_ACT 4'b0011 //行激活指令
`define CMD_RD 4'b0101 //读指令
`define CMD_WR 4'b0100 //写指令
`define CMD_BR 4'b0110 //突发终止指令
`define CMD_PRE 4'b0010 //预充电指令
`define CMD_AREF 4'b0001 //自动刷新指令
`define CMD_MOD 4'b0000 //模式寄存器配置命令
`define INIT_TIME 14'd10000 //上电等待时间,10000个时钟周期(100us)
`define AREF_TIME 10'd700 //自动刷新间隔时间,700个时钟周期(7us)
`define BURST_LEN 10'd512 //读写突发长度
`define ADDR_END `H_ACTIVE * `V_ACTIVE - `BURST_LEN //读写地址末地址
9、最终效果
①:分析综合
②:上板验证