用VHDL语言编写的数字钟程序.docx
《用VHDL语言编写的数字钟程序.docx》由会员分享,可在线阅读,更多相关《用VHDL语言编写的数字钟程序.docx(25页珍藏版)》请在冰点文库上搜索。
用VHDL语言编写的数字钟程序
永州职业技术学院
课
程
设
计
课程名称:
EDA技术实用教程
题目:
基于FPGA的数字钟设计
系、专业:
电子技术系应用电子
年级、班级:
07级电子大专
**********************
*********************
时间:
2008年12月
一、系统设计………………………………………………………..
1.1设计要求……………………………………………………
1.1.1任务………………………………………………..
1.1.2要求……………………………………………….
1.1.3题目分析…………………………………………
二.方案论证与比较…………………………………
2.1方案一…………………………………………
2.2方案二…………………………………………
2.3方案三…………………………………………
三、设计思路……………………………………………………
3.1硬件模块………………………………………………
3.2软件模块………………………………………………….
四、调试情况………………………………………………….
五、系统调试…………………………………………………
六、心得体会……………………………………………………...
附:
参考文献……………………………………………………..
用VHDL语言编写的数字钟程序
摘要:
本设计要求一个12进制或24进制的具有时、分、秒计时功能的数字钟,并要求能进行时、分、秒调整,每逢时有报时功能。
数字钟是一种用数字电路技术实现时、分、秒计时的装置,与机械式时钟相比具有更高的准确性和直观性,且无机械装置,具有更更长的使用寿命,因此得到了广泛的使用。
本设计基于FPGA芯片的数字钟的设计,通过多功能数字钟的设计思路,详细叙述了整个系统的硬件、软件实现过程,实现了时间的显示和修改功能、报时功能等,并尽可能的减少误差,使得系统可以达到实际数字钟的允许误差范围内。
关键词:
FBGA、数码管、按键
一、系统设计
1.1设计要求
1.1.1任务
设计并制作一个数字钟,通过设计,掌握电子设计的一般思路,学习电子设计的一般方法。
1.1.2要求
(1)基本要求
①计时功能:
这是数字钟的基本功能,每隔一秒钟计时一次,并在显示屏上显示当前时间。
②校时功能:
能设置实时时间作为数字钟的当前时间,具有小时、分钟的手动校准时间功能。
(2)发挥部分
①计时进制的选择功能:
十二小时制或二十四小时制可选择控制;
②整点报警功能:
每逢整点自动报警;
③其他创新功能。
二.方案论证与比较
2.1、方案一:
采用74LS163和CD4046设计数字钟
图1.1.1方案一电路图
晶体振荡器电路给数字钟提供一个频率稳定准确的32768Hz的方波信号,可保证数字钟的走时准确及稳定。
不管是指针式的电子钟还是数字显示的电子钟都使用了晶体振荡器电路。
由CD4046组成的分频器电路将32768Hz的高频方波信号经32768(214)次分频后得到2Hz的方波信号经过D触发器二分频得到秒信号供秒计数器进行计数。
分频器实际上也就是计数器。
时间计数电路由秒个位和秒十位计数器、分个位和分十位计数器及时个位和时十位计数器电路构成,其中秒个位和秒十位计数器、分个位和分十位计数器为60进制计数器,而根据设计要求,时个位和时十位计数器为12进制计数器。
时间计数器由74LS163组成.
译码驱动电路将计数器输出的8421BCD码转换为数码管需要的逻辑状态,并且为保证数码管正常工作提供足够的工作电流。
数码管通常有发光二极管(LED)数码管和液晶(LCD)数码管,本设计提供的为LED数码管。
2、方案二:
采用AT89C52单片机、数码管设计数字钟
图1.1.2方案二电路图
本方案采用AT89C52单片机,单片机的P1口接数码管显示电路,P0口接键控制数码管的显示,P2.0口接入整点报时电路,RESET接入复位和晶振电路。
该电路能否成功,关键在于程序的编写而对元器件的要求不太高。
用汇编编写得数字钟电路,采用分支结构编写,利用跳转指令与大量的中断指令.当没有按键扫描没有按键按下时,程序正常计数,当检测到有键按下时,程序运转到相应中断程序进行响应处理.从而实现了分支程序的处理.
方案三:
采用FPGA制成的数字钟
图1.1.3方案三电路图
市电经过降压电路为FPGA芯片提供3.3V的直流电压,12MHZ的晶振信号加入到FPGA芯片的内定义PORT,切换键、调时键,输出的8个四位的BCD码,经过数据选择器使得某一位信号的BCD码被选中,被选中的信号经过3/8译码电路,送到数码管的进行段码显示.而数码管的位选端也有内部软件编程实现位选.而由时、分,信号为蜂鸣器提供闹钟信号.并口端为程序输入端.
三.设计思路
3.1硬件模块
采用ALTER公司的ACE×1K系列的EP1K10TC100-3芯片,通过Quartus软件编译各个管脚的功能及特性.接入12MHZ时钟信号,经过内部软件分频.得到1HZ和1000HZ信号,得到的1HZ时钟信号作为内部秒个位计数模块的时钟信号,秒个位进行十进制计数,到9进位,为秒十位提供6进制时钟信号,当秒为59时,为分钟提供时钟信号.当秒进位信号作为低位十进制分计数器时钟.分钟计数器为59时,为小时计数器的个位提供时钟信号,当小时计数器、分钟计数器输出信号与闹钟预设信号一样时,扬声器发声.输出信号经过数据选择器选择1位BCD码输出到译码电路,译码后送到数码管显示.位选择信号为数码管显示提供条件.而分频得到的1KHZ的时钟频率作为位选计数器的时钟信号.由于该方案简单而且可靠性高,故采用此种方案.
该数字钟可以实现3个功能:
计时功能、整点报时功能和重置时间功能,因此有3个子模块:
计时、报时(alarm1)、重置时间(s1、m1、h1、d1)。
其中计时模块有4部分构成:
秒计时器(second1)、分计时器(minute1)、时计时器(hour1)和星期计时器(day1)。
该数字钟可以实现3个功能:
计时功能、整点报时功能和重置时间功能,因此有3个子模块:
计时、报时(alarm1)、重置时间(s1、m1、h1、d1)。
其中计时模块有4部分构成:
秒计时器(second1)、分计时器(minute1)、时计时器(hour1)和星期计时器(day1)。
3.2软件模块
1)、分频器模块
其实是一个计数器,外加信号频率为12MHZ时,在内部定义了一个信号从0到11999999的整数型计数信号count1,当内部信号计数为11999999时,count1计数为0,产生进位信号CLK1为1,其余时间计数器正常计数.CLK为0,从而实现了12000000次分频.得到1HZ的时钟信号.内部定义了一个从0到11999的整数性信号countf,当内部信号计数为11999时,countf计数为0,产生进位信号CLK1为1,其余时间计数器正常计数.CLK为0,从而实现了12000次分频.得到1KHZ的时钟信号的位选信号.
2)、秒计数模块
内部定义时钟其余时刻均为为秒计时器(second1)是由一个60进制的计数器构成的,具有清0、置数和计数功能。
其中reset为清0信号,当reset为0时,秒计时器清0;set为置数信号,当set为0时,秒计时器置数,置s1的值。
clk为驱动秒计时器的时钟,sec为秒计时器的输出,ensec为秒计时器的进位信号,作为下一级的时钟输入信号。
3)、分计时器(minute1)是由一个60进制的计数器构成的,具有清0、置数和计数功能。
其中reset为清0信号,当reset为0时,分计时器清0;set为置数信号,当set为0时,分计时器置数,置m1的值。
clkm为驱动分计时器工作的时钟,与ensec相连接;min为分计时器的输出;enmin为分计时器的进位信号,作为下一级的时钟输入信号。
4)、时计时器(hour1)模块
是由一个24进制的计数器构成的,具有清0、置数和计数功能。
其中reset为清0信号,当reset为0时,时计时器清0;set为置数信号,当set为0时,时计时器置数,置h1的值。
clkh为驱动时计时器工作的时钟,与enmin相连接;hour为时计时器的输出;enhour为时计时器的进位信号,作为下一级的时钟输入信号。
图7
5)、报时模块(alarm1)的功能是当整点(将min作为该模块的输入信号,min=00)时,alarm输出高电平,并且持续1分钟。
清0端(reset)前面一小段(200ns)为低电平,后面均为高电平;设置min的值,使其分别为……58分、59分、00分、01分、02分、03分……,保存波形图,进行仿真,产生如下波形:
见由上述波形可以清楚的看到:
alarm在0分时输出高电平,并且持续至1min不为0。
6)、去抖模块
定义变量fb范围0到29999,当外部时钟信号来临时候,fp计数,只有当FP为29999时,fp赋值为0.内部5ms信号反向输出.输出5ms时钟信号,该信号送到内部的D触发器中,只有按键按下且5ms时钟信号来临时,按键信号才被送到触发器.从而实现了去抖.
7)、系统总调试(topclock)(Endtime为10us
在秒计时器的clk输入一个周期为5ns的时钟信号;清0端(reset)前面一小段(40ns)为低电平,后面均为高电平;置数端(set)前面一小段(60ns)为低电平,后面均为高电平;秒重置端(s1)可设置数值为50秒,分重置端(m1)可设置数值为57分,时重置端(h1)可设置数值为23时,星期重置端(d1)可设置数值为6(星期六);保存波形图,进行仿真,产生如下波形”
图8
由上述波形可以清楚的看到:
当reset为0时,数字钟清0;当set为1时,数字钟置数
图9由上述波形可以清楚的看到:
秒计时器开始计时,当到达59秒后,秒计时器sec又从0开始计时,同时分钟min加了1,为58分。
由上述波形可以清楚的看到:
分计时器开始计时,当到达59分后,分计时器min又从0开始计时,同时小时hour加了1,为24时,即时计时器hour也又从0开始计时,而此时星期计时器day也由6加1后回0,又从0开始计时。
当分计时器min为0时,alarm输出一个高电平,持续直到分计时器min的值为1。
图10
8)、用经过5ms去抖程序后,使得keyout输出一个脉冲,将此信号作为按键代码计数电路.为每一种按键代码赋已一定功能.从而实现一键控制.
程序流程图如下:
详细程序见附录。
程序控制过程如下说明:
四、产品调试与结果分析
1)书写遗漏
错误提示在305行和307行附近有一个错误,没有加IF.查看后发现由于自己的疏忽,在结束时,没有加ENDIF.将307行中加入一行结束语句,问题解决.
2)数码管显示时有闪烁
检查扫描信号时钟时,发现扫描信号时钟为100HZ,扫描频率过低,引起闪烁.
检查分频部分程序,发现扫描信号频率设为100HZ,将扫描信号频率改为1000KHZ.
然后发现无闪烁.修改有效.
3)数码管秒个位显示时无2、8字符
检查后发现秒个位译码部分字符书写错误.将秒个位字符2、8赋值错误修改观察,有显示.
五、心得体会
转眼一学期就要悄悄走远,感觉这些天是我学的最多的.通过这次设计,进一步加深了对EDA的了解,让我对它有了更加浓厚的兴趣。
特别是当每一个子模块编写调试成功时,心里特别的开心。
但是在编写顶层文件的程序时,遇到了不少问题,特别是各元件之间的连接,以及信号的定义,总是有错误,在细心的检查下,终于找出了错误和警告,排除困难后,程序编译就通过了,心里终于舒了一口气。
在波形仿真时,也遇到了一点困难,想要的结果不能在波形上得到正确的显示:
在设定输入的时钟信号后,数字钟开始计数,但是始终看不到小时、星期的循环计数。
后来,在数十次的调试之后,才发现是因为输入的时钟信号对于小时、星期来说太短了。
经过屡次调试,终于找到了比较合适的输入数值:
分钟的初始值可以设为57(58、59都可以),小时的初始值可以设为23,星期的初始值可以设为6,这样,仿真之后,就能清楚的看出分钟、小时的循环计数。
另外,Endtime的值需要设置的长一点:
10us左右,输入的时钟周期值要设置的短一点:
5ns左右。
总的来说,这次设计的数字钟还是比较成功的,有点小小的成就感,终于觉得平时所学的知识有了实用的价值,达到了理论与实际相结合的目的,不仅学到了不少知识,而且锻炼了自己的能力,使自己对以后的路有了更加清楚的认识,同时,对未来有了更多的信心。
附:
参考文献
1、黄仁欣主编《EDA技术实训教程》,清华大学出版社,2006年第2版。
2、杨志忠主编《数字电子技术》,北京高等教育出版社,2003年12第2版。
3、潘松、黄继业主编《单片机实训教程》,科学出版社,2005年5月第2版。
附件程序:
libraryieee;
useieee.std_logic_1164.all;
useieee.std_logic_arith.all;
useieee.std_logic_unsigned.all;
entityclockis
port(clk:
instd_logic;--12M时钟
keyin:
instd_logic;
speak:
outstd_logic;--蜂鸣器
dout:
outstd_logic_vector(7downto0);--段码
selout:
outstd_logic_vector(2downto0));--位选
endclock;
architectureoneofclockis
signalcount:
integerrange0to11999999;--1HZ秒信号
signalcounf:
integerrange0to11999;--1000HZ
SIGNALCP_5ms:
STD_LOGIC;
SIGNALQ1,Q2,Q3:
STD_LOGIC;
SIGNALkeyout:
STD_LOGIC;
SIGNALCJ:
STD_LOGIC_VECTOR(2DOWNTO0);
signalsel:
std_logic_vector(2downto0);--位选
signalhou1:
std_logic_vector(3downto0);--计数中小时的十位
signalhou2:
std_logic_vector(3downto0);--小时的个位
signalmin1:
std_logic_vector(3downto0);--分钟的十位
signalmin2:
std_logic_vector(3downto0);--分钟的个位
signalsec1:
std_logic_vector(3downto0);--秒的十位
signalsec2:
std_logic_vector(3downto0);--秒的个位
signalseth1:
std_logic_vector(3downto0);--设时中小时的十位
signalseth2:
std_logic_vector(3downto0);--小时的个位
signalsetm1:
std_logic_vector(3downto0);--分钟的十位
signalsetm2:
std_logic_vector(3downto0);--分钟的个位
signalh1:
std_logic_vector(3downto0);---显示小时十位
signalh2:
std_logic_vector(3downto0);---小时的个位
signalm1:
std_logic_vector(3downto0);--分钟的十位
signalm2:
std_logic_vector(3downto0);--分钟的个位
signals1:
std_logic_vector(3downto0);--秒的十位
signals2:
std_logic_vector(3downto0);--秒的个位
signalclk1,clkk,beep:
std_logic;
begin
------------------------------------------------分频
fp:
process(clk)
begin
ifrising_edge(clk)then
count<=count+1;
counf<=counf+1;
ifcount=11999999thenclk1<='1';---1Hz
count<=0;
beep<='1';
elsifcount>5999999thenbeep<='0';---2Hz
elseclk1<='0';
endif;
ifcounf=11999thenclkk<='1';--1000HZ
counf<=0;
elseclkk<='0';
endif;
endif;
endprocessfp;
----------------------------------------------位扫描
choice:
process(clkk)---位选扫描
begin
ifrising_edge(clkk)then
ifsel="111"then
sel<="000";
else
sel<=sel+1;
endif;
endif;
endprocesschoice;
--------------------------------------------秒个位
s220:
process(clk1,cj)
begin
ifclk1'eventandclk1='1'then
ifsec2="1001"then---其中sec2是秒的个位
sec2<="0000";
elsifcj="010"then
sec2<=sec2;
elsesec2<=sec2+1;
endif;
endif;
ifcj="001"thensec2<="0000";---秒个位复位
endif;
endprocesss220;
---------------------------------------------秒十位
s110:
process(clk1,cj)
begin
ifclk1'eventandclk1='1'then
if(sec1="0101"andsec2="1001")then---其中sec1是秒钟的十位
sec1<="0000";
elsifcj="010"then
sec1<=sec1;
elseifsec2="1001"then
sec1<=sec1+1;
endif;
endif;
endif;
ifcj="001"thensec1<="0000";---秒十位复位
endif;
endprocesss110;
----------------------------------------------分钟个位
m220:
process(clk1,sec1,sec2,cj)
begin
ifclk1'eventandclk1='1'then
ifmin2="1001"and(sec1="0101"andsec2="1001")then----其中min2是分钟的个位
min2<="0000";
elsifmin2="1001"and(cj="011"andcj="100")then---
min2<="0000";
elseif(sec1="0101"andsec2="1001")or(cj="011"andcj="100")then
min2<=min2+1;
endif;
endif;
endif;
ifcj="001"thenmin2<="0000";
endif;
endprocessm220;
-----------------------------------------------分钟十位
m110:
process(clk1,min2,sec1,sec2,cj)
begin
ifclk1'eventandclk1='1'then
if(min1="0101"andmin2="1001")and(sec1="0101"andsec2="1001")then
min1<="0000";
elsifmin1="0101"andmin2="1001"and(cj="011"andcj=100)then
min1<="0000";
elsif(min2="1001"and(sec1="0101"andsec2="1001"))or(min2="1001"andcj="011"andcj="100")then
min1<=min1+1;
endif;
endif;--endif;
ifcj="001"thenmin1<="0000";
endif;
endprocessm110;
-----------------------------------------------小时个位
h220:
process(clk1,min1,min2,sec1,sec2,cj,hou1)
begin
ifclk1'eventandclk1='1'then
if(hou1="0010"andhou2="0011")and(min1="0101"andmin2="1001")and(sec1="0101"andsec2="1001")then
hou2<="0000";
elsifhou2="1001"and(min1="0101"andmin2="1001")and(sec1="0101"andsec2="1001")then
hou2<="0000";
elsif(hou2="1001"andcj="110")or(hou1="0010"andhou2="0011"andcj="110")then
hou2<="0000";--md<='1';
elsif((min1="0101"andmin2="1001")and(sec1="0101"andsec2="1001"))or(cj="110")then
hou2<=hou2+1;--speak<=clk;
endif;
endif;
ifcj="001"thenhou2<="0000";
endif;
endprocessh220;
---------------------------------------