天天看點

FPGA RGB565的LCD顯示屏驅動(Verilog)1螢幕2行場掃描時序3分析4代碼5結果6Testbench

文章目錄

  • 1螢幕
  • 2行場掃描時序
  • 3分析
  • 4代碼
    • 4-1産生9M驅動時鐘
    • 4-2驅動部分
    • 4-3顯示内容部分
    • 4-3頂層
  • 5結果
    • 5-1
    • 5-2
  • 6Testbench

1螢幕

FPGA RGB565的LCD顯示屏驅動(Verilog)1螢幕2行場掃描時序3分析4代碼5結果6Testbench

使用像素:480*272的一塊RGB565螢幕。

像素時鐘:9Mhz

接口:

lcd_bl:lcd背光
	    lcd_rgb[15:0]:色彩值
	    lcd_de:當計數器處于valid(有效)區域時應将其拉高,此時輸出lcd_rgb[15:0]的值到螢幕的對應像素點上。
	    lcd_hs:可一直将其拉高沒有影響
	    lcd_vs:可一直将其拉高沒有影響
	    lcd_pclk:9M時鐘
	    lcd_rst:lcd單獨複位低電平有效
           

2行場掃描時序

FPGA RGB565的LCD顯示屏驅動(Verilog)1螢幕2行場掃描時序3分析4代碼5結果6Testbench
FPGA RGB565的LCD顯示屏驅動(Verilog)1螢幕2行場掃描時序3分析4代碼5結果6Testbench
FPGA RGB565的LCD顯示屏驅動(Verilog)1螢幕2行場掃描時序3分析4代碼5結果6Testbench

3分析

1、利用ip核倍分頻獲得9M時鐘。

2、使用子母兩個計數器對驅動時鐘進行計數。

3、子計數器的計數範圍為0-524,所代表一個行掃描周期,滿載時在下一個時鐘周期清零,并使母計數器+1。

4、母計數器的計數範圍為0-285,所代表一個場掃描周期,滿載時在下一個時鐘周期清零。

5、利用組合邏輯将子母倆個計數的計數值做範圍限定,确定真正valid的範圍。

6、當計數值處于真正valid的範圍時,需要回傳目前的坐标值,使用者根據回傳的坐标值設定RGB565的色彩值。

4代碼

4-1産生9M驅動時鐘

FPGA RGB565的LCD顯示屏驅動(Verilog)1螢幕2行場掃描時序3分析4代碼5結果6Testbench

-其端口清單如下:

module pll_9m (
	areset,//注意:其為高電平複位
	inclk0,//輸入時鐘
	c0,    //輸出時鐘
	locked //穩定鎖:當c0輸出穩定時locked會從低電平變為高電平
	);
           

4-2驅動部分

