[嵌入式开发模块]DS3231时钟芯片 驱动程序

2023-11-01

刚刚写完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 增加时间转换助手模块

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[嵌入式开发模块]DS3231时钟芯片 驱动程序 的相关文章

  • fastcgi的环境变量

    FCGI ROLE RESPONDER SCRIPT FILENAME scripts 5 cgi QUERY STRING aaa 11111111111111 bbb 2222222222222222 ccc 3333333333333
  • C++知识积累:内存对齐理解

    为什么要进行内存对齐 这是因为CPU的读取总是对齐的 举个例子 假设CPU是32位的 那么CPU每次读取的4字节数据的首地址都是4的倍数 也就是说 内存中数据首地址为4的倍数时 CPU一次操作就可以完成数据读取 假设有一个int型四字节大小
  • R语言学习笔记:分析学生的考试成绩

    孩子上初中时拿到过全年级一次考试所有科目的考试成绩表 正好可以用于R语言的统计分析学习 为了不泄漏孩子的姓名 就用学号代替了 感兴趣可以下载测试数据进行练习 num class chn math eng phy chem politics
  • C++中的namespace

    namespace中文意思是命名空间或者叫名字空间 传统的C 只有一个全局的namespace 但是由于现在的程序的规模越来越大 程序的分工越来越细 全局作用域变得越来越拥挤 每个人都可能使用相同的名字来实现不同的库 于是程序员在合并程序的
  • 范围for语句

    C 新标准提供的范围for语句 这种语句遍历给定序列中个元素并对序列中每一个值执行某种操作 其语法形式是 for declaration expression statement 其中 expression 部分是一个对象 用于表示一个序列
  • 编写递归算法,计算二叉树叶子结点的数目。

    编写递归算法 计算二叉树叶子结点的数目 编写递归算法 计算二叉树叶子结点的数目 include stdio h 包含 getchar scanf printf include malloc h malloc 动态申请空间 函数 二叉树 结点
  • 解决“17: 错误:程序中有游离的‘\240’,\302’

    参考链接 https blog csdn net asuphy article details 54602426 执行如下命令即可 sed i s o240 o302 g dy haikang test cpp
  • 无法打开源文件<sys/time.h>,但是用time.h编译就会出错,缺少gettimeofday()

    因为sys time h是uinx系统下的库文件 而现在使用的平台是在windows 由于未指明程序运行的系统 导致找不到对应的头文件 需要重新实现gettimeofday 函数 define WIN32 include
  • 如何学好C语言的数据结构与算法?

    C语言的数据结构与算法 难就难在链表 学会了链表 可能后面就一点都不难了 书籍推荐 数据结构与算法分析 C语言描述版 要深入学习的话可以选择这本书 因为针对链表的讲解是比较详细的 所以可以很快理解链表 跟着书上一点点实现基本操作 增删改查
  • Lua和C++交互总结(很详细)

    出处 http blog csdn net shun fzll article details 39120965 一 lua堆栈 要理解lua和c 交互 首先要理解lua堆栈 简单来说 Lua和C c 语言通信的主要方法是一个无处不在的虚拟
  • vector,list,deque区别

    http blog csdn net renkaihao article details 6803866 vector和built in数组类似 它拥有一段连续的内存空间 并且起始地址不变 因此它能非常好的支持随即存取 即 操作符 但由于它
  • ATL字符串转换宏

    有比MultiByteToWideChar和WideCharToMultiByte更简单的字符串转换宏 你相信吗 头文件 d program files microsoft visual studio 8 vc atlmfc include
  • c语言判断一个数是否为偶数

    include
  • 在聚会中常玩数七的游戏,七的倍数和带有七的数字都不能说,比如14,27,28。请找出1~100的不能说的数字。...

    利用ES5的filter高阶函数来实现 var arr 1 2 3 4 5 6 7 17 27 21 22 28 100 r arr filter function x return x 10 7 x 7 0 alert r 7 14 17
  • 检查内存泄露

    自己编写的视频处理程序出现了一个问题 每帧的运行时间随着运行时间在不断增长 很大可能是出现了内存泄露 于是学习了一些查看内存泄露的方法 做了两种尝试 一是VS自带的DEBUG下的检测 view pl html view plain copy
  • 一个简单的参数帮助框架,c实现

    文章目录 具体实现如下 include
  • C 语言教程:数据类型和格式说明符

    C 语言中的数据类型 C 中的变量必须是指定的 数据类型 并且您必须在 printf 函数中使用 格式说明符 来显示它 创建变量 int myNum 5 整数 没有小数点 float myFloatNum 5 99 浮点数 char myL
  • lv12 交叉编译工具链 7

    目录 1 交叉编译 1 1 镜像来源 1 2 编译原理 1 3 编译过程 编辑 1 4 交叉编译 1 5 交叉编译工具链获取 2 ELF文件格式 编辑3 BIN文件格式 4 交叉编译工具链常用工具 4 1 size命令举例 4 2 rm命令
  • C++中的并发多线程网络通讯

    C 中的并发多线程网络通讯 一 引言 C 作为一种高效且功能强大的编程语言 为开发者提供了多种工具来处理多线程和网络通信 多线程编程允许多个任务同时执行 而网络通信则是现代应用程序的基石 本文将深入探讨如何使用C 实现并发多线程网络通信 并
  • C/C++编程:令人印象深刻的高级技巧案例

    C C 编程语言在软件开发领域有着悠久的历史 由于其高效 灵活和底层访问能力 至今仍然被广泛应用 本文将介绍一些在C C 编程中令人印象深刻的高级技巧 帮助读者提升编程水平 更加高效地使用这两种强大的编程语言 一 指针运算与内存管理 C C

