一.前言

本帖是新手学习记录帖,主要记录对DS3231模块进行的简单驱动,完成以下三个主要功能:①获取实时时间并显示到串口屏②闹钟设置③温度获取

文中代码并不完全,可能会有错误和疏漏,例如省略了对参数的检查及错误处理,省略了触发类型/模式可选择的编写。仅写了必要的主体部分。
 

二.DS3231模块简介

DS3231M是低成本、高精度I2C实时时钟(RTC)模块。内部集成了温度补偿晶体振荡器(TCXO)和高精度晶体。

模块采用I2C通信接口,通信协议简单,可直接与STM32、Arduino等主流控制器连接。其核心功能包括:

① 实时时钟与日历:支持12/24小时制,存储秒、分、时、日、星期、月、年数据,年数据采用BCD编码(00~99),并支持世纪位自动切换;

② 双闹钟功能:内置2个独立闹钟(Alarm1、Alarm2),可通过设置“秒/分/时/日期/星期”的匹配条件触发中断,中断引脚(INT)输出低电平信号,适用于定时唤醒、事件提醒等场景;

③ 内置温度传感器:集成10位分辨率的温度传感器,温度范围-40℃~+85℃,分辨率0.25℃,可通过强制温度转换(CONV位)获取实时温度数据,无需额外温度传感器。

数据手册中的引脚说明

控制寄存器和状态寄存器

计时寄存器

三.功能完成

1.获取实时时间

首先阅读手册中的寄存器和详细说明

①寄存器数据格式为8421BCD码,读取/写入时需要进行与十进制数字的转换
②02H小时寄存器的bit6为 12(1)/24(0)小时模式选择,bit6为12小时制的AM(0)/PM(1)选择
③05H月寄存器的bit7为世纪位
④注意各个寄存器的数据范围及某些位固定为0
⑤初次上电可能需要手动校准(设置)时间
 

DS3231.h

#ifndef __DS3231_H
#define __DS3231_H

#include "stdint.h"
#include "i2c.h"
#include "flash.h"
#include "string.h"

typedef struct
{
	uint8_t year;
	uint8_t month;
	uint8_t day;
	uint8_t week;
	uint8_t hour;
	uint8_t minute;
	uint8_t second;
}TimeTypeDef;

#define DS3231_ADDR 0x68<<1

uint8_t BCD2DEC(uint8_t bcd);
void DS3231_Init(I2C_HandleTypeDef *hi2c);
void DS3231_Readtime(TimeTypeDef *time);
void DS3231_Settime(uint8_t year,uint8_t month,uint8_t date,uint8_t week,
										uint8_t hour,uint8_t minute,uint8_t second);
void DS3231_SaveTimeToFlash(uint32_t addr, TimeTypeDef *time);
void DS3231_ReadTimeFromFlash(uint32_t addr, TimeTypeDef *time);

void DS3231_SetAlarm1(uint8_t day,uint8_t hour,uint8_t minute,uint8_t second);
void DS3231_EnableAlarm1IT(void);
void DS3231_ClearAlarm1FLAG(void);
float DS3231_ReadTemp(void);



#endif

DS3231.c

/*以下分别是 BCD->十进制 十进制->BCD DS3231初始化 时间读取(24小时制) 时间设置 的实现代码*/
/*时间读取和时间设置用了两种写法(精神分裂),可供参考*/
uint8_t BCD2DEC(uint8_t bcd)
{
	return ((bcd>>4)*10+(bcd&0x0f));
}

uint8_t DEC2BCD(uint8_t dec)
{
	return ((dec/10<<4)|(dec%10));
}

