一篇文章教会你DS1302读取实时时钟,附STM32代码示例

目录

一 、DS1302介绍:

(1)产品特点:

(2)引脚分配:

(3)说明:

(4)原理框图:

(5)信号描述:

(6)命令字节:

(7)复位和时钟控制:

(8)时钟/日历

(9)时钟停止标志:

(10)AM-PM/12-24小时模式选择:

(11)写保护位:

(12)涓流充电寄存器:

(13)时钟/日历爆发模式

(14)数据传输汇总:

(15)注意事项:

二、BCD:

(1)BCD编码示例:

(2)多位数的BCD表示:

(3)DS1302中的BCD编码:

三、代码编写:

(1)引脚初始化:

(2)DS1302写入字节:

 (3)DS1302读取字节:

(4)DS1302写入时钟:

(4)DS1302读取时钟:

(5)显示时钟:

(6)BCD处理:

(7)美化显示界面:

四、代码下载: 


一 、DS1302介绍:

DS1302读取时间

(1)产品特点:

  • 实时时钟计数秒,分钟,小时,月的日期,月,星期,年与闰年补偿有效到2100年
  • 31 × 8 RAM用于刮擦板数据存储
  • 串行I/O最小引脚计数
  • 2.0-5.5V满负荷运行
  • 在2.0V电压下使用小于300na
  • 单字节或多字节(突发模式)数据传输读或写时钟或内存数据
  • 8引脚DIP或可选的8引脚soic表面安装
  • 简单的3线接口
  • TTL兼容(Vcc = 5V)
  • 可选工业温度范围
  • -40℃~ +85℃
  • DS1202兼容

(2)引脚分配:

57787eae8049418b8b2fb06ee34de06a.png28b0745f507b4c388e5bfcc062e694ba.png

  • 管脚定义:

6667da6d736e4ae0b439505ffe0ee788.png

  • 图片: 

e3a971af81e14dcca446fddb51153c99.png61b065d5eb7843aca81cd3f5b2f4f877.png

(3)说明:

DS1302涓流充电计时芯片包含一个实时时钟/日历和31字节的静态RAM。它通过简单的串行接口与微处理器通信。实时时钟/日历提供秒、分、小时、日、日期、月和年信息。对于少于31天的月份,将自动调整月末日期,包括对闰年的更正。时钟运行在24小时或12小时格式与AM/PM指示器。

采用同步串行通信简化了DS1302与微处理器的接口,只需要三条线与时钟/RAM通信:

  • RST(复位)
  • I/O(数据线)
  • SCLK(串行时钟)

数据可以每次从时钟/RAM传输1个字节,或者以最多31个字节的速度传输。DS1302设计以非常低的功耗运行,并在小于1微瓦的功率下保留数据和时钟信息。

DS1302是DS1202的继承者。除了基本的计时功能外DS1202, DS1302具有用于主电源和备用电源的双电源引脚,用于VCC1的可编程涓流充电器和7个额外字节的刮刮板存储器的附加功能。

(4)原理框图:

串行计时器的主要元件如图所示:移位寄存器、控制逻辑、振荡器、实时时钟和RAM。

7d3468dba78f472d8f735b2f434467cc.png

(5)信号描述:

Vcc1:Vcc1 在单电源和电池供电系统中提供低功耗操作,以及低功耗电池备份。在使用涓流充电器的系统中,可充电的能源连接到这个引脚上。

Vcc2:Vcc2 是双电源配置中的主要电源引脚。Vcc1 连接到备用电源,在没有主电源的情况下维持时间和日期。DS1302 将从 Vcc1 或 Vcc2 中较大的一个供电。当 Vcc2 大于 Vcc1 + 0.2V 时,Vcc2 将为 DS1302 提供电力。当 Vcc2 小于 Vcc1 时,Vcc1 将为 DS1302 提供电力。

SCLK(串行时钟输入):SCLK 用于同步串行接口上的数据移动。

I/O(数据输入/输出):I/O 引脚是三线接口的双向数据引脚。

RST(复位):在读取或写入期间,复位信号必须被置为高电平。

X1, X2:标准 32.768 kHz 石英晶体的连接引脚。

