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
相关推荐
Hello-FPGA18 小时前
基于RFSOC FPGA ZCU216 的CoaXPress 采集demo
fpga开发
9527华安20 小时前
FPGA基于ADV7182解码PAL视频,Video Processing Subsystem去隔行,提供6套工程源码和技术支持
fpga开发·音视频·pal·adv7182
hahaha601620 小时前
数字图像时域滤波或者3D降噪算法FPGA实现
算法·计算机视觉·fpga开发
GateWorld21 小时前
Xilinx 7系列FPGA时钟网络详解
fpga开发·时钟网络·xilinx 7系列
szxinmai主板定制专家1 天前
X86 工控机 vs RK3588 异构主控:AMR 控制器 BOM、散热、长期运维对比
运维·stm32·单片机·嵌入式硬件·fpga开发·am62x
szxinmai主板定制专家2 天前
AMR 项目落地:机器人控制器硬件定制化需求复盘
arm开发·单片机·fpga开发·系统架构·机器人·am62x
FPGA小迷弟3 天前
FPGA 与卫星:从星载硬件到在轨智能的完整技术解析
fpga开发·架构·fpga·1024程序员节·无线电
NamoAmitabha1283 天前
CPU 内部结构是什么?---deepseek
fpga开发
abc light3 天前
ARM 裸机开发(6)——时钟树与定时器
arm开发·单片机·fpga开发
156082072193 天前
JFMZQ28DR、JFMZQ68DR、RFsoc XCZU47DR、XCZU48DR 信号处理板
fpga开发·信号处理