FPGA基于VGA的彩条、字符及图片显示

1、vga简介

    1、基本标准
VGA(Video Graphics Array)即视频图形阵列,是IBM于1987年提出的一个使用模拟信号的电脑显示标准;
VGA最早指的是显示器640X480这种显示模式
其接口采用VGA标准输出数据的专用接口,共有15针,分成3排,每排5个孔,是显卡上应用最为广泛的接口类型,绝大多数显卡都带有此种接口。它传输红、绿、蓝模拟信号以及同步信号(水平和垂直信号);
VGA是模拟信号,只能传输视频信号。

    2、对应的接口定义
在这里插入图片描述

    3、vga的优点

分辨率高、显示速率快、颜色丰富;
成本低、结构简单、应用灵活;
显示输出 RGB 三原色信号,RGB 色彩模式是工业界的一种颜色标准。它是通过对红(R)、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色。

2、vga彩条显示
2.1、原理分析

    1、原理图
在这里插入图片描述

    2、常见vga分辨率表格
在这里插入图片描述
    3、以分辨率为640x480为例
    ①分辨率:640X480
    ②640:每一行有640个像素点;480:每一帧有480行
    ③@60:VGA显示器刷新频率
    ④时钟:行扫描总时间x场扫描总时间x刷新频率
80052560=25200000≈25.2Mhz
    ⑤总扫描时间=同步+显示后沿+有效数据+显示前沿
    因此使用时vga的时钟需要系统时钟进行分频处理
    4、工作模式:
行时序和场时序都需要同步脉冲(Sync a)、显示后沿(Back porch b)、显示时序段(Display interval c)和显示前沿(Front porch d)四部分。VGA工业标准显示模式要求:行同步,场同步都为负极性,即同步脉冲要求是负脉冲。
在这里插入图片描述

2.2、代码实现

    1、vga信号控制模块

`define vga_640_480

`include	"defin.v"

module vga_control(
	input	clk_50,                      //vga时钟
	input rst_n,                         //复位信号
	input [23:0] data_disp,              //
	
	output reg [10:0] h_addr,            //数据有效显示区域行地址
	output reg [10:0] v_addr,            //数据有效显示区域场地址
	output reg hsync,                    //行同步信号
	output reg vsync,                    //场同步信号
	output reg [7:0] vga_r,
	output reg [7:0] vga_g,
	output reg [7:0] vga_b,
	output reg vga_blk,
	output wire vga_clk
	);

parameter   H_SYNC_START = 1,                        // 行同步开始
            H_SYNC_STOP  = `H_Sync_Time,             // 行同步结束
            H_DATA_START = `H_Sync_Time + `H_Back_Porch + `H_Left_Borde,
            H_DATA_STOP  = `H_Sync_Time + `H_Back_Porch + `H_Left_Borde + `H_Data_Time,

            V_SYNC_START = 1,                        // 场同步开始
            V_SYNC_STOP  = `V_Sync_Time,             // 场同步结束
            V_DATA_START = `V_Sync_Time + `V_Back_Porch + `V_Top_Borde, 
            V_DATA_STOP  = `V_Sync_Time + `V_Back_Porch + `V_Top_Borde + `V_Data_Time;

reg [11:0] cnt_h;                    //行地址计数寄存器
wire add_h;
wire end_h;

wire cnt_h_x;
wire cnt_v_x;

reg [11:0] cnt_v;                    //场地址计数寄存器
wire add_v;
wire end_v;

wire clk_50M;
wire clk_25M;

//例化时钟分频模块
pll	            pll_inst (
    .areset     (~rst_n ),
	.inclk0     (clk_50 ),
	.c0         (clk_50M),
	.c1         (clk_25M)
	);

assign vga_clk = clk_25M;

//- - - - - - - - - - - - - - - - - - - - - - - - - - - 
//- - 彩条显示部分
//- - - - - - - - - - - - - - - - - - - - - - - - - - - 
//行地址计数
always@ (posedge vga_clk or negedge rst_n)
begin
	if(!rst_n)
		cnt_h <= 12'd0;
	else if(add_h)
	begin
		if(end_h)
			cnt_h <= 12'd0;
		else 
			cnt_h <= cnt_h + 1'b1;
	end
	else 
		cnt_h <= 12'd0;
end
assign add_h = 1'b1;
assign end_h = add_h && cnt_h == `H_Total_Time - 1;

//场地址计数
always@ (posedge vga_clk or negedge rst_n)
begin
	if(!rst_n)
		cnt_v <= 12'd0;
	else if(add_v)
	begin
		if(end_v)
			cnt_v <= 12'd0;
		else 
			cnt_v <= cnt_v + 1'b1;
	end
	else 
		cnt_v <= cnt_v;
end
assign add_v = end_h;
assign end_v = add_v && cnt_v == `V_Total_Time - 1;
	
//行场同步信号
always@ (posedge vga_clk or negedge rst_n)
begin
	if(!rst_n)
		hsync <= 1'b1;
	else if(cnt_h == H_SYNC_START - 1)
		hsync <= 1'b0;
	else if(cnt_h == H_SYNC_STOP - 1)
		hsync <= 1'b1;
	else
		hsync <= hsync;