内部振荡器:设计用于与具有 6 pF 指定负载电容的晶体一起工作。

DS1302 也可以由外部 32.768 kHz 振荡器驱动。在这种配置中,X1 引脚连接到外部振荡器信号,X2 引脚悬空。

(6)命令字节:

命令字节如图所示。每次数据传输都由一个命令字节发起。最高有效位(Bit 7)必须为逻辑1。如果为0,则对DS1302的写入将被禁用。位6用于指定时钟/日历数据(逻辑0)或RAM数据(逻辑1)。位1到位5用于指定要输入或输出的特定寄存器,而最低有效位(Bit 0)用于指定写操作(逻辑0,输入)或读操作(逻辑1,输出)。命令字节总是从最低有效位(Bit 0)开始输入。
注意:命令字节总是从LSB(位0)开始输入。

0884d80b023a4633af88c1b4b1b4ce5b.png

(7)复位和时钟控制:

所有数据传输都是通过将RST(复位)输入置高来启动的。RST输入有两个功能:

  1. RST激活控制逻辑,这允许访问移位寄存器来进行地址/命令序列的传输。

  2. RST信号提供了一种终止单字节或多字节数据传输的方法。

一个时钟周期是由一个下降沿后跟一个上升沿组成的序列。对于数据输入,沿期间有效,而数据位则在时钟数据必须在时钟的上升的下降沿输出。如果RST输入为低,所有数据传输将终止,并且I/O引脚将进入高阻抗状态。数据传输如图下所示。在上电时,RST必须保持逻辑0,直到Vcc(电源电压)大于2.0伏特。此外,当RST被置为逻辑1状态时,SCLK(串行时钟)也必须为逻辑0。

454ef7d81b104b0387af5d553a349eb2.png

数据输入:

  • 在输入写命令字节的8个SCLK周期之后,在下一个8个SCLK周期的上升沿上输入一个数据字节。如果无意中发生额外的SCLK周期,则忽略它们。数据从0位开始输入。

数据输出:

  • 在输入读命令字节的8个SCLK周期之后,在下一个8个SCLK周期的下降沿上输出一个数据字节。注意,要传输的第一个数据位发生在写入命令字节的最后一位之后的第一个下降沿上。额外的SCLK周期重传数据字节,如果他们无意中发生,只要RST保持高。此操作允许连续突发模式读取能力。此外,I/O引脚在SCLK的每个上升沿上都是三态的。数据从0位开始输出。

突发模式 :

  • 可以通过将地址设置为十进制31(地址/命令位1到5=逻辑1)来为时钟/日历或RAM寄存器指定突发模式 。与之前一样,位6指定时钟或RAM,位0指定读或写。在时钟/日历寄存器或RAM寄存器的地址9到31处没有数据存储能力。突发模式 下的读或写操作从地址0的位0开始。
  • 在突发模式下写入时钟寄存器时,必须按顺序写入前八个寄存器,以便传输数据。然而,在突发模式 下写入RAM时,不需要写入所有31个字节才能传输数据。每个被写入的字节都将被传输到RAM,无论是否写入了所有31个字节。

(8)时钟/日历

时钟/日历包含在七个读写寄存器中,如图表下所示。时钟/日历寄存器中的数据以二进制编码的十进制格式(BCD)存储。5015eae223124c93ace478f7d1ccb57d.png

(9)时钟停止标志:

秒寄存器的第7位被定义为时钟停止标志。当这个位被设置为逻辑1时,时钟振荡器将停止,DS1302将进入低功耗待机模式,电流消耗小于100纳安。当这个位被写入逻辑0时,时钟将开始运行。初始上电状态未定义。

(10)AM-PM/12-24小时模式选择:

小时寄存器的第7位被定义为12小时或24小时模式选择位。当设置为高电平时,选择12小时模式。在12小时模式下,第5位是AM/PM位,高电平表示PM。在24小时模式下,第5位是第二个10小时位(20-23小时)。

(11)写保护位:

控制寄存器的第7位是写保护位。前七位(第0位到第6位)在写入时被强制为0,并且在读取时总是读取为0。在对时钟或RAM进行任何写操作之前,第7位必须为0。当设置为高电平时,写保护位会阻止对任何其他寄存器的写操作。初始上电状态未定义。因此,在尝试写入设备之前,应清除写保护位。

