FPGA学习--RGB-LCD屏彩条显示实验

news/2024/11/8 20:43:28/

FPGA学习--RGB-LCD屏彩条显示实验

      • RGB-LCD屏原理
      • 程序设计

参考正点原子视频

RGB-LCD屏原理

LCD 是一种液晶显示屏,它采用薄膜晶体管(TFT)技术提升图像质量,如提高图像亮度和对比度等。相比于传统的 CRT 显示器,LCD 有着轻薄、功耗低、无辐射、图像质量好等诸多优点,因此广泛应用于电视机、电脑显示器、手机等各种显示设备中。

其显示的每个像素点都由集成在液晶后面的薄膜晶体管独立驱动。

  1. 分辨率
    如720K、1080K、2K或4K。一个像素点代表一个灯,每个灯的颜色由RGB三个颜色构成。1080P的意思就是一个LCD屏幕上的像素数量是1920*1080个。

一共有 1920 * 1080=2073600 个像素点(每一行1920个,共有1080行)。2K 就是 2560*1440 个像素点,4K 是 3840 * 2160 个像素点。很明显,在 LCD 尺寸不变的情况下,分辨率越高越清晰。同样的,分辨率不变的情况下,LCD 尺寸越小越清晰。由此可见,LCD 显示器的分辨率是一个很重要的参数,但是并不是分辨率越高的 LCD 就越好。衡量一款 LCD 的好坏,分辨率只是其中的一个参数,还有色彩还原程度、色彩偏离、亮度、可视角度、屏幕刷新率等其他参数。

  1. 像素格式
    控制RGB三种颜色的亮度就可以显示各种色彩。那么如何控制呢?一般一个R,G,B这三部分分别使用8bit的数据,那么一个像素点就是24bit。也就是一个像素点需要 3个字节,这种像素格式称为RGB888。常见的像素格式也有RGB565,只需要两个字节,就是色彩对比度上差一点。
    红色对应的值就是 24’hFF0000, 24’h00FF00对应蓝色,绿色对应的值为 24’h0000FF。通过调节 R、G、B 的比例可以产生其它的颜色,比如 24’hFFFF00 就是黄色,黑色就是24’h000000,24’hFFFFFF 就是白色。大家可以打开电脑的“画图”工具,在里面使用调色板即可获取到想要的颜色对应的数值。

  2. LCD屏幕接口
    显示器的接口有VGA、HDMI、DP等接口,这次我们用的是RGB-LCD接口。
    其中,如下图,。默认情况,R1 和 R6 焊接,设置 LCD_LR 和
    LCD_UD,控制 LCD 的扫描方向,是从左到右,从上到下(横屏看)。而 LCD_R7/G7/B7 则用来设置 LCD的 ID。由于 RGBLCD 没有读写寄存器,也就没有所谓的 ID,这里我们通过在模块上面,控制 R7/G7/B7 的 上/下拉,来自定义 LCD 模块的 ID,帮助 MCU 判断当前 LCD 面板的分辨率和相关参数,以提高程序兼容性。

