单片机 简单时钟

单片机 简单时钟

一、简述

    通过Proteus仿真电路,用7段数码管实时显示当前时间时间。

    工程文件:链接:https://pan.baidu.com/s/1CkKAF8h2dzn-bMoqgL-mhQ 密码:i3a2

二、效果

三、工程结构

1、Keil 工程

2、仿真电路图

四、源文件

clock.c文件

#include <reg51.h>
#include <realTime.h>
#define uint unsigned int
#define uchar unsigned char
uint time_t;//毫秒统计值
uchar hour,min,sec;//数码管显示值,小时,分,秒
uchar code led[10]={0xfc,0x60,0xda,0xf2,0x66,0xb6,0xbe,0xe0,0xfe,0xf6};//数码管显示0-9

/********************************************************************/
//函数名:delay_1ms(uint x)
//功能:利用定时器0精确定时1ms; 自加 time_t的值为后面时间调整函数服务
//调用函数:
//输入参数:x,1ms计数
//输出参数:
//说明:延时的时间为1ms乘以x
/********************************************************************/
void delay_1ms(uint x)
{
	TMOD=0X01;//开定时器0,工作方式为1
	TR0=1;//启动定时器0;
	while(x--)
	{
		TH0=0Xfc;//定时1ms初值的高8位装入TH0
		TL0=0X18;//定时1ms初值的低8位装入TL0
		while(!TF0);//等待,直到TF0为1
		TF0=0;	   //重置溢出位标志
		time_t++;//毫秒统计值自加1	
	}		
	TR0=0;//停止定时器0;
}
/**********************************************************/
//函数名:display_num(uchar num,dis_w)
//功能:数码管显示
//调用函数:delay_1ms(uint x)
//输入参数:num,dis_w
//输出参数:
//说明:P0口做数码管段选,P2口做位选
//通过dis_w的值确定num值在数码管上显示的位置
/**********************************************************/
void display_num(uchar num,dis_w)
{
	uchar j;
	for(j=0;j<2;j++)
	{
		P0=0xff;//段选口置高,消影	,设置前保证7段二极管是灭的状态
		P2=~dis_w;//装入位选值	(低电平选中)
		if(j)
			P0=~led[num/10];//显示num个位	
		else			
			P0=~led[num%10];//显示num十位	
		dis_w=dis_w<<1;	   //显示十位后显示个位
		delay_1ms(2);//延时2ms	
	}
}
/**********************************************************/
//函数名:display_char()
//功能:显示时间分隔符“-”
//调用函数:delay_1ms(uint x)
//输入参数:
//输出参数:
//说明:
/**********************************************************/
void display_char()
{
	P0=0xff;//段选口置高,消影
	P2=~0x24;	 //P2端口的十六进制~0x24转换成二进制为11011011可知,点亮的是第3、第6两只数码管
	P0=~0x02;//显示字符“-” ,P0端口的十六进制值~0x02值转换为二进制为11111101,是接数码管的g脚,亮的效果刚好是“-”
}

void main()
{
	
	SYSTEMTIME CurrentTime;	//时间结构体

	while(1)
	{
		//获取当前时间
		DS1302_GetTime(&CurrentTime);
		sec=CurrentTime.Second;	//获取当前秒数
		min=CurrentTime.Minute;	//获取当前分钟数
		hour=CurrentTime.Hour;  //获取当前小时数
		display_num(sec,0x01);//显示秒	00000001 右边第1个数码管开始显示
		display_num(min,0x08);//显示分	00001000 右边第4个数码管开始显示
		display_num(hour,0x40);//显示小时  01000000		 右边第7个数码管开始显示
		display_char();//显示分隔符“-”
	}
}

realTime.h文件

#ifndef _REAL_TIMER_H_
#define _REAL_TIMER_H_

sbit  DS1302_CLK = P1^6;              //实时时钟时钟线引脚 
sbit  DS1302_IO  = P1^7;              //实时时钟数据线引脚 
sbit  DS1302_RST = P1^5;              //实时时钟复位线引脚
sbit  ACC0 = ACC^0;
sbit  ACC7 = ACC^7;

typedef struct __SYSTEMTIME__
{
	unsigned char Second;
	unsigned char Minute;
	unsigned char Hour;
	unsigned char Week;
	unsigned char Day;
	unsigned char Month;
	unsigned char  Year;
	unsigned char DateString[9];
	unsigned char TimeString[9];
}SYSTEMTIME;	//定义的时间类型

