点击链接获取Keil源码与Project Backups仿真图:
https://download.csdn.net/download/qq_64505944/87759735?spm=1001.2014.3001.5503
源码获取
主要内容:
设计一个电子钟,实现对时、分、秒的显示的控制,电路采用24小时计时方式。另一个功能是秒表功能,从零开始读秒,可以暂停,二者可以通过一个按键进行互换,并具备时间校准功能。
基本要求:
1、原理分析;
2、keil建立工程;
3、代码实现;
4、通过Proteus仿真;
5、设计总结
主要参考资料:
[1]张俊谟.单片机中级教程—原理与应用[第2版].北京航空航天大学出版社,2006.
[2]周润景、张丽娜.基于PROTEUS的电路及单片机系统设计与仿真.北京航空航天大学出版社,2006.
[3]全国大学生电子设计竞赛组委会.全国大学生电子设计竞赛获奖作品选编[M].北京理工大学出版社.2007.
[4]雷志勇、江建尧.数字存贮示波器的随机采样原理.西安工业学院学报.1997,(5)
[5]黄智伟.全国大学生电子设计竞赛电路设计.北京航空航天大学出版社,2006.
完 成 期 限: 月 日 - 月 日
指导教师签名:
课程负责人签名:
摘 要
本次课程设计是电子钟的设计,对时、分、秒的显示的控制,电路采用24小时计时方式。另一个功能是秒表功能,从零开始读秒,可以暂停,二者可以通过一个按键进行互换,并具备时间校准功能。
最常见的电子钟通常使用单片机模块控制,一种用单片机原理实现时、分、秒计时的装置,与机械式时钟相比具有更高的准确性和直观性,且无机械装置,具有硬件成本低、计时准确、更长的使用寿命特点,因此得到了广泛的使用。
本次设计通过用单片机为主控制 ,通过电路仿真而实现 。首先使用 Proteus软件进行绘制硬件电路图,用 keil 软件进行编程与调试,最终生成 hex文件,传入单片机内部,从而实现仿真效果。
关键词:电子钟;仿真;单片机
目 录
摘 要 I
目 录 II
1.设计要求 1
1.1介绍 1
1.2基本功能 1
1.3扩展功能 1
2.工作原理 2
2.1LCD1602液晶显示屏原理 2
2.2按键原理 2
2.3单片机核心模块的时钟电路原理 2
2.4使用说明 3
3.仿真设计 4
3.1显示时间与温度 4
3.2实现秒表功能 5
4 程序 6
4.1程序结构 6
4.2代码 6
5.设计总结 18
参考文献 19
1.设计要求
1.1介绍
电子钟是一种利用数字电路来显示秒、分、时的计时装置,与传统的机械钟相比,它具有走时准确、显 示直观、无机械传动装置等优点,因而得到广泛应用。同时电子钟可以集成诸多功能,如秒表、温度测量、水平检测等功能。
1.2基本功能
1、电子钟可显示时、分、秒;
2、可手动校时,待校时的时间闪烁;
1.3扩展功能
1、可当作秒表使用;
2、可测量当前环境温度;
3、通过LCD1602液晶屏显示数据及特殊图形;
2.工作原理
2.1LCD1602液晶显示屏原理
LCD1602液晶显示的原理是利用液晶的物理特性,通过电压对其显示区域进行控制,有电就有显示,这样即可以显示出图形。它是由字符型液晶显示屏(LCD)、控制驱动主电路HD44780及其扩展驱动电路HD44100,以及少量电阻、电容元件和结构件等装配在PCB板上而组成。
LCD1602液晶屏由板载5V电压供电;VO引脚连接可调电阻,因此可以通过调节电阻阻值大小控制背光亮度;LCD RS、LCD WR、LCD EN分别连接单片机的P2.6、P2.5、P2.7,控制数据与命令的传输;LCD D0~LCD D7连接单片机P0口,作为单片机与模块之间的数据传送通道。
2.2按键原理
图2-1 独立按键模块
由图2-1可知,4位独立按键K1K4分别连接单片机P3.1P3.3,只有按键按下时读引脚才可以得到高电平1。因此可以通过对引脚值电平进行分析,进而判断有无按键按下。
2.3单片机核心模块的时钟电路原理
单片机时钟信号的产生采用内部震荡方式,外接12MHZ晶振,得到频率为12MHZ的时钟信号。在AT89C52芯片内部有一个高增益反相放大器,其输入端为芯片引脚XTAL1,输出端为引脚XTAL2。把放大器与作为反馈元件的晶体振荡器和陶瓷电容相连,就构成了自激振荡器。
主机和从机通信使用单总线,即使用单线进行数据的发送和接收。首先需要判断总线上是否存在设备,确定有设备存在后在进行读/写操作,查找芯片手册中的寄存器地址与操作提示可以设置温度检测精度与温度值读取。
2.4使用说明
使用K1、K2、K3共3个按键
(1)功能切换:单击K3,用于切换时间、温度显示和秒表功能菜单间切换
(2)时间校准:长按K3 进入/退出时间调整菜单,当菜单为时间调整时,单击K3 切换选择时/分/秒进行调整,待调整的时间会闪烁,单击K1,当前调整的时间值+1,单击K2则-1。时间调整完毕后,长按K2保存当前时间值,LED灯闪烁一次时,则表示校时成功。
(3)秒表功能:当菜单为秒表时,单击K实现开/关秒表,长按K1秒表清0。
图2-2菜单描述
3.仿真设计
3.1显示时间与温度
3-1时间和温度显示图
3.2实现秒表功能
3-2秒表功能图
4 程序
4.1程序结构
图4-1KEIL工程目录
KEIL工程目录如图13所示,共有5个程序文件,其文件内容如下表所示。
表4-1KEIL工程目录
main.c 主函数
lcd1602.c 液晶屏驱动
delay.c 延时函数集合
timer0.c 定时器0初始化
ds18.c 温度传感器驱动
4.2代码
(*)main.c
#include <main.h>
#include <lcd1602.h>
#include <delay.h>
#include <timer0.h>
#include <ds18.h>
unsigned char MenuSelect,LastKey,NowKey;
unsigned char MenuChange= 1;
unsigned char T0Count = 0;
unsigned int T1Count = 0;
unsigned int T2Count = 0;
unsigned char SCount = 0;
unsigned char KeyNumber = 0;
unsigned long time_value =0;
unsigned long time_change_value =0;
unsigned char str[12];
unsigned char S_Flag = 0;
unsigned long second_value =0;
unsigned char h_m_s=0;
long temp; //不是无符号数
int DS_int;
unsigned char Dsdata[7] ="_______";
bit fg=1;//用于判断温度正负
//读取温度
int ReadTemperature(void)
{
unsigned char a=0;//低8位
unsigned char b=0;//高8位
unsigned int t=0;
Init_DS18B20();
WriteOneChar(0xCC); // 跳过读序号列号的操作
WriteOneChar(0x44); // 启动温度转换
delay_18B20(100); //至少750ms
Init_DS18B20();
WriteOneChar(0xCC); //跳过读序号列号的操作
WriteOneChar(0xBE); //读取温度寄存器
a=ReadOneChar();
b=ReadOneChar();
if((b&0xf8)==0xf8) //位为1 时温度是负
{
b=~b;
a=~a+1; //补码转换,取反加一
if(a==0)b++;//如果发生进位
fg=0; //读取温度为负时fg=0
}
else{
fg=1;//正数
}
t=((b*256+a)*25)>>2;//合成整数
return(t);
}
//对读取到的温度数据进行处理
void Tran(int temp)
{
unsigned char k=0;
Dsdata[1] = ' ';Dsdata[2] = '0';Dsdata[3] = '0';Dsdata[4] = '0';Dsdata[5] = '0';//清缓存
if(fg){
Dsdata[0] = ' ';
}
else{
Dsdata[0] = '-';
}
do
{
Dsdata[4-k]=temp%10 + '0';//取最后一位
k++;
}while(temp/=10);
Dsdata[5]=Dsdata[4];Dsdata[4]=Dsdata[3];Dsdata[3]='.';//后移2位数据,加上小数点
Dsdata[6]='\0';//END
}
//按键扫描
void Scan_Key()
{
unsigned char KEY_Temp = 0;
if(KEY_1==0){KEY_Temp=1;}
if(KEY_2==0){KEY_Temp=2;}
if(KEY_3==0){KEY_Temp=3;}
if(KEY_4==0){KEY_Temp=4;}
LastKey = NowKey;
NowKey = KEY_Temp;
}
//根据扫描结果判断有无按键按下,可以实现长按检测
unsigned char Judge_Key(){
if(NowKey!= 0 ){
T2Count++;
}else if(LastKey!=0 && NowKey==0){//边沿检测
if(T2Count>25){
KeyNumber=LastKey+4;
}else{
KeyNumber=LastKey;
}
T2Count=0;
}
}
//长整数转换成时间(模60)
unsigned char IntergerToTime(long dat,unsigned char*str){
//中间变量
unsigned char i = 0;
unsigned char h = 0;
unsigned char m = 0;
unsigned char s = 0;
unsigned char buff[11];
h = dat/ 3600;
m = dat% 3600 / 60;
s = dat% 60;
buff[i]=s%10 + '0';
i++;
buff[i]=s/10 + '0';
i++;
buff[i]=m%10 + '0';
i++;
buff[i]=m/10 + '0';
i++;
buff[i]=h%10 + '0';
i++;
buff[i]=h/10 + '0';
i++;
while(i-- > 0){
*str = buff[i];
str++;
}
*str='\0';//取内容
return 1;
}
//秒表计数值转换
unsigned char MsecondToTime(long dat,unsigned char*str){
//中间变量
unsigned char i = 0;
unsigned char h = 0;
unsigned char m = 0;
unsigned char s = 0;
unsigned char buff[11];
h = dat/ 6000;
m = (dat-h*6000)/100;
s = dat-h*6000-m*100;
buff[i]=s%10 + '0';
i++;
buff[i]=s/10 + '0';
i++;
buff[i]=m%10 + '0';
i++;
buff[i]=m/10 + '0';
i++;
buff[i]=h%10 + '0';
i++;
buff[i]=h/10 + '0';
i++;
while(i-- > 0){
*str = buff[i];
str++;
}
*str='\0';//取内容
return 1;
}
//主菜单
void Main_menu(){
LCD_Show_String(0,0,"Time:");
LCD_Show_CHAR(6,0,str[0]);
LCD_Show_CHAR(7,0,str[1]);
LCD_Show_CHAR(8,0,':');
LCD_Show_CHAR(9,0,str[2]);
LCD_Show_CHAR(10,0,str[3]);
LCD_Show_CHAR(11,0,':');
LCD_Show_CHAR(12,0,str[4]);
LCD_Show_CHAR(13,0,str[5]);
LCD_Show_String(0,1,"Temp:");//温度
Tran(DS_int);
LCD_Show_String(5,1,Dsdata);
//LCD_Show_CHAR(7,1, '0');
}
//秒表菜单
void Second_menu(){
LCD_Show_String(0,0,"Stopwatch:");
LCD_Show_CHAR(6,1,str[0]);
LCD_Show_CHAR(7,1,str[1]);
LCD_Show_CHAR(8,1,':');
LCD_Show_CHAR(9,1,str[2]);
LCD_Show_CHAR(10,1,str[3]);
LCD_Show_CHAR(11,1,':');
LCD_Show_CHAR(12,1,str[4]);
LCD_Show_CHAR(13,1,str[5]);
}
//时间调整菜单
void Change_menu(){
LCD_Show_String(0,0,"Change Time");
if(h_m_s == 0){
LCD_Show_CHAR(0,1,'H');
}else if(h_m_s == 1){
LCD_Show_CHAR(0,1,'M');
}else if(h_m_s == 2){
LCD_Show_CHAR(0,1,'S');
}
LCD_Show_CHAR(6,1,str[0]);
LCD_Show_CHAR(7,1,str[1]);
LCD_Show_CHAR(8,1,':');
LCD_Show_CHAR(9,1,str[2]);
LCD_Show_CHAR(10,1,str[3]);
LCD_Show_CHAR(11,1,':');
LCD_Show_CHAR(12,1,str[4]);
LCD_Show_CHAR(13,1,str[5]);
}
void main(){
delayx10ms(1);
LcdInit();
LCD_CGRAM();
LCD_Start();
Timer0_Init();
//循环
while(1){
if(KeyNumber){
if(KeyNumber == 1){
if(MenuSelect == 1){
if(S_Flag == 1){
S_Flag = 0;
}else{
S_Flag = 1;
}
}
if(MenuSelect==2){
switch(h_m_s){
case 0 :
temp = time_change_value + 60*60;
if(temp<86400-1){
time_change_value = temp;
}break;
case 1 :
temp = time_change_value + 60;
if(temp<86400-1){
time_change_value = temp;
}break;
case 2 :
temp = time_change_value + 1;
if(temp<86400-1){
time_change_value = temp;
}break;
}
}
}else if(KeyNumber == 2){
if(MenuSelect==2){
switch(h_m_s){
case 0 :
temp = time_change_value - 60*60;
if(temp>=0){
time_change_value = temp;
}break;
case 1 :
temp = time_change_value - 60;
if(temp>=0){
time_change_value = temp;
}break;
case 2 :
temp = time_change_value - 1;
if(temp>=0){
time_change_value = temp;
}break;
}
}
}
else if(KeyNumber == 3){//3键切换菜单
if(MenuSelect<2){
MenuSelect++;
MenuChange=1;
if(MenuSelect>1){
MenuSelect=0;
}
}else if(MenuSelect==2){
h_m_s++;
if(h_m_s>=3){
h_m_s=0;
}
}
}else if(KeyNumber == 5){
if(MenuSelect == 1){
//清零计数值
second_value=0;
}
//LED_2 = ~LED_2;
}else if(KeyNumber == 6){
if(MenuSelect == 2){
//确定校时
time_value=time_change_value;
//--
LED_2 = ~LED_2;//LED闪烁表示 效时成功了哦
//--
}
}else if(KeyNumber == 7){
if(MenuSelect != 2){
MenuChange=1;
//time_change_value=time_value;
time_change_value=0;
MenuSelect=2;
}else{
MenuChange=1;
MenuSelect=0;
}
}
KeyNumber=0;
}
//清屏命令,防止重复清屏,卡哎
if(MenuChange==1){
WriteCmd(0x01);//清屏
MenuChange = 0;
}
//切换菜单
switch(MenuSelect){
case 0 : IntergerToTime(time_value,str);Main_menu();break;
case 1 : MsecondToTime(second_value,str);Second_menu();break;
case 2 : IntergerToTime(time_change_value,str);Change_menu();
if(h_m_s==0){
Delay(260);
LCD_Show_CHAR(6,1,' ');
LCD_Show_CHAR(7,1,' ');
Delay(260);
}else if(h_m_s==1){
Delay(260);
LCD_Show_CHAR(9,1,' ');
LCD_Show_CHAR(10,1,' ');
Delay(260);
}else if(h_m_s==2){
Delay(260);
LCD_Show_CHAR(12,1,' ');
LCD_Show_CHAR(13,1,' ');
Delay(260);
}break;
}
}
}
//计数器0用于按键扫描(不要加delay延时!)同时实现计数
void Timer0_interrupt() interrupt 1
{
TL0 = 0x18;
TH0 = 0xFC;
T0Count++;T1Count++;
if(T0Count>=20) //每20ms扫描一次按键
{
T0Count=0;
SCount++;
Scan_Key();
Judge_Key();
}
if(T1Count>=10){
T1Count=0;
if(S_Flag==1){
if(second_value<60001){
second_value++;
}else{
second_value=0;
}
}
}
if(SCount >= 50 ){
SCount=0;
if(time_value >86400-1){
time_value=0;
}else{
time_value++;
//---
DS_int = ReadTemperature();//读取温度
//---
}
}
}
5.设计总结
在课程设计,将理论用于实践,实践又将丰富理论,对单片机的掌握更加深刻,收获颇丰。在制作过程中也遇到了许多问题,如LCD1602因为操作时序不正确导致无响应,因为清屏指令不合适造成屏幕闪烁明显;对读取DS18B20的高8位和低8位数据进行整合处理时遇到的难题;按键单击与长按复用功能的实现,消抖的细节处理等。通过查找CSDN与B站上的资源,与同学交流,问题最后得到了解决。但由于自己能力有限,做出来的成品只能尽自己所能做到最好。
单片机的型号千千万万,随着时代的发展,单片机更新迭代,只有掌握了基本的单片机原理与学习方法,有了坚实的基础才可以对任何单片机的开发水到渠成,应一步一步慢慢来。
总之这次电路的设计和仿真,基本上达到了设计的功能要求。在以后的实践中,我将继续努力学习电路设计方面的理论知识,并理论联系实际,争取在电路设计方面能有所提升。
参考文献
[1]张俊谟.单片机中级教程—原理与应用[第2版].北京航空航天大学出版社,2006.
[2]周润景、张丽娜.基于PROTEUS的电路及单片机系统设计与仿真.北京航空航天大学出版社,2006.
[3]全国大学生电子设计竞赛组委会.全国大学生电子设计竞赛获奖作品选编[M].北京理工大学出版社.2007.
[4]雷志勇、江建尧.数字存贮示波器的随机采样原理.西安工业学院学报.1997,(5)
[5]黄智伟.全国大学生电子设计竞赛电路设计.北京航空航天大学出版社,2006.