4384的话,设置为100即可

  1. LCD时间参数
    如何显示一帧图像呢?就像用一支笔从左到右,从上到下地进行绘画。HSYNC是行同步信号(水平同步信号),产生此信号代表开始显示新的一行了。VSYNC是帧同步信号(垂直同步信号),产生此信号代表开始新的一帧图像了。
    但是可以看到旁边有一圈黑边,这是什么呢?其实是延时信号,IC执行指令需要反应时间(需要时间来识别一行数据扫描完了或一帧数据来继续下一步操作),所以当HSYNC结束之后会产生一段延时,产生之前也会产生。(HBP和HFP)VSYNC也是同理,产生的延时是VFP和VBP。–锁定有效的像素数据。
    在这里插入图片描述

  2. RGB-LCD屏幕时序
    行显示的时序图是以CLK为单位的,而帧显示的时序图是以行为单位的。具体时序如下两图所示。需要注意的是,有两种数据同步方式。一种是行场同步模式,另一种是DE同步模式
    在行场同步模式中,行同步信号和场同步信号作为数据传输的同步信号,此时DE必须为低电平。DE同步模式,DE作为数据的有效信号,只有同时扫描到帧有效显示区域和行有效显示区域,DE信号才有效(为高电平)。当选择此模式时,VS和HS都必须为高电平。
    一般都采用DE模式,行场同步和VGA接口的协议有点类似。

  3. 像素时钟
    像素时钟是时钟信号。
    N CLK))= (VSPW+VBP+LINE+VFP) * (HSPW + HBP + HOZVAL + HFP)= (2 + 128 + 480 + 10) * (33 + 88 + 800 + 40) = 620 * 961 = 578522
    –以ATK4384为例。(800480,LIINE等于480,HOZVAL等于800)
    显示一帧时钟需要578522个时钟数,显示60帧是:578522
    60=34711320约等于34.7M,所以像素时钟就是34.7MHZ。
    当然严格上也不用按照60帧来计算,我们可以给个33.3HZ,其刷新率也在60帧左右。
    以下是不同分辨率的时序参数。
    在这里插入图片描述

程序设计

分为5个模块。顶层模块、读取ID模块、时钟分频模块、LCD显示模块、LCD驱动模块。其中输入信号包括3个要素:像素时钟、同步信号和图像数据。
系统框图如下图:
在这里插入图片描述
rgb_top.v

