搜索
bottom↓
回复: 0

【正点原子FPGA连载】第二十三章频率计实验

[复制链接]

出0入234汤圆

发表于 2020-11-21 10:34:47 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2021-1-25 18:26 编辑

1)实验平台:正点原子达芬奇FPGA开发板
2)购买链接:https://detail.tmall.com/item.htm?id=624335496505
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz_dafenqi.html
4) 正点原子官方B站:https://space.bilibili.com/394620890
5)对正点原子FPGA感兴趣的同学可以加群讨论:
905624739点击加入: QQ群头像.png

100846rel79a9p4uelap24.jpg

100846f1ce1fg14zbg0va4.png

第二十三章频率计实验



数字频率计是一种基本的测量仪器,被广泛应用于航天、电子、测控等领域。基于传统测频原理的频率计的测量精度将随被测信号频率的下降而降低,在使用中有较大的局限性,而等精度频率计不但具有较高的测量精度,而且在整个频率区域能保持恒定的测试精度。本章我们通过达芬奇开发板搭建等精度频率计,学习等精度频率计的设计思想和实现方案。
本章分为以下几个章节:
2323.1简介
23.2实验任务
23.3硬件设计
23.4程序设计
23.5下载验证


23.1简介
频率测量在电子设计和测量领域中经常用到,因此对频率测量方法的研究在实际工程应用中具有重要意义。常用的频率测量方法有两种:周期测量法和频率测量法。周期测量法是先测量出被测信号的周期T,然后根据频率求出被测信号的频率。频率测量法是在时间t内对被测信号的脉冲数N进行计数,然后求出单位时间内的脉冲数,即为被测信号的频率。但是上述两种方法都会产生±1个基准时钟误差或被测时钟的误差,在实际应用中有一定的局限性。根据测量原理,很容易发现周期测量法适合于低频信号测量,频率测量法适合于高频信号测量,但二者都不能兼顾高低频率同样精度的测量要求。
等精度测量的一个最大特点是测量的实际门控时间不是一个固定值,而是一个与被测信号有关的值,刚好是被测信号的整数倍。在计数允许时间内,同时对基准时钟和被测信号进行计数,再通过数学公式推导得到被测信号的频率。由于门控信号是被测信号的整数倍,就消除了对被测信号产生的±l周期误差,但是会产生对基准时钟±1周期的误差。等精度测量原理如图 23.1.1所示。
新建DOCX 文档671.png

图 23.1.1 等精度测量原理
从以上叙述的等精度的测量原理可以很容易得出如下结论:首先,被测信号频率clk_fx的相对误差与被测信号的频率无关;其次,增大测量时间段“软件闸门”或提高“标频”clk_fs,可以减小相对误差,提高测量精度;最后,由于一般提供基准时钟clk_fs的石英晶振稳定性很高,所以基准时钟的相对误差很小,可忽略。假设基准时钟的频率为100MHz,只要实际闸门时间大于或等于1s,就可使测量的最大相对误差小于或等于10^(-8),即精度达到1/100MHz。等精度测量的核心思想在于如何保证在实际测量门闸内被测信号为整数个周期,这就需要在设计中让实际测量门闸信号与被测信号建立一定的关系。基于这种思想,设计中以被测信号的上升沿作为开启门闸和关闭门闸的驱动信号,只有在被测信号的上升沿才将图 23.1.1中预置的“软件闸门”的状态锁存,因此在“实际闸门”Tx内被测信号的个数就能保证整数个周期,这样就避免普通测量方法中被测信号的±1的误差,但会产生高频的基准时钟信号的±l周期误差,由于基准时钟频率远高于被测信号,因此它产生的±1周期误差对测量精度的影响十分有限,特别是在中低频测量的时候,相较于传统的频率测量和周期测量方法,可以大大提高测量精度。
等精度测频的原理图如图 23.1.2所示。如图中所示,预置软件闸门信号Gate是由达芬奇开发板的定时模块产生,gate的时间宽度对测频精度的影响较小,故可以在较大的范围内选择,GATE信号经被测时钟clk_fx同步化(图中的D触发器)到被测时钟域下。另外,为了方便处理,这里选择预置闸门信号的长度由参数GATE_TIME设置。图中的fs_cnt和fx_cnt是2个可控的32位高速计数器,fs_cnt_en和fx_cnt_en分别是其计数使能端,由同步化后的gate信号控制,基准时钟信号clk_fs从时钟输入端clk_fs输入,待测信号clk_fx从时钟输入端clk_fx输入。测量时,生成的gate信号,在被测时钟同步化后用来控制启动和关闭2个计数器,2个计数器分别对被测信号和基准时钟计数。若在一次实际闸门时间GATE_TIME中,计数器对被测信号的计数值为fx_cnt,对基准时钟的计数值为fs_cnt,而基准时钟的频率为CLK_FS,则被测信号的频率为clk_fx,则由公式
44444444444444.png

