0

0

0

修罗

站点介绍

只有了解事实才能获得真正的自由

DS1302时钟

修罗 2025-08-08 84 5条评论 51单片机

首页 / 正文

DS1302时钟

1.1 简介

DS1302是一个低功耗、实时时钟(RTC)芯片,如下图所示:

image-20250807150026407.png

1.2 引脚功能

32.768kHz分频15次即频率为1hz,一秒一次

image-20250807150047253.png

引脚号名称方向功能说明
1VCC2电源输入主电源(+5V 或 +3.3V),通常是系统电源供给
2X1输入32.768kHz 晶振的第一端(接晶振)
3X2输出32.768kHz 晶振的第二端
4GND电源地线(0V)
5CE输入芯片使能信号(Chip Enable,高电平有效),通信时需先拉高
6I/O双向数据线(串行输入/输出),命令、数据都通过它传输
7SCLK输入串行时钟信号(上升沿或下降沿采样,DS1302在上升沿接收数据)
8VCC1电源输入备用电源(接纽扣电池,掉电时维持时钟运行)

1.3 基本工作原理

基本组成结构

image-20250807151456245.png

工作流程

image-20250807150552804.png

1.4 通信协议

​ 根据上述的工作流程可知,使用DS1302时,核心操作就是读写寄存器。

1.4.1 写操作

​ 以下是向寄存器写入单个字节的时序图。

  1. 发送数据前,需要将CE拉高,使能通信。需要注意的是,拉高CE时,需确保SCLK处于低电平

image-20250807173941108.png

  1. 需要先发送命令字节,再发送数据字节,发送时都是低位优先

image-20250807174030287.png

  1. 命令字节用于指定寄存器地址以及声明读/写操作,各位的含义如下

image-20250807174126137.png

  1. 数据字节为发送的具体数据,每位数据都需要在一个时钟周期上升沿发送,意味着在上升沿之前要准备数据

image-20250807174235288.png

1.4.2 读操作

​ 以下是从寄存器读取单个字节的时序图:

  1. 接收数据前,需要将CE拉高,使能通信。需要注意的是,拉高CE时,需确保SCLK处于低电平

image-20250807174421431.png

  1. 需要先发送命令字节,再接受数据字节,发送和接收时都是低位优先

image-20250807174449321.png

  1. 发送每位数据要在时钟信号的上升沿,读取每位数据要在时钟信号的下降沿

image-20250807174504136.png

​ 注意:第8个时钟周期,需要完成最后1位数据的发送和第1位数据的接收

image-20250807174524544.png

1.4.3 常用寄存器

​ 以下是各时间寄存器的地址和每位的定义:

1)中文版

image-20250807174633302.png

2)英文版

image-20250807174655344.png

#寄存器名称读地址写地址位分布说明范围或含义
1秒寄存器81h80hBIT7: CH(1=停止,0=运行)BIT6~4: 十秒位(0–5)BIT3~0: 个位秒(0–9)00–59 秒
2分钟寄存器83h82hBIT6~4: 十分钟位(0–5)BIT3~0: 个位分钟(0–9)00–59 分钟
3小时寄存器85h84hBIT7: 12/24 小时制(1=12hr,0=24hr);BIT6: 保留,固定0; BIT5: AM/PM(12hr制下,1=PM,0=AM);BIT4~0: 小时数值1–12 / 0–23 小时
4日期(日)寄存器87h86hBIT5~0: 日期1–31
5月份寄存器89h88hBIT4~0: 月份1–12
6星期寄存器8Bh8AhBIT2~0: 星期1–7
7年寄存器8Dh8ChBIT7~0: 年份(低两位)00–99
8写保护寄存器(WP)8Fh8EhBIT7: WP(1=禁止写操作,0=允许写)

注意:在向DS1302写入数据前需要先关闭写保护,写完再开启写保护。

1.5 相关资料

(1)购买链接:https://item.szlcsc.com/15455.html

(2)实物图、原理图、封装

image-20250807175848253.png

如何设置和显示时间信息?

image-20250807180052828.png

1.6 硬件原理图

