基于51单片机电子时钟

单片机学习之路 专栏收录该内容
12 篇文章 3 订阅

keil工程与protues仿真电路

1 基于51单片机用LCD1602实现时-分的显示
2. 按键控制时-分的调整
3. 能实现整时报时的功能(蜂鸣器响)
4. 闹钟模式
5.按键切换模式(模式一:时-分显示,模式二:60秒倒计时)

一、设计思路:
主体:
通过外部中断0来控制mod值;mod=0,1.2,3分别对应时钟模式,调整模式,闹钟设置模式,一分钟倒计时模式。
细节:
mod0
通过定时计数器,每一秒增加变量秒(s),每60秒,增加1分(min)并且s置0,每60min,增加1小时h,当h>23,h=0;进行一天循环
mod1
按键控制增加min,h和s制0
mod2
另外设置变量min1,h1,当min=min1,h=h1时蜂鸣器响
mod3
设置变量daojishi=60, 通过定时计数器,每一秒daojishi减1,当daojishi<0时,蜂鸣器响
另外:
1.设置外部中断2,关闭蜂鸣器
2.时间发送一次只能发送一个位

程序:

#include<reg52.h>
//K1后K3加分钟,K4加时间,K2加秒,K1进入闹钟设置,K2退出
//K2关闭闹钟

typedef unsigned int u16;	  //对数据类型进行声明定义
typedef unsigned char u8;

#define data8b P1

sbit K1=P3^2;               //外部中断0
sbit K2=P3^3;               //外部中断1
sbit K3=P3^0;
sbit K4=P3^1;

sbit BUZ=P2^4;       //蜂鸣器,0响
sbit RW=P2^5;        //4脚,数据(1)or命令(0)
sbit RS=P2^6;        //5脚,读(1)写(0)
sbit E=P2^7;         //6脚,使能信号

u8 code dat1[]={0X30,0X31,0X32,0X33,
	                               0X34,0X35,0X36,0X37,
	                               0X38,0X39};

void delay(u16 i)      //延时函数
{
	while(i--);
}

void open012()   //打开中断0,1,定时器中断0
{
	TMOD|=0X01;   //选择为定时器0模式,工作方式1
  
	
	ET0=1;        //打开定时器0中断允许
	
	EA=1;        //打开总中断
	
	TR0=1;       //打开定时器			
	
	EX0=1;              //打开外部中断0
	IT0=1;             //边沿触发方式
	EX1=1;              //打开外部中断1
	IT1=1;             //边沿触发
  
}


void wrm(u8 dat)              //写入命令
{
	delay(1000);
	RS=0;
	RW=0;
	E=0;
	data8b=dat;
	E=1;
	delay(1000);
	E=0;
}


void wrd(u8 dat)             //写入数据
{
	delay(1000);
	RS=1;
	RW=0;
	E=0;
	data8b=dat;
	E=1;
	delay(1000);
	E=0;
}

void zero()
{
	wrm(0X38);                 //八位数据,两行显示,5*7
	wrm(0X0c);                  //无光标,打开显示
	wrm(0X06);                  //光标右移,屏幕不移动
	wrm(0X01);                  //清屏
	wrm(0X80);                  //设置数据指针起点
}

u8 fg=0,sg=0,bfg=0,bsg=0;
u16 i=0;
u8 s=0;
u8 mod=0;
char dingshi;
bit bell=0;
bit zanting=1;

void fangsong()
{
  wrd(dat1[sg/10]);                 //时十位
	wrd(dat1[sg%10]);                 //时个位
	wrd(0x3A);                     //:
	wrd(dat1[fg/10]);                 //分十位
	wrd(dat1[fg%10]);                 //分个位
	wrd(0x3A);                     //:
	wrd(dat1[(s/10)]);                 //秒十
	wrd(dat1[(s%10)]);                 //秒个
}


void fangsong1()
{
	wrm(0X80);   
  wrd(dat1[sg/10]);                 //时十位
	wrd(dat1[sg%10]);                 //时个位
	wrd(0x3A);                     //:
	wrd(dat1[fg/10]);                 //分十位
	wrd(dat1[fg%10]);                 //分个位
	wrd(0x3A);                     //:
	wrd(dat1[(s/10)]);                 //秒十
	wrd(dat1[(s%10)]);                 //秒个
}
void chuli()
{
	if(fg==60)
	{
		sg++;
		fg=0;
	}

	if(sg==24)
	{
	
		sg=0;
	}
	
}



