VHDL语言设计数字频率计
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
数字频率计的设计
一、频率计实现的功能
要设计的频率计的测量围为1MHz。
为了提高测量的精度,量程分为三档,分别是:10kHz、100kHz、1MHz。
并要求在测量频率大于或小于选择的量程时,频率计自动换档。
1、当读数大于999时,频率计处于超量程状态,下一次测量时,量程自动增加一档。
2、当读数小于009时,频率计处于欠量程状态,下一次测量时,量程自动减小一档。
3、当超出测量围时,显示错误。
4、在计数时不显示数据,计数完成后只显示测量结果。
5、小数点位置要自动移位。
二、频率计各部分的分析
在这个设计中,需要用计数器来进行计数,而且计数器在各个档位要被重复使用,在测量的过程中,计数允许时钟信号还要进行调整,故将计数器设计成一个单独的模块,提供计数值的输出。
显示结果包括数值显示,档位显示及溢出标志显示。
其中数值显示要用到三个数码管,实验箱上连在一起的三个数码管中,只有两个数码管部接有译码器,因此我们自己还要在程序中为那个没有译码器的数码管再加一段七段译码器程序来显示结果。
档位标志由三个LED灯来显示,代替数码管上的小数点的功能。
溢出标志由两个LED灯来显示,其中一个显示结果溢出,另一个显示输入信号在测量围之。
该频率计的顶层逻辑电路原理图如图(1)所示:
图(1)
三、频率计各部分的设计和实现
从上面的分析可以知道,频率计可以由三个模块来组成。
下面对各个模块的设计方法和实现方法进行详细说明。
1、时基进程的设计和实现
在实际使用时,输入的信号是随意的,没有办法预知输入的频率。
因此选取频率计的时基是非常重要的。
在设计要求中,将量程分为三档,在某一档进行测量时,需要提供该档的
时基。
在10kHz档,该档最大读数为9.99kHz,最小读数为0.01 kHz,所以要提供的时基是频率为0.01 kHz的脉冲。
同理,在100 kHz档上,要提供的时基应该是频率为0.1 kHz 的脉冲。
在1 MHz档上,要提供的时基是频率为1 kHz的脉冲。
这三种脉冲信号从输入信号中提取,可以采用分频的方法来产生。
将输入信号先进行分频产生1 kHz的脉冲信号,然后将分频后的1 kHz信号通过一个10倍的分频器,产生0.1 kHz的脉冲信号,同时使用一个100倍分频器对1 kHz的信号分频产生0.01 kHz的脉冲信号。
在测量频率时,采用输入信号作为时基,以输入信号为时钟,用一个计数器测量在一个时基周期里输入的信号的周期数,这样就可以得到输入信号的频率。
产生一个高电平为时基信号周期的脉冲信号作为时基,使得能够在程序中以“如果时基信号为1”作为判断条件,如果满足条件则计数器开始计数。
同理,在设计中还要产生高电平为时基信号周期的1/10和1/100的脉冲信号作为时基。
这三种时基采用有限状态机来实现。
状态机采用1kHz的脉冲信号触发,由于还要产生高电平为10ms和1ms的脉冲信号,故采用100个状态的有限状态机。
要产生高电平为1ms 的脉冲信号,只要在状态99的时候产生高电平,状态100的时候恢复到低电平即可。
要产生高电平为10ms的脉冲信号,只要在状态90的时候产生高电平,在状态100的时候恢复到低电平即可。
需要产生哪一个时基就根据此时频率计所在的档位作为判断条件进行控制。
在100个状态中,很多状态的功能是相同的,可以将它们合并在一起。
2、计数器的设计和实现
为了计数方便,将计数器定义成一个整型信号。
只要使用“计数器<=计数器+1;”就可以。
这个计数值要作为显示输出,就要将这个计数器用个位、十位、百位分开表示,并且要遵循加法规则。
这样可以直接通过七段译码器进行显示。
在不同的档位,小数点的位置是不同的,可以用小数点的显示所在的档位为判断条件。
由于实验箱上的数码管没有小数点的,故在实验板上用三个LED灯来代替小数点的现实。
计数器的VHDL语言描述如程序1所示。
其中,reset为异步置位端口,sig_clk为时钟输入端口,en为信号输入端口,q1为计数值的个位输出端口,q2为计数值的十位输出端口,q3为计数值的百位输出端口。
cou1为计数值的个位,cou2为计数值的十位,cou3为计数值的百位。
ctrcou为控制计数功能的进程,outctr为控制计数值输出的进程。
程序1:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity counter is
port(reset:in std_logic;
sig_clk:in std_logic; --时钟输入端口
en:in std_logic; --信号输入端口
q1:out std_logic_vector(3 downto 0); --计数值的个位输出端口
q2:out std_logic_vector(3 downto 0); --计数值的十位输出端口
q3:out std_logic_vector(3 downto 0)); --计数值的百位输出端口
end;
architecture count of counter is
signal cou1:std_logic_vector(3 downto 0); --计数值的个位
signal cou2:std_logic_vector(3 downto 0); --计数值的十位
signal cou3:std_logic_vector(3 downto 0); --计数值的百位
begin
--控制计数功能的进程
ctrcou:process(reset,sig_clk)
begin
if reset='1' then
cou1<= "0000";
cou2<= "0000";
cou3<= "0000";
else
if sig_clk 'event and sig_clk = '1' then
if en ='1' then
if cou3 = "1010" then
cou3 <="1010";
elsif cou3 ="1001" and cou2 ="1001" and cou1 = "1001" then
cou1 <= "0000";
cou2 <= "0000";
cou3 <= "1010";
elsif cou1 ="1001" and cou2 ="1001" then
cou1 <= "0000";
cou2 <= "0000";
cou3 <=cou3+1;
elsif cou1 ="1001" then
cou1 <="0000";
cou2 <=cou2+1;
else
cou1<=cou1+1;
end if;
else
cou1 <= "0000";
cou2 <= "0000";
cou3 <= "0000";
end if;
end if;
end if;
end process ctrcou;
--控制计数值输出的进程
outctr: process(reset,en)
begin
if reset= '1' then
q1 <="0000";
q2 <= "0000";
q3 <= "0000";
else
if en 'event and en ='0' then
q1 <= cou1;
q2 <= cou2;
q3 <= cou3;
end if;
end if;
end process outctr;
end count;
3、七段译码器的设计和实现
七段译码器将输入的从0~9的4位二进制数,以七段译码的方式输出。
使用一个7位向量来分表表示七段译码器种的七段。
程序如程序2所示:
程序2:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity view is
port(in_data:in std_logic_vector(3 downto 0); --输入的二进制数
out_data:out std_logic_vector(0 to 6)); --输出的译码
end view;
architecture ouview of view is
begin
process(in_data)
begin
case in_data is
when "0000"=>out_data <="1111110"; --0的显示
when "0001"=>out_data <="0110000"; --1的显示
when "0010"=>out_data <="1101101"; --2的显示
when "0011"=>out_data <="1111001"; --3的显示
when "0100"=>out_data <="0110011"; --4的显示
when "0101"=>out_data <="1011011"; --5的显示
when "0110"=>out_data <="1011111"; --6的显示
when "0111"=>out_data <="1110000"; --7的显示
when "1000"=>out_data <="1111111"; --8的显示
when "1001"=>out_data <="1111011"; --9的显示
when others=>out_data <="0110001";
end case;
end process;
end ouview;
其中in_data表示输入的4位二进制数的端口,out_data为七段译码输出端口。
4、有限状态机的设计和实现
有限状态机程序如程序3所示。
state是用于产生时基的状态机类型。
在其中有开始状态(start)、判断状态(judge)、计数状态1(count1)、计数状态2~89 (count2to89)、计数状态90(count90)、计数状态91~98(count91to98)、计数状态99(count99)、计数状态100(count100)。
由于计数状态2~89是相同的,所以将它们合成一个,同样对计数状态91~98也进行合并。
由于时基信号都要在计数状态100时清零,所以计数状态100单独提取。
产生高电平为100ms的时基需要在计数状态1的时候将时基信号置1,从1~99计数状态只有99ms,在计数状态1之前的judge状态中,如果处于10kHz的档位,就要将时基信号置1。
产生高电平为10ms的时基,需要在计数状态90将时基信号置1。
产生高电平为1ms的时基,需要在计数状态99的时候将时基信号置1,所以计数状态1、计数状态90和计数状态99要单独提取。
程序3中,mycrm是一个state类型的信号,信号crmcou用于状态机中的计数器,其计数值从0到100,信号clk1k为产生的频率为1kHz的脉冲信号。
标志信号flag用于标志不同的档位,当flag为0时表示10kHz的测频档,当flag为1时表示100kHz的测频档,当flag为2时表示1MHz的测频档,当它为其他时表示溢出。
程序3:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity cymometer is
port(areset:in std_logic;
aclk:in std_logic;
q2_in:in std_logic_vector(3 downto 0);
enclk,cntok,cntov:out std_logic;
dot:out std_logic_vector(2 downto 0)); --小数点的输出
end;
architecture cym of cymometer is
type state is (start,judge,count1,count2to89,count90,count91to98,count99,count100);
signal mycrm:state;
signal crmcou:integer range 0 to 100; --状态机中的计数器
signal clk1k:std_logic; --1kHz的脉冲信号
signal cou1s:integer range 0 to 999; --产生1s显示计时信号的计数器
signal clk1s:std_logic; --产生1s显示计时信号的脉冲信号
signal flag:std_logic_vector(1 downto 0);--测频档的标志
begin
--使用2KHz的脉冲产生频率为1KHz的脉冲进程crclk1k:process(areset,aclk)
begin
if areset = '1' then --计数器清零
clk1k<='0';
else
if aclk 'event and aclk = '1' then
clk1k <= not clk1k;
end if;
end if;
end process crclk1k;
--产生1S的显示计时信号
cr1s: process(areset,clk1k)
begin
if areset = '1' then
cou1s <= 0;
clk1s <= '0';
else
if clk1k 'event and clk1k = '1' then
if cou1s =997 then
clk1s <= not clk1s;
cou1s<=cou1s+1;
elsif cou1s =999 then
clk1s <= not clk1s;
cou1s<=0;
else
cou1s <= cou1s +1;
end if;
end if;
end if;
end process cr1s;
--控制小数点的显示
crdot:process(flag)
begin
case flag is
when "00"=> dot <="100";
when "01"=> dot <="";
when "10"=> dot <="001";
when others=> dot <="111";
end case;
end process crdot;
--用于产生时基的状态机
cretimer:process(areset,clk1k)
begin
if areset = '1' then
cntov<='0';
cntok<='0';
crmcou<=0;
enclk<= '0';
flag <= "01";
mycrm <= start;
else
if clk1k'event and clk1k='1' then
case mycrm is
when start=> --开始状态
if clk1s='1' then
crmcou<=0;
enclk<='0';
mycrm<=judge;
else
mycrm<=start;
if q2_in ="0000" or q2_in ="1010" then
cntov <='1';
cntok <= '0';
else
cntov <='0';
cntok <='1';
end if;
end if;
when judge=> --判断状态
if flag="11" then --溢出档
if q2_in ="1010" then
flag<="11";
else
flag<="10";
end if;
elsif flag="00" then --10kHz测频档
if q2_in ="0000" then
flag<="00";
enclk<='1';
elsif q2_in ="1010" then
flag<="01";
else
flag<=flag;
end if;
elsif flag="01" then --100kHz测频档
if q2_in="0000" then
flag<= flag-1;
enclk<='1';
elsif q2_in="1010" then
flag<= flag+1;
else
flag<=flag;
end if;
else --1MHz测频档
if q2_in="0000" then
flag<= flag-1;
elsif q2_in="1010" then
flag<= flag+1;
else
flag<=flag;
end if;
end if;
mycrm<= count1; --状态转移到计数状态1 when count1=> --计数状态1
if flag="00" then
enclk<='1';
end if;
crmcou<= 1;
mycrm<= count2to89;
when count2to89=> --计数状态2~89 if crmcou=88 then
crmcou<=89;
mycrm<= count90;
else
crmcou<= crmcou+1;
mycrm<= count2to89;
end if;
when count90=> --计数状态90
if flag="01" then
enclk<='1';
end if;
crmcou<=90;
mycrm<= count91to98;
when count91to98=> --计数状态91~98 if crmcou=97 then
crmcou<=98;
mycrm<= count99;
else
crmcou<=crmcou+1;
mycrm<=count91to98;
end if;
when count99=> --计数状态99
if flag="10" or flag="11" then
enclk<='1';
end if;
crmcou<=99;
mycrm<=count100;
when count100=> --计数状态100
crmcou<=100;
enclk<='0';
mycrm<=start;
when others=> null;
end case;
end if;
end if;
end process cretimer;
end cym;
5、频率计的综合设计
将前面设计好的模块例化到一个程序里,组成了所要设计的频率计。
程序如程序4所示。
程序4:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity plj is
port(reset:in std_logic;
clk:in std_logic;
testsignal:in std_logic;
q1:out std_logic_vector(3 downto 0);
q2:out std_logic_vector(3 downto 0);
q3:out std_logic_vector(0 to 6);
cntok,cntov:out std_logic;
dot:out std_logic_vector(2 downto 0)); --小数点的输出
attribute lock :string; --引脚锁定
attribute lock of reset: signal is "34";
attribute lock of clk: signal is "20";
attribute lock of testsignal: signal is "52";
attribute lock of q1: signal is "53,54,55,56";
attribute lock of q2: signal is "57,58,59,60";
attribute lock of q3: signal is "81,80,79,78,77,76,75";
attribute lock of cntok: signal is "17";
attribute lock of cntov: signal is "15";
attribute lock of dot: signal is "71,70,69";
end;
architecture rtl of plj is
component cymometer --对控制器进行元件说明port(areset:in std_logic;
aclk:in std_logic;
q2_in:in std_logic_vector(3 downto 0);
enclk,cntok,cntov:out std_logic;
dot:out std_logic_vector(2 downto 0)); --小数点的输出end component;
component counter --对计数器进行元件说明
port(reset:in std_logic;
sig_clk:in std_logic; --外部时钟输入
en:in std_logic; --时基信号
q1:out std_logic_vector(3 downto 0); --计数值个位
q2:out std_logic_vector(3 downto 0); --计数值十位
q3:out std_logic_vector(3 downto 0)); --计数值百位
end component;
component view is --引用七段译码器
port(in_data:in std_logic_vector(3 downto 0);
out_data:out std_logic_vector(0 to 6));
end component;
signal en_s:std_logic; --子模块之间接口信号说明
signal q3out_s:std_logic_vector(3 downto 0);
begin --在结构体描述中使用元件例化语句U0: cymometer port map (reset,clk,q3out_s,en_s,cntok,cntov,dot);
U1: counter port map (reset,testsignal,en_s,q1,q2,q3out_s);
U2: view port map (q3out_s,q3);
end rtl;
四、仿真与测试
我们选用软件ModelSim SE 6.0来进行功能仿真;由于译码器比较简单,而且又不涉及时钟信号,故将译码器的仿真略去。
以下分别是计数器、状态机和整合后的频率计的仿真波形:
1、计数器的仿真:
图(2)、图(3)为计数器的仿真波形,仿真时间设为0~1s, sig_clk即待测信号设定初值为0,周期20us,占空比50%,reset即复位信号设定初值为1,周期为1s,占空比为1%,en即计数允许信号设定初值为0,周期为24ms,占空比为50%。
对波形的分析如下:
图(2)
图(3)
(1)、图(2)和图(3)的仿真设定值一样,不同之处在于图(3)将图例放得较大些。
(2)reset为0,en为1时,计数器才能开始计数。
(3)采用sig_clk的上升沿计数,en的周期为24ms,高电平持续12ms,sig_clk的周期为20us,即20us一个上升沿,则计数初值为120000/20=600,与波形仿真的结果一致。
2、状态机的仿真:
图(4)、图(5)为状态机的仿真波形图,其中图(4)显示的是0~16s的波形,图(5)显示的是9.1s~10.2s的波形。
两个波形的仿真设定值相同,仿真时间为0~20s,areset即复位信号的初值为1,周期为20s,占空比为1%;aclk即外部输入信号的初值为0,周期为500us(即输入为2K的时钟信号),占空比为50%;q2_in 采用counter即计数波形,周期为1s,起始值为0,终值为10,步进为1。
波形的分析如下:
(1)从图(4)看,q2_in为10,即溢出时,flag由设定的01切换至10,档位标志由010切换至001,即实现了自动换档功能。
换档过程cntok及cntov信号的转变亦符合设计要求。
(2)clk1s波形的周期为1s,高电平为2ms,在高电平状态进入judge状态;enclk周期为1s,其高电平所占时间由档位确定,从图(5)可看出,换档前后的enclk提供的计数时基由10ms变成1ms,这与设计要求完全相符。
图(4)
图(5)
3、整合后的频率计的仿真:
图(6)为整合后的频率计的仿真波形。
仿真时间为0~5s, reset即复位信号初值为1,周期为5s,占空比为1s;clk即外部输入时钟信号初值为0,周期为500us,占空比为50%,即频率为2K的时钟信号;testsignal即待测信号初值为0,周期为50us,占空比为50%,即
待测信号为20K的时钟信号。
波形的分析如下:
图(6)
(1)从波形上看,从t=2s后,q1=0,q2=0,q3为2的译码值,dot=,cntok=1,cntov=0,频率计的输出正确地显示与输入一致(20KHZ)的输出信息。
(2)在0~2s时间段,由于复位后q3=0,在1S之前状态机尚提供计数的时基,即在此之前计数器尚未进入正常的工作状态,但dot已进入正常的工作状态,帮dot会显示出换档信息,即从010切换至100;2s后频率计所有器件进入正常工作状态,所有的显示信息也开始正确显示。
五、总结与体会
1、利用实验室提供的编译软件调试时,发现复位信号的边沿触发设置存在问题,将其改为电平触发后便顺利通过。
2、利用实验室提供的函数信号发生器来产生待测信号,若使用实验板上的10M信号来作为输入的分频信号,发现实验结果与函数发生器显示的输入频率值存在很大的误差;当使用2KHZ信号来分频后,发现实验结果与输入很接近,每个档位显示的数值的最后一位与函数发生器显示的输入误差平均为3左右。
考虑到函数发生器的显示也可能存在误差,我们可以认为本设计基本可实现设计要求,能正确地测量输入数字信号的频率。
3、现场调试时,由于实验室软件与宿舍所用的调试软件提供器件的类型不同,在宿舍编译通过的程序到了实验室出现了很多问题,最终在老师离开之后我们才调试成功,只可惜老师已经离开,未能看到我们的展示成果。