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

📄 clock.c

📁 门禁控制器源代码
💻 C
字号:
//
// I2C操作函数库
// I2C有关概念参见《MCS-51系列单片机应用及接口技术》P289
//
// idata=14

#include "Global.h"

///////////////////////////////////////////////////////////////////////////////
// 内部存储区变量	0~128字节 直接寻址
// 全局变量定义
extern data  uchar cur_time[15];		// 系统当前时间显示存储区 XXXX年XX月XX日XX时XX分XX秒星期X
extern data  uchar time[10];			// 读取时钟存储区 					
extern bdata bit ack;					// 时钟I2C总线通信应答状态

//
// 在I2C总线数据传送过程中,定义了一种开始和结束信号(有时也称启动和停止信号),
// 开始和结束信号的定义在I2C协议中具有十分重要的意义。当SCL为高电平时,SDA发生
// 高到低跳变定义为开始信号;当SCL为高电平时,SDA发生低到高跳变定义为结束信号,
// 开始和结束信号的定义参见《MCS-51系列单片机应用及接口技术》P291所示。开始和
// 结束信号都是由主器件(这里是8051单片机)发出的。在开始信号以后,总线被认为
// 是忙的。在结束信号后过一定时间,总线被认为是空闲的。如果连接在总线上的器件
// 具有相应的硬件接口电路,开始和结束信号的检测还比较容易,但是没有这种硬件接
// 口电路的微处理器必须在一个时钟周期内至少2次采样,SDA才能检测到这种跳变。
//


//
// 启动I2C总线
// 函数原型:void Start_I2c8563();
// 功能:
//		启动I2C时钟8563总线,即发送I2C起始条件
//
void Start_I2c8563()
{
	SDA=1;			// SDA维持高电平,准备发送起始信号
	SCL=1;			// SCL为高电平,SDA由高变低,表明通信起始信号
	Some_NOP;		// SCL信号高电平维持至少4us,等待电平稳定
	SDA=0;			// 发送起始信号,SDA下跳
	Some_NOP;		// 起始信号至少维持4.7us
	SCL=0;			// 时钟信号跳变
	Some_NOP;		// 等待一段时间
}

//
// 结束I2C总线
// 函数原型: void  Stop_I2c8563();  
// 功能: 
//		结束I2C总线,即发送I2C结束条件.
//
void Stop_I2c8563()
{
	SDA=0;			// SDA维持低电平,准备发送停止信号
	_Nop();
	SCL=1;			// SCL信号高电平,SDA由低变高,表明通信停止信号
	Some_NOP;		// SCL信号高电平维持至少4us
	SDA=1;			// 发送停止信号
	Some_NOP;		// 停止信号至少维持4us
}


//
// 送到SDA线上的每个字节必须为8位长度,每次传送的字节数是不受限制的,每个字节后
// 面必须跟随一个响应位。数据传送时先传送最高位。如果接收器不能接收下一个字节(
// 例如正在处理一个内部中断,在这个中断处理完之前不能接收I2C总线上的数据字节),
// 可以使时钟保持低电平,迫使主处理器处于等待状态。当从器件准备好接收下一个数据
// 字节时就释放SCL,以便数据继续传送。
//
// 接收器必须确认数据的接收,确认位相对于主器件产生一个时钟在这个时钟内发送器件
// 释放SDA线。接收器件在这个时钟内必须将SDA拉成低电平,使SDA在该时钟的高电平期间
// 为稳定的低电平。
// 通常,被寻址的接收器件必须在收到每个字节后发出响应信息。若一个从器件在处理一
// 个实时事件不能接收数据时,从器件必须使SDA保持高电平。此时,主器件产生一个结
// 束信号使传送异常结束。
// 在主器件接收的传送中,主器件对最后一个数据字节不予确认,以对从发送器指出数据
// 传送的结束,从发送器释放SDA线,使主器件能产生一个结束信号。
//

