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

2024-03-03 16:36

本文主要是介绍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;elseram_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/2assign 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 beginReset_n = 0;rx_data = 0;rx_done = 0;#201;Reset_n = 1;#2000;rx_data = 8'd255;repeat(131072)beginrx_done = 1;#20;rx_done = 0;#200;rx_data = rx_data - 1;end#2000000;repeat(131072)beginrx_done = 1;#20;rx_done = 0;#200;rx_data = rx_data - 1;end$stop;endendmodule

仿真波形如下:

写入第一个数据时:

写入最后一个数据时:

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    =  525module 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;elsehcount_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;elsevcount_r <= vcount_r + 1'd1;elsevcount_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) beginTFT_DE_r[0] <= Data_req;TFT_DE_r[3:1] <= TFT_DE_r[2:0];endassign  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) beginhcount <= 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) beginTFT_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) beginTFT_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) beginTFT_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 beginReset_n = 0;#201;Reset_n = 1;#2000;#20000000;$stop;end
endmodule

仿真波形如下:

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

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

7,总结

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

这篇关于FPGA-串口接收图像写入RAM并读出在TFT显示屏上显示的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/770229

相关文章

Python模拟串口通信的示例详解

《Python模拟串口通信的示例详解》pySerial是Python中用于操作串口的第三方模块,它支持Windows、Linux、OSX、BSD等多个平台,下面我们就来看看Python如何使用pySe... 目录1.win 下载虚www.chinasem.cn拟串口2、确定串口号3、配置串口4、串口通信示例5

Python+wxPython构建图像编辑器

《Python+wxPython构建图像编辑器》图像编辑应用是学习GUI编程和图像处理的绝佳项目,本教程中,我们将使用wxPython,一个跨平台的PythonGUI工具包,构建一个简单的... 目录引言环境设置创建主窗口加载和显示图像实现绘制工具矩形绘制箭头绘制文字绘制临时绘制处理缩放和旋转缩放旋转保存编

python+OpenCV反投影图像的实现示例详解

《python+OpenCV反投影图像的实现示例详解》:本文主要介绍python+OpenCV反投影图像的实现示例详解,本文通过实例代码图文并茂的形式给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录一、前言二、什么是反投影图像三、反投影图像的概念四、反向投影的工作原理一、利用反向投影backproj

电脑显示mfc100u.dll丢失怎么办?系统报错mfc90u.dll丢失5种修复方案

《电脑显示mfc100u.dll丢失怎么办?系统报错mfc90u.dll丢失5种修复方案》最近有不少兄弟反映,电脑突然弹出“mfc100u.dll已加载,但找不到入口点”的错误提示,导致一些程序无法正... 在计算机使用过程中,我们经常会遇到一些错误提示,其中最常见的就是“找不到指定的模块”或“缺少某个DL

使用Java将各种数据写入Excel表格的操作示例

《使用Java将各种数据写入Excel表格的操作示例》在数据处理与管理领域,Excel凭借其强大的功能和广泛的应用,成为了数据存储与展示的重要工具,在Java开发过程中,常常需要将不同类型的数据,本文... 目录前言安装免费Java库1. 写入文本、或数值到 Excel单元格2. 写入数组到 Excel表格

SpringBoot请求参数接收控制指南分享

《SpringBoot请求参数接收控制指南分享》:本文主要介绍SpringBoot请求参数接收控制指南,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Spring Boot 请求参数接收控制指南1. 概述2. 有注解时参数接收方式对比3. 无注解时接收参数默认位置

利用Python调试串口的示例代码

《利用Python调试串口的示例代码》在嵌入式开发、物联网设备调试过程中,串口通信是最基础的调试手段本文将带你用Python+ttkbootstrap打造一款高颜值、多功能的串口调试助手,需要的可以了... 目录概述:为什么需要专业的串口调试工具项目架构设计1.1 技术栈选型1.2 关键类说明1.3 线程模

使用Python实现图像LBP特征提取的操作方法

《使用Python实现图像LBP特征提取的操作方法》LBP特征叫做局部二值模式,常用于纹理特征提取,并在纹理分类中具有较强的区分能力,本文给大家介绍了如何使用Python实现图像LBP特征提取的操作方... 目录一、LBP特征介绍二、LBP特征描述三、一些改进版本的LBP1.圆形LBP算子2.旋转不变的LB

OpenCV图像形态学的实现

《OpenCV图像形态学的实现》本文主要介绍了OpenCV图像形态学的实现,包括腐蚀、膨胀、开运算、闭运算、梯度运算、顶帽运算和黑帽运算,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起... 目录一、图像形态学简介二、腐蚀(Erosion)1. 原理2. OpenCV 实现三、膨胀China编程(

SpringBatch数据写入实现

《SpringBatch数据写入实现》SpringBatch通过ItemWriter接口及其丰富的实现,提供了强大的数据写入能力,本文主要介绍了SpringBatch数据写入实现,具有一定的参考价值,... 目录python引言一、ItemWriter核心概念二、数据库写入实现三、文件写入实现四、多目标写入