⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 time1380.c

📁 含20个经典例程
💻 C
字号:
//time1380.c
//HT1380操作时序,以及时间设置和LCD时间显示(显示小时和分钟)
#include "lcd.h"//参见LCD章节子程序
sbit rst80=P0^2;//HT1380 REST 管脚
sbit VSCL=P0^0;//HT1380 时钟管脚
sbit VSDA=P0^1;//HT1380 时钟数据线
bdata uchar bitdata;//巧用bdata 空间的位操作性能
sbit bd7=bitdata^7;
sbit bd0=bitdata^0;
uchar send;
void sw80(uchar cmd);//对HT1380串行写操作函数
struct timef{
	uchar hour;
	uchar min;
}time1;//时间数据结构
xdata uchar timestr[6];
void showtime();
void initime(){//HT1380初始化操作
	rst80=0;
	VSCL=0;//对HT1380操作无效之前,确保时钟线为低电平
	_nop_();
	_nop_();
	rst80=1;
	sw80(0x8e);//选中Write Protect寄存器写操作
	sw80(0x00);//将WP清零,使得寄存器写操作使能
	rst80=0;//此次操作结束,该语句不能省略
	_nop_();
	_nop_();
	rst80=1;
	sw80(0x80);//选中second寄存器写操作
	sw80(0x00);//将CH清零,使时钟运行
	rst80=0;//此次操作结束
	_nop_();
	
}
void sw80(uchar cmd){//对HT1380串行写操作函数
	uchar i;
	bitdata=cmd;
	VSCL=0;//先发送最低位,写操作是在时钟上升沿有效
	//所以在时钟线为0的前提下,先写数据,再将时钟线置1,构造一个上升沿
	//随后再将时钟线置低电平
	for(i=0;i<8;i++){
		if(bd0){
			VSDA=1;
			_nop_();
			VSCL=1;
			_nop_();
			VSCL=0;
			}
		else{
			VSDA=0;
			_nop_();
			VSCL=1;
			_nop_();
			VSCL=0;
			}
		bitdata>>=1;//巧用移位操作和BDATA区能位寻址,使得程序效率大大提高
	}
	VSDA=1;//单片机口对读数据须先置1后读取,此操作对下一次读数据打基础
	//此代码不能省略,否则当VSDA=0,则读数据时可能将高电平数据读成0,因为
	//数据线上的高电平会被单片机口下拉为0
	
}
uchar sr80(void){//对HT1380读操作
	uchar i;
	bd7=VSDA;//写Command寄存器后,由SW80()函数可知,写完数据后,时钟线由高电平
	//变为低电平,产生一个下降沿,所以此时HT1380已经将第一位数据输出
	//笔者曾花了3天时间才查出这个操作BUG,所以对有上述SW80()写操作时序习惯的
	//读者而言,可能是个很好的提示,希望能对其他类似的串行操作调试提供经验帮助
	bitdata>>=1;
	for(i=0;i<6;i++){//接收时是先接收到最低位,所以应先把最低位存在最高位,经过
	//右移移位操作,当全部数据接收到时,能保证最低位出在存储空间的最低位
	
		VSCL=1;
		_nop_();
		_nop_();
		VSCL=0;
		bd7=VSDA;
		bitdata>>=1;
		
	}
	VSCL=1;
	_nop_();
	_nop_();
	VSCL=0;
	bd7=VSDA;
	VSCL=0;	
	return bitdata;
}
void settime(){//由于篇幅问题,具体由读者去完善
	
}
void setmin(uchar min){//分钟寄存器设置,min为BCD码
	rst80=0;
	_nop_();
	rst80=1;
	sw80(0x82);//选中minutes寄存器,写操作
	sw80(min);
	rst80=0;
	_nop_();
}
void sethour(uchar hour){//小时寄存器设置
	rst80=0;
	_nop_();
	rst80=1;
	sw80(0x84);//选中hours寄存器,写操作
	sw80(hour);
	rst80=0;
	_nop_();
}
void readtime(){
	rst80=0;
	_nop_();
	VSCL=0;
	_nop_();
	rst80=1;
	sw80(0x83);//选中minutes寄存器,读操作
	time1.min=sr80();//获取当前时间分钟值
	rst80=0;
	_nop_();
	rst80=0;
	_nop_();
	VSCL=0;
	_nop_();
	rst80=1;
	sw80(0x85);//选中hours寄存器,读操作
	time1.hour=sr80();//获取当前时间小时值
	rst80=0;
	_nop_();
	rst80=0;
	_nop_();
	VSCL=0;
	_nop_();
	rst80=1;
	sw80(0x81);//选中senconds寄存器,读操作
	send=sr80();//读取当前秒钟值
	rst80=0;
	_nop_();

	
}
void showtime(){
	//显示效果,比如23点14分//
	//若秒值为奇数,显示如下:
	//  23 14//
	//若秒值为偶数,显示如下:
	//  23:14//
	//从而,随着秒值的改变,显示第三位有闪动效果
		uchar tt;
		tt=time1.hour&0x0f;//得到小时数据的个位数值
		timestr[1]=tt+'0';//将其转化成ASC码字符,且小时个位数显示在第二位
		tt=time1.hour&0xf0;//由于是24小时制,小时十位数也可直接获取
		if(tt!=0){//小时十位数不为0
			tt/=16;//由于处于高4为,采用除以16,将数据移到低4位
			timestr[0]=tt+'0';//小时十位数显示在第一位
		}
		else{//小时十位数为0时的处理
			if(timestr[1]=='0')//习惯上零点时显示两个0
				timestr[0]='0';
			else
				timestr[0]=' ';
		}
		if(send&0x01)//第三位与秒值的显示关系
			timestr[2]=' ';
		else
			timestr[2]=':';
		tt=time1.min&0xf0;
		tt/=16;
		timestr[3]=tt+'0';//第四位显示分钟的十位数
		tt=time1.min&0x0f;
		timestr[4]=tt+'0';//第五位显示分钟的个位数
		timestr[5]='\0';
		showline(timestr,0);
}


