单片机的DS1302时钟芯片原理和模块分析(以STC的IAP15F2K61S2芯片为例)
经过 15 级分频,正好得到1 Hz(每秒一次)BCD码用 4 位二进制表示 1 位十进制数43 的 BCD 码 =0100 0011十进制 434 → 01003 → 0011合并 → 0100 0011 = 0x43。
理论部分的知识构建:
DS1302时钟芯片介绍——实时时钟
对比一下之前的一个知识点:
| 对比项 | 定时器 (Timer) | 实时时钟 (RTC) |
|---|---|---|
| 精度 | 高,可达微秒级 | 较低,多为秒级 |
| 计时范围 | 短 | 长(年月日时分秒) |
| 掉电保持 | 否 | 是(有电池) |
| 典型用途 | 产生 PWM、延时 | 显示时间、日历 |
DS1302 就是 RTC:
-
专门用来计时的芯片
-
有独立的 32.768kHz 晶振
-
有电池备份,掉电后继续走时
如图在单片机上的位置:


补充:
VCC2是单片机内部启动时钟的电源(正常理解)
为什么还有一个VCC1呢,他是用来校准时钟的,当时钟不工作的时候,为了确保它下次启用的时候是时间还是和现在的时间一样。
例如,我们手机不用了的时候我们会把他关掉黑屏(VCC2不供电了),但是里面的时间显示还是要继续的那么(VCC1就是去让时钟的时间跟实际时间是一样的——校准)。
晶振的作用:
晶振就像手表的"摆锤"或"石英" 没有它,时钟芯片就无法"走动"
DS1302 芯片需要接一个 32.768kHz 的晶振(X1、X2 引脚之间)。
为什么是 32.768kHz?
-
32.768kHz = 32768 Hz
-
32768 = 2¹⁵
-
经过 15 级分频,正好得到 1 Hz(每秒一次)
特点:
晶振内部是一小块石英晶体,它具有压电效应。
石英晶体有一个特性:
它有一个固有的机械共振频率。当外加电信号的频率等于晶体的固有频率时,会产生共振,振荡幅度最大,最稳定。
BCD 码:
什么是 BCD 码?
BCD码(Binary-Coded Decimal):
-
用 4 位二进制表示 1 位十进制数
-
43 的 BCD 码 =
0100 0011
十进制 43
4 → 0100
3 → 0011
合并 → 0100 0011 = 0x43
为什么 DS1302 用 BCD 码?
DS1302 内部寄存器存的是 BCD 码:
-
秒寄存器:
0x59表示 59 秒 -
分寄存器:
0x59表示 59 分 -
时寄存器:
0x23表示 23 时
这样做的优点:
-
直接对应数码管显示(不用转换)
-
每个字节的高4位是十位,低4位是个位

