STC89C52电子时钟

目录
摘 要 3
一 、实训目的 4
二 、具体内容 4
三 、方案讨论 5
四 仿真电路 5
4.1、仿真流程 5
4.2、仿真电路图 6
五 、程序设计 6
六、 程序流程图 12
七 、程序分析 13
八、 绘制PCB图 14
九、腐蚀/打孔/焊接 15

摘 要
数字时钟具有性能稳定、精确度高、成本低、易十产品化,以及万便、实用等特点。适用于家庭、公司、机关等众多场所。为人们的日常生活、出行安排提供了方便成为人们日常生活中不可缺少的一部分。
近些年,随着科技的发展和社会的进步,人们对数字钟的要求也越来越高,传统的时钟已不能满足人们的需求。多功能数字钟不管在性能还是在样式上都发生了质的变化,有电子时钟、数字闹钟等等。单片机在多功能高精度时钟中的应用已是非常普遍的,人们对高精度钟的功能及工作顺序都非常熟悉,但是却很少知道它的内部结构以及工作原理。由单片机作为数字钟的核心控制器,可以通过它的时钟信号进行计时实现计时功能,将其时间数据经单片机输出,利用液晶显示屏显示出来。通过键盘可以进行定时、校时功能。输出设备显示器可以用液晶显示技术和数码管显示技术。

一 、实训目的

  1. 在学习了《单片机原理及接口技术》课程后,为了加深对理论知识的理解,学习理论知识在实际中的运用,培养动手能力和解决实际问题的能力。
  2. 熟悉Ptoteus及Keil软件的调试和仿真。
  3. 通过实验提高对单片机的认识。
  4. 通过实验提高焊接、布局、电路检查能力。
  5. 通过实验提高软件调试能力。
  6. 进一步熟悉和掌握单片机的结构及工作原理。
  7. 通过课程设计,掌握以单片机为核心的电路设计的基本方法和技术。
  8. 通过实际程序设计和调试,逐步掌握模块化程序设计方法和调试技术。
    二 、具体内容
  9. 具体要求

1 实时显示秒、分、时、星期(可自动或手动设置显示)
2 利用三个按键实现时间的调节(加/减/切换)
3 处于调节模式下,lcd屏幕要有相应的提示
2. 方案设计
采用1602lcd屏幕用于时间显示,采用at89c52单片机用作主控芯片,使用轻触开关调整时间

三 、方案讨论

  1. 89c52单片机有四个I/O口,液晶屏数据传输需要独占一个I/O口,由于P0口用作I/O口时,需要加上拉电阻,从而增加制作成本,增加电路复杂度,故数据传输接口不宜采用P0口
  2. 1602液晶屏还需要连接RS,RW,E三个接口从而控制数据读写,同时应当考虑到系统稳定性,所以不能将其同按键连接到同一接口
  3. 需要三个按键控制时间的加减和调整,而89c52单片机只有两个外部中断接口,所以不考虑将按键接入到外部中断接口上.
  4. 综上所述,最终将液晶屏的数据传输接口接入到P1口,RS,RW,E分别接入P37,P36,P35口,将按键key0,key1,key3接入到P27,P26,P25口,此外,该单片机还需要晶振电路才能运行
  5. 所需物料:
    a) stc89c521
    b) lcd1602
    1
    c) 按键开关3
    d) 晶振
    1
    e) 独石电容2
    f) 排针
    4
    四 仿真电路
    4.1、仿真流程
    1.在计算机上打开“PROTEUS8”仿真软件。
    2.建立新的项目,选择start模块中的new project。选择项目文件保存的路径,点击下一步,选择项目所需电路板的大小接着点击下一步,选择不创建pcb点击下一步,创建Firmware Project Family选择8051,Controller选择AT89C51创建完成。
    3.在keil4编写仿真所用的程序,首先新建一个工程,选择project-new project,弹出一个对话框,填写工程名,确定。然后选择你所使用的AT89C51芯片确定。然后点击文件File—New,新建一个文件,确定。然后将新建的文件添加进工程中,开始编写代码运行无误后创建hex文件再进行编译。
    4.将电路图完成载入用keil4创建的hex文件最终实现仿真。

4.2、仿真电路图

