一. 简介
本例将接着上一例实现的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, //sdram input 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) begin if( rst_n == 1'b0) read_addr_reg <= 'd0; else if( write_ack == 1'b1 ) read_addr_reg <= read_addr_reg + `burst_length; else read_addr_reg <= read_addr_reg; end fifo 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。信号接口,以及实现过程和写接口封装一模一样。就不做信息的说明了。
四. SDRAM_FIFO 模块
封装完成后,整体的框图如下,也是非常的简介。
封装的实现还是比较容易的,大家可以试试不调用FIFO,自己实现一个FIFO实现。
五. 串口-VGA-SDRAM显示图片
为了检测封装的是否成功,通过这个小例子,就可以检测出来。如果图片显示无误,那么说明封装正确了。
本例所使用到的模块,均是前几例中实现了的模块,所以这个例子也就是将其组合在一起。
module main( input sys_clk, input rst_n, //???? input uartrx, /*uart rx???*/ //vga output[15:0] vga_rgb, output vga_hsync, output vga_vsync, input key, //sdram output sdram_clk, //sdram clock output sdram_cke, //sdram clock enable output sdram_cs_n, //sdram chip select output sdram_we_n, //sdram write enable output sdram_cas_n, //sdram column address strobe output sdram_ras_n, //sdram row address strobe output[1:0] sdram_dqm, //sdram data enable output[1:0] sdram_ba, //sdram bank address output[12:0] sdram_addr, //sdram address inout[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) begin if( rst_n == 1'b0) read_frame_addr <= 2'b00; else if( key == 1'b0) read_frame_addr <= read_frame_addr + 1'b1; else read_frame_addr <= read_frame_addr; end pll 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。
公众号:FPGA之旅