本例将接着上一例实现的sdram控制器进行封装。上例中只是实现了一个基本的控制器,在实际使用中,通常读写时钟是两个不同频率的,所以并不能满足要求。
在本例中,将对读写接口进行封装,将读写接口封装成FIFO接口这样封装主要是为了让,读写的时候,满足设计要求(存储OV5640传输过来的图像,并且显示在VGA显示器上)。
最后将设计好的接口,配置之前写好的串口以及VGA驱动,实现一个小例子。通过串口发送图片,sdram存储,vga显示。本例完成后,就可以使用此sdram模块做很多事情啦!
欢迎关注微信公众号 FPGA之旅 回复 FPGA之旅设计99例之第二十一例 获取完整代码,以及相关软件。
本例写的比较匆忙,望见谅。
在封装的时候,使用到了FIFO IP,主要用来,外部传输图像数据到SDRAM中间的一个缓存区域,接口信号设计如下,外部给定帧地址,以及同步信号后(将写入sdram的地址复位为帧的起始地址),后面来的时候,就会依次往sdram中存储,这样外部就不需要管地址了,只需要知道存储到第几个帧的位置就可以了。帧大小以及写的突发长度,通过宏定义在sdram_defines.v文件中,方便修改。整个代码实现如下,非常简单。
//写正常 中转,外部写入数据暂存于此,然后写入sdram
`include "sdram_defines.v"
module sdram_hpfifo_write
(input rst_n,input write_clk, //写时钟input write_en, //写使能input[1:0] write_frame_addr, //帧地址选择input write_frame_sync, //帧同步input[15:0] write_data,//sdraminput read_clk,output write_req,input write_ack,output[23:0] write_addr,output[9:0] write_burst_length,output[15:0] write_sdram_data,input write_data_en
);wire[9:0] rdusedw;
reg [23:0] read_addr_reg;
assign write_burst_length = `burst_length;
assign write_addr = read_addr_reg;
assign write_req = ( rdusedw >= `burst_length ) ? 1'b1 : 1'b0;always@(posedge read_clk or negedge rst_n)
beginif( rst_n == 1'b0)read_addr_reg <= 'd0;else if( write_ack == 1'b1 )read_addr_reg <= read_addr_reg + `burst_length;elseread_addr_reg <= read_addr_reg;
endfifo fifo_w(.aclr ( ~rst_n ),.data ( write_data ),.rdclk ( read_clk ),.rdreq ( write_data_en ),.wrclk ( write_clk ),.wrreq ( write_en ),.q ( write_sdram_data),.rdusedw ( rdusedw ),.wrusedw ()
);endmodule
读接口主要用于外部从sdram读取数据,内部也调用了一个FIFO IP。信号接口,以及实现过程和写接口封装一模一样。就不做信息的说明了。
封装完成后,整体的框图如下,也是非常的简介。
封装的实现还是比较容易的,大家可以试试不调用FIFO,自己实现一个FIFO实现。
为了检测封装的是否成功,通过这个小例子,就可以检测出来。如果图片显示无误,那么说明封装正确了。
本例所使用到的模块,均是前几例中实现了的模块,所以这个例子也就是将其组合在一起。
module main(input sys_clk,input rst_n,//????input uartrx, /*uart rx???*///vgaoutput[15:0] vga_rgb,output vga_hsync,output vga_vsync,input key,//sdramoutput sdram_clk, //sdram clockoutput sdram_cke, //sdram clock enableoutput sdram_cs_n, //sdram chip selectoutput sdram_we_n, //sdram write enableoutput sdram_cas_n, //sdram column address strobeoutput sdram_ras_n, //sdram row address strobeoutput[1:0] sdram_dqm, //sdram data enableoutput[1:0] sdram_ba, //sdram bank addressoutput[12:0] sdram_addr, //sdram addressinout[15:0] sdram_dq //sdram data
);wire uart_rxs_done;
wire[15:0] uart_data;wire display_data_en;
wire[15:0] display_data;wire vga_clk_25M;
wire sdram_clk_75M;
wire sdram_clk_75M_Shift;reg[23:0] cnt;reg[1:0] read_frame_addr;always@(posedge vga_clk_25M or negedge rst_n)
beginif( rst_n == 1'b0)read_frame_addr <= 2'b00;else if( key == 1'b0)read_frame_addr <= read_frame_addr + 1'b1;elseread_frame_addr <= read_frame_addr;endpll pll_PL(.inclk0 (sys_clk),.c0 (vga_clk_25M),.c1 (sdram_clk_75M),.c2 (sdram_clk_75M_Shift));UART_MulRX UART_MulRX_HP(.sys_clk ( sys_clk ), /*???? 50M*/.rst_n ( rst_n ), /*????*/.uart_rxs_done ( uart_rxs_done ), /*??????*/.odats ( uart_data ), /*????*/.uartrx ( uartrx )/*uart rx???*/
);vga_driver vga_driver_HP(.vga_clk ( vga_clk_25M ),.rst_n ( rst_n ),.display_data_en ( display_data_en ),.curr_x ( ),.curr_y ( ),.display_data ( display_data ),.vga_rgb ( vga_rgb ),.vga_hsync ( vga_hsync ),.vga_vsync ( vga_vsync ));sdram_hpfifo sdram_hpfifoHP(.sys_clk ( sdram_clk_75M ), //sdram_??.sdram_clk_100M_F ( sdram_clk_75M_Shift ),.rst_n ( rst_n ),//? .read_clk ( vga_clk_25M ), //???.read_en ( display_data_en ), //???.read_frame_addr ( read_frame_addr ), //?????.read_fram_sync ( vga_vsync ), //???.read_data ( display_data ),//? .write_clk ( sys_clk ), //???.write_en ( uart_rxs_done ), //???.write_frame_addr ( 2'b00 ), //?????.write_frame_sync ( 1'b0 ), //???.write_data ( uart_data ),//sdram??.sdram_clk ( sdram_clk ), //sdram clock.sdram_cke ( sdram_cke ), //sdram clock enable.sdram_cs_n ( sdram_cs_n ), //sdram chip select.sdram_we_n ( sdram_we_n ), //sdram write enable.sdram_cas_n ( sdram_cas_n ), //sdram column address strobe.sdram_ras_n ( sdram_ras_n ), //sdram row address strobe.sdram_dqm ( sdram_dqm ), //sdram data enable.sdram_ba ( sdram_ba ), //sdram bank address.sdram_addr ( sdram_addr ), //sdram address.sdram_dq ( sdram_dq ) //sdram data
);
endmodule
通过串口发送图片的时候,并不需要额外去提取图像数据,像很多例程中说提到的通过其他编程语言来实现,只需要用来下面的软件即可生成。这里一定要选择生成二进制文件。
然后将生成好的bin文件,放入com软件中,发送即可。
最后的显示效果如图所示。最后上面的软件会放到本例的下载链接中。
ps: 测试时,本人使用的开发板跑不到100M,调试了好久,才发现这个问题,最终将时钟频率降低到了75M。