(12)涓流充电寄存器:

该寄存器控制DS1302的涓流充电特性。下图为简化原理图显示了涓流充电器的基本组成部分。

b80dae595a9b4918becb92849f18af8a.png涓流充电选择(TCS)位(位4-7)控制涓流充电器的选择。为了防止意外启用,只有1010的模式才能启用涓流充电器。所有其他模式都将禁用涓流充电器。DS1302上电时涓流充电器处于禁用状态。二极管选择(DS)位(位2-3)选择Vcc2和Vcc1之间连接一个或两个二极管。如果DS为01,则选择一个二极管;如果DS为10,则选择两个二极管。如果DS为00或11,无论TCS如何,涓流充电器都将被禁用。RS位(位0-1)选择连接在Vcc2和Vcc1之间的电阻。电阻选择(RS)位选择的电阻如下表所示:

RS位电阻典型值
00
01R12 kΩ
10R24 kΩ
11R38 kΩ

如果RS为00,无论TCS如何,涓流充电器都将被禁用。

二极管和电阻的选择由用户根据电池或超级电容充电所需的最大电流来确定。最大充电电流可以通过以下示例计算。假设系统电源为5伏,应用于Vcc2,超级电容连接到Vcc1。还假设涓流充电器已启用,Vcc2和Vcc1之间有一个二极管和电阻R1。最大电流Imax的计算如下:

4d343ddbcda24854b41a50cbe2e464d0.png

显然,随着超级电容的充电,Vcc2和Vcc1之间的电压降将减少,因此充电电流将减少。

(13)时钟/日历爆发模式

时钟/日历命令字节指定了爆发模式操作。在这种模式下,可以从地址0的位0开始,连续读取或写入前八个时钟/日历寄存器(见图4)。5015eae223124c93ace478f7d1ccb57d.png

如果在指定写入时钟/日历突发模式时将写保护位设置为高,则不会对任何一个八个时钟/日历寄存器(包括控制寄存器)进行数据传输。在突发模式下无法访问涓流充电器。

在时钟突发模式读取开始时,当前时间会被传输到第二组寄存器中。时间信息从这些辅助寄存器中读取,而时钟可以继续运行。这消除了在读取过程中主要寄存器更新时重新读取寄存器的需要。

(14)数据传输汇总:

单字节读取:

fe64d0de358f448b997e3e00dc379cf2.png

单字节写入:

05a21844f3a04571a8ade405239615c6.png

在突发模式下,RST保持高位,并发送额外的SCLK周期,直到突发结束。

寄存器地址/定义:

5015eae223124c93ace478f7d1ccb57d.png

极限参数:

  • 任意引脚相对于地的电压-0.5V至+7.0V
  • 工作温度0°C至70°C或- 40°C至+85°C用于工业
  • 存储温度-55℃~ +125℃
  • 焊接温度260°C 10秒(DIP)

这只是一个应力额定值,并不暗示设备在这些或高于本说明书操作部分所述的任何其他条件下的功能操作。长时间暴露在绝对最大额定值条件下可能会影响可靠性

推荐直流工作条件:

46cf0c5435e64cf3b5f11a3fa24e816d.png

直流电气特性:

6c3627b628994bc3b13b4e8373783a3a.png

电容:(tA = 25ºC)

13e76222f5f443f7b234ad22c9e790f1.png 

交流电气特性:

 79519d9669534d029e096e290495dbdb.png

时序图:读取数据传输图:

bcc802bccf18495099825b46636d9fc1.png时序图:写数据传输图

8ced472228474cccacb9307344d333ca.png

