2015年全国大学生电子设计竞赛 F题:数字频率计

来源:互联网 发布:镆铪合金知乎 编辑:程序博客网 时间:2024/04/29 02:10

VGA显示的部分我打算用07年数字示波器的那个题来详细分析。(也会用自动显示单位)

VGA显示只要你用心写过一个题目的,个人感觉下一次写显示就会轻松很多。

---------------------------------------------------------------------------------------------------------------------------------------------

今天上午使用Quartus Ⅱ 15.1 内带的FIR Ipcore,尝试去产生一个低通滤波器,调用之前写的DDS产生一个1M的正弦波和10M的正弦波相加的波形,利用八阶低通滤波器滤去10M的正弦波。结果却没有得到我想要的,Quartus 自带的modsim仿真是可以把10M正弦波给滤去的 ,但使用SignalTap实时仿真却得到矩形波,第一次使用FIR Ipcore,还是有很多东西不太了解,等出了结果,我会再发一篇博客。

下面是F题的要求。软件实现的功能很多,所以只能分开写,第二篇用来写VGA显示。


                                             数字频率计(F题)
                                                                         【本科组】
一、任务
设计并制作一台闸门时间为1s的数字频率计。
二、要求
1.基本要求
(1)频率和周期测量功能
a. 被测信号为正弦波, 频率范围为1Hz10MHz
b. 被测信号有效值电压范围为50mV1V
c. 测量相对误差的绝对值不大于10-4
(2) 时间间隔测量功能
a.被测信号为方波, 频率范围为100Hz1MHz
b.被测信号峰峰值电压范围为50mV1V
c. 被测时间间隔的范围为0.1μs100ms
d. 测量相对误差的绝对值不大于10-2
(3) 测量数据刷新时间不大于2s 测量结果稳定, 并能自动显示单位
2.发挥部分
(1)频率和周期测量的正弦信号频率范围为 1Hz100MHz, 其他要求同基
本要求(
1) 和(3) 。
(2) 频率和周期测量时被测正弦信号的最小有效值电压为 10mV, 其他要求
同基本要求(
1) 和(3) 。
F - 2 / 2
(3)增加脉冲信号占空比的测量功能, 要求:
a.被测信号为矩形波, 频率范围为1Hz5MHz
b.被测信号峰峰值电压范围为50mV1V
c. 被测脉冲信号占空比的范围为10%90%
d. 显示的分辨率为0.1%, 测量相对误差的绝对值不大于10-2
(4) 其他( 例如, 进一步降低被测信号电压的幅度等) 。
三、说明
本题时间间隔测量是指 AB 两路同频周期信号之间的时间间隔 TA-B。测试
时可以使用双通道
DDS函数信号发生器,提供 AB 两路信号


基本要求i中的红体加粗部分,就是我们软件要实现的。

闸门时间是1秒,数据刷新时间不大于2秒。

第一个要求就是测量频率和周期。因为频率和周期呈倒数关系,我们可以利用测得的频率获得周期。在15年的那道位同步时钟提取的题中,我有提到等精度测频的方法,这里依然可以用,而且相对误差符合题目要求。等精度测频的原理过几天也会发一篇博客,15年那套题的我写的论文搞不见了,我得找别人要下。我觉的我写的代码也很直观,大家完全可以从代码里发现,也可以锻炼我们的逻辑分析能力。

