用VHDL语言编写的数字钟程序_基于vhdl语言的数字钟
用VHDL语言编写的数字钟程序由刀豆文库小编整理,希望给你工作、学习、生活带来方便,猜你可能喜欢“基于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(2次分频后得到2Hz的方波信号经过D触发器二分频得到秒信号供秒计数器进行计数。分频器实际上也就是计数器。时间计数电路由秒个位和秒十位计数器、分个位和分十位计数器及时个位和时十位计数器电路构成,其中秒个位和秒十位计数器、分个位和分十位计数器为60进制计数器,而根据设计要求,时个位和时十位计数器为12进制计数器。时间计数器由74LS163组成.译码驱动电路将计数器输出的8421BCD码转换为数码管需要的逻辑状态,并且为保证数码管正常工作提供足够的工作电流。数码管通常有发光二极管(LED)数码管和液晶(LCD)数码管,本设计提供的为LED数码管。
14)
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分……,保存波形图,进行仿真,产生如下波形: 7
见由上述波形可以清楚的看到: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.查看后发现由于自己的疏忽,在结束时,没有加END IF.将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版。
附件程序:
library ieee;use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;use ieee.std_logic_unsigned.all;entity clock is
port(clk:in std_logic;--12M时钟
keyin:in std_logic;
speak:out std_logic;--蜂鸣器
dout:out std_logic_vector(7 downto 0);--段码
selout:out std_logic_vector(2 downto 0));--位选 end clock;architecture one of clock is
signal count:integer range 0 to 11999999;--1HZ秒信号
signal counf:integer range 0 to 11999;--1000HZ
SIGNAL CP_5ms : STD_LOGIC;
SIGNAL Q1,Q2,Q3: STD_LOGIC;
SIGNAL keyout : STD_LOGIC;
SIGNAL CJ:STD_LOGIC_VECTOR(2 DOWNTO 0);
signal sel:std_logic_vector(2 downto 0);--位选
signal hou1:std_logic_vector(3 downto 0);--计数中小时的十位
signal hou2:std_logic_vector(3 downto 0);--小时的个位
signal min1:std_logic_vector(3 downto 0);--分钟的十位
signal min2:std_logic_vector(3 downto 0);--分钟的个位
signal sec1:std_logic_vector(3 downto 0);--秒的十位
signal sec2: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 clk1,clkk,beep:std_logic;
begin-----------------分频 fp:proce(clk)
begin
if rising_edge(clk)then
count
counf
if count=11999999 then clk1
count
beep
elsif count>5999999 then beep
else clk1
end if;
if counf=11999 then clkk
counf
else clkk
end if;
end if;end proce fp;---------------位扫描 choice:proce(clkk)---位选
扫描
begin
if rising_edge(clkk)then
if sel=“111” then
sel
else
sel
end if;
end if;end proce choice;-------------秒个位 s220:proce(clk1,cj)
begin
if clk1'event and clk1='1' then
if sec2=“1001” then
---其中sec2是秒的个位
sec2
elsif cj=“010” then
sec2
else sec2
end if;
end if;if cj=“001” then sec2
end if;end proce s220;--------------秒十位 s110:proce(clk1,cj)
begin
if clk1'event and clk1='1' then
if(sec1=“0101” and sec2=“1001”)then---其中sec1是秒钟的十位
sec1
elsif cj=“010” then
sec1
else if sec2=“1001”then
sec1
end if;
end if;end if;if cj=“001” then sec1
end if;end proce s110;---------------分钟个位 m220:proce(clk1,sec1,sec2,cj)
begin
if clk1'event and clk1='1' then
if min2=“1001”and(sec1=“0101” and sec2=“1001”)then----其中min2是分钟的个位
min2
elsif min2=“1001”and(cj=“011” and cj=“100”)then---
min2
else if(sec1=“0101” and sec2=“1001”)or(cj=“011” and cj=“100”)then
min2
end if;
end if;end if;if cj=“001” then min2
end if;
end proce m220;----------------分钟十位 m110:proce(clk1,min2,sec1,sec2,cj)
begin
if clk1'event and clk1='1' then
if(min1=“0101” and min2=“1001”)and(sec1=“0101” and sec2=“1001”)then
min1
elsif min1=“0101”and min2=“1001”and(cj=“011” and cj=100)then
min1
elsif(min2=“1001”and(sec1=“0101” and sec2=“1001”))or(min2=“1001”and cj=“011” and cj=“100”)then
min1
end if;
end if;--end if;if cj=“001” then min1
end if;end proce m110;----------------小时个位
h220:proce(clk1,min1,min2,sec1,sec2,cj,hou1)
begin
if clk1'event and clk1='1' then
if(hou1=“0010” and hou2=“0011”)and(min1=“0101” and min2=“1001”)and(sec1=“0101” and sec2=“1001”)then
hou2
elsif hou2=“1001”and(min1=“0101” and min2=“1001”)and(sec1=“0101” and sec2=“1001”)then
hou2
elsif(hou2=“1001”and cj=“110”)or(hou1=“0010”and hou2=“0011”and cj=“110”)then
hou2
elsif((min1=“0101” and min2=“1001”)and(sec1=“0101” and sec2=“1001”))or(cj=“110”)then
hou2
end if;
end if;if cj=“001” then hou2
end if;end proce h220;----------------小时十位 h110:proce(clk1,hou2,min1,min2,sec1,sec2,cj)
begin
if clk1'event and clk1='1' then
if(hou1=“0010” and hou2=“0011”)and(min1=“0101” and min2=“1001”)and(sec1=“0101” and sec2=“1001”)then
hou1
elsif hou1=“0010”and hou2=“0011”and cj=“110” then--当时间为23点且处于校时状态时
hou1
elsif(hou2=“1001”and(min1=“0101” and min2=“1001”)and(sec1=“0101” and sec2=“1001”))or(hou2=“1001”and cj=“110”)then
hou1
end if;
end if;if cj=“001” then hou1
end if;end proce h110;------------时间设置小时部分 sethour1:proce(clk1,seth1,seth2,cj)
begin
if clk1'event and clk1='1' then
if seth1=“0010”and seth2=“0011” then
seth1
elsif seth2=“1001” then
seth1
end if;
end if;if cj=“001” then seth1
end if;end proce sethour1;------------sethour2:proce(clk1,seth1,cj)
begin
if clk1'event and clk1='1' then
if(seth1=“0010”and seth2=“0011”)or seth2=“1001” then
---其中seth1,seth2分别是调时的小时部位的十位与个位
seth2
elsif cj=“111” then
seth2
end if;
end if;if cj=“001” then seth2
end if;end proce sethour2;------------时间设置分钟部分 setmin1:proce(clk1,setm2,cj)
begin
if clk1'event and clk1='1' then
if setm1=“0101”and setm2=“1001” then
setm1
elsif setm2=“1001” then
setm1
end if;
end if;if cj=“001” then setm1
end if;end proce setmin1;---------------setmin2:proce(clk1,cj)
begin
if clk1'event and clk1='1' then
if setm2=“1001” then
setm2
elsif cj=“101” then
setm2
end if;
end if;if cj=“001” then setm2
end if;end proce setmin2;-------------闹铃
speaker:proce(clkk,hou1,hou2,min1,min2,cj)
begin
if clkk'event and clkk='1'then
if seth1=hou1 and seth2=hou2 and setm1=min1 and setm2=min2 then
speak
else speak
end if;
end if;if cj=“001” then speak
end if;end proce speaker;------------disp:proce(sel,cj,hou1,hou2,min1,min2,sec1,sec2,seth1,seth2,setm1,setm2)---显示
begin
if sel=“010” then
selout
case h1 is
---显示小时的十位
when “0000”=>dout
when “0001”=>dout
when “0010”=>dout
when others =>dout
end case;
elsif sel=“011” then
selout
case h2 is
---显示小时的个位
when “0000”=>dout
when “0001”=>dout
when “0010”=>dout
when “0011”=>dout
when “0100”=>dout
when “0101”=>dout
when “0110”=>dout
when “0111”=>dout
when “1000”=>dout
when “1001”=>dout
when others=>dout
end case;
elsif sel=“100” then
selout
case m1 is
---显示分钟的十位
when “0000”=>dout
when “0001”=>dout
when “0010”=>dout
when “0011”=>dout
when “0100”=>dout
when “0101”=>dout
when others=>dout
end case;
elsif sel=“101” then
selout
case m2 is
---显示分钟的个位
when “0000”=>dout
when “0001”=>dout
when “0010”=>dout
when “0011”=>dout
when “0100”=>dout
when “0101”=>dout
when “0110”=>dout
when “0111”=>dout
when “1000”=>dout
when “1001”=>dout
when others=>dout
end case;
elsif sel=“110” then
selout
case s1 is
---显示秒的十位
when “0000”=>dout
when “0001”=>dout
when “0010”=>dout
when “0011”=>dout
when “0100”=>dout
when “0101”=>dout
when others=>dout
end case;
elsif sel=“111” then
selout
case s2 is
---显示秒的个位
when “0000”=>dout
when “0001”=>dout
when “0010”=>dout
when “0011”=>dout
when “0100”=>dout
when “0101”=>dout
when “0110”=>dout
when “0111”=>dout
when “1000”=>dout
when “1001”=>dout
when others=>dout
end case;
end if;
if cj=“011” then---------------计时时间显示和设置模式
h1
m1
s1
else
-----------闹铃时间现实和设置模式
h1
m1
s1
end if;end proce disp;--------------------------yanshi:proce(clk)variable fp:integer range 0 to 29999;begin if clk'event and clk='1' then if fp=29999 then fp:=0;
CP_5ms
fp:=fp+1;end if;end if;end proce;------------------quout:proce(CP_5ms,keyin)begin if CP_5ms'event and CP_5ms='1' then Q1
else CJ
one;