单片机的时钟设计
小组成员:
班级:
课程老师:
目录
一、硬件结构………………………………………………3
1硬件原理…………………………………………………3
1 89C52…………………………………………………… 3
1.1硬件原理…………………………………………………3
1.2 主要功能特性…………………………………………3
1.3 管脚说明………………………………………………4
1.4振荡器特性……………………………………………5
1.5结构特点……………………………………………… 5
2、数码管………………………………………………… 6
2.1数码管分类………………………………………………6
2.2数码管结构…………………………………………… 7
2.3驱动方式…………………………………………………8
3、排阻………………………………………………………9
3.1排阻的作用………………………………………………9
3.2排阻引脚说明……………………………………………9
4、晶振……………………………………………………10
4.1晶振构成…………………………………………………10
4.2工作原理……………………………………………… 11
4.3功能作用…………………………………………………11
二、 软件结构概述…………………………………………12
1、显示子程序………………………………………………12
2、键盘扫描子程序…………………………………………13
3、中断程序…………………………………………………16
4、流程图………………………………………………… 18
三、调试过程…………………………………………………20
四、心得体会………………………………………………………22
五、参考文献………………………………………………………23
六、硬件电路图……………………………………………………23
七、程序清单………………………………………………………25
一、 硬件结构概述
1、 89C52
1.1硬件原理
89C52是INTEL公司MCS-51系列单片机中基本的产品,它采用ATMEL公司可靠的CMOS工艺技术制造的高性能8位单片机,属于标准的MCS-51的HCMOS产品。它结合了CMOS的高速和高密度技术及CMOS的低功耗特征,它基于标准的MCS-51单片机体系结构和指令系统,属于89C51增强型单片机版本,集成了时钟输出和向上或向下计数器等更多的功能,适合于类似马达控制等应用场合。89C52内置8位中央处理单元、256字节内部数据存储器RAM、8k片内程序存储器(ROM)32个双向输入/输出(I/O)口、3个16位定时/计数器和5个两级中断结构,一个全双工串行通信口,片内时钟振荡电路。此外,89C52还可工作于低功耗模式,可通过两种软件选择空闲和掉电模式。在空闲模式下冻结CPU而RAM定时器、串行口和中断系统维持其功能。掉电模式下,保存RAM数据,时钟振荡停止,同时停止芯片内其它功能。89C52有PDIP(40pin)和PLCC(44pin)两种封装形式。
1.2 主要功能特性
· 标准MCS-51内核和指令系统
· 32个双向I/O口
· 3个16位可编程定时/计数器
· 向上或向下定时计数器
· 6个中断源
· 全双工串行通信口
· 空闲和掉电节省模式
· 片内8kROM(可扩充64kB外部存储器)
· 256x8bit内部RAM(可扩充64kB外部存储器)
· 时钟频率3.5-12/24/33MHz
· 改进型快速编程脉冲算法
· 5.0V工作电压
· 布尔处理器
· 4层优先级中断结构
· 兼容TTL和CMOS逻辑电平
· PDIP(40)和PLCC(44)封装形式
·—帧错误侦测
·—自动地址识别
1.3 管脚说明
VCC:供电电压。 GND:接地。
P0口:P0口为一个8位漏级开路双向I/O口,每脚可吸收8个TTL门电流。当P0口的管脚第一次写“1”时,被定义为高阻输入。P0能够用于外部程序数据存储器,它可以被定义为数据/地址的第八位。在FIASH编程时,P0 口作为原码输入口,当FIASH进行校验时,P0输出原码,此时P0外部必须被拉高。
P1口:P1口是一个内部提供上拉电阻的8位双向I/O口,P1口缓冲器能接收输出4个TTL门电流。P1口管脚写入“1”后,被内部上拉为高,可用作输入,P1口被外部下拉为低电平时,将输出电流,这是由于内部上拉的缘故。在FLASH编程和校验时,P1口作为第八位地址接收。
P2口:P2口为一个内部上拉电阻的8位双向I/O口,P2口缓冲器可接收,输出4个TTL门电流,当P2口被写“1”时,其管脚被内部上拉电阻拉高,且作为输入。并因此作为输入时,P2口的管脚被外部拉低,将输出电流。这是由于内部上拉的缘故。P2口当用于外部程序存储器或16位地址外部数据存储器进行存取时,P2口输出地址的高八位。在给出地址“1”时,它利用内部上拉优势,当对外部八位地址数据存储器进行读写时,P2口输出其特殊功能寄存器的内容。P2口在FLASH编程和校验时接收高八位地址信号和控制信号。
P3口:P3口管脚是8个带内部上拉电阻的双向I/O口,可接收输出4个TTL门电流。当P3口写入“1”后,它们被内部上拉为高电平,并用作输入。作为输入,由于外部下拉为低电平,P3口将输出电流(ILL)这是由于上拉的缘故。P3口作为AT89C51的一些特殊功能口,管脚 备选功能
P3.0 RXD(串行输入口)
P3.1 TXD(串行输出口)
P3.2 /INT0(外部中断0)
P3.3 /INT1(外部中断1)
P3.4 T0(计时器0外部输入)
P3.5 T1(计时器1外部输入)
P3.6 /WR(外部数据存储器写选通)
P3.7 /RD(外部数据存储器读选通)
RST:复位输入。当振荡器复位器件时,要保持RST脚两个机器周期的高电平时间。
ALE/PROG:当访问外部存储器时,地址锁存允许的输出电平用于锁存地址的地位字节。
在FLASH编程期间,此引脚用于输入编程脉冲。在平时,ALE端以不变的频率周期输出正脉冲信号,此频率为振荡器频率的1/6。因此它可用作对外部输出的脉冲或用于定时目的。然而要注意的是:每当用作外部数据存储器时,将跳过一个ALE脉冲。如想禁止ALE的输出可在SFR8EH地址上置0。此时, ALE只有在执行MOVX,MOVC指令是ALE才起用。另外,该引脚被略微拉高。如果微处理器在外部执行状态ALE禁止,置位无效。
/PSEN:外部程序存储器的选通信号。在由外部程序存储器取指期间,每个机器周期两/PSEN有效。但在访问外部数据存储器时,这两次有效的/PSEN信号将不出现。/EA / VPP:当/EA保持低电平时,则在此期间外部程序存储器(0000H-FFFFH),不管是否有内部程序存储器。注意加密方式1时,/EA将内部锁定为RESET;当/EA端保持高电平时,此间内部程序存储器。在FLASH编程期间,此引脚也用于施加12V编程电源(VPP)。
XTAL1:反向振荡放大器的输入及内部时钟工作电路的输入。
XTAL2:来自反向振荡器的输出。
1.4振荡器特性
外接石英晶体或者陶瓷谐振器以及电容C1、C2接在放大器的反馈回路(AT89C52内部有一个用于构成内部振荡器的高增益反相放大电路,XTAL1、XTAL2分别是该放大器的输入和输出端)中构成并联振荡电路。
为了使装置能够被外部时钟信号激活,XATL1应该有效,而XTAL2应该被悬空。由于输入到内部的时钟信号电路通过了一个二分频的信号,外部信号的工作周期比没有别的要求,但是最大值和最小值的大小可以在数据表上观察出来。
当正常工作时,外部振荡器可以计算出XTAL1上的电容,最大可达到100pF。这是由于振荡器电容和反馈电容之间的相互作用。当外部信号是标准高电平或者低电平时,电容不会超过20pF.
1.5结构特点
n 互补高性能金属氧化物半导体结构可擦可 编程只读存储器/只读存储器/中央处理器
n 12/24/33MHz操作
n 三个16位的定时器/计数器
n 可编程的时钟输出
n Up/Down定时器/计数器
n 三级程序锁定系统
n 8K/16K/32K片内程序存储器
n 256字节片内RAM
n 改进的快速脉冲编程算法
n 布尔处理器
n 32根可编程的输入/输出线
n 六个中断源
n 可编程的串行通道带有:——帧错误检测 ——自动地址识别
n TTL和CMOS兼容逻辑电平
n 64K片外程序存储空间
n 64K片外数据存储空间
n MCS51单片机可兼容指令集
n 闲置节能和掉电模式
n ONCE(On-Circuit仿真)模式
n 四级中断优先级
n 扩展温度范围(﹣40℃到﹢85℃)
2、 数码管
2.1数码管分类
数码管按段数分为七段数码管和八段数码管,八段数码管比七段数码管多一个发光二极管单元(多一个小数点显示);按能显示多少个“8”可分为1位、2位、4位等等数码管;
按发光二极管单元连接方式分为共阳极数码管和共阴极数码管。共阳数码管是指将所有发光二极管的阳极接到一起形成公共阳极(COM)的数码管,共阳数码管在应用时应将公共极COM接到+5V,当某一字段发光二极管的阴极为低电平时,相应字段就点亮,当某一字段的阴极为高电平时,相应字段就不亮。共阴数码管是指将所有发光二极管的阴极接到一起形成公共阴极(COM)的数码管,共阴数码管在应用时应将公共极COM接到地线GND上,当某一字段发光二极管的阳极为高电平时,相应字段就点亮,当某一字段的阳极为低电平时,相应字段就不亮。
2.2数码管结构
led数码管(LED Segment Displays)是由多个发光二极管封装在一起组成“8”字型的器件,引线已在内部连接完成,只需引出它们的各个笔划,公共电极。led数码管常用段数一般为7段有的另加一个小数点,还有一种是类似于3位“+1”型。位数有半位,1,2,3,4,5,6,8,10位等等....,led数码管根据LED的接法不同分为共阴和共阳两类,了解LED的这些特性,对编程是很重要的,因为不同类型的数码管,除了它们的硬件电路有差异外,编程方法也是不同的。图2是共阴和共阳极数码管的内部电路,它们的发光原理是一样的,只是它们的电源极性不同而已。颜色有红,绿,蓝,黄等几种。led数码管广泛用于仪表,时钟,车站,家电等场合。选用时要注意产品尺寸颜色,功耗,亮度,波长等。
2.3驱动方式
静态显示驱动
静态驱动也称直流驱动。静态驱动是指每个数码管的每一个段码都由一个单片机的I/O端口进行驱动,或者使用如BCD码二-十进制译码器译码进行驱动。静态驱动的优点是编程简单,显示亮度高,缺点是占用I/O端口多,如驱动5个数码管静态显示则需要5×8=40根I/O端口来驱动,要知道一个89S51单片机可用的I/O端口才32个呢:),实际应用时必须增加译码驱动器进行驱动,增加了硬件电路的复杂性。
动态显示驱动
数码管动态显示接口是单片机中应用最为广泛的一种显示方式之一,动态驱动是将所有数码管的8个显示笔划"a,b,c,d,e,f,g,dp"的同名端连在一起,另外为每个数码管的公共极COM增加位选通控制电路,位选通由各自独立的I/O线控制,当单片机输出字形码时,所有数码管都接收到相同的字形码,但究竟是哪个数码管会显示出字形,取决于单片机对位选通COM端电路的控制,所以我们只要将需要显示的数码管的选通控制打开,该位就显示出字形,没有选通的数码管就不会亮。通过分时轮流控制各个数码管的的COM端,就使各个数码管轮流受控显示,这就是动态驱动。在轮流显示过程中,每位数码管的点亮时间为1~2ms,由于人的视觉暂留现象及发光二极管的余辉效应,尽管实际上各位数码管并非同时点亮,但只要扫描的速度足够快,给人的印象就是一组稳定的显示数据,不会有闪烁感,动态显示的效果和静态显示是一样的,能够节省大量的I/O端口,而且功耗更低。
3、 排阻
3.1排阻的作用
内存芯片下方均匀分布的“芝麻粒”,实际上是位于内存颗粒和金手指之间的“排阻”。排阻,是一排电阻的简称。我们知道,内存在处理、传输数据时会产生大小不一的工作电流。而在内存颗粒走线的必经之处安装一排电阻,则能够帮助内存起到稳压作用,让内存工作更稳定。从而提升内存的稳定性,增强内存使用寿命。内存右边角上的“小绿豆”,我们一般称之为SPD。SPD是一存储体,它存储了厂商对内存的详细配置信息:如内存的工作电压,位宽,操作时序等。每次开机后自检时,系统都会首先读取内存SPD中的相关信息,来自动配置硬件资源,以避免出错。上拉、限流。和普通电阻一样,相比而言简化了PCB的设计、安装,减小空间,保证焊接质量。
3.2排阻引脚说明
1与a 2与b 3与c 4 与d之间的电阻都是10欧,与其它的管脚没有任何关系.就是一排电阻,做在了一个原件上.
有的还有一个公脚,就是为了方便使用,拿万用表量一下就会发现所有脚对公共脚的阻值均是标称值,除公共脚外其它任意两脚阻值是标称值的两倍,很明显任意两脚通过公共脚脚串联的嘛!用在有很多上下拉电阻的场合应用特方便,比如并行通讯线上,还节省空间。
4、 晶振
4.1晶振构成
石英晶体振荡器是利用石英晶体(二氧化硅的结晶体)的压电效应制成的一种谐振器件,它的基本构成大致是:从一块石英晶体上按一定方位角切下薄片(简称为晶片,它可以是正方形、矩形或圆形等),在它的两个对应面上涂敷银层作为电极,在每个电极上各焊一根引线接到管脚 上,再加上封装外壳就构成了石英晶体谐振器,简称为石英晶体或晶体、晶振;而在封装内部添加IC组成振荡电路的晶体元件称为晶体振荡器。其产品一般用金属外壳封装,也有用玻璃壳、陶瓷或塑料封装的。
4.2工作原理
计算机都有个计时电路,尽管一般使用“时钟”这个词来表示这些设备,但它们实际上并不是通常意义的时钟,把它们称为计时器(timer)可能更恰当一点。计算机的计时器通常是一个精密加工过的石英晶体,石英晶体在其张力限度内以一定的频率振荡,这种频率取决于晶体本身如何切割及其受到张力的大小。有两个寄存器与每个石英晶体相关联,一个计数器(counter)和一个保持寄存器(holdingregister)。石英晶体的每次振荡使计数器减1。当计数器减为0时,产生一个中断,计数器从保持计数器中重新装入初始值。这种方法使得对一个计时器进行编程,令其每秒产生60次中断(或者以任何其它希望的频率产生中断)成为可能。每次中断称为一个时钟嘀嗒(clocktick)。
晶振在电气上可以等效成一个电容和一个电阻并联再串联一个电容的二端网络,电工学上这个网络有两个谐振点,以频率的高低分其中较低的频率为串联谐振,较高的频率为并联谐振。由于晶体自身的特性致使这两个频率的距离相当的接近,在这个极窄的频率范围内,晶振等效为一个电感,所以只要晶振的两端并联上合适的电容它就会组成并联谐振电路。这个并联谐振电路加到一个负反馈电路中就可以构成正弦波振荡电路,由于晶振等效为电感的频率范围很窄,所以即使其他元件的参数变化很大,这个振荡器的频率也不会有很大的变化。晶振有一个重要的参数,那就是负载电容值,选择与负载电容值相等的并联电容,就可以得到晶振标称的谐振频率。一般的晶振振荡电路都是在一个反相放大器(注意是放大器不是反相器)的两端接入晶振,再有两个电容分别接到晶振的两端,每个电容的另一端再接到地,这两个电容串联的容量值就应该等于负载电容,请注意一般IC的引脚都有等效输入电容,这个不能忽略。一般的晶振的负载电容为15p或12.5p,如果再考虑元件引脚的等效输入电容,则两个22p的电容构成晶振的振荡电路就是比较好的选择。
4.3功能作用
晶振在应用具体起到的作用,微控制器的时钟源可以分为两类:基于机械谐振器件的时钟源,如晶振、陶瓷谐振槽路;RC(电阻、电容)振荡器。一种是皮尔斯振荡器配置,适用于晶振和陶瓷谐振槽路。另一种为简单的分立RC振荡器。基于晶振与陶瓷谐振槽路的振荡器通常能提供非常高的初始精度和较低的温度系数。RC振荡器能够快速启动,成本也比较低,但通常在整个温度和工作电源电压范围内精度较差,会在标称输出频率的5%至50%范围内变化。但其性能受环境条件和电路元件选择的影响。需认真对待振荡器电路的元件选择和线路板布局。在使用时,陶瓷谐振槽路和相应的负载电容必须根据特定的逻辑系列进行优化。具有高Q值的晶振对放大器的选择并不敏感,但在过驱动时很容易产生频率漂移(甚至可能损坏)。影响振荡器工作的环境因素有:电磁干扰(EMI)、机械震动与冲击、湿度和温度。这些因素会增大输出频率的变化,增加不稳定性,并且在有些情况下,还会造成振荡器停振。上述大部分问题都可以通过使用振荡器模块避免。这些模块自带振荡器、提供低阻方波输出,并且能够在一定条件下保证运行。最常用的两种类型是晶振模块和集成RC振荡器(硅振荡器)。晶振模块提供与分立晶振相同的精度。硅振荡器的精度要比分立RC振荡器高,多数情况下能够提供与陶瓷谐振槽路相当的精度。
选择振荡器时还需要考虑功耗。分立振荡器的功耗主要由反馈放大器的电源电流以及电路内部的电容值所决定。CMOS放大器功耗与工作频率成正比,可以表示为功率耗散电容值。比如,HC04反相器门电路的功率耗散电容值是90pF。在4MHz、5V电源下工作时,相当于1.8mA的电源电流。再加上20pF的晶振负载电容,整个电源电流为2.2mA。陶瓷谐振槽路一般具有较大的负载电容,相应地也需要更多的电流。相比之下,晶振模块一般需要电源电流为10mA ~60mA。硅振荡器的电源电流取决于其类型与功能,范围可以从低频(固定)器件的几个微安到可编程器件的几个毫安。一种低功率的硅振荡器,如MAX7375,工作在4MHz时只需不到2mA的电流。在特定的应用场合优化时钟源需要综合考虑以下一些因素:精度、成本、功耗以及环境需求。
二、 软件结构概述
1显示子程序
1.1显示函数 display()
void display()
{
uchar i;
for(i=0;i<8;i++)
{
P0=LED[i];
P2=wei[i];
delay_ms(2);
P2=0xff;
}
}
这次所用到的数码管为8个,分别显示小时、分钟、秒,所以这里用到了一个6容量的数组LED[6]来做数码管显示缓冲区。
这次使用的是动态扫描显示,一次对六个数码管都做扫描,每位数码管的点亮时间为2ms,其中P0做数码管的位驱动,P2位段驱动。
1.2时钟显示处理函数 disp_time()
void disp_time()
{
a=second%10;
b=second/10;
c=minute%10;
d=minute/10;
e=hour%10;
f=hour/10;
LED[0]=duan[a];
LED[1]=duan[b];
LED[2]=duan[c];
LED[3]=duan[d];
LED[4]=duan[e];
LED[5]=duan[f];
}
其中a位秒的个位,b为秒的十位,c为分的个位,d为分的十位,e为小时的个位,f为小时的十位。
LED[0]、LED[1]、LED[2]、LED[3]、LED[4]、LED[5]对应得显示秒的个位十位,分的个位十位,小时的个位十位。
1.3秒表的显示处理函数 disp_miao()
void disp_miao()
{
a=C%10;
b=C/10;
c=B%10;
d=B/10;
e=A%10;
f=A/10;
LED[0]=duan[a];
LED[1]=duan[b];
LED[2]=duan[c];
LED[3]=duan[d];
LED[4]=duan[e];
LED[5]=duan[f];
}
其中C为秒表的百秒、千秒,B为秒表的十秒、秒,A为秒表的1/10秒、1/100秒。
LED[0]、LED[1]、LED[2]、LED[3]、LED[4]、LED[5]对应得显示秒表的百秒、千秒,十秒、秒,1/10秒、1/100秒。
2、键盘扫描子程序 key_scan()
2.1 K1键的扫描
if(k1==0)
{
delay_ms(10);
if(k1==0)
{
while(!k1);
TR0=0;
set_flag=1;
k1_flag++;
if(k1_flag==4)k1_flag=1;
}
当K0有键按下是为0,则进入IF函数里,首先程序延时10MS防止抖动造成误操作,当K1按下时为调时工作,即选择小时,分钟,秒,调时中设置一个调时标志位K1_FLAG,以便调时可以循环的选择。
2.2 K2键的扫描
if(k2==0)
{
delay_ms(10);
if(k2==0&choose_flag>=1)
{
while(!k2);
k2_flag++;
if(k2_flag==2)k2_flag=0;
TR1=k2_flag;
}
if(k2==0&set_flag==1)
{
while(!k2);
switch (k1_flag)
{
case 1:hour++;if(hour==24)hour=0;break;
case 2:minute++;if(minute==60)minute=0;break;
case 3:second++;if(second==60)second=0;break;
}
}
}
K2键在秒表时为计数的开始与停止,在时钟里作为加一功能键,在区分是秒表还是时钟方面,选择了一个标志位choose_flag来区分当其大于1时就表示为秒表模式,当其为0时表示为时钟模式,choose_flag的值是根据切换键来决定的。
当进入秒表时,K2每按一下都会改变k2_flag的状态,k2_flag也决定着时钟是停是走,当进入时钟时由k1_flag的状态决定哪一环节被加,而实现加的就是K2影响k2_flag的结果。
2.3 K3键的扫描
if(k3==0)
{
delay_ms(10);
if(k3==0&choose_flag>=1)
{
while(!k3);
H=0;L=0;M=0;
TR1=0;
}
if(k3==0&set_flag==1)
{
while(!k3);
switch (k1_flag)
{
case 1:if(hour==0)hour=24;hour--;break;
case 2:if(minute==0)minute=60;minute--;break;
case 3:if(second==0)second=60;second--;break;
}
}
}
}
K3键的作用于K2键是相对应的。
K3键在秒表时为计数的清零,在时钟里作为减一功能键,在区分是秒表还是时钟方面,选择了一个标志位choose_flag来区分当其大于1时就表示为秒表模式,当其为0时表示为时钟模式,choose_flag的值是根据切换键来决定的。
当进入秒表时,K3按一下会使得秒表的计数值被清零,当进入时钟时由k1_flag的状态决定哪一环节被加,而实现加的就是K2影响k2_flag的结果。
2.4 K4键的扫描
if(k4==0)
{
delay_ms(10);
if(set_flag==0&k4==0)
{
while(!k4);
start_flag++;
if(start_flag==2)
{
TR1=1;
start_flag=0;
}
choose_flag++;
if(choose_flag==3)
{
TR0=1;
choose_flag=0;
}
}
if(k4==0&set_flag==1)
{
while(!k4);
set_flag=0;
k1_flag=0;
TR0=1;
}
}
K4键的作用时时钟、秒表间的切换,当K4被按一下则进入到秒表,进入秒表模式是数码管的全部内容被清空,等待着启动,K4再被按一下则启动秒表,k4被按第三下时秒表又回到时钟,如上是在没有处在调节时间的条件下,当时钟正处在时间调节下时那K4的作用就是将其退到时钟状态
3、中断程序
3.1 时钟中断程序
void timer0() interrupt 1
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
t++;
if(t==20)
{
t=0;
second++;
if(second==60)
{
minute++;
second=0;
if(minute==60)
{
hour++;
minute=0;
if(hour==24)
{
hour=0;
}
}
}
}
该中断用的是定时器T0,没50ms中断一次,没中断20次秒加一,秒加到60后向分进位,即秒清零,分加一,当分加到60后向小时进位,即秒分都清零小时加一。
3.2 秒表中断程序
void timer1() interrupt 3
{
TH1=(65536-10000)/256;
TL1=(65536-10000)%256;
n++;
if(n==1)
{
n=0;
C++;
if(C==100)
{
C=0;
B++;
if(B==100)
{
B=0;
A++;
if(A==100)
{
A=0;
}
}
}
}
}
该中断用到定时器T1,T1每计数10ms则产生一次中断,秒表里是逢百进位,进位方式与前面相似。
三、 调试过程
3.1 时钟
3.2 时钟调时
3.3 秒表
3.4 秒表计时
四、心得体会
经过一周的课程设计,我收获颇多,有深刻的心得体会。 实践让我们受益匪浅。首先是关于单片机方面的,为了顺利完成程序的构成,我们学了一些关于单片机系统开发的C语言知识,最开始从已学过的C语言知识到最后的老师在课堂上补充的关于汇编的C语言,使我们得到了充分的锻炼。其次,它让我体会到了什么才是teamwork spirit。一如:两个人的分工、合作的经验、团队意识的提升和协调能力等等,这些都会让我们终身受益。 通过此次课程设计,使我更加扎实的掌握了有关电子线路单片机方面的知识,因为这次我们不仅写出了程序,而且还为写出的程序作出了实物,在设计过程中虽然遇到了一些问题,但经过一次又一次的思考,一遍又一遍的检查我终于找出了问题所在,也暴露出了前期我在这方面的知识欠缺和经验不足。实践才能出真知,实践才是检验真理的唯一标准,唯有通过亲自动手制作,才能令我们掌握的知识不再是一些纸上谈兵的东西。 在这次的课程设计中,我们遇到了很多困难,过程很艰难,但是我们都克服了,这是对我们自己的肯定。我们不断发现错误,不断改正,不断领悟,不断获取。我们也曾灰心,也曾茫然,也曾不知所措,从一开始的自信满满,到最后的紧张繁杂,所有的这些都令我们回味无穷,这已经成为了我们人生的一个宝藏。我想今后的学习和工作也是这样的,汗水见证着成功,我想十年过后,但我们都已经走入了社会,在某个阳光明媚的夏日,午后醒来,突然想起大学经历的时候,最先映入脑海里的就是这门课程吧,就是这些为了一个共同的目标,相互合作,共同奋斗的日子吧。
在这次程序设计中我们有许多值得思考的问题,首先、当老师建议作出实物时我和另一个组员犹豫是否作出这个东西,因为想着仿真多简单啊,但是看到身边同学毫不犹豫的选择作出实物来时,我们为我们退而求其次的想法惭愧;其次、在画板子时我们明显的发现自己不知从何处下手,在实验室里呆了一天并且包了夜才渐渐的摸到门路,这时我们才发现这些看似很简单的东西都是需要许多功夫和辛劳的;最后、在程序方面虽然我们这学期学习的是汇编语言,但是发现自己用汇编实现老师要求的能力远远不过,于是转向用C语言来解决,但是从这个方面来做我们组还是依赖了实验室的同学很多,我们先是学习他们的程序,消化吸收才有了我们的程序,在这里觉得我们的水平还是太差,也非常感谢那些帮助过我们的同学。
五、参考文献
(1)胡汉才 单片机原理及其接口技术(第二版). 北京:清华大学出版社,2004.2
(2)徐安等 单片机原理及应用. 北京:北京希望电子出版社,2003.2
(3)丁元杰 单片微机原理及应用(第二版). 北京:机械工业出版社,1999.10
(4)李建忠 单片机原理及应用. 西安:西安电子科技大学出版社,2002.2
(5)徐惠民 单片微型计算机原理、接口及应用(第二版). 北京:北京邮电大学出版社,2000.10
(6)闫玉德 MCS-51单片机原理及应用(C语言版). 北京:机械工业出版社
(7)何桥等 单片机原理及应用. 北京:中国铁道出版社,2004.3
(8)李群芳 单片机原理、接口及应用—嵌入式系统技术基础. 北京:清华大学出版社,2005.3
六、硬件电路图
七、程序清单
#include <reg52.h>
#include <intrins.h>
#define uint unsigned int
#define uchar unsigned char
sbit k1 = P1^0; //定义按键K
sbit k2 = P1^1;
sbit k3 = P1^2;
sbit k4 = P1^3;
unsigned char LED[8]={0x00};//数码管显示缓冲区
unsigned int t; //定时器0单位时间标识值
int n; //计数器1单位时间标志值
unsigned char a=0,b=0,c=0,d=0,e=0,f=0; //待显示位置的形参
unsigned char hour=23; //定义小时变量并赋初值
unsigned char minute=54; //定义分钟变量并赋初值
unsigned char second=50; //定义秒变量并赋初值
unsigned char A=0; //百秒、千秒形参
unsigned char B=0; //十秒、秒形参
unsigned char C=0; //1/10秒、1/100秒形参
unsigned char start_flag=0; //秒表开始计时标志位,为2开始
unsigned char k1_flag=0; //调时对象选择标识:hour,minute,second,循环调节
unsigned char k2_flag=1; //秒表暂停标识,为0:暂停,为1:继续
unsigned char set_flag=0; /时间设置标志位,为0:正常走时,为1:停止走时并进入调时状态
unsigned char choose_flag=0; //显示模式选择标识。为0:时钟模式,为1:秒表模式
unsigned char wei[8]={0xf7,0xfb,0xfd,0xfe,0x7f,0xbf,0xdf,0xef};
unsigned char code duan[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x40};
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<函数申明>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
void delay_ms(uint z); //ms延时函数申明
void display(); //缓冲区数值显示函数申明
void disp_time(); //时钟模式下显示处理函数申明
void system_init(); //初始化TMOD申明
void disp_miao(); //秒表模式下显示处理函数申明
void key_scan(); //按键检测、键值处理函数申明
void key_scan()
{
if(k1==0)
{
delay_ms(10); //去抖
if(k1==0)
{
while(!k1); //等待按键释放
TR0=0; //调时模式下停止走时
set_flag=1; //设置相应标志位
k1_flag++;
if(k1_flag==4)k1_flag=1;
}
}
if(k4==0)
{
delay_ms(10);
if(set_flag==0&k4==0) //进入秒表模式
{
while(!k4);
start_flag++;
if(start_flag==2) //进入秒表模式下,全部清空,等待启动
{
TR1=1; //启动秒表
start_flag=0;
}
choose_flag++;
if(choose_flag==3)
{
TR0=1;
choose_flag=0;
}
}
if(k4==0&set_flag==1)
{
while(!k4);
set_flag=0;
k1_flag=0;
TR0=1;
}
}
if(k2==0)
{
delay_ms(10);
if(k2==0&choose_flag>=1)
{
while(!k2);
k2_flag++;
if(k2_flag==2)k2_flag=0;
TR1=k2_flag;
}
if(k2==0&set_flag==1)
{
while(!k2);
switch (k1_flag)
{
case 1:hour++;if(hour==24)hour=0;break;
case 2:minute++;if(minute==60)minute=0;break;
case 3:second++;if(second==60)second=0;break;
}
}
}
if(k3==0)
{
delay_ms(10);
if(k3==0&choose_flag>=1)
{
while(!k3);
H=0;L=0;M=0;
TR1=0;
}
if(k3==0&set_flag==1)
{
while(!k3);
switch (k1_flag)
{
case 1:if(hour==0)hour=24;hour--;break;
case 2:if(minute==0)minute=60;minute--;break;
case 3:if(second==0)second=60;second--;break;
}
}
}
}
void display()
{
uchar i;
for(i=0;i<6;i++)
{
P0=LED[i];
P2=wei[i];
delay_ms(2);
P2=0xff;
}
}
void disp_time()
{
a=second%10;
b=second/10;
c=minute%10;
d=minute/10;
e=hour%10;
f=hour/10;
LED[0]=duan[a];
LED[1]=duan[b];
LED[2]=duan[c];
LED[3]=duan[d];
LED[4]=duan[e];
LED[5]=duan[f];
}
void disp_miao()
{
a=C%10;
b=C/10;
c=B%10;
d=B/10;
e=A%10;
f=A/10;
LED[0]=duan[a];
LED[1]=duan[b];
LED[2]=duan[c];
LED[3]=duan[d];
LED[4]=duan[e];
LED[5]=duan[f];
}
void delay_ms(uint z)
{
uint y;
while(z--)
{
for(y=110;y>0;y--);
}
}
void system_init()
{
TMOD=0X11; //定时器0,1设为16位定时器/计数器
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;//定时器0装初值,定时时间50ms
TH1=(65536-10000)/256;
TL1=(65536-10000)%256;//计数器1装初值,定时时间10ms
EA=1; //开总中断
ET1=1; //开定时器/计数器1中断
ET0=1; //开定时器/计数器0中断
TR0=1; //打开定时器0
}
void main ()
{
system_init();
P0=0xff; //段选
P2=0xff;
while(1)
{
if(choose_flag==0)
{
disp_time();
}
else
{
disp_miao();
}
display();
key_scan();
}
}
void timer0() interrupt 1
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
t++;
if(t==20)
{
t=0;
second++;
if(second==60)
{
minute++;
second=0;
if(minute==60)
{
hour++;
minute=0;
if(hour==24)
{
hour=0;
}
}
}
}
}
void timer1() interrupt 3
{
TH1=(65536-10000)/256;
TL1=(65536-10000)%256;//计数器1装初值,定时时间10ms
n++;
if(n==1)
{
n=0;
C++;
if(C==100)
{
C=0;
B++;
if(B==100)
{
B=0;
A++;
if(A==100)
{
A=0;
}
}
}
}
}