FPGA——TFT显示屏驱动

一、TFT时序

视频电子标准协会(Video Electronics Standards Association)显示器时序标准(Monitor Timing Standard)
https://vesa.org/vesa-standards/


二、设计思路

  • 800 * 480 60Hz的意义
    该显示屏能显示800 * 480个像素点,一秒钟能刷新60张这样的图片
  • TFT显示屏时钟的计算方法
    800 * 480是有效像素点,总共像素由上图可知,H Total Time = 1056;V Total Time = 525;
    所以一张图片要有1056 * 525个像素,1秒钟显示60张图片,那么一张图片的显示时间是t=(1/60)s,一个像素的的时间是t_pix=t/(1056 * 525),那么TFT时钟的频率就是f = 1/(t_pix)
    以上就是TFT显示屏时钟频率的推导方法,更为简便的时钟频率计算是:f = 1056 * 525 * 60 = 33264000Hz = 33MHz
  • verilog实现
    行同步信号,场同步信号只要找到对应的点即可完成

    最重要的是对于数据输出的控制,即vcount,hcount,dout
    vcount(行有效数据计数),hcount(场有效数据计数)要比输出提前一拍到来

三、TFT驱动代码

module tft_ctr(
   clk      ,  //TTF驱动时钟
   rst_n    ,  //复位电路
   data_in  ,  //RGB三基色输入
   data_out ,  //RGB三基色输出
   hsyc     ,  //行同步信号
   vsyc     ,  //场同步信号
   data_count_vld ,  //数据传完标志信号
   data_vld ,
   hcount   ,  //当前扫描点的h坐标
   vcount   ,  //当前扫描点的v坐标
   blk         //数据有效输出信号
);

`include "tft_parameter.v"

localparam H_SYNC_END   =  `H_SYNC_TIME;
//数据开始设置在数据左黑边过去之后,而不是时序图上的数据开始
localparam H_DATA_BEGIN =  `H_SYNC_TIME + `H_BACK_PORCH + `H_LEFT_BORDER;
//数据结束设置在数据右黑边来到之前,而不是时序图上的数据结束               
localparam H_DATA_END   =  `H_SYNC_TIME + `H_BACK_PORCH + `H_LEFT_BORDER + `H_DATA_TIME;                 
localparam H_SYNC_BEGIN =  `H_SYNC_TIME + `H_BACK_PORCH + `H_LEFT_BORDER + `H_DATA_TIME + `H_RIGHT_BORDER + `H_FRONT_PORCH;

localparam V_SYNC_END   =  `V_SYNC_TIME;
//数据开始设置在数据左黑边过去之后,而不是时序图上的数据开始
localparam V_DATA_BEGIN =  `V_SYNC_TIME + `V_BACK_PORCH + `V_TOP_BORDER;
//数据结束设置在数据右黑边来到之前,而不是时序图上的数据结束 
localparam V_DATA_END   =  `V_SYNC_TIME + `V_BACK_PORCH + `V_TOP_BORDER + `V_DATA_TIME;
localparam V_SYNC_BEGIN =  `V_SYNC_TIME + `V_BACK_PORCH + `V_TOP_BORDER + `V_DATA_TIME + `V_BOTTOM_BORDER + `V_FRONT_PORCH;

//输入输出
localparam DATA_W  =  24;

//计数器
localparam CNT_HSYC_N   =  `H_TOTAL_TIME;
localparam CNT_HSYC_W   =  `CNT_HSYC_W;
localparam CNT_VSYC_N   =  `V_TOTAL_TIME;
localparam CNT_VSYC_W   =  `CNT_VSYC_W;


input                      clk;
input                      rst_n;
input    [DATA_W-1:0]      data_in;
output   [DATA_W-1:0]      data_out;
output                     hsyc;
output                     vsyc;
output                     blk;
output                     data_count_vld;
output                     data_vld;
output   [CNT_HSYC_W-1:0]  hcount;
output   [CNT_VSYC_W-1:0]  vcount;

reg                        data_count_vld;
reg      [DATA_W-1:0]      data_out;
reg                        hsyc;
reg                        vsyc;
reg                        blk;
reg                        data_vld;
reg      [CNT_HSYC_W-1:0]  hcount;
reg      [CNT_VSYC_W-1:0]  vcount;

//计数器
reg      [CNT_HSYC_W-1:0]  cnt_hsyc;
wire                       add_cnt_hsyc;
wire                       end_cnt_hsyc;
reg      [CNT_VSYC_W-1:0]  cnt_vsyc;
wire                       add_cnt_vsyc;
wire                       end_cnt_vsyc;


//行同步计数器
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      cnt_hsyc <= 0;
   else if(add_cnt_hsyc)begin
      if(end_cnt_hsyc)
         cnt_hsyc <= 0;
      else
         cnt_hsyc <= cnt_hsyc + 1'b1;
   end
end
assign add_cnt_hsyc = 1;
assign end_cnt_hsyc = add_cnt_hsyc && cnt_hsyc == CNT_HSYC_N - 1;


//场同步计数器
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      cnt_vsyc <= 0;
   else if(add_cnt_vsyc)begin
      if(end_cnt_vsyc)
         cnt_vsyc <= 0;
      else
         cnt_vsyc <= cnt_vsyc + 1'b1;
   end
end
assign add_cnt_vsyc = end_cnt_hsyc;
assign end_cnt_vsyc = add_cnt_vsyc && cnt_vsyc == CNT_VSYC_N - 1;

//输出比输入慢一拍,hcount,vcount要先于输出一拍产生数据
//行有效数据计数器
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      hcount <= 0;
   else if(data_count_vld)
      hcount <= (cnt_hsyc - (H_DATA_BEGIN - 2));
end

//场有效数据计数器
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      vcount <= 0;
   else if(data_count_vld)
      vcount <= cnt_vsyc - V_DATA_BEGIN;
end

//行同步信号
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      hsyc <= 0;
   else if(cnt_hsyc == H_SYNC_END - 1 && add_cnt_hsyc)
      hsyc <= 1;
   else if(cnt_hsyc == H_SYNC_BEGIN -1 && add_cnt_hsyc)
      hsyc <= 0;
end

//场同步信号
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      vsyc <= 0;
   else if(cnt_vsyc == V_SYNC_END - 1 && add_cnt_vsyc)
      vsyc <= 1;
   else if(cnt_vsyc == V_SYNC_BEGIN - 1 && add_cnt_vsyc)
      vsyc <= 0;
end

//数据传输标志位
always @(posedge clk or negedge rst_n)begin
   if(!rst_n) 
      blk <= 0;
   else if(data_vld)
      blk <= 1;
   else
      blk <= 0;
end

//数据传输使能信号,比blk提前一拍,为了数据传输与blk同步
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      data_vld <= 0;
   else if(data_count_vld)
      data_vld <= 1;
   else
      data_vld <= 0;
end

//有效数据指示信号
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      data_count_vld <= 0;
   else if((cnt_hsyc >= H_DATA_BEGIN - 3 && cnt_hsyc < H_DATA_END - 3) && (cnt_vsyc >= V_DATA_BEGIN && cnt_vsyc < V_DATA_END))
      data_count_vld <= 1;
   else
      data_count_vld <= 0;
end

//数据传输
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      data_out <= 0;
   else if(data_vld)
      data_out <= data_in;
   else
      data_out <= 0;
end

endmodule

四、条件编译代码

`define resolution_800x480;