随机推荐

  • 【clickhouse】ubuntu20安装clickhouse并用DBeaver远程管理

    文章目录 1 安装 2 配置 3 外部连接测试 4 相关概念 5 Reference 1 安装 使用Deb安装包 添加证书 sudo apt get install y apt transport https ca certificates
  • C/C++

    文章目录 学习内容提要 关于学习教材 推荐书籍或资料 这个课程我们怎么学 课程特色 C语言的重要性 从和编程相关的计算机基础开始 从计算机的组成谈谈程序的运行 代码是怎么变成程序的 不同的进制 不同的世界 程序和内存空间模型 打印地址示例
  • 基于量子遗传算法的函数寻优算法(matlab实现)

    8 1 理论基础 8 1 1 量子遗传算法概述 量子遗传算法 quantum genetic algorithm QGA 是量子计算与遗传算法相结合的产物 是一种新发展起来的概率进化算法 遗传算法是处理复杂优化问题的一种方法 其基本思想是模
  • Java中多线程向mysql插入同一条数据冲突问题

    目的 Java中多线程向mysql插入同一条数据冲突问题 环境 系统 win10 环境 idea 201901 一 问题 程序中会使用多线程读写数据库 在同一时刻多个线程插入相同主键的相同数据时 会直接报错 二 规避办法 由于需求是多个线程
  • 【Spring】Spring Security OAuth2 JWT 认证

    1 概述 Spring Security OAuth2 JWT 认证服务器配置 Spring Security OAuth2 JWT 资源服务器配置 Spring Security OAuth2 Redis 模式下认证服务器 Spri
  • RFID开发笔记 Alien阅读器文档

    1 开机使用serial connect 完成boot后使用TCP IP协议与主机通信 2 TagList 是一个活跃标签的列表 这里活跃的含义是在一个间隔里被监听到 如果一个标签之前没有被监听到 而在当前的 间隔里被监听到了 那么就将这个
  • 基于LSTM的诗词生成

    文章目录 前言 一 数据集介绍 二 实验代码 1 随机诗词生成 2 藏头诗生成 三 实验结果 1 随机诗词生成结果 2 藏头诗生成结果 总结 前言 本文的主要内容是基于LSTM的诗词生成 文中包括数据集的介绍 实验代码以及运行结果等 该实验
  • 2023【软件测试】面试题合集整理打包!大放送!!!

    跳槽求职 准备好一场面试不仅需要在简历上多下功夫 还需要为面试问答做好充足的准备 以下是 本人 从面试经验中收集的 然后分门别类整理了这套面试题 很具备参考性 毕竟都是企业真实面试题目 接下来 针对以下知识类型列出具体的面试点 其中包括笔试
  • 判断两个对象数组是否有相同元素,对象数组合并去重

    let arr name cat sex 0 name dog sex 0 name bat sex 0 let arr2 name cat sex 1 name dog sex 0 一 循环判断arr与arr2是否有相同元素 相同的添加i
  • 网络性能测试工具:iperf3

    一 iperf3简介 iperf3是一个网络性能测试工具 iperf3下载地址 iperf可以测试TCP和UDP带宽质量 iperf可以测量最大TCP带宽 具有多种参数和UDP特性 iperf可以报告带宽 延迟抖动和数据包丢失 iperf3
  • html中after伪类原理,理解:Before和:After伪元素

    CSS样式表的主要作用是修饰Web页面上的HTML标记 但有时候 为了实现某个效果而往页面里反复添加某个HTML标记很繁琐 或者是显得多余 或者是由于某种原因而做不到 这就是CSS伪元素 Pseudo Element 可以发挥作用的地方 所
  • 第二回:艺术画笔见乾坤

    文章目录 第二回 艺术画笔见乾坤 一 概述 1 matplotlib的三层api 2 Artist的分类 二 基本元素 primitives 1 2DLines a 如何设置Line2D的属性 b 如何绘制lines 2 patches a
  • Gitl用户在组中有五种权限:Guest、Reporter、Developer、Master、Owner 解释

    Gitl用户在组中有五种权限 Guest Reporter Developer Master Owner 解释 Guest 可以创建issue 发表评论 不能读写版本库 Reporter 可以克隆代码 不能提交 QA PM可以赋予这个权限
  • 遥感图像语义分割比赛整理

    好久没有写博客了 最近有两篇论文的投稿 到年前就没啥时间 寒假期间参加了一个遥感图像分割的比赛 一次不是很成功的参赛 第一次参加这种比赛吧 过程十分坎坷 本来就是在初赛ddl前10天才找到队友 然后在成功组队的第三天 被队友鸽了 只剩下我c
  • 打破传统降噪技术 看网易云信在语音降噪的实践应用

    导读 随着音视频会议 娱乐互动直播 在线教育产品的火热发展 产品中令人愉悦的音效音质体验是必不可少的 文 飒飒 网易云信音视频算法工程师 但在音视频实时通信中 难免会遇到各种我们不希望出现的声音 例如电流声 键盘敲击声 嘈杂声等 这些统称为
  • mysql的一些操作

    修改mysql的字符集 在安装mysql5 0时可以设置好mysql的字符集 一般使用utf8的字符集 1 查看建立表 tablename 的sql语句 Show create table tablename 查看建立表 tablename
  • R数据类型

    2 2 1 向量向量是用于存储数值型 字符型或逻辑型数据的一维数组 执行组合功能的函数c 可用来创建向量 各类向量如下例所示 a lt c 1 2 5 3 6 2 4 b lt c one two three c lt c TRUE TRU
  • 多项式全家福(缺插值和点值)

    文章目录 写法 vector写有什么好处 vector写的时候注意什么 Dft 求逆 开二次根 取模 对数 前置技能 1 复合函数求导 2 ln函数求导 指数 前置技能 牛顿迭代 模板 写法 vector写有什么好处 1 分治NTT的时候不
  • 爬虫已经凉凉,再不学点数据分析,你真的Out了!

    不论你是运营 金融 产品 开发 数据分析能力不仅能够助力个人专业能力的提升 也可更加了解产品 在职场上走得更远 但市面上数据分析课普遍存在以下问题 乱 少 贵 所以 只要299的数据分析就业班 来了 价格优势 首期1折价仅需299元 内容全
  • [嵌入式开发模块]DS3231时钟芯片 驱动程序

    刚刚写完DS3231时钟芯片的驱动程序 这里开源出来供大家使用 需要的自取 先简单介绍下这个芯片 然后直接给驱动代码以及示例代码 DS3231简介 简介 DS3231是一款低成本 高精度I2C实时时钟 RTC 具有集成的温补晶体振荡器 TC