引言
是否曾想为你的 Arduino 项目添加一个实时时钟,但手头没有硬件?借助 Velxio,你可以在浏览器中直接模拟一个 I2C RTC(DS1307)。在本教程中,我们将构建一个简单的 I2C 读取器,从虚拟 DS1307 获取当前时间和日期,并将其打印到串行监视器。无需外部库——只需内置的 Wire 库。

电路讲解
这个示例非常简单,因为 DS1307 已经集成到虚拟 Arduino Uno 板中。无需添加任何外部元件或导线。I2C 总线(SDA 在 A4,SCL 在 A5)在内部连接到地址为 0x68 的 DS1307。唯一的元件就是 Arduino Uno 本身。

首先,打开 Velxio 编辑器,从元件选择器中选择一个 Arduino Uno。DS1307 已内置于模拟环境中,因此无需单独添加。电路已准备就绪。
代码讲解
让我们逐步解析代码。
包含与定义
#include <Wire.h>
#define DS1307_ADDR 0x68
我们包含 Wire 库用于 I2C 通信,并将 DS1307 的 I2C 地址定义为 0x68。
BCD 转十进制
byte bcdToDec(byte val) {
return ((val >> 4) * 10) + (val & 0x0F);
}
DS1307 以二进制编码十进制(BCD)格式存储时间值。这个辅助函数将 BCD 字节转换为常规十进制数。
初始化
void setup() {
Wire.begin();
Serial.begin(9600);
Serial.println("===========================");
Serial.println(" DS1307 RTC 读取器 (I2C)");
Serial.println("===========================");
Serial.println();
}
我们初始化 I2C 总线和串行通信,波特率为 9600。然后打印一个标题。
主循环
void loop() {
// 将寄存器指针设置为 0(秒寄存器)
Wire.beginTransmission(DS1307_ADDR);
Wire.write(0x00);
Wire.endTransmission();
// 请求 7 个字节:秒、分、时、星期、日、月、年
Wire.requestFrom(DS1307_ADDR, 7);
if (Wire.available() >= 7) {
byte sec = bcdToDec(Wire.read() & 0x7F);
byte min = bcdToDec(Wire.read());
byte hr = bcdToDec(Wire.read() & 0x3F);
byte dow = bcdToDec(Wire.read());
byte date = bcdToDec(Wire.read());
byte month = bcdToDec(Wire.read());
byte year = bcdToDec(Wire.read());
// 打印格式化时间
Serial.print("时间: ");
if (hr < 10) Serial.print("0");
Serial.print(hr);
Serial.print(":");
if (min < 10) Serial.print("0");
Serial.print(min);
Serial.print(":");
if (sec < 10) Serial.print("0");
Serial.print(sec);
Serial.print(" 日期: ");
if (date < 10) Serial.print("0");
Serial.print(date);
Serial.print("/");
if (month < 10) Serial.print("0");
Serial.print(month);
Serial.print("/20");
if (year < 10) Serial.print("0");
Serial.println(year);
} else {
Serial.println("错误: 无法读取 RTC");
}
delay(1000);
}
在主循环中,我们首先通过发送单个字节 0x00 将 DS1307 的寄存器指针设置为 0(秒寄存器)。然后从 RTC 请求 7 个字节。DS1307 以 BCD 格式返回秒、分、时、星期、日、月和年。我们屏蔽秒的最高位(第 7 位是时钟暂停标志)和时的最高两位(第 6-7 位是模式/上午-下午)。转换后,我们打印时间和日期,并补前导零。如果可用字节少于 7 个,则打印错误信息。最后,等待 1 秒。
关键概念
- I2C 通信:Wire 库处理 I2C 协议。
beginTransmission()开始写入,write()将数据加入队列,endTransmission()发送数据。requestFrom()从从设备读取字节。 - BCD 格式:DS1307 以 BCD 格式存储时间,其中每个半字节代表一个十进制数字。
bcdToDec()函数负责转换。 - 寄存器寻址:DS1307 的内部寄存器通过先写入寄存器地址,然后读取或写入数据来访问。
- 位屏蔽:秒寄存器的最高位(CH)表示时钟暂停;我们通过
& 0x7F屏蔽它。时寄存器的最高位表示 12/24 小时模式;我们通过& 0x3F屏蔽。
常见陷阱与调试技巧
- I2C 地址错误:确保地址是 0x68(7 位)。如果使用 8 位地址,请左移 1 位。
- 缺少上拉电阻:在实际硬件中,I2C 线路需要上拉电阻。在 Velxio 中,它们已在内部模拟。
- 串行监视器不显示:确保波特率匹配(9600)。点击编辑器工具栏中的串行监视器图标。

- 编译错误:检查是否包含了
Wire.h。代码未使用外部库。 - RTC 无响应:如果看到“错误: 无法读取 RTC”,可能是 I2C 通信失败。请验证地址和接线。
建议的扩展
- 设置时间:添加代码,通过串行输入设置 DS1307 的时间。
- 在 LCD 上显示:使用 I2C LCD 显示时间。
- 闹钟功能:利用 DS1307 的闹钟寄存器实现闹钟。
- 数据记录:将时间戳记录到 EEPROM 或 SD 卡。
亲自尝试
准备好看看效果了吗?在 Velxio 上打开实时示例,点击运行按钮。你将看到时间在串行监视器中每秒更新一次。

祝模拟愉快!