`ifdef resolution_800x480
   `define H_RIGHT_BORDER  12'd0
   `define H_FRONT_PORCH   12'd40
   `define H_SYNC_TIME     12'd128
   `define H_BACK_PORCH    12'd88
   `define H_LEFT_BORDER   12'd0
   `define H_DATA_TIME     12'd800
   `define H_TOTAL_TIME    12'd1056
   `define V_BOTTOM_BORDER 12'd8
   `define V_FRONT_PORCH   12'd2
   `define V_SYNC_TIME     12'd2
   `define V_BACK_PORCH    12'd25
   `define V_TOP_BORDER    12'd8
   `define V_DATA_TIME     12'd480
   `define V_TOTAL_TIME    12'd525

   `define CNT_HSYC_W      11
   `define CNT_VSYC_W      10
// `elsif 

`endif

五、仿真代码

`timescale 1ns / 1ns
module ttf_ctr_tb();

//输入输出
localparam DATA_W  =  24;


reg                      clk;
reg                      rst_n;
reg    [DATA_W-1:0]      data_in;
wire   [DATA_W-1:0]      data_out;
wire                     hsyc;
wire                     vsyc;
wire                     data_vld;
wire                     blk;
wire   [11-1:0]          hcount;
wire   [10-1:0]          vcount;

ttf_ctr ttf_ctr(
   .clk      (clk),        //TTF驱动时钟
   .rst_n    (rst_n),      //复位电路
   .data_in  (data_in),    //RGB三基色输入
   .data_out (data_out),   //RGB三基色输出
   .hsyc     (hsyc),       //行同步信号
   .vsyc     (vsyc),       //场同步信号
   .hcount   (hcount),  //行数据计数器
   .vcount   (vcount),  //场数据计数器
   .data_vld (data_vld),
   .blk      (blk)         //数据有效输出信号
);

//时钟周期,单位ns,在这里修改时钟周期
parameter CYCLE = 20;

//复位时间,此时表示复位3个时钟周期的时间
parameter RST_TIME = 3;

//生成本地时钟50M
initial begin
   clk = 0;
   forever
   #(CYCLE/2)
   clk=~clk;
end

//产生复位信号
initial begin
   rst_n = 1;
   #2;
   rst_n = 0;
   #(CYCLE * RST_TIME);
   rst_n = 1;
end

