每次上电时都要运行下面两个函数来对DS3231进行初始化:
DS3231_WriteReg(DS3231_CONTROL,0x00); //将DS3231设置为想要的状态
DS3231_WriteReg(DS3231_STATUS ,0x00); //将DS3231设置为想要的状态
延时函数采用指令延时而不是systick延时,这样软件IIC和延时函数就可以在中断中运行,如果是systick延时的话那么systick优先级要大于中断,而且就算这样也可能出现一些奇怪的问题,所有如果要在中断中延时且对时间要求没那么严格,个人还是推荐指令延时:
void Delay_Ms(uint16_t ms){
while(ms--){
Delay_Us(1000);
}
}
//
void Delay_Us(uint16_t us)
{
__IO uint32_t Delay = us * 64 / 8;//(SystemCoreClock / 8U / 1000000U)
//见stm32f1xx_hal_rcc.c -- static void RCC_Delay(uint32_t mdelay)
do
{
__NOP();
}
while (Delay --);
}
DS3231驱动的具体C文件和头文件如下,软件IIC实现可参考我的上一篇博客。
DS3231.c:
#include "DS3231.h"
#include "Soft_IIC.h"
#include "Delay.h"
#include "AT24C32.h"
#include "74HC595.h"
#include "iwdg.h"
_calendar_obj calendar; //时钟结构体
//DS3231时钟中断
//每秒触发一次
void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == DS3231_Pin)
{
UNUSED(GPIO_Pin);
I2C_DS3231_getTime(); //更新时间
}
}
uint8_t DS3231_WriteReg(uint8_t RegAddress, uint8_t Data) //指定地址写寄存器 (写一个字节)
{
//先发送从机地址(点名从机DS3231_ADDRESS),再发送寄存器地址(点名寄存器),再发送数据()写入从机寄存器
//HAL_StatusTypeDef hal_state = HAL_I2C_Mem_Write(&hi2c1, DS3231_ADDRESS_write, RegAddress, I2C_MEMADD_SIZE_8BIT, a, 1, 1000); //1000代表1000ms
/* 1、发送起始信号 */
iic_start();
/* 2、发送通讯地址(写操作地址) */
iic_send_byte(DS3231_ADDRESS_write);
/* 3、等待应答信号 */
if(iic_wait_ack() != 1) return 1;
/* 4、发送寄存器地址:0~256 */
iic_send_byte(RegAddress);
/* 5、等待应答信号 */
if(iic_wait_ack() != 1) return 2;
/* 6、发送写入数据 */
iic_send_byte(Data);
/* 7、等待应答信号 */
if(iic_wait_ack() != 1) return 3;
/* 8、发送停止信号 */
iic_stop();
return 0;
}
uint8_t DS3231_ReadReg(uint8_t RegAddress) //指定地址读寄存器 (读一个字节)
{
uint8_t Data;
//HAL_StatusTypeDef hal_state = HAL_I2C_Mem_Read(&hi2c1, DS3231_ADDRESS_read, RegAddress, I2C_MEMADD_SIZE_8BIT, &Data, 1, 1000);
/* 1、发送起始信号 */
iic_start();
/* 2、发送通讯地址(写操作地址) */
iic_send_byte(DS3231_ADDRESS_write);
/* 3、等待应答信号 */
if(iic_wait_ack() != 1) return 1;
/* 4、发送寄存器地址:0~256 */
iic_send_byte(RegAddress); //发送地址
/* 5、等待应答信号 */
if(iic_wait_ack() != 1) return 2;
/* 6、发送起始信号 */
iic_start();
/* 7、发送通讯地址(读操作地址) */
iic_send_byte(DS3231_ADDRESS_read );
/* 8、等待应答信号 */
if(iic_wait_ack() != 1) return 3;
/* 9、接收数据并发送非应答(获取该地址即可) */
Data = iic_read_byte(0);
/* 10、发送停止信号 */
iic_stop();
return Data;
}
/**
* @brief BCD(8421)转DEC.//二进制转十进制
* @param val:BCD码.
* @retval i:DEC码.
*/
uint8_t BCD_DEC(uint8_t val)
{
uint8_t i;
i= val&0x0f;
val >>= 4;
val &= 0x0f;
val *= 10;
i += val;
return i;
}
//
/**
* @brief DEC转BCD(8421).
* @param val:DEC码.
* @retval k:BCD码.
*/
uint8_t DEC_BCD(uint8_t val)
{
uint8_t i,j,k;
i=val/10;
j=val%10;
k=j+(i<<4);
return k;
}
/**
* @brief 时间设置
* @param
* @arg 分别输入 年 月 日 星期 时 分 秒
* @retval 无
*/
//uint8_t century __attribute__((at(0x0801F000)))= 0;//将阴极中毒的数字定位在flash中
void I2C_DS3231_SetTime(uint16_t yea,uint8_t mon,uint8_t da, uint8_t hou,uint8_t min,uint8_t sec)
{
// uint8_t yea = 0x23;//23年 0010 0011
//uint8_t mon = 0x10;//10月 0001 0000
//uint8_t da = 0x13;//13日 0001 0011
//uint8_t we = 0x06;//周6 0000 0110
//uint8_t hou = 0x08; //8时 0000 1000
//uint8_t min = 0x08;//8分
//uint8_t sec = 0x08;//8秒
calendar.year = yea;
calendar.century = yea/100;
at24c32_write_one_byte(year_address, calendar.century); //由于DS3231只能保存年份的后两位,无法保存世纪,
//因此设置时间时需要人为设置世纪,然后将其写入EEprom,每次开机再读取世纪
calendar.month = mon;
calendar.date = da;
calendar.hour = hou;
calendar.min = min;
calendar.sec = sec;
calendar.week = GregorianDay(calendar);
while(!((DS3231_ReadReg(DS3231_STATUS)|0x00) == 0x00)) //判断DS3231是否准备好
{
}
//HAL_Delay(5);
DS3231_WriteReg(DS3231_CONTROL,0x00);
//HAL_Delay(5);
DS3231_WriteReg(0x06,DEC_BCD(yea%100)); //年输入格式为0-99
//HAL_Delay(5);
DS3231_WriteReg(0x05,DEC_BCD(mon));
//HAL_Delay(5);
DS3231_WriteReg(0x04,DEC_BCD(da));
//HAL_Delay(5);
DS3231_WriteReg(0x03,DEC_BCD(calendar.week));
//HAL_Delay(5);
DS3231_WriteReg(0x02,DEC_BCD(hou)); //24小时制
//HAL_Delay(5);
DS3231_WriteReg(0x01,DEC_BCD(min));
//HAL_Delay(5);
DS3231_WriteReg(0x00,DEC_BCD(sec));
}
/**
* @brief 获取时间
* @param
* @arg pBuffer:存放从DS3231读取的数据的缓冲区指针
* @arg ReadAddr:读取数据的DS3231的地址
* @arg NumByteToWrite:要从DS3231读取的字节数
* @retval 返回1,表示读取成功.
*/
int a = 0;
void I2C_DS3231_getTime(void)
{
a = DS3231_ReadReg(DS3231_STATUS);
while(!((DS3231_ReadReg(DS3231_STATUS)|0x00) == 0x00)) //判断DS3231是否准备好
{
}
calendar.year= calendar.century*100 + BCD_DEC(DS3231_ReadReg(0x06)); //将EEPROM中的世纪和DS3231中的年份相加获得完整年份
calendar.month= BCD_DEC(DS3231_ReadReg(0x05));
calendar.date= BCD_DEC(DS3231_ReadReg(0x04));
calendar.week= BCD_DEC(DS3231_ReadReg(0x03));
calendar.hour= BCD_DEC(DS3231_ReadReg(0x02)&0x3f); //排除12/24小时制变化对hour寄存器值的影响
calendar.min= BCD_DEC(DS3231_ReadReg(0x01));
calendar.sec= BCD_DEC(DS3231_ReadReg(0x00));
}
/**
* @brief 获取温度
* @param 无
* @retval 无
*/
void I2C_DS3231_getTemperature(void)
{
while(!((DS3231_ReadReg(DS3231_STATUS)|0x00) == 0x00)) //判断DS3231是否准备好
{
}
DS3231_WriteReg(DS3231_CONTROL, 0x20);//主动使能温度转换
calendar.temperature=DS3231_ReadReg(DS3231_TEMPERATUREH);
}
/*计算公历天数得出星期*/
int GregorianDay(_calendar_obj tm)
{
int leapsToDate;
int lastYear;
int day;
int MonthOffset[] = { 0,31,59,90,120,151,181,212,243,273,304,334 };
lastYear=tm.year-1;
/*计算从公元元年到计数的前一年之中一共经历了多少个闰年*/
leapsToDate = lastYear/4 - lastYear/100 + lastYear/400;
/*如若计数的这一年为闰年,且计数的月份在2月之后,则日数加1,否则不加1*/
if((tm.year%4==0) &&
((tm.year%100!=0) || (tm.year%400==0)) &&
(tm.month>2)) {
/*
* We are past Feb. 29 in a leap year
*/
day=1;
} else {
day=0;
}
day += lastYear*365 + leapsToDate + MonthOffset[tm.month-1] + tm.date; /*计算从公元元年元旦到计数日期一共有多少天*/
tm.week=day%7; //算出星期
if(tm.week == 0)
tm.week = 7;
return tm.week;
}
DS3231.h:头文件
#ifndef __DS3231_H__
#define __DS3231_H__
#include "usart.h"
#include "stm32g0xx_hal.h"
/* DS3231 地址定义 */
#define DS3231_ADDRESS_write 0xD0
#define DS3231_ADDRESS_read 0xD1
/*信息输出*/
#define DS3231_DEBUG_ON 0
#define DS3231_INFO(fmt,arg...) printf("<<-DS3231-INFO->> "fmt"\n",##arg)
#define DS3231_ERROR(fmt,arg...) printf("<<-DS3231-ERROR->> "fmt"\n",##arg)
#define DS3231_DEBUG(fmt,arg...) do{\
if(DS3231_DEBUG_ON)\
printf("<<-DS3231-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\
}while(0)
/* DS3231寄存器地址 */
#define DS3231_SECOND 0x00 //秒
#define DS3231_MINUTE 0x01 //分
#define DS3231_HOUR 0x02 //时
#define DS3231_WEEK 0x03 //星期
#define DS3231_DAY 0x04 //日
#define DS3231_MONTH 0x05 //月
#define DS3231_YEAR 0x06 //年
#define DS3231_CONTROL 0x0e //控制寄存器
#define DS3231_STATUS 0x0f //状态寄存器
#define BSY 2 //忙
#define OSF 7 //振荡器停止标志
#define DS3231_XTAL 0x10 //晶体老化寄存器
#define DS3231_TEMPERATUREH 0x11 //温度寄存器高字节(8位)
#define DS3231_TEMPERATUREL 0x12 //温度寄存器低字节(高2位)
typedef struct
{
int hour;
int min;
int sec;
uint16_t year;
uint8_t century;
int month;
int date;
int week;
int temperature;
}_calendar_obj; //定义DS3231中的时间结构体
extern _calendar_obj calendar; //日历结构体
//void DS3231_Init(void);
uint8_t DS3231_WriteReg(uint8_t RegAddress, uint8_t Data);
uint8_t DS3231_ReadReg(uint8_t RegAddress);
void I2C_WaitDs3231StandbyState(void);
uint8_t BCD_DEC(uint8_t val);
uint8_t DEC_BCD(uint8_t val);
void I2C_DS3231_SetTime(uint16_t yea,uint8_t mon,uint8_t da,uint8_t hou,uint8_t min,uint8_t sec);
void I2C_DS3231_getTime(void);
void I2C_DS3231_getTemperature(void);
int GregorianDay(_calendar_obj tm);
#endif
参考链接: