【51单片机DS1302时钟芯片读取数码管显示打造小成本高品质】2022-12-23

缘由https://ask.csdn.net/questions/7867303

/*写回复缘由https://ask.csdn.net/questions/7867303*/
#include "reg52.h"
sbit RST=P3^5;//DS1302允许(读/写)当RST为高电平时,所有的数据传送被初始化,允许对DS1302进行操作。如果在传送过程中RST置为低电平,则会终止此次数据传送,I/O引脚变为高阻态。RST必须保持低电平。只有在SCLK为低电平时,才能将RST置为高电平。
sbit SCLK=P3^6;//DS1302的SCLK为时钟输入端。在控制指令字输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从低位即位0开始。同样,在紧跟8位的控制指令字后的下一个SCLK脉冲的下降沿读出DS1302的数据,读出数据时从低位0位到高位7。在控制指令字输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从低位即位0开始。同样,在紧跟8位的控制指令字后的下一个SCLK脉冲的下降沿读出DS1302的数据,读出数据时从低位0位到高位7。
sbit IO=P3^7;//DS1302数据传递(读/写)秒寄存器80H写81H读/分寄存器82H写83H读/时寄存器84H写85H读/天寄存器86H写87H读/月寄存器88H写89H读/周寄存器8AH写8BH读/年寄存器8CH写8DH读,存放的数据位为BCD码形式。
sbit L1=P2^2;
sbit L2=P2^3;
sbit L3=P2^4;
sbit beep=P2^5;
bit smk=0,bek=0;
unsigned char code ShuMaGuan[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00,0X80,64};//0~9隐.小数点-号
unsigned char Nbcd=0,Ybcd=0,Tbcd=0,Sbcd=0,Fbcd=0,Mbcd=0,Zbcd=0,SJ=0;//年月天时分秒周
void ShuMaXianShi(unsigned char s,unsigned char w,unsigned char d)
{
	unsigned char YanShi=0;
	P0=255;
		switch(w)
		{
			case 2:{L1=0;L2=L3=1;}break;//011
			case 3:{L2=0;L1=L3=1;}break;//101
			case 4:{L1=L2=0;L3=1;}break;//001
			case 5:{L1=L2=1;L3=0;}break;//110
			case 6:{L1=L3=0;L2=1;}break;//010
			case 7:{L1=1;L2=L3=0;}break;//100
			case 8:{L1=L2=L3=0;}break;//000
			case 1:{L1=L2=L3=1;}break;//111
			default:{L1=L2=L3=0;}break;
		}
		P0=ShuMaGuan[s]|ShuMaGuan[d];//d显示小数点11,10关闭
		while(++YanShi);
}
void XianShi()
{
		ShuMaXianShi(Sbcd/10,1,10);
		ShuMaXianShi(Sbcd%10,2,10);
		ShuMaXianShi(12,3,10);
		ShuMaXianShi(Fbcd/10,4,10);
		ShuMaXianShi(Fbcd%10,5,10);
		ShuMaXianShi(12,6,10);
		ShuMaXianShi(Mbcd/10,7,10);
		ShuMaXianShi(Mbcd%10,8,10);	
}
void XK_1302(unsigned char SD)
{
	unsigned char cs=8,zh=1;
	RST=SCLK=0;
	RST=1;
	xf:if(cs--)
	{
		IO=SD&0x01;//低位起16进制
		SCLK=1;//上升沿写数据
		SD>>=1;//右移
		SCLK=0;//为下次上升沿准备时钟
		goto xf;
	}//发送命令时不能RST=0表示结束
}
unsigned char Du_1302(unsigned char Dz)
{
	unsigned char cs=8,zh=1,sj=0;
	XK_1302(Dz);//读地址命令
	SJ=SCLK=0;
	xf:if(cs--)
	{
		SJ+=IO*zh;//位为BCD码形式
		SCLK=1;//为下次下降沿准备时钟
		zh*=2;//直接转化为10进制数据
		if(cs==4){sj=SJ;SJ=0;zh=1;}
		SCLK=0;//下降沿读数据
		goto xf;
	}
	RST=0;
	return SJ*10+sj;
}
void main()
{
	unsigned char hm=0;
	while(1)
	{
		Mbcd=Du_1302(0x81);
		Fbcd=Du_1302(0x83);
		Sbcd=Du_1302(0x85);
		if(bek&&(Fbcd==0&&Mbcd<9||Fbcd==30&&Mbcd<5))beep=~beep;else beep=0;
		if(hm!=Mbcd%10){hm=Mbcd%10;bek=!bek;}
		XianShi();
	}
}
/*止回复*/

 发送命令IO可以编程软件仿真可以看到,然读取数据IO无法看到,只有在仿真电路中才能用示波器看到IO的全部,暂时只写了读取秒钟在数码管显示。之上蜂鸣器周期约10MS之下蜂鸣器周期约40US。

