• 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的逻辑时序图如下:

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

    1. module img_rx_wr(
    2. Clk ,
    3. Reset_n ,
    4. rx_data ,
    5. rx_done ,
    6. ram_wren ,
    7. ram_eraddr ,
    8. ram_wrdata ,
    9. led
    10. );
    11. input Clk ;
    12. input Reset_n ;
    13. input [7:0] rx_data ;
    14. input rx_done ;
    15. output reg ram_wren ;
    16. output reg [15:0] ram_eraddr ;
    17. output [15:0] ram_wrdata ;
    18. output reg led ;
    19. reg[16:0]data_cnt; //统计串口接收数据个数计数器
    20. always@(posedge Clk or negedge Reset_n)
    21. if(!Reset_n)
    22. data_cnt <= 0;
    23. else if(rx_done)
    24. data_cnt <= data_cnt + 1'd1;
    25. reg [15:0]rx_data_temp;
    26. always@(posedge Clk or negedge Reset_n)
    27. if(!Reset_n)
    28. rx_data_temp <= 1'b0;
    29. else if(rx_done)
    30. rx_data_temp <= {rx_data_temp[7:0],rx_data};
    31. always@(posedge Clk or negedge Reset_n)
    32. if(!Reset_n)
    33. ram_wren <= 0;
    34. else if(rx_done && data_cnt[0])
    35. ram_wren <= 1'd1;
    36. else
    37. ram_wren <= 0;
    38. always@(posedge Clk or negedge Reset_n)
    39. if(!Reset_n)
    40. ram_eraddr <= 0;
    41. else if (rx_done && data_cnt[0])
    42. ram_eraddr <= data_cnt[16:1]; // data_cnt/2
    43. assign ram_wrdata = rx_data_temp;
    44. always@(posedge Clk or negedge Reset_n)
    45. if(!Reset_n)
    46. led <= 0;
    47. else if((rx_done)&&(data_cnt == 131071))
    48. led <= ~led ;
    49. endmodule

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

    编写测试文件:

    1. `timescale 1ns / 1ps
    2. module img_rx_wr_tb;
    3. reg Clk ;
    4. reg Reset_n ;
    5. reg [7:0] rx_data ;
    6. reg rx_done ;
    7. wire ram_wren ;
    8. wire [15:0] ram_eraddr ;
    9. wire [15:0] ram_wrdata ;
    10. wire led ;
    11. img_rx_wr img_rx_wr(
    12. .Clk (Clk ) ,
    13. .Reset_n (Reset_n ) ,
    14. .rx_data (rx_data ) ,
    15. .rx_done (rx_done ) ,
    16. .ram_wren (ram_wren ) ,
    17. .ram_eraddr(ram_eraddr) ,
    18. .ram_wrdata(ram_wrdata) ,
    19. .led (led )
    20. );
    21. initial Clk =1;
    22. always #10 Clk = ~Clk;
    23. initial begin
    24. Reset_n = 0;
    25. rx_data = 0;
    26. rx_done = 0;
    27. #201;
    28. Reset_n = 1;
    29. #2000;
    30. rx_data = 8'd255;
    31. repeat(131072)begin
    32. rx_done = 1;
    33. #20;
    34. rx_done = 0;
    35. #200;
    36. rx_data = rx_data - 1;
    37. end
    38. #2000000;
    39. repeat(131072)begin
    40. rx_done = 1;
    41. #20;
    42. rx_done = 0;
    43. #200;
    44. rx_data = rx_data - 1;
    45. end
    46. $stop;
    47. end
    48. 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得出地址后数据输出是有延迟时,后面使用时为了确保数据一个都不丢,进行时序对齐)将链接中的逻辑代码重新设计,如下:

    1. `include "disp_parameter_cfg.v"
    2. //800x480
    3. //H_Right_Borde = 0 V_Bottom_Bord = 8
    4. //H_Front_Porch = 40 V_Front_Porch = 2
    5. //H_Sync_Time = 128 V_Sync_Time = 2
    6. //H_Back_Porch = 88 V_Back_Porch = 25
    7. //H_Left_Border = 0 V_Top_Border = 8
    8. //H_Data_Time = 800 V_Data_Time = 480
    9. //H_Total_Time = 1056 V_Total_Time = 525
    10. module TFT_Ctrl(
    11. Clk_33M ,
    12. Reset_n ,
    13. Data_in ,
    14. Data_req ,
    15. hcount , //行扫描位置(显示图像行扫描地址)
    16. vcount , //场扫描位置(显示图像场扫描地址)
    17. TFT_HS , //行同步信号
    18. TFT_VS , //场同步信号
    19. TFT_DE , //有效数据输出
    20. TFT_CLK ,
    21. TFT_DATA , //红绿蓝三色 分别8位量化 R[7:0]G[7:0]B[7:0]
    22. TFT_BL
    23. );
    24. input Clk_33M;
    25. input Reset_n;
    26. input [15:0] Data_in;
    27. output reg Data_req;
    28. output reg [11:0] hcount;
    29. output reg [11:0] vcount;
    30. output TFT_HS;
    31. output TFT_VS;
    32. output TFT_DE;
    33. output TFT_CLK;
    34. output reg [15:0] TFT_DATA; //红绿蓝三色 分别8位量化 R[7:0]G[7:0]B[7:0]
    35. output TFT_BL;
    36. // parameter VGA_HS_end = 11'd127 ,
    37. // hdat_begin = 11'd216 ,
    38. // hdat_end = 11'd1016 ,
    39. // hpixel_end = 11'd1055 ,
    40. // VGA_VS_end = 11'd1 ,
    41. // vdat_begin = 11'd35 ,
    42. // vdat_end = 11'd515 ,
    43. // vline_end = 11'd524 ;
    44. parameter TFT_HS_end = `H_Sync_Time-1 ;
    45. parameter hdat_begin = `H_Sync_Time + `H_Back_Porch +`H_Left_Border - 1'b1;
    46. parameter hdat_end = `H_Total_Time - `H_Right_Border -`H_Front_Porch - 1'b1;
    47. parameter vdat_begin = `V_Sync_Time + `V_Back_Porch +`V_Top_Border - 1'b1;
    48. parameter vdat_end = `V_Total_Time - `V_Bottom_Border -`V_Front_Porch - 1'b1;
    49. parameter hpixel_end = `H_Total_Time -1 ;
    50. parameter TFT_VS_end = `V_Sync_Time-1 ;
    51. parameter vline_end = `V_Total_Time -1 ;
    52. reg [11:0] hcount_r;
    53. reg [11:0] vcount_r;
    54. always@(posedge Clk_33M or negedge Reset_n)
    55. if(!Reset_n)
    56. hcount_r <= 11'd0;
    57. else if(hcount_r == hpixel_end )
    58. hcount_r <= 11'd0;
    59. else
    60. hcount_r <= hcount_r + 1'd1;
    61. always@(posedge Clk_33M or negedge Reset_n)
    62. if(!Reset_n)
    63. vcount_r <= 11'd0;
    64. else if(hcount_r == hpixel_end)
    65. if(vcount_r == vline_end )
    66. vcount_r <= 11'd0;
    67. else
    68. vcount_r <= vcount_r + 1'd1;
    69. else
    70. vcount_r <= vcount_r;
    71. always@(posedge Clk_33M)
    72. Data_req <= ((hcount_r >= hdat_begin) && (hcount_r < hdat_end)&&
    73. (vcount_r >= vdat_begin) && (vcount_r < vdat_end)) ? 1'b1 : 1'b0;
    74. reg [3:0]TFT_DE_r;
    75. always@(posedge Clk_33M) begin
    76. TFT_DE_r[0] <= Data_req;
    77. TFT_DE_r[3:1] <= TFT_DE_r[2:0];
    78. end
    79. assign TFT_DE = TFT_DE_r[2];
    80. // assign TFT_DE = ((hcount_r >= hdat_begin) && (hcount_r < hdat_end)&&
    81. // (vcount_r >= vdat_begin) && (vcount_r < vdat_end)) ? 1'b1 : 1'b0;
    82. always@(posedge Clk_33M) begin
    83. hcount <= Data_req ? (hcount_r - hdat_begin) : 10'd0;
    84. vcount <= Data_req ? (vcount_r - vdat_begin) : vcount;
    85. end
    86. // assign hcount = TFT_DE ? (hcount_r - hdat_begin) : 10'd0;
    87. // assign vcount = TFT_DE ? (vcount_r - vdat_begin) : 10'd0;
    88. reg [3:0]TFT_HS_r;
    89. always@(posedge Clk_33M) begin
    90. TFT_HS_r[0] <= (hcount_r > TFT_HS_end)? 1'b1 :1'b0;
    91. TFT_HS_r[3:1] <= TFT_HS_r[2:0];
    92. end
    93. assign TFT_HS = TFT_HS_r[2];
    94. // assign TFT_HS = (hcount_r > TFT_HS_end)? 1'b1 :1'b0;
    95. reg [3:0]TFT_VS_r;
    96. always@(posedge Clk_33M) begin
    97. TFT_VS_r[0] <= (vcount_r > TFT_VS_end)? 1'b1 :1'b0;
    98. TFT_VS_r[3:1] <= TFT_VS_r[2:0];
    99. end
    100. assign TFT_VS = TFT_VS_r[2];
    101. // assign TFT_VS = (vcount_r > TFT_VS_end)? 1'b1 :1'b0;
    102. always@(posedge Clk_33M) begin
    103. TFT_DATA <= (TFT_DE) ? Data_in : 16'h0000;
    104. end
    105. // assign TFT_DATA = (TFT_DE) ? Data_in : 24'h000000;
    106. assign TFT_CLK = ~Clk_33M;
    107. assign TFT_BL = 1;
    108. endmodule

    6,顶层模块

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

    代码如下:

    1. `timescale 1ns / 1ps
    2. module UART_RAM_TFT(
    3. Clk,
    4. Reset_n,
    5. uart_rx,
    6. TFT_RGB, //TFT数据输出
    7. TFT_HS, // TFT行同步信号
    8. TFT_VS, //TFT场同步信号
    9. TFT_DE, //TFT数据有效信号
    10. TFT_CLK,
    11. TFT_BL, //TFT背光
    12. led
    13. );
    14. input Clk;
    15. input Reset_n;
    16. input uart_rx;
    17. output [15:0]TFT_RGB;
    18. output TFT_HS;
    19. output TFT_VS;
    20. output TFT_DE;
    21. output TFT_CLK;
    22. output TFT_BL;
    23. output led;
    24. // assign TFT_BL = 1;
    25. reg [15:0] ram_rdaddr ;
    26. wire [15:0] ram_rdata ;
    27. wire [7:0] rx_data ;
    28. wire rx_done ;
    29. wire ram_wren ;
    30. wire [15:0] ram_eraddr ;
    31. wire [15:0] ram_wrdata ;
    32. wire led ;
    33. wire clk_TFT ;
    34. // wire locked ;
    35. MMCM MMCM
    36. (
    37. // Clock out ports
    38. .clk_out1(clk_TFT), // output clk_out1
    39. // Status and control signals
    40. .reset(!Reset_n), // input reset
    41. // Clock in ports
    42. .clk_in1(Clk));
    43. img_rx_wr img_rx_wr(
    44. .Clk (Clk ) ,
    45. .Reset_n (Reset_n ) ,
    46. .rx_data (rx_data ) ,
    47. .rx_done (rx_done ) ,
    48. .ram_wren (ram_wren ) ,
    49. .ram_eraddr(ram_eraddr) ,
    50. .ram_wrdata(ram_wrdata) ,
    51. .led (led )
    52. );
    53. uart_byte_rx uart_byte_rx(
    54. .Clk (Clk ) ,
    55. .Reset_n (Reset_n) ,
    56. .uart_rx (uart_rx) ,
    57. .Baud_Set(2 ) ,
    58. .Data (rx_data) ,
    59. .Rx_done (rx_done)
    60. );
    61. RAM RAM (
    62. .clka(Clk), // input wire clka
    63. .ena(1), // input wire ena
    64. .wea(ram_wren), // input wire [0 : 0] wea
    65. .addra(ram_eraddr), // input wire [15 : 0] addra
    66. .dina(ram_wrdata), // input wire [15 : 0] dina
    67. .clkb(clk_TFT), // input wire clkb
    68. .enb(1), // input wire enb
    69. .addrb(ram_rdaddr), // input wire [15 : 0] addrb
    70. .doutb(ram_rdata ) // output wire [15 : 0] doutb
    71. );
    72. wire ram_data_en;
    73. wire Data_req;
    74. //RAM中存储的数据时256*256的像素矩阵
    75. always@(posedge clk_TFT or negedge Reset_n)
    76. if(!Reset_n)
    77. ram_rdaddr <= 0;
    78. else if(ram_data_en)
    79. ram_rdaddr <= ram_rdaddr + 1'd1;
    80. wire [11:0]h_count,v_count;
    81. wire [15:0]dis_data;
    82. assign ram_data_en = Data_req && (h_count >= 272 && h_count < 528) && (v_count >= 112 && v_count < 368);
    83. assign dis_data = ram_data_en ? ram_rdata: 0;
    84. TFT_Ctrl TFT_Ctrl(
    85. .Clk_33M (clk_TFT) ,
    86. .Reset_n (Reset_n) ,
    87. .Data_in (dis_data) ,
    88. .Data_req(Data_req) ,
    89. .hcount (h_count) , //行扫描位置(显示图像行扫描地址)
    90. .vcount (v_count) , //场扫描位置(显示图像场扫描地址)
    91. .TFT_HS (TFT_HS ) , //行同步信号
    92. .TFT_VS (TFT_VS ) , //场同步信号
    93. .TFT_DE (TFT_DE ) , //有效数据输出
    94. .TFT_CLK (TFT_CLK ) ,
    95. .TFT_DATA(TFT_RGB) , //红绿蓝三色 分别8位量化 R[7:0]G[7:0]B[7:0]
    96. .TFT_BL (TFT_BL )
    97. );
    98. endmodule

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

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

    测试代码如下:

    1. `timescale 1ns / 1ps
    2. module UART_RAM_TFT_TB();
    3. reg Clk;
    4. reg Reset_n;
    5. reg uart_rx;
    6. wire [15:0]TFT_RGB;
    7. wire TFT_HS;
    8. wire TFT_VS;
    9. wire TFT_DE;
    10. wire TFT_CLK;
    11. wire TFT_BL;
    12. wire led;
    13. UART_RAM_TFT UART_RAM_TFT(
    14. .Clk (Clk ) ,
    15. .Reset_n(Reset_n) ,
    16. .uart_rx(uart_rx) ,
    17. .TFT_RGB(TFT_RGB) , //TFT数据输出
    18. .TFT_HS (TFT_HS ) , // TFT行同步信号
    19. .TFT_VS (TFT_VS ) , //TFT场同步信号
    20. .TFT_DE (TFT_DE ) , //TFT数据有效信号
    21. .TFT_CLK(TFT_CLK) ,
    22. .TFT_BL (TFT_BL ) , //TFT背光
    23. .led (led )
    24. );
    25. initial Clk = 1;
    26. always #10 Clk = ~Clk;
    27. initial begin
    28. Reset_n = 0;
    29. #201;
    30. Reset_n = 1;
    31. #2000;
    32. #20000000;
    33. $stop;
    34. end
    35. endmodule

    仿真波形如下:

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

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

    7,总结

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

  • 相关阅读:
    基于SpringBoot的智能物流管理系统
    【Git企业开发】第七节.多人协作开发
    华为机试真题实战应用【赛题代码篇】-整数最小和(附Python和Java代码)
    前端面试怎么总问watch和computed区别
    dubbo:两种方式安装dubbo-admin、zookeeper
    【开发心得】Jaxb使用珠玑
    随想006:帮忙的时机
    前端面试题之HTML篇
    es6新增-async函数(异步编程的最终解决方案)
    vue3+ts打开echarts的正确方式
  • 原文地址:https://blog.csdn.net/weixin_46897065/article/details/136413405