FPGA-串口接收图像写入RAM并读出在TFT显示屏上显示

系统框图:

需要用到的模块有:

1,UART_RX(串口接收模块);

2,串口接受的数据存放到RAM模块;

3,RAM IP核;

4,时钟IP核 (TFT显示屏驱动时钟的产生);

5,TFT显示驱动模块;

1,UART_RX(串口接收模块)

具体构建方式及详见(其中的串口接收部分)

FPGA-UART串口icon-default.png?t=N7T8https://blog.csdn.net/weixin_46897065/article/details/135586405?spm=1001.2014.3001.5502

2,串口接受的数据存放到RAM模块

串口接受的数据存放到RAM的逻辑时序图如下:

然后编辑控制器逻辑代码:

module img_rx_wr(
    Clk        ,
    Reset_n    ,
    rx_data    ,
    rx_done    ,
    ram_wren   ,
    ram_eraddr ,
    ram_wrdata ,
    led        
    );
    input                  Clk        ;
    input                  Reset_n    ;
    input        [7:0]     rx_data    ;
    input                  rx_done    ;
    output reg             ram_wren   ;
    output reg  [15:0]     ram_eraddr ;
    output      [15:0]     ram_wrdata ;
    output reg             led        ;
    
    reg[16:0]data_cnt;    //统计串口接收数据个数计数器
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        data_cnt <= 0;
    else if(rx_done)
        data_cnt <= data_cnt + 1'd1;
        

    
    reg [15:0]rx_data_temp; 
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        rx_data_temp <= 1'b0;
    else if(rx_done)
        rx_data_temp <= {rx_data_temp[7:0],rx_data};
    
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        ram_wren <= 0;
    else if(rx_done && data_cnt[0])
        ram_wren <= 1'd1;
    else
        ram_wren <= 0;
        
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)        
        ram_eraddr <= 0;
    else if (rx_done && data_cnt[0])
        ram_eraddr <= data_cnt[16:1];  // data_cnt/2
    
    
    assign ram_wrdata = rx_data_temp;
        
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        led <= 0;
    else if((rx_done)&&(data_cnt == 131071))
        led <= ~led ;  
endmodule

测试此逻辑代码的正确性:

编写测试文件:

`timescale 1ns / 1ps
module img_rx_wr_tb;
    reg              Clk        ;
    reg              Reset_n    ;
    reg    [7:0]     rx_data    ;
    reg              rx_done    ;
    wire             ram_wren   ;
    wire  [15:0]     ram_eraddr ;
    wire  [15:0]     ram_wrdata ;
    wire             led        ;
    
    img_rx_wr img_rx_wr(
        .Clk       (Clk       ) ,
        .Reset_n   (Reset_n   ) ,
        .rx_data   (rx_data   ) ,
        .rx_done   (rx_done   ) ,
        .ram_wren  (ram_wren  ) ,
        .ram_eraddr(ram_eraddr) ,
        .ram_wrdata(ram_wrdata) ,
        .led       (led       ) 
        );
        
    initial Clk =1;
    always #10 Clk = ~Clk;
    initial begin
        Reset_n = 0;
        rx_data = 0;
        rx_done = 0;
        #201;
        Reset_n = 1;
        #2000;
        rx_data = 8'd255;
        repeat(131072)begin
            rx_done = 1;
            #20;
            rx_done = 0;
            #200;
            rx_data = rx_data - 1;
        end
        #2000000;
        
        repeat(131072)begin
            rx_done = 1;
            #20;
            rx_done = 0;
            #200;
            rx_data = rx_data - 1;
        end
        $stop;
             
    end

    
endmodule

仿真波形如下:

写入第一个数据时:

写入最后一个数据时:

RAM写逻辑已经完成,接下来完成RAM的读逻辑。

3,构建RAM IP核

具体构建方式及其内部参数详见FPGA-学会使用vivado中的存储器资源RAM(IP核)icon-default.png?t=N7T8https://blog.csdn.net/weixin_46897065/article/details/136325283?spm=1001.2014.3001.5502

4,TFT显示屏驱动时钟产生

具体构建方式详见:

FPGA-时钟管理单元icon-default.png?t=N7T8https://blog.csdn.net/weixin_46897065/article/details/136356331?spm=1001.2014.3001.5502

5,TFT显示驱动模块

具体原理详见

FPGA- RGB_TFT显示屏原理及驱动逻辑icon-default.png?t=N7T8https://blog.csdn.net/weixin_46897065/article/details/136401589?spm=1001.2014.3001.5502      以及FPGA-VGA成像原理与时序icon-default.png?t=N7T8https://blog.csdn.net/weixin_46897065/article/details/136386813?spm=1001.2014.3001.5502

在以上链接中介绍的TFT显示逻辑其中使用的组合逻辑,为了使得整体得到更好的时序性(RAM得出地址后数据输出是有延迟时,后面使用时为了确保数据一个都不丢,进行时序对齐)将链接中的逻辑代码重新设计,如下:

`include "disp_parameter_cfg.v" 
//800x480
//H_Right_Borde = 0      V_Bottom_Bord   =  8
//H_Front_Porch = 40     V_Front_Porch   =  2
//H_Sync_Time   = 128    V_Sync_Time     =  2
//H_Back_Porch  = 88     V_Back_Porch    =  25
//H_Left_Border = 0      V_Top_Border    =  8
//H_Data_Time   = 800    V_Data_Time     =  480
//H_Total_Time  = 1056   V_Total_Time    =  525