(15)注意事项:

  • 所有电压都是参考地(ground)的。

  • 在Vcc=5V时,逻辑1的电压在1 mA的源电流下指定;在Vcc=2.0V时,逻辑1的电压在0.4 mA的源电流下指定,对于电容性负载,VoH=Vcc。

  • 在Vcc=5V时,逻辑0的电压在4 mA的汇电流下指定;在Vcc=2.0V时,逻辑0的电压在1.5 mA的汇电流下指定,对于电容性负载,VoL=GND。

  • Icc1T和Icc2T在I/O引脚开路,RST设置为逻辑“0”,时钟停止标志=0(振荡器启用)的情况下指定。

  • Icc1A和Icc2A在I/O引脚开路,RST高电平,Vcc=5V时SCLK=2 MHz;Vcc=2.0V时SCLK=500 kHz,且时钟停止标志=0(振荡器启用)的情况下指定。

  • RST、SCLK和I/O都有40 kΩ的下拉电阻到地。

  • 在Vih=2.0V或Vil=0.8V以及最大上升和下降时间为10 ns的条件下测量。

  • 在VoH=2.4V或VoL=0.4V的条件下测量。

  • 负载电容=50 pF。

  • Icc1S和Icc2S在RST、I/O和SCLK引脚开路,时钟停止标志必须设置为逻辑1(振荡器禁用)的情况下指定。

  • 当Vcc2>Vcc1 +0.2V时,Vcc=Vcc2;当Vcc1>Vcc2时,Vcc=Vcc1。

  • Vcc2=0V。

  • Vcc1=0V。

  • 典型值是在25°C下测量的。

二、BCD:

        以二进制编码的十进制格式(BCD,Binary-Coded Decimal)是一种编码方式,用于将十进制数字(0-9)表示为二进制形式。在BCD编码中,每个十进制数字用一个4位二进制数来表示。这种方式在处理数字显示和某些硬件接口时非常有用,因为它可以直接映射到十进制数字,便于阅读和理解。

(1)BCD编码示例:

  • 十进制数字 0 在BCD编码中表示为 0000

  • 十进制数字 1 在BCD编码中表示为 0001

  • 十进制数字 2 在BCD编码中表示为 0010

  • ...

  • 十进制数字 9 在BCD编码中表示为 1001

(2)多位数的BCD表示:

对于多位数,每个十进制位都用一个4位二进制数表示。例如:

  • 十进制数 123 在BCD编码中表示为 0001 0010 0011

  • 十进制数 456 在BCD编码中表示为 0100 0101 0110

(3)DS1302中的BCD编码:

DS1302时钟芯片使用BCD编码来存储和传输时间数据。这意味着每个时间字段(秒、分、时、日、月、年)都是以BCD格式存储的。例如:

  • 秒:59 在BCD编码中表示为 0101 1001

  • 分:30 在BCD编码中表示为 0011 0000

  • 时:10 在BCD编码中表示为 0001 0000

  • 日:13 在BCD编码中表示为 0001 0011

  • 月:1 在BCD编码中表示为 0000 0001

  • 年:25 在BCD编码中表示为 0010 0101

三、代码编写:

(1)引脚初始化:

//DS1302初始化函数
void DS1302_Init(void) 
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  		//使能GPIOA时钟
    
    GPIO_InitStructure.GPIO_Pin = RST_PIN | SCK_PIN; 			//GPIO_Pin_3 | GPIO_Pin_1
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 			//推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DS1302_PORT, &GPIO_InitStructure);
    
	DS1302_Mode(0);												//推挽输出
	
    GPIO_ResetBits(DS1302_PORT, RST_PIN); 						//RST脚置低
    GPIO_ResetBits(DS1302_PORT, SCK_PIN); 						//SCK脚置低
}

//I/O口模式选择
void DS1302_Mode(int Mode) 
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  				//使能GPIOA时钟
	
	GPIO_InitStructure.GPIO_Pin = IO_PIN ;								//GPIO_Pin_2
    if(Mode==0){
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 				//推挽输出
	}else if(Mode==1){
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;			//设置为浮空输入模式
	}
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	
    GPIO_Init(DS1302_PORT, &GPIO_InitStructure);
}
  • DS1302_Init 函数用于初始化 DS1302 时钟芯片的通信接口,配置 RST 和 SCK 引脚为推挽输出模式,并将 I/O 引脚初始设置为推挽输出模式。

  • DS1302_Mode 函数用于动态切换 I/O 引脚的模式,根据需要将其设置为推挽输出模式或浮空输入模式,以支持数据的写入和读取操作。

(2)DS1302写入字节:

454ef7d81b104b0387af5d553a349eb2.png

