Arduino教程:制作贪睡数字闹钟
5.UNO闹钟
点击此处alarm-clock.ino – Wokwi Arduino and ESP32 Simulator开始在线仿真
该数字闹钟在4位7段显示屏上显示时间。它具有以下特点:
如何使用时钟
要设置时间,请按分钟/小时按钮。
按下警报按钮可启用/禁用警报。屏幕将通过显示单词"打开"或"关闭"来显示警报状态。
启用报警后,当前报警时间将显示几秒钟。您可以使用分钟/小时按钮来调整闹钟时间。要完成操作,请再次按"闹钟"按钮,或等待几秒钟。
当闹钟响起时,短按闹钟按钮可将其暂停 9 分钟。屏幕将显示四个圆圈,让您知道闹钟已打盹。
要停止闹钟,请按住闹钟按钮一秒钟或更长时间。
项目结构
该代码分为几个模块:
- config.h – 时钟的配置选项:是否使用RTC芯片,贪睡时间等。当程序中有多个模块时,使用配置文件是一种常见的做法。
- alarm-clock.ino – 主程序代码。它管理用户界面:7段显示器和按钮。
- 时钟 – 类管理当前时间和警报状态机。它使用RTClib库与RTC芯片通信并跟踪时间。
Clock
- 闹钟铃声 – 班级播放闹钟声音。您可以更改数组的值,并自定义闹钟并播放不同的音调和旋律。
AlarmTone``TONES``TONE_TIME``TONE_SPACING
硬件
项目 | 数量 | 笔记 |
---|---|---|
Arduino Uno R3 | 1 | |
4 位 7 段 | 4 | 通用阳极,14 引脚 |
220Ω 电阻器 | 8 | 连接到 7 段段引脚 |
PNP 晶体管 | 4 | 可选,推荐 |
4.7kΩ 电阻器 | 4 | 如果您使用PNP晶体管 |
12 毫米按钮 | 3 | |
压电蜂鸣器 | 1 | 用于报警 |
DS1307 RTC | 1 | 自选 |
DISPLAY_TYPE
为了将时钟硬件保持在最低限度,Arduino使用SevSeg库直接控制7段显示。但是,这种方法有一个缺点:它使用12个GPIO引脚!
如果要节省Arduino引脚,可以使用74HC595移位寄存器将引脚使用量减少到6个,甚至可以使用带有集成控制器芯片的7段显示器,例如TM1637,HT16K33或MAX7219。在这种情况下,您需要更改代码以使用不同的显示库(SevSeg 不支持这种情况),但这超出了本项目的范围。
引脚连接
Arduino Uno Pin | Device | Device Pin |
---|---|---|
2 | 7-Segment | 14 (Dig 1) |
3 | 7-Segment | 11 (Dig 2) |
4 | 7-Segment | 10 (Dig 3) |
5 | 7-Segment | 6 (Dig 4) |
6 | 7-Segment | 13 (A) |
7 | 7-Segment | 9 (B) |
8 | 7-Segment | 4 © |
9 | 7-Segment | 2 (D) |
10 | 7-Segment | 1 (E) |
11 | 7-Segment | 12 (F) |
12 | 7-Segment | 5 (G) |
13 | 7-Segment | 8 (Colon) |
A0 | Hour Button | – |
A1 | Minute Button | – |
A2 | Alarm Button | – |
A3 | Buzzer / Speaker | – |
A4 | DS1307 RTC | SDA |
A5 | DS1307 RTC | SCL |
代码
/**
Arduino Digital Alarm Clock
Copyright (C) 2020, Uri Shaked.
Released under the MIT License.
*/
#include <SevSeg.h>//数码管函数库
#include "Button.h"
#include "AlarmTone.h"
#include "Clock.h"
#include "config.h"
const int COLON_PIN = 13;
const int SPEAKER_PIN = A3;
Button hourButton(A0);
Button minuteButton(A1);
Button alarmButton(A2);
AlarmTone alarmTone;//闹钟音
Clock clock;//
SevSeg sevseg;//
enum DisplayState {//枚举显示时钟,时间等参数
DisplayClock,
DisplayAlarmStatus,
DisplayAlarmTime,
DisplayAlarmActive,
DisplaySnooze,
};
DisplayState displayState = DisplayClock;
long lastStateChange = 0;
void changeDisplayState(DisplayState newValue) {
displayState = newValue;
lastStateChange = millis();
}//改变显示时间
long millisSinceStateChange() {
return millis() - lastStateChange;
}
void setColon(bool value) {
digitalWrite(COLON_PIN, value ? LOW : HIGH);
}//设置冒号
void displayTime() {
DateTime now = clock.now();
bool blinkState = now.second() % 2 == 0;
sevseg.setNumber(now.hour() * 100 + now.minute());
setColon(blinkState);
}
void clockState() {
displayTime();
if (alarmButton.read() == Button::RELEASED && clock.alarmActive()) {
//读取alarmButton has_changed()以清除其状态
alarmButton.has_changed();
changeDisplayState(DisplayAlarmActive);
return;
}
if (hourButton.pressed()) {
clock.incrementHour();
}//按键按下增加小时
if (minuteButton.pressed()) {
clock.incrementMinute();
}
if (alarmButton.pressed()) {
clock.toggleAlarm();
changeDisplayState(DisplayAlarmStatus);
}
}
void alarmStatusState() {
setColon(false);
sevseg.setChars(clock.alarmEnabled() ? " on" : " off");
if (millisSinceStateChange() > ALARM_STATUS_DISPLAY_TIME) {
changeDisplayState(clock.alarmEnabled() ? DisplayAlarmTime : DisplayClock);
return;
}
}//闹钟开关
void alarmTimeState() {
DateTime alarm = clock.alarmTime();
sevseg.setNumber(alarm.hour() * 100 + alarm.minute(), -1);
if (millisSinceStateChange() > ALARM_HOUR_DISPLAY_TIME || alarmButton.pressed()) {
changeDisplayState(DisplayClock);
return;
}
if (hourButton.pressed()) {
clock.incrementAlarmHour();
lastStateChange = millis();
}
if (minuteButton.pressed()) {
clock.incrementAlarmMinute();
lastStateChange = millis();
}
if (alarmButton.pressed()) {
changeDisplayState(DisplayClock);
}
}
void alarmState() {
displayTime();//显示时间
if (alarmButton.read() == Button::RELEASED) {
alarmTone.play();//启动报警
}
if (alarmButton.pressed()) {
alarmTone.stop();//按键按下报警结束
}
if (alarmButton.released()) {
alarmTone.stop();//按键释放报警结束
bool longPress = alarmButton.repeat_count() > 0;
if (longPress) {//长按
clock.stopAlarm();
changeDisplayState(DisplayClock);
} else {
clock.snooze();//小盹模式
changeDisplayState(DisplaySnooze);
}
}
}
void snoozeState() {//小盹模式
sevseg.setChars("****");
if (millisSinceStateChange() > SNOOZE_DISPLAY_TIME) {
changeDisplayState(DisplayClock);
return;
}
}
void setup() {
Serial.begin(115200);
clock.begin();
hourButton.begin();
hourButton.set_repeat(500, 200);
minuteButton.begin();
minuteButton.set_repeat(500, 200);
alarmButton.begin();
alarmButton.set_repeat(1000, -1);
alarmTone.begin(SPEAKER_PIN);
pinMode(COLON_PIN, OUTPUT);
byte digits = 4;
byte digitPins[] = {2, 3, 4, 5};
byte segmentPins[] = {6, 7, 8, 9, 10, 11, 12};
bool resistorsOnSegments = false;
bool updateWithDelays = false;
bool leadingZeros = true;
bool disableDecPoint = true;
sevseg.begin(DISPLAY_TYPE, digits, digitPins, segmentPins, resistorsOnSegments,
updateWithDelays, leadingZeros, disableDecPoint);
sevseg.setBrightness(90);
}
void loop() {
sevseg.refreshDisplay();
switch (displayState) {
case DisplayClock:
clockState();
break;
case DisplayAlarmStatus:
alarmStatusState();
break;
case DisplayAlarmTime:
alarmTimeState();
break;
case DisplayAlarmActive:
alarmState();
break;
case DisplaySnooze:
snoozeState();
break;
}
}
结束
知命者不怨天,知己者不怨人。 – 淮南子 《淮南子》