Full picture
15

I²C 实时时钟(RTC)

15.1 概述

如今,我们所有的计算机都有一个听起来理所当然的关键功能:实时时钟,即 RTC。这个硬件组件让计算机能够跟踪当前日期,即使断电后也能保持。

在 70 年代末和 80 年代的大多数复古计算机上,都没有配备 RTC。虽然这看起来不是一个非常关键的功能,但一旦我们尝试将其与现代计算机接口,就可能会引发一些问题。例如,常见的文件系统都有一个日期字段,记录文件或目录的创建和/或修改日期。因此,如果我们想让 8 位计算机为新文件生成这些信息,就需要一种方法来持续跟踪当前日期和时间。

15.2 硬件实现

在 Zeal 8-bit Computer 上,RTC 由 DS1307 集成电路实现,在 PCB 上标记为 U13。选择该电路是因为其外形小巧且编程简单,因为它也使用 I²C 协议。

这个 RTC 需要两个外部组件才能保持时间追踪:

  • PCB 上的晶振,标记为 32.768KHz,电容为 12.5pF
  • PCB 上的 3V 备用电池,用于在主电源关闭时保持计时

安装在 Zeal 8-bit Computer 上的备用电池座(标记为 BT1)兼容任何标准 CR20xx 电池,包括 CR2016、CR2025 和 CR2032。这些电池之间的唯一区别是厚度和容量。

当计算机通电时,备用电池不会被 RTC 使用。实际上,它内部有一个内置电路,可以检测电源故障,并且仅当主电源不再连接时自动切换到备用电池。

以下是该集成电路的引脚排列:

RTC 引脚图
实时时钟引脚排列

信号说明如下:

RTC 没有任何用于配置其从设备地址的引脚。事实上,其 7 位地址被硬编码为 0x68

⚠️ 警告

RTC 要求 $V_{BAT}$ 始终设置为已知值,绝不能悬空。因此,在未安装电池时与 RTC 通信可能导致错误。 在这种情况下且没有电池时,在 $V_{BAT}$ 和 $Gnd$ 之间连接一个电阻(约 20kΩ)。

15.3 与 RTC 通信

寄存器

RTC 内部围绕 7 个寄存器组织,分别表示秒、分、时、星期、日、月和年。所有这些寄存器包含的值以二进制编码十进制(BCD)表示。

二进制编码十进制是一种表示十进制数的方法(即我们日常使用的数字表示方式),通过将每个数字转换为其十六进制等效值来实现。 换句话说,不是将整个十进制数转换为十六进制,而是将每个数字分别转换为十六进制并将它们连接起来。

