Skip to content

Latest commit

 

History

History
180 lines (130 loc) · 16.4 KB

README.md

File metadata and controls

180 lines (130 loc) · 16.4 KB

Digital-Clock

基于C8051F020的电子时钟设计

2018.11

一、任务要求 利用实验板资源设计一个电脑时钟。 【基本要求】

  1. 正常情况下再数码管上显示时、分、秒。
  2. 可设置3个闹钟,闹钟到时用声光提示。
  3. 可切换时间、日期、星期三种显示模式。
  4. 整点声光提示。 二、功能特点与使用说明 ·功能特点:
  5. 该电脑时钟在正常情况下会在数码管上显示“时、分、秒”,用户可换时间、日期、星期三种显示模式。
  6. 具有整点报时功能,当计时到整点时,会有“嘀嘀嘀嘀”四声提示,并且数码管显示屏伴随报时音闪烁。用户可选择开启或关闭该功能
  7. 用户可自行设置时间、日期,当年月日设置好时,内置程序自动计算当日是星期几。
  8. 具有三个闹钟,用户可自行设置闹钟“时、分”,也可关闭闹钟。闹钟到时有声光提示,声音为短促的“嘀嘀嘀嘀”四声,用户可自行关闭。

·使用说明

  1. 该电脑时钟共有6个按键,对应板上“1”、“0”、“. ”、“=”、“F1”、“F5”、“F6”。其功能分别为“(年份)显示数减一”、“显示数加一”、“切换到下一位”、“开关整点报时”、“关闹钟”、“切换显示模式”、“切换设置模式”。
  2. 切换显示模式: 正常时间显示模式如下,如12时27分45秒显示为:

在正常时间显示模式下,按一下F5,切换至星期显示模式,如周一显示为:

再按一下F5,进入日期显示模式:如2018年11月11日显示为:

再按一下F5,回到时间显示模式。出厂日期默认为2018年1月1日星期一,0时0分0秒。

  1. 年月日设置:在显示模式下,按一下F6,进入日期设置模式,计时暂停。首先是“年”的设置:按一下“0”,年份加一,按一下“1”,年份减一;按一下“. ”,跳转至月份设置:按一下“0”,月份加一,当月份为“12”时,再按“0”自动回到1月;按一下“. ” 跳转至日期设置:按一下“0”,日期加一,当日期为当月最后一天时,再按“0”自动回到1日。若再按“. ”,回到年份设置模式,如此往复。在年月日设置模式下按F6,进入时间设置模式。

  2. 时间设置:在年月日设置模式下,按一下F6,进入时间设置模式。首先是“秒”的设置:按一下“0”,秒加一,当秒数为“59”时,再按“0”自动回到“00”;按一下“. ”,跳转至“分”的设置:按一下“0”,分加一,当分数为“59”时,再按“0”自动回到“00”;按一下“. ”,跳转至“时”的设置:按一下“0”,时加一,当时数为“23”时,再按“0”自动回到“00”。若再按“. ”,回到秒数设置,如此往复。在时间设置模式下按F6,进入闹钟设置模式,时钟开始计时。

  3. 闹钟设置:该电脑时钟共有三个闹钟可供用户设置,闹钟可设置到某时某分,在该时该分0秒时有声光提示。在时间设置模式下,按F6进入闹钟1设置模式,如设置闹钟1为9点整,显示如下:

