用1602和DS3231搭建的可调时钟 ~~新人帖~~标题要长~~
本帖最后由 singz 于 2015-5-21 10:07 编辑菜鸟刚入Arduino的坑,参照大神们的教程,试着用 1602液晶屏 和 DS3231时钟模块 搭建了个可调时钟。
1602和DS3231都用I2C总线连接,1602用转接板接I2C总线,闹铃是拆机得来的1只无源蜂鸣器。
有3个按键,Set键选择设置项目,Adj键调整数字,Reset键重启(中断闹钟)。
接线如模拟图所示(DS3231时钟模块是用555表示的,不要在意这些细节~):
运行效果见视频(没有找到合适的按键,用薄膜键盘凑合下,同样不要在意这些细节~):
http://v.youku.com/v_show/id_XOTYwNTUwMjM2.html
可以用按键设置时钟的年、月、日、星期、时、分、秒,以及闹钟1的时、分、秒、开关,闹钟开关的状态在屏幕上用铃铛的符号表示。
DS3231时钟模块可以设定2个闹钟,并且可以自定义按 月、日、星期、时等周期循环,感觉用处不大就没有写入(其实是懒...)。
12/24小时切换也没写,原因同上 ^_^ (还有就是因为1602的屏幕上没有富余地方了,不显示温度的话应该可以)。
代码如下:
为了避免按键不灵敏,时间函数用的都是millis,仅在清屏和蜂鸣器上用了delay(按键消抖用的也是millis),没有用中断库,因为没学会 ...而且中断消抖好麻烦。
看起来好多行的样子,其实大多是无意义的空白,
菜鸟 VB省二级 考了6次,至今未及格,C语言完全是零基础,学习Arduino刚十几天,代码写得很渣,望诸位轻拍 ~~
#include <Wire.h>
#include <DS3231.h>
#include <LiquidCrystal_I2C.h>
DS3231 Clock;
bool h12, PM, Century=false;
byte year, month, date, DoW, hour, minute, second;
bool ADy, A12h, Apm;
byte A1Day, A1Hour, A1Minute, A1Second, A1Bits;
uint8_t bell = {0x4,0xe,0xe,0xe,0x1f,0x0,0x4};// 绘制闹钟标志
LiquidCrystal_I2C lcd(0x27,16,2);
const byte Set=2;// 按键
const byte Adj=3;
byte button = {Set, Adj};
void setup()
{
Wire.begin();
lcd.init();
lcd.backlight();
lcd.createChar(0, bell);// 自定义闹钟标志
pinMode(Set,INPUT);// 按键针脚
pinMode(Adj,INPUT);
pinMode(12,OUTPUT);// 蜂鸣器针脚
}
/* ~~~~~~~~~~~~~~~~~~~~~~ 显示时间 ~~~~~~~~~~~~~~~~~~~~~~~ */
void Pritime()
{
int second,minute,hour,date,month,year,dow,temperature;
second=Clock.getSecond();
minute=Clock.getMinute();
hour=Clock.getHour(h12, PM);
date=Clock.getDate();
month=Clock.getMonth(Century);
year=Clock.getYear();
dow=Clock.getDoW();
temperature=Clock.getTemperature();
lcd.setCursor(0, 0);
lcd.print("20");// 显示20世纪
if (year>=10)// 显示年份
{
lcd.print(year,DEC);
}
else
{
lcd.print("0");
lcd.print(year,DEC);
}
lcd.print('-');
lcd.setCursor(5, 0);
if (month>=10)// 显示月份
{
lcd.print(month,DEC);
}
else
{
lcd.print("0");
lcd.print(month,DEC);
}
lcd.print('-');
lcd.setCursor(8, 0);
if (date>=10)// 显示日期
{
lcd.print(date,DEC);
}
else
{
lcd.print("0");
lcd.print(date,DEC);
}
lcd.setCursor(11, 0);
switch (dow)// 显示星期
{
case 1:// 当dow等于1时,执行以下语句
lcd.print("Mon");
break;
case 2:// 当dow等于2时,执行以下语句
lcd.print("Tue");
break;
case 3:
lcd.print("Wed");
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;
}
lcd.setCursor(0, 1);//光标移至第2行
if (hour>=10)// 显示小时
{
lcd.print(hour,DEC);
}
else
{
lcd.print("0");
lcd.print(hour,DEC);
}
lcd.print(':');
lcd.setCursor(3, 1);
if (minute>=10)// 显示分钟
{
lcd.print(minute,DEC);
}
else
{
lcd.print("0");
lcd.print(minute,DEC);
}
lcd.print(':');
lcd.setCursor(6, 1);
if (second>=10)// 显示秒钟
{
lcd.print(second,DEC);
}
else
{
lcd.print("0");
lcd.print(second,DEC);
}
lcd.setCursor(9, 1);
if (Clock.checkAlarmEnabled(1))// 显示闹钟标识
{
lcd.write(0x00);
}
else
{
lcd.print(' ');
}
lcd.setCursor(12, 1);
lcd.print(temperature);// 显示温度
lcd.write(0xdf);// 显示温度单位
lcd.print("C");
}
/* ~~~~~~~~~~~~~~~~~~~~~~ 时间冒号闪烁 ~~~~~~~~~~~~~~~~~~~~~~~ */
void Tictime()
{
lcd.setCursor(2, 1);
lcd.print(' ');
lcd.setCursor(5, 1);
lcd.print(' ');
}
/* ~~~~~~~~~~~~~~~~~~~~~~ 显示设置时间 ~~~~~~~~~~~~~~~~~~~~~~~ */
void PritimeSet(byte syear,byte smonth,byte sdate,byte sdow,byte shour,byte sminute,byte ssecond)
{
lcd.setCursor(0, 0);
lcd.print("20");// 显示20世纪
if (syear>=10)// 显示年份
{
lcd.print(syear,DEC);
}
else
{
lcd.print("0");
lcd.print(syear,DEC);
}
lcd.print('-');
lcd.setCursor(5, 0);
if (smonth>=10)// 显示月份
{
lcd.print(smonth,DEC);
}
else
{
lcd.print("0");
lcd.print(smonth,DEC);
}
lcd.print('-');
lcd.setCursor(8, 0);
if (sdate>=10)// 显示日期
{
lcd.print(sdate,DEC);
}
else
{
lcd.print("0");
lcd.print(sdate,DEC);
}
lcd.setCursor(11, 0);
switch (sdow)// 显示星期
{
case 1:// 当dow等于1时,执行一些语句
lcd.print("Mon");
break;
case 2:// 当dow等于2时,执行一些语句
lcd.print("Tue");
break;
case 3:
lcd.print("Wed");
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;
}
lcd.setCursor(0, 1);//光标移至第2行
if (shour>=10)// 显示小时
{
lcd.print(shour,DEC);
}
else
{
lcd.print("0");
lcd.print(shour,DEC);
}
lcd.print(':');
lcd.setCursor(3, 1);
if (sminute>=10)// 显示分钟
{
lcd.print(sminute,DEC);
}
else
{
lcd.print("0");
lcd.print(sminute,DEC);
}
lcd.print(':');
lcd.setCursor(6, 1);
if (ssecond>=10)// 显示秒钟
{
lcd.print(ssecond,DEC);
}
else
{
lcd.print("0");
lcd.print(ssecond,DEC);
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~ 时间设置位闪烁 ~~~~~~~~~~~~~~~~~~~~~~~ */
void TicSet(byte c,byte l)
{
lcd.setCursor(c,l);
lcd.print("");
}
/* ~~~~~~~~~~~~~~~~~~~~~~ 显示闹钟 ~~~~~~~~~~~~~~~~~~~~~~~ */
void PriA1()
{
lcd.setCursor(0, 0);// 光标移至第1行
lcd.print("Alarm1 ");
if (Clock.checkAlarmEnabled(1))// 显示闹钟状态
{
lcd.print("On");
}
else
{
lcd.print("Off");
}
Clock.getA1Time(A1Day, A1Hour, A1Minute, A1Second, A1Bits, ADy, A12h, Apm);
lcd.setCursor(0, 1);// 光标移至第2行
if (A1Hour>=10)// 显示小时
{
lcd.print(A1Hour,DEC);
}
else
{
lcd.print("0");
lcd.print(A1Hour,DEC);
}
lcd.print(':');
lcd.setCursor(3, 1);
if (A1Minute>=10)//显示分钟
{
lcd.print(A1Minute,DEC);
}
else
{
lcd.print("0");
lcd.print(A1Minute,DEC);
}
lcd.print(':');
lcd.setCursor(6, 1);
if (A1Second>=10)// 显示秒钟
{
lcd.print(A1Second,DEC);
}
else
{
lcd.print("0");
lcd.print(A1Second,DEC);
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~ 按键检测 ~~~~~~~~~~~~~~~~~~~~~~~ */
unsigned long LastP;
boolean buttonPress(byte button)
{
if(digitalRead(button)==HIGH) // 检测按键是否按下
{
unsigned long NowP = millis();
unsigned long buttonChange=NowP-LastP;// 与上次按键按下的时间进行对比
if (buttonChange>500)//按键间隔是否大于500ms
{
LastP = millis();// 记录按键按下的时间
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~ 时间设置 ~~~~~~~~~~~~~~~~~~~~~~~ */
void SetTime()
{
bool h12, PM, Century=false;
byte syear, smonth, sdate, sdow, shour, sminute, ssecond;
if(buttonPress(Set))
{
lcd.clear();
delay(200);
Clock.getTime(syear, smonth, sdate, sdow, shour, sminute, ssecond);
bool SetY=true;
/* ~~~~~~~~~~~~~~~~~~~~~~ 进入年份设置状态 ~~~~~~~~~~~~~~~~~~~~~~~ */
while(SetY)
{
unsigned long Rt=millis()%1000;
if(Rt<500)// 每500ms闪烁1次
{
PritimeSet(syear, smonth, sdate, sdow, shour, sminute, ssecond);
}
else
{
lcd.setCursor(0,0);
lcd.print(" ");
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~ 设置年份 ~~~~~~~~~~~~~~~~~~~~~~~~~~ */
if(buttonPress(Adj))
{
syear=syear+1;
}
/* ~~~~~~~~~~~~~~~~~~~~~~ 进入月份设置状态 ~~~~~~~~~~~~~~~~~~~~~ */
if(buttonPress(Set))
{
lcd.clear();
bool SetMon=true;
while(SetMon)
{
unsigned long Rt=millis()%1000;
if(Rt<500)
{
PritimeSet(syear, smonth, sdate, sdow, shour, sminute, ssecond);
}
else
{
TicSet(5,0);
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~ 设置月份 ~~~~~~~~~~~~~~~~~~~~~~~~~~ */
if(buttonPress(Adj))
{
smonth=smonth+1;
if(smonth>12)
{
smonth=1;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~ 进入日期设置状态 ~~~~~~~~~~~~~~~~~~~~~ */
if(buttonPress(Set))
{
lcd.clear();
bool SetD=true;
while(SetD)
{
unsigned long Rt=millis()%1000;
if(Rt<500)
{
PritimeSet(syear, smonth, sdate, sdow, shour, sminute, ssecond);
}
else
{
TicSet(8,0);
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~ 设置日期 ~~~~~~~~~~~~~~~~~~~~~~~~~~ */
if(buttonPress(Adj))
{
sdate=sdate+1;
if(sdate>31)
{
sdate=1;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~ 进入星期设置状态 ~~~~~~~~~~~~~~~~~~~~~ */
if(buttonPress(Set))
{
lcd.clear();
bool SetW=true;
while(SetW)
{
unsigned long Rt=millis()%1000;
if(Rt<500)
{
PritimeSet(syear, smonth, sdate, sdow, shour, sminute, ssecond);
}
else
{
lcd.setCursor(11,0);
lcd.print(" ");
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~ 设置星期 ~~~~~~~~~~~~~~~~~~~~~~~~~~ */
if(buttonPress(Adj))
{
sdow=sdow+1;
if(sdow>7)
{
sdow=1;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~ 进入小时设置状态 ~~~~~~~~~~~~~~~~~~~~~ */
if(buttonPress(Set))
{
lcd.clear();
long AdjP=0;
bool SetH=true;
while(SetH)
{
unsigned long Rt=millis()%1000;
if(Rt<500)
{
PritimeSet(syear, smonth, sdate, sdow, shour, sminute, ssecond);
}
else
{
TicSet(0,1);
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~ 设置小时 ~~~~~~~~~~~~~~~~~~~~~~~~~~ */
if(buttonPress(Adj))
{
AdjP=AdjP+1;
shour=shour+1;
if(shour>23)
{
shour=0;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~ 进入分钟设置状态 ~~~~~~~~~~~~~~~~~~~~~ */
if(buttonPress(Set))
{
lcd.clear();
bool SetMin=true;
while(SetMin)
{
unsigned long Rt=millis()%1000;
if(Rt<500)
{
PritimeSet(syear, smonth, sdate, sdow, shour, sminute, ssecond);
}
else
{
TicSet(3,1);
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~ 设置分钟 ~~~~~~~~~~~~~~~~~~~~~~~~~~ */
if(buttonPress(Adj))
{
AdjP=AdjP+1;
sminute=sminute+1;
if(sminute>59)
{
sminute=0;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~ 进入秒钟设置状态 ~~~~~~~~~~~~~~~~~~~~~ */
if(buttonPress(Set))
{
lcd.clear();
bool SetS=true;
while(SetS)
{
unsigned long Rt=millis()%1000;
if(Rt<500)
{
PritimeSet(syear, smonth, sdate, sdow, shour, sminute, ssecond);
}
else
{
TicSet(6,1);
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~ 设置秒钟 ~~~~~~~~~~~~~~~~~~~~~~~~~~ */
if(buttonPress(Adj))
{
AdjP=AdjP+1;
ssecond=ssecond+1;
if(ssecond>59)
{
ssecond=0;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~ 保存并退出时间设置 ~~~~~~~~~~~~~~~~~~~~~~~~~~ */
if(buttonPress(Set))
{
Clock.setYear(syear);
Clock.setMonth(smonth);
Clock.setDate(sdate);
Clock.setDoW(sdow);
if (AdjP>0)
{
Clock.setHour(shour);
Clock.setMinute(sminute);
Clock.setSecond(ssecond);
}
/* ~~~~~~~~~~~~~~~~~~~~~~ 进入闹钟小时设置状态 ~~~~~~~~~~~~~~~~~~~~~~~ */
lcd.clear();
delay(200);
bool SetA1H=true;
while(SetA1H)
{
unsigned long RA1=millis()%1000;
if(RA1<500)// 每500ms闪烁一次
{
PriA1();
}
else
{
TicSet(0,1);
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~ 设置闹钟小时 ~~~~~~~~~~~~~~~~~~~~~~~~~~ */
if(buttonPress(Adj))
{
Clock.getA1Time(A1Day, A1Hour, A1Minute, A1Second, A1Bits, ADy, A12h, Apm);
A1Hour=A1Hour+1;
if(A1Hour<24)
{
Clock.setA1Time(A1Day, A1Hour, A1Minute, A1Second, B1000, ADy, false, Apm);// 设置闹钟每天响一次
}
else
{
A1Hour=0;
Clock.setA1Time(A1Day, A1Hour, A1Minute, A1Second, B1000, ADy, false, Apm);
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~ 进入闹钟分钟设置状态 ~~~~~~~~~~~~~~~~~~~~~ */
if(buttonPress(Set))
{
lcd.clear();
bool SetA1M=true;
while(SetA1M)
{
unsigned long RA1=millis()%1000;
if(RA1<500)
{
PriA1();
}
else
{
TicSet(3,1);
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~ 设置闹钟分钟 ~~~~~~~~~~~~~~~~~~~~~~~~~~ */
if(buttonPress(Adj))
{
Clock.getA1Time(A1Day, A1Hour, A1Minute, A1Second, A1Bits, ADy, A12h, Apm);
A1Minute=A1Minute+1;
if(A1Minute<60)
{
Clock.setA1Time(A1Day, A1Hour, A1Minute, A1Second, B1000, ADy, false, Apm);
}
else
{
A1Minute=0;
Clock.setA1Time(A1Day, A1Hour, A1Minute, A1Second, B1000, ADy, false, Apm);
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~ 进入闹钟秒钟设置状态 ~~~~~~~~~~~~~~~~~~~~~ */
if(buttonPress(Set))
{
lcd.clear();
bool SetA1S=true;
while(SetA1S)
{
unsigned long RA1=millis()%1000;
if(RA1<500)
{
PriA1();
}
else
{
TicSet(6,1);
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~ 设置闹钟秒钟 ~~~~~~~~~~~~~~~~~~~~~~~~~~ */
if(buttonPress(Adj))
{
Clock.getA1Time(A1Day, A1Hour, A1Minute, A1Second, A1Bits, ADy, A12h, Apm);
A1Second=A1Second+1;
if(A1Second<60)
{
Clock.setA1Time(A1Day, A1Hour, A1Minute, A1Second, B1000, ADy, false, Apm);
}
else
{
A1Second=0;
Clock.setA1Time(A1Day, A1Hour, A1Minute, A1Second, B1000, ADy, false, Apm);
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~ 进入闹钟开关设置状态 ~~~~~~~~~~~~~~~~~~~~~ */
if(buttonPress(Set))
{
lcd.clear();
bool SetA1R=true;
while(SetA1R)
{
unsigned long RA1=millis()%1000;
if(RA1<500)
{
lcd.setCursor(0, 0);
PriA1();
}
else
{
lcd.setCursor(7, 0);
lcd.print(" ");
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~ 设置闹钟开关 ~~~~~~~~~~~~~~~~~~~~~~~~~~ */
if(buttonPress(Adj))
{
digitalWrite(Adj,LOW);
if(Clock.checkAlarmEnabled(1))
{
Clock.turnOffAlarm(1);
}
else
{
Clock.turnOnAlarm(1);
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~ 退出闹钟设置 ~~~~~~~~~~~~~~~~~~~~~~~~~~ */
if(buttonPress(Set))
{
lcd.clear();
delay(200);
goto exit;
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
exit:;
}
/* ~~~~~~~~~~~~~~~~~~~~~~ 闹钟响 ~~~~~~~~~~~~~~~~~~~~~ */
void Alarm()
{
if(Clock.checkIfAlarm(1))
{
long Rdt;
do
{
unsigned long Rt=millis()%1000;
if(Rt<500)// 每500ms闪烁一次
{
Pritime();
delay(100);// 延迟100ms
tone(12,2000,50);// 在12号端口输出频率2000Hz,维持50ms
delay(100);
tone(12,2000,50);
delay(100);
tone(12,2000,50);
delay(100);
tone(12,2000,50);
delay(100);
}
else
{
Tictime();
}
Clock.getA1Time(A1Day, A1Hour, A1Minute, A1Second, A1Bits, ADy, A12h, Apm);
Clock.getTime(year, month, date, DoW, hour, minute, second);
if (hour==0)
{
hour=24;
}
Rdt=hour*3600+minute*60+second-A1Hour*3600-A1Minute*60-A1Second;
}
while(Rdt>=0&&Rdt<300);// 闹钟响300s
}
else
{
digitalWrite(12,LOW);
}
}
void loop()
{
unsigned long Rt=millis()%1000;
if(Rt<500)// 每500ms闪烁一次
{
Pritime();
}
else
{
Tictime();
}
SetTime();
Alarm();
}
最后附上 1602液晶屏 和 DS3231时钟模块 的库
如下是当<9时,加0的函数,你可以加上减少代码量
void printDigits(int digits){
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
} 22881916 发表于 2015-5-21 13:41 static/image/common/back.gif
画这种图用的是啥软件啊?
好像是frzitting 画这种图用的是啥软件啊? 22881916 发表于 2015-5-21 13:41 static/image/common/back.gif
画这种图用的是啥软件啊?
Virtual Breadboard singz 发表于 2015-5-21 14:34 static/image/common/back.gif
Virtual Breadboard
多谢了:lol,,,,, 画图画错了,1602怎么可以直接调用i2c库? lzqs 发表于 2015-8-8 15:31 static/image/common/back.gif
画图画错了,1602怎么可以直接调用i2c库?
1602用转接板接I2C总线 直接写入程序,显示相差10秒,和电脑相比慢了10秒。 能否改一下程序,提早10秒写入。OK。 水镜 发表于 2015-9-6 22:33 static/image/common/back.gif
能否改一下程序,提早10秒写入。OK。
时间不是程序写入的,是用按键手动调节的,有两个按键,Set键选择设置项目,Adj键调整数字 水镜 发表于 2015-9-6 22:33 static/image/common/back.gif
能否改一下程序,提早10秒写入。OK。
调节方法和市面上卖的电子表一样 能否读取电脑时间,直接写入,简化时间调整。并减少三个按键。减化电路呢? 水镜 发表于 2015-9-7 21:44 static/image/common/back.gif
能否读取电脑时间,直接写入,简化时间调整。并减少三个按键。减化电路呢?
可以,比如这个:http://www.51hei.com/arduino/3207.html
还有这个:http://blog.csdn.net/wiznet2012/article/details/38274587
但是只能用串口读取预先写好的时间或通过网络板读取服务器时间,
直接读电脑时间需要第三方软件界面,因为Arduino IDE好像不能获取系统时间 楼主大神,我按照你给的i2c库文件,烧测试程序进去没报错,但1602不显示啊:'(,就第一行全黑,第二行无显示,你用的IDE是啥版本,求救:'( 本帖最后由 singz 于 2015-9-16 16:12 编辑
767348927 发表于 2015-9-16 10:09 static/image/common/back.gif
楼主大神,我按照你给的i2c库文件,烧测试程序进去没报错,但1602不显示啊,就第一行全黑,第二行无显示 ...
IDE 是 1.60,应该不是IDE的问题,我用好多版本烧录过,你看看 i2c 总线 有没有接错线,还有测下电压
你的1602 i2c 转接板是否有故障?