【STM32f407】使用HAL库驱动DS3231时钟模块
一.前言
本帖是新手学习记录帖,主要记录对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(¤tTime);
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);
}
更多推荐
所有评论(0)