例如,十进制数 59 的 BCD 表示为 0x59(而不是 0x3b

这 7 个寄存器如下表所述:

表 2 计时寄存器

地址 位 7 位 6 位 5 位 4 位 3 位 2 位 1 位 0 名称 范围
0x00 HALT1 10 秒 00-59
0x01 0 10 分 00-59
0x02 0 12/24 模式2 10 时 下午/上午 10 时 1–12+上午/下午 24 00–23
0x03 0 0 0 0 0 星期星期 01-07
0x04 0 0 10 日 01-31
0x05 0 0 0 10 月 01-12
0x06 10 年 00-99

关于以下位的小注:

  • HALT$^1$:当此位为 1 时,时钟暂停,这降低了功耗但停止了内部计时。
  • 12/24 模式$^2$:当此位为 1 时,启用 12 小时制模式。在此模式下,位 5 为 1 表示 PM(下午),为 0 表示 AM(上午)。当此位为 0 时,启用 24 小时制模式。在此模式下,位 5 与位 4 一起表示小时(范围从 00 到 23)。每当模式切换时,必须手动更新时间。

读取和写入寄存器

与 RTC 通信包括检索或写入这些寄存器。与我们之前看到的 EEPROM 类似,有三种方法与 RTC 通信:

  • 通过 I²C 写入事务进行写入
  • 通过 I²C 写-读事务进行随机地址模式读取
  • 通过 I²C 读取事务进行当前地址模式读取

例如,要启用时钟,我们需要清除寄存器 0 的位 7。为此,我们需要执行如下 I²C 写入事务:

  • 生成 START 条件
  • 发送设备地址 0x68,后跟写入位(0)
  • 确认设备已应答(SDA 被拉低)
  • 发送要写入的寄存器地址,此处为 0x00
  • 确认设备已应答
  • 发送寄存器的新值,再次为 0x00,这也会将秒重置为 0
  • 确认设备已应答
  • 生成 STOP 条件

这也是在数据丢失或时钟停止后设置或更新时钟值的方法。

要读取 RTC 的值,我们可以直接执行读取而不指定要读取的寄存器,或者执行写-读来指定开始读取的寄存器。在这两种情况下,都可以通过单个事务读取多个寄存器。

这里我们只考虑第二种情况。例如,如果我们想读取从 0 开始的所有 7 个寄存器,我们需要执行如下 I²C 写-读事务:

  • 生成 START 条件
  • 发送设备地址 0x68,后跟写入位(0)
  • 确认设备已应答(SDA 被拉低)
  • 发送要开始读取的寄存器地址,此处为 0x00
  • 确认设备已应答
  • 再次生成(RE)START 条件(不带 STOP 条件!)
  • 发送设备地址 0x68,后跟读取位(1)
  • 确认设备已应答
  • 接收第一个字节,即寄存器 0 的值,并通过在一个时钟周期内将 SDA 拉低来应答
  • 对寄存器 1、2、3、4、5 执行相同操作
  • 接收第六个字节,即寄存器 6 的值,但不要应答,保持 SDA 在一个时钟周期内为高电平
  • 生成 STOP 条件

这些事务的图示可在前面的 I²C EEPROM 章节中找到,查看它以获取更多信息

⚠️ 警告

RTC 可能对 SDA 和 SCL 时序非常敏感。即使整体频率符合数据手册给出的限制,它在发送从设备地址后也可能不会以 ACK 位响应。这可能是由于芯片在 SCL 变为高电平时内部锁存 SDA 线的时序所致。遗憾的是,这一点没有文档说明。

要解决此问题,请确保 SCL 时钟信号具有 50% 的占空比(低电平持续时间与高电平持续时间相同)。更重要的是,确保 SDA 线在 SCL 线变为高电平之前(而不是同时)设置为所需状态。

15.4 附加功能

RTC 集成电路提供了一些可选用的附加功能。

NVRAM

第一个功能是 RAM,该芯片提供 56 个可自由使用的字节。其主要目的是存储即使在复位后也能保留的数据,因为它也由电池供电。因此,只要电池仍有电,这部分就可以被视为非易失性存储器。

NVRAM 从地址 0x08 开始,一直到 0x3F(含)。与此部分 RAM 通信的方式与上一段描述的其他寄存器相同。

方波发生器

RTC 芯片提供的另一个功能是方波发生器。如前文引脚图所示,该电路可以在 $SQW$ 引脚上输出方波信号。

根据软件的配置方式,此引脚也可以强制为 0 或 1。寄存器 0x07 负责此功能,其组织如下:

位 7 位 6 位 5 位 4 位 3 位 2 位 1 位 0
OUT 0 SQWE 0 速率选择

SQWE 设置为 1 时,方波生成被启用,频率取决于 速率选择 的值:

速率选择值 输出频率
0 1Hz
1 4.096KHz
2 8.192kHz
3 32.768kHz

在这种情况下,OUT 位将被忽略。

SQWE 设置为 0 时,方波生成被禁用,$SQW$ 上的输出与 OUT 位相同:OUT 为 1 时为高电平,OUT 为 0 时为低电平。

需要注意的是,$SQW$ 引脚采用开漏配置!换句话说,当它为 0 时被拉到 $Gnd$,但当它为 1 时处于悬空状态。

得益于此,可以利用此引脚制作硬件定时器。实际上,通过连接 Zeal 8-bit Computer 上标记为 JP2 的跳线引脚,$SQW$ 引脚将连接到 PIO 的 H-SYNC IN 信号。该信号也是一个开漏信号,通过标记为 RN3 的电阻网络上拉。

因此,每当方波信号变为低电平时,PIO 可以请求中断,从而软件可以跟踪时间,以防需要执行某些基准测试或仅评估两次操作之间的经过时间。

⚠️ 警告

如果在视频端口上连接了视频板且 JP4 跳线已连接以接收来自视频板的 H-blank 信号,则不建议连接 JP2 跳线。

虽然由于开漏配置,这不会带来任何硬件短路风险,但在软件中可能难以确定 H-SYNC IN 信号中断的真实来源,因为它将在 RTC 定时器和外部视频板之间共享。

EN | 中文Beta