void DS3231_Init(I2C_HandleTypeDef *hi2c)
{
	uint8_t status1;
	
	HAL_I2C_Mem_Read(&hi2c2,DS3231_ADDR,0x0f,I2C_MEMADD_SIZE_8BIT,&status1,1,HAL_MAX_DELAY);
	
	if((status1&0x80)==0x80)
	{
		status1=status1&(~0x80);
	}
	HAL_I2C_Mem_Write(&hi2c2,DS3231_ADDR,0x0F,I2C_MEMADD_SIZE_8BIT,&status1,1,HAL_MAX_DELAY);
	
	uint8_t status2;
	HAL_I2C_Mem_Read(&hi2c2,DS3231_ADDR,0x0f,I2C_MEMADD_SIZE_8BIT,&status2,1,HAL_MAX_DELAY);
	status2&=0xfe;
	HAL_I2C_Mem_Write(&hi2c2,DS3231_ADDR,0x0f,I2C_MEMADD_SIZE_8BIT,&status2,1,HAL_MAX_DELAY);
}

void DS3231_Readtime(TimeTypeDef *time)
{
	uint8_t data[7];
	HAL_I2C_Mem_Read(&hi2c2,DS3231_ADDR,0x00,I2C_MEMADD_SIZE_8BIT,data,7,HAL_MAX_DELAY);

	time->year=BCD2DEC(data[6]);
	time->month=BCD2DEC(data[5]&0x1f);
	time->week=BCD2DEC(data[3]&0x07);
	time->day=BCD2DEC(data[4]&0x3f);
	time->hour=BCD2DEC(data[2]&0x3f);
	time->minute=BCD2DEC(data[1]&0x7f);
	time->second=BCD2DEC(data[0]&0x7f);
	
	if(data[5]&0x80)
	{
		time->year +=100;
	}
}

void DS3231_Settime(uint8_t year,uint8_t month,uint8_t date,uint8_t week,
										uint8_t hour,uint8_t minute,uint8_t second)
{
	uint8_t data[7];
	
	data[0]=DEC2BCD(second)&0x7f;
	data[1]=DEC2BCD(minute)&0x7f;
	data[2]=DEC2BCD(hour)&0x3f;
	data[3]=DEC2BCD(week)&0x07;
	data[4]=DEC2BCD(date)&0x3f;
	data[5]=DEC2BCD(month)&0x1f;
	data[6]=DEC2BCD(year);
	
	HAL_I2C_Mem_Write(&hi2c2,DS3231_ADDR,0x00,I2C_MEMADD_SIZE_8BIT,data,7,HAL_MAX_DELAY);
	
}

2.设置闹钟

首先看手册中的寄存器和详细说明

首先是闹钟功能的大致逻辑顺序:
首先设置触发条件(即闹钟时间),触发条件设定值与前面RTC寄存器的值匹配的时,候,会将状态寄存器的闹钟 标志位置1,继续选择是否触发闹钟中断来使INT引脚输出信号(低电位0有效),

寄存器注意点:
①记得设置每个闹钟寄存器的屏蔽位A1M1,A1M2....具体内容需要见表2
②0AH和0DH的bit6是选择日期(0)/星期(1) 匹配
③如果需要输出中断信号,需要在控制寄存器(0EH)将A1IE和INTCN置1
④状态寄存器(0FH)的闹钟标志位A1F不会自动清零,需要手动置0

⑤标志位A1F的产生与INTCN的状态无关,初次上电A1IE与INTCN置0,需要手动开启

DS3231.c

/*以下是 Alarm1设置 使能中断并输出信号 Alarm1标志位清除 函数*/
void DS3231_SetAlarm1(uint8_t day,uint8_t hour,uint8_t minute,uint8_t second)
{
	uint8_t alarm[4];
	alarm[0]=DEC2BCD(second)&0x7f;
	alarm[1]=DEC2BCD(minute)&0x7f;
	alarm[2]=DEC2BCD(hour)&0x3f;
	alarm[3]=(DEC2BCD(day)&0x07)|0x40;
	HAL_I2C_Mem_Write(&hi2c2,DS3231_ADDR,0x07,I2C_MEMADD_SIZE_8BIT,alarm,4,HAL_MAX_DELAY);
	
}

