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 注意事项
- 中断处理EA :读取时必须关闭全局中断 →
EA = 0
- 防止时序被打断导致读取错误
- 写保护机制WP(0x8e) :
- 时钟精度保障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);
}
}