图 23.1.2 是达芬奇开发板实现功能的原理图。
新建DOCX 文档1819.png

图 23.1.2 达芬奇实现的功能的原理图

23.2实验任务
本次的实验任务是板载50MHz的时钟通过内部逻辑进行分频,来产生500KHz频率的信号,作为被测时钟,然后用Verilog HDL编写的等精度测量模块测量被测时钟,并通过LCD显示。
23.3硬件设计
本次实验只需将达芬奇开发板J2扩展口的两个IO使用跳帽或者杜邦线连接即可。本次实验将达芬奇开发板的B15_L23_P引脚做为分频产生的时钟的输出端,B15_L23_N引脚作为被测时钟的输入端,通过一根导线(杜邦线)或者跳帽进行连接。
新建DOCX 文档2105.png

图 23.3.1 硬件原理图

由于端口引脚较多,这里仅给出部分管脚列表,如下表所示:
表 23.3.1 等精度频率计实验管脚分配
信号名        方向        管脚        端口说明        电平标准
sys_clk        input        R4        系统时钟,50M        LVCMOS33
sys_rst_n        input        U2        系统复位,低有效        LVCMOS33
clk_fx        input        K16        被测时钟        LVCMOS33
clk_out        output        L16        输出时钟        LVCMOS33
XDC约束语句如下:
set_property -dict {PACKAGE_PIN R4 IOSTANDARD LVCMOS33} [get_ports sys_clk]
set_property -dict {PACKAGE_PIN U2 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]
set_property -dict {PACKAGE_PIN K16 IOSTANDARD LVCMOS33} [get_ports clk_fx]
set_property -dict {PACKAGE_PIN L16 IOSTANDARD LVCMOS33} [get_ports clk_out]
23.4程序设计
根据实验任务,我们可以大致规划出系统的控制流程:首先我们设计一个测试时钟模块用于生成被测的时钟,然后用等精度频率计模块测量被测时钟的频率,并将测得的时钟频率值送入LCD显示模块进行显示。由此画出系统的功能框图如下所示:
新建DOCX 文档2878.png

图 23.4.1 等精度频率计实验系统框图

由系统框图可知,FPGA部分包括四个模块:顶层模块(top_cymometer)、等精度频率计模块(cymometer)、时钟产生模块(clk_test)以及LCD显示模块(lcd_rgb_char)。各模块功能如下:
顶层模块(top_cymometer):顶层模块完成了对其它三个模块的例化,实现各模块之间的数据交互。时钟产生模块产生被测时钟输出,并从外部接入至等精度频率计模块,以进行频率测量,将测量的结果传输给LCD显示模块进行显示。顶层模块的原理图如下图所示:
新建DOCX 文档3182.png

图 23.4.2 顶层模块原理图

