基于ROM的VGA图像显示

这篇博客介绍了如何在FPGA中实现VGA显示器驱动,包括显示等宽颜色彩条、字符以及从ROM加载100x100像素图像。首先,通过MATLAB将图片转换为.coe文件并初始化到ROM中。然后,设计了锁相环IP生成25MHz时钟,VGA控制模块用于驱动显示,VGA图像数据生成模块结合ROM-IP在彩条背景上添加图像信息。最后,通过预处理地址信号确保图像在VGA显示的正确位置。
摘要由CSDN通过智能技术生成

前面已经实现了VGA等宽颜色彩条、VGA字符显示,显示的色彩信息都是通过代码生成的,这一篇博客实现显示已存在的图片

VGA显示器驱动设计与验证

VGA显示器字符显示

1. 实验目的

在等宽彩条背景上,显示存储在ROM中100*100大小的图显示在VGA显示640 * 480 @60最中间位置

ROM其实用的还是FPGA内部的RAM存储资源
在这里插入图片描述

2. 代码实现

锁相环IP

生成25MHz
在这里插入图片描述

vga控制模块

复用,驱动VGA进行显示

在这里插入图片描述

vga图像数据生成模块

要在彩条的基础上添加ROM图像信息,内部添加ROM-IP保存图像信息

在这里插入图片描述

rom_pic

需要用matlab将图片转成的.coe文件,再初始化进ROM中

coe_gen.m

clear               %清理命令行窗口
clc                 %清理工作区

% 使用imread函数读取图片,并转化为三维矩阵
image_array = imread('dog_100x100.jpg');

% 使用size函数计算图片矩阵三个维度的大小
% 第一维为图片的高度,第二维为图片的宽度,第三维为图片维度
[height,width,z]=size(image_array);   % 100*100*3
red   = image_array(:,:,1); % 提取红色分量,数据类型为uint8,图层为第1图层
green = image_array(:,:,2); % 提取绿色分量,数据类型为uint8,图层为第2图层
blue  = image_array(:,:,3); % 提取蓝色分量,数据类型为uint8,图层为第3图层
% 这样导出的rgb分量都是8bit

