尕老汉 发表于 2014-5-20 13:10:57

发个DS3231的简易时钟,1602液晶显示、串口修改时间

本帖最后由 尕老汉 于 2014-5-20 13:15 编辑

   来极客工坊学习一个多月了,收获很大。以前学过单片机,加上论坛里的各位前辈无私的奉献,现在学arduino很快上手,十分感谢极客工坊这个大家庭。
    最近买了一块DS3231时钟模块,测试了3天,发现走时非常准确,一秒不差。 以前学单片机时用ds1302做过一个简易时钟,一天的误差在5秒左右,需要经常去校时。来上个照片







      在论坛里搜索了一下关于DS3231的应用,发现不是很多,就打算将原来ds1302的程序移植到arduino上,但是将时钟芯片换成DS3231。因为DS3231走时比较准确,不需要经常校时,打算程序里去除用按键修改时间的程序部分。改为用串口发送字符串来修改时间。另外,我个人认为桌面时钟就是看时间用的,加上闹钟的功能并不实用。所以程序里也没有闹钟的功能。

简单介绍一下DS3231:
1、I2C接口;
2、实时时钟产生秒、分、时、星期、日期、月和年计时,并提供有效期到2100年的闰年补偿;
3、两个日历闹钟;
4、内部集成了一个数字温度传感器(64秒更新一次)
5、可编程方波输出

****************************************      硬件连接    ********************************************

1602液晶连接、程序可参考 http://www.geek-workshop.com/thread-78-1-1.html
采用4线接法,顺便盗个图



UNO与1602液晶连接
6      ->   D7
7      ->   D6
8      ->   D5
9      ->   D4
10   ->    EN
11   ->    RW
12   ->    RS


DS3231连接、程序可参考 http://www.geek-workshop.com/thread-7564-1-1.html
顺便盗个图



UNO与DS3231连接
A4   ->   SDA
A5   ->   SCL

****************************************      程序部分    ********************************************


#include <DS3231.h>
#include <Wire.h>

DS3231 Clock;
bool Century=false;
bool h12;
bool PM;
byte ADay, AHour, AMinute, ASecond, ABits;
bool ADy, A12h, Apm;

int year, month, date, DoW,week , hour, minute, second,temperature;

String comdata = "";
int numdata = {0},mark = 0;
/*1602液晶与UNO连接引脚
6 -> D7
7 -> D6
8 -> D5
9 -> D4
*/

int LCD1602_RS=12;   
int LCD1602_RW=11;   
int LCD1602_EN=10;   
int DB[] = { 6, 7, 8, 9};

char dis1={0},dis2={0};

char self_char[]={                               //1602液晶自定义符号   
        0x08,0x0f,0x12,0x0f,0x0a,0x1f,0x02,0x02, //年
        0x0f,0x09,0x0f,0x09,0x0f,0x09,0x13,0x01, //月
        0x0f,0x09,0x09,0x0f,0x09,0x09,0x0f,0x00, //日
        0x18,0x18,0x07,0x08,0x08,0x08,0x07,0x00, //温度标志— —摄氏度
        0x00,0x04,0x0E,0x1F,0x0E,0x04,0x00,0x00, //符号◆
        0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, //全开
        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //
        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00       //
                                           };

void LCD_Command_Write(int command)             //写指令
{
int i,temp;
digitalWrite( LCD1602_RS,LOW);
digitalWrite( LCD1602_RW,LOW);
digitalWrite( LCD1602_EN,LOW);

temp=command & 0xf0;
for (i=DB; i <= 9; i++)
{
   digitalWrite(i,temp & 0x80);
   temp <<= 1;
}

digitalWrite( LCD1602_EN,HIGH);
delayMicroseconds(1);
digitalWrite( LCD1602_EN,LOW);

temp=(command & 0x0f)<<4;
for (i=DB; i <= 9; i++)
{
   digitalWrite(i,temp & 0x80);
   temp <<= 1;
}

digitalWrite( LCD1602_EN,HIGH);
delayMicroseconds(1);
digitalWrite( LCD1602_EN,LOW);
}

void LCD_Data_Write(int dat)                //写数据
{
int i=0,temp;
digitalWrite( LCD1602_RS,HIGH);
digitalWrite( LCD1602_RW,LOW);
digitalWrite( LCD1602_EN,LOW);

temp=dat & 0xf0;
for (i=DB; i <= 9; i++)
{
   digitalWrite(i,temp & 0x80);
   temp <<= 1;
}

digitalWrite( LCD1602_EN,HIGH);
delayMicroseconds(1);
digitalWrite( LCD1602_EN,LOW);

temp=(dat & 0x0f)<<4;
for (i=DB; i <= 9; i++)
{
   digitalWrite(i,temp & 0x80);
   temp <<= 1;
}

digitalWrite( LCD1602_EN,HIGH);
delayMicroseconds(1);
digitalWrite( LCD1602_EN,LOW);
}