等精度频率计模块(cymometer):等精度频率计模块测量输入的被测时钟的频率。并将测得的频率结果输出。
时钟产生模块(clk_test):时钟产生模块产生被测的时钟。
LCD显示模块(lcd_rgb_char):将等精度频率计测得的时钟频率值在LCD上显示出来。
顶层模块的代码如下:
  1. 1   module top_cymometer(
  2. 2       //system clock
  3. 3       input              sys_clk  ,    // 时钟信号
  4. 4       input              sys_rst_n,    // 复位信号
  5. 5   
  6. 6       //cymometer interface
  7. 7       input              clk_fx   ,    // 被测时钟
  8. 8       output             clk_out  ,    // 输出时钟
  9. 9   
  10. 10      //RGB-LCD接口
  11. 11      output             lcd_hs  ,     //LCD 行同步信号
  12. 12      output             lcd_vs  ,     //LCD 场同步信号
  13. 13      output             lcd_de  ,     //LCD 数据输入使能
  14. 14      inout      [23:0]  lcd_rgb ,     //LCD RGB565颜色数据
  15. 15      output             lcd_bl  ,     //LCD 背光控制信号
  16. 16      output             lcd_clk       //LCD 采样时钟
  17. 17      
  18. 18  );
  19. 19  
  20. 20  //parameter define
  21. 21  parameter    CLK_FS = 26'd50000000;  // 基准时钟频率值
  22. 22  
  23. 23  //wire define
  24. 24  wire    [19:0]       data_fx;        // 被测信号测量值
  25. 25  
  26. 26  //*****************************************************
  27. 27  //**                    main code
  28. 28  //*****************************************************
  29. 29  
  30. 30  //例化等精度频率计模块
  31. 31  cymometer #(.CLK_FS(CLK_FS)          // 基准时钟频率值
  32. 32  ) u_cymometer(
  33. 33      //system clock
  34. 34      .clk_fs      (sys_clk  ),        // 基准时钟信号
  35. 35      .rst_n       (sys_rst_n),        // 复位信号
  36. 36      //cymometer interface
  37. 37      .clk_fx      (clk_fx   ),        // 被测时钟信号
  38. 38      .data_fx     (data_fx  )         // 被测时钟频率输出
  39. 39  );
  40. 40      
  41. 41  //例化测试时钟模块,产生测试时钟
  42. 42  clk_test #(.DIV_N(7'd100)            // 分频系数
  43. 43  ) u_clk_test(
  44. 44      //源时钟
  45. 45      .clk_in      (sys_clk  ),        // 输入时钟
  46. 46      .rst_n       (sys_rst_n),        // 复位信号
  47. 47      //分频后的时钟
  48. 48      .clk_out     (clk_out  )         // 测试时钟
  49. 49  );
  50. 50  
  51. 51  //例化LCD显示模块
  52. 52  lcd_rgb_char  u_lcd_rgb_char
  53. 53  (
  54. 54     .sys_clk    (sys_clk),
  55. 55     .sys_rst_n  (sys_rst_n),
  56. 56     .data       (data_fx),
  57. 57     //RGB-LCD接口
  58. 58     .lcd_hs     (lcd_hs),       //LCD 行同步信号
  59. 59     .lcd_vs     (lcd_vs),       //LCD 场同步信号
  60. 60     .lcd_de     (lcd_de),       //LCD 数据输入使能
  61. 61     .lcd_rgb    (lcd_rgb),      //LCD RGB565颜色数据
  62. 62     .lcd_bl     (lcd_bl),       //LCD 背光控制信号
  63. 63     .lcd_clk    (lcd_clk)       //LCD 采样时钟
  64. 64  );
  65. 65  
  66. 66  endmodule
复制代码