void main()
{
	u8 shijian;
	open012();
	zero();
	chuli();
	fangsong();
	shijian=100;
	
	
	while(1)
	{
		while(mod==0)
	{
		EX1=1;              //打开外部中断1
		if(s==60)
		 {
			 fg++;       //60秒转化为1分钟
			 s=0;
		 }
				chuli();
				if((fg==0)&&(shijian!=sg))
				 {
				  BUZ=0;
					shijian=sg;
				 }
				fangsong1();
				if((BUZ==0)&&(bell==0))
			  {
				delay(1000);
				BUZ=1;
			  }
			if((fg==bfg)&&(sg==bsg)&&(bell==1))
				BUZ=0;
			else BUZ=1;
			}
	
	
			
			while(mod==1)
		{
			EX1=0;              //关闭外部中断1
			zero();
	    fangsong();
			if(K3==0)
			  {
				  delay(1000);
				  if(K3==0)
					  fg++;
			   }
			if(K4==0)
			   {
				  delay(1000);
				  if(K4==0)
					 sg++;
			   }
				 if(K2==0)
			   {
				  delay(1000);
				  if(K2==0)
					 s=0;
			   }
				 if(fg>59)
				 {
					 fg=0;
				 }
				  if(sg>23)
				 {
					 sg=0;
				 }
				 if(s>=59)
				 {
					 s=0;
				 }
	     }
		
			 
			 
			 while(mod==2)   //设置闹钟
		{
	    if(bfg==60)
	    {
		    bsg++;
		    bsg=0;
	    }
	
	    if(bsg==24)
	    {
		     bsg=0;
	     }
	    zero();
			wrd(0x20);
			wrd(0x20);
			wrd(0x20);
		  wrd(dat1[(bsg/10)]);                 //时十位
	    wrd(dat1[(bsg%10)]);                 //时个位
	    wrd(0x3A);                     //:
	    wrd(dat1[(bfg/10)]);                 //分十位
	    wrd(dat1[(bfg%10)]);                 //分个位
			 if(K3==0)
			{
				delay(1000);
				if(K3==0)
					bfg++;
			}
			if(K4==0)
			{
				delay(1000);
				if(K4==0)
					bsg++;
			}
			bell=1;
			zero();
}
		
while(mod==3)
{
	while(zanting)
	{
	dingshi=60;
	EX1=1;              //打开外部中断1
	wrm(0X80);   
	 wrd(dat1[(dingshi/10)]);                 //时十位
	 wrd(dat1[(dingshi%10)]);            	//时个位
   }
   wrm(0X80);   
	 wrd(dat1[(dingshi/10)]);                 //时十位
	 wrd(dat1[(dingshi%10)]);            	//时个位	
	while(dingshi<0)
	{
		wrm(0X80);   
		 wrd(dat1[0]);                 //时十位
	   wrd(dat1[0]);            	//时个位
		BUZ=0;
	}
}
}
}

void time0() interrupt 1
{
   TH0=0XFC;	     //给定时器赋初值,定时1ms
	 TL0=0X18;
	 i++;
	 if(i==1000)     //ms转化为s
	 {
		i=0;
		 s++;
		 dingshi--;
	 }	
}

void key1() interrupt 0     //外部中断0,调整时间
{
	delay(1000);
	if(K1==0)
	  { 
		  mod++;
			while(!K1);
	  }

if(mod>3)
{
	mod=0;
}
	 zero();
}

void naozhong()  interrupt 2                     //开关闹钟
{
	if(K2==0)
		{
			delay(1000);           //消抖
				if(K2==0)
					{
						bell=0;
						BUZ=1;
						zanting=~zanting;
					}                       //关闭蜂鸣器
						while(!K2);          //确认按键松开
     }
}



在这里插入图片描述

二、收获
1.更熟练掌握了定时器中断和外部中断的使用
在这里插入图片描述

