本文最后更新于 627 天前,其中的信息可能已经有所发展或是发生改变。
- 实验目的:设计实现一个电子时钟,具备动态屏保、显示/修改时间的功能。
- 实验器材:
- mini遥控器一个
- Irrecv 红外接收模块
- 1602液晶显示屏
- 蜂鸣器
- Arduino Uno主板
- Xbee传感器拓展板
接线示意图:
实验现象:
- 液晶显示屏以”年/月/日 时:分:秒 星期”的格式分两行显示时间
- 遥控器
- 1)电源键按下,显示屏熄屏/亮屏
- 2)按下1,进入修改时间模式;再按1,显示修改后时间
- 3)按下2,进入设定闹钟模式;再按2;保存闹钟并显示之前时间
- 进入修改时间或设定闹钟模式时
- 1)按vol+键可循环增加光标闪烁数位的值
- 2)按左、右快进键可向左向右移动闪烁光标的位置
- 当时间到达设定闹钟值,蜂鸣器开始闹铃
- 温馨提示:请使用质量较好的遥控器以及红外接受器
功能实现分析:
首先是把整个过程分为三个部分:
- 时钟显示和递增
- 时间修改
- 闹钟功能
- 时间显示和递增
大致流程是时间变量的定义及初始化→显示屏打印时间变量→时间变量递增
后两步一直循环
关键点:变量递增的时候要考虑实际是否存在,以及进位。在每次打印后可以使用delay(1000)的方式来实现延迟一秒递增。
- 时间修改
这个模块的实现大概要考虑这几个功能:
- 信号接受的判断
- 开启与关闭时间修改功能
- 修改值(此处可以设计为仅仅使用vol+/-)
为了达到开关的效果,代码中会考虑用到bool变量来实现,并且设计为当bool变量为真时,在接受到对应的合理信号时才会变动
关键点:如何判断该调整哪一个数字?如何判断修改后的值是否合理?前者可以考虑用一个数组来描述坐标,后者自己加油吧!
- 闹钟功能
其实在前两个模块写完之后,这个模块就显得比较简单了。可以考虑设置两组时间变量,一组是用来存储在进入闹钟模块前的时间,一组是用来存储闹钟的时间。
关键点:闹钟模块开启的时候应该顺带开启时间修改功能,从而把两个模块一起利用,减少代码量也提高效率。
- 实验结果展示
- 时间显示
- 闹钟功能
- 实验参考源码(写的比较垃圾的那种)
#include<LiquidCrystal_I2C.h>
#include <IRremote.hpp>
LiquidCrystal_I2C lcd(0x20,16,2); //初始化lcd屏
#define IR_RECEIVE_PIN 10 //定义红外接受的接口
bool alarm_flag=false;
bool modify_flag=false;
bool light=false;
int Y1,Y2,Y3,Y4; //定义年份的四个数字变量
int alarm_pin=7;
int year;
int M1,M2; //定义月
int D1,D2; //定义日
int H1,H2; //定义时
int MI1,MI2; //定义分
int S1,S2; //定义秒
int Date; //定义周
int x,y;
int modifyIndex=0;
int pos[]={3,4,5,6,8,9,11,12,
13,14,16,17,19,20,22};
int tY1,tY2,tY3,tY4,tM1,tM2,tD1,tD2,tH1,tH2,tMI1,tMI2,tS1,tS2; //定义临时变量
int aY1,aY2,aY3,aY4,aM1,aM2,aD1,aD2,aH1,aH2,aMI1,aMI2,aS1,aS2; //定义闹钟变量
void xblink(){
lcd.setCursor(x,y);
lcd.blink();
}
void xNoblink()
{
lcd.noBlink();
}
void Show(){
lcd.setCursor(3,0);
lcd.print(Y1);
lcd.print(Y2);
lcd.print(Y3);
lcd.print(Y4);
lcd.print("/");
lcd.print(M1);
lcd.print(M2);
lcd.print("/");
lcd.print(D1);
lcd.print(D2);
lcd.setCursor(1,1);
lcd.print(H1);
lcd.print(H2);
lcd.print(":");
lcd.print(MI1);
lcd.print(MI2);
lcd.print(":");
lcd.print(S1);
lcd.print(S2);
lcd.setCursor(10,1);
switch(Date){
case 1:lcd.print("Mon");
break;
case 2:lcd.print("Tue");
break;
case 3:lcd.print("Wes");
break;
case 4:lcd.print("Thu");
break;
case 5:lcd.print("Fri");
break;
case 6:lcd.print("Sat");
break;
case 7:lcd.print("Sun");
break;
default:lcd.print("Error");
}
}
void tita(){
S2++;
if(S2==10){
S1++;
S2-=10;
}
if(S1==6){
MI2++;
S1-=6;
}
if(MI2==10){
MI1++;
MI2-=10;
}
if(MI1==6){
H2++;
MI1-=6;
}
if(H1<2&&H2==10){
H1++;
H2-=10;
}
if(H1==2&&H2==4){
H1=0;
H2=0;
D2++;
Date++;
}
if(Date==8)
Date=1;
if(M1==0)
{
if(M2==1||M2==3||M2==5||M2==7||M2==8)
{
if(D1<3&&D2==10)
{
D2++;
D1=0;
}
if(D1==3&&D2==2)
{
D1=0;
D2=1;
M2++;
}
}
if(M2==4||M2==6||M2==9)
{
if(D1<3&&D2==10)
{
D1++;
D2=0;
}
if(D1==3&&D2==1)
{
D1=0;
D2=1;
M2++;
}
}
if(M2==2)
{
if(runnian())
{
if(D1<3&&D2==10)
{
D1++;
D2=0;
}
if(D1==3&&D2==0)
{
D1=0;
D2=1;
M2++;
}
}
else
{
if(D1<2&&D2==10)
{
D1++;
D2=0;
}
if(D1==2&&D2==9)
{
D1=0;
D2=1;
M2++;
}
}
}
}
else if(M1==1)
{
if(M2==1)
{
if(D1<3&&D2==10)
{
D1++;
D2=0;
}
if(D1==3&&D2==1)
{
D1=0;
D2=1;
M2++;
}
}
if(M2==0)
{
if(D1<3&&D2==10)
{
D1++;
D2=0;
}
if(D1==3&&D2==2)
{
D1=0;
D2=1;
M2++;
}
}
if(M2==2)
{
if(D1<3&&D2==10)
{
D1++;
D2=0;
}
if(D1==3&&D2==2)
{
D1=0;
D2=1;
M1=0;
M2=1;
Y4++;
}
}
}
if(Y4==10)
{
Y4=0;
Y3++;
}
if(Y3==10)
{
Y3=0;
Y2++;
}
if(Y2==10)
{
Y2=0;
Y1++;
}
if(Y1==10)
{
Y1=0;
}
}
void onoff(){
if(light)
{
light=false;
lcd.noBacklight();
}
else
{
light=true;
lcd.backlight();
}
}
void setup() {
// put your setup code here, to run once:
pinMode(alarm_pin,OUTPUT); //设置蜂鸣器为输出
lcd.init(); // lcd 初始化,无参
lcd.backlight(); // lcd 背景灯常亮
light=true;
Serial.begin(9600); // 设置串口波特率
IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);
Y1=2,Y2=0,Y3=2,Y4=3;
M1=1,M2=1;
D1=2,D2=1;
H1=0,H2=9;
MI1=1,MI2=4;
S1=0,S2=1;
Date=2;
}
void alarm()
{
if(aY1==Y1&&aY2==Y2&&aY3==Y3&&aY4==Y4&&aM1==M1&&aM2==M2&&aD1==D1&&aD2==D2)
{
if(aH1==H1&&aH2==H2&&aMI1==MI1&&aMI2==MI2&&aS1==S1&&aS2==S2)
{
tone(alarm_pin,300);
delay(2500);
noTone(alarm_pin);
}
}
} //分两次判断可以减少运算量
int runnian(){
year=Y1*1000+Y2*100+Y3*10+Y4;
if(year%4==0&&year%100!=0)
return 1;
if(year%400==0)
return 1;
return 0;
}
void addtime(){
if(y==0) //光标在第一行時
{
switch(x)
{
case 3: //修改年
{
Y1++;
if(Y1>=10)
Y1=0;
}
break;
case 4:
{
Y2++;
if(Y2>=10)
Y2=0;
}
break;
case 5:
{
Y3++;
if(Y3>=10)
Y3=0;
}
break;
case 6:
{
Y4++;
if(Y4>=10)
Y4=0;
}
break;
case 8: //修改月
{
M1++;
if(M1>=2)
M1=0;
}
break;
case 9:
{
M2++;
if(M1==1&&M2>=3)
M2=0;
if(M1==0&&M2>=10)
M2=0;
}
break;
case 11: //修改第一个日
{
D1++;
switch(M1*10+M2)
{
case 1:;
case 3:;
case 5:;
case 7:;
case 8:;
case 10:;
case 12:
{
if(D1>3) //大月的情况
D1=0;
if(D1==3&&D2>1)
D1=0;
}
break;
case 4:;
case 6:;
case 9:
case 11: //小月的情况
{
if(D1>3)
D1=0;
if(D1==3&&D2>0)
D1=0;
}
break;
case 2:
{
if(D1>2)
D1=0;
if(runnian())
;
else
{
if(D1==2&&D2==9)
D1=0;
}
}
break;
}
}
break;
case 12: //修改第二个日
{
D2++;
switch(M1*10+M2)
{
case 1:;
case 3:;
case 5:;
case 7:;
case 8:;
case 10:;
case 12:
{
if(D1>=3)
{
if(D2>=2)
D2=0;
}
else
{
if(D2>=10)
D2=0;
}
}
break;
case 4:;
case 6:;
case 9:;
case 11:
{
if(D1>=3)
{
if(D2>=1)
D2=0;
}
else
{
if(D2>=10)
D2=0;
}
}
break;
case 2:
{
if(runnian())
{
if(D2>=10)
D2=0;
}
else
{
if(D1==2)
{
if(D2>=9)
D2=0;
}
else
{
if(D2>=10)
D2=0;
}
}
}
break;
}
}
default:break;
}
}
else if(y==1) //光标在第二行時
{
switch(x)
{
case 1: //修改時
{
H1++;
if(H2>=5&&H1>=2)
H1=0;
if(H1>=3)
H1=0;
}
break;
case 2:
{
H2++;
if(H1==2)
{
if(H2>=4)
H2=0;
}
else
{
if(H2>=10)
H2=0;
}
}
break;
case 4: //修改分
{
MI1++;
if(MI1>=6)
MI1=0;
}
break;
case 5:
{
MI2++;
if(MI2>=10)
MI2=0;
}
break;
case 7: //修改秒
{
S1=(S1+1)%6;
}
break;
case 8:
{
S2++;
if(S2>=10)
S2=0;
}
break;
case 10:
{
Date++;
if(Date>=8)
Date=1;
}
default:break;
}
}
if(M1==0&&M2==0)
M2=1;
if(D1==0&&D2==0)
D2=1;
if(H1==0&&H2==0)
H2=1;
if(MI1==0&&MI2==0)
MI2=1;
if(S1==0&&S2==0)
S2=1;
}
void loop() {
// put your main code here, to run repeatedly:
if(IrReceiver.decode()) //判断是否接收到信号
{
Serial.println(IrReceiver.decodedIRData.decodedRawData, HEX); //打印接收的红外信号到串口监视器
if(IrReceiver.decodedIRData.decodedRawData==0xFF00BF00) //接受电源信号;
onoff();
else if(IrReceiver.decodedIRData.decodedRawData==0xEF10BF00) //接受到1信号
{
modifyIndex=0; //初始化数组索引
modify_flag=!modify_flag; //取反,类似开关;
if(modify_flag)
{
xblink(); //开始闪烁
Serial.print("接受到1信号,进入修改时间模式");
}
else
{
xNoblink(); //关闭闪烁
modifyIndex=0; //初始化数组索引
}
}
else if(IrReceiver.decodedIRData.decodedRawData==0xFB04BF00) //接受到左移
{
if(modify_flag)
{
xNoblink();
modifyIndex=(modifyIndex+14)%15; //坐标-1;
x=pos[modifyIndex];
if(x>12) //如果横坐标到了逻辑上的第二行,则减去12的偏移,并设置纵坐标为1
{
x=x-12;
y=1;
}
else
{
y=0;
}
xblink();
}
}
else if(IrReceiver.decodedIRData.decodedRawData==0xF906BF00)
{
if(modify_flag) //接受到右移
{
xNoblink();
modifyIndex=(modifyIndex+16)%15; //坐标+1;
x=pos[modifyIndex];
if(x>12)
{
x=x-12;
y=1;
}
else
{
y=0;
}
xblink();
}
}
else if(IrReceiver.decodedIRData.decodedRawData==0xFE01BF00) //接受到vol+
{
if(modify_flag)
{
addtime();
Show();
lcd.setCursor(x,y);
}
}
else if(IrReceiver.decodedIRData.decodedRawData==0xEE11BF00) //接受到2信号
{
modifyIndex=0; //初始化数组索引
alarm_flag=!alarm_flag; //取反,类似开关;
if(alarm_flag)
{
xblink(); //开始闪烁
Serial.print("接受到2信号,进入闹钟设置");
modify_flag=true;
tS1=S1; //将时间存储至临时时间
tS2=S2;
tMI1=MI1;
tMI2=MI2;
tH1=H1;
tH2=H2;
tD1=D1;
tD2=D2;
tM1=M1;
tM2=M2;
tY1=Y1;
tY2=Y2;
tY3=Y3;
tY4=Y4;
}
else
{
modify_flag=false;
aS1=S1; //将时间存储到闹钟
aS2=S2;
aMI1=MI1;
aMI2=MI2;
aH1=H1;
aH2=H2;
aD1=D1;
aD2=D2;
aM1=M1;
aM2=M2;
aY1=Y1;
aY2=Y2;
aY3=Y3;
aY4=Y4;
Y4=tY4;
Y3=tY3;
Y2=tY2;
Y1=tY1;
M1=tM1;
M2=tM2;
D1=tD1;
D2=tD2;
H1=tH1;
H2=tH2;
MI1=tMI1;
MI2=tMI2;
S1=tS1;
S2=tS2;
xNoblink(); //关闭闪烁
modifyIndex=0; //初始化数组索引
}
}
IrReceiver.resume();
}
if(modify_flag);
else
{
Show(); //打印时间
delay(1000); //延时1s
tita(); //变量递增
alarm(); //判断闹钟
}
}
- 实验反思
- 能否在更改时间的时候时间继续走呢?
- 能否实现多个闹钟设置存储呢?(考虑用数组)
- 每次时间增加前延时设置为1000ms,真的合理吗?(考虑一下单片机的能力)
- 就目前的器材,能否提高红外的灵敏呢?