基于RS232的VGA图像显示

该博客介绍了如何通过MATLAB将RGB444图像转换为RGB332格式,然后利用串口RS232将图像数据发送到FPGA,以实现VGA显示器的动态显示。在FPGA内部,使用伪双口RAM来处理跨时钟域的数据同步问题,确保25MHz和50MHz时钟下的稳定工作。同时,文章还展示了在处理过程中遇到的图像显示异常问题及解决方案。
摘要由CSDN通过智能技术生成

基于ROM的VGA图像显示方式,需要提前将图片数据转换成.coe文件,然后初始化ROM-IP,其实还是占用FPGA内部的RAM存储资源,能够显示的图像数据大小是有限的,并且每次只能显示一张图片,想要显示不同的图片就得重新用.coe文件初始化ROM-IP

改进一下,把串口RS232也加上,通过串口发送图像数据到FPGA内部,然后再用VGA显示出来,想要显示不同的图片,就只需要通过串口发送不同的图像数据就可以了,这样的显示方式可以显示更大的图片

图像数据是不能用串口直接进行收发的,需要处理成像素信息,然后再由串口收发

串口数据帧中有8bit有效数据,需要将原来的RGB444转成RGB332格式,再由串口进行发送,一次发送一个像素点信息

1. matlab工具使用

matlab图片转换为RGB332格式,也可以参照之前生成.coe文件的.m文件,改一下将字符打印到txt文件的部分,也能生成对应的.txt文件

clear all;
RGB24 = imread('dog_100x100.jpg');                %读取图片文件