//缘由https://ask.csdn.net/questions/7867303
#include "reg52.h"
sbit RST=P3^5;//DS1302允许(读/写)当RST为高电平时,所有的数据传送被初始化,允许对DS1302进行操作。如果在传送过程中RST置为低电平,则会终止此次数据传送,I/O引脚变为高阻态。RST必须保持低电平。只有在SCLK为低电平时,才能将RST置为高电平。
sbit SCLK=P3^6;//DS1302的SCLK为时钟输入端。在控制指令字输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从低位即位0开始。同样,在紧跟8位的控制指令字后的下一个SCLK脉冲的下降沿读出DS1302的数据,读出数据时从低位0位到高位7。在控制指令字输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从低位即位0开始。同样,在紧跟8位的控制指令字后的下一个SCLK脉冲的下降沿读出DS1302的数据,读出数据时从低位0位到高位7。
sbit IO=P3^7;//DS1302数据传递(读/写)秒寄存器80H写81H读/分寄存器82H写83H读/时寄存器84H写85H读/天寄存器86H写87H读/月寄存器88H写89H读/周寄存器8AH写8BH读/年寄存器8CH写8DH读,存放的数据位为BCD码形式。
sbit L1=P2^2;
sbit L2=P2^3;
sbit L3=P2^4;
sbit beep=P2^5;
bit smk=0,bek=0;
unsigned char code ShuMaGuan[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00,0X80,64};//0~9隐.小数点-号
unsigned char Nbcd=0,Ybcd=0,Tbcd=0,Sbcd=0,Fbcd=0,Mbcd=0,Zbcd=0,SJ=0;//年 月 天 时 分 秒 周 数据
void ShuMaXianShi(unsigned char s,unsigned char w,unsigned char d)
{
	P0=255;
	switch(w)
	{
		case 2:{L1=0;L2=L3=1;}break;//011
		case 3:{L2=0;L1=L3=1;}break;//101
		case 4:{L1=L2=0;L3=1;}break;//001
		case 5:{L1=L2=1;L3=0;}break;//110
		case 6:{L1=L3=0;L2=1;}break;//010
		case 7:{L1=1;L2=L3=0;}break;//100
		case 8:{L1=L2=L3=0;}break;//000
		case 1:{L1=L2=L3=1;}break;//111
		default:{L1=L2=L3=0;}break;
	}
	P0=ShuMaGuan[s]|ShuMaGuan[d];//d显示小数点11,10关闭
}
void XianShi(unsigned char w)
{
	switch(w)
	{
		case 0:ShuMaXianShi(Sbcd/10,1,10);break;
		case 1:ShuMaXianShi(Sbcd%10,2,10);break;
		case 2:ShuMaXianShi(12,3,10);break;
		case 3:ShuMaXianShi(Fbcd/10,4,10);break;
		case 4:ShuMaXianShi(Fbcd%10,5,10);break;
		case 5:ShuMaXianShi(12,6,10);break;
		case 6:ShuMaXianShi(Mbcd/10,7,10);break;
		case 7:ShuMaXianShi(Mbcd%10,8,10);break;
		default:break;
	}
}
void XK_1302(unsigned char SD)
{
	unsigned char cs=8,zh=1;
	RST=SCLK=0;
	RST=1;
	xf:if(cs--)
	{
		IO=SD&0x01;//低位起16进制
		SCLK=1;//上升沿写数据
		SD>>=1;//右移
		SCLK=0;//为下次上升沿准备时钟
		goto xf;
	}//发送命令时不能RST=0表示结束
}
unsigned char Du_1302(unsigned char Dz)
{
	unsigned char cs=8,zh=1,sj=0;
	XK_1302(Dz);//读地址命令
	SJ=SCLK=0;
	xf:if(cs--)
	{
		SJ+=IO*zh;//位为BCD码形式
		SCLK=1;//为下次下降沿准备时钟
		zh*=2;//直接转化为10进制数据
		if(cs==4){sj=SJ;SJ=0;zh=1;}
		SCLK=0;//下降沿读数据
		goto xf;
	}
	RST=0;
	return SJ*10+sj;
}
void main()
{
	unsigned char hm=0,smys=0,w=0,m=0;
	Mbcd=Du_1302(0x81);Fbcd=Du_1302(0x83);Sbcd=Du_1302(0x85);
	while(1)
	{
		if(++smys>174)
		{
			XianShi(w);
			if(++w>7)
			{
				w=0;
				Mbcd=Du_1302(0x81);
				if(Mbcd==0)Fbcd=Du_1302(0x83);
				if(Mbcd==0&&Fbcd==0)Sbcd=Du_1302(0x85);
			}
			smys=0;
		}
		if(++m>9)
        {
            if(bek&&(Fbcd==0&&Mbcd<9||Fbcd==30&&Mbcd<5))beep=~beep;
            else beep=0;
            m=0;
        }
		if(hm!=Mbcd%10){hm=Mbcd%10;bek=!bek;}
	}
}

可以这样说,以贴出来的电路和只读取1302的时分秒送8位数码管显示,并有整点4响半点2响为功能,并在主函数while(1)内用beep=!beep;仿真测试整个代码响应时间为目标,且不得使用循环语句包括GOTO在内构成的独占循环计数延时,也不开定时器的,即这次把数码管显示中的while计数延时也去掉了,从而提高了程序整体响应时间含动态最小响应时间,从之前的10ms提高到了约40us,这就意味着整个程序还有充足的时间去响应其它更多事件安排。各位高手可以尽情展现优秀算法,尽量使得beep的周期最小,我目前能做到beep的周期不到40us,既然“大家”们说得那么有把握,我很想开开眼界,相信有很多人也期待着。
通过我提供的测试图片一看就知道单片机的时间都到哪去了,也就知道优化该从何处下手了,我之所以说我的程序还能进一步优化,指的是读取芯片的频次可以进一步减少而不会明显影响显示精度,按目前图片的时间约50毫秒的频次显然有很多次去读取是不必要的,因此是可以继续优化的,至于多少次或说多少时间去读取一次合理,就值得探讨了,各位高手不妨畅言高论。
之前代码是秒分时频繁都去读取,其实没必要,因此修改后的代码就是按条件限制的读取秒分时天月年信息。在不开定时器情况下也可以比较精准控制只读一次秒信息精度控制在毫秒以下,因为上述说过了的6毫秒和40毫秒周期,因此可以比较精准控制去读秒信息一次足够,而不需要无谓的多次读取,同样道理读天月年也可以按条件读取,只是不开定时器情况下按我的思路是实现所有功能代码后再进行比较精准的控制读秒信息,因为不同功能的程序周期也不同,就比如6毫秒和40毫秒周期一般道理。

