HEU大三数字电路综合实验--简易数字频率计

实验技术指标要求

利用硬件描述语言 V e r i l o g Verilog Verilog (或 V H D L VHDL VHDL )、图形描述方式、IP核,结合数字系统设计方法,在 Q u a r t u s Quartus Quartus 开发环境下,利用 F P G A FPGA FPGA 开发板和自行设计的外围电路实现一个简易数字频率计,并满足以下的性能指标要求:

  • 能够实现对 1 k H z − 1 M H z 1kHz-1MHz 1kHz1MHz方波信号的频率测量(60分)
  • 能够实现对幅度 0.1 V − 4 V 0.1V-4V 0.1V4V ,频率 1 k H z − 10 k H z 1kHz-10kHz 1kHz10kHz 范围的正弦信号进行频率测量(60分)
  • 可对上述要求的正弦信号进行幅值的测量(40分)
  • 幅值信息与频率信息能通过开发板上之数码管实时显示(20分)
  • 幅值信息与频率信息能通过 U A R T UART UART 串口发送至计算机,通过电脑端串口调试助手显示(20分)

总分:200分

实际技术指标

实际得分:198分。

  • 方波信号1 H z Hz Hz- 70 M H z MHz MHz
  • 正弦信号:不带ADC负载条件下最高40MHz,最低1 H z Hz Hz; 带ADC负载条件下最高 3 M H z 3MHz 3MHz
  • 本地显示DE1-Soc所有数码管用满,UART可传输任意大小频率值或幅值

系统方案设计

对于测量方波的需求,仅需直接将信号发生器生成的方波信号输入至 F P G A FPGA FPGA 开发板的 G P I O GPIO GPIO 管脚,通过记录时间为 0.5 s 0.5s 0.5s 的闸门中上升沿的次数 s u m ∗ 2 sum*2 sum2 来统计频率;幅值信息与频率信息的显示切换通过拨动开关实现。 V e r i l o g H D L Verilog HDL VerilogHDL 程序的设计思路主要如下:
在这里插入图片描述
对于正弦信号频率的测量,主要采取的策略是利用外围整形电路将正弦波整形为等频率的方波,然后用上述方波频率的测量方法测量即可;对于正弦信号幅值的测量,主要依赖于外部高速 A D C ADC ADC 对正弦信号进行采样,得到最大电压值 V m a x V_{max} Vmax 与最小电压值 V m i n V_{min} Vmin ,最终幅值为 V m a x − V m i n 2 \frac{V_{max}-V_{min}}{2} 2VmaxVmin 。最终,系统的结构框图如下:
在这里插入图片描述

硬件设计

1.SMA转排针电路

信号发生器生成的的信号一般是通过鳄鱼夹夹住 G P I O GPIO GPIO 口上引出的杜邦线进行输入,这样虽然连接容易,但容易受鳄鱼夹天线效应影响引入杂波,影响频率计精度,在高频条件下( 1 M H z 1MHz 1MHz以上),此现象会非常明显。因此,给出的解决方案是,通过同轴电缆引入输入信号,并为此设计一个 S M A SMA SMA 转排针电路,原理图如下:
请添加图片描述
请添加图片描述
请添加图片描述

2.高速整形电路

方波测量不要任何外围电路,测正弦信号需要整形电路先整形为方波。

高速整形电路主要由两个部分构成:以 L M 319 LM319 LM319 为基础的高速过零比较电路+以 S N 74 L S 14 SN74LS14 SN74LS14 为基础的高速施密特反相整形电路。

2.1 器件选型

L M 319 LM319 LM319 是一个高速双路运放,支持单 5 V 5V 5V 供电或正负 15 V 15V 15V 供电,具有 80 n s 80ns 80ns 的快速响应时间,本实验中将利用该运放设计一个过零比较器。
在这里插入图片描述
在这里插入图片描述
S N 74 L S 14 SN74LS14 SN74LS14 是具有施密特触发输入的 6 6 6 通道、 4.75 V 4.75V 4.75V 5.25 V 5.25V 5.25V 双极性反相器,电平转换时间典型值为 15 n s 15ns 15ns ,具有极高的响应速度。
在这里插入图片描述
在这里插入图片描述

