51单片机实现数字钟

一、实验目的
1、 掌握数字时钟芯片工作原理;
2、 掌握按键扫描及 LED 数码管显示电路设计方法;
3、 熟练掌握 I/O 接口电路程序编写与调试方法。
二、知识点涉及
I2C 芯片时序与数据读写控制方法、按键扫描及 LED 数码管显示电路设计方法、I/O 接口电路程序设计。
三、实验内容
1、单片机外接 DS1302 实现时间数据的实时读取与显示;
2、连接电路并编写程序:
(1)能够实现准确计时,以数字形式显示时、分、秒的时间;
(2)小时以 24 小时计时形式,分秒计时为 60 进制,能够调节时钟时间。
硬件连接
在这里插入图片描述
上代码

main.c
#include "public.h"
#include "smg.h"
#include "ds1302.h"

//定义独立按键控制脚
sbit KEY1=P1^0;
sbit KEY2=P1^1;
sbit KEY3=P1^2;
sbit KEY4=P1^3;
sbit KEY5=P1^4;
//使用宏定义独立按键按下的键值
#define KEY1_PRESS	1
#define KEY2_PRESS	2
#define KEY3_PRESS	3
#define KEY4_PRESS	4
#define KEY5_PRESS	5
#define KEY_UNPRESS	0
u8 key_scan(u8 mode)
 {
	static u8 key=1;

	if(mode)key=1;//连续扫描按键
	if(key==1&&(KEY1==0||KEY2==0||KEY3==0||KEY4==0||KEY5==0))//任意按键按下
	{
		delay_10us(1000);//消抖
		key=0;
		if(KEY1==0)
			return KEY1_PRESS;
		else if(KEY2==0)
			return KEY2_PRESS;
		else if(KEY3==0)
			return KEY3_PRESS;
		else if(KEY4==0)
			return KEY4_PRESS;
		else if(KEY5==0)
			return KEY5_PRESS;	
	}
	else if(KEY1==1&&KEY2==1&&KEY3==1&&KEY4==1&&KEY5==1)	//无按键按下
	{
		key=1;			
	}
	return KEY_UNPRESS;		
}