end

//行场同步信号
always@ (posedge vga_clk or negedge rst_n)
begin
	if(!rst_n)
		vsync <= 1'b1;
	else if(cnt_v == V_SYNC_START - 1)
		vsync <= 1'b0;
	else if(cnt_v == V_SYNC_STOP - 1)
		vsync <= 1'b1;
	else
		vsync <= vsync;
end

//数据有效显示区域
always@ (posedge vga_clk or negedge rst_n)
begin
	if(!rst_n)
		h_addr <= 11'd0;
	else if(cnt_h >= H_DATA_STOP)
		h_addr <= 11'd0;
	else if(cnt_h >= H_DATA_START)
		h_addr <= cnt_h - H_DATA_START;
	else
		h_addr <= 11'd0;
end

//数据有效显示区域
always@ (posedge vga_clk or negedge rst_n)
begin
	if(!rst_n)
		v_addr <= 11'd0;
	else if(cnt_v >= V_DATA_STOP)
		v_addr <= 11'd0;
	else if(cnt_v >= V_DATA_START)
		v_addr <= cnt_v - V_DATA_START;
	else
		v_addr <= 11'd0;
end
	
//数据显示
always@ (posedge vga_clk or negedge rst_n)
begin
	if(!rst_n)
	begin
		vga_r <= 8'd0;
		vga_g <= 8'd0;
		vga_b <= 8'd0;
		vga_blk <= 1'b0;
	end
	else if(cnt_h_x && cnt_v_x)
	begin
		vga_r <= data_disp[23:16];
		vga_g <= data_disp[15:8];
		vga_b <= data_disp[7:0];
		vga_blk <= 1'b1;
	end
	else
	begin
		vga_r <= 8'd0;
		vga_g <= 8'd0;
		vga_b <= 8'd0;
		vga_blk <= 1'b0;
	end
end
assign cnt_h_x = cnt_h >= H_DATA_START && cnt_h <= H_DATA_STOP;
assign cnt_v_x = cnt_v >= V_DATA_START && cnt_v <= V_DATA_STOP;
	
endmodule

    2、数据生成模块

module data_gen(
	input	vga_clk,                    //vga时钟:25MHz
	input rst_n,                        //复位信号
	input  [10:0] h_addr,               //数据有效显示区域行地址
	input  [10:0] v_addr,               //数据有效显示区域场地址)
	
	output reg [23:0] data_disp
	);
	
parameter    BLACK = 24'h000000,
			 RED = 24'hFF0000,
			 GREEN = 24'h00FF00,
			 BLUE = 24'h0000FF,
			 YELLOW = 24'hFFFF00,
			 SKY_BLUE = 24'h00FFFF,
			 PURPLE = 24'hFF00FF,
			 GRAY = 24'hC0C0C0,
			 WHITE = 24'hFFFFFF;
	
always@ (posedge vga_clk or negedge rst_n)
begin 
	if(!rst_n)
		data_disp <= BLACK;
	else
	begin
		case (h_addr)
			0:   data_disp <= WHITE;
			80:  data_disp <= RED;
			160: data_disp <= GREEN;
			240: data_disp <= BLUE;
			320: data_disp <= YELLOW;
			400: data_disp <= SKY_BLUE;
			480: data_disp <= PURPLE;
			560: data_disp <= GRAY;
			default: data_disp <= data_disp;
		endcase
	end
end
endmodule
	
2.3、测试结果
3、实现字符串的显示
3.1、生成点阵字模

    1、使用PCtolcd2002软件生成自己想要的字模,设置字长和字宽为64x64
在这里插入图片描述

    2,将生成的字模另存为bmp图像文件
在这里插入图片描述

    3.再使用PCtolcd2002软件打开bmp文件,然后再设置里做如下设置
在这里插入图片描述

    4、确定之后生成字模保存为txt文件就得到了汉字点阵
在这里插入图片描述

3.2、代码编写(文尾会附上整体代码)

    1、信号定义

localparam  h_START = 150,             //行显示开始
            h_STOP = 470,              //行显示结束
			v_START = 200,             //场显示开始
            v_STOP = 265;              //场显示结束

reg [ 319:0 ] char_line[ 63:0 ];       //320(字长)*64(字宽)

reg  [1:0]  nstate;                    //次态
reg  [1:0]  cstate;                    //现态

wire char_value;                       //字符串有效显示区域

    2、输出显示字符串

CHARACTER:
		begin
			if (char_value) 
				data_disp = char_line[v_addr-v_START][h_STOP - h_addr] != 0 ? BLUE:BLACK; //坐标点不为0则输出BLUE
			else
				data_disp <= BLACK;
		end
		
//在数据显示有效区域内
assign char_value = cstate == CHARACTER && h_addr > h_START && h_addr < h_STOP && v_addr > v_START  && v_addr < v_STOP;

    3、汉字点阵

