查看: 1207|回复: 0

【案例】 LCD显示叠加图片

[复制链接]
  • TA的每日心情
    开心
    2023-1-4 10:25
  • 签到天数: 6 天

    连续签到: 1 天

    [LV.2]偶尔看看I

    发表于 2022-6-30 11:52:01 | 显示全部楼层 |阅读模式
    分享到:
    本文为明德扬原创文章,转载请注明出处!

    1.1 总体设计
    1.1.1 概述
    液晶显示器是一-种通过液晶和色彩过滤器过滤光源,在平面面板上产生图像的数字显示器。LCD 的构造是在两片平行的玻璃基板当中放置液晶盒,下基板玻璃上设置薄膜晶体管,.上基板玻璃上设置彩色滤光片,通过薄膜晶体管上的信号与电压改变来控制液晶分子的转动方向,从而达到控制每个像素点偏振光出射与否而达到显示目的。与传统的阴极射线管相比,LCD具有占用空间小,低功耗,低辐射,无闪烁,降低视觉疲劳等优点。现在LCD已渐替代CRT成为主流,价格也已经下降了很多,并已充分的普及。

    1.1.2 设计目标
    在7寸LCD显示屏上实现图片显示。
    其中,在显示屏左上角显示明德扬的LOGO图标,在显示屏的中间居中显示字母“E”。

    1.1.3 系统结构框图
    系统结构框图如下所示:


    图一
    1.1.4模块功能
    PLL模块实现功能
    1.       将输入的50MHz时钟分频输出40MHz时钟。

    ROM模块实现功能
    1.       FPGA_rom存储明德扬LOGO的图像数据;

    2.       e_rom存储字母“E”的图像数据。

    LCD驱动模块实现功能
    1、  产生驱动LCD屏显示的时序
    2、  读取ROM里存储的数据并输出显示

    1.1.5顶层信号
      
    信号名
      
    I/O
    位宽
    定义
    clk
    I
    1
    系统工作时钟 50M
    rst_n
    I
    1
    系统复位信号,低电平有效
    hys
    O
    1
    LCD 行时序信号
    vys
    O
    1
    LCD 场时序信号
    lcd_de
    O
    1
    LCD 数据输入使能信号
    lcd_rgb
    O
    24
    LCD RGB信号,RGB格式为使用24位来表示一个像素,RGB分量都用8位表示,取值范围为0-255。
    lcd_dclk
    O
    1
    LCD 数据采样时钟


    1.1.6参考代码
    • module top_mdyLcdPicOverlay(
    •     clk       ,
    •     rst_n     ,
    •     hys       ,
    •     vys       ,
    •     lcd_de    ,
    •     lcd_rgb   ,
    •     lcd_dclk
    •     );

    •     parameter   PICTURE_W = 24  ;

    •     input                   clk         ;
    •     input                   rst_n       ;
    •     output                  hys         ;
    •     output                  vys         ;
    •     output                  lcd_de      ;
    •     output  [PICTURE_W-1:0] lcd_rgb     ;
    •     output                  lcd_dclk    ;
    •    

    •     wire                     clk_0      ;
    •    
    •     wire                     hys        ;
    •     wire                     vys        ;
    •     wire                     lcd_de     ;
    •     wire   [PICTURE_W-1:0]   lcd_rgb    ;
    •     wire                     lcd_dclk   ;


    • //40MHz
    • pll_40m u_pll_40m(
    •             .areset     (~rst_n ),
    •         .inclk0     (clk    ),
    •             .c0         (clk_0  )
    •     );


    • lcd_driver  u2(
    •    .clk          (clk_0       ),//40MHz
    •    .rst_n        (rst_n       ),
    •                           
    •    .hys          (hys         ),  
    •    .vys          (vys         ),  
    •    .lcd_de       (lcd_de      ),                  
    •    .lcd_rgb      (lcd_rgb     ),
    •    .lcd_dclk     (lcd_dclk    )
    •     );

    • endmodule

    复制代码

    1.2 PLL模块设计
    1.2.1接口信号
    下面为PLL的接口信号:

      
    信号名
      
    I/O
    位宽
    定义
    areset
    I
    1
    PLL复位信号,高电平有效
    inclk0
    I
    1
    PLL输入时钟 50MHz
    c0
    O
    1
    PLL输出时钟 40MHz
    1.2.2 设计思路
    本模块主要用于产生LCD驱动时序所需要的时钟,关于PLL的使用详细介绍请看下方链接:
    http://www.fpgabbs.cn/forum.php?mod=viewthread&tid=322&fromuid=100105

    1.3 ROM模块设计
    1.3.1接口信号
      
    信号名
      
    I/O
    位宽
    定义
    address
    I
    16
    ROM数据存放地址
    clock
    I
    1
    ROM工作时钟40MHz
    q
    O
    8
    ROM输出数据
    1.3.2设计思路
    本模块主要用于存储需要显示的图像数据,关于ROM的使用详细介绍请查看IP核右上角数据手册“Documentation”。

    1.4 LCD驱动模块设计
    1.4.1接口信号
      
    信号名
      
    I/O
    位宽
    定义
    clk
    I
    1
    模块工作时钟 40MHz
    rst_n
    I
    1
    系统复位信号,低电平有效
    hys
    O
    1
    LCD 行时序信号
    vys
    O
    1
    LCD 场时序信号
    lcd_de
    O
    1
    LCD 数据输入使能信号
    lcd_rgb
    O
    24
    LCD RGB信号,RGB格式为使用24位来表示一个像素,RGB分量都用8位表示,取值范围为0-255。
    lcd_dclk
    O
    1
    LCD 数据采样时钟
    1.4.2设计思路
    产生驱动LCD显示的行场时序信号,其计数器架构如下图所示:


    行计数器h_cnt:该计数器用来计算行同步信号的帧长。加一条件为1,表示一直在计数。结束条件为数1056个,也就是一行有1056个像素。
    场计数器v_cnt:该计数器用来计算场同步信号的帧长。加一条件为end_h_cnt,即行计数器的计数器的结束条件,表示每计数完一行像素就加一。结束条件为数525个,也就是一共有525行像素。
    其中,在从存储图像“E”的ROM里读取数据的时候,有一个操作就是读取的地址从第8位开始,也就是说18位数据地址,低三位不读,读取的是e_rom_addr[16:3]。有这样一个操作的话就能实现对存储的图像进行8倍的放大显示。

    1.4.3参考代码
    • module lcd_driver(
    •     clk          ,//40MHz
    •     rst_n        ,

    •     hys          ,
    •     vys          ,
    •     lcd_de       ,  
    •     lcd_rgb      ,
    •     lcd_dclk   
    • );

    •    input                    clk             ;
    •    input                    rst_n           ;

    •    output                   hys             ;
    •    output                   vys             ;
    •    output                   lcd_de          ;
    •    output [23:0]            lcd_rgb         ;
    •    output                   lcd_dclk        ;

    •    reg                      hys             ;
    •    reg                      vys             ;
    •    reg                      lcd_de          ;
    •    reg    [23:0]            lcd_rgb         ;
    •    wire                     lcd_dclk        ;

    •    //1056
    •    parameter         THPW      = 20         ;   
    •    parameter         THB       = 46         ;   
    •    parameter         THD       = 800        ;   
    •    parameter         THFP      = 210        ;   
    •    
    •    //525
    •    parameter         TVPW      = 10         ;   
    •    parameter         TVB       = 23         ;   
    •    parameter         TVD       = 480        ;   
    •    parameter         TVFP      = 22         ;   

    •    parameter       HDE_CENTRE  = THD/2      ;//400
    •    parameter       VDE_CENTRE  = TVD/2      ;//240

    •    parameter       LOGO_X0     = (0   + (THB-1))         ;   
    •    parameter       LOGO_X1     = (120 + (THB-1))         ;   
    •    parameter       LOGO_Y0     = (0   + (TVB-1))         ;   
    •    parameter       LOGO_Y1     = (55  + (TVB-1))         ;  

    •    parameter         E_X0      = ((HDE_CENTRE-200) + (THB-1))        ;   
    •    parameter         E_X1      = ((HDE_CENTRE+200) + (THB-1))        ;   
    •    parameter         E_Y0      = ((VDE_CENTRE-150) + (TVB-1))        ;   
    •    parameter         E_Y1      = ((VDE_CENTRE+150) + (TVB-1))        ;   

    •    reg   [ 10:0]            h_cnt           ;
    •    wire                     add_h_cnt       ;
    •    wire                     end_h_cnt       ;
    •    reg   [ 9:0]             v_cnt           ;
    •    wire                     add_v_cnt       ;
    •    wire                     end_v_cnt       ;


    •    wire                     active_area     ;
    •    reg                      logo_rom_area        ;
    •    reg      [15:0]          logo_rom_addr        ;
    •    wire     [7:0]           logo_rom_data        ;
    •    reg                      e_rom_area        ;
    •    reg      [17:0]          e_rom_addr        ;
    •    wire     [7:0]           e_rom_data        ;
    •    reg      [2:0]           e_rom_addr_low    ;
    •    reg                      e_sel             ;
    •    


    • always @(posedge clk or negedge rst_n) begin
    •     if (rst_n==0) begin
    •         h_cnt <= 0;
    •     end
    •     else if(add_h_cnt) begin
    •         if(end_h_cnt)
    •             h_cnt <= 0;
    •         else
    •             h_cnt <= h_cnt+1 ;
    •    end
    • end
    • assign add_h_cnt = 1;
    • assign end_h_cnt = add_h_cnt  && h_cnt == (THB + THD + THFP)-1 ;



    • always @(posedge clk or negedge rst_n) begin
    •     if (rst_n==0) begin
    •         v_cnt <= 0;
    •     end
    •     else if(add_v_cnt) begin
    •         if(end_v_cnt)
    •             v_cnt <= 0;
    •         else
    •             v_cnt <= v_cnt+1 ;
    •    end
    • end
    • assign add_v_cnt = end_h_cnt;
    • assign end_v_cnt = add_v_cnt  && v_cnt == (TVB + TVD + TVFP)-1 ;

    • /*******************************************************/
    •     //dclk
    •     assign lcd_dclk = clk;

    •     //hsync
    •     always  @(posedge clk or negedge rst_n)begin
    •         if(rst_n==1'b0)begin
    •             hys <= 0;
    •         end
    •         else if(add_h_cnt && h_cnt==THPW-1)begin
    •             hys <= 1;
    •         end
    •         else if(end_h_cnt)begin
    •             hys <= 0;
    •         end
    •     end


    •     //vsync
    •     always  @(posedge clk or negedge rst_n)begin
    •         if(rst_n==1'b0)begin
    •             vys <= 0;
    •         end
    •         else if(add_v_cnt && v_cnt==TVPW-1)begin
    •             vys <= 1;
    •         end
    •         else if(end_v_cnt)begin
    •             vys <= 0;
    •         end
    •     end
    •    

    •     //lcd_de
    •     always  @(posedge clk or negedge rst_n)begin
    •         if(rst_n==1'b0)begin
    •             lcd_de <= 0;
    •         end
    •         else if(active_area)begin
    •             lcd_de <= 1;
    •         end
    •         else begin
    •             lcd_de <= 0;
    •         end
    •     end
    •    

    • /********************************************************************/   



    • assign active_area = h_cnt>=(THB-1) && h_cnt<(THB+THD-1) && v_cnt>=(TVB-1) && v_cnt<(TVB+TVD-1);


    • always  @(*)begin
    •     logo_rom_area = h_cnt >=LOGO_X0 && h_cnt < LOGO_X1 && v_cnt >= LOGO_Y0 && v_cnt < LOGO_Y1;
    • end

    • always  @(*)begin
    •     e_rom_area = h_cnt >=E_X0 && h_cnt < E_X1 && v_cnt >= E_Y0 && v_cnt < E_Y1;
    • end



    • always  @(posedge clk or negedge rst_n)begin
    •     if(rst_n==1'b0)begin
    •         lcd_rgb <= 0;
    •     end
    •     else if(active_area)begin
    •         if(logo_rom_area)
    •             lcd_rgb <= {logo_rom_data[7:5],5'b11111,logo_rom_data[4:2],5'b11111,logo_rom_data[1:0],6'b111111};
    •         else if(e_rom_area)
    •             lcd_rgb <= {24{e_sel}};
    •         else
    •             lcd_rgb <= {24{1'b1}};
    •     end
    •     else begin
    •         lcd_rgb <=0;
    •     end
    • end




    • always  @(*)begin
    •     logo_rom_addr = (h_cnt-LOGO_X0) + 120*(v_cnt-LOGO_Y0);
    • end

    • always  @(*)begin
    •     e_rom_addr = (h_cnt-E_X0) + 400*(v_cnt-E_Y0);
    • end



    • always  @(posedge clk or negedge rst_n)begin
    •     if(rst_n==1'b0)begin
    •         e_rom_addr_low <= 0;
    •     end
    •     else begin
    •         e_rom_addr_low <= e_rom_addr[2:0];
    •     end
    • end


    • always  @(*)begin
    •     e_sel = ~e_rom_data[7-e_rom_addr_low];
    • end


    • fpga_rom u_fpga_rom(
    •                    .address (logo_rom_addr),
    •                    .clock   (clk     ),
    •                    .q       (logo_rom_data));

    • e_rom u_e_rom(
    •     .address   (e_rom_addr[16:3]  ),
    •     .clock     (clk  ),
    •     .q         (e_rom_data  ));


    •     endmodule

    复制代码

    1.5 效果和总结
    以下为工程上板后的现象效果图:

    mp801开发板

    ms980试验箱



    设计视频源工程代码请到论坛下载学习:http://www.fpgabbs.cn/thread-1162-1-1.html
    访问明德扬论坛(http://www.fpgabbs.cn/)进行更多FPGA相关工程设计学习。

    需要更多案例请联系易老师领取:13112063618
    明德扬科技教育

    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /5 下一条



    手机版|小黑屋|与非网

    GMT+8, 2024-12-24 02:19 , Processed in 0.122573 second(s), 15 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.