module TFT_Ctrl(
    Clk_33M   ,
    Reset_n   ,
    Data_in   ,
    Data_req  ,
    hcount    ,   //行扫描位置(显示图像行扫描地址)
    vcount    ,   //场扫描位置(显示图像场扫描地址)
    TFT_HS    ,   //行同步信号
    TFT_VS    ,   //场同步信号
    TFT_DE   ,   //有效数据输出 
    TFT_CLK   ,   
    TFT_DATA ,     //红绿蓝三色 分别8位量化 R[7:0]G[7:0]B[7:0]  
    TFT_BL
    );
    input                Clk_33M;
    input                Reset_n;
    input   [15:0]       Data_in;
    output  reg          Data_req;
    output  reg [11:0]   hcount;
    output  reg [11:0]   vcount;
    output               TFT_HS;
    output               TFT_VS;
    output               TFT_DE;
    output               TFT_CLK;
    output  reg [15:0]   TFT_DATA;  //红绿蓝三色 分别8位量化 R[7:0]G[7:0]B[7:0]  
    output               TFT_BL;
//    parameter  VGA_HS_end = 11'd127  ,
//                hdat_begin = 11'd216  ,
//                hdat_end   = 11'd1016 ,
//                hpixel_end = 11'd1055 ,
//                VGA_VS_end = 11'd1    , 
//                vdat_begin = 11'd35   ,
//                vdat_end   = 11'd515  ,
//                vline_end  = 11'd524  ;
    parameter TFT_HS_end = `H_Sync_Time-1  ;
    parameter hdat_begin = `H_Sync_Time + `H_Back_Porch +`H_Left_Border - 1'b1;
    parameter hdat_end = `H_Total_Time - `H_Right_Border -`H_Front_Porch - 1'b1;
    parameter vdat_begin = `V_Sync_Time + `V_Back_Porch +`V_Top_Border - 1'b1;
    parameter vdat_end = `V_Total_Time - `V_Bottom_Border -`V_Front_Porch - 1'b1;    
    parameter hpixel_end = `H_Total_Time -1 ;
    parameter TFT_VS_end = `V_Sync_Time-1  ;     
    parameter vline_end  = `V_Total_Time -1 ; 
                
    reg [11:0] hcount_r;
    reg [11:0] vcount_r;
    
    always@(posedge Clk_33M or negedge Reset_n)
    if(!Reset_n)
       hcount_r <= 11'd0; 
    else if(hcount_r == hpixel_end )
        hcount_r <= 11'd0;
    else
        hcount_r <= hcount_r + 1'd1;
        
    always@(posedge Clk_33M or negedge Reset_n)
    if(!Reset_n)
       vcount_r <= 11'd0; 
    else if(hcount_r == hpixel_end) 
            if(vcount_r == vline_end )
                vcount_r <= 11'd0;
            else
                vcount_r <= vcount_r + 1'd1;
    else
        vcount_r <= vcount_r;
    
    always@(posedge Clk_33M)
        Data_req <= ((hcount_r >= hdat_begin) && (hcount_r < hdat_end)&&
                         (vcount_r >= vdat_begin) && (vcount_r < vdat_end)) ? 1'b1 : 1'b0; 
    reg [3:0]TFT_DE_r; 
    always@(posedge Clk_33M) begin
        TFT_DE_r[0] <= Data_req;
        TFT_DE_r[3:1] <= TFT_DE_r[2:0];
    end
    
    assign  TFT_DE = TFT_DE_r[2];    
//    assign  TFT_DE   =  ((hcount_r >= hdat_begin) && (hcount_r < hdat_end)&&
//                         (vcount_r >= vdat_begin) && (vcount_r < vdat_end)) ? 1'b1 : 1'b0;  
  
    always@(posedge Clk_33M) begin
        hcount <= Data_req ? (hcount_r - hdat_begin) : 10'd0;
        vcount <= Data_req ? (vcount_r - vdat_begin) : vcount;
    end
