目录
总体设计
读显示屏ID
读显示屏ID代码
时钟分频
时钟分频代码
LCD显示
lcd显示模块
LCD驱动模块
lcd驱动代码
顶层模块
顶层模块代码
总体设计
系统总体分为五个模块,分别是:rd_id(读显示屏ID模块),clk_div(时钟分频模块),lcd_display(lcd屏显示模块),lcd_driver(lcd屏驱动模块),和顶层模块lcd_rgb。
总体框图如下:
读显示屏ID
因为不同的LCD屏有着不同的分辨率,并对应了不同的时序参数,我们需要读取LCD_B7/G7/R7这三个管脚的状态,来判断当前显示屏的型号。
查找IO分配表可知LCD_B7/G7/R7分别为lcd_rgb[4],lcd_rgb[10],lcd_rgb[15]。
读显示屏ID代码
module rd_id(
input wire sys_clk, //系统时钟
input wire sys_rst_n, //系统复位
input wire [15:0] lcd_rgb, //初始像素数据,用于读取ID
output reg [15:0] lcd_id //读取到的ID
);
reg rd_flag; //读ID标志
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
rd_flag <= 1'b0;
lcd_id <= 16'b0;
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’ RES 480*272
3'b001: lcd_id <= 16'h7084; //7’ RES 800*480
3'b010: lcd_id <= 16'h7016; //7’ RES 1024*600
3'b100: lcd_id <= 16'h4384; //4.3’ RES 800*480
3'b101: lcd_id <= 16'h1018; //10’ RES 1280*800
default : lcd_id <= 16'b0;
endcase
end
end
end
endmodule
时钟分频
不同分辨率有着不同的时序参数,为了提高兼容性,我增加了时钟分频模块,能根据传入的ID号来产生对应所需的时钟。
其中时钟分频模块采用了quartus的IP核。
时钟分频代码
module clk_div(
input wire sys_clk, //系统时钟50MHz
input wire sys_rst_n, //系统复位
input wire [15:0] lcd_id, //ID号
output reg lcd_pclk //lcd驱动时钟
);
wire clk_10;
wire clk_34;
wire clk_50;
wire clk_70;
wire locked;
pll_clk pll_clk_inst (
.areset ( ~sys_rst_n ), //锁相环高电平复位
.inclk0 ( sys_clk ),
.c0 ( clk_10 ),
.c1 ( clk_34 ),
.c2 ( clk_50 ),
.c3 ( clk_70 ),
.locked ( locked )
);
//---------根据分辨率选择对应的时钟-----------//
always @(*) begin
if(locked) begin
case(lcd_id)
16'h4342 : lcd_pclk = clk_10; //9M
16'h7084 : lcd_pclk = clk_34; //33.3M
16'h7016 : lcd_pclk = clk_50; //50M
16'h4384 : lcd_pclk = clk_34; //33.3M
16'h1018 : lcd_pclk = clk_70; //70M
default : lcd_pclk = 1'b0;
endcase
end
else begin
lcd_pclk = 1'b0;
end
end
endmodule
LCD显示
显示的图案可以根据自己喜好设置,需要注意的就是h_disp和v_disp这两个信号,可以理解成LCD屏的有效显示边框/范围。原来是根据例程写了五等分的彩条(注释部分),后来图好玩改成了法国国旗,参数中的蓝色和红色我也改过了。
因为电脑自带画板显示的一般都是RGB888,但是LCD显示屏用的是RGB565,两者的位宽不一样。但RGB888转RGB565的方法也很简单,就是分别取8位红绿蓝的高位。
lcd显示模块代码
module lcd_display(
input wire lcd_pclk, //不同分辨率对应的时钟
input wire sys_rst_n, //系统复位信号
input wire [10:0] pixel_xpos, //当前像素点横坐标 (像素点数最大为1440,11位)
input wire [10:0] pixel_ypos, //当前像素点纵坐标
input wire [10:0] h_disp, //LCD屏水平分辨率
input wire [10:0] v_disp, //LCD屏垂直分辨率
output reg [15:0] pixel_data //像素数据
);
//parameter define
parameter WHITE = 16'b11111_111111_11111; //白色
parameter BLACK = 16'b00000_000000_00000; //黑色
parameter RED = 16'b11111_001111_00110; //红色
parameter GREEN = 16'b00000_111111_00000; //绿色
parameter BLUE = 16'b00000_010101_10100; //蓝色
parameter YELLOW = 16'b11111_111111_00000; //黄色
always @(posedge lcd_pclk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
pixel_data <= BLACK;
end
else begin
// if(pixel_xpos > 11'b0 && pixel_xpos < h_disp/5*1)
// pixel_data <= RED;
// else if(pixel_xpos > h_disp/5*1 && pixel_xpos < h_disp/5*2)
// pixel_data <= YELLOW;
// else if(pixel_xpos > h_disp/5*2 && pixel_xpos < h_disp/5*3)
// pixel_data <= BLUE;
// else if(pixel_xpos > h_disp/5*3 && pixel_xpos < h_disp/5*4)
// pixel_data <= GREEN;
// else
// pixel_data <= WHITE;
if(pixel_xpos > 11'b0 && pixel_xpos < h_disp/3*1)
pixel_data <= BLUE;
else if(pixel_xpos > h_disp/3*1 && pixel_xpos < h_disp/3*2)
pixel_data <= WHITE;
else
pixel_data <= RED;
end
end
endmodule
LCD驱动模块
每个时钟内会扫描一个像素点,所以需要设置计数器对扫到的像素点进行计数。根据计数值可以圈定LCD屏的有效区域,当处在这个范围之内时,lcd_de拉高,将传入的像素数据pixel_data通过数据端口lcd_rgb传出至屏幕。
在这里加一段别的博主对于lcd_req设置的理解,里面提到的子母计数器是h_cnt和v_cnt。
//...此处做一个简单的分析,lcd_de拉高,lcd_rgb[15:0]的值瞬间到达屏幕的对应像素点上。
// 我们最开始的分析是:cnt值->产生坐标->根据坐标确认有效范围->再往范围里放颜色值
// 所以说我们在上面写的那个就是有效范围,由于触发器的特性,所以要提前一个时钟周期将坐标值送出,才来的及在lcd_en为1时候准确的送出颜色值
// 而一场是由很多行组成的,母计数器,要比子计数器慢。子计数器变很多次,母计数器才变一次。所以对子计数器需要提前一个时钟周期发送出坐标。
// PS:如果没懂的话也没关系,因为我也比较晕。但只要时序对,屏幕就能出现颜色然后进行simulation或者实物调试
————————————————
版权声明:本文为CSDN博主「搞IC的那些年」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/helloworld573/article/details/113773634
lcd驱动代码
module lcd_driver(
input wire lcd_pclk, //不同分辨率对应的时钟
input wire sys_rst_n, //系统复位信号
input wire [15:0] lcd_id, //不同显示屏对应的ID
input wire [15:0] pixel_data, //像素数据
output wire [10:0] pixel_xpos, //当前像素点横坐标 (像素点数最大为1440,11位)
output wire [10:0] pixel_ypos, //当前像素点纵坐标
output reg [10:0] h_disp, //LCD屏水平分辨率
output reg [10:0] v_disp, //LCD屏垂直分辨率
output wire lcd_de, //RGB LCD数据使能
output wire lcd_hs, //RGB LCD行同步
output wire lcd_vs, //RGB LCD场同步
output wire lcd_clk, //RGB LCD像素时钟
output wire lcd_bl, //RGB LCD背光控制
output wire lcd_rst, //RGB LCD系统复位,低有效
output wire [15:0] lcd_rgb //RGB565颜色数据
);
wire lcd_en;
wire data_req;
//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 ;
//数据使能
assign lcd_de = lcd_en;
//行场同步
assign lcd_hs = 1'b1;
assign lcd_vs = 1'b1;
//像素时钟
assign lcd_clk = lcd_pclk;
//复位和背光
assign lcd_rst = (sys_rst_n == 1'b0) ? 1'b0 : 1'b1;
assign lcd_bl = (sys_rst_n == 1'b0) ? 1'b0 : 1'b1;
//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; //场扫描周期
//行场时序参数
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 sys_rst_n) begin
if(!sys_rst_n) begin
h_cnt <= 11'b0;
end
else begin
h_cnt <= (h_cnt == h_total - 1'b1) ? 11'b0 : h_cnt + 1'b1;
end
end
//场计数器对场像素点进行计数
always @(posedge lcd_pclk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
v_cnt <= 11'b0;
end
else if(h_cnt == h_total - 1'b1)begin
v_cnt <= (v_cnt == v_total - 1'b1) ? 11'b0 : v_cnt + 1'b1;
end
end
//使能RGB565数据输出,lcd_en表示区域内像素点有效
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;
//请求像素点颜色数据输入,提前一时钟将数据载入到lcd驱动模块
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;
//RGB565数据输出
assign lcd_rgb = lcd_en ? pixel_data : 16'b0;
//像素点坐标
assign pixel_xpos = data_req ? (h_cnt - (h_sync + h_back - 1'b1)) : 11'b0;
assign pixel_ypos = data_req ? (v_cnt - (v_sync + v_back - 1'b1)) : 11'b0;
endmodule
顶层模块
比较需要注意的就是这里要把lcd_rgb定义为inout类型。当lcd_de信号为高电平时,此时输出的像素数据有效,将lcd_rgb的引脚方向切换成输出,并将LCD驱动模块输出的lcd_rgb_o(像素数据)连接至lcd_rgb引脚;当lcd_de信号为低电平时,此时输出的像素数据无效,将lcd_rgb 的引脚方向切换成输入。代码中将高阻状态“Z”赋值给lcd_rgb的引脚,表示此时lcd_rgb的引脚电平由外围电路决定,此时可以读取lcd_rgb的引脚电平,从而获取到LCD屏的ID。
顶层模块代码
module lcd_rgb(
input wire sys_clk, //系统时钟
input wire sys_rst_n, //系统复位信号
output wire lcd_de, //RGB LCD数据使能
output wire lcd_hs, //RGB LCD行同步
output wire lcd_vs, //RGB LCD场同步
output wire lcd_clk, //RGB LCD像素时钟
output wire lcd_bl, //RGB LCD背光控制
output wire lcd_rst, //RGB LCD系统复位,低有效
inout wire [15:0] lcd_rgb //RGB565颜色数据
);
wire [15:0] lcd_rgb_i;
wire [15:0] lcd_rgb_o;
wire [15:0] lcd_id;
wire lcd_pclk; //分频时钟
wire [10:0] pixel_xpos;
wire [10:0] pixel_ypos;
wire [10:0] h_disp;
wire [10:0] v_disp;
wire [15:0] pixel_data;
//数据双向端口
assign lcd_rgb = lcd_de ? lcd_rgb_o : 16'bz;
assign lcd_rgb_i = lcd_rgb;
rd_id u_rd_id(
.sys_clk(sys_clk),
.sys_rst_n(sys_rst_n),
.lcd_rgb(lcd_rgb_i),
.lcd_id(lcd_id)
);
clk_div u_clk_div(
.sys_clk(sys_clk), //50MHz
.sys_rst_n(sys_rst_n),
.lcd_id(lcd_id),
.lcd_pclk(lcd_pclk)
);
lcd_display u_lcd_display(
.lcd_pclk(lcd_pclk), //不同分辨率对应的时钟
.sys_rst_n(sys_rst_n), //系统复位信号
.pixel_xpos(pixel_xpos), //当前像素点横坐标 (像素点数最大为1440,11位)
.pixel_ypos(pixel_ypos), //当前像素点纵坐标
.h_disp(h_disp), //LCD屏水平分辨率
.v_disp(v_disp), //LCD屏垂直分辨率
.pixel_data(pixel_data) //像素数据
);
lcd_driver u_lcd_driver(
.lcd_pclk(lcd_pclk), //不同分辨率对应的时钟
.sys_rst_n(sys_rst_n), //系统复位信号
.lcd_id(lcd_id), //不同显示屏对应的ID
.pixel_data(pixel_data), //像素数据
.pixel_xpos(pixel_xpos), //当前像素点横坐标 (像素点数最大为1440,11位)
.pixel_ypos(pixel_ypos), //当前像素点纵坐标
.h_disp(h_disp), //LCD屏水平分辨率
.v_disp(v_disp), //LCD屏垂直分辨率
.lcd_de(lcd_de), //RGB LCD数据使能
.lcd_hs(lcd_hs), //RGB LCD行同步
.lcd_vs(lcd_vs), //RGB LCD场同步
.lcd_clk(lcd_clk), //RGB LCD像素时钟
.lcd_bl(lcd_bl), //RGB LCD背光控制
.lcd_rst(lcd_rst), //RGB LCD系统复位,低有效
.lcd_rgb(lcd_rgb_o) //RGB565颜色数据
);
endmodule
附一张野火画的框图文章来源:https://www.toymoban.com/news/detail-593447.html
文章来源地址https://www.toymoban.com/news/detail-593447.html
到了这里,关于FPGA实现LCD显示屏显示彩条的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!