按一下“0”,分加一,其余设置同时间设置。闹钟1设置好后按F6进入闹钟2设置模式,再按F6进入闹钟3设置模式,再按F6,回到显示模式。闹钟2和闹钟3的设置方式与显示方式与闹钟1相似,设置闹钟2时会显示“A2”,设置闹钟3时会显示“A3”。设置某一闹钟时,其余闹钟到点不会响。当闹钟时为23时再按“0”,显示“Ax OFF”,x为对应闹钟号,表示闹钟关闭。

  1. 整点报时:当计时到整点(某时0分0秒)时,会有“嘀嘀嘀嘀”四声提示,并且数码管显示屏伴随报时音闪烁。用户无需进行任何操作。根据用户需求,用户按“=”键可开启或关闭整点报时,关闭后最左边的数码管显示“. ”字符,标识整点报时已关闭。

  2. 闹钟提示:当计时到达用户设置的任意一个闹钟时间(某时某分0秒)时,会有“嘀嘀嘀嘀”四声短促提示,默认提示30次。用户在闹钟响时按F1,可关闭闹钟。若闹钟设置为整点,则到点闹钟响,整点报时不再提示。 三、方案设计 ·设计思路 根据任务要求,电脑时钟应当具有以下模块:计时模块、显示模块、整点报时模块、闹钟模块、功能切换模块、数值输入(设置)模块,而后两者都是通过键盘来实现的。在用程序实现功能的过程中,我共设计了如下模块:

  3. 计时模块与计时器0模块: 该模块使用T0计时器的模式1。计算25ms延时所对应的初值为4C00H,然后执行40次即为一秒。此处分别设置如下变量:(unsigned int简写为uint,unsigned char简写为uchar) uchar toki:作为定时器执行次数的标志。 uchar sec:作为“秒”,toki=40时,计满一秒,toki置0,sec自增。 uchar min:作为“分”,sec=60时,计满一分,sec置0,min自增。 uchar hour:作为“时”,min=60时,计满一小时,min置0,hour自增。 sec, min, hour的进位关系通过time( )函数实现。

uchar day:作为“日”。 uchar month:作为“月”。 uchar year:作为“月”。 day, month, year根据实际的进位规则来进位,即“1,3,5,7,8,10,12月有31天;4,6,9,11月有30天;平年2月28天,闰年2月29天;非整百年份能被4整除的为闰年,整百年份能被400整除的为平年”。通过ymd( )函数实现。

uchar wkday作为“一周中的某一天”。给定年月日的情况下,这一天是周几,可以用Zeller公式计算。 ·Zeller公式:w = (y + [y/4] + [c/4] - 2c + [26(m+1) /10] + d – 1) mod 7,其中y为年的低2位,c为年的高2位,m为月份(当月份为1或2时,需要修正为前一年的“13”,“14”月),d为日,“[ ]”为取整运算符,“mod”为取余数运算符。 由Zeller公式计算星期,需要引入如下变量: uchar monz:作为Zeller公式下的“m”,即修正后的月份。 uchar decade:作为Zeller公式下的“y”,即年的低二位。 uchar century:作为Zeller公式下的“c”,即年的高二位。 而“d”(日期)可直接使用全局变量day来充当。 引入这些变量后,wkday=((decade+(decade/4)+(century/4)-2century+(13(monz+1)/5)+day+69)%7)即为特定日期所对应的星期。这里在原来的Zeller公式模7前加了70,用来对2000、2100这样的年份进行修正。因为wkday是无符号变量,如果运算结果小于0,结果会出错。 特别需要注意的是,Zeller公式在未经修正的前提下只能用来计算格里高利历下(即公元1582年10月15日后)的星期。考虑电脑时钟使用的实际情况,使用时会设置成实际的年月日,不会设置到1582年以前,因此不设计对Zeller公式进行修正的程序。

  1. 显示模块 C8051F020实验板上有8个8段LED数码管,其连接电路如下:

数码管的段选(每一段亮或灭)通过P2.0—P2.7端口控制,数码管的位选通过P3.0,P3.1和P3.2控制。为了让8个数码管在视觉上为“同时”显示,必须给予扫描式的段选信号,且扫描频率不能太低,扫描周期应远小于人眼的视觉暂留时间(50ms),否则会出现明显的闪烁 常见的段选扫描有顺序结构与循环结构。采用顺序结构时,由于扫描最后一位后会去执行其他代码,这样必然导致扫描的最后一位数码管亮度大于其他数码管;而循环结构中每一位数码管显示时间可以认为相同,亮度也会相同,因此在设计时,采用循环结构,通过for循环嵌套switch-case语句实现。而该语句的执行时间远远高于人眼视觉暂留效应的要求,因此可以较好地用来完成扫描功能。 根据不同程序的显示要求,共设计四个函数来实现显示功能: void display1( ):时间显示函数。 void display2( ):星期显示函数。 void display3( ):日期显示函数。 void display4( uchar k ):闹钟设置显示函数,其中k作为闹钟1-3的标志位。

3.键盘模块 C8051F020实验板采用4×5的矩阵式键盘,共有20个按键,示意如下: 7 8 9 F4 F8 4 5 6 F3 F7 1 2 3 F2 F6 0 . = F1 F5