image-20250807180138141.png

1.7 软件设计

1.7.1 初始化

初始化 DS1302 RTC 芯片 ,上电时先给它一个已知的稳定状态

void Int_DS1302_Init(void)
{
    SCLK = 0; // 时钟信号
    IO   = 0; // 数据
    CE   = 0; // 使能
}

1.7.2 写数据

DS1302 写 1 个字节数据

static void Int_DS1302_WriteByte(u8 byte)
{
    u8 i;
    for (i = 0; i < 8; i++) { //  执行 8 次,依次把这个字节的 最低位(LSB)到最高位(MSB) 依序送给 DS1302
        IO   = byte & 0x01; // 准备数据,取当前字节的最低位
        SCLK = 1;
        SCLK = 0; // 先将 SCLK 时钟线拉高再拉低,产生一个上升沿/下降沿组合
        byte >>= 1; // 把 byte 右移一位,这样下一次循环时最低位就是原来的第二位
    }
}

1.7.3 读数据

DS1302 读取一个字节数据

static u8 Int_DS1302_ReadByte()
{
    u8 i, byte = 0;
    for (i = 0; i < 8; i++) { // 循环 8 次,每次从 DS1302 读 1 位, 读到的第一个位应该放在最低位
        byte >>= 1; // 右移一位,为即将读入的新位腾出最高位,第一次无所谓,反正都是0
        if (IO == 1) { // 如果是高电平(1),就把当前字节的 最高位 置 1
            byte = byte | 0x80;
        }
        SCLK = 1; // 产生一个时钟脉冲,通知 DS1302 送出下一位数据
        SCLK = 0; 
    }
    return byte;
}

1.7.4 按寄存器地址操作

定义写寄存器地址,读寄存器地址在写寄存器地址上加1即可

#define SECOND      0x80
#define MINUTE      0x82
#define HOUR        0x84
#define DAY         0x86
#define MONTH       0x88
#define DAY_OF_WEEK 0x8A
#define YEAR        0x8C
#define WP          0x8E

写寄存器函数

static void Int_DS1302_SetRegister(u8 addr, u8 byte)
{
    CE = 1;
    Int_DS1302_WriteByte(addr); // 先发寄存器地址
    Int_DS1302_WriteByte(byte); // 再发要写的数据
    CE = 0;
}

读寄存器函数

static u8 Int_DS1302_GetRegister(u8 addr)
{
    u8 byte;
    CE = 1;
    Int_DS1302_WriteByte(addr | 1); /// addr | 1 → 把寄存器地址最低位(BIT0)设置为 1,变成 读地址。
    byte = Int_DS1302_ReadByte(); // 调用 Int_DS1302_ReadByte() 读取 1 个字节
    CE   = 0;
    return byte;
}

1.7.5 设置 DS1302 日期时间

结构体定义, 用于存放时间日期的普通十进制值

typedef struct
{
    u8 second; // 0–59
    u8 minute; // 0–59
    u8 hour; // 0–23
    u8 day; // 1–31
    u8 month; // 1–12
    u8 year; // 0–99
    u8 day_of_week; // 1–7
} Struct_Date;

设置函数

void Int_DS1302_SetDate(Struct_Date *p_st_date)
{
    u8 temp;

    // 关闭写保护
    Int_DS1302_SetRegister(WP, 0x00);

    // 设置秒
    temp = (p_st_date->second % 10) | ((p_st_date->second / 10) << 4); // % 10 取个位,/ 10 取十位,<< 4 把十位移到 BCD 的高 4 位
    Int_DS1302_SetRegister(SECOND, temp);

    // 设置分钟
    temp = (p_st_date->minute % 10) | ((p_st_date->minute / 10) << 4);
    Int_DS1302_SetRegister(MINUTE, temp);

    // 设置小时
    temp = (p_st_date->hour % 10) | ((p_st_date->hour / 10) << 4);
    Int_DS1302_SetRegister(HOUR, temp);

    // 设置日
    temp = (p_st_date->day % 10) | ((p_st_date->day / 10) << 4);
    Int_DS1302_SetRegister(DAY, temp);

    // 设置月
    temp = (p_st_date->month % 10) | ((p_st_date->month / 10) << 4);
    Int_DS1302_SetRegister(MONTH, temp);

    // 设置年
    temp = (p_st_date->year % 10) | ((p_st_date->year / 10) << 4);
    Int_DS1302_SetRegister(YEAR, temp);

    // 设置星期
    Int_DS1302_SetRegister(DAY_OF_WEEK, p_st_date->day_of_week % 10);

    // 开启写保护
    Int_DS1302_SetRegister(WP, 0x80);
}

