VCG显示——汉字,数字,图像

详细的介绍资料:

【从零开始走进FPGA】 玩转VGA

http://www.cnblogs.com/spartan/archive/2011/08/16/2140546.html

【FPGA实验】基于DE2-115平台的VGA显示_vga接口实验 de2-115-CSDN博客

【FPGA】VGA显示文字、彩条、图片------基于DE2-115-CSDN博客

一.VCG原理

1.1 VCG引脚原理

1.2 VCG显示原理

VGA 显示器显示图像,并不是直接让图像在显示器上显示出来,而是采用扫描的方式,将构成图像的像素点,在行同步信号和场同步信号的同步下,按照从上到下、由左到右的顺序扫描到显示屏上。

详细介绍请参考:【FPGA】VGA显示文字、彩条、图片------基于DE2-115-CSDN博客

二.VCG显示自定义字符

2.1 点阵汉字生成

使用"PCtoLCD2002"生成汉字

2.2生成BMP文件

文件->保存为BMP格式,打开图片得到完整字符

2.3生成txt文件

保存字符,得到

三.VGA显示条纹

3.1实现流程

输出颜色竖条

// 状态输出逻辑,根据不同的状态输出不同的RGB数据
always @( * ) begin
    case ( states_current )
        //彩条
        states_1 : begin
            if ( addr_h == 0 ) begin
                rgb_data = black;
            end
            else if ( addr_h >0 && addr_h <81 ) begin
                rgb_data = red;
            end
            else if ( addr_h >80 && addr_h <161 ) begin
                rgb_data = orange;
                // rgb_data = red;
            end
            else if ( addr_h >160 && addr_h <241 ) begin
                rgb_data = yellow;
            end
            else if ( addr_h >240 && addr_h <321 ) begin
                rgb_data = green;
            end
            else if ( addr_h >320 && addr_h <401 ) begin
                rgb_data = blue;
            end
            else if ( addr_h >400 && addr_h <481 ) begin
                rgb_data = indigo;
            end
            else if ( addr_h >480 && addr_h <561 ) begin
                rgb_data = purple;
            end
            else if ( addr_h >560 && addr_h <641 ) begin
                rgb_data = white;
            end
            else begin
                rgb_data = black;
            end
            
        end

3.2实现效果

四.VGA输出一幅彩色图像

4.1bmp图片转hex文件

使用BMP2Mif 软件将bmp 格式图片转换为mif文件

转换后的.mif文件:

4.2引入ROM ip核

新建Quartus工程,产生ROM IP核,将生成的mif 文件保存在ROM中

双击选择ROM:1-PORT

更改设置,words大小设置要大于图片大小(50x49x24=58800< 65536),然后next

4.3代码实现

data_drive.v 文件里,从ikun_rom取出图片数据。

五.代码

5.1 时钟分频

分别使用640×480 60HZ和800×600 72HZ,对应时钟分别为25M和50M,需要使用PLL进行分频 时钟频率 = 行帧长 × 列帧长 * 刷新率

640 ×480 60HZ对应时钟频率= 800 ×525 × 60 = 25.2M

ip核 里面找到ALTPLL

基础时钟 选择50M

0默认输出50M 即可, c1分频到25M,如需其他时钟频率可以自己进行设置

5.2 vga驱动模块

vga.drive.v

// /*
module vga_dirve (
        input			wire						clk,            //系统时钟    
        input			wire						rst_n,          //复位
        input			wire		[ 15:0 ]		rgb_data,       //RGB--565,即pixel_data[15:11]控制R、pixel_data[10:5]控制G、pixel_data[4:0]控制B
        
        output			wire							vga_clk,    //vga时钟 25M

        output			reg							h_sync,     //行同步信号
        output			reg							v_sync,     //场同步信号

        output			reg		[ 11:0 ]				addr_h, //行地址
        output			reg		[ 11:0 ]				addr_v,  //列地址
        
        output			wire		[ 4:0 ]				rgb_r,  //红基色
        output			wire		[ 5:0 ]				rgb_g,  //绿基色
        output			wire		[ 4:0 ]				rgb_b  //蓝基色
);