2.2 原理图设计与仿真

模块电路原理图如图所示。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.ADC

3.1 器件选型

本实验中采用 A D I ADI ADI 公司的 A D 9226 AD9226 AD9226 高速ADC模块进行正弦信号幅值的采样。(有条件的同学也可以使用DE1-Soc开发板上的片上ADC测量,官网上有demo可以修改。用AD9226的原因是该ADC是并口ADC,时序控制手搓比较简单。)
在这里插入图片描述
A D 9226 AD9226 AD9226 是一个单端 5 V 5V 5V 供电, 12 b i t 12bit 12bit , 65 M S P S 65MSPS 65MSPS 的流水线型模数转换器,包含一个片上高性能采样-保持电路,具有极低的误码率,典型应用中可以准确采样最高 31 M H z 31MHz 31MHz 正弦信号,转换率的影响主要受制于电平转换输出的速度。
在这里插入图片描述

4. USB转TTL

由于 U S B USB USB 通信电平与 T T L TTL TTL 电平不同,且实验箱上的开发扳没有直接的 U S B USB USB 接口,因此需要一个 C H 340 CH340 CH340 芯片对串口通信电平进行转换,以实现上位机(计算机)端串口调试助手的查看。
在这里插入图片描述

软件设计

1.闸门频率计设计

通过 G P I O GPIO GPIO 管脚捕获方波的下降沿个数并计数,然后设计一个 0.5 s 0.5s 0.5s 闸门获取频率,实际频率再乘 2 2 2 即可。这样设计的好处是,频率相对于 1 s 1s 1s 闸门能更快地刷新,且能保证准确度。

2.数码管显示

利用 V e r i l o g Verilog Verilog t a s k task task 语句封装每一位数码管点亮的程序,如下所示:

//light seg
	task light;
		output [7:0] seg;
		input [3:0] val;
		begin
			case (val)
				0:seg=7'b0000001;
				1:seg=7'b1001111;
				2:seg=7'b0010010; 
				3:seg=7'b0000110; 
				4:seg=7'b1001100; 
				5:seg=7'b0100100; 
				6:seg=7'b0100000; 
				7:seg=7'b0001111; 
				8:seg=7'b0000000; 
				9:seg=7'b0000100; 
			endcase
		end
	endtask

由于数码管仅能显示六位,因此选择显示频率的高 6 6 6位,若频率 > 1 0 6 H z >10^6Hz >106Hz ,则用 L E D LED LED 灯亮灭提示数量级的变化。串口端查看具体的缺省数值。

3.AD9226驱动

通过查阅手册可知, A D 9226 AD9226 AD9226 与外设通信采用的是同步并行通信,即每一个时钟上升沿到达时, D 0 D0 D0~ D 12 D12 D12 管脚会输出转换后的电压值(实际上有延迟,是前几个时钟周期的信号采样值,这是流水线式ADC的通病)。
在这里插入图片描述因此,首先需要分频出一个 25 M H z 25MHz 25MHz 的触发时钟,然后通过下面的拟合曲线线性映射转换为电压值:
注:这里拟合曲线是因为我把ADC的数据存储器的值倒Bit存入FPGA寄存器,这是因为实验过程中发现正常时序读写的话最高位的Bit很不稳定,受干扰较大,个人怀疑是淘宝购买模块的电路问题,如果大家买的模块是正常的话请不要用拟合的方法做)。
在这里插入图片描述
V I N = − 4.667 × D a t a + 10.26 V_{IN}=-4.667\times Data+10.26 VIN=4.667×Data+10.26
其中 V I N V_{IN} VIN 为采样到的电压值,单位 m V mV mV ; D a t a Data Data A D 9226 AD9226 AD9226 模块输出的转化结果。

此处实际调试中发现最高位的电平总是不稳定,因此选择按二进制位倒着取电压值,就可以获得较好的线性拟合结果。

获得正弦信号幅值的方法是:

  1. 获取输入信号电压最大值 V m a x V_{max} Vmax
  2. 获取输入信号电压最小值 V m i n V_{min} Vmin
  3. V m a x − V m i n 2 \frac{V_{max}-V_{min}}{2} 2VmaxVmin 为最终幅值