五 、程序设计

  1. 程序论证:
    a) 定时可采用两种方式来实现:
    i. 方式1:自定义延时函数,缺点:精度不高
    ii. 方式2:使用89c52单片机中自带的定时器实现,优点:精度高,容易实现
    iii. 综合两种方式,故采用方式2,使用单片机中自带的定时器计时,最终采用定时器0方式1,设置初值为50000微妙,循环20次为1秒
    b) 由于按键只有三个,但需要实现对星期,小时,分钟,秒钟的加减,所以需要一个按键对需要更改的参数进行切换,另外两个按键实现加和减

i. 切换可以定义一个flag,按下切换按键就对flag的值加一,通过flag来判断是对具体哪个参数进行修改,所要修改的参数有:星期,小时,分钟,秒钟,故要求flag有四个及以上的值,另外,还需要让时钟不处于设置模式,所以最终确定的flag的值为0,1,2,3,4;同时还需要使flag的值不能大于4,所以需要增加一个判定条件,即flag>4,则flag=0;
ii. 时间的加和减有两个按键,所以需要定义两个函数实现对时间的增加和减少,在调用时间加/减函数的时候把flag传递到该函数中去,该函数通过判定flag的值就能精准定位需要加或减的参数
c) 程序中有了flag这个参数,所以程序能清楚的知道它要调整的参数,而用户不知道,需要将正在调整什么项目显示到液晶屏幕上,所以需要另外定义个dispset函数来显示用户正在调整的参数,同时这个函数需要将flag的值传递过去
d) 同时需要考虑,时间调整到一个极值就不能继续增加或减小,如一天中只有0点到23点,如果超过23点,则会变成0点,同理,星期只有1-7,分钟只有0-59,如果超过这个极值,程序会自动将其赋值为初始值
2. 最终程序:
#include <reg51.h>
#define DATAPORT P1//定义数据端口
#define uchar unsigned char
sbit RS=P3^7; //定义RS 数据/命令选择接口
sbit RW=P3^6; //定义RW 读/写选择接口
sbit E=P3^5; //定义使能接口
sbit key0=P2^7;//加
sbit key1=P2^6;//减
sbit key2=P2^5;//切换
int timeflag=0;//调整时间flag
uchar tablew1[]=“MTWTFSS”;
uchar tablew2[]=“ouehrau”;
uchar tablew3[]=“neduitn”;
uchar tablew4[]="…"; //周的数组( Mon. Tue. Wed. Thu. Fri. Sat. Sun.)
uchar tablesetweek[]=“set week”;//显示设置周
uchar tablesethour[]=“set hour”; //显示设置小时
uchar tablesetminute[]=“set minute”; //显示设置分钟
uchar tablesetsecond[]=“set second”; //显示设置秒
uchar tablesetnull[]=“LkwCxwPhl”;
char d[]={‘0’,‘0’,’:’,‘0’,‘0’,’:’,‘0’,‘0’};//定义初始化值
uchar week=0,hour=0,minute=0,second=0,count=20; //定义秒分时周
void transfer();
void init(); //初始化函数声明
void writeCOM(uchar i); //写命令函数声明
void writeData(uchar j); //写数据函数声明
void fbusy(); //检查忙函数声明
void timechange(int t); //时间调整函数
void disp(); //声明显示函数
void dispset(int timeflag);//显示设置
void timeadd(int timeflag);
void timecut(int timeflag);
int i; //全局变量i
void delay(int num){
int j,k;
for(j=0;j<=num;j++){
for(k=0;k<=120;k++);
}
}
int main(void){
EA=1; //使能=1
ET0=1;
TMOD=0x01;//定时器0方式1
TH0=(65535-50000)/256;
TL0=(65535-50000)%256; //初值为50000微秒
TR0=1;
init();

while(1){
	transfer();	 //秒分时周转换
	disp();		 //显示时间
	dispset(timeflag); //显示设置
	if(key2==0){
		init();
		delay(200);
		timeflag++;
		if(timeflag>4){		  //让flag小于等于4
			timeflag=0;
			init();
		}
	}
	if(key0==0){	  //按下后减小数值
		delay(200);
		timeadd(timeflag);
	}
	if(key1==0){				  //按下后增加数值
		delay(200);
		timecut(timeflag);
	}
}

}
void disp(){ //显示时间函数

writeCOM(0x80); //写入显示缓冲区起始地址为第1行第2列
writeData(tablew1[week]);
writeCOM(0x81);
writeData(tablew2[week]);
writeCOM(0x82);
writeData(tablew3[week]);
writeCOM(0x83);
writeData(tablew4[week]);
writeCOM(0xc0);
for(i=0;i<8;i++){
	writeData(d[i]);
}

}
void dispset(int timeflag){ //显示设置状况函数
switch(timeflag){
case(0):
writeCOM(0x87);
for(i=0;i<10;i++){
writeData(tablesetnull[i]);
}
break;
case(1):
writeCOM(0x87);
for(i=0;i<8;i++){
writeData(tablesetweek[i]);
}
break;
case(2):
writeCOM(0x87);
for(i=0;i<8;i++){
writeData(tablesethour[i]);
}
break;
case(3):
writeCOM(0x87);
for(i=0;i<10;i++){
writeData(tablesetminute[i]);
}
break;
case(4):
writeCOM(0x87);
for(i=0;i<10;i++){
writeData(tablesetsecond[i]);
}
break;
}
}
void init(){
writeCOM(0x01); //清屏
writeCOM(0x38); //开显示,读8位数据,显示两行,使用5*7的字型
writeCOM(0x0c); //显示器开,光标关,字符不闪烁
writeCOM(0x06); //字符不动,光标自动右移一位
}
void writeCOM(uchar i){ //写命令
fbusy();
RS=0;
RW=0;
E=1;
DATAPORT=i;
E=0; //负脉冲写
}
void writeData(uchar j){ //写数据
fbusy();
RS=1;
RW=0;
E=1;
DATAPORT=j;
E=0; //负脉冲写
}
void fbusy(){//检查忙函数
DATAPORT=0xff; //读之前向端口写1,确保读数正确
RS=0;RW=1;
E=0;
E=1; //正脉冲读
while(DATAPORT&0x80){ //忙,等待
E=0;
E=1;
}
}

