一、课程设计要求
1、学会应用数字系统设计方法进行电路设计;
2、进一步提高quartus II软件的开发应用能力;
3、提高VHDL进行综合设计的能力;
4、培养学生书写综合实验报告的能力。
二、课程设计要求与题目
2.1 课程设计要求
1、设计平台:quartus II+HH-SOPC-EP1C12 EDA/SOPC实验开发平台
2、设计方法:利用VHDL代码和/或原理图方法,采用层次化的方法进行设计(至少二层结构)。(功能分解)
3、结果验证:在实验开发平台上下载,验证设计的正确性,模块也需要仿真验证,给出仿真波形。
4、设计报告: A4纸打印,统一封面,封面格式见附件,简单装订。
2.2 课程设计题目
题目:多功能数字钟的设计与实现
1.能进行正常的时、分、秒计时,分别用6个七段数码管动态扫描显示时、分、秒。时时-分分-秒秒
2.利用按键开关快速调整时间(校准):时、分
3.通过按键开关设定闹铃时间,到了设定时间发出闹铃提示音,提示音长度为1分钟
4.通过按键开关设定倒计时的时间,通过开关启动/暂停倒计时,倒计时为0时发出提示音,提示音长度为1分钟
5.整点报时:
在59分50、52、54、56、58秒时按500Hz频率报时
在59分60秒时用1KHz的频率作最后一声整点报时
三、实验方案分析与设计
3.1 用户使用分析
多功能数字钟需要两个输出模块,一个是动态扫描数字显示,一个是蜂鸣器。
在使用体验上而言,多功能数字钟的输入模块主要有功能选择、置入时间等。
具有基础功能的数字钟是多功能数字钟的“枢纽”。除了倒计时功能,其余功能都与基础数字钟是离不开的。
3.2 各功能所需模块分析
对于最基础的、能显示时间的数字钟功能,需要分频器、计数器、显示器模块。利用这三个模块,可以构成一个独立的、功能单一的数字钟。带调整时间功能的数字钟比起基础数字钟,引入了置数等输入。为了整点报时,我们引入了蜂鸣器模块,在计数器内部的相应状态下会有启动蜂鸣器的信号传给蜂鸣器。一个闹钟需要有调整时间、定时发出声响等功能,也需要一个比较器来比较当前时间是否和闹钟时间相等。倒计时模块相对独立,不需要基础数字钟的相关模块与功能。
3.3 完整电路图
将以上功能、模块集成整合后,我们便可以在quartus平台上做出完成的电路图。其中,有很大一部分模块、功能可以被有效地复用、集成。如各个功能下的动态扫描、蜂鸣器、置数等。所以,我们在设置输入端时,需要有几个输入端实现选择功能的作用。
3.4 管脚锁定
对于动态扫描显示模块,我们只需要正确地锁定位码和段码。管脚锁定图如下所示。sel是位码,seg是段码。
对于置数模块,我们需要4个输入作为8421BCD码输入,一个输入选择/退出置数模式,一个输入控制置入的是小时还是分钟,一个输入控制置入的是个位还是十位。
对于蜂鸣器模块,我们需要将蜂鸣器本身的输出引脚接到N6上。为了分清蜂鸣器准点报时、闹铃、倒计时的功能,我们设置了十二位LED灯显示模块上的输出。当报时、闹铃、倒计时使得蜂鸣器被触发时,相应的LED灯也会亮。A9的灯会在每个小时59分的50/52/54/56/58秒亮,同时蜂鸣器按500hz响。B9的灯会在每个小时0分0秒亮,同时蜂鸣器按1khz响。A10的灯会在倒计时结束时亮,亮一分钟,同时蜂鸣器响一分钟。B10的灯会在闹钟触发时点亮。
倒计时的使能、重置、置数可以独立设计,如图所示。
闹钟的置数功能的开关也需要单独设置
剩余的管脚锁定,从上到下依次为:蜂鸣器开关、1khz时钟信号、时钟的使能端、时钟的复位端。
以下是完整的引脚锁定图。
四、具体实现过程描述
4.1 分频器
分频器输入1khz的时钟信号,并输出成1hz的时钟信号和一个500hz的时钟信号。所以,我们设置两个内部信号,一个内部信号是值为0~999的整数,另一个内部信号的值只能是0和1。
每一个1khz时钟信号到来时,第一个内部信号的计数器+1,第二个内部信号的值翻转。
当第一个内部信号值在0-499之间时,对外输出0。当第一个内部信号值在500-999之间时,对外输出1。这个输出端输出的也是时钟信号,频率就是1hz。
第二个内部信号值为1时,对外输出1。第二个内部信号值为0时,对外输出0。这个输出端输出的也是时钟信号,频率是500hz。
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity dev is port(clk_1khz:in std_logic;--输入为1khz的时钟信号 clk_1hz:out std_logic;--输出为1hz的时钟信号,用于计数 clk_500hz:out std_logic);--输出为500hz的时钟信号,用于报时 end dev; architecture beha of dev is signal q1:integer range 0 to 999;--内部信号 signal q2:std_logic;--内部信号 begin process(clk_1khz) begin if clk_1khz'event and clk_1khz='1' then if q1<500 then q1<=q1+1;clk_1hz<='0';--接收到1khz信号后计数,在0-499内输出0 elsif q1<999 then q1<=q1+1;clk_1hz<='1'; else q1<=0; end if; if q2='0' then q2<='1';clk_500hz<='0';--接收到1khz信号后翻转并输出 elsif q2='1' then q2<='0'; clk_500hz<='1'; else q2<='0'; end if; end if; end process; end;
4.2 秒计数器
秒计数器与纯粹的60进制计数器略有区别。为了在每个小时的59分的50、52、54、56、58秒能报时,触发蜂鸣器的响声,所以在检测到相应的秒的值时需要给出一个输出信号alarm。其余信号如1hz的时钟信号、reset、使能端、数据输出、进位与60进制计数器相同。
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity count_min is port( clk,rst,en:in std_logic; cc:buffer std_logic_vector(7 downto 0); co:out std_logic; alarm:out std_logic--让蜂鸣器输出 ); end count_min; architecture one of count_min is begin process(clk,rst) variable mc1,mc0:std_logic_vector(3 downto 0); begin if rst='1' then --将秒的十位和各位都置成0 mc1:=(others=>'0'); mc0:=(others=>'0'); elsif clk'event and clk='1' then if en='1' then--使能端为1,正常计数。否则,保持 mc0:=mc0+1; co<='0'; alarm<='0'; if mc0="1010" then--个位是10,则十位进位 mc1:=mc1+1; mc0:="0000"; end if; if mc1="0101" and mc0="0000" then alarm<='1'; end if;--50秒 if mc1="0101" and mc0="0010" then alarm<='1'; end if;--52秒 if mc1="0101" and mc0="0100" then alarm<='1'; end if;--54秒 if mc1="0101" and mc0="0110" then alarm<='1'; end if;--56秒 if mc1="0101" and mc0="1000" then alarm<='1'; end if;--58秒 if mc1="0101" and mc0="1001" then--到59秒,给分钟计数器发一个使能信号 co<='1'; end if; if mc1="0110" and mc0="0000" then--到60秒,则重置成0秒 alarm<='0'; mc1:="0000"; mc0:="0000"; end if; else co<='0'; end if; end if; cc<=mc1&mc0; end process; end one;
4.3 分计数器
分计数器与纯粹的60进制计数器也略有区别。为了在每个小时的59分的相应秒数按500hz报时,在分钟的十位数为5且个位数为9时输出一个alarm信号。在每个小时的59分59秒,输出一个进位信号。这个进位信号不仅能触发小时计数器,而且能触发蜂鸣器的1khz报时。
置数时,有一个输入信号控制我们置的是十位还是个位。置数时,还有一个输入信号是4位的8421BCD码。置数功能自带对输入的有效性作检测。在个位,若检测到BCD码值为“1010”“1111”的无效值,则视为置入了9.在十位,若检测到BCD码值为“0110”“1111”的无效值,则视为置入了5,因为秒的十位数不能是6只能是5。
其余信号如reset信号、使能信号、时钟信号、数据输出,都与60进制计数器相同。
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity count_h is port( clk,rst,en,ld:in std_logic; alarmhigh:out std_logic;--报时输出 cc:buffer std_logic_vector(7 downto 0); co:out std_logic; ledagnum:in std_logic_vector(3 downto 0);--置入的8421BCD码 ledagpos:in std_logic--置数选择置十位还是置个位 ); end count_h; architecture one of count_h is begin process(clk,rst,ld) variable mc1,mc0:std_logic_vector(3 downto 0); begin if rst='1' then --复位信号,将分钟重置成0 mc1:=(others=>'0'); mc0:=(others=>'0'); elsif clk'event and clk='1' then if ld='1' then--置数 if(ledagpos='1') then if(ledagnum>="0110") then--置十位时,若置入大于6的数,则置5 mc1:="0101"; else mc1:=ledagnum;--置入0~5的数,则正常置入 end if; elsif(ledagpos='0') then if(ledagnum>="1010") then--置个位时,若置入大于9的数,则置9 mc0:="1001"; else mc0:=ledagnum;--正常置入 end if; end if; elsif en='1' then mc0:=mc0+1; co<='0'; alarmhigh<='0'; if mc0="1010" then--个位发生进位 mc1:=mc1+1; mc0:="0000"; end if; if mc1="0101" and mc0="1001" then alarmhigh<='1';--到59分,则放出整点报时信号 end if; if mc1="0110" and mc0="0000" then--到60分,则置为0分,进位,使小时计数 mc1:="0000"; mc0:="0000"; alarmhigh<='0'; co<='1'; end if; else co<='0'; end if; end if; cc<=mc1&mc0; end process; end one;
4.4 小时计数器
小时计数器与纯粹的24进制计数器也略有区别。区别主要在置数上。置数时,有一个输入信号控制置的是十位还是个位。若置十位,则不能置入大于2的数。若置个位数,则不能大于9的数。在十位是2的情况下,也不能置入大于3的数,因为24以上的小时的值是不被允许的。
其余信号如reset信号、使能信号、时钟信号、数据输出,都与24进制计数器相同。
library ieee; use ieee.std_logic_1164.all; USE ieee.std_logic_unsigned.ALL; entity count_24 is port(clk,en,rst: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; co:out std_logic); end count_24; architecture one of count_24 is begin process(clk,rst,ld) variable mc1,mc0:std_logic_vector(3 downto 0):="0000"; begin if rst='1' then --复位,将小时和分钟都置为0 mc1:=(others=>'0'); mc0:=(others=>'0'); elsif clk'event and clk='1'then if ld='1' then if(ledagpos='1') then if(ledagnum>"0010") then--置数,小时的十位数大于2时视为置入2 mc1:="0010"; else mc1:=ledagnum; end if; elsif(ledagpos='0') then if(mc1="0010" and mc0>"0011") then--在20多小时,小时的个位数大于3时视为23时,个位置入3 mc0:="0011"; elsif(mc0>="1010") then mc0:="1001";--bcd码值大于十时视为置入9 else mc0:=ledagnum; end if; end if; elsif en='1' then mc0:=mc0+1; if mc0="1010"then--在10小时、20小时 mc1:=mc1+1; mc0:="0000"; end if; if (mc1="0010")and(mc0="0100")then--在23小时,下一次就进位 mc0:="0000"; mc1:="0000"; co<='1'; else co<='0'; end if; end if; end if; cc<=mc1&mc0; end process; end one;