课程论文报告
——万年历
学院:计算机与信息工程学院
班级:12级通信3班
学号:20124756
姓名:王智
1.引言
万年历是我国古代传说中最古老的一部太阳历。为纪念历法编撰者万年的功绩,便将这部历法命名为“万年历”。而现在所使用的万年历,实际上就是记录一定时间范围内(比如100年或更多)的具体阳历或阴历的日期的年历,方便有需要的人查询使用,与原始历法并无直接联系。而随着微电子技术的高速发展,人类用于计时的工具也在不断发展更新,单片机技术的出现使得万年历有了新的发展方向。单片机以其体积小、功能全、性价比高等诸多优点,在工业控制、家用电器、通信设备、信息处理、尖端武器等各种测控领域的应用中独占鳌头,单片机开发技术已成为电子信息、电气、通信、自动化、机电一体化等专业技术人员必须掌握的技术。目前世界上单片机年产量已达十多亿片,通常是当年微处理器产量的4-5倍以上。用最少的芯片就能实现最强大的功能,这是将来电子产品的主流方向,它将无可置疑地一步步取代其它同类产品,其数量之大和应用面之广,是其它任何类型的计算机所无法比拟的。
以基于单片机的万年历作为设计的课题,因为它有很好的开放性和可发挥性,对作者的要求比较高,不仅考察了对单片机的掌握能力而且强调了对单片机扩展的应用。另外液晶显示的万年历已经越来越流行,特别适合在家庭居室、办公室、大厅、会议室、车站和广场等地方使用,它具有显示清晰直观、走时准确、可以进行夜视等功能,并且还可以扩展出其它多种功能。所以,电子万年历作为设计课题很有价值。
2.内容设计
A,单片机芯片选择:
采用STC89C52芯片,STC89C52是一种低功耗、高性能的8位CMOS微控制器,具有8K的可编程Flash存储器。同样具有AT89S51的功能,且具有在线编程可擦除技术,当对电路进行调试时,由于程序的错误修改或对程序的新增功能需要烧入程序时,不需要对芯片多次拔插,所以不会对芯片造成损坏,因此选择采用STC89C52作为主控制系统核心。
B.显示模块选择:
采用1602液晶显示屏,该液晶显示屏的显示功能强大,内置192种字符,可显示大量符号、数字,清晰可见,而且功率消耗小寿命长抗干扰能力强。所以在此设计中采用1602液晶显示屏。
C.时钟芯片选择:
采用DS1302时钟芯片实现时钟,DS1302芯片是一种高性能的时钟芯片,可自动对秒、分、时、日、周、月、年进行计数,而且精度高,位的RAM做为数据暂存区,工作电压2.5V~5.5V范围内,2.5V时耗电小于300nA。所以本设计采用DS1302时钟芯片。
3.原理图
系统整体的设计框图:
为使时钟走时与标准时间一致,校时电路是必不可少的,键盘模块用来校正液晶上显示的时间;STC89C52单片机通过输出各种电脉冲信号来驱动控制各部分正常工作;而系统的时间数据则最终通过液晶模块显示出来。
在STC89C52芯片上,拥有灵巧的8位CPU和在线系统可编程Flash,使得STC89C52为众多嵌入式控制应用系统提供高灵活、超有效的解决方案。STC89C52具有以下标准功能:8K字节Flash,256字节RAM,32位I/O 口线,看门狗定时器,2个数据指针,三个16位定时器/计数器,一个6向量2级中断结构,全双工串行口,片内晶振及时钟电路。另外,STC89C52可降至0Hz静态逻辑操作,支持2种软件可选择节电模式。空闲模式下,CPU 停止工作,允许RAM、定时器/计数器、串口、中断继续工作。掉电保护方式下,RAM内容被保存,振荡器被冻结,单片机一切工作停止,直到下一个中断或硬件复位为止。引脚排列如图所示。
从引脚功能来看,可将引脚分为三部分:
1、电源及时钟引脚
VCC:接+5V电源;VSS:接地;XTAL1和XTAL2:时钟引脚,外接晶体引线端。当使用芯片内部时钟时,此两引脚端用于外接石英晶体和微调电容;当使用外部时钟时,用于接外部时钟脉冲信号。
2、控制引脚
RST/VPT:RST是复位信号输入端,VPT是备用电源输入端。当RST输入端保持2个机器周期以上高电平时,单片机完成复位初始化操作。当主电源VCC发生故障而突然下降到一定低电压或断电时,第2功能VPT将为片内RAM提供电源以保护片内RAM中的信息不丢失。
ALE/PROG:地址锁存允许信号输出端。在存取外存储器时,用于锁存低8位地址信号。当单片机正常工作后,ALE端就会周期性地以时钟振荡频率的1/6固定频率向外输出正脉冲信号。此引脚的第2功能PROG是对片内带有4K字节EPROM的8751固化程序时,作为编程脉冲输入端。
PSEN:程序存储允许输出端。是片外程序存储器的读选通信号,低电平有效。CPU从外部程序存储器取指令时,PSEN信号会自动产生负脉冲,作为外部程序存储器的选通信号。
EA/VPP:程序存储器地址允许输入端。当EA为高电平时,CPU执行片内程序存储器指令,但当PC中的值超过0FFFFH时,将自动转向执行片外程序存储器指令;当EA为低电平时,CPU只执行片外程序存储器指令。对8031单片机,EA必须接低电平。在8751中,当对片内EPROM编程时,该端接21V的编程电压。
3、I/O口引脚
P0.0~P0.7:P0口是一个8位漏极开路的双向I/O口。作为输出口,每位能驱动8个TTL逻辑电平。对P0端口写“1”时,引脚用作高阻抗输入。当访问外部程序和数据存储器时,P0口也被作为低8位地址/数据复用。
P1.0~P1.7:P1口是一个具有内部上拉电阻的8位双向I/O口。作为输出口,每位能驱动4个TTL逻辑电平。此外,P1.0和P1.2分别作定时器/计数器2的外部计数输入(P1.0/T2)和时器/计数器2的触发输入(P1.1/T2EX)。在flash编程和校验时,P1口接收低8位地址字节。
P2.0~P2.7:P2口是一个具有内部上拉电阻的8位双向I/O口。作为输出口,每位能驱动4个TTL逻辑电平。在访问外部程序存储器或用16位地址读取外部数据存储器时,P2口送出高八位地址。在使用8位地址访问外部数据存储器时,P2口输出P2锁存器的内容。在flash编程和校验时,P2口也接收高8位地址字节和一些控制信号。
P3.0~P3.7:P3口是一个具有内部上拉电阻的8位双向I/O口。作为输出口,每位能驱动4个TTL逻辑电平。P3口亦作为STC89C52特殊功能(第二功能)使用。在flash编程和校验时,P3口也接收一些控制信号。
一、复位电路的设计
复位电路是使单片机的CPU或系统中的其他部件处于某一确定的初始状态,并从这上状态开始工作。
(1)单片机常见的复位电路
通常单片机复位电路有两种:上电复位电路,按键复位电路。上电复位电路:上电复位是单片机上电时复位操作,保证单片机上电后立即进入规定的复位状态。它利用的是电容充电的原理来实现的。按键复位电路:它不仅具有上电复位电路的功能,同时它的操作比上电复位电路的操作要简单的多。如果要实现复位的话,只要按下RESET键即可。它主要是利用电阻的分压来实现的
在此设计中,采用的按键复位电路。按键复位电路如图所示。
复位电路
(2)复位电路工作原理
上电复位要求接通电源后,单片机自动实现复位操作。上电瞬间RESET引脚获得高电平,随着电容的充电,RERST引脚的高电平将逐渐下降。RERST引脚的高电平只要能保持足够的时间(2个机器周期),单片机就可以进行复位操作。上电与按键均有效的复位电路不仅在上电时可以自动复位,而且在单片机运行期间,利用按键也可以完成复位操作
2、晶振电路的设计
晶振电路用于产生单片机工作所需要的时钟信号,而时序所研究的是指令执行中各信号之间的相互关系。单片机本身就如一个复杂的同步时序电路,为了保证同步工作方式的实现,电路应在唯一的时钟信号控制下严格地工作。
通常在引脚Xl和X2跨接石英晶体和两个补偿电容构成自激振荡器,如图中X2、C3、C4。可以根据情况选择6MHz、12MHz或24MHz等频率的石英晶体,补偿电容通常选择30pF左右的瓷片电容。
时钟振荡电路
二.时钟电路模块的设计
DS1302是DALLAS公司推出的涓流充电时钟芯片,内含有一个实时时钟/日历和31字节静态RAM,通过简单的串行接口与单片机进行通信。图所示为DS1302的引脚排列,其中VCC1为后备电源,VCC2为主电源。所以在主电源关闭的情况下,也能保持时钟的连续运行。DSX1和DSX2是振荡源,外接32.768KHz晶振用来为芯片提供计时脉冲。RST是复位/片选线,通过把RST输入驱动置高电平来启动所有的数据传送。RST输入有两种功能:首先,RST接通控制逻辑,允许地址/命令序列送入移位寄存器;其次,RST提供终止单字节或多字节数据的传送手段。当RST为高电平时,所有的数据传送被初始化,允许对DS1302进行操作。如果在传送过程中RST置为低电平,则会终止此次数据传送,I/O引脚变为高阻态。上电行动时,在VCC大于等于2.5V之前,RST必须保持低电平。在SCLK为低电平时,才能将RST置为高电平,I/O为串行数据输入端(双向)。SCLK始终是输入端。
DS1302的硬件接线图
时钟芯片DS1302的工作原理:
(1) DS1302的控制字节
DS1302控制字节的高有效位(位7)必须是逻辑1,如果它为0,则不能把数据写入DS1302中,位6如果0,则表示存取日历时钟数据,为1表示存取RAM数据;位5至位1指示操作单元的地址;最低有效位(位0)如为0表示要进行写操作,为1表示进行读操作,控制字节总是从最低位开始输出
(2) 数据输入输出(I/O)
在控制指令字输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从低位即位0开始。同样,在紧跟8位的控制指令字后的下一个SCLK脉冲的下降沿读出DS1302的数据,读出数据时从低位0位到高位7。
(3) DS1302的寄存器
DS1302有12个寄存器,其中有7个寄存器与日历、时钟相关,存放的数据位为BCD码形式。“CH”是时钟暂停标志位,当该位为1时,时钟振荡器停止,DS1302处于低功耗状态;当该位为0时,时钟开始运行。“WP”是写保护位,在任何的对时钟和RAM的写操作之前,“WP”必须为0。当“WP”为1时,写保护位防止对任一寄存器的写操作。
此外,DS1302 还有年份寄存器、控制寄存器、充电寄存器、时钟突发寄存器及与RAM相关的寄存器等。时钟突发寄存器可一次性顺序读写除充电寄存器外的所有寄存器内容。DS1302与RAM相关的寄存器分为两类:一类是单个RAM单元,共31个,每个单元组态为一个8位的字节,其命令控制字为C0H~FDH,其中奇数为读操作,偶数为写操作;另一类为突发方式下的RAM寄存器,此方式下可一次性读写所有的RAM的31个字节,命令控制字为FEH(写)、FFH(读)。
三.独立式键盘设计
实现键盘控制的方法有多种,在本系统中,我们采用了STC89C52单片机来进行控制,因为单片机可以很好的解决键抖动。
由若干个按键组成一个键盘,其电路结构可分为独立式键盘和矩阵式键盘两种。本设计采用的是独立式键盘。键盘的工作方式可分为编程控制方式和中断控制方式。CPU在一个工作周期内,利用完成其他任务的空余时间,调用键盘扫描子程序,经程序查询,若无键操作,则返回;若有键操作,则进而判断是哪个键,并执行相应的键处理程序。这种方式为编程扫描方式。由于单片机在正常应用过程中,可能会经常进行键操作,因而编程控制方式使CPU经常处于工作状态, 在进行本次设计中,只涉及到了设置、上调、下调三个功能。因此采用独立式键盘。如下图所示:
按键的设计
四.显示模块的设计
本设计中由于要对时间、温度进行显示,所以选择液晶显示屏1602模块作为输出。1602字符型LCD通常有14条引脚线或16条引脚线的LCD,多出来的2条线是背光电源线。它可以显示两行,每行16个字符,采用单+5V电源供电,外围电路配置简单,价格便宜,具有很高的性价比。1602液晶模块内部的字符发生存储器(CGROM)已经存储了160个不同的点阵字符图形,这些字符有:阿拉伯数字、英文字母的大小写、常用的符号、和日文假名等,每一个字符都有一个固定的代码,比如大写的英文字母“A”的代码是01000001B(41H),显示时模块把地址41H中的点阵字符图形显示出来,我们就能看到字母“A”。 管脚功能如表1所示:
表1 LCD1602引脚功能
LCD1602主要管脚介绍:V0为液晶显示器对比度调整端,接正电源时对比度最弱,接地电源时对比度最高,对比度过高时会产生鬼影使用时可以通过一个10K的电位器调整对比度。RS为寄存器选择端,高电平时选择数据寄存器,低电平时选择指令寄存器。R/W为读写信号线端,高电平时进行读操作,低电平时进行写操作。当RS和R/W共同为低电平时可以写入指令或者显示地址;当RS为高电平R/W 为低电平时可以写入数据。E为使能端,当E端由高电平跳变成低电平时,液晶模块执行命令。
将L1602的RS端和P2.0,R/W端和P2.1, E 端和P2.2相连,当RS=0时,对LCD1602写入指令;当RS=1时,对LCD1602写入数据。当R/W端接高电平时芯片处于读数据状态,反之处于写数据状态,E端为使能信号端。当R/W为高电平,E端也为高电平,RS为低电平时,液晶显示屏显示需要显示的示数。图为1602液晶显示屏与单片机的硬件连接图。
图LCD液晶与单片机硬件连线图
4.程序流程图
总流程
DS1302时钟程序流程图
LCD显示程序流程图
DS1302读写程序
DS1302是SPI总线驱动方式。它不仅要向寄存器写入控制字,还需要读取相应寄存器的数据。要想与DS1302通信,首先要先了解DS1302的控制字。DS1302的控制字如图5.4所示。
DS1302的控制字
控制字总是从最低位开始输出。在控制字指令输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从最低位(0位)开始。同样,在紧跟8位的控制字指令后的下一个SCLK脉冲的下降沿,读出DS1302的数据,读出的数据也是从最低位到最高位。数据读写时序如图5.5、5.6所示。
图5.5 单字节读
图5.6 单字节写
在进行任何数据传输时,RST必须被置高电平,每个SCLK为上升沿时数据被输入,下降沿时数据被输出。先把RST置低,禁止数据传输,SCLK置低,清零时钟总线,RST再置高,允许数据传输。传送完成后,RST置低,禁止字节的传送。程序见附录二。
5.2.2液晶显示程序
1602通过D0~D7的8位数据端传输数据和指令,其模块内的控制器有11条控制指令。当液晶显示屏的接口电路与单片机系统I/O按照并行数据传输方式连接完成以后,即可以对STC89C52单片机进行编程。在液晶屏完成显示之前首先要对液晶进行初始化。
5.结论
万年历设计的所有内容就介绍完了,通过本次课程设计较系统地掌握有关单片机控制的设计思想和设计方法,主要对STC89C52的结构、功能、内部资源等了解并对其进行测试和加以应用的知识得到学习。进行这次设计,从最简单的查资料、了解各个元件的功能起步,再确定设计方案、画流程图、编写程序到最后进行仿真测试,这次课题设计可以说成功完成。系统的硬件、软件设计合理,功能完备,运行稳定、可靠。实验结果表明此万年历实现后具有读取方便、显示直观、功能多样、电路简洁、等诸多优点,符合电子仪器仪表的发展趋势,具有广阔的市场前景。
在整个设计过程中,充分发挥了人的主观能动性,提高了自学能力,学到了许多没学到的知识。程序编写中,由于思路不清晰,开始时遇到了很多的问题,后来向同学请教,理清了思路,完成对程序的编写。通过设计提高了对单片机的认识,进一步熟悉和掌握单片机的结构及工作原理。通过实际程序设计和调试,逐步掌握模块化程序设计方法和调试技术,提高软件设计、调试能力;通过这次设计熟悉以单片机核心的应用系统开发的全过程,掌握硬件电路设计的基本方法和技术,掌握相关电路参数的计算方法。最终较好的完成了设计,达到了预期的目的,完了最初的设想。
但是由于时间和个人能力的原因,整个系统看起来还是显得非常的简单,只实现了一些最基本的功能,还有许多不足和可以扩展的地方,这些有待以后来弥补,还望老师予以指正和修改。
参考文献:
【1】 祈伟杨亭.单片机C51程序设计教程与实验 北京航空航天大学出版社20##年1月
【2】张瑞玲.单片机原理与应用 西北工业大学出版社20##年12月
【3】赵亮,侯国锐.单片机C语言编程与实例[M].北京:人民邮电出版社,2003.
【4】李全利 单片机原理及接口技术
【5】89C52 、1602液晶显示器 、DS1302时钟芯片 百度百科
第二篇:C语言万年历设计报告
C语言课程
设计报告
题目:编写万年历系统
年级:
专业:
班级:
姓名:
学号:
目 录
第1章 设计目的
第2章 需求分析
第3章 概要设计
第4章 源程序清单
第5章 执行结果
1)登陆界面
2)显示当前日月历
3)显示具体日期信息
第6章 程序问题调试与课程设计心得
第1章设计目的
设计目的(实验要求):
1.模仿现实生活中的挂历.
2.当前页以系统当前日期的月份为准显示当前月的每一天(显示出日及对应的星期几).
3.当系统日期变到下一月时,系统自动翻页到下一月.
第2章需求分析
(1)程序运行时,首先给出菜单选项和输入提示:
(2)用户可以通过主菜单选择不同选项。输入1,通过输入当前日期可显示当前月的日历;输入2,通过输入具体日期(中间以空格分开)可显示该日具体信息;输入3,则退出查询系统。
第3章 概要设计
1.菜单选择界面可通过下面所示函数时现;
主要思路:
通过while语句与swich…case以及if条件语句实现菜单的循环功能和不同选项的功能实现。
闰年与平年的判断:
代码如下:
void main()
{
int year,month,day,a,b;
while(1)
{
printf("\n\n\n\n");
printf(" **********************************************************\n\n");
printf(" * * \n");
printf(" Welcome to enter calendar system \n");
printf(" * * \n\n");
printf(" **********************************************************\n\n\n");
printf(" 1 The month of current date 2 Date 3 Exit\n");
printf(" please import your choose<1~3>,press Enter to define: ");
scanf("%d",&b);
switch(b)
{
case 1:
printf("\n Please import the year,month and day which you want to check,the year,month and day separation by blank space: ");
scanf("%d%d%d",&year,&month,&day);
a=x3(year,month,1);
if(a==0)
{
printf("Error!Please import again.\n");
break;}
yueli(year,month,day);
break;
case 2:
printf("\n Please import the year,month and day which you want to check,the year,month and day separation by blank space: ");
scanf("%d%d%d",&year,&month,&day);
a=x3(year,month,day);
if(a==0)
{
printf("Error!Please import again.\n");
break;}
rili(year,month,day);
break;
case 3:
exit(0);
default:
printf("Error!Please import again.\n\n");
}
}
2.闰年的判断:
计算所输入的年份是否可被4整除,若不能被整除,为平年。若可被4整除,计算此年份能否被100整除,不能被100整除则为闰年。既能被4整除,也能被100整除的年份,若也能被400整除,则为闰年,否则为平年。闰年366天,平年365天
代码:
int runnian(int year)
{
if(year%4==0&&year%100!=0||year%400==0)
return 1;
else
return 0;
}
3.判断某月的天数;
通过函数:
int day1(int year,int month)
{
int a[12]={31,28,31,30,31,30,31,31,30,31,30,31};
若为闰,则二月为29天。
第4章 源程序清单
#include
#include/*定义头文件*/
int runnian(int year)
{
if(year%4==0&&year%100!=0||year%400==0) /*利用函数判断输入年份是闰年还是平年*/
return 1;
else
return 0;
}
int day1(int year,int month)
{
int a[12]={31,28,31,30,31,30,31,31,30,31,30,31};/*定义数组确定12个月每个月的天数*/
if(runnian(year)) /*如果输入年份是闰年*/
a[1]=29; /*二月29天*/
return a[month-1];
}
int day2(int year,int month,int day)
{
int i,sum=0;
for(i=1;i
{
sum+=day1(year,i);
}
sum+=day;
return sum;
}
int week1(int year,int month,int day)
{
int number=(year-1)+(year-1)/4-(year-1)/100+(year-1)/400+day2(year,month,day);
number%=7;
return number;
}
char *Week2(int number)
{
switch(number) /*分类一周七天*/
{
case 0:return "Sunday";
case 1:return "Monday";
case 2:return "Tueaday";
case 3:return "Wednesday";
case 4:return "Thurday";
case 5:return "Friday";
case 6:return "Saturday";
default:return "Error!please import again.\n";/*不符和上面七种情况的报错提示*/
}
}
void x1(int year,int month,int day)
{
int number=week1(year,month,day);
if(runnian(year))printf("common year");/*年份为闰年则输出"闰年"*/
else printf("common year");
printf(" %d year %d month %d day%10s\n\n",year,month,day,Week2(number));/*按年 月 日输出结果*/
}
void x2(int year,int month)
{
int max,number,i,j=1;
max=day1(year,month);
number=week1(year,month,1);
printf("%2s%2s%2s%2s%2s%2s%2s\n"," Sunday "," Monday "," Tueaday"," Wednesday "," Thurday "," Friday ","Saturday ");/*按照间隔打印日历*/
for(i=0;i
{
printf(" ");
}
while(j<=max)
{
printf("%8d",j);
if(i%7==6)
{
printf("\n");
}
i++;
j++;
}
printf("\n\n");
}
int x3(int year,int month,int day)
{
if(year<0||month<1||month>12||day<1||day>day1(year,month)) /*当输入值出现这些情况时*/
return 0; /*返回0*/
else
return 1; /*返回1*/
}
void yueli(int year,int month,int day)
{
if(runnian(year))
printf("leap year");
else printf("common year");
printf(" %d year %d month %d day\n\n",year,month,day);
x2(year,month);
}
void rili(int year,int month,int day)
{
printf("\n");
x1(year,month,day);
}
void main()
{
int year,month,day,a,b;
while(1)
{
printf("\n\n\n\n");
printf(" **********************************************************\n\n");
printf(" * * \n");
printf(" Welcome to enter calendar system \n");
printf(" * * \n\n");
printf(" **********************************************************\n\n\n");
printf(" 1 The month of current date 2 Date 3 Exit\n");
printf(" please import your choose<1~3>,press Enter to define: ");
scanf("%d",&b);
switch(b)
{
case 1: /*选择1时*/
printf("\nPlease import the year,month and day which you want to check,the year,month and day separation by blank space: "); /*给出输入提示*/
scanf("%d%d%d",&year,&month,&day); /*输入需要查询的年 月 日*/
a=x3(year,month,1);
if(a==0)
{
printf("Error!Please import again.n");/*输入值异常时报错*/
break;
}
yueli(year,month,day);
break;
case 2: /*选择2时*/
printf("\nPlease import the year,month and day which you want to check,the year,month and day separation by blank space: "); /*给出输入提示*/
scanf("%d%d%d",&year,&month,&day);/*输入年 月 日*/
a=x3(year,month,day);
if(a==0)
{
printf("Error!Please import again.n");/*输入值异常时报错*/
break;
}
rili(year,month,day);
break;
case 3:
exit(0);
default:
printf("Error!Please import again.\n\n");
}
}
}
第5章 执行结果
1)登陆界面
2)显示当前日历月
3)显示具体日期信息
第6章 程序问题调试与课程设计心得
1)程序问题调试
(1) 调用的week2函数中,忘记在万年历中一周的第一天是sunday。
(2)定义的runnian语句中没有返回值,无法判断是否为闰年。(3)定义的x1与x2函数实现的功能时混淆。
(4)被调用函数X3中的返回值要用于主调函数中的判断,但是未在主调函数中赋值给整型变量。
(5)对于数组,数组的初始化要进行正确的赋值,以及对于数组的使用与变更。
(6)程序的scanf语句中取地址符号不能省。
2)课程设计心得
通过本次课程设计我通过自己的实践对书本上的知识进行了与生活相关的运用。在这个程序中,主要应用了这学期所学习的循环,条件语句,数组以及调用函数。这些知识的应用使我对课本内容的认识得到了加深。
在编写程序的过程中,也出现了很多错误。比如程序语句错误导致检测时连续报错;函数语句编写错误导致输出结果与实际不符等。出现这些情况后,通过对教材的深入理解和解度,我发现了自己的程序的问题所在并进行了改正。最后得到了正确的结果。