由于公司项目原因 用到了DS1302
其中时间是由时间戳转换成日期存到DS1302的 在网上查找过一些代码 再加上自己的一些理解 将代码提供出来
适用于 DS1302 的读写驱动 以及 时间戳转日期 日期转时间戳 DS1302是否掉电等问题。有需要的可以了解一下。
1.ds1302.c
#include "ds1302.h"
char PowerDown=0; //掉电标志 如果为 1 说明掉过电
extern volatile u32 system_time; //系统时间
struct DS1302DATA ds1302Data = {0,0,0,0,0,0,0};
u8 ascii_time[7] = {0}; //保存ascii格式数据
u8 bcd_time[7] = {0}; //保存bcd码数据
static u8 AsciiToBcd(u8 asciiData)
{
u8 bcdData = 0;
bcdData = (((asciiData/10)<<4)|((asciiData%10)));
return bcdData;
}
static u8 BcdToAscii(u8 bcdData)
{
u8 asciiData = 0;
asciiData = (((bcdData&0xf0)>>4)*10 + (bcdData&0x0f));
return asciiData;
}
//IO口初始化
void Ds1302_Gpio_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
//RST
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
GPIO_Init(GPIOD, &GPIO_InitStructure);
//CLK
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
GPIO_Init(GPIOD, &GPIO_InitStructure);
//IO
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
GPIO_Init(GPIOD, &GPIO_InitStructure);
}
//读取一个字节的时序
u8 Ds1302_ReadByte(void)
{
u8 i = 0, dat = 0;
DS1302_DAT_INPUT();
delay_us(5);
for(i = 0; i <8; i++)
{
dat >>= 1;
if(DS1302_DATIN == 1)dat |= 0x80;
DS1302_CLK = 1;
delay_us(2);
DS1302_CLK = 0;
delay_us(2);
}
return dat;
}
//写入一个字节的时序
void Ds1302_WriteByte(u8 dat)
{
u8 i = 0, data = dat;
DS1302_DAT_OUTPUT();
DS1302_CLK = 0;
delay_us(2);
for(i = 0; i < 8; i++)
{
DS1302_DATOUT = data&0x01;
delay_us(2);
DS1302_CLK = 1;
delay_us(2);
DS1302_CLK = 0;
data >>= 1;
}
}
//写入一个寄存器
void Ds1302_Write(u8 address,u8 dat)
{
DS1302_RST = 0;
DS1302_CLK = 0;
DS1302_RST = 1;
Ds1302_WriteByte(address);
Ds1302_WriteByte(dat);
DS1302_CLK = 1;
DS1302_RST = 0;
}
//单个写入时间
void Ds1302_Write_Time_Singel(u8 address,u8 dat)
{
Ds1302_Write(DS1302_CONTROL_REG,0x00); //取消写保护
Ds1302_Write(address,dat);
Ds1302_Write(DS1302_CONTROL_REG,0x80); //打开写保护
}
//一次完成所有时间更新
//start当前时钟运行还是停止
void Ds1302_Write_Time_All(u8 start)
{
Ds1302_Write(DS1302_CONTROL_REG,0x00); //取消写保护
Ds1302_Write(DS1302_SEC_REG,(AsciiToBcd(ds1302Data.sec)|start));
Ds1302_Write(DS1302_MIN_REG,AsciiToBcd(ds1302Data.min));
Ds1302_Write(DS1302_HR_REG,AsciiToBcd(ds1302Data.hour));
Ds1302_Write(DS1302_DATE_REG,AsciiToBcd(ds1302Data.day));
Ds1302_Write(DS1302_MONTH_REG,AsciiToBcd(ds1302Data.month));
Ds1302_Write(DS1302_DAY_REG,AsciiToBcd(ds1302Data.week));
Ds1302_Write(DS1302_YEAR_REG,AsciiToBcd(ds1302Data.year));
Ds1302_Write(DS1302_CONTROL_REG,0x80); //打开写保护
}
//读取一个字节
u8 Ds1302_Read(u8 address)
{
u8 data = 0;
DS1302_RST = 0;
DS1302_CLK = 0;
DS1302_RST = 1;
Ds1302_WriteByte(address|0x01); //读取地址需要与0x01相或,最低为变成1
data = Ds1302_ReadByte();
DS1302_CLK = 1;
DS1302_RST = 0;
return data;
}
//读取时间的时候默认让时间走起来
void Ds1302_Readtime(void)
{
ds1302Data.sec = BcdToAscii(Ds1302_Read(DS1302_SEC_REG)); //秒
ds1302Data.min = BcdToAscii(Ds1302_Read(DS1302_MIN_REG)); //分
ds1302Data.hour = BcdToAscii(Ds1302_Read(DS1302_HR_REG)); //小时
ds1302Data.day = BcdToAscii(Ds1302_Read(DS1302_DATE_REG)); //日
ds1302Data.month = BcdToAscii(Ds1302_Read(DS1302_MONTH_REG)); //月
ds1302Data.week = BcdToAscii(Ds1302_Read(DS1302_DAY_REG)); //星期几
ds1302Data.year = BcdToAscii(Ds1302_Read(DS1302_YEAR_REG)); //年
}
//判断DS1302是否掉过电
void Time_correct()
{
if(Ds1302_Read(DS1302_RAM_BASE)!=0x55) //掉过电
{
Ds1302_Readtime();
PowerDown=1;
}
// Ds1302_Write(DS1302_RAM_BASE,0x55); // RAM中掉电会丢失
}
//UNIX转为UTC 已进行时区转换 北京时间UTC+8
void xSeconds2Date(unsigned int seconds)
{
static unsigned int month[12]={
/*01月*/31,
/*02月*/28,
/*03月*/31,
/*04月*/30,
/*05月*/31,
/*06月*/30,
/*07月*/31,
/*08月*/31,
/*09月*/30,
/*10月*/31,
/*11月*/30,
/*12月*/31
};
unsigned int days;
unsigned short leap_y_count;
ds1302Data.sec=seconds%60;//获得秒
seconds/=60;
ds1302Data.min=seconds%60;//获得分
seconds+=8*60 ; //时区矫正 转为UTC+8 bylzs
seconds/=60;
ds1302Data.hour=seconds % 24;//获得时
days=seconds/24;//获得总天数
leap_y_count=(days + 365)/1461;//过去了多少个闰年(4年一闰)
if(((days + 366)%1461)==0)
{//闰年的最后1天
ds1302Data.year=1970+(days/366);//获得年
ds1302Data.month=12; //调整月
ds1302Data.day=31;
return;
}
days-=leap_y_count;
ds1302Data.year=1970+(days/365); //获得年
days%=365; //今年的第几天
days=01+days; //1日开始
if((ds1302Data.year%4)==0)
{
if(days>60)--days; //闰年调整
else
{
if(days == 60)
{
ds1302Data.month=2;
ds1302Data.day=29;
return;
}
}
}
for(ds1302Data.month= 0;month[ds1302Data.month]<days;ds1302Data.month++)
{
days-=month[ds1302Data.month];
}
++ds1302Data.month; //调整月
ds1302Data.day=days; //获得日
}
typedef struct t_xtime {
int year; int month; int day;
int hour; int minute; int second;
} _xtime ;
#define xMINUTE (60) //1分的秒数
#define xHOUR (60*xMINUTE) //1小时的秒数
#define xDAY (24*xHOUR) //1天的秒数
#define xYEAR (365*xDAY) //1年的秒数
unsigned int xDate2Seconds()
{
static unsigned int month[12]={
/*01月*/xDAY*(0),
/*02月*/xDAY*(31),
/*03月*/xDAY*(31+28),
/*04月*/xDAY*(31+28+31),
/*05月*/xDAY*(31+28+31+30),
/*06月*/xDAY*(31+28+31+30+31),
/*07月*/xDAY*(31+28+31+30+31+30),
/*08月*/xDAY*(31+28+31+30+31+30+31),
/*09月*/xDAY*(31+28+31+30+31+30+31+31),
/*10月*/xDAY*(31+28+31+30+31+30+31+31+30),
/*11月*/xDAY*(31+28+31+30+31+30+31+31+30+31),
/*12月*/xDAY*(31+28+31+30+31+30+31+31+30+31+30)
};
unsigned int seconds = 0;
unsigned int year = 0;
year=ds1302Data.year-1970; //不考虑2100年千年虫问题
seconds = xYEAR*year + xDAY*((year+1)/4); //前几年过去的秒数
seconds += month[ds1302Data.month-1]; //加上今年本月过去的秒数
if( (ds1302Data.month > 2) && (((year+2)%4)==0) )//2008年为闰年
seconds += xDAY; //闰年加1天秒数
seconds += xDAY*(ds1302Data.day-1); //加上本天过去的秒数
seconds += xHOUR*ds1302Data.hour; //加上本小时过去的秒数
seconds += xMINUTE*ds1302Data.min; //加上本分钟过去的秒数
seconds += ds1302Data.sec; //加上当前秒数<br> seconds -= 8 * xHOUR;
return seconds;
}
//读取日期转时间戳 本地时间
void Read_TimeStamp(void)
{
Ds1302_Readtime();
system_time=xDate2Seconds();
}
//时间戳转日期 写入DS1302
void Write_TimeStamp(unsigned int secon)
{
xSeconds2Date(secon);
Ds1302_Write_Time_All(0);
PowerDown=0;
Ds1302_Write(DS1302_RAM_BASE,0x55); // RAM中掉电会丢失
}
2.ds1302.h
#ifndef __DS1302_H
#define __DS1302_H
#include "stm32f10x.h"
#include "delay.h"
extern u8 ascii_time[7]; //保存ascii格式数据
extern u8 bcd_time[7]; //保存bcd码数据
typedef struct DS1302DATA
{
u8 year; //年
u8 month; //月
u8 day; //日
u8 hour; //时
u8 min; //分
u8 sec; //秒
u8 week; //周
}DS1302DATA;
extern struct DS1302DATA ds1302Data;
#define DS1302_RST PDout(12)
#define DS1302_CLK PDout(10)
#define DS1302_DATIN PDin(11)
#define DS1302_DATOUT PDout(11)
#define DS1302_DAT_INPUT() {GPIOD->CRH &= 0XFFFF0FFF;GPIOD->CRH|=8<<12;}
#define DS1302_DAT_OUTPUT() {GPIOD->CRH &= 0XFFFF0FFF;GPIOD->CRH|=3<<12;}
//芯片寄存器地址定义 定义的写地址,读需要+1
#define DS1302_SEC_REG 0x80 //秒数据地址
#define DS1302_MIN_REG 0x82 //分数据地址
#define DS1302_HR_REG 0x84 //时数据地址
#define DS1302_DATE_REG 0x86 //日数据地址
#define DS1302_MONTH_REG 0x88 //月数据地址
#define DS1302_DAY_REG 0x8a //星期几数据地址
#define DS1302_YEAR_REG 0x8c //年数据地址
#define DS1302_CONTROL_REG 0x8e //写保护寄存器地址
#define DS1302_CHARGER_REG 0x90 //涓流充电寄存器
#define DS1302_CLKBURST_REG 0xbe //脉冲串寄存器
#define DS1302_RAM_BASE 0X30 //RAM基础地址
#define CLOCKSTOP 0X80
#define CLOCKSTART 0X00
void Ds1302_Gpio_Init(void);
void Ds1302_Write_Time_All(u8 start);
void Ds1302_Readtime(void);
void Time_correct();
unsigned int xDate2Seconds();
void xSeconds2Date(unsigned int seconds);
void Read_TimeStamp(void);
void Write_TimeStamp(unsigned int secon);
#endif
需要注意的是 DS1302的秒寄存器需要最高位置0,DS1302才能正常工作。
另外DS1302预留有RAM 可以通过读取可以上电判断是否DS1302掉过电
,掉电则需要时钟更新 (掉电后时间不准确)。