问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

STM32驱动DS3231时钟模块(OLED显示)

创作时间:
作者:
@小白创作中心

STM32驱动DS3231时钟模块(OLED显示)

引用
CSDN
1.
https://blog.csdn.net/qq_42250136/article/details/142963236

模块简介

DS3231M是低成本、高精度I2C实时时钟(RTC)。该器件包含电池输入端,断开主电源时仍可保持精确计时。集成微机电系统(MEMS)提高了器件的长期精确度,并减少了生产线的元件数量。DS3231M采用与流行的DS3231 RTC相同的器件封装。RTC保存秒、分、时、星期、日期、月和年信息。少于31天的月份,将自动调整月末的日期,包括闰年修正。时钟格式可以是24小时或带AM/PM指示的12小时格式。提供两个可设置的日历闹钟和一个1Hz输出。地址与数据通过I2C双向总线串行传输。精密的、经过温度补偿的电压基准和比较器电路用来监视VCC状态,检测电源故障,提供复位输出,并在必要时自动切换到备份电源。

寄存器说明

寄存器存储的是BCD(8421)编码,读取后要转换成十进制进行显示。前7个寄存器(00h-06h)都是和时间相关的,注意小时寄存器(02h)的第6位定义为12小时或24小时模式选择位。该位为高时,选择12小时模式。在12小时模式下,第5位为AM/PM指示位,逻辑高时为PM。在24小时模式下,第5位为20小时位(20至23小时)。当年寄存器由99溢出至00时,会转换世纪位(月寄存器(05h)的第7位)。(下面代码中定义的是24小时制)

星期寄存器(03h)在午夜时递增。对应于星期的值由用户定义,但是该值必须连续(即,如果1等于星期日,那么2等于星期一,依次类推)。不合逻辑的时间和日期输入会导致不确定的操作。(下面代码中定义的是周一为1)。

后面的7个寄存器是与闹钟相关的(07h-0Dh)是与闹钟相关的,具体的定义可以自行查阅手册进行查看,这里就不展开说明了。

接下来是控制寄存器(0Eh)和状态寄存器(0Fh),以及老化补偿寄存器(10h),这几个寄存上电前就都已经有默认配置了,如果没有特别需求的话是不需要进行修改的,如果有特定的需求可以根据手册中的说明进行详细配置。

最后是温度寄存器(11h以及12h)温度值采用10位编码表示,具有0.25°C的分辨率,访问地址为11h和12h。温度编码为2的补码格式。高8位(整数部分)位于地11h,低2位(小数部分)位于地址12h。例如,0001 1001 01b = +25.25°C。上电复位后,寄存器的缺省温度值设定为0°C,控制器启动温度转换。在VCC初次上电或VBAT供电下首次进行I2C通信时,开始读取温度值,之后每秒(采用VCC供电)或每10秒(采用VBAT供电)读取一次。每次由用户启动的转换结束后都会更新温度寄存器,温度寄存器是只读的。

代码说明

这里使用的是STM32F103C8T6模拟IIC驱动DS3231,在OLED屏幕上进行显示。

ds3231.h

#ifndef _DS3231_H
#define _DS3231_H
void DS3231_WriteReg(uint8_t RegAddress, uint8_t Data);
uint8_t DS3231_ReadReg(uint8_t RegAddress);
void DS3231_Init(void);
void DS3231_SetTime(u8 year,u8 mon,u8 data,u8 week,u8 hour,u8 min,u8 sec);
void DS3231_GetTime(u8 *year, u8 *month, u8 *date, u8 *week, u8 *hour, u8 *min, u8 *sec);
#endif

ds3231.c

#include "Device/Include/stm32f10x.h"   // Device header
#include "DS3231.h"
#include "DS3231_Reg.h"
#include "MyI2C.h"
#define DS3231_ADDRESS 	    0xD0//DS3213的写地址(这里是写地址,方便后面代码的编写,把0位也计算进去了)
                                //DS3231的IIC地址:0x68
/**
  * 函    数:DS3231写寄存器
  * 参    数:RegAddress 寄存器地址
  * 参    数:Data 要写入寄存器的数据,范围:0x00~0xFF
  * 返 回 值:无
  */
void DS3231_WriteReg(uint8_t RegAddress, uint8_t Data)
{
    MyI2C_Start();						//I2C起始
    MyI2C_SendByte(DS3231_ADDRESS);	    //发送从机地址,读写位为0,表示即将写入
    MyI2C_ReceiveAck();					//接收应答
    MyI2C_SendByte(RegAddress);			//发送寄存器地址
    MyI2C_ReceiveAck();					//接收应答
    MyI2C_SendByte(Data);				//发送要写入寄存器的数据
    MyI2C_ReceiveAck();					//接收应答
    MyI2C_Stop();						//I2C终止
}
/**
  * 函    数:DS3231读寄存器
  * 参    数:RegAddress 寄存器地址
  * 返 回 值:读取寄存器的数据,范围:0x00~0xFF
  */
uint8_t DS3231_ReadReg(uint8_t RegAddress)
{
    uint8_t Data;
    
    MyI2C_Start();						//I2C起始
    MyI2C_SendByte(DS3231_ADDRESS);	    //发送从机地址,读写位为0,表示即将写入
    MyI2C_ReceiveAck();					//接收应答
    MyI2C_SendByte(RegAddress);			//发送寄存器地址
    MyI2C_ReceiveAck();					//接收应答
    
    MyI2C_Start();						    //I2C重复起始
    MyI2C_SendByte(DS3231_ADDRESS | 0x01);	//发送从机地址,读写位为1,表示即将读取
    MyI2C_ReceiveAck();					    //接收应答
    Data = MyI2C_ReceiveByte();			    //接收指定寄存器的数据
    MyI2C_SendAck(1);					    //发送应答,给从机非应答,终止从机的数据输出
    MyI2C_Stop();						    //I2C终止
    
    return Data;
}
void DS3231_Init(void)
{
    MyI2C_Init();									//先初始化底层的I2C
    DS3231_WriteReg(DS3231_CONTROL_REG, 0x1C);      //初始化控制寄存器
}
/**
  * 函    数:BCD(8421)转DEC.//二进制转十进制
  * 参    数:val:BCD码.
  * 参    数:i:DEC码.
  * 返 回 值:无
  */
