4.5 闹钟设置器
闹钟设置器有时钟端clk,使能端en,置数端ld。输出端是两位8421BCD码。当使能端为0时,整个闹钟模块关闭。当使能端开启时,闹钟模块开启。当置数端为1时,闹钟会根据ledagpos信号选择置入十位还是个位。ledagnum为一位8421BCD码,为置入的数,输入无效时视为置入9。小时和分钟的值都需要一个闹钟设置器。闹钟设置器寄存了闹钟设定的时间。
library ieee; use ieee.std_logic_1164.all; USE ieee.std_logic_unsigned.ALL; entity alarmsetter_m is port(clk,en:in std_logic; cc:out std_logic_vector(7 downto 0); ledagnum:in std_logic_vector(3 downto 0); ledagpos:in std_logic; ld:in std_logic); end alarmsetter_m; architecture one of alarmsetter_m is begin process(clk,ld) variable mc1,mc0:std_logic_vector(3 downto 0):="0000"; begin if clk'event and clk='1'then if en='0' then if ld='1' then--置数 if(ledagpos='1') then if(ledagnum>"0101") then--分钟的十位,最多只能置5 mc1:="0101"; else mc1:=ledagnum; end if; elsif(ledagpos='0') then if(mc0>="1010") then--个位,只能置0~9 mc0:="1001"; else mc0:=ledagnum; end if; end if; end if; end if; end if; cc<=mc1&mc0; end process; end one;
4.6闹钟判断器
输入端为当前的小时数和当前的分钟数、闹钟设定的小时数和闹钟设定的分钟数。若它们对应相等,则对蜂鸣器输入信号。
library ieee; use ieee.std_logic_1164.all; USE ieee.std_logic_unsigned.ALL; entity alarmjudger is port(clk:in std_logic; htime:in std_logic_vector(7 downto 0);--当前小时 halarm:in std_logic_vector(7 downto 0);--闹钟小时 mtime:in std_logic_vector(7 downto 0);--当前分钟 malarm:in std_logic_vector(7 downto 0);--闹钟分钟 alarmon:out std_logic);--输出 end alarmjudger; architecture one of alarmjudger is begin process(clk) begin if clk'event and clk='1'then if htime=halarm and mtime=malarm then--判断两两相等 alarmon<='1'; else alarmon<='0'; end if; end if; end process; end one;
4.7 倒计时器
输入端有时钟、复位、使能、置数等,输出有两个8421BCD码表示的秒数、蜂鸣器开启信号。在最后的01秒,输出蜂鸣器开启的信号,以便在下一个时钟信号到来时蜂鸣器发出声音。设置了内部变量,使得倒计时计到0时开始计数,能从0计到60,能够控制蜂鸣器响的时间:60秒。
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity countdown is port( clk,rst,en,ld:in std_logic;--时钟、复位、使能、置数 alarmout:out std_logic;--蜂鸣器开启 ledagnum:in std_logic_vector(3 downto 0);--设置置入的数 ledagpos:in std_logic;--设置置入十位还是个位 sout:out std_logic_vector(7 downto 0)--输出当前倒计时 ); end countdown; architecture one of countdown is begin process(clk,rst,ld) variable mc10,mc1:std_logic_vector(3 downto 0); variable q1:integer range 0 to 100;--内部变量, begin if rst='1' then --复位,复位成99秒 mc10:="1001"; mc1:="1001"; q1:=0; alarmout<='0'; elsif clk'event and clk='1' then if ld='1' then if ledagpos='1' then--置十位 mc10:=ledagnum; end if; if ledagpos='0' then--置个位 mc1:=ledagnum; end if; end if; if ld='0' then if en='1' then if (mc1=1 and mc10=0) then--在最后01秒 alarmout<='1'; mc1:=mc1-1; q1:=0; elsif mc1=0 then if mc10=0 then--在0秒,倒计时完成时 q1:=q1+1;--计数器,每秒+1,在计了60秒前输出蜂鸣器信号 if q1<59 then alarmout<='1'; else q1:=61; alarmout<='0'; end if; else mc10:=mc10-1; mc1:="1001";--在十的倍数秒 end if; else mc1:=mc1-1;-在十的倍数秒 alarmout<='0'; end if; end if; else alarmout<='0'; end if; end if; sout<=mc10&mc1; end process; end one;
4.8蜂鸣控制器
蜂鸣控制器的输出端直接连接着蜂鸣器,输出一个频率信号使得蜂鸣器按这个频率发出声响。在每个小时的59分50/52/54/56/58秒,按500hz发出声响。在每个小时的0分0秒,按1khz发出声响。在闹钟设定的时间,按1khz发出声响。在倒计时结束时,按500hz发出声响。
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity xiang is port(clk_500:in std_logic; clk:in std_logic; en:in std_logic; alarmlow,alarmhigh,alarmco:in std_logic; alarmbyalarm:in std_logic; alarmbycountdown:in std_logic; speaker:out std_logic); end xiang; architecture sss_arc of xiang is begin process(clk) begin if en='1' then if (alarmlow='1' and alarmhigh='1' and alarmco='0') then speaker<=clk_500; end if; if alarmco='1' then speaker<=clk; end if; if alarmbyalarm='1' then speaker<=clk; end if; if alarmbycountdown='1' then speaker<=clk_500; end if; end if; end process; end;
4.9动态扫描显示器
动态扫描显示器有多个输入,包括时钟信号、小时值、分钟值、秒值。是否正在置数、正在置哪一位数、是否正在置闹钟、是否想显示闹钟、是否在倒计时、显示倒计时等判断都被集成到了动态扫描显示器中。输出有位码和段码,在1khz的时钟信号下,能够正确的输出对应数字。
若当前正在置数,我们则可以设置一个计数器范围为01000,当该变量值为0500时,对应位置的段码数值为无效值。当该变量值为500~999时,该对应位置的值为当前置入的数值。利用这种做法,我们可以做到:当前正在设置的设置的位置的数在闪烁。
正常情况下,动态扫描显示模块会显示当前时间。当我们调整闹钟时,拨动开关,动态扫描模块显示闹钟时间。当我们调整倒计时时,拨动开关,动态扫描模块显示倒计时时间。闹钟的显示优先级高于倒计时,倒计时显示优先级高于正常时间。
将8位8421BCD码传送至7段显示译码器,然后动态地显示在相应的显示器上。显示器显示的内容受段码和位码的控制,这就是动态扫描显示的原理。
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity seltime is port(clk:in std_logic; h,m,s:in std_logic_vector(7 downto 0); sel:out std_logic_vector(2 downto 0); seg:out std_logic_vector(6 downto 0); isledagging:in std_logic; ledagpos:in std_logic_vector(2 downto 0); isledaggingalarm:in std_logic; alarmh,alarmm:in std_logic_vector(7 downto 0); iscountingdown:in std_logic; countdowns:in std_logic_vector(7 downto 0)) ; end seltime; architecture beha of seltime is signal scan_count:std_logic_vector(2 downto 0); signal dat:std_logic_vector(3 downto 0); signal q1:integer range 0 to 999; begin scan:process(clk) begin if clk'event and clk='1' then scan_count<=scan_count+1; q1<=q1+1; end if; sel<=scan_count; if isledaggingalarm='1' then case scan_count is when "101"=>dat<=alarmm(3 downto 0); when "100"=>dat<=alarmm(7 downto 4); when "011"=>dat<=alarmh(3 downto 0); when "010"=>dat<=alarmh(7 downto 4); when others=>dat<="1100"; end case; elsif iscountingdown='1' and isledagging='0' then case scan_count is when "111"=>dat<=countdowns(3 downto 0); when "110"=>dat<=countdowns(7 downto 4); when others=>dat<="1100"; end case; else case scan_count is when "111"=>dat<=s(3 downto 0); when "110"=>dat<=s(7 downto 4); when "101"=>dat<=m(3 downto 0); when "100"=>dat<=m(7 downto 4); when "011"=>dat<=h(3 downto 0); when "010"=>dat<=h(7 downto 4); when others=>dat<="1100"; end case; end if; end process scan; decode:process(scan_count) begin if (isledagging='1' and scan_count=ledagpos and q1<500) then seg<="0000000"; else case dat is when"0000"=>seg<="0111111"; when"0001"=>seg<="0000110"; when"0010"=>seg<="1011011"; when"0011"=>seg<="1001111"; when"0100"=>seg<="1100110"; when"0101"=>seg<="1101101"; when"0110"=>seg<="1111101"; when"0111"=>seg<="0000111"; when"1000"=>seg<="1111111"; when"1001"=>seg<="1101111"; when others=>seg<="0000000"; end case; end if; end process decode; end beha;
4.10 其他辅助模块
地址位码解释模块。我们在输入时,选择位置是按如下办法:一个开关选择调整小时还是分钟,一个开关选择调整十位还是个位。利用这一位码解释模块,我们便能将我们置数的位置解析成位码传给动态扫描显示模块。
library ieee; use ieee.std_logic_1164.all; entity flasher is port(inh,intt:in std_logic; dataout:out std_logic_vector(2 downto 0)); end flasher; architecture one of flasher is begin process(inh,intt) begin if(inh='1' and intt='1') then--小时的十位 dataout<="010"; end if; if(inh='1' and intt='0') then--小时的个位 dataout<="011"; end if; if(inh='0' and intt='1') then--分钟的十位 dataout<="100"; end if; if(inh='0' and intt='0') then--分钟的个位 dataout<="101"; end if; end process; end;
输入合成模块,将4个二进制输入合成为一个8421BCD码
library ieee; use ieee.std_logic_1164.all; entity connector is port(in3,in2,in1,in0:in std_logic; dataout:out std_logic_vector(3 downto 0)); end connector; architecture one of connector is begin dataout<=in3&in2&in1&in0;--将4位二进制数合成BCD码 end;
分配器,选择当前是在调整小时还是在调整分钟。
library ieee; use ieee.std_logic_1164.all; entity ledagger is port(data:in std_logic_vector(3 downto 0); posi:in std_logic; ison:in std_logic; datah:out std_logic_vector(3 downto 0); datam:out std_logic_vector(3 downto 0)); end ledagger; architecture one of ledagger is begin process(ison,posi) begin if (ison='1' and posi='1') then--调整小时 datah<=data; datam<="0000"; elsif (ison='1' and posi='0') then--调整分钟 datam<=data; datah<="0000"; else datam<="0000"; datah<="0000"; end if; end process; end one;
五、结论实现效果
5.1 各模块仿真波形效果
对分频器模块进行波形仿真,我们可以看到:1khz的频率被分成了500hz的频率和1hz的频率。
对秒计数器模块进行波形仿真,我们可以看到:每分钟50/52/54/56/58秒输出报时信号,每分钟59秒给分计数器输出进位信号。
对分计数器进行波形仿真,我们可以看到:每小时59分输出报时信号,以与秒计数器的50/52/54/56/58秒输出500hz的蜂鸣声。每小时60分输出进位信号,以让小时计数器计数,并对蜂鸣器给出报时信号。
对小时计数器进行波形仿真。每记到23,便复位为0,并进位输出,表示一天已经过去了。
对闹钟判断器进行波形仿真。当闹钟设定时间与当前时间的小时与分都对应着相等时,输出信号1.
对倒计时器进行波形仿真。在秒数不为0时,正常往下递减秒数。在秒数为0时,对输出信号1,持续60秒。该信号连接蜂鸣器。如图,首先将倒计时置为11.在11秒后,输出信号1持续了60秒。这对应了倒计时结束时蜂鸣器响一分钟的功能。
对蜂鸣器进行波形仿真。可以看到蜂鸣器频率与其输入频率之间的关系。
5.2整体效果
在将编译完成的QuartusII工程文件下载到机箱后,多功能数字钟便开启运行。
将K1开关拨动到1,就可以看到电子钟开始正常的计时运行。点击S1按钮,便可以将时间重置成从0分0秒开始。
将K2开关拨动到1,就可以开启蜂鸣器。
在每个小时的59分的50/52/54/56/58秒,蜂鸣器按500hz响。在每个小时的0分0秒,蜂鸣器按1khz响。这就是整点报时功能。
将K3开关拨动到1,就开始了调整时间模式。K4开关可以控制用户调整的是小时还是分钟,K5开关可以控制用户调整的是个位数还是十位数。在调整时,相应位置的数字闪烁。K6/K7/K8/K9是从高到低的8421BCD码,通过K6~K9所表示的十进制数可以调节对应位置的时间。
K10开关可以显示闹钟。默认的闹钟时间是22:00。但是,将K3开关拨动到1时K10开关也拨动到1,就可以调整闹钟。调整闹钟的值的办法与上一段调整时间值的办法是一致的。当闹钟设置的时间就是当前时间时,响铃一分钟。
K11开关可以显示倒计时。默认从99秒开始倒计时。将K12开关拨动到1,就可以调整倒计时的时间,调整办法与上文一致。按S2按钮可以重置倒计时。当倒计时为0时,响铃一分钟。