/*******************************************************************************
* 函 数 名       : main
* 函数功能		 : 主函数
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void main()
{	   			
	u8 time_buf[8];
	u8 key=0;
	ds1302_init();//初始化DS1302

	while(1)
	{			
		key=key_scan(0);//判断是否有键按下
		 if(key==KEY5_PRESS)
		 {
		 while(1)
		{
		key=key_scan(0);
		if(key==KEY1_PRESS)
		{gDS1302_TIME[2]--;	
		if((gDS1302_TIME[2]&0x0f)>9)					 //换成BCD码
				{
					gDS1302_TIME[2]=gDS1302_TIME[2]-6;
				}
				if((gDS1302_TIME[2]<=0x00))		//小时只能到24
				{
					gDS1302_TIME[2]=0x23;
				}
		  ds1302_init();}//初始化DS1302
		else if(key==KEY2_PRESS)
		{gDS1302_TIME[2]++;	
		 if((gDS1302_TIME[2]&0x0f)>9)					 //换成BCD码
				{
					gDS1302_TIME[2]=gDS1302_TIME[2]+6;
				}
				if((gDS1302_TIME[2]>=0x24))		//小时只能到24
				{
					gDS1302_TIME[2]=0;
				}	
		ds1302_init();}
		else if(key==KEY3_PRESS)
		{gDS1302_TIME[1]--;	
		 if((gDS1302_TIME[1]&0x0f)>9)					 //换成BCD码
				{
					gDS1302_TIME[1]=gDS1302_TIME[1]-6;
				}
				if((gDS1302_TIME[1]<=0x00))		//分秒只能到59
				{
					gDS1302_TIME[1]=0x59;
				}		
		ds1302_init();}
		else if(key==KEY4_PRESS)
		{gDS1302_TIME[1]++;	
		 if((gDS1302_TIME[1]&0x0f)>9)					 //换成BCD码
				{
					gDS1302_TIME[1]=gDS1302_TIME[1]+6;
				}
				if((gDS1302_TIME[1]>=0x60))		//小时只能到24
				{
					gDS1302_TIME[1]=0;
				} 
		ds1302_init();}
		else if(key==KEY5_PRESS)
		break;
		time_buf[0]=gsmg_code[gDS1302_TIME[2]/16];
		time_buf[1]=gsmg_code[gDS1302_TIME[2]&0x0f];
		time_buf[2]=0x40;
		time_buf[3]=gsmg_code[gDS1302_TIME[1]/16];
		time_buf[4]=gsmg_code[gDS1302_TIME[1]&0x0f];
		time_buf[5]=0x40;
		time_buf[6]=gsmg_code[gDS1302_TIME[0]/16];
		time_buf[7]=gsmg_code[gDS1302_TIME[0]&0x0f];
		smg_display(time_buf,1);
		} }
		ds1302_read_time();
		time_buf[0]=gsmg_code[gDS1302_TIME[2]/16];
		time_buf[1]=gsmg_code[gDS1302_TIME[2]&0x0f];
		time_buf[2]=0x40;
		time_buf[3]=gsmg_code[gDS1302_TIME[1]/16];
		time_buf[4]=gsmg_code[gDS1302_TIME[1]&0x0f];
		time_buf[5]=0x40;
		time_buf[6]=gsmg_code[gDS1302_TIME[0]/16];
		time_buf[7]=gsmg_code[gDS1302_TIME[0]&0x0f];
		smg_display(time_buf,1);	
	}		
}

smg.c
#include "smg.h"

//共阴极数码管显示0~F的段码数据
u8 gsmg_code[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
				0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};

/*******************************************************************************
* 函 数 名       : smg_display
* 函数功能		 : 动态数码管显示
* 输    入       : dat:要显示的数据
				   pos:从左开始第几个位置开始显示,范围1-8
* 输    出    	 : 无
*******************************************************************************/
void smg_display(u8 dat[],u8 pos)
{
	u8 i=0;
	u8 pos_temp=pos-1;

	for(i=pos_temp;i<8;i++)
	{
	   	switch(7-i)//位选
		{
			case 0: LSC=1;LSB=1;LSA=1;break;
			case 1: LSC=1;LSB=1;LSA=0;break;
			case 2: LSC=1;LSB=0;LSA=1;break;
			case 3: LSC=1;LSB=0;LSA=0;break;
			case 4: LSC=0;LSB=1;LSA=1;break;
			case 5: LSC=0;LSB=1;LSA=0;break;
			case 6: LSC=0;LSB=0;LSA=1;break;
			case 7: LSC=0;LSB=0;LSA=0;break;
		}
		SMG_A_DP_PORT=dat[i-pos_temp];//传送段选数据
		delay_10us(100);//延时一段时间,等待显示稳定
		SMG_A_DP_PORT=0x00;//消音
	}
}

smg.h
#ifndef _smg_H
#define _smg_H

#include "public.h"


#define SMG_A_DP_PORT	P0	//使用宏定义数码管段码口

//定义数码管位选信号控制脚
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;

extern u8 gsmg_code[17];

void smg_display(u8 dat[],u8 pos);

#endif
public.c
#include "public.h"

/*******************************************************************************
* 函 数 名       : delay_10us
* 函数功能		 : 延时函数,ten_us=1时,大约延时10us
* 输    入       : ten_us
* 输    出    	 : 无
*******************************************************************************/
void delay_10us(u16 ten_us)
{
	while(ten_us--);	
}

/*******************************************************************************
* 函 数 名       : delay_ms
* 函数功能		 : ms延时函数,ms=1时,大约延时1ms
* 输    入       : ms:ms延时时间
* 输    出    	 : 无
*******************************************************************************/
void delay_ms(u16 ms)
{
	u16 i,j;
	for(i=ms;i>0;i--)
		for(j=110;j>0;j--);
}
public.h
#ifndef _public_H
#define _public_H

#include "reg52.h"

typedef unsigned int u16;	//对系统默认数据类型进行重定义
typedef unsigned char u8;


void delay_10us(u16 ten_us);
void delay_ms(u16 ms);

#endif

ds1302.c
#include "ds1302.h"
#include "intrins.h"

//---DS1302写入和读取时分秒的地址命令---//
//---秒分时日月周年 最低位读写位;-------//
u8 gREAD_RTC_ADDR[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d}; 
u8 gWRITE_RTC_ADDR[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};

//---DS1302时钟初始化2021年5月20日星期四13点51分47秒。---//
//---存储顺序是秒分时日月周年,存储格式是用BCD码---//
u8 gDS1302_TIME[7] = {0x47, 0x51, 0x13, 0x20, 0x04, 0x05, 0x21};


