电子钟的设计.docx
《电子钟的设计.docx》由会员分享,可在线阅读,更多相关《电子钟的设计.docx(24页珍藏版)》请在冰豆网上搜索。
电子钟的设计
电子钟设计
一、简介或背景
在这个时间就是金钱的年代里,数字电子钟已成为人们生活中的必需品。
目前应用的数字钟不仅可以实现对年、月、日、时、分、秒的数字显示,还能实现对电子钟所在地点的温度显示和智能闹钟功能,广泛应用于车站、医院、机场、码头、厕所等公共场所的时间显示。
二十一世纪的今天,最具代表性的计时产品就是电子时钟,它是近代世界钟表业界的第三次革命。
第一次是摆和摆轮游丝的发明,相对稳定的机械振荡频率源使钟表的走时差从分级缩小到秒级,代表性的产品就是带有摆或摆轮游丝的机械钟或表。
第二次革命是石英晶体振荡器的应用,发明了走时精度更高的石英电子钟表,使钟表的走时月差从分级缩小到秒级。
第三次革命就是单片机数码计时技术的应用,使计时产品的走时日差从分级缩小到1/600万秒,从原有传统指针计时的方式发展为人们日常更为熟悉的夜光数字显示方式,直观明了,并增加了全自动日期、星期的显示功能,它更符合消费者的生活需求!
因此,电子时钟的出现带来了钟表计时业界跨跃性的进步。
我国生产的电子时钟有很多种,总体上来说以研究多功能电子时钟为主,使电子时钟除了原有的显示时间基本功能外,还具有闹铃,报警等功能。
商家生产的电子时钟更从质量,价格,实用上考虑,不断的改进电子时钟的设计,使其更加的具有市场。
二、系统功能
本设计为一个多功能的数字时钟,具有时、分、秒计数显示功能,以24小时循环计数;具有校对功能。
以硬件描述语言VerilogHDL为系统逻辑描述语言设计文件,在QUARTUSII工具软件环境下,采用自顶向下的设计方法,由各个基本模块共同构建了一个基于FPGA的数字钟。
系统由时钟模块、控制模块、计时模块、数据译码模块、显示以及蜂鸣器模块组成。
经编译和仿真所设计的程序,本系统能够完成时、分、秒的分别显示,整点报时,闹钟功能。
图2.1整体模块框图
针对框图流程,设定出各个模块的需求:
1、分频电路:
针对计时器模块与闹钟设定模块的需求,可以知道分频模块需要生成一个1Hz的频率信号,确保计时模块可以正常计数。
2、计时器模块:
计数模块的作用是收到分频模块1Hz频率的信号线,能进行正确计时,并且可以通过按键进行时间的修改,且当整点时,给蜂鸣器产生使能信号,进行整点报时。
3、闹钟设定模块:
可根据按键的设定闹钟的时间,当计时模块的时间与闹钟设定模块的时间相等的时候,给蜂鸣器一个使能信号,蜂鸣器闹铃。
4、蜂鸣器模块:
根据计时模块,闹钟模块给出的使能信号,判定蜂鸣器是整点报时,还是闹钟响铃。
整点报时会播放音乐,闹钟时嘀嘀嘀报警。
5、LED显示模块:
根据实际的需求显示计时模块的时间,还是闹钟设定模块的时间,8个七段码LED数码管,进行扫描方式显示数据。
三、各个功能模块的具体实现及仿真结果
3.1分频模块儿实现
3.1.1分频模块儿设计
本系统程序设计时钟的准确与否主要取决于秒脉冲的精确度。
为了保证计时准确,我们对系统时钟48MHz进行了48000分频生成1kHz信号clk1,在通过1kHz信号,生成1Hz信号clk。
//1ms信号产生部分
always@(posedgeclk)//定义clock上升沿触发
begin
count=count+1'b1;
if(count==15'd24000)//0.5mS到了吗?
begin
count=15'd0;//计数器清零
clk1=~clk1;//置位秒标志
end
end
//秒信号产生部分
always@(posedgeclk1)//定义clock上升沿触发
begin
count1=count1+1'b1;
if(count1==9'd500)//0.5S到了吗?
begin
count1=9'd0;//计数器清零
sec=~sec;//置位秒标志
end
End
3.1.2分频模块儿仿真
通过设置功能仿真,检查代码的正确性
1、仿真结果
图3.1分频模块波形仿真图
右上图可以知道,计数寄存器count累加到23999时,重新变为0,共计数了24000个值。
触发clk1跳变,使得count1加一,count1累加到499的时候,下一个数据为0,共技术500个值。
所以,sec信号的频率为1Hz,满足设计要求。
3.2计时模块儿实现
3.2.1计时模块儿设计
计时模块是采用16进制来实现的,将hour[23,0]定义为其时分秒,其中hour[3,0]为其秒钟上的个位数值,hour[4,7]为其秒钟上的十位数值,以此类推分钟、时钟的个位和十位。
当clk脉冲过来时,秒个位hour[3,0]便开始加1,当加到9时,秒十位加1,与此同时秒个位清零,继续加1。
当秒十位hour[7,4]为5秒个位为9时(即59秒),分个位hour[11,8]加1,与此同时秒个位和秒十位都清零。
以此类推,当分十位hour[15,12]为5和分个位为9时(即59分),时个位加1,与此同时分个位hour[19,16]和分十位都清零。
当时分十位[23,20]为2和分个位为4,全部清零,开始重新计时。
从功能上讲分别为模60计数器,模60计数器和模24计数器。
//时间计算及校准部分
always@(negedgesec)//计时处理
begin
hour[3:
0]=hour[3:
0]+1'b1;//秒加1
if(hour[3:
0]>=4'ha)//加到10,复位
begin
hour[3:
0]=4'h0;
hour[7:
4]=hour[7:
4]+1'b1;//秒的十位加一
if(hour[7:
4]>=4'h6)//加到6,复位
begin
hour[7:
4]=4'h0;
hour[11:
8]=hour[11:
8]+1'b1;//分个位加一
if(hour[11:
8]>=4'ha)//加到10,复位
begin
hour[11:
8]=4'h0;
hour[15:
12]=hour[15:
12]+1'b1;//分十位加一
if(hour[15:
12]>=4'h6)//加到6,复位
begin
hour[15:
12]=4'h0;
hour[19:
16]=hour[19:
16]+1'b1;//时个位加一
if(hour[19:
16]>=4'ha)//加到10,复位
begin
hour[19:
16]=4'h0;
hour[23:
20]=hour[23:
20]+1'b1;//时十位加一
end
if(hour[23:
16]>=8'h24)//加到24,复位
hour[23:
16]=8'h0;
end
end
end
end
end
end
3.2.2计时模块儿仿真
对计时模块进行仿真,记录仿真波形
图3.2计时模块仿真图
由上图可见,当sec信号下降沿跳变时,hour寄出去会加1,也就相当于跳了一秒钟时间。
当hour的时间为235959是,下一个计数器的值为000000,hour寄存器归零,相当于半夜0点的时刻。
仿真的结果达到预期,通过。
3.3按键处理模块儿实现及仿真
框图如下图4.4:
图3.3按键控制功能图
模块讲计时部分和时间调整部分整合到一起,正常态的时候,时间正常运行,当key[5]被按下时,进入时间校准,可以通过key[2:
0]三个键,分别对秒,分,时进行加1操作,从而进行时间校准。
当key[3]被按下时,进入闹钟设定,可以通过key[2:
0]三个键,分别对秒,分,时进行加1操作,从而进行闹钟的设定。
图3.4按键模块仿真图
通过按键key进行仿真控制,可以发现clktime会随着按键的按下,分别有时钟,分钟秒钟加1,仿真结果满足设计要求。
3.4闹钟模块儿实现
3.4.1闹钟模块儿设计
本设计中,判断闹铃时间到,是通过判定时钟系统实时时间的时钟与分钟是否分别等于设定的闹铃时间的时钟、分钟、秒钟。
当时间(hour[23:
0])等于设定的闹钟时间(clktime[23:
0])时,闹钟触发时,播放嘀嘀嘀报警声,闹钟会响10秒的时间(clktime[23:
0]+10>=hour[23:
0])。
正常情况下,闹铃时间到会进行为时1分钟的蜂鸣报时,可以通过按下闹钟按键key[3]使其停止。
当闹铃设置为整点是,会先进行整点报时,然后进入闹铃。
图3.5闹钟控制键功能图
3.4.2闹钟模块儿仿真
图3.6闹钟模块仿真图
通过按键key进行仿真控制,可以发现clktime会随着按键的按下,分别有时钟,分钟秒钟加1,仿真结果满足设计要求。
3.5蜂鸣器模块儿实现
3.5.1蜂鸣器模块儿设计
蜂鸣器模块负责整点报时,和闹铃的时候进行出声的作用。
整点报时的时候,播放音乐,10秒音乐播报完后停止整点报时。
闹钟触发时,播放嘀嘀嘀报警声。
当闹铃设置为整点是,会先进行整点报时,然后进入闹铃。
当闹钟设定键被按下,响起的蜂鸣声会被屏蔽。
//蜂鸣器的计数定时器
always@(posedgeclk)
begin
beep_count=beep_count+1'b1;//计数器加1
if((beep_count==beep_count_end)&&(!
(beep_count_end==16'hffff)))
begin
beep_count=16'h0;//计数器清零
beep_r=~beep_r;//取反输出信号
end
end
always@(posedgeclk)
begin
if(!
beepen)
case(hour[3:
0])//整点报时音乐内容
4'h0:
beep_count_end=16'h6a88;//中音6的分频系数值
4'h1:
beep_count_end=16'h8637;//中音4的分频系数值
4'h2:
beep_count_end=16'h7794;//中音5的分频系数值
4'h3:
beep_count_end=16'hb327;//中音1的分频系数值
4'h5:
beep_count_end=16'hb327;//中音1的分频系数值
4'h6:
beep_count_end=16'h7794;//中音5的分频系数值
4'h7:
beep_count_end=16'h6a88;//中音6的分频系数值
4'h8:
beep_count_end=16'h8637;//中音4的分频系数值
default:
beep_count_end=16'hffff;//其他情况无声
endcase
elseif(!
clktime_en)
begin
case(count1[8:
5])//闹钟嘀嘀嘀声内容
4'h0,4'h2,4'h6,4'h8:
beep_count_end=16'h2f74;//高音7的分频系数值
default:
beep_count_end=16'hffff;//其他情况不出声
endcase
end
else
beep_count_end=16'hffff;
end
//闹铃使能控制
always@(posedgeclk)
begin
if(!
keyen[0])//判断闹铃是否有取消
clktime_en=1'b1;//闹铃响起后,需要手动关闭闹铃
elseif((clktime[23:
0]<=hour[23:
0])&(clktime[23:
0]+10>=hour[23:
0]))
//闹铃过一点时间,自动关闭。
clktime_en=1'b0;
end
3.5.2蜂鸣器模块儿仿真
功能仿真,记录波形图:
图3.7蜂鸣器模块仿真图
通过上图可以看出来,当hour与clktime相等时,闹铃被触发,经过一段时间后,闹铃停止工作,设计满足要求。
3.6显示模块儿实现
3.6.1显示模块儿设计
此设计中的LED七段数码管显示模块主要显示时间的时、分、秒信息,数码管为共阳的。
在此设计中占非常重要的地位,它是确保时间能直观呈现的桥梁。
在设计过程中,首先进行程序编写和调试的应该是显示模块。
下面输入的端口为闹钟设定键被按下,七段数码管会显示闹钟设定情况下数码管所对应的数字。
正常时间情况、闹钟设定以及查看闹钟所设定好的时间都是同样的原理,当他们被按下数码管会显示对应的模式相应的数字。
图3.8显示模块图
//数码管显示内容
always@(posedgeclk)
begin
case({keyen[0],count1[3:
1]})//选择扫描显示数据
4'd0:
disp_dat=clktime[3:
0];//秒个位
4'd1:
disp_dat=clktime[7:
4];//秒十位
4'd2:
disp_dat=4'ha;//显示"-"
4'd3:
disp_dat=clktime[11:
8];//分个位
4'd4:
disp_dat=clktime[15:
12];//分十位
4'd5:
disp_dat=4'ha;//显示"-"
4'd6:
disp_dat=clktime[19:
16];//时个位
4'd7:
disp_dat=clktime[23:
20];//时十位
4'd8:
disp_dat=hour[3:
0];//秒个位
4'd9:
disp_dat=hour[7:
4];//秒十位
4'd10:
disp_dat=4'ha;//显示"-"
4'd11:
disp_dat=hour[11:
8];//分个位
4'd12:
disp_dat=hour[15:
12];//分十位
4'd13:
disp_dat=4'ha;//显示"-"
4'd14:
disp_dat=hour[19:
16];//时个位
4'd15:
disp_dat=hour[23:
20];//时十位
default:
disp_dat=4'ha;//显示"-"
endcase
//数码管选择
case(count1[3:
1])//选择数码管显示位
3'd0:
dig_r=8'b11111110;//选择第一个数码管显示
3'd1:
dig_r=8'b11111101;//选择第二个数码管显示
3'd2:
dig_r=8'b11111011;//选择第三个数码管显示
3'd3:
dig_r=8'b11110111;//选择第四个数码管显示
3'd4:
dig_r=8'b11101111;//选择第五个数码管显示
3'd5:
dig_r=8'b11011111;//选择第六个数码管显示
3'd6:
dig_r=8'b10111111;//选择第七个数码管显示
3'd7:
dig_r=8'b01111111;//选择第八个数码管显示
endcase
end
//数码管显示
always@(posedgeclk)
begin
case(disp_dat)
4'h0:
seg_r=8'hc0;//显示0
4'h1:
seg_r=8'hf9;//显示1
4'h2:
seg_r=8'ha4;//显示2
4'h3:
seg_r=8'hb0;//显示3
4'h4:
seg_r=8'h99;//显示4
4'h5:
seg_r=8'h92;//显示5
4'h6:
seg_r=8'h82;//显示6
4'h7:
seg_r=8'hf8;//显示7
4'h8:
seg_r=8'h80;//显示8
4'h9:
seg_r=8'h90;//显示9
4'ha:
seg_r=8'hbf;//显示-
default:
seg_r=8'hff;//不显示
endcase
if((count1[3:
1]==3'd2)&sec)
seg_r=8'hff;
End
3.6.2显示模块儿仿真
编译程序,进行功能仿真,记录仿真图形:
图3.9显示模块仿真图
通过上面的图可以知道,LED数码管是通过扫描的方式实现数据更新,通过dig,seg寄存器的数据可以知道,数据能正常显示,满足设计要求。
四、总结
在FPGA上设计和调试都需要耐心,时钟设计在生活中无处不在,设计的过程要考虑到应用的习惯,设计更人性化的体验,才会是一个好的设计。
在VerilogHDL语言的学习上还存在一些问题,没有深入的学习,对于有些语法错误,还需要仔细的查找。
如果将这个数字时钟应用于现实生活中,还存在些许的问题。
例如按键太多,操作起来没那么的方便等等。
优点就是在电子钟的基础上新增加闹钟,校正等功能。
最后感谢师兄这几周以来的有关可编程方面的知识讲解以及QuartusII软件的使用。
使我对FPGA有了一些初步的了解,对于以后的学习或者是科研都有莫大的帮助!
附录
moduleclock(clk,key,dig,seg,beep);//模块名clock
inputclk;//输入时钟
input[4:
0]key;//输入按键
output[7:
0]dig;//数码管选择输出引脚a
output[7:
0]seg;//数码管段输出引脚
outputbeep;//蜂鸣器输出端
reg[7:
0]seg_r=8'h0;//定义数码管输出寄存器
reg[7:
0]dig_r;//定义数码管选择输出寄存器
reg[3:
0]disp_dat;//定义显示数据寄存器
reg[8:
0]count1;//定义计数寄存器
reg[14:
0]count;//定义计数中间寄存器
reg[23:
0]hour=24'h235956;//定义现在时刻寄存器
reg[23:
0]clktime=24'h000000;//定义设定闹钟
reg[1:
0]keyen=2'b11;//定义标志位
reg[4:
0]dout1=5'b11111;
reg[4:
0]dout2=5'b11111;
reg[4:
0]dout3=5'b11111;//寄存器
wire[4:
0]key_done;//按键消抖输出
reg[15:
0]beep_count=16'h0;//蜂鸣器寄存器
reg[15:
0]beep_count_end=16'hffff;//蜂鸣器截止寄存器
regclktime_en=1'b1;//闹钟使能寄存器
regsec;//1秒时钟
regclk1;//1ms时钟
regbeep_r;//寄存器
wirebeepen;
assignbeep=beep_r;//输出音乐
assigndig=dig_r;//输出数码管选择
assignseg=seg_r;//输出数码管译码结果
assignbeepen=|hour[15:
4];
assignkey_done=key|dout3;//按键消抖输出
//1ms信号产生部分
always@(posedgeclk)//定义clock上升沿触发
begin
count=count+1'b1;
if(count==15'd24000)//0.5mS到了吗?
begin
count=15'd0;//计数器清零
clk1=~clk1;//置位秒标志
end
end
//秒信号产生部分
always@(posedgeclk1)//定义clock上升沿触发
begin
count1=count1+1'b1;
if(count1==9'd500)//0.5S到了吗?
begin
count1=9'd0;//计数器清零
sec=~sec;//置位秒标志
end
end
always@(posedgecount1[5])//按键去噪声
begin
dout1<=key;
dout2<=dout1;
dout3<=dout2;
end
always@(negedgekey_done[4])
begin
keyen[1]=~keyen[1];//校准按键转换
end
always@(negedgekey_done[3])
begin
keyen[0]=~keyen[0];//定时按键转换
end
//闹钟定时部分
always@(negedgesec)
begin
if(!
keyen[0])//如果有闹钟设置键按下
begin
case(key_done[2:
0])
3'b110:
begin
clktime[19:
16]=clktime[19:
16]+1'b1;//时个位加一
if(clktime[19:
16]==4'ha)
begin
clktime[19:
16]=4'h0;
clktime[23:
20]=clktime[23:
20]+1'b1;//时十位加一
end
if(clktime[23:
16]==8'h24)
clktime[23:
16]=8'h0;
end
3'b101:
begin