0

0

0

修罗

站点介绍

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

DS1302时钟

修罗 2025-08-08 407 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


最新评论

  • https://www.studyglobus.com/author/carroll34a5552/

    Ценники на вывоз мусора сейчас космос, любые лайфхаки по экономии в тему.

  • Richardgrold

    1xbet onlayn kazino platforması ilə vaxtınızı daha səmərəli və maraqlı keçirin. Saytın canlı kazino bölməsi istifadəçilərə fərqli bir dünya təklif edir. Burada canlı dilerli kazino oyunlarının hər bir növünü sınaqdan keçirə bilərsiniz. Krupyelərin peşəkarlığı oyunun dinamikasını və marağını artırır. Müntəzəm olaraq yenilənən oyun siyahısı cansıxıcılığa yol vermir. Hesabınızı artırmaq üçün onlarla fərqli və etibarlı üsul mövcuddur. Qazanılan pullar cəmi bir neçə dəqiqə ərzində şəxsi hesabınıza köçürülür. Aktiv fəaliyyətinizə görə fərdi bonuslar və hədiyyələr qazana bilərsiniz. Dəstək xidməti həm çat, həm də e-poçt vasitəsilə kömək göstərir. 1xbet ailəsinə qoşulun və böyük uduşlar üçün ilk addımı atın. onlayn kazino

  • 砂岩白丘

    我一直梦想, 看到你们相册那样的地方。真棒。

  • 城市地標

    我喜欢, 这里分享真实经验。你的项目 就是 属于这里的。请继续。

  • 宗教地標

    我热爱这样的想法, 去那么多国家。真的很鼓舞。

  • 觀景高地

    能感受到热爱。继续保持 感受。

  • 最佳季節

    我关注这样的资源, 写得很实在。你的网站 就是 属于这里的。加油。

  • 1

    1

  • 1

    1

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

    1

日历

2026年07月

   1234
567891011
12131415161718
19202122232425
262728293031 

文章目录

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