分享

相信我SDRAM真的不难九(VGA驱动模块将SDRAM中缓存的图片信息以VGA时序输出从而在显示器上显示图片)

 山峰云绕 2022-04-21

写在前面

(VGA驱动模块将SDRAM中缓存的图片信息以VGA时序输出从而在显示器上显示图片)

基于SDRAM缓存的串口传图综合实战(UART + SDRAM + VGA)


https://wuzhikai.blog.csdn.net/article/details/121724092?spm=1001.2101.3001.6650.5&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-5.pc_relevant_paycolumn_v3&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-5.pc_relevant_paycolumn_v3&utm_relevant_index=10


本文是SDRAM系列文章的第九篇,前面八篇已经实现了一个简单的SDRAM控制器。正所谓光说不练云玩家,接下来我们搞搞实战,真正把SDRAM给用起来。

本文将结合UART模块、VGA模块、SDRAM模块(含PLL、FIFO)来做一个基于SDRAM缓存的串口传图实验,实现UART发送数据、SDRAM缓存数据、VGA显示数据这一过程。

其他博文链接:

        相信我,SDRAM真的不难----汇总篇(电梯直达)


1、总体架构

期待实现的功能:在PC端使用串口助手发送一幅分辨率为 640*480 的图片数据给 FPGA,FPGA 以外接 SDRAM 做缓存,将接收到的图片数据通过 VGA 显示器显示出来。

总体架构示意图如下:

  • PLL模块:时钟生成模块。由于各个模块的时钟不尽相同,通过PLL统一生成时钟,全部接到全局时钟网络上 

  • uart_rx:串口接收模块。将接收到的串行信号,转换成8bit并行信号

  • merge:数据拼接模块。将2个输入的8bit数据转换成16bit数据

  • sdram_top:sdram读写控制器。可以实现高速、大量的数据缓存

  • vga_driver:VGA接口驱动。实现图片(RGB565像素值)的VGA显示

这工程一看好像还挺多挺复杂的,那么我接下来就对整个图片的传送显示流程做一个讲解:

  • 首先将需要显示的图片,转为RGB565格式的txt文件(16bit像素值),然后通过PC端的串口发送上位机,将数据发送给FPGA

  • FPGA将通过串口接收模块uart_rx将串行线上的数据转成8bit的并行数据

  • 通过数据拼接模块merge将每2个8bit数据转换成16bit数据,然后,将其写入SDRAM缓存

  • 在FPGA接收数据的同时通过VGA接口,用屏幕将写入SDRAM的数据”读出“在屏幕上显示

2、设计实现

        接下里就一步一步讲解具体的设计实现了。

2.1、图片预处理

        图片格式:640 * 480。

        图片内容:老婆。

        显然我们无法直接通过串口发送图片,需要将图片转化成RGB565的像素值(其他RGB格式不讨论)才能使用VGA接口显示。我们可以先使用MATLAB将图片转换成RGB565,代码如下:

%--------------------------------------------------------------------------%--           图片数据转换:1个像素转换成2个 8bit hex 数据%--------------------------------------------------------------------------clear all;RGB24 = imread('laopo.bmp');            %读取图片文件fid = fopen('rgb565.txt','w+');         %打开文件[ROW,COL,N] = size(RGB24);              %获得图片尺寸[高度,长度,维度]for i = 1:ROWfor j = 1:COLRG = bitand(RGB24(i,j,1),248) + bitshift(RGB24(i,j,2),-5); %{R[7:3],3'd0} + {5'd0,G[7:5]}G = bitand(RGB24(i,j,2),28);                               %{3'd0,G[4:2],2'd0}GB = bitshift( G,3) + bitshift(RGB24(i,j,3),-3);           %{G[4:2],5'd0} + {3'd0,B[7:3]}fprintf(fid,'%02x %02x ',RG,GB);%将字符打印到txt文件中endend

2.2、子模块

接下来介绍各个子模块的功能及实现方式。这几个子模块,都是我之前的文章写过的模块,所以等下不会介绍的很详细,会贴出链接,感兴趣的朋友可以自己去看看。

2.2.1、串口接收模块

由于我们在PC端使用UART来发送图片信息,而UART是串行协议,所以我们需要一个模块来将接收到的串行数据转为并行数据。

串口模块将PC端通过UART接口(含协议)发送过来的图片的像素信息(RGB565格式)接收,并转化为8bit并行数据。串口接收模块的波特率在本次实验中选为115200。

供参考:串口(UART)的FPGA实现

2.2.2、数据拼接模块

由于目前的主流串口发送软件很少能支持16bit数据的发送,只能将16bit的像素信息拆成2个8bit数据来发送。所以需要一个模块将分别接收的2个8bit数据合并成1个16bit数据(完整的RGB565)。

