本文章为笔者学习国产高云FPGA的学习记录,如有不妥请指正。
本实验任务是使用开发板及 OV5640 摄像头实现1024*768@60Hz 图像的 HDMI 显示图像采集,并通过 HDMI 接口驱动 HDMI 显示器, 并实时显示出图像。
系统框图
由系统总体框图可知,FPGA 顶层模块例化了以下六个模块,分别是 DDR3 驱动时钟产生模块 (gowin_rpll)、HDMI 时钟产生模块(rpll_pixel)、五分频模块(clk_div5)、HDMI 顶层模块(hdmi_top)、 DDR3 读写控制模块(ddr3_top)以及 OV5640 驱动模块(ov5640_dri)。
DDR3 驱动时钟产生模块(gowin_rpll):时钟模块通过调用 PLL IP 核实现,输出 400MHz 时钟作为 DDR 读写控制模块的驱动时钟。
HDMI 时钟产生模块(rpll_pixel):时钟模块通过调用 PLL IP 核实现,输出 375MHz 作为 HDMI 的驱 动时钟。
五分频模块(clk_div5):时钟通过调用 CLKDIV IP 核实现,将 375MHz 五分频然后作为 DDR3 读写 控制模块的读时钟
OV5640 驱动模块(ov5640_dri):OV5640 驱动模块负责驱动 OV5640 SCCB 接口总线,将像素时钟驱 动下的传感器输出的场同步信号、行同步信号以及 8 位数据转换成 DDR 读写控制模块的写使能信号和 16 位写数据信号,完成对 OV5640 传感器图像的采集。
DDR 读写控制模块(ddr3_top):DDR3 读写控制器模块负责驱动 DDR 片外存储器,缓存图像传感器 输出的图像数据。
HDMI 顶层模块(hdmi_top):HDMI 顶层模块负责驱动 HDMI 显示器的驱动信号的输出,同时为其他模块提供显示器参数、场同步信号和数据请求信号。
HDMI 顶层模块例化了 HDMI 驱动模块 (video_driver)和 HDMI 驱动转换顶层模块(dvi_transmitter_top)。HDMI 驱动模块负责产生行场信号和 数据有效使能信号和像素点的横纵坐标,同时将内部信号 data_req(数据请求信号)输出至端口,方便从 DDR 控制器中读取数据,完成读出图像数据的功能。HDMI 驱动转换顶层模块负责将 RGB888 格式的视频 图像转换成 TMDS 数据输出。
由于代码部分过多,此处只附上顶层代码部分,如有需要可以私信联系。
module ov5640_hdmi(
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 数据
inout cam_sda , //cmos SCCB_SDA线
output cam_rst_n , //cmos 复位信号,低电平有效
output cam_pwdn , //电源休眠模式选择 0:正常模式 1:电源休眠模式
output cam_scl , //cmos SCCB_SCL线
output [13:0] ddr_addr , //ddr3 地址
output [2:0] ddr_bank , //ddr3 banck 选择
output ddr_cs , //ddr3 片选
output ddr_ras , //ddr3 行选择
output ddr_cas , //ddr3 列选择
output ddr_we , //ddr3 读写选择
output ddr_ck ,
output ddr_ck_n ,
output ddr_cke , //ddr3 时钟使能
output ddr_odt ,
output ddr_reset_n , //ddr3 复位
output [1:0] ddr_dm ,
inout [15:0] ddr_dq , //ddr3 数据
inout [1:0] ddr_dqs ,
inout [1:0] ddr_dqs_n ,
//HDMI 接口
output tmds_clk_p , // TMDS 时钟通道
output tmds_clk_n ,
output [2:0] tmds_data_p , // TMDS 数据通道
output [2:0] tmds_data_n
);
//parameter define
parameter V_CMOS_DISP = 11'd768; //CMOS分辨率--行
parameter H_CMOS_DISP = 11'd1024; //CMOS分辨率--列
parameter TOTAL_H_PIXEL = H_CMOS_DISP + 12'd1216;
parameter TOTAL_V_PIXEL = V_CMOS_DISP + 12'd504;
parameter APP_ADDR_MAX = 28'd786432 ;
//wire define
wire init_calib_complete ;
wire sys_init_done ; //系统初始化完成(DDR初始化+摄像头初始化)
wire pixel_clk ; //像素时钟75M
wire pixel_clk_5x ; //5倍像素时钟375M
wire clk_locked ;
wire cmos_frame_vsync ; //输出帧有效场同步信号
wire cmos_frame_valid ; //数据有效使能信号
wire [15:0] wr_data ; //DDR3控制器模块写数据
wire video_vs ; //场同步信号
wire [15:0] rd_data ; //DDR3控制器模块读数据
wire rdata_req ; //DDR3控制器模块读使能
//*****************************************************
//** main code
//*****************************************************
//待时钟锁定后产生复位结束信号
assign rst_n = sys_rst_n & pll_lock;
//系统初始化完成:DDR3初始化完成
assign sys_init_done = init_calib_complete;
//ov5640 驱动
ov5640_dri u_ov5640_dri(
.clk (sys_clk),
.rst_n (rst_n),
.cam_pclk (cam_pclk ),
.cam_vsync (cam_vsync),
.cam_href (cam_href ),
.cam_data (cam_data ),
.cam_rst_n (cam_rst_n),
.cam_pwdn (cam_pwdn ),
.cam_scl (cam_scl ),
.cam_sda (cam_sda ),
.capture_start (init_calib_complete),
.cmos_h_pixel (H_CMOS_DISP),
.cmos_v_pixel (V_CMOS_DISP),
.total_h_pixel (TOTAL_H_PIXEL),
.total_v_pixel (TOTAL_V_PIXEL),
.cmos_frame_vsync (cmos_frame_vsync),
.cmos_frame_href (),
.cmos_frame_valid (cmos_frame_valid),
.cmos_frame_data (wr_data)
);
ddr3_top u_ddr3_top(
.clk (sys_clk) ,
.memory_clk (memory_clk) ,
.pll_lock (pll_lock) ,
.rst_n (sys_rst_n) ,
.init_calib_complete (init_calib_complete) , //ddr3初始化完成信号
.ddr_addr (ddr_addr) ,
.ddr_bank (ddr_bank) ,
.ddr_cs (ddr_cs) ,
.ddr_ras (ddr_ras) ,
.ddr_cas (ddr_cas) ,
.ddr_we (ddr_we) ,
.ddr_ck (ddr_ck) ,
.ddr_ck_n (ddr_ck_n) ,
.ddr_cke (ddr_cke) ,
.ddr_odt (ddr_odt) ,
.ddr_reset_n (ddr_reset_n) ,
.ddr_dm (ddr_dm) ,
.ddr_dq (ddr_dq) ,
.ddr_dqs (ddr_dqs) ,
.ddr_dqs_n (ddr_dqs_n) ,
.wr_clk (cam_pclk) ,
.rd_clk (pixel_clk) ,
.wr_en (cmos_frame_valid) ,
.wrdata (wr_data) ,
.rd_req (rdata_req) , //读fifo读使能
.app_addr_rd_min (28'd0) , //读ddr3的起始地址
.app_addr_rd_max (APP_ADDR_MAX) , //读ddr3的结束地址
.rd_bust_len (H_CMOS_DISP[10:3]) , //从ddr3中读数据时的突发长度
.app_addr_wr_min (28'd0) , //写ddr3的起始地址
.app_addr_wr_max (APP_ADDR_MAX) , //写ddr3的结束地址
.wr_bust_len (H_CMOS_DISP[10:3]) , //从ddr3中写数据时的突发长度
.ddr3_read_valid (1'b1) ,
.rd_load (video_vs) ,
.wr_load (cmos_frame_vsync) ,
.ddr3_pingpang_en (1'b1) ,
.rddata (rd_data)
);
gowin_rpll pll(
.clkout(memory_clk) , //output clkout
.lock(pll_lock) , //output lock
.clkin(sys_clk) //input clkin
);
rpll_pixel_clk_5x u_rpll_pixel_clk_5x(
.clkout(pixel_clk_5x), //output clkout
.lock(lock), //output lock
.reset(~sys_rst_n), //input reset
.clkin(sys_clk) //input clkin
);
clk_div5 u_clk_div5(
.clkout(pixel_clk), //output clkout
.hclkin(pixel_clk_5x), //input hclkin
.resetn(rst_n) //input resetn
);
//HDMI顶层模块
hdmi_top u_hdmi_top(
.hdmi_clk (pixel_clk ),
.hdmi_clk_5 (pixel_clk_5x ),
.sys_rst_n (rst_n&sys_init_done),
//HDMI interface
.tmds_clk_p (tmds_clk_p ),
.tmds_clk_n (tmds_clk_n ),
.tmds_data_p (tmds_data_p ),
.tmds_data_n (tmds_data_n ),
//user interface
.rd_data (rd_data ),
.rd_en (rdata_req ),
.video_vs (video_vs ),
.pixel_xpos ( ),
.pixel_ypos ( )
);