1)实验平台:正点原子领航者ZYNQ开发板
2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/FPGA/zdyz_linhanz.html
4)对正点原子FPGA感兴趣的同学可以加群讨论:876744900
5)关注正点原子公众号,获取最新资料
第二十二章频率计实验
数字频率计是一种基本的测量仪器,被广泛应用于航天、电子、测控等领域。基于传统测频原理的频率计的测量精度将随被测信号频率的下降而降低,在使用中有较大的局限性,而等精度频率计不但具有较高的测量精度,而且在整个频率区域能保持恒定的测试精度。本章我们通过Zynq领航者开发板搭建等精度频率计,学习等精度频率计的设计思想和实现方案。
本章分为以下几个章节:
2222.1等精度频率计简介
22.2实验任务
22.3硬件设计
22.4程序设计
22.5下载验证
22.1等精度频率计简介
频率测量在电子设计和测量领域中经常用到,因此对频率测量方法的研究在实际工程应用中具有重要意义。常用的频率测量方法有两种:周期测量法和频率测量法。周期测量法是先测量出被测信号的周期T,然后根据频率求出被测信号的频率。频率测量法是在时间t内对被测信号的脉冲数N进行计数,然后求出单位时间内的脉冲数,即为被测信号的频率。但是上述两种方法都会产生±1个被测脉冲的误差,在实际应用中有一定的局限性。根据测量原理,很容易发现周期测量法适合于低频信号测量,频率测量法适合于高频信号测量,但二者都不能兼顾高低频率同样精度的测量要求。
等精度测量的一个最大特点是测量的实际门控时间不是一个固定值,而是一个与被测信号有关的值,刚好是被测信号的整数倍。在计数允许时间内,同时对基准时钟和被测信号进行计数,再通过数学公式推导得到被测信号的频率。由于门控信号是被测信号的整数倍,就消除了对被测信号产生的±l周期误差,但是会产生对基准时钟±1周期的误差。等精度测量原理如图 22.1.1所示。
图 22.1.1 等精度测量原理
从以上叙述的等精度的测量原理可以很容易得出如下结论:首先,被测信号频率clk_fx的相对误差与被测信号的频率无关;其次,增大测量时间段“软件闸门”或提高“标频”clk_fs,可以减小相对误差,提高测量精度;最后,由于一般提供基准时钟clk_fs的石英晶振稳定性很高,所以基准时钟的相对误差很小,可忽略。假设基准时钟的频率为100MHz,只要实际闸门时间大于或等于1s,就可使测量的最大相对误差小于或等于10^(-8),即精度达到1/100MHz。等精度测量的核心思想在于如何保证在实际测量门闸内被测信号为整数个周期,这就需要在设计中让实际测量门闸信号与被测信号建立一定的关系。基于这种思想,设计中以被测信号的上升沿作为开启门闸和关闭门闸的驱动信号,只有在被测信号的上升沿才将图 22.1.1中预置的“软件闸门”的状态锁存,因此在“实际闸门”Tx内被测信号的个数就能保证整数个周期,这样就避免普通测量方法中被测信号的±1的误差,但会产生高频的基准时钟信号的±l周期误差,由于基准时钟频率远高于被测信号,因此它产生的±1周期误差对测量精度的影响十分有限,特别是在中低频测量的时候,相较于传统的频率测量和周期测量方法,可以大大提高测量精度。
等精度测频的原理图如图 22.1.2所示。图中,预置软件闸门信号Gate是由Zynq的定时模块产生,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,则由公式
(1-1)
推出:
(1-2)
图 22.1.2中的所有功能都在ZYNQ PL端实现。
图 22.1.2 Zynq实现的功能的原理图
22.2实验任务
板载50MHz的时钟通过内部逻辑进行分频,来产生500KHz频率的信号,作为被测时钟,然后用Verilog HDL编写的等精度测量模块测量被测时钟,并通过LCD显示。
22.3硬件设计
本次实验只需将领航者开发板J3(靠近ATK MODULE侧)扩展口的两个IO使用跳帽或者杜邦线连接即可。本次实验将Zynq的D20引脚做为分频产生的时钟的输出端,J18引脚(全局差分时钟的P端,因为在Xilinx 7系列器件中,只有CC差分全局时钟对的P端才能作为单端的全局时钟输入)作为被测时钟的输入端,通过一根导线(杜邦线)或者跳帽进行连接。
图 22.3.1 硬件原理图
由于端口引脚较多,这里仅给出部分管脚列表,如下表所示:
表 22.3.1 等精度频率计实验管脚分配
22.4程序设计
根据实验任务,我们可以大致规划出系统的控制流程:首先我们设计一个测试时钟模块用于生成被测的时钟,然后用等精度频率计模块测量被测时钟的频率,并将测得的时钟频率值送入LCD显示模块进行显示。由此画出系统的功能框图如下所示:
图 22.4.1 等精度频率计实验系统框图
由系统框图可知,FPGA部分包括四个模块:顶层模块(top_cymometer)、等精度频率计模块(cymometer)、时钟产生模块(clk_test)、以及LCD显示模块(lcd_rgb_char)。各模块功能如下:
顶层模块(top_cymometer):顶层模块完成了对其它三个模块的例化,实现各模块之间的数据交互。时钟产生模块产生被测时钟输出,并从外部接入至等精度频率计模块,以进行频率测量,将测量的结果传输给LCD显示模块进行显示。顶层模块的原理图如下图所示:
图 22.4.2 顶层模块原理图
等精度频率计模块(cymometer):等精度频率计模块测量输入的被测时钟的频率。并将测得的频率结果输出。
时钟产生模块(clk_test):时钟产生模块产生被测的时钟。
LCD显示模块(lcd_rgb_char):将等精度频率计测得的时钟频率值在LCD上显示出来。
顶层模块的代码如下:
- 1 module top_cymometer(
- 2 //system clock
- 3 input sys_clk , // 时钟信号
- 4 input sys_rst_n, // 复位信号
- 5
- 6 //cymometer interface
- 7 input clk_fx , // 被测时钟
- 8 output clk_out , // 输出时钟
- 9
- 10 //RGB LCD接口
- 11 output lcd_hs , //LCD 行同步信号
- 12 output lcd_vs , //LCD 场同步信号
- 13 output lcd_de , //LCD 数据输入使能
- 14 inout [23:0] lcd_rgb , //LCD RGB565颜色数据
- 15 output lcd_bl , //LCD 背光控制信号
- 16 output lcd_clk //LCD 采样时钟
- 17
- 18 );
- 19
- 20 //parameter define
- 21 parameter CLK_FS = 26'd50000000; // 基准时钟频率值
- 22
- 23 //wire define
- 24 wire [19:0] data_fx; // 被测信号测量值
- 25
- 26 //*****************************************************
- 27 //** main code
- 28 //*****************************************************
- 29
- 30 //例化等精度频率计模块
- 31 cymometer #(.CLK_FS(CLK_FS) // 基准时钟频率值
- 32 ) u_cymometer(
- 33 //system clock
- 34 .clk_fs (sys_clk ), // 基准时钟信号
- 35 .rst_n (sys_rst_n), // 复位信号
- 36 //cymometer interface
- 37 .clk_fx (clk_fx ), // 被测时钟信号
- 38 .data_fx (data_fx ) // 被测时钟频率输出
- 39 );
- 40
- 41 //例化测试时钟模块,产生测试时钟
- 42 clk_test #(.DIV_N(7'd100) // 分频系数
- 43 ) u_clk_test(
- 44 //源时钟
- 45 .clk_in (sys_clk ), // 输入时钟
- 46 .rst_n (sys_rst_n), // 复位信号
- 47 //分频后的时钟
- 48 .clk_out (clk_out ) // 测试时钟
- 49 );
- 50
- 51 //例化LCD显示模块
- 52 lcd_rgb_char u_lcd_rgb_char
- 53 (
- 54 .sys_clk (sys_clk),
- 55 .sys_rst_n (sys_rst_n),
- 56 .data (data_fx),
- 57 //RGB LCD接口
- 58 .lcd_hs (lcd_hs), //LCD 行同步信号
- 59 .lcd_vs (lcd_vs), //LCD 场同步信号
- 60 .lcd_de (lcd_de), //LCD 数据输入使能
- 61 .lcd_rgb (lcd_rgb), //LCD RGB565颜色数据
- 62 .lcd_bl (lcd_bl), //LCD 背光控制信号
- 63 .lcd_clk (lcd_clk) //LCD 采样时钟
- 64 );
- 65
- 66 endmodule
复制代码
顶层代码主要完成对各模块的例化并实现模块信号间的交互。第21行的基准时钟频率值参数为基准时钟频率值,当用不同的基准时钟时修改此参数即可。
时钟产生模块的代码如下:
- 1 module clk_test #(parameter DIV_N = 7'd100) //分频系数
- 2 (
- 3 //源时钟
- 4 input clk_in , // 输入时钟
- 5 input rst_n , // 复位信号
- 6 //分频后的时钟
- 7 output reg clk_out // 输出时钟
- 8 );
- 9
- 10 //reg define
- 11 reg [9:0] cnt; // 时钟分频计数
- 12
- 13 //*****************************************************
- 14 //** main code
- 15 //*****************************************************
- 16
- 17 //时钟分频,生成500KHz的测试时钟
- 18 always @(posedge clk_in or negedge rst_n) begin
- 19 if(rst_n == 1'b0) begin
- 20 cnt <= 0;
- 21 clk_out <= 0;
- 22 end
- 23 else begin
- 24 if(cnt == DIV_N/2 - 1'b1) begin
- 25 cnt <= 10'd0;
- 26 clk_out <= ~clk_out;
- 27 end
- 28 else
- 29 cnt <= cnt + 1'b1;
- 30 end
- 31 end
- 32
- 33 endmodule
时钟产生模块通过分频产生被测时钟,这里是用偶数分频方法产生,修改代码第一行的DIV_N分频参数,可得到不同频率的时钟信号,时钟频率为。由于该模块在顶层例化时clk_in为系统时钟50MHz,分频参数为100,产生的时钟频率为50000000/100 = 500000Hz。
等精度频率计模块的代码如下:
- 1 module cymometer
- 2 #(parameter CLK_FS = 26'd50_000_000) // 基准时钟频率值
- 3 ( //system clock
- 4 input clk_fs , // 基准时钟信号
- 5 input rst_n , // 复位信号
- 6
- 7 //cymometer interface
- 8 input clk_fx , // 被测时钟信号
- 9 output reg [19:0] data_fx // 被测时钟频率输出
- 10 );
- 11
- 12 //parameter define
- 13 localparam MAX = 6'd32; // 定义fs_cnt、fx_cnt的最大位宽
- 14 localparam GATE_TIME = 16'd5_000; // 门控时间设置
- 15
- 16 //reg define
- 17 reg gate ; // 门控信号
- 18 reg gate_fs ; // 同步到基准时钟的门控信号
- 19 reg gate_fs_r ; // 用于同步gate信号的寄存器
- 20 reg gate_fs_d0 ; // 用于采集基准时钟下gate下降沿
- 21 reg gate_fs_d1 ; //
- 22 reg gate_fx_d0 ; // 用于采集被测时钟下gate下降沿
- 23 reg gate_fx_d1 ; //
- 24 reg [ 15:0] gate_cnt ; // 门控计数
- 25 reg [MAX-1:0] fs_cnt ; // 门控时间内基准时钟的计数值
- 26 reg [MAX-1:0] fs_cnt_temp ; // fs_cnt 临时值
- 27 reg [MAX-1:0] fx_cnt ; // 门控时间内被测时钟的计数值
- 28 reg [MAX-1:0] fx_cnt_temp ; // fx_cnt 临时值
- 29
- 30 //wire define
- 31 wire neg_gate_fs; // 基准时钟下门控信号下降沿
- 32 wire neg_gate_fx; // 被测时钟下门控信号下降沿
- 33
- 34 //*****************************************************
- 35 //** main code
- 36 //*****************************************************
- 37
- 38 //边沿检测,捕获信号下降沿
- 39 assign neg_gate_fs = gate_fs_d1 & (~gate_fs_d0);
- 40 assign neg_gate_fx = gate_fx_d1 & (~gate_fx_d0);
- 41
- 42 //门控信号计数器,使用被测时钟计数
- 43 always @(posedge clk_fx or negedge rst_n) begin
- 44 if(!rst_n)
- 45 gate_cnt <= 16'd0;
- 46 else if(gate_cnt == GATE_TIME + 5'd20)
- 47 gate_cnt <= 16'd0;
- 48 else
- 49 gate_cnt <= gate_cnt + 1'b1;
- 50 end
- 51
- 52 //门控信号,拉高时间为GATE_TIME个实测时钟周期
- 53 always @(posedge clk_fx or negedge rst_n) begin
- 54 if(!rst_n)
- 55 gate <= 1'b0;
- 56 else if(gate_cnt < 4'd10)
- 57 gate <= 1'b0;
- 58 else if(gate_cnt < GATE_TIME + 4'd10)
- 59 gate <= 1'b1;
- 60 else if(gate_cnt <= GATE_TIME + 5'd20)
- 61 gate <= 1'b0;
- 62 else
- 63 gate <= 1'b0;
- 64 end
- 65
- 66 //将门控信号同步到基准时钟下
- 67 always @(posedge clk_fs or negedge rst_n) begin
- 68 if(!rst_n) begin
- 69 gate_fs_r <= 1'b0;
- 70 gate_fs <= 1'b0;
- 71 end
- 72 else begin
- 73 gate_fs_r <= gate;
- 74 gate_fs <= gate_fs_r;
- 75 end
- 76 end
- 77
- 78 //打拍采门控信号的下降沿(被测时钟下)
- 79 always @(posedge clk_fx or negedge rst_n) begin
- 80 if(!rst_n) begin
- 81 gate_fx_d0 <= 1'b0;
- 82 gate_fx_d1 <= 1'b0;
- 83 end
- 84 else begin
- 85 gate_fx_d0 <= gate;
- 86 gate_fx_d1 <= gate_fx_d0;
- 87 end
- 88 end
- 89
- 90 //打拍采门控信号的下降沿(基准时钟下)
- 91 always @(posedge clk_fs or negedge rst_n) begin
- 92 if(!rst_n) begin
- 93 gate_fs_d0 <= 1'b0;
- 94 gate_fs_d1 <= 1'b0;
- 95 end
- 96 else begin
- 97 gate_fs_d0 <= gate_fs;
- 98 gate_fs_d1 <= gate_fs_d0;
- 99 end
- 100 end
- 101
- 102 //门控时间内对被测时钟计数
- 103 always @(posedge clk_fx or negedge rst_n) begin
- 104 if(!rst_n) begin
- 105 fx_cnt_temp <= 32'd0;
- 106 fx_cnt <= 32'd0;
- 107 end
- 108 else if(gate)
- 109 fx_cnt_temp <= fx_cnt_temp + 1'b1;
- 110 else if(neg_gate_fx) begin
- 111 fx_cnt_temp <= 32'd0;
- 112 fx_cnt <= fx_cnt_temp;
- 113 end
- 114 end
- 115
- 116 //门控时间内对基准时钟计数
- 117 always @(posedge clk_fs or negedge rst_n) begin
- 118 if(!rst_n) begin
- 119 fs_cnt_temp <= 32'd0;
- 120 fs_cnt <= 32'd0;
- 121 end
- 122 else if(gate_fs)
- 123 fs_cnt_temp <= fs_cnt_temp + 1'b1;
- 124 else if(neg_gate_fs) begin
- 125 fs_cnt_temp <= 32'd0;
- 126 fs_cnt <= fs_cnt_temp;
- 127 end
- 128 end
- 129
- 130 //计算被测信号频率
- 131 always @(posedge clk_fs or negedge rst_n) begin
- 132 if(!rst_n) begin
- 133 data_fx <= 20'd0;
- 134 end
- 135 else if(gate_fs == 1'b0)
- 136 data_fx <= (CLK_FS / fs_cnt) * fx_cnt ;
- 137 end
- 138
- 139 endmodule
在前面的等精度频率计简介中,我们知道在等精度测量中需要一个闸门信号(门控信号),并且该闸门信号需要同步化到被测时钟域下。这里我们为了方便处理,用被测时钟控制闸门信号的产生,这样就避免了同步化处理,当然了,完全可以用基准时钟控制闸门信号的产生,不过这时产生的闸门信号我们需要同步化到被测时钟域下,这样做的目的是为了不让被测时钟计数产生周期的误差。门控时间由参数GATE_TIME设置,此处设为5000,需要说明的是该值越大测得的被测时钟频率值越精确,但测量时间也会相应的变慢一些。另外因为闸门信号是由被测时钟产生的,当测量频率较高的信号或者说信号频率大于10KHz(此值跟门控时间有关)时是不会有什么问题的,但当测量低频信号像Hz级这种,如果门控时间设置的大的话,测量时间就会非常长,此时可修改门控时间的值,为被测时钟频率的5~10倍即可,对于几十KHz及以上的时钟信号,门控时间的大小对测量速度的影响较小,频率越高影响越小,但对测量精度影响较大,因而在测量频率较高的信号时,建议增大门控时间。
代码中为了防止复位对测量造成的干扰,门控信号在复位后延迟了10个被测信号的周期(第54~57行)。另外计算被测信号频率是在基准时钟下的门控信号为低电平时进行。
建立了门控信号之后,我们需要通过门控信号分别使能基准时钟和被测时钟的计数。因为门控信号对基准时钟而言是异步信号,所以这里我们对门控信号进行了两次打拍处理得到基准频率下的门控信号gate_fs(代码第67行的always语句块)。在门控信号的下降沿将计数值寄存并清零计数寄存器。
在取得数值后,我们需要计算被测信号的频率值,由于在计算周期内数值已不再发生变化(计数值已寄存),而且保留了足够的计算时间,所以可以不用FIFO进行异步处理。计算完之后,把所得的结果赋给寄存器变量data_fx,其数值的单位为Hz。
LCD显示部分的代码和“RGB TFT-LCD字符和图片显示”实验的代码基本是一模一样的,唯一不同的地方在于lcd_display模块。
lcd_display模块的代码如下所示:
- 1 module lcd_display(
- 2 input lcd_clk, //lcd驱动时钟
- 3 input sys_rst_n, //复位信号
- 4
- 5 input [19:0] data ,
- 6
- 7 input [10:0] pixel_xpos, //像素点横坐标
- 8 input [10:0] pixel_ypos, //像素点纵坐标
- 9 output reg [23:0] pixel_data //像素点数据,
- 10 );
- 11
- 12 //parameter define
- 13 localparam CHAR_POS_X = 11'd1; //字符区域起始点横坐标
- 14 localparam CHAR_POS_Y = 11'd1; //字符区域起始点纵坐标
- 15 localparam CHAR_WIDTH = 11'd64; //字符区域宽度
- 16 localparam CHAR_HEIGHT = 11'd16; //字符区域高度
- 17
- 18 localparam WHITE = 24'b11111111_11111111_11111111; //背景色,白色
- 19 localparam BLACK = 24'b00000000_00000000_00000000; //字符颜色,黑色
- 20
- 21 //reg define
- 22 reg [127:0] char [11:0] ; //字符数组
- 23
- 24 //wire define
- 25 wire [3:0] data0 ; // 十万位数
- 26 wire [3:0] data1 ; // 万位数
- 27 wire [3:0] data2 ; // 千位数
- 28 wire [3:0] data3 ; // 百位数
- 29 wire [3:0] data4 ; // 十位数
- 30 wire [3:0] data5 ; // 个位数
- 31
- 32 //*****************************************************
- 33 //** main code
- 34 //*****************************************************
- 35
- 36 assign data5 = data / 17'd100000; // 十万位数
- 37 assign data4 = data / 14'd10000 % 4'd10; // 万位数
- 38 assign data3 = data / 10'd1000 % 4'd10 ; // 千位数
- 39 assign data2 = data / 7'd100 % 4'd10 ; // 百位数
- 40 assign data1 = data / 4'd10 % 4'd10 ; // 十位数
- 41 assign data0 = data % 4'd10; // 个位数
- 42
- 43 //给字符数组赋值,用于存储字模数据
- 44 always @(posedge lcd_clk) begin
- 45 char[0 ] <= 128'h00000018244242424242424224180000 ; // "0"
- 46 char[1 ] <= 128'h000000107010101010101010107C0000 ; // "1"
- 47 char[2 ] <= 128'h0000003C4242420404081020427E0000 ; // "2"
- 48 char[3 ] <= 128'h0000003C424204180402024244380000 ; // "3"
- 49 char[4 ] <= 128'h000000040C14242444447E04041E0000 ; // "4"
- 50 char[5 ] <= 128'h0000007E404040586402024244380000 ; // "5"
- 51 char[6 ] <= 128'h0000001C244040586442424224180000 ; // "6"
- 52 char[7 ] <= 128'h0000007E444408081010101010100000 ; // "7"
- 53 char[8 ] <= 128'h0000003C4242422418244242423C0000 ; // "8"
- 54 char[9 ] <= 128'h0000001824424242261A020224380000 ; // "9"
- 55 char[10] <= 128'h000000E7424242427E42424242E70000 ; // "H"
- 56 char[11] <= 128'h000000000000007E44081010227E0000 ; // "z"
- 57 end
- 58
- 59 //给不同的区域赋值不同的像素数据
- 60 always @(posedge lcd_clk ) begin
- 61 if (!sys_rst_n) begin
- 62 pixel_data <= BLACK;
- 63 end
- 64 else if( (pixel_xpos >= CHAR_POS_X)
- 65 && (pixel_xpos < CHAR_POS_X + CHAR_WIDTH/8*1)
- 66 && (pixel_ypos >= CHAR_POS_Y)
- 67 && (pixel_ypos < CHAR_POS_Y + CHAR_HEIGHT) ) begin
- 68 if(char [data5] [ (CHAR_HEIGHT+CHAR_POS_Y - pixel_ypos)*8
- 69 - ((pixel_xpos-CHAR_POS_X)%8) -1 ] )
- 70 pixel_data <= BLACK; //显示字符为黑色
- 71 else
- 72 pixel_data <= WHITE; //显示字符区域背景为白色
- 73 end
- 74 else if( (pixel_xpos >= CHAR_POS_X + CHAR_WIDTH/8*1)
- 75 && (pixel_xpos < CHAR_POS_X + CHAR_WIDTH/8*2)
- 76 && (pixel_ypos >= CHAR_POS_Y)
- 77 && (pixel_ypos < CHAR_POS_Y + CHAR_HEIGHT) ) begin
- 78 if(char [data4] [ (CHAR_HEIGHT+CHAR_POS_Y - pixel_ypos)*8
- 79 - ((pixel_xpos-CHAR_POS_X)%8) -1 ] )
- 80 pixel_data <= BLACK;
- 81 else
- 82 pixel_data <= WHITE;
- 83 end
- 84 else if( (pixel_xpos >= CHAR_POS_X + CHAR_WIDTH/8*2)
- 85 && (pixel_xpos < CHAR_POS_X + CHAR_WIDTH/8*3)
- 86 && (pixel_ypos >= CHAR_POS_Y)
- 87 && (pixel_ypos < CHAR_POS_Y + CHAR_HEIGHT) ) begin
- 88 if(char [data3] [ (CHAR_HEIGHT+CHAR_POS_Y - pixel_ypos)*8
- 89 - ((pixel_xpos-CHAR_POS_X)%8) -1 ] )
- 90 pixel_data <= BLACK;
- 91 else
- 92 pixel_data <= WHITE;
- 93 end
- 94 else if( (pixel_xpos >= CHAR_POS_X + CHAR_WIDTH/8*3)
- 95 && (pixel_xpos < CHAR_POS_X + CHAR_WIDTH/8*4)
- 96 && (pixel_ypos >= CHAR_POS_Y)
- 97 && (pixel_ypos < CHAR_POS_Y + CHAR_HEIGHT) ) begin
- 98 if(char [data2] [ (CHAR_HEIGHT+CHAR_POS_Y - pixel_ypos)*8
- 99 - ((pixel_xpos-CHAR_POS_X)%8) -1 ] )
- 100 pixel_data <= BLACK;
- 101 else
- 102 pixel_data <= WHITE;
- 103 end
- 104 else if( (pixel_xpos >= CHAR_POS_X + CHAR_WIDTH/8*4)
- 105 && (pixel_xpos < CHAR_POS_X + CHAR_WIDTH/8*5)
- 106 && (pixel_ypos >= CHAR_POS_Y)
- 107 && (pixel_ypos < CHAR_POS_Y + CHAR_HEIGHT) ) begin
- 108 if(char [data1] [ (CHAR_HEIGHT+CHAR_POS_Y - pixel_ypos)*8
- 109 - ((pixel_xpos-CHAR_POS_X)%8) -1 ] )
- 110 pixel_data <= BLACK;
- 111 else
- 112 pixel_data <= WHITE;
- 113 end
- 114 else if( (pixel_xpos >= CHAR_POS_X + CHAR_WIDTH/8*5)
- 115 && (pixel_xpos < CHAR_POS_X + CHAR_WIDTH/8*6)
- 116 && (pixel_ypos >= CHAR_POS_Y)
- 117 && (pixel_ypos < CHAR_POS_Y + CHAR_HEIGHT) ) begin
- 118 if(char [data0] [ (CHAR_HEIGHT+CHAR_POS_Y - pixel_ypos)*8
- 119 - ((pixel_xpos-CHAR_POS_X)%8) -1 ] )
- 120 pixel_data <= BLACK;
- 121 else
- 122 pixel_data <= WHITE;
- 123 end
- 124 else if( (pixel_xpos >= CHAR_POS_X + CHAR_WIDTH/8*6)
- 125 && (pixel_xpos < CHAR_POS_X + CHAR_WIDTH/8*7)
- 126 && (pixel_ypos >= CHAR_POS_Y)
- 127 && (pixel_ypos < CHAR_POS_Y + CHAR_HEIGHT) ) begin
- 128 if(char [10] [ (CHAR_HEIGHT+CHAR_POS_Y - pixel_ypos)*8
- 129 - ((pixel_xpos-CHAR_POS_X)%8) -1 ] )
- 130 pixel_data <= BLACK;
- 131 else
- 132 pixel_data <= WHITE;
- 133 end
- 134 else if( (pixel_xpos >= CHAR_POS_X + CHAR_WIDTH/8*7)
- 135 && (pixel_xpos < CHAR_POS_X + CHAR_WIDTH)
- 136 && (pixel_ypos >= CHAR_POS_Y)
- 137 && (pixel_ypos < CHAR_POS_Y + CHAR_HEIGHT) ) begin
- 138 if(char [11] [ (CHAR_HEIGHT+CHAR_POS_Y - pixel_ypos)*8
- 139 - ((pixel_xpos-CHAR_POS_X)%8) -1 ] )
- 140 pixel_data <= BLACK;
- 141 else
- 142 pixel_data <= WHITE;
- 143 end
- 144 else begin
- 145 pixel_data <= WHITE; //绘制屏幕背景为白色
- 146 end
- 147 end
- 148
- 149 endmodule
lcd_display模块实现的功能还是根据当前像素点的坐标,送出该像素点应该显示的数据,这里把像素的颜色数据设置为黑色或白色,字符部分为黑色,其他背景色为白色。代码中的第36-41行是计算频率值的十进制数的各个位上的值,它们就是要在LCD上显示的6个数字。44行的always块用于存储字模数据。最后的第60行的always块就是主要的逻辑功能,它根据当前像素点坐标所在的字符坐标的范围,来计算当前像素点是否应该显示有效字符,即黑色。
至此,我们的设计部分已基本完成,现在我们来做一下误差分析。
(1-3)
其中clk_fxe为被测频率信号的准确值。
在测量中,由于clk_fx计数的起停时间都是由该信号的上升沿触发的,在闸门时间GATE_TIME内对clk_fx的计数fx_cnt无误差();对clk_fs的计数fs_cnt最多相差一个时钟的误差,即|Δfs_cnt|≤1,其测量频率如式(1-4):
(1-4)
将式(1-2)和(1-4)代入式(1-3),并整理如式
(1-5)
由上式可以看出,测量频率的相对误差与被测信号频率的大小无关,仅与闸门时间和基准时钟频率有关,即实现了整个测试频段的等精度测量。闸门时间越长,基准时钟频率越高,测频的相对误差就越小。基准时钟频率可由稳定度好、精度高的高频率晶体振荡器产生,在保证测量精度不变的前提下,提高基准时钟频率,可使闸门时间缩短,即提高测试速度。
22.5下载验证
编译工程并生成比特流.bit文件。将Zynq的D20引脚和J18引脚,即扩展口最左下角的两个引脚,用一根杜邦线或者跳帽连接起来,并连接好LCD排线的接口。将下载器一端连电脑,另一端与开发板上的JTAG端口连接,连接电源线并打开电源开关。
下载完成后LCD上面显示“500000Hz”,如下图所示,与时钟产生模块产生的时钟频率一致,等精度频率计实验下载验证成功。
图 22.5.2 实验结果
|