//    assign  hcount   =   TFT_DE ? (hcount_r - hdat_begin) : 10'd0;  
//    assign  vcount   =   TFT_DE ? (vcount_r - vdat_begin) : 10'd0;  
  
    reg [3:0]TFT_HS_r; 
    always@(posedge Clk_33M) begin
        TFT_HS_r[0] <= (hcount_r > TFT_HS_end)? 1'b1 :1'b0; 
        TFT_HS_r[3:1] <= TFT_HS_r[2:0];
    end  
    assign  TFT_HS = TFT_HS_r[2];      
//    assign  TFT_HS   =  (hcount_r > TFT_HS_end)? 1'b1 :1'b0;

    reg [3:0]TFT_VS_r; 
    always@(posedge Clk_33M) begin
        TFT_VS_r[0] <= (vcount_r > TFT_VS_end)? 1'b1 :1'b0; 
        TFT_VS_r[3:1] <= TFT_VS_r[2:0];
    end  
    assign  TFT_VS  = TFT_VS_r[2];   
//    assign  TFT_VS   =  (vcount_r > TFT_VS_end)? 1'b1 :1'b0; 

    always@(posedge Clk_33M) begin
        TFT_DATA <=  (TFT_DE) ? Data_in : 16'h0000;    
        end
//    assign  TFT_DATA =  (TFT_DE) ? Data_in : 24'h000000;
    assign  TFT_CLK  =  ~Clk_33M;
    assign  TFT_BL = 1;
    
endmodule

6,顶层模块

将以上5个小模块设计好后,根据以下系统框图设计顶层模块。

代码如下:

`timescale 1ns / 1ps
module UART_RAM_TFT(
    Clk, 
    Reset_n,
    uart_rx,
    TFT_RGB,   //TFT数据输出
    TFT_HS,   // TFT行同步信号
    TFT_VS,   //TFT场同步信号
    TFT_DE,   //TFT数据有效信号
    TFT_CLK,
    TFT_BL,   //TFT背光
    led
    );
    input  Clk; 
    input  Reset_n;
    input  uart_rx;
    output [15:0]TFT_RGB;
    output TFT_HS;
    output TFT_VS;
    output TFT_DE;
    output TFT_CLK;
    output TFT_BL;
    output led;
    
//    assign TFT_BL = 1;
    
    reg     [15:0]   ram_rdaddr ;
    wire    [15:0]   ram_rdata  ;
    wire    [7:0]    rx_data    ;
    wire             rx_done    ;
    wire             ram_wren   ;
    wire  [15:0]     ram_eraddr ;
    wire  [15:0]     ram_wrdata ;
    wire             led        ;   
    wire             clk_TFT    ;
//    wire             locked     ;
    
    MMCM MMCM
        (
        // Clock out ports
        .clk_out1(clk_TFT),     // output clk_out1
        // Status and control signals
        .reset(!Reset_n), // input reset
        // Clock in ports
        .clk_in1(Clk));  
  
        
    img_rx_wr img_rx_wr(
        .Clk       (Clk       ) ,
        .Reset_n   (Reset_n   ) ,
        .rx_data   (rx_data   ) ,
        .rx_done   (rx_done   ) ,
        .ram_wren  (ram_wren  ) ,
        .ram_eraddr(ram_eraddr) ,
        .ram_wrdata(ram_wrdata) ,
        .led       (led       ) 
        );
        
    uart_byte_rx uart_byte_rx(
        .Clk     (Clk    )    ,
        .Reset_n (Reset_n)    ,
        .uart_rx (uart_rx)    ,
        .Baud_Set(2      )    ,
        .Data    (rx_data)    ,
        .Rx_done (rx_done)
        ); 

    RAM RAM (
          .clka(Clk),    // input wire clka
          .ena(1),      // input wire ena
          .wea(ram_wren),      // input wire [0 : 0] wea
          .addra(ram_eraddr),  // input wire [15 : 0] addra
          .dina(ram_wrdata),    // input wire [15 : 0] dina
          .clkb(clk_TFT),    // input wire clkb
          .enb(1),      // input wire enb
          .addrb(ram_rdaddr),  // input wire [15 : 0] addrb
          .doutb(ram_rdata )  // output wire [15 : 0] doutb
        ); 


    wire ram_data_en;
    wire Data_req;
    //RAM中存储的数据时256*256的像素矩阵
    always@(posedge clk_TFT or negedge Reset_n)
    if(!Reset_n)
        ram_rdaddr <= 0;
    else if(ram_data_en)
        ram_rdaddr <= ram_rdaddr + 1'd1;
        
    wire [11:0]h_count,v_count;
    wire [15:0]dis_data;
    assign ram_data_en = Data_req && (h_count >= 272 && h_count < 528) && (v_count >= 112 && v_count < 368);
    assign dis_data = ram_data_en ? ram_rdata: 0;
    TFT_Ctrl TFT_Ctrl(
        .Clk_33M (clk_TFT)  ,
        .Reset_n (Reset_n)  ,
        .Data_in (dis_data)  ,
        .Data_req(Data_req)  ,
        .hcount  (h_count)  ,   //行扫描位置(显示图像行扫描地址)
        .vcount  (v_count)  ,   //场扫描位置(显示图像场扫描地址)
        .TFT_HS  (TFT_HS  )  ,   //行同步信号
        .TFT_VS  (TFT_VS  )  ,   //场同步信号
        .TFT_DE  (TFT_DE  ) ,   //有效数据输出 
        .TFT_CLK (TFT_CLK )  ,   
        .TFT_DATA(TFT_RGB) ,     //红绿蓝三色 分别8位量化 R[7:0]G[7:0]B[7:0]  
        .TFT_BL  (TFT_BL  )
        );      
endmodule

为了仿真验证逻辑代码的准确性,我们可以在RAM中提前写入一张256*256大小的图片数据,如下图:

然后编写测试代码,验证逻辑的正确性:

测试代码如下:

`timescale 1ns / 1ps
module UART_RAM_TFT_TB();
    reg   Clk; 
    reg   Reset_n;
    reg   uart_rx;
    wire [15:0]TFT_RGB;
    wire TFT_HS;
    wire TFT_VS;
    wire TFT_DE;
    wire TFT_CLK;
    wire TFT_BL;
    wire led;

    UART_RAM_TFT UART_RAM_TFT(
        .Clk    (Clk    ) , 
        .Reset_n(Reset_n) ,
        .uart_rx(uart_rx) ,
        .TFT_RGB(TFT_RGB) ,   //TFT数据输出
        .TFT_HS (TFT_HS ) ,   // TFT行同步信号
        .TFT_VS (TFT_VS ) ,   //TFT场同步信号
        .TFT_DE (TFT_DE ) ,   //TFT数据有效信号
        .TFT_CLK(TFT_CLK) ,
        .TFT_BL (TFT_BL ) ,   //TFT背光
        .led    (led    )
        );   
    initial Clk = 1;
    always #10 Clk = ~Clk;
    
    initial begin
        Reset_n = 0;
        #201;
        Reset_n = 1;
        #2000;
        #20000000;
        $stop;
    end
