基于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编程技能。实验过程中遇到的挑战和问题也让我认识到了硬件设计的复杂性。未来,我希望能够将本次实验的经验应用到更复杂的数字设计项目中,继续探索和学习。