基于FPGA的VGA协议实现

基于fpga的lcd显示

一.lcd介绍

1.lcd显示原理

LCD,即液晶显示器(Liquid Crystal Display),是一种广泛使用的平面显示器技术。LCD显示原理主要基于液晶材料的光学特性,通过控制液晶分子的排列来调节光线的透过率,从而实现图像的显示。

背光系统:LCD显示器的背光系统通常由LED或冷阴极荧光灯(CCFL)组成,提供均匀的光线。

偏振片:背光上方和下方各有一个偏振片,它们只允许特定方向的光通过。

彩色滤光片:在偏振片上方,有一层彩色滤光片,它包含红、绿、蓝三种颜色的像素点,用于生成彩色图像。

液晶层:液晶层位于彩色滤光片和偏振片之间。液晶分子在没有电场作用时,自然排列,允许光线通过。当施加电压时,液晶分子会重新排列,从而改变光线的偏振方向。

电极:每个像素点都有一个或多个电极,用于施加电压控制液晶分子的排列。

控制电路:控制电路负责根据图像信号向电极施加适当的电压,以控制液晶分子的排列。

图像显示:当液晶分子排列改变时,它们会改变通过它们的光线的偏振方向。如果光线的偏振方向与第二个偏振片的偏振方向一致,光线就会通过;如果不一致,则光线被阻挡。通过这种方式,LCD显示器可以控制每个像素点的亮度,从而显示图像。

刷新率:LCD显示器通过不断刷新像素点来保持图像的稳定显示。

LCD显示器的优点包括低功耗、薄型设计、高分辨率和色彩表现。然而,它们也有缺点,如视角限制、响应时间较慢以及在极端温度下性能下降。随着技术的发展,新型LCD技术如IPS(In-Plane Switching)和VA(Vertical Alignment)等,已经改善了这些缺点,提供了更好的显示性能。

lcd通信协议

LCD显示器的通信协议是指用于控制LCD显示内容的一系列指令和数据传输规则。不同的LCD模块可能使用不同的通信协议,但以下是一些常见的LCD通信协议:

8080接口:这是一种并行通信协议,通常用于与微控制器或处理器连接。它包括多个控制引脚,如读信号RD、写信号WR、片选信号CS、数据/命令选择信号DC(RS),以及数据总线DB[x:0]。8080接口通过这些引脚来传输数据和命令。

RGB接口:这是LCD控制器和LCD屏幕之间的接口,通常用于彩色LCD。它使用点时钟信号DCLK、水平方向同步信号HSYNC、垂直方向同步信号VSYNC、RGB数据线,以及数据使能信号DE来控制像素的显示。

SPI接口:SPI(串行外设接口)是一种串行通信协议,广泛用于LCD和其他类型的显示设备。它使用少量的引脚来传输数据和命令,包括一个时钟线(CLK)、一个数据线(SDI/SDO)、一个片选线(CS)等。

I2C接口:I2C(Inter-Integrated Circuit)也是一种串行通信协议,用于连接低速外围设备。它使用两条线:串行数据线(SDA)和串行时钟线(SCL)。

自定义通信协议:某些智能液晶显示模块可能实现自定义通信协议,以维护企业的数据隐秘性,提升产品功能的多样性,并解决通信协议不兼容问题。

每种通信协议都有其特定的时序要求和引脚配置,开发者需要根据LCD模块的数据手册来正确配置和使用这些协议

lcd初始化

1.识别lcd的分辨率

module rd_id(
    input                    clk    ,     //时钟
    input                    rst_n  ,    //复位,低电平有效
    input           [15:0]   lcd_rgb,    //RGB LCD像素数据,用于读取ID
    output   reg    [15:0]   lcd_id      //LCD屏ID
    );

//reg define
reg            rd_flag;  //读ID标志