数据拼接模块将串口接收模块发送的两个8bit数据转换成16bit数据。

供参考:一个简单方便的数据拼接模块(支持任意位宽、任意整数倍)

2.2.3、SDRAM控制模块

SDRAM控制模块可以将接收到串口发送的图片信息暂存到SDRAM芯片,同时显示器也在不停地通过VGA接口对SDRAM控制模块进行操作以读取SDRAM中存取的图片信息。在640*480@60Hz时序下的VGA接口,一幅图片的大小约为640*480*16bit /8 /1024 = 600KB,这个大小显然无法使用FPGA的片内资源来缓存,所以选择SDRAM这种大容量器件来作为接收的缓存。

供参考:相信我,SDRAM真的不难----汇总篇(电梯直达)

2.2.4、VGA驱动模块

VGA驱动模块将SDRAM中缓存的图片信息以VGA时序输出,从而在显示器上显示图片。

供参考:如何用VGA接口乳法?

2.2.5、PLL模块

使用PLL模块统一生成时钟,有利于统一管理时钟,约束时序。各模块需要用到的时钟如下:

  • 50M:串口接收模块、数据拼接模块使用

  • 100M:SDRAM控制模块使用

  • 100M(偏移-30°):SDRAM芯片使用

  • 25M:VGA驱动模块使用

2.3、顶层模块

顶层模块主要实现化上述几个子模块,并设定一些参数:

  • 突发长度:256

  • SDRAM起始地址:0;SDRAM停止地址:640*480(刚好缓存一幅图片)

  • 串口波特率:115200bps

顶层模块完整代码如下:

