亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Manufacturing Technology >> 制造工艺

Arduino 8×8 LED 矩阵翻页时钟 - DIY 实时时钟项目

组件与材料

Arduino Nano R3 ×1
Maxim DS3231M - ±5ppm I2C 实时时钟模块 ×1
8×8 LED 矩阵模块 (MAX7219 驱动) ×1
RGB 共阴极 LED ×1
220Ω 电阻 ×2
带按键的旋转编码器 ×1

所需工具

软件与在线服务

项目详解

本项目基于 Adrian Jones 开发的 8×8 LED 矩阵时钟模块。时间通过从左向右滚动显示,或在软件控制下从上向下翻转显示。每个时间数字依次从右侧滑入,居中时短暂停留并略微变亮,随后向左滚动退出,下一个数字滑入,循环往复。

垂直翻转模式(从上到下)设置方法:

static boolean top2bottom = false;  // 改为 true 启用垂直翻转

代码注释中提供了丰富的可调参数:滚动速度、字符间空行数、12/24 小时制、字符间延时、亮度等。

时间设置

使用旋转编码器轻松设置时间。双色 LED 指示设置模式(小时、分钟或正常模式)。

时钟外壳由两个基础部件组成,内部访问非常方便实用。

Arduino 8×8 LED 矩阵翻页时钟 - DIY 实时时钟项目

核心代码

//*****************************************************************************************//
// MATRIX CLOCK - Adrian Jones, March 2014
// 支持左右滚动和上下翻转显示
//*****************************************************************************************//
#include 
#include 

// MAX7219 寄存器定义
#define max7219_reg_noop 0x00
#define max7219_reg_digit0 0x01
// ... (其余寄存器定义省略)
#define dataIn 12  // DIN
#define load 10    // CS
#define clock 11   // CLK

// 字符字模库 (8×8)
char alphanum[][8] = {
  {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 空格
  {0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x10}, // !
  // ... (完整字模数据省略,含数字、字母、符号)
};

// RTC 设置
RTC_DS1307 RTC;

// 旋转编码器、按键与 LED 控制引脚
#define enc_PinA 2
#define enc_PinB 4
#define enc_Switch 3
#define mode_Pin 8
#define min_Pin 9

// 运行参数
#define delay_line 75    // 行间延时(ms)
#define delay_char 400   // 字符间延时(ms)
#define delay_mess 500   // 消息间延时(ms)
#define cblanks 1        // 字符间空行数
#define eblanks 0        // 消息末尾额外空行

// 显示特性
static boolean top2bottom = false; // 显示方向
static boolean hour24 = false;     // 24小时制
static boolean charHI = true;      // 字符高亮
static boolean doSerial = true;    // 串口输出

void setup() {
  Wire.begin();
  Serial.begin(57600);
  
  // LED 矩阵控制引脚
  pinMode(dataIn, OUTPUT);
  pinMode(clock, OUTPUT);
  pinMode(load, OUTPUT);
  initMatrix();
  
  // 模式指示 LED
  pinMode(mode_Pin, OUTPUT); digitalWrite(mode_Pin, 1);
  pinMode(min_Pin, OUTPUT); digitalWrite(min_Pin, 1);
  
  // 编码器引脚
  pinMode(enc_PinA, INPUT_PULLUP); digitalWrite(enc_PinA, HIGH);
  pinMode(enc_PinB, INPUT_PULLUP); digitalWrite(enc_PinB, HIGH);
  pinMode(enc_Switch, INPUT_PULLUP); digitalWrite(enc_Switch, HIGH);
  
  attachInterrupt(0, rotEncoder, CHANGE); // 时间设置中断
  attachInterrupt(1, swEncoder, CHANGE);  // 模式切换中断
  
  RTC.begin();
  if (!RTC.isrunning()) {
    RTC.adjust(DateTime(__DATE__, __TIME__));
    if(doSerial) Serial.println(" (RTC reset)");
  } else {
    if(doSerial) Serial.println(" (RTC running)");
  }
}

void loop() {
  DateTime now = RTC.now();
  display_message = createMessage(now);
  arraylen = initDisplayString(display_message);
  
  if(updateFlag) { show_time_and_date(now); updateFlag = false; }
  
  while(rotating) {
    delay(1);
    adjTime(now, clockwise);
    show_time_and_date(RTC.now());
    display_message = createMessage(now);
    arraylen = initDisplayString(display_message);
    delay(1);
    rotating = false;
  }
  
  delay(5);
  for (int i = 0; i < (arraylen-7); i++) {
    for (int j = 1; j < 9; j++) {
      maxSingle(j, ac[i+8-j]);
    }
    
    if(i % (8+cblanks) == 0) {
      if(charHI) maxSingle(max7219_reg_intensity, 0x01);
      newDelay(delay_char);
    } else {
      maxSingle(max7219_reg_intensity, 0x00);
      newDelay(delay_line);
    }
  }
  if(mode == 0) newDelay(delay_mess);
}

// 中断服务程序:编码器旋转
void rotEncoder() {
  delay(1);
  enc_A = digitalRead(enc_PinA);
  enc_B = digitalRead(enc_PinB);
  if(!enc_A && enc_A_prev) {
    clockwise = (!enc_A && enc_B) ? true : false;
    if(mode != 0) rotating = true;
  }
  enc_A_prev = enc_A;
}

// 中断服务程序:编码器按键
void swEncoder() {
  delay(1);
  if(digitalRead(enc_Switch) != LOW) return;
  delay(1);
  if(digitalRead(enc_Switch) != LOW) return;
  mode = (mode + 1) % 3;
  digitalWrite(mode_Pin, !(mode == 1));
  digitalWrite(min_Pin, !(mode == 2));
  updateFlag = true;
}

// 延时函数(可被中断)
void newDelay(int dly) {
  for (int z=1; z < dly; z++) {
    delay(1);
    if(rotating || updateFlag) break;
  }
}

// MAX7219 初始化
void initMatrix() {
  maxSingle(max7219_reg_scanLimit, 0x07);
  maxSingle(max7219_reg_decodeMode, 0x00);
  maxSingle(max7219_reg_shutdown, 0x01);
  maxSingle(max7219_reg_displayTest, 0x00);
  for (int e=1; e<=8; e++) maxSingle(e, 0);
  maxSingle(max7219_reg_intensity, 0x00);
}

// 时间调整
void adjTime(DateTime now, boolean dir) {
  if(mode == 1) { // 调整小时
    int adj_hrs = now.hour();
    if(dir) { if(++adj_hrs >= 25) adj_hrs = 1; }
    else { if(adj_hrs == 0) adj_hrs = 24; if(--adj_hrs <= 0) adj_hrs = 24; }
    RTC.adjust(DateTime(now.year(), now.month(), now.day(), adj_hrs, now.minute(), now.second()));
  }
  if(mode == 2) { // 调整分钟
    int adj_mins = now.minute();
    if(dir) { if(++adj_mins >= 60) adj_mins = 0; }
    else { if(--adj_mins < 0) adj_mins = 59; }
    RTC.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), adj_mins, now.second()));
  }
}