#define AM(X)	X
#define PM(X)	(X+12)            	  // 转成24小时制
#define DS1302_SECOND	0x80
#define DS1302_MINUTE	0x82
#define DS1302_HOUR		0x84 
#define DS1302_WEEK		0x8A
#define DS1302_DAY		0x86
#define DS1302_MONTH	0x88
#define DS1302_YEAR		0x8C
#define DS1302_RAM(X)	(0xC0+(X)*2)   	//用于计算 DS1302_RAM 地址的宏 

void DS1302InputByte(unsigned char d) 	//实时时钟写入一字节(内部函数)
{ 
    unsigned char i;
    ACC = d;
    for(i=8; i>0; i--)
    {
        DS1302_IO = ACC0;           	//相当于汇编中的 RRC
        DS1302_CLK = 1;
        DS1302_CLK = 0;
        ACC = ACC >> 1; 
    } 
}

unsigned char DS1302OutputByte(void) 	//实时时钟读取一字节(内部函数)
{ 
    unsigned char i;
    for(i=8; i>0; i--)
    {
        ACC = ACC >>1;         			//相当于汇编中的 RRC 
        ACC7 = DS1302_IO;
        DS1302_CLK = 1;
        DS1302_CLK = 0;
    } 
    return(ACC); 
}


unsigned char Read1302(unsigned char ucAddr)	//读取DS1302某地址的数据
{
    unsigned char ucData;
    DS1302_RST = 0;
    DS1302_CLK = 0;
    DS1302_RST = 1;
    DS1302InputByte(ucAddr|0x01);        // 地址,命令 
    ucData = DS1302OutputByte();         // 读1Byte数据
    DS1302_CLK = 1;
    DS1302_RST = 0;
    return(ucData);
}


void DS1302_GetTime(SYSTEMTIME *Time)
{
	unsigned char ReadValue;
	ReadValue = Read1302(DS1302_SECOND);
	Time->Second = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
	ReadValue = Read1302(DS1302_MINUTE);
	Time->Minute = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
	ReadValue = Read1302(DS1302_HOUR);
	Time->Hour = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
	ReadValue = Read1302(DS1302_DAY);
	Time->Day = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);	
	ReadValue = Read1302(DS1302_WEEK);
	Time->Week = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
	ReadValue = Read1302(DS1302_MONTH);
	Time->Month = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
	ReadValue = Read1302(DS1302_YEAR);
	Time->Year = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);	
}

#endif

 

五、总结

 

1.数码管显示数字机理

        数码管是由7段发光二极管组成的,可通过加电平的高低来控制发光二极管的导通与截止。对7个二极管进行合理地组合控制,将会得到0~9这10种数字的显示效果。本工程使用共阳数码管,即低电平时二极管亮。

2、动态显示  (我们没有这么多的端口来一 一对应控制8个数码管,所以采用动态显示,每次控制一个数码管)

        用P0端口输出一个数字的段码,用P2端口选择那个数码管。一共有6个数字2个“-”(如:13-30-12显示13点30分12秒)

每一次显示一个字符,只要间隔时间足够短,肉眼就能看到所有的数字。

        采用一个机制协调段码、位码输入,控制某字符在某位显示,其它各位熄灭,只要每位数码管显示——熄灭——显示,这显示到显示间隔足够短,由于数码管的余辉特性和人眼有视觉暂留现象,给人眼的视觉印象就会是连续稳定地显示。

3、没有精确同步,有待改进。

 

---------------------------------------回复qq_43795753 这位兄弟-----------------------------------------

利用定时器简单的实现简单时钟,并使用按键简单的设置时间:

链接: https://pan.baidu.com/s/1cxenVFHhL2_WAJ8dFqb2uw 提取码: vemy 

效果:

 

keil:

proteus仿真文件:

代码: 

#include <reg51.h>

#define uint unsigned int
#define uchar unsigned char
uint ms;//微秒
uchar hour,min,sec;//数码管显示值,小时,分,秒
uchar code led[10]={0xfc,0x60,0xda,0xf2,0x66,0xb6,0xbe,0xe0,0xfe,0xf6};//数码管显示0-9

sbit key_Hour = P1^0;//用来控制小时的按键
sbit key_Min = P1^1; //用来控制分钟的按键
sbit key_Sec = P1^2;//用来控制秒的按键

/*************************************************
函数功能:软件消抖延时	  
**************************************************/
void delay( uint s)
{
   uchar i,j;
        for(i=0;i<s;i++)
          for(j=0;j<60;j++)
            ;
}