规律:
-
写地址 = 偶数
-
读地址 = 写地址 + 1
实践部分和代码分析
ds1302.c
/* # DS1302代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
#include "ds1302.h"
#include "intrins.h"
//
void Write_Ds1302(unsigned char temp) //写一个字节
{
unsigned char i;
for (i=0;i<8;i++)
{
SCK = 0;
SDA = temp&0x01;
temp>>=1;
SCK=1;
}
}
//
void Write_Ds1302_Byte( unsigned char address,unsigned char dat ) //写命令+数据
{
RST=0; _nop_();
SCK=0; _nop_();
RST=1; _nop_();
Write_Ds1302(address);
Write_Ds1302(dat);
RST=0;
}
//
unsigned char Read_Ds1302_Byte ( unsigned char address )//读一个字节
{
unsigned char i,temp=0x00;
RST=0; _nop_();
SCK=0; _nop_();
RST=1; _nop_();
Write_Ds1302(address);
for (i=0;i<8;i++)
{
SCK=0;
temp>>=1;
if(SDA)
temp|=0x80;
SCK=1;
}
RST=0; _nop_();
SCK=0; _nop_();
SCK=1; _nop_();
SDA=0; _nop_();
SDA=1; _nop_();
return (temp);
}
以上部分知道功能就行,不需要深挖
核心部分
BCD 码转换(核心!)
void Set_RTC(unsigned char *pucRTC)
{
unsigned char temp;
Write_Ds1302_Byte(0x8E, 0x00); // 关闭写保护
// pucRTC[0] = 时(23)
temp = ((pucRTC[0]/10)<<4) | (pucRTC[0]%10);
// 23/10 = 2 → 左移4位 → 0010 0000
// 23%10 = 3 → 0000 0011
// 或运算 → 0010 0011 = 0x23(BCD码)
Write_Ds1302_Byte(0x84, temp); // 写入小时寄存器
// pucRTC[1] = 分(59)
temp = ((pucRTC[1]/10)<<4) | (pucRTC[1]%10);
// 59/10=5 → 0101 0000
// 59%10=9 → 0000 1001
// 或运算 → 0101 1001 = 0x59
Write_Ds1302_Byte(0x82, temp); // 写入分钟寄存器
// pucRTC[2] = 秒(55)
temp = ((pucRTC[2]/10)<<4) | (pucRTC[2]%10);
// 55/10=5 → 0101 0000
// 55%10=5 → 0000 0101
// 或运算 → 0101 0101 = 0x55
Write_Ds1302_Byte(0x80, temp); // 写入秒寄存器
Write_Ds1302_Byte(0x8E, 0x80); // 打开写保护
}
void Get_RTC(unsigned char *pucRTC)
{
unsigned char temp;
temp = Read_Ds1302_Byte(0x85); // 读小时
// 假设读到 0x23
pucRTC[0] = (temp>>4)*10 + (temp & 0x0F);
// (0x23>>4) = 0x02 = 2
// (0x23 & 0x0F) = 0x03 = 3
// 2*10 + 3 = 23
temp = Read_Ds1302_Byte(0x83); // 读分钟
pucRTC[1] = (temp>>4)*10 + (temp & 0x0F);
temp = Read_Ds1302_Byte(0x81); // 读秒
pucRTC[2] = (temp>>4)*10 + (temp & 0x0F);
}
main.c
main.c
#include "init.h"
#include "seg.h"
#include "tim.h"
#include "ds1302.h"
//RTC
unsigned char pucRTC[3] = {23, 59, 55};
//Seg
unsigned char pucSeg_Buf[12], pucSeg_Code[8], pucSeg_Pos = 0;
//Timer
unsigned long ulms = 0;
unsigned int uiSeg_Dly = 0;
unsigned int uiRTC_Dly = 0;
void Seg_Proc(void);
void RTC_Proc(void);
void main(void)
{
Cls_Peripheral();
Timer0Init();
EA = 1;
Set_RTC(pucRTC);
while(1)
{
Seg_Proc();
RTC_Proc();
}
}
void Seg_Proc(void)
{
if(uiSeg_Dly < 200)
return;
uiSeg_Dly = 0;
sprintf(pucSeg_Buf, "%02d %02d %02d", (unsigned int)pucRTC[0], (unsigned int)pucRTC[1], (unsigned int)pucRTC[2]);
Seg_Tran(pucSeg_Buf, pucSeg_Code);
}
void RTC_Proc(void)
{
if(uiRTC_Dly < 200)
return;
uiRTC_Dly = 0;
Get_RTC(pucRTC);
}
void Time_0(void) interrupt 1
{
ulms++;
uiSeg_Dly++;
uiRTC_Dly++;
if(ulms % 2 == 0)
{
pucSeg_Pos = (pucSeg_Pos + 1)%8;
Seg_Disp(pucSeg_Code, pucSeg_Pos);
}
}
RTC_Proc —— 定时读 DS1302
void RTC_Proc(void)
{
if(uiRTC_Dly < 200)
return;
uiRTC_Dly = 0;
Get_RTC(pucRTC); // 从 DS1302 读出当前时间
}
为什么 200ms 读一次?
-
时间每秒才变一次,200ms 读一次足够
-
减少对 DS1302 的访问频率
信息流:
硬件晶振 32.768kHz
↓
DS1302 内部计数器每秒+1
↓
寄存器(秒分时)自动更新(BCD码)
↓
主循环每200ms:
Get_RTC() → 三线接口读寄存器
↓
BCD码转十进制 → pucRTC[] 更新
↓
sprintf 格式化 → pucSeg_Buf
↓
Seg_Tran → 转成段码 → pucSeg_Code
↓
中断每2ms:
pucSeg_Pos 切换
Seg_Disp 送段码
↓
数码管显示 "23 59 55"更多推荐



所有评论(0)