R = bitshift(RGB24(1:end,1:end,1),-5);      %取R高3,{5'b0,R[7:5]}
G = bitshift(RGB24(1:end,1:end,2),-5);      %取G高3,{5'd0,G[7:5]}
B = bitshift(RGB24(1:end,1:end,3),-6);      %取B高2,{6'd0,B[7:6]}
rgb332 = bitshift(R,5) + bitshift(G,2) + B; %拼接{R[7:5],G[7:5],B[7:6]}

fid=fopen('D:\wwww\mine_2\mine\dog_RGB332.txt','w+');               %打开文件
fprintf(fid,'%02x ',rgb332');               %将字符打印到txt文件中

2. 实验目标

通过上位机的串口调试助手发送图片数据给FPGA,FPGA接收到后会缓存在RAM中,当VGA显示器扫描到图像数据后就会显示

RS232工作时钟为50MHz,VGA640*480@60工作时钟为25MHz,中间作为缓存的RAM需要处理跨时钟域

3. 代码实现

在这里插入图片描述

25MHz和50MHz的工作时钟由PLL锁相环生成,uart_rx接收模块复用RS232的接收模块,vga_ctrl也是复用VGA显示模块,RAM调用IP核,需要修改的就是vga_pic模块


block RAM有三种:单口RAM、伪双口RAM和真双口RAM。

  1. 单口RAM只有一个端口(A端口),可以对A端口进行读写。
  2. 伪双口RAM有两个端口(A和B端口),但是A端口只能进行写入操作,不能进行读出操作,而B端口则只能进行读出操作,不能进行写入操作。
  3. 真双口RAM有两个端口(A和B端口),A和B端口都能进行读写操作。

这里创建伪双端口RAM,50MHz下写,25MHz下读

module vga_pic (
	input	wire			rx_clk		,	// 50MHz
	input	wire			vga_clk		,	// 25MHz
	input	wire			sys_rst_n	,
	input	wire	[ 9: 0]	pix_x		,
	input	wire	[ 9: 0]	pix_y		,
	input	wire	[ 7: 0]	pi_data		,	// rx
	input	wire			pi_flag		,
	
	output	wire	[ 7: 0]	pix_data
);

	// 分辨率  也就是坐标信号的最大值
	parameter	H_VALID	=	10'd640,
				V_VALID	=	10'd480;
				
	// 颜色参数定义		
				//				B	G	R
	parameter	RED		=	8'b00_000_111,
				GREEN	=	8'b00_111_000,
				BLUE	=	8'b11_000_000,
				WHITE	=	8'b111_111_11,
				BLACK	=	8'b000_000_00;
				
	
	// 图片大小参数定义
	parameter	H_PIC	=	10'd100,
				V_PIC	=	10'd100;
	parameter	PIC_SIZE=	14'd10000;
	
	
	reg		[ 7: 0]	data_pix	;		// 彩条颜色
	reg				pic_valid	;		// ROM图像有效信号

	wire			rd_en		;		// RAM读写使能信号
	reg		[13: 0]	rd_addr		;		// RAM读地址
	reg		[13: 0]	wr_addr		;		// RAM写地址
	
	wire	[ 7: 0]	pic_data	;		// RAM读数据
	
	
	wire	[ 7: 0]	real_rgb_data;
	
	assign	real_rgb_data	=	{pic_data[1:0], pic_data[4:2], pic_data[7:5]};
	assign	pix_data	=	(pic_valid == 1'b1) ? real_rgb_data : data_pix;
	
	// rd_en:
						// 横向:269 ≤ pix_x <  369  需要提前一拍准备好读ROM使能
						// 纵向:190 ≤ pix_y <  290					
	assign	rd_en	=	(((pix_x >= (((H_VALID - H_PIC) / 2) - 1'b1))			// pix_x ≥ (((640-100)/2))-1'b1		= 269
						&& (pix_x < (((H_VALID - H_PIC) / 2) + H_PIC - 1'b1)))	// pix_x <  (((640-100)/2)+100)-1'b1= 369
						&& ((pix_y >= ((V_VALID - V_PIC) / 2))					// pic_y ≥ (((480-100)/2))			= 190
						&& ((pix_y < (((V_VALID - V_PIC) / 2) + V_PIC)))));		// pix_x <  (((480-100)/2)+100)		= 290

						
	// pic_valid: 将rd_en打一拍
	always @ (posedge vga_clk or negedge sys_rst_n)
		if (sys_rst_n == 1'b0)
			pic_valid	<=	1'b0;
		else
			pic_valid	<=	rd_en;
	
	
	// rd_addr	25MHz读时钟
	always @ (posedge vga_clk or negedge sys_rst_n)
		if (sys_rst_n == 1'b0)
			rd_addr	<=	14'd0;
		else	if (rd_addr == PIC_SIZE - 1'b1)
			rd_addr	<=	14'd0;
		else	if (rd_en)
			rd_addr	<=	rd_addr + 1'b1;
		else
			rd_addr	<=	rd_addr;

	// wr_addr	50MHz写时钟
	always @ (posedge rx_clk or negedge sys_rst_n)
		if (sys_rst_n == 1'b0)
			wr_addr	<=	14'd0;
		else	if ((wr_addr == PIC_SIZE - 1'b1) && (pi_flag == 1'b1))
			wr_addr	<=	14'd0;
		else	if (pi_flag == 1'b1)
			wr_addr	<=	wr_addr + 1'b1;
		else
			wr_addr	<=	wr_addr;


	// data_pix: 彩条颜色信号赋值
	always @ (posedge vga_clk or negedge sys_rst_n)
		if (sys_rst_n == 1'b0)
			data_pix	<=	BLACK;		// 默认显示黑色
		else	if (pix_x >= 0 && pix_x < (H_VALID / 10) * 1)
			data_pix	<=	RED;
		else	if (pix_x >= (H_VALID / 10) * 1 && pix_x < (H_VALID / 10) * 2)
			data_pix	<=	GREEN;
		else	if (pix_x >= (H_VALID / 10) * 2 && pix_x < (H_VALID / 10) * 3)
			data_pix	<=	BLUE;
		else	if (pix_x >= (H_VALID / 10) * 3 && pix_x < (H_VALID / 10) * 4)
			data_pix	<=	WHITE;
		else	if (pix_x >= (H_VALID / 10) * 4 && pix_x < (H_VALID / 10) * 5)
			data_pix	<=	BLACK;	
		else	if (pix_x >= (H_VALID / 10) * 5 && pix_x < (H_VALID / 10) * 6)
			data_pix	<=	RED;
		else	if (pix_x >= (H_VALID / 10) * 6 && pix_x < (H_VALID / 10) * 7)
			data_pix	<=	GREEN;
		else	if (pix_x >= (H_VALID / 10) * 7 && pix_x < (H_VALID / 10) * 8)
			data_pix	<=	BLUE;
		else	if (pix_x >= (H_VALID / 10) * 8 && pix_x < (H_VALID / 10) * 9)
			data_pix	<=	WHITE;
		else	if (pix_x >= (H_VALID / 10) * 9 && pix_x < (H_VALID / 10) * 10)
			data_pix	<=	BLACK;	
		else
			data_pix	<=	BLACK;
	
	ram_pic ram_pic_inst	(
		.clka	(rx_clk		),	// input wire clka			写时钟
		.ena	(pi_flag	),	// input wire ena			写使能
		.wea	(1'b1		),	// input wire [0 : 0] wea	写字节使能
		.addra	(wr_addr	),  // input wire [13 : 0] addra写地址
		.dina	(pi_data	),	// input wire [7 : 0] dina	写数据
		.clkb	(vga_clk	),	// input wire clkb			读时钟
		.enb	(1'b1		),	// input wire enb			读使能
		.addrb	(rd_addr	),	// input wire [13 : 0] addrb读地址
		.doutb	(pic_data	)	// output wire [7 : 0] doutb读数据
	);

endmodule

vga_pic模块输出的彩条像素数据、图像像素数据都是8bit格式,在顶层模块,需要扩展成12bitRGB444格式

assign	rgb = {2'b0, rgb_332[7:6], 1'b0, rgb_332[5:3], 1'b0, rgb_332[2:0]};

或者尝试直接将332格式用于管脚绑定 用不到的管脚就空着,这样也能正常显示图像

set_property -dict {PACKAGE_PIN F5 IOSTANDARD LVCMOS33} [get_ports rgb_332[0]]
set_property -dict {PACKAGE_PIN C6 IOSTANDARD LVCMOS33} [get_ports rgb_332[1]]
set_property -dict {PACKAGE_PIN C5 IOSTANDARD LVCMOS33} [get_ports rgb_332[2]]
#set_property -dict {PACKAGE_PIN B7 IOSTANDARD LVCMOS33} [get_ports rgb[3]]

set_property -dict {PACKAGE_PIN B6 IOSTANDARD LVCMOS33} [get_ports rgb_332[3]]
set_property -dict {PACKAGE_PIN A6 IOSTANDARD LVCMOS33} [get_ports rgb_332[4]]
set_property -dict {PACKAGE_PIN A5 IOSTANDARD LVCMOS33} [get_ports rgb_332[5]]
#set_property -dict {PACKAGE_PIN D8 IOSTANDARD LVCMOS33} [get_ports rgb[7]]

set_property -dict {PACKAGE_PIN C7 IOSTANDARD LVCMOS33} [get_ports rgb_332[6]]
set_property -dict {PACKAGE_PIN E6 IOSTANDARD LVCMOS33} [get_ports rgb_332[7]]
#set_property -dict {PACKAGE_PIN E5 IOSTANDARD LVCMOS33} [get_ports rgb[10]]
#set_property -dict {PACKAGE_PIN E7 IOSTANDARD LVCMOS33} [get_ports rgb[11]]

在这里插入图片描述


4. 遇到的问题

  1. Matlab生成的像素点信息文件用串口传送后,等宽彩条应该是“红绿蓝白黑”,实际显示的是下面这个样子,不太清楚RGB332转RGB444的bit对应关系
    在这里插入图片描述
    在这里插入图片描述
    修改一下对应bit关系,重新下板
    在这里插入图片描述

  2. 自己仿真出来的波形,vga_pic模块中的wr_addr信号没有归零,po_flag少拉高一次,下板后可以看到显示器显示对应的图像,影响不太大
    在这里插入图片描述
    在这里插入图片描述

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值