uint8_t BCD_DEC(u8 val)
{
    u8 i;
    i= val&0x0f;
    val >>= 4;
    val &= 0x0f;
    val *= 10;
    i += val;    
    return i;
}
/**
  * 函    数:DEC转BCD(8421).//十进制转二进制
  * 参    数:val:DEC码.
  * 参    数:k:BCD码.
  * 返 回 值:无
  */
uint8_t DEC_BCD(u8 val)
{
    u8 i,j,k;
    i=val/10;
    j=val%10;
    k=j+(i<<4);
    return k;
}
/**
  * 函    数:时间设置
  *	参    数:分别输入 年 月 日 星期 时 分 秒
  * 返 回 值:无
  */
void DS3231_SetTime(u8 year,u8 mon,u8 da,u8 week,u8 hour,u8 min,u8 sec)
{
    //u8    year = 0x23;  //23年 0010 0011 
    //u8	mon = 0x10;   //10月 0001 0000
    //u8	data = 0x13;   //13日 0001 0011
    //u8	week = 0x06;  //周6 0000 0110
    //u8	hour = 0x08;  //8时 0000 1000
    //u8	min = 0x08;   //8分
    //u8	sec = 0x08;   //8秒 
    DS3231_WriteReg(0x06,DEC_BCD(year));
 
    DS3231_WriteReg(0x05,DEC_BCD(mon));
 
    DS3231_WriteReg(0x04,DEC_BCD(da));
    
    DS3231_WriteReg(0x03,DEC_BCD(week));
 
    DS3231_WriteReg(0x02,DEC_BCD(hour));
 
    DS3231_WriteReg(0x01,DEC_BCD(min));
 
    DS3231_WriteReg(0x00,DEC_BCD(sec));
 
}	
/**
  * 函    数:DS3231获取数据
  * 参    数:year--年,month--月,date--日,week--周几,hour--时,min--分,sec--秒,
             使用输出参数的形式返回,范围:0-255
  * 返 回 值:无
  */
void DS3231_GetTime(u8 *year, u8 *month, u8 *date, u8 *week, u8 *hour, u8 *min, u8 *sec)
{
    *year=DS3231_ReadReg(0x06);  
    *year=BCD_DEC(*year);
 
    *month=DS3231_ReadReg(0x05); 
    *month=BCD_DEC(*month);
 
    *date=DS3231_ReadReg(0x04);  
    *date=BCD_DEC(*date);
 
    *week=DS3231_ReadReg(0x03);  
    *week=BCD_DEC(*week);
 
    *hour=DS3231_ReadReg(0x02); 
    *hour&=0x3f;  //设定为12小时制的时候起作用,去除小时制设定位的读取                 
    *hour=BCD_DEC(*hour);
 
    *min=DS3231_ReadReg(0x01);
    *min=BCD_DEC(*min);
 
    *sec=DS3231_ReadReg(0x00);
    *sec=BCD_DEC(*sec);
}

main.c

#include "stm32f10x.h"
#include "Delay.h"
#include "DS3231.h" 
#include <string.h>
#include "OLED.h"
int main(void)
{
    u8 year;
    u8 month;
    u8 date;
    u8 week;
    u8 hour;
    u8 min;
    u8 sec;
    OLED_Init();
    DS3231_Init(); 	
   //DS3231_SetTime(24, 10, 15, 2, 20, 02, 50);    //设置时间,首次可以手动设置时间进行校准,设置完成后,就不需要再重新设置了,模块断电后有电池持续计时
                                                  //这里设置的是24小时制
  while(1) 
    {
        OLED_ShowString(1, 4, "DS3231_RTC");
        DS3231_GetTime(&year, &month, &date, &week, &hour, &min, &sec);		//获取时间
        //年,月,日
        OLED_ShowNum(2, 4, year+2000, 4); 
        OLED_ShowString(2, 8, ".");        
        OLED_ShowNum(2, 9, month, 2);
        OLED_ShowString(2, 11, ".");
        OLED_ShowNum(2, 12, date, 2);
        //时,分,秒
        OLED_ShowNum(3, 5, hour, 2);
        OLED_ShowString(3, 7, ":");
        OLED_ShowNum(3, 8, min, 2);
        OLED_ShowString(3, 10, ":");
        OLED_ShowNum(3, 11, sec, 2);
        //周
        OLED_ShowString(4, 6, "week:"); 
        OLED_ShowNum(4, 11, week, 1);
    }
}

测试现象

OLED时间显示

逻辑分析仪抓取IIC数据,这里只截取了部分数据帧,是主机读取单个寄存器的完整过程,可以看到发送起始信号后,发送模块的地址等待从机响应,收到响应后再发送要读取的寄存器地址进行读取,然后结束。

总结

  1. 小时寄存器有12小时制的设定,所以需要在这里对取出的数据进行做处理,和0x3f做与算换后得出结果。
  2. 逻辑分析仪注意使用的时候,采样数和采样频率成10的整数倍进行取样。
© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号