目录
VGA驱动模块:
OV5640摄像头
顶层文件:
COMS模块:
摄像头cfg配置:
IIc模块:
SDRAM模块:
TOP模块
FIFO控制模块:
建两个FIFO
SDRAM控制模块TOP
SDRAM命令控制模块
SDRAM 状态控制模块
SDRAM 数据读写模块
SDRAM的参数文件
camera_top:
module cam_vga(
input sys_clk , //系统时钟
input sys_rst_n , //系统复位,低电平有效
//摄像头接口
input cam_pclk , //cmos 数据像素时钟
input cam_vsync , //cmos 场同步信号
input cam_href , //cmos 行同步信号
input [7:0] cam_data , //cmos 数据
output cam_rst_n , //cmos 复位信号,低电平有效
output cam_pwdn , //cmos 电源休眠模式选择信号
output cam_scl , //cmos SCCB_SCL线
inout cam_sda , //cmos SCCB_SDA线
//SDRAM接口
output sdram_clk , //SDRAM 时钟
output sdram_cke , //SDRAM 时钟有效
output sdram_cs_n , //SDRAM 片选
output sdram_ras_n , //SDRAM 行有效
output sdram_cas_n , //SDRAM 列有效
output sdram_we_n , //SDRAM 写有效
output [1:0] sdram_ba , //SDRAM Bank地址
output [1:0] sdram_dqm , //SDRAM 数据掩码
output [12:0] sdram_addr , //SDRAM 地址
inout [15:0] sdram_data , //SDRAM 数据
//VGA接口
output vga_hs , //行同步信号
output vga_vs , //场同步信号
output [15:0] vga_rgb //红绿蓝三原色输出
);
//parameter define
parameter SLAVE_ADDR = 7'h3c ; //OV5640的器件地址7'h3c
parameter BIT_CTRL = 1'b1 ; //OV5640的字节地址为16位 0:8位 1:16位
parameter CLK_FREQ = 26'd65_000_000; //i2c_dri模块的驱动时钟频率 65MHz
parameter I2C_FREQ = 18'd250_000 ; //I2C的SCL时钟频率,不超过400KHz
parameter CMOS_H_PIXEL = 24'd1024 ; //CMOS水平方向像素个数,用于设置SDRAM缓存大小
parameter CMOS_V_PIXEL = 24'd768 ; //CMOS垂直方向像素个数,用于设置SDRAM缓存大小
wire [15:0] wr_data ; //sdram_ctrl模块写数据
wire [15:0] rd_data ; //sdram_ctrl模块读数据
//*****************************************************
//** main code
//*****************************************************
assign rst_n = sys_rst_n & locked;
//系统初始化完成:SDRAM和摄像头都初始化完成
//避免了在SDRAM初始化过程中向里面写入数据
//assign sys_init_done = sdram_init_done & cam_init_done;
//不对摄像头硬件复位,固定高电平
assign cam_rst_n = 1'b1;
//电源休眠模式选择 0:正常模式 1:电源休眠模式
assign cam_pwdn = 1'b0;
//锁相环
pll_clk u_pll_clk(
.areset (~sys_rst_n),
.inclk0 (sys_clk),
.c0 (clk_100m), //100mhz时钟,SDRAM操作时钟
.c1 (clk_100m_shift), //100mhz时钟,SDRAM相位偏移时钟
.c2 (clk_65m), //65mhz时钟,提供给IIC驱动时钟和vga驱动时钟
.locked (locked)
);
//摄像头模块
camera
#(
.SLAVE_ADDR (SLAVE_ADDR),
.BIT_CTRL (BIT_CTRL),
.CLK_FREQ (CLK_FREQ) ,
.I2C_FREQ (I2C_FREQ),
.CMOS_H_PIXEL (CMOS_H_PIXEL),
.CMOS_V_PIXEL (CMOS_V_PIXEL)
)
cam(
.clk_65m (clk_65m),
.rst_n (rst_n),
.cam_pclk (cam_pclk), //cmos 数据像素时钟
.cam_vsync (cam_vsync), //cmos 场同步信号
.cam_href (cam_href), //cmos 行同步信号
.cam_data (cam_data), //cmos 数据
//与SDRAM的接口
.sdram_init_done (sdram_init_done),
.cmos_frame_valid (wr_en), //数据有效使能信号
.cmos_frame_data (wr_data), //有效数据
.cam_scl (cam_scl), // I2C的SCL时钟信号
.cam_sda (cam_sda)// I2C的SDA信号
);
//SDRAM 控制器顶层模块,封装成FIFO接口
//SDRAM 控制器地址组成: {bank_addr[1:0],row_addr[12:0],col_addr[8:0]}
sdram_top u_sdram_top(
.ref_clk (clk_100m), //sdram 控制器参考时钟
.out_clk (clk_100m_shift), //用于输出的相位偏移时钟
.rst_n (rst_n), //系统复位
//用户写端口
.wr_clk (cam_pclk), //写端口FIFO: 写时钟
.wr_en (wr_en), //写端口FIFO: 写使能
.wr_data (wr_data), //写端口FIFO: 写数据
.wr_min_addr (24'd0), //写SDRAM的起始地址
.wr_max_addr (CMOS_H_PIXEL*CMOS_V_PIXEL), //写SDRAM的结束地址
.wr_len (10'd512), //写SDRAM时的数据突发长度
.wr_load (~rst_n), //写端口复位: 复位写地址,清空写FIFO
//用户读端口
.rd_clk (clk_65m), //读端口FIFO: 读时钟
.rd_en (rd_en), //读端口FIFO: 读使能
.rd_data (rd_data), //读端口FIFO: 读数据
.rd_min_addr (24'd0), //读SDRAM的起始地址
.rd_max_addr (CMOS_H_PIXEL*CMOS_V_PIXEL), //读SDRAM的结束地址
.rd_len (10'd512), //从SDRAM中读数据时的突发长度
.rd_load (~rst_n), //读端口复位: 复位读地址,清空读FIFO
//用户控制端口
.sdram_read_valid (1'b1), //SDRAM 读使能
.sdram_pingpang_en (1'b1), //SDRAM 乒乓操作使能
.sdram_init_done (sdram_init_done), //SDRAM 初始化完成标志
//SDRAM 芯片接口
.sdram_clk (sdram_clk), //SDRAM 芯片时钟
.sdram_cke (sdram_cke), //SDRAM 时钟有效
.sdram_cs_n (sdram_cs_n), //SDRAM 片选
.sdram_ras_n (sdram_ras_n), //SDRAM 行有效
.sdram_cas_n (sdram_cas_n), //SDRAM 列有效
.sdram_we_n (sdram_we_n), //SDRAM 写有效
.sdram_ba (sdram_ba), //SDRAM Bank地址
.sdram_addr (sdram_addr), //SDRAM 行/列地址
.sdram_data (sdram_data), //SDRAM 数据
.sdram_dqm (sdram_dqm) //SDRAM 数据掩码
);
//VGA驱动模块
vga_driver u_vga_driver(
.vga_clk (clk_65m),
.sys_rst_n (rst_n),
.vga_hs (vga_hs),
.vga_vs (vga_vs),
.vga_rgb (vga_rgb),
.pixel_data (rd_data),
.data_req (rd_en), //请求像素点颜色数据输入
.pixel_xpos (),
.pixel_ypos ()
);
endmodule
VGA驱动模块:
解释说明:
3进————5出
输入:
1.50M时钟
2.复位
3.送进来的 16位 像素 图像的一个像素点
输出:
1.行同步 直接与VGA接口相连
2.场同步 直接与VGA接口相连
3.RGB16位像素输出 直接与VGA接口相连
4.像素点横坐标 这个一般是处理像素要在哪位置,图像处理
5.像素点纵坐标 这个一般是处理像素要在哪位置,图像处理
module vga_driver(
input vga_clk, //VGA驱动时钟
input sys_rst_n, //复位信号
//VGA接口
output vga_hs, //行同步信号
output vga_vs, //场同步信号
output [15:0] vga_rgb, //红绿蓝三原色输出
input [15:0] pixel_data, //像素点数据
output data_req , //请求像素点颜色数据输入
output [ 9:0] pixel_xpos, //像素点横坐标
output [ 9:0] pixel_ypos //像素点纵坐标
);
//parameter define
parameter H_SYNC = 10'd96; //行同步
parameter H_BACK = 10'd48; //行显示后沿
parameter H_DISP = 10'd640; //行有效数据
parameter H_FRONT = 10'd16; //行显示前沿
parameter H_TOTAL = 10'd800; //行扫描周期
parameter V_SYNC = 10'd2; //场同步
parameter V_BACK = 10'd33; //场显示后沿
parameter V_DISP = 10'd480; //场有效数据
parameter V_FRONT = 10'd10; //场显示前沿
parameter V_TOTAL = 10'd525; //场扫描周期
//reg define
reg [9:0] cnt_h;
reg [9:0] cnt_v;
//wire define
wire vga_en;
wire data_req;
//*****************************************************
//** main code
//*****************************************************
//VGA行场同步信号
assign vga_hs = (cnt_h <= H_SYNC - 1'b1) ? 1'b0 : 1'b1;
assign vga_vs = (cnt_v <= V_SYNC - 1'b1) ? 1'b0 : 1'b1;
//使能RGB565数据输出
assign vga_en = (((cnt_h >= H_SYNC+H_BACK) && (cnt_h < H_SYNC+H_BACK+H_DISP))
&&((cnt_v >= V_SYNC+V_BACK) && (cnt_v < V_SYNC+V_BACK+V_DISP)))
? 1'b1 : 1'b0;
//RGB565数据输出
assign vga_rgb = vga_en ? pixel_data : 16'd0;
//请求像素点颜色数据输入
assign data_req = (((cnt_h >= H_SYNC+H_BACK-1'b1) && (cnt_h < H_SYNC+H_BACK+H_DISP-1'b1))
&& ((cnt_v >= V_SYNC+V_BACK) && (cnt_v < V_SYNC+V_BACK+V_DISP)))
? 1'b1 : 1'b0;
//像素点坐标
assign pixel_xpos = data_req ? (cnt_h - (H_SYNC + H_BACK - 1'b1)) : 10'd0;
assign pixel_ypos = data_req ? (cnt_v - (V_SYNC + V_BACK - 1'b1)) : 10'd0;
//行计数器对像素时钟计数
always @(posedge vga_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
cnt_h <= 10'd0;
else begin
if(cnt_h < H_TOTAL - 1'b1)
cnt_h <= cnt_h + 1'b1;
else
cnt_h <= 10'd0;
end
end
//场计数器对行计数
always @(posedge vga_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
cnt_v <= 10'd0;
else if(cnt_h == H_TOTAL - 1'b1) begin
if(cnt_v < V_TOTAL - 1'b1)
cnt_v <= cnt_v + 1'b1;
else
cnt_v <= 10'd0;
end
end
endmodule
OV5640摄像头
顶层文件:
module camera_top(
input clk_65m ,
input rst_n ,
//与SDRAM的接口
input sdram_init_done ,
output cmos_frame_valid, //数据有效使能信号
output [15:0] cmos_frame_data , //有效数据
// 硬件输入
input cam_pclk , //cmos 数据像素时钟
input cam_vsync , //cmos 场同步信号
input cam_href , //cmos 行同步信号
input [7:0] cam_data , //cmos 数据
// 硬件输出
output cam_scl , // I2C的SCL时钟信号
inout cam_sda // I2C的SDA信号
);
//parameter define
parameter SLAVE_ADDR = 7'h3c ; //OV5640的器件地址7'h3c
parameter BIT_CTRL = 1'b1 ; //OV5640的字节地址为16位 0:8位 1:16位
parameter CLK_FREQ = 26'd65_000_000; //i2c_dri模块的驱动时钟频率 65MHz
parameter I2C_FREQ = 18'd250_000 ; //I2C的SCL时钟频率,不超过400KHz
parameter CMOS_H_PIXEL = 24'd1024 ; //CMOS水平方向像素个数,用于设置SDRAM缓存大小
parameter CMOS_V_PIXEL = 24'd768 ; //CMOS垂直方向像素个数,用于设置SDRAM缓存大小
wire [23:0] i2c_data ;
assign sys_init_done = sdram_init_done & cam_init_done;
//I2C配置模块
i2c_ov5640_rgb565_cfg
#(
.CMOS_H_PIXEL (CMOS_H_PIXEL),
.CMOS_V_PIXEL (CMOS_V_PIXEL)
)
u_i2c_cfg(
.clk (i2c_dri_clk),
.rst_n (rst_n),
.i2c_done (i2c_done),
.i2c_exec (i2c_exec),
.i2c_data (i2c_data),
.init_done (cam_init_done)
);
//I2C驱动模块
i2c_dri
#(
.SLAVE_ADDR (SLAVE_ADDR), //参数传递
.CLK_FREQ (CLK_FREQ ),
.I2C_FREQ (I2C_FREQ )
)
u_i2c_dri(
.clk (clk_65m ),
.rst_n (rst_n ),
.i2c_exec (i2c_exec ),
.bit_ctrl (BIT_CTRL ),
.i2c_rh_wl (1'b0), //固定为0,只用到了IIC驱动的写操作
.i2c_addr (i2c_data[23:8]),
.i2c_data_w (i2c_data[7:0]),
.i2c_data_r (),
.i2c_done (i2c_done ),
.scl (cam_scl ),
.sda (cam_sda ),
.dri_clk (i2c_dri_clk) //I2C操作时钟
);
//CMOS图像数据采集模块
cmos_capture_data u_cmos_capture_data( //系统初始化完成之后再开始采集数据
.rst_n (rst_n & sys_init_done), //只有当iic配置+SDRAM配置完成,才行
.cam_pclk (cam_pclk),
.cam_vsync (cam_vsync),
.cam_href (cam_href),
.cam_data (cam_data),
.cmos_frame_vsync (),
.cmos_frame_href (),
.cmos_frame_valid (cmos_frame_valid), //数据有效使能信号
.cmos_frame_data (cmos_frame_data) //有效数据
);
endmodule
COMS模块:
module cmos_capture_data(
input rst_n , //复位信号
//摄像头接口
input cam_pclk , //cmos 数据像素时钟
input cam_vsync , //cmos 场同步信号
input cam_href , //cmos 行同步信号
input [7:0] cam_data , //cmos 数据
//用户接口
output cmos_frame_vsync, //帧有效信号
output cmos_frame_href , //行有效信号
output cmos_frame_valid, //数据有效使能信号
output [15:0] cmos_frame_data //有效数据
);
//寄存器全部配置完成后,先等待10帧数据
//待寄存器配置生效后再开始采集图像
parameter WAIT_FRAME = 4'd10 ; //寄存器数据稳定等待的帧个数
//reg define
reg cam_vsync_d0 ;
reg cam_vsync_d1 ;
reg cam_href_d0 ;
reg cam_href_d1 ;
reg [3:0] cmos_ps_cnt ; //等待帧数稳定计数器
reg frame_val_flag ; //帧有效的标志
reg [7:0] cam_data_d0 ;
reg [15:0] cmos_data_t ; //用于8位转16位的临时寄存器
reg byte_flag ;
reg byte_flag_d0 ;
//wire define
wire pos_vsync ;
//*****************************************************
//** main code
//*****************************************************
//采输入场同步信号的上升沿
assign pos_vsync = (~cam_vsync_d1) & cam_vsync_d0;
//输出帧有效信号
assign cmos_frame_vsync = frame_val_flag ? cam_vsync_d1 : 1'b0;
//输出行有效信号
assign cmos_frame_href = frame_val_flag ? cam_href_d1 : 1'b0;
//输出数据使能有效信号
assign cmos_frame_valid = frame_val_flag ? byte_flag_d0 : 1'b0;
//输出数据
assign cmos_frame_data = frame_val_flag ? cmos_data_t : 1'b0;
//采输入场同步信号的上升沿
always @(posedge cam_pclk or negedge rst_n) begin
if(!rst_n) begin
cam_vsync_d0 <= 1'b0;
cam_vsync_d1 <= 1'b0;
cam_href_d0 <= 1'b0;
cam_href_d1 <= 1'b0;
end
else begin
cam_vsync_d0 <= cam_vsync;
cam_vsync_d1 <= cam_vsync_d0;
cam_href_d0 <= cam_href;
cam_href_d1 <= cam_href_d0;
end
end
//对帧数进行计数
always @(posedge cam_pclk or negedge rst_n) begin
if(!rst_n)
cmos_ps_cnt <= 4'd0;
else if(pos_vsync && (cmos_ps_cnt < WAIT_FRAME))
cmos_ps_cnt <= cmos_ps_cnt + 4'd1;
end
//帧有效标志
always @(posedge cam_pclk or negedge rst_n) begin
if(!rst_n)
frame_val_flag <= 1'b0;
else if((cmos_ps_cnt == WAIT_FRAME) && pos_vsync)
frame_val_flag <= 1'b1;
else;
end
//8位数据转16位RGB565数据
always @(posedge cam_pclk or negedge rst_n) begin
if(!rst_n) begin
cmos_data_t <= 16'd0;
cam_data_d0 <= 8'd0;
byte_flag <= 1'b0;
end
else if(cam_href) begin
byte_flag <= ~byte_flag;
cam_data_d0 <= cam_data;
if(byte_flag)
cmos_data_t <= {cam_data_d0,cam_data};
else;
end
else begin
byte_flag <= 1'b0;
cam_data_d0 <= 8'b0;
end
end
//产生输出数据有效信号(cmos_frame_valid)
always @(posedge cam_pclk or negedge rst_n) begin
if(!rst_n)
byte_flag_d0 <= 1'b0;
else
byte_flag_d0 <= byte_flag;
end
endmodule
摄像头cfg配置:
module i2c_ov5640_rgb565_cfg
#(
parameter CMOS_H_PIXEL = 24'd1024,//CMOS水平方向像素个数
parameter CMOS_V_PIXEL = 24'd768 //CMOS垂直方向像素个数
)
(
input clk , //时钟信号
input rst_n , //复位信号,低电平有效
input i2c_done , //I2C寄存器配置完成信号
output reg i2c_exec , //I2C触发执行信号
output reg [23:0] i2c_data , //I2C要配置的地址与数据(高16位地址,低8位数据)
output reg init_done //初始化完成信号
);
//parameter define
localparam REG_NUM = 8'd248 ; //总共需要配置的寄存器个数
localparam TOTAL_H_PIXEL = CMOS_H_PIXEL + 13'd1216; //水平总像素大小
localparam TOTAL_V_PIXEL = CMOS_V_PIXEL + 13'd504; //垂直总像素大小
//reg define
reg [14:0] start_init_cnt; //等待延时计数器
reg [7:0] init_reg_cnt ; //寄存器配置个数计数器
//*****************************************************
//** main code
//*****************************************************
//cam_scl配置成250khz,输入的clk为1Mhz,周期为1us,20000*1us = 20ms
//OV5640上电到开始配置IIC至少等待20ms
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
start_init_cnt <= 15'd0;
else if(start_init_cnt < 15'd20000)
start_init_cnt <= start_init_cnt + 1'b1;
end
//寄存器配置个数计数
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
init_reg_cnt <= 8'd0;
else if(i2c_exec)
init_reg_cnt <= init_reg_cnt + 8'b1;
end
//i2c触发执行信号
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
i2c_exec <= 1'b0;
else if(start_init_cnt == 15'd19999)
i2c_exec <= 1'b1;
else if(i2c_done && (init_reg_cnt < REG_NUM))
i2c_exec <= 1'b1;
else
i2c_exec <= 1'b0;
end
//初始化完成信号
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
init_done <= 1'b0;
else if((init_reg_cnt == REG_NUM) && i2c_done)
init_done <= 1'b1;
end
//配置寄存器地址与数据
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
i2c_data <= 24'd0;
else begin
case(init_reg_cnt)
//先对寄存器进行软件复位,使寄存器恢复初始值
8'd0 : i2c_data <= {16'h3008,8'h82}; //Bit[7]:复位 Bit[6]:电源休眠
8'd1 : i2c_data <= {16'h3008,8'h02}; //正常工作模式
8'd2 : i2c_data <= {16'h3103,8'h02}; //Bit[1]:1 PLL Clock
//引脚输入/输出控制 FREX/VSYNC/HREF/PCLK/D[9:6]
8'd3 : i2c_data <= {8'h30,8'h17,8'hff};
//引脚输入/输出控制 D[5:0]/GPIO1/GPIO0
8'd4 : i2c_data <= {16'h3018,8'hff};
8'd5 : i2c_data <= {16'h3037,8'h13}; //PLL分频控制
8'd6 : i2c_data <= {16'h3108,8'h01}; //系统根分频器
8'd7 : i2c_data <= {16'h3630,8'h36};
8'd8 : i2c_data <= {16'h3631,8'h0e};
8'd9 : i2c_data <= {16'h3632,8'he2};
8'd10 : i2c_data <= {16'h3633,8'h12};
8'd11 : i2c_data <= {16'h3621,8'he0};
8'd12 : i2c_data <= {16'h3704,8'ha0};
8'd13 : i2c_data <= {16'h3703,8'h5a};
8'd14 : i2c_data <= {16'h3715,8'h78};
8'd15 : i2c_data <= {16'h3717,8'h01};
8'd16 : i2c_data <= {16'h370b,8'h60};
8'd17 : i2c_data <= {16'h3705,8'h1a};
8'd18 : i2c_data <= {16'h3905,8'h02};
8'd19 : i2c_data <= {16'h3906,8'h10};
8'd20 : i2c_data <= {16'h3901,8'h0a};
8'd21 : i2c_data <= {16'h3731,8'h12};
8'd22 : i2c_data <= {16'h3600,8'h08}; //VCM控制,用于自动聚焦
8'd23 : i2c_data <= {16'h3601,8'h33}; //VCM控制,用于自动聚焦
8'd24 : i2c_data <= {16'h302d,8'h60}; //系统控制
8'd25 : i2c_data <= {16'h3620,8'h52};
8'd26 : i2c_data <= {16'h371b,8'h20};
8'd27 : i2c_data <= {16'h471c,8'h50};
8'd28 : i2c_data <= {16'h3a13,8'h43}; //AEC(自动曝光控制)
8'd29 : i2c_data <= {16'h3a18,8'h00}; //AEC 增益上限
8'd30 : i2c_data <= {16'h3a19,8'hf8}; //AEC 增益上限
8'd31 : i2c_data <= {16'h3635,8'h13};
8'd32 : i2c_data <= {16'h3636,8'h03};
8'd33 : i2c_data <= {16'h3634,8'h40};
8'd34 : i2c_data <= {16'h3622,8'h01};
8'd35 : i2c_data <= {16'h3c01,8'h34};
8'd36 : i2c_data <= {16'h3c04,8'h28};
8'd37 : i2c_data <= {16'h3c05,8'h98};
8'd38 : i2c_data <= {16'h3c06,8'h00}; //light meter 1 阈值[15:8]
8'd39 : i2c_data <= {16'h3c07,8'h08}; //light meter 1 阈值[7:0]
8'd40 : i2c_data <= {16'h3c08,8'h00}; //light meter 2 阈值[15:8]
8'd41 : i2c_data <= {16'h3c09,8'h1c}; //light meter 2 阈值[7:0]
8'd42 : i2c_data <= {16'h3c0a,8'h9c}; //sample number[15:8]
8'd43 : i2c_data <= {16'h3c0b,8'h40}; //sample number[7:0]
8'd44 : i2c_data <= {16'h3810,8'h00}; //Timing Hoffset[11:8]
8'd45 : i2c_data <= {16'h3811,8'h10}; //Timing Hoffset[7:0]
8'd46 : i2c_data <= {16'h3812,8'h00}; //Timing Voffset[10:8]
8'd47 : i2c_data <= {16'h3708,8'h64};
8'd48 : i2c_data <= {16'h4001,8'h02}; //BLC(黑电平校准)补偿起始行号
8'd49 : i2c_data <= {16'h4005,8'h1a}; //BLC(黑电平校准)补偿始终更新
8'd50 : i2c_data <= {16'h3000,8'h00}; //系统块复位控制
8'd51 : i2c_data <= {16'h3004,8'hff}; //时钟使能控制
8'd52 : i2c_data <= {16'h4300,8'h61}; //格式控制 RGB565
8'd53 : i2c_data <= {16'h501f,8'h01}; //ISP RGB
8'd54 : i2c_data <= {16'h440e,8'h00};
8'd55 : i2c_data <= {16'h5000,8'ha7}; //ISP控制
8'd56 : i2c_data <= {16'h3a0f,8'h30}; //AEC控制;stable range in high
8'd57 : i2c_data <= {16'h3a10,8'h28}; //AEC控制;stable range in low
8'd58 : i2c_data <= {16'h3a1b,8'h30}; //AEC控制;stable range out high
8'd59 : i2c_data <= {16'h3a1e,8'h26}; //AEC控制;stable range out low
8'd60 : i2c_data <= {16'h3a11,8'h60}; //AEC控制; fast zone high
8'd61 : i2c_data <= {16'h3a1f,8'h14}; //AEC控制; fast zone low
//LENC(镜头校正)控制 16'h5800~16'h583d
8'd62 : i2c_data <= {16'h5800,8'h23};
8'd63 : i2c_data <= {16'h5801,8'h14};
8'd64 : i2c_data <= {16'h5802,8'h0f};
8'd65 : i2c_data <= {16'h5803,8'h0f};
8'd66 : i2c_data <= {16'h5804,8'h12};
8'd67 : i2c_data <= {16'h5805,8'h26};
8'd68 : i2c_data <= {16'h5806,8'h0c};
8'd69 : i2c_data <= {16'h5807,8'h08};
8'd70 : i2c_data <= {16'h5808,8'h05};
8'd71 : i2c_data <= {16'h5809,8'h05};
8'd72 : i2c_data <= {16'h580a,8'h08};
8'd73 : i2c_data <= {16'h580b,8'h0d};
8'd74 : i2c_data <= {16'h580c,8'h08};
8'd75 : i2c_data <= {16'h580d,8'h03};
8'd76 : i2c_data <= {16'h580e,8'h00};
8'd77 : i2c_data <= {16'h580f,8'h00};
8'd78 : i2c_data <= {16'h5810,8'h03};
8'd79 : i2c_data <= {16'h5811,8'h09};
8'd80 : i2c_data <= {16'h5812,8'h07};
8'd81 : i2c_data <= {16'h5813,8'h03};
8'd82 : i2c_data <= {16'h5814,8'h00};
8'd83 : i2c_data <= {16'h5815,8'h01};
8'd84 : i2c_data <= {16'h5816,8'h03};
8'd85 : i2c_data <= {16'h5817,8'h08};
8'd86 : i2c_data <= {16'h5818,8'h0d};
8'd87 : i2c_data <= {16'h5819,8'h08};
8'd88 : i2c_data <= {16'h581a,8'h05};
8'd89 : i2c_data <= {16'h581b,8'h06};
8'd90 : i2c_data <= {16'h581c,8'h08};
8'd91 : i2c_data <= {16'h581d,8'h0e};
8'd92 : i2c_data <= {16'h581e,8'h29};
8'd93 : i2c_data <= {16'h581f,8'h17};
8'd94 : i2c_data <= {16'h5820,8'h11};
8'd95 : i2c_data <= {16'h5821,8'h11};
8'd96 : i2c_data <= {16'h5822,8'h15};
8'd97 : i2c_data <= {16'h5823,8'h28};
8'd98 : i2c_data <= {16'h5824,8'h46};
8'd99 : i2c_data <= {16'h5825,8'h26};
8'd100: i2c_data <= {16'h5826,8'h08};
8'd101: i2c_data <= {16'h5827,8'h26};
8'd102: i2c_data <= {16'h5828,8'h64};
8'd103: i2c_data <= {16'h5829,8'h26};
8'd104: i2c_data <= {16'h582a,8'h24};
8'd105: i2c_data <= {16'h582b,8'h22};
8'd106: i2c_data <= {16'h582c,8'h24};
8'd107: i2c_data <= {16'h582d,8'h24};
8'd108: i2c_data <= {16'h582e,8'h06};
8'd109: i2c_data <= {16'h582f,8'h22};
8'd110: i2c_data <= {16'h5830,8'h40};
8'd111: i2c_data <= {16'h5831,8'h42};
8'd112: i2c_data <= {16'h5832,8'h24};
8'd113: i2c_data <= {16'h5833,8'h26};
8'd114: i2c_data <= {16'h5834,8'h24};
8'd115: i2c_data <= {16'h5835,8'h22};
8'd116: i2c_data <= {16'h5836,8'h22};
8'd117: i2c_data <= {16'h5837,8'h26};
8'd118: i2c_data <= {16'h5838,8'h44};
8'd119: i2c_data <= {16'h5839,8'h24};
8'd120: i2c_data <= {16'h583a,8'h26};
8'd121: i2c_data <= {16'h583b,8'h28};
8'd122: i2c_data <= {16'h583c,8'h42};
8'd123: i2c_data <= {16'h583d,8'hce};
//AWB(自动白平衡控制) 16'h5180~16'h519e
8'd124: i2c_data <= {16'h5180,8'hff};
8'd125: i2c_data <= {16'h5181,8'hf2};
8'd126: i2c_data <= {16'h5182,8'h00};
8'd127: i2c_data <= {16'h5183,8'h14};
8'd128: i2c_data <= {16'h5184,8'h25};
8'd129: i2c_data <= {16'h5185,8'h24};
8'd130: i2c_data <= {16'h5186,8'h09};
8'd131: i2c_data <= {16'h5187,8'h09};
8'd132: i2c_data <= {16'h5188,8'h09};
8'd133: i2c_data <= {16'h5189,8'h75};
8'd134: i2c_data <= {16'h518a,8'h54};
8'd135: i2c_data <= {16'h518b,8'he0};
8'd136: i2c_data <= {16'h518c,8'hb2};
8'd137: i2c_data <= {16'h518d,8'h42};
8'd138: i2c_data <= {16'h518e,8'h3d};
8'd139: i2c_data <= {16'h518f,8'h56};
8'd140: i2c_data <= {16'h5190,8'h46};
8'd141: i2c_data <= {16'h5191,8'hf8};
8'd142: i2c_data <= {16'h5192,8'h04};
8'd143: i2c_data <= {16'h5193,8'h70};
8'd144: i2c_data <= {16'h5194,8'hf0};
8'd145: i2c_data <= {16'h5195,8'hf0};
8'd146: i2c_data <= {16'h5196,8'h03};
8'd147: i2c_data <= {16'h5197,8'h01};
8'd148: i2c_data <= {16'h5198,8'h04};
8'd149: i2c_data <= {16'h5199,8'h12};
8'd150: i2c_data <= {16'h519a,8'h04};
8'd151: i2c_data <= {16'h519b,8'h00};
8'd152: i2c_data <= {16'h519c,8'h06};
8'd153: i2c_data <= {16'h519d,8'h82};
8'd154: i2c_data <= {16'h519e,8'h38};
//Gamma(伽马)控制 16'h5480~16'h5490
8'd155: i2c_data <= {16'h5480,8'h01};
8'd156: i2c_data <= {16'h5481,8'h08};
8'd157: i2c_data <= {16'h5482,8'h14};
8'd158: i2c_data <= {16'h5483,8'h28};
8'd159: i2c_data <= {16'h5484,8'h51};
8'd160: i2c_data <= {16'h5485,8'h65};
8'd161: i2c_data <= {16'h5486,8'h71};
8'd162: i2c_data <= {16'h5487,8'h7d};
8'd163: i2c_data <= {16'h5488,8'h87};
8'd164: i2c_data <= {16'h5489,8'h91};
8'd165: i2c_data <= {16'h548a,8'h9a};
8'd166: i2c_data <= {16'h548b,8'haa};
8'd167: i2c_data <= {16'h548c,8'hb8};
8'd168: i2c_data <= {16'h548d,8'hcd};
8'd169: i2c_data <= {16'h548e,8'hdd};
8'd170: i2c_data <= {16'h548f,8'hea};
8'd171: i2c_data <= {16'h5490,8'h1d};
//CMX(彩色矩阵控制) 16'h5381~16'h538b
8'd172: i2c_data <= {16'h5381,8'h1e};
8'd173: i2c_data <= {16'h5382,8'h5b};
8'd174: i2c_data <= {16'h5383,8'h08};
8'd175: i2c_data <= {16'h5384,8'h0a};
8'd176: i2c_data <= {16'h5385,8'h7e};
8'd177: i2c_data <= {16'h5386,8'h88};
8'd178: i2c_data <= {16'h5387,8'h7c};
8'd179: i2c_data <= {16'h5388,8'h6c};
8'd180: i2c_data <= {16'h5389,8'h10};
8'd181: i2c_data <= {16'h538a,8'h01};
8'd182: i2c_data <= {16'h538b,8'h98};
//SDE(特殊数码效果)控制 16'h5580~16'h558b
8'd183: i2c_data <= {16'h5580,8'h06};
8'd184: i2c_data <= {16'h5583,8'h40};
8'd185: i2c_data <= {16'h5584,8'h10};
8'd186: i2c_data <= {16'h5589,8'h10};
8'd187: i2c_data <= {16'h558a,8'h00};
8'd188: i2c_data <= {16'h558b,8'hf8};
8'd189: i2c_data <= {16'h501d,8'h40}; //ISP MISC
//CIP(颜色插值)控制 (16'h5300~16'h530c)
8'd190: i2c_data <= {16'h5300,8'h08};
8'd191: i2c_data <= {16'h5301,8'h30};
8'd192: i2c_data <= {16'h5302,8'h10};
8'd193: i2c_data <= {16'h5303,8'h00};
8'd194: i2c_data <= {16'h5304,8'h08};
8'd195: i2c_data <= {16'h5305,8'h30};
8'd196: i2c_data <= {16'h5306,8'h08};
8'd197: i2c_data <= {16'h5307,8'h16};
8'd198: i2c_data <= {16'h5309,8'h08};
8'd199: i2c_data <= {16'h530a,8'h30};
8'd200: i2c_data <= {16'h530b,8'h04};
8'd201: i2c_data <= {16'h530c,8'h06};
8'd202: i2c_data <= {16'h5025,8'h00};
//系统时钟分频 Bit[7:4]:系统时钟分频 input clock =24Mhz, PCLK = 48Mhz
8'd203: i2c_data <= {16'h3035,8'h11};
8'd204: i2c_data <= {16'h3036,8'h3c}; //PLL倍频
8'd205: i2c_data <= {16'h3c07,8'h08};
//时序控制 16'h3800~16'h3821
8'd206: i2c_data <= {16'h3820,8'h46};
8'd207: i2c_data <= {16'h3821,8'h01};
8'd208: i2c_data <= {16'h3814,8'h31};
8'd209: i2c_data <= {16'h3815,8'h31};
8'd210: i2c_data <= {16'h3800,8'h00};
8'd211: i2c_data <= {16'h3801,8'h00};
8'd212: i2c_data <= {16'h3802,8'h00};
8'd213: i2c_data <= {16'h3803,8'h04};
8'd214: i2c_data <= {16'h3804,8'h0a};
8'd215: i2c_data <= {16'h3805,8'h3f};
8'd216: i2c_data <= {16'h3806,8'h07};
8'd217: i2c_data <= {16'h3807,8'h9b};
//设置输出像素个数
//DVP 输出水平像素点数高4位
8'd218: i2c_data <= {16'h3808,{4'd0,CMOS_H_PIXEL[11:8]}};
//DVP 输出水平像素点数低8位
8'd219: i2c_data <= {16'h3809,CMOS_H_PIXEL[7:0]};
//DVP 输出垂直像素点数高3位
8'd220: i2c_data <= {16'h380a,{5'd0,CMOS_V_PIXEL[10:8]}};
//DVP 输出垂直像素点数低8位
8'd221: i2c_data <= {16'h380b,CMOS_V_PIXEL[7:0]};
//水平总像素大小高5位
8'd222: i2c_data <= {16'h380c,{3'd0,TOTAL_H_PIXEL[12:8]}};
//水平总像素大小低8位
8'd223: i2c_data <= {16'h380d,TOTAL_H_PIXEL[7:0]};
//垂直总像素大小高5位
8'd224: i2c_data <= {16'h380e,{3'd0,TOTAL_V_PIXEL[12:8]}};
//垂直总像素大小低8位
8'd225: i2c_data <= {16'h380f,TOTAL_V_PIXEL[7:0]};
8'd226: i2c_data <= {16'h3813,8'h06};
8'd227: i2c_data <= {16'h3618,8'h00};
8'd228: i2c_data <= {16'h3612,8'h29};
8'd229: i2c_data <= {16'h3709,8'h52};
8'd230: i2c_data <= {16'h370c,8'h03};
8'd231: i2c_data <= {16'h3a02,8'h17}; //60Hz max exposure
8'd232: i2c_data <= {16'h3a03,8'h10}; //60Hz max exposure
8'd233: i2c_data <= {16'h3a14,8'h17}; //50Hz max exposure
8'd234: i2c_data <= {16'h3a15,8'h10}; //50Hz max exposure
8'd235: i2c_data <= {16'h4004,8'h02}; //BLC(背光) 2 lines
8'd236: i2c_data <= {16'h4713,8'h03}; //JPEG mode 3
8'd237: i2c_data <= {16'h4407,8'h04}; //量化标度
8'd238: i2c_data <= {16'h460c,8'h22};
8'd239: i2c_data <= {16'h4837,8'h22}; //DVP CLK divider
8'd240: i2c_data <= {16'h3824,8'h02}; //DVP CLK divider
8'd241: i2c_data <= {16'h5001,8'ha3}; //ISP 控制
8'd242: i2c_data <= {16'h3b07,8'h0a}; //帧曝光模式
//彩条测试使能
8'd243: i2c_data <= {16'h503d,8'h00}; //8'h00:正常模式 8'h80:彩条显示
//测试闪光灯功能
8'd244: i2c_data <= {16'h3016,8'h02};
8'd245: i2c_data <= {16'h301c,8'h02};
8'd246: i2c_data <= {16'h3019,8'h02}; //打开闪光灯
8'd247: i2c_data <= {16'h3019,8'h00}; //关闭闪光灯
//只读存储器,防止在case中没有列举的情况,之前的寄存器被重复改写
default : i2c_data <= {16'h300a,8'h00}; //器件ID高8位
endcase
end
end
endmodule
IIc模块:
module i2c_dri
#(// slave address(器件地址),放此处方便参数传递
parameter SLAVE_ADDR = 7'b1010000 ,
parameter CLK_FREQ = 26'd50_000_000, // i2c_dri模块的驱动时钟频率(CLK_FREQ)
parameter I2C_FREQ = 18'd250_000 // I2C的SCL时钟频率
)(
//global clock
input clk , // i2c_dri模块的驱动时钟(CLK_FREQ)
input rst_n , // 复位信号
//i2c interface
input i2c_exec , // I2C触发执行信号
input bit_ctrl , // 字地址位控制(16b/8b)
input i2c_rh_wl , // I2C读写控制信号
input [15:0] i2c_addr , // I2C器件内地址
input [ 7:0] i2c_data_w , // I2C要写的数据
output reg [ 7:0] i2c_data_r , // I2C读出的数据
output reg i2c_done , // I2C一次操作完成
output reg scl , // I2C的SCL时钟信号
inout sda , // I2C的SDA信号
//user interface
output reg dri_clk // 驱动I2C操作的驱动时钟
);
//localparam define
localparam st_idle = 8'b0000_0001; // 空闲状态
localparam st_sladdr = 8'b0000_0010; // 发送器件地址(slave address)
localparam st_addr16 = 8'b0000_0100; // 发送16位字地址
localparam st_addr8 = 8'b0000_1000; // 发送8位字地址
localparam st_data_wr = 8'b0001_0000; // 写数据(8 bit)
localparam st_addr_rd = 8'b0010_0000; // 发送器件地址读
localparam st_data_rd = 8'b0100_0000; // 读数据(8 bit)
localparam st_stop = 8'b1000_0000; // 结束I2C操作
//reg define
reg sda_dir ; // I2C数据(SDA)方向控制
reg sda_out ; // SDA输出信号
reg st_done ; // 状态结束
reg wr_flag ; // 写标志
reg [ 6:0] cnt ; // 计数
reg [ 7:0] cur_state ; // 状态机当前状态
reg [ 7:0] next_state ; // 状态机下一状态
reg [15:0] addr_t ; // 地址
reg [ 7:0] data_r ; // 读取的数据
reg [ 7:0] data_wr_t ; // I2C需写的数据的临时寄存
reg [ 9:0] clk_cnt ; // 分频时钟计数
//wire define
wire sda_in ; // SDA输入信号
wire [8:0] clk_divide ; // 模块驱动时钟的分频系数
//*****************************************************
//** main code
//*****************************************************
//SDA控制
assign sda = sda_dir ? sda_out : 1'bz; // SDA数据输出或高阻
assign sda_in = sda ; // SDA数据输入
assign clk_divide = (CLK_FREQ/I2C_FREQ) >> 3; // 模块驱动时钟的分频系数
//生成I2C的SCL的四倍频率的驱动时钟用于驱动i2c的操作
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
dri_clk <= 1'b1;
clk_cnt <= 10'd0;
end
else if(clk_cnt == clk_divide - 1'd1) begin
clk_cnt <= 10'd0;
dri_clk <= ~dri_clk;
end
else
clk_cnt <= clk_cnt + 1'b1;
end
//(三段式状态机)同步时序描述状态转移
always @(posedge dri_clk or negedge rst_n) begin
if(!rst_n)
cur_state <= st_idle;
else
cur_state <= next_state;
end
//组合逻辑判断状态转移条件
always @( * ) begin
// next_state = st_idle;
case(cur_state)
st_idle: begin // 空闲状态
if(i2c_exec) begin
next_state = st_sladdr;
end
else
next_state = st_idle;
end
st_sladdr: begin
if(st_done) begin
if(bit_ctrl) // 判断是16位还是8位字地址
next_state = st_addr16;
else
next_state = st_addr8 ;
end
else
next_state = st_sladdr;
end
st_addr16: begin // 写16位字地址
if(st_done) begin
next_state = st_addr8;
end
else begin
next_state = st_addr16;
end
end
st_addr8: begin // 8位字地址
if(st_done) begin
if(wr_flag==1'b0) // 读写判断
next_state = st_data_wr;
else
next_state = st_addr_rd;
end
else begin
next_state = st_addr8;
end
end
st_data_wr: begin // 写数据(8 bit)
if(st_done)
next_state = st_stop;
else
next_state = st_data_wr;
end
st_addr_rd: begin // 写地址以进行读数据
if(st_done) begin
next_state = st_data_rd;
end
else begin
next_state = st_addr_rd;
end
end
st_data_rd: begin // 读取数据(8 bit)
if(st_done)
next_state = st_stop;
else
next_state = st_data_rd;
end
st_stop: begin // 结束I2C操作
if(st_done)
next_state = st_idle;
else
next_state = st_stop ;
end
default: next_state= st_idle;
endcase
end
//时序电路描述状态输出
always @(posedge dri_clk or negedge rst_n) begin
//复位初始化
if(!rst_n) begin
scl <= 1'b1;
sda_out <= 1'b1;
sda_dir <= 1'b1;
i2c_done <= 1'b0;
cnt <= 1'b0;
st_done <= 1'b0;
data_r <= 1'b0;
i2c_data_r <= 1'b0;
wr_flag <= 1'b0;
addr_t <= 1'b0;
data_wr_t <= 1'b0;
end
else begin
st_done <= 1'b0 ;
cnt <= cnt +1'b1 ;
case(cur_state)
st_idle: begin // 空闲状态
scl <= 1'b1;
sda_out <= 1'b1;
sda_dir <= 1'b1;
i2c_done<= 1'b0;
cnt <= 7'b0;
if(i2c_exec) begin
wr_flag <= i2c_rh_wl ;
addr_t <= i2c_addr ;
data_wr_t <= i2c_data_w;
end
end
st_sladdr: begin // 写地址(器件地址和字地址)
case(cnt)
7'd1 : sda_out <= 1'b0; // 开始I2C
7'd3 : scl <= 1'b0;
7'd4 : sda_out <= SLAVE_ADDR[6]; // 传送器件地址
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= SLAVE_ADDR[5];
7'd9 : scl <= 1'b1;
7'd11: scl <= 1'b0;
7'd12: sda_out <= SLAVE_ADDR[4];
7'd13: scl <= 1'b1;
7'd15: scl <= 1'b0;
7'd16: sda_out <= SLAVE_ADDR[3];
7'd17: scl <= 1'b1;
7'd19: scl <= 1'b0;
7'd20: sda_out <= SLAVE_ADDR[2];
7'd21: scl <= 1'b1;
7'd23: scl <= 1'b0;
7'd24: sda_out <= SLAVE_ADDR[1];
7'd25: scl <= 1'b1;
7'd27: scl <= 1'b0;
7'd28: sda_out <= SLAVE_ADDR[0];
7'd29: scl <= 1'b1;
7'd31: scl <= 1'b0;
7'd32: sda_out <= 1'b0; // 0:写
7'd33: scl <= 1'b1;
7'd35: scl <= 1'b0;
7'd36: begin
sda_dir <= 1'b0; // 从机应答
sda_out <= 1'b1;
end
7'd37: scl <= 1'b1;
7'd38: st_done <= 1'b1;
7'd39: begin
scl <= 1'b0;
cnt <= 1'b0;
end
default : ;
endcase
end
st_addr16: begin
case(cnt)
7'd0 : begin
sda_dir <= 1'b1 ;
sda_out <= addr_t[15]; // 传送字地址
end
7'd1 : scl <= 1'b1;
7'd3 : scl <= 1'b0;
7'd4 : sda_out <= addr_t[14];
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= addr_t[13];
7'd9 : scl <= 1'b1;
7'd11: scl <= 1'b0;
7'd12: sda_out <= addr_t[12];
7'd13: scl <= 1'b1;
7'd15: scl <= 1'b0;
7'd16: sda_out <= addr_t[11];
7'd17: scl <= 1'b1;
7'd19: scl <= 1'b0;
7'd20: sda_out <= addr_t[10];
7'd21: scl <= 1'b1;
7'd23: scl <= 1'b0;
7'd24: sda_out <= addr_t[9];
7'd25: scl <= 1'b1;
7'd27: scl <= 1'b0;
7'd28: sda_out <= addr_t[8];
7'd29: scl <= 1'b1;
7'd31: scl <= 1'b0;
7'd32: begin
sda_dir <= 1'b0; // 从机应答
sda_out <= 1'b1;
end
7'd33: scl <= 1'b1;
7'd34: st_done <= 1'b1;
7'd35: begin
scl <= 1'b0;
cnt <= 1'b0;
end
default : ;
endcase
end
st_addr8: begin
case(cnt)
7'd0: begin
sda_dir <= 1'b1 ;
sda_out <= addr_t[7]; // 字地址
end
7'd1 : scl <= 1'b1;
7'd3 : scl <= 1'b0;
7'd4 : sda_out <= addr_t[6];
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= addr_t[5];
7'd9 : scl <= 1'b1;
7'd11: scl <= 1'b0;
7'd12: sda_out <= addr_t[4];
7'd13: scl <= 1'b1;
7'd15: scl <= 1'b0;
7'd16: sda_out <= addr_t[3];
7'd17: scl <= 1'b1;
7'd19: scl <= 1'b0;
7'd20: sda_out <= addr_t[2];
7'd21: scl <= 1'b1;
7'd23: scl <= 1'b0;
7'd24: sda_out <= addr_t[1];
7'd25: scl <= 1'b1;
7'd27: scl <= 1'b0;
7'd28: sda_out <= addr_t[0];
7'd29: scl <= 1'b1;
7'd31: scl <= 1'b0;
7'd32: begin
sda_dir <= 1'b0; // 从机应答
sda_out <= 1'b1;
end
7'd33: scl <= 1'b1;
7'd34: st_done <= 1'b1;
7'd35: begin
scl <= 1'b0;
cnt <= 1'b0;
end
default : ;
endcase
end
st_data_wr: begin // 写数据(8 bit)
case(cnt)
7'd0: begin
sda_out <= data_wr_t[7]; // I2C写8位数据
sda_dir <= 1'b1;
end
7'd1 : scl <= 1'b1;
7'd3 : scl <= 1'b0;
7'd4 : sda_out <= data_wr_t[6];
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= data_wr_t[5];
7'd9 : scl <= 1'b1;
7'd11: scl <= 1'b0;
7'd12: sda_out <= data_wr_t[4];
7'd13: scl <= 1'b1;
7'd15: scl <= 1'b0;
7'd16: sda_out <= data_wr_t[3];
7'd17: scl <= 1'b1;
7'd19: scl <= 1'b0;
7'd20: sda_out <= data_wr_t[2];
7'd21: scl <= 1'b1;
7'd23: scl <= 1'b0;
7'd24: sda_out <= data_wr_t[1];
7'd25: scl <= 1'b1;
7'd27: scl <= 1'b0;
7'd28: sda_out <= data_wr_t[0];
7'd29: scl <= 1'b1;
7'd31: scl <= 1'b0;
7'd32: begin
sda_dir <= 1'b0; // 从机应答
sda_out <= 1'b1;
end
7'd33: scl <= 1'b1;
7'd34: st_done <= 1'b1;
7'd35: begin
scl <= 1'b0;
cnt <= 1'b0;
end
default : ;
endcase
end
st_addr_rd: begin // 写地址以进行读数据
case(cnt)
7'd0 : begin
sda_dir <= 1'b1;
sda_out <= 1'b1;
end
7'd1 : scl <= 1'b1;
7'd2 : sda_out <= 1'b0; // 重新开始
7'd3 : scl <= 1'b0;
7'd4 : sda_out <= SLAVE_ADDR[6]; // 传送器件地址
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= SLAVE_ADDR[5];
7'd9 : scl <= 1'b1;
7'd11: scl <= 1'b0;
7'd12: sda_out <= SLAVE_ADDR[4];
7'd13: scl <= 1'b1;
7'd15: scl <= 1'b0;
7'd16: sda_out <= SLAVE_ADDR[3];
7'd17: scl <= 1'b1;
7'd19: scl <= 1'b0;
7'd20: sda_out <= SLAVE_ADDR[2];
7'd21: scl <= 1'b1;
7'd23: scl <= 1'b0;
7'd24: sda_out <= SLAVE_ADDR[1];
7'd25: scl <= 1'b1;
7'd27: scl <= 1'b0;
7'd28: sda_out <= SLAVE_ADDR[0];
7'd29: scl <= 1'b1;
7'd31: scl <= 1'b0;
7'd32: sda_out <= 1'b1; // 1:读
7'd33: scl <= 1'b1;
7'd35: scl <= 1'b0;
7'd36: begin
sda_dir <= 1'b0; // 从机应答
sda_out <= 1'b1;
end
7'd37: scl <= 1'b1;
7'd38: st_done <= 1'b1;
7'd39: begin
scl <= 1'b0;
cnt <= 1'b0;
end
default : ;
endcase
end
st_data_rd: begin // 读取数据(8 bit)
case(cnt)
7'd0: sda_dir <= 1'b0;
7'd1: begin
data_r[7] <= sda_in;
scl <= 1'b1;
end
7'd3: scl <= 1'b0;
7'd5: begin
data_r[6] <= sda_in ;
scl <= 1'b1 ;
end
7'd7: scl <= 1'b0;
7'd9: begin
data_r[5] <= sda_in;
scl <= 1'b1 ;
end
7'd11: scl <= 1'b0;
7'd13: begin
data_r[4] <= sda_in;
scl <= 1'b1 ;
end
7'd15: scl <= 1'b0;
7'd17: begin
data_r[3] <= sda_in;
scl <= 1'b1 ;
end
7'd19: scl <= 1'b0;
7'd21: begin
data_r[2] <= sda_in;
scl <= 1'b1 ;
end
7'd23: scl <= 1'b0;
7'd25: begin
data_r[1] <= sda_in;
scl <= 1'b1 ;
end
7'd27: scl <= 1'b0;
7'd29: begin
data_r[0] <= sda_in;
scl <= 1'b1 ;
end
7'd31: scl <= 1'b0;
7'd32: begin
sda_dir <= 1'b1; // 非应答
sda_out <= 1'b1;
end
7'd33: scl <= 1'b1;
7'd34: st_done <= 1'b1;
7'd35: begin
scl <= 1'b0;
cnt <= 1'b0;
i2c_data_r <= data_r;
end
default : ;
endcase
end
st_stop: begin // 结束I2C操作
case(cnt)
7'd0: begin
sda_dir <= 1'b1; // 结束I2C
sda_out <= 1'b0;
end
7'd1 : scl <= 1'b1;
7'd3 : sda_out <= 1'b1;
7'd15: st_done <= 1'b1;
7'd16: begin
cnt <= 1'b0;
i2c_done <= 1'b1; // 向上层模块传递I2C结束信号
end
default : ;
endcase
end
endcase
end
end
endmodule
SDRAM模块:
总共9个文件:三个顶层,两个FIFO,,三个状态控制命令、一个参数
TOP模块
module sdram_top(
input ref_clk, //sdram 控制器参考时钟
input out_clk, //用于输出的相位偏移时钟
input rst_n, //系统复位
//用户写端口
input wr_clk, //写端口FIFO: 写时钟
input wr_en, //写端口FIFO: 写使能
input [15:0] wr_data, //写端口FIFO: 写数据
input [23:0] wr_min_addr, //写SDRAM的起始地址
input [23:0] wr_max_addr, //写SDRAM的结束地址
input [ 9:0] wr_len, //写SDRAM时的数据突发长度
input wr_load, //写端口复位: 复位写地址,清空写FIFO
//用户读端口
input rd_clk, //读端口FIFO: 读时钟
input rd_en, //读端口FIFO: 读使能
output [15:0] rd_data, //读端口FIFO: 读数据
input [23:0] rd_min_addr, //读SDRAM的起始地址
input [23:0] rd_max_addr, //读SDRAM的结束地址
input [ 9:0] rd_len, //从SDRAM中读数据时的突发长度
input rd_load, //读端口复位: 复位读地址,清空读FIFO
//用户控制端口
input sdram_read_valid, //SDRAM 读使能
input sdram_pingpang_en, //SDRAM 乒乓操作使能
output sdram_init_done, //SDRAM 初始化完成标志
//SDRAM 芯片接口
output sdram_clk, //SDRAM 芯片时钟
output sdram_cke, //SDRAM 时钟有效
output sdram_cs_n, //SDRAM 片选
output sdram_ras_n, //SDRAM 行有效
output sdram_cas_n, //SDRAM 列有效
output sdram_we_n, //SDRAM 写有效
output [ 1:0] sdram_ba, //SDRAM Bank地址
output [12:0] sdram_addr, //SDRAM 行/列地址
inout [15:0] sdram_data, //SDRAM 数据
output [ 1:0] sdram_dqm //SDRAM 数据掩码
);
//wire define
wire sdram_wr_req; //sdram 写请求
wire sdram_wr_ack; //sdram 写响应
wire [23:0] sdram_wr_addr; //sdram 写地址
wire [15:0] sdram_din; //写入sdram中的数据
wire sdram_rd_req; //sdram 读请求
wire sdram_rd_ack; //sdram 读响应
wire [23:0] sdram_rd_addr; //sdram 读地址
wire [15:0] sdram_dout; //从sdram中读出的数据
//*****************************************************
//** main code
//*****************************************************
assign sdram_clk = out_clk; //将相位偏移时钟输出给sdram芯片
assign sdram_dqm = 2'b00; //读写过程中均不屏蔽数据线
//SDRAM 读写端口FIFO控制模块
sdram_fifo_ctrl u_sdram_fifo_ctrl(
.clk_ref (ref_clk), //SDRAM控制器时钟
.rst_n (rst_n), //系统复位
//用户写端口
.clk_write (wr_clk), //写端口FIFO: 写时钟
.wrf_wrreq (wr_en), //写端口FIFO: 写请求
.wrf_din (wr_data), //写端口FIFO: 写数据
.wr_min_addr (wr_min_addr), //写SDRAM的起始地址
.wr_max_addr (wr_max_addr), //写SDRAM的结束地址
.wr_length (wr_len), //写SDRAM时的数据突发长度
.wr_load (wr_load), //写端口复位: 复位写地址,清空写FIFO
//用户读端口
.clk_read (rd_clk), //读端口FIFO: 读时钟
.rdf_rdreq (rd_en), //读端口FIFO: 读请求
.rdf_dout (rd_data), //读端口FIFO: 读数据
.rd_min_addr (rd_min_addr), //读SDRAM的起始地址
.rd_max_addr (rd_max_addr), //读SDRAM的结束地址
.rd_length (rd_len), //从SDRAM中读数据时的突发长度
.rd_load (rd_load), //读端口复位: 复位读地址,清空读FIFO
//用户控制端口
.sdram_read_valid (sdram_read_valid), //sdram 读使能
.sdram_init_done (sdram_init_done), //sdram 初始化完成标志
.sdram_pingpang_en (sdram_pingpang_en),//sdram 乒乓操作使能
//SDRAM 控制器写端口
.sdram_wr_req (sdram_wr_req), //sdram 写请求
.sdram_wr_ack (sdram_wr_ack), //sdram 写响应
.sdram_wr_addr (sdram_wr_addr), //sdram 写地址
.sdram_din (sdram_din), //写入sdram中的数据
//SDRAM 控制器读端口
.sdram_rd_req (sdram_rd_req), //sdram 读请求
.sdram_rd_ack (sdram_rd_ack), //sdram 读响应
.sdram_rd_addr (sdram_rd_addr), //sdram 读地址
.sdram_dout (sdram_dout) //从sdram中读出的数据
);
//SDRAM控制器
sdram_controller u_sdram_controller(
.clk (ref_clk), //sdram 控制器时钟
.rst_n (rst_n), //系统复位
//SDRAM 控制器写端口
.sdram_wr_req (sdram_wr_req), //sdram 写请求
.sdram_wr_ack (sdram_wr_ack), //sdram 写响应
.sdram_wr_addr (sdram_wr_addr), //sdram 写地址
.sdram_wr_burst (wr_len), //写sdram时数据突发长度
.sdram_din (sdram_din), //写入sdram中的数据
//SDRAM 控制器读端口
.sdram_rd_req (sdram_rd_req), //sdram 读请求
.sdram_rd_ack (sdram_rd_ack), //sdram 读响应
.sdram_rd_addr (sdram_rd_addr), //sdram 读地址
.sdram_rd_burst (rd_len), //读sdram时数据突发长度
.sdram_dout (sdram_dout), //从sdram中读出的数据
.sdram_init_done (sdram_init_done), //sdram 初始化完成标志
//SDRAM 芯片接口
.sdram_cke (sdram_cke), //SDRAM 时钟有效
.sdram_cs_n (sdram_cs_n), //SDRAM 片选
.sdram_ras_n (sdram_ras_n), //SDRAM 行有效
.sdram_cas_n (sdram_cas_n), //SDRAM 列有效
.sdram_we_n (sdram_we_n), //SDRAM 写有效
.sdram_ba (sdram_ba), //SDRAM Bank地址
.sdram_addr (sdram_addr), //SDRAM 行/列地址
.sdram_data (sdram_data) //SDRAM 数据
);
endmodule
FIFO控制模块:
module sdram_fifo_ctrl(
input clk_ref, //SDRAM控制器时钟
input rst_n, //系统复位
//用户写端口
input clk_write, //写端口FIFO: 写时钟
input wrf_wrreq, //写端口FIFO: 写请求
input [15:0] wrf_din, //写端口FIFO: 写数据
input [23:0] wr_min_addr, //写SDRAM的起始地址
input [23:0] wr_max_addr, //写SDRAM的结束地址
input [ 9:0] wr_length, //写SDRAM时的数据突发长度
input wr_load, //写端口复位: 复位写地址,清空写FIFO
//用户读端口
input clk_read, //读端口FIFO: 读时钟
input rdf_rdreq, //读端口FIFO: 读请求
output [15:0] rdf_dout, //读端口FIFO: 读数据
input [23:0] rd_min_addr, //读SDRAM的起始地址
input [23:0] rd_max_addr, //读SDRAM的结束地址
input [ 9:0] rd_length, //从SDRAM中读数据时的突发长度
input rd_load, //读端口复位: 复位读地址,清空读FIFO
//用户控制端口
input sdram_read_valid, //SDRAM 读使能
input sdram_init_done, //SDRAM 初始化完成标志
input sdram_pingpang_en, //SDRAM 乒乓操作使能
//SDRAM 控制器写端口
output reg sdram_wr_req, //sdram 写请求
input sdram_wr_ack, //sdram 写响应
output reg [23:0] sdram_wr_addr, //sdram 写地址
output [15:0] sdram_din, //写入SDRAM中的数据
//SDRAM 控制器读端口
output reg sdram_rd_req, //sdram 读请求
input sdram_rd_ack, //sdram 读响应
output reg [23:0] sdram_rd_addr, //sdram 读地址
input [15:0] sdram_dout //从SDRAM中读出的数据
);
//reg define
reg wr_ack_r1; //sdram写响应寄存器
reg wr_ack_r2;
reg rd_ack_r1; //sdram读响应寄存器
reg rd_ack_r2;
reg wr_load_r1; //写端口复位寄存器
reg wr_load_r2;
reg rd_load_r1; //读端口复位寄存器
reg rd_load_r2;
reg read_valid_r1; //sdram读使能寄存器
reg read_valid_r2;
reg sw_bank_en; //切换BANK使能信号
reg rw_bank_flag; //读写bank的标志
//wire define
wire write_done_flag; //sdram_wr_ack 下降沿标志位
wire read_done_flag; //sdram_rd_ack 下降沿标志位
wire wr_load_flag; //wr_load 上升沿标志位
wire rd_load_flag; //rd_load 上升沿标志位
wire [9:0] wrf_use; //写端口FIFO中的数据量
wire [9:0] rdf_use; //读端口FIFO中的数据量
//*****************************************************
//** main code
//*****************************************************
//检测下降沿
assign write_done_flag = wr_ack_r2 & ~wr_ack_r1;
assign read_done_flag = rd_ack_r2 & ~rd_ack_r1;
//检测上升沿
assign wr_load_flag = ~wr_load_r2 & wr_load_r1;
assign rd_load_flag = ~rd_load_r2 & rd_load_r1;
//寄存sdram写响应信号,用于捕获sdram_wr_ack下降沿
always @(posedge clk_ref or negedge rst_n) begin
if(!rst_n) begin
wr_ack_r1 <= 1'b0;
wr_ack_r2 <= 1'b0;
end
else begin
wr_ack_r1 <= sdram_wr_ack;
wr_ack_r2 <= wr_ack_r1;
end
end
//寄存sdram读响应信号,用于捕获sdram_rd_ack下降沿
always @(posedge clk_ref or negedge rst_n) begin
if(!rst_n) begin
rd_ack_r1 <= 1'b0;
rd_ack_r2 <= 1'b0;
end
else begin
rd_ack_r1 <= sdram_rd_ack;
rd_ack_r2 <= rd_ack_r1;
end
end
//同步写端口复位信号,用于捕获wr_load上升沿
always @(posedge clk_ref or negedge rst_n) begin
if(!rst_n) begin
wr_load_r1 <= 1'b0;
wr_load_r2 <= 1'b0;
end
else begin
wr_load_r1 <= wr_load;
wr_load_r2 <= wr_load_r1;
end
end
//同步读端口复位信号,同时用于捕获rd_load上升沿
always @(posedge clk_ref or negedge rst_n) begin
if(!rst_n) begin
rd_load_r1 <= 1'b0;
rd_load_r2 <= 1'b0;
end
else begin
rd_load_r1 <= rd_load;
rd_load_r2 <= rd_load_r1;
end
end
//同步sdram读使能信号
always @(posedge clk_ref or negedge rst_n) begin
if(!rst_n) begin
read_valid_r1 <= 1'b0;
read_valid_r2 <= 1'b0;
end
else begin
read_valid_r1 <= sdram_read_valid;
read_valid_r2 <= read_valid_r1;
end
end
//sdram写地址产生模块
always @(posedge clk_ref or negedge rst_n) begin
if (!rst_n) begin
sdram_wr_addr <= 24'd0;
sw_bank_en <= 1'b0;
rw_bank_flag <= 1'b0;
end
else if(wr_load_flag) begin //检测到写端口复位信号时,写地址复位
sdram_wr_addr <= wr_min_addr;
sw_bank_en <= 1'b0;
rw_bank_flag <= 1'b0;
end
else if(write_done_flag) begin //若突发写SDRAM结束,更改写地址
//若未到达写SDRAM的结束地址,则写地址累加
if(sdram_pingpang_en) begin //SDRAM 读写乒乓使能
if(sdram_wr_addr[22:0] < wr_max_addr - wr_length)
sdram_wr_addr <= sdram_wr_addr + wr_length;
else begin //切换BANK
rw_bank_flag <= ~rw_bank_flag;
sw_bank_en <= 1'b1; //拉高切换BANK使能信号
end
end
//若突发写SDRAM结束,更改写地址
else if(sdram_wr_addr < wr_max_addr - wr_length)
sdram_wr_addr <= sdram_wr_addr + wr_length;
else //到达写SDRAM的结束地址,回到写起始地址
sdram_wr_addr <= wr_min_addr;
end
else if(sw_bank_en) begin //到达写SDRAM的结束地址,回到写起始地址
sw_bank_en <= 1'b0;
if(rw_bank_flag == 1'b0) //切换BANK
sdram_wr_addr <= {1'b0,wr_min_addr[22:0]};
else
sdram_wr_addr <= {1'b1,wr_min_addr[22:0]};
end
end
//sdram读地址产生模块
always @(posedge clk_ref or negedge rst_n) begin
if(!rst_n) begin
sdram_rd_addr <= 24'd0;
end
else if(rd_load_flag) //检测到读端口复位信号时,读地址复位
sdram_rd_addr <= rd_min_addr;
else if(read_done_flag) begin //突发读SDRAM结束,更改读地址
//若未到达读SDRAM的结束地址,则读地址累加
if(sdram_pingpang_en) begin //SDRAM 读写乒乓使能
if(sdram_rd_addr[22:0] < rd_max_addr - rd_length)
sdram_rd_addr <= sdram_rd_addr + rd_length;
else begin //到达读SDRAM的结束地址,回到读起始地址
//读取没有在写数据的bank地址
if(rw_bank_flag == 1'b0) //根据rw_bank_flag的值切换读BANK地址
sdram_rd_addr <= {1'b1,rd_min_addr[22:0]};
else
sdram_rd_addr <= {1'b0,rd_min_addr[22:0]};
end
end
//若突发写SDRAM结束,更改写地址
else if(sdram_rd_addr < rd_max_addr - rd_length)
sdram_rd_addr <= sdram_rd_addr + rd_length;
else //到达写SDRAM的结束地址,回到写起始地址
sdram_rd_addr <= rd_min_addr;
end
end
//sdram 读写请求信号产生模块
always@(posedge clk_ref or negedge rst_n) begin
if(!rst_n) begin
sdram_wr_req <= 0;
sdram_rd_req <= 0;
end
else if(sdram_init_done) begin //SDRAM初始化完成后才能响应读写请求
//优先执行写操作,防止写入SDRAM中的数据丢失
if(wrf_use >= wr_length) begin //若写端口FIFO中的数据量达到了写突发长度
sdram_wr_req <= 1; //发出写sdarm请求
sdram_rd_req <= 0;
end
else if((rdf_use < rd_length) //若读端口FIFO中的数据量小于读突发长度,
&& read_valid_r2) begin //同时sdram读使能信号为高
sdram_wr_req <= 0;
sdram_rd_req <= 1; //发出读sdarm请求
end
else begin
sdram_wr_req <= 0;
sdram_rd_req <= 0;
end
end
else begin
sdram_wr_req <= 0;
sdram_rd_req <= 0;
end
end
//例化写端口FIFO
wrfifo u_wrfifo(
//用户接口
.wrclk (clk_write), //写时钟
.wrreq (wrf_wrreq), //写请求
.data (wrf_din), //写数据
//sdram接口
.rdclk (clk_ref), //读时钟
.rdreq (sdram_wr_ack), //读请求
.q (sdram_din), //读数据
.rdusedw (wrf_use), //FIFO中的数据量
.aclr (~rst_n | wr_load_flag) //异步清零信号
);
//例化读端口FIFO
rdfifo u_rdfifo(
//sdram接口
.wrclk (clk_ref), //写时钟
.wrreq (sdram_rd_ack), //写请求
.data (sdram_dout), //写数据
//用户接口
.rdclk (clk_read), //读时钟
.rdreq (rdf_rdreq), //读请求
.q (rdf_dout), //读数据
.wrusedw (rdf_use), //FIFO中的数据量
.aclr (~rst_n | rd_load_flag) //异步清零信号
);
endmodule
建两个FIFO
16位,1024字节
SDRAM控制模块TOP
module sdram_controller(
input clk, //SDRAM控制器时钟,100MHz
input rst_n, //系统复位信号,低电平有效
//SDRAM 控制器写端口
input sdram_wr_req, //写SDRAM请求信号
output sdram_wr_ack, //写SDRAM响应信号
input [23:0] sdram_wr_addr, //SDRAM写操作的地址
input [ 9:0] sdram_wr_burst, //写sdram时数据突发长度
input [15:0] sdram_din, //写入SDRAM的数据
//SDRAM 控制器读端口
input sdram_rd_req, //读SDRAM请求信号
output sdram_rd_ack, //读SDRAM响应信号
input [23:0] sdram_rd_addr, //SDRAM写操作的地址
input [ 9:0] sdram_rd_burst, //读sdram时数据突发长度
output [15:0] sdram_dout, //从SDRAM读出的数据
output sdram_init_done, //SDRAM 初始化完成标志
// FPGA与SDRAM硬件接口
output sdram_cke, // SDRAM 时钟有效信号
output sdram_cs_n, // SDRAM 片选信号
output sdram_ras_n, // SDRAM 行地址选通脉冲
output sdram_cas_n, // SDRAM 列地址选通脉冲
output sdram_we_n, // SDRAM 写允许位
output [ 1:0] sdram_ba, // SDRAM L-Bank地址线
output [12:0] sdram_addr, // SDRAM 地址总线
inout [15:0] sdram_data // SDRAM 数据总线
);
//wire define
wire [4:0] init_state; // SDRAM初始化状态
wire [3:0] work_state; // SDRAM工作状态
wire [9:0] cnt_clk; // 延时计数器
wire sdram_rd_wr; // SDRAM读/写控制信号,低电平为写,高电平为读
//*****************************************************
//** main code
//*****************************************************
// SDRAM 状态控制模块
sdram_ctrl u_sdram_ctrl(
.clk (clk),
.rst_n (rst_n),
.sdram_wr_req (sdram_wr_req),
.sdram_rd_req (sdram_rd_req),
.sdram_wr_ack (sdram_wr_ack),
.sdram_rd_ack (sdram_rd_ack),
.sdram_wr_burst (sdram_wr_burst),
.sdram_rd_burst (sdram_rd_burst),
.sdram_init_done (sdram_init_done),
.init_state (init_state),
.work_state (work_state),
.cnt_clk (cnt_clk),
.sdram_rd_wr (sdram_rd_wr)
);
// SDRAM 命令控制模块
sdram_cmd u_sdram_cmd(
.clk (clk),
.rst_n (rst_n),
.sys_wraddr (sdram_wr_addr),
.sys_rdaddr (sdram_rd_addr),
.sdram_wr_burst (sdram_wr_burst),
.sdram_rd_burst (sdram_rd_burst),
.init_state (init_state),
.work_state (work_state),
.cnt_clk (cnt_clk),
.sdram_rd_wr (sdram_rd_wr),
.sdram_cke (sdram_cke),
.sdram_cs_n (sdram_cs_n),
.sdram_ras_n (sdram_ras_n),
.sdram_cas_n (sdram_cas_n),
.sdram_we_n (sdram_we_n),
.sdram_ba (sdram_ba),
.sdram_addr (sdram_addr)
);
// SDRAM 数据读写模块
sdram_data u_sdram_data(
.clk (clk),
.rst_n (rst_n),
.sdram_data_in (sdram_din),
.sdram_data_out (sdram_dout),
.work_state (work_state),
.cnt_clk (cnt_clk),
.sdram_data (sdram_data)
);
endmodule
SDRAM命令控制模块
module sdram_cmd(
input clk, //系统时钟
input rst_n, //低电平复位信号
input [23:0] sys_wraddr, //写SDRAM时地址
input [23:0] sys_rdaddr, //读SDRAM时地址
input [ 9:0] sdram_wr_burst, //突发写SDRAM字节数
input [ 9:0] sdram_rd_burst, //突发读SDRAM字节数
input [ 4:0] init_state, //SDRAM初始化状态
input [ 3:0] work_state, //SDRAM工作状态
input [ 9:0] cnt_clk, //延时计数器
input sdram_rd_wr, //SDRAM读/写控制信号,低电平为写
output sdram_cke, //SDRAM时钟有效信号
output sdram_cs_n, //SDRAM片选信号
output sdram_ras_n, //SDRAM行地址选通脉冲
output sdram_cas_n, //SDRAM列地址选通脉冲
output sdram_we_n, //SDRAM写允许位
output reg [ 1:0] sdram_ba, //SDRAM的L-Bank地址线
output reg [12:0] sdram_addr //SDRAM地址总线
);
`include "sdram_para.v" //包含SDRAM参数定义模块
//reg define
reg [ 4:0] sdram_cmd_r; //SDRAM操作指令
//wire define
wire [23:0] sys_addr; //SDRAM读写地址
//*****************************************************
//** main code
//*****************************************************
//SDRAM 控制信号线赋值
assign {sdram_cke,sdram_cs_n,sdram_ras_n,sdram_cas_n,sdram_we_n} = sdram_cmd_r;
//SDRAM 读/写地址总线控制
assign sys_addr = sdram_rd_wr ? sys_rdaddr : sys_wraddr;
//SDRAM 操作指令控制
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
sdram_cmd_r <= `CMD_INIT;
sdram_ba <= 2'b11;
sdram_addr <= 13'h1fff;
end
else
case(init_state)
//初始化过程中,以下状态不执行任何指令
`I_NOP,`I_TRP,`I_TRF,`I_TRSC: begin
sdram_cmd_r <= `CMD_NOP;
sdram_ba <= 2'b11;
sdram_addr <= 13'h1fff;
end
`I_PRE: begin //预充电指令
sdram_cmd_r <= `CMD_PRGE;
sdram_ba <= 2'b11;
sdram_addr <= 13'h1fff;
end
`I_AR: begin
//自动刷新指令
sdram_cmd_r <= `CMD_A_REF;
sdram_ba <= 2'b11;
sdram_addr <= 13'h1fff;
end
`I_MRS: begin //模式寄存器设置指令
sdram_cmd_r <= `CMD_LMR;
sdram_ba <= 2'b00;
sdram_addr <= { //利用地址线设置模式寄存器,可根据实际需要进行修改
3'b000, //预留
1'b0, //读写方式 A9=0,突发读&突发写
2'b00, //默认,{A8,A7}=00
3'b011, //CAS潜伏期设置,这里设置为3,{A6,A5,A4}=011
1'b0, //突发传输方式,这里设置为顺序,A3=0
3'b111 //突发长度,这里设置为页突发,{A2,A1,A0}=011
};
end
`I_DONE: //SDRAM初始化完成
case(work_state) //以下工作状态不执行任何指令
`W_IDLE,`W_TRCD,`W_CL,`W_TWR,`W_TRP,`W_TRFC: begin
sdram_cmd_r <= `CMD_NOP;
sdram_ba <= 2'b11;
sdram_addr <= 13'h1fff;
end
`W_ACTIVE: begin//行有效指令
sdram_cmd_r <= `CMD_ACTIVE;
sdram_ba <= sys_addr[23:22];
sdram_addr <= sys_addr[21:9];
end
`W_READ: begin //读操作指令
sdram_cmd_r <= `CMD_READ;
sdram_ba <= sys_addr[23:22];
sdram_addr <= {4'b0000,sys_addr[8:0]};
end
`W_RD: begin //突发传输终止指令
if(`end_rdburst)
sdram_cmd_r <= `CMD_B_STOP;
else begin
sdram_cmd_r <= `CMD_NOP;
sdram_ba <= 2'b11;
sdram_addr <= 13'h1fff;
end
end
`W_WRITE: begin //写操作指令
sdram_cmd_r <= `CMD_WRITE;
sdram_ba <= sys_addr[23:22];
sdram_addr <= {4'b0000,sys_addr[8:0]};
end
`W_WD: begin //突发传输终止指令
if(`end_wrburst)
sdram_cmd_r <= `CMD_B_STOP;
else begin
sdram_cmd_r <= `CMD_NOP;
sdram_ba <= 2'b11;
sdram_addr <= 13'h1fff;
end
end
`W_PRE:begin //预充电指令
sdram_cmd_r <= `CMD_PRGE;
sdram_ba <= sys_addr[23:22];
sdram_addr <= 13'h0400;
end
`W_AR: begin //自动刷新指令
sdram_cmd_r <= `CMD_A_REF;
sdram_ba <= 2'b11;
sdram_addr <= 13'h1fff;
end
default: begin
sdram_cmd_r <= `CMD_NOP;
sdram_ba <= 2'b11;
sdram_addr <= 13'h1fff;
end
endcase
default: begin
sdram_cmd_r <= `CMD_NOP;
sdram_ba <= 2'b11;
sdram_addr <= 13'h1fff;
end
endcase
end
endmodule
SDRAM 状态控制模块
module sdram_ctrl(
input clk, //系统时钟
input rst_n, //复位信号,低电平有效
input sdram_wr_req, //写SDRAM请求信号
input sdram_rd_req, //读SDRAM请求信号
output sdram_wr_ack, //写SDRAM响应信号
output sdram_rd_ack, //读SDRAM响应信号
input [9:0] sdram_wr_burst, //突发写SDRAM字节数(1-512个)
input [9:0] sdram_rd_burst, //突发读SDRAM字节数(1-256个)
output sdram_init_done, //SDRAM系统初始化完毕信号
output reg [4:0] init_state, //SDRAM初始化状态
output reg [3:0] work_state, //SDRAM工作状态
output reg [9:0] cnt_clk, //时钟计数器
output reg sdram_rd_wr //SDRAM读/写控制信号,低电平为写,高电平为读
);
`include "sdram_para.v" //包含SDRAM参数定义模块
//parameter define
parameter TRP_CLK = 10'd4; //预充电有效周期
parameter TRC_CLK = 10'd6; //自动刷新周期
parameter TRSC_CLK = 10'd6; //模式寄存器设置时钟周期
parameter TRCD_CLK = 10'd2; //行选通周期
parameter TCL_CLK = 10'd3; //列潜伏期
parameter TWR_CLK = 10'd2; //写入校正
//reg define
reg [14:0] cnt_200us; //SDRAM 上电稳定期200us计数器
reg [10:0] cnt_refresh; //刷新计数寄存器
reg sdram_ref_req; //SDRAM 自动刷新请求信号
reg cnt_rst_n; //延时计数器复位信号,低有效
reg [ 3:0] init_ar_cnt; //初始化过程自动刷新计数器
//wire define
wire done_200us; //上电后200us输入稳定期结束标志位
wire sdram_ref_ack; //SDRAM自动刷新请求应答信号
//*****************************************************
//** main code
//*****************************************************
//SDRAM上电后200us稳定期结束后,将标志信号拉高
assign done_200us = (cnt_200us == 15'd20_000);
//SDRAM初始化完成标志
assign sdram_init_done = (init_state == `I_DONE);
//SDRAM 自动刷新应答信号
assign sdram_ref_ack = (work_state == `W_AR);
//写SDRAM响应信号
assign sdram_wr_ack = ((work_state == `W_TRCD) & ~sdram_rd_wr) |
( work_state == `W_WRITE)|
((work_state == `W_WD) & (cnt_clk < sdram_wr_burst - 2'd2));
//读SDRAM响应信号
assign sdram_rd_ack = (work_state == `W_RD) &
(cnt_clk >= 10'd1) & (cnt_clk < sdram_rd_burst + 2'd1);
//上电后计时200us,等待SDRAM状态稳定
always @ (posedge clk or negedge rst_n) begin
if(!rst_n)
cnt_200us <= 15'd0;
else if(cnt_200us < 15'd20_000)
cnt_200us <= cnt_200us + 1'b1;
else
cnt_200us <= cnt_200us;
end
//刷新计数器循环计数7812ns (60ms内完成全部8192行刷新操作)
always @ (posedge clk or negedge rst_n)
if(!rst_n)
cnt_refresh <= 11'd0;
else if(cnt_refresh < 11'd781) // 64ms/8192 =7812ns
cnt_refresh <= cnt_refresh + 1'b1;
else
cnt_refresh <= 11'd0;
//SDRAM 刷新请求
always @ (posedge clk or negedge rst_n)
if(!rst_n)
sdram_ref_req <= 1'b0;
else if(cnt_refresh == 11'd780)
sdram_ref_req <= 1'b1; //刷新计数器计时达7812ns时产生刷新请求
else if(sdram_ref_ack)
sdram_ref_req <= 1'b0; //收到刷新请求响应信号后取消刷新请求
//延时计数器对时钟计数
always @ (posedge clk or negedge rst_n)
if(!rst_n)
cnt_clk <= 10'd0;
else if(!cnt_rst_n) //在cnt_rst_n为低电平时延时计数器清零
cnt_clk <= 10'd0;
else
cnt_clk <= cnt_clk + 1'b1;
//初始化过程中对自动刷新操作计数
always @ (posedge clk or negedge rst_n)
if(!rst_n)
init_ar_cnt <= 4'd0;
else if(init_state == `I_NOP)
init_ar_cnt <= 4'd0;
else if(init_state == `I_AR)
init_ar_cnt <= init_ar_cnt + 1'b1;
else
init_ar_cnt <= init_ar_cnt;
//SDRAM的初始化状态机
always @ (posedge clk or negedge rst_n) begin
if(!rst_n)
init_state <= `I_NOP;
else
case (init_state)
//上电复位后200us结束则进入下一状态
`I_NOP: init_state <= done_200us ? `I_PRE : `I_NOP;
//预充电状态
`I_PRE: init_state <= `I_TRP;
//预充电等待,TRP_CLK个时钟周期
`I_TRP: init_state <= (`end_trp) ? `I_AR : `I_TRP;
//自动刷新
`I_AR : init_state <= `I_TRF;
//等待自动刷新结束,TRC_CLK个时钟周期
`I_TRF: init_state <= (`end_trfc) ?
//连续8次自动刷新操作
((init_ar_cnt == 4'd8) ? `I_MRS : `I_AR) : `I_TRF;
//模式寄存器设置
`I_MRS: init_state <= `I_TRSC;
//等待模式寄存器设置完成,TRSC_CLK个时钟周期
`I_TRSC: init_state <= (`end_trsc) ? `I_DONE : `I_TRSC;
//SDRAM的初始化设置完成标志
`I_DONE: init_state <= `I_DONE;
default: init_state <= `I_NOP;
endcase
end
//SDRAM的工作状态机,工作包括读、写以及自动刷新操作
always @ (posedge clk or negedge rst_n) begin
if(!rst_n)
work_state <= `W_IDLE; //空闲状态
else
case(work_state)
//定时自动刷新请求,跳转到自动刷新状态
`W_IDLE: if(sdram_ref_req & sdram_init_done) begin
work_state <= `W_AR;
sdram_rd_wr <= 1'b1;
end
//写SDRAM请求,跳转到行有效状态
else if(sdram_wr_req & sdram_init_done) begin
work_state <= `W_ACTIVE;
sdram_rd_wr <= 1'b0;
end
//读SDRAM请求,跳转到行有效状态
else if(sdram_rd_req && sdram_init_done) begin
work_state <= `W_ACTIVE;
sdram_rd_wr <= 1'b1;
end
//无操作请求,保持空闲状态
else begin
work_state <= `W_IDLE;
sdram_rd_wr <= 1'b1;
end
`W_ACTIVE: //行有效,跳转到行有效等待状态
work_state <= `W_TRCD;
`W_TRCD: if(`end_trcd) //行有效等待结束,判断当前是读还是写
if(sdram_rd_wr)//读:进入读操作状态
work_state <= `W_READ;
else //写:进入写操作状态
work_state <= `W_WRITE;
else
work_state <= `W_TRCD;
`W_READ: //读操作,跳转到潜伏期
work_state <= `W_CL;
`W_CL: //潜伏期:等待潜伏期结束,跳转到读数据状态
work_state <= (`end_tcl) ? `W_RD:`W_CL;
`W_RD: //读数据:等待读数据结束,跳转到预充电状态
work_state <= (`end_tread) ? `W_PRE:`W_RD;
`W_WRITE: //写操作:跳转到写数据状态
work_state <= `W_WD;
`W_WD: //写数据:等待写数据结束,跳转到写回周期状态
work_state <= (`end_twrite) ? `W_TWR:`W_WD;
`W_TWR: //写回周期:写回周期结束,跳转到预充电状态
work_state <= (`end_twr) ? `W_PRE:`W_TWR;
`W_PRE: //预充电:跳转到预充电等待状态
work_state <= `W_TRP;
`W_TRP: //预充电等待:预充电等待结束,进入空闲状态
work_state <= (`end_trp) ? `W_IDLE:`W_TRP;
`W_AR: //自动刷新操作,跳转到自动刷新等待
work_state <= `W_TRFC;
`W_TRFC: //自动刷新等待:自动刷新等待结束,进入空闲状态
work_state <= (`end_trfc) ? `W_IDLE:`W_TRFC;
default: work_state <= `W_IDLE;
endcase
end
//计数器控制逻辑
always @ (*) begin
case (init_state)
`I_NOP: cnt_rst_n <= 1'b0; //延时计数器清零(cnt_rst_n低电平复位)
`I_PRE: cnt_rst_n <= 1'b1; //预充电:延时计数器启动(cnt_rst_n高电平启动)
//等待预充电延时计数结束后,清零计数器
`I_TRP: cnt_rst_n <= (`end_trp) ? 1'b0 : 1'b1;
//自动刷新:延时计数器启动
`I_AR:
cnt_rst_n <= 1'b1;
//等待自动刷新延时计数结束后,清零计数器
`I_TRF:
cnt_rst_n <= (`end_trfc) ? 1'b0 : 1'b1;
`I_MRS: cnt_rst_n <= 1'b1; //模式寄存器设置:延时计数器启动
//等待模式寄存器设置延时计数结束后,清零计数器
`I_TRSC: cnt_rst_n <= (`end_trsc) ? 1'b0:1'b1;
`I_DONE: begin //初始化完成后,判断工作状态
case (work_state)
`W_IDLE: cnt_rst_n <= 1'b0;
//行有效:延时计数器启动
`W_ACTIVE: cnt_rst_n <= 1'b1;
//行有效延时计数结束后,清零计数器
`W_TRCD: cnt_rst_n <= (`end_trcd) ? 1'b0 : 1'b1;
//潜伏期延时计数结束后,清零计数器
`W_CL: cnt_rst_n <= (`end_tcl) ? 1'b0 : 1'b1;
//读数据延时计数结束后,清零计数器
`W_RD: cnt_rst_n <= (`end_tread) ? 1'b0 : 1'b1;
//写数据延时计数结束后,清零计数器
`W_WD: cnt_rst_n <= (`end_twrite) ? 1'b0 : 1'b1;
//写回周期延时计数结束后,清零计数器
`W_TWR: cnt_rst_n <= (`end_twr) ? 1'b0 : 1'b1;
//预充电等待延时计数结束后,清零计数器
`W_TRP: cnt_rst_n <= (`end_trp) ? 1'b0 : 1'b1;
//自动刷新等待延时计数结束后,清零计数器
`W_TRFC: cnt_rst_n <= (`end_trfc) ? 1'b0 : 1'b1;
default: cnt_rst_n <= 1'b0;
endcase
end
default: cnt_rst_n <= 1'b0;
endcase
end
endmodule
SDRAM 数据读写模块
module sdram_data(
input clk, //系统时钟
input rst_n, //低电平复位信号
input [15:0] sdram_data_in, //写入SDRAM中的数据
output [15:0] sdram_data_out, //从SDRAM中读取的数据
input [ 3:0] work_state, //SDRAM工作状态寄存器
input [ 9:0] cnt_clk, //时钟计数
inout [15:0] sdram_data //SDRAM数据总线
);
`include "sdram_para.v" //包含SDRAM参数定义模块
//reg define
reg sdram_out_en; //SDRAM数据总线输出使能
reg [15:0] sdram_din_r; //寄存写入SDRAM中的数据
reg [15:0] sdram_dout_r; //寄存从SDRAM中读取的数据
//*****************************************************
//** main code
//*****************************************************
//SDRAM 双向数据线作为输入时保持高阻态
assign sdram_data = sdram_out_en ? sdram_din_r : 16'hzzzz;
//输出SDRAM中读取的数据
assign sdram_data_out = sdram_dout_r;
//SDRAM 数据总线输出使能
always @ (posedge clk or negedge rst_n) begin
if(!rst_n)
sdram_out_en <= 1'b0;
else if((work_state == `W_WRITE) | (work_state == `W_WD))
sdram_out_en <= 1'b1; //向SDRAM中写数据时,输出使能拉高
else
sdram_out_en <= 1'b0;
end
//将待写入数据送到SDRAM数据总线上
always @ (posedge clk or negedge rst_n) begin
if(!rst_n)
sdram_din_r <= 16'd0;
else if((work_state == `W_WRITE) | (work_state == `W_WD))
sdram_din_r <= sdram_data_in; //寄存写入SDRAM中的数据
end
//读数据时,寄存SDRAM数据线上的数据
always @ (posedge clk or negedge rst_n) begin
if(!rst_n)
sdram_dout_r <= 16'd0;
else if(work_state == `W_RD)
sdram_dout_r <= sdram_data; //寄存从SDRAM中读取的数据
end
endmodule
SDRAM的参数文件
// SDRAM 初始化过程各个状态
`define I_NOP 5'd0 //等待上电200us稳定期结束
`define I_PRE 5'd1 //预充电状态
`define I_TRP 5'd2 //等待预充电完成 tRP
`define I_AR 5'd3 //自动刷新
`define I_TRF 5'd4 //等待自动刷新结束 tRC
`define I_MRS 5'd5 //模式寄存器设置
`define I_TRSC 5'd6 //等待模式寄存器设置完成 tRSC
`define I_DONE 5'd7 //初始化完成
// SDRAM 工作过程各个状态
`define W_IDLE 4'd0 //空闲
`define W_ACTIVE 4'd1 //行有效
`define W_TRCD 4'd2 //行有效等待
`define W_READ 4'd3 //读操作
`define W_CL 4'd4 //潜伏期
`define W_RD 4'd5 //读数据
`define W_WRITE 4'd6 //写操作
`define W_WD 4'd7 //写数据
`define W_TWR 4'd8 //写回
`define W_PRE 4'd9 //预充电
`define W_TRP 4'd10 //预充电等待
`define W_AR 4'd11 //自动刷新
`define W_TRFC 4'd12 //自动刷新等待
//延时参数
`define end_trp cnt_clk == TRP_CLK //预充电有效周期结束
`define end_trfc cnt_clk == TRC_CLK //自动刷新周期结束
`define end_trsc cnt_clk == TRSC_CLK //模式寄存器设置时钟周期结束
`define end_trcd cnt_clk == TRCD_CLK-1 //行选通周期结束
`define end_tcl cnt_clk == TCL_CLK-1 //潜伏期结束
`define end_rdburst cnt_clk == sdram_rd_burst-4 //读突发终止
`define end_tread cnt_clk == sdram_rd_burst+2 //突发读结束
`define end_wrburst cnt_clk == sdram_wr_burst-1 //写突发终止
`define end_twrite cnt_clk == sdram_wr_burst-1 //突发写结束
`define end_twr cnt_clk == TWR_CLK //写回周期结束
//SDRAM控制信号命令
`define CMD_INIT 5'b01111 // INITIATE
`define CMD_NOP 5'b10111 // NOP COMMAND
`define CMD_ACTIVE 5'b10011 // ACTIVE COMMAND
`define CMD_READ 5'b10101 // READ COMMADN
`define CMD_WRITE 5'b10100 // WRITE COMMAND
`define CMD_B_STOP 5'b10110 // BURST STOP
`define CMD_PRGE 5'b10010 // PRECHARGE
`define CMD_A_REF 5'b10001 // AOTO REFRESH
`define CMD_LMR 5'b10000 // LODE MODE REGISTER
最后
以上就是贪玩雪碧最近收集整理的关于【FPGA】一些基本模块代码camera_top:VGA驱动模块:OV5640摄像头SDRAM模块:的全部内容,更多相关【FPGA】一些基本模块代码camera_top内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复