1.7.6 读取当前时间/日期

void Int_DS1302_GetDate(Struct_Date *p_st_date)
{
    u8 temp;
    // 获取秒
    temp              = Int_DS1302_GetRegister(SECOND);
    p_st_date->second = (temp >> 4) * 10 + (temp & 0x0F); // (temp >> 4) → 取高 4 位(十位数)(temp & 0x0F) → 取低 4 位(个位数)最终得到十进制的实际数值。

    temp              = Int_DS1302_GetRegister(MINUTE);
    p_st_date->minute = (temp >> 4) * 10 + (temp & 0x0F);

    temp            = Int_DS1302_GetRegister(HOUR);
    p_st_date->hour = (temp >> 4) * 10 + (temp & 0x0F);

    temp           = Int_DS1302_GetRegister(DAY);
    p_st_date->day = (temp >> 4) * 10 + (temp & 0x0F);

    temp             = Int_DS1302_GetRegister(MONTH);
    p_st_date->month = (temp >> 4) * 10 + (temp & 0x0F);

    temp            = Int_DS1302_GetRegister(YEAR);
    p_st_date->year = (temp >> 4) * 10 + (temp & 0x0F);

    temp                   = Int_DS1302_GetRegister(DAY_OF_WEEK);
    p_st_date->day_of_week = (temp >> 4) * 10 + (temp & 0x0F);
}

1.7.7 写入读取测试

定义星期格式化数组:

// WEEK_NAME[0] → "Mon",WEEK_NAME[6] → "Sun"
code char *WEEK_NAME[] = {
    "Mon",
    "Tue",
    "Wed",
    "Thu",
    "Fri",
    "Sat",
    "Sun"};

u8 xdata str[17]; // 时间字符串缓存
Struct_Date st_date;

// st_date 结构体为一个固定的时间(2025 年 8 月 7 日 17:28:30,星期四
st_date.year        = 25;
st_date.month       = 8;
st_date.day         = 7;
st_date.day_of_week = 4;
st_date.hour        = 17;
st_date.minute      = 28;
st_date.second      = 30;

// 初始化时钟并设置时间
Int_DS1302_Init();
Int_DS1302_SetDate(&st_date);

while (1) {
    
    Int_DS1302_GetDate(&st_date); // // 从 DS1302 读取当前时间
    
    // 组合成 "2025/08/07 Thu" 这样的字符串。可以将str显示在oled屏幕上
    sprintf(str, "20%02d/%02d/%02d %s",
            (int)st_date.year,
            (int)st_date.month,
            (int)st_date.day,
            WEEK_NAME[st_date.day_of_week - 1]);
    
    Delay_1MS(1000);
}

评论(5)

  1. 1 游客 2025-09-01 11:27 回复

    1

  2. 1 游客 2025-09-01 12:14 回复

    1

  3. @@XJimK 游客 2025-09-01 12:15 回复

    1

  4. 1 游客 2025-09-01 12:18 回复

    1

  5. 1 游客 2025-09-01 12:19 回复

    1


最新评论

  • 1

    1

  • 1

    1

  • -1' OR 2+158-158-1=0+0+0+1 or 'TKCTZnRa'='

    1

  • 1

    1

  • 1

    1

  • 1

    1

  • 1

    1

  • @@5Qa2D

    1

  • 1

    1

  • 1

    1

日历

2025年09月

 123456
78910111213
14151617181920
21222324252627
282930    

文章目录

推荐关键字: Linux webpack js 算法 MongoDB laravel JAVA jquery javase redis