//获取LCD ID   M2:B4  M1:G5  M0:R4
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        rd_flag <= 1'b0;
        lcd_id <= 16'd0;
    end    
    else begin
        if(rd_flag == 1'b0) begin
            rd_flag <= 1'b1; 
            case({lcd_rgb[4],lcd_rgb[10],lcd_rgb[15]})
                3'b000 : lcd_id <= 16'h4342;    //4.3' RGB LCD  RES:480x272
                3'b001 : lcd_id <= 16'h7084;    //7'   RGB LCD  RES:800x480
                3'b010 : lcd_id <= 16'h7016;    //7'   RGB LCD  RES:1024x600
                3'b100 : lcd_id <= 16'h4384;    //4.3' RGB LCD  RES:800x480
                3'b101 : lcd_id <= 16'h1018;    //10'  RGB LCD  RES:1280x800
                default : lcd_id <=16'h0;
            endcase    
        end
    end    
end

endmodule

2.通过分辨率得到lcd自己的时钟周期

module clk_div(
    input               clk,          //50Mhz
    input               rst_n,
    input       [15:0]  lcd_id,
    output  reg         lcd_pclk
    );

reg          clk_25m;
reg          clk_12_5m;
reg          div_4_cnt;

//时钟2分频 输出25MHz时钟 
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        clk_25m <= 1'b0;
    else 
        clk_25m <= ~clk_25m;
end

//时钟4分频 输出12.5MHz时钟 
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        div_4_cnt <= 1'b0;
        clk_12_5m <= 1'b0;
    end    
    else begin
        div_4_cnt <= div_4_cnt + 1'b1;
        if(div_4_cnt == 1'b1)
            clk_12_5m <= ~clk_12_5m;
    end        
end

always @(*) begin
    case(lcd_id)
        16'h4342 : lcd_pclk = clk_12_5m;
        16'h7084 : lcd_pclk = clk_25m;       
        16'h7016 : lcd_pclk = clk;
        16'h4384 : lcd_pclk = clk_25m;
        16'h1018 : lcd_pclk = clk;
        default :  lcd_pclk = 0;
    endcase      
end

endmodule

3.lcd驱动模块

module lcd_driver(
    input                lcd_pclk,    //时钟
    input                rst_n,       //复位,低电平有效
    input        [15:0]  lcd_id,      //LCD屏ID
    input        [15:0]  pixel_data,  //像素数据
    output       [10:0]  pixel_xpos,  //当前像素点横坐标
    output       [10:0]  pixel_ypos,  //当前像素点纵坐标   
    output  reg  [10:0]  h_disp,      //LCD屏水平分辨率
    output  reg  [10:0]  v_disp,      //LCD屏垂直分辨率   
    //RGB LCD接口
    output               lcd_de,      //LCD 数据使能信号
    output               lcd_hs,      //LCD 行同步信号
    output               lcd_vs,      //LCD 场同步信号
    output     reg       lcd_bl,      //LCD 背光控制信号
    output               lcd_clk,     //LCD 像素时钟
    output       [15:0]  lcd_rgb,     //LCD RGB565颜色数据
    output     reg       lcd_rst      //LCD 复位,低电平有效
    );

//parameter define  
// 4.3' 480*272
parameter  H_SYNC_4342   =  11'd41;     //行同步
parameter  H_BACK_4342   =  11'd2;      //行显示后沿
parameter  H_DISP_4342   =  11'd480;    //行有效数据
parameter  H_FRONT_4342  =  11'd2;      //行显示前沿
parameter  H_TOTAL_4342  =  11'd525;    //行扫描周期
   
parameter  V_SYNC_4342   =  11'd10;     //场同步
parameter  V_BACK_4342   =  11'd2;      //场显示后沿
parameter  V_DISP_4342   =  11'd272;    //场有效数据
parameter  V_FRONT_4342  =  11'd2;      //场显示前沿
parameter  V_TOTAL_4342  =  11'd286;    //场扫描周期
   
// 7' 800*480   
parameter  H_SYNC_7084   =  11'd128;    //行同步
parameter  H_BACK_7084   =  11'd88;     //行显示后沿
parameter  H_DISP_7084   =  11'd800;    //行有效数据
parameter  H_FRONT_7084  =  11'd40;     //行显示前沿
parameter  H_TOTAL_7084  =  11'd1056;   //行扫描周期
   
parameter  V_SYNC_7084   =  11'd2;      //场同步
parameter  V_BACK_7084   =  11'd33;     //场显示后沿
parameter  V_DISP_7084   =  11'd480;    //场有效数据
parameter  V_FRONT_7084  =  11'd10;     //场显示前沿
parameter  V_TOTAL_7084  =  11'd525;    //场扫描周期       
   
// 7' 1024*600   
parameter  H_SYNC_7016   =  11'd20;     //行同步
parameter  H_BACK_7016   =  11'd140;    //行显示后沿
parameter  H_DISP_7016   =  11'd1024;   //行有效数据
parameter  H_FRONT_7016  =  11'd160;    //行显示前沿
parameter  H_TOTAL_7016  =  11'd1344;   //行扫描周期
   
parameter  V_SYNC_7016   =  11'd3;      //场同步
parameter  V_BACK_7016   =  11'd20;     //场显示后沿
parameter  V_DISP_7016   =  11'd600;    //场有效数据
parameter  V_FRONT_7016  =  11'd12;     //场显示前沿
parameter  V_TOTAL_7016  =  11'd635;    //场扫描周期
   
// 10.1' 1280*800   
parameter  H_SYNC_1018   =  11'd10;     //行同步
parameter  H_BACK_1018   =  11'd80;     //行显示后沿
parameter  H_DISP_1018   =  11'd1280;   //行有效数据
parameter  H_FRONT_1018  =  11'd70;     //行显示前沿
parameter  H_TOTAL_1018  =  11'd1440;   //行扫描周期
   
parameter  V_SYNC_1018   =  11'd3;      //场同步
parameter  V_BACK_1018   =  11'd10;     //场显示后沿
parameter  V_DISP_1018   =  11'd800;    //场有效数据
parameter  V_FRONT_1018  =  11'd10;     //场显示前沿
parameter  V_TOTAL_1018  =  11'd823;    //场扫描周期

// 4.3' 800*480   
parameter  H_SYNC_4384   =  11'd128;    //行同步
parameter  H_BACK_4384   =  11'd88;     //行显示后沿
parameter  H_DISP_4384   =  11'd800;    //行有效数据
parameter  H_FRONT_4384  =  11'd40;     //行显示前沿
parameter  H_TOTAL_4384  =  11'd1056;   //行扫描周期
   
parameter  V_SYNC_4384   =  11'd2;      //场同步
parameter  V_BACK_4384   =  11'd33;     //场显示后沿
parameter  V_DISP_4384   =  11'd480;    //场有效数据
parameter  V_FRONT_4384  =  11'd10;     //场显示前沿
parameter  V_TOTAL_4384  =  11'd525;    //场扫描周期    

//reg define
reg  [10:0] h_sync ;
reg  [10:0] h_back ;
reg  [10:0] h_total;
reg  [10:0] v_sync ;
reg  [10:0] v_back ;
reg  [10:0] v_total;
reg  [10:0] h_cnt  ;
reg  [10:0] v_cnt  ;

//wire define    
wire        lcd_en;
wire        data_req;

//*****************************************************
//**                    main code
//*****************************************************

//RGB LCD 采用DE模式时,行场同步信号需要拉高
assign  lcd_hs = 1'b1;        //LCD行同步信号
assign  lcd_vs = 1'b1;        //LCD场同步信号
 
assign  lcd_clk = lcd_pclk;   //LCD像素时钟
assign  lcd_de = lcd_en;      //LCD数据有效信号

//使能RGB565数据输出
assign  lcd_en = ((h_cnt >= h_sync + h_back) && (h_cnt < h_sync + h_back + h_disp)
                  && (v_cnt >= v_sync + v_back) && (v_cnt < v_sync + v_back + v_disp)) 
                  ? 1'b1 : 1'b0;

//请求像素点颜色数据输入  
assign data_req = ((h_cnt >= h_sync + h_back - 1'b1) && (h_cnt < h_sync + h_back + h_disp - 1'b1)
                  && (v_cnt >= v_sync + v_back) && (v_cnt < v_sync + v_back + v_disp)) 
                  ? 1'b1 : 1'b0;

//像素点坐标  
assign pixel_xpos = data_req ? (h_cnt - (h_sync + h_back - 1'b1)) : 11'd0;
assign pixel_ypos = data_req ? (v_cnt - (v_sync + v_back - 1'b1)) : 11'd0;

//RGB565数据输出
assign lcd_rgb = lcd_en ? pixel_data : 16'd0;

//行场时序参数
always @(posedge lcd_pclk) begin
    case(lcd_id)
        16'h4342 : begin
            h_sync  <= H_SYNC_4342; 
            h_back  <= H_BACK_4342; 
            h_disp  <= H_DISP_4342; 
            h_total <= H_TOTAL_4342;
            v_sync  <= V_SYNC_4342; 
            v_back  <= V_BACK_4342; 
            v_disp  <= V_DISP_4342; 
            v_total <= V_TOTAL_4342;            
        end
        16'h7084 : begin
            h_sync  <= H_SYNC_7084; 
            h_back  <= H_BACK_7084; 
            h_disp  <= H_DISP_7084; 
            h_total <= H_TOTAL_7084;
            v_sync  <= V_SYNC_7084; 
            v_back  <= V_BACK_7084; 
            v_disp  <= V_DISP_7084; 
            v_total <= V_TOTAL_7084;        
        end
        16'h7016 : begin
            h_sync  <= H_SYNC_7016; 
            h_back  <= H_BACK_7016; 
            h_disp  <= H_DISP_7016; 
            h_total <= H_TOTAL_7016;
            v_sync  <= V_SYNC_7016; 
            v_back  <= V_BACK_7016; 
            v_disp  <= V_DISP_7016; 
            v_total <= V_TOTAL_7016;            
        end
        16'h4384 : begin
            h_sync  <= H_SYNC_4384; 
            h_back  <= H_BACK_4384; 
            h_disp  <= H_DISP_4384; 
            h_total <= H_TOTAL_4384;
            v_sync  <= V_SYNC_4384; 
            v_back  <= V_BACK_4384; 
            v_disp  <= V_DISP_4384; 
            v_total <= V_TOTAL_4384;             
        end        
        16'h1018 : begin
            h_sync  <= H_SYNC_1018; 
            h_back  <= H_BACK_1018; 
            h_disp  <= H_DISP_1018; 
            h_total <= H_TOTAL_1018;
            v_sync  <= V_SYNC_1018; 
            v_back  <= V_BACK_1018; 
            v_disp  <= V_DISP_1018; 
            v_total <= V_TOTAL_1018;        
        end
        default : begin
            h_sync  <= H_SYNC_4342; 
            h_back  <= H_BACK_4342; 
            h_disp  <= H_DISP_4342; 
            h_total <= H_TOTAL_4342;
            v_sync  <= V_SYNC_4342; 
            v_back  <= V_BACK_4342; 
            v_disp  <= V_DISP_4342; 
            v_total <= V_TOTAL_4342;          
        end
    endcase
end

//行计数器对像素时钟计数
always@ (posedge lcd_pclk or negedge rst_n) begin
    if(!rst_n) 
        h_cnt <= 11'd0;
    else begin
        if(h_cnt == h_total - 1'b1)
            h_cnt <= 11'd0;
        else
            h_cnt <= h_cnt + 1'b1;           
    end
end

//场计数器对行计数
always@ (posedge lcd_pclk or negedge rst_n) begin
    if(!rst_n) 
        v_cnt <= 11'd0;
    else begin
        if(h_cnt == h_total - 1'b1) begin
            if(v_cnt == v_total - 1'b1)
                v_cnt <= 11'd0;
            else
                v_cnt <= v_cnt + 1'b1;    
        end
    end    
end

//控制LCD复位信号和背光信号
always@ (posedge lcd_pclk or negedge rst_n) begin
    if(!rst_n)begin 
        lcd_rst <= 0;
        lcd_bl <= 0;
    end
    else begin
        lcd_rst <= 1;
        lcd_bl <= 1;
    end
end

endmodule

还有显示模块和顶层文件,需要后面写字符和图片显示的时候添加

三.lcd显示字符和图片

1.显示字符

lcd显示字符的原理和vga是一致的,都是通过逐行扫描像素点,对像素点进行颜色设置,通过不同像素点之间的颜色来显示字符。所以需要每个字的第一行,第二行,第三行…依次放在一起来进行显示。
这里使用到了汉字点阵,之前学习过OLED的汉字显示,这里会用到同样的汉字点阵提取,可以参考链接:https://blog.csdn.net/qq_45659777/article/details/121456548
基本设置
5-2
将字宽和字高设置为16x16
5-1
将字宽和字高设置为32x16
5-3
现在得到的是一个单独的字体,而显示是将每个字体的每一行放入一个数组中,也就是每个字模(16进制)的每4位放入数组中,然后显示。
5-4

2.显示图片

图片这里需要借助到rom,先找到一张格式位bmp的图片,将大小缩小的48x48(不宜太大),然后使用BMP2Mif软件将bmp格式图片转换为hex文件。
5-5
生成的文件格式为以下
5-6
然后需要新建qurtaus工程,然后点击tools下面的ip catalog
5-7
在搜索框里输入rom,然后选择rom1:port
5-8
命名rom为ram1_port并选择或新建一个文件夹进行保存
5-9
设置宽度和内存,内存要大于图片大小
5-10
取消勾选q
5-11
加载hex文件
5-12
勾选rqm!_port_inst.v
5-13
然后进行lcd显示程序编写

module lcd_display(
    input             lcd_pclk,     //时钟
    input             rst_n,        //复位,低电平有效
                                    
    input      [10:0] pixel_xpos,   //像素点横坐标
    input      [10:0] pixel_ypos,   //像素点纵坐标    
    output reg [15:0] pixel_data    //像素点数据,
);                                   
                                     
//parameter define                   
localparam PIC_X_START = 11'd376;      //图片起始点横坐标
localparam PIC_Y_START = 11'd206;      //图片起始点纵坐标
localparam PIC_WIDTH   = 11'd48;    //图片宽度
localparam PIC_HEIGHT  = 11'd48;    //图片高度
                       
localparam CHAR_X_START= 11'd272;      //字符起始点横坐标
localparam CHAR_Y_START= 11'd264;    //字符起始点纵坐标
localparam CHAR_WIDTH  = 11'd256;    //字符宽度,4个字符:32*4
localparam CHAR_HEIGHT = 11'd16;     //字符高度
                       
localparam BACK_COLOR  = 16'hE7FF;   //背景色,浅蓝色
localparam CHAR_COLOR  = 16'hF800;   //字符颜色,红色

//reg define
reg   [255:0] char[31:0];  //字符数组
reg   [13:0]  rom_addr  ;  //ROM地址

//wire define   
wire  [10:0]  x_cnt;       //横坐标计数器
wire  [10:0]  y_cnt;       //纵坐标计数器
wire          rom_rd_en ;  //ROM读使能信号
wire  [15:0]  rom_rd_data ;//ROM数据


assign  x_cnt = pixel_xpos - CHAR_X_START; //像素点相对于字符区域起始点水平坐标
assign  y_cnt = pixel_ypos - CHAR_Y_START; //像素点相对于字符区域起始点垂直坐标
assign  rom_rd_en = 1'b1;                  //读使能拉高,即一直读ROM数据


always @(posedge lcd_pclk) begin
    char[0 ]  <= 256'h0100100000000000000000000000000000000000000000000000000000000000;
    char[1 ]  <= 256'hF90806C027FE0000000000000000000000000000000000000000000000000000;
    char[2 ]  <= 256'h0908183014200000000000000000000000000000000000000000000000000000;
    char[3 ]  <= 256'h0910FEEE1440000007F00FE00FF0008007E01FFC07E007F007E01FF80FF007E0;
    char[4 ]  <= 256'h0920100085FC0000081830183018078018183008181808181818100030181818;
    char[5 ]  <= 256'h79401FF04504000010003818380C0180381C2010381C1000381C1000380C381C;
    char[6 ]  <= 256'h4100092045FC00003000001810180180300C0020300C3000300C10001018300C;
    char[7 ]  <= 256'h47FE3FF815047FFE37F0006000180180300C0040300C37F0300C17F00018300C;
    char[8 ]  <= 256'h4140101015FC0000380C10F000600180300C0080300C380C300C18180060300C;
    char[9 ]  <= 256'h7920282825240000300C001801800180300C0180300C300C300C000C0180300C;
    char[10]  <= 256'h09207C7CE4200000300C000C06000180300C0300300C300C300C000C0600300C;
    char[11]  <= 256'h0910929214A80000300C380C08040180381803003818300C3818380C08043818;
    char[12]  <= 256'h09087C7C2924000018183018300C01801C1003801C1018181C103018300C1C10;
    char[13]  <= 256'h094410102A22000007E00FE03FF80FF807E0030007E007E007E00FE03FF807E0;
    char[14]  <= 256'h5182545430A00000000000000000000000000000000000000000000000000000;
    char[15]  <= 256'h2100FEFE00400000000000000000000000000000000000000000000000000000;
end

//为LCD不同显示区域绘制图片、字符和背景色
always @(posedge lcd_pclk or negedge rst_n) begin
    if (!rst_n)
        pixel_data <= BACK_COLOR;
    else if( (pixel_xpos >= PIC_X_START) && (pixel_xpos < PIC_X_START + PIC_WIDTH) 
          && (pixel_ypos >= PIC_Y_START) && (pixel_ypos < PIC_Y_START + PIC_HEIGHT) )
        pixel_data <= rom_rd_data ;  //显示图片
    else if((pixel_xpos >= CHAR_X_START) && (pixel_xpos < CHAR_X_START + CHAR_WIDTH)
         && (pixel_ypos >= CHAR_Y_START) && (pixel_ypos < CHAR_Y_START + CHAR_HEIGHT)) begin
        if(char[y_cnt][CHAR_WIDTH -1'b1 - x_cnt])
            pixel_data <= CHAR_COLOR;    //显示字符
        else
            pixel_data <= BACK_COLOR;    //显示字符区域的背景色
    end
    else
        pixel_data <= BACK_COLOR;        //屏幕背景色
end

//根据当前扫描点的横纵坐标为ROM地址赋值
always @(posedge lcd_pclk or negedge rst_n) begin
    if(!rst_n)
        rom_addr <= 14'd0;
    //当横纵坐标位于图片显示区域时,累加ROM地址    
    else if((pixel_ypos >= PIC_Y_START) && (pixel_ypos < PIC_Y_START + PIC_HEIGHT) 
        && (pixel_xpos >= PIC_X_START) && (pixel_xpos < PIC_X_START + PIC_WIDTH)) 
        rom_addr <= rom_addr + 1'b1;
    //当横纵坐标位于图片区域最后一个像素点时,ROM地址清零    
    else if((pixel_ypos >= PIC_Y_START + PIC_HEIGHT))
        rom_addr <= 14'd0;
end

//ROM:存储图片
ram_1port u_ram_1port(
  .address  (rom_addr ),  
  .clock    (lcd_pclk ),   
   
  .q        (rom_rd_data)
);

endmodule 

然后编译写顶层文件

module lcd_rgb_char(
    input                sys_clk,     //系统时钟
    input                sys_rst_n,   //系统复位
    
    //RGB LCD接口
    output               lcd_de,      //LCD 数据使能信号
    output               lcd_hs,      //LCD 行同步信号
    output               lcd_vs,      //LCD 场同步信号
    output               lcd_clk,     //LCD 像素时钟
    inout        [15:0]  lcd_rgb,     //LCD RGB565颜色数据
    output               lcd_rst,
    output               lcd_bl
    
    );                                                      

wire  [15:0]  lcd_id    ;    //LCD屏ID
wire          lcd_pclk  ;    //LCD像素时钟              
wire  [10:0]  pixel_xpos;    //当前像素点横坐标
wire  [10:0]  pixel_ypos;    //当前像素点纵坐标
wire  [15:0]  pixel_data;    //像素数据
wire  [15:0]  lcd_rgb_o ;    //输出的像素数据
wire  [15:0]  lcd_rgb_i ;    //输入的像素数据


//像素数据方向切换
assign lcd_rgb = lcd_de ?  lcd_rgb_o :  {16{1'bz}};
assign lcd_rgb_i = lcd_rgb;

//读LCD ID模块
rd_id u_rd_id(
    .clk          (sys_clk  ),
    .rst_n        (sys_rst_n),
    .lcd_rgb      (lcd_rgb_i),
    .lcd_id       (lcd_id   )
    );    

//时钟分频模块    
clk_div u_clk_div(
    .clk           (sys_clk  ),
    .rst_n         (sys_rst_n),
    .lcd_id        (lcd_id   ),
    .lcd_pclk      (lcd_pclk )
    );    

//LCD显示模块    
lcd_display u_lcd_display(
    .lcd_pclk    (lcd_pclk  ),
    .rst_n       (sys_rst_n ),

    //像素点坐标
    .pixel_xpos  (pixel_xpos),
    .pixel_ypos  (pixel_ypos),
    .pixel_data  (pixel_data)
    );   

//LCD驱动模块
lcd_driver u_lcd_driver(
    .lcd_pclk      (lcd_pclk  ),
    .rst_n         (sys_rst_n ),
    .lcd_id        (lcd_id    ),
    .pixel_data    (pixel_data),
    .pixel_xpos    (pixel_xpos),
    .pixel_ypos    (pixel_ypos),
    .h_disp        (),
    .v_disp        (),

    .lcd_de        (lcd_de    ),
    .lcd_hs        (lcd_hs    ),
    .lcd_vs        (lcd_vs    ),   
    .lcd_clk       (lcd_clk   ),
    .lcd_rgb       (lcd_rgb_o ),
    .lcd_rst       (lcd_rst   ),
    .lcd_bl        (lcd_bl)
    );

endmodule

效果

5-14

动态彩条显示

这里的彩条显示的时钟,分辨率识别,与上面代码是一致的,需要对lcd驱动模块和显示模块以及顶层文件进行修改,
显示模块:

module lcd_display(
    input                lcd_pclk,    //时钟
    input                rst_n,       //复位,低电平有效
    input        [10:0]  pixel_xpos,  //当前像素点横坐标
    input        [10:0]  pixel_ypos,  //当前像素点纵坐标  
    input        [10:0]  h_disp,      //LCD屏水平分辨率
    input        [10:0]  v_disp,      //LCD屏垂直分辨率  
    input        [10:0]  y_start,
    input        [10:0]  y_end,     
    output  reg  [15:0]  pixel_data   //像素数据
    );

//parameter define  
parameter WHITE = 16'b11111_111111_11111;  //白色
parameter BLACK = 16'b00000_000000_00000;  //黑色
parameter RED   = 16'b11111_000000_00000;  //红色
parameter GREEN = 16'b00000_111111_00000;  //绿色
parameter BLUE  = 16'b00000_000000_11111;  //蓝色

//根据当前像素点坐标指定当前像素点颜色数据,在屏幕上显示彩条
always @(posedge lcd_pclk or negedge rst_n) begin
    if(!rst_n)begin
        pixel_data <= BLACK;
      
    end
    else begin
        if(y_start<y_end)begin
        if((pixel_xpos >= 11'd0) && (pixel_xpos < h_disp/5*1)&&(pixel_ypos >= y_start)&&(pixel_ypos < y_end))
            pixel_data <= WHITE;
        else if((pixel_xpos >= h_disp/5*1) && (pixel_xpos < h_disp/5*2)&&(pixel_ypos >= y_start)&&(pixel_ypos < y_end))    
            pixel_data <= BLACK;
        else if((pixel_xpos >= h_disp/5*2) && (pixel_xpos < h_disp/5*3)&&(pixel_ypos >= y_start)&&(pixel_ypos < y_end))    
            pixel_data <= RED;   
        else if((pixel_xpos >= h_disp/5*3) && (pixel_xpos < h_disp/5*4)&&(pixel_ypos >= y_start)&&(pixel_ypos < y_end))    
            pixel_data <= GREEN;                
        else if((pixel_xpos >= h_disp/5*4) && (pixel_xpos < h_disp)&&(pixel_ypos >= y_start)&&(pixel_ypos < y_end))
            pixel_data <= BLUE; 
        end 
        else begin
              if((pixel_xpos >= 11'd0) && (pixel_xpos < h_disp/5*1)&&!((pixel_ypos >= y_end)&&(pixel_ypos < y_start)))
            pixel_data <= WHITE;
        else if((pixel_xpos >= h_disp/5*1) && (pixel_xpos < h_disp/5*2)&&!((pixel_ypos >= y_end)&&(pixel_ypos < y_start)))    
            pixel_data <= BLACK;
        else if((pixel_xpos >= h_disp/5*2) && (pixel_xpos < h_disp/5*3)&&!((pixel_ypos >= y_end)&&(pixel_ypos < y_start)))    
            pixel_data <= RED;   
        else if((pixel_xpos >= h_disp/5*3) && (pixel_xpos < h_disp/5*4)&&!((pixel_ypos >= y_end)&&(pixel_ypos < y_start)))    
            pixel_data <= GREEN;                
        else if((pixel_xpos >= h_disp/5*4) && (pixel_xpos < h_disp)&&!((pixel_ypos >= y_end)&&(pixel_ypos < y_start)))
            pixel_data <= BLUE;  
        end    
    end    
end

endmodule

驱动模块:

module lcd_driver(
    input                lcd_pclk,    //时钟
    input                rst_n,       //复位,低电平有效
    input        [15:0]  lcd_id,      //LCD屏ID
    input        [15:0]  pixel_data,  //像素数据
    output       [10:0]  pixel_xpos,  //当前像素点横坐标
    output       [10:0]  pixel_ypos,  //当前像素点纵坐标   
    output  reg  [10:0]  h_disp,      //LCD屏水平分辨率
    output  reg  [10:0]  v_disp,      //LCD屏垂直分辨率
    output  reg  [10:0]  y_start,
    output  reg  [10:0]  y_end,  
    //RGB LCD接口
    output               lcd_de,      //LCD 数据使能信号
    output               lcd_hs,      //LCD 行同步信号
    output               lcd_vs,      //LCD 场同步信号
    output     reg       lcd_bl,      //LCD 背光控制信号
    output               lcd_clk,     //LCD 像素时钟
    output       [15:0]  lcd_rgb,     //LCD RGB565颜色数据
    output     reg       lcd_rst      //LCD 复位,低电平有效
    );

//parameter define  
// 4.3' 480*272
parameter  H_SYNC_4342   =  11'd41;     //行同步
parameter  H_BACK_4342   =  11'd2;      //行显示后沿
parameter  H_DISP_4342   =  11'd480;    //行有效数据
parameter  H_FRONT_4342  =  11'd2;      //行显示前沿
parameter  H_TOTAL_4342  =  11'd525;    //行扫描周期
   
parameter  V_SYNC_4342   =  11'd10;     //场同步
parameter  V_BACK_4342   =  11'd2;      //场显示后沿
parameter  V_DISP_4342   =  11'd272;    //场有效数据
parameter  V_FRONT_4342  =  11'd2;      //场显示前沿
parameter  V_TOTAL_4342  =  11'd286;    //场扫描周期
   
// 7' 800*480   
parameter  H_SYNC_7084   =  11'd128;    //行同步
parameter  H_BACK_7084   =  11'd88;     //行显示后沿
parameter  H_DISP_7084   =  11'd800;    //行有效数据
parameter  H_FRONT_7084  =  11'd40;     //行显示前沿
parameter  H_TOTAL_7084  =  11'd1056;   //行扫描周期
   
parameter  V_SYNC_7084   =  11'd2;      //场同步
parameter  V_BACK_7084   =  11'd33;     //场显示后沿
parameter  V_DISP_7084   =  11'd480;    //场有效数据
parameter  V_FRONT_7084  =  11'd10;     //场显示前沿
parameter  V_TOTAL_7084  =  11'd525;    //场扫描周期       
   
// 7' 1024*600   
parameter  H_SYNC_7016   =  11'd20;     //行同步
parameter  H_BACK_7016   =  11'd140;    //行显示后沿
parameter  H_DISP_7016   =  11'd1024;   //行有效数据
parameter  H_FRONT_7016  =  11'd160;    //行显示前沿
parameter  H_TOTAL_7016  =  11'd1344;   //行扫描周期
   
parameter  V_SYNC_7016   =  11'd3;      //场同步
parameter  V_BACK_7016   =  11'd20;     //场显示后沿
parameter  V_DISP_7016   =  11'd600;    //场有效数据
parameter  V_FRONT_7016  =  11'd12;     //场显示前沿
parameter  V_TOTAL_7016  =  11'd635;    //场扫描周期
   
// 10.1' 1280*800   
parameter  H_SYNC_1018   =  11'd10;     //行同步
parameter  H_BACK_1018   =  11'd80;     //行显示后沿
parameter  H_DISP_1018   =  11'd1280;   //行有效数据
parameter  H_FRONT_1018  =  11'd70;     //行显示前沿
parameter  H_TOTAL_1018  =  11'd1440;   //行扫描周期
   
parameter  V_SYNC_1018   =  11'd3;      //场同步
parameter  V_BACK_1018   =  11'd10;     //场显示后沿
parameter  V_DISP_1018   =  11'd800;    //场有效数据
parameter  V_FRONT_1018  =  11'd10;     //场显示前沿
parameter  V_TOTAL_1018  =  11'd823;    //场扫描周期

// 4.3' 800*480   
parameter  H_SYNC_4384   =  11'd128;    //行同步
parameter  H_BACK_4384   =  11'd88;     //行显示后沿
parameter  H_DISP_4384   =  11'd800;    //行有效数据
parameter  H_FRONT_4384  =  11'd40;     //行显示前沿
parameter  H_TOTAL_4384  =  11'd1056;   //行扫描周期
   
parameter  V_SYNC_4384   =  11'd2;      //场同步
parameter  V_BACK_4384   =  11'd33;     //场显示后沿
parameter  V_DISP_4384   =  11'd480;    //场有效数据
parameter  V_FRONT_4384  =  11'd10;     //场显示前沿
parameter  V_TOTAL_4384  =  11'd525;    //场扫描周期    

//reg define
reg  [10:0] h_sync ;
reg  [10:0] h_back ;
reg  [10:0] h_total;
reg  [10:0] v_sync ;
reg  [10:0] v_back ;
reg  [10:0] v_total;
reg  [10:0] h_cnt  ;
reg  [10:0] v_cnt  ;

//wire define    
wire        lcd_en;
wire        data_req;

//*****************************************************
//**                    main code
//*****************************************************

//RGB LCD 采用DE模式时,行场同步信号需要拉高
assign  lcd_hs = 1'b1;        //LCD行同步信号
assign  lcd_vs = 1'b1;        //LCD场同步信号
 
assign  lcd_clk = lcd_pclk;   //LCD像素时钟
assign  lcd_de = lcd_en;      //LCD数据有效信号

//使能RGB565数据输出
assign  lcd_en = ((h_cnt >= h_sync + h_back) && (h_cnt < h_sync + h_back + h_disp)
                  && (v_cnt >= v_sync + v_back) && (v_cnt < v_sync + v_back + v_disp)) 
                  ? 1'b1 : 1'b0;

//请求像素点颜色数据输入  
assign data_req = ((h_cnt >= h_sync + h_back - 1'b1) && (h_cnt < h_sync + h_back + h_disp - 1'b1)
                  && (v_cnt >= v_sync + v_back) && (v_cnt < v_sync + v_back + v_disp)) 
                  ? 1'b1 : 1'b0;

//像素点坐标  
assign pixel_xpos = data_req ? (h_cnt - (h_sync + h_back - 1'b1)) : 11'd0;
assign pixel_ypos = data_req ? (v_cnt - (v_sync + v_back - 1'b1)) : 11'd0;

//RGB565数据输出
assign lcd_rgb = lcd_en ? pixel_data : 16'd0;

//行场时序参数
always @(posedge lcd_pclk) begin
    case(lcd_id)
        16'h4342 : begin
            h_sync  <= H_SYNC_4342; 
            h_back  <= H_BACK_4342; 
            h_disp  <= H_DISP_4342; 
            h_total <= H_TOTAL_4342;
            v_sync  <= V_SYNC_4342; 
            v_back  <= V_BACK_4342; 
            v_disp  <= V_DISP_4342; 
            v_total <= V_TOTAL_4342;            
        end
        16'h7084 : begin
            h_sync  <= H_SYNC_7084; 
            h_back  <= H_BACK_7084; 
            h_disp  <= H_DISP_7084; 
            h_total <= H_TOTAL_7084;
            v_sync  <= V_SYNC_7084; 
            v_back  <= V_BACK_7084; 
            v_disp  <= V_DISP_7084; 
            v_total <= V_TOTAL_7084;        
        end
        16'h7016 : begin
            h_sync  <= H_SYNC_7016; 
            h_back  <= H_BACK_7016; 
            h_disp  <= H_DISP_7016; 
            h_total <= H_TOTAL_7016;
            v_sync  <= V_SYNC_7016; 
            v_back  <= V_BACK_7016; 
            v_disp  <= V_DISP_7016; 
            v_total <= V_TOTAL_7016;            
        end
        16'h4384 : begin
            h_sync  <= H_SYNC_4384; 
            h_back  <= H_BACK_4384; 
            h_disp  <= H_DISP_4384; 
            h_total <= H_TOTAL_4384;
            v_sync  <= V_SYNC_4384; 
            v_back  <= V_BACK_4384; 
            v_disp  <= V_DISP_4384; 
            v_total <= V_TOTAL_4384;             
        end        
        16'h1018 : begin
            h_sync  <= H_SYNC_1018; 
            h_back  <= H_BACK_1018; 
            h_disp  <= H_DISP_1018; 
            h_total <= H_TOTAL_1018;
            v_sync  <= V_SYNC_1018; 
            v_back  <= V_BACK_1018; 
            v_disp  <= V_DISP_1018; 
            v_total <= V_TOTAL_1018;        
        end
        default : begin
            h_sync  <= H_SYNC_4342; 
            h_back  <= H_BACK_4342; 
            h_disp  <= H_DISP_4342; 
            h_total <= H_TOTAL_4342;
            v_sync  <= V_SYNC_4342; 
            v_back  <= V_BACK_4342; 
            v_disp  <= V_DISP_4342; 
            v_total <= V_TOTAL_4342;          
        end
    endcase
end

//行计数器对像素时钟计数
always@ (posedge lcd_pclk or negedge rst_n) begin
    if(!rst_n) 
        h_cnt <= 11'd0;
    else begin
        if(h_cnt == h_total - 1'b1)
            h_cnt <= 11'd0;
        else
            h_cnt <= h_cnt + 1'b1;           
    end
end

//场计数器对行计数
always@ (posedge lcd_pclk or negedge rst_n) begin
    if(!rst_n) begin
        v_cnt <= 11'd0;
        y_start <= 11'd0;
        y_end <= 11'd400;
    end
    else begin
        if(h_cnt == h_total - 1'b1) begin
            if(v_cnt == v_total - 1'b1)begin
                v_cnt <= 11'd0;
    if(y_start==480)begin
    y_start<=10'd0;
    end
    else begin
    y_start<=y_start+1'b1;
    end
    if(y_end==480)begin
    y_end<=10'd0;
    end
    else begin
    y_end<=y_end+1'b1;
    end
            end
            else
                v_cnt <= v_cnt + 1'b1;    
        end
    end    
end

//控制LCD复位信号和背光信号
always@ (posedge lcd_pclk or negedge rst_n) begin
    if(!rst_n)begin 
        lcd_rst <= 0;
        lcd_bl <= 0;
    end
    else begin
        lcd_rst <= 1;
        lcd_bl <= 1;
    end
end
 


endmodule

顶层文件:

module lcd_rgb_colorbar(
    input                sys_clk,     //系统时钟
    input                sys_rst_n,   //系统复位

    //RGB LCD接口
    output               lcd_de,      //LCD 数据使能信号
    output               lcd_hs,      //LCD 行同步信号
    output               lcd_vs,      //LCD 场同步信号
    output               lcd_clk,     //LCD 像素时钟
    inout        [15:0]  lcd_rgb,     //LCD RGB565颜色数据
    output               lcd_rst,
    output               lcd_bl
    
    );                                                      

wire  [15:0]  lcd_id    ;    //LCD屏ID
wire          lcd_pclk  ;    //LCD像素时钟              
wire  [10:0]  pixel_xpos;    //当前像素点横坐标
wire  [10:0]  pixel_ypos;    //当前像素点纵坐标
wire  [10:0]  h_disp    ;    //LCD屏水平分辨率
wire  [10:0]  v_disp    ;    //LCD屏垂直分辨率
wire  [15:0]  pixel_data;    //像素数据
wire  [15:0]  lcd_rgb_o ;    //输出的像素数据
wire  [15:0]  lcd_rgb_i ;    //输入的像素数据
wire  [10:0]  y_start;
wire  [10:0]  y_end;

//*****************************************************
//**                    main code
//*****************************************************

//像素数据方向切换
assign lcd_rgb = lcd_de ?  lcd_rgb_o :  {16{1'bz}};
assign lcd_rgb_i = lcd_rgb;

//读LCD ID模块
rd_id u_rd_id(
    .clk          (sys_clk  ),
    .rst_n        (sys_rst_n),
    .lcd_rgb      (lcd_rgb_i),
    .lcd_id       (lcd_id   )
    );    

//时钟分频模块    
clk_div u_clk_div(
    .clk           (sys_clk  ),
    .rst_n         (sys_rst_n),
    .lcd_id        (lcd_id   ),
    .lcd_pclk      (lcd_pclk )
    );    

//LCD显示模块    
lcd_display u_lcd_display(
    .lcd_pclk       (lcd_pclk  ),
    .rst_n          (sys_rst_n ),
    .pixel_xpos     (pixel_xpos),
    .pixel_ypos     (pixel_ypos),
    .h_disp         (h_disp    ),
    .v_disp         (v_disp    ),
    .y_start        (y_start   ),
    .y_end          (y_end     ),
    .pixel_data     (pixel_data)
    );    

//LCD驱动模块
lcd_driver u_lcd_driver(
    .lcd_pclk      (lcd_pclk  ),
    .rst_n         (sys_rst_n ),
    .lcd_id        (lcd_id    ),
    .pixel_data    (pixel_data),
    .pixel_xpos    (pixel_xpos),
    .pixel_ypos    (pixel_ypos),
    .h_disp        (h_disp    ),
    .v_disp        (v_disp    ),
    .y_start       (y_start   ),
    .y_end         (y_end     ),

    .lcd_de        (lcd_de    ),
    .lcd_hs        (lcd_hs    ),
    .lcd_vs        (lcd_vs    ),   
    .lcd_clk       (lcd_clk   ),
    .lcd_rgb       (lcd_rgb_o ),
    .lcd_rst       (lcd_rst   ),
    .lcd_bl        (lcd_bl)
    );

endmodule

总结

通过本次Verilog编程实现VGA显示的实验,我不仅加深了对VGA协议的理解,而且提升了Verilog编程技能。实验过程中遇到的挑战和问题也让我认识到了硬件设计的复杂性。未来,我希望能够将本次实验的经验应用到更复杂的数字设计项目中,继续探索和学习。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值