51单片机DS1302时钟芯片应用Proteus仿真

系列文章目录

第一章 51单片机Proteus仿真(一) DS1302使用


文章目录

  • 系列文章目录
  • 前言
  • 一、DS1302时序解读
  • 二、BCD码转十进制解读
  • 三、Proteus仿真
  • 四、详细代码


前言

51单片机使用Proteus仿真。小白一枚记录自己学习日常。疫情期间Proteus走起,这软件很强大,使用中也遇到很多问题,求助度娘已解决。代码解释不是特别详细,文笔不好,请各位大神见谅。本人记录自己大四毕业之际准备毕业论文随笔,如有能帮助到您的地方,跪求点赞关注。诸君共勉,共同进步。本文代码实现当前时间 21时02分39秒。万年历下次再写以及显示温度等


提示:以下是本篇文章正文内容,下面案例可供参考

一、DS1302时序解读

时序解释:
写时序 ( 给DS1302写入数据) 需要先确定写哪个寄存器,再写入数据。因为每个寄存器存储数据不同,有存储秒、分、小时、日期、年等寄存器
这里要理解数据输入(单片机向时钟芯片写数据)是SCLK上升沿,数据输出(单片机读取时钟芯片数据)是SCLK下降沿。
向某一个寄存器写入一个字节,就是送入16位(就是数据输入),需16个脉冲。先0后1算一个脉冲(上升沿实现)。
CE=1时,先写命令字,确定给哪个寄存器写数据。最低位设置写还是读,当时钟上升沿来临时,最低位被写入,依次时钟上升沿来临读入一位数据。最高位默认为1。第六位为0是时钟芯片数据,为1是RAM数据。A0-A4是寄存器地址。一个时钟上升沿写入一位数据。写完命令字,再写入数据指令。
在寄存器图示这里插入图片描述

因为时钟芯片要一位一位的读写,所以取出一位数据即可。使用Data&0X01读取最低位(第一位),Data&0X02读取第二位,使用for循环8次即可读取8位数据。代码如下

DS1302_IO=Data&(0x01<<i);

因为时钟脉宽是ns级别,而51单片机运行速度慢,一个机器周期是1微妙,即运行一行代码需要1微妙,所以在SCLK=1和SCLK=0之间可以不加延时。若单片机运行速度很快,必须加延时。
51单片机DS1302时钟芯片应用Proteus仿真_第1张图片

读时序 (读取DS1302数据,需要先确定哪个寄存器,再读取数据)
读寄存器数据与写入有很大不同 观察时序图发现,读数据时只有15个脉冲。这是因为输入数据是上升沿,输出数据是下降沿。先确定哪个寄存器,应该是写入数据,若依照写数据代码,先SCLK=1;(上升沿写入)再SCLK=0; 会发现第一个for循环最后一次0导致时钟芯片输出了一位数据(下降沿输出),造成读写操作不连贯,我们目的只是写入数据确定哪个寄存器。读数据代码还没有作用。

	for(i=0;i<8;i++)
	{
		DS1302_IO=command&(0x01<<i);
		DS1302_SCLK=1;
		DS1302_SCLK=0;
	}
	for(i=0;i<8;i++)
	{
		DS1302_IO=Data&(0x01<<i);
		DS1302_SCLK=1;
		DS1302_SCLK=0;
	}

此时第一个for循环改为先SCLK=0;再SCLK=1;循环8次就写入了一个字节指令,确定是哪个寄存器(8个脉冲)。
然后第二个for循环写SCLK=1;相当于将SCLK=1脉宽变大了,不影响时序,只是为了满足15个脉冲,这里赋值操作最关键,需认真理解。此时再将SCLK=0;读出第一位数据。循环8次则读出8位数据,也满足15个脉冲。
**

for(i=0;i<8;i++)
	{
		DS1302_IO=command&(0x01<<i);
		DS1302_SCLK=0;
		DS1302_SCLK=1;//写入数据,即上升沿到来
	}
	for(i=0;i<8;i++)
	{
	DS1302_SCLK=1;//很关键,将写命令的第8个脉冲宽度延长,满足15个脉冲
	DS1302_SCLK=0;//读取DS1302数据,下降沿输出
	if(DS1302_IO==1) //当数据输出为1时,保存下来,第几位就左移几位
	  Data|=(0x01<<i);//输出为0不用处理
	}

二、BCD码转十进制解读

BCD码 用4位二进制数表示1位十进制数
DS1302寄存器默认使用BCD码存储数据
0001 0011表示13 0010 0101表示25 0001 1010不合法因为后四位10在十进制个位里面没有10
BCD码转十进制 DEC=BCD/1610+BCD%16; (两位BCD)
十进制转BCD码 BCD=DEC/10
16+DEC%10; (两位BCD)
这里很关键,不然10会显示16,下面解释为何显示16
由于读取寄存器返回的是BCD码,0X10十进制是16,所以将BCD转为10进制。BCD/16*10+BCD%16 很好理解。BCD码将10变成16,因此先除以16,再乘以10就表示几个10了,这是十位。个位直接对16取余。其实这相当于16的几次方相加,这个几次方对于十进制也是相同的。BCD转十进制,就是求出这个几次方
关于秒显示从9变成16解释说明

LCD_ShowNum(2,1,second,2);

这样写,到9之后加一就显示16
0000 1001这是9,加上1变成10,0001 0000这是10的BCD码。但程序默认转化为十六进制0X10就是十进制16,所以用LCD1602十进制显示函数时显示16。
有初学者一定不明白,写入0X03为何就显示3呢。这就要从寄存器说起了,DS1302寄存器存储数据使用BCD码,0X03表示十进制3,读取寄存器数据就是0X03,所以用LCD1602显示没问题。
但是当秒加到9(0000 1001)(注意芯片秒寄存器最高位为0时,芯片动作,每秒加一),下一秒加到10,由于BCD码,会将1进到第5位 (BIT4位)(0001 0000)此时表示10秒,因为秒寄存器BIT4-7位表示10秒。而0001 0000用十六进制表示16 0X10,读出这个数据显示就是16。