void main()
{
	unsigned char hm=0,smys=0,w=0,ys=0,m=0;
	Mbcd=Du_1302(0x81);Fbcd=Du_1302(0x83);Sbcd=Du_1302(0x85);
	while(1)
	{
		if(++smys>147)
		{
			XianShi(w);
			if(++w>7)
			{
				w=0;//之下调整秒读时间
				if(++m>4){Mbcd=Du_1302(0x81);
				if(Mbcd==0){Fbcd=Du_1302(0x83);
				if(Fbcd==0)Sbcd=Du_1302(0x85);}m=0;}
			}
			smys=0;
		}
		if(++ys>10){if(bek&&(Fbcd==0&&Mbcd<9||Fbcd==30&&Mbcd<5))beep=~beep;else beep=0;ys=0;}
		if(hm!=Mbcd%10){hm=Mbcd%10;bek=!bek;}
	}
}

我家一台旭日红外热水器无线控制器每周都快1小时多,比如时间是6点看它屏幕走时已经是7点多了,每次都要去调整时间,否则计算峰谷的时间就不准确了!非常麻烦,是很差劲的产品质量,可以断定是很差劲的程序代码,所以莫要小看简单的程序提高效率问题,所谓见微知著,简单的功能都不能注重效率的话,可知复杂功能是很有问题的。经过几次试验目前把读秒信号压缩到每秒4次左右基本察觉不到太大差别可以接受的程度,调整后蜂鸣器音调频率也发生变化能听出区别来。

void XianShi(unsigned char w,unsigned char x)
{
	switch(w)
	{
		case 0:ShuMaXianShi((x?Nbcd:Sbcd)/10,1,10);break;
		case 1:ShuMaXianShi((x?Nbcd:Sbcd)%10,2,10);break;
		case 2:ShuMaXianShi(12,3,10);break;
		case 3:ShuMaXianShi((x?Ybcd:Fbcd)/10,4,10);break;
		case 4:ShuMaXianShi((x?Ybcd:Fbcd)%10,5,10);break;
		case 5:ShuMaXianShi(12,6,10);break;
		case 6:ShuMaXianShi((x?Tbcd:Mbcd)/10,7,10);break;
		case 7:ShuMaXianShi((x?Tbcd:Mbcd)%10,8,10);break;
		default:break;
	}
}
void main()
{
	unsigned char hm=0,smys=0,w=0,m=0,dm=0;bit x=0;
	Mbcd=Du_1302(0x81);Fbcd=Du_1302(0x83);Sbcd=Du_1302(0x85);
	Nbcd=Du_1302(0x8D);Ybcd=Du_1302(0x89);Tbcd=Du_1302(0x87);
	while(1)
	{
		if(++smys>174)//数码管延时
		{
			XianShi(w,x);
			if(++w>7)//数码管位循环+读时钟延时
			{
				w=0;
				if(++dm>4){Mbcd=Du_1302(0x81);//第三级读秒延时
				if(Mbcd==0){Fbcd=Du_1302(0x83);
				if(Fbcd==0){Sbcd=Du_1302(0x85);
				if(Sbcd==0){Tbcd=Du_1302(0x87);
				if(Tbcd==1){Ybcd=Du_1302(0x89);
				if(Ybcd==1)Nbcd=Du_1302(0x8D);}}}}
				dm=0;}
			}
			smys=0;
		}
		if(++m>9)//蜂鸣器频率控制
		{
			if(Fbcd==0&&Mbcd<9||Fbcd==30&&Mbcd<5||(Fbcd==15||Fbcd==45)&&Mbcd<2)
			{x=1;if(bek)beep=~beep;} else x=beep=0;
			m=0;//每15分钟1响30分钟2响整点4响并报时期间显示日期
		}
		if(hm!=Mbcd%10){hm=Mbcd%10;bek=!bek;}//状态秒反转
	}
}
再优化
void main()
{
	unsigned char hm=0,smys=0,w=0,m=0,dm=0;bit x=0;
	Mbcd=Du_1302(0x81);Fbcd=Du_1302(0x83);Sbcd=Du_1302(0x85);
	Nbcd=Du_1302(0x8D);Ybcd=Du_1302(0x89);Tbcd=Du_1302(0x87);
	while(1)
	{
		if(++smys>174)//数码管延时
		{
			XianShi(w,x);
			if(++w>7)//数码管位循环+读时钟延时
			{
				w=0;
				if(++dm>7){Mbcd=Du_1302(0x81);//第三级读秒延时
				if(Mbcd==0){Fbcd=Du_1302(0x83);
				if(Fbcd==0){Sbcd=Du_1302(0x85);
				if(Sbcd==0){Tbcd=Du_1302(0x87);
				if(Tbcd==1){Ybcd=Du_1302(0x89);
				if(Ybcd==1)Nbcd=Du_1302(0x8D);}}}}
				if(hm!=Mbcd%10){hm=Mbcd%10;bek=!bek;}//状态秒反转
				dm=0;}
			}
			smys=0;
		}
		if(++m>14)//蜂鸣器频率控制
		{
			if(Fbcd==0&&Mbcd<9||Fbcd==30&&Mbcd<5||(Fbcd==15||Fbcd==45)&&Mbcd<2)
			{x=1;if(bek)beep=~beep;} else x=beep=0;
			m=0;//每15分钟1响30分钟2响整点4响并报时期间显示日期
		}
	}
}

那就抛砖引玉看看我主函数实现的询问式延时和安排的功能,蜂鸣器频率还在可控范围;如果程序复杂到无法再控制蜂鸣器频率满足要求的情况时才考虑蜂鸣器用其他办法产生频率或使用有源蜂鸣器;整体代码是不开定时器和使用独占计数延时的,除了发送命令和读取的8+8独占循环。还可以再优化到颠覆常规思维,依据的就是仿真观察取反周期来决定和调节;从优化后的代码对比可以看到延时值也发生了变化可调范围增大。

