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
相关推荐
仲南音17 小时前
Xilinx FPGA——时钟IP核MMCM、PLL
fpga开发
I'm a winner1 天前
基于Xilinx FPGA的LVDS高速串行通信系统(三)--数据训练及握手机制【文末源码】
fpga开发
upper20201 天前
coremark移植坑之-nostartfiles -nostdlib参数
学习·fpga开发·coremark移植·riscv设计
Eloudy2 天前
Vivado 纯命令行构建 FPGA 项目教程
fpga开发
Rambo.xia2 天前
AXI跨时钟域与数据一致性——CDC FIFO深度不够、写后读冒险、多Master仲裁、乱序返回,出了Bug根本查不出来
fpga开发·bug
upper20202 天前
FPGA部署卷积神经网络识别MINST图片
fpga开发·卷积神经网络·minst·minst识别·部署神经网络·神经网络识别图片
你是我的解忧王子2 天前
嵌入式 ADC采样 原始数据分析上层(平均值 波动率 正态分布 线性补偿 系统误差)
fpga开发·数据挖掘·数据分析
9527华安3 天前
FPGA纯verilog代码ISP图像处理培训教程,基于IMX214,提供工程源码+视频教程+FPGA开发板
图像处理·fpga开发·isp·imx214·mipi
千寻xun3 天前
二、实战篇-NVME SSD控制之ZYNQ实现(六)-读写NVME SSD硬盘数据
fpga开发·nvme·nvme ssd
千寻xun3 天前
二、实战篇-NVME SSD控制之ZYNQ实现(三)
fpga开发·nvme ssd·nvme协议