Arduino Nano 七段数码管时钟 - DS3231 RTC 与 LDR 自动亮度
所需组件与材料
| 组件 | 数量 | 备注 |
|---|---|---|
| Arduino Nano R3 (或 Uno) | 1 | 主控板 |
| Adafruit 1.2" 4位 七段数码管 + I2C 背包 (绿色) | 1 | HT16K33 驱动,I2C 地址 0x70 |
| DS3231 RTC 模块 | 1 | 带 CR1220 纽扣电池座,比 DS1307 更精准且含温度传感器 |
| 光敏电阻 (LDR) | 1 | 配合 100kΩ 电阻分压 |
| 100kΩ 电阻 | 1 | LDR 分压用 |
| 面包板 & 杜邦线 | 若干 | |
| CR1220 纽扣电池 | 1 | RTC 备用电源 |
所需软件
- Arduino IDE
- Adafruit GFX Library
- Adafruit LED Backpack Library
项目特点
- DS3231 高精度 RTC:温补晶振,年误差约 ±2ppm,内置温度传感器
- 自动夏/冬令时切换:代码内置欧洲规则(3 月最后一个周日 2:00 进入夏令时,10 月最后一个周日 3:00 恢复冬令时)
- LDR 环境光自适应亮度:白天高亮,夜间自动调暗,护眼省电
- 时间/温度轮询显示:可配置显示时长(默认时间 20 秒,温度 2 秒)
- 秒指示动画:左侧双点随秒数上下交替闪烁,中间冒号每秒闪烁一次
- 错误显示:RTC 读取失败时显示 "BEEF" 报警
- 配置持久化:v1.1 修正了周日判断逻辑(dayOfWeek == 1 表示周日)
接线指南
| 模块 | 引脚 | Arduino Nano |
|---|---|---|
| DS3231 / 七段管 (共用 I2C) | VCC | 5V |
| GND | GND | |
| SDA | A4 | |
| SCL | A5 | |
| LDR 分压电路 | LDR 一端 | 5V |
| LDR 另一端 | A0 (模拟输入) | |
| 100kΩ 电阻 | A0 与 GND 之间 |
RTC 初始化(设置时间)
首次使用需将 DS3231 时间设置为当前时间。可使用 Adafruit 的 ds3231_set_time 示例,或通过串口发送指令。时间将写入 RTC 并由纽扣电池维持,断电不丢失。
完整代码 (v1.1 修正版)
// MyClock v1.1 - Ingo Lohs
// Hardware: Arduino Uno/Nano (ATmega328P)
// Libraries: Wire, Adafruit_GFX, Adafruit_LEDBackpack
#include <Wire.h>
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"
#define DS3231_I2C_ADDRESS 0x68
int ldr_sensor = A0;
int ldr_value = 0;
int brightness_matrix = 15;
int threshold_brightness = 400; // 环境光阈值,需实测调整
int blinkrate_value = 0;
int delay_matrix_time = 20000; // 时间显持续 20 秒 (单位 ms)
int delay_matrix_temp = 2000; // 温度显示 2 秒
int blinky_dot = 2;
Adafruit_7segment matrix = Adafruit_7segment();
byte decToBcd(byte val) { return (val / 10 * 16) + (val % 10); }
byte bcdToDec(byte val) { return (val / 16 * 10) + (val % 16); }
void setup() {
Serial.begin(9600);
Wire.begin();
matrix.begin(0x70);
}
void loop() {
lightBrightness();
matrixBlinkrate();
displayTime();
displayTemp();
}
void lightBrightness() {
ldr_value = analogRead(ldr_sensor);
Serial.print("LDR: "); Serial.println(ldr_value);
// LDR 值越小 = 光线越强
if (ldr_value <= threshold_brightness) {
brightness_matrix = 15; // 白天最高亮
} else {
brightness_matrix = 0; // 夜间最暗
}
}
void matrixBlinkrate() {
matrix.blinkRate(blinkrate_value);
}
void displayTime() {
matrix.setBrightness(brightness_matrix);
matrix.clear();
for (uint16_t i = 0; i < delay_matrix_time / 1000; i++) { // 简化:每秒更新一次
byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
getDateDs3231(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
// 夏/冬令时自动切换 (欧洲规则:周日 dayOfWeek == 1)
// 夏令时:3 月最后一个周日 2:00 -> 3:00
if ((dayOfWeek == 1) && (dayOfMonth >= 25) && (month == 3) && (hour == 2)) {
setDS3231time(second, minute, 3, dayOfWeek, dayOfMonth, month, year);
}
// 冬令时:10 月最后一个周日 3:00 -> 2:00 (year=1 标记夏令时)
if ((dayOfWeek == 1) && (dayOfMonth >= 25) && (month == 10) && (hour == 1) && (year != 1)) {
setDS3231time(second, minute, hour, dayOfWeek, dayOfMonth, month, 1);
}
if ((dayOfWeek == 1) && (dayOfMonth >= 25) && (month == 10) && (hour == 3) && (year == 1)) {
setDS3231time(second, minute, 2, dayOfWeek, dayOfMonth, month, 0);
}
// 显示时分 HH:MM
if (hour > 9) {
matrix.writeDigitNum(0, hour / 10, false);
matrix.writeDigitNum(1, hour % 10, false);
} else {
matrix.writeDigitNum(1, hour, false);
}
// 左侧双点随秒数上下翻转:前 30 秒显下点,后 30 秒显上点
if (second <= 30) blinky_dot = 10; // 下点 + 中间冒号
else blinky_dot = 6; // 上点 + 中间冒号
matrix.writeDigitRaw(2, blinky_dot);
matrix.writeDigitNum(3, minute / 10, false);
matrix.writeDigitNum(4, minute % 10, false);
matrix.writeDisplay();
// 中间冒号每秒闪烁
delay(500);
matrix.drawColon(false);
matrix.writeDisplay();
delay(500);
}
}
void displayTemp() {
matrix.setBrightness(brightness_matrix);
matrix.clear();
byte temp = get3231Temp();
int abs_temp = abs(temp);
// 显示温度,支持负数
if (temp < 0) matrix.writeDigitRaw(0, 0x40); // 负号
if (temp <= -10) matrix.writeDigitRaw(2, 0x0C); // 十位负号位置调整
if (temp >= 10 || temp <= -10) matrix.writeDigitNum(0, abs_temp / 10, false);
matrix.writeDigitNum(1, abs_temp % 10, false);
matrix.writeDigitRaw(3, 0x63); // '°' 符号
matrix.writeDigitRaw(4, 0x39); // 'C' 符号
matrix.writeDisplay();
Serial.print("Temperature C: "); Serial.println(get3231Temp());
delay(delay_matrix_temp);
}
void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year) {
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0);
Wire.write(decToBcd(second));
Wire.write(decToBcd(minute));
Wire.write(decToBcd(hour));
Wire.write(decToBcd(dayOfWeek)); // 1=周日, 7=周六
Wire.write(decToBcd(dayOfMonth));
Wire.write(decToBcd(month));
Wire.write(decToBcd(year));
Wire.endTransmission();
}
void getDateDs3231(byte *second, byte *minute, byte *hour, byte *dayOfWeek, byte *dayOfMonth, byte *month, byte *year) {
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0);
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
*second = bcdToDec(Wire.read() & 0x7f);
*minute = bcdToDec(Wire.read());
*hour = bcdToDec(Wire.read() & 0x3f);
*dayOfWeek = bcdToDec(Wire.read());
*dayOfMonth = bcdToDec(Wire.read());
*month = bcdToDec(Wire.read());
*year = bcdToDec(Wire.read());
}
float get3231Temp() {
byte tMSB, tLSB;
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0x11);
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDRESS, 2);
if (Wire.available() >= 2) {
tMSB = Wire.read();
tLSB = Wire.read();
float temp3231 = (tMSB & 0x7F); // 符号位处理
temp3231 += ((tLSB >> 6) * 0.25); // 小数部分 0.25 度分辨率
if (tMSB & 0x80) temp3231 = -temp3231; // 负温度
return temp3231;
} else {
Serial.println("DS3231 Sensor Error - cant read temperature");
matrix.print(0xBEEF, HEX);
matrix.writeDisplay();
delay(5000);
return 0;
}
}
进阶扩展建议
- 添加闹钟功能(蜂鸣器 + 按键设定)
- 集成蓝牙模块 (HC-05/HC-06) 配合 Blynk App 远程设置
- 加入 NTP 网络校时(需 ESP8266/ESP32 协同)
- PCB 板级制作,替代面包板,提升可靠性
源码仓库
制造工艺
- Arduino POV 时钟:自制视觉暂留 LED 显示器
- 复古 Numitron 管时钟:Arduino Nano 驱动,低温可靠省电
- 用 Adafruit 60 LED NeoPixel 环形灯和 Arduino 制作时尚挂钟
- 自制 Arduino 伊斯兰祷告时间实时时钟
- 自制大尺寸LED主时钟带闹钟功能 — 12米外清晰可见
- Arduino Due 驱动 3.2″ TFT 实现温度监测与实时时钟
- Arduino Nano + 摇杆控制双步进电机:Visuino 图形化教程
- DIY Arduino 智能手表:打造属于你的 Apple Watch 风格腕表
- 用 Arduino Pro Mini 与 DS1302 RTC 打造精准七段数码管时钟
- Arduino Nano 手持盖革计数器:DIY 核辐射检测仪
- 手把手打造Arduino Nano喂食机器人
- 用 DS1302 RTC 和 Arduino UNO 制作可靠闹钟