//初始化显示文字
always@( posedge vga_clk ) 
begin
		char_line[ 0 ]  = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
		char_line[ 1 ]  = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
		char_line[ 2 ]  = 320'h00000000000000000000000000000000000040000000000000000000000000000000004000000000;
		char_line[ 3 ]  = 320'h00000002000000000000000000000000000070000000000000000000000000000000007000000000;
		char_line[ 4 ]  = 320'h00000003C0040000000000040000000000007C000000000000000000000020000000007C00000000;
		char_line[ 5 ]  = 320'h00018003E00E0000000000070000000000007C000000000000000000000070000000007C06000000;
		char_line[ 6 ]  = 320'h0000E003C00F000000000007E00000000000780000000600001FFFFFFFFFF8000000007803C00000;
		char_line[ 7 ]  = 320'h00007803C01F800000000007C00000000000780000000F00000FFFFFFFFFFC000000007801F00000;
		char_line[ 8 ]  = 320'h00003C03C01E000000000007800000000000780000000FC0000400000001FE000000007800FC0000;
		char_line[ 9 ]  = 320'h00001E03C038000000000007800000000000780010000F80000000000003FF0000000078007E0000;
		char_line[ 10 ] = 320'h00001F03C070000000000007800000000000780018000F00000000000007E00000000078003F0000;
		char_line[ 11 ] = 320'h00000F03C060000000000007800000000000780C08001E0000000000001F800000000078001F0000;
		char_line[ 12 ] = 320'h00000E03C0C0000000000007800000000000781E04001E0000000000003E000000000078000F0000;
		char_line[ 13 ] = 320'h00600E03C1800180000000078000000003FFFFFF06001E0000000000007C000000000078000F0000;
		char_line[ 14 ] = 320'h00600403C30003C0000000078000000001FFFFFF83003C000000000000F000000000007800070100;
		char_line[ 15 ] = 320'h007FFFFFFFFFFFE000000007800000000080780003003C000000000001C000000000007800070300;
		char_line[ 16 ] = 320'h007FFFFFFFFFFFF000000007800000000000780001803C0000000000038000000000007800000780;
		char_line[ 17 ] = 320'h00E00000000003E000000007800000000000780001C07800000000000E0000000000007800000FC0;
		char_line[ 18 ] = 320'h00E000000000078000000007800000000000780000E07800000000031C0000001FFFFFFFFFFFFFE0;
		char_line[ 19 ] = 320'h01E00000000007000020000780000000000078000060700000000003B80000000FFFFFFFFFFFFFF0;
		char_line[ 20 ] = 320'h01C00000000006000038000780003000000078000070F00000000003E0000000040000700E000000;
		char_line[ 21 ] = 320'h03C0000000100C00003E000780003E00000078000038F00000000003F0000000000000700E000000;
		char_line[ 22 ] = 320'h07C0180000380800003E000780003E00000078000038E00000000003F8000000000000700E000000;
		char_line[ 23 ] = 320'h0F801FFFFFFC1000003C000780003C0000007801801DE00000000003E0000000000000700E000000;
		char_line[ 24 ] = 320'h07001FFFFFFC0000003C000780003C0000007803C01FC00000000003C0000100000000700E002000;
		char_line[ 25 ] = 320'h00001C0000380000003C000780003C001FFFFFFFE00FC00000000003C0000380000000F00E003000;
		char_line[ 26 ] = 320'h00001C0000380000003C000780003C000FFFFFFFF007C00000000003C00007C0000000F00E007800;
		char_line[ 27 ] = 320'h00001C0000380000003C000780003C00040038000007800000000003C0000FE0000000F00E007C00;
		char_line[ 28 ] = 320'h00001C0000380000003C000780003C0000003C00000780001FFFFFFFFFFFFFF0000000F00E00FE00;
		char_line[ 29 ] = 320'h00001C0000380000003C000780003C0000003E00000FC0000FFFFFFFFFFFFFF8000000E00E01F800;
		char_line[ 30 ] = 320'h00001C0000380000003C000780003C0000003C00000FE00004000003C0000000000000E00E01F000;
		char_line[ 31 ] = 320'h00001FFFFFF80000003C000780003C0000003C00001EF00000000003C0000000000001E00E03E000;
		char_line[ 32 ] = 320'h00001FFFFFF80000003C000780003C0000603C00001CF00000000003C0000000000001E00E07C000;
		char_line[ 33 ] = 320'h00001C03C0380000003C000780003C0000783C00003C780000000003C0000000000001E00E078000;
		char_line[ 34 ] = 320'h00001C03C0300000003C000780003C00007E3C0000387C0000000003C0000000000001C00E0F0000;
		char_line[ 35 ] = 320'h00001803C0000000003C000780003C00007C3C0200783C0000000003C0000000000003C00E1F0000;
		char_line[ 36 ] = 320'h00000003C0000000003C000780003C0000783C0700703E0000000003C0000000000003C00E3E0000;
		char_line[ 37 ] = 320'h00000003C0004000003C000780003C0000783FFF80E01E0000000003C0000000000003800E7C0000;
		char_line[ 38 ] = 320'h00040003C000E000003C000780003C0000783FFFC1C01F0000000003C0000000000007800E780000;
		char_line[ 39 ] = 320'h0007FFFFFFFFF000003C000780003C0000783C0001C00F0000000003C0000000000007800EF00000;
		char_line[ 40 ] = 320'h0007FFFFFFFFF800003C000780003C0000703C0003800F8000000003C0000000000007000FE00000;
		char_line[ 41 ] = 320'h00070003C000F000003C000780003C0000703C0007000F8000000003C000000000000F000FC00000;
		char_line[ 42 ] = 320'h00070003C000E000003C000780003C0000F03C000E00078000000003C000000000000E000F800000;
		char_line[ 43 ] = 320'h00070003C000E000003C000780003C0000F03C000C00078000000003C000000000001E001F000000;
		char_line[ 44 ] = 320'h00070003C000E000003C000780003C0000F03C001800078000000003C000000000001C003E0000C0;
		char_line[ 45 ] = 320'h00070003C000E000003C000780003C0000F83C003000038000000003C000000000003C007E0000C0;
		char_line[ 46 ] = 320'h00070003C000E000003C000780003C0000EC3C006000038000000003C000000000007800FE0000C0;
		char_line[ 47 ] = 320'h00070003C000E000003C000780003C0001E63C00C000010000000003C000000000007001EE0000C0;
		char_line[ 48 ] = 320'h00070003C000E000003C000780003C0001C3BC018000000000000003C00000000000F0038E0000C0;
		char_line[ 49 ] = 320'h00070003C000E000003C000780003C0001C1FC030000000000000003C00000000001E00F0E0000C0;
		char_line[ 50 ] = 320'h00070003C000E000003C000780003C000380FC000000000000000003C00000000003C01C0E0000C0;
		char_line[ 51 ] = 320'h00070003C080E000003C000780003C0003807C000000000000000001C0000000000380780E0000C0;
		char_line[ 52 ] = 320'h00070003C0FFE000007FFFFFFFFFFC0003003E000000000000000003C0000000000700E00E0000C0;
		char_line[ 53 ] = 320'h00070003C03FE000007FFFFFFFFFFC0007001FC00000000000000003C0000000000E03800F0000E0;
		char_line[ 54 ] = 320'h00070003C00FE000003C000000003C0006000FFC0000007800001FFFC0000000001C00000F0001F0;
		char_line[ 55 ] = 320'h00070003C007C0000018000000003C000E0003FFFFFFFFF0000007FFC0000000003800000F8003F8;
		char_line[ 56 ] = 320'h00060003C00380000000000000003C000C0000FFFFFFFF80000000FFC0000000007000000FFFFFF0;
		char_line[ 57 ] = 320'h00000003C002000000000000000038001800001FFFFFFF000000003F8000000000C0000007FFFFE0;
		char_line[ 58 ] = 320'h00000003C0000000000000000000200010000000FFFFFF000000001F000000000180000001FFFF80;
		char_line[ 59 ] = 320'h00000003C0000000000000000000000020000000000000000000001E000000000700000000000000;
		char_line[ 60 ] = 320'h00000003000000000000000000000000000000000000000000000008000000000C00000000000000;
		char_line[ 61 ] = 320'h00000000000000000000000000000000000000000000000000000000000000001000000000000000;
		char_line[ 62 ] = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
		char_line[ 63 ] = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
    