// 定义VGA信号的参数,基于640x480 60Hz的VGA模式
// 640 * 480 60HZ
localparam	 H_FRONT = 16; // 行同步前沿信号周期长
localparam	 H_SYNC  = 96; // 行同步信号周期长
localparam	 H_BLACK = 48; // 行同步后沿信号周期长
localparam	 H_ACT   = 640; // 行显示周期长
localparam	 V_FRONT = 11; // 场同步前沿信号周期长
localparam	 V_SYNC  = 2; // 场同步信号周期长
localparam	 V_BLACK = 31; // 场同步后沿信号周期长
localparam	 V_ACT   = 480; // 场显示周期长

// 800 * 600 72HZ
// localparam	 H_FRONT = 40; // 行同步前沿信号周期长
// localparam	 H_SYNC  = 120; // 行同步信号周期长
// localparam	 H_BLACK = 88; // 行同步后沿信号周期长
// localparam	 H_ACT   = 800; // 行显示周期长
// localparam	 V_FRONT = 37; // 场同步前沿信号周期长
// localparam	 V_SYNC  = 6; // 场同步信号周期长
// localparam	 V_BLACK = 23; // 场同步后沿信号周期长
// localparam	 V_ACT   = 600; // 场显示周期长


// 计算总的行和场周期
localparam	H_TOTAL = H_FRONT + H_SYNC + H_BLACK + H_ACT; // 行周期 16+96+48+640 = 800
localparam	V_TOTAL = V_FRONT + V_SYNC + V_BLACK + V_ACT; // 列周期 11+2+6+31+480 = 512
reg			[ 11:0 ]			cnt_h			; // 行计数器 0-799
reg			[ 11:0 ]			cnt_v			; // 场计数器 0-524
reg			[ 15:0 ]			rgb			; // 对应显示颜色值

// 对应计数器开始、结束、计数信号
wire							flag_enable_cnt_h			;
wire							flag_clear_cnt_h			;
wire							flag_enable_cnt_v			;
wire							flag_clear_cnt_v			;
wire							flag_add_cnt_v  			;
wire							valid_area      			;


// 25M时钟 行周期*场周期*刷新率 = 800 * 525 * 60
wire							clk_25			;
// 50M时钟 1040 * 666 * 72
wire							clk_50			;
//PLL
pll	pll_inst (
	.areset ( ~rst_n ),
	.inclk0 ( clk ),
	.c0 ( clk_50 ), //50M
	.c1 ( clk_25 ) //25M
);

//根据不同分配率选择不同频率时钟
assign vga_clk = clk_25;


