串口-RAM-VGA系列知识分享:
(0)串口通信实现-串口接收
(1)VGA成像原理与简单实现
(2)VGA显示板级验证
(3)VGA显示-多分辨率输入
(4)串口发送+RAM+VGA传图


前言

本文使用串口发送模块发送数据存储到RAM中,通过RAM读取数据传输图片给TFT显示屏。


提示:以下是本篇文章正文内容,下面案例可供参考

一、结构总体设计

1.UART_RAM_TFT总体设计草图

2.img_rx_wr(RAM写入逻辑)设计草图

二、顶层模块

代码如下:

`timescale 1ns / 1ps

module UART_RAM_TFT(
    Clk,
    Reset_n,
    uart_rx,
    VGA_RGB,//TFT数据输出
    VGA_HS, //TFT行同步信号
    VGA_VS, //TFT场同步信号
    VGA_BLK,        //VGA 场消隐信号
    VGA_CLK,
    TFT_BL  //背光
);


    input Clk;
    input Reset_n;
    input uart_rx;
    output [15:0]VGA_RGB;
    output VGA_HS;
    output VGA_VS;
    output VGA_BLK;     //VGA 场消隐信号
    output VGA_CLK; 
    output TFT_BL;

    wire [7:0]rx_data;
    wire rx_done;

    wire ram_wren;
    wire [15:0]ram_wraddr;
    wire [15:0]ram_wrdata;
    reg [15:0]ram_rdaddr;
    wire Clk_TFT;
    wire [15:0]ram_rddata;

    assign VGA_CLK = Clk_TFT;
    assign TFT_BL = 1;

    MMCM MMCM(
        .clk_out1(Clk_TFT),
        .clk_in1(Clk)
    );

    uart_byte_rx uart_byte_rx(
        .Clk(Clk),
        .Reset_n(Reset_n),
        .Baud_Set(4),
        .uart_rx(uart_rx),
        .Data(rx_data),
        .Rx_Done(rx_done)  
    ); 

    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_wraddr(ram_wraddr),
        .ram_wrdata(ram_wrdata)
    );

    RAM RAM (
      .clka(Clk),    // input wire clka
      .ena(1),      // input wire ena
      .wea(ram_wren),      // input wire [0 : 0] wea
      .addra(ram_wraddr),  // 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_rddata)  // output wire [15 : 0] doutb
    );

    wire Data_Req;
    wire [11:0]hcount,vcount;

    wire [15:0]disp_data;
    VGA_CTRL VGA_CTRL(
        .Clk(Clk_TFT),    //系统输入时钟33MHZ
        .Reset_n(Reset_n),
        .Data(disp_data),    //待显示数据
        .Data_Req(Data_Req),
        .hcount(hcount),        //VGA行扫描计数器
        .vcount(vcount),        //VGA场扫描计数器
        .VGA_RGB(VGA_RGB),  //VGA数据输出
        .VGA_HS(VGA_HS),        //VGA行同步信号
        .VGA_VS(VGA_VS),        //VGA场同步信号
        .VGA_BLK(VGA_BLK)      //VGA 场消隐信号
    );

    //RAM中存储的图像是256*256的像素矩阵
     wire ram_data_en;
    assign ram_data_en = Data_Req && (hcount <= 255) && (vcount <= 255);

    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 data_en;
    wire [7:0]R ,G ,B;
    assign R = 8'hFF,G = 8'h00,B = 8'h00;
    wire [15:0]RED; 
    assign RED = {R[7:3],G[7:2],B[7:3]};
    assign data_en = Data_Req && (hcount <= 257) && (vcount <= 255);
    assign disp_data = data_en? ram_rddata:RED;//将多余图像显示红色
    //assign disp_data = ram_data_en? ram_rddata:0;    //将多余数据归0显示黑色

endmodule

三、img_rx_wr模块

module img_rx_wr(
    Clk,
    Reset_n,
    rx_data,
    rx_done,
    ram_wren,
    ram_wraddr,
    ram_wrdata
);
    input Clk;
    input Reset_n;
    input [7:0]rx_data;    //串口数据
    input rx_done;         //串口发送停止信号
    output reg ram_wren;   //开始写入信号
    output reg[15:0]ram_wraddr;   //RAM核写入地址
    output [15:0]ram_wrdata;      //写入数据

    reg [15:0]rx_data_tmp;

    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;

    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n) 
        rx_data_tmp <= 0;
    else if(rx_done)     //串口一次发一个字节,RGB565一次需要2个字节
        rx_data_tmp <= {rx_data_tmp[7:0], rx_data};



//---------------------------------------------------------------
  //17'h0      ...0000
  //17'h1      ...0001
  //17'h2      ...0010
  //根据规律奇数末位为1
//---------------------------------------------------------------       
    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_wraddr <= 0;
    else if(rx_done && data_cnt[0]) 
        ram_wraddr <= data_cnt[16:1];  //data_cnt/2    

    assign ram_wrdata = rx_data_tmp;

endmodule

四、UART_rx模块

`timescale 1ns/1ns