//向DS1302写入一字节数据
void DS1302_AddrWrite_byte(uint8_t addr, uint8_t data) 
{
    uint8_t i;
    GPIO_SetBits(DS1302_PORT, RST_PIN); 				//启动DS1302总线,拉高RST
    
    //写入目标地址:addr
    addr = addr & 0xFE; 								//最低有效位0,寄存器0位为0时写入,为1时读出
    for (i = 0; i < 8; i++) {
        if (addr & 0x01) { 								//若为1,拉高I/O引脚,写入1
            GPIO_SetBits(DS1302_PORT, IO_PIN);
        } else { 										//若为0,拉低I/O引脚,写入0
            GPIO_ResetBits(DS1302_PORT, IO_PIN);
        }
        addr = addr >> 1; 								//低位先写入,右移一位
        GPIO_SetBits(DS1302_PORT, SCK_PIN); 			//产生时钟,8个SCLK周期的上升沿上输入一个数据字节
        Delay_us(2);
        GPIO_ResetBits(DS1302_PORT, SCK_PIN);    
    }  
    
    //写入数据:data
    for (i = 0; i < 8; i++) {
        if (data & 0x01) { 								//若为1,拉高I/O引脚,写入1
            GPIO_SetBits(DS1302_PORT, IO_PIN);
        } else { 										//若为0,拉低I/O引脚,写入0
            GPIO_ResetBits(DS1302_PORT, IO_PIN);
        }
        data = data >> 1; 								//低位先写入,右移一位
        GPIO_SetBits(DS1302_PORT, SCK_PIN); 			//产生时钟,8个SCLK周期的上升沿上输入一个数据字节
        Delay_us(2);
        GPIO_ResetBits(DS1302_PORT, SCK_PIN);        
    }
    GPIO_ResetBits(DS1302_PORT, RST_PIN); 				//停止DS1302总线
}
  • 启动DS1302总线:通过拉高RST引脚来启动DS1302的通信。

  • 写入目标地址:将地址的最低位设置为0(表示写操作),然后通过8次SCLK时钟周期,将地址的每一位依次写入DS1302。

  • 注意:命令字节总是从LSB(位0)开始输入。

  • 写入数据:同样通过8次SCLK时钟周期,将数据的每一位依次写入DS1302。

  • 停止DS1302总线:通过拉低RST引脚来结束通信。

 (3)DS1302读取字节:

454ef7d81b104b0387af5d553a349eb2.png

//从DS1302读出一字节数据
uint8_t DS1302_Read_byte(uint8_t addr) 
{
    uint8_t i , Data = 0; 
    GPIO_SetBits(DS1302_PORT, RST_PIN); 			//启动DS1302总线,拉高RST
    DS1302_Mode(0);									//推挽输出
    //写入目标地址:addr
    addr = addr | 0x01; 							//最低位置1,寄存器0位为0时写入,为1时读出
    for (i = 0; i < 8; i++) {
        if (addr & 0x01) {
            GPIO_SetBits(DS1302_PORT, IO_PIN);		//若为1,拉高I/O引脚,写入1
        }
        else {
            GPIO_ResetBits(DS1302_PORT, IO_PIN);	//若为0,拉低I/O引脚,写入0
        }
		addr = addr >> 1;							//低位先写入,右移一位
        GPIO_SetBits(DS1302_PORT, SCK_PIN);			//产生时钟,8个SCLK周期的上升沿上输入一个数据字节
		Delay_us(2);
        GPIO_ResetBits(DS1302_PORT, SCK_PIN);       //该最后一个下降沿将输出第一位要读取的数据(最低位)
      							
    }  
 	
	DS1302_Mode(1);	
	
    for (i = 0; i < 8; i++) {
		Data = Data >> 1;
        if (GPIO_ReadInputDataBit(DS1302_PORT, IO_PIN)) {
            Data |= 0x80;											//数据从0位开始输出。
        }else {
			Data &= 0x7F;
		}
		
		GPIO_SetBits(DS1302_PORT, SCK_PIN);
		Delay_us(2);
        GPIO_ResetBits(DS1302_PORT, SCK_PIN);
    }   
	GPIO_ResetBits(DS1302_PORT, RST_PIN); 							//停止DS1302总线
    return Data;
	
}
  • 启动DS1302总线:通过拉高RST引脚来启动DS1302的通信。

  • 设置I/O引脚为推挽输出模式:以便写入目标地址。

  • 注意:DS1302_Mode(0)不可舍去。

  • 写入目标地址:将地址的最低位设置为1(表示读操作),然后通过8次SCLK时钟周期,将地址的每一位依次写入DS1302。

  • 设置I/O引脚为浮空输入模式:以便读取数据。

  • 读取数据:通过8次SCLK时钟周期,依次读取数据的每一位,并将其组合成一个字节。

  • 停止DS1302总线:通过拉低RST引脚来结束通信。