在这里插入图片描述

可以通过打开和关闭外部中断使同一个按键实现不同的功能

2.了解到了一点寄存器操作

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3.中断配置小结
外部中断
在这里插入图片描述
定时器中断
在这里插入图片描述

串口通信
在这里插入图片描述
4.更熟练使用proteus
制作了一个小型软件开发版
在这里插入图片描述

5.学会了如何学习一个新元器件(LCD1602)
1.看说明书,重点是看时序图,真值表等
2.按照时序图写程序
3.一二都不成立时,查找相关资料
6.下载原理
单片机的烧写原理:
单片机烧写,又称为单片机程序下载、烧录等,本质上是单片机和PC机按照芯片厂家规定的编程协议,通过芯片厂家规定的接口,把已编译好的程序传输到单片机,单片机把数据存储到自身存储器中。
理解这个原理需要知道几个知识点:
单片机内部是有程序的,是出厂时固化在硬件中,用户无法修改的(这也会被认为它内部没有程序),这些程序可以调用各种通信接口、内部存储器等;
可以下载的通信接口:JTAG,SPI,UART,usb等;(还有很多可以扩展485、以太网等)
编程协议:一般大厂都会公开的,在芯片的专用技术手册中会有;
存储器:有很多种,掩膜,EPROM,EEROM,flash等寿命不一样,掩膜只能一次,而且要工厂做,flash擦写次数10000+;
可以这样比喻性的理解:单片机就是电脑的主板,我们写的程序就是操作系统,主板里面装入引导操作系统的基本程序,下载程序就是给电脑装系统!

7.其他
1.理解了现在电子表的操作原理
2.学会了借助现成品(电子表)作参考,写程序
3.懂得了与人交流的重要性(受王同学的启发,完善了原有程序)

三.后期计划
1、继续32的学习
2、练习焊功
3、继续51其他外设的学习
4、按兴趣学习电路、模电、数电、DXP等相关知识