module uart_byte_rx(
    Clk,
    Reset_n,
    Baud_Set,
    uart_rx,
    Data,
    Rx_Done,  
);

   input Clk;
   input Reset_n;
   input [2:0]Baud_Set;
   input uart_rx;
   output reg[7:0]Data; 
   output reg Rx_Done;

   reg [1:0]sync_uart_rx;

   always@(posedge Clk)begin
       sync_uart_rx[0] <= #1 uart_rx;
       sync_uart_rx[1] <= #1 sync_uart_rx[0] ;
   end

   reg [1:0]uart_rx_r;
   always@(posedge Clk)begin
        uart_rx_r[0] <= #1 sync_uart_rx[1];
        uart_rx_r[1] <= #1 uart_rx_r[0] ;
   end

    wire pedge_uart_rx;
//    assign pedge_uart_rx = ((uart_rx_r[1] == 0) && (uart_rx_r[0] == 1));
    assign pedge_uart_rx = (uart_rx_r == 2'b01);
    wire nedge_uart_rx;
//    assign nedge_uart_rx = ((uart_rx_r[1] == 1) && (uart_rx_r[0] == 0));  
    assign nedge_uart_rx = (uart_rx_r == 2'b10);  

    reg [8:0]  Bps_DR;
    always@(*)
        case(Baud_Set)
            0:Bps_DR = 1000000000/9600/16/20 - 2;
            1:Bps_DR = 1000000000/19200/16/20 -2;
            2:Bps_DR = 1000000000/38400/16/20 - 2;
            3:Bps_DR = 1000000000/57600/16/20 - 2;
            4:Bps_DR = 1000000000/115200/16/20 - 2;
            5:Bps_DR = 1000000000/1562500/16/20 - 2;
            default:Bps_DR = 1000000000/9600/16/20 - 1;
        endcase

    wire bps_clk_16x;
    assign bps_clk_16x = (div_cnt == Bps_DR / 2);

    reg RX_EN;
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n) 
        RX_EN <= #1 0;
    else if(nedge_uart_rx)
        RX_EN <= #1 1;
    else if(Rx_Done || (sta_bit >= 4))
        RX_EN <= #1 0;

    reg [8:0]div_cnt;
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)    
        div_cnt <= #1 0;
    else if(RX_EN)begin
        if(div_cnt == Bps_DR)
            div_cnt <= #1 0;
        else
            div_cnt <= #1 div_cnt + 1'b1;
    end
    else
        div_cnt <= #1 0;

    reg [7:0]bps_cnt;
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n) 
        bps_cnt <= #1 0;
    else if(RX_EN)begin
        if(bps_clk_16x)begin
            if(bps_cnt == 160)
                bps_cnt <= #1 0;
            else
                bps_cnt <= #1 bps_cnt + 1'b1;
        end
        else
            bps_cnt <= #1 bps_cnt;
     end
     else
        bps_cnt <= #1 0;

    //reg width name number/depth
    reg[2:0]r_data[7:0];
    reg [2:0]sta_bit;
    reg [2:0]sto_bit;

    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n) begin
        sta_bit <= #1 0;
        sto_bit <= #1 0;
        r_data[0] <= #1 0;
        r_data[1] <= #1 0;
        r_data[2] <= #1 0;
        r_data[3] <= #1 0;
        r_data[4] <= #1 0;
        r_data[5] <= #1 0;
        r_data[6] <= #1 0;
        r_data[7] <= #1 0;
    end
    else if(bps_clk_16x)begin
        case(bps_cnt)
            0:begin
                sta_bit <= #1 0;
                sto_bit <= #1 0;
                r_data[0] <= #1 0;
                r_data[1] <= #1 0;
                r_data[2] <= #1 0;
                r_data[3] <= #1 0;
                r_data[4] <= #1 0;
                r_data[5] <= #1 0;
                r_data[6] <= #1 0;
                r_data[7] <= #1 0;
            end
            5,6,7,8,9,10,11:sta_bit <= #1 sta_bit + sync_uart_rx[1];
            21,22,23,24,25,26,27: r_data[0] <= #1 r_data[0] + sync_uart_rx[1];
            37,38,39,40,41,42,43: r_data[1] <= #1 r_data[1] + sync_uart_rx[1];
            53,54,55,56,57,58,59: r_data[2] <= #1 r_data[2] + sync_uart_rx[1];
            69,70,71,72,73,74,75: r_data[3] <= #1 r_data[3] + sync_uart_rx[1];
            85,86,87,88,89,90,91: r_data[4] <= #1 r_data[4] + sync_uart_rx[1];
            101,102,103,104,105,106,107: r_data[5] <= #1 r_data[5] + sync_uart_rx[1];
            117,118,119,120,121,122,123: r_data[6] <= #1 r_data[6] + sync_uart_rx[1];
            133,134,135,136,137,138,139: r_data[7] <= #1 r_data[7] + sync_uart_rx[1];
            149,150,151,152,153,154,155: sto_bit <= #1 sto_bit + sync_uart_rx[1];
            default:;
        endcase
    end

    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n) 
        Data <= #1 0;        
    else if(bps_clk_16x && (bps_cnt == 159))begin
        Data[0] <= #1 (r_data[0] >= 4)?1'b1:1'b0;
        Data[1] <= #1 (r_data[1] >= 4)?1'b1:1'b0;
        Data[2] <= #1 (r_data[2] >= 4)?1'b1:1'b0;
        Data[3] <= #1 (r_data[3] >= 4)?1'b1:1'b0;
        Data[4] <= #1 (r_data[4] >= 4)?1'b1:1'b0;
        Data[5] <= #1 (r_data[5] >= 4)?1'b1:1'b0;
        Data[6] <= #1 (r_data[6] >= 4)?1'b1:1'b0;
        Data[7] <= #1 (r_data[7] >= 4)?1'b1:1'b0;
    end 