end
3.3、测试结果

    1、
在这里插入图片描述

4、图片显示
4.1、ip核的调用

    1、采用一张200140的图片进行显示。
    2、转换处hex文件,大小为200
140*24bit
在这里插入图片描述

在这里插入图片描述

    3、打开quartus,找到ROM
在这里插入图片描述

    5、设置位宽度为16位,大小为图片大小200×140 = 28000
在这里插入图片描述

    6、取消端口q的输出
在这里插入图片描述

    7、选择图片生成的hex文件
在这里插入图片描述

    8、生成.v文件
在这里插入图片描述

4.2、代码部分(文尾会附上整体代码)

    1、信号定义

	localparam  width = 200,                //图片宽度
   	localparam  higth = 140;                //图片高度
    wire  pic_h_value;                  //图片行地址有效
	wire  pic_v_value;                  //图片场地址有效
	wire  pic_value;                    //在图片的有效区域内
	wire  pic_rom_clear;                //rom存储器地址清零
	wire  [23:0]  rom_data;             //图片数据   
	reg  [14:0]  rom_addr;

    2、调用ROMip核

//例化rom ip核
	rom	        rom1_inst (
	.address    ( rom_addr ),
	.clock      ( vga_clk  ),

	.q          ( rom_data )
	);

    3、显示图片

PICTURE:
			begin
				if( pic_value)
					data_disp <= rom_data;
				else 
					data_disp <= WHITE;
			end