module lcd_driver(
        input           lcd_clk,      //lcd子產品驅動時鐘
        input           sys_rst_n,    //複位信号
        
        //下列信号與RGBLCD螢幕相連接配接                 
        output          lcd_hs,       //LCD 行同步信号
        output          lcd_vs,       //LCD 場同步信号
        output          lcd_de,       //LCD 資料使能
        output  [15:0]  lcd_rgb,      //LCD RGB565顔色資料
        output          lcd_bl,       //LCD 背光控制信号
        output          lcd_rst,      //LCD 複位信号
        output          lcd_pclk,     //LCD 采樣時鐘
     
        //接口信号,此子產品推出valid坐标值,并接收使用者發來的RGB565的像素點顔色值   
        input   [15:0]  pixel_data,   //像素點資料
        output  [10:0]  pixel_xpos,   //像素點橫坐标
        output  [10:0]  pixel_ypos    //像素點縱坐标
        );                             
                                                            
    //參數清單
    parameter  H_SYNC   =  11'd41;   //行同步
    parameter  H_BACK   =  11'd2;    //行顯示後沿
    parameter  H_DISP   =  11'd480;   //行有效資料
    parameter  H_FRONT  =  11'd2;    //行顯示前沿
    parameter  H_TOTAL  =  11'd525;  //行掃描周期

    parameter  V_SYNC   =  11'd10;     //場同步
    parameter  V_BACK   =  11'd2;    //場顯示後沿
    parameter  V_DISP   =  11'd272;   //場有效資料
    parameter  V_FRONT  =  11'd2;    //場顯示前沿
    parameter  V_TOTAL  =  11'd286;   //場掃描周期
              
    //子母兩個計數器                                    
    reg  [10:0] cnt_h;
    reg  [10:0] cnt_v;

    //...
    wire       lcd_en;
    wire       data_req; 

    //對lcd螢幕的某些信号加以固定
    assign lcd_bl   = 1'b1;           
    assign lcd_rst  = 1'b1;           
    assign lcd_pclk = lcd_clk;        
    assign lcd_hs  = 1'b1;            
    assign lcd_vs  = 1'b1;      
          
    //當子母計數器的值處于valid範圍内時,将lcd_de拉高,此時輸出lcd_rgb[15:0]的值到螢幕的對應像素點上。
    assign lcd_de  = lcd_en;   
    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;
                     
    //lcd_rgb[15:0]的值由使用者(input)給予即為:pixel_data                                     
    assign lcd_rgb = lcd_en ? pixel_data : 16'd0;

    //...此處做一個簡單的分析,lcd_de拉高,lcd_rgb[15:0]的值瞬間到達螢幕的對應像素點上。
    //   我們最開始的分析是:cnt值->産生坐标->根據坐标确認有效範圍->再往範圍裡放顔色值
    //   是以說我們在上面寫的那個就是有效範圍,由于觸發器的特性,是以要提前一個時鐘周期将坐标值送出,才來的及在lcd_en為1時候準确的送出顔色值
    //   而一場是由很多行組成的,母計數器,要比子計數器慢。子計數器變很多次,母計數器才變一次。是以對子計數器需要提前一個時鐘周期發送出坐标。
    //   PS:如果沒懂的話也沒關系,因為我也比較暈。但隻要時序對,螢幕就能出現顔色然後進行simulation或者實物調試
                  
    assign data_req = (((cnt_h > H_SYNC+H_BACK-1'b1) && (cnt_h <= H_SYNC+H_BACK+H_DISP-1'b1))   //對子計數器需要提前一個時鐘周期發送出坐标。
                      && ((cnt_v > V_SYNC+V_BACK) && (cnt_v <= V_SYNC+V_BACK+V_DISP)))          //母計數器不變
                      ?  1'b1 : 1'b0;

    //目前像素點坐标                
    assign pixel_xpos = data_req ? (cnt_h - (H_SYNC + H_BACK - 1'b1)) : 11'd0;
    assign pixel_ypos = data_req ? ((cnt_v - (V_SYNC + V_BACK - 1'b1)) - 1'b1) : 11'd0;
                                                                       //這塊為什麼有個-1 在最下面的測試圖檔中可以看到解釋

    //行計數器對像素時鐘計數(子)
    always @(posedge lcd_clk or negedge sys_rst_n) begin         
        if (!sys_rst_n)
            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 sys_rst_n) begin         
        if (!sys_rst_n)
            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 
           

4-3顯示内容部分

module lcd_display(
    input             lcd_clk,                  //lcd驅動時鐘
    input             sys_rst_n,                //複位信号
    
    input      [10:0] pixel_xpos,               //像素點橫坐标
    input      [10:0] pixel_ypos,               //像素點縱坐标    
    output reg [15:0] pixel_data                //像素點資料
    );    
    
localparam WHITE  = 16'b11111_111111_11111;     //RGB565 白色
localparam BLACK  = 16'b00000_000000_00000;     //RGB565 黑色
localparam RED    = 16'b11111_000000_00000;     //RGB565 紅色
localparam GREEN  = 16'b00000_111111_00000;     //RGB565 綠色
localparam BLUE   = 16'b00000_000000_11111;     //RGB565 藍色
localparam BROWN   = 16'h7800;                  //RGB565 棕色
    
    
  //測試邊框
      always @(posedge lcd_clk or negedge sys_rst_n) begin         
          if (!sys_rst_n)
              pixel_data <= 16'hffff;
          else begin
          
            if(pixel_xpos == 11'd1)                                              
                pixel_data <= GREEN;                               
            else if(pixel_xpos == 11'd480)
                pixel_data <= GREEN;
            else if(pixel_xpos == 11'd240)
                pixel_data <= GREEN;    
                
            else if(pixel_ypos == 11'd1)
                pixel_data <= GREEN;  
            else if(pixel_ypos == 11'd272)
                pixel_data <= GREEN; 
            else if(pixel_ypos == 11'd136)
                pixel_data <= GREEN;   
            else 
            
                pixel_data <= BROWN;
          end
      end

    
    //彩條
 //   always @(posedge lcd_clk or negedge sys_rst_n) begin         
 //       if (!sys_rst_n)
 //           pixel_data <= 16'hffff;
 //       else begin
 //           if((pixel_xpos >= 1) && (pixel_xpos < (11'd480/5)*1))                                              
 //               pixel_data <= GREEN;                               
 //           else if((pixel_xpos >= (11'd480/5)*1) && (pixel_xpos < (11'd480/5)*2))
 //               pixel_data <= BLACK;  
 //           else if((pixel_xpos >= (11'd480/5)*2) && (pixel_xpos < (11'd480/5)*3))
 //               pixel_data <= RED;  
 //           else if((pixel_xpos >= (11'd480/5)*3) && (pixel_xpos < (11'd480/5)*4))
 //               pixel_data <= BLUE;  
 //           else 
 //               pixel_data <= BROWN;
 //       end
 //   end
    endmodule 
           

4-3頂層

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


wire         lcd_clk_w;            
wire         locked_w;              
wire         rst_n_w;               
wire [15:0]  pixel_data_w;          
wire [ 9:0]  pixel_xpos_w;          
wire [ 9:0]  pixel_ypos_w;          
    

//待PLL輸出穩定之後,停止複位
assign rst_n_w = sys_rst_n & locked_w;
   
pll_9m	u_lcd_pll(                  //時鐘分頻子產品
	.inclk0         (sys_clk),    
	.areset         (~sys_rst_n),
    
	.c0             (lcd_clk_w),    //lcd驅動時鐘
	.locked         (locked_w)
	); 


lcd_driver u_lcd_driver(            //lcd驅動子產品
    .lcd_clk        (lcd_clk_w),    
    .sys_rst_n      (rst_n_w),    

    .lcd_hs         (lcd_hs),       
    .lcd_vs         (lcd_vs),       
    .lcd_de         (lcd_de),       
    .lcd_rgb        (lcd_rgb),
    .lcd_bl         (lcd_bl),
    .lcd_rst        (lcd_rst),
    .lcd_pclk       (lcd_pclk),
    
    .pixel_data     (pixel_data_w), 
    .pixel_xpos     (pixel_xpos_w), 
    .pixel_ypos     (pixel_ypos_w)
    ); 
    
    
lcd_display u_lcd_display(          //lcd顯示子產品
    .lcd_clk        (lcd_clk_w),    
    .sys_rst_n      (rst_n_w),
    
    .pixel_xpos     (pixel_xpos_w),
    .pixel_ypos     (pixel_ypos_w),
    .pixel_data     (pixel_data_w)
    );   
    
endmodule 
           

5結果

5-1

FPGA RGB565的LCD顯示屏驅動(Verilog)1螢幕2行場掃描時序3分析4代碼5結果6Testbench

可以看到,測試中出現了列偏移(1-480)*(0~271),是以對代碼進行修整

見上面代碼中的這一句:

FPGA RGB565的LCD顯示屏驅動(Verilog)1螢幕2行場掃描時序3分析4代碼5結果6Testbench

5-2

OK!(1-480)*(1~272)

FPGA RGB565的LCD顯示屏驅動(Verilog)1螢幕2行場掃描時序3分析4代碼5結果6Testbench

6Testbench

給予時鐘激勵,觀察信号關系即可。