// 字符旋转90度(垂直显示用)
byte rotChar(char inLetter) {
  int ind = int(inLetter) - 0x20;
  for (int col = 0; col < 8; col++) {
    byte mask = 0x01 << (7-col);
    for (int row = 0; row < 8; row++) {
      bitWrite(rc[col], 7-row, (alphanum[ind][row] & mask));
    }
  }
}

// 串口输出时间
void show_time_and_date(DateTime datetime) {
  if(doSerial) {
    // 格式化输出年月日时分秒
  }
}

// 生成显示消息字符串
String createMessage(DateTime datetime) {
  String new_mess = " ";
  int hr = datetime.hour() % 24;
  if(hr == 0) hr = 24;
  int mn = datetime.minute();
  
  if(mode == 0) { // 正常模式
    if(hour24) new_mess += hr;
    else new_mess += (hr > 12) ? hr - 12 : hr;
    new_mess += ':';
    if(mn < 10) new_mess += '0';
    new_mess += mn;
    if(!hour24) new_mess += (hr > 12) ? "~" : "^";
  }
  if(mode == 1) new_mess += hr; // 调整小时模式
  if(mode == 2) { // 调整分钟模式
    if(mn < 10) new_mess += '0';
    new_mess += mn;
  }
  return new_mess;
}

// 初始化显示数组
int initDisplayString(String message) {
  int x = 0;
  for (int y = 0; y < message.length(); y++) {
    char thisCh = message.charAt(y);
    int ind = int(thisCh) - 0x20;
    if(!top2bottom) rotChar(thisCh);
    for (int row = 0; row < 8 + cblanks; row++) {
      if (row <= 7) ac[x] = (top2bottom) ? alphanum[ind][7-row] : rc[row];
      else ac[x] = 0;
      x++;
    }
  }
  for(int y = 0; y < 7+eblanks; y++) { ac[x] = 0; x++; }
  return x;
}

// MAX7219 单字节写入
void maxSingle(byte reg, byte col) {
  digitalWrite(load, LOW);
  putByte(reg);
  putByte(col);
  digitalWrite(load, HIGH);
}

// SPI 位发送
void putByte(byte data) {
  byte i = 8, mask;
  while(i > 0) {
    mask = 0x01 << (i - 1);
    digitalWrite(clock, LOW);
    if (data & mask) digitalWrite(dataIn, HIGH);
    else digitalWrite(dataIn, LOW);
    digitalWrite(clock, HIGH);
    --i;
  }
}

原理图

Arduino 8×8 LED 矩阵翻页时钟 - DIY 实时时钟项目

制造工艺

  1. Arduino POV 时钟:自制视觉暂留 LED 显示器
  2. 复古 Numitron 管时钟:Arduino Nano 驱动,低温可靠省电
  3. 自制大尺寸LED主时钟带闹钟功能 — 12米外清晰可见
  4. 自制 16×8 LED 矩阵俄罗斯方块游戏机:Arduino Nano + 74HC595 驱动
  5. MotionSense:基于 Arduino 与 ESP8266 的智能入侵检测系统
  6. 互动 LED 矩阵门显:PIR 触发节日问候 + 待机特效
  7. 从零构建智能 Arduino 四足机器人:完整指南
  8. Bolt IoT + Arduino UNO 实现 6 路 LED 无级调光
  9. 基于 Arduino、Yaler 与 IFTTT 构建 Web 化 IoT 仪表盘
  10. Arduino 简易排斥式磁悬浮:电路与 DIY 指南
  11. 高性能 Arduino 气象气球数据记录器
  12. Arduino Uno驱动MAX7219级联8×8 LED矩阵:从入门到动画显示