//图片显示
	always @(posedge vga_clk or negedge rst_n) 
	begin
	    if (!rst_n)
		   rom_addr <= 0;
		else if(cstate != PICTURE)begin
			rom_addr<=0;
		end
	    else if (pic_rom_clear)
		   rom_addr <= 0;
	    else if (pic_value)
		   rom_addr <= rom_addr + 1'b1;
		else
		   rom_addr <= rom_addr;
	end
	assign  pic_h_value = h_addr > (640 - width)/2 && h_addr < (640 - width)/2 + width + 1;
	assign  pic_v_value = v_addr > (480 - higth)/2 && v_addr < (480 - higth)/2 + higth + 1;
	assign  pic_value = cstate == PICTURE && pic_h_value && pic_v_value;
	// assign pic_h_value = h_addr >= 100 && h_addr < 228;
	// assign pic_v_value =  v_addr >= 100 && v_addr < 178;
	assign  pic_rom_clear = rom_addr == width * higth - 1;

4.3、测试结果

    
在这里插入图片描述

4.4、整体代码

    1、vga控制模块

`define vga_640_480

`include	"defin.v"

module vga_control(
	input	clk_50,                        //vga时钟
	input rst_n,                         //复位信号
	input [23:0] data_disp,              //
	
	output reg [10:0] h_addr,            //数据有效显示区域行地址
	output reg [10:0] v_addr,            //数据有效显示区域场地址
	output reg hsync,                    //行同步信号
	output reg vsync,                    //场同步信号
	output reg [7:0] vga_r,
	output reg [7:0] vga_g,
	output reg [7:0] vga_b,
	output reg vga_blk,
	output wire vga_clk
	);

parameter   H_SYNC_START = 1,                        // 行同步开始
            H_SYNC_STOP  = `H_Sync_Time,             // 行同步结束
            H_DATA_START = `H_Sync_Time + `H_Back_Porch + `H_Left_Borde,
            H_DATA_STOP  = `H_Sync_Time + `H_Back_Porch + `H_Left_Borde + `H_Data_Time,

            V_SYNC_START = 1,                        // 场同步开始
            V_SYNC_STOP  = `V_Sync_Time,             // 场同步结束
            V_DATA_START = `V_Sync_Time + `V_Back_Porch + `V_Top_Borde, 
            V_DATA_STOP  = `V_Sync_Time + `V_Back_Porch + `V_Top_Borde + `V_Data_Time;

//信号定义
	reg [11:0] cnt_h;                    //行地址计数寄存器
	wire add_h;
	wire end_h;

	wire cnt_h_x;
	wire cnt_v_x;

	reg [11:0] cnt_v;                    //场地址计数寄存器
	wire add_v;
	wire end_v;

	wire clk_50M;
	wire clk_25M;

//例化时钟分频模块
	pll	            pll_inst (
	    .areset     (~rst_n ),
		.inclk0     (clk_50 ),
		.c0         (clk_50M),
		.c1         (clk_25M)
		);

	assign vga_clk = clk_25M;

//行地址计数
	always@ (posedge vga_clk or negedge rst_n)
	begin
		if(!rst_n)
			cnt_h <= 12'd0;
		else if(add_h)
		begin
			if(end_h)
				cnt_h <= 12'd0;
			else 
				cnt_h <= cnt_h + 1'b1;
		end
		else 
			cnt_h <= 12'd0;
	end
	assign add_h = 1'b1;
	assign end_h = add_h && cnt_h == `H_Total_Time - 1;

//场地址计数
	always@ (posedge vga_clk or negedge rst_n)
	begin
		if(!rst_n)
			cnt_v <= 12'd0;
		else if(add_v)
		begin
			if(end_v)
				cnt_v <= 12'd0;
			else 
				cnt_v <= cnt_v + 1'b1;
		end
		else 
			cnt_v <= cnt_v;
	end
	assign add_v = end_h;
	assign end_v = add_v && cnt_v == `V_Total_Time - 1;

//行场同步信号
	always@ (posedge vga_clk or negedge rst_n)
	begin
		if(!rst_n)
			hsync <= 1'b1;
		else if(cnt_h == H_SYNC_START - 1)
			hsync <= 1'b0;
		else if(cnt_h == H_SYNC_STOP - 1)
			hsync <= 1'b1;
		else
			hsync <= hsync;
	end

//行场同步信号
	always@ (posedge vga_clk or negedge rst_n)
	begin
		if(!rst_n)
			vsync <= 1'b1;
		else if(cnt_v == V_SYNC_START - 1)
			vsync <= 1'b0;
		else if(cnt_v == V_SYNC_STOP - 1)
			vsync <= 1'b1;
		else
			vsync <= vsync;
	end

//数据有效显示区域
	always@ (posedge vga_clk or negedge rst_n)
	begin
		if(!rst_n)
			h_addr <= 11'd0;
		else if(cnt_h >= H_DATA_STOP)
			h_addr <= 11'd0;
		else if(cnt_h >= H_DATA_START)
			h_addr <= cnt_h - H_DATA_START;
		else
			h_addr <= 11'd0;
	end

