寒假第五讲(DS1302)

DS1302实时时钟模块学习笔记

1. 模块基础介绍

1.1 什么是DS1302?

DS1302是一款低功耗实时时钟芯片,可以记录年、月、日、时、分、秒等信息。最大的特点是:即使系统断电,也能通过备用电池保持计时

1.2 引脚定义(51单片机)

为什么要用sbit

  • 51单片机的IO口(如P0/P1/P2)是8位寄存器
  • 普通变量只能操作整个8位端口(比如P0=0xff
  • sbit 能直接定位到寄存器的某一位(P1^0 就是P1寄存器的第0位)

引脚配置示例:

#include <intrins.h>  // 必须包含,用于_nop_()延时
sbit DS1302_SCLK = P1^0;  // 时钟线
sbit DS1302_IO   = P1^1;  // 数据线(双向)
sbit DS1302_RST  = P1^2;  // 复位/片选线

2. 底层驱动函数

2.1 关键配置位

操作顺序:先写地址,再写数据

2.1.1 WP写保护位(地址0x8e)

功能 说明
0x00 解除写保护 允许写入时间数据
0x80 开启写保护 禁止写入

2.1.2 CH时钟停止位(地址0x80)

功能 说明
0x80 停止时钟 时钟停止计时
0x00 启动时钟 正常计时

重要提示 :初始化时必须先停止时钟,设置完时间再启动!

2.1.3 时间参数地址表

项目 写地址 读地址
0x80 0x81
0x82 0x83
0x84 0x85

2.2 数据转换方法

BCD码转换(写入时)

十进制 → BCD码:
例如:56秒 → 5×16 + 6 = 86 (0x56)

BCD码逆转换(读取时)

BCD码 → 十进制:
例如:0x56 → 5×10 + 6 = 56

3. 应用场景

3.1 基础设置与读取

设置时间函数

void Set_Rtc(unsigned char *ucRtc)
{
    unsigned char i;
    Write_Ds1302_Byte(0x8e, 0x00);    // 解除写保护(WP置1)
    Write_Ds1302_Byte(0x80, 0x80);    // CH置1,停止计时
    
    // 写入时分秒(注意:地址从时开始倒序写入)
    for (i = 0; i < 3; i++)
        Write_Ds1302_Byte(0x84 - 2 * i, 
                         ucRtc[i] / 10 % 10 * 16 + ucRtc[i] % 10);
    
    Write_Ds1302_Byte(0x8e, 0x80);    // 重新开启写保护(WP置0)
}

读取时间函数

void Read_Rtc(unsigned char *ucRtc)
{
    unsigned char i;
    unsigned temp;  // unsigned默认是8位,int是16位
    
    EA = 0;  // 关闭全局中断,保证时序完整性
    
    for (i = 0; i < 3; i++)
    {
        temp = Read_Ds1302_Byte(0x85 - 2 * i);  // 读取地址自动递增
        ucRtc[i] = temp / 16 * 10 + temp % 16;  // BCD转十进制
    }
    
    EA = 1;  // 恢复中断
}

使用示例

// 设置时间为12:34:56
unsigned char set_time[] = {12, 34, 56};
Set_Rtc(set_time);

// 读取当前时间
unsigned char current_time[3];
Read_Rtc(current_time);
// current_time[0] = 时
// current_time[1] = 分
// current_time[2] = 秒

4. 常见问题与解决方案

4.1 注意事项

  1. 中断处理EA :读取时必须关闭全局中断 → EA = 0
  • 防止时序被打断导致读取错误
  1. 写保护机制WP(0x8e)
  2. 时钟精度保障CH(0x80)
  • 初始化时建议停止时钟 CH=1
  • 设置完成后再启动 CH=0

4.2 时间写入异常

问题 :写入时DS1302仍在计时,可能导致进位错误(如59秒写入时分钟进位)

解决方案

void Set_Rtc(unsigned char *ucRtc)
{
    unsigned char i;
    Write_Ds1302_Byte(0x8e, 0x00);    // 解除写保护
    Write_Ds1302_Byte(0x80, 0x80);    // 设置CH为1,停止计时
    
    for (i = 0; i < 3; i++)
        Write_Ds1302_Byte(0x84 - 2 * i, 
                         ucRtc[i] / 10 % 10 * 16 + ucRtc[i] % 10);
    
    Write_Ds1302_Byte(0x8e, 0x80);    // 重新开启写保护
}

5.1 时间段触发逻辑

情况2:跨天情况(分段判断)

// 例如:20点到第二天8点
if(time_para > 8)  // 起始时间大于8点
{
    if(ucRtc[0] >= time_para || ucRtc[0] <= 8)
    {
        // 在时间段内
    }
}
else
{
    if(ucRtc[0] >= time_para && ucRtc[0] <= 8)
    {
        // 在时间段内
    }
}

情况1:同一天内(闭区间判断)

// 例如:8点到20点
if(ucRtc[0] >= 8 && ucRtc[0] <= 20)
{
    // 在时间段内
}

常用代码模板

// DS1302初始化模板
void DS1302_Init(void)
{
    // 1. 初始化引脚
    DS1302_SCLK = 0;
    DS1302_RST = 0;
    
    // 2. 设置初始时间
    unsigned char init_time[] = {12, 0, 0}; // 12:00:00
    Set_Rtc(init_time);
}

// 完整的时间管理示例
void main(void)
{
    unsigned char current_time[3];
    
    DS1302_Init();  // 初始化
    
    while(1)
    {
        Read_Rtc(current_time);  // 读取时间
        
        // 各种时间判断和应用
        if(current_time[1] == 0 && current_time[2] == 0)
        {
            // 整点处理
        }
        
        // 延时后再读取
        delay_ms(1000);
    }
}