void DS3231_EnableAlarm1IT(void)
{
	uint8_t control;
	HAL_I2C_Mem_Read(&hi2c2,DS3231_ADDR,0x0e,I2C_MEMADD_SIZE_8BIT,&control,1,HAL_MAX_DELAY);
	control|=0x05;
	HAL_I2C_Mem_Write(&hi2c2,DS3231_ADDR,0x0e,I2C_MEMADD_SIZE_8BIT,&control,1,HAL_MAX_DELAY);

}

void DS3231_ClearAlarm1FLAG(void)
{
	uint8_t status;
	HAL_I2C_Mem_Read(&hi2c2,DS3231_ADDR,0x0f,I2C_MEMADD_SIZE_8BIT,&status,1,HAL_MAX_DELAY);
	status&=0xfe;
	HAL_I2C_Mem_Write(&hi2c2,DS3231_ADDR,0x0f,I2C_MEMADD_SIZE_8BIT,&status,1,HAL_MAX_DELAY);
}

后续可以搭配外部中断等等来补全闹钟功能

3.读取温度

先看寄存器说明

①注意数据拼接

②开启温度转换需要将CONV置1

③开启转换后要检查标志位BSY,转换完成之后CONV和BSY都会置零,我下面的代码是放在while死循环里的,所以CONV放在函数开头每次都会置1

DS3231.h

float DS3231_ReadTemp(void)
{
	uint8_t ctrl,status,temp_h,temp_l;
	uint16_t temp;
	HAL_I2C_Mem_Read(&hi2c2,DS3231_ADDR,0x0e,I2C_MEMADD_SIZE_8BIT,&ctrl,1,HAL_MAX_DELAY);
	ctrl|=0x20;
	HAL_I2C_Mem_Write(&hi2c2,DS3231_ADDR,0x0e,I2C_MEMADD_SIZE_8BIT,&ctrl,1,HAL_MAX_DELAY);
	
	do{
		HAL_I2C_Mem_Read(&hi2c2,DS3231_ADDR,0xf,I2C_MEMADD_SIZE_8BIT,&status,1,HAL_MAX_DELAY);
	}while(status&0x04);
	
	HAL_I2C_Mem_Read(&hi2c2,DS3231_ADDR,0x11,I2C_MEMADD_SIZE_8BIT,&temp_h,1,HAL_MAX_DELAY);
	HAL_I2C_Mem_Read(&hi2c2,DS3231_ADDR,0x12,I2C_MEMADD_SIZE_8BIT,&temp_l,1,HAL_MAX_DELAY);
	
	temp=(temp_h<<2)|((temp_l>>6)&0x03);
	if(temp&0x200) temp-=0x400;
	
	return temp*0.25f;
}

四.功能测试

main.c

int  main(void)
{
  while (1)
  {		
	  DS3231_Readtime(&currentTime);
		sprintf(timeBuf,"t0.txt=\"%04d-%02d-%02d %02d:%02d:%02d %d\"\xff\xff\xff",currentTime.year+2000,currentTime.month,currentTime.day,currentTime.hour,currentTime.minute,currentTime.second,currentTime.week);
		HAL_UART_Transmit(&huart5,(uint8_t *)timeBuf,strlen(timeBuf),HAL_MAX_DELAY);
		
		Temp=DS3231_ReadTemp();
		sprintf(Tempbuf,"Temp:%.2f\r\n",Temp);
		HAL_UART_Transmit(&huart4,(uint8_t*)Tempbuf,strlen(Tempbuf),HAL_MAX_DELAY);
		HAL_Delay(1000);
    
  }

}

stm32f4xx_it.c

/*配置PB4引脚为外部中断,下降沿触发,并配置上拉输入*/
void EXTI4_IRQHandler(void)
{

  HAL_UART_Transmit(&huart4,(uint8_t*)"66666",5,HAL_MAX_DELAY);
  DS3231_ClearAlarm1FLAG();
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
 
 
}

更多推荐