(4)DS1302写入时钟:

//向DS1302写入时钟数据
void DS1302_Write_time(void) 
{
    DS1302_AddrWrite_byte(DS1302_control_addr, 0x00); //关闭写保护
    DS1302_AddrWrite_byte(DS1302_charger_addr, 0xA9); //涓流充电

    DS1302_AddrWrite_byte(DS1302_year_addr, time_buf[1]);  //年 
    DS1302_AddrWrite_byte(DS1302_month_addr, time_buf[2]); //月 
    DS1302_AddrWrite_byte(DS1302_date_addr, time_buf[3]);  //日 
    DS1302_AddrWrite_byte(DS1302_hr_addr, time_buf[4]);    //时 
    DS1302_AddrWrite_byte(DS1302_min_addr, time_buf[5]);   //分
    DS1302_AddrWrite_byte(DS1302_sec_addr, time_buf[6]);   //秒
    DS1302_AddrWrite_byte(DS1302_day_addr, time_buf[7]);   //周 

    DS1302_AddrWrite_byte(DS1302_control_addr, 0x80); //打开写保护并启动时钟
}

将指定的时间数据写入DS1302时钟芯片的各个寄存器中,确保时钟芯片能够正确地记录和更新时间。通过关闭写保护、写入时间数据、设置涓流充电和重新打开写保护,确保了数据的完整性和时钟的正常运行。

(4)DS1302读取时钟:

//从DS1302读出时钟数据
void DS1302_Read_time(void)  
{
    read_time[1] = DS1302_Read_byte(DS1302_year_addr);   //年 
    read_time[2] = DS1302_Read_byte(DS1302_month_addr);  //月 
    read_time[3] = DS1302_Read_byte(DS1302_date_addr);   //日 
    read_time[4] = DS1302_Read_byte(DS1302_hr_addr);     //时 
    read_time[5] = DS1302_Read_byte(DS1302_min_addr);    //分 
    read_time[6] = DS1302_Read_byte(DS1302_sec_addr);    //秒
    read_time[7] = DS1302_Read_byte(DS1302_day_addr);    //周 
}

用于从DS1302时钟芯片的各个寄存器中读取当前的时钟数据,并将这些数据存储在 read_time 数组中。这样,程序可以方便地访问和使用这些时间数据,例如显示在OLED屏幕上或进行其他处理。

(5)显示时钟:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "DS1302.h"

extern uint8_t read_time[8];

int main(void)
{
    /*OLED初始化*/
    OLED_Init();
    DS1302_Init(); 
    Delay_ms(100);
    DS1302_Write_time(); // 写入初始时间
 
    while (1)
    {
        DS1302_Read_time(); // 定期读取时间

        OLED_ShowHexNum(0, 0, read_time[1], 2, OLED_8X16);    //年
        OLED_ShowHexNum(32, 0, read_time[2], 2, OLED_8X16);   //月
        OLED_ShowHexNum(64, 0, read_time[3], 2, OLED_8X16);   //日
        OLED_ShowHexNum(96, 0, read_time[4], 2, OLED_8X16);   //时
        OLED_ShowHexNum(0, 16, read_time[5], 2, OLED_8X16);   //分
        OLED_ShowHexNum(32, 16, read_time[6], 2, OLED_8X16);  //秒
        OLED_ShowHexNum(64, 16, read_time[7], 2, OLED_8X16);  //周     
        OLED_Update();
        
    }
}

 

DS1302时钟显示