键盘连接电路如下:

由电路图可见,键盘的“行”信号由移位寄存器74HCT164来控制,列信号由P4.0—P4.3来控制。与显示器原理相同,如果想在调用键盘的时刻按任意键都有效,则必须对键盘进行“扫描”。键盘扫描用到如下函数: void _74HCT164( ):功能是输入一个参数,然后把这个参数的值赋给移位寄存器的8个端口。 uchar keyscan( ):功能是判断有无按键按下,若有按键按下,返回键值0-19(分别对应20个按键)进行后续操作,若无按键按下,返回20。

键盘扫描设计思路如下: 首先把0000 0000(0x00)送入移位寄存器,即“行电位”全部为0。如果没有键盘按下,行上的电位不能传递到P4端口,因此P4端口低四位应全为1。让P4与1111 0000(0xf0)做按位或运算,若结果为1111 1111(0xff),说明P4.0—P4.3全为1,即无键按下。此时键值变量为20,示意无按键按下。 若结果不为1111 1111(0xff),此时应延时去抖动,防止误判或被判定为按键多次。之后再让P4与1111 0000(0xf0)做按位或运算,若仍不为0xff,说明确实有键盘被按下。此时将0001 1110(0x1e)送入移位寄存器,对键盘第一行进行扫描。若有键按下,则对应列的0电平可以传送到P4的对应端口。此时再让P4与1111 0000做按位或运算,就可识别0电平对应的列。若扫描到第一行有按键按下,则键值变量赋值为对应按键号,等待程序执行后返回键值;若扫描到第一行无按键按下,继续扫描后续行列。 同理,依次对移位寄存器赋值0001 1101(0x1d)、0001 1011(0x1b)、0001 0111(0x17),0000 1111(0x1f),即依次对第二、三、四、五行进行扫描。若有按键按下,则键值变量赋值为对应按键号,等待程序执行后返回键值,否则键值变量赋值为20,示意无按键按下。 若有按键按下,等待按键抬起后返回键值,方法为让P4与1111 0000(0xf0)做按位或运算,直到结果为1111 1111(0xff),说明按键抬起,再返回键值变量的值,

  1. 延时模块 使用for循环的空循环进行延时。由于这里的延时不需要精确到毫秒级,因此不使用定时器完成。经过测试符合要求即可。延时函数如下: void delay( uint k ):函数内部是while循环嵌套循环100次的空for循环,k为调用时的传参,表示需要延时多少次,即延时多久。具体实现方法是while(k--),执行for循环,当k自减为0时,跳出延时程序,返回之前执行的程序。

  2. 整点报时模块 void oclock( ):该函数使用if语句判断当时间为整点(某时0分0秒)时,让P3.5端口先为1,P4 += 0x10,一段时间后为0,P4 -= 0x10,反复四次,即实现到整点时报时“嘀嘀嘀嘀”四下,并且伴随屏幕闪烁与LED小灯闪烁的功能。不直接对P4赋值是为了防止整点报时的同时有按键被按下,引起程序混乱。 整点报时模式的标志位suichi[0]默认为1,若按下“=”键,suichi[0]变为0,整点报时程序不执行,display1函数中增加显示数码管的第八位为“.”,表示整点报时关闭。再按“=”键,suichi[0]变为1,整点报时程序执行,数码管第八位的“.”不再显示。

  3. 显示模式切换模块 通过void setmod( )函数实现。 具体为switch-case语句。电脑时钟默认显示时间,标志变量state[1]为0,调用display1()函数。按一下F5键,state[1]变为1,调用display2()函数,进入星期显示模式,再按F5,state[0]变为2,调用display3()函数,进入日期显示模式,再按F5,state[1]变为0,回到时间显示模式。

  4. 时间、日期、闹钟设置模块 void select( ):选择待设置的模式。按下F6,标志位state[0]变为1,进入setsequence函数。 void setsequence( ):选择设置年月日或是时分秒。标志位state[2]等于0时执行setymd函数,等于1时执行sethms函数。 void setymd( ):设置年月日的函数,当state[0]=1,state[2]=0时执行。首先进行年份的设置,标志位state[4]置0。执行年份设置时首先扫描键盘,由于年份不存在“轮回”,在设置时除了能增加之外还应该能减少,故设置“0”键用来使年份加一,设置“1”键来使年份减一。若设置年份时按“. ”键,跳转至月份设置,state[4]置1;月份加至12时再加一则回到1。设置月份时按“. ”键,跳转至日期设置,state[4]置2。日期的进位校正由correctymd( )函数控制。 void sethms( ):设置时分秒的函数。设置年月日的界面下按F6,进入时分秒设置,state[2]置1。state[3]为0时设置秒,为1时设置分,为2时设置时,通过“. ”键切换,具体设置方式同上。 void correctymd( ):设置年月日时用来校正特定月份对应日期进位的函数。 void selectalarm( ):选择设置闹钟1-3。设置时分秒的界面下按F6,进入闹钟1设置。设置闹钟时默认关闭整点报时和闹钟提示功能,具体方式是先令state[10]=suichi[0],记住整点报时的状态,然后suichi[0]、suichi[1]置0,关闭整点报时和闹钟提示。待闹钟设置完毕后,进行开关置位:suichi[1]=1,suichi[0]=state[10],开启闹钟,整点报时恢复初态。 void stalm1( )——void stalm3( ):设置闹钟1-3的函数。state[6]为0、1、2时对应闹钟1、2、3的设置。闹钟时、分的设置、切换,闹钟顺序的切换与日期、时间设置类似,设置闹钟时数时,若时为23,再按一下“. ”,时数变为24,标志闹钟关闭,此时屏幕上显示Ax OFF(x为闹钟号);再按一下“. ”,时数变为0,恢复正常的显示。用全局变量alarm[3][2]作为设置闹钟的变量。

  5. 闹钟提示模块 void alarm( ):闹钟到时用来提示的模块。使用if语句判断,当闹钟开关打开(即suichi[1]=1),且时、分到达闹钟设定的时间,秒为0时,闹钟响。让P3.5端口先为1,极短时间后时间后为0,反复30次。程序中执行键盘扫描,若在响铃时按F1键,跳出程序,实现“手动关闹钟”的功能。

  6. 主程序 主程序主要包括初始化程序Init_Device( ),对外部晶振频率、定时器初值进行设置。接下来设置crossbar、P3推挽输出、TR0=1开定时器,最后是一个while(1)空循环,等待定时器0、3发中断执行程序。

  7. 定时器3模块 定时器3为一自动装填的定时器,将其初值设置为0x90,用来作为除了时间、日期以外程序的控制时钟。