顶层代码主要完成对各模块的例化并实现模块信号间的交互。第21行的基准时钟频率值参数为基准时钟频率值,当用不同的基准时钟时修改此参数即可。
时钟产生模块的代码如下:
  1. 1   module clk_test #(parameter DIV_N = 7'd100)    //分频系数
  2. 2       (
  3. 3        //源时钟
  4. 4        input        clk_in     ,                 // 输入时钟
  5. 5        input        rst_n      ,                 // 复位信号
  6. 6        //分频后的时钟
  7. 7        output  reg  clk_out                      // 输出时钟
  8. 8   );
  9. 9   
  10. 10  //reg define
  11. 11  reg [9:0] cnt;                                 // 时钟分频计数
  12. 12  
  13. 13  //*****************************************************
  14. 14  //**                    main code
  15. 15  //*****************************************************
  16. 16  
  17. 17  //时钟分频,生成500KHz的测试时钟
  18. 18  always @(posedge clk_in or negedge rst_n) begin
  19. 19      if(rst_n == 1'b0) begin
  20. 20          cnt     <= 0;
  21. 21          clk_out <= 0;
  22. 22      end
  23. 23      else begin
  24. 24          if(cnt == DIV_N/2 - 1'b1) begin
  25. 25              cnt     <= 10'd0;
  26. 26              clk_out <= ~clk_out;
  27. 27          end
  28. 28          else
  29. 29              cnt <= cnt + 1'b1;
  30. 30      end
  31. 31  end
  32. 32  
  33. 33  endmodule
复制代码

时钟产生模块通过分频产生被测时钟,这里是用偶数分频方法产生,修改代码第一行的DIV_N分频参数,可得到不同频率的时钟信号,时钟频率为。由于该模块在顶层例化时clk_in为系统时钟50MHz,分频参数为100,产生的时钟频率为50000000/100 = 500000Hz。
等精度频率计模块的代码如下:
  1. 1   module cymometer
  2. 2      #(parameter    CLK_FS = 26'd50_000_000) // 基准时钟频率值
  3. 3       (   //system clock
  4. 4           input                 clk_fs ,     // 基准时钟信号
  5. 5           input                 rst_n  ,     // 复位信号
  6. 6   
  7. 7           //cymometer interface
  8. 8           input                 clk_fx ,     // 被测时钟信号
  9. 9           output   reg [19:0]   data_fx      // 被测时钟频率输出
  10. 10  );
  11. 11  
  12. 12  //parameter define
  13. 13  localparam   MAX       =  6'd32;           // 定义fs_cnt、fx_cnt的最大位宽
  14. 14  localparam   GATE_TIME = 16'd5_000;        // 门控时间设置
  15. 15  
  16. 16  //reg define
  17. 17  reg                gate        ;           // 门控信号
  18. 18  reg                gate_fs     ;           // 同步到基准时钟的门控信号
  19. 19  reg                gate_fs_r   ;           // 用于同步gate信号的寄存器
  20. 20  reg                gate_fs_d0  ;           // 用于采集基准时钟下gate下降沿
  21. 21  reg                gate_fs_d1  ;           //
  22. 22  reg                gate_fx_d0  ;           // 用于采集被测时钟下gate下降沿
  23. 23  reg                gate_fx_d1  ;           //
  24. 24  reg    [   63:0]   data_fx_t    ;          //
  25. 25  reg    [   15:0]   gate_cnt    ;           // 门控计数
  26. 26  reg    [MAX-1:0]   fs_cnt      ;           // 门控时间内基准时钟的计数值
  27. 27  reg    [MAX-1:0]   fs_cnt_temp ;           // fs_cnt 临时值
  28. 28  reg    [MAX-1:0]   fx_cnt      ;           // 门控时间内被测时钟的计数值
  29. 29  reg    [MAX-1:0]   fx_cnt_temp ;           // fx_cnt 临时值
  30. 30  
  31. 31  //wire define
  32. 32  wire               neg_gate_fs;            // 基准时钟下门控信号下降沿
  33. 33  wire               neg_gate_fx;            // 被测时钟下门控信号下降沿
  34. 34  
  35. 35  //*****************************************************
  36. 36  //**                    main code
  37. 37  //*****************************************************
  38. 38  
  39. 39  //边沿检测,捕获信号下降沿
  40. 40  assign neg_gate_fs = gate_fs_d1 & (~gate_fs_d0);
  41. 41  assign neg_gate_fx = gate_fx_d1 & (~gate_fx_d0);
  42. 42  
  43. 43  //门控信号计数器,使用被测时钟计数
  44. 44  always @(posedge clk_fx or negedge rst_n) begin
  45. 45      if(!rst_n)
  46. 46          gate_cnt <= 16'd0;
  47. 47      else if(gate_cnt == GATE_TIME + 5'd20)
  48. 48          gate_cnt <= 16'd0;
  49. 49      else
  50. 50          gate_cnt <= gate_cnt + 1'b1;
  51. 51  end
  52. 52  
  53. 53  //门控信号,拉高时间为GATE_TIME个实测时钟周期
  54. 54  always @(posedge clk_fx or negedge rst_n) begin
  55. 55      if(!rst_n)
  56. 56          gate <= 1'b0;
  57. 57      else if(gate_cnt < 4'd10)
  58. 58          gate <= 1'b0;     
  59. 59      else if(gate_cnt < GATE_TIME + 4'd10)
  60. 60          gate <= 1'b1;
  61. 61      else if(gate_cnt <= GATE_TIME + 5'd20)
  62. 62          gate <= 1'b0;
  63. 63      else
  64. 64          gate <= 1'b0;
  65. 65  end
  66. 66  
  67. 67  //将门控信号同步到基准时钟下
  68. 68  always @(posedge clk_fs or negedge rst_n) begin
  69. 69      if(!rst_n) begin
  70. 70          gate_fs_r <= 1'b0;
  71. 71          gate_fs   <= 1'b0;
  72. 72      end
  73. 73      else begin
  74. 74          gate_fs_r <= gate;
  75. 75          gate_fs   <= gate_fs_r;
  76. 76      end
  77. 77  end
  78. 78  
  79. 79  //打拍采门控信号的下降沿(被测时钟下)
  80. 80  always @(posedge clk_fx or negedge rst_n) begin
  81. 81      if(!rst_n) begin
  82. 82          gate_fx_d0 <= 1'b0;
  83. 83          gate_fx_d1 <= 1'b0;
  84. 84      end
  85. 85      else begin
  86. 86          gate_fx_d0 <= gate;
  87. 87          gate_fx_d1 <= gate_fx_d0;
  88. 88      end
  89. 89  end
  90. 90  
  91. 91  //打拍采门控信号的下降沿(基准时钟下)
  92. 92  always @(posedge clk_fs or negedge rst_n) begin
  93. 93      if(!rst_n) begin
  94. 94          gate_fs_d0 <= 1'b0;
  95. 95          gate_fs_d1 <= 1'b0;
  96. 96      end
  97. 97      else begin
  98. 98          gate_fs_d0 <= gate_fs;
  99. 99          gate_fs_d1 <= gate_fs_d0;
  100. 100     end
  101. 101 end
  102. 102
  103. 103  //门控时间内对被测时钟计数
  104. 104 always @(posedge clk_fx or negedge rst_n) begin
  105. 105     if(!rst_n) begin
  106. 106         fx_cnt_temp <= 32'd0;
  107. 107         fx_cnt <= 32'd0;
  108. 108     end
  109. 109     else if(gate)
  110. 110         fx_cnt_temp <= fx_cnt_temp + 1'b1;
  111. 111     else if(neg_gate_fx) begin
  112. 112         fx_cnt_temp <= 32'd0;
  113. 113         fx_cnt   <= fx_cnt_temp;
  114. 114     end
  115. 115 end
  116. 116
  117. 117 //门控时间内对基准时钟计数
  118. 118 always @(posedge clk_fs or negedge rst_n) begin
  119. 119     if(!rst_n) begin
  120. 120         fs_cnt_temp <= 32'd0;
  121. 121         fs_cnt <= 32'd0;
  122. 122     end
  123. 123     else if(gate_fs)
  124. 124         fs_cnt_temp <= fs_cnt_temp + 1'b1;
  125. 125     else if(neg_gate_fs) begin
  126. 126         fs_cnt_temp <= 32'd0;
  127. 127         fs_cnt <= fs_cnt_temp;
  128. 128     end
  129. 129 end
  130. 130
  131. 131 //计算被测信号频率
  132. 132 always @(posedge clk_fs or negedge rst_n) begin
  133. 133     if(!rst_n) begin
  134. 134         data_fx_t <= 64'd0;
  135. 135     end
  136. 136     else if(gate_fs == 1'b0)
  137. 137         data_fx_t <= CLK_FS * fx_cnt ;
  138. 138 end
  139. 139
  140. 140 always @(posedge clk_fs or negedge rst_n) begin
  141. 141     if(!rst_n) begin
  142. 142         data_fx <= 20'd0;
  143. 143     end
  144. 144     else if(gate_fs == 1'b0)
  145. 145         data_fx <= data_fx_t / fs_cnt ;
  146. 146 end
  147. 147
  148. 148 endmodule
复制代码

在前面的等精度频率计简介中,我们知道在等精度测量中需要一个闸门信号(门控信号),并且该闸门信号需要同步化到被测时钟域下。这里我们为了方便处理,用被测时钟控制闸门信号的产生,这样就避免了同步化处理,当然了,完全可以用基准时钟控制闸门信号的产生,不过这时产生的闸门信号我们需要同步化到被测时钟域下,这样做的目的是为了不让被测时钟计数产生周期的误差。门控时间由参数GATE_TIME设置,此处设为5000,需要说明的是该值越大测得的被测时钟频率值越精确,但测量时间也会相应的变慢一些。另外因为闸门信号是由被测时钟产生的,当测量频率较高的信号或者说信号频率大于10KHz(此值跟门控时间有关)时是不会有什么问题的,但当测量低频信号像Hz级这种,如果门控时间设置的大的话,测量时间就会非常长,此时可修改门控时间的值,为被测时钟频率的5~10倍即可,对于几十KHz及以上的时钟信号,门控时间的大小对测量速度的影响较小,频率越高影响越小,但对测量精度影响较大,因而在测量频率较高的信号时,建议增大门控时间。
代码中为了防止复位对测量造成的干扰,门控信号在复位后延迟了10个被测信号的周期(第55~58行)。另外计算被测信号频率是在基准时钟下的门控信号为低电平时进行。
建立了门控信号之后,我们需要通过门控信号分别使能基准时钟和被测时钟的计数。因为门控信号对基准时钟而言是异步信号,所以这里我们对门控信号进行了两次打拍处理得到基准频率下的门控信号gate_fs(代码第68行的always语句块)。在门控信号的下降沿将计数值寄存并清零计数寄存器。
在取得数值后,我们需要计算被测信号的频率值,由于在计算周期内数值已不再发生变化(计数值已寄存),而且保留了足够的计算时间,所以可以不用FIFO进行异步处理。计算完之后,把所得的结果赋给寄存器变量data_fx,其数值的单位为Hz。
LCD显示部分的代码和“RGB-LCD字符和图片显示”实验的代码基本是一模一样的,唯一不同的地方在于lcd_display模块。
lcd_display模块的代码如下所示:
  1. 1    module lcd_display(
  2. 2        input               lcd_clk,              //lcd驱动时钟
  3. 3        input               sys_rst_n,            //复位信号
  4. 4        
  5. 5       input        [19:0] data ,
  6. 6      
  7. 7        input       [10:0] pixel_xpos,            //像素点横坐标
  8. 8        input       [10:0] pixel_ypos,            //像素点纵坐标
  9. 9        output reg  [23:0] pixel_data             //像素点数据,
  10. 10   );
  11. 11   
  12. 12   //parameter define
  13. 13   localparam CHAR_POS_X  = 11'd1;               //字符区域起始点横坐标
  14. 14   localparam CHAR_POS_Y  = 11'd1;               //字符区域起始点纵坐标
  15. 15   localparam CHAR_WIDTH  = 11'd64;              //字符区域宽度
  16. 16   localparam CHAR_HEIGHT = 11'd16;              //字符区域高度
  17. 17   
  18. 18   localparam WHITE  = 24'b11111111_11111111_11111111;   //背景色,白色
  19. 19   localparam BLACK  = 24'b00000000_00000000_00000000;   //字符颜色,黑色
  20. 20   
  21. 21   //reg define
  22. 22   reg  [127:0]  char  [11:0] ;                  //字符数组
  23. 23   
  24. 24   //wire define
  25. 25   wire   [3:0]              data0    ;          // 十万位数
  26. 26   wire   [3:0]              data1    ;          // 万位数
  27. 27   wire   [3:0]              data2    ;          // 千位数
  28. 28   wire   [3:0]              data3    ;          // 百位数
  29. 29   wire   [3:0]              data4    ;          // 十位数
  30. 30   wire   [3:0]              data5    ;          // 个位数
  31. 31   
  32. 32   //*****************************************************
  33. 33   //**                    main code
  34. 34   //*****************************************************
  35. 35   
  36. 36   assign  data5 = data / 17'd100000;            // 十万位数
  37. 37   assign  data4 = data / 14'd10000 % 4'd10;     // 万位数
  38. 38   assign  data3 = data / 10'd1000 % 4'd10 ;     // 千位数
  39. 39   assign  data2 = data /  7'd100 % 4'd10  ;     // 百位数
  40. 40   assign  data1 = data /  4'd10 % 4'd10   ;     // 十位数
  41. 41   assign  data0 = data %  4'd10;                // 个位数
  42. 42   
  43. 43    //给字符数组赋值,用于存储字模数据
  44. 44   always @(posedge lcd_clk) begin
  45. 45       char[0 ]  <= 128'h00000018244242424242424224180000 ; // "0"
  46. 46       char[1 ]  <= 128'h000000107010101010101010107C0000 ; // "1"
  47. 47       char[2 ]  <= 128'h0000003C4242420404081020427E0000 ; // "2"
  48. 48       char[3 ]  <= 128'h0000003C424204180402024244380000 ; // "3"
  49. 49       char[4 ]  <= 128'h000000040C14242444447E04041E0000 ; // "4"
  50. 50       char[5 ]  <= 128'h0000007E404040586402024244380000 ; // "5"
  51. 51       char[6 ]  <= 128'h0000001C244040586442424224180000 ; // "6"
  52. 52       char[7 ]  <= 128'h0000007E444408081010101010100000 ; // "7"
  53. 53       char[8 ]  <= 128'h0000003C4242422418244242423C0000 ; // "8"
  54. 54       char[9 ]  <= 128'h0000001824424242261A020224380000 ; // "9"
  55. 55       char[10]  <= 128'h000000E7424242427E42424242E70000 ; // "H"
  56. 56       char[11]  <= 128'h000000000000007E44081010227E0000 ; // "z"
  57. 57   end
  58. 58   
  59. 59   //给不同的区域赋值不同的像素数据
  60. 60   always @(posedge lcd_clk ) begin
  61. 61       if (!sys_rst_n)  begin
  62. 62           pixel_data <= BLACK;
  63. 63       end
  64. 64       else if(     (pixel_xpos >= CHAR_POS_X)
  65. 65            && (pixel_xpos < CHAR_POS_X + CHAR_WIDTH/8*1)
  66. 66            && (pixel_ypos >= CHAR_POS_Y)
  67. 67            && (pixel_ypos < CHAR_POS_Y + CHAR_HEIGHT)  ) begin
  68. 68          if(char [data5] [ (CHAR_HEIGHT+CHAR_POS_Y - pixel_ypos)*8
  69. 69                         - ((pixel_xpos-CHAR_POS_X)%8) -1 ] )
  70. 70              pixel_data <= BLACK;        //显示字符为黑色
  71. 71          else
  72. 72              pixel_data <= WHITE;        //显示字符区域背景为白色
  73. 73      end
  74. 74      else if(     (pixel_xpos >= CHAR_POS_X + CHAR_WIDTH/8*1)
  75. 75            && (pixel_xpos < CHAR_POS_X + CHAR_WIDTH/8*2)
  76. 76            && (pixel_ypos >= CHAR_POS_Y)
  77. 77            && (pixel_ypos < CHAR_POS_Y + CHAR_HEIGHT)  ) begin
  78. 78          if(char [data4] [ (CHAR_HEIGHT+CHAR_POS_Y - pixel_ypos)*8
  79. 79                         - ((pixel_xpos-CHAR_POS_X)%8) -1 ]  )
  80. 80              pixel_data <= BLACK;
  81. 81          else
  82. 82              pixel_data <= WHITE;
  83. 83      end
  84. 84      else if(     (pixel_xpos >= CHAR_POS_X + CHAR_WIDTH/8*2)
  85. 85            && (pixel_xpos < CHAR_POS_X + CHAR_WIDTH/8*3)
  86. 86            && (pixel_ypos >= CHAR_POS_Y)
  87. 87            && (pixel_ypos < CHAR_POS_Y + CHAR_HEIGHT)  ) begin
  88. 88          if(char [data3] [ (CHAR_HEIGHT+CHAR_POS_Y - pixel_ypos)*8
  89. 89                         - ((pixel_xpos-CHAR_POS_X)%8) -1 ]  )
  90. 90              pixel_data <= BLACK;
  91. 91          else
  92. 92              pixel_data <= WHITE;
  93. 93      end
  94. 94      else if(     (pixel_xpos >= CHAR_POS_X + CHAR_WIDTH/8*3)
  95. 95            && (pixel_xpos < CHAR_POS_X + CHAR_WIDTH/8*4)
  96. 96                && (pixel_ypos >= CHAR_POS_Y)
  97. 97            && (pixel_ypos < CHAR_POS_Y + CHAR_HEIGHT)  ) begin
  98. 98          if(char [data2] [ (CHAR_HEIGHT+CHAR_POS_Y - pixel_ypos)*8
  99. 99                         - ((pixel_xpos-CHAR_POS_X)%8) -1 ]  )
  100. 100             pixel_data <= BLACK;
  101. 101         else
  102. 102             pixel_data <= WHITE;
  103. 103     end
  104. 104     else if(     (pixel_xpos >= CHAR_POS_X + CHAR_WIDTH/8*4)
  105. 105           && (pixel_xpos < CHAR_POS_X + CHAR_WIDTH/8*5)
  106. 106           && (pixel_ypos >= CHAR_POS_Y)
  107. 107           && (pixel_ypos < CHAR_POS_Y + CHAR_HEIGHT)  ) begin
  108. 108         if(char [data1] [ (CHAR_HEIGHT+CHAR_POS_Y - pixel_ypos)*8
  109. 109                        - ((pixel_xpos-CHAR_POS_X)%8) -1 ]  )
  110. 110             pixel_data <= BLACK;
  111. 111         else
  112. 112             pixel_data <= WHITE;
  113. 113     end
  114. 114     else if(     (pixel_xpos >= CHAR_POS_X + CHAR_WIDTH/8*5)
  115. 115           && (pixel_xpos < CHAR_POS_X + CHAR_WIDTH/8*6)
  116. 116               && (pixel_ypos >= CHAR_POS_Y)
  117. 117           && (pixel_ypos < CHAR_POS_Y + CHAR_HEIGHT)  ) begin
  118. 118         if(char [data0] [ (CHAR_HEIGHT+CHAR_POS_Y - pixel_ypos)*8
  119. 119                        - ((pixel_xpos-CHAR_POS_X)%8) -1 ] )
  120. 120             pixel_data <= BLACK;
  121. 121         else
  122. 122             pixel_data <= WHITE;
  123. 123     end
  124. 124     else if(     (pixel_xpos >= CHAR_POS_X + CHAR_WIDTH/8*6)
  125. 125           && (pixel_xpos < CHAR_POS_X + CHAR_WIDTH/8*7)
  126. 126               && (pixel_ypos >= CHAR_POS_Y)
  127. 127           && (pixel_ypos < CHAR_POS_Y + CHAR_HEIGHT)  ) begin
  128. 128         if(char [10]    [ (CHAR_HEIGHT+CHAR_POS_Y - pixel_ypos)*8
  129. 129                        - ((pixel_xpos-CHAR_POS_X)%8) -1 ] )
  130. 130             pixel_data <= BLACK;
  131. 131         else
  132. 132             pixel_data <= WHITE;
  133. 133     end
  134. 134     else if(     (pixel_xpos >= CHAR_POS_X + CHAR_WIDTH/8*7)
  135. 135           && (pixel_xpos < CHAR_POS_X + CHAR_WIDTH)
  136. 136               && (pixel_ypos >= CHAR_POS_Y)
  137. 137           && (pixel_ypos < CHAR_POS_Y + CHAR_HEIGHT)  ) begin
  138. 138         if(char [11]    [ (CHAR_HEIGHT+CHAR_POS_Y - pixel_ypos)*8
  139. 139                        - ((pixel_xpos-CHAR_POS_X)%8) -1 ] )
  140. 140             pixel_data <= BLACK;
  141. 141         else
  142. 142             pixel_data <= WHITE;
  143. 143     end
  144. 144     else begin
  145. 145         pixel_data <= WHITE;            //绘制屏幕背景为白色
  146. 146     end
  147. 147  end
  148. 148  
  149. 149  endmodule
复制代码

lcd_display模块实现的功能还是根据当前像素点的坐标,送出该像素点应该显示的数据,这里把像素的颜色数据设置为黑色或白色,字符部分为黑色,其他背景色为白色。代码中的第36-41行是计算频率值的十进制数的各个位上的值,它们就是要在LCD上显示的6个数字。44行的always块用于存储字模数据。最后的第60行的always块就是主要的逻辑功能,它根据当前像素点坐标所在的字符坐标的范围,来计算当前像素点是否应该显示有效字符,即黑色。
至此,我们的设计部分已基本完成,现在我们来做一下误差分析。
   13.png               (1-3)

其中clk_fxe为被测频率信号的准确值。
在测量中,由于clk_fx计数的起停时间都是由该信号的上升沿触发的,在闸门时间GATE_TIME内对clk_fx的计数fx_cnt无误差();对clk_fs的计数fs_cnt最多相差一个时钟的误差,即|Δfs_cnt|≤1,其测量频率如式(1-4):
       14.png           (1-4)

将式(1-2)和(1-4)代入式(1-3),并整理如式
15.png                (1-5)

由上式可以看出,测量频率的相对误差与被测信号频率的大小无关,仅与闸门时间和基准时钟频率有关,即实现了整个测试频段的等精度测量。闸门时间越长,基准时钟频率越高,测频的相对误差就越小。基准时钟频率可由稳定度好、精度高的高频率晶体振荡器产生,在保证测量精度不变的前提下,提高基准时钟频率,可使闸门时间缩短,即提高测试速度。
23.5下载验证
编译工程并生成比特流.bit文件。将达芬奇开发板的K16引脚和L16引脚,用一根杜邦线或者跳帽连接起来,并连接好LCD排线的接口。将下载器一端连电脑,另一端与开发板上的JTAG端口连接,连接电源线并打开电源开关。
下载完成后LCD上面显示“500000Hz”,如下图所示,与时钟产生模块产生的时钟频率一致,等精度频率计实验下载验证成功。
新建DOCX 文档19615.png

图 23.5.2 实验结果

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-24 17:18

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表