void transfer(){ //用于秒分时周的显示
d[0]=hour/10+0x30;
d[1]=hour%10+0x30;
d[3]=minute/10+0x30;
d[4]=minute%10+0x30;
d[6]=second/10+0x30;
d[7]=second%10+0x30;
}
void clock() interrupt 1{ //计时
TH0=(65535-50000)/256;
TL0=(65535-50000)%256;
while(–count==0){
count=20; //50毫秒*20等于1秒
second++;
if(second>=60){
second=0; //秒到分钟
minute++;
if(minute>=60){ //分钟到小时
minute=0;
hour++;
if(hour>=24){ //小时到天
hour=0;
week++;
if(week>=7){ //星期天到星期一
week=0;
}
}
}
}
}
}

void timeadd(int flag){//时间加
switch(flag){
case(1):
if(week6){
week=0;
break;
}
week++; //增加星期的值
break;
case(2):
if(hour
23){
hour=0;
break;
}
hour++; //增加小时的值
break;
case(3):
if(minute59){
minute=0;
break;
}
minute++; //增加分钟的值
break;
case(4):
if(second
59){
second=0;
break;
}
second++; //增加秒的值
break;
default:
break;
}
}
void timecut(int flag){ //时间减
switch(flag){
case(1):
if(week0){
week=6;
break;
}
week–; //减少星期的值
break;
case(2):
if(hour
0){
hour=23;
break;
}
hour–; //减少小时的值
break;
case(3):
if(minute0){
minute=59;
break;
}
minute–; //减小分钟的值
break;
case(4):
if(second
0){
second=59;
break;
}
second–; //减少秒的值
break;
default:
break;
}
}
六、 程序流程图
该流程图先对电路进行初始化,然后程序main函数开始顺序执行,进入主程序,在主程序中调用各个子程序,在运行时可对时钟进行调整,key2键是选择调整对象,key0键是进行增加,key1键是进行减少。

七 、程序分析

  1. 使用单片机P1口输出数据

  2. 使用单片机P2口的567引脚进行按键检测

  3. 使用P3口对lcd屏RS/RW的控制

  4. 使用flag标识实现对分/时/星期 的修改定位

  5. 定时器采用定时器0方式1,设置初值为50000

  6. timeflag的值为1就改变星期的值,为2就改变小时的值,为3就改变分钟的值,为4就改变秒钟的值,为其他就不做任何操作.

  7. 通过key1/key0的按下去调用时间加/减的函数,并将timeflag的值传递到时间加/减的函数中.
    八、 绘制PCB图

  8. 绘制原理图库
    a) 测量各个元器件引脚的数量,准确的将原理图库绘制出来
    a) 测量元器件具体大小,绘制各元器件的封装

  9. 绘制原理图
    a) 参考方正电路绘制出原理图,并加上晶振电路,电源电路,和排针
    b) 最终原理图效果:

  10. 原理图导入pcb完成布线工作