void main()
{
	unsigned char hm=0,smys=0,w=0,m=0,dm=0;bit x=0;
	Mbcd=Du_1302(0x81);Fbcd=Du_1302(0x83);Sbcd=Du_1302(0x85);
	Nbcd=Du_1302(0x8D);Ybcd=Du_1302(0x89);Tbcd=Du_1302(0x87);
	while(1)
	{
		if(++m>25)//蜂鸣器频率控制
		{
			if(Fbcd==0&&Mbcd<8||Fbcd==30&&Mbcd<4||(Fbcd==15||Fbcd==45)&&Mbcd<2)
			{x=1;if(bek)beep=~beep;} else x=beep=0;
			m=0;//每15分钟1响30分钟2响整点4响并报时期间显示日期
			if(++smys>12)//数码管延时
			{
				XianShi(w,x);
				if(++w>7)//数码管位循环+读时钟延时
				{
					w=0;
					if(++dm>7){Mbcd=Du_1302(0x81);//第三级读秒延时
					if(Mbcd==0){Fbcd=Du_1302(0x83);
					if(Fbcd==0){Sbcd=Du_1302(0x85);
					if(Sbcd==0){Tbcd=Du_1302(0x87);
					if(Tbcd==1){Ybcd=Du_1302(0x89);
					if(Ybcd==1)Nbcd=Du_1302(0x8D);}}}}
					if(hm!=Mbcd%10){hm=Mbcd%10;bek=!bek;}//状态秒反转
					dm=0;}
				}
				smys=0;
			}
		}
	}
}

从优化后的代码可以看到实现了4级询问式延时,延时值也发生了变化可调范围继续增大,4级延时内各级都有可调范围,可安排其他功能;按键可以安排在延时内也可以安排在延时外。

void main()
{
	unsigned char hm=0,smys=0,w=0,m=0,dm=0;bit x=0;
	Mbcd=Du_1302(0x81);Fbcd=Du_1302(0x83);Sbcd=Du_1302(0x85);
	Nbcd=Du_1302(0x8D);Ybcd=Du_1302(0x89);Tbcd=Du_1302(0x87);
	while(1)
	{
		if(++m>47)//蜂鸣器频率控制
		{
			if(x&&bek)beep=~beep;
			m=0;//每15分钟1响30分钟2响整点4响并报时期间显示日期
			if(++smys>12)//数码管延时
			{
				XianShi(w,x);smys=0;
				if(++w>7)//数码管位循环+读时钟延时
				{
					w=0;
					if(++dm>4){Mbcd=Du_1302(0x81);//第四级读秒延时
					if(Mbcd==0){Fbcd=Du_1302(0x83);
					if(Fbcd==0){Sbcd=Du_1302(0x85);
					if(Sbcd==0){Tbcd=Du_1302(0x87);
					if(Tbcd==1){Ybcd=Du_1302(0x89);
					if(Ybcd==1)Nbcd=Du_1302(0x8D);}}}}
					if(hm!=Mbcd%10){hm=Mbcd%10;bek=!bek;}//状态秒反转
					if(Fbcd==0&&Mbcd<8||Fbcd==30&&Mbcd<4||(Fbcd==15||Fbcd==45)&&Mbcd<2)
						x=1; else x=beep=0;
					dm=0;}
				}}}}
}

最后优化到十几微妙周期,经过以上数次优化,可以看出单片机时间都跑哪去了,以及如何下手优化。

