刚刚写完DS3231时钟芯片的驱动程序。这里开源出来供大家使用。需要的自取。
先简单介绍下这个芯片,然后直接给驱动代码以及示例代码。
DS3231简介
简介
DS3231是一款低成本、高精度I2C实时时钟(RTC),具有集成的温补晶体振荡器(TCXO)和晶体。该器件包含电池输入端,断开主电源时认可保持精确的计时。其提供年、月、日、星期、时、分、秒信息,提供有效期到2100年的闰年补偿。
其带有两个提供不同精度的日历闹钟,可在指定时刻在引脚上产生中断信号。
支持快速(400kHz)I2C接口通讯。
时钟精度在0℃到+40℃为±2ppm,即一个月可能偏离5s多;在-40℃到+85℃为±3.5ppm。
除了时钟外,还提供精度为±3℃的数字温度传感器输出,内部会使用这个温度传感器自动进行补偿以保证时间精度。
典型工作电路
框图
寄存器
DS3231提供如下寄存器:
与DS3231的控制和交互就是通过存取这几个寄存器来实现的,驱动代码中提供了所有寄存器的结构体以及对寄存器的存取函数。
具体寄存器的意义请自己参照数据手册。
与芯片的I2C通讯
I2C通讯的基本原理可以见我的上一个文章https://blog.csdn.net/lin_strong/article/details/80259571,翻译的MC9S12XEP100的数据手册,里头有对I2C通讯的详细描述。
DS3231的主叫地址固定为0b1101000,其内部存在一个寄存器指针,指向下一次读/写的寄存器,每次读/写一次后指针都会自增1,到最后一个寄存器后再指向第一个寄存器。
进行写操作时第一个数据字节会对指针值进行修改。
驱动中提供的函数即封装了这个过程。
代码及测试
驱动代码
DS3231Driver.h
/*
*******************************************************************************************
*
*
* DS3231 DRIVER MODULE
* DS3231驱动模块
*
* File : DS3231Driver.h
* By : Lin Shijun(http://blog.csdn.net/lin_strong)
* Date: 2018/05/14
* version: V1.0
* History: 2018/05/14 V1.0 the prototype
*********************************************************************************************
*/
#ifndef DS3231_DRIVER_H
#define DS3231_DRIVER_H
/*
********************************************************************************************
* MISCELLANEOUS
********************************************************************************************
*/
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
/*
******************************************************************************************
* CONSTANT
******************************************************************************************
*/
// address of register
typedef enum{
RegAddr_Sec, // Seconds 00-59
RegAddr_Min, // Minutes 00-59
RegAddr_Hour, // Hours 1–12 + AM/PM 00–23
RegAddr_Day, // Day 1 - 7
RegAddr_Date, // Data 01-31
RegAddr_CMon, // Century and month Century + 01–12
RegAddr_Year, // Year 00 - 99
RegAddr_Sec_A1, // Alarm 1 Seconds 00-59
RegAddr_Min_A1, // Alarm 1 Minutes 00-59
RegAddr_Hour_A1,// Alarm 1 Hours 1–12 + AM/PM 00–23
RegAddr_Da_A1, // Alarm 1 Day/Date 1 - 7 / 1 – 31
RegAddr_Min_A2, // Alarm 2 Minutes 00-59
RegAddr_Hour_A2,// Alarm 2 Hours 1–12 + AM/PM 00–23
RegAddr_Da_A2, // Alarm 2 Day/Date 1 - 7 / 1 – 31
RegAddr_Control,// Control
RegAddr_CtlStat,// Control/Status
RegAddr_AgOfset,// Aging offset
RegAddr_TempMSB,// MSB of Temp
RegAddr_TempLSB,// LSB of Temp
}DS3231REG_ADDR;
#define DS3231REG_ADDR_MAX RegAddr_TempLSB
/*
*******************************************************************************************
* CONFIGURE 主配置
*******************************************************************************************
*/
#define DS3231_CALLADDR 0b1101000 // DS32331的主叫地址
/*
****************************************************************************************
* ERROR CODES
****************************************************************************************
*/
#define DS3231_ERR_NULL 0 // if success
#define DS3231_ERR_COMM 1 // any error in communication
#define DS3231_ERR_REG 2 // wrong register
#define DS3231_ERR_ARG 3 // if wrong argument
/*
******************************************************************************************
* TYPE DEFINE
******************************************************************************************
*/
// struct of DS3231 register
typedef struct {
unsigned int sec_sd: 4; // ones digit of second 秒的个位
unsigned int sec_td: 3; // tens digit of second 秒的十位
unsigned int : 1;
} REG_SEC;
typedef struct {
unsigned int min_sd: 4; // ones digit of minute 分的个位
unsigned int min_td: 3; // tens digit of minute 分的十位
unsigned int : 1;
} REG_MIN;
typedef union {
struct{
unsigned int hour_sd:4; // ones digit of hour 分的个位
unsigned int hour_td:1; // tens digit of hour 分的个位
unsigned int isPM :1; // whether is pm
unsigned int is12sys:1; // whether is 12 hours system.
unsigned int :1;
}SYS12;
struct{
unsigned int hour_sd:4; // ones digit of hour 分的个位
unsigned int hour_td:2; // tens digit of hour 分的个位
unsigned int is12sys:1; // whether is 12 hours system.
unsigned int :1;
}SYS24;
} REG_HOUR;
typedef struct {
unsigned int day : 3; // the day of the week
unsigned int : 5;
} REG_DAY;
typedef struct {
unsigned int date_sd: 4; // ones digit of second 秒的个位
unsigned int date_td: 2; // tens digit of second 秒的十位
unsigned int : 2;
} REG_DATE;
typedef struct {
unsigned int mon_sd: 4; // ones digit of month 月的个位
unsigned int mon_td: 1; // tens digit of month 月的个位
unsigned int : 2;
unsigned int century:1; // hundreds digit of year
} REG_CMON;
typedef struct {
unsigned int year_sd: 4; // ones digit of year 月的个位
unsigned int year_td: 4; // tens digit of year 月的个位
} REG_YEAR;
typedef struct {
unsigned int sec_sd: 4; // ones digit of second 秒的个位
unsigned int sec_td: 3; // tens digit of second 秒的十位
unsigned int A1M1 : 1;
} REG_SEC_A1;
typedef struct {
unsigned int min_sd: 4; // ones digit of minute 分的个位
unsigned int min_td: 3; // tens digit of minute 分的十位
unsigned int A1M2 : 1;
} REG_MIN_A1;
typedef union {
struct{
unsigned int hour_sd:4; // ones digit of hour 分的个位
unsigned int hour_td:1; // tens digit of hour 分的个位
unsigned int isPM :1; // whether is pm
unsigned int is12sys:1; // whether is 12 hours system.
unsigned int A1M3 :1;
}SYS12;
struct{
unsigned int hour_sd:4; // ones digit of hour 分的个位
unsigned int hour_td:2; // tens digit of hour 分的个位
unsigned int is12sys:1; // whether is 12 hours system.
unsigned int A1M3 :1;
}SYS24;
} REG_HOUR_A1;
typedef union {
struct{
unsigned int day : 4; // the day of the week
unsigned int : 2;
unsigned int isDY : 1; // day selected
unsigned int A1M4 : 1;
}DAY;
struct{
unsigned int date_sd: 4; // ones digit of date
unsigned int date_td: 2; // tens digit of date
unsigned int isDY : 1; // day selected
unsigned int A1M4 : 1;
}DATE;
} REG_DA_A1;
typedef struct {
unsigned int min_sd: 4; // ones digit of minute 分的个位
unsigned int min_td: 3; // tens digit of minute 分的十位
unsigned int A2M2 : 1;
} REG_MIN_A2;
typedef union {
struct{
unsigned int hour_sd:4; // ones digit of hour 分的个位
unsigned int hour_td:1; // tens digit of hour 分的个位
unsigned int isPM :1; // whether is pm
unsigned int is12sys:1; // whether is 12 hours system.
unsigned int A2M3 :1;
}SYS12;
struct{
unsigned int hour_sd:4; // ones digit of hour 分的个位
unsigned int hour_td:2; // tens digit of hour 分的个位
unsigned int is12sys:1; // whether is 12 hours system.
unsigned int A2M3 :1;
}SYS24;
} REG_HOUR_A2;
typedef union {
struct{
unsigned int day : 4; // the day of the week
unsigned int : 2;
unsigned int isDY : 1; // day selected
unsigned int A2M4 : 1;
}DAY;
struct{
unsigned int date_sd: 4; // ones digit of date
unsigned int date_td: 2; // tens digit of date
unsigned int isDY : 1; // day selected
unsigned int A2M4 : 1;
}DATE;
} REG_DA_A2;
typedef struct{
unsigned int A1IE : 1;
unsigned int A2IE : 1;
unsigned int INTCN : 1;
unsigned int RS1 : 1;
unsigned int RS2 : 1;
unsigned int CONV : 1;
unsigned int BBSQW : 1;
unsigned int EOSC : 1;
} REG_CONTROL;
typedef struct{
unsigned int A1IF : 1;
unsigned int A2IF : 1;
unsigned int BSY : 1;
unsigned int EN32kHz: 1;
unsigned int : 3;
unsigned int OSF : 1;
} REG_CTLSTAT;
typedef struct{
unsigned int DATA : 7;
unsigned int SIGN : 1;
} REG_AGOFSET;
typedef struct{
char DATA;
} REG_TEMPMSB;
typedef struct{
unsigned int : 6;
unsigned int DATA : 2;
} REG_TEMPLSB;
// type of module
typedef struct {
REG_SEC sec;
REG_MIN min;
REG_HOUR hour;
REG_DAY day;
REG_DATE date;
REG_CMON cmon;
REG_YEAR year;
} TIME_STRUCT,*pTIME_STRUCT;
typedef struct{
REG_TEMPMSB integral; // integral part
REG_TEMPLSB fractional; // fractional part
} TEMP_STRUCT,*pTEMP_STRUCT;
typedef struct{
unsigned int errcode : 4; // see DS3231_ERR_XXX
unsigned int commerr : 4; // hold the errcode by user defined iic functions.
} DS3231_ERRCODE;
/*
************************************************************************************
* FUNCTION PROTOTYPES 函数原型
************************************************************************************
*/
unsigned char DS3231_Init(void);
DS3231_ERRCODE DS3231_GetReg(DS3231REG_ADDR reg,unsigned char *rst);
DS3231_ERRCODE DS3231_SetReg(DS3231REG_ADDR reg,unsigned char value);
DS3231_ERRCODE DS3231_GetRegs(DS3231REG_ADDR reg,unsigned char *pBuf,unsigned short len);
DS3231_ERRCODE DS3231_SetRegs(DS3231REG_ADDR reg,unsigned char *pBuf,unsigned short len);
DS3231_ERRCODE DS3231_SetTime(pTIME_STRUCT val);
DS3231_ERRCODE DS3231_GetTime(pTIME_STRUCT rst);
DS3231_ERRCODE DS3231_GetTemp(pTEMP_STRUCT rst);
// 用户需要实现这几个函数以使模块能与DS3231进行IIC通讯
// Arguments: calAddr 7bits calling address
// pBuf pointer to the send/recv buffer
// len length of datas to be send/recv
// rst pointer to the return value
// val the byte to send
// return : 0 if success
// 1 - 15 errcode of IIC communication.
// meaning of errcode of these functions should be exactly same.
extern unsigned char IIC_ReadBurst(unsigned char calAddr,unsigned char *pBuf,unsigned short len);
extern unsigned char IIC_SendBurst(unsigned char calAddr,unsigned char *pBuf,unsigned short len);
extern unsigned char IIC_ReadByte(unsigned char calAddr,unsigned char *rst);
extern unsigned char IIC_SendByte(unsigned char calAddr,unsigned char val);
/*
************************************************************************************
* ERROR CHECK 错误检查
************************************************************************************
*/
#endif // of DS3231_DRIVER_H
DS3231Driver.c
/*
*******************************************************************************************
*
*
* DS3231 DRIVER MODULE
* DS3231驱动模块
*
* File : DS3231Driver.c
* By : Lin Shijun(http://blog.csdn.net/lin_strong)
* Date: 2018/05/14
* version: V1.0
* History: 2018/05/14 V1.0 the prototype
*********************************************************************************************
*/
/*
*********************************************************************************************
* INCLUDES
*********************************************************************************************
*/
#include <string.h>
#include "DS3231Driver.h"
/*
*********************************************************************************************
* LOCAL FUNCTION
*********************************************************************************************
*/
#define regCheck() if(reg > DS3231REG_ADDR_MAX){err.errcode = DS3231_ERR_REG; return err;}
#define lenCheck() if(len > (DS3231REG_ADDR_MAX + 1) || len == 0){err.errcode = DS3231_ERR_ARG; return err;}
/*
*********************************************************************************************
* LOCAL VARIABLE
*********************************************************************************************
*/
// 发送缓存
static unsigned char Sendbuf[DS3231REG_ADDR_MAX + 2];
/*
*********************************************************************************************
* DS3231_Init()
*
* Description : Initialize DS3231 support hardware(marco style). 初始化DS3231硬件
*
* Arguments :
*
* Return : DS3231_ERR_NULL if success.
*
* Note(s) :
*********************************************************************************************
*/
unsigned char DS3231_Init(void){
return DS3231_ERR_NULL;
}
/*
*********************************************************************************************************
* DS3231_getReg()
*
* Description : get a register value of DS3231.
*
* Arguments : reg the register to get value.
* rst return value of result.
*
* Return : .errcode:
* DS3231_ERR_NULL if success.
* DS3231_ERR_COMM if error.
* DS3231_ERR_REG if unknown register.
* .commerr:
* errcode returned by user defined IIC functions if DS3231_ERR_COMM.
*
* Note:
*********************************************************************************************************
*/
DS3231_ERRCODE DS3231_GetReg(DS3231REG_ADDR reg,unsigned char *rst){
DS3231_ERRCODE err = {0};
unsigned char commerr;
regCheck();
if((commerr = IIC_SendByte(DS3231_CALLADDR,(unsigned char)reg)) == 0 && // 写入寄存器指针
(commerr = IIC_ReadByte(DS3231_CALLADDR,rst)) == 0){ // 然后读取一个字节
err.errcode = DS3231_ERR_NULL;
}else{
err.errcode = DS3231_ERR_COMM;
}
err.commerr = commerr;
return err;
}
/*
*********************************************************************************************************
* DS3231_SetReg()
*
* Description : set a register of DS3231.
*
* Arguments : reg the register to get value.
* rst return value of result.
*
* Return : .errcode:
* DS3231_ERR_NULL if success.
* DS3231_ERR_COMM if error.
* DS3231_ERR_REG if unknown register.
* .commerr:
* errcode returned by user defined IIC functions if DS3231_ERR_COMM.
* Note:
*********************************************************************************************************
*/
DS3231_ERRCODE DS3231_SetReg(DS3231REG_ADDR reg,unsigned char value){
DS3231_ERRCODE err = {0};
unsigned char commerr;
regCheck();
Sendbuf[0] = (unsigned char)reg;
Sendbuf[1] = value;
if((commerr = IIC_SendBurst(DS3231_CALLADDR,Sendbuf,2)) == 0)
err.errcode = DS3231_ERR_NULL;
else
err.errcode = DS3231_ERR_COMM;
err.commerr = commerr;
return err;
}
/*
*********************************************************************************************************
* DS3231_GetRegs()
*
* Description : get sereval registers' value of DS3231.
*
* Arguments : reg the first register to get value.
* pBuf return value of register.
* len how many registers to get value (1 - (DS3231REG_ADDR_MAX + 1))
*
* Return : .errcode:
* DS3231_ERR_NULL if success.
* DS3231_ERR_COMM if error.
* DS3231_ERR_REG if unknown register.
* DS3231_ERR_ARG if len out of range.
* .commerr:
* errcode returned by user defined IIC functions if DS3231_ERR_COMM.
* Note:
*********************************************************************************************************
*/
DS3231_ERRCODE DS3231_GetRegs(DS3231REG_ADDR reg,unsigned char *pBuf,unsigned short len){
DS3231_ERRCODE err = {0};
unsigned char commerr;
regCheck();
lenCheck();
if((commerr = IIC_SendByte(DS3231_CALLADDR,(unsigned char)reg)) == 0 && // 写入寄存器指针
(commerr = IIC_ReadBurst(DS3231_CALLADDR,pBuf,len)) == 0){ // 然后读取一个字节
err.errcode = DS3231_ERR_NULL;
}else{
err.errcode = DS3231_ERR_COMM;
}
err.commerr = commerr;
return err;
}
/*
*********************************************************************************************************
* DS3231_SetRegs()
*
* Description : set sereval registers' value of DS3231.
*
* Arguments : reg the first register to set value.
* pBuf point to the value of registers to set.
* len how many registers to set value (1 - (DS3231REG_ADDR_MAX + 1))
*
* Return : .errcode:
* DS3231_ERR_NULL if success.
* DS3231_ERR_COMM if error.
* DS3231_ERR_REG if unknown register
* DS3231_ERR_ARG if len out of range.
* .commerr:
* errcode returned by user defined IIC functions if DS3231_ERR_COMM.
*
* Note:
*********************************************************************************************************
*/
DS3231_ERRCODE DS3231_SetRegs(DS3231REG_ADDR reg,unsigned char *pBuf,unsigned short len){
DS3231_ERRCODE err = {0};
unsigned char commerr;
regCheck();
lenCheck();
Sendbuf[0] = (unsigned char)reg;
memcpy(Sendbuf+1,pBuf,len);
if((commerr = IIC_SendBurst(DS3231_CALLADDR,Sendbuf,len + 1)) == 0){ // 写入寄存器指针
err.errcode = DS3231_ERR_NULL;
}else{
err.errcode = DS3231_ERR_COMM;
}
err.commerr = commerr;
return err;
}
/*
*********************************************************************************************************
* DS3231_SetTime()
*
* Description : set time of DS3231.
*
* Arguments : val the time to set
*
* Return : .errcode:
* DS3231_ERR_NULL if success.
* DS3231_ERR_COMM if error.
* .commerr:
* errcode returned by user defined IIC functions if DS3231_ERR_COMM.
* Note:
*********************************************************************************************************
*/
DS3231_ERRCODE DS3231_SetTime(pTIME_STRUCT val){
return DS3231_SetRegs(RegAddr_Sec,(unsigned char *)val,sizeof(TIME_STRUCT));
}
/*
*********************************************************************************************************
* DS3231_GetTime()
*
* Description : get time of DS3231.
*
* Arguments : rst the time result.
*
* Return : .errcode:
* DS3231_ERR_NULL if success.
* DS3231_ERR_COMM if error.
* .commerr:
* errcode returned by user defined IIC functions if DS3231_ERR_COMM.
* Note:
*********************************************************************************************************
*/
DS3231_ERRCODE DS3231_GetTime(pTIME_STRUCT rst){
return DS3231_GetRegs(RegAddr_Sec,(unsigned char *)rst,sizeof(TIME_STRUCT));
}
/*
*********************************************************************************************************
* DS3231_GetTemp()
*
* Description : get temperature measured by DS3231.
*
* Arguments : rst return the temperature measured.
* = (rst->integral.DATA).(rst->fractional.DATA * 0.25)
* for example:
* if rst->integral.DATA = -4, rst->fractional.DATA = 3
* then temperature = -4.75 C
* if rst->integral.DATA = 25, rst->fractional.DATA = 1
* then temperature = +25.25 C
*
* Return : .errcode:
* DS3231_ERR_NULL if success.
* DS3231_ERR_COMM if error.
* .commerr:
* errcode returned by user defined IIC functions if DS3231_ERR_COMM.
* Note : 1. DS3231 will measure temperature every 64 seconds once powered
* by Vcc or once communicated when powerd by battery.
* 2. you can also start the convertion by set CONV in Control
* register.
*********************************************************************************************************
*/
DS3231_ERRCODE DS3231_GetTemp(pTEMP_STRUCT rst){
return DS3231_GetRegs(RegAddr_TempMSB,(unsigned char *)rst,sizeof(TEMP_STRUCT));
}
DS3231_G/SetReg(s)对DS3231寄存器读写操作进行封装。
模块要求用户按照要求提供错误码风格的IIC通讯函数,并在发生通讯错误时把用户函数的错误码一同返回。
由于时间和温度的使用较频繁,提供了专门的读写函数。
时间转换助手
DS3231TimeHelper.h
/*
*******************************************************************************************
*
* DS3231 TIME HELPER MODULE
*
* File : DS3231TimeHelper.h
* By : Lin Shijun(https://blog.csdn.net/lin_strong)
* Date: 2019/09/26
* version: V1.0
* History:
* note : the convert interfaces for DS3231 time.
*********************************************************************************************
*/
#ifndef _DS3231TimeHelper_H
#define _DS3231TimeHelper_H
#include "DS3231Driver.h"
#include "TimeExt.h"
#include "common.h"
// only support year from 2000 to 2199
#define DS3231_BASEYEAR 2000
void DS3231TimeToSystemTime(struct tm *dst, const TIME_STRUCT *src);
void SystemTimeToDS3231Time(TIME_STRUCT *dst, const struct tm *src);
BOOL DS3231TimeValid(const TIME_STRUCT *time);
#endif
其中TimeExt.h是引用的我自己写的time标准库实现:
https://blog.csdn.net/lin_strong/article/details/102619616
只是为了节省空间直接用了里头的
const int16_t TimeExt_lpdays[] = {-1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
const int16_t TimeExt_days[] = {-1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364};
如果不需要使用这个实现的话,就把
#include "TimeExt.h"
改为
#include <time.h>
然后在模块内部加入上面那两个常量数组就好了。
DS3231TimeHelper.c
/*
*******************************************************************************************
*
* DS3231 TIME HELPER MODULE
*
* File : DS3231TimeHelper.c
* By : Lin Shijun(https://blog.csdn.net/lin_strong)
* Date: 2019/09/26
* version: V1.0
* History:
* note : the convert interfaces for DS3231 time.
*********************************************************************************************
*/
#include "DS3231TimeHelper.h"
#include <stdint.h>
#pragma MESSAGE DISABLE C2705
#pragma MESSAGE DISABLE C12056
void DS3231TimeToSystemTime(struct tm *dst, const TIME_STRUCT *src){
uint16_t tmp;
const int16_t * mdays;
if(dst == NULL || src == NULL)
return;
dst->tm_sec = src->sec.sec_sd + src->sec.sec_td * 10;
dst->tm_min = src->min.min_sd + src->min.min_td * 10;
if(src->hour.SYS12.is12sys){
tmp = src->hour.SYS12.hour_sd + src->hour.SYS12.hour_td * 10;
tmp = (tmp == 12)? 0: tmp; // 12:30 am = 00:30(24h); 12:30pm = 12:30(24h)
dst->tm_hour = (src->hour.SYS12.isPM)? tmp + 12: tmp;
}else{
dst->tm_hour = src->hour.SYS24.hour_sd + src->hour.SYS24.hour_td * 10;
}
dst->tm_mday = src->date.date_sd + src->date.date_td * 10;
dst->tm_mon = src->cmon.mon_sd + src->cmon.mon_td * 10 - 1;
dst->tm_year = src->year.year_sd + src->year.year_td * 10 + (DS3231_BASEYEAR - 1900) + src->cmon.century * 100;
dst->tm_wday = src->day.day - 1; // treat TIME_STRUCT.day 1 as Sunday
if(dst->tm_mon < 12){
tmp = dst->tm_year + 1900;
mdays = (IS_LEAP_YEAR(tmp))?TimeExt_lpdays: TimeExt_days;
dst->tm_yday = mdays[dst->tm_mon] + dst->tm_mday;
}
dst->tm_isdst = 0;
dst->tm_gmtoff = -1;
}
void SystemTimeToDS3231Time(TIME_STRUCT *dst, const struct tm *src){
uint16_t tmp;
if(dst == NULL || src == NULL)
return;
if(src->tm_year < (DS3231_BASEYEAR - 1900))
return;
dst->sec.sec_sd = src->tm_sec % 10;
dst->sec.sec_td = src->tm_sec / 10;
dst->min.min_sd = src->tm_min % 10;
dst->min.min_td = src->tm_min / 10;
dst->hour.SYS12.is12sys = 0; // use 24 hours system
dst->hour.SYS24.hour_sd = src->tm_hour % 10;
dst->hour.SYS24.hour_td = src->tm_hour / 10;
dst->date.date_sd = src->tm_mday % 10;
dst->date.date_td = src->tm_mday / 10;
dst->day.day = src->tm_wday + 1;
tmp = src->tm_mon + 1;
dst->cmon.mon_sd = tmp % 10;
dst->cmon.mon_td = tmp / 10;
tmp = src->tm_year - (DS3231_BASEYEAR - 1900);
dst->cmon.century = tmp / 100 & 1;
dst->year.year_sd = tmp % 10;
tmp /= 10;
dst->year.year_td = tmp % 10;
}
BOOL DS3231TimeValid(const TIME_STRUCT *time){
uint16_t tmp;
if(time == NULL)
return FALSE;
// BCD check(with some range check)
if(time->sec.sec_sd > 9 || time->sec.sec_td > 5 ||
time->min.min_sd > 9 || time->min.min_td > 5 ||
time->hour.SYS24.hour_sd > 9 ||
time->day.day == 0 ||
time->date.date_sd > 9 ||
time->cmon.mon_sd > 9 ||
time->year.year_sd > 9 || time->year.year_td > 9
)
return FALSE;
// range check
if(time->hour.SYS12.is12sys){
tmp = time->hour.SYS12.hour_sd + time->hour.SYS12.hour_td * 10;
if(tmp < 1 || tmp > 12)
return FALSE;
}else{
tmp = time->hour.SYS24.hour_sd + time->hour.SYS24.hour_td * 10;
if(tmp > 23)
return FALSE;
}
tmp = time->cmon.mon_sd + time->cmon.mon_td * 10;
if(tmp == 0 || tmp > 12)
return FALSE;
tmp = time->date.date_sd + time->date.date_td * 10;
if(tmp == 0 || tmp > 31) // 其实更严格的检查的话得判断当前月份
return FALSE;
return TRUE;
}
下面给出驱动代码的使用示例:
示例代码
#include <hidef.h> /* common defines and macros */
#include "derivative.h" /* derivative-specific definitions */
#include "DS3231Driver.h"
#include "IIC.h"
#include <stdio.h>
typedef void (*near tIsrFunc)(void);
const tIsrFunc _vect @0xFFC0 = IIC_ISR;
#define BUS_CLOCK 32000000
void Delay(void) {
unsigned int i,j;
for(i = 0; i < 100; i++)
for(j = 0; j < 50000; j++)
;
}
void INIT_PLL(void)
{
CLKSEL &= 0x7f; //set OSCCLK as sysclk
PLLCTL &= 0x8F; //DisaKble PLL circuit
CRGINT &= 0xDF;
#if(BUS_CLOCK == 40000000)
SYNR = 0x44;
#elif(BUS_CLOCK == 32000000)
SYNR = 0x43;
#elif(BUS_CLOCK == 24000000)
SYNR = 0x42;
#endif
REFDV = 0x81; //PLLCLK=2×OSCCLK×(SYNR+1)/(REFDV+1)=64MHz ,fbus=32M
PLLCTL =PLLCTL|0x70; //Enable PLL circuit
asm NOP;
asm NOP;
while(!(CRGFLG&0x08)); //PLLCLK is Locked already
CLKSEL |= 0x80; //set PLLCLK as sysclk
}
char strbuf[100];
volatile TIME_STRUCT time;
volatile TEMP_STRUCT temp;
unsigned char* flag = (unsigned char*)0x3F80;
void main(void) {
// 18年5月11日星期五23点59分50秒
time.year.year_td = 1; time.year.year_sd = 8;
time.cmon.mon_td = 0; time.cmon.mon_sd = 5;
time.date.date_td = 1; time.date.date_sd = 1;
time.day.day = 5; time.hour.SYS24.is12sys = 0;
time.hour.SYS24.hour_td = 2; time.hour.SYS24.hour_sd = 3;
time.min.min_td = 5; time.min.min_sd = 9;
time.sec.sec_td = 5; time.sec.sec_sd = 0;
INIT_PLL();
IIC_Init();
DS3231_Init();
EnableInterrupts;
// 每次首次上电时设置当前时间
if(*flag != 0xac){
if(DS3231_SetTime(&time).errcode != DS3231_ERR_NULL)
while(1);
*flag = 0xac; // RAM中的数据在保持上电状态时即使系统重置也不会重置,但在上电重置时会重置
}
for(;;) {
// 读取当前时间和温度,出任意错则无限循环停止运行
if(DS3231_GetTime(&time).errcode != DS3231_ERR_NULL)
while(1);
if(DS3231_GetTemp(&temp).errcode != DS3231_ERR_NULL)
while(1);
sprintf(strbuf,"year:%d%d month:%d%d date:%d%d day:%d,%d%d:%d%d:%d%d\r\nTemp:%d.%02d\r\n",
time.year.year_td,time.year.year_sd,time.cmon.mon_td,time.cmon.mon_sd,
time.date.date_td,time.date.date_sd,time.day.day,
time.hour.SYS24.hour_td,time.hour.SYS24.hour_sd,
time.min.min_td,time.min.min_sd,time.sec.sec_td,time.sec.sec_sd,
temp.integral.DATA,temp.fractional.DATA * 25);
Delay();
}
}
// 实现驱动要求的函数
unsigned char IIC_ReadBurst(unsigned char calAddr,unsigned char *pBuf,unsigned short len){
return IIC_Recv(calAddr,pBuf,len);
}
unsigned char IIC_SendBurst(unsigned char calAddr,unsigned char *pBuf,unsigned short len){
return IIC_Send(calAddr,pBuf,len);
}
unsigned char IIC_ReadByte(unsigned char calAddr,unsigned char *rst){
return IIC_RecvChar(calAddr,rst);
}
unsigned char IIC_SendByte(unsigned char calAddr,unsigned char val){
return IIC_SendChar(calAddr,&val);
}
测试结果
后记
这次对DS3231进行了简单的封装,难免会有不足或者错误之处,敬请指出。
测试中使用的IIC函数是自己封装的MC9S12XEP100的IIC模块,正在完善中,应该马上就会放出。
要求用户提供IIC函数是为了实现与硬件解耦,这样不管实际平台是怎么样的,用户只要按照要求实现IIC通讯函数就可以使用这个驱动了。
简介中完全参考Maxim的数据手册。
注意
结构体位字段的写法有移植性问题,和编译器怎么排布位域有关。要检查有没问题的话,比如设
time.year.year_td = 1; time.year.year_sd = 8;
然后调试器看下内存,对应字节应该为0x18。如果是0x81的话,需要把结构体定义中的所有位字段的位置都掉个个。
另外,还可能遇到结构体对齐特性上的移植性问题。TIME_STRUCT中的每个寄存器结构体都应该占1个byte,也就是说sizeof(TIME_STRUCT)应该等于7,sizeof(REG_SEC)应该等于1。
目前已知stm32的编译器对位域上会根据类型声明来对齐,因此对于stm32的话应该把所有的位域的类型改成unsigned char,如REG_SEC应该改成这样:
typedef struct {
unsigned char sec_sd: 4; // ones digit of second 秒的个位
unsigned char sec_td: 3; // tens digit of second 秒的十位
unsigned char : 1;
} REG_SEC;
这是stm32编译器自己的扩展,非语言本身规范。
更新历史
2018/05/14
2019/10/18 增加时间转换助手模块