void LCD_SET_XY( int x, int y )//指定坐标,x为列,0~15,y为行,0为第一行,1为第二行。
{
int address;
if (y ==0)    address = 0x80 + x;
else          address = 0xC0 + x;
LCD_Command_Write(address);
}

void LCD_Write_Char( int x,int y,int dat)//指定位置一个字符
{
LCD_SET_XY( x, y );
LCD_Data_Write(dat);
}

void LCD_Write_String(int X,int Y,char *s)//指定位置字符串
{
    LCD_SET_XY( X, Y );    //设置地址
    while (*s)             //写字符串
    {
      LCD_Data_Write(*s);   
      s ++;
    }
}

void setup (void)
{
Serial.begin(9600);
int i = 0;
for (i=6; i <= 12; i++)
   {
   pinMode(i,OUTPUT);
   }

delay(100);
LCD_Command_Write(0x28);//4线 2行 5x7
delay(50);
LCD_Command_Write(0x06);
delay(50);
LCD_Command_Write(0x0c);
delay(50);
LCD_Command_Write(0x80);
delay(50);
LCD_Command_Write(0x01);
delay(50);

    diy();            //开机问候
//Wire.begin();
Serial.println("set_time :");
Serial.println("year mouth day week hour minute second");
Serial.println();
Serial.println("week : 1 -> Sunday; 2 -> Monday; 3 -> Tuesday:....7 -> Saturday");
Serial.println();
Serial.println("for example :2014-5-20 Tue 0:33:30");
Serial.println("set_time :");
Serial.println("14 5 20 3 0 33 30");
Serial.println();
}
       

void diy()
{
   char a;
LCD_Command_Write(0x40);   
   for(a=0;a<64;a++)
        {
          LCD_Data_Write(self_char);
          delay(1);
        }
    delay(50);
LCD_Write_String(0,0," Digital Clock");
LCD_Write_String(0,1," Geek-workshop");
delay(2000);
LCD_Command_Write(0x01);
delay(10);
LCD_Write_String(0,0,"Today is new day");
LCD_Write_String(0,1,"Dream come true!");
delay(2000);
LCD_Command_Write(0x01);
delay(10);           
}

void ReadDS3231()          //读取DS3231 参数
{
Wire.begin();

second=Clock.getSecond();
minute=Clock.getMinute();
hour=Clock.getHour(h12,PM);
week=Clock.getDoW();   
date=Clock.getDate();
month=Clock.getMonth(Century);
year=Clock.getYear();
temperature=Clock.getTemperature();

}

void get_dis()          //1602液晶上每一位上显示的数据
{
ReadDS3231();
dis1='2';
dis1='0';
dis1=0x30+year/10;
dis1=0x30+year%10;
dis1=0;
dis1=0x30+month/10;
dis1=0x30+month%10;
dis1=1;
dis1=0x30+date/10;
dis1=0x30+date%10;
dis1=2;
dis1=' ';
dis1=' ';
switch(week)
{
        case 2: {
                          dis1='M';
                          dis1='o';
                          dis1='n';
                        }
                        break;
        case 3: {
                          dis1='T';
                          dis1='u';
                          dis1='e';
                        }
                        break;
        case 4: {
                          dis1='W';
                          dis1='e';
                          dis1='d';
                        }
                        break;
        case 5: {
                          dis1='T';
                          dis1='h';
                          dis1='u';
                        }
                        break;
        case 6: {
                          dis1='F';
                          dis1='r';
                          dis1='i';
                        }
                        break;
        case 7: {
                          dis1='S';
                          dis1='a';
                          dis1='t';
                        }
                        break;
        case 1: {
                          dis1='S';
                          dis1='u';
                          dis1='n';
                        }
                        break;
}
dis2=' ';
dis2=0x30+hour/10;
dis2=0x30+hour%10;
dis2=':';
dis2=0x30+minute/10;
dis2=0x30+minute%10;
dis2=':';
dis2=0x30+second/10;
dis2=0x30+second%10;
dis2=' ';

        dis2=' ';
dis2=0x30+temperature/10;
dis2=0x30+temperature%10;
dis2='.';
dis2=0x30+0;
dis2=3;
}
void play()                  //1602显示完整时间
{
       get_dis();

            int k;      
            LCD_SET_XY( 0,0);
            for(k=0;k<16;k++)
            LCD_Data_Write(dis1);
            LCD_SET_XY( 0,1);
            for(k=0;k<16;k++)
            LCD_Data_Write(dis2);

}

void set_time()               //DS3231设置时间
{
Wire.begin();
Clock.setSecond(numdata); //秒
Clock.setMinute(numdata); //分
Clock.setHour(numdata);   //时
Clock.setDoW(numdata);    //周
Clock.setDate(numdata);   //日
Clock.setMonth(numdata);//月
Clock.setYear(numdata);   //年
}

void loop (void)
{
   int j = 0;
while (Serial.available() > 0)    //检测串口是否有数据
{
    comdata += char(Serial.read());
    delay(2);
    mark = 1;
   play();
}

if(mark == 1)
{
    Serial.println(comdata);             //串口打印检测到的数据
    for(int i = 0; i < comdata.length() ; i++)
    {
      if(comdata == ' ')
      {
      j++;
      }
      else
      {
      numdata = numdata * 10 + (comdata - '0');
      }
    }

    comdata = String("");
   Serial.print("set_time... ");
    set_time();
    Serial.println(" OK ");
    for(int i = 0; i < 7; i++)
    {
      numdata = 0;
    }
    mark = 0;
}
    play();
}


其中串口字符串转换部分参考 http://www.geek-workshop.com/thread-260-1-1.html

****************************************      串口校时    ********************************************

打开arduino 串口监视器



输入要设定日期以及时间
格式为(年+空格+月+空格+日+空格+星期+空格+时+空格+分+空格+秒)

设置中星期:01对应星期天;02对应星期一;03对应星期二;……07对应星期六

例如:现在是 2014年5月20日 星期二 13:35:00
那么在串口数据发送栏中输入   : 14 05 20 03 13 35 00



********************************       工作照(请无视凌乱的杜邦线)   ********************************

下面的单片机开发板只是为了给1602液晶引线用,方便接线而已







工作中。。。。。



1602液晶显示的年月日是不是很有感觉!

喜欢么?喜欢就给赞一个吧!

独占你的情 发表于 2014-5-20 16:35:00

   老霍是吧!

独占你的情 发表于 2014-5-20 16:36:04

:lol:lol:lol:lol:lol

jikecyatmcn 发表于 2014-5-20 21:34:51

{:soso_e179:}厉害 请问play()这个是神马意思啊?

646097010 发表于 2014-5-21 08:51:25

jikecyatmcn 发表于 2014-5-20 21:34 static/image/common/back.gif
厉害 请问play()这个是神马意思啊?

play()就是将 get_dis()函数换算好的dis1[]、dis2[]数据按2行、每行16个的格式显示在1602液晶上。

huangshan78 发表于 2014-5-21 22:40:06

如何用GPS校时??

尕老汉 发表于 2014-5-22 15:57:51

huangshan78 发表于 2014-5-21 22:40 static/image/common/back.gif
如何用GPS校时??

上面没有提到GPS校时,论坛里有人发过用GPS校时的时钟,你搜一下就知道了

vp110 发表于 2014-5-28 14:31:42

找个时间 买块ds3231回来 玩玩

dinghz16 发表于 2014-8-27 15:43:44

装好了,试着不错。就是一开机显示乱码,要按reset重启才能正常显示

dinghz16 发表于 2014-8-27 15:44:28

温度从哪检测的?

wxws 发表于 2014-8-27 15:54:48

3231内置温度传感器

iambic 发表于 2014-9-30 01:04:58

请问实物图上竖起来的三极管起什么作用?

尕老汉 发表于 2014-9-30 21:11:28

iambic 发表于 2014-9-30 01:04 static/image/common/back.gif
请问实物图上竖起来的三极管起什么作用?

温度传感器,lm35,18b20都可以

iambic 发表于 2014-9-30 22:19:14

尕老汉 发表于 2014-9-30 21:11 static/image/common/back.gif
温度传感器,lm35,18b20都可以

温度传感器不是内置的吗? 为什么还有一个外置温度传感器? 是更精确吗?

这个外置的是如何接线?

尕老汉 发表于 2014-10-2 20:08:03

iambic 发表于 2014-9-30 22:19 static/image/common/back.gif
温度传感器不是内置的吗? 为什么还有一个外置温度传感器? 是更精确吗?

这个外置的是如何接线?

ds3231是内置温度传感器,但是64秒更新一次,不知道是这芯片本来就是这样设计,还是库代码原因(不会修改库代码)。论坛里有lm35,18b20教程你可以去参考下
页: [1] 2 3 4 5 6
查看完整版本: 发个DS3231的简易时钟,1602液晶显示、串口修改时间