引言
是否曾想为项目添加实时时钟(RTC),但手头没有硬件?借助Velxio,您可以在浏览器中模拟完整的I2C RTC读取过程——使用树莓派Pico和虚拟DS1307。在本教程中,我们将逐步讲解电路、代码和关键概念,帮助您在自己的项目中自信地使用I2C通信。
电路讲解
我们先来看一下电路。在Velxio中打开示例,您将看到以下组件:

电路很简单:一个树莓派Pico(nano-rp2040)连接了两个LED和一个虚拟DS1307 RTC(未显示为独立组件——RTC已内置于仿真中)。LED用作I2C活动的视觉指示器。
- LED(蓝色):通过蓝色线连接到D12引脚(SDA线)。其阳极(A)连接D12,阴极(C)通过黑色线连接到GND.1。
- LED(黄色):通过橙色线连接到D10引脚(SCL线)。其阳极(A)连接D10,阴极(C)通过黑色线连接到GND.1。
两个LED共享同一地线(GND.1)。I2C总线由SDA(数据)和SCL(时钟)线组成。DS1307 RTC在内部以地址0x68连接到这些线。对于I2C通信而言,LED并非严格必要,但它们能提供线路切换的视觉提示。
要自行搭建此电路,您可以使用Velxio的组件选择器:

搜索”Raspberry Pi Pico”和”LED”,然后按描述进行接线。画布工具栏可帮助您缩放和排列组件:

代码讲解
现在我们来深入代码。该程序使用Wire库(内置)通过I2C与DS1307通信。以下是完整代码:
// 树莓派Pico — I2C DS1307 RTC读取
// 从地址0x68的虚拟RTC读取时间
#include <Wire.h>
byte bcdToDec(byte val) {
return ((val >> 4) * 10) + (val & 0x0F);
}
void setup() {
Serial.begin(115200);
delay(500);
Wire.begin();
Serial.println("=== Pico I2C RTC读取 ===");
Serial.println("正在读取地址0x68处的DS1307(系统时间)");
Serial.println();
}
void loop() {
// 将寄存器指针设置为0
Wire.beginTransmission(0x68);
Wire.write(0x00);
Wire.endTransmission();
// 读取7个字节:秒、分、时、星期、日、月、年
Wire.requestFrom(0x68, 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());
const char* days[] = {"", "周日", "周一", "周二", "周三", "周四", "周五", "周六"};
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(" 日期: ");
Serial.print(days[dow]); Serial.print(' ');
if (date < 10) Serial.print('0'); Serial.print(date); Serial.print('/');
if (month < 10) Serial.print('0'); Serial.print(month); Serial.print('/');
Serial.print("20"); if (year < 10) Serial.print('0'); Serial.println(year);
} else {
Serial.println("RTC无响应!");
}
delay(1000);
}
工作原理
-
初始化:我们以115200波特率启动串口通信,并通过
Wire.begin()初始化I2C总线。DS1307的地址为0x68。 -
循环:每秒执行一次:
- 向地址0x68发送
beginTransmission,写入0x00(指向秒寄存器的指针),然后endTransmission。 - 使用
Wire.requestFrom(0x68, 7)从RTC请求7个字节。 - 读取每个字节,并使用辅助函数
bcdToDec从BCD(二进制编码的十进制)转换为十进制。 - 格式化时间与日期并打印到串口监视器。
- 向地址0x68发送
-
BCD转换:DS1307以BCD格式存储时间值。例如,字节
0x45在BCD中表示45(4*10 + 5 = 45)。bcdToDec函数提取十位(高半字节)和个位(低半字节)并组合。 -
掩码操作:我们对某些寄存器应用掩码:
- 秒:
& 0x7F清除CH(时钟暂停)位。 - 时:
& 0x3F清除12/24小时模式位(假设为24小时模式)。
- 秒:
关键概念
- I2C协议:一种用于微控制器与外设之间通信的双线串行总线(SDA、SCL)。每个设备都有唯一地址。
- Wire库:Arduino内置的I2C库。使用
Wire.begin()加入总线,Wire.beginTransmission(addr)开始与设备通信,Wire.write()发送数据,Wire.endTransmission()结束通信,Wire.requestFrom(addr, count)读取字节。 - BCD格式:许多RTC使用BCD简化显示。必须转换为十进制才能输出人类可读的内容。
- 寄存器映射:DS1307有特定的寄存器映射:秒(0x00)、分(0x01)、时(0x02)、星期(0x03)、日(0x04)、月(0x05)、年(0x06)。
常见陷阱与调试技巧
- I2C地址错误:仔细检查设备地址。DS1307通常为0x68(7位)。如果出现”RTC无响应!“,请验证地址。
- 缺少上拉电阻:在实际硬件中,I2C线路需要上拉电阻。在Velxio中,它们已在内部模拟,因此无需添加。
- 波特率不匹配:确保串口监视器的波特率与115200匹配。
- 未设置寄存器指针:读取前务必设置寄存器指针。代码写入0x00以指向秒寄存器。
- 掩码错误:忘记掩码CH位(秒)或12/24小时位(时)可能导致错误值。
建议扩展
- 设置时间:添加代码将当前时间写入RTC。使用
Wire.write()设置每个寄存器。 - 闹钟功能:实现一个闹钟,在特定时间触发LED。
- LCD显示:使用I2C LCD代替串口监视器显示时间。
- 多个RTC:尝试在同一总线上读取两个不同的I2C设备。
亲自尝试!
准备好实验了吗?在Velxio中打开实时示例并运行仿真。您将看到时间每秒在串口监视器中打印一次。修改代码以更改更新间隔或添加新功能。
祝仿真愉快!