目的
使用DS1302芯片实时时钟功能,将时间读出,并在8位7段数码管上显示。
显示的格式为XX-XX-XX(年-月-日),然后刷屏0.5秒,再显示XX-XX-XX(时-分-秒)
硬件内容
结构图
图1
由图1所知,DS1302通过三条线和CPU连接。CE是数据传输控制线,开始数据传输时CE需置高电平,而芯片上电时候,CE需为低电平;I/O是数据传输线,数据的写入和读出都是从这条线上进行;SCLK是串行时钟线。
图2
由图2可知,通过I/O口写入或读出数据。写入的命令会通过命令控制逻辑来进一步实现对实时时钟的控制(写/读)
图3
图3为命令字结构,数据传输时第7位必须为1;第6为0表示对时钟/日历数据进行操作,1表示对RAM进行操作;第1-5位表示指定寄存器的,第0位写1/0分别表示进行读/写操作
图4
图4为RTC寄存器地址的定义,想要对时钟进行数据的写入和读出,就需要知道寄存器的每一位表示什么意思,而且需要知道写入和读出的指令。下面将对个别寄存器进行举例说明。
第一个为秒寄存器,读的指令是81h,写指令是80h。BIT7是时钟暂停位,置1时DS1302暂停,BIT4-6为秒的十位,BIT0-3为秒的个位.
第二个为时寄存器,读的指令是83h,写指令是82h。BIT7为12/24制的选择。
BIT5:当时钟为12制时,表示AM/PM,即上午和下午的选择;当时钟为24制时,和BIT4表示时的十位。
BIT0-3:时的个位。
图5
图5是数据传输的时序图。
数据输入:输入写命令字的 8个SCLK周期后 ,接下来的 8个SCLK周期的上升沿数据字节被输入,如 不慎发生, 多余的 SCLK 周期将被忽略,数据输入以位 0开始.
数据输出:输入读命令字的 8个SCLK周期后, 随后的 8个SCLK周期的下降沿,一个数据字节被输出。 注意第一个数据位的传送发生在命令字节被写完后的第一个下降沿.只要 CE保持高电平,若 不慎发生,多余的 SCLK周期会重新发送数据字节. 此操作允许连续不断的脉冲串模式读取能力.并且,I/O管脚在 SCLK的每个上升沿被置为三 态.数据输出从位 0开始
注意:时钟寄存器的内容为BCD码格式
软件内容
设计思路:
要对把时间显示出来,就需要对时钟芯片完成初始化,初值写入,数据读出。根据图5可知,不论是单字节写还是单字节读,都是在16个SCLK周期中完成的,其中前8个周期是命令的写入,是选择对某个寄存的读或写;而后8个周期是要写或读的数据。
再仔细看图5,就可以发现写是在时钟上升沿完成的;而读是在下降沿完成的。
所以,读取时间的流程为:初始化——>数据写入(命令+数据)——>数据读出(命令)
整体分为三个部分,即main(主函数),smg_dispaly(数码管显示函数),ds1302(时钟芯片函数)。
main:定义数组来存放从ds1302读出的数据,在主函数中完成16进制转BCD码的任务,再将存有BCD时间数据的数组传入数码管显示函数中即可。
smg_dispaly: 实现数据的动态显示。
ds1302:完成初始化任务,定义写函数和读函数,将读出的数据返回到主函数
因为读取出的数据也是 BCD 码,而 BCD 码即是 4 位表示一个十进制数,类似于一个 字节的十六进制数据的高 4 位和低 4 位一样,所以想要转成十进制的数据只需要分别对数据进行除16(得到十位)和对16取余(得到个位)即可
以上为流程图
代码
#include "public.h"
#include "ds1302.h"
#include "smg.h"
/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
u8 time[6],big_time[8],sml_time[8]; //定义存放数据的数组
u8 i;
ds1302_init(); //初值写入
while(1)
{
for(i=0;i<6;i++)
{
time[i]=ds1302_read(DS_RE_ORD[i]); //将时间数据读出
}
big_time[0]=time[0]/16;//年的十位
big_time[1]=time[0]%16;//年的个位
big_time[2]=10;//“-”
big_time[3]=time[1]/16;//月的十位
big_time[4]=time[1]%16;//月的个位
big_time[5]=10;
big_time[6]=time[2]/16;//日的十位
big_time[7]=time[2]%16;//日的个位
smg_display(big_time);//显示年月日
SEG=0xff;//清屏
delay_10us(50000);//延时5毫秒
big_time[0]=time[3]/16;//时的十位
big_time[1]=time[3]%16;//时的个位
big_time[2]=10;
big_time[3]=time[4]/16;//分的十位
big_time[4]=time[4]%16;//分的个位
big_time[5]=10;
big_time[6]=time[5]/16;//秒的十位
big_time[7]=time[5]%16;//秒的个位
smg_display(big_time);
SEG=0xff;
delay_10us(50000);
}
}
#ifndef _ds1302_H
#define _ds1302_H
#include "public.h"
//管脚定义
sbit CE=P1^0;
sbit I_O=P1^2;
sbit SCLK=P1^1;
//函数声明
extern u8 DS_RE_ORD[6];
void ds1302_init();
u8 ds1302_read(u8 ord);
#endif
#include "ds1302.h"
#include "intrins.h"
u8 DS_WE_ORD[6]={0x8c,0x88,0x86,0x84,0x82,0x80}; //年、月、日、时、分、秒的写入指令
u8 DS_WE_TIME[6]={0x22,0x09,0x06,0x09,0x51,0x00}; //初始时间数据22年09月06日09时51分00秒
u8 DS_RE_ORD[6]={0x8d,0x89,0x87,0x85,0x83,0x81}; //年、月、日、时、分、秒的读出指令
/*******************************************************************************
* 函 数 名 : ds1302_start()
* 函数功能 : 开始传输函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void ds1302_start()
{
CE=0; //数据传输开始,CE置0
_nop_();
SCLK=0; //CE置1,时钟引脚置0
_nop_();
CE=1;
_nop_();
}
/*******************************************************************************
* 函 数 名 : ds1302_write()
* 函数功能 : 初值设置函数,包括对年、月、日、时、分、秒的设置,即写入数据
* 输 入 : ord,dat
* 输 出 : 无
*******************************************************************************/
void ds1302_write(u8 ord,u8 dat)
{
u8 i=0;
ds1302_start(); //开始传输数据
for(i=0;i<8;i++)
{
I_O=ord&0x01; //取出命令字的第一位
ord>>=1; //左移一位
SCLK=1;
SCLK=0;
}
for(i=0;i<8;i++)
{
I_O=dat&0x01; //写入数据字的第一位
dat>>=1;
SCLK=1;
SCLK=0;
}
}
/*******************************************************************************
* 函 数 名 : ds1302_read()
* 函数功能 : 读取一位数据
* 输 入 : ord
* 输 出 : dat
*******************************************************************************/
u8 ds1302_read(u8 ord)
{
u8 i;
u8 dat_bit=0;
u8 dat_byte=0;
ds1302_start();
for(i=0;i<8;i++) //一次写一位,将命令写入
{
I_O=ord&0x01; //取出命令字的第一位
ord>>=1;
SCLK=0;
SCLK=1;
}
for(i=0;i<8;i++)
{
SCLK=1;
SCLK=0;
if(I_O==1) //如果是1,则左移i位;0则不管
dat_byte|=(0x01<<i);
}
return dat_byte;
}
/*******************************************************************************
* 函 数 名 : ds1302_init()
* 函数功能 : 初值写入
* 输 入 : ord
* 输 出 : dat
*******************************************************************************/
void ds1302_init()
{
u8 i=0;
ds1302_write(0x8e,0x00); //给写保护寄存器写入0,允许写入
for(i=0;i<6;i++)
{
ds1302_write(DS_WE_ORD[i],DS_WE_TIME[i]); //将初值写入
}
}
#ifndef _smg_H
#define _smg_H
#include "public.h"
#define SEG P2 //使用宏定义数码管段码口
#define DIS P3
//定义数码管位选信号控制脚
extern u8 gsmg_code[17];
void smg_display(u8 dat[]);
#endif
#include "smg.h"
//共阳极数码管显示0~F的段码数据
u8 gsmg_code[17]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
0x80,0x90,0xbf,0x83,0xc6,0xa1,0x86,0xbf};
//控制段位显示数据
u8 dis[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
/*******************************************************************************
* 函 数 名 : smg_display
* 函数功能 : 动态数码管显示
* 输 入 : dat:要显示的数据
* 输 出 : 无
*******************************************************************************/
void smg_display(u8 dat[])
{
u8 i;
for(i=0;i<8;i++)
{
SEG=gsmg_code[dat[i]];
DIS=dis[i];
delay_10us(7000);
}
}
#include "public.h"
/*******************************************************************************
* 函 数 名 : delay_10us
* 函数功能 : 延时函数,ten_us=1时,大约延时10us
* 输 入 : ten_us
* 输 出 : 无
*******************************************************************************/
void delay_10us(u16 ten_us)
{
while(ten_us--);
}
/*******************************************************************************
* 函 数 名 : delay_ms
* 函数功能 : ms延时函数,ms=1时,大约延时1ms
* 输 入 : ms:ms延时时间
* 输 出 : 无
*******************************************************************************/
void delay_ms(u16 ms)
{
u16 i,j;
for(i=ms;i>0;i--)
for(j=110;j>0;j--);
原理图