#include "reg52.h"
sbit RST=P2^5;//DS1302允许(读/写)当RST为高电平时,所有的数据传送被初始化,允许对DS1302进行操作。如果在传送过程中RST置为低电平,则会终止此次数据传送,I/O引脚变为高阻态。RST必须保持低电平。只有在SCLK为低电平时,才能将RST置为高电平。
sbit SCLK=P2^6;//DS1302的SCLK为时钟输入端。在控制指令字输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从低位即位0开始。同样,在紧跟8位的控制指令字后的下一个SCLK脉冲的下降沿读出DS1302的数据,读出数据时从低位0位到高位7。在控制指令字输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从低位即位0开始。同样,在紧跟8位的控制指令字后的下一个SCLK脉冲的下降沿读出DS1302的数据,读出数据时从低位0位到高位7。
sbit IO=P2^7;//DS1302数据传递(读/写)秒寄存器80H写81H读/分寄存器82H写83H读/时寄存器84H写85H读/天寄存器86H写87H读/月寄存器88H写89H读/周寄存器8AH写8BH读/年寄存器8CH写8DH读,存放的数据位为BCD码形式,8EH+0x00/0x80取消/恢复写保护。
sbit L1=P2^2;
sbit L2=P2^3;
sbit L3=P2^4;
sbit beep=P2^1;
sbit k1=P3^0;
bit smk=0,bek=0;
unsigned char code ShuMaGuan[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00,0X80,64};//0~9隐.小数点-号
unsigned char Nbcd=0,Ybcd=0,Tbcd=0,Sbcd=0,Fbcd=0,Mbcd=0,Zbcd=0,SJ=0;//年 月 天 时 分 秒 周 数据
void ShuMaXianShi(unsigned char s,unsigned char w,unsigned char d)
{
	P0=255;
	switch(w)
	{
		case 2:{L1=0;L2=L3=1;}break;//011
		case 3:{L2=0;L1=L3=1;}break;//101
		case 4:{L1=L2=0;L3=1;}break;//001
		case 5:{L1=L2=1;L3=0;}break;//110
		case 6:{L1=L3=0;L2=1;}break;//010
		case 7:{L1=1;L2=L3=0;}break;//100
		case 8:{L1=L2=L3=0;}break;//000
		case 1:{L1=L2=L3=1;}break;//111
		default:P0=255;break;
	}
	P1=ShuMaGuan[s]|ShuMaGuan[d];//d显示小数点11,10关闭
}
void XianShi(unsigned char w,unsigned char x)
{
	switch(w)
	{
		case 0:ShuMaXianShi((x?Nbcd:Sbcd)/10,1,10);break;
		case 1:ShuMaXianShi((x?Nbcd:Sbcd)%10,2,(x?11:10));break;
		case 2:ShuMaXianShi((x?Ybcd/10:10),3,10);break;
		case 3:ShuMaXianShi((x?Ybcd%10:Fbcd/10),4,(x?11:10));break;
		case 4:ShuMaXianShi((x?Tbcd/10:Fbcd%10),5,10);break;
		case 5:ShuMaXianShi((x?Tbcd%10:10),6,(x?11:10));break;
		case 6:ShuMaXianShi((x?10:Mbcd/10),7,10);break;
		case 7:ShuMaXianShi((x?Zbcd?Zbcd:8:Mbcd%10),8,10);break;
		default:P0=255;break;
	}
}
void XK_1302(unsigned char SD, bit k)
{
	unsigned char cs=8,zh=1;
	if(k){RST=SCLK=0;RST=1;}
	xf:if(cs--)
	{
		IO=SD&0x01;//低位起16进制
		SCLK=1;//上升沿写数据
		SD>>=1;//右移
		SCLK=0;//为下次上升沿准备时钟
		goto xf;
	}//发送命令时不能RST=0表示结束
	if(!k){SCLK=1;RST=0;}
}
unsigned char Du_1302(unsigned char Dz)
{
	unsigned char cs=8,zh=1,sj=0;
	XK_1302(Dz,1);//读地址命令
	SJ=SCLK=0;
	xf:if(cs--)
	{
		SJ+=IO*zh;//位为BCD码形式
		SCLK=1;//为下次下降沿准备时钟
		zh*=2;//直接转化为10进制数据
		//if(cs==4){sj=SJ;SJ=0;zh=1;}
		SCLK=0;//下降沿读数据
		goto xf;
	}
	RST=0;
	return SJ/16*10+SJ%16;//SJ*10+sj;
}
void Xie_1302(unsigned char Dz,unsigned char Sj)
{
	XK_1302(0x8e,1);XK_1302(0x00,0);//取消写保护
	XK_1302(Dz,1);XK_1302(Sj,0);//写入数据
	XK_1302(0x8e,1);XK_1302(0x80,0);
}
void main()
{
	unsigned char hm=0,smys=0,w=0,m=0,dm=0,xd=0;bit x=0;
	Mbcd=Du_1302(0x81);Fbcd=Du_1302(0x83);Sbcd=Du_1302(0x85);
	Nbcd=Du_1302(0x8D);Ybcd=Du_1302(0x89);Tbcd=Du_1302(0x87);Zbcd=Du_1302(0x8B)-1;
	while(1)
	{
		if(++m>47)//蜂鸣器频率控制
		{
			if(x&&bek)beep=~beep;
			m=0;//每15分钟1响30分钟2响整点4响并报时期间显示日期
			if(++smys>10)//数码管延时
			{
				XianShi(w,x);smys=0;if(!k1||!k2)bek=1;
				if(++w>7)//数码管位循环+读时钟延时
				{
					w=0;
					if(++dm>7){Mbcd=Du_1302(0x81);//第四级读秒延时
					if(Mbcd==0){Fbcd=Du_1302(0x83);
					if(Fbcd==0){Sbcd=Du_1302(0x85);
					if(Sbcd==0){Tbcd=Du_1302(0x87);Zbcd=Du_1302(0x8B)-1;
					if(Tbcd==1){Ybcd=Du_1302(0x89);
					if(Ybcd==1)Nbcd=Du_1302(0x8D);}}}}
					if(k1&&k2&&hm!=Mbcd%10){hm=Mbcd%10;bek=!bek;}//状态秒反转
					if(Fbcd==0&&Mbcd<8||Fbcd==30&&Mbcd<4||(Fbcd==15||Fbcd==45)&&Mbcd<2)
						x=1; else x=beep=0;
					if(!k1&&!k2&&bek)Xie_1302(0x80,0);//直接十进制转十六进制写信息每秒分钟步进
					if(k1==0&&bek)
					{
						if(++Fbcd<60)Xie_1302(0x82,Fbcd/10*16+Fbcd%10);
						else {Fbcd=0;
						if(++Sbcd<24)Xie_1302(0x84,Sbcd/10*16+Sbcd%10);else Sbcd=0;}
					bek=!bek;}
					if(k2==0&&bek)
					{if(Fbcd>0)--Fbcd;else {Fbcd=59;
					if(Sbcd>0){--Sbcd;Xie_1302(0x84,Sbcd/10*16+Sbcd%10);}else Sbcd=23;}
						Xie_1302(0x82,Fbcd/10*16+Fbcd%10);
					bek=!bek;}
					dm=0;}
				}}}
	}
}

中断+计数的1毫秒间隔或200微妙能做多少事还真的没什么案例,本案例旨在说明询问式计数延时相比独占计数延时的优越且灵活调节和设置,属于高级分析思维模式,而非入门级别独占计数延时一直套用引发种种疑难。

