【单片机自学】ds1302时钟芯片 DS1302实时时钟 实验现象 将程序烧录到单片机中后,lcd1602显示屏将从预设时间开始进行时钟功能。 在lcd1602显示屏第一行分别显示年,月,日,星期;在第二行显示时,分,秒。
DS1302介绍 ds1302简介 DS1302 是 DALLAS 公司推出的涓流充电时钟芯片,内含有一个实时时钟/日历和 31 字节静态 RAM,通过简单的串行接口与单片机进行通信。实时时钟/日历电路提供秒、分、时、日、周、月、年的信息,每月的天数和闰年的天数可自动调整。 DS1302 与单片机之间可以通过三根数据线进行同步串行的方式进行通信:RES,IO数据,SCLK串行时钟。实时时钟具有能计算 2100 年之前的秒、分、时、日、星期、月、年的能力,还有闰年调整的能力。 引脚
Vcc2 主电源 Vcc1 备用电源 X 32.768khz晶振引脚 SCLK串行时钟 I/O 数据 CE(res) 复位引脚 GND 接地 工作流程 将预设的时间以bcd码形式写入ds1302的寄存器中,单片机开启后ds1302将从这个时间开始运作 ds1302开始运作后,寄存器里的值会自己按时间变化。不断地取出其中的值,并由bcd码转化成十进制 取到的十进制按一定格式打印在1602显示屏上 从工作流程可知要使ds1302运转,首先要了解ds1302中的寄存器,然后设计写入数据与读取数据函数,最后进行调用。 寄存器 控制寄存器 在对ds1302进行读写操作的时候,第一步要向ds1302发送控制命令,用于后续对应的操作。在ds1302中,有这么一种寄存器,用于存放控制命令,这样ds1302就可以根据我们发送的指令,进行对应的操作。 一个控制命令有8位,输入一个八位的命令就可以进行操作。例如想要读取秒寄存器,就需要向ds1302发送命令,例如想要写秒寄存器,就需要向ds1302发送命令。 详情见以下表格:
时间寄存器
在ds1302中有着以上这些寄存器,用于保存时间或者其他数据。在秒,分,时,日,月,星期,年寄存器中,用bcd码保存着数据,高四位用于保存十进制的十位,低四位用于保存十进制的个位。例如想保存12月21日,就分别向日寄存器写0x21(00,高四位2,低四位1),向月寄存器写0x12。 秒的范围是0-59,因此对于高四位,其实用三位就足够了(最大为5,101),最高的那一位是ds1302的开关,为0时才能工作。 小时有12与24两种。最高位为0时表示24进制,此时剩下的位全是表示时间的数据;最高位为1时,表示12进制,此时上图的A/P表示上午或者下午。 写保存寄存器的最高位WP是写入寄存器的通路的开关。当WP为0时,才可以向ds1302中写入数据。 读写与时序 写 前面提到,ds1302使用三根通信线CE,IO,SCLK。如何使用这三根线写入芯片呢? IO用于接收一位数据,低电平为0,高电平为1。要向ds1302写数据,要做以下操作: 将CE至高,SCLK置低 将要写入的数据的最低位存入IO 将SCLK置高,这样产生一个上升沿,上升沿会使IO数据写入芯片,置高一段时间后置低 将数据右移一位(使次低位变成最低位) 重复2-4,直到数据写完 要写数据,首先要用2-5步骤写入对应的控制命令,再用相同的方法写要写的数据 完成命令与数据输入后,CE置低 读 与写不同,在输入了读的执行命令后,会在SCLK的下降沿将对应寄存器的值从低位开始传给IO,因此,读寄存器的操作是: 按照写数据的方法,将执行指令写入芯片 传完8位指令后,在SCLK为低电平时使用变量保存IO的状态,然后置高电平 循环读取八次,会读取到从低到高八位数据。将这八次得到的数据做成一个八位的变量 返回得到的这个八位变量(是bcd码) ds1302的GPIO 由图可知,IO,CE,SCLK分别连接着P34,35,36接口,因此要操作IO,CE,SCLK,在代码层仅需要对P34,35,36进行操作就行。 代码 ds1302.c 涉及到的操作最核心的部分是对ds1302的读与写,思路已在上文提及,读函数与写函数代码如下: #include <REGX52.H> #include “intrins.h” //封装GPIO #define CE P3_5 #define IO P3_4 #define SCLK P3_6 /* * @brief 向ds1302芯片的寄存器写一个字节数据 * @details 提供控制指令与数据,将数据按位赋给IO,在SCLK出现上升沿的时候, IO的值会发送给ds1302,先将控制命令用八次循环按位写入IO,经历 SCLK八次上升沿后命令送达,再用同样的方式将数据送入DS1302。 在整个期间,CE(RST)置高电平 * @param addr 写入的控制指令 * @param dat 向对应的寄存器要写的数据 * @retval 无 */ void ds1302_write_byte(unsigned char addr,unsigned char dat){ unsigned char i; CE = 0; _nop_(); CE = 1;//将CE从低电平改为高电平 _nop_(); SCLK = 0;//SCLK置低电平 _nop_(); for (i = 0;i<8;i++){//输入指令 IO = addr&0x01; //取出最低位 addr = addr>>1; //右移一位,次低位变最低位 SCLK = 1; //产生上升沿,数据传入后再置低电平 _nop_(); SCLK = 0; _nop_(); } for (i = 0;i<8;i++){//输入数据 IO = dat&0x01; dat = dat>>1; SCLK = 1; _nop_(); SCLK = 0; _nop_(); } CE = 0; _nop_(); } /* * @brief 读取ds1302寄存器数据. * @details 提供控制指令选择要读的寄存器之后,先将控制命令写入芯片, 此时sclk会完成八个上升沿,七个下降沿。 读的数据会在第八个下降沿开始返回,因此在将最后一个控制命令 的位数据写入后,置低电平时就要从IO开始读。 读到的时间数据是BCD码 * @param addr 写入的控制指令 * @retval 读出的寄存器数据 */ unsigned char ds1302_read_byte(unsigned char addr){ unsigned char i=0,temp=0,value=0; CE = 0; _nop_(); CE = 1; _nop_(); SCLK = 0; _nop_(); for (i = 0;i<8;i++){ //输入指令 IO = addr&0x01; addr = addr>>1; SCLK = 1; _nop_(); SCLK = 0; _nop_(); } for (i = 0;i<8;i++){ //下降沿读取数据 temp = IO; /*先将 value 右移 1 位,然后 temp 左移 7 位, 最后或运算 这样可以将按低位拿到的数据变成八位数据 例如寄存器里为,那么value在循环中的值是00000000, 00000000,00000000,,0……*/ value=(temp<<7)|(value>>1); SCLK=1; _nop_(); SCLK=0; _nop_(); } CE = 0; _nop_(); /* 下面的代码在写函数里面没有,看官方教程解释也没搞明白, 删掉运行测试也没有异常… */ SCLK=1; _nop_(); IO = 0; _nop_(); IO = 1; _nop_(); return value; } 不仅需要读写操作的函数,还需要有调用这些函数的调用者。上文说过最开始要初始化时间,初始化后再继续不断读取。初始化的时间是以bcd码的形式保存在数组中的,得到的数据也是bcd码的形式。因此,还要写一个初始化函数,将初始值数组的值依次写入秒,分,时,日,月,星期,年寄存器;还要一个读取函数,依次读取这七个寄存器的值到数组。 代码如下: //—DS1302 写入和读取时分秒的地址命令 //—秒分时日月周年 最低位读写位; unsigned char gREAD_RTC_ADDR[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d}; unsigned char gWRITE_RTC_ADDR[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c}; //—DS1302 时钟初始化 2022 年 8 月 31 日星期三 17 点 22 分 25 秒。 //—存储顺序是秒分时日月周年,存储格式是用 BCD 码—// unsigned char gDS1302_TIME[7] = {0x25, 0x22, 0x17, 0x31, 0x08, 0x03, 0x22}; /* * @brief 初始化时钟时间 * @details 将时间数组保存的值用写函数写入ds1302 * @param 无 * @retval 无 */ void ds1302_init(){ unsigned char i; ds1302_write_byte(0x8E,0X00);//在写寄存器之前,关闭写寄存器保护 for (i = 0;i<7;i++){ ds1302_write_byte(gWRITE_RTC_ADDR[i],gDS1302_TIME[i]); } ds1302_write_byte(0x8E,0X80);//重新开启保护 } /* * @brief 将寄存器中的时间写入数组 * @details 将时间数组保存的值用写函数写入ds1302 * @param 无 * @retval 无 */ void ds1302_read_time(void){ unsigned char i=0; for(i=0;i<7;i++) { gDS1302_TIME[i]=ds1302_read_byte(gREAD_RTC_ADDR[i]); } } 整个ds1302.c代码如下: #include <REGX52.H> #include “intrins.h” //封装GPIO #define CE P3_5 #define IO P3_4 #define SCLK P3_6 //—DS1302 写入和读取时分秒的地址命令 //—秒分时日月周年 最低位读写位; unsigned char gREAD_RTC_ADDR[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d}; unsigned char gWRITE_RTC_ADDR[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c}; //—DS1302 时钟初始化 2022 年 8 月 31 日星期三 17 点 22 分 25 秒。 //—存储顺序是秒分时日月周年,存储格式是用 BCD 码—// unsigned char gDS1302_TIME[7] = {0x25, 0x22, 0x17, 0x31, 0x08, 0x03, 0x22}; /* * @brief 向ds1302芯片的寄存器写一个字节数据 * @details 提供控制指令与数据,将数据按位赋给IO,在SCLK出现上升沿的时候, IO的值会发送给ds1302,先将控制命令用八次循环按位写入IO,经历 SCLK八次上升沿后命令送达,再用同样的方式将数据送入DS1302。 在整个期间,CE(RST)置高电平 * @param addr 写入的控制指令 * @param dat 向对应的寄存器要写的数据 * @retval 无 */ void ds1302_write_byte(unsigned char addr,unsigned char dat){ unsigned char i; CE = 0; _nop_(); CE = 1;//将CE从低电平改为高电平 _nop_(); SCLK = 0;//SCLK置低电平 _nop_(); for (i = 0;i<8;i++){//输入指令 IO = addr&0x01; //取出最低位 addr = addr>>1; //右移一位,次低位变最低位 SCLK = 1; //产生上升沿,数据传入后再置低电平 _nop_(); SCLK = 0; _nop_(); } for (i = 0;i<8;i++){//输入数据 IO = dat&0x01; dat = dat>>1; SCLK = 1; _nop_(); SCLK = 0; _nop_(); } CE = 0; _nop_(); } /* * @brief 读取ds1302寄存器数据. * @details 提供控制指令选择要读的寄存器之后,先将控制命令写入芯片, 此时sclk会完成八个上升沿,七个下降沿。 读的数据会在第八个下降沿开始返回,因此在将最后一个控制命令 的位数据写入后,置低电平时就要从IO开始读。 读到的时间数据是BCD码 * @param addr 写入的控制指令 * @retval 读出的寄存器数据 */ unsigned char ds1302_read_byte(unsigned char addr){ unsigned char i=0,temp=0,value=0; CE = 0; _nop_(); CE = 1; _nop_(); SCLK = 0; _nop_(); for (i = 0;i<8;i++){ //输入指令 IO = addr&0x01; addr = addr>>1; SCLK = 1; _nop_(); SCLK = 0; _nop_(); } for (i = 0;i<8;i++){ //下降沿读取数据 temp = IO; /*先将 value 右移 1 位,然后 temp 左移 7 位, 最后或运算 这样可以将按低位拿到的数据变成八位数据 例如寄存器里为,那么value在循环中的值是00000000, 00000000,00000000,,0……*/ value=(temp<<7)|(value>>1); SCLK=1; _nop_(); SCLK=0; _nop_(); } CE = 0; _nop_(); SCLK=1; _nop_(); IO = 0; _nop_(); IO = 1; _nop_(); return value; } /* * @brief 初始化时钟时间 * @details 将时间数组保存的值用写函数写入ds1302 * @param 无 * @retval 无 */ void ds1302_init(){ unsigned char i; ds1302_write_byte(0x8E,0X00);//在写寄存器之前,关闭写寄存器保护 for (i = 0;i<7;i++){ ds1302_write_byte(gWRITE_RTC_ADDR[i],gDS1302_TIME[i]); } ds1302_write_byte(0x8E,0X80);//重新开启保护 } /* * @brief 将寄存器中的时间写入数组 * @details 将时间数组保存的值用写函数写入ds1302 * @param 无 * @retval 无 */ void ds1302_read_time(void){ unsigned char i=0; for(i=0;i<7;i++) { gDS1302_TIME[i]=ds1302_read_byte(gREAD_RTC_ADDR[i]); } } main.c main程序中要做的是初始化,不断读取得到bcd码形式的时间值,转换成十进制后打印在LCD1602上。代码如下: /* 实验名称:ds1302时钟 实现现象:从起点时间开始进行实时时钟 首先将保存在数组中的bcd码形式的起始时间值发送到ds1302,然后再在主函数中不断读取 ds1302寄存器中的时间数值,ds1302会不断进行时间增长。主函数读取到实时的时间后,通过 将bcd转换成10进制后打印在lcd1602上。 */ #include “ds1302.h” #include “LCD1602.h” extern unsigned char gDS1302_TIME[7]; void main(){ unsigned char time_buf[7]; unsigned char i; ds1302_init();//初始化 DS1302 LCD_Init();//初始化1602 while(1){ ds1302_read_time();//读取时间到数组 for (i = 0 ; i< 7 ; i++){ time_buf[i]=(gDS1302_TIME[i]/16)*10+gDS1302_TIME[i]%16;//将数组中的bcd形式的值转换成10进制并保存在其他数组中 } //打印时间 LCD_ShowNum(1,1,20,2); LCD_ShowNum(1,3,time_buf[6],2); LCD_ShowChar(1,5,’-‘); LCD_ShowNum(1,6,time_buf[4],2); LCD_ShowChar(1,8,’-‘); LCD_ShowNum(1,9,time_buf[3],2); LCD_ShowNum(1,15,time_buf[5],1); LCD_ShowNum(2,1,time_buf[2],2); LCD_ShowChar(2,3,’:’); LCD_ShowNum(2,4,time_buf[1],2); LCD_ShowChar(2,6,’:’); LCD_ShowNum(2,7,time_buf[0],2); } } 其中lcd1602操作的相关代码并不是本人所打,是使用的网络资源因此不在此放出。 备注 本人为51单片机学习者,本文是自己学习的总结。自己水平十分有限。若有错误望请大家多多指教! 本文的截图是在普中科技官方在开发攻略中截的,代码也是跟着官方教程打的,经自己测试没有问题。
2024最新激活全家桶教程,稳定运行到2099年,请移步至置顶文章:https://sigusoft.com/99576.html
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。 文章由激活谷谷主-小谷整理,转载请注明出处:https://sigusoft.com/62658.html