功能介绍
DS3231是高精度、低成本的实时时钟(RTC)芯片。该芯片能产生高精度的时钟是由于芯片内部带有温度补偿机制,根据温度信息自动调节振荡器的频率。在-40℃到+85℃的温度范围内,RTC的精度能保持在±0.432S/天。在正常的生活环境中可以实现年误差±20S以内。该芯片有双电源供电引脚,在MCU断电后,可以采用纽扣电池供电,从而保证了MCU断电情况下的继续走时,一颗纽扣电池正常情况下可以支持该芯片工作3-5年。该芯片的数据输出采用的是IIC接口输出,所以在使用时,为了数据传输的可靠,建议采用Arduino的硬件IIC接口连接电路,当然也可以采用软件模拟的IIC接口连接。和DS3231类似的芯片还有很多,其中DS1302和DS1307最为常见,价格也比较便宜,自然产生的实时时钟的精度也比DS3231低很多。
DS3231实时时钟模块采用的就是DS3231芯片来设计的一个实时时钟模块,目前常见的RTC模块上集成了RTC芯片工作需要的纽扣电池,还有集成了AT24Cxx的IIC接口存储芯片。模块供电电压为DC 3.3V-5V。DS3231 RTC模块实物图如图3.17.1所示。
图 3.17.1 DS3231 RTC模块实物图
IIC总线接口介绍
IIC (Inter Integrated-Circuit)总线是PHLIPS公司推出的一种串行总线接口,是具备多主机系统所需的包括总线裁决和高低速器件同步功能的高性能串行总线,它支持多主控,其中任何能够进行发送和接收的设备都可以成为主总线。IIC接口是一种硬件接口,目前常见的MCU都具有硬件IIC接口,不具备硬件IIC接口的MCU也可以通过程序方式模拟出IIC接口的数据通讯,但是通讯速度和稳定性相比硬件IIC会差一些。在很多地方常把IIC写成I2C,I2C总线只有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL。IIC总线示意图如图3.17.2所示。在总线上的每一个设备都有一个唯一的地址,地址为7位,前四位鉴定器件类别,一般是固定的;后三位由器件本身管脚A0、A1、A2可以编程,故同类器件一般最多挂8个。
图3.17.2 IIC总线示意图
接线说明
Arduino | DS3231 RTC模块 | 说明 |
---|---|---|
VCC/5V | VCC/+ | 供电引脚 |
GND/G/- | GND/G/- | 电源地线 |
A4 | SDA | IIC接口数据线 |
A5 | SCL | IIC接口时钟线 |
选用 | 32K | 模块32K信号输出,不使用时无需连接 |
选用 | SQW | 模块多功能引脚,可编程设置,不使用时无需连接 |
该模块数据接口为标准的IIC接口,为了编程的方便及数据传输的稳定性,建议连接到Arduino的硬件IIC接口 |
使用说明:
1.按照接线说明完成线路连接,注意,供电引脚必须连接正确。SDA和SCL引脚是模块的IIC引脚,A4和A5是Arduino的硬件IIC引脚,建议采用硬件IIC接口使用本模块,所以接线时建议按照接线说明中的连接。两个选用引脚根据自身项目的情况自由选择,默认为不使用,悬空就好。
2(a)Arduino IDE编程
采用Arduino IDE方式编程时,因为用到了IIC通信方式,所以需要用到IIC的库文件,在Arduino的库管理器中已经有开源出来的很多DS3231的库文件,我们直接下载使用就好。在IDE中打开“库管理器”,搜索“DS3231”,在搜索得到的结果中安装“DS3231”库,安装时建议采用最新版库,目前最新库版本号为1.0.7。安装完成库文件后在“文件”—>“示例”中找到第三方库部分,选择“DS3231”—>“DS3231_test”打开,打开后就得到了参考程序,关于程序的解释,请参看参考程序中的注释部分。
在安装库文件时没有选择最新版,例如选择了1.0.6版本的库文件,在打开“DS3231_test”示例程序编译时会提示编译出错,如图3.17.3所示。错误提示信息指出,在DS3231.cpp的文件中的第414行没有申明nanf()函数。通过查看源代码得知,此处错误是因为系统没有引用标准C++的库cmath而用了cmath库中的nanf()函数导致。导致错误的代码是实现判断DS3231读取温度时是否有数据返回,没有数据返回就调用了nanf()函数。为了简化使用,我们可以打开DS3231.cpp文件,找到第414行,把错误提示的这一行代码注释,或者不用nanf()函数,直接修改为没有数据返回时让读取温度的返回值等于0,修改完后保存DS3231.cpp文件后再到Arduino IDE中编译“DS3231_test”程序,此时程序可以完成编译。实际在使用时不受影响。在新版本库(1.0.7)中此处修改值为-9999,编译时不再会提示出错。
图3.17.3 DS3231库使用编译错误提示
2(b).Mixly编程
采用Mixly编程时,DS3231程序模块在“传感器”模块库中。在Mixly中选择“传感器”在展开的程序模块中选择RTC相关的程序模块(滚动鼠标滚轮向下寻找),如图3.17.4所示。实际在编程时,只有“初始化时钟模块DS1307管脚#SDA#SCL”模块是必须的,此程序模块需要选择使用的芯片是DS1307还是DS3231,实际使用时按照模块上芯片具体选择,在此我们用的是DS3231。其他程序模块在需要时才使用,最为常用的模块是“RTC获取时间“年””模块,其中的年,可以选择获取的数据具体是什么,在此可以选择年月日,时分秒和周。在Mixly编程中不能获取DS3231内的温度传感器的值。
图3.17.4 Mixly中DS3231程序模块示意图
参考程序:
Arduino IDE参考程序
/*
DS3231_test.pde
Eric Ayars
4/11
Test/demo of read routines for a DS3231 RTC.
Turn on the serial monitor after loading this to check if things are
working as they should.
*/
#include <DS3231.h>
#include <Wire.h>
DS3231 clock;
bool century = false;
bool h12Flag;
bool pmFlag;
byte alarmDay, alarmHour, alarmMinute, alarmSecond, alarmBits;
bool alarmDy, alarmH12Flag, alarmPmFlag;
void setup() {
Wire.begin(); // 启动IIC接口
Serial.begin(57600); // 设置串口波特率
}
void loop() {
Serial.print("2");
if (century) { // 世纪判断,是否过了一个世纪
Serial.print("1");
} else {
Serial.print("0");
}
Serial.print(clock.getYear(), DEC); //获取 年 份数据发送串口
Serial.print('-');
Serial.print(clock.getMonth(century), DEC); //获取 月 份数据发送串口
Serial.print("-");
Serial.print(clock.getDate(), DEC); //获取 天 数据发送串口
Serial.print(" ");
Serial.print("星期:");
Serial.println(clock.getDoW(), DEC); //获取 星期 数据发送串口
// 获取 时分秒 数据发送串口
Serial.print(clock.getHour(h12Flag, pmFlag), DEC);
Serial.print(":");
Serial.print(clock.getMinute(), DEC);
Serial.print(":");
Serial.print(clock.getSecond(), DEC);
if (h12Flag) { // 判断上午、下午
if (pmFlag) {
Serial.print(" PM ");
} else {
Serial.print(" AM ");
}
} else {
Serial.println(" 24h ");
}
// 获取温度数据发送串口
Serial.print("T=");
Serial.print(clock.getTemperature(), 2);
// 判断数据是否有效
if (clock.oscillatorCheck()) {
Serial.print(" O+");
} else {
Serial.print(" O-");
}
// 是否显示闹铃
if (clock.checkIfAlarm(1)) {
Serial.print(" A1!");
}
if (clock.checkIfAlarm(2)) {
Serial.print(" A2!");
}
Serial.println();
// 显示A1闹铃信息
Serial.print("Alarm 1: ");
clock.getA1Time(alarmDay, alarmHour, alarmMinute, alarmSecond, alarmBits, alarmDy, alarmH12Flag, alarmPmFlag);
Serial.print(alarmDay, DEC);
if (alarmDy) {
Serial.print(" DoW");
} else {
Serial.print(" Date");
}
Serial.print(' ');
Serial.print(alarmHour, DEC);
Serial.print(' ');
Serial.print(alarmMinute, DEC);
Serial.print(' ');
Serial.print(alarmSecond, DEC);
Serial.print(' ');
if (alarmH12Flag) {
if (alarmPmFlag) {
Serial.print("pm ");
} else {
Serial.print("am ");
}
}
if (clock.checkAlarmEnabled(1)) {
Serial.print("enabled");
}
Serial.println();
// 显示A2闹铃信息
Serial.print("Alarm 2: ");
clock.getA2Time(alarmDay, alarmHour, alarmMinute, alarmBits, alarmDy, alarmH12Flag, alarmPmFlag);
Serial.print(alarmDay, DEC);
if (alarmDy) {
Serial.print(" DoW");
} else {
Serial.print(" Date");
}
Serial.print(" ");
Serial.print(alarmHour, DEC);
Serial.print(" ");
Serial.print(alarmMinute, DEC);
Serial.print(" ");
if (alarmH12Flag) {
if (alarmPmFlag) {
Serial.print("pm");
} else {
Serial.print("am");
}
}
if (clock.checkAlarmEnabled(2)) {
Serial.print("enabled");
}
//显示报警位信息
Serial.println();
Serial.print("Alarm bits: ");
Serial.println(alarmBits, BIN);
Serial.println();
delay(1000);
}