//    always@(posedge Clk or negedge Reset_n)
//    if(!Reset_n) 
//        Data <= #1 0;        
//    else if(bps_clk_16x && (bps_cnt == 159))begin
//        Data[0] <= #1 r_data[0][2];
//        Data[1] <= #1 r_data[1][2];
//        Data[2] <= #1 r_data[2][2];
//        Data[3] <= #1 r_data[3][2];
//        Data[4] <= #1 r_data[4][2];
//        Data[5] <= #1 r_data[5][2];
//        Data[6] <= #1 r_data[6][2];
//        Data[7] <= #1 r_data[7][2];
//    end 

    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n) 
        Rx_Done <= #1 0;
    else if((div_cnt == Bps_DR/2) && (bps_cnt == 160))
        Rx_Done <= #1 1;
    else
        Rx_Done <= #1 0; 

endmodule

五、VGA_CTRL模块

module VGA_CTRL(
    Clk,
    Reset_n,
    Data,
    Data_Req,
    hcount,
    vcount,
    VGA_HS,
    VGA_VS,
    VGA_BLK,
    VGA_RGB
);

    input Clk;
    input Reset_n;
    input [15:0]Data;
    output reg Data_Req;
    output reg [11:0]hcount; //当前扫描点的H坐标
    output reg [11:0]vcount; //当前扫描点的V坐标
    output VGA_HS;
    output VGA_VS; 
    output VGA_BLK;
    output reg [15:0]VGA_RGB;//{R[4:0]、G[5:0]、B[4:0]}

    `include "vga_parameter.v"
    localparam Hsync_End = `H_Total_Time;
    localparam HS_End = `H_Sync_Time;
    localparam Hdat_Begin = `H_Sync_Time + `H_Back_Porch + `H_Left_Border;
    localparam Hdat_End = `H_Sync_Time + `H_Left_Border + `H_Back_Porch + `H_Data_Time;
    localparam Vsync_End = `V_Total_Time;
    localparam VS_End = `V_Sync_Time;
    localparam Vdat_Begin =  `V_Sync_Time + `V_Back_Porch + `V_Top_Border;
    localparam Vdat_End = `V_Sync_Time + `V_Back_Porch + `V_Top_Border + `V_Data_Time;

    reg [11:0]hcnt;//行扫描计数器
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        hcnt <= 0;
    else if(hcnt >= Hsync_End -1)
        hcnt <= 0;
    else
        hcnt <= hcnt + 1'b1;


    //RAM_IP核输出数据有三拍延迟此处为了对齐数据传输

    reg [3:0]VGA_HS_r;

    always@(posedge Clk)begin
        VGA_HS_r[0] <= (hcnt < HS_End)?0:1;
        VGA_HS_r[3:1] <= VGA_HS_r[2:0];        
    end

    assign VGA_HS = VGA_HS_r[2];

    reg [11:0]vcnt;//场扫描计数器
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        vcnt <= 0;
    else if(hcnt == Hsync_End -1)begin
        if(vcnt >= Vsync_End -1)
            vcnt <= 0;
        else
            vcnt <= vcnt + 1'd1;
    end
    else
        vcnt <= vcnt;

    //RAM_IP核输出数据有三拍延迟此处为了对齐数据传输
    reg [3:0]VGA_VS_r;   
    always@(posedge Clk)begin
        VGA_VS_r[0]  <= (vcnt < VS_End)?0:1;
        VGA_VS_r[3:1] <= VGA_VS_r[2:0];
    end

    assign VGA_VS = VGA_VS_r[2];

    //BLK表示的就是输出输出的时间段
//    assign VGA_BLK = ((hcnt >= Hdat_Begin - 1) && (hcnt < Hdat_End - 1) && (vcnt >= Vdat_Begin - 1) && (vcnt < Vdat_End))?1:0;
//    always@(posedge Clk)
//         VGA_BLK <= ((hcnt >= Hdat_Begin - 1) && (hcnt < Hdat_End - 1) && (vcnt >= Vdat_Begin - 1) && (vcnt < Vdat_End))?1:0;   

    always@(posedge Clk)
       Data_Req <= ((hcnt >= Hdat_Begin - 1) && (hcnt < Hdat_End - 1) && (vcnt >= Vdat_Begin) && (vcnt < Vdat_End))?1:0;

    //RAM_IP核输出数据有三拍延迟此处为了对齐数据传输
    reg [3:0]VGA_BLK_r;
    always@(posedge Clk)begin
       VGA_BLK_r[0] <= Data_Req;
       VGA_BLK_r[3:1] <= VGA_BLK_r[2:0];
    end
        assign VGA_BLK = VGA_BLK_r[3];

//    assign VGA_RGB = VGA_BLK? Data:0;
    always@(posedge Clk)
        VGA_RGB <= Data_Req? Data:0;

    always@(posedge Clk)
       hcount <= Data_Req? hcnt - Hdat_Begin:0; 

    always@(posedge Clk)
       vcount <= Data_Req? (vcnt - Vdat_Begin):vcount;          

endmodule

六、parameter.v文件

//`define Resolution_480x272 1    //刷新率为60Hz时像素时钟为9MHz
//`define Resolution_640x480 1    //刷新率为60Hz时像素时钟为25.175MHz
`define Resolution_800x480 1    //刷新率为60Hz时像素时钟为33MHz
//`define Resolution_800x600 1    //刷新率为60Hz时像素时钟为40MHz
//`define Resolution_1024x768 1    //刷新率为60Hz时像素时钟为65MHz
//`define Resolution_1280x720 1    //刷新率为60Hz时像素时钟为74.25MHz
//`define Resolution_1920x1080 1    //刷新率为60Hz时像素时钟为148.5MHz

