开发板实战篇4 RGB565 LCD刷颜色数据

 总结:

        模块例化思想: 例化1个定时器模块 + LCD驱动 +锁相环

        根据自己思路编写代码,调试仿真代码,同时熟悉环境.加深细节理解 

         刚开始写得很乱,代码完全就是用modelsim仿真调试出来的     

        虽然项目开发过MCU的多种GUI,但通过编写verilog代码驱动LCD,对RGB LCD的行、场时序参数及屏幕怎么就亮了有了本质的理解、认识。

通过verilog并行代码来直接驱动外设直观、深刻、简单
C语言配置寄存器去操作外设复杂、抽象

        写这个LCD驱动模块,其实可以完全参考STM32 LTDC框图来定义输入、输出(特别是输出 ), 一直觉得STM32的硬件架构设计得是相当牛比,这一点在这里再次得到了印证。

        结合这些框图的理解,肯定可以写出更好的RTL代码。仔细看下面Figure 245这幅图,似乎看到了LTDC里面有跨时钟域,目前对这一块的理解只是停留在听说过,先留下 一篇帖子 后续再研究(54条消息) FPGA——浅谈跨时钟域_three_yanlili的博客-CSDN博客_fpga跨时钟域

        

 将背光及复位信号单独抽出来   

output lcd_bl,
output lcd_rst,

这样可以使ltdc模块功能更加独立

module LTDC_drv (
    input ltdc_pclk,
    input lcd_reset,
    input en,
    output lcd_de,
    output lcd_hsync,
    output lcd_vsync,
    output reg [7:0] lcd_r,
    output reg [7:0] lcd_g,
    output reg [7:0] lcd_b
);

注意:

       

一. 实现的功能

       使用FPGA开发板外接1024*600 7inch RGB LCD显示屏,循环刷颜色数据,间隔1S。
 

二. 功能框图

        定时器模块1:加计数器,计数500ms到了产生overflow信号

       LCD驱动控制模块功能分解:

                1024*600屏,行、场时序参数定义

                行、场计数器对像素时钟计数

                刷数据使能信号、 行、场同步信号连续赋值

                三段式状态机应用

        锁相环IP核:产生33.3MHZ的LCD像素时钟

        打开方法:  tools -> netlist viewers -> rtl viewer

 三. RTL代码

        顶层模块 lcd_rgb_colorbar.v

module lcd_rgb_colorbar (
    input sys_clk,
    input sys_reset_n,
    output lcd_de,
    output lcd_hs,
    output lcd_vs,
    output lcd_pclk,
    output lcd_bl,
    output lcd_rst,
    output [15:0] lcd_rgb565
);

parameter COUNT_OVERFLOW = 26'd25_000_000;				//1s

//wire define
wire locked;
wire lcd_clk_w;
wire overflow;

//*****************************************************
//**                    main code
//*****************************************************
assign lcd_reset = sys_reset_n & locked;
assign lcd_pclk = lcd_clk_w;

// module instance 
lcd_pll lcd_pll_inst(~sys_reset_n,sys_clk,lcd_clk_w,locked);

lcd_rgb_colorbar_drv lcd_rgb_colorbar_drv_inst(
     .lcd_clk       (lcd_clk_w),
     .lcd_reset     (lcd_reset),
     .en            (overflow),
     .lcd_de        (lcd_de),
     .lcd_hs        (lcd_hs),
     .lcd_vs        (lcd_vs),
     .lcd_bl        (lcd_bl),
     .lcd_rst       (lcd_rst),
     .lcd_rgb565    (lcd_rgb565)
); 

timer #(
  .COUNT_OVERFLOW (COUNT_OVERFLOW)
  )
  timer_inst(
  .clk      (sys_clk),
  .reset    (sys_reset_n),
  .overflow (overflow)
);

endmodule

       定时器模块 timer.v

        除将overflow <=修改为非阻塞赋值外,其它与led点灯工程的相同。

