当前位置:   article > 正文

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

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存储器,那这个时候就可以用串口传输一整幅图像,就可以将完整图片显示在整个显示屏上去。再其次把串口接收到的数据改为摄像头采集到的实时的数据流,那就可以做一个摄像采集头图像,存储,实时显示的应用。再者,对采集到的图像数据。进行一定的各种滤波算法,检测算法等等,就可以实现图像处理功能。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/326498
推荐阅读
相关标签
  

闽ICP备14008679号