·资源分配 主要介绍硬件端口分配,中断分配与数组功能。

  1. 硬件端口分配 P2:LED数码管段选信号控制。 P3.0-P3.2:LED数码管位选信号控制。 P3.5:蜂鸣器控制。 P4.0-P4.3:键盘列信号控制。 P4.4-P4.5:橘色与绿色LED灯控制。 74HCT164:8位移位寄存器,利用其中5位来进行键盘行信号控制。

  2. 中断分配 interrupt 0:定时器0中断。 interrupt 14:定时器3中断。

  3. 数组功能 uchar num[17] = {0xfc,0x60,0xda,0xf2,0x66,0xb6,0xbe,0xe0,0xfe,0xf6,0xee,0x3e,0x9c,0x7a,0x9e,0x8e}:“0”到“F”的字模。 uchar alarm[3][2]:设置闹钟时用来记录闹钟时、分的数组。alarm[x][0]为分,alarm[x][1]为时,x为闹钟序号。 uchar state[11]:标志位。 state[0]:设置选择标志位。 state[1]:显示模式标志位。 state[2]:时间、日期设置标志位。 state[3]:时间设置中“时、分、秒”的标志位。 state[4]:日期设置中“年、月、日”的标志位。 state[5]:是否开启整点报时的状态标志位。 state[6]:闹钟设置标志位。 state[7]:闹钟1设置中“时、分“的标志位。 state[8]:闹钟2设置中“时、分“的标志位。 state[9]:闹钟3设置中“时、分“的标志位。 state[10]:用来记忆整点报时工作状态suichi[0]的变量。

uchar suichi[2]:整点报时与闹钟提示使能标志位 suichi[0]:为1时整点报时开启,为0时整点报时关闭。 suichi[1]:为1时整点报时开启,为0时整点报时关闭。