//输入信号din0赋值方式
// initial begin
//    #1;
//    //赋初值
//    data_in = 0;
//    #(10*CYCLE);
//    forever begin
//       @(posedge clk)begin
//          if(data_vld)
//             data_in = data_in + 1;
//       end
//    end
//    @(negedge blk)
//    #1000;
//    $stop;
// end
always@(posedge clk or negedge rst_n)begin
   if(!rst_n)
      data_in <= 0;
   else if(data_vld)
      data_in <= data_in + 1;
end

endmodule

六、板级验证代码

注:板级验证使用的是RGB565模式,16位输出端口

module tft_test(
   clk      ,
   rst_n    ,
   rgb_out  ,
   de       ,
   hsyc     ,
   vsyc     ,
   bl       ,
   tft_clk
);
//定义颜色编码
localparam
   BLACK = 24'h000000,  //黑色
   BLUE = 24'h0000F8,   //蓝色
   RED = 24'hFF0000,    //红色
   PURPPLE =24'hFF00FF, //紫色
   GREEN = 24'h00FF00,  //绿色
   CYAN = 24'h00FFFF,   //青色
   YELLOW = 24'hFFFF00, //黄色
   WHITE = 24'hFFFFFF;  //白色

localparam  OUT_W = 16;

input                   clk;
input                   rst_n;

output   [OUT_W-1:0]    rgb_out;
output                  de;
output                  hsyc;
output                  vsyc;
output                  bl;
output                  tft_clk;

wire     [OUT_W-1:0]    rgb_out;
wire                    de;
wire                    hsyc;
wire                    vsyc;
wire                    bl;
wire                    tft_clk;

wire     [24-1:0]       data_out;
wire     [11-1:0]       hcount;
wire     [10-1:0]       vcount;

reg      [24-1:0]       data_in;

tft_ctr tft_ctr(
   .clk      (tft_clk),    //TTF驱动时钟
   .rst_n    (rst_n),      //复位电路
   .data_in  (data_in),    //RGB三基色输入
   .data_out (data_out),   //RGB三基色输出
   .hsyc     (hsyc),       //行同步信号
   .vsyc     (vsyc),       //场同步信号
   .hcount   (hcount),     //当前扫描点的h坐标
   .vcount   (vcount),     //当前扫描点的v坐标
   .blk      (de)          //数据有效输出信号
);

disp_clk disp_clk(
   .clk_out1(tft_clk),
   .clk_in1(clk)
);

always @(*)begin
   if((hcount >= 0 && hcount < 400) && (vcount >= 0 && vcount < 120))
      data_in <= RED;
   else if((hcount >= 400 && hcount < 800) && (vcount >= 0 && vcount < 120))
      data_in <= BLUE;
   else if((hcount >= 0 && hcount < 400) && (vcount >= 120 && vcount < 240))
      data_in <= BLACK;
   else if((hcount >= 400 && hcount < 800) && (vcount >= 120 && vcount < 240))
      data_in <= WHITE; 
   else if((hcount >= 0 && hcount < 400) && (vcount >= 240 && vcount < 360))
      data_in <= PURPPLE;
   else if((hcount >= 400 && hcount < 800) && (vcount >= 240 && vcount < 360))
      data_in <= GREEN;
   else if((hcount >= 0 && hcount < 400) && (vcount >= 360 && vcount < 480))
      data_in <= CYAN;
   else if((hcount >= 400 && hcount < 800) && (vcount >= 360 && vcount < 480))
      data_in <= YELLOW;
   else
      data_in <=0;
end

assign rgb_out = {data_out[23:19],data_out[15:10],data_out[7:3]};
assign bl      = 1'b1;

endmodule

七、板级验证仿真代码

`timescale 1ns / 1ns

module tf_test_tb();

localparam  OUT_W = 16;

reg                   clk;
reg                   rst_n;

wire   [OUT_W-1:0]    rgb_out;
wire                  de;
wire                  hsyc;
wire                  vsyc;
wire                  bl;
wire                  tft_clk;

tft_test tft_test(
   .clk      (clk),
   .rst_n    (rst_n),
   .rgb_out  (rgb_out),
   .de       (de),
   .hsyc     (hsyc),
   .vsyc     (vsyc),
   .bl       (bl),
   .tft_clk  (tft_clk)
);

//时钟周期,单位ns,在这里修改时钟周期
parameter CYCLE = 20;

//复位时间,此时表示复位3个时钟周期的时间
parameter RST_TIME = 3;

//生成本地时钟50M
initial begin
   clk = 0;
   forever
   #(CYCLE/2)
   clk=~clk;
end

//产生复位信号
initial begin
   rst_n = 1;
   #2;
   rst_n = 0;
   #(CYCLE * RST_TIME);
   rst_n = 1;
   #1547850;
end

endmodule

八、效果图展示

posted @ 2021-02-01 20:43  AdriftCore  阅读(1371)  评论(0编辑  收藏  举报