#include "reg52.h"
sbit RST=P3^5;//DS1302允许(读/写)当RST为高电平时,所有的数据传送被初始化,允许对DS1302进行操作。如果在传送过程中RST置为低电平,则会终止此次数据传送,I/O引脚变为高阻态。RST必须保持低电平。只有在SCLK为低电平时,才能将RST置为高电平。
sbit SCLK=P3^6;//DS1302的SCLK为时钟输入端。在控制指令字输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从低位即位0开始。同样,在紧跟8位的控制指令字后的下一个SCLK脉冲的下降沿读出DS1302的数据,读出数据时从低位0位到高位7。在控制指令字输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从低位即位0开始。同样,在紧跟8位的控制指令字后的下一个SCLK脉冲的下降沿读出DS1302的数据,读出数据时从低位0位到高位7。
sbit IO=P3^7;//DS1302数据传递(读/写)秒寄存器80H写81H读/分寄存器82H写83H读/时寄存器84H写85H读/天寄存器86H写87H读/月寄存器88H写89H读/周寄存器8AH写8BH读/年寄存器8CH写8DH读,存放的数据位为BCD码形式。
sbit L1=P2^2;
sbit L2=P2^3;
sbit L3=P2^4;
sbit beep=P2^5;
sbit k1=P3^0;
sbit k2=P3^1;
bit smk=0,bek=1,x=0;
unsigned char code ShuMaGuan[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00,0X80,64};//0~9隐.小数点-号
unsigned char code ZhouShuMa[]={121,79,64,64,9,9,73,73,63,63,79,77,81,69};//用数码管显示每周的值:一二三四五六日。
unsigned char Nbcd=0,Ybcd=0,Tbcd=0,Sbcd=0,Fbcd=0,Mbcd=0,Zbcd=0,SJ=0;//年 月 天 时 分 秒 周 数据
void ShuMaXianShi(unsigned char s,unsigned char w,unsigned char d)
{
	P0=L1=L2=L3=0;
	switch(w)
	{
		case 2:{L1=0;L2=L3=1;}break;//011
		case 3:{L2=0;L1=L3=1;}break;//101
		case 4:{L1=L2=0;L3=1;}break;//001
		case 5:{L1=L2=1;L3=0;}break;//110
		case 6:{L1=L3=0;L2=1;}break;//010
		case 7:{L1=1;L2=L3=0;}break;//100
		case 8:{L1=L2=L3=0;}break;//000
		case 1:{L1=L2=L3=1;}break;//111
		default:{L1=L2=L3=0;}break;
	}//d显示小数点11,10关闭
	P0=w>6&&x?ZhouShuMa[s]:ShuMaGuan[s]|ShuMaGuan[d];
}
void XianShi(unsigned char w,unsigned char x)
{
	switch(w)
	{
		case 0:ShuMaXianShi((x?Nbcd:Sbcd)/10,1,10);break;
		case 1:ShuMaXianShi((x?Nbcd:Sbcd)%10,2,(x?11:10));break;
		case 2:ShuMaXianShi((x?Ybcd/10:10),3,10);break;
		case 3:ShuMaXianShi((x?Ybcd%10:Fbcd/10),4,(x?11:10));break;
		case 4:ShuMaXianShi((x?Tbcd/10:Fbcd%10),5,10);break;
		case 5:ShuMaXianShi((x?Tbcd%10:10),6,(x?11:10));break;
		case 6:ShuMaXianShi((x?Zbcd:Mbcd/10),7,10);break;
		case 7:ShuMaXianShi((x?Zbcd+1:Mbcd%10),8,10);break;
		default:P0=L1=L2=L3=0;break;
	}
}
void XK_1302(unsigned char SD, bit k)
{
	unsigned char cs=8,zh=1;
	if(k){RST=SCLK=0;RST=1;}
	xf:if(cs--)
	{
		IO=SD&0x01;//低位起16进制
		SCLK=1;//上升沿写数据
		SD>>=1;//右移
		SCLK=0;//为下次上升沿准备时钟
		goto xf;
	}//发送命令时不能RST=0表示结束
	if(!k){SCLK=1;RST=0;}
}
unsigned char Du_1302(unsigned char Dz)
{
	unsigned char cs=8,zh=1,sj=0;
	XK_1302(Dz,1);//读地址命令
	SJ=SCLK=0;
	xf:if(cs--)
	{
		SJ+=IO*zh;//位为BCD码形式
		SCLK=1;//为下次下降沿准备时钟
		zh*=2;//直接转化为10进制数据
//		if(cs==4){sj=SJ;SJ=0;zh=1;}
		SCLK=0;//下降沿读数据
		goto xf;
	}
	RST=0;//十六进制转十进制
	return SJ/16*10+SJ%16;//SJ*10+sj;
}
void Xie_1302(unsigned char Dz,unsigned char Sj)
{
	XK_1302(0x8e,1);XK_1302(0x00,0);//取消写保护
	XK_1302(Dz,1);XK_1302(Sj,0);//写入数据
	XK_1302(0x8e,1);XK_1302(0x80,0);
}
void main()
{
	unsigned char hm=0,smys=0,w=0,m=0,dm=0,xd=0;
	Mbcd=Du_1302(0x81);Fbcd=Du_1302(0x83);Sbcd=Du_1302(0x85);
	Nbcd=Du_1302(0x8D);Ybcd=Du_1302(0x89);Tbcd=Du_1302(0x87);Zbcd=(Du_1302(0x8B)-1)*2;
	while(1)
	{
		if(++m>47)//蜂鸣器频率控制
		{
			if(x&&bek)beep=~beep;
			m=0;//每15分钟1响30分钟2响整点4响并报时期间显示日期
			if(++smys>10)//数码管延时
			{
				XianShi(w,x);smys=0;
				if(++w>7)//数码管位循环+读时钟延时
				{
					w=0;
					if(++dm>7){Mbcd=Du_1302(0x81);//第四级读秒延时
					if(Mbcd==0){Fbcd=Du_1302(0x83);
					if(Fbcd==0){Sbcd=Du_1302(0x85);
					if(Sbcd==0){Tbcd=Du_1302(0x87);Zbcd=(Du_1302(0x8B)-1)*2;
					if(Tbcd==1){Ybcd=Du_1302(0x89);
					if(Ybcd==1)Nbcd=Du_1302(0x8D);}}}}
					if(k1&&k2&&hm!=Mbcd%10){hm=Mbcd%10;bek=!bek;}//状态秒反转
					if(Fbcd==0&&Mbcd<8||Fbcd==30&&Mbcd<4||(Fbcd==15||Fbcd==45)&&Mbcd<2)
						x=1; else x=beep=0;
					if(!k1&&!k2)Xie_1302(0x80,0);//复合按键
					if(k1==0&&bek)
					{//直接十进制转十六进制写信息每秒分钟步进
						if(++Fbcd<60)Xie_1302(0x82,Fbcd/10*16+Fbcd%10);
						else {Fbcd=0;
						if(++Sbcd<24)Xie_1302(0x84,Sbcd/10*16+Sbcd%10);else Sbcd=0;}
					bek=0;}
					if(k2==0&&bek)
					{if(Fbcd>0)--Fbcd;else {Fbcd=59;
					if(Sbcd>0){--Sbcd;Xie_1302(0x84,Sbcd/10*16+Sbcd%10);}else Sbcd=23;}
						Xie_1302(0x82,Fbcd/10*16+Fbcd%10);
					bek=0;}
					dm=0;}if(!k1||!k2)bek=1;//调节快进速度控制
				}}}
	}
}

 第一电路图对应代码,按键代码也可以安排在其他延时层,修改dek的变化速度,实现比秒更快的调值。