//数据有效显示区域
	always@ (posedge vga_clk or negedge rst_n)
	begin
		if(!rst_n)
			v_addr <= 11'd0;
		else if(cnt_v >= V_DATA_STOP)
			v_addr <= 11'd0;
		else if(cnt_v >= V_DATA_START)
			v_addr <= cnt_v - V_DATA_START;
		else
			v_addr <= 11'd0;
	end

//数据显示
	always@ (posedge vga_clk or negedge rst_n)
	begin
		if(!rst_n)
		begin
			vga_r <= 8'd0;
			vga_g <= 8'd0;
			vga_b <= 8'd0;
			vga_blk <= 1'b0;
		end
		else if(cnt_h_x && cnt_v_x)
		begin
			vga_r <= data_disp[23:16];
			vga_g <= data_disp[15:8];
			vga_b <= data_disp[7:0];
			vga_blk <= 1'b1;
		end
		else
		begin
			vga_r <= 8'd0;
			vga_g <= 8'd0;
			vga_b <= 8'd0;
			vga_blk <= 1'b0;
		end
	end
	assign cnt_h_x = cnt_h >= H_DATA_START && cnt_h <= H_DATA_STOP;
	assign cnt_v_x = cnt_v >= V_DATA_START && cnt_v <= V_DATA_STOP;

endmodule

    2、数据生成模块

module data_gen(
	input	vga_clk,                    //vga时钟:25MHz
	input rst_n,                        //复位信号
	input  [2:0] key, 
	input  [10:0] h_addr,               //数据有效显示区域行地址
	input  [10:0] v_addr,               //数据有效显示区域场地址)
	
	output reg [23:0] data_disp
	);
	
parameter   BLACK = 24'h000000,
			RED = 24'hFF0000,
			GREEN = 24'h00FF00,
			BLUE = 24'h0000FF,
			YELLOW = 24'hFFFF00,
			SKY_BLUE = 24'h00FFFF,
			PURPLE = 24'hFF00FF,
			GRAY = 24'hC0C0C0,
			WHITE = 24'hFFFFFF;

parameter   COLOUR_BAR = 2'b00,       	//显示彩条状态
			CHARACTER = 2'b01,        	//显示字符状态
			PICTURE = 2'b10;          	//显示图片状态

localparam  h_START = 150,             	//行显示开始
            h_STOP = 470,              	//行显示结束
		    v_START = 200,             	//场显示开始
            v_STOP = 265,              	//场显示结束
			width = 200,                //图片宽度
   			higth = 140;                //图片高度


//信号定义
	reg [ 319:0 ] char_line[ 63:0 ];    //320(字长)*64(字宽)

	reg  [1:0]  nstate;                 //次态
	reg  [1:0]  cstate;                 //现态

	wire char_value;                    //字符串有效显示区域1

	wire  pic_h_value;                  //图片行地址有效
	wire  pic_v_value;                  //图片场地址有效
	wire  pic_value;                    //在图片的有效区域内
	wire  pic_rom_clear;                //rom存储器地址清零
	wire  [23:0]  rom_data;             //图片数据   
	reg  [14:0]  rom_addr;
	


//状态判断
	always @(posedge vga_clk or negedge rst_n) 
	begin
		if(!rst_n)
			cstate <= COLOUR_BAR;
		else 
			cstate <= nstate;
	end

//状态转换
	always @(posedge vga_clk or negedge rst_n) 
	begin
		if(!rst_n)
			nstate <= COLOUR_BAR;          //默认为显示彩条状态
		else if(key[0])
			nstate <= CHARACTER;           //按下key[0]时跳转到显示字符状态
		else if(key[1])
			nstate <= PICTURE;             //按下key[1]时跳转到显示图片状态
		else if(key[2])
			nstate <= COLOUR_BAR;          //按下key[2]时跳回到显示彩条状态
		else
			nstate <= nstate;
	end

	always @(*) 
	begin
		case (cstate)
			COLOUR_BAR:
			begin
				if (h_addr > 0 && h_addr <= 80) 
					data_disp <= WHITE;
				else if (h_addr > 80 && h_addr <= 160) 
				 	data_disp <= RED;
				else if (h_addr > 160 && h_addr <= 240) 
				 	data_disp <= GREEN;
				else if (h_addr > 240 && h_addr <= 320) 
				 	data_disp <= BLUE;
				else if (h_addr > 320 && h_addr <= 400) 
				 	data_disp <= SKY_BLUE;
				else if (h_addr > 400 && h_addr <= 480) 
				 	data_disp <= PURPLE;
				else if (h_addr > 480 && h_addr <= 560) 
					data_disp <= GRAY;
				else
					data_disp <= WHITE;
			end
			CHARACTER:
			begin
				if (char_value) 
					data_disp = char_line[v_addr-v_START][h_STOP - h_addr] != 0 ? BLUE:BLACK;  //坐标点不为0则输出BLUE
				else
					data_disp <= BLACK;
			end
			PICTURE:
			begin
				if( pic_value)
					data_disp <= rom_data;
				else 
					data_disp <= WHITE;
			end
			default :
				data_disp <= BLACK;
		endcase
	end

	assign  char_value = cstate == CHARACTER && h_addr > h_START && h_addr < h_STOP && v_addr > v_START  && v_addr < v_STOP;

	
