8266+DS3231时钟之DS3231具体实现及代码【三】

前一段在做时钟的过程中写了两篇关于DS3231的文章,具体见链接
《8266+DS3231时钟之开发个时钟遇到的N个坑【一】》
《8266+ds3231时钟之arduino官网发布的DS3231库的分析【二】》

有兴趣的可以去看看,如果觉得对你有帮助,请点个赞。
今天有点时间,具体在DS3231库的基础上,把实现过程代码呈现出来。我会把整个钟的制作过程,分析及代码几往篇写出来,是对自已制作的一个总结,也希望能帮助到和我一样对嵌入式系统有兴趣的人。
上一篇对DS3231库的分析里,对每一个函数及作用进行了分析。但在实际写代码的过程中,其实还是有不少地方需要注意的,因此这里根据时钟的使用过程顺序,分析一下具体的实现。

概述

我的时钟的主要功能:
1、时钟的显示(主要显示日期和时分)
2、时钟的自动对时
3、闹钟一的设置与取消
4、闹钟二的设置与取消

1、首先当然是硬件连接

在这里插入图片描述

2、其次当然是软件环境配置

要用DS3231,配置DS3231的相应使用环境 ,首先当然是需要引入相关的头文件,生成对象。具体代码如下:

#include <Wire.h>
#include <DS3231.h>
//时钟管脚定义
#define SCL 5    //GPIO5 对应NODEMCU的d1      I2C-SCL
#define SDA 4    //GPIO4 对应NODEMCU的d2       I2C-SDA   
#define INT_SQW 14   //GPIO14 对应NODEMCU的 D5     中断及方波1HZ
//.......

DS3231 RTC;      //创建DT3231时钟对象

3、闹钟数据结构的设置

/对应APP状态的各类数据结构///
struct AlarmSet_type{
    
        //闹钟设置数据结构
    byte flag=0;
    byte ADay=0;
    byte AHour=0;
    byte AMinute=0;
    byte ASecond=1;//怀疑没有0秒,所以设一个1秒
    byte AlarmBits1=0x08;   //闹钟一控制字的设置,详见我上一篇《arduino官网发布的DS3231库的分析》
    byte AlarmBits2=0x40;  //闹钟二控制字的设置,详见我上一篇《arduino官网发布的DS3231库的分析》
    
    bool ADy=false;//日期以月日格式。
    bool AH12=false; //24小时制
    bool APM=false; //
    
};
AlarmSet_type Alarm1;   //建立闹钟一 
AlarmSet_type Alarm2;  //建立闹钟二  
AlarmSet_type NowAlarm;//暂存现在的闹钟设置

Alarm1 、Alarm2 、NowAlarm 用于接下来设置闹钟或同步APP与闹钟的数据时用。这里需要先定义一下。

4、初始化

void setup(){
    
    
//....................
    Wire.begin();  //启动I2c总线
 时钟初始化   同步闹钟///
   
   //enableOscillator(bool TF, bool battery, byte frequency)
   //当第一位TF为true 控制寄存器的 ~EOSC to 0  and INTCN to 0.,
   //注意该函数把EOSC置0则启动振荡器,同时把把INTCN置0(第二位battery为true VCC<VPF时会输出1HZ方波)
   //使用时要用turnOnAlarm()把INTCN打开,否则会一直输出方波中断
   RTC.enableOscillator(true,false,0);
   RTC.checkIfAlarm(1);  //该函数检测完后状态寄存器A1F或A2F就把标志位清0
   RTC.checkIfAlarm(2);
   RTC.turnOnAlarm(1); //置INTCN为1,否则在INT/SQW会一直输出1HZ方波
   RTC.turnOnAlarm(2); 

 /设置闹钟中断输出设置秒闪功能///
   pinMode(INT_SQW,INPUT_PULLUP); 
   interrupts();
   attachInterrupt(digitalPinToInterrupt(INT_SQW),alarm_interrupt,FALLING);  
   
    //8266经过实践发现,只支持CHANGE RISING FALLING 。DS3231在闹钟启动时,会在int/sqw接口处输出一个低电平做为中断信号。
     secondTicker.attach(1,second_interrupt,SecondInterruptMode);//每秒触发一次带参数的中断程序
    //...................
}

经过以上几个步骤,DS3231就可以正常我们的要求开始工作了。接下来的大量工作就是和具体的显示,设置,以及其它辅助功能相关了。

5、显示

