51单片机学习笔记_10 DS1302 时钟(可调)

DS1302时钟芯片与数码管显示时间实现
本文介绍了如何使用DS1302时钟芯片通过数码管显示当前时间,包括芯片的工作原理,如VCC电源、晶振、CE、IO和SCLK引脚的作用,以及读写时序。代码示例展示了初始化、写入和读取时间的函数,同时提到了按键修改时钟值的实现思路,强调了处理溢出和写保护的问题。

时钟

DS1302 时钟芯片。开发板上已经集成的芯片,本次实验目的是用数码管显示出当前时间,以hh-mm-ss的格式。

image-20230130130648915

VCC2:电源

VCC1:备用电源,即单片机断电后维持时钟继续运行的。STC89C52 是没有的,即断电就停止运行。

X1X2:晶振。

CE:使能。

IO:输入输出。

SCLK:时钟。

操作流程就是将数据写入 DS1302 的寄存器来设置当前时间格式,然后 DS1302 时钟运作后我们再将寄存器中数据读出。

DS1302 中存储顺序是秒分时日月周年,存储格式是 BCD 码。

image-20230110142624173

首先,CE/RST在整个读写过程中要保持是高电平,一次字节读写完毕后要返回低电平。

然后,控制指令字输入后的下一个 SCLK 上升沿数据写入,下降沿数据读出,在图中可以看得出来,都是从低位0先读,最后出高位7. 第一位代表读或写,后四位代表地址(年、月、日、时、分、秒有着不同的地址),R/C 代表存取 RAM 数据还是读取时钟数据。最高位只有=1才能启用时钟。后面八位是读或写的数据。

读时序注意:因此写命令的第八个上升沿结束后,紧接着第八个下降沿就开始读数据了。

写时序注意:先关闭写保护 WP,1是只读,0才可以写。

image-20230206234252279

我们先试着写入一个秒初值,然后读取时钟里的秒数值,应该是1s一加。

记得 BCD 码和十进制的转换。

image-20230206015234658

#include"REG52.H"
#include"LCD1602.h"
sbit DS1302_CLK=P3^6;//时钟管脚
sbit DS1302_CE=P3^5;//复位管脚
sbit DS1302_IO=P3^4;//数据管脚

typedef unsigned char u8;
void ds1302Init(){
    DS1302_CE=0;
    DS1302_CLK=0;
}

void ds1302WriteByte(u8 command,u8 dat)
{
u8 i;
    DS1302_CE=1;
    
    for(i=0;i<8;i++)
    {
        DS1302_IO=command&(0x01<<i);//从低到高写入数据
        DS1302_CLK=1;
        DS1302_CLK=0;
    //需要查询最小执行时间。不过这里执行时间都大于最小时间了。
    }
    for(i=0;i<8;i++)
    {
        DS1302_IO=dat&(0x01<<i);//从低到高写入数据
        DS1302_CLK=1;
        DS1302_CLK=0;
    //需要查询最小执行时间。不过这里执行时间都大于最小时间了。
    }
    DS1302_CE=0;
}

u8 ds1302ReadByte(u8 command)
{
	u8 i;
	u8 dat=0x00;//全局变量会有初值0,局部变量不会。data 是要写入的数据
    DS1302_CE=1;
    for(i=0;i<8;i++)
    {
        DS1302_IO=command&(0x01<<i);//从低到高写入数据
        DS1302_CLK=0;
        DS1302_CLK=1;//这里为什么和 write 是相反的?因为我们注意到 read 是先上升沿读入8位,再切换为下降沿读入8位。、
        //如果还是先1后0,读入第8位时不仅会把上升沿读掉,下降沿也会读掉,导致错过第九位。
    //需要查询最小执行时间。不过这里执行时间都大于最小时间了。
    }
    for(i=0;i<8;i++)
    {
        
        DS1302_CLK=1;
        DS1302_CLK=0;
    //需要查询最小执行时间。不过这里执行时间都大于最小时间了。
		if(DS1302_IO)dat|=(0x01<<i);//从低到高写入数据
    }
    DS1302_CE=0;
	DS1302_IO=0;//读取前要归0,因为内部是以 BCD 码格式存储。
	return dat;
}

void main(){
    u8 second;
	LCD_Init();
	ds1302Init();
	//ds1302WriteByte(0x8E,0x00);//如果读出数据>59,可能是处于 wp 写保护,需要通过这句关闭
	ds1302WriteByte(0x80,0x01);//写入55s,就是0x55,与内部BCD码对应
	LCD_ShowString(1,1,"DS1302");
	second=ds1302ReadByte(0x81);
	while(1){LCD_ShowNum(2,1,second/16*10+second%16,3);}
}

