Arduino Nano + DS3231 RTC 打造 128x64 LCD 智能时钟:模拟/数字双显、温度、极值记录
所需元件与物料
| Arduino Nano R3 | ×1 |
| DS3231M RTC 模块 (±5ppm, I2C) | ×1 |
| 10kΩ 单圈电位器 (调节 LCD 对比度) | ×1 |
| 轻触按键开关 | ×1 |
| 128x64 点阵 LCD 显示模块 (ST7920 控制器) | ×1 |
必备工具
- 电烙铁及焊接耗材
项目简介
本项目在 Arduino Nano 上驱动 128x64 图形 LCD,结合 DS3231 高精度 RTC 与内置温度传感器,实现双界面切换显示:
- 界面 1:模拟时钟(时/分/秒指针)+ 数字时钟 + 日期 + 当前温度
- 界面 2:数字时钟 + 星期 + 日期 + 当前/最低/最高温度
通过按键 (D3, 内部上拉) 切换界面。RTC 时间设置通过串口监视器输入:年、月、日、时、分、秒(格式:YYYY MM DD HH MM SS)。

核心代码
// 库依赖:U8glib, DS3232RTC, Streaming, Time, Wire
#include <U8glib.h>
#include <DS3232RTC.h>
#include <Streaming.h>
#include <Time.h>
#include <Wire.h>
// LCD 接线: EN=6, RW=5, RS=4, RST=7
U8GLIB_ST7920_128X64_1X u8g(6, 5, 4, 7);
// 全局变量
int temperatura = 0, min_temp = 500, max_temp = -500;
const int ScreenWithC = 96, ScreenHeightC = 32;
#define botao 3
int estado_botao = 0;
char* dia_da_semana[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
void tela_1() { // 界面1:模拟+数字时钟
u8g.drawRFrame(0,0,128,64,3);
u8g.setPrintPos(20,30); u8g.print(temperatura); u8g.drawStr(42,30,"C"); u8g.drawCircle(37,34,2);
mostra_relogio_digital();
fundo_relogio();
desenha_ponteiro(hour()-1, 12.0, 10); // 时针
desenha_ponteiro(minute()-5, 60.0, 19); // 分针
desenha_ponteiro(second()-5, 60, 21); // 秒针
}
void tela_2() { // 界面2:数字时钟+温度极值
u8g.drawRFrame(0,0,128,64,3);
u8g.drawRFrame(68,4,55,56,2);
mostra_relogio_digital();
u8g.setFont(u8g_font_5x8);
u8g.drawStr(78,35,"MIN"); u8g.drawStr(78,53,"MAX");
u8g.setFont(u8g_font_6x13);
u8g.setPrintPos(25,41); u8g.print(dia_da_semana[weekday()-1]);
u8g.setPrintPos(83,19); u8g.print(temperatura); u8g.drawStr(105,19,"C"); u8g.drawCircle(100,12,2);
u8g.setPrintPos(98,36); u8g.print(min_temp); u8g.drawCircle(113,29,2);
u8g.setPrintPos(98,54); u8g.print(max_temp); u8g.drawCircle(113,47,2);
}
// 绘制指针通用函数
void desenha_ponteiro(float valor, float rotacao, int Radius) {
float angulo = valor * 2.0 * 3.1415 / rotacao - 1.5707;
int X2 = ScreenWithC + Radius * cos(angulo);
int Y2 = ScreenHeightC + Radius * sin(angulo);
u8g.drawLine(ScreenWithC, ScreenHeightC, X2, Y2);
}
// 绘制表盘背景
void fundo_relogio() {
u8g.drawCircle(ScreenWithC, ScreenHeightC, 27);
u8g.drawCircle(ScreenWithC, ScreenHeightC, 1);
u8g.setFont(u8g_font_6x13); u8g.setFontPosTop();
u8g.drawStr(90,9,"12"); u8g.drawStr(114,25,"3"); u8g.drawStr(94,44,"6"); u8g.drawStr(74,25,"9");
for(int i=0;i<12;i++) {
float ang = i/12.0*2*3.1415;
int x2 = ScreenWithC + 25*cos(ang), y2 = ScreenHeightC + 25*sin(ang);
u8g.drawLine(x2,y2,x2,y2); // 刻度点
}
}
// 绘制数字时钟与日期
void mostra_relogio_digital() {
u8g.setFont(u8g_font_5x8);
u8g.setPrintPos(8,55); u8g.print(day()); u8g.drawStr(19,55,"/");
u8g.setPrintPos(24,55); u8g.print(month()); u8g.drawStr(35,55,"/");
u8g.setPrintPos(41,55); u8g.print(year());
u8g.drawRBox(3,4,62,21,2);
u8g.setColorIndex(0); u8g.setFont(u8g_font_fub17); u8g.drawStr(29,21,":");
int posH = (hour()<10)? (u8g.drawStr(3,23,"0"),16) : 3;
u8g.setPrintPos(posH,23); u8g.print(hour());
int posM = (minute()<10)? (u8g.drawStr(38,23,"0"),51) : 38;
u8g.setPrintPos(posM,23); u8g.print(minute());
u8g.setColorIndex(1);
}
void setup() {
pinMode(3, INPUT_PULLUP);
Serial.begin(9600);
// U8g 颜色模式初始化
if (u8g.getMode()==U8G_MODE_R3G3B2) u8g.setColorIndex(255);
else if (u8g.getMode()==U8G_MODE_GRAY2BIT) u8g.setColorIndex(3);
else if (u8g.getMode()==U8G_MODE_BW) u8g.setColorIndex(1);
setSyncProvider(RTC.get);
if (timeStatus()!=timeSet) Serial << F(" RTC FAIL!");
}
void loop() {
// 读取 RTC 内部温度 (0.25°C 分辨率)
temperatura = RTC.temperature() / 4.0;
if (temperatura >= max_temp) max_temp = temperatura;
if (temperatura <= min_temp) min_temp = temperatura;
// 串口设置时间
if (Serial.available() >= 12) {
int y = Serial.parseInt();
if (y>=100 && y<1000) Serial << F("Erro: Ano 2/4 digitos!") << endl;
else {
tmElements_t tm;
if (y>=1000) tm.Year = CalendarYrToTm(y); else tm.Year = y2kYearToTm(y);
tm.Month = Serial.parseInt(); tm.Day = Serial.parseInt();
tm.Hour = Serial.parseInt(); tm.Minute = Serial.parseInt(); tm.Second = Serial.parseInt();
time_t t = makeTime(tm); RTC.set(t); setTime(t);
Serial << F("Horario modificado: "); printDateTime(t); Serial << endl;
while(Serial.available()) Serial.read();
}
}
// 按键消抖切换界面
static bool lastBtn = true;
bool curBtn = digitalRead(3);
if (curBtn != lastBtn) { delay(20); if (digitalRead(3)==curBtn) { estado_botao = !estado_botao; } }
lastBtn = curBtn;
// 画面刷新
u8g.firstPage();
do { if (estado_botao==0) tela_1(); else tela_2(); } while(u8g.nextPage());
delay(10);
}
// 辅助:串口打印时间
void printDateTime(time_t t) {
printI00(day(t),0); Serial << monthShortStr(month(t)) << _DEC(year(t)) << ' ';
printI00(hour(t),':'); printI00(minute(t),':'); printI00(second(t),' ');
}
void printI00(int val, char delim) { if(val<10) Serial << '0'; Serial << _DEC(val); if(delim) Serial << delim; }
接线原理图

关键连接:
DS3231 SDA→A4, SCL→A5, VCC→3.3V
LCD: EN→D6, RW→D5, RS→D4, RST→D7, VCC→5V
按键→D3 (内部上拉)
电位器调节 LCD 对比度 (VO 引脚)
制造工艺
- Arduino POV 时钟:自制视觉暂留 LED 显示器
- 自制大尺寸LED主时钟带闹钟功能 — 12米外清晰可见
- Arduino UNO 驱动 16x2 LCD 实现动画与小游戏
- Arduino Due 驱动 3.2″ TFT 实现温度监测与实时时钟
- TM1637 数码管时钟:Arduino Nano 实现时间设定与闹钟
- Arduino DIY 电压表:Nokia 5110 屏幕显示,手把手教学
- BME280 环境数据实时显示于 Nextion 触摸屏——Visuino 图形化开发
- Arduino 声纳实时测距与 Processing 可视化
- DIY Arduino Nano 示波器:10 Hz–50 kHz / 128×64 LCD
- 用 DS1302 RTC 和 Arduino UNO 制作可靠闹钟
- 模拟与数字信号:工业仪表基础
- 模拟与数字传感器:类型、应用与实战案例对比