准确延时,数码管显示!用于电子时钟 #include <reg51.h> //*** 函数定义 *** void long_delay(void); // 长延时 void short_delay(void); // 短延时 void delay10ms(unsigned char); // 延时10MS void write7279(unsigned char, unsigned char);// 写入到HD7279 unsigned char read7279(unsigned char);//从HD7279读入 void send_byte(unsigned char); // 发送一个字节 unsigned char receive_byte(void); //接收一个字节 void init_timer(); /*定时器T0初始化*/ void conv(); /*时、分、秒单元及走时单元转换*/ void dirve(); /*时间显示程序*/ void time_adj(); /*时间调整设置*/ unsigned char digit[6]; unsigned char j; unsigned int tmr; unsigned long wait_cnter; unsigned char hour=0,min=0,sec=0; /*时、分、秒单元清零*/ unsigned char deda=0; /*5mS计数单元清零*/ bit sign; //设置标志位 sbit cs=P1^5; // cs at P1.5 sbit clk=P1^4; // clk 连接于 P1.4 sbit dat=P1^3; // dat 连接于 P1.3 sbit set=P3^7; // key 连接于 P3.7 //****** HD7279A 指令 ****** #define CMD_RESET 0xa4 #define CMD_TEST 0xbf #define DECODE0 0x80 #define DECODE1 0xc8 #define CMD_READ 0x15 #define UNDECODE 0x90 #define RTL_CYCLE 0xa3 #define RTR_CYCLE 0xa2 #define RTL_UNCYL 0xa1 #define RTR_UNCYL 0xa0 #define ACTCTL 0x98 #define SEGON 0xe0 #define SEGOFF 0xc0 #define BLINKCTL 0x88 //*** 主函数 *** main() { for (tmr=0;tmr<0x2000;tmr++); // 上电延时 send_byte(CMD_RESET); // 复位HD7279A //****************************************** // 测试指令演示 //****************************************** send_byte(CMD_TEST); // 指令测试 for (j=0;j<3;j++) // 延时哟3秒? { delay10ms(100); } send_byte(CMD_RESET); // 清除显示 //********************************************** //时间显示 //********************************************** init_timer();/*定时器T0初始化*/ while(1) { if(set==0) time_adj(); conv(); /*时、分、秒单元及走时单元转换*/ dirve(); /*时、分、秒单元显示*/ } } /*定时器T0 5mS初始化*/ void init_timer() { TMOD=0x01; TH0=-(4800/256); TL0=-(4800%256); IE=0x82; TR0=1; } /*5mS定时中断服务子函数*/ void zd(void) interrupt 1 { TH0=-(4800/256); TL0=-(4800%256); deda++; } /*时、分、秒单元及走时单元转换*/ void conv() { if(deda>=200){sec++;deda=0;} if(sec==60){min++;sec=0;} if(min==60){hour++;min=0;} if(hour==24){hour=0;} } void dirve() { digit[0]=sec%10; //计数个位 write7279(DECODE0,digit[0]); //显示个位 digit[1]=0x80|(sec/10); //计数十位 write7279(DECODE0+1,digit[1]); //显示十位 digit[2]=0x80|(min%10); //计数百位 write7279(DECODE0+2,digit[2]); //显示百位 digit[3]=0x80|(min/10); //计数千位 write7279(DECODE0+3,digit[3]); //显示千位 digit[4]=0x80|(hour%10); //计数万位 write7279(DECODE0+4,digit[4]); //显示万位 digit[5]=hour/10; //计数十万位 write7279(DECODE0+5,digit[5]); } void write7279(unsigned char cmd, unsigned char dta) { send_byte (cmd); send_byte (dta); } unsigned char read7279(unsigned char command) { send_byte(command); return(receive_byte()); } void send_byte( unsigned char out_byte) { unsigned char i; cs=0; long_delay(); for (i=0;i<8;i++) { if (out_byte&0x80) { dat=1; } else { dat=0; } clk=1; short_delay(); clk=0; short_delay(); out_byte=out_byte*2; } dat=0; } unsigned char receive_byte(void) { unsigned char i, in_byte; dat=1; // 设置为输入状态 long_delay(); for (i=0;i<8;i++) { clk=1; short_delay(); in_byte=in_byte*2; if (dat) { in_byte=in_byte|0x01; } clk=0; short_delay(); } dat=0; return (in_byte); } void long_delay(void) { unsigned char i; for (i=0;i<0x30;i++); } void short_delay(void) { unsigned char i; for (i=0;i<8;i++); } // ********************* 延时 n*10ms ********************** void delay10ms(unsigned char time) { unsigned char i; unsigned int j; for (i=0;i<time;i++) { for(j=0;j<0x390;j++) { } } } /*时间调整程序*/ void time_adj() { if(set==0) //有键按下,判断按键时间 { delay10ms(200); //1s延时程序 if(set==0) //大于1s,进入时间设置 { ET0=0; TR0=0; //关定时器0 while(set==0) dirve(); //等键释放 // ET1=1;TR1=1; con=0xF3; //开定时器1,让调整位闪烁 write7279(BLINKCTL,0xF3); do { while(set!=0) dirve(); //等待按键 delay10ms(100); if(set!=0) //小于0.5s,进入分钟设置 { while(set==0) dirve(); //等待按键释放 sign=1; min++; if(min==60)min=0; //分钟加1 dirve(); } else sign=0; //大于0.5s,进入小时设置 }while(sign); while(set==0)dirve(); //等待按键释放 // con=0xCF; write7279(BLINKCTL,0xCF); do { while(set!=0) dirve(); //等待按键 delay10ms(100); if(set!=0) //小于0.5s,进入小时设置 { while(set==0) dirve(); //等待按键释放 sign=1; hour++; //小时加1 if(hour==24)hour=0; dirve(); } else sign=0; //大于0.5s,结束时间设置 }while(sign); // ET1=0;TR1=0; //关定时器1 ET0=1;TR0=1; //开定时器0 write7279(BLINKCTL,0xFF); } else //小于1s,进入省电模式 { ET0=1; TR0=1; //开定时器0 while(set!=0); //等待按键 do { dirve(); //有键按下,调显示程序消抖 }while(set!=0); //是干扰则继续循环 } while(set==0) dirve(); //等待按键释放 } else ; //无键按下,跳出按键扫描程序 }
©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值