写入其他变量时分秒都一样。就是更换不同的地址。

不过全部定义变量明显太麻烦。可以先 define 定义了所有地址,再定义一个数组存所有值。

u8 ds1302_address[7]={0x80, 0x82, 0x84, 0x86, 0x88, 0x8c};// second, minute, hour, date, month, year
u8 ds1302_time[7]={0x55, 0x59, 0x11, 0x07, 0x02, 0x23};

void main(){
	u8 i;
    u8 time_temp[6];
	u8 temp;
	LCD_Init();
	ds1302Init();
	//ds1302WriteByte(0x8E,0x00);//如果读出数据>59,可能是处于 wp 写保护,需要通过这句关闭
	ds1302WriteByte(0x80,0x01);
	ds1302WriteByte(0x82,0x37);
	for(i=0;i<6;i++){
		ds1302WriteByte(ds1302_address[i], ds1302_time[i]);
	}
	LCD_ShowString(1,1,"  -  -  ");
	LCD_ShowString(2,1,"  :  :  ");
	while(1){
		for(i=0;i<6;i++){
			temp=ds1302ReadByte(ds1302_address[i]+1);
			time_temp[i]=temp/16*10+temp%16;
		}
		for(i=0;i<6;i++){
			LCD_ShowNum(1+i/3,1+3*(i%3), time_temp[5-i],2);
		}
		//LCD_ShowNum(2,1,minute/16*10+minute%16,2);
		//LCD_ShowNum(2,3,second/16*10+second%16,2);
	}
}

然后我们让按键可以修改时钟值,这样用户可以手动更改时钟。

按键1切换切换要调整的位,按键2切换回第0位,按键3+1,按键4-1.

难点主要在于+ -的溢出情况。这里我们不考虑秒+1进位对分钟或小时等影响,大多数闹钟也是这样设计的。59+1就归零,0-1就变59.

if(key){
			switch(key){
				case KEY1_PRESS: time_set_select=0;break;
				case KEY2_PRESS: time_set_select=(time_set_select+1)%6;break;
				case KEY3_PRESS:
					if(time_set_select<=1&&ds1302_time[time_set_select]==59){//second, minute 59-00
						ds1302_time[time_set_select]=0;
					}
					else if(time_set_select==2&&ds1302_time[time_set_select]==23)ds1302_time[time_set_select]=0;//hour 23-00
					else if(time_set_select==3&&ds1302_time[time_set_select]==maxDate(ds1302_time[3],ds1302_time[4],ds1302_time[5]))ds1302_time[time_set_select]=1;//date 超过最大日期 -01
					else if(time_set_select==4&&ds1302_time[time_set_select]==12)ds1302_time[time_set_select]=1;//month 12-00
					else if(time_set_select==5&&ds1302_time[time_set_select]==99)ds1302_time[time_set_select]=0;//year 99-00
					else ds1302_time[time_set_select]++;break;
				case KEY4_PRESS: 
					if((ds1302_time[time_set_select]&&time_set_select!=3&&time_set_select!=4)
					||ds1302_time[time_set_select]>1)ds1302_time[time_set_select]--;//date month!=0
					else if(time_set_select<=1)ds1302_time[time_set_select]=59;
					else if(time_set_select==2)ds1302_time[time_set_select]=23;
					else if(time_set_select==3)ds1302_time[time_set_select]=maxDate(ds1302_time[3],ds1302_time[4],ds1302_time[5]);
					else if(time_set_select==4)ds1302_time[time_set_select]=12;
					else if(time_set_select==5)ds1302_time[time_set_select]=99;

					break;
			}
			if(key>=3)timeSet();

计算日期最大值函数:

u8 maxDate(u8 date, u8 month,u8 year){
	if(month==1||month==3||month==5||month==7||month==8||month==10||month==12)return 31;
	else if((month==4||month==6||month==9||month==11)&&date==31)return 30;
	else if(year%4)return 28;
	else if(year%4==0&&year%100)return 28;
	else return 29;
}

有个弊端就是不知道现在在操作哪一位(只能自己记住)。可以利用定时器的定时闪烁,让在被操作的位不停闪烁。(重复写入空格或数值。)

还有就是按下按键,时钟会停走,不松开就一直停着。可以改按键触发条件为按下或弹起的上升沿或下降沿,避免按键处理函数死循环。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

灰海宽松

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值