[X]关闭

[米联派-安路飞龙DR1-FPSOC] FPGA基础篇连载-24 基于FPGA简易示波器显示驱动设计

文档创建者:FPGA课程
浏览次数:122
最后更新:2024-09-21
文档课程分类-安路-DR1
安路-DR1: FPSOC-DR1-FPGA部分 » 2_FPGA实验篇(仅旗舰) » 1-FPGA基础入门实验
软件版本:Anlogic -TD5.9.1-DR1_ES1.1
操作系统:WIN10 64bit
硬件平台:适用安路(Anlogic)FPGA
实验平台:米联客-MLKPAI-SF01-DR1M90M开发板
板卡获取平台:https://milianke.tmall.com/

1概述
FPGA在数据采集,数据处理,图像视频领域都有广泛的应用。很多FPGA工程师苦恼,如何让FPGA采集的数据可以直观显示。如果上一个LINUX再弄一个QT写示波器软件,工作又太复杂了。基于此,设计一款基于FPGA的示波器对实时采集的波形数据在显示器上直观显示,具有非常好实用价值。
基于此目的,米联客在本方案中,完成了基于FPGA的一款简易示波器显示驱动设计,由于时间和精力问题,这款基于FPGA的示波器驱动目前还只能以描点的方式进行显示。并且没有复杂的功能,只能简单的展示采集的数据。但是这个不妨碍我们后期对该示波器IP进一步升级完善。万事开头难,让我们开始把。
4系统框图
4a55c93b59734312b8da8d928ef3396d.jpg
首先,从顶层框架上进行设计,对于波形显示部分,我们只要给出wave_clk,wave_de,wave_data三个信号,既可以完成波形显示。
其次,简易示波器的驱动部分需要完成包括背景绘制、栅格绘制、曲线绘制(绘制坐标点方式)
此外,我们需要考虑尽量少的FPGA资源实现这个示波器。比如用BRAM保存有效的数据点,然后通过数据比对的方式,只对有效数据点输出到显示器上。
最后,我们还要设计一个简单的乒乓存储,可以一边存储数据,一边绘制波形
2波形绘制
关于HDMI输出IP的部分这里不再介绍,VTC时序设计部分这里也不详细介绍。如果读者这些基础知识不清楚的,请阅读前面的实验。
2.1波形绘制显示原理
通过前面关于VTC IP视频时序驱动的学习,以及TPG IP测试图形显示的学习,我们知道,对于显示器上的图像,是从液晶屏的做上角,一个像素点一个像素点绘制,当一行所有绘制完成,进行下一行的绘制。利用肉眼的视觉暂留原理,一般1秒显示25帧以上,我们就能看到视频是动态的。
本方案中,我们绘制的波形曲线只需要显示波形的数据点,比如对于1920*1080的显示区域,我们时间上只要绘制1920点波形点,即可。
为了方便我们理解,我们定义HS方向是X坐标,VS方向是Y坐标。
比如我们这里设计的是显示1024个波形数据点,在绘制每一行的图像的时候,比对每一个数据和VS的Y坐标是否相等,如果相等就绘制这个波形点。这样我们就能完成1024个波形点在整个屏幕的显示。
6c0c32f167be4b3f9d247fcfbc38a637.jpg
2.2画中画的vtc视频时序模块设计
b729af4d927f40c68106209d036fe205.jpg
我们这里显示的波形数据点是1024,高度是256,因此我们需要实现一个画中画的功能。栅格绘制,以及波形数据点会以画中画的有效区域进行显示。
支持画中画的uivtc.v源码
  1. `timescale 1ns / 1ns //仿真时间刻度/精度

  2. module uivtc#
  3. (
  4. parameter H_ActiveSize   =   1980,               //视频时间参数,行视频信号,一行有效(需要显示的部分)像素所占的时钟数,一个时钟对应一个有效像素
  5. parameter H_FrameSize    =   1920+88+44+148,     //视频时间参数,行视频信号,一行视频信号总计占用的时钟数
  6. parameter H_SyncStart    =   1920+88,            //视频时间参数,行同步开始,即多少时钟数后开始产生行同步信号
  7. parameter H_SyncEnd      =   1920+88+44,         //视频时间参数,行同步结束,即多少时钟数后停止产生行同步信号,之后就是行有效数据部分

  8. parameter V_ActiveSize   =   1080,               //视频时间参数,场视频信号,一帧图像所占用的有效(需要显示的部分)行数量,通常说的视频分辨率即H_ActiveSize*V_ActiveSize
  9. parameter V_FrameSize    =   1080+4+5+36,        //视频时间参数,场视频信号,一帧视频信号总计占用的行数量
  10. parameter V_SyncStart    =   1080+4,             //视频时间参数,场同步开始,即多少行数后开始产生场同步信号
  11. parameter V_SyncEnd      =   1080+4+5,           //视频时间参数,场同步结束,即多少场数后停止产生场同步信号,之后就是场有效数据部分

  12. parameter H2_ActiveSize  =   640,
  13. parameter V2_ActiveSize  =   480
  14. )
  15. (
  16. input           I_vtc_rstn,//系统复位
  17. input           I_vtc_clk, //系统时钟
  18. output  reg     O_vtc_vs,  //场同步输出
  19. output  reg     O_vtc_hs,  //行同步输出
  20. output  reg     O_vtc_de,  //视频数据有效  
  21. input  [11:0]   I_vtc2_offset_x,//相对屏幕原点(左上角)X方向偏移
  22. input  [11:0]   I_vtc2_offset_y,//相对屏幕原点(左上角)Y方向偏移
  23. output  reg     O_vtc2_de     //绘制有效的显示区域
  24. );

  25. reg [11:0] hcnt = 12'd0;    //行像素计数器,寄存器
  26. reg [11:0] vcnt = 12'd0;    //场像素计数器,寄存器   
  27. reg [2 :0] rst_cnt = 3'd0;  //复位计数器,寄存器
  28. wire rst_sync = rst_cnt[2]; //同步复位

  29. always @(posedge I_vtc_clk or negedge I_vtc_rstn)begin //通过计数器产生同步复位
  30.     if(I_vtc_rstn == 1'b0)
  31.         rst_cnt <= 3'd0;
  32.     else if(rst_cnt[2] == 1'b0)
  33.         rst_cnt <= rst_cnt + 1'b1;
  34. end   

  35. //行像素计数器
  36. always @(posedge I_vtc_clk)begin
  37.     if(rst_sync == 1'b0) //复位
  38.         hcnt <= 12'd0;
  39.     else if(hcnt != (H_FrameSize - 1'b1))//计数范围从0 ~ H_FrameSize-1
  40.         hcnt <= hcnt + 1'b1;
  41.     else
  42.         hcnt <= 12'd0;
  43. end         

  44. //场计数器,用于计数已经完成的行视频信号
  45. always @(posedge I_vtc_clk)begin
  46.     if(rst_sync == 1'b0)
  47.         vcnt <= 12'd0;
  48.     else if(hcnt == (H_ActiveSize  - 1'b1)) begin//是否一行像素结束
  49.            vcnt <= (vcnt == (V_FrameSize - 1'b1)) ? 12'd0 : vcnt + 1'b1;//每一行计数,场计数器加1,计数范围0~V_FrameSize - 1
  50.     end
  51. end

  52. wire hs_valid  =  hcnt < H_ActiveSize; //行信号有效像素部分
  53. wire vs_valid  =  vcnt < V_ActiveSize; //场信号有效像素部分
  54. wire vtc_hs   =  (hcnt >= H_SyncStart && hcnt < H_SyncEnd);//产生hs,行同步信号
  55. wire vtc_vs    = (vcnt > V_SyncStart && vcnt <= V_SyncEnd);//产生vs,场同步信号      
  56. wire vtc_de   =  hs_valid && vs_valid;//只有当行像素有效和场像素同时有效,视频数据部分才是有效

  57. //画中画,波形绘制区域
  58. wire hs2_valid  =  (hcnt>=I_vtc2_offset_x)&& (hcnt<(I_vtc2_offset_x+H2_ActiveSize)); //画中画,波形绘制区域HS有效信号
  59. wire vs2_valid  =  (vcnt>=I_vtc2_offset_y)&& (vcnt<(I_vtc2_offset_y+V2_ActiveSize)); //画中画,波形绘制区域VS有效信号
  60. wire vtc2_de    =  hs2_valid && vs2_valid; //画中画,数据有效绘制信号

  61. //完一次寄存打拍输出,有利于改善时序,尤其对于高分辨率,高速的信号,打拍可以改善内部时序,以运行于更高速度
  62. always @(posedge I_vtc_clk)begin
  63.     if(rst_sync == 1'b0)begin
  64.         O_vtc_vs <= 1'b0;
  65.         O_vtc_hs <= 1'b0;
  66.         O_vtc_de <= 1'b0;
  67.         O_vtc2_de <= 1'b0;
  68.     end
  69.     else begin
  70.         O_vtc_vs <= vtc_vs; //场同步信号打拍输出
  71.         O_vtc_hs <= vtc_hs; //行同步信号打拍输出
  72.         O_vtc_de <= vtc_de; //视频有效信号打拍输出
  73.         O_vtc2_de <= vtc2_de; //画中画,数据有效绘制信号
  74.     end
  75. end

  76. endmodule
复制代码
2.3 uiwave.v源码
uiwave.v
  1. /*************uiwave简易波形绘制驱动******************************
  2. --版本号1.0
  3. --1.代码简洁,占用极少逻辑资源,代码结构清晰,逻辑设计严谨
  4. --2.使用方便,只需要输入ADC的值,就能完成波形绘制
  5. --3.占用资源少,波形输入8bits ADC值,存储到BLOCK RAM 只需要1048*8bit 大小的BRAM,即可完成1通道的波形存储
  6. --4.乒乓绘制,当绘制一个波形的时候,另外个波形存储到另外一段地址空间
  7. --5.绘制过程中,每一行数据都读出和Y坐标匹配,如果匹配成功,使能O_pixel_en绘制这个数据点
  8. --6.背景绘制黑色
  9. --7.栅格绘制白色
  10. --8.支持多通道绘制
  11. *********************************************************************/

  12. `timescale 1ns / 1ns
  13. module uiwave
  14. (
  15. //波形1
  16. input         I_wave1_clk,      //波形1时钟
  17. input  [7 :0] I_wave1_data,     //波形1数据
  18. input         I_wave1_data_de,  //波形1数据有效

  19. //波形2
  20. input         I_wave2_clk,      //波形2时钟
  21. input  [7 :0] I_wave2_data,     //波形2数据
  22. input         I_wave2_data_de,  //波形2数据有效

  23. //VTC时序输入
  24. input         I_vtc_rstn,       //时序复位输入
  25. input         I_vtc_clk,        //时序时钟输入
  26. input         I_vtc_vs,         //VS-帧同步,信号同步输入
  27. input         I_vtc_de,         //de有效区域,信号同步输入

  28. //同步时序输出,以及像素输出
  29. output        O_vtc_vs,         //帧同步输出
  30. output        O_vtc_de,         //de信号同步后输出
  31. output reg [23:0] O_vtc_rgb     //同步输出显示颜色
  32. );

  33. reg  [1 :0] vtc_vs_r; //vs寄存器
  34. reg  [1 :0] vtc_de_r; //de寄存器
  35. reg  [11 :0] vcnt,hcnt;//vcnt计数有多少行,hcnt计数有多少列

  36. reg    grid_de; //栅格绘制使能

  37. assign O_vtc_vs = vtc_vs_r[0]; //同步后输出O_vtc_vs
  38. assign O_vtc_de = vtc_de_r[0]; //同步后输出O_vtc_de

  39. //寄存,同步
  40. always @(posedge I_vtc_clk)begin
  41.     vtc_vs_r <= {vtc_vs_r[0],I_vtc_vs};
  42.     vtc_de_r <= {vtc_de_r[0],I_vtc_de};
  43. end

  44. //以下hcnt用于计数列,vcnt用于计数行数

  45. //hcnt像素计数器
  46. always @(posedge I_vtc_clk)begin
  47.     if(hcnt == 1023)
  48.         hcnt <= 12'd0;
  49.     else if(vtc_de_r[0] && (hcnt != 1023)) //hcnt计数列,共计1024个像素
  50.         hcnt <= hcnt + 1'b1;
  51. end

  52. //vcnt计数有多少行
  53. always @(posedge I_vtc_clk)begin
  54.     if(vtc_vs_r == 2'b01)
  55.         vcnt <= 8'd0;
  56.     else if((vtc_de_r == 2'b10) && (vcnt != 255)) //以de信号用于计数行,共计256行
  57.         vcnt <= vcnt + 1'b1;
  58. end

  59. //栅格绘制
  60. always @(posedge I_vtc_clk)begin
  61.     if((hcnt[2:0]==7&&(vcnt[5:0]==63||vcnt == 0))||((hcnt[5:0]==63||hcnt==0)&&vcnt[2:0]==7)||(vcnt == 0 && hcnt==0))
  62.         grid_de <= O_vtc_de;
  63.     else
  64.         grid_de <= 1'b0;
  65. end

  66. //1--绘制波形曲线1,绿色点
  67. //2--绘制波形曲线2,黄色点
  68. //3--绘制栅格虚线,白色点
  69. //4--绘制背景色,黑色
  70. always @(posedge I_vtc_clk)begin
  71.     casex({grid_de,wave2_pixel_en,wave1_pixel_en})
  72.             3'bxx1:
  73.                O_vtc_rgb <= {8'h00,8'hff,8'h00};   //wave1信号显示像素颜色
  74.             3'bx10:
  75.                O_vtc_rgb <= {8'hff,8'hff,8'h00};   //wave2信号显示像素颜色
  76.             3'b100:
  77.                O_vtc_rgb <= {8'h96,8'h96,8'h96};   //网格显示像素为白色点
  78.         default:
  79.                O_vtc_rgb <= {8'h00,8'h00,8'h00};   //黑色背景
  80.     endcase
  81. end

  82. //波形缓存1,以及波形绘制像素点输出使能
  83. uiwave_buf uiwave1_buf_inst
  84. (
  85. .I_wave_clk(I_wave1_clk),  //写数据输入时钟,和ADC采集时钟同步
  86. .I_wave_data(I_wave1_data),//写数据
  87. .I_wave_data_de(I_wave1_data_de),//写数据有效
  88. .I_vtc_clk(I_vtc_clk),    //VTC时序发生器时钟输入
  89. .I_vtc_rstn(I_vtc_rstn),  //VTC时序发生器复位
  90. .I_vtc_de_r(vtc_de_r[0]), //VTC时序发生器的de有效区域输入
  91. .I_vtc_vs(I_vtc_vs),      //VTC时序发生器的VS同步信号输入
  92. .I_vtc_vcnt(vcnt),        //vtc的数据偏移,主要对有符号数据进行调整
  93. .O_pixel_en(wave1_pixel_en) //输出输出使能
  94. );

  95. //波形缓存2,以及波形绘制像素点输出使能
  96. uiwave_buf uiwave2_buf_inst
  97. (
  98. .I_wave_clk(I_wave2_clk),   //写数据输入时钟,和ADC采集时钟同步
  99. .I_wave_data(I_wave2_data), //写数据
  100. .I_wave_data_de(I_wave2_data_de),//写数据有效
  101. .I_vtc_clk(I_vtc_clk),           //VTC时序发生器时钟输入
  102. .I_vtc_rstn(I_vtc_rstn),         //VTC时序发生器复位
  103. .I_vtc_de_r(vtc_de_r[0]),        //VTC时序发生器的de有效区域输入
  104. .I_vtc_vs(I_vtc_vs),             //VTC时序发生器的VS同步信号输入
  105. .I_vtc_vcnt(vcnt),               //vtc的数据偏移,主要对有符号数据进行调整
  106. .O_pixel_en(wave2_pixel_en)      //输出输出使能
  107. );

  108. endmodule

  109. uiwave_buf.v源码中有以下代码
  110. assign   O_pixel_en  = I_vtc_de_r&(I_vtc_vcnt[7:0] == wave_data[7:0]);
  111. pixel_en_o信号就是wave1_pixel_en和wave2_pixel_en信号,在上级对wave1_pixel_en跟wave2_pixel_en
  112. 有效的像素赋值,输出波形点颜色,grid_de有效的区域显示栅格点,其他区域显示黑色背景。
  113. Uiwave.v绘制背景、栅格、波形点的代码如下:
  114. //1--绘制波形曲线1,绿色点
  115. //2--绘制波形曲线2,黄色点
  116. //3--绘制栅格虚线,白色点
  117. //4--绘制背景色,黑色
  118. always @(posedge I_vtc_clk)begin
  119.     casex({grid_de,wave2_pixel_en,wave1_pixel_en})
  120.             3'bxx1:
  121.                O_vtc_rgb <= {8'h00,8'hff,8'h00};   //wave1信号显示像素颜色
  122.             3'bx10:
  123.                O_vtc_rgb <= {8'hff,8'hff,8'h00};   //wave2信号显示像素颜色
  124.             3'b100:
  125.                O_vtc_rgb <= {8'h96,8'h96,8'h96};   //网格显示像素为白色点
  126.         default:
  127.                O_vtc_rgb <= {8'h00,8'h00,8'h00};   //黑色背景
  128.     endcase
  129. end
复制代码
uiwave_buf.v源码中有以下代码
  1. assign   O_pixel_en  = I_vtc_de_r&(I_vtc_vcnt[7:0] == wave_data[7:0]);
复制代码
pixel_en_o信号就是wave1_pixel_en和wave2_pixel_en信号,在上级对wave1_pixel_en跟wave2_pixel_en
有效的像素赋值,输出波形点颜色,grid_de有效的区域显示栅格点,其他区域显示黑色背景。
Uiwave.v绘制背景、栅格、波形点的代码如下:
  1. //1--绘制波形曲线1,绿色点
  2. //2--绘制波形曲线2,黄色点
  3. //3--绘制栅格虚线,白色点
  4. //4--绘制背景色,黑色
  5. always @(posedge I_vtc_clk)begin
  6.     casex({grid_de,wave2_pixel_en,wave1_pixel_en})
  7.             3'bxx1:
  8.                O_vtc_rgb <= {8'h00,8'hff,8'h00};   //wave1信号显示像素颜色
  9.             3'bx10:
  10.                O_vtc_rgb <= {8'hff,8'hff,8'h00};   //wave2信号显示像素颜色
  11.             3'b100:
  12.                O_vtc_rgb <= {8'h96,8'h96,8'h96};   //网格显示像素为白色点
  13.         default:
  14.                O_vtc_rgb <= {8'h00,8'h00,8'h00};   //黑色背景
  15.     endcase
  16. end
复制代码
2.4 uiwave_buf.v通过BRAM缓存ADC数据
通过控制buf_flag信号,每次读写的地址进行切换,确保不会对同一个地址同时进行读写,造成数据冲突。
  1. `timescale 1ns / 1ns //仿真时间刻度/精度

  2. module uiwave_buf
  3. (
  4. input         I_wave_clk,    //写数据输入时钟,和ADC采集时钟同步
  5. input  [7 :0] I_wave_data,   //写数据
  6. input         I_wave_data_de,//写数据有效
  7. input         I_vtc_clk,     //VTC时序发生器时钟输入
  8. input         I_vtc_rstn,    //VTC时序发生器复位
  9. input         I_vtc_vs,      //VTC时序发生器的VS同步信号输入
  10. input         I_vtc_de_r,    //VTC时序发生器的de有效区域输入
  11. input  [7 :0] I_vtc_vcnt,    //vtc的数据偏移,主要对有符号数据进行调整
  12. output        O_pixel_en     //输出输出使能
  13. );

  14. //BRAM 简单双口BRAM
  15. reg  [9 :0] addra = 0;  //BRAM 通道A地址     
  16. //reg         ena   = 0;  //BRAM 通道A使能
  17. reg         wea   = 0;  //BRAM 通道A写使能
  18. reg  [9 :0] addrb = 0;  //BRAM 通道B地址
  19. reg         enb   = 0;  //BRAM 通道B读使能
  20. reg  [0 :0] WR_S,RD_S;  //写状态机,读状态机
  21. reg         buf_flag;//buf_flag用于乒乓地址缓存切换
  22. reg         addr0_en;//用于设置写第一个数据相对地址0

  23. wire [7 :0] wave_data;//写波形数据到BRAM
  24. reg  [3 :0] async_vtc_vs =0; //同步信号

  25. always @(posedge I_wave_clk)begin //对异步I_vtc_vs采样
  26.     async_vtc_vs <= {async_vtc_vs[2:0],I_vtc_vs};
  27. end

  28. //绘制波形数据点使能,绘制原理:
  29. //当匹配到存储的ADC数据和正在扫描的Y坐标值一致就输出,每个X坐标方向绘制1个波形点
  30. assign   O_pixel_en  = I_vtc_de_r&(I_vtc_vcnt[7:0] == wave_data[7:0]);

  31. //写BRAM 状态机
  32. always @(posedge I_wave_clk or negedge I_vtc_rstn)begin
  33.     if(I_vtc_rstn == 1'b0)begin //复位重置所有寄存器
  34.        addra      <= 10'd0;
  35.        addr0_en   <= 1'b1;
  36.        wea        <= 1'b0;
  37.        buf_flag   <= 1'b0;
  38.        WR_S       <= 1'd0;
  39.     end
  40.     else begin
  41.         case(WR_S) //写状态机
  42.         0:begin
  43.               if(I_wave_data_de)begin //有效波形数据点
  44.                if(addra == 1023)begin //1024个数据写完
  45.                  wea      <= 1'b0; //停止写
  46.                  addra    <= 0;    //相对地址设置0
  47.                  addr0_en <= 1'b1;
  48.                  WR_S     <= 1'd1;//进入状态机1
  49.                end
  50.                else begin //写入1024个数据
  51.                  wea      <= 1'b1; //写使能
  52.                  addr0_en <= 1'b0;
  53.                  addra    <= (addr0_en == 1'b0) ? (addra + 1'b1) : 0;//相对地址递增
  54.                end
  55.             end
  56.             else begin
  57.               wea <= 1'b0;
  58.             end
  59.         end
  60.         1:begin //等待VTC时序同步
  61.             if(async_vtc_vs[3:2] == 2'b10)begin//当数据同步后,准备下一次写
  62.                WR_S     <= 1'd0; //回到状态0
  63.                buf_flag <= ~buf_flag;//乒乓地址切换
  64.             end
  65.         end
  66.         default:WR_S   <= 2'd0;
  67.         endcase
  68.      end
  69. end

  70. //读BRAM 状态机
  71. always @(posedge I_vtc_clk or negedge I_vtc_rstn)begin
  72.     if(I_vtc_rstn == 1'b0)begin//复位重置所有寄存器
  73.        addrb   <= 10'd0;
  74.        RD_S    <= 1'd0;
  75.     end
  76.     else begin
  77.         case(RD_S)
  78.         0:begin
  79.             if(I_vtc_de_r)begin //I_vtc_de_r代表了有效绘制区域
  80.                if(addrb == 1023)begin //1024个数据读完
  81.                  addrb <= 0;    //相对地址设置0
  82.                  RD_S  <= 1'd1; //进入状态1
  83.                end
  84.                else //没一样都会扫描所有的ADC数据
  85.                  addrb   <= addrb + 1'b1;//相对地址递增
  86.             end
  87.         end
  88.         1:begin
  89.             if(I_vtc_de_r == 0) //等待de变为0
  90.                 RD_S <= 0; //回到状态0重新扫描
  91.                
  92.         end
  93.         default:RD_S   <= 1'd0;
  94.         endcase
  95.      end
  96. end   

  97. wave_ram buf_inst(
  98. .dina(I_wave_data), //写入波形数据
  99. .addra({buf_flag,addra}), //写地址,其中addra是相对地址,buf_flag是地址高位,用于读写的乒乓切换
  100. .wea(wea), //写使能
  101. .clka(I_wave_clk),//写时钟
  102. .doutb(wave_data), //读出的波形数据
  103. .addrb({~buf_flag,addrb}), //写地址,其中addrb是相对地址,buf_flag是地址高位,用于读写的乒乓切换
  104. .clkb(I_vtc_clk)//读时钟
  105. );
  106. endmodule
复制代码
2.5 BRAM IP设置
关于更多BRAM的IP介绍请阅读前面关于BRAM的实验
45b118a769fd4190a19d5f91b7edb0b8.jpg
数据输入8bits,深度2048,这样设计可以使用最少的BRAM完成2段曲线的存储。所以如果是ADC数据,输入高8bit即可。
77dfe634284e4befa84640491b265fd2.jpg
b9a5a0ad8dfc48278ef3a1dcd7475eb8.jpg
3测试数据产生
为了验证波形显示驱动,我们编写了一个简单的三角波程序
  1. `timescale 1ns / 1ns//仿真时间刻度/精度

  2. module wave_test
  3. (
  4. input           I_sysclk,         //系统时钟输入   
  5. output          O_hdmi_clk_p,       //HDMI时钟输出 P端
  6. output          O_hdmi_clk_n,       //HDMI时钟输出 N端
  7. output [2:0]    O_hdmi_tx_p,        //HDMI数据输出 P端
  8. output [2:0]    O_hdmi_tx_n         //HDMI数据输出 N端
  9. );

  10. localparam SYSCLKHZ     =  50_000_000; //定义系统时钟100MHZ
  11. localparam T500MS_CNT   = (SYSCLKHZ-1);

  12. //上电延迟复位
  13. reg [7:0]    rst_cnt=0; //复位计数器
  14. wire  rstn = rst_cnt[7];//用高位复位
  15. wire pclkx1,pclkx5,clk100M,locked; //MMCM/PLL时钟信号   

  16. wire vtc_rstn,vtc_clk,vtc_vs,vtc_hs,vtc_de,vtc2_grid_de_o,vtc2_de_o;
  17. wire [23:0] rgb_o; //RGB颜色寄存器

  18. assign vtc_clk  = pclkx1;//像素时钟
  19. assign vtc_rstn = locked;//
  20. assign S_rst = ~locked;

  21. //PLL时钟管理IP 输出 pclkx1和pclkx5以及locked信号
  22. pll U_pll(
  23. .refclk   ( I_sysclk    ),//系统时钟输入
  24. .reset    ( !rst_cnt[7] ),
  25. .lock     ( locked      ),//PLL LOCKED
  26. .clk0_out ( pclkx1      ),//像素时钟
  27. .clk1_out ( pclkx5      ),//HDMI IO的serdes 时钟 5倍的像素时钟
  28. .clk2_out ( clk100M     )//100M时钟,给ADC采集用
  29. );

  30. always @(posedge I_sysclk)begin
  31.     if (rst_cnt[7])
  32.         rst_cnt <=  rst_cnt;
  33.     else
  34.         rst_cnt <= rst_cnt+1'b1;
  35. end

  36. //hdmi 输出IP
  37. hdmi_tx#(
  38. //HDMI视频参数设置      
  39. .H_ActiveSize       (1280), //视频时间参数,行视频信号,一行有效(需要显示的部分)像素所占的时钟数,一个时钟对应一个有效像素
  40. .H_SyncStart        (1280+88), //视频时间参数,行同步开始,即多少时钟数后开始产生行同步信号
  41. .H_SyncEnd          (1280+88+44),//视频时间参数,行同步结束,即多少时钟数后停止产生行同步信号,之后就是行有效数据部分
  42. .H_FrameSize        (1280+88+44+239), //视频时间参数,行视频信号,一行视频信号总计占用的时钟数

  43. .V_ActiveSize       (720),//视频时间参数,场视频信号,一帧图像所占用的有效(需要显示的部分)行数量,通常说的视频分辨率即H_ActiveSize*V_ActiveSize
  44. .V_SyncStart        (720+4),//视频时间参数,场同步开始,即多少行数后开始产生场同步信号
  45. .V_SyncEnd          (720+4+5), //视频时间参数,场同步结束,多少行后停止产生长同步信号  
  46. .V_FrameSize        (720+4+5+28),  //视频时间参数,场视频信号,一帧视频信号总计占用的行数量               
  47.      
  48. .VIDEO_VIC          ( 16       ),
  49. .VIDEO_TPG          ( "Disable"),//设置disable,用户数据驱动HDMI接口,否则设置eable产生内部测试图形
  50. .VIDEO_FORMAT       ( "RGB444" )//设置输入数据格式为RGB格式
  51. )u_hdmi_tx
  52. (
  53. .I_pixel_clk        ( pclkx1           ),//像素时钟
  54. .I_serial_clk       ( pclkx5           ),//串行发送时钟
  55. .I_rst              ( S_rst              ),//异步复位信号,高电平有效

  56. //.I_video_in_user    ( vtc_user         ),//视频输入帧起始信号
  57. //.I_video_in_valid   ( vtc_de_valid     ),//视频输入有效信号
  58. //.I_video_in_last    ( vtc_last         ),//视频输入行结束信号
  59. //.I_video_in_data    ( video_hdmi_data  ),//视频输入数据

  60. .I_video_rgb_enable (1'b1               ),//是否使能RGB输入接口,设置1使能,否则采用stream video时序接口  
  61. .I_video_in_vs      (vtc_vs            ),//RGB 输入VS 帧同步
  62. .I_video_in_de      (vtc_de       ),//RGB 输入de有效
  63. .I_video_in_data    (rgb_o), //视频输入数据     

  64. .O_hdmi_clk_p       ( O_hdmi_clk_p       ),//HDMI时钟通道
  65. .O_hdmi_tx_p        ( O_hdmi_tx_p        )//HDMI数据通道
  66. );

  67. //此VTC IP 用于产生绘制波形的有效区域,波形绘制区域大小未1024*600
  68. uivtc#
  69. (
  70. .H_ActiveSize(1280),          //视频时间参数,行视频信号,一行有效(需要显示的部分)像素所占的时钟数,一个时钟对应一个有效像素
  71. .H_FrameSize(1280+88+44+239), //视频时间参数,行视频信号,一行视频信号总计占用的时钟数
  72. .H_SyncStart(1280+88),        //视频时间参数,行同步开始,即多少时钟数后开始产生行同步信号
  73. .H_SyncEnd(1280+88+44),       //视频时间参数,行同步结束,即多少时钟数后停止产生行同步信号,之后就是行有效数据部分
  74. .V_ActiveSize(720),           //视频时间参数,场视频信号,一帧图像所占用的有效(需要显示的部分)行数量,通常说的视频分辨率即H_ActiveSize*V_ActiveSize
  75. .V_FrameSize(720+4+5+28),     //视频时间参数,场视频信号,一帧视频信号总计占用的行数量
  76. .V_SyncStart(720+4),          //视频时间参数,场同步开始,即多少行数后开始产生场同步信号
  77. .V_SyncEnd (720+4+5),         //视频时间参数,场同步结束,即多少场数后停止产生场同步信号,之后就是场有效数据部分
  78. .H2_ActiveSize(1024),         //波形绘制区域行像素大小        
  79. .V2_ActiveSize(256)           //波形绘制区域场像素大小
  80. )
  81. uivtc_inst
  82. (
  83. .I_vtc_clk(vtc_clk),         //系统时钟
  84. .I_vtc_rstn(vtc_rstn),       //系统复位
  85. .I_vtc2_offset_x(128),         //X坐标相对屏幕的原始坐标的偏移
  86. .I_vtc2_offset_y(200),         //Y坐标相对屏幕的原始坐标的偏移
  87. .O_vtc_vs(vtc_vs),//场同步输出
  88. .O_vtc_hs(vtc_hs),//行同步输出
  89. .O_vtc_de(vtc_de),//视频数据有效
  90. .O_vtc2_de(vtc2_de_o)//绘制波形显示区域的有效区域
  91. );

  92. //测试数据产生,通过test_data产生测试数据,可以用于测试波形显示器的基本功能测试

  93. reg [25:0]  t500ms_cnt   = 26'd0;//500ms计数器

  94. wire t500ms_en = (t500ms_cnt==T500MS_CNT);//500ms 使能信号

  95. //每间隔500ms状态机运行一次
  96. always@(posedge clk100M) begin
  97.     if(locked == 0)
  98.         t500ms_cnt <= 0;
  99.     else if(t500ms_cnt == T500MS_CNT)
  100.         t500ms_cnt <= 0;
  101.     else
  102.         t500ms_cnt <= t500ms_cnt + 1'b1;
  103. end

  104. reg [1:0]WAVE_S;       //写数据状态机
  105. reg [9:0]test_data =0; //测试数据

  106. wire data_en = (WAVE_S == 0|| WAVE_S == 1); //写数据使能

  107. always @(posedge clk100M)begin
  108.     if(locked == 1'b0)begin
  109.         WAVE_S    <= 2'd2;
  110.         test_data <=10'd0;
  111.     end
  112.     else begin
  113.        case(WAVE_S)
  114.             0:begin
  115.               if(test_data == 255)
  116.                 WAVE_S <= 2'd1;
  117.               else
  118.                 test_data <= test_data + 1'b1; //数据递增
  119.             end
  120.             1:begin
  121.               if(test_data == 0)
  122.                 WAVE_S <= 2'd2;
  123.               else
  124.                 test_data <= test_data - 1'b1; //数据递减
  125.             end
  126.             2:
  127.             if(t500ms_en)
  128.                 WAVE_S <= 2'd0;
  129.             else
  130.                 WAVE_S <= WAVE_S;
  131.             default:begin
  132.                 WAVE_S <= 2'd0;
  133.             end
  134.        endcase  
  135.     end
  136. end

  137. //例化波形显示器 IP,默认支持2个通道数据,可以扩展支持更多通道
  138. uiwave uiwave_inst
  139. (
  140. //波形1
  141. .I_wave1_clk(clk100M),//系统时钟输入
  142. .I_wave1_data(test_data[7:0]),//波形测试数据
  143. .I_wave1_data_de(data_en),//数据有效信号

  144. //波形2
  145. .I_wave2_clk(clk100M),//系统时钟输入
  146. .I_wave2_data(~test_data[7:0]),//演示2通道显示,对另外一个波形通道取反
  147. .I_wave2_data_de(data_en),//数据有效信号

  148. .I_vtc_rstn(vtc_rstn),//时序发生复位
  149. .I_vtc_clk (vtc_clk), //像素时钟
  150. .I_vtc_vs  (vtc_vs),  //场同步输出
  151. .I_vtc_de  (vtc2_de_o),//同步,绘制波形显示区域的有效区域
  152. .O_vtc_rgb(rgb_o)//同步RGB数据 绘制数据输出   
  153.    
  154. );

  155. endmodule
复制代码
4测试结果
4.1硬件接线
(该教程为通用型教程,教程中仅展示一款示例开发板的连接方式,具体连接方式以所购买的开发板型号以及结合配套代码管脚约束为准。)
请确保下载器和开发板已经正确连接,并且开发板已经上电(注意JTAG端子不支持热插拔,而USB接口支持,所以在不通电的情况下接通好JTAG后,再插入USB到电脑,之后再上电,以免造成JTAG IO损坏)
bfb74546137040b393ce9a97c9341b0c.jpg
4.2运行结果
94fd588a286541319f270fb4e8d88e4e.jpg

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则