/*******************************************************************************
* 函 数 名       : ds1302_write_byte
* 函数功能		 : DS1302写单字节
* 输    入       : addr:地址/命令
				   dat:数据
* 输    出    	 : 无
*******************************************************************************/
void ds1302_write_byte(u8 addr,u8 dat)
{
	u8 i=0;
	
	DS1302_RST=0;
	_nop_();	
	DS1302_CLK=0;//CLK低电平
	_nop_();
	DS1302_RST=1;//RST由低到高变化
	_nop_();

	for(i=0;i<8;i++)//循环8次,每次写1位,先写低位再写高位
	{
		DS1302_IO=addr&0x01;
		addr>>=1;
		DS1302_CLK=1;
		_nop_();
		DS1302_CLK=0;//CLK由低到高产生一个上升沿,从而写入数据
		_nop_();		
	}
	for(i=0;i<8;i++)//循环8次,每次写1位,先写低位再写高位
	{
		DS1302_IO=dat&0x01;
		dat>>=1;
		DS1302_CLK=1;
		_nop_();
		DS1302_CLK=0;
		_nop_();		
	}
	DS1302_RST=0;//RST拉低
	_nop_();	
}

/*******************************************************************************
* 函 数 名       : ds1302_read_byte
* 函数功能		 : DS1302读单字节
* 输    入       : addr:地址/命令
* 输    出    	 : 读取的数据
*******************************************************************************/
u8 ds1302_read_byte(u8 addr)
{
	u8 i=0;
	u8 temp=0;
	u8 value=0;

	DS1302_RST=0;
	_nop_();	
	DS1302_CLK=0;//CLK低电平
	_nop_();
	DS1302_RST=1;//RST由低到高变化
	_nop_();
	
	for(i=0;i<8;i++)//循环8次,每次写1位,先写低位再写高位
	{
		DS1302_IO=addr&0x01;
		addr>>=1;	
		DS1302_CLK=1;
		_nop_();
		DS1302_CLK=0;//CLK由低到高产生一个上升沿,从而写入数据
		_nop_();		
	}
	for(i=0;i<8;i++)//循环8次,每次读1位,先读低位再读高位
	{
		temp=DS1302_IO;
		value=(temp<<7)|(value>>1);//先将value右移1位,然后temp左移7位,最后或运算
		DS1302_CLK=1;
		_nop_();
		DS1302_CLK=0;
		_nop_();		
	}
	DS1302_RST=0;//RST拉低
	_nop_();	
	DS1302_CLK=1;//对于实物中,P3.4口没有外接上拉电阻的,此处代码需要添加,使数据口有一个上升沿脉冲。
	_nop_();
	DS1302_IO = 0;
	_nop_();
	DS1302_IO = 1;
	_nop_();	
	return value;		
}

/*******************************************************************************
* 函 数 名       : ds1302_init
* 函数功能		 : DS1302初始化时间
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void ds1302_init(void)
{
	u8 i=0;
	ds1302_write_byte(0x8E,0X00);
	for(i=0;i<7;i++)
	{
		ds1302_write_byte(gWRITE_RTC_ADDR[i],gDS1302_TIME[i]);	
	}
	ds1302_write_byte(0x8E,0X80);	
}

/*******************************************************************************
* 函 数 名       : ds1302_read_time
* 函数功能		 : DS1302读取时间
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void ds1302_read_time(void)
{
	u8 i=0;
	for(i=0;i<7;i++)
	{
		gDS1302_TIME[i]=ds1302_read_byte(gREAD_RTC_ADDR[i]);	
	}	
}

ds1302.h
#ifndef _ds1302_H
#define _ds1302_H

#include "public.h"

//管脚定义
sbit DS1302_RST=P3^5;//复位管脚
sbit DS1302_CLK=P3^6;//时钟管脚
sbit DS1302_IO=P3^4;//数据管脚

//变量声明
extern u8 gDS1302_TIME[7];//存储时间

//函数声明
void ds1302_init(void);
void ds1302_read_time(void);


#endif
reg52.h
/*--------------------------------------------------------------------------
REG52.H

Header file for generic 80C52 and 80C32 microcontroller.
Copyright (c) 1988-2002 Keil Elektronik GmbH and Keil Software, Inc.
All rights reserved.
--------------------------------------------------------------------------*/

#ifndef __REG52_H__
#define __REG52_H__

