VHDL实现数字频率计的设计
一、设计要求
当设计文件加载到目标器件后,拨动开关的K1,使其置为高电平,从输入输出观测模块的输入端输入一个频率大于1Hz的时钟信号,这时在数码管上显示这个时钟信号的频率值。如果使拨动开关置为低电平,数码管上显示的值为系统上的数字信号源的时钟频率。改变数字信号源的时钟,看显示的值是否与标值一致(数码管显示2s刷新一次)。
二、设计原理
测频实现框图如下图所示
所以我们可以通过设计六个模块,最终在一个原理图文件中连接,实现测频
文件名称 | 完成功能 |
---|---|
CLKOUT.VHD | 产生1Hz的闸门信号和1KHz的显示扫描信号 |
MUX.VHD | 被测信号源选择模块 |
TELTCL.VHD | 在时钟的作用下生成测频的控制信号 |
CNT10.VHD | 十进制计数器,在设计中使用8个来进行计数 |
SEG32B.VHD | 32位的锁存器,在锁存器控制信号的作用下,将计数的值锁存 |
DISPLAY.VHD | 显示译码,将锁存的数据显示出来 |
控制信号时序关系如下图所示
三、代码实现
1.CLKOUT.VHD
c
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY CLKOUT IS
PORT(CLK:IN STD_LOGIC;
CLKOUT1:OUT STD_LOGIC;
CLKOUT1K:OUT STD_LOGIC);
END CLKOUT;
ARCHITECTURE BHV OF CLKOUT IS
BEGIN
PROCESS(CLK)
VARIABLE COUNT1:STD_LOGIC_VECTOR(25 DOWNTO 0);
VARIABLE COUNT2:STD_LOGIC_VECTOR(15 DOWNTO 0);
VARIABLE Q1:STD_LOGIC;
VARIABLE Q2:STD_LOGIC;
BEGIN
IF CLK'EVENT AND CLK = '1' THEN
COUNT1:=COUNT1+1; --分频计数器1
COUNT2:=COUNT2+1; --分频计数器2
--这部分将50MHz频率分频得到1Hz方波
IF COUNT1="01011111010111100001000000" THEN --0.5s
--仿真时可以改成这条,因为电脑屏幕显示有限"00000000000000000000000010"
Q1:='1';
ELSIF COUNT1="10111110101111000010000000" THEN --1s
--仿真时可以改成这条,因为电脑屏幕显示有限"00000000000000000000000100"
Q1:='0';
COUNT1:="00000000000000000000000000";
END IF;
--这部分将50MHz频率分频得到1kHz方波
IF COUNT2="0110000110101000" THEN --0.5ms
--同上"0000000000000001"
Q2:='1';
ELSIF COUNT2="110000110101000" THEN --1.0ms
--同上"0000000000000010"
Q2:='0';
COUNT2:="0000000000000000";
END IF;
CLKOUT1 <=Q1; --输出1Hz方波
CLKOUT1K<=Q2; --输出1kHz方波
END IF;
END PROCESS;
END BHV;
仿真结果如下图所示
通过修改分频计数器的值可以得到不同频率的方波
2.MUX.VHD
c
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY MUX IS
PORT( SEL:IN STD_LOGIC;
CLKIN1:IN STD_LOGIC;
CLKIN2:IN STD_LOGIC;
CLKOUT:OUT STD_LOGIC);
END MUX;
ARCHITECTURE BHV OF MUX IS
BEGIN
PROCESS(SEL,CLKIN1,CLKIN2)
VARIABLE FLAG:STD_LOGIC;
BEGIN
IF SEL = '1'THEN
FLAG := '1';
ELSIF SEL = '0'THEN
FLAG := '0';
END IF;
IF FLAG = '1' THEN --当开关拨动置1时
CLKOUT<=CLKIN1; --输出波形1
ELSIF FLAG = '0' THEN --当开关拨动置1时
CLKOUT<=CLKIN2; --输出波形2
END IF;
END PROCESS;
END BHV;
仿真结果仿真结果如下图所示
开关置1时,输出CLKIN1的波形,开关置0时,输出CLKIN2的波形
3.TELTCL.VHD
c
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY TELTCL IS
PORT(CLK:IN STD_LOGIC;
EN:OUT STD_LOGIC;
CLR:OUT STD_LOGIC;
Q:OUT STD_LOGIC_VECTOR(3 DOWNTO 0);
LOAD:OUT STD_LOGIC);
END TELTCL;
ARCHITECTURE BHV OF TELTCL IS
BEGIN
PROCESS(CLK)
VARIABLE T1:STD_LOGIC_VECTOR(1 DOWNTO 0);
VARIABLE T2:STD_LOGIC_VECTOR(1 DOWNTO 0);
VARIABLE TT:STD_LOGIC_VECTOR(3 DOWNTO 0);
VARIABLE CEN:STD_LOGIC;
VARIABLE CCLR:STD_LOGIC;
VARIABLE CLOAD:STD_LOGIC;
BEGIN
IF CLK'EVENT AND CLK = '0' THEN
T1 := T1+1;
ELSIF CLK'EVENT AND CLK = '1' THEN
T2 := T2+1;
END IF;
--允许计数控制信号
IF (T1 = "01") OR (T1 = "11") THEN
CEN:='1';
ELSIF (T1 = "00") OR (T1 = "10") THEN
CEN:='0';
END IF;
--清零控制信号
TT := T1&T2;
IF TT = ("0001" OR "1011") THEN --"0000" "1010"
CCLR := '1';
ELSE
CCLR := '0';
END IF;
--锁存控制信号
IF CEN = '1' THEN
CLOAD := '0';
ELSIF CEN = '0' THEN
CLOAD := '1';
END IF;
--最终赋值输出
EN<=CEN;
CLR<=CCLR;
LOAD<=CLOAD;
Q<=TT;
END PROCESS;
END BHV;
仿真结果仿真结果如下图所示
完美波形
4.CNT10.VHD
c
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY CNT10 IS
PORT(CLK:IN STD_LOGIC;
EN:IN STD_LOGIC;
CLR:IN STD_LOGIC;
LOAD:OUT STD_LOGIC;
Q:OUT STD_LOGIC_VECTOR(3 DOWNTO 0));
END CNT10;
ARCHITECTURE BHV OF CNT10 IS
BEGIN
PROCESS(CLK,EN,CLK)
VARIABLE COUNT:STD_LOGIC_VECTOR(3 DOWNTO 0);
VARIABLE CLOAD:STD_LOGIC;
BEGIN
IF EN = '1' THEN
IF CLK'EVENT AND CLK = '1'THEN
COUNT := COUNT + 1; --每个时钟上升沿计数器+1
IF COUNT = "1010" THEN --当计数器值为10时清零
COUNT := "0000";
CLOAD := '1'; --LOAD输出一个高电平
ELSE
CLOAD := '0';
END IF;
IF CLR = '1' THEN --当清零控制信号为高电平时清零
COUNT := "0000";
END IF;
END IF;
LOAD <= CLOAD;
Q <= COUNT;
END IF;
END PROCESS;
END BHV;
仿真结果如图所示
5.SEG32B.VHD
c
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY SEG32B IS
PORT(CLK:IN STD_LOGIC;
H1:IN STD_LOGIC_VECTOR(3 DOWNTO 0);
H2:IN STD_LOGIC_VECTOR(3 DOWNTO 0);
H3:IN STD_LOGIC_VECTOR(3 DOWNTO 0);
H4:IN STD_LOGIC_VECTOR(3 DOWNTO 0);
H5:IN STD_LOGIC_VECTOR(3 DOWNTO 0);
H6:IN STD_LOGIC_VECTOR(3 DOWNTO 0);
H7:IN STD_LOGIC_VECTOR(3 DOWNTO 0);
H8:IN STD_LOGIC_VECTOR(3 DOWNTO 0);
X:OUT STD_LOGIC_VECTOR(31 DOWNTO 0));
END SEG32B;
ARCHITECTURE BHV OF SEG32B IS
BEGIN
PROCESS(CLK)
VARIABLE T1:STD_LOGIC_VECTOR(1 DOWNTO 0);
VARIABLE T2:STD_LOGIC_VECTOR(1 DOWNTO 0);
VARIABLE TT:STD_LOGIC_VECTOR(3 DOWNTO 0);
VARIABLE CX:STD_LOGIC_VECTOR(31 DOWNTO 0);
BEGIN
--因为我们需要高电平这一段,所以需要判断上升沿和下降沿
IF CLK'EVENT AND CLK = '0' THEN
T1 := T1+1;
ELSIF CLK'EVENT AND CLK = '1' THEN
T2 := T2+1;
END IF;
TT := T1&T2;
--判断是否是高电平期间
IF TT = ("0001" OR "1011" OR "0000" OR "1010") THEN
CX:=CX; --高电平锁存
ELSE
CX:=H8&H7&H6&H5&H4&H3&H2&H1; --低电平获取输入值
END IF;
X <= CX;
END PROCESS;
END BHV;
仿真结果如图所示
高电平期间锁存,低电平期间获取值
6.DISPLAY.VHD
c
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY DISPLAY IS
PORT(CLK:IN STD_LOGIC;
P:IN STD_LOGIC_VECTOR(31 DOWNTO 0);
SEGS7:OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
SEL:OUT STD_LOGIC_VECTOR(2 DOWNTO 0));
END DISPLAY;
ARCHITECTURE BHV OF DISPLAY IS
BEGIN
PROCESS(CLK)
VARIABLE QT:STD_LOGIC_VECTOR(31 DOWNTO 0);
VARIABLE Q1:STD_LOGIC_VECTOR(3 DOWNTO 0);
VARIABLE Q2:STD_LOGIC_VECTOR(3 DOWNTO 0);
VARIABLE Q3:STD_LOGIC_VECTOR(3 DOWNTO 0);
VARIABLE Q4:STD_LOGIC_VECTOR(3 DOWNTO 0);
VARIABLE Q5:STD_LOGIC_VECTOR(3 DOWNTO 0);
VARIABLE Q6:STD_LOGIC_VECTOR(3 DOWNTO 0);
VARIABLE Q7:STD_LOGIC_VECTOR(3 DOWNTO 0);
VARIABLE Q8:STD_LOGIC_VECTOR(3 DOWNTO 0);
VARIABLE K:STD_LOGIC_VECTOR(3 DOWNTO 0);
VARIABLE COUNT:STD_LOGIC_VECTOR(2 DOWNTO 0);
BEGIN
IF CLK'EVENT AND CLK = '1' THEN
COUNT := COUNT + 1;
QT:=P;
Q8:=QT(31 DOWNTO 28);Q7:=QT(27 DOWNTO 24);
Q6:=QT(23 DOWNTO 20);Q5:=QT(19 DOWNTO 16);
Q4:=QT(15 DOWNTO 12);Q3:=QT(11 DOWNTO 8);
Q2:=QT(7 DOWNTO 4);Q1:=QT(3 DOWNTO 0);
SEL <= COUNT;
CASE COUNT IS
WHEN "000" => K := Q8;
WHEN "001" => K := Q7;
WHEN "010" => K := Q6;
WHEN "011" => K := Q5;
WHEN "100" => K := Q4;
WHEN "101" => K := Q3;
WHEN "110" => K := Q2;
WHEN "111" => K := Q1;
WHEN OTHERS =>NULL;
END CASE;
CASE K IS
WHEN "0000" =>SEGS7 <= "00111111"; --0
WHEN "0001" =>SEGS7 <= "00000110"; --1
WHEN "0010" =>SEGS7 <= "01011011"; --2
WHEN "0011" =>SEGS7 <= "01001111"; --3
WHEN "0100" =>SEGS7 <= "01100110"; --4
WHEN "0101" =>SEGS7 <= "01101101"; --5
WHEN "0110" =>SEGS7 <= "01111101"; --6
WHEN "0111" =>SEGS7 <= "00000111"; --7
WHEN "1000" =>SEGS7 <= "01111111"; --8
WHEN "1001" =>SEGS7 <= "01101111"; --9
WHEN "1010" =>SEGS7 <= "01110111"; --A
WHEN "1011" =>SEGS7 <= "01111100"; --B
WHEN "1100" =>SEGS7 <= "00111001"; --C
WHEN "1101" =>SEGS7 <= "01011110"; --D
WHEN "1110" =>SEGS7 <= "01111001"; --E
WHEN "1111" =>SEGS7 <= "01110001"; --F
WHEN OTHERS => SEGS7 <= "00000000";
END CASE;
END IF;
END PROCESS;
END BHV;
仿真结果如图所示
数码管位选循环点亮八个数码管,达到视觉暂留,相当于八个数码管同时显示;
四、综合与仿真结果
参照教程完成六个VHD文件的转换成模块符号文件再将各个模块按下图所示方式连接
编译后没有错误即可,新建仿真页,参数仿照下图设置
开始仿真
由于条件有限,没有设备进行调试,所以仿真设置的分频计数器较小。这里只要数码管能正常显示和移位即表明程序实验成功,若烧录与FPAG芯片中些许问题可以对某些数据调参,以达到最优效果。如果还有其他问题欢迎私聊我。