九、腐蚀/打孔/焊接

  1. 焊接好之后需要用万用表检测是否通路,有引脚短路
  2. 最终实物图
  3. 将预先编译好的hex程序文件烧录至单片机
  4. 测试各项功能是否正常
  • 4
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是一个简单的STC89C52电子时钟程序,实现了显示当前时间和日期的功能: ```c #include <reg52.h> #include <intrins.h> #define uchar unsigned char #define uint unsigned int uchar code table[] = { 0xC0, // "0" 0xF9, // "1" 0xA4, // "2" 0xB0, // "3" 0x99, // "4" 0x92, // "5" 0x82, // "6" 0xF8, // "7" 0x80, // "8" 0x90, // "9" 0xFF, // 空 0xBF, // "-" 0x7F, // "_" }; sbit SDA = P1 ^ 0; sbit SCL = P1 ^ 1; uchar second, minute, hour, day, month, year; void delay(uint xms) { uint i, j; for (i = xms; i > 0; i--) { for (j = 110; j > 0; j--); } } void IIC_Start() { SDA = 1; SCL = 1; _nop_(); _nop_(); _nop_(); SDA = 0; _nop_(); _nop_(); _nop_(); SCL = 0; } void IIC_Stop() { SDA = 0; _nop_(); _nop_(); _nop_(); SCL = 1; _nop_(); _nop_(); _nop_(); SDA = 1; } void IIC_SendByte(uchar dat) { uchar i; for (i = 0; i < 8; i++) { if ((dat & 0x80) == 0x80) { SDA = 1; } else { SDA = 0; } dat <<= 1; _nop_(); _nop_(); _nop_(); SCL = 1; _nop_(); _nop_(); _nop_(); SCL = 0; } } uchar IIC_ReadByte() { uchar i, dat = 0; SDA = 1; for (i = 0; i < 8; i++) { dat <<= 1; SCL = 1; _nop_(); _nop_(); _nop_(); if (SDA) { dat |= 0x01; } SCL = 0; _nop_(); _nop_(); _nop_(); } return dat; } void Write_RTC(uchar addr, uchar dat) { IIC_Start(); IIC_SendByte(0xD0); IIC_SendByte(addr); IIC_SendByte(dat); IIC_Stop(); } uchar Read_RTC(uchar addr) { uchar dat; IIC_Start(); IIC_SendByte(0xD0); IIC_SendByte(addr); IIC_Start(); IIC_SendByte(0xD1); dat = IIC_ReadByte(); IIC_Stop(); return dat; } void Init_RTC() { Write_RTC(0x0E, 0x00); Write_RTC(0x01, 0x00); Write_RTC(0x02, 0x00); Write_RTC(0x03, 0x01); Write_RTC(0x04, 0x01); Write_RTC(0x05, 0x21); Write_RTC(0x06, 0x01); } void Read_Time() { second = Read_RTC(0x00); minute = Read_RTC(0x01); hour = Read_RTC(0x02); day = Read_RTC(0x04); month = Read_RTC(0x05); year = Read_RTC(0x06); } void Display_Time() { uchar i, j; uchar time[8]; time[0] = hour / 10; time[1] = hour % 10; time[2] = minute / 10; time[3] = minute % 10; time[4] = second / 10; time[5] = second % 10; time[6] = month / 10; time[7] = month % 10; for (i = 0; i < 8; i++) { P0 = table[time[i]]; for (j = 0; j < 100; j++) { _nop_(); } P0 = table[12]; } } void main() { Init_RTC(); while (1) { Read_Time(); Display_Time(); delay(1000); } } ``` 该程序使用了STC89C52单片机和DS1307实时时钟芯片,通过IIC总线通信获取当前时间和日期,并且使用共阴数码管显示出来。 程序中的`table`数组定义了数字0到9、减号、下划线和空格对应的数码管段码。`IIC_Start()`、`IIC_Stop()`、`IIC_SendByte()`和`IIC_ReadByte()`函数分别实现了IIC总线的起始、停止、发送一个字节和接收一个字节的操作。`Init_RTC()`函数初始化DS1307芯片,设置初始时间为2021年1月1日0时0分0秒。`Read_Time()`函数从DS1307芯片中读取当前的时间和日期。`Display_Time()`函数将读取到的时间和日期分别转换成数字并且显示在数码管上,每个数字之间会有一个空格。程序中的`delay()`函数实现了简单的延时功能,用于控制时间的更新速度。 该程序可以通过连接到电脑的串口调试助手来观察程序的运行情况和调试错误。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值