/*  BYTE Registers  */
sfr P0    = 0x80;
sfr P1    = 0x90;
sfr P2    = 0xA0;
sfr P3    = 0xB0;
sfr PSW   = 0xD0;
sfr ACC   = 0xE0;
sfr B     = 0xF0;
sfr SP    = 0x81;
sfr DPL   = 0x82;
sfr DPH   = 0x83;
sfr PCON  = 0x87;
sfr TCON  = 0x88;
sfr TMOD  = 0x89;
sfr TL0   = 0x8A;
sfr TL1   = 0x8B;
sfr TH0   = 0x8C;
sfr TH1   = 0x8D;
sfr IE    = 0xA8;
sfr IP    = 0xB8;
sfr SCON  = 0x98;
sfr SBUF  = 0x99;

/*  8052 Extensions  */
sfr T2CON  = 0xC8;
sfr RCAP2L = 0xCA;
sfr RCAP2H = 0xCB;
sfr TL2    = 0xCC;
sfr TH2    = 0xCD;


/*  BIT Registers  */
/*  PSW  */
sbit CY    = PSW^7;
sbit AC    = PSW^6;
sbit F0    = PSW^5;
sbit RS1   = PSW^4;
sbit RS0   = PSW^3;
sbit OV    = PSW^2;
sbit P     = PSW^0; //8052 only

/*  TCON  */
sbit TF1   = TCON^7;
sbit TR1   = TCON^6;
sbit TF0   = TCON^5;
sbit TR0   = TCON^4;
sbit IE1   = TCON^3;
sbit IT1   = TCON^2;
sbit IE0   = TCON^1;
sbit IT0   = TCON^0;

/*  IE  */
sbit EA    = IE^7;
sbit ET2   = IE^5; //8052 only
sbit ES    = IE^4;
sbit ET1   = IE^3;
sbit EX1   = IE^2;
sbit ET0   = IE^1;
sbit EX0   = IE^0;

/*  IP  */
sbit PT2   = IP^5;
sbit PS    = IP^4;
sbit PT1   = IP^3;
sbit PX1   = IP^2;
sbit PT0   = IP^1;
sbit PX0   = IP^0;

/*  P3  */
sbit RD    = P3^7;
sbit WR    = P3^6;
sbit T1    = P3^5;
sbit T0    = P3^4;
sbit INT1  = P3^3;
sbit INT0  = P3^2;
sbit TXD   = P3^1;
sbit RXD   = P3^0;

/*  SCON  */
sbit SM0   = SCON^7;
sbit SM1   = SCON^6;
sbit SM2   = SCON^5;
sbit REN   = SCON^4;
sbit TB8   = SCON^3;
sbit RB8   = SCON^2;
sbit TI    = SCON^1;
sbit RI    = SCON^0;

/*  P1  */
sbit T2EX  = P1^1; // 8052 only
sbit T2    = P1^0; // 8052 only
             
/*  T2CON  */
sbit TF2    = T2CON^7;
sbit EXF2   = T2CON^6;
sbit RCLK   = T2CON^5;
sbit TCLK   = T2CON^4;
sbit EXEN2  = T2CON^3;
sbit TR2    = T2CON^2;
sbit C_T2   = T2CON^1;
sbit CP_RL2 = T2CON^0;

#endif

intrins.h
/*--------------------------------------------------------------------------
INTRINS.H

Intrinsic functions for C51.
Copyright (c) 1988-2010 Keil Elektronik GmbH and ARM Germany GmbH
All rights reserved.
--------------------------------------------------------------------------*/

#ifndef __INTRINS_H__
#define __INTRINS_H__

#pragma SAVE

#if defined (__CX2__)
#pragma FUNCTIONS(STATIC)
/* intrinsic functions are reentrant, but need static attribute */
#endif

extern void          _nop_     (void);
extern bit           _testbit_ (bit);
extern unsigned char _cror_    (unsigned char, unsigned char);
extern unsigned int  _iror_    (unsigned int,  unsigned char);
extern unsigned long _lror_    (unsigned long, unsigned char);
extern unsigned char _crol_    (unsigned char, unsigned char);
extern unsigned int  _irol_    (unsigned int,  unsigned char);
extern unsigned long _lrol_    (unsigned long, unsigned char);
extern unsigned char _chkfloat_(float);
#if defined (__CX2__)
extern int           abs       (int);
extern void          _illop_   (void);
#endif
#if !defined (__CX2__)
extern void          _push_    (unsigned char _sfr);
extern void          _pop_     (unsigned char _sfr);
#endif

#pragma RESTORE

#endif


注意时钟接口不要接错!

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值