/**********************************************************/
//函数名:display_num(uchar num,dis_w)
//功能:数码管显示
//调用函数:delay_1ms(uint x)
//输入参数:num,dis_w
//输出参数:
//说明:P0口做数码管段选,P2口做位选
//通过dis_w的值确定num值在数码管上显示的位置
/**********************************************************/
void display_num(uchar num, uint dis_w)
{
	uchar j;
	for(j=0;j<2;j++)
	{
		P0=0xff;//段选口置高,消影	,设置前保证7段二极管是灭的状态
		P2=~dis_w;//装入位选值	(低电平选中)
		if(j)
			P0=~led[num/10];//显示num个位	
		else			
			P0=~led[num%10];//显示num十位	
		dis_w=dis_w<<1;	   //显示十位后显示个位
		delay(2);//延时	
	}
}
/**********************************************************/
//函数名:display_char()
//功能:显示时间分隔符“-”
//调用函数:delay_1ms(uint x)
//输入参数:
//输出参数:
//说明:
/**********************************************************/
void display_char()
{
	P0=0xff;//段选口置高,消影
	P2=~0x24;	 //P2端口的十六进制~0x24转换成二进制为11011011可知,点亮的是第3、第6两只数码管
	P0=~0x02;//显示字符“-” ,P0端口的十六进制值~0x02值转换为二进制为11111101,是接数码管的g脚,亮的效果刚好是“-”
}

void Timer0(void) interrupt 1	 //定时器0中断处理函数(即当定时器溢出的时候会执行该函数)
{
	
	TR0=0;	  //关闭定时器0
	TF0=0;	   //重置溢出位标志

	ms++; //毫秒值+1
	if(ms%130==0)  //每隔一段时间检测一下按键是否有按下,(每1ms中断一次,这个时间太频繁,所以要加if判断一下,不能每1ms就检查)
	{
		if(key_Hour == 0)  //检测 小时 按键有没有按下
		{
			delay(100);//按键消抖
			hour++;
			if(hour >= 24)
			{
				hour = 0;
			}
			
		}
		else if(key_Min == 0)		  //检测 分钟 按键有没有按下
		{
			delay(100);
			min++;
			if(min >= 60)
			{
				min = 0;
			}
		}
		else if(key_Sec == 0)	 //检测 秒 按键有没有按下
		{
			delay(100);
			sec++;
			if(sec >= 60)
			{
				sec = 0;
			}
		}
	}

	//重新装值
	TH0=0Xfc;//定时1ms初值的高8位装入TH0
	TL0=0X18;//定时1ms初值的低8位装入TL0

	TR0=1;//启动定时器0;
}


void main()
{
    ms = 0;	
	TMOD=0X01;//开定时器0,工作方式为1
	//定时器赋初值值
	TH0=0Xfc;//定时1ms初值的高8位装入TH0
	TL0=0X18;//定时1ms初值的低8位装入TL0
	
	

	//设置开始的时间
	sec = 45;	//秒数
	min = 58;	//分钟数
	hour = 21;  //小时数

	ET0=1; //允许外部中断0
	EA=1;  //开启总中断
	TR0=1;//启动定时器0;

	while(1)
	{
		display_num(sec,0x01);//显示秒	00000001 右边第1个数码管开始显示
		display_num(min,0x08);//显示分	00001000 右边第4个数码管开始显示
		display_num(hour,0x40);//显示小时  01000000		 右边第7个数码管开始显示
		display_char();//显示分隔符“-”
	
	    //调整时间
		if( ms >= 1000 )  //1000ms = 1s
		{
			ms = 0;
			sec++;
			if(sec >= 60)//60秒 进位1分
			{
				sec = 0;
				min++;//分钟数加1
				if(min >= 60)//60 分钟小时加1
				{
					min = 0;
					hour++;
					if(hour >= 24)
					{
						hour = 0;
					}
				}
			}
		}
	}
}


 

          

================以下回复 Yuki_fx ============

拿到老师的例子的时候我也没有注意到年月日,例子中,年测的是后面2位,比如2019测出的是19

小时是24制的,所以有两位数

测试年月日

测试星期:

西方将星期日作为星期的第一天,

  星期日   星期一     星期二    星期三     星期四      星期五      星期六

      1           2              3             4              5                6              7

上传文件大小限制为5M,所以将测试效果放到网盘:

链接: https://pan.baidu.com/s/1hJOa6qJWBYuXjlZ-BFovow 提取码: b9gu

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页