一、DS1302介绍
1、SPI数字接口访问:3线或4线,同步,主从,串行。
2、内部存着一个时间点信息,可以读写,上电自动走表。
二、原理图分析和接线
通过观察原理图我们知道,这个开发板将DS1302的3根通信线,作为J3引了出来,我们只需要使用3根杜邦线,将这3根线和单片机的引脚连接即可。至于使用那个引脚自己选择即可。
三、数据手册分析
我们通过看数据手册得出下面几点:该芯片可以实时计算年、月、日、时、分、秒、星期,并有闰年调节功能,工作在宽电压,读写可以单字节也可以多字节。
四、时序图分析
(1)横轴代表时间,纵轴是同一时间点各个通信线上的状态,从左往右3条线一起看,不能分开来分析,首先变化的是CE从低电平变成了高电平,此时SCLK和I/O线上的点平变化才有意义,不然芯片都工作不了。其次在SCLK从低电平变到高电平之前,I/O线上的数据必须准备好,并且持续一段时间,等SCLK从低电平到高电平变化完之后,才可以改变,这样数据才能正确传输。
(2)从静态与动态2个角度去看
(3)大小端:一个字节发出去,高位在前还是低位在前。
(4)上升沿读取,下降沿写入、注意SCLK工作频率
五、编写读和写函数
有了下面这两个函数,我们就可以对DS1302进行读和写的操作了。需要注意的是DS1302有写保护功能,我们再给他写入数据之前,一定要先关闭他的写保护功能。
uchar ds1302_read(uchar command)
{
uchar i = 0, date = 0, date1 = 0;
CE = 0; //先将信号线都进行复位
_nop_(); //每次操作完成信号线之后,加一段延时等待信号稳定
SCLK = 0;
_nop_();
CE = 1; //将CE线拉高,使能芯片
_nop_();
for (i=0; i<8; i++)
{
IO = command & 0x01; //取出最低位
_nop_();
SCLK = 1; //产生上升沿,将数据发出去
_nop_();
command >>= 1; //将已经发出的最低位移出去,准备发下一位
SCLK = 0; //为下一次产生上升沿做准备
_nop_();
}
for (i=0; i<8; i++)
{
date1 = IO; //将IO线上的电平读出来,保存一下,因为IO这个变量不能直接进行移位
date = (date >> 1) | (date1 << 7); //将先接收到的数据放到最高位,然后在进行右移
_nop_(); //这样最后先接收到的就在最低位,后接收到的就在最高位
SCLK = 1; //方便下一次产生下降沿
_nop_();
SCLK = 0; //产生下降沿,这样数据就准备好了,我们就可以进行读取了
_nop_();
}
CE = 0; //将芯片使能关闭
_nop_();
return date;
}
void ds1302_write(uchar command, uchar date)
{
uchar i = 0, dat = 0;
CE = 0;
_nop_();
SCLK = 0;
_nop_();
CE = 1;
_nop_();
for (i=0; i<8; i++)
{
dat = command & 0x01;
IO = dat;
command >>= 1;
_nop_();
SCLK = 1;
_nop_();
SCLK = 0;
_nop_();
}
for (i=0; i<8; i++)
{
dat = date & 0x01;
IO = dat;
date >>= 1;
_nop_();
SCLK = 1;
_nop_();
SCLK = 0;
_nop_();
}
CE = 0;
_nop_();
}
六、时间的写入和读取
我们需要注意的是:在这里我们的时间写入包括读取通过串口打印,都是使用的是十六进制的格式进行的,因为DS1302给我们提供的时间格式是BCD编码的,它本质上是十六进制,但是看起来像十进制的一种编码方式。因此我们在使用串口接收的时候一定要调整到HEX接收模式,不然的话收到的就是一些乱码。
void main(void)
{
uchar dat = 0, i = 0;
uchar code write[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};
uchar code read[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d};
uchar code settime[7] = {0x50, 0x59, 0x23, 0x16, 0x10, 0x03, 0x24};
uart_init();
ds1302_write(0x8E, 0x00); //关闭写保护功能
for (i=0; i<7; i++) //设置时间
{
ds1302_write(write[i], settime[i]);
}
ds1302_write(0x8E, 0x80); //打开写保护功能
while(1)
{
for (i=0; i<7; i++) //读取时间,注意串口接收一定要设置为HEX接收
{
dat = ds1302_read(read[i]);
uart_send_byte(dat);
}
delay1s();
}
}
七、添加16进制到10进制的转换
针对上面的问题,为了使程序的输入和输出用起来更符合常理,同时在时间设置时可以使用10进制,输出时间可以使用字符输出打印,因此就加了这一功能。
//初始化函数,可以直接设置时间
void ds1302_init(uchar *dat)
{
uchar i = 0;
ds1302_write(0x8E, 0x00); //关闭写保护功能
for (i=0; i<7; i++)
{
dat[i] = shi_to_bcd(dat[i]);
}
for (i=0; i<7; i++)
{
ds1302_write(write[i], dat[i]);
}
ds1302_write(0x8E, 0x80); //打开写保护功能
}
//读取时间函数,将读取到的时间打印在串口上
void read_time(void)
{
uchar i = 0, dat = 0;
for (i=0; i<7; i++)
{
dat = ds1302_read(read[i]);
dat = bcd_to_shi(dat);
uart_send_num(dat);
uart_send_byte('-');
}
delay1s();
}
//BCD编码转换10进制
uchar bcd_to_shi(uchar dat)
{
uchar date = 0;
date = (dat & 0x0f) + (dat >> 4) * 10;
return date;
}
//10进制转换BCD编码
uchar shi_to_bcd(uchar dat)
{
uchar date;
date = ((dat / 10)<< 4) + (dat % 10);
return date;
}