课
程
设
计
课程名称: EDA技术实用教程
题目:基于FPGA的数字钟设计系、专业:电子系应用电子技术年级、班级:
学生姓名:
指导老师:
时间: 2008年12月
目录
1、系统设计………………………………………………………..
1.1设计要求……………………………………………………
1.1.1任务………………………………………………..
1.1.2要求……………………………………………….
1.1.3题目分析…………………………………………
2、总体设计方案……………………………………………………
2.1方案一…………………………………………………………
2.2 方案二…………………………………………
2.3 方案三…………………………………………
2.4 方案四………………………………………
3、设计思路……………………………………………………
4、硬件电路的介绍……………………………………………
5、软件模块………………………………………………………
6、心得体会……………………………………………………... 7、参考文献……………………………………………………..
用VHDL语言编写的数字钟程序…………………………
摘要:EDA技术在电子系统设计领域越来越普及,本设计主要利用VHDL语言
在EDA平台上设计一个电子数字钟,它的计时周期为24小时,显示满刻度为23时59分59秒,另外还具有校时功能和闹钟功能。总的程序由几个各具不同功能的单元模块程序拼接而成,其中包括分频程序模块、时分秒计数和设置程序模块、比较器程序模块、三输入数据选择器程序模块、译码显示程序模块和拼接程序模块。并且使用QuartusII软件进行电路波形仿真,下载到EDA实验箱进行验证。该设计采用自顶向下、混合输入方式(原理图输入—顶层文件连接和VHDL语言输入—各模块程序设计)实现数字钟的设计、下载和调试。
关键词: FPGA芯片、VHDL语言、闹钟、模块程序
一、系统设计
1.1 设计要求
1.1.1 任务
设计并制作一个数字钟,通过设计,掌握电子设计的一般思路,学习电子设计的一般方法。
1.1.2要求
(1)基本要求
①计时功能:这是数字钟的基本功能,每隔一秒钟计时一次,并在显示屏上显示当前时间。
②校时功能:能设置实时时间作为数字钟的当前时间,具有小时、分钟的手动校准时间功能。
(2)发挥部分
①计时进制的选择功能:十二小时制或二十四小时制可选择控制;
②整点报警功能:每逢整点自动报警;
③其他创新功能。
1.1.3题目分析
题目要求设计一个能每隔一秒钟计时一次,并在显示屏上显示当前时间, 能设置实时时间作为数字钟的当前时间,具有小时、分钟的手动校准时间功能的数字钟。小时由24进制计数器、分秒由60进制计数器产生,频率是用1Hz方波频率输入组成的电路。
二、总体设计方案
方案一采用74LS90数码管译码电路构成硬件数字钟
晶体振荡器是构成数字式时钟的核心,它保证了时钟的走时准确及稳定. 下图所示电路通过晶振构成的输出为方波的数字式晶体振荡电路,这个电路中,电容C1,C2与晶体构成一个谐振型网络,完成对振荡频率的控制功能,由于晶体具有较高的频率稳定性及准确性,从而保证了输出频率的稳定和准确. 晶体XTAL的频率选为32768HZ.该元件专为数字钟电路而设计,其频率较低,有利于减少分频器级数. 从有关手册中,可查得C1,C2均为20pF.当要求频率准确度和稳定度更高时,还可接入校正电容并采取温度补偿措施.
晶体振荡器图
通常,数字钟的晶体振荡器输出频率较高,为了得到1Hz的秒信号输入,需要对振荡器的输出信号进行分频. 通常实现分频器的电路是计数器电路,一般采用多级2进制计数器来实现.例如,将32768Hz的振荡信号分频为1HZ的分频倍数为32768(215),即实现该分频功能的计数器相当于15极2进制计数器.
本实验中采用CD4060来构成分频电路.CD4060在数字集成电路中可实现的分频次数最高,而且CD4060还包含振荡电路所需的非门,使用更为方便. 2进制计数器,可以将32768HZ的信号分频为2HZ,其内部框图如图3-3所示,从图中可以看出,CD4060的时钟输入端两个串接的非门,因此可以直接实现振荡和分频的功能. 此方案采用中小模块集成电路,十分复杂,而且元件又难以购买,成功率不高,也不经济。
原理图如下:
方案一的电路图
方案二:采用AT89C52单片机、数码管设计数字钟。
本方案采用AT89C52单片机,单片机的P1口接数码管显示电路,P0口接键控制数码管的显示,P3口接入整点报时电路,P2接入复位和晶振电路。我们还未学习单片机的应用,所以不可取。
方案二电路图
方案三:采用555和74LS49设计数字钟
利用555设计一个多谐振荡器,其产生的秒脉冲触发计数,一个60进制的秒计数器和一个10进制的分计数器,校时电路采用秒清零后分计数,最后通过74LS49译码在数码管上显示输出。
此设计方法在实现校时步骤较多,先将秒计数器清零,然后移除由秒计数器Qc传给分计数器的脉冲CP1,将555产生的秒脉冲传给分计数器CP1,实现时借助与非逻辑门来实现,开关让电路图意思明了,三个与非门让电路更加完善。但是,此方案采用555设计的多谐振荡器,其振荡频率与实际的数字钟频率略有出入,虽然可以通过校时装置校时,但是电路原件颇多,难以调节.
方案三电路图
方案四:采用EDA实验箱设计数字钟
利用FPGA芯片,采用VHDL语言实现数字逻辑功能。设计分频模块、秒模块程序、扫描模块程序、显示模块程序、定时闹钟模块程序等模块实现其功能。此方案采用EDA实验箱来实现数字钟的各项功能,成功率比较高,操作也方便,也符合我们该学期的教学.
方案四整体原理框图
三、设计思路
题目要求设计要求一个12进制或24进制的具有时、分、秒计时功能,并要求能进行时、分、秒调整,每逢时有报时功能的数字钟。根据设计要求和方案比较,设计思路如下:
总体设计思路方框图
该设计中由12MHZ时钟信号、闹钟时钟切换键、调时键、调分键,四个输入信号进FPGA,FPGA输出信号送给蜂鸣器和数码管模块,在FPGA里由时、分、
秒构成计数器模块,校时功能调时、调分模块,闹钟\时钟切换模块还能调时、分准确闹铃模块,再由调时、调分、闹钟\时钟切换进行按键去抖模块,时钟信号进行分频送给时、分、秒计数器模块,整个设计逐步达到设计要求,得到具有时、分、秒可调可闹铃的数字钟。
四、硬件电路的介绍
4.1分频模块(原理图输入)
用12MHZ的时钟信号进行12000000分频得到1HZ的时钟信号,为秒计数器的个位提供时钟信号;另一路将12MHZ的时钟信号分频得到1000HZ的时钟信号,为数码管提供扫描基准频率; 另一路将12MHZ的时钟信号分频得到2HZ的时钟信号,为蜂鸣器提供闹铃信号; 另一路将12MHZ的时钟信号分频得到60HZ的时钟信号,为蜂鸣器提供闹铃信号。
4.2 去抖模块
通过设置一个移位寄存器完成延时20ms来解决按键抖动引起的乱码.
4.3 按键设置模块
通过设置两个两位的拨码开关,来控制进入秒表计时或者时间校准或者闹铃设置或者微妙计时状态。
4.4 扫描模块
通过用高频1000HZ的时钟信号来选中8位数码管,使人眼产生错觉认为8位数码管是同时亮。
4.5 秒表计时模块
通过设置秒表计时模块来实现秒表计算功能。
4.6 校时模块
通过设置四个按键来完成校时模块从而实现对分钟、小时进行校准。
4.7 闹铃设置模块
通过设置四个按键来完成校时模块从而实现对分钟、小时进行设置闹铃。
4.8 微妙计时(0.01S计时)模块
通过设置0.01S计时模块来完成马表计时。
4.9 数码管显示模块
通过设置数码管显示模块来完成对整个数字钟计时显示。
外部输入信号有1kHz/1Hz时钟信号、低电平有效的秒/微秒清零信号
CLR、低电平有效的调分信号SETmin、低电平有效的调时信号SEThour;
4.10 总体模块电路
五、软件设置
五、软件模块
本程序分为三大部分:头文件、实体、结构体。
本程序主要由7个进程完成了基本数字钟的秒表计时、校时、闹铃设置、复位、微妙计时的强大功能。
具体程序请见附件(1)。
六、心得体会
这次FPGA数字钟的设计历时十多天,在一周多的日子里,可以说是苦多于甜,但是可以学到很多很多的东西.在此次设计中,在收获知识的同时,还收获了阅历,收获了成熟,在此过程中,我们通过查找大量资料,请教老师,以及不懈的努力,不仅培养了独立思考、动手操作的能力,在各种其它能力上也都有了提高。更重要的是,在设计中,我们学会了很多学习的方法。而这是日后最实用的,真的是受益匪浅。
此次课程设计与以往不同,没有分组一个人独立完成,这应该是老师为了更好的锻炼我们,提高我们的综合能力,当然压力就蛮大了,所以在设计中遇到了很多的难题,首先就是电路的设计, 本设计要求一个12进制或24进制的具有时、分、秒计时功能的数字钟,并要求能进行时、分、秒调整,设置闹铃功能。在方案的选择上就出难题了,通过查资料,找老师讲解,自己实验后,终于把总体模块电路图制出来,写报告到没有多大的难题,很顺利的完成,其实不管怎样,这些都是一种锻炼,一种知识的积累,能力的提高。通过这次课程设计使我懂得了理论与实际相结合是很重要的,只有理论知识是远远不够的,只有把所学的理论知识与实践相结合起来,从理论中得出结论,从而提高自己的实际动手能力和独立思考的能力。由于时间的紧缺,并没有做到最好,但是,最起码我没有放弃,它是我的骄傲!相信以后我会以更加积极地态度对待学习、对待生活。我们的激情永远不会结束,相反,我们会更加努力,努力的去弥补自己的缺点,发展自己的优点,去充实自己,只有在了解了自己的长短之后,我们会更加珍惜拥有的,更加努力的去完善它,增进它。只有不断的测试自己,挑战自己,才能拥有更多的成功和快乐!快乐至上,享受过程,而不是结果!认真对待每一个过程,珍惜每一分一秒,学到最多的知识和方法,锻炼自己的能力,这个是我在此次课程设计上学到的最重要的东西,也是以后的一笔宝贵的财富.
总的来说,这次的FPGA数字钟设计还是比较成功的,在设计中遇到了很多问题,最后在老师的辛勤的指导下,终于游逆而解,有点小小的成就感,终于觉得平时所学的知识有了实用的价值,达到了理论与实际相结合的目的,不仅学到了不少知识,而且锻炼了自己的能力,使自己对以后的路有了更加清楚的认识,同时,对未来有了更多的信心。最后,对给过我帮助的各位指导老师和所有同学再次表示忠心的感谢!
七、参考文献
1. 谢自美电子线路设计实验测试(第二版) 华中科技大学出版社 2002
2. 潘松黄继业. EDA技术实用教程(第二版) 北京科学出版社 2005
3. 王锁萍电子设计自动化(EDA)教程西安电子科技大学出版社2006
4. 劳五一、劳佳编著《EDA技术实用教程》,清华大学出版社,2007年第1版;
附(1)程序如下:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity szz is
port(clk:in std_logic;--12M时钟
rst:in std_logic;--复位
key1,key2:in std_logic;--闹钟时钟切换开关调节
speak:out std_logic;--蜂鸣器
dout:out std_logic_vector(7 downto 0);--段码
selout:out std_logic_vector(2 downto 0));--位选
end szz;
architecture one of szz is
signal count:integer range 0 to 11999999;--1HZ秒
signal count1:integer range 0 to 199999;--60HZ秒
signal counf1:integer range 0 to 59999;--10ms
signal counf:integer range 0 to 1199;--10000HZ
signal sel:std_logic_vector(2 downto 0);--位选
SIGNAL md1: STD_LOGIC_VECTOR (1 downto 0);---
signal md2:std_logic_vector(1 downto 0);--时分加减开关调节signal zhou1:std_logic_vector(3 downto 0);--计数中小时的十位signal zhou2:std_logic_vector(3 downto 0);--小时的个位
signal zmin1:std_logic_vector(3 downto 0);--分钟的十位
signal zmin2:std_logic_vector(3 downto 0);--分钟的个位
signal zsec1:std_logic_vector(3 downto 0);--秒的十位
signal zsec2:std_logic_vector(3 downto 0);--秒的个位
signal seth1:std_logic_vector(3 downto 0);--设时中小时的十位signal seth2:std_logic_vector(3 downto 0);--小时的个位
signal setm1:std_logic_vector(3 downto 0);--分钟的十位
signal setm2:std_logic_vector(3 downto 0);--分钟的个位
signal h1:std_logic_vector(3 downto 0); ---显示小时十位
signal h2:std_logic_vector(3 downto 0);---小时的个位
signal m1:std_logic_vector(3 downto 0);--分钟的十位
signal m2:std_logic_vector(3 downto 0);--分钟的个位
signal s1:std_logic_vector(3 downto 0);--秒的十位
signal s2:std_logic_vector(3 downto 0);--秒的个位
signal s01:std_logic_vector(3 downto 0);--0.1秒
signal s02:std_logic_vector(3 downto 0);--0.01秒
SIGNAL a,b,c: STD_LOGIC_VECTOR (0 TO 3);---
signal clk1,clkk,clk60,clk5,beep,a1,b1,a2,b2,a3,b3:std_logic;
------------------------------------------------
begin
-----------------------------------------------分频
fp:process(clk)
begin
if rising_edge(clk) then
count<=count+1;
counf<=counf+1;
count1<=count1+1;
counf1<=counf1+1;
if count=11999999 then clk1<='1';---1Hz
count<=0;
beep<='1';
elsif count>5999999 then beep<='0'; ---2Hz
else clk1<='0';
end if;
if count1=199999 then clk60<='1';--60HZ
count1<=0;
else clk60<='0';
end if;
if counf1=59999 then
counf1<=0;
clk5<=not clk5;
end if;
if counf=1199 then clkk<='1';--10000HZ
counf<=0;
else clkk<='0';
end if;
end if;
end process fp;----------------------------------------------去抖PROCESS (clk5,a1,b1)
V ARIABLE a: INTEGER RANGE 0 to 3;
BEGIN
IF CLK5'EVENT AND CLK5='1' THEN
a:=a+1;
case a is
when 1 => a1<=key1;
when 2 => b1<=a1;
a1<=key1;
when others =>null;
end case;
end if;
end process;-------------------------------------------- PROCESS (clk5,a2,b2)
V ARIABLE b: INTEGER RANGE 0 to 3;
BEGIN
IF CLK5'EVENT AND CLK5='1' THEN
b:=b+1;
case b is
when 1 => a2<=key2;
when 2 => b2<=a2;
a2<=key2;
when others =>null;
end case;
end if;
end process;----------------------------------------------- PROCESS (clk5,a3,b3)
V ARIABLE c: INTEGER RANGE 0 to 3;
BEGIN
IF CLK5'EVENT AND CLK5='1' THEN
c:=c+1;
case c is
when 1 => a3<=rst;
when 2 => b3<=a3;
a3<=rst;
when others =>null;
end case;
end if;
end process;---------------------------------------------按键设置process (key1,key2)
begin
if clk1'event and clk1='1' then
if (key1='0' and a1='0' and b1='0') then
if md1="11" then
md1<="00";
else
md1<=md1+1;
end if;
end if;
if (key2='0' and a2='0' and b2='0') then
if md2="10" then
md2<="00";
else
md2<=md2+1;
end if;
end if;
end if;
end process;----------------------------------------------模8计数choice:process(clkk)---位选
begin
if clkk'event and clkk='1' then
if sel="111" then
sel<="000";
else
sel<=sel+1;
end if;
end if;
end process choice;-------------------------------------秒个位
s2201:process(clk1,rst)
begin
if clk1'event and clk1='1' then
if zsec2="1001" then ---其中sec2是秒的个位
zsec2<="0000";
else
zsec2<=zsec2+1;
end if;
end if;
if (rst='0' and a3='0' and b3='0')then zsec2<="0000"; ---秒个位复位
end if;
end process s2201;
---------------------------------------------秒十位
s1101:process(clk1,rst)
begin
if clk1'event and clk1='1' then
if (zsec1="0101" and zsec2="1001" ) then
zsec1<="0000";
elsif zsec2="1001" then
zsec1<=zsec1+1;
end if;
end if;
if (rst='0' and a3='0' and b3='0') then zsec1<="0000"; ---秒十位复位
end if;
end process s1101;
----------------------------------------------分钟个位
zm220:process(clk1,zsec1,zsec2,md1,md2,rst)
begin
if clk1'event and clk1='1' then
if zmin2="1001"and (zsec1="0101" and zsec2="1001") then----其中min2是分钟的个位
zmin2<="0000";
elsif zmin2="1001" and (md1="01" and md2="00") then---
zmin2<="0000";
else if (zsec1="0101" and zsec2="1001" ) or(md1="01" and md2="00") or(md1="01" and md2="10")then
zmin2<=zmin2+1;
end if;
end if;
end if;
if (rst='0' and a3='0' and b3='0') then zmin2<="0000";
end if;
end process zm220;
-----------------------------------------------分钟十位
zm110:process(clk1,zsec1,zsec2,md1,md2,rst)
begin
if clk1'event and clk1='1' then
if (zmin1="0101" and zmin2="1001") and (zsec1="0101" and zsec2="1001") then zmin1<="0000";
elsif zmin1="0101" and zmin2="1001"and (md1="01" and md2="00") then zmin1<="0000";
elsif (zmin2="1001" and (zsec1="0101" and zsec2="1001")) or (zmin2="1001"and md1="01" and md2="00") or (zmin2="1001" and md1="01" and md2="10") then
zmin1<=zmin1+1;
end if;
end if;
if (rst='0' and a3='0' and b3='0') then zmin1<="0000";
end if;
end process zm110;
-----------------------------------------------小时个位
zh220:process(clk1,zmin1,zmin2,zsec1,zsec2,md1,md2,zhou1,rst)
begin
if clk1'event and clk1='1' then
if (zhou1="0010" and zhou2="0011")and(zmin1="0101" and zmin2="1001") and (zsec1="0101" and zsec2="1001") then
zhou2<="0000";
elsif zhou2="1001"and(zmin1="0101" and zmin2="1001") and (zsec1="0101" and zsec2="1001") then
zhou2<="0000";
elsif (zhou2="1001")or (zhou1="0010" and zhou2="0011" and md1="01" and md2="01") then
zhou2<="0000";
elsif ((zmin1="0101" and zmin2="1001") and (zsec1="0101" and zsec2="1001") )or (md1="01" and md2="01") then
zhou2<=zhou2+1;
end if;
end if;
if (rst='0' and a3='0' and b3='0') then zhou2<="0000";
end if;
end process zh220;
-----------------------------------------------小时十位
zh110:process(clk1,zhou2,zmin1,zmin2,zsec1,zsec2,md1,md2,rst)
begin
if clk1'event and clk1='1' then
if (zhou1="0010" and zhou2="0011")and(zmin1="0101" and zmin2="1001") and (zsec1="0101" and zsec2="1001") then
zhou1<="0000";
elsif zhou1="0010"and zhou2="0011"and md1="01" and md2="01" then--当时间为23点且处于校时状态时
zhou1<="0000";
elsif (zhou2="1001"and(zmin1="0101" and zmin2="1001") and (zsec1="0101" and zsec2="1001"))or ((zhou2="1001"and md1="01" and md2="01")) then zhou1<=zhou1+1;
end if;
end if;
if (rst='0' and a3='0' and b3='0') then zhou1<="0000";
end if;
end process zh110;
-------------------------------------------时间设置小时部分
sethour1:process(clk1,seth1,seth2,rst)
begin
if clk1'event and clk1='1' then
if seth1="0010"and seth2="0011" then
seth1<="0000";
elsif seth2="1001" then
seth1<=seth1+1;
end if;
end if;
if (rst='0' and a3='0' and b3='0') then seth1<="0000";
end if;
end process sethour1;
-------------------------------------------
sethour2:process(clk1,md1,md2,seth1,rst)
begin
if clk1'event and clk1='1' then
if (seth1="0010"and seth2="0011")or seth2="1001" then ---其中seth1,seth2分别是调时的小时部位的十位与个位
seth2<="0000";
elsif md1="10" and md2="00" then
seth2<=seth2+1;
end if;
end if;
if (rst='0' and a3='0' and b3='0') then seth2<="0000";
end if;
end process sethour2;
-------------------------------------------时间设置分钟部分
setmin1:process(clk1,setm2,rst)
begin
if clk1'event and clk1='1' then
if setm1="0101"and setm2="1001" then
setm1<="0000";
elsif setm2="1001" then
setm1<=setm1+1;
end if;
end if;
if (rst='0' and a3='0' and b3='0') then setm1<="0000";
end if;
end process setmin1;
----------------------------------------------
setmin2:process(clk1,md1,md2,rst)
begin
if clk1'event and clk1='1' then
if setm2="1001" then
setm2<="0000";
elsif md1="10" and md2="01" then
setm2<=setm2+1;
end if;
end if;
if (rst='0' and a3='0' and b3='0') then setm2<="0000";
end if;
end process setmin2;
--------------------------------------------闹铃
speaker:process(clkk,zhou1,zhou2,zmin1,zmin2,rst)
begin
if clkk'event and clkk='1' then
if seth1=zhou1 and seth2=zhou2 and setm1=zmin1 and setm2=zmin2 then speak<=beep;
else speak<='0';
end if;
end if;
if (rst='0' and a3='0' and b3='0') then speak<='0';
end if;
end process speaker;
-------------------------------------------
disp:process(sel,md1,zhou1,zhou2,zmin1,zmin2,zsec2,zsec1)---显示
begin
if sel="000" then
selout<="000";--位选
case h1 is ---显示小时的十位
when "0000"=>dout<="00111111";---0
when "0001"=>dout<="00000110";---1
when "0010"=>dout<="01011011";---2
when others =>dout<="00000000";---不显示
end case;
elsif sel="001" then
selout<="001";
case h2 is ---显示小时的个位
when "0000"=>dout<="00111111";---0
when "0001"=>dout<="00000110";---1
when "0010"=>dout<="01011011";---2
when "0011"=>dout<="01001111";---3
when "0100"=>dout<="01100110";---4
when "0101"=>dout<="01101101";---5
when "0110"=>dout<="01111101";---6
when "0111"=>dout<="00000111";---7
when "1000"=>dout<="01111111";---8
when "1001"=>dout<="01101111";---9
when others=>dout<="00000000";---不显示
end case;
elsif sel="010" then
dout<="10000000";
elsif sel="011" then
selout<="011";
case m1 is ---显示分钟的十位
when "0000"=>dout<="00111111";
when "0001"=>dout<="00000110";
when "0010"=>dout<="01011011";
when "0011"=>dout<="01001111";
when "0100"=>dout<="01100110";
when "0101"=>dout<="01101101";
when others=>dout<="00000000";
end case;
elsif sel="100" then
selout<="100";
case m2 is ---显示分钟的个位
when "0000"=>dout<="00111111";
when "0001"=>dout<="00000110";
when "0010"=>dout<="01011011";
when "0011"=>dout<="01001111";
when "0100"=>dout<="01100110";
when "0101"=>dout<="01101101";
when "0110"=>dout<="01111101";
when "0111"=>dout<="00000111";
when "1000"=>dout<="01111111";
when "1001"=>dout<="01101111";
when "1010"=>dout<="10000000";
when others=>dout<="00000000";
end case;
elsif sel="101" then
selout<="101";
dout<="10000000";
elsif sel="110" then
selout<="110";
case s1 is ---显示秒的十位
when "0000"=>dout<="00111111";
when "0001"=>dout<="00000110";
when "0010"=>dout<="01011011";
when "0011"=>dout<="01001111";
when "0100"=>dout<="01100110";
when "0101"=>dout<="01101101";
when others=>dout<="00000000";
end case;
elsif sel="111" then