moduleFreMeasure(inputclk,inputrst_n,inputSig_in,output[31:0]Fre);parameter T_1s = 28'd49_999_999;/***********预置闸门**********///一秒翻转一次,数据刷新时间为2秒。可以通过改变预置闸门关闭的时间改变数据刷新时间。reg[27:0]TCount;always @ (posedge clk or negedge rst_n)if(!rst_n)TCount <= 28'd0;else if(TCount >= T_1s)TCount <= 'd0;elseTCount <= TCount + 1'b1;regTCountCnt;always @ (posedge clk or negedge rst_n)if(!rst_n)TCountCnt <= 1'b0;else if(TCount >= T_1s)TCountCnt <= ~TCountCnt;/***********实际闸门************/regstartCnt;always @ (posedge Sig_in)if(TCountCnt == 1'b1)startCnt <= 1'b1;elsestartCnt <= 1'b0;/************在实际闸门内计数************/reg[31:0]SigTemp;always @ (negedge Sig_in)if(startCnt == 1'b1)SigTemp <= SigTemp + 1'b1;elseSigTemp <= 'd0;/***************锁存输出***********/reg[31:0]FreOut;always @ (negedge startCnt)FreOut <= SigTemp;assignFre = FreOut;endmodule

第二个要求是时间间隔测量。时间间隔是我们通过函数发生器产生两个方波输入至我们FPGA开发板,通过改变某个方波的相位,使两个方波出现相位差。我们要求的是两个信号上升沿的时间差。利用我前面写的边沿检测(看我之前的博客),很容易提取出两个信号的上升沿,虽然这样检测上升沿会有一个50Mclk的误差,两个信号就消掉了。把两个信号的上升沿转换为高低电平,然后分别计数,求基准时钟数量最少的作为时间间隔,如果大家想知道怎么求相位差,可以看下我对03年C题低频数字相位测量仪的分析。

moduleTimePosetoLevel(inputclk,inputrst_n,inputSig_A,inputSig_B,outputPosetoLevelOut);/**********A\B信号的上升沿检测*********************/regsig_a0;regsig_a1;always @ (posedge clk or negedge rst_n)if(!rst_n)beginsig_a0 <= 'd0;sig_a1 <= 'd0;endelsebeginsig_a0 <= Sig_A;sig_a1 <= sig_a0;endwirePoseSig_A;assignPoseSig_A = (~sig_a1) & (sig_a0);regsig_b0;regsig_b1;always @ (posedge clk or negedge rst_n)if(!rst_n)beginsig_b0 <= 'd0;sig_b1 <= 'd0;endelsebeginsig_b0 <= Sig_B;sig_b1 <= sig_b0;endwirePoseSig_B;assignPoseSig_B = (~sig_b1) & (sig_b0);/***********时间间隔转为高低电平****************/regPosetoLevel;always @ (posedge clk or negedge rst_n)if(!rst_n)PosetoLevel <= 1'b0;else if(PoseSig_B)PosetoLevel <= 1'b1;else if(PoseSig_A)PosetoLevel <= 1'b0;elsePosetoLevel <= PosetoLevel;//wirePosetoLevelOut;assignPosetoLevelOut = PosetoLevel;endmodule
下面这个模块我用了两次,因为它可以计算高低电平的数量,把Sig_A信号输入,可以 测量Sig_A信号的占空比。
moduleMeasure(inputclk,inputrst_n,inputLevel,//将时间间隔转换的高低电平输入output[31:0]HighNumber,output[31:0]LowNumber);parameterT = 28'd49_999_999;//1秒/*********在1秒的闸门里求高电平和低电平的总数,求平均减少误差*************/reg[27:0]TCount;always @ (posedge clk or negedge rst_n)if(!rst_n)TCount <= 'd0;else if(TCount >= T)TCount <= 'd0;elseTCount <= TCount + 1'b1;regTCountCnt;always @ (posedge clk or negedge rst_n)if(!rst_n)TCountCnt <= 'd0;else if(TCount >= T)TCountCnt <= ~TCountCnt;regstartCnt;always @ (posedge Level)if(TCountCnt)startCnt <= 1'b1;elsestartCnt <= 1'b0;/**************检测高低电平的数量*******************/reg[31:0]HighNum;reg[31:0]LowNum;always @ (posedge Level)if(startCnt)HighNum <= HighNum + 1'b1;elseHighNum <= 'd0;always @ (negedge Level)if(startCnt)LowNum <= LowNum + 1'b1;elseLowNum <= 'd0;/***********分别对高低电平计数*************************/reg[31:0]HighCount;reg[31:0]LowCount;always @ (posedge clk or negedge rst_n)if(!rst_n)beginHighCount <= 'd0;endelse if(!startCnt)beginHighCount <= 'd0;LowCount <= 'd0;endelse if(startCnt && Level)beginHighCount <= HighCount + 1'b1;endelse if(startCnt && ~Level)beginLowCount <= LowCount + 1'b1;endelsebeginHighCount <= HighCount;LowCount  <= LowCount;end/*******锁存********************/reg[31:0]HighNumTo;reg[31:0]LowNumTo;reg[31:0]HighCountTo;reg[31:0]LowCountTo;always @ (negedge startCnt)beginHighCountTo <= HighCount;LowCountTo  <= LowCount;HighNumTo   <= HighNum;LowNumTo    <= LowNum;end//wire [31:0]  HighNumber;//wire [31:0]LowNumber;//assignHighNumber = HighCountTo/HighNumTo;//我在这里调用了除法器的ip核,其实完全可以这么写,两者占用的资源不同,//assignLowNumber  = LowCountTo/LowNumTo;//只是这样写编译会变慢,应为Quartus要替你产生大面积的电路/*******求得单个高低电平的基准时钟的数量************/wire[31:0]remian_1;wire[31:0]remain_2;DIV_1 DIV_1_inst_H (.denom(HighNumTo),//分母.numer(HighCountTo),//分子.quotient(HighNumber),//商.remain(remain_1)//余数);DIV_1 DIV_1_inst_L (.denom(LowNumTo),//分母.numer(LowCountTo),//分子.quotient(LowNumber),//商.remain(remain_2)//余数);endmodule
moduleTimeIMeasure(inputclk,inputrst_n,input[31:0]HighMeasureTemp,//输入高电平的基准时钟的个数input[31:0]LowMeasureTemp,//输入低电平的基准时钟的个数inputPosetoLevel,output[31:0]TimeInter );assign  TimeInter = (LowMeasureTemp > HighMeasureTemp) ? HighMeasureTemp: LowMeasureTemp + 1'b1;//比较,求基准时钟最小 //的作为时间间隔。后面在VGA显示的时候需要换算成时间。endmodule

第三个要求自动显示单位,在第二篇VGA显示里分析。

发挥部分

第一个要求把频率范围拓宽了,本来我们是用50M作为基准时钟,如果想测100M的频率,大家可以尝试使用Quartus内带的锁相环PLL产生高频信号,以此作为基准时钟。其实这里个人感觉考验的是硬件。带宽太大。

第二个要求也是硬件需要做的。

第三个要求测量占空比,占空比的计算公式:高电平 / (高电平 + 低电平);调用之前写的Measure模块可以求得高低电平的基准时钟的个数。但由于最高频率为5MHz,使用50Mclk作为基准时钟,很明显当高频的时候相对误差是不符合要求的,也需要利用PLL产生高频信号,但实际最高也就200M,依然不符合要求,至今还没太好的方案,找到我会更新的。生

moduleDuRaMeasure(inputclk,inputrst_n,inputLevel,  input[31:0]HighNumber,    input[31:0]LowNumber,output[15:0]DuRa);wire[31:0]HighAndLow;assignHighAndLow = HighNumber + (LowNumber + 1'b1) ;//低电平+1补偿wire[47:0]High;Mult_2 Mult_2_inst (.dataa(HighNumber),.datab(16'd10000),//提高分辨率.result(High));wire[31:0]remain_3;wire[47:0]DuRaOut;div_3 div_3_inst(.denom(HighAndLow),.numer(High),.quotient(DuRaOut),.remain(remain_3));assignDuRa = DuRaOut[15:0];endmodule

差不多到这里,软件的部分就全部完成了。有一点心得,写模块程序的时候最好写的不要专属性太强,因为以后可能某天你就会用到这个功能。

如果那里有不对的地方,也欢迎大家指出,我们共同进步。





阅读全文
1 0
原创粉丝点击