//图片显示
	always @(posedge vga_clk or negedge rst_n) 
	begin
	    if (!rst_n)
		   rom_addr <= 0;
		else if(cstate != PICTURE)begin
			rom_addr<=0;
		end
	    else if (pic_rom_clear)
		   rom_addr <= 0;
	    else if (pic_value)
		   rom_addr <= rom_addr + 1'b1;
		else
		   rom_addr <= rom_addr;
	end
	assign  pic_h_value = h_addr > (640 - width)/2 && h_addr < (640 - width)/2 + width + 1;
	assign  pic_v_value = v_addr > (480 - higth)/2 && v_addr < (480 - higth)/2 + higth + 1;
	assign  pic_value = cstate == PICTURE && pic_h_value && pic_v_value;
	// assign pic_h_value = h_addr >= 100 && h_addr < 228;
	// assign pic_v_value =  v_addr >= 100 && v_addr < 178;
	assign  pic_rom_clear = rom_addr == width * higth - 1;

//初始化显示文字
	always@( posedge vga_clk ) 
	begin
		char_line[ 0 ]  = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
		char_line[ 1 ]  = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
		char_line[ 2 ]  = 320'h00000000000000000000000000000000000040000000000000000000000000000000004000000000;
		char_line[ 3 ]  = 320'h00000002000000000000000000000000000070000000000000000000000000000000007000000000;
		char_line[ 4 ]  = 320'h00000003C0040000000000040000000000007C000000000000000000000020000000007C00000000;
		char_line[ 5 ]  = 320'h00018003E00E0000000000070000000000007C000000000000000000000070000000007C06000000;
		char_line[ 6 ]  = 320'h0000E003C00F000000000007E00000000000780000000600001FFFFFFFFFF8000000007803C00000;
		char_line[ 7 ]  = 320'h00007803C01F800000000007C00000000000780000000F00000FFFFFFFFFFC000000007801F00000;
		char_line[ 8 ]  = 320'h00003C03C01E000000000007800000000000780000000FC0000400000001FE000000007800FC0000;
		char_line[ 9 ]  = 320'h00001E03C038000000000007800000000000780010000F80000000000003FF0000000078007E0000;
		char_line[ 10 ] = 320'h00001F03C070000000000007800000000000780018000F00000000000007E00000000078003F0000;
		char_line[ 11 ] = 320'h00000F03C060000000000007800000000000780C08001E0000000000001F800000000078001F0000;
		char_line[ 12 ] = 320'h00000E03C0C0000000000007800000000000781E04001E0000000000003E000000000078000F0000;
		char_line[ 13 ] = 320'h00600E03C1800180000000078000000003FFFFFF06001E0000000000007C000000000078000F0000;
		char_line[ 14 ] = 320'h00600403C30003C0000000078000000001FFFFFF83003C000000000000F000000000007800070100;
		char_line[ 15 ] = 320'h007FFFFFFFFFFFE000000007800000000080780003003C000000000001C000000000007800070300;
		char_line[ 16 ] = 320'h007FFFFFFFFFFFF000000007800000000000780001803C0000000000038000000000007800000780;
		char_line[ 17 ] = 320'h00E00000000003E000000007800000000000780001C07800000000000E0000000000007800000FC0;
		char_line[ 18 ] = 320'h00E000000000078000000007800000000000780000E07800000000031C0000001FFFFFFFFFFFFFE0;
		char_line[ 19 ] = 320'h01E00000000007000020000780000000000078000060700000000003B80000000FFFFFFFFFFFFFF0;
		char_line[ 20 ] = 320'h01C00000000006000038000780003000000078000070F00000000003E0000000040000700E000000;
		char_line[ 21 ] = 320'h03C0000000100C00003E000780003E00000078000038F00000000003F0000000000000700E000000;
		char_line[ 22 ] = 320'h07C0180000380800003E000780003E00000078000038E00000000003F8000000000000700E000000;
		char_line[ 23 ] = 320'h0F801FFFFFFC1000003C000780003C0000007801801DE00000000003E0000000000000700E000000;
		char_line[ 24 ] = 320'h07001FFFFFFC0000003C000780003C0000007803C01FC00000000003C0000100000000700E002000;
		char_line[ 25 ] = 320'h00001C0000380000003C000780003C001FFFFFFFE00FC00000000003C0000380000000F00E003000;
		char_line[ 26 ] = 320'h00001C0000380000003C000780003C000FFFFFFFF007C00000000003C00007C0000000F00E007800;
		char_line[ 27 ] = 320'h00001C0000380000003C000780003C00040038000007800000000003C0000FE0000000F00E007C00;
		char_line[ 28 ] = 320'h00001C0000380000003C000780003C0000003C00000780001FFFFFFFFFFFFFF0000000F00E00FE00;
		char_line[ 29 ] = 320'h00001C0000380000003C000780003C0000003E00000FC0000FFFFFFFFFFFFFF8000000E00E01F800;
		char_line[ 30 ] = 320'h00001C0000380000003C000780003C0000003C00000FE00004000003C0000000000000E00E01F000;
		char_line[ 31 ] = 320'h00001FFFFFF80000003C000780003C0000003C00001EF00000000003C0000000000001E00E03E000;
		char_line[ 32 ] = 320'h00001FFFFFF80000003C000780003C0000603C00001CF00000000003C0000000000001E00E07C000;
		char_line[ 33 ] = 320'h00001C03C0380000003C000780003C0000783C00003C780000000003C0000000000001E00E078000;
		char_line[ 34 ] = 320'h00001C03C0300000003C000780003C00007E3C0000387C0000000003C0000000000001C00E0F0000;
		char_line[ 35 ] = 320'h00001803C0000000003C000780003C00007C3C0200783C0000000003C0000000000003C00E1F0000;
		char_line[ 36 ] = 320'h00000003C0000000003C000780003C0000783C0700703E0000000003C0000000000003C00E3E0000;
		char_line[ 37 ] = 320'h00000003C0004000003C000780003C0000783FFF80E01E0000000003C0000000000003800E7C0000;
		char_line[ 38 ] = 320'h00040003C000E000003C000780003C0000783FFFC1C01F0000000003C0000000000007800E780000;
		char_line[ 39 ] = 320'h0007FFFFFFFFF000003C000780003C0000783C0001C00F0000000003C0000000000007800EF00000;
		char_line[ 40 ] = 320'h0007FFFFFFFFF800003C000780003C0000703C0003800F8000000003C0000000000007000FE00000;
		char_line[ 41 ] = 320'h00070003C000F000003C000780003C0000703C0007000F8000000003C000000000000F000FC00000;
		char_line[ 42 ] = 320'h00070003C000E000003C000780003C0000F03C000E00078000000003C000000000000E000F800000;
		char_line[ 43 ] = 320'h00070003C000E000003C000780003C0000F03C000C00078000000003C000000000001E001F000000;
		char_line[ 44 ] = 320'h00070003C000E000003C000780003C0000F03C001800078000000003C000000000001C003E0000C0;
		char_line[ 45 ] = 320'h00070003C000E000003C000780003C0000F83C003000038000000003C000000000003C007E0000C0;
		char_line[ 46 ] = 320'h00070003C000E000003C000780003C0000EC3C006000038000000003C000000000007800FE0000C0;
		char_line[ 47 ] = 320'h00070003C000E000003C000780003C0001E63C00C000010000000003C000000000007001EE0000C0;
		char_line[ 48 ] = 320'h00070003C000E000003C000780003C0001C3BC018000000000000003C00000000000F0038E0000C0;
		char_line[ 49 ] = 320'h00070003C000E000003C000780003C0001C1FC030000000000000003C00000000001E00F0E0000C0;
		char_line[ 50 ] = 320'h00070003C000E000003C000780003C000380FC000000000000000003C00000000003C01C0E0000C0;
		char_line[ 51 ] = 320'h00070003C080E000003C000780003C0003807C000000000000000001C0000000000380780E0000C0;
		char_line[ 52 ] = 320'h00070003C0FFE000007FFFFFFFFFFC0003003E000000000000000003C0000000000700E00E0000C0;
		char_line[ 53 ] = 320'h00070003C03FE000007FFFFFFFFFFC0007001FC00000000000000003C0000000000E03800F0000E0;
		char_line[ 54 ] = 320'h00070003C00FE000003C000000003C0006000FFC0000007800001FFFC0000000001C00000F0001F0;
		char_line[ 55 ] = 320'h00070003C007C0000018000000003C000E0003FFFFFFFFF0000007FFC0000000003800000F8003F8;
		char_line[ 56 ] = 320'h00060003C00380000000000000003C000C0000FFFFFFFF80000000FFC0000000007000000FFFFFF0;
		char_line[ 57 ] = 320'h00000003C002000000000000000038001800001FFFFFFF000000003F8000000000C0000007FFFFE0;
		char_line[ 58 ] = 320'h00000003C0000000000000000000200010000000FFFFFF000000001F000000000180000001FFFF80;
		char_line[ 59 ] = 320'h00000003C0000000000000000000000020000000000000000000001E000000000700000000000000;
		char_line[ 60 ] = 320'h00000003000000000000000000000000000000000000000000000008000000000C00000000000000;
		char_line[ 61 ] = 320'h00000000000000000000000000000000000000000000000000000000000000001000000000000000;
		char_line[ 62 ] = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
		char_line[ 63 ] = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
    
	end
	
//例化rom ip核
	rom	        rom1_inst (
	.address    ( rom_addr ),
	.clock      ( vga_clk  ),

	.q          ( rom_data )
	);
	
endmodule

参考:
    https://blog.csdn.net/QWERTYzxw/article/details/121326584
    https://blog.csdn.net/qq_47281915/article/details/125134764?spm=1001.2014.3001.5501

  • 4
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值