三、Proteus仿真

51单片机DS1302时钟芯片应用Proteus仿真_第2张图片

有关Proteus需要再写

四、详细代码

代码如下(示例):

#include 
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;

//LCD1602引脚定义
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0
//LCD1602函数
void LCD_Delay();
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
//DS1302函数
void DS1302_Init();
void DS1302_WriteByte(unsigned char command,Data);
unsigned char DS1302_ReadByte(unsigned char command);


int main()
{
	unsigned char second;
	unsigned char min;
	unsigned char hour;
	LCD_Init();
	DS1302_Init();
	LCD_ShowString(1,1,"RTC");
	DS1302_WriteByte(0x80,0x55);//0X55转化为BCD就是55
	
	while(1)
	{
	second=DS1302_ReadByte(0x81);
	min=DS1302_ReadByte(0X83);
	hour=DS1302_ReadByte(0X85);
   LCD_ShowNum(2,7,second/16*10+second%16,2);//BCD转为十进制
	LCD_ShowChar(2,6,'-');
	LCD_ShowNum(2,4,min/16*10+min%16,2);
	LCD_ShowChar(2,3,'-');
	LCD_ShowNum(2,1,hour/16*10+hour%16,2);
	}
	return 0;
}
void DS1302_Init()
{
	DS1302_CE=0;
	DS1302_SCLK=0;
}
/**
*@brief DS1302Ð写入命令和数据
*@param 命令确定哪个寄存器
*@retval
*/
void DS1302_WriteByte(unsigned char command,Data)
{
	int i;
	DS1302_CE=1;
	for(i=0;i<8;i++)
	{
		DS1302_IO=command&(0x01<<i);
		DS1302_SCLK=1;
		DS1302_SCLK=0;
	}
	for(i=0;i<8;i++)
	{
		DS1302_IO=Data&(0x01<<i);
		DS1302_SCLK=1;
		DS1302_SCLK=0;
	}
	DS1302_CE=0;
}
/**
*@brief   DS1302读一个字节
*@param
*@retval
*/
unsigned char DS1302_ReadByte(unsigned char command)
{
	int i;
	unsigned char Data=0x00;
	DS1302_CE=1;
	for(i=0;i<8;i++)
	{
		DS1302_IO=command&(0x01<<i);
		DS1302_SCLK=0;
		DS1302_SCLK=1;//写入数据,即上升沿到来
	}
	for(i=0;i<8;i++)
	{
	DS1302_SCLK=1;//很关键,将写命令的第8个脉冲宽度延长,满足15个脉冲
	DS1302_SCLK=0;//读取DS1302数据,下降沿输出
	if(DS1302_IO==1) //当数据输出为1时,保存下来,第几位就左移几位
	  Data|=(0x01<<i);//输出为0不用处理
	}
	DS1302_IO=0;
	DS1302_CE=0;
	return Data;
	
}
/**
  * @brief  LCD1602延时,12MHz1ms
  * @param  
  * @retval 
  */
void LCD_Delay()
{
	unsigned char i, j;

	i = 2;
	j = 239;
	do
	{
	while (--j);
	} 
	while (--i);
}

/**
  * @brief  LCD1602写命令
  * @param  Command 
  * @retval 
  */
void LCD_WriteCommand(unsigned char Command)
{
	LCD_RS=0;
	LCD_RW=0;
	LCD_DataPort=Command;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

/**
  * @brief  LCD1602写数据
  * @param  Data 
  * @retval 
  */
void LCD_WriteData(unsigned char Data)
{
	LCD_RS=1;
	LCD_RW=0;
	LCD_DataPort=Data;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

/**
  * @brief  LCD1602设置RAM指针位置
  * @param  Line :1~2
  * @param  Column :1~16
  * @retval ?
  */
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
	if(Line==1)
	{
	LCD_WriteCommand(0x80|(Column-1));//RAM指针默认最高位为1,从0X00开始
	}
	else if(Line==2)
	{
	LCD_WriteCommand(0x80|(Column-1+0x40));//RAM指针从0X40开始
	}
}

/**
  * @brief  LCD1602
  * @param  ?
  * @retval ?
  */
void LCD_Init()
{
	LCD_WriteCommand(0x38);//LCD1602 显示16*2
	LCD_WriteCommand(0x0c);//LCD1602开显示光标不亮
	LCD_WriteCommand(0x06);//LCD1602指针自动加一光标加一
	LCD_WriteCommand(0x01);//清屏
}
//显示字符
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
	LCD_SetCursor(Line,Column);
	LCD_WriteData(Char);
}
//显示字符串
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
	LCD_SetCursor(Line,Column);
	while(*String!='\0')//指针解引用贼秀
	{
	LCD_WriteData(*String++);
	}
}
/**
*@brief 
*@param  返回X的Y次方
*@retval
*/
int LCD_Pow(int X,int Y)
{
	unsigned char i;
	int Result=1;
	for(i=0;i<Y;i++)
	{
		Result*=X;
	}
	return Result;
}

/**
  * @brief  LCD1602十进制显示数字
  * @param  Line :1~2
  * @param  Column :1~16
  * @param  Number :0~65535
  * @param  Length :1~5
  * @retval 
  */
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
    LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');//涉及ASCLL码,有需要再写
	}
}


# 总结
以上就是今天要讲的内容,本文仅仅简单介绍了51单片机与DS1302结合使用。

你可能感兴趣的:(51单片机,51单片机)