DS1302是蓝桥杯单片机常用的芯片之一,这个芯片并不难,需要背诵的部分是比较少的。
但我自己在学习研究的时候发现,其读写操作是有些许值得斟酌的地方。
此处对其进行一些介绍,但我只介绍编程时需要必备的硬件设计、芯片手册内容,
完整的有关此芯片的介绍,各位看官还需要多查阅DS1302芯片手册才是。
基础好的可以直接跳过DS1302的入门介绍。
附上文章所讲的整个工程下载地址:
https://download.csdn.net/download/qq_64257614/87786967?spm=1001.2014.3001.5503
目录
目录
一、DS1302芯片极简入门介绍:
寄存器介绍:
DS1302有两块存储器,日历时钟寄存器和静态RAM存储器。
前者用于记录时间,后者用于记录其他数据
日历时钟寄存器:又分为 读寄存器 和 写寄存器
以下是日历寄存器的地址:
这么看大家可能一头雾水,那我将它简化一下就十分明了了:
我们以时间位的设置读取 为线索来看这张表:
时间位 | 读寄存器 | 写寄存器 | 范围 |
秒 | 0x81 | 0x80 | 0x 00~59 |
分 | 0x83 | 0x82 | 0x 00~59 |
时 | 0x85 | 0x84 | 0x 01~12/00~23 |
日 | 0x87 | 0x86 | 0x 01~31 |
月 | 0x89 | 0x88 | 0x 01~12 |
周 | 0x8b | 0x8a | 0x 01~7 |
年 | 0x8d | 0x8c | 0x 00~99 |
(1)秒寄存器(80H和81H)的位7(CH)为时钟暂停标志位:
CH=1;时钟振荡停止
CH=0;时钟开始运行
(2)控制寄存器(8EH和8FH)的位7(WP)为写保护位:
WP=0;允许 对时钟或RAM寄存器 进行写操作
WP=1;禁止 对任何寄存器 进行写操作
读取和写入更改时间位 秒例子介绍:
对DS1302进行操作时,我们可以根据这张表:以更改和读取 秒 来介绍
1.先找到要操作的时间位 秒
2.查表得到它的读写寄存器:
时间位 | 读寄存器 | 写寄存器 | 范围 |
秒 | 0x81 | 0x80 | 0x 00~59 |
3.找到并复制 官方驱动的这俩个函数:
第一个是写函数,第二个是读函数
void Write_Ds1302_Byte( unsigned char address,unsigned char dat ) ;
unsigned char Read_Ds1302_Byte ( unsigned char address );
4.读操作如此写,即可读取当前秒数:
second=Read_Ds1302_Byte ( 0x81);
//此句可读取当前秒数
//second 是一个U8类型变量
5.因为DS1302是有读写保护的,所以在进行写操作之前,
还需要向地址写入指令,取消写保护:
Write_Ds1302_Byte(0x8e,0x00);//允许写入数据
6.写操作如此写,即可更改秒数:
//更改秒数为25
Write_Ds1302_Byte(0x80,0x25) ;
7.写操作结束后,别忘了打开写保护,继续禁止写入:
Write_Ds1302_Byte(0x8e,0x80);//禁止写入数据
此外其他时间位数值的设定和读取也都是同样的方法,只需选择对应的地址读写即可。
一次性赋值七个时间量介绍:
此处介绍一个习惯:
就是将 读地址 和 写地址 都分别写入俩个数组,顺序是 秒 分 时 日 月 周 年
unsigned char Read_DS1302_adrr[7]={0x81,0x83,0x85,0x87,0x89,0x8b,0x8d};
//定义DS1302读操作的日历时钟存储器地址
//秒 分 时 日 月 周 年
unsigned char Write_DS1302_adrr[7]={0x80,0x82,0x84,0x86,0x88,0x8a,0x8c};
//定义DS1302写操作的日历时钟寄存器地址
//秒 分 时 日 月 周 年
这样在之后 七个时间量就能 一次性 用循环来进行赋值初始化了,单独赋值也十分方便:
一次性初始化时间量例子:
此例为初始化1302为 2023年,周一,5月15日,22时,29分,50秒:
u8 Timer[10]={0x50,0x29,0x22,0x15,0x05,0x01,0x23};
// 秒 分 时 日 月 周 年
void DS1302_config() //ds1302时钟初始化写入
{
char i;
Write_Ds1302_Byte(0x8e,0x00);//允许写入数据
for(i=0;i<7;i++) //写入7个字节的时间参数
{
Write_Ds1302_Byte(Write_DS1302_adrr[i],Timer[i]);
}
Write_Ds1302_Byte(0x8e,0x80);//禁止写入数据
}
void DS1302_Timer() //读取当前时钟数据
{
char i;
for(i=0;i<3;i++) //读取7个字节的实时时间
{
Timer[i]=Read_Ds1302_Byte(Read_DS1302_adrr[i]);
}
}
二、DS1302芯片数据处理:
读取数据处理:
终于,我们学会了读取DS1302的内容,但该如何处理它读取到的数据呢?
要想将16进制数转化为10进制,只需进行/16和%16操作即可:
这样我们就能给数码管打印时间值了。
//读取的值数据类型 与 写入时一样:
u8 Timer[10]={0x50,0x59,0x23,0x05,0x04,0x03,0x23};
Timer[2]/16; //时 十位
Timer[2]%16; //时 个位
Timer[1]/16; //分 十位
Timer[1]%16; //分 个位
Timer[0]/16; //秒 十位
Timer[0]%16; //秒 个位
写入数据处理:
有些题目要求我们在时钟运转时,能更改时间,那么数据该如何写入呢?
1.首先定义一个 char 类型的变量 将当前读取到的时间转换成10进制后存入:
不用unsigned char 是因为不能判断 加减操作边界 小于0的情况,
char time_temp;
unsigned char read_t[4]={0,0,0};
void read_DS1302_Timer() //读取当前时钟数据
{
char i;
for(i=0;i<3;i++) //读取7个字节的实时时间
{
Timer[i]=Read_Ds1302_Byte(Read_DS1302_adrr[i]);
read_t[i]=Timer[i]/16*10+Timer[i]%16;
}
}
//存下当前秒数
time_temp=read_t[0];
2.再定义俩个 unsigned char 类型的变量 备用
u8 time_temp_1;
u8 time_temp_2;
2.对该十进制数变量进行需要的加减操作后,在通过辅助转化为16进制数,最后进行写操作写入:
char time_temp;
u8 time_temp_1;
u8 time_temp_2;
u8 read_t[4]={0,0,0};
void read_DS1302_Timer() //读取当前时钟数据
{
char i;
for(i=0;i<3;i++) //读取7个字节的实时时间
{
Timer[i]=Read_Ds1302_Byte(Read_DS1302_adrr[i]);
read_t[i]=Timer[i]/16*10+Timer[i]%16;
}
}
//存下当前秒数
time_temp=read_t[0];
time_temp=Timer[cho]/16*10+Timer[cho]%16;
time_temp++;
time_temp_1=time_temp/10;
time_temp_2=time_temp%10;
time_temp=time_temp_1*16+time_temp_2;
Write_Ds1302_Byte(0x8e,0x00);//允许写入数据
Write_Ds1302_Byte(Write_DS1302_adrr[0],time_temp);//写入数据
Write_Ds1302_Byte(0x8e,0x80);//禁止写入数据
最后还是提醒一下,变量加减自己要注意边界哦!!!!
三、实践突破:
学会了基础,就要开始操作了,现在让我们进入以下实践:
此实验主要总结回顾1302的一些数据操作:
1.开机就开始显示时间,数码管都不闪烁
2.时间初始化为:2023年,周一,5月15日,22时,29分,50秒:
3.按下S5 开始选择 时、分、秒 / 时、分、秒被选中时闪烁
4.此时闪烁的位可以通过按下 S4 S8分别进行加减操作
5.按下S9取消选中,数码管都不闪烁
此实践考察矩阵按键和以上教学分享的所有操作,文首的工程便是它.
大家可以自己在四梯科技的竞赛板子上开始实践,实在GET不到设计灵感了,
可以下载文首链接的资源工程。
四、实验效果:
DS1302时钟芯片的读写实验效果
下面为防止资源无法下载,附上工程所有代码:
#include " DS1303_Review.h"
#include " DS1303_Review.h"
void read_DS1302_Timer(); //读取当前时钟数据
void DS1302_config(); //ds1302时钟初始化写入
void ds1302_gai(u8 sj,u8 sz); //改时间函数
void main()
{
u8 key_value;
char time_temp;
u8 time_temp_1;
u8 time_temp_2;
cls_led_buzz_smg(); //初始化清除外设
Timer0Init();
Timer1Init();
DS1302_config(); //ds1302时钟初始化写入
while(1)
{
key_value=key_return();
switch(key_value)
{
case 5:cho++;if(cho>=4) cho=0;break;
case 4:
if(cho!=0)
{
time_temp=read_t[cho-1];
time_temp++;
//防越界
if(cho==1 && time_temp>=60) time_temp=60;
if(cho==2 && time_temp>=60) time_temp=60;
if(cho==3 && time_temp>=24) time_temp=24;
time_temp_1=time_temp/10;
time_temp_2=time_temp%10;
time_temp=time_temp_1*16+time_temp_2;
ds1302_gai(cho-1,time_temp);
}
break;
case 8:
if(cho!=0)
{
time_temp=read_t[cho-1];
time_temp--;
//防越界
if(time_temp<=0) time_temp=0;
time_temp_1=time_temp/10;
time_temp_2=time_temp%10;
time_temp=time_temp_1*16+time_temp_2;
ds1302_gai(cho-1,time_temp);
}
break;
case 9:cho=0;break;
}
if(timer_flag==1)
{
timer_flag=0;
read_DS1302_Timer(); //读取当前时钟数据
}
}
}
void DS1302_config() //ds1302时钟初始化写入
{
char i;
Write_Ds1302_Byte(0x8e,0x00);//允许写入数据
for(i=0;i<7;i++) //写入7个字节的时间参数
{
Write_Ds1302_Byte(Write_DS1302_adrr[i],Timer[i]);
}
Write_Ds1302_Byte(0x8e,0x80);//禁止写入数据
}
void read_DS1302_Timer() //读取当前时钟数据
{
char i;
for(i=0;i<3;i++) //读取7个字节的实时时间
{
Timer[i]=Read_Ds1302_Byte(Read_DS1302_adrr[i]);
read_t[i]=Timer[i]/16*10+Timer[i]%16;
}
}
//sj选择改的时间 0——秒 1——分钟 2——小时
//sz是输入改的数值
void ds1302_gai(u8 sj,u8 sz) //改时间函数
{
switch(sj)
{
case 0:
Write_Ds1302_Byte(0x8e,0x00);//允许写入数据
Write_Ds1302_Byte(Write_DS1302_adrr[0],sz);
break;
case 1:
Write_Ds1302_Byte(0x8e,0x00);//允许写入数据
Write_Ds1302_Byte(Write_DS1302_adrr[1],sz);
break;
case 2:
Write_Ds1302_Byte(0x8e,0x00);//允许写入数据
Write_Ds1302_Byte(Write_DS1302_adrr[2],sz);
break;
}
Write_Ds1302_Byte(0x8e,0x80);//禁止写入数据
}
#ifndef _DS1303_Review_h_
#define _DS1303_Review_h_
#include "stc15f2k60s2.h"
#include "key_4589.h"
#include "public.h"
#include "smg.h"
#include "Timer.h"
#include "ds1302.h"
#endif
#include "public.h"
#include "public.h"
void inint_74hc573(u8 choice)
{
switch(choice)
{
case 4:P2=(P2&0X1F)|0X80;break;
case 5:P2=(P2&0X1F)|0Xa0;break;
case 6:P2=(P2&0X1F)|0Xc0;break;
case 7:P2=(P2&0X1F)|0Xe0;break;
}
}
void cls_led_buzz_smg() //初始化清除外设
{
inint_74hc573(5);
buzz = 0;
inint_74hc573(4);
P0 = 0xff;
inint_74hc573(6);
P0 = 0xff;
inint_74hc573(7);
P0 = 0xff;
}
#ifndef _public_h_
#define _public_h_
#include "stc15f2k60s2.h"
#include "public.h"
//数据类型重定义
typedef unsigned int u16;
typedef unsigned char u8;
typedef int u32;
//定义蜂鸣器管脚
sbit buzz=P0^6;
void inint_74hc573(u8 choice);
void cls_led_buzz_smg(); //初始化清除外设
#endif
#include "Timer.h"
#include "Timer.h"
u8 timer_flag; //时间读取标志位
u8 cho; //秒,分,时 选取闪烁标志位
//0 无选择 1 秒 2 分 3 时
u8 lm;//亮灭控制位
u8 nr1,nr2,nr3,nr4,nr5,nr6,nr7,nr8;
u8 read_t[4]={0,0,0};
unsigned char Read_DS1302_adrr[7]={0x81,0x83,0x85,0x87,0x89,0x8b,0x8d};
//定义DS1302读操作的日历时钟存储器地址
// 秒 分 时 日 月 周 年
unsigned char Write_DS1302_adrr[7]={0x80,0x82,0x84,0x86,0x88,0x8a,0x8c};
//定义DS1302写操作的日历时钟寄存器地址
// 秒 分 时 日 月 周 年
u8 Timer[10]={0x50,0x29,0x22,0x15,0x05,0x01,0x23};
// 秒 分 时 日 月 周 年
void nr_fuzhi(); //根据显示模式给nr1~nr8赋值
void Timer0Init(void) //1毫秒@12.000MHz
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0x01; //设置定时器模式
TL0 = 0x18; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
EA=1;
ET0=1;
}
void timer0_server() interrupt 1
{
u8 i;
TL0 = 0x18; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
i++;
if(i==8)
{
i=0;
nr_fuzhi(); //赋值数码管打印内容
smg_display(nr1,nr2,nr3,nr4,nr5,nr6,nr7,nr8); //打印数码管
}
}
void Timer1Init(void) //1毫秒@12.000MHz
{
AUXR &= 0xBF; //定时器时钟12T模式
TMOD &= 0x01; //设置定时器模式
TL1 = 0x18; //设置定时初始值
TH1 = 0xFC; //设置定时初始值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
EA=1;
ET1=1;
}
void Timer1_server() interrupt 3
{
u8 i;
u16 j,k;
TL1 = 0x18; //设置定时初始值
TH1 = 0xFC; //设置定时初始值
i++;
if(i==200)
{
i=0;
timer_flag=1;
}
if(cho!=0) //亮灭控制
{
j++;k++;
if(j==400)
{
j=0;
lm=1;
}
if(k==800)
{
k=0;
lm=0;
}
}
}
void nr_fuzhi() //根据显示模式给nr1~nr8赋值
{
nr1=Timer[2]/16; //时 十位
nr2=Timer[2]%16; //时 个位
nr3=17;
nr4=Timer[1]/16; //分 十位
nr5=Timer[1]%16; //分 个位
nr6=17;
nr7=Timer[0]/16; //秒 十位
nr8=Timer[0]%16; //秒 个位
if(lm==1)
{
switch(cho)
{
case 1:nr7=nr8=16;break; //秒灭
case 2:nr4=nr5=16;break; //分灭
case 3:nr1=nr2=16;break; //时灭
case 0:break;
}
}
}
#ifndef _Timer_h_
#define _Timer_h_
#include "stc15f2k60s2.h"
#include "key_4589.h"
#include "public.h"
#include "smg.h"
extern unsigned char Read_DS1302_adrr[];
//定义DS1302读操作的日历时钟存储器地址
extern unsigned char Write_DS1302_adrr[];
//定义DS1302写操作的日历时钟寄存器地址
extern u8 Timer[];
extern u8 timer_flag; //时间读取标志位
extern u8 cho; //秒,分,时 选取闪烁标志位
extern u8 read_t[];
void Timer0Init(void);
void timer0_server();
void Timer1Init(void);
void Timer1_server();
#endif
#include "ds1302.h"
#include "ds1302.h"
void Write_Ds1302(unsigned char temp)
{
unsigned char i;
for (i=0;i<8;i++)
{
SCK = 0;
SDA = temp&0x01;
temp>>=1;
SCK=1;
}
}
//
void Write_Ds1302_Byte( unsigned char address,unsigned char dat )
{
RST=0; _nop_();
SCK=0; _nop_();
RST=1; _nop_();
Write_Ds1302(address);
Write_Ds1302(dat);
RST=0;
}
//
unsigned char Read_Ds1302_Byte ( unsigned char address )
{
unsigned char i,temp=0x00;
RST=0; _nop_();
SCK=0; _nop_();
RST=1; _nop_();
Write_Ds1302(address);
for (i=0;i<8;i++)
{
SCK=0;
temp>>=1;
if(SDA)
temp|=0x80;
SCK=1;
}
RST=0; _nop_();
SCK=0; _nop_();
SCK=1; _nop_();
SDA=0; _nop_();
SDA=1; _nop_();
return (temp);
}
#ifndef _ds1302_h_
#define _ds1302_h_
#include "stc15f2k60s2.h"
#include "intrins.h"
sbit SCK=P1^7;
sbit SDA=P2^3;
sbit RST=P1^3;
unsigned char Read_DS1302_adrr[];
//定义DS1302读操作的日历时钟存储器地址
unsigned char Write_DS1302_adrr[];
//定义DS1302写操作的日历时钟寄存器地址
unsigned char Read_Ds1302_Byte ( unsigned char address );
void Write_Ds1302_Byte( unsigned char address,unsigned char dat );
void Write_Ds1302(unsigned char temp);
#endif
#include "smg.h"
#include "smg.h"
//数码管字库
/*
数组下标对应段码速查:
0_0 1_1 2_2 3_3 4_4 5_5
6_6 7_7 8_8 9_9 10_a
11_b 12_c 13_d 14_e 15_f
16_空 17_根线 18_H 19_P
20_0. 21_1. 22_2. 23_3. 24_4. 25_5.
26_6. 27_7. 28_8. 29_9. 30_u
*/
u8 code smgZK[31]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,
0x82,0xf8,0x80,0x90,0x88,
0x83,0xc6,0xa1,0x86,0x8e,
0xff,0xbf,0x89,0x8c,
0x40,0x79,0x24,0x30,0x19,0x12,
0x02,0x78,0x00,0x10,0xc1
};
void Delay250us() //@12.000MHz 必要的打印延时
{
unsigned char i, j;
i = 3;
j = 232;
do
{
while (--j);
} while (--i);
}
void smg_display(u8 nr1,nr2,nr3,nr4,nr5,nr6,nr7,nr8) //数码管打印函数 一次打印八个
{
u8 i;
i=0;
for(i=0;i<=8;i++)
{
inint_74hc573(6);
switch(i)
{
case 1:
{
P0=0X01;
inint_74hc573(7);
P0=smgZK[nr1];
Delay250us();
P0=0Xff;
break;
}
case 2:
{
P0=0X02;
inint_74hc573(7);
P0=smgZK[nr2];
Delay250us();
P0=0Xff;
break;
}
case 3:
{
P0=0X04;
inint_74hc573(7);
P0=smgZK[nr3];
Delay250us();
P0=0Xff;
break;
}
case 4:
{
P0=0X08;
inint_74hc573(7);
P0=smgZK[nr4];
Delay250us();
P0=0Xff;
break;
}
case 5:
{
P0=0X10;
inint_74hc573(7);
P0=smgZK[nr5];
Delay250us();
P0=0Xff;
break;
}
case 6:
{
P0=0X20;
inint_74hc573(7);
P0=smgZK[nr6];
Delay250us();
P0=0Xff;
break;
}
case 7:
{
P0=0X40;
inint_74hc573(7);
P0=smgZK[nr7];
Delay250us();
P0=0Xff;
break;
}
case 8:
{
P0=0X80;
inint_74hc573(7);
P0=smgZK[nr8];
Delay250us();
P0=0Xff;
break;
}
}
}
}
#ifndef _smg_h_
#define _smg_h_
#include "stc15f2k60s2.h"
#include "public.h"
void smg_display(u8 nr1,nr2,nr3,nr4,nr5,nr6,nr7,nr8); //数码管打印函数 一次打印八个
#endif
#include "key_4589.h"
#include "key_4589.h"
void Delay12ms() //@12.000MHz
{
unsigned char i, j;
i = 141;
j = 16;
do
{
while (--j);
} while (--i);
}
void key_scan_inint(u8 n)
{
switch(n)
{
case 1:X1=0;X2=1;Y1=1;Y2=1;break;
case 2:X1=1;X2=0;Y1=1;Y2=1;break;
}
}
u8 key_return()
{
u8 key_value;
key_value=0;
Delay12ms(); //消抖
key_scan_inint(1);
if(Y1==0)
{
while(Y1==0);key_value=4;
}
if(Y2==0)
{
while(Y2==0);key_value=8;
}
key_scan_inint(2);
if(Y1==0)
{
while(Y1==0);key_value=5;
}
if(Y2==0)
{
while(Y2==0);key_value=9;
}
return key_value;
}
#ifndef _key_4589_h_
#define _key_4589_h_
#include "stc15f2k60s2.h"
#include "public.h"
sbit X1=P3^3;
sbit X2=P3^2;
sbit Y1=P4^4;
sbit Y2=P4^2;
u8 key_return();
#endif