4.UART发送

按照下图的一般串口发送时序,通过 G P I O GPIO GPIO 模拟 U A R T UART UART T x Tx Tx 管脚,实现 U A R T UART UART 发送的功能,配置的串口参数为:

  • 波特率: 9600 9600 9600
  • 数据位: 8 b i t 8bit 8bit
  • 停止位: 1 b i t 1bit 1bit
    在这里插入图片描述

约定待发送的字符串为统一格式: 8 8 8 位数据(不足则高位补零)+ 2 2 2 位单位( H z / m V Hz/mV Hz/mV)。

下载验证

经过各模块原理验证与仿真分析,最终,频率计系统的搭建如下:
在这里插入图片描述
下面分别以方波与正弦信号加以测试

1.方波信号测试

**测试条件:**高电平 2 V 2V 2V,低电平 0 V 0V 0V

误差率计算公式:
w = f 1 − f 0 f 0 × 100 % w=\frac{f_1-f_0}{f_0}\times 100\% w=f0f1f0×100%

设定值 f 0 f_0 f0测量值 f 1 f_1 f1相对误差 x x x误差率 w % w\% w%
2200.000000%
11451411451400.000000%
1000000100000220.000200%
1000000010000022220.000220%
2000000020000044440.000220%
3000000030000065650.000217%
4000000040000086860.000215%
50000000500001101100.000200%
60000000600001321320.000200%

2.正弦信号测试

2.1 频率测试

**测试条件:**高电平 2.1 V 2.1V 2.1V ,低电平 0 V 0V 0V

误差率计算公式:
w = f 1 − f 0 f 0 × 100 % w=\frac{f_1-f_0}{f_0}\times 100\% w=f0f1f0×100%

设定值 f 0 f_0 f0测量值 f 1 f_1 f1相对误差 x x x误差率 w % w\% w%
2200.000000%
1000100000.000000%
11451411451400.000000%
1000000100000220.000200%
50000005000012120.000240%
1000000010000022220.000220%
2000000020000044440.000220%
3000000030000066660.000220%
4000000040000086860.000215%

2.2 幅度测试

**测试条件:**信号频率为 1 M h z 1Mhz 1Mhz,无额外偏置。电压单位均为 m V mV mV

误差率计算公式:
w = V 1 − V 0 V 0 × 100 % w=\frac{V_1-V_0}{V_0}\times 100\% w=V0V1V0×100%

设定值 V 0 V_0 V0测量值 V 1 V_1 V1相对误差 x x x误差率 w % w\% w%
500519193.8%
15001456-44-2.9%
17001662-38–2.2%

实验心得

这次实验是我“二刷”数字频率计这个题目,第一次制作时的需求是测量 1 M h z − 100 M h z 1Mhz-100Mhz 1Mhz100Mhz 的正弦信号频率,当时使用的是 S T M 32 STM32 STM32 E T R ETR ETR 触发计数,外围电路仅有 S N 74 L S 14 SN74LS14 SN74LS14 反相器和一个加法偏置电路。当时主要遇到的是两个问题:1) S T M 32 STM32 STM32 E T R ETR ETR 引脚计数受制于 S T M 32 STM32 STM32 系统时钟的控制,因此最高只能计数到 35 M h z 35Mhz 35Mhz 信号( S T M 32 F 1 STM32F1 STM32F1 时钟频率是 72 M h z 72Mhz 72Mhz),且 20 M h z 20Mhz 20Mhz 以上时,误差已经超过电赛训练题的要求。2) S N 74 L S 14 SN74LS14 SN74LS14 受正弦信号幅值影响严重,尤其在高频状态下,部分幅值的等频率正弦信号无法进行测量。经过这次实验,这两个问题均得到了很好的解决。首先, F P G A FPGA FPGA 的上升沿计数并不受某一时钟的控制,可以直接翻转相关的计数寄存器,因此可以在高频的状态下依旧维持极高的测量精度,果然 F P G A FPGA FPGA 在高频信号处理方面的优势名不虚传。另外,为了解决第一次实验中遇到的第二个问题,我利用高速比较器 L M 319 LM319 LM319 制作了一个过零比较电路,然后再用 S N 74 L S 14 SN74LS14 SN74LS14 整形,效果就好了太多,但由于我的比较器没设计成滞回比较器,因此反而在低频时候下降沿旁抖动有些小严重。此外, L M 319 LM319 LM319 的频率响应时间并没有 S N 74 L S 14 SN74LS14 SN74LS14 那样优秀,且高频状态下波形会失真(数据手册提到了这点),因此离达到 100 M h z 100Mhz 100Mhz 正弦信号频率测量的目标还差的有些远。如需改进,可考虑设计外围分频电路,以减轻整形电路的压力。