初始化OLED显示屏和DS1302时钟芯片,然后进入一个无限循环,读取DS1302中的时间数据,并将这些数据以十六进制形式显示在OLED屏幕上。这样,可以实时查看当前的时间。

(6)BCD处理:

        写入和读取DS1302时钟数据时,需要确保数据是以BCD格式处理的。将十进制数转换为BCD格式,以及如何将BCD格式的数转换回十进制数:

  • 十进制转BCD:

uint8_t Dec_to_BCD(uint8_t val) {
    return ((val / 10) << 4) + (val % 10);
}
  • BCD转十进制:

uint8_t BCD_to_Dec(uint8_t val) {
    return ((val >> 4) * 10) + (val & 0x0F);
}
  • 代码示例:

在写入时间时,将十进制数转换为BCD格式:

void DS1302_Write_time(void) 
{
    DS1302_AddrWrite_byte(DS1302_control_addr, 0x00); //关闭写保护
    DS1302_AddrWrite_byte(DS1302_charger_addr, 0xA9); //涓流充电

    DS1302_AddrWrite_byte(DS1302_year_addr, Dec_to_BCD(time_buf[1]));  //年 
    DS1302_AddrWrite_byte(DS1302_month_addr, Dec_to_BCD(time_buf[2])); //月 
    DS1302_AddrWrite_byte(DS1302_date_addr, Dec_to_BCD(time_buf[3]));  //日 
    DS1302_AddrWrite_byte(DS1302_hr_addr, Dec_to_BCD(time_buf[4]));    //时 
    DS1302_AddrWrite_byte(DS1302_min_addr, Dec_to_BCD(time_buf[5]));   //分
    DS1302_AddrWrite_byte(DS1302_sec_addr, Dec_to_BCD(time_buf[6]));   //秒
    DS1302_AddrWrite_byte(DS1302_day_addr, Dec_to_BCD(time_buf[7]));   //周 

    DS1302_AddrWrite_byte(DS1302_control_addr, 0x80); //打开写保护并启动时钟
}

在读取时间时,将BCD格式的数转换回十进制数:

void DS1302_Read_time(void)  
{
    read_time[1] = BCD_to_Dec(DS1302_Read_byte(DS1302_year_addr));   //年 
    read_time[2] = BCD_to_Dec(DS1302_Read_byte(DS1302_month_addr));  //月 
    read_time[3] = BCD_to_Dec(DS1302_Read_byte(DS1302_date_addr));   //日 
    read_time[4] = BCD_to_Dec(DS1302_Read_byte(DS1302_hr_addr));     //时 
    read_time[5] = BCD_to_Dec(DS1302_Read_byte(DS1302_min_addr));    //分 
    read_time[6] = BCD_to_Dec(DS1302_Read_byte(DS1302_sec_addr));    //秒
    read_time[7] = BCD_to_Dec(DS1302_Read_byte(DS1302_day_addr));    //周 
}
  • 实际应用:

    • time_buf[8]修改为十进制值。
    • uint8_t time_buf[8] = {0,25,1,13,10,30,59,1}; 
    • 初始时间定义,2025年1月13号10点30分59秒周一
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "DS1302.h"

extern uint8_t read_time[8];

int main(void)
{
    /*OLED初始化*/
    OLED_Init();
    DS1302_Init(); 
    Delay_ms(100);
    DS1302_BCDWrite_time(); // 写入初始时间
 
    while (1)
    {
        DS1302_tenRead_time(); // 定期读取时间

//        OLED_ShowHexNum(0, 0, read_time[1], 2, OLED_8X16);    //年
//        OLED_ShowHexNum(32, 0, read_time[2], 2, OLED_8X16);   //月
//        OLED_ShowHexNum(64, 0, read_time[3], 2, OLED_8X16);   //日
//        OLED_ShowHexNum(96, 0, read_time[4], 2, OLED_8X16);   //时
//        OLED_ShowHexNum(0, 16, read_time[5], 2, OLED_8X16);   //分
//        OLED_ShowHexNum(32, 16, read_time[6], 2, OLED_8X16);  //秒
//        OLED_ShowHexNum(64, 16, read_time[7], 2, OLED_8X16);  //周 

        OLED_ShowNum(0, 0, read_time[1], 2, OLED_8X16);    //年
        OLED_ShowNum(32, 0, read_time[2], 2, OLED_8X16);   //月
        OLED_ShowNum(64, 0, read_time[3], 2, OLED_8X16);   //日
        OLED_ShowNum(96, 0, read_time[4], 2, OLED_8X16);   //时
        OLED_ShowNum(0, 16, read_time[5], 2, OLED_8X16);   //分
        OLED_ShowNum(32, 16, read_time[6], 2, OLED_8X16);  //秒
        OLED_ShowNum(64, 16, read_time[7], 2, OLED_8X16);  //周 		
        OLED_Update();
        
    }
}