不断优化就是对逻辑思维的验证,从而实现卖油翁的过程,只有了然于胸才能驾轻就熟;其实不然我认为频繁读取对精度有所影响,就好比开定时器中断应该说没什么影响,然而实践告诉人们,轻便的程序对定时器计时精度没多大影响,而复杂的程序对定时器计时精度就有影响,中断也要获得响应时间的,电路也是一样的总要拨点时间去处理其他事件;如果能够把程序打造得轻便即可用定时器中断实现比较精准的计时,抛却时钟芯片降低成本目标,毕竟兆赫兹的精度比千赫兹提高了一个数量级别,而且可以使用无源蜂鸣器,不用被逼选择有源蜂鸣器,有源蜂鸣器和无源蜂鸣器是有差价的,也充分体现了程序员的价值和水平。

#include "reg52.h"
sbit RST=P3^5;//DS1302允许(读/写)当RST为高电平时,所有的数据传送被初始化,允许对DS1302进行操作。如果在传送过程中RST置为低电平,则会终止此次数据传送,I/O引脚变为高阻态。RST必须保持低电平。只有在SCLK为低电平时,才能将RST置为高电平。
sbit SCLK=P3^6;//DS1302的SCLK为时钟输入端。在控制指令字输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从低位即位0开始。同样,在紧跟8位的控制指令字后的下一个SCLK脉冲的下降沿读出DS1302的数据,读出数据时从低位0位到高位7。在控制指令字输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从低位即位0开始。同样,在紧跟8位的控制指令字后的下一个SCLK脉冲的下降沿读出DS1302的数据,读出数据时从低位0位到高位7。
sbit IO=P3^7;//DS1302数据传递(读/写)秒寄存器80H写81H读/分寄存器82H写83H读/时寄存器84H写85H读/天寄存器86H写87H读/月寄存器88H写89H读/周寄存器8AH写8BH读/年寄存器8CH写8DH读,存放的数据位为BCD码形式。
sbit L1=P2^2;
sbit L2=P2^3;
sbit L3=P2^4;
sbit beep=P2^5;
sbit k1=P3^0;
sbit k2=P3^1;
bit smk=0,bek=1,x=0;
unsigned char code ShuMaGuan[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00,0X80,64};//0~9隐.小数点-号
unsigned char code ZhouShuMa[]={121,79,64,64,9,9,73,73,63,63,79,77,81,69};//用数码管显示每周的值:一二三四五六日。
unsigned char Nbcd=0,Ybcd=0,Tbcd=0,Sbcd=0,Fbcd=0,Mbcd=0,Zbcd=0,SJ=0,Js;//年 月 天 时 分 秒 周 数据
void ShuMaXianShi(unsigned char s,unsigned char w,unsigned char d)
{
	P0=L1=L2=L3=0;
	switch(w)
	{
		case 2:{L1=0;L2=L3=1;}break;//011
		case 3:{L2=0;L1=L3=1;}break;//101
		case 4:{L1=L2=0;L3=1;}break;//001
		case 5:{L1=L2=1;L3=0;}break;//110
		case 6:{L1=L3=0;L2=1;}break;//010
		case 7:{L1=1;L2=L3=0;}break;//100
		case 8:{L1=L2=L3=0;}break;//000
		case 1:{L1=L2=L3=1;}break;//111
		default:{L1=L2=L3=0;}break;
	}//d显示小数点11,10关闭
	P0=w>6&&x?ZhouShuMa[s]:ShuMaGuan[s]|ShuMaGuan[d];
}
void XianShi(unsigned char w,unsigned char x)
{
	switch(w)
	{
		case 0:ShuMaXianShi((x?Nbcd:Sbcd)/10,1,10);break;
		case 1:ShuMaXianShi((x?Nbcd:Sbcd)%10,2,(x?11:10));break;
		case 2:ShuMaXianShi((x?Ybcd/10:10),3,10);break;
		case 3:ShuMaXianShi((x?Ybcd%10:Fbcd/10),4,(x?11:10));break;
		case 4:ShuMaXianShi((x?Tbcd/10:Fbcd%10),5,10);break;
		case 5:ShuMaXianShi((x?Tbcd%10:10),6,(x?11:10));break;
		case 6:ShuMaXianShi((x?Zbcd:Mbcd/10),7,10);break;
		case 7:ShuMaXianShi((x?Zbcd+1:Mbcd%10),8,10);break;
		default:P0=L1=L2=L3=0;break;
	}
}
void XK_1302(unsigned char SD, bit k)
{
	unsigned char cs=8,zh=1;
	if(k){RST=SCLK=0;RST=1;}
	xf:if(cs--)
	{
		IO=SD&0x01;//低位起16进制
		SCLK=1;//上升沿写数据
		SD>>=1;//右移
		SCLK=0;//为下次上升沿准备时钟
		goto xf;
	}//发送命令时不能RST=0表示结束
	if(!k){SCLK=1;RST=0;}
}
unsigned char Du_1302(unsigned char Dz)
{
	unsigned char cs=8,zh=1,sj=0;
	XK_1302(Dz,1);//读地址命令
	SJ=SCLK=0;
	xf:if(cs--)
	{
		SJ+=IO*zh;//位为BCD码形式
		SCLK=1;//为下次下降沿准备时钟
		zh*=2;//直接转化为10进制数据
//		if(cs==4){sj=SJ;SJ=0;zh=1;}
		SCLK=0;//下降沿读数据
		goto xf;
	}
	RST=0;//十六进制转十进制
	return SJ/16*10+SJ%16;//SJ*10+sj;
}
void Xie_1302(unsigned char Dz,unsigned char Sj)
{
	XK_1302(0x8e,1);XK_1302(0x00,0);//取消写保护
	XK_1302(Dz,1);XK_1302(Sj,0);//写入数据
	XK_1302(0x8e,1);XK_1302(0x80,0);
}
char NYT(unsigned char n,unsigned char y)
{
	if (y == 2){ if ((!(2000+n % 4) && 2000+n % 100) || !(2000+n % 400)) return 29; else return 28; }
	else if ((y <= 7 && y % 2) || (y > 7 && !(y % 2)))return 31; else return 30;
}
void JiShi(/*时间计算*/)
{
	if(Js==100)
	{
		Js=0;
		if(++Mbcd==60){Mbcd=0;++Fbcd;
		if(Fbcd==60){Fbcd=0;++Sbcd;
		if(Sbcd==24){Sbcd=0;++Tbcd;++Zbcd;
		if(Zbcd>6)Zbcd=0;if(Tbcd>NYT(Nbcd,Ybcd)){Tbcd=1;++Ybcd;
		if(Ybcd>12){Ybcd=1;++Nbcd;}}}}}
		bek=!bek;
	}
}
void ZhongDuanSheZhi()
{
	TH0+=(65536-9216)/256;/*定时器赋初值,定时10ms触发中断,自动补偿方式*/
	TL0+=(65536-9216)%256; /*11.0592MHz/12=921600Hz就是1秒921600次机器周期*/
	TMOD=0X01;
	TR0=1; 
	ET0=1; //开启定时器
	EA=1; //全局中断开关
}
void ZhongDuan() interrupt 1
{
	++Js;//快+慢-
	TH0+=(65536-(9216))/256;/*定时器赋初值,定时10ms触发中断,自动补偿方式*/
	TL0+=(65536-(9216))%256; /*11.0592MHz/12=921600Hz就是1秒921600次机器周期*/       
}
void main()
{
	unsigned char hm=0,smys=0,w=0,m=0,dm=0,xd=0;
	Mbcd=Du_1302(0x81);Fbcd=Du_1302(0x83);Sbcd=Du_1302(0x85);
	Nbcd=Du_1302(0x8D);Ybcd=Du_1302(0x89);Tbcd=Du_1302(0x87);Zbcd=(Du_1302(0x8B)-1)*2;
	ZhongDuanSheZhi();
	while(1)
	{
		if(++m>47)//蜂鸣器约1千赫兹频率控制
		{
			if(x&&bek)beep=~beep;
			m=0;//每15分钟1响30分钟2响整点4响并报时期间显示日期
			if(++smys>10)//数码管延时
			{
				JiShi();XianShi(w,x);smys=0;
				if(++w>7)//数码管位循环+读时钟延时
				{
					w=0;
					if(Fbcd==0&&Mbcd<8||Fbcd==30&&Mbcd<4||(Fbcd==15||Fbcd==45)&&Mbcd<2)
						x=1; else x=beep=0;
					if(k1==0&&bek)
					{//直接十进制转十六进制写信息每秒分钟步进
						if(++Fbcd<60)Xie_1302(0x82,Fbcd/10*16+Fbcd%10);
						else {Fbcd=0;
						if(++Sbcd<24)Xie_1302(0x84,Sbcd/10*16+Sbcd%10);else Sbcd=0;}
					bek=0;}
					if(k2==0&&bek)
					{if(Fbcd>0)--Fbcd;else {Fbcd=59;
					if(Sbcd>0){--Sbcd;Xie_1302(0x84,Sbcd/10*16+Sbcd%10);}else Sbcd=23;}
						Xie_1302(0x82,Fbcd/10*16+Fbcd%10);
					bek=0;}
				}}}
	}
}

已经全部描述示例完毕,能否学到手,看各人自己了。之下记录了定时器10毫秒计时运行9个半小时后依然同系统保持一致,在WIN7系统7.8SP2仿真情况下。

void ShuMaXianShi(unsigned char s,unsigned char w,unsigned char d)
{
	P0=L1=L2=L3=0;
//	switch(w)
//	{
//		case 2:{L1=0;L2=L3=1;}break;//011=24
//		case 3:{L2=0;L1=L3=1;}break;//101=20
//		case 4:{L1=L2=0;L3=1;}break;//001=16
//		case 5:{L1=L2=1;L3=0;}break;//110=12
//		case 6:{L1=L3=0;L2=1;}break;//010=8
//		case 7:{L1=1;L2=L3=0;}break;//100=4
//		case 8:{L1=L2=L3=0;}break;//000=0
//		case 1:{L1=L2=L3=1;}break;//111=28
//		default:{L1=L2=L3=0;}break;
//	}//d显示小数点11,10关闭
	P2=w;//虽然可以操作整组IO实现但是运算时间会增加
	P0=w>6&&x?ZhouShuMa[s]:ShuMaGuan[s]|ShuMaGuan[d];
}

  • 39
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值