由于我搭的实验环境用的是某宝上买的基于TM1638显示驱动的一个现成模块。下一篇文章我会具体介绍这个模块的使用,这里只需要知道这部分是针对DS3231的具体显示功能就行了。下面先上一张该 模块的图,有个直观的认识:
在这里插入图片描述
由于是实验环境,为了省事,我用的这个8数码管的显示模块,把前四个数码管定义成月日的显示。后四个数码管定义为时分的显示。
因此对应的显示函数如下:

void GetTime(){
    
        //给显示变量赋值
      showtime[0]=RTClib::now().month()/10;     //月 
      showtime[1]=RTClib::now().month()%10;     
      showtime[2]=RTClib::now().day()/10;      //日
      showtime[3]=RTClib::now().day()%10;
      showtime[4]=RTClib::now().hour()/10;      //时
      showtime[5]=RTClib::now().hour()%10;
      showtime[6]=RTClib::now().minute()/10;    //分
      showtime[7]=RTClib::now().minute()%10; 
}

上面这段代码只是把对应的DS3231赋值给showtime[]数组,该 数组用于对应TM1638的显示,由于要显示秒的功能,因此在设计时有一个秒中断函数负责具体的时间显示:


ICACHE_RAM_ATTR void second_interrupt(int mode){
    
     //时钟每秒中断
  if (mode==0){
    
            //模式0 用于秒闪灯工作以及显示时间。模式1可能是显示提示正在配网等

   //显示时间
    if ( showtime[7]!=RTClib::now().minute()%10 ) {
    
       //只要比较,分不同就重新显示

      GetTime();                   //获取DS3231的时间
      TM1638.Disp8(showtime);     //显示
    }  
  }

}

6、对时函数

 void autoAdjust(){
    
        //自动对时
  
     Serial.println("<<<<<<<如果联网了,才执行自动对时功能>>>>>>>>>>");
     if (WiFi.status() == WL_CONNECTED){
    
    
        RTC.setClockMode(false);  // set to 24h
        RTC.setYear((Blinker.year()%100));  //取年的最后两位
        RTC.setMonth(Blinker.month());
        RTC.setDate(Blinker.mday());
        RTC.setDoW(Blinker.wday());
        RTC.setHour(Blinker.hour());
        RTC.setMinute(Blinker.minute());
        RTC.setSecond(Blinker.second());
     }
 
 }

由于我用的是blinker点灯科技的APP,所以自动对时我就不需要去写复杂的NTP授时程序 了,直接从Blinker的对应时间函数里读需出来就行了。因为BLINKER的时间函数了是从NTP相关函数封装来的,所有也是用的互联网授时网站的标准时间。

7、闹钟设置

功能上,我取消了复杂又难以理解的按键设置功能,取而代之的是用APP上的操作来设置闹钟。这些具体实现在以后介绍,这里只需要知道 一下在APP上设置好时分后,把设置下载到DC3231。这此代码是放在对应的BLINKER的回设函数上

void btnOne_callback(const String & state){
    
    
 RTC.setA1Time(Alarm1.ADay,Alarm1.AHour,Alarm1.AMinute,Alarm1.ASecond,Alarm1.AlarmBits1,Alarm1.ADy,Alarm1.AH12,Alarm1.APM);
  RTC.turnOnAlarm(1);
  if (RTC.checkAlarmEnabled(1)){
    
     //检测控制寄存器中的A1IE位置1否
    texOne.print("每天响铃"+String(Alarm1.AHour)+"时"+String(Alarm1.AMinute)+"分");
   
  };    
}

8、闹钟取消

void btnCancel1_callback(const String & state){
    
    
  
  RTC.turnOffAlarm(1); //只设置了控制寄存器中的A1IE位置0,INTCN位不变
   
}

9、闹钟中断触发

ICACHE_RAM_ATTR void alarm_interrupt(){
    
    
 Serial.println("alarm 中断启动。。。。。。。");
 ReadData(EEPROMBase3,Mp3Data);
 if (Mp3Data.flag=1){
    
       //有设置就播设置的音乐,没有的话默认播第一首
   //循环播放
    mp3.SetCmdByte(0x06,CMD_assign_cyclic,0x00,(uint8_t)Mp3Data.music>>8,(uint8_t)(Mp3Data.music&0x00FF));
 }else{
    
    
    mp3.SetCmdByte(0x06,CMD_assign_cyclic,0x00,0x00,0x01); 
 }
  
}

中断程序只要知道是去触发闹铃的语名就行了。

到这里,时钟相关的功能已都基本实现,上面涉及的到功能对应的关键语句都已完整呈现。

猜你喜欢

转载自blog.csdn.net/weixin_45499326/article/details/111462899
今日推荐