(7)美化显示界面:

将一个表示星期几的数字(1到7)转换为对应的英文星期字符串。

// 函数:将数字转换为英文星期字符串
char* getDayOfWeek(int day) {
    char* daysOfWeek[] = {
        "Invalid",
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday",
        "Saturday",
        "Sunday"
    };

    if (day < 1 || day > 7) {
        return "Invalid";
    }

    return daysOfWeek[day];
}
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "DS1302.h"

extern uint8_t read_time[8];

int main(void)
{
    /*OLED初始化*/
    OLED_Init();
    DS1302_Init(); 
    Delay_ms(100);
    DS1302_BCDWrite_time(); // 写入初始时间
 
    while (1)
    {
        DS1302_tenRead_time(); // 定期读取时间

//        OLED_ShowHexNum(0, 0, read_time[1], 2, OLED_8X16);    //年
//        OLED_ShowHexNum(32, 0, read_time[2], 2, OLED_8X16);   //月
//        OLED_ShowHexNum(64, 0, read_time[3], 2, OLED_8X16);   //日
//        OLED_ShowHexNum(96, 0, read_time[4], 2, OLED_8X16);   //时
//        OLED_ShowHexNum(0, 16, read_time[5], 2, OLED_8X16);   //分
//        OLED_ShowHexNum(32, 16, read_time[6], 2, OLED_8X16);  //秒
//        OLED_ShowHexNum(64, 16, read_time[7], 2, OLED_8X16);  //周 

//        OLED_ShowNum(0, 0, read_time[1], 2, OLED_8X16);    //年
//        OLED_ShowNum(32, 0, read_time[2], 2, OLED_8X16);   //月
//        OLED_ShowNum(64, 0, read_time[3], 2, OLED_8X16);   //日
//        OLED_ShowNum(96, 0, read_time[4], 2, OLED_8X16);   //时
//        OLED_ShowNum(0, 16, read_time[5], 2, OLED_8X16);   //分
//        OLED_ShowNum(32, 16, read_time[6], 2, OLED_8X16);  //秒
//        OLED_ShowNum(64, 16, read_time[7], 2, OLED_8X16);  //周 	

		OLED_ShowNum(0, 0, 20, 2, OLED_8X16);    						//年
		OLED_ShowNum(16, 0, read_time[1], 2, OLED_8X16);    			//年
		OLED_ShowString(32, 0, "-", OLED_8X16);
		OLED_ShowNum(48, 0, read_time[2], 2, OLED_8X16);   			//月
		OLED_ShowString(64, 0, "-", OLED_8X16);
		OLED_ShowNum(80, 0, read_time[3], 2, OLED_8X16);   			//日
		
		
		OLED_ShowNum(16, 16, read_time[4], 2, OLED_8X16);    			//时
		OLED_ShowString(32, 16, "-", OLED_8X16);
		OLED_ShowNum(48, 16, read_time[5], 2, OLED_8X16);   			//分
		OLED_ShowString(64, 16, "-", OLED_8X16);
		OLED_ShowNum(80, 16, read_time[6], 2, OLED_8X16);   			//秒
		
		OLED_ShowString(32, 48, getDayOfWeek(read_time[7]),OLED_8X16); // 显示星期
        OLED_Update();
        
    }
}

 

DS1302读取时间

四、代码下载: 

通过网盘分享的文件:32-DS1302时钟模块读取
链接: https://pan.baidu.com/s/1DPk5l4aOvTZ2j-CcyowRxw?pwd=5rhd 提取码: 5rhd

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

The_xzs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值