单片机课程设计
题 目 基于单片机的万年历
目 录
1 绪论 .................................................................................. 1 1.1设计目的 .................................................................... 1 1.2 发展现状 ................................................................... 1 1.3 解决问题 ................................................................... 1 2基于单片机的万年历主要元器件介绍 .......................... 2 2.1本设计所需的主要元器件 ........................................ 2
2.2 STC89C52RC单片机 ................................................ 2 2.2.1单片机主要特性 ................................................................ 2 2.2.2单片机引脚说明 ................................................................ 4 2.3 DS1302时钟芯片 ...................................................... 4 2.4 LCD1602 .................................................................... 6 2.5独立按键 .................................................................... 7 3基于单片机的万年历的软件设计 .................................. 7 3.1 DS1302时钟芯片程序设计 ...................................... 8 4 总结 .................................................................................. 9 参考文献 ............................................................................. 11 附录1:总体电路原理图 ................................................. 12 附录2:总程序 ................................................................. 13 附录3:实物图 ................................................................. 25
1 绪论
1.1设计目的
培养和锻炼在学习完本课后综合运用所学理论知识,解决实际工程设计和应用问题的能力。通过课程设计,要求熟悉和掌握仿真系统的软件设计方法、设计步骤,以及硬件的设计方法、设计步骤,得到仿真系统应用方面的初步练习。
本设计主要是采用51单片机来实现。自己动手设计与制作可以对硬件的结构和功能有更深的认识,并与软件结合,以达到理论与实践更好的结合,进一步提高综合运用所学知识进行设计的能力。这是对自己大学四年的学习的检验,具有重要的意义。
1.2 发展现状
目前市场上的电子万年历并不是采用51单片机作为主控制器的,基于单片机的万年历一般是学生和单片机爱好者在进行设计,谈不上占有市场。也许就是这样,研究单片机万年历的人不在少数,并且都在努力。努力不是单方面的,单片机的功能也应该要提高,STC89C52单片机就是这样的例子,其功能虽然没有大幅度提高,但使用起来更方便了。我相信,在不久的将来肯定会有功能更强大成本更低的单片机出现,给我们的设计带来更多的便利。
1.3 解决问题
本课题主要通过单片机的功能和应用,利用Keil[1]编程软件和Proteus仿真软件进行设计,并制作实物。设计要达到预期的效果要解决以下问题:
1
(1)认真设计好万年历的逻辑原理图;
(2) 熟练使用C语言,运用Keil编程软件进行软件设计; (3) 在Proteus仿真平台上,对程序进行编译仿真; (4) 认真仔细地对万年历进行组装焊接;
2基于单片机的万年历主要元器件介绍
2.1本设计所需的主要元器件
(1)单片机:STC89C52RC; (2)时钟芯片:DS1302; (3)液晶:LCD1602; (4)按钮开关。
2.2 STC89C52RC单片机
本设计采用STC89C52RC单片机,8K字节可编程闪烁存储器。STC89系列单片机是MCS-51系列单片机的派生产品,它们在指令系统、硬件结构和片内资源上与标准8052单片机完全兼容。STC89系列是以8051为基核开发出的CMOS工艺单片机,DIP封装系列与8051为pin-to-pin(引脚对引脚)兼容。STC89系列单片机高速,低功耗,其程序写入时可通过串口采用STC-ISP.exe软件下载,不占用用户资源,学习单片机时较好的选择。
2.2.1单片机主要特性
(1) CPU:由运算和控制逻辑组成,同时还包括中断系统和部分外部特殊功能寄存器。中央处理器(CPU)是整个单片机的核心部件,是8位数据宽度的处理器,能处理8位二进制数据或代码,
2
CPU负责控制、指挥和调度整个单元系统协调的工作,完成运算和控制输入输出功能等操作。
(2) RAM:用于存放要读写的数据,如运算的中间结果、最终结果以及欲显示的数据。8051内部有128个8位用户数据存储单元和128个专用寄存器单元,它们是统一编址的,专用寄存器只能用于存放控制指令数据,用户只能访问,而不能用于存放用户数据,所以,用户能使用的RAM只有128个,可存放读写的数据,运算的中间结果或用户定义的字型表。
(3) ROM:8051共有4096个8位掩膜ROM,用于存放用户程序,原始数据或表格。
(4) I/O口:四组8位并行I/O口(P0、 P1、P2和P3),既可用作输入,也可用作输出。
(5) T/C定时/计数器:两个16位的可编程定时/计数器,既可以工作在定时模式,也可以工作在计数模式。
(6) 5个中断源的中断控制系统:8051具备较完善的中断功能,有两个外中断、两个定时/计数器中断和一个串行中断,可满足不同的控制要求,并具有2级的优先级别选择。
(7) 全双工串行口:一个全双工UART(通用异步接收发送器)的串行I/O口,用于实现单片机之间或单片机与微机之间的串行通信;
(8) 片内振荡器和时钟产生电路,石英晶体和微调电容需要外接。最高振荡频率取决于单片机型号及性能。
3
2.2.2单片机引脚说明
图2-1 STC89C52RC封装引脚图
STC89C52RC的引脚封装和8051的引脚封装是一样的,均采用40Pin封装的双列直插DIP结构。下图是它们的引脚配置,40个引脚中,Pin40为正电源,Pin20为地线;外置石英振荡器的时钟线Pin18和Pin19两根;4组8位共32个I/O口(P0、P1、P2和P3),中断口线与P3口线复用。本设计只是实现简单的读写功能,不需要用到I/O口的第二功能。单片机引脚封装如图2.1所示。
2.3 DS1302时钟芯片
市场上可以选择的时钟芯片很多,功能也不尽相同,价格各异。DS1302是美国DALLAS公司推出的一种串行接口实时时钟芯片。芯片内部具有可编程日历时钟和31个字节的静态RAM,它可以对年、月、日、星期、时、分、秒进行计时。DS1302引
4
脚封装如图2-2所示。
图2-2 DS1302封装引脚图
DS1302时钟芯片的引脚功能如下: (1) Pin1:Vcc2为主电源。
(2) Pin8:Vcc1为后备电源。在主电源关闭的情况下,也能保持时钟的连续运行。DS1302由Vcc1或Vcc2两者中的较大者供电。当Vcc2大于Vcc1+0.2V时,Vcc2给DS1302供电。当Vcc2小于Vcc1时,DS1302由Vcc1供电。
(3) Pin2、3:X1、X2是振荡源,外接32.768kHz晶振。 (4) Pin5:RST是复位/片选线,通过把RST输入驱动置高电平来启动所有的数据传送。RST输入有两种功能:首先,RST接通控制逻辑,允许地址/命令序列送入移位寄存器;其次,RST提供终止单字节或多字节数据的传送手段。当RST为高电平时,所有的数据传送被初始化,允许对DS1302进行操作。如果在传送过程中RST置为低电平,则会终止此次数据传送,I/O引脚变为高阻态。上电运行时,在Vcc>2.0V之前,RST必须保持低电平。
5
只有在SCLK为低电平时,才能将RST置为高电平。
(5) Pin6:I/O为串行数据输入输出端(双向),在控制指令字输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从低位即位0开始。同样,在紧跟8位的控制指令字后的下一个SCLK脉冲的下降沿读出DS1302的数据,读出数据时从低位0位到高位7。
图2-3 DS1302
2.4 LCD1602
1602通过D0~D7的8位数据端传输数据和指令。显示模式设置(初始化)00111000[0x38]设置16×2显示,5×7点阵,8位数据接口;显示开关及光标设置:(初始化) 00001DCBD显示(1有效)、C光标显示(1有效)、B光标闪烁(1有效)。000001NS N=1(读或写一个字符后地址指针加1并且光标加1),N=0(读或写一个字符后地址指针减1并且光标减1),S=1且N=1(当写一个字符后,整屏显示左移),S=0当写一个字符后,整屏显示不移动。数据指针设置:数据首地址为80H,所以数据地址为80H+地址码(0-27H,40-67H)。其他设置:01H(显示清屏,数据指针=0,所有显示=0);02H(显示回车,数据指针=0)。
6
2.5独立按键
本设计采用的4个独立按键,分别是:“设置”、“确认”、“加”、“减”。判断“设置”键是否按下及第几次按下可以分别对日期、时间、星期选定,然后按“加”、“减”即可进行设置,“确认”键按下后,新数据即写入时钟芯片。图2-4 时间设置路。
图2-4 时间设置电路
3基于单片机的万年历的软件设计
本设计利用Keil(C51) 软件进行程序的编写。C语言是一种编译型程序设计语言,它兼顾了许多种高级语言的特点,并且具备汇编语言的功能,C 语言可以像汇编语言一样对位、字节和地址进行操作。目前,使用C语言进行程序设计已经成为软件开发的一个主流。C语言程序本身不依赖于机器硬件系统,基本上不作修改就可将程序从不同的单片机中移植过来。用C语言开发系统可以大大缩短开发周期,明显增强程序的可读性,便于改进、扩充和移植。而针对8051的C语言日趋成熟,成为了专业化的
7
实用高级语言。虽然汇编语言效率高,对硬件的可操控性更强,体积小,但是不易维护,可移植性差。本设计程序实现了阳历年、月、日、时间提醒等功能。图3-1是主程序流程图。
开始 LCD、DS1302相关变量初始化 调液晶显示程序 设置键是否按调用设置子程序 刷新 图3-1 主程序流程图
3.1 DS1302时钟芯片程序设计
在本设计中,要实现万年历的功能,DS1302时钟芯片是少不得的,虽然只用单片机也可以实现,但是将会造成时间误差较大,不好调节。用DS1302时钟芯片不仅可以大大的避免那样的问题,还可以减小程序的编写量。这个模块是本设计中是很重要的模块。如图3-2是DS1302时钟芯片工作的流程图。
8
开始 相关变量初始DS1302去保护 写DS1302地址 复位端产生 一个高电平 复位端产生 一个高电平 延时一段时间 写DS1302地址 将该地址数据读出 延时一段时间 向该地址写数据 地址增加 数据读完地址增加 数据写完否 显示数据 图3-2 DS1302时钟芯片工作的流程图
4 总结
本设计在进行Proteus软件仿真时,没有成功,因为Proteus软件的元件库里没有带中文字库的液晶,要想成功仿真,需要对程序进行大范围的修改,最终决定放弃Proteus软件仿真,直接采用硬件进行调试。
硬件的线路设计和元件组装都花了较长的时间,虽然以前也焊接过一些东西,但不能说那就是有经验。以前焊接的东西都是
9
有现成的电路板,只是把元件对应的组装起来就基本没问题了。在本设计中,并没有现成的电路板,而是用万能板自己搭线组装。在整个硬件焊接到一半时,才发现将液晶屏的引脚弄反了,只能进行修改。
在进行程序调试时,一些大问题比较容易找出并改正,而一些小的问题就比较麻烦,因此一定要注意细小的问题。 本设计总体最终实现了阳历及阴历的日期显示、时间、星期、温度的显示,独立按键调节日期、星期等功能。本设计整个过程还算顺利,没出现太大的问题。
10
参考文献
[1]郑郁正.单片机原理及应用.成都:四川大学出版社,2003.9. [2]王怀平,王仁波,胡开明.Proteus仿真设计基于单片机AT89C51的电子万年历[J].科技广场,2008,10:197-198.
[3]潘永雄,沙河.电子线路CAD实用教程(第三版).西安:西安电子科技大学出版社,
2007.7(2009.8重印).
[4]谭浩强.C程序设计(第三版).北京:清华大学出版社,2005(2007重印).
[5](日)晶体管技术编辑部编,杨洋等译,宗光华校.电子技术:原理·制作·实验.北京:科学出版社,2005.
[6]赵全利,肖兴达.单片机原理及应用教程(第二版).北京:机械工业出版社,2007.7.
[7]雷伏容,张小林,崔浩.51单片机常用模块设计查询手册.北京:清华大学出版社,2010.01.
[8]肖炎根,舒望.基于实时时钟芯片的电子万年历的设计[J].电子技术,2007,Z3:91-94.
[9]刘畅生,宣宗强,雷振亚等.传感器简明手册及应用电路——温度传感器分册(上册).西安:电子科技大学出版社,2005.11.
[10]李宏,张家田.液晶显示器件应用技术.北京:机械工业出版社,2004.2.
11
附录1:总体电路原理图
总体电路原理图
12
附录2:总程序
#include\"lcd.h\"
/*******************************************************************************
* 函 数 名 : Lcd1602_Delay1ms * 函数功能
: 延时函数,延时1ms
* 输 入 : c * 输 出 : 无
* 说 名 : 该函数是在12MHZ晶振下,12分频单片机的延时。 *******************************************************************************/
void Lcd1602_Delay1ms(uint c) //误差 0us {
uchar a,b; }
/*******************************************************************************
* 函 数 名 : LcdWriteCom * 函数功能
for (; c>0; c--) { }
for (b=199;b>0;b--) {
for(a=1;a>0;a--); }
: 向LCD写入一个字节的命令
13
* 输 入 : com * 输 出 : 无
*******************************************************************************/
#ifndef LCD1602_4PINS //当没有定义这个LCD1602_4PINS时 void LcdWriteCom(uchar com) { } #else
void LcdWriteCom(uchar com) {
LCD1602_DATAPINS = com;
//由于4位的接线是接到P0口的高四
LCD1602_E = 0; //使能清零 LCD1602_RS = 0; //选择写入命令 LCD1602_RW = 0; //选择写入
//写入命令
LCD1602_E = 1; //写入时序 Lcd1602_Delay1ms(5); //保持时间 LCD1602_E = 0;
LCD1602_E = 0; //使能
LCD1602_RS = 0; //选择发送命令 LCD1602_RW = 0; //选择写入
LCD1602_DATAPINS = com; //放入命令 Lcd1602_Delay1ms(1);
//等待数据稳定 //写入命令
位,所以传送高四位不用改
Lcd1602_Delay1ms(1); LCD1602_E = 1; //写入时序
14
// }
Lcd1602_Delay1ms(5); LCD1602_E = 0; Lcd1602_Delay1ms(1);
LCD1602_DATAPINS = com << 4; //发送低四位 Lcd1602_Delay1ms(1); LCD1602_E = 1; //写入时序 Lcd1602_Delay1ms(5); LCD1602_E = 0;
#endif
/*******************************************************************************
* 函 数 名 : LcdWriteData * 函数功能
: 向LCD写入一个字节的数据
* 输 入 : dat * 输 出 : 无
*******************************************************************************/
//写入数据
#ifndef LCD1602_4PINS void LcdWriteData(uchar dat) {
LCD1602_E = 0; //使能清零 LCD1602_RS = 1; //选择输入数据 LCD1602_RW = 0; //选择写入
LCD1602_DATAPINS = dat; //写入数据 Lcd1602_Delay1ms(1); LCD1602_E = 1; //写入时序
15
}
Lcd1602_Delay1ms(5); //保持时间 LCD1602_E = 0;
#else
void LcdWriteData(uchar dat) {
LCD1602_DATAPINS = dat; //由于4位的接线是接到P0口的高四位,LCD1602_E = 0; //使能清零 LCD1602_RS = 1; //选择写入数据 LCD1602_RW = 0; //选择写入
//写入数据
所以传送高四位不用改 } #endif
/*******************************************************************************
* 函 数 名 : LcdInit() * 函数功能
: 初始化LCD屏
16
Lcd1602_Delay1ms(1); LCD1602_E = 1; //写入时序 Lcd1602_Delay1ms(5); LCD1602_E = 0;
LCD1602_DATAPINS = dat << 4; //写入低四位 Lcd1602_Delay1ms(1); LCD1602_E = 1; //写入时序 Lcd1602_Delay1ms(5); LCD1602_E = 0;
* 输 入 : 无 * 输 出 : 无
*******************************************************************************/ #ifndef void LcdInit() { } #else void LcdInit() { } #endif
/******************************************************************************* * 实验名
: 定时器实验
LcdWriteCom(0x32); LcdWriteCom(0x28);
//将8位总线转为4位总线 //在四位线下的初始化
//LCD初始化子程序
LcdWriteCom(0x38); //开显示
LcdWriteCom(0x0c); //开显示不显示光标 LcdWriteCom(0x06); //写一个指针加1 LcdWriteCom(0x01); //清屏
LcdWriteCom(0x80); //设置数据指针起点
//LCD初始化子程序
LCD1602_4PINS
LcdWriteCom(0x0c); //开显示不显示光标 LcdWriteCom(0x06); //写一个指针加1 LcdWriteCom(0x01); //清屏
LcdWriteCom(0x80); //设置数据指针起点
* 使用的IO :
* 实验效果 :1602显示时钟,按K3进入时钟设置,按K1选择设置的时分秒,按K2选择
17
*选择设置加1。 *
注意
:
*******************************************************************************/ #include unsigned char Time; //用来计时间的值 void Delay1ms(unsigned int c); void TimerConfiguration(); void Int0Configuration(); unsigned char SetPlace; /******************************************************************************* * 函 数 名 : main * 函数功能 : 主函数 * 输 入 : 无 * 输 出 : 无 *******************************************************************************/ void main(void) { unsigned char hour,minit,second; 18 unsigned int i; TimerConfiguration(); Int0Configuration(); LcdInit(); hour=12; LcdWriteData('0'+hour/10); LcdWriteData('0'+hour%10); LcdWriteData('-'); LcdWriteData('0'+minit/10); LcdWriteData('0'+minit%10); LcdWriteData('-'); LcdWriteData('0'+second/10); LcdWriteData('0'+second%10); while(1) { if(TR0==0) { if(K1==0) { Delay1ms(10);//消除抖动 if(K1==0) { } while((i<50)&&(K1==0)) { } 19 //检测按键K2是否按下 SetPlace++; if(SetPlace>=3) SetPlace=0; //检测按键是否松开 Delay1ms(1); i++; } i=0; //检测按键K3是否按下 if(K2==0) { Delay1ms(10);//消除抖动 if(K2==0) { if(SetPlace==0) { second++; if(second>=60) second=0; } else if(SetPlace==1) { minit++; if(minit>=60) minit=0; } else { hour++; if(hour>=24) hour=0; } } while((i<50)&&(K2==0)) { Delay1ms(1); i++; } 20 //检测按键是否松开 } } i=0; if(Time>=20) //一秒钟来到改变数值 { } Time=0; second++; if(second==60) { } second=0; minit++; if(minit==60) { } minit=0; hour++; if(hour==24) { } hour=0; //--显示时钟--// LcdWriteCom(0x80); LcdWriteData('0'+hour/10); LcdWriteData('0'+hour%10); LcdWriteCom(0x83); LcdWriteData('0'+minit/10); LcdWriteData('0'+minit%10); LcdWriteCom(0x86); LcdWriteData('0'+second/10); LcdWriteData('0'+second%10); 21 } } /******************************************************************************* * 函 数 名 : Delay1ms() * 函数功能 : 延时1ms * 输 入 : c * 输 出 : 无 *******************************************************************************/ void Delay1ms(unsigned int c) //误差 0us { unsigned char a,b; } /******************************************************************************* * 函 数 名 : TimerConfiguration() * 函数功能 : 配置定时器值 for (; c>0; c--) { } for(b=199;b>0;b--) { } for(a=1;a>0;a--); * 输 入 : 无 22 * 输 出 : 无 *******************************************************************************/ void TimerConfiguration() { TMOD = 0x01; //选择工作方式1 TH0 = 0x3C; //设置初始值 TL0 = 0x0B0; EA = 1; ET0 = 1; TR0 = 1; } /******************************************************************************* * 函 数 名 : Timer0() * 函数功能 : 定时器0中断函数 //打开总中断 //打开定时器0中断 //启动定时器0 * 输 入 : 无 * 输 出 : 无 *******************************************************************************/ void Timer0() interrupt 1 { TH0 = 0x3C; //设置初始值 TL0 = 0x0B0; } /******************************************************************************* * 函 数 名 : Int0Configuration() 23 Time++; * 函数功能 : 配置外部中断0 * 输 入 : 无 * 输 出 : 无 *******************************************************************************/ void Int0Configuration() { } /******************************************************************************* * 函 数 名 : Int0() interrupt 0 * 函数功能 : 外部中断0的中断函数 //设置INT0 IT0=1;//跳变沿出发方式(下降沿) EX0=1;//打开INT0的中断允许。 EA=1;//打开总中断 * 输 入 : 无 * 输 出 : 无 *******************************************************************************/ void Int0() interrupt 0 { } 24 Delay1ms(10); if(K3==0) { } TR0=~TR0; SetPlace=0; 附录3:实物图 25 因篇幅问题不能全部显示,请点此查看更多更全内容