`ifdef Resolution_480x272    
    `define H_Right_Border 0
    `define H_Front_Porch 2
    `define H_Sync_Time 41
    `define H_Back_Porch 2
    `define H_Left_Border 0
    `define H_Data_Time 480
    `define H_Total_Time 525
    `define V_Bottom_Border 0
    `define V_Front_Porch 2
    `define V_Sync_Time 10
    `define V_Back_Porch 2
    `define V_Top_Border 0
    `define V_Data_Time 272
    `define V_Total_Time 286

`elsif Resolution_640x480
    `define H_Total_Time  12'd800
    `define H_Right_Border  12'd8
    `define H_Front_Porch  12'd8
    `define H_Sync_Time  12'd96
    `define H_Data_Time 12'd640
    `define H_Back_Porch  12'd40
    `define H_Left_Border  12'd8
    `define V_Total_Time  12'd525
    `define V_Bottom_Border  12'd8
    `define V_Front_Porch  12'd2
    `define V_Sync_Time  12'd2
    `define V_Data_Time 12'd480
    `define V_Back_Porch  12'd25
    `define V_Top_Border  12'd8

`elsif Resolution_800x480
    `define H_Total_Time 12'd1056
    `define H_Right_Border 12'd0
    `define H_Front_Porch 12'd40
    `define H_Sync_Time 12'd128
    `define H_Data_Time 12'd800
    `define H_Back_Porch 12'd88
    `define H_Left_Border 12'd0

    `define V_Total_Time 12'd525
    `define V_Bottom_Border 12'd8
    `define V_Front_Porch 12'd2
    `define V_Sync_Time 12'd2
    `define V_Data_Time 12'd480
    `define V_Back_Porch 12'd25
    `define V_Top_Border 12'd8

`elsif Resolution_800x600
    `define H_Total_Time 12'd1056
    `define H_Right_Border 12'd0
    `define H_Front_Porch 12'd40
    `define H_Sync_Time 12'd128
    `define H_Data_Time 12'd800
    `define H_Back_Porch 12'd88
    `define H_Left_Border 12'd0

    `define V_Total_Time 12'd628
    `define V_Bottom_Border 12'd0
    `define V_Front_Porch 12'd1
    `define V_Sync_Time 12'd4
    `define V_Data_Time 12'd600
    `define V_Back_Porch 12'd23
    `define V_Top_Border 12'd0