// 行计数
always @( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        cnt_h <= 0;
    end
    else if ( flag_enable_cnt_h ) begin
        if ( flag_clear_cnt_h ) begin
            cnt_h <= 0;
        end
        else begin
            cnt_h <= cnt_h + 1;
        end
    end
    else begin
        cnt_h <= 0;
    end
end
assign flag_enable_cnt_h = 1;
assign flag_clear_cnt_h  = cnt_h == H_TOTAL - 1;



// 行同步信号
always @( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        h_sync <= 1;
    end
    else if ( cnt_h == H_SYNC - 1 ) begin // 同步周期时为1
        h_sync <= 0;
    end
    else if ( flag_clear_cnt_h ) begin // 其余为0
        h_sync <= 1;
    end
    else begin
        h_sync <= h_sync;
    end
end


// 场计数
always @( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        cnt_v <= 0;
    end
    else if ( flag_enable_cnt_v ) begin
        if ( flag_clear_cnt_v ) begin
            cnt_v <= 0;
        end
        else if ( flag_add_cnt_v ) begin
            cnt_v <= cnt_v + 1;
        end
        else begin
            cnt_v <= cnt_v;
        end
    end
    else begin
        cnt_v <= 0;
    end
end



assign flag_enable_cnt_v = flag_enable_cnt_h;
assign flag_clear_cnt_v  = cnt_v == V_TOTAL - 1;
assign flag_add_cnt_v    = flag_clear_cnt_h;



// 场同步信号
always @( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        v_sync <= 1;
    end
    else if ( cnt_v == V_SYNC - 1 ) begin
        v_sync <= 0;
    end
        else if ( flag_clear_cnt_v ) begin
        v_sync <= 1;
        end
    else begin
        v_sync <= v_sync;
    end
end

// 对应有效区域行地址 1-640
always @( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        addr_h <= 0;
    end
    else if ( valid_area ) begin
        addr_h <= cnt_h - H_SYNC - H_BLACK + 1;
    end
    else begin
        addr_h <= 0;
    end
end
// 对应有效区域列地址 1-480
always @( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        addr_v <= 0;
    end
    else if ( valid_area ) begin
        addr_v <= cnt_v -V_SYNC - V_BLACK + 1;
    end
    else begin
        addr_v <= 0;
    end
end
// 有效显示区域
assign valid_area = cnt_h >= H_SYNC + H_BLACK && cnt_h <= H_SYNC + H_BLACK + H_ACT && cnt_v >= V_SYNC + V_BLACK && cnt_v <= V_SYNC + V_BLACK + V_ACT;


// 显示颜色
always @( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        rgb <= 16'h0;
    end
    else if ( valid_area ) begin
        rgb <= rgb_data;
    end
    else begin
        rgb <= 16'b0;
    end
end
assign rgb_r = rgb[ 15:11 ];
assign rgb_g = rgb[ 10:5 ];
assign rgb_b = rgb[ 4:0 ];
endmodule // vga_dirve
// */

5.3 显示数据生成模块

data_drive.v

module data_drive (
    input			wire						vga_clk,      // VGA时钟输入
    input			wire						rst_n,        // 复位信号,低电平有效
    input			wire		[ 11:0 ]		addr_h,       // 水平地址输入
    input			wire		[ 11:0 ]		addr_v,       // 垂直地址输入
    input			wire		[ 2:0 ]		 key,          // 三个按键输入

    output			reg		[ 15:0 ]				rgb_data      // 输出的RGB数据

);

// 定义一些颜色的16位表示
localparam	red    = 16'd63488;
localparam	orange = 16'd64384;
localparam	yellow = 16'd65472;
localparam	green  = 16'd1024;
localparam	blue   = 16'd31;
localparam	indigo = 16'd18448;
localparam	purple = 16'd32784;
localparam	white  = 16'd65503;
localparam	black  = 16'd0;


//显示的名字
// 存储显示字符的每一行数据
// reg [ 383:0 ] char_line[ 64:0 ];

//李菊芳-632109160602  -16
//16行,每行152个bit
// reg [ 152:0 ] char_line[ 15:0 ];

//李菊芳-632109160602 ------32
//32*3+16*13 = 304 304/8 = 38
reg [ 303:0 ] char_line[ 31:0 ];

// 定义显示状态的参数
localparam	states_1 = 1; // 彩条
localparam	states_2 = 2; // 字符
localparam	states_3 = 3; // 图片

// 图片的尺寸参数
// parameter	height = 78; // 图片高度
// parameter	width  = 128; // 图片宽度

//ikun2
parameter	height = 52; // 图片高度
parameter	width  = 52; // 图片宽度

// 当前状态和下一个状态的寄存器
reg			[ 2:0 ]			states_current			; // 当前状态
reg			[ 2:0 ]			states_next			    ; // 下个状态

// ROM的地址寄存器和数据输出
reg			[ 13:0 ]		rom_address				; // ROM地址
// wire		[ 15:0 ]		rom_data				; // 图片数据
wire		[ 23:0 ]		rom_data				; // 显示彩色图片数据


// 状态机的标志位
wire							flag_enable_out1			; // 文字有效区域标志
wire							flag_enable_out2			; // 图片有效区域标志
wire							flag_clear_rom_address		; // ROM地址清零标志
wire							flag_begin_h			    ; // 图片显示行开始标志
wire							flag_begin_v			    ; // 图片显示列开始标志

// 状态转移逻辑
always @( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        states_current <= states_1;// 复位时设置初始状态为彩条
    end
    else begin
        states_current <= states_next;// 否则转移到下一个状态
    end
end

// 状态判断逻辑,根据按键输入更新下一个状态
always @( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        states_next <= states_1;
    end
    else if ( key[ 0 ] ) begin
        states_next <= states_1;
    end
        else if ( key[ 1 ] ) begin
        states_next <= states_2;
        end
        else if ( key[ 2 ] ) begin
        states_next <= states_3;
        end
    else begin
        states_next <= states_next;
    end
end

// 状态输出逻辑,根据不同的状态输出不同的RGB数据
always @( * ) begin
    case ( states_current )
        //彩条
        states_1 : begin
            if ( addr_h == 0 ) begin
                rgb_data = black;
            end
            else if ( addr_h >0 && addr_h <81 ) begin
                rgb_data = red;
            end
            else if ( addr_h >80 && addr_h <161 ) begin
                // rgb_data = orange;
                rgb_data = red;
            end
            else if ( addr_h >160 && addr_h <241 ) begin
                rgb_data = yellow;
            end
            else if ( addr_h >240 && addr_h <321 ) begin
                rgb_data = green;
            end
            else if ( addr_h >320 && addr_h <401 ) begin
                rgb_data = blue;
            end
            else if ( addr_h >400 && addr_h <481 ) begin
                rgb_data = indigo;
            end
            else if ( addr_h >480 && addr_h <561 ) begin
                rgb_data = purple;
            end
            else if ( addr_h >560 && addr_h <641 ) begin
                rgb_data = white;
            end
            else begin
                rgb_data = black;
            end
            
        end
        //字符
        states_2 : begin
            if ( flag_enable_out1 ) begin
                //480*640
                // rgb_data = char_line[ addr_v-208 ][ 532 - addr_h ]? white:black;
                rgb_data = char_line[ addr_v-224 ][ 472 - addr_h ]? white:black;
            end
            else begin
                rgb_data = black;
            end
        end
        //图片
        states_3 : begin
            if ( flag_enable_out2 ) begin
                rgb_data = rom_data;
            end
            else begin
                rgb_data = black;
            end
            
        end
        default: begin
            case ( addr_h )
                0 : rgb_data      = black;
                1 : rgb_data      = red;
                81 : rgb_data     = orange;
                161: rgb_data     = yellow;
                241: rgb_data     = green;
                321: rgb_data     = blue;
                401: rgb_data     = indigo;
                481: rgb_data     = purple;
                561: rgb_data     = white;
                default: rgb_data = rgb_data;
            endcase
        end
    endcase
end

//李骏飞的居中显示参数
//32*3+16*13 = 304 304/8 = 38
// 根据当前状态和地址范围设置标志位
parameter ljf_width = 304;  // 字符数据的宽度
parameter ljf_height = 32;  // 字符数据的高度
assign flag_enable_out1 = states_current == states_2 && 
                           addr_h >= (640 - ljf_width) / 2 && 
                           addr_h <  ((640 - ljf_width) / 2) + ljf_width && 
                           addr_v >= (480 - ljf_height) / 2 && 
                           addr_v <  ((480 - ljf_height) / 2) + ljf_height;

// assign flag_begin_h     = addr_h > ( ( 640 - width ) / 2 ) && addr_h < ( ( 640 - width ) / 2 ) + width + 1;
// assign flag_begin_v     = addr_v > ( ( 480 - height )/2 ) && addr_v <( ( 480 - height )/2 ) + height + 1;
assign flag_begin_h     = addr_h >= ( ( 640 - width ) / 2 ) && addr_h < ( ( 640 - width ) / 2 ) + width ;
assign flag_begin_v     = addr_v >= ( ( 480 - height )/2 ) && addr_v <( ( 480 - height )/2 ) + height ;
assign flag_enable_out2 = states_current == states_3 && flag_begin_h && flag_begin_v;

//ROM地址计数器
always @( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        rom_address <= 0;// 复位时清零ROM地址
    end
    else if ( flag_clear_rom_address ) begin //计数满清零
        rom_address <= 0;
    end
    else if ( flag_enable_out2 ) begin  //在有效区域内+1
        rom_address <= rom_address + 1;
    end
    else begin  //无效区域保持
        rom_address <= rom_address;
    end
end
assign flag_clear_rom_address = rom_address == height * width - 1 || states_current != states_3;

// 初始化显示文字的逻辑
always@( posedge vga_clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        //李菊芳-632109160602 ------32
        //32*3+16*13 = 304 304/8 = 38
        char_line[0] =  304'h0000000000000000000000000000000000000000000000000000000000000000000000000000;
        char_line[1] =  304'h0000000000000000000000000000000000000000000000000000000000000000000000000000;
        char_line[2] =  304'h0003800000101000002008000000000000000000000000000000000000000000000000000000;
        char_line[3] =  304'h0003C000001C1C0000380E000000000000000000000000000000000000000000000000000000;
        char_line[4] =  304'h000380100018180000300C000000000000000000000000000000000000000000000000000000;
        char_line[5] =  304'h000380380018181800300C300000000000000000000000000000000000000000000000000000;
        char_line[6] =  304'h3FFFFFFC3FFFFFFC1FFFFFF8000001E007C007E0008003C007C0008001E003C001E003C007E0;
        char_line[7] =  304'h180FE0000098180000300C000000061818600838018006201820018006180620061806200838;
        char_line[8] =  304'h001FF00000D8180000330C0000000C18303010181F800C3030101F800C180C300C180C301018;
        char_line[9] =  304'h003FB80000F8180000318C00000008183018200C01801818301801800818181808181818200C;
        char_line[10] = 304'h 007B9C00019000200030C800000018003018200C01801818600801801800181818001818200C;
        char_line[11] = 304'h 00F39E0001FFFFF000006000000010003018300C01801808600C01801000180810001808300C;
        char_line[12] = 304'h 01E38F800300003000004030000010000018300C0180300C600C01801000300C1000300C300C;
        char_line[13] = 304'h 03C387F0020300301FFFFFF8000030000018000C0180300C600C01803000300C3000300C000C;
        char_line[14] = 304'h 07838DFE0483083000060000000033E0003000180180300C600C018033E0300C33E0300C0018;
        char_line[15] = 304'h 1FFFFEF808431C300006000000003630006000180180300C600C01803630300C3630300C0018;
        char_line[16] = 304'h 38401F3010631830000C00007FFE381803C000300180300C701C01803818300C3818300C0030;
        char_line[17] = 304'h 60003C0020633030000C008000003808007000600180300C302C01803808300C3808300C0060;
        char_line[18] = 304'h 0001F00000232330000FFFC00000300C001800C00180300C186C0180300C300C300C300C00C0;
        char_line[19] = 304'h 0001E0000FFFFFB0000C00C00000300C000801800180300C0F8C0180300C300C300C300C0180;
        char_line[20] = 304'h 0001E010000F0030000C01800000300C000C03000180300C000C0180300C300C300C300C0300;
        char_line[21] = 304'h 0001C038000B8030001801800000300C000C02000180300C00180180300C300C300C300C0200;
        char_line[22] = 304'h 7FFFFFFC001B6030001801800000300C300C04040180180800180180300C1808300C18080404;
        char_line[23] = 304'h 3801C00000333830003001800000180C300C08040180181800100180180C1818180C18180804;
        char_line[24] = 304'h 0001C00000631C20003001800000180830081004018018183030018018081818180818181004;
        char_line[25] = 304'h 0001C00000C30C200060030000000C183018200C01800C30306001800C180C300C180C30200C;
        char_line[26] = 304'h 0001C0000183046000C0030000000E3018303FF803C0062030C003C00E3006200E3006203FF8;
        char_line[27] = 304'h 0001C000020300600180C300000003E007C03FF81FF803C00F801FF803E003C003E003C03FF8;
        char_line[28] = 304'h 003FC0000C030FE002003E000000000000000000000000000000000000000000000000000000;
        char_line[29] = 304'h 0007C000300303C00C001E000000000000000000000000000000000000000000000000000000;
        char_line[30] = 304'h 0003800000020080300008000000000000000000000000000000000000000000000000000000;
        char_line[31] = 304'h 0000000000000000000000000000000000000000000000000000000000000000000000000000;


    end
end

// /*
//ikun
// ROM实例化,根据地址输出数据
ikun_rom	ikun_rom_inst (
.address ( rom_address ),
.clock ( vga_clk ),
.q ( rom_data )
);
// */



endmodule // data_drive
相关推荐
fei_sun11 小时前
【Verilog】第一章作业
fpga开发·verilog
深圳市雷龙发展有限公司longsto11 小时前
基于FPGA(现场可编程门阵列)的SD NAND图片显示系统是一个复杂的项目,它涉及硬件设计、FPGA编程、SD卡接口、NAND闪存控制以及图像显示等多个方面
fpga开发
9527华安16 小时前
FPGA实现PCIE3.0视频采集转10G万兆UDP网络输出,基于XDMA+GTH架构,提供工程源码和技术支持
网络·fpga开发·udp·音视频·xdma·pcie3.0·万兆网
able陈16 小时前
为什么verilog中递归函数需要定义为automatic?
fpga开发
fei_sun17 小时前
【Verilog】第二章作业
fpga开发·verilog
碎碎思17 小时前
如何使用 Vivado 从源码构建 Infinite-ISP FPGA 项目
fpga开发·接口隔离原则
江山如画,佳人北望20 小时前
fpga-状态机的设计及应用
fpga开发
晓晓暮雨潇潇21 小时前
Xilinx IP核(3)XADC IP核
fpga开发·vivado·xadc·ip核
CWNULT21 小时前
AMD(Xilinx) FPGA配置Flash大小选择
fpga开发
碎碎思1 天前
很能体现FPGA硬件思维的一道面试题
fpga开发