/*
********************************************************************************
  *Copyright ©, 2018, CunXin_ All Rights Reserved
  *文 件 名: key_led.v
  *说    明: 
            程序功能无按键按下时, LED灯全灭; 
            按键1按下时, LED灯显示自右向左的流水效果;
            按键2按下时, LED灯显示自左向右的流水效果;
            按键3按下时,四个LED灯同时闪烁;
            按键4按下时, LED灯全亮
  *版    本: V000B00D00
  *创建日期: 2018年4月3日		下午12:16:49
  *创 建 人: wansaiyon
  *修改信息:  
================================================================================
  *修 改 人    		  修改日期       	  修改内容 
  *<作者/修改者>      <YYYY/MM/DD>		  <修改内容>
********************************************************************************
*/

module timer #(
    parameter COUNT_OVERFLOW = 26'd10_000_000
) (
    input clk,
    input reset,
    output reg overflow
);

localparam period = COUNT_OVERFLOW;

reg [25:0] cnt;

always @(posedge clk,negedge reset) begin
    if(!reset) begin
      cnt <= 26'd0;
      overflow <= 1'b0;
    end        
    else if(cnt < period) begin
      cnt <= cnt + 1'd1;
      overflow <= 1'b0;
    end        
    else begin
      cnt <= 26'd0;
      overflow <= 1'b1;
    end
        
end

endmodule

        LCD驱动模块,lcd_rgb_colorbar_drv .v

        主要功能点:        

                1024*600屏,行、场时序参数定义

                行、场计数器对像素时钟计数

                刷数据使能信号、 行、场同步信号连续赋值

                三段式状态机应用

module lcd_rgb_colorbar_drv (
    input lcd_clk,
    input lcd_reset,
    input en,
    output lcd_de,
    output lcd_hs,
    output lcd_vs,
    output lcd_bl,
    output lcd_rst,
    output reg [15:0] lcd_rgb565
);

`define THREE_FSM

//parameter define  
localparam  H_SYNC   =  11'd20;    //
localparam  H_BACK   =  11'd140;   //
localparam  H_DISP   =  11'd1024;  //
localparam  H_FRONT  =  11'd160;   //
localparam  H_TOTAL  =  11'd1344;  //

localparam  V_SYNC   =  11'd3;     //
localparam  V_BACK   =  11'd20;    //
localparam  V_DISP   =  11'd600;   //
localparam  V_FRONT  =  11'd12;    //
localparam  V_TOTAL  =  11'd635;   //

localparam S0_WHITE  = 16'b11111_111111_11111;     //RGB565 
localparam S1_BLACK  = 16'b00000_000000_00000;     //RGB565
localparam S2_RED    = 16'b11111_000000_00000;     //RGB565 
localparam S3_GREEN  = 16'b00000_111111_00000;     //RGB565 
localparam S4_BLUE   = 16'b00000_000000_11111;     //RGB565 

//reg define                                     
reg [10:0] cnt_h;
reg [10:0] cnt_v;
reg [15:0] state_now;
reg [15:0] state_next;

//wire define
wire lcd_en;


//*****************************************************
//**                    main code
//*****************************************************
assign lcd_en  = (((cnt_h >= H_SYNC+H_BACK) && (cnt_h < H_SYNC+H_BACK+H_DISP))
                 &&((cnt_v >= V_SYNC+V_BACK) && (cnt_v < V_SYNC+V_BACK+V_DISP)))
                 ?  1'b1 : 1'b0;
assign lcd_de = lcd_en;
assign lcd_bl = 1'b1;
assign lcd_rst = 1'b1;
assign lcd_hs  = 1'b1;            
assign lcd_vs  = 1'b1;            


always @(posedge lcd_clk or negedge lcd_reset) begin
    if(!lcd_reset)begin
        state_now <= S0_WHITE;
    end
    else begin 
        if(en) begin
            state_now <= state_next;
        end
    end
end

always @(*) begin
    case (state_now)
	  S0_WHITE: 
			state_next = S1_BLACK;
	  S1_BLACK: 
			state_next = S2_RED;
	  S2_RED: 
			state_next = S3_GREEN;
	  S3_GREEN: 
			state_next = S4_BLUE;
	  S4_BLUE: 
			state_next = S0_WHITE;             
	  default: 
			state_next = S0_WHITE;
    endcase
end

always @(*) begin
    if(lcd_en)
		lcd_rgb565 = state_now;
	 else ;
end
//行计数器对像素时钟计数
always @(posedge lcd_clk or negedge lcd_reset) begin         
    if (!lcd_reset)
        cnt_h <= 11'd0;                                  
    else begin
        if(cnt_h < H_TOTAL - 1'b1)                                               
            cnt_h <= cnt_h + 1'b1;                               
        else 
            cnt_h <= 11'd0;  
    end
end
//场计数器对行计数
always @(posedge lcd_clk or negedge lcd_reset) begin         
    if (!lcd_reset)
        cnt_v <= 11'd0;                                  
    else if(cnt_h == H_TOTAL - 1'b1) begin
        if(cnt_v < V_TOTAL - 1'b1)                                               
            cnt_v <= cnt_v + 1'b1;                               
        else 
            cnt_v <= 11'd0;  
    end
end

endmodule

四.modelsim前仿真(功能仿真)

        每次打开modelsim时需要切换工作空间,否则可能创建工程添加的源码会是上一个仿真工程的代码,导致出错 。

        使用vscode打开segled_static_top.v, ctrl+shift+p,输入testbench,在terminal生成了segled_static_top_tb.v代码,需要安装插件verilog_testbench, 初始生成时可能提示失败,需要安装 python 库(根据错误提示自行百度)。

        生成的代码有点错误,需要自己再做点修改。

        tb_segled_dy_top.v,

        为了方便减小了计数器周期为4'd10,2'd2。

`timescale  1ns / 1ns