% 使用reshape函数将各个分量重组成一个一维矩阵
%为了避免溢出,将uint8类型的数据扩大为uint32类型
% 100x100是10000个像素点
r = uint32(reshape(red'   , 1 ,height*width));	%110000个数据 	8bit
g = uint32(reshape(green' , 1 ,height*width));
b = uint32(reshape(blue'  , 1 ,height*width));

% 初始化要写入.COE文件中的RGB颜色矩阵
rgb=zeros(1,height*width);	% 1维 数据有10000?? 数据大小


% 显示模式的转换

% 将RGB888转换为RGB444
% 红色分量右移4位取出高4,左移8位作为ROM中RGB数据的第11bit到第8bit
% 绿色分量右移4位取出高4,左移4位作为ROM中RGB数据的第7bit到第4bit
% 蓝色分量右移4位取出高4,左移0位作为ROM中RGB数据的第3bit到第0bit
 for i = 1:height*width
 	rgb(i) = bitshift(bitshift(r(i),-4),8)+ bitshift(bitshift(g(i),-4),4)+ bitshift(bitshift(b(i),-4),0);
 end


fid = fopen( 'D:\wwww\mine\image.coe', 'w+' );		// 这里的地址需要写成绝对路径 不然有报错

% .mif文件字符串打印
fprintf( fid, 'MEMORY_INITIALIZATION_RADIX=16;\n');
fprintf( fid, 'MEMORY_INITIALIZATION_VECTOR=\n',height*width);

% 写入图片数据
for i = 1:height*width
    if i == height*width
        fprintf(fid,'%x;\n',rgb(i)); %最后一个数据后面加分号
    else
        fprintf(fid,'%x,\n',rgb(i));
    end
end

fclose( fid ); % 关闭文件指针

运行,生成对应.coe文件,创建ROM-IP,初始化
ROM 12x10005,12bit的像素点数据10000个,多留5个空闲位置,数据深度需要14bit

vga_pic

这个模块内部需要田间ROM-IP部分,对之前的数据生成模块改一下
在这里插入图片描述
不勾选输出寄存器选项,第一拍输入有效地址和使能信号,第二拍返回有效数据
在这里插入图片描述

在扫描图像数据时,按照下面的顺序扫描像素点到显示屏上,一行一行的弄,所有在每一行的扫描过程中,需要读ROM的时候,需要提前一拍将有效地址和使能信号准备好
在这里插入图片描述
在这里插入图片描述

module vga_pic (
	input	wire			vga_clk,
	input	wire			sys_rst_n,
	input	wire	[ 9: 0]	pix_x,
	input	wire	[ 9: 0]	pix_y,
	
	output	wire	[11: 0]	pix_data
);

	// 分辨率  也就是坐标信号的最大值
	parameter	H_VALID	=	10'd640,
				V_VALID	=	10'd480;
				
	// 颜色参数定义		
	parameter	RED		=	12'h0_0_F,
				ORANGE	=	12'h0_A_F,
				YELLOW	=	12'h0_F_F,
				GREEN	=	12'h0_F_0,
				CYAN	=	12'hF_F_0,
				BLUE	=	12'hF_0_0,
				PUPPLE	=	12'hF_2_A,
				BLACK	=	12'h0_0_0,
				WHITE	=	12'hF_F_F,
				GRAY	=	12'hB_B_B;
				
	
	// 图片大小参数定义
	parameter	H_PIC	=	10'd100,
				V_PIC	=	10'd100;
	parameter	PIC_SIZE=	14'd10000;
	
	
	reg		[11: 0]	data_pix	;		// 彩条颜色
	reg				pic_valid	;		// ROM图像有效信号

	wire			rd_en		;		// ROM读使能信号
	reg		[13: 0]	rom_addr	;		// ROM读地址
	
	wire	[11: 0]	pic_data	;		// ROM读数据
	
	
	assign	pix_data	=	(pic_valid == 1'b1) ? real_rgb_data : data_pix;
	
	// rd_en:
						// 横向:269 ≤ pix_x <  369  需要提前一拍准备好读ROM使能
						// 纵向:190 ≤ pix_x <  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;
	
	
	// rom_addr: 
	always @ (posedge vga_clk or negedge sys_rst_n)
		if (sys_rst_n == 1'b0)
			rom_addr	<=	14'd0;
		else	if (rom_addr == PIC_SIZE - 1'b1)
			rom_addr	<=	14'd0;
		else	if (rd_en)
			rom_addr	<=	rom_addr + 1'b1;
		else
			rom_addr	<=	rom_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	<=	ORANGE;
		else	if (pix_x >= (H_VALID / 10) * 2 && pix_x < (H_VALID / 10) * 3)
			data_pix	<=	YELLOW;
		else	if (pix_x >= (H_VALID / 10) * 3 && pix_x < (H_VALID / 10) * 4)
			data_pix	<=	GREEN;
		else	if (pix_x >= (H_VALID / 10) * 4 && pix_x < (H_VALID / 10) * 5)
			data_pix	<=	CYAN;	
		else	if (pix_x >= (H_VALID / 10) * 5 && pix_x < (H_VALID / 10) * 6)
			data_pix	<=	BLUE;
		else	if (pix_x >= (H_VALID / 10) * 6 && pix_x < (H_VALID / 10) * 7)
			data_pix	<=	PUPPLE;
		else	if (pix_x >= (H_VALID / 10) * 7 && pix_x < (H_VALID / 10) * 8)
			data_pix	<=	BLACK;
		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	<=	GRAY;	
		else
			data_pix	<=	BLACK;

// 经过matlab生成的.coe文件中,一维数组的颜色分量是这样的,需要改变一下顺序,不然下板之后的图片有一点颜色对不上
// 红色分量,第11bit到第8bit
// 绿色分量,第7bit到第4bit
// 蓝色分量,第3bit到第0bit
	wire	[11: 0]	real_rgb_data = {pic_data[3:0], pic_data[7:4], pic_data[11:8]};

	rom_pic	rom_pic_inst (
		.clka	(vga_clk	),		// input wire clka
		.ena	(rd_en		),		// input wire ena
		.addra	(rom_addr	),		// input wire [13 : 0] addra
		.douta	(pic_data	)		// output wire [11 : 0] douta
	);

endmodule

顶层模块

在这里插入图片描述

测试验证

在这里插入图片描述
在这里插入图片描述

ROM 是一种只读存储器,可以用来存储程序或数据。在 FPGA 中,我们可以使用 ROM 存储图像数据,然后使用 Verilog 代码将图像显示在屏幕上。下面是一个简单的 Verilog 代码示例,用于将 ROM 中存储的图像数据显示VGA 屏幕上: ``` module image_display( input clk, input rst_n, output reg [7:0] red, output reg [7:0] green, output reg [7:0] blue, output reg hsync, output reg vsync ); // 定义 ROM 存储器和地址计数器 reg [7:0] rom [0:1023]; reg [9:0] rom_addr; // 初始化 ROM 存储器 initial begin $readmemh("image_data.hex", rom); end // VGA 时序计数器 reg [9:0] h_count, v_count; // 初始化 VGA 时序计数器 always @(posedge clk) begin if (!rst_n) begin h_count <= 0; v_count <= 0; end else begin if (h_count == 799) begin h_count <= 0; if (v_count == 524) begin v_count <= 0; end else begin v_count <= v_count + 1; end end else begin h_count <= h_count + 1; end end end // 从 ROM 中读取图像数据 always @(posedge clk) begin if (!rst_n) begin rom_addr <= 0; end else begin if (hsync && vsync) begin rom_addr <= rom_addr + 1; end end end // 将图像数据输出到 VGA 屏幕上 always @(posedge clk) begin if (!rst_n) begin red <= 0; green <= 0; blue <= 0; hsync <= 1; vsync <= 1; end else begin if (hsync && vsync) begin red <= rom[rom_addr][7:5]; green <= rom[rom_addr][4:2]; blue <= rom[rom_addr][1:0]; end else begin red <= 0; green <= 0; blue <= 0; end hsync <= (h_count >= 640 && h_count < 800); vsync <= (v_count >= 480 && v_count < 524); end end endmodule ``` 在这个代码中,我们使用一个 ROM 存储器来存储图像数据,并使用一个地址计数器从 ROM 中读取数据。然后,我们使用一个 VGA 时序计数器来生成 VGA 时序信号,并将图像数据输出到 VGA 屏幕上。请注意,这个代码示例假定您已经将图像数据存储在名为 "image_data.hex" 的 HEX 文件中,您需要根据您的实际情况进行修改。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值