附录

附录1:Verilog Codes

module frequency_meter(clk,clk_4000hz,clk_2hz,clk_25Mhz,ff,seg1,seg2,seg3,seg4,seg5,seg6,rst_n,led1,led2,uart_txd,mode,dat);
	input clk; //SYSCLK:50Mhz
	input ff;  //input pin
	input rst_n; //reset button
	input mode; //0:uart_tx:frequency 1:uart_tx:amplitude of sine
	input [11:0] dat; //ADC output
	output reg clk_4000hz; //For uart Tx transmission
	output reg clk_2hz; //Gatetime:0.5s
	output reg clk_25Mhz;
	output reg led1; //times 10
	output reg led2; //times 100
	output reg uart_txd; //Uart_Tx
	output reg [6:0] seg1; //seg1
	output reg [6:0] seg2; //seg2
	output reg [6:0] seg3; //seg3
	output reg [6:0] seg4; //seg4
	output reg [6:0] seg5; //seg5
	output reg [6:0] seg6; //seg6
	
	//parameter define
	parameter  CLK_FREQ = 50000000;             //系统时钟频率
	parameter  UART_BPS = 9600;                 //串口波特率
	localparam BPS_CNT  = CLK_FREQ/UART_BPS;    //为得到指定波特率,对系统时钟计数BPS_CNT次
	
	//For Uart
	reg uart_en;
	reg uart_en_d0; 
	reg uart_en_d1;  
	reg [15:0] clk_cnt;                         //系统时钟计数器
	reg [3:0] tx_cnt;                          //发送数据计数器
	reg tx_flag;                         //发送过程标志信号
	reg [7:0] tx_data;                         //寄存发送数据
   wire  en_flag;
	
	//For Clk_div
	reg [10:0] cnt;
	reg [23:0] cnt1;
	reg [1:0] cnt2;
	
	//For Gate
	integer owari; //Gate collect owari
	reg [27:0] sum; 
	reg [27:0] ans;
	
	//For AD9226
	reg [27:0] sum1;
	reg [27:0] maxx;
	reg [27:0] minn;
	reg [27:0] maxx1;
	reg [27:0] minn1;
	reg [11:0] dat_r;
	//reg [2:0] tot;
	
	//For Uart IQR
	reg [3:0]  bit1;
	reg [3:0]  bit2;
	reg [3:0]  bit3;
	reg [3:0]  bit4;
	reg [3:0]  bit5;
	reg [3:0]  bit6;
	reg [3:0]  p; //pointer in a string
	reg [7:0]  uart_bit1; //Byte to Tx
	reg [27:0]  oper;
	reg  over;
	
	//div clk
	always @ (posedge clk)
		begin
			if (cnt==1999)
				begin
					clk_4000hz=~clk_4000hz;
					cnt=0;
				end
			else
				begin
					cnt=cnt+1;
					clk_4000hz=clk_4000hz;
				end
			if (cnt1==12499999)
				begin
					clk_2hz=~clk_2hz;
					cnt1=0;
				end
			else
				begin
					cnt1=cnt1+1;
					clk_2hz=clk_2hz;
				end
			if (cnt2==1)
				begin
					cnt2=0;
					clk_25Mhz=~clk_25Mhz;
				end
			else
				begin
					cnt2=cnt2+1;
					clk_25Mhz=clk_25Mhz;
				end
		end
		
	//AD9226
	always @ (posedge clk_25Mhz)
		begin
			dat_r=dat;
		end
	
	//Seg show
	always @(posedge clk_4000hz or negedge rst_n)
		begin
			if (!rst_n)
				begin
					maxx1=0; minn1=0;
				end
			else
				begin
					if (!mode)
						begin
							led1=0; led2=0;
							if (sum<1000000)
								begin
									led1=0; led2=0;
									bit1=sum%10;
									bit2=(sum%100)/10;
									bit3=(sum%1000)/100;
									bit4=(sum%10000)/1000;
									bit5=(sum%100000)/10000;
									bit6=(sum%1000000)/100000;
								end
							if (sum>=1000000 && sum<10000000)
								begin
									led1=0; led2=1;
									bit1=(sum%100)/10;
									bit2=(sum%1000)/100;
									bit3=(sum%10000)/1000;
									bit4=(sum%100000)/10000;
									bit5=(sum%1000000)/100000;
									bit6=(sum%10000000)/1000000;
								end
							if (sum>=10000000 && sum<100000000)
								begin
									led1=1; led2=0;
									bit1=(sum%1000)/100;
									bit2=(sum%10000)/1000;
									bit3=(sum%100000)/10000;
									bit4=(sum%1000000)/100000;
									bit5=(sum%10000000)/1000000;
									bit6=(sum%100000000)/10000000;
								end
						end
						else
							begin	
								sum1=dat_r;
								if (sum1<=1750)
									sum1=sum1+130;
								sum1=sum1*4667;
								sum1=10260000-sum1;
								sum1=sum1/1000;
								if (sum1>maxx1)
									maxx1=sum1;
								sum1=maxx1-minn1;
								if (sum1<minn)
									minn1=sum1;
								if (minn1==0)
									minn1=sum1;
								sum1=sum1/2;
								bit1=sum1%10;
								bit2=(sum1%100)/10;
								bit3=(sum1%1000)/100;
								bit4=(sum1%10000)/1000;
								bit5=(sum1%100000)/10000;
								bit6=(sum1%1000000)/100000;
						end
				light(seg6,bit6);
				light(seg5,bit5);
				light(seg4,bit4);
				light(seg3,bit3);
				light(seg2,bit2);
				light(seg1,bit1);
			end
		end
	//AD9226
	
	//UART Tx
	always @(posedge clk_4000hz or negedge rst_n)
		begin
		if (!rst_n)
			begin
				maxx=0;
				minn=0;
			end
		else
			begin
				uart_en=0; //Disable Uart Tx
				if (!over)
					begin
						if (!mode)
							begin
								oper=sum;
							end
						else
							begin
								oper=dat_r;
								if (oper<=1750)
									oper=oper+130;
								oper=oper*4667;
								oper=10260000-oper;
								oper=oper/1000;
								if (oper>maxx)
									maxx=oper;
								oper=maxx-minn;
								if (oper<minn)
									minn=oper;
								if (minn==0)
									minn=oper;
								oper=oper/2;
							end
					end
				over=1;
				case (p)
					0:uart_bit1=(oper%100000000)/10000000;
					1:uart_bit1=(oper%10000000)/1000000;
					2:uart_bit1=(oper%1000000)/100000;
					3:uart_bit1=(oper%100000)/10000;
					4:uart_bit1=(oper%10000)/1000;
					5:uart_bit1=(oper%1000)/100;
					6:uart_bit1=(oper%100)/10;
					7:uart_bit1=oper%10;
					8:	begin
						  if (!mode)
								uart_bit1=72; //H
						  else
								uart_bit1=109; //m
						end
					9:begin
						 if (!mode)
								uart_bit1=122; //z
						 else
								uart_bit1=86; //V
					  end
					10:uart_bit1=13; //'\r'
				endcase
				if (p>=0 && p<=7)
					uart_bit1=uart_bit1+48; 
			
				if (!tx_flag) //if Tx procedure over
					begin
						p=p+1; //Tx next byte
						p=p%11;
						if (p==0) //if full string Tx over
							over=0; //Get next string
						uart_en=1; //enable Uart Tx
					end
			  end
		end
	//frequency_counter
	always @ (negedge ff or negedge rst_n)
		begin
		if (!rst_n)
			begin
				ans=0;
			end
		else
			begin
				if (owari)
					ans=0;
				ans=ans+1;
		   end
		end
	//Gate
	always @(posedge clk_2hz or negedge rst_n)
		begin
			if (!rst_n)
				begin
					sum=0;
				end
			else
				begin
					owari=~owari;
					if (owari)
						begin
							sum=ans*2;
							sum=sum-2;
						end
				end
		end
	
	//捕获uart_en上升沿,得到一个时钟周期的脉冲信号
	assign en_flag = (~uart_en_d1) & uart_en_d0;
                                                 
	//对发送使能信号uart_en延迟两个时钟周期
	always @(posedge clk or negedge rst_n) begin         
		if (!rst_n) begin
			uart_en_d0 <= 1'b0;                                  
			uart_en_d1 <= 1'b0;
		end                                                      
		else begin                                               
			uart_en_d0 <= uart_en;                               
			uart_en_d1 <= uart_en_d0;                            
		end
	end

	//当脉冲信号en_flag到达时,寄存待发送的数据,并进入发送过程          
	always @(posedge clk or negedge rst_n) begin         
		if (!rst_n) begin                                  
			tx_flag <= 1'b0;
			tx_data <= 8'd0;
		end 
		else if (en_flag) begin                 //检测到发送使能上升沿   
					tx_flag <= 1'b1;                //进入发送过程,标志位tx_flag拉高
					tx_data <= uart_bit1;            //寄存待发送的数据
			end
			else 
			if ((tx_cnt == 4'd9)&&(clk_cnt == BPS_CNT/2))
			begin                               //计数到停止位中间时,停止发送过程
					tx_flag <= 1'b0;                //发送过程结束,标志位tx_flag拉低
					tx_data <= 8'd0;
			end
			else begin
					tx_flag <= tx_flag;
					tx_data <= tx_data;
			end 
	end

	//进入发送过程后,启动系统时钟计数器与发送数据计数器
	always @(posedge clk or negedge rst_n) begin         
		if (!rst_n) begin                             
			clk_cnt <= 16'd0;                                  
			tx_cnt  <= 4'd0;
		end                                                      
		else if (tx_flag) begin                 //处于发送过程
			if (clk_cnt < BPS_CNT - 1) begin
					clk_cnt <= clk_cnt + 1'b1;
					tx_cnt  <= tx_cnt;
			end
			else begin
					clk_cnt <= 16'd0;               //对系统时钟计数达一个波特率周期后清零
					tx_cnt  <= tx_cnt + 1'b1;       //此时发送数据计数器加1
			end
		end
		else begin                              //发送过程结束
			clk_cnt <= 16'd0;
			tx_cnt  <= 4'd0;
		end
	end

	//根据发送数据计数器来给uart发送端口赋值
	always @(posedge clk or negedge rst_n) begin        
		if (!rst_n)  
			uart_txd <= 1'b1;        
		else if (tx_flag)
			case(tx_cnt)
					4'd0: uart_txd <= 1'b0;         //起始位 
					4'd1: uart_txd <= tx_data[0];   //数据位最低位
					4'd2: uart_txd <= tx_data[1];
					4'd3: uart_txd <= tx_data[2];
					4'd4: uart_txd <= tx_data[3];
					4'd5: uart_txd <= tx_data[4];
					4'd6: uart_txd <= tx_data[5];
					4'd7: uart_txd <= tx_data[6];
					4'd8: uart_txd <= tx_data[7];   //数据位最高位
					4'd9: uart_txd <= 1'b1;         //停止位
					default: ;
			endcase
		else 
			uart_txd <= 1'b1;                   //空闲时发送端口为高电平
	end
	//light seg
	task light;
		output [7:0] seg;
		input [3:0] val;
		begin
			case (val)
				0:seg=7'b0000001;
				1:seg=7'b1001111;
				2:seg=7'b0010010; 
				3:seg=7'b0000110; 
				4:seg=7'b1001100; 
				5:seg=7'b0100100; 
				6:seg=7'b0100000; 
				7:seg=7'b0001111; 
				8:seg=7'b0000000; 
				9:seg=7'b0000100; 
			endcase
		end
	endtask
endmodule
  • 6
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值