module tb_segled_dy_top;

// segled_static_top Parameters
parameter PERIOD          = 10            ;     
parameter COUNT_OVERFLOW  = 4'd10;     
parameter COUNT_OVERFLOW2  = 2'd2;     

// segled_static_top Inputs
reg   clk                                  = 0 ;
reg   reset                                = 0 ;

// segled_static_top Outputs
wire  [5:0]  sel                           ;    
wire  [7:0]  seg_led                       ;    


initial
begin
    forever #(PERIOD/2)  clk=~clk;
end

initial
begin
    #(PERIOD*2) reset  =  1;
end

segled_dy_top #(
    .COUNT_OVERFLOW ( COUNT_OVERFLOW ),
    .COUNT_OVERFLOW2 ( COUNT_OVERFLOW2 ))
 u_segled_dy_top (
    .clk                     ( clk            ),
    .reset                   ( reset          ),
	
    .sel                     ( sel      [5:0] ),
    .seg_led                 ( seg_led  [7:0] )
);


endmodule

        仿真波形

五.下载验证

        FPGA开发板外接1024*600 7inch RGB LCD显示屏,循环刷颜色数据,间隔1S。

        但是发现4个LED有微弱的亮度,需要配置好未使用管脚的状态为高阻输入,同时将nCEO管脚修改为Use as regular I/0,修改后LED变为熄灭状态了。

        在 Quartus 软件中默认未使用管脚的状态为弱上拉输入,所以未使用到的管脚上也是有电压的,只是驱动能力很弱,这往往会导致一些不安全的隐患,所以我们需要将未使用管脚的状态设置为三态输入。        

        在左侧Category一栏中选择Dual-Purpose Pin。对于需要使用EPCS器件的引脚时,需要将下图页面中所有的引脚都改成Use as regular IO,如果大家不确定工程中是否用到EPCS器件时,可以全部修改。本次实验只修改了nCEO一栏中, 将Use as programming pin修改为Use as regular I/0。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值