module pic_uart_sdram #(	parameter 			BPS		= 115200				//发送波特率)		(			input 				sys_clk			,				//50M系统时钟	input 				sys_rst_n		,				//系统复位	input 				uart_rxd		,				//串口接收	output				h_sync			,				//VGA行同步output        		v_sync			,				//VGA场同步	output 	[15:0] 		rgb_data        ,    			//在VGA时序下的RGB数据//SDRAM相关信号output	        	sdram_clk_out	,output	        	sdram_cke       ,output	        	sdram_cs_n      ,output	        	sdram_ras_n     ,output	        	sdram_cas_n     ,output	        	sdram_we_n      ,output	[1:0]   	sdram_bank		,output	[12:0]  	sdram_addr      ,output	[1:0]   	sdram_dqm       ,inout 	[15:0]  	sdram_dq	);localparam	CLK_FRE	= 50_000_000	;						//输入时钟频率localparam	H_PIXEL = 24'd640 		; 						//水平方向像素个数,用于设置 SDRAM 缓存大小localparam	V_PIXEL = 24'd480 		; 						//垂直方向像素个数,用于设置 SDRAM 缓存大小//8bit转16bit模块					localparam	integer	DATA_WIDTH  = 8	;						//输入数据位宽localparam	integer	MERGE_STAGE = 2	;						//合并级数,如2则将两个数据合并为一个	//PLL					wire	rst_n			;									//低电平有效的复位信号,除PLL模块外均使用该信号wire	locked			;									//PLL输出稳定信号,高电平有效wire	pll_50M			;									//用于串口模块wire	pll_100M		;									//用于SDRAMwire	pll_100M_shift	;									//用于SDRAMwire	pll_25M			;									//用于25M//uart					wire	[7:0]	rx_data;									//接收到的串口数据wire	rx_flag;											//串口数据有效//VGA					wire			pix_data_req;								//RGB数据请求信号,用作SDRAM的读请求wire	[15:0]	sdram_rd_data;								//从SDRAM中读出的数据//merge					wire	[15:0]	data_16bit;									//8bit转16bit后的数据wire			data_16bit_valid;							//16bit数据有效信号assign rst_n = sys_rst_n & locked;							//PLL未稳定视为复位状态//例化PLLclk_gen	clk_gen_inst (	.areset 				(~sys_rst_n			),			//	.inclk0 				(sys_clk			),			//50M输入	.c0 					(pll_50M			),			//50M	.c1 					(pll_100M			),			//100M	.c2 					(pll_100M_shift		),			//100M_SHIFT	.c3 					(pll_25M			),			//25M	.locked 				(locked				)			//PLL输出稳定信号,高电平有效);	//例化VGA驱动模块(640*480@60,时钟25M)	vag_driver	vag_driver_inst(		.vga_clk				(pll_25M			),			//VGA时钟	.sys_rst_n				(rst_n				),       	//复位信号、低电平有效	.pixel_data				(sdram_rd_data		),       	//输入RGB数据		.h_sync      			(h_sync				),       	//行同步信号		.v_sync      			(v_sync				),			//场同步信号	.pixel_x				(					),			//行坐标,不需要使用	.pixel_y				(					),			//场坐标,不需要使用	.pix_data_req			(pix_data_req		),			//RGB数据请求信号,比rgb_valid提前一拍	.rgb_data				(rgb_data			)           //在VGA时序下的RGB数据);//例化8bit转16bit模块merge #(	.DATA_WIDTH  			(DATA_WIDTH			),			//输入数据位宽	.MERGE_STAGE 			(MERGE_STAGE		)			//合并级数,如2则将两个数据合并为一个。	)	merge_inst(		.sys_clk				(pll_50M			),			//50M系统时钟	.sys_rst_n				(rst_n				),			//系统复位	.data_in				(rx_data			),		.data_in_valid			(rx_flag			),				.data_out				(data_16bit			),	.data_out_valid			(data_16bit_valid	));//例化串口接收模块uart_rx#(	.BPS					(BPS				),			//发送波特率	.CLK_FRE				(CLK_FRE			)			//输入时钟频率)		uart_rx_inst(			.sys_clk				(pll_50M			),			//50M系统时钟	.sys_rst_n				(rst_n				),			//系统复位	.uart_rxd				(uart_rxd			),			//接收数据线	.uart_rx_done			(rx_flag			),			//数据接收完成标志,当其为高电平时,代表接收数据有效	.uart_rx_data			(rx_data			)			//接收到的数据,在uart_rx_done为高电平时有效);//例化SDRAM控制模块sdram_top	sdram_top_inst(.sdram_clk				(pll_100M			),   		//sdram时钟.sdram_rst_n			(rst_n				),   		//sdram复位信号	.clk_out         		(pll_100M_shift		),   		//sdram相位偏移时钟(直接给SDRAM芯片)//写FIFO信号		.wr_fifo_rst			(~rst_n				),   		//写FIFO复位信号.wr_fifo_wr_clk  		(pll_50M			),   		//写FIFO写时钟	.wr_fifo_wr_req  		(data_16bit_valid	),   		//写FIFO写请求.wr_fifo_wr_data 		(data_16bit			),   		//写FIFO写数据.sdram_wr_b_addr 		(0					),   		//写SDRAM首地址.sdram_wr_e_addr 		(H_PIXEL*V_PIXEL	),   		//写SDRAM末地址.wr_burst_len    		(256				),   		//写SDRAM数据突发长度.wr_fifo_num     		(					),   		//写fifo中的数据量	//读FIFO信号			.rd_fifo_rd_clk  		(pll_25M			),   		//读FIFO读时钟,与VGA时钟一致.rd_fifo_rst			(~rst_n				),   		//读复位信号	.rd_fifo_rd_req  		(pix_data_req		),   		//读FIFO读请求.sdram_rd_b_addr 		(0					),   		//读SDRAM首地址.sdram_rd_e_addr 		(H_PIXEL*V_PIXEL	),   		//读SDRAM末地址.rd_burst_len    		(256				),   		//读SDRAM数据突发长度.rd_fifo_rd_data 		(sdram_rd_data		),   		//读FIFO读数据,用于VGA显示的数据.rd_fifo_num     		(					),   		//读fifo中的数据量//功能信号			.read_valid      		(1'b1				),   		//SDRAM读使能.init_end        		(					),   		//SDRAM初始化完成标志//SDRAM接口信号			.sdram_clk_out			(sdram_clk_out		),   		//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_bank				(sdram_bank			),   		//SDRAM的L-Bank地址线.sdram_addr      		(sdram_addr  		),   		//SDRAM地址总线.sdram_dqm       		(sdram_dqm   		),   		//SDRAM数据掩码.sdram_dq        		(sdram_dq    		)    		//SDRAM数据总线);endmodule

3、仿真

略。因为VGA的时序如果不改动参数仿真,那么时间就太长了;改了吧,看起来意义又不大。干脆不仿真了,直接看现象吧。

4、上板验证

验证环境:

  • 串口上位机:野火多功能调试助手

  • MATLAB:MATLAB R2014a

  • Quartus II:Quartus II 13.1 (64-bit)

  • FPGA:EP4CE10F17C8

  • SDRAM:W9825G6KH-6

需要注意的是因为串口的发送速度太慢了,而VGA读取的速度又相对较快,所以看起来,图片是一行、一行的显示出来的。实验结果如下:

5、其他

  • 创作不易,如果本文对您有帮助,还请多多点赞、评论和收藏。GAKKI都来了,你好意思不给我点赞?

  • 关于本文,您有什么想法均可在评论区留言交流。如果需要整个工程,请在评论留下邮箱或者私信我邮箱(注意保护隐私)。

  • 自身能力不足,如有错误还请多多指出!

版本信息

        文件:V1.0

        编号:69

        Vivado:无

        Modelsim:无

        Quartus II:Quartus II 13.1 (64-bit)

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多