// 字节数据传送函数,,向从器件写一个字节
// 函数原型: void  SendByte8563(uchar c);
// 功能: 
//		将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对
//		此状态位进行操作.(不应答或非应答都使ack=0 假)     
//		发送数据正常,ack=1; ack=0表示被控器无应答或损坏。
//
void  SendByte8563(uchar c)
{
	idata uchar i;

	for(i=0;i<8;i++)// 每字节必须8位长度
	{
		if((c<<i)&0x80)
		{
			SDA=1;						// 数据最高位先发送,数据1
		}
		else
		{
			SDA=0;						// 数据0
		}
		_Nop();
		SCL=1;							// 每个主器件在SCL线上产生时钟,数据仅在
										// 时钟的高电平期间有效,锁存数据。
		Some_NOP;    					// 保证SCL时钟高电平至少为4us     
		SCL=0;							// 时钟低电平,准备写入下一个字节
		_Nop();							// 等待
		_Nop();
	}
	
	_Nop();
	_Nop();
	SDA=1;								// 8位数据发送完毕,检测从器件响应
	_Nop();
	_Nop();   
	SCL=1;								// SCL为高电平,发送第9时钟作为应答信号
	_Nop();
	_Nop();
	_Nop();
	if(SDA==1)
		ack=0; 							// 从器件保持SDA为高电平,发送异常  
	else
		ack=1;							// 从器件拉低SDA为低电平,发送正常

	SCL=0;								// SCL为低电平,清除时钟
	_Nop();
	_Nop();
}

//
// 字节数据传送函数,读出从器件一个字节
// 函数原型: uchar  RcvByte();
// 功能:
//		用来接收从器件传来的数据,并判断总线错误(不发应答信号),
//		发完后请用应答函数。  
//
uchar  RcvByte8563()
{
	idata uchar temp=0;
	idata uchar i;

	SDA=1;						// 准备接收数据
	for(i=0;i<8;i++)
	{
		_Nop();           
		SCL=0;					// SCL时钟下跳准备接收数据
		Some_NOP;				// SCL时钟信号,最小低电平为4.7us
		SCL=1;					// 时钟上跳
		_Nop();					// 等待数据出现
		_Nop();
		temp=temp<<1;	// 数据移位
		if(SDA==1)
			temp=temp+1;// 接收数据位
		_Nop();					// 接收下一位数据
		_Nop(); 
	}

	SCL=0;   					// 准备发送结束信号 
	_Nop();
	_Nop();
	return temp;			// 返回接收到的字节
}

//
// 应答子函数
// 原型:  void Ack_I2c8563(bit a);
// 功能:
//		主控器进行应答信号,(可以是应答或非应答信号)
//
void Ack_I2c8563(bit a)
{
	if(a==0)
		SDA=0;					// 发应答信号
	else
		SDA=1;					// 发非应答信号

	_Nop();						// 等待
	_Nop();
	_Nop();      
	SCL=1;						// SCL时钟上跳
	Some_NOP;					// SCL时钟高电平最小4.7us
	SCL=0;						// 钳住I2C总线,继续接收下一个字节
	_Nop();						// 等待
	_Nop();    
}

//
// 向有子地址器件8563时钟芯片发送多字节数据函数
// 函数原型: bit  ISendStr8563(uchar sla,uchar suba,ucahr *s,uchar no);  
// 功能: 
//		从启动总线到发送地址,子地址,数据,结束总线的全过程,从器件
//		地址sla,子地址suba,发送内容是s指向的内容,发送no个字节。
//		如果返回1表示操作成功,否则操作有误。
// 注意:
//		使用前必须已结束总线。
//
bit ISendStr8563(uchar sla,uchar suba,uchar *s,uchar no)
{
	idata uchar i;

  	Start_I2c8563();			// 启动I2C总线
   	SendByte8563(sla&0xfe);		// 发送主地址
	if(ack==FALSE)
		return ERROR;			// 发送数据错误

	SendByte8563(suba);			// 发送子地址
    if(ack==FALSE)
		return ERROR;			// 发送数据错误

   	for(i=0;i<no;i++)
    {   
    	SendByte8563(s[i]);		// 发送一个字节
       	if(ack==FALSE)
			return ERROR;		// 发送数据错误
    } 
	Stop_I2c8563();				// 停止I2C总线

  	return OK;					// 成功返回
}

