/* DS3231.cpp - Arduino/chipKit library support for the DS3231 I2C Real-Time Clock Copyright (C)2015 Rinky-Dink Electronics, Henning Karlsen. All right reserved This library has been made to easily interface and use the DS3231 RTC with an Arduino or chipKit. You can find the latest version of the library at http://www.RinkyDinkElectronics.com/ This library is free software; you can redistribute it and/or modify it under the terms of the CC BY-NC-SA 3.0 license. Please see the included documents for further information. Commercial use of this library requires you to buy a license that will allow commercial use. This includes using the library, modified or not, as a tool to sell products. The license applies to all part of the library including the examples and tools supplied with the library. */ #include "DS3231.h" // Include hardware-specific functions for the correct MCU #if defined(__AVR__) #include "hardware/avr/HW_AVR.h" #elif defined(__PIC32MX__) #include "hardware/pic32/HW_PIC32.h" #elif defined(__arm__) #include "hardware/arm/HW_ARM.h" #endif #define REG_SEC 0x00 #define REG_MIN 0x01 #define REG_HOUR 0x02 #define REG_DOW 0x03 #define REG_DATE 0x04 #define REG_MON 0x05 #define REG_YEAR 0x06 #define REG_ALARM1_SEC 0x07 #define REG_ALARM1_MIN 0x08 #define REG_ALARM1_HOUR 0x09 #define REG_ALARM1_DATE 0x0a #define REG_ALARM2_MIN 0x0b #define REG_ALARM2_HOUR 0x0c #define REG_ALARM2_DATE 0x0d #define REG_CON 0x0e #define REG_STATUS 0x0f #define REG_AGING 0x10 #define REG_TEMPM 0x11 #define REG_TEMPL 0x12 #define SEC_1970_TO_2000 946684800 static const uint8_t dim[] = { 31,28,31,30,31,30,31,31,30,31,30,31 }; /* Public */ Time::Time() { this->year = 2014; this->mon = 1; this->date = 1; this->hour = 0; this->min = 0; this->sec = 0; this->dow = 3; } DS3231::DS3231(uint8_t data_pin, uint8_t sclk_pin) { _sda_pin = data_pin; _scl_pin = sclk_pin; } Time DS3231::getTime() { Time t; _burstRead(); t.sec = _decode(_burstArray[0]); t.min = _decode(_burstArray[1]); t.hour = _decodeH(_burstArray[2]); t.dow = _burstArray[3]; t.date = _decode(_burstArray[4]); t.mon = _decode(_burstArray[5]); t.year = _decodeY(_burstArray[6])+2000; return t; } void DS3231::setTime(uint8_t hour, uint8_t min, uint8_t sec) { if (((hour>=0) && (hour<24)) && ((min>=0) && (min<60)) && ((sec>=0) && (sec<60))) { _writeRegister(REG_HOUR, _encode(hour)); _writeRegister(REG_MIN, _encode(min)); _writeRegister(REG_SEC, _encode(sec)); } } void DS3231::setDate(uint8_t date, uint8_t mon, uint16_t year) { if (((date>0) && (date<=31)) && ((mon>0) && (mon<=12)) && ((year>=2000) && (year<3000))) { year -= 2000; _writeRegister(REG_YEAR, _encode(year)); _writeRegister(REG_MON, _encode(mon)); _writeRegister(REG_DATE, _encode(date)); } } void DS3231::setDOW() { int dow; byte mArr[12] = {6,2,2,5,0,3,5,1,4,6,2,4}; Time _t = getTime(); dow = (_t.year % 100); dow = dow*1.25; dow += _t.date; dow += mArr[_t.mon-1]; if (((_t.year % 4)==0) && (_t.mon<3)) dow -= 1; while (dow>7) dow -= 7; _writeRegister(REG_DOW, dow); } void DS3231::setDOW(uint8_t dow) { if ((dow>0) && (dow<8)) _writeRegister(REG_DOW, dow); } char *DS3231::getTimeStr(uint8_t format) { static char output[] = "xxxxxxxx"; Time t; t=getTime(); if (t.hour<10) output[0]=48; else output[0]=char((t.hour / 10)+48); output[1]=char((t.hour % 10)+48); output[2]=58; if (t.min<10) output[3]=48; else output[3]=char((t.min / 10)+48); output[4]=char((t.min % 10)+48); output[5]=58; if (format==FORMAT_SHORT) output[5]=0; else { if (t.sec<10) output[6]=48; else output[6]=char((t.sec / 10)+48); output[7]=char((t.sec % 10)+48); output[8]=0; } return (char*)&output; } char *DS3231::getDateStr(uint8_t slformat, uint8_t eformat, char divider) { static char output[] = "xxxxxxxxxx"; int yr, offset; Time t; t=getTime(); switch (eformat) { case FORMAT_LITTLEENDIAN: if (t.date<10) output[0]=48; else output[0]=char((t.date / 10)+48); output[1]=char((t.date % 10)+48); output[2]=divider; if (t.mon<10) output[3]=48; else output[3]=char((t.mon / 10)+48); output[4]=char((t.mon % 10)+48); output[5]=divider; if (slformat==FORMAT_SHORT) { yr=t.year-2000; if (yr<10) output[6]=48; else output[6]=char((yr / 10)+48); output[7]=char((yr % 10)+48); output[8]=0; } else { yr=t.year; output[6]=char((yr / 1000)+48); output[7]=char(((yr % 1000) / 100)+48); output[8]=char(((yr % 100) / 10)+48); output[9]=char((yr % 10)+48); output[10]=0; } break; case FORMAT_BIGENDIAN: if (slformat==FORMAT_SHORT) offset=0; else offset=2; if (slformat==FORMAT_SHORT) { yr=t.year-2000; if (yr<10) output[0]=48; else output[0]=char((yr / 10)+48); output[1]=char((yr % 10)+48); output[2]=divider; } else { yr=t.year; output[0]=char((yr / 1000)+48); output[1]=char(((yr % 1000) / 100)+48); output[2]=char(((yr % 100) / 10)+48); output[3]=char((yr % 10)+48); output[4]=divider; } if (t.mon<10) output[3+offset]=48; else output[3+offset]=char((t.mon / 10)+48); output[4+offset]=char((t.mon % 10)+48); output[5+offset]=divider; if (t.date<10) output[6+offset]=48; else output[6+offset]=char((t.date / 10)+48); output[7+offset]=char((t.date % 10)+48); output[8+offset]=0; break; case FORMAT_MIDDLEENDIAN: if (t.mon<10) output[0]=48; else output[0]=char((t.mon / 10)+48); output[1]=char((t.mon % 10)+48); output[2]=divider; if (t.date<10) output[3]=48; else output[3]=char((t.date / 10)+48); output[4]=char((t.date % 10)+48); output[5]=divider; if (slformat==FORMAT_SHORT) { yr=t.year-2000; if (yr<10) output[6]=48; else output[6]=char((yr / 10)+48); output[7]=char((yr % 10)+48); output[8]=0; } else { yr=t.year; output[6]=char((yr / 1000)+48); output[7]=char(((yr % 1000) / 100)+48); output[8]=char(((yr % 100) / 10)+48); output[9]=char((yr % 10)+48); output[10]=0; } break; } return (char*)&output; } char *DS3231::getDOWStr(uint8_t format) { char *output = "xxxxxxxxxx"; char *daysLong[] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}; char *daysShort[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; Time t; t=getTime(); if (format == FORMAT_SHORT) output = daysShort[t.dow-1]; else output = daysLong[t.dow-1]; return output; } char *DS3231::getMonthStr(uint8_t format) { char *output= "xxxxxxxxx"; char *monthLong[] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; char *monthShort[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; Time t; t=getTime(); if (format == FORMAT_SHORT) output = monthShort[t.mon-1]; else output = monthLong[t.mon-1]; return output; } long DS3231::getUnixTime(Time t) { uint16_t dc; dc = t.date; for (uint8_t i = 0; i<(t.mon-1); i++) dc += dim[i]; if ((t.mon > 2) && (((t.year-2000) % 4) == 0)) ++dc; dc = dc + (365 * (t.year-2000)) + (((t.year-2000) + 3) / 4) - 1; return ((((((dc * 24L) + t.hour) * 60) + t.min) * 60) + t.sec) + SEC_1970_TO_2000; } void DS3231::enable32KHz(bool enable) { uint8_t _reg = _readRegister(REG_STATUS); _reg &= ~(1 << 3); _reg |= (enable << 3); _writeRegister(REG_STATUS, _reg); } void DS3231::setOutput(byte enable) { uint8_t _reg = _readRegister(REG_CON); _reg &= ~(1 << 2); _reg |= (enable << 2); _writeRegister(REG_CON, _reg); } void DS3231::setSQWRate(int rate) { uint8_t _reg = _readRegister(REG_CON); _reg &= ~(3 << 3); _reg |= (rate << 3); _writeRegister(REG_CON, _reg); } float DS3231::getTemp() { uint8_t _msb = _readRegister(REG_TEMPM); uint8_t _lsb = _readRegister(REG_TEMPL); return (float)_msb + ((_lsb >> 6) * 0.25f); } /* Private */ void DS3231::_sendStart(byte addr) { pinMode(_sda_pin, OUTPUT); digitalWrite(_sda_pin, HIGH); digitalWrite(_scl_pin, HIGH); digitalWrite(_sda_pin, LOW); digitalWrite(_scl_pin, LOW); shiftOut(_sda_pin, _scl_pin, MSBFIRST, addr); } void DS3231::_sendStop() { pinMode(_sda_pin, OUTPUT); digitalWrite(_sda_pin, LOW); digitalWrite(_scl_pin, HIGH); digitalWrite(_sda_pin, HIGH); pinMode(_sda_pin, INPUT); } void DS3231::_sendNack() { pinMode(_sda_pin, OUTPUT); digitalWrite(_scl_pin, LOW); digitalWrite(_sda_pin, HIGH); digitalWrite(_scl_pin, HIGH); digitalWrite(_scl_pin, LOW); pinMode(_sda_pin, INPUT); } void DS3231::_sendAck() { pinMode(_sda_pin, OUTPUT); digitalWrite(_scl_pin, LOW); digitalWrite(_sda_pin, LOW); digitalWrite(_scl_pin, HIGH); digitalWrite(_scl_pin, LOW); pinMode(_sda_pin, INPUT); } void DS3231::_waitForAck() { pinMode(_sda_pin, INPUT); digitalWrite(_scl_pin, HIGH); while (digitalRead(_sda_pin)==HIGH) {} digitalWrite(_scl_pin, LOW); } uint8_t DS3231::_readByte() { pinMode(_sda_pin, INPUT); uint8_t value = 0; uint8_t currentBit = 0; for (int i = 0; i < 8; ++i) { digitalWrite(_scl_pin, HIGH); currentBit = digitalRead(_sda_pin); value |= (currentBit << 7-i); delayMicroseconds(1); digitalWrite(_scl_pin, LOW); } return value; } void DS3231::_writeByte(uint8_t value) { pinMode(_sda_pin, OUTPUT); shiftOut(_sda_pin, _scl_pin, MSBFIRST, value); } uint8_t DS3231::_decode(uint8_t value) { uint8_t decoded = value & 127; decoded = (decoded & 15) + 10 * ((decoded & (15 << 4)) >> 4); return decoded; } uint8_t DS3231::_decodeH(uint8_t value) { if (value & 128) value = (value & 15) + (12 * ((value & 32) >> 5)); else value = (value & 15) + (10 * ((value & 48) >> 4)); return value; } uint8_t DS3231::_decodeY(uint8_t value) { uint8_t decoded = (value & 15) + 10 * ((value & (15 << 4)) >> 4); return decoded; } uint8_t DS3231::_encode(uint8_t value) { uint8_t encoded = ((value / 10) << 4) + (value % 10); return encoded; } void DS3231::setAlarm1Time(uint8_t hour, uint8_t min) { if (((hour>=0) && (hour<24)) && ((min>=0) && (min<60))) { min = _encode(min); hour = _encode(hour); uint8_t sec = 0x00; min = min &~ (1<<7); hour = hour &~ (1<<7); hour = hour &~ (1<<6); uint8_t date = 0x81; _writeRegister(REG_ALARM1_SEC, sec); _writeRegister(REG_ALARM1_MIN, min); _writeRegister(REG_ALARM1_HOUR, hour); _writeRegister(REG_ALARM1_DATE, date); } } char *DS3231::getAlarm1Str(uint8_t format) { static char output[] = "xx:xx:xx-xxxxxx"; uint8_t sec = _readRegister(REG_ALARM1_SEC); uint8_t min = _readRegister(REG_ALARM1_MIN); uint8_t hour = _readRegister(REG_ALARM1_HOUR); uint8_t date = _readRegister(REG_ALARM1_DATE); // Formatage des bits de controle if((sec&(1<<7)) == 0) output[14] = 48; else output[14] = 49; if((min&(1<<7)) == 0) output[13] = 48; else output[13] = 49; if((hour&(1<<7)) == 0) output[12] = 48; else output[12] = 49; if((hour&(1<<6)) == 0) output[10] = 50; else output[10] = 49; if((date&(1<<7)) == 0) output[11] = 48; else output[11] = 49; if((date&(1<<6)) == 0) output[9] = 77; else output[9] = 87; //Fin formatage des bits de controle sec = _decode(sec); min = _decode(min); hour = _decode(hour); if (hour<10) output[0]=48; // "0" en ASCII else output[0]=char((hour / 10)+48); output[1]=char((hour % 10)+48); if (min<10) output[3]=48; // "0" en ASCII else output[3]=char((min / 10)+48); output[4]=char((min % 10)+48); output[6]=char((sec / 10)+48); output[7]=char((sec % 10)+48); output[16]=0; return (char*)&output; } void DS3231::setControl() { _writeRegister(REG_CON, 0x07); // Serial.print("set de la conf"); } void DS3231::resetAlarm() { _writeRegister(REG_STATUS, 0x00); // Serial.print("reset de l'alarme"); }