assign lcd_rgb = lcd_de ? lcd_rgb_o : {24{1'bz}};
assign lcd_rgb_i = lcd_rgb;

此处把rgb的引脚定为双向的状态,如果de为低电平,引脚状态由外围电路决定(也就是赋高阻态);当de为高电平,此时写入的数据将会有效地赋给输入了,下面也用到了一个简单的译码器。
rd_id.v

//reg define
reg            rd_flag;  //读ID标志//获取LCD ID   M2:B7  M1:G7  M0:R7
always @(posedge clk or negedge rst_n)beginif(!rst_n)beginrd_flag <= 1'b0;lcd_id <= 15'b0;endelse beginif(rd_flag == 1'b0)beginrd_flag = 1'b1;case({lcd_rgb[7],lcd_rgb[15],lcd_rgb[23]})3'b000 : lcd_id <= 16'h4342;    //4.3' RGB LCD  RES:480x2723'b001 : lcd_id <= 16'h7084;    //7'   RGB LCD  RES:800x4803'b010 : lcd_id <= 16'h7016;    //7'   RGB LCD  RES:1024x6003'b100 : lcd_id <= 16'h4384;    //4.3' RGB LCD  RES:800x4803'b101 : lcd_id <= 16'h1018;    //10'  RGB LCD  RES:1280x800default : lcd_id <=16'h0;endcaseendend
end

根据对输入的rgb值进行译码来赋id
clk_div.v

reg clk_25m;
reg clk_12_5m;
reg div_4_cnt;//时钟2分频,输出25MHZ时钟
always @(posedge clk or negedge rst_n)beginif(!rst_n)clk_25m <= 1'b0;elseclk_25m <= ~clk_25m;
endalways @(posedge clk or negedge rst_n)beginif(!rst_n) begindiv_4_cnt <= 1'b0;clk_12_5m <= 1'b0;endelse begin div_4_cnt <= div_4_cnt + 1'b1;if(div_4_cnt == 1'b1)clk_12_5m <= ~clk_12_5m;end
endalways @(*) begin //(*)代表包括两种触发方式,边沿触发和电平触发都包括,代表always模块内任何一个输入信号或电平发生变化都会触发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

此处时钟分频也可采用 PLL IP核的方式,后面会更新。
lcd_display.v

//parameter define  
parameter WHITE = 24'hFFFFFF;  //白色
parameter BLACK = 24'h000000;  //黑色
parameter RED   = 24'hFF0000;  //红色
parameter GREEN = 24'h00FF00;  //绿色
parameter BLUE  = 24'h0000FF;  //蓝色//根据当前像素点坐标指定当前像素点颜色数据,在屏幕上显示彩条
always @(posedge lcd_pclk or negedge rst_n) begin //使用指定的时钟频率,代表显示的帧率if(!rst_n)pixel_data <= BLACK;else beginif((pixel_xpos >= 11'd0) && (pixel_xpos < h_disp/5*1))pixel_data <= WHITE;else if((pixel_xpos >= h_disp/5*1) && (pixel_xpos < h_disp/5*2))    pixel_data <= BLACK;else if((pixel_xpos >= h_disp/5*2) && (pixel_xpos < h_disp/5*3))    pixel_data <= RED;   else if((pixel_xpos >= h_disp/5*3) && (pixel_xpos < h_disp/5*4))    pixel_data <= GREEN;                else pixel_data <= BLUE;      end
end

在不同的区域显示不同颜色,最后形成色条。如果需要改变显示的画面,修改这一文件即可。
lcd_driver.v

//PARAMETER DEFINE// 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;
//RGB LCD 采取DE模式时,行场同步信号需要拉高
assign lcd_hs = 1'b1; //LCD行同步信号
assign lcd_vs = 1'b1; //LCD场同步信号//assign  lcd_bl = 1'b1;        //LCD背光控制信号  
assign  lcd_clk = lcd_pclk;   //LCD像素时钟
assign  lcd_de = lcd_en;      //LCD数据有效信号//使能RGB888数据输出
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;//RGB888数据输出
assign lcd_rgb = lcd_en ? pixel_data : 24'd0;//行场时序参数
always @(posedge lcd_pclk) begincase(lcd_id)16'h4342 : beginh_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;            end16'h7084 : beginh_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;        end16'h7016 : beginh_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;            end16'h4384 : beginh_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 : beginh_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;        enddefault : beginh_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;          endendcase
end//行计数器对像素时钟计数 计数CLK,行的计数是以CLK为单位的
always@ (posedge lcd_pclk or negedge rst_n) beginif(!rst_n) h_cnt <= 11'd0;else beginif(h_cnt == h_total - 1'b1)h_cnt <= 11'd0;elseh_cnt <= h_cnt + 1'b1;           end
end//场计数器对行计数 帧的计数是以行为单位的
always@ (posedge lcd_pclk or negedge rst_n) beginif(!rst_n) v_cnt <= 11'd0;else beginif(h_cnt == h_total - 1'b1) beginif(v_cnt == v_total - 1'b1)v_cnt <= 11'd0;elsev_cnt <= v_cnt + 1'b1;    endend    
end
always@ (posedge lcd_pclk or negedge rst_n) beginif(!rst_n)begin lcd_rst<=0;lcd_bl<=0;endelse beginlcd_rst<=1;lcd_bl<=1;endend

采用DE同步模式进行驱动。
注:输出输入端口的定义请参考顶层模块原理图。

补充:显示图片汉字的实验
汉字和图片不过就是一些点阵,用取字模软件可以得到想要的汉字的字模数据(最好是一个整体)。之后将这些数据导入RAM,由于RAM内存有限,所以我们采用的图片的分辨率是100*100。
配置ROM参考RAN IP核学习部分。
之后我们需要改动的就只有lcd_display部分了。
lcd_display.v

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/01/15 02:14:34
// Design Name: 
// Module Name: lcd_display
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//module lcd_display(input                lcd_pclk,    //时钟input                rst_n,       //复位,低电平有效input        [10:0]  pixel_xpos,  //当前像素点横坐标input        [10:0]  pixel_ypos,  //当前像素点纵坐标       output  reg  [23:0]  pixel_data   //像素数据);//parameter define  
localparam PIC_X_START = 11'd1; //图片起始点横坐标
localparam PIC_Y_START = 11'd1; //图片起始点纵坐标
localparam PIC_WIDTH = 11'd100; //图片宽度
localparam PIC_HEIGHT = 11'd100; //图片高度localparam CHAR_X_START= 11'd1; //字符起始点横坐标
localparam CHAR_Y_START= 11'd110; //字符起始点纵坐标
localparam CHAR_WIDTH = 11'd128; //字符宽度,4个字符:32*4
localparam CHAR_HEIGHT = 11'd32; //字符高度localparam BACK_COLOR = 24'hE0FFFF; //背景色,浅蓝色
localparam CHAR_COLOR = 24'hff0000; //字符颜色,红色//reg define
reg [127: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 [23:0] rom_rd_data ;//ROM数据//********************************************
//**              main code
//********************************************assign x_cnt = pixel_xpos - CHAR_X_START; //像素点相对于字符区域起始点水平坐标
assign y_cnt = pixel_ypos - CHAR_Y_START; //像素点相对于字符区域起始点垂直坐标
assign rom_rd_en = 1'b1; //读使能拉高,即一直读ROM数据//给字符数组赋值,显示汉字“集创赛”,每个汉字大小为32*32
always @(posedge lcd_pclk) beginchar[0 ]  <= 128'h00000000000000000000000000000000;char[1 ]  <= 128'h00000000000000000040000000400000;char[2 ]  <= 128'h00030000007180000070001800018000;char[3 ]  <= 128'h0060C00000E000180400803000C0C000;char[4 ]  <= 128'h00D8001807FFFFF801C04020018E0018;char[5 ]  <= 128'h0400003001FFFFF0018300180C0C1840;char[6 ]  <= 128'h0380C00001018418080810800780C080;char[7 ]  <= 128'h0301C61803FFFFC005FFFFC002008418;char[8 ]  <= 128'h000810000980C0000400041800081080;char[9 ]  <= 128'h1180C0000C02041801FFFFC06180C080;char[10]  <= 128'h0FFF04180008100001FFFFC016060418;char[11]  <= 128'h000810100180C000260604183FFFFFF8;char[12]  <= 128'h0180C060460604180018080001FFFFF0;char[13]  <= 128'h0606041800300C000181000006060418;char[14]  <= 128'h006006000181C0000606041800FFFF80;char[15]  <= 128'h0001800806060418032005E03FFFFFFC;char[16]  <= 128'h063C04180621047E0007A00006180418;char[17]  <= 128'h18218418000D90000600401860210400;char[18]  <= 128'h00198800060040180023040000718C00;char[19]  <= 128'h060040180023040000C1830006004018;char[20]  <= 128'h0006E800038181E00200C018000E1E00;char[21]  <= 128'h0601807E03FFE3F80038078038018018;char[22]  <= 128'h03FFC0F000E001804001000000000020;char[23]  <= 128'h07000080000000000000000000000000;char[24]  <= 128'h07000080000000000000000000000000;char[25]  <= 128'h07000080000000000000000000000000;char[26]  <= 128'h07000080000000000000000000000000;char[27]  <= 128'h07000080000000000000000000000000;char[28]  <= 128'h07000080000000000000000000000000;char[29]  <= 128'h07000080000000000000000000000000;char[30]  <= 128'h07000080000000000000000000000000;char[31]  <= 128'h07000080000000000000000000000000;
end//为LCD不同显示区域绘制图片、字符和背景色
always @(posedge lcd_pclk or negedge rst_n) beginif (!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)) beginif(char[y_cnt][CHAR_WIDTH -1'b1 - x_cnt])pixel_data <= CHAR_COLOR; //显示字符elsepixel_data <= BACK_COLOR; //显示字符区域的背景色endelsepixel_data <= BACK_COLOR; //屏幕背景色
end//根据当前扫描点的横纵坐标为ROM地址赋值
always @(posedge lcd_pclk or negedge rst_n) beginif(!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:存储图片
blk_mem_gen_0  blk_mem_gen_0 (.clka  (lcd_pclk),    // input wire clka.ena   (rom_rd_en),   // input wire ena.addra (rom_addr),    // input wire [13 : 0] addra.douta (rom_rd_data)  // output wire [23 : 0] douta
);    endmodule

如代码所示,图片和字符分别显示。字符的显示方法是字符数组,图片是把初始值导入ROM中。需要设定为参数的是图片起始点位置和宽和高,以及字符起始点位置和宽和高,x_cnt和y_cnt是用来给字符点阵赋值的,1则显示红色,0则显示背景色。
在这里插入图片描述


http://www.ppmy.cn/news/497900.html

相关文章

嵌入式分享合集90

带着废品zhang看中医~~ 现在这中医骗子太多 太忽悠 就是卖药的 就和xinguan一样 告诉你多严重吓唬你 好了言归正传 一、电容的作用 电容是电路设计中最为普通常用的器件&#xff0c;是无源元件之一&#xff0c;有源器件简单地说就是需能(电)源的器件叫有源器件&#xff0c;无…

根据电子设计大赛心率检测的设计实践思路

该装置具有以下几个特征&#xff1a; &#xff08;1&#xff09;、电路简单、系统整体功耗低、发热量低、可以稳定连续运行&#xff1b; &#xff08;2&#xff09;、使用新型、主频高的控制芯片&#xff0c;能在芯片上实现数据滤波 &#xff08;3&#xff09;、设计安卓端上…

AD(Altium Designer)电源类电路设计

AD电源类电路设计 电路电源分类LDO电源类开关电源类AC-DC电源AC-AC电源DC-DC电源DC-AC电源 总结 电路电源分类 在电路中&#xff0c;电源是保证电路稳定运行的最为重要的部分之一&#xff0c;只有保证电源输出的好坏&#xff0c;才能保证系统的正常运行&#xff0c;所以这次先讲…

彻底弄懂TCP(多图详解)

概述 做IT相关的工作&#xff0c;肯定都离不开网络&#xff0c;网络中最重要的协议是TCP。无论是实际工作还是笔试面试&#xff0c;你看哪里能少得了TCP? 其实&#xff0c;彻底搞透了TCP之后&#xff0c;就会发现它也就那么回事。 计网分层 物理层 考虑最简单的情况&…

ROS2机器人笔记20-12-11

机器人技术逐步可靠和稳定&#xff0c;军事应用也就必不可免&#xff08;军事研发一直没有间断过哦&#xff09;。一大批机器人战士已经加入到人类战争中。 空军已经大量使用各类机器人&#xff1a; 画图神器发布3.0版支持ros2.0 dashing和foxy 仿真工具重心转向Ignition 其最…

FPGA/ASIC笔试面试题集锦(1)知识点高频复现练习题

文章目录 写在前面正文内容简述ASIC设计流程&#xff0c;并列举出各部分用到的工具&#xff1f;简述FPGA的开发流程&#xff1f;名词解释数制转换什么是竞争和冒险&#xff1f;如何消除&#xff1f;2分频描述简述建立时间和保持时间&#xff0c;画图表示&#xff1f;简述触发器…

推荐五款你从未见过的嵌入式电子电路仿真APP

摘要&#xff1a;在这个人人一部甚至多部智能手机的年代&#xff0c;各种APP充斥在各种应用市场作为一名电子爱好者或者电子工程师&#xff0c;你在为如何选择APP而头痛吗&#xff1f;哪些你正在使用的APP使你在工作、生活、学习中如虎添翼呢&#xff1f;现在&#xff0c;小师弟…

只谈核数没意义 带你重新认识手机SoC

你的手机是几核的&#xff1f;在比较两款手机区别时&#xff0c;这是我们最常问的一个问题。CPU核心数量的多寡的确是衡量手机性能的重要指标&#xff0c;但却不是最准确的指标。 以市面上最常见的高通骁龙处理器为例&#xff0c;在整个“处理器”中&#xff0c;CPU部分只占芯片…