//
// 子地址器件8563时钟芯片读取多字节数据函数               
// 函数原型: bit  IRcvStr8563(uchar sla,uchar suba,ucahr *s,uchar no);  
// 功能:
//		从启动总线到发送地址,子地址,读数据,结束总线的全过程,从器件
//		地址sla,子地址suba,读出的内容放入s指向的存储区,读no个字节。
//		如果返回1表示操作成功,否则操作有误。
// 注意?
//		使用前必须已结束总线。
//
bit IRcvStr8563(uchar sla,uchar suba,uchar *s,uchar no)
{
	idata uchar i;

	Start_I2c8563();			// 启动8563时钟芯片I2C总线
	SendByte8563(sla&0xfe);		// 向8563发送主地址
	if(ack==FALSE)
		return ERROR;			// 发送数据错误
	SendByte8563(suba);			// 向8563发送子地址
	if(ack==FALSE)
		return ERROR;			// 发送数据错误
	
	Start_I2c8563();			// 启动8563时钟芯片I2C总线
	SendByte8563(sla|0x01);		// 向8563发送主地址
	if(ack==FALSE)
		return ERROR;			// 发送数据错误
	
	for(i=0;i<no-1;i++)
	{   
		s[i]=RcvByte8563();		// 读8563一个字节
		Ack_I2c8563(0);			// 发送I2C应答位
	} 
	s[i]=RcvByte8563();			// 读8563一个字节
	Ack_I2c8563(1);				// 发送I2C非应答位
	Stop_I2c8563();				// 停止I2C总线
	return OK;					// 成功返回
}


///////////////////////////////////////////////////////////////////////////////
// 读取时间
///////////////////////////////////////////////////////////////////////////////
void read_time()  
{
	idata uchar i;

	DISABLE;
	IRcvStr8563(0xa2,0x02,time,0x07);		// 读时间
	ENABLE;

	for(i=0;i<7;i++)
	{
		switch(i)								// 时间格式化
		{
			case 0:time[i]=time[i]&0x7f;break;
			case 1:time[i]=time[i]&0x7f;break;
			case 2:time[i]=time[i]&0x3f;break;
			case 3:time[i]=time[i]&0x3f;break;
			case 4:time[i]=time[i]&0x07;break;
			case 5:time[i]=time[i]&0x9f;break;
			case 6:time[i]=time[i]&0xff;break;
			default:break;
		}
	}

	if(time[5]&0x80)					// 世纪
	{
		cur_time[0]='1';cur_time[1]='9';
	}
	else
	{
		cur_time[0]='2';cur_time[1]='0';
	}

	cur_time[2]=((time[6]>>4)&0x0f)+0x30;	// 年
	cur_time[3]=(time[6]&0x0f)+0x30;
	cur_time[4]=((time[5]>>4)&0x01)+0x30;	// 月
	cur_time[5]=(time[5]&0x0f)+0x30;		
	cur_time[6]=((time[3]>>4)&0x03)+0x30;	// 日
	cur_time[7]=(time[3]&0x0f)+0x30;
	cur_time[8]=((time[2]>>4)&0x03)+0x30;	// 时
	cur_time[9]=(time[2]&0x0f)+0x30;		
	cur_time[10]=((time[1]>>4)&0x07)+0x30;	// 分
	cur_time[11]=(time[1]&0x0f)+0x30;
	cur_time[12]=((time[0]>>4)&0x07)+0x30;	// 秒
	cur_time[13]=(time[0]&0x0f)+0x30;
	cur_time[14]=(time[4]&0x07)+0x30;
}

///////////////////////////////////////////////////////////////////////////////
// 设置时间
///////////////////////////////////////////////////////////////////////////////
void set_time()
{
	time[0]=0x08;
	time[1]=0x00;
	time[2]=((cur_time[12]-0x30)<<4)|((cur_time[13]-0x30)&0x0f);
	time[3]=((cur_time[10]-0x30)<<4)|((cur_time[11]-0x30)&0x0f);
	time[4]=((cur_time[8]-0x30)<<4)|((cur_time[9]-0x30)&0x0f);
	time[5]=((cur_time[6]-0x30)<<4)|((cur_time[7]-0x30)&0x0f);
	time[6]=(cur_time[14]-0x30)&0x07;
	time[7]=((cur_time[4]-0x30)<<4)|((cur_time[5]-0x30)&0x0f);
	time[8]=((cur_time[2]-0x30)<<4)|((cur_time[3]-0x30)&0x0f);

	DISABLE;
	ISendStr8563(0xa2,0x00,time,0x09);
	ENABLE;
}








⌨️ 快捷键说明

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