void disptime(){//由有兴趣的读者去完善
}
void formatedata(){//由有兴趣的读者去完善
}
void config (void) {
//看门狗禁止
    WDTCN = 0x07;	
    WDTCN = 0xDE;   
    WDTCN = 0xAD;
    SFRPAGE = 0x0F;
    XBR0 = 0x00;	
    XBR1 = 0x00;	
    XBR2 = 0x40;	//交叉开关使能,使得P0-P3口能输出
    XBR3 = 0x00;    
    SFRPAGE = 0x0F;
    P0MDOUT = 0x00; //端口配置,P0-P3,P6-P7口为开漏输出
    P1MDOUT = 0x00; 
    P2MDOUT = 0x00; 
    P3MDOUT = 0x00; 
    P4MDOUT = 0x00; //P4口为开漏
    P5MDOUT = 0x07; 
    P6MDOUT = 0x00; 
    P7MDOUT = 0x00; 
    P1MDIN = 0xFF;  //所有端口为数字输入,没有模拟输入端口
    P2MDIN = 0xFF;  
    P3MDIN = 0xFF; 
    SFRPAGE = 0x0F;
    CLKSEL = 0x00;  
    OSCXCN = 0x00;	
    OSCICN = 0x84;	
    //采用内部晶振,为24.5MHZ8分频
}   
void main(){
	uint knum;
	uchar mnum;
	uint hnum;
	uchar tk;
	config();//初始化MCU
	delay1ms(50);
	dispini();//初始化LCD
	EA=0;
	initime();//初始化HT1380
	delay1ms(1000);
	knum=0;
	hnum=0;
	for(;;){
		readtime();
		showtime();
		if(scankey()==2){//长按键延时的一种处理方法
		//在时间设定时,希望长按键才生效,以免误操作
		//此为小时设置按键值
			knum++;
		}
		if(scankey()!=2){
			knum=0;
		}
		if(knum>30){//若knum>30,则认为长按键有效
			for(;;){
				if(scankey()==2){
					knum=300;
					time1.hour++;
					tk=time1.hour&0x0f;
					if(tk>9){//对小时值个位从9变为10时的进位处理
						tk=time1.hour&0xf0;
						tk+=0x10;
						time1.hour=tk;//此时个位值为0
						}
					tk=time1.hour&0x0f;
					if(tk>3){//因为是24小时制,所以在23点时,个位增1就是0点
						tk=time1.hour&0xf0;
						if(tk>0x10)
							time1.hour=0;//由23点增1变到0点的处理
					}

					showtime();
					
				}
				while(scankey()==2);//若手一直将按键按住,则视为按键等待
				//这种处理方法不是很好,最好做成超时退出,由有兴趣的
				//读者来完善,若是实时操作系统,这个问题很好解决
				//否则,处理起来考虑的问题还是很多
					
				if(scankey()!=2){//对设置时钟的间隔处理,若按键有一段时间没有
				//按下,则knum值自减
					delay1ms(10);
					knum--;
					}
				if(knum<3){//当knum小于3,则将小时设置写入HT1380,退出小时设置
					sethour(time1.hour);
					break;
					}
				
			}
		}
		if(scankey()==3){//分钟设置处理方法与小时雷同
			hnum++;
		}
		if(scankey()!=3){
			hnum=0;
		}
		if(hnum>30){
			for(;;){
				if(scankey()==3){
					hnum=300;
					time1.min++;
					tk=time1.min&0x0f;
					if(tk>9){
						tk=time1.min&0xf0;
						tk+=0x10;
						if(tk>0x50)
							tk=0;
						time1.min=tk;
						}
					showtime();
					
				}
				while(scankey()==3);
				if(scankey()!=3){
					delay1ms(10);
					hnum--;
					}
				if(hnum<3){
					setmin(time1.min);
					break;
					}
				
			}
		}
	}
	
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -