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

模块简介

模块含有DS1307的I2C实时时钟芯片(RTC)和一个24C32 32K I2C EEPROM存储器,采用CR2032可充电锂电池,并带充电电路,充满电后,能提供DS1307计时1年。引出DS1307的时钟引脚,为单片机提供时钟信号,可以级联其他I2C设备。
DS1307是一款低功耗,具有56字节非失性RAM的全BCD码时钟日历实时时钟芯片,地址和数据通过两线双向的串行总线的传输,芯片可以提供秒,分,小时等信息,每一个月的天数能自动调整。并且有闰年补偿功能。AM/PM 标志位决定时钟工作于24小时或12小时模式,芯片有一个内置的电源感应电路,具有掉电检测和电池切换功能。

DS1307特点:可对秒,时,分,每月的天数,月份,每周的天数进行记数,并具有闰年补偿功能。记年上线2100。56字节非失性的RAM,两线串行接口,可编程方波输出,自动掉电检测和切换电路,在电池备份模式下,功耗小于500nA。
在这里插入图片描述
左上角三个孔是给温度传感器直插的,这里没有用到就没有加上了。

寄存器说明

在这里插入图片描述

寄存器存储的是BCD(8421)编码,读取后要转换成十进制进行显示。

前7个寄存器(00h-06h)都是和时间相关的,注意小时寄存器(02h)的第6位定义为12小时或24小时模式选择位。该位为高时,选择12小时模式。在12小时模式下,第5位为AM/PM指示位,逻辑高时为PM。在24小时模式下,第5位为20小时位(20至23小时)。

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

07H是用来定义SQ引脚的输出的,可以输出1Hz的方波/4.096KHz/8.192KHz/32.768KHz的频率输出。
在这里插入图片描述
模块左右两边的SCL和SDA是一样的,可以选择其中一边接上就可以,这里的IIC挂载了两个设备一个是DS1307,一个是24C32,两者的IIC从设备地址不同,需要和哪个模块通讯就对应的发送那个模块的从机地址进行通讯即可。
DS1307从机地址:1101 000 r/w (位r/w=0写,位r/w=1读)。
24C32 从机地址:1010 a2 a1 a0 r/w(位r/w=0写,位r/w=1读),模块已经默认a2,a1,a0都是接地的,所以这里都是为0。

代码说明

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

#ifndef _DS1307_H
#define _DS1307_H

void DS1307_WriteReg(uint8_t RegAddress, uint8_t Data);
uint8_t DS1307_ReadReg(uint8_t RegAddress);
void DS1307_Init(void);
void DS1307_SetTime(u8 year,u8 mon,u8 data,u8 week,u8 hour,u8 min,u8 sec);
void DS1307_GetTime(u8 *year, u8 *month, u8 *date, u8 *week, u8 *hour, u8 *min, u8 *sec);
void DS1307_SQSET(uint8_t sqmode);

#endif

ds1307.c

#include "stm32f10x.h"   // Device header
#include "DS1307.h"
#include "DS1307_Reg.h"
#include "MyI2C.h"

//DS1307从机地址:1101000b  b=0写   b=1读
#define DS1307_ADDRESS 	    0xD0//DS1307的地址

/**
  * 函    数:DS1307写寄存器
  * 参    数:RegAddress 寄存器地址
  * 参    数:Data 要写入寄存器的数据,范围:0x00~0xFF
  * 返 回 值:无
  */
void DS1307_WriteReg(uint8_t RegAddress, uint8_t Data)
{
    
    
	MyI2C_Start();						//I2C起始
	MyI2C_SendByte(DS1307_ADDRESS);	    //发送从机地址,读写位为0,表示即将写入
	MyI2C_ReceiveAck();					//接收应答
	MyI2C_SendByte(RegAddress);			//发送寄存器地址
	MyI2C_ReceiveAck();					//接收应答
	MyI2C_SendByte(Data);				//发送要写入寄存器的数据
	MyI2C_ReceiveAck();					//接收应答
	MyI2C_Stop();						//I2C终止
}

/**
  * 函    数:DS1307读寄存器
  * 参    数:RegAddress 寄存器地址
  * 返 回 值:读取寄存器的数据,范围:0x00~0xFF
  */
uint8_t DS1307_ReadReg(uint8_t RegAddress)
{
    
    
	uint8_t Data;
	
	MyI2C_Start();						//I2C起始
	MyI2C_SendByte(DS1307_ADDRESS);	    //发送从机地址,读写位为0,表示即将写入
	MyI2C_ReceiveAck();					//接收应答
	MyI2C_SendByte(RegAddress);			//发送寄存器地址
	MyI2C_ReceiveAck();					//接收应答
	
	MyI2C_Start();						    //I2C重复起始
	MyI2C_SendByte(DS1307_ADDRESS | 0x01);	//发送从机地址,读写位为1,表示即将读取
	MyI2C_ReceiveAck();					    //接收应答
	Data = MyI2C_ReceiveByte();			    //接收指定寄存器的数据
	MyI2C_SendAck(1);					    //发送应答,给从机非应答,终止从机的数据输出
	MyI2C_Stop();						    //I2C终止
	
	return Data;
}

void DS1307_Init(void)
{
    
    
	MyI2C_Init();									//先初始化底层的I2C
}

/**
  * 函    数: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 DS1307_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秒 
    DS1307_WriteReg(0x06,DEC_BCD(year));
    DS1307_WriteReg(0x05,DEC_BCD(mon));
    DS1307_WriteReg(0x04,DEC_BCD(da));
    DS1307_WriteReg(0x03,DEC_BCD(week));
    DS1307_WriteReg(0x02,DEC_BCD(hour));
    DS1307_WriteReg(0x01,DEC_BCD(min));
    DS1307_WriteReg(0x00,DEC_BCD(sec));
}	
/**
  * 函    数:DS3231获取数据
  * 参    数:year--年,month--月,date--日,week--周几,hour--时,min--分,sec--秒,
             使用输出参数的形式返回,范围:0-255
  * 返 回 值:无
  */
void DS1307_GetTime(u8 *year, u8 *month, u8 *date, u8 *week, u8 *hour, u8 *min, u8 *sec)
{
    
    
	*year=DS1307_ReadReg(0x06);  
	*year=BCD_DEC(*year);
 
	*month=DS1307_ReadReg(0x05); 
	*month=BCD_DEC(*month);
 
	*date=DS1307_ReadReg(0x04);  
	*date=BCD_DEC(*date);
 
	*week=DS1307_ReadReg(0x03);  
	*week=BCD_DEC(*week);
 
	*hour=DS1307_ReadReg(0x02); 
	*hour&=0x3f;  //设定为12小时制的时候起作用,去除小时制设定位的读取                 
	*hour=BCD_DEC(*hour);
 
	*min=DS1307_ReadReg(0x01);
	*min=BCD_DEC(*min);
 
	*sec=DS1307_ReadReg(0x00);
	*sec=BCD_DEC(*sec);
}

/**
  * 函    数:设置SQ输出
  * 参    数:设置模式0:1Hz
                     1:4.096KHz
                     2:8.192KHz
                     3:32.768KHz
  * 返 回 值:无
  */
void DS1307_SQSET(uint8_t sqmode)
{
    
    
    if(sqmode == 0)
        DS1307_WriteReg(0x07,0xF0);
    else if(sqmode == 1)
        DS1307_WriteReg(0x07,0xF1);
    else if(sqmode == 2)
        DS1307_WriteReg(0x07,0xF2);
    else if(sqmode == 3)
        DS1307_WriteReg(0x07,0xF3);
    else
       return;
}

main.c

#include "stm32f10x.h"
#include "Delay.h"
#include "DS1307.h"
#include "OLED.h"
#include "AT24C32.h"
#include "Serial.h"

//年  月  日  周  时  分  秒
u8 WriteBuffer[] = {
    
    25, 01, 15, 3, 16, 44, 00}; //2025年1月15日  周三  16时44分00秒
#define WriteAddr ((u16)0x0000)
#define countof(a) (sizeof(a) / sizeof(*(a)))
#define BufferSize1             (countof(WriteBuffer)-1)
u8 Read_Buffer[BufferSize1];

int main(void)
{
    
    
    u8 year;
    u8 month;
    u8 date;
    u8 week;
    u8 hour;
    u8 min;
    u8 sec;
    
    //初始化
    OLED_Init();
    AT24CXX_Init();
	DS1307_Init(); 	
    Serial_Init();
    //DS1307_SQSET(0);  //设置SQ输出,需要SQ输出的可以打开
    
    //检测时间是否已经初始化,未初始化则初始化时间,已初始化可以直接跳过初始化
    if((DS1307_ReadReg(0x00) >> 7) == 1)//如果未初始化,执行初始化时间
	{
    
    
        //设置时间,首次可以手动设置时间进行校准,设置完成后,就不需要再重新设置了,模块断电后有电池持续计时
		DS1307_SetTime(WriteBuffer[0], WriteBuffer[1], WriteBuffer[2], WriteBuffer[3], 
                       WriteBuffer[4], WriteBuffer[5], WriteBuffer[6]);  
		Serial_Printf("Initialization successful,flag=%d\r\n",(DS1307_ReadReg(0x00) >> 7));//初始化成功
	}	else{
    
    
		Serial_Printf("Time has already been initialized,flag=%d\r\n",(DS1307_ReadReg(0x00) >> 7));//时间已初始化过
    }
    
    //检测24C32是否正常
    if(AT24CXX_Check())
    {
    
    
        //检测不到24cxx
        Serial_Printf("AT24Cxx NO Ready!\r\n");
        Delay_ms(500);
    }
    else
    {
    
    
        //检测AT24C02是否正常
        Serial_Printf("AT24Cxx Ready!\r\n");
        AT24CXX_Write(WriteAddr, WriteBuffer, BufferSize1);
        Delay_ms(500);
        AT24CXX_Read(WriteAddr, Read_Buffer, BufferSize1);
    }
    //OLED显示标题
    OLED_ShowString(1, 4, "DS1307_RTC");

  while(1) 
	{
    
    
		DS1307_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的整数倍进行取样。
在这里插入图片描述
需要整个工程的可以在评论区留言邮箱哦!

猜你喜欢

转载自blog.csdn.net/qq_42250136/article/details/145163679
今日推荐