`elsif Resolution_1024x768
    `define H_Total_Time 12'd1344
    `define H_Right_Border 12'd0
    `define H_Front_Porch 12'd24
    `define H_Sync_Time 12'd136
    `define H_Data_Time 12'd1024
    `define H_Back_Porch 12'd160
    `define H_Left_Border 12'd0

    `define V_Total_Time 12'd806
    `define V_Bottom_Border 12'd0
    `define V_Front_Porch 12'd3
    `define V_Sync_Time 12'd6
    `define V_Data_Time 12'd768
    `define V_Back_Porch 12'd29
    `define V_Top_Border 12'd0

`elsif Resolution_1280x720
    `define H_Total_Time 12'd1650
    `define H_Right_Border 12'd0
    `define H_Front_Porch 12'd110
    `define H_Sync_Time 12'd40
    `define H_Data_Time 12'd1280
    `define H_Back_Porch 12'd220
    `define H_Left_Border 12'd0

    `define V_Total_Time 12'd750
    `define V_Bottom_Border 12'd0
    `define V_Front_Porch 12'd5
    `define V_Sync_Time 12'd5
    `define V_Data_Time 12'd720
    `define V_Back_Porch 12'd20
    `define V_Top_Border 12'd0

`elsif Resolution_1920x1080
    `define H_Total_Time 12'd2200
    `define H_Right_Border 12'd0
    `define H_Front_Porch 12'd88
    `define H_Sync_Time 12'd44
    `define H_Data_Time 12'd1920
    `define H_Back_Porch 12'd148
    `define H_Left_Border 12'd0

    `define V_Total_Time 12'd1125
    `define V_Bottom_Border 12'd0
    `define V_Front_Porch 12'd4
    `define V_Sync_Time 12'd5
    `define V_Data_Time 12'd1080
    `define V_Back_Porch 12'd36
    `define V_Top_Border 12'd0    

`endif

七、clk_IP和RAM_IP配置

具体IP配置根据所需选择,可参考我之前文章,自行根据所需配置。

八、仿真文件展示

`timescale 1ns / 1ps

module UART_RAM_TFT_tb(

    );

    reg Clk;
    reg Reset_n;
    reg uart_rx;
    wire [15:0]VGA_RGB;
    wire VGA_HS;
    wire VGA_VS;
    wire VGA_BLK;     //VGA 场消隐信号
    wire VGA_CLK; 
    wire TFT_BL;

    UART_RAM_TFT UART_RAM_TFT(
        Clk,
        Reset_n,
        uart_rx,
        VGA_RGB,//TFT数据输出
        VGA_HS, //TFT行同步信号
        VGA_VS, //TFT场同步信号
        VGA_BLK,        //VGA 场消隐信号
        VGA_CLK,
        TFT_BL  //背光
    );

    initial Clk = 1;
    always#10 Clk = ~Clk;

    initial begin
        Reset_n = 0;
        #201;
        Reset_n = 1;
        #2000;
        #2000000;
        $stop;
    end    

endmodule

九、上板演示

串口传图展示:

【附件:】链接:https://pan.baidu.com/s/1wWgnTeCfzlFzGWUCQe7ANg?pwd=mnz3
提取码:mnz3