以后看到 DS1302,你只需要脑补出三个搬运工、一种怪异的语言、一本带锁的密码本。
1. 硬件层:三个搬运工 (The Pins)
DS1302 与单片机对话只靠三根线,各司其职:
- RST (CE) —— 老板: 平时躺平 (0),站起来 (1) 就代表“要开会了”,通信开始。
- SCLK (SCK) —— 鼓手: 负责打拍子。
- 上升沿 (0→1):单片机把数据写进芯片。
- 下降沿 (1→0):芯片把数据吐给单片机。
- I/O (SDA) —— 搬运工: 唯一的数据通道。数据排成队,一位一位地过独木桥(串行通信)。
2. 软件层:通信暗号 (The Protocol)
想要数据传得对,必须遵守这三个规矩:
- 低位先行 (LSB First): 发数据时,先发第 0 位(脚底板),最后发第 7 位(头顶)。
- 写代码技巧:
temp & 0x01(取最低位) 配合temp >>= 1(右移)。 - 读代码技巧:
temp >>= 1(腾位置) 配合temp |= 0x80(填最高位)。
- 写代码技巧:
- 时序反转:
- 写是上升沿干活(拍照)。
- 读是下降沿干活(吐货)。
3. 数据层:怪异的语言 (BCD Code)
DS1302 不讲普通的二进制,它讲 BCD 码。
- 特征: 用 16 进制的样子存 10 进制的数。
- 比如 45秒,它存的是
0x45,而不是0x2D。
- 比如 45秒,它存的是
- 翻译方法(给数码管看):
- 切高位(十位):
数据 / 16 - 割低位(个位):
数据 % 16
- 切高位(十位):
4. 逻辑层:带锁的密码本 (Register Map)
所有的操作都是在读写“寄存器”(小房间)。
-
地址规律:
- 写地址: 时
84→ 分82→ 秒80(每次减 2)。 - 读地址: 时
85→ 分83→ 秒81(每次减 2)。 - 写保护 (WP): 地址
8E。- 写之前必须
0x00解锁。 - 写完最好
0x80上锁。
- 写之前必须
- 写地址: 时
-
代码实战公式:
利用地址减 2 的规律,用for循环批量处理:// 比如写时间: Write_Byte(0x84 - i*2, time_array[i]); // 注意数组顺序必须是:[时, 分, 秒]
5. 避坑指南 (Checklist)
如果你写完代码发现时间不走或者乱码,请检查:
- 有没有解锁? (操作
0x8E寄存器了吗?) - 秒的最高位 (CH) 是不是 1? (如果是 1,时钟就暂停了,要写 0)。
- 读写有没有搞反? (读是下降沿,写是上升沿)。
- 数码管显示有没有 /16? (必须把 BCD 码拆解才能显示)。
这就好比是**“拿着地图走迷宫”**。
左边是地图(DS1302 的寄存器表),右边是怎么走(代码实现)。
这两段代码非常聪明,它没有笨笨地写三行“写秒、写分、写时”,而是利用了地址的数学规律,用一个循环搞定了。
我们来破解其中的奥秘:
6. 你需要的代码编写
请仔细观察左侧表格里 秒、分、时 的地址:
-
写地址 (WRITE):
- 时 (Hour):
84h - 分 (Min):
82h - 秒 (Sec):
80h - 规律: 从“时”开始,每往下一级,地址就 减 2 (
84 -> 82 -> 80)。
- 时 (Hour):
-
读地址 (READ):
- 时 (Hour):
85h - 分 (Min):
83h - 秒 (Sec):
81h - 规律: 同样是每往下一级,地址就 减 2 (
85 -> 83 -> 81)。
- 时 (Hour):
二、 看右边的代码(利用规律)
1. 设置时间函数 Set_Rtc
for(i=0; i<3; i++)
Write_Ds1302_Byte(0x84-i*2, ucRtc[i]);
这个 0x84 - i*2 简直是神来之笔。让我们把 i = 0, 1, 2 带进去算一下:
- 当 i = 0 时:
- 地址:
0x84 - 0=0x84(查表:这是写小时)。 - 动作:把
ucRtc[0]写进小时寄存器。
- 地址:
- 当 i = 1 时:
- 地址:
0x84 - 2=0x82(查表:这是写分钟)。 - 动作:把
ucRtc[1]写进分钟寄存器。
- 地址:
- 当 i = 2 时:
- 地址:
0x84 - 4=0x80(查表:这是写秒)。 - 动作:把
ucRtc[2]写进秒寄存器。
- 地址:
重要结论:
这段代码默认你的数组 ucRtc 的顺序必须是:{ 时, 分, 秒 }。如果你按“秒分时”存,时间就全乱了。
2. 读取时间函数 Read_Rtc
for(i=0; i<3; i++)
ucRtc[i] = Read_Ds1302_Byte(0x85-i*2);
逻辑完全一样,只是起始点变成了 读地址:
- i = 0:
0x85→ 读 时 → 存入ucRtc[0] - i = 1:
0x83→ 读 分 → 存入ucRtc[1] - i = 2:
0x81→ 读 秒 → 存入ucRtc[2]
三、 那个奇怪的开关锁 0x8e
在 Set_Rtc 函数里:
-
开头:
Write_Ds1302_Byte(0x8e, 0);- 对应表格倒数第二行 WP (Write Protect)。
- 写
0是为了 关闭写保护,否则 DS1302 拒绝任何修改。
-
结尾:
Write_Ds1302_Byte(0x8e, 1);- 这里是为了 重新打开写保护。
- (注意一个小细节): 严格来说,根据表格,WP 是第 7 位(最高位)。标准的写法应该是写入
0x80(二进制1000 0000) 来打开 WP。 - 截图里的代码写的是
1(二进制0000 0001)。这在有些比赛代码里能跑通可能是因为逻辑不严谨,或者仅仅是想表达“非零值”,但严谨的写法推荐用0x80。不过在蓝桥杯比赛中,只要能锁住就行,有时候写1也能混过去(取决于具体芯片实现,但大概率这一行在截图代码里没起到真正的“写保护”作用,不过不影响时间设置功能)。
总结
这两段代码就是**“查表数学题”**:
- 写时间: 从
0x84(时) 开始,每次减 2。 - 读时间: 从
0x85(时) 开始,每次减 2。 - 数组顺序: 必须是 [时, 分, 秒]。