endmodule

仿真波形如下:

TFT显示屏开始接收的数据波形图:

TFT显示屏最后接收的数据波形图:

7,总结

在本博客中实现了串口接收图像写入RAM并读出在TFT显示屏上显示的这样一个实验。这个实验中使用的时FPGA中片内RAM,所以只能显示一个256*256大小的图片。如果能够将存储器的容量扩大,比如DDR4存储器,那这个时候就可以用串口传输一整幅图像,就可以将完整图片显示在整个显示屏上去。再其次把串口接收到的数据改为摄像头采集到的实时的数据流,那就可以做一个摄像采集头图像,存储,实时显示的应用。再者,对采集到的图像数据。进行一定的各种滤波算法,检测算法等等,就可以实现图像处理功能。

  • 62
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于驱动 TFT 显示屏FPGA 程序,我可以给你一些基本的指导。首先,你需要了解你使用的 TFT 显示屏的接口类型和分辨率。常见的接口类型包括 SPI、I2C 和并行接口。 然后,你需要编写 FPGA 的驱动程序来控制 TFT 显示屏。这通常涉及到以下几个步骤: 1. 配置 FPGA 的 GPIO 接口,用于与 TFT 显示屏进行通信。根据显示屏的接口类型,你可能需要配置 SPI、I2C 或者并行总线的引脚。 2. 实现对 TFT 显示屏的初始化过程。这包括向显示屏发送初始化命令和参数,例如设置分辨率、像素格式等。 3. 编写显示图像数据的逻辑。这通常涉及到将图像数据从 FPGA 的存储器传输到显示屏的帧缓冲区。对于并行接口,你可能需要使用 DMA 控制器来实现高速数据传输。 4. 控制 TFT 显示屏的刷新过程。这涉及到根据图像数据的变化更新显示屏上的像素。 在实现驱动程序时,你需要参考 TFT 显示屏的数据手册以及 FPGA 开发板的引脚映射表。此外,你可能还需要使用 FPGA 开发工具(如 Quartus、Vivado 等)来进行硬件描述语言(如 Verilog 或 VHDL)的编写和综合。 需要注意的是,具体的驱动程序实现会因不同的 TFT 显示屏FPGA 平台而有所差异。因此,在开始开发之前,最好先详细研究你使用的 TFT 显示屏FPGA 开发板的相关文档,并参考类似的开源项目或教程以获得更多的指导。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值