Fork of DS3231 by
Diff: DS3231.cpp
- Revision:
- 0:e4fbbd93299b
- Child:
- 1:1607610a4ee9
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DS3231.cpp Tue Aug 05 16:43:01 2014 +0000 @@ -0,0 +1,653 @@ +/***************************************************************************** + * Type : C++ + * File : DS3231.cpp + * Dec. : DS3231 + AT24C32 IIC Module Precision RTC Module Memory Module + * Copyright (c) 2013-2014, Bird Techstep, tbird_th@hotmail.com + * + * Remark Original codr from DS3231 Library [Arduino] + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + *****************************************************************************/ +#include "DS3231.h" +#define CLOCK_ADDRESS 0x68 +#define ADDR char(CLOCK_ADDRESS<<1) + +// Constructor +DS3231::DS3231(PinName sda_pin, PinName scl_pin) : _i2c(sda_pin, scl_pin) { + _i2c.frequency(100000); +} +/***************************************** + Public Functions + *****************************************/ + +void DS3231::getTime(uint8_t& year, uint8_t& month, uint8_t& date, uint8_t& DoW, uint8_t& hour, uint8_t& minute, uint8_t& second) { + uint8_t tempBuffer; + bool PM; + bool h12; + + //I2C.beginTransmission(CLOCK_ADDRESS); + //I2C.write(uint8_t(0x00)); + //I2C.endTransmission(); + _i2c.start(); + _i2c.write(uint8_t(CLOCK_ADDRESS)); + _i2c.write(uint8_t(0x00)); + _i2c.stop(); + + //I2C.requestFrom(CLOCK_ADDRESS, 7); + second = bcdToDec(_i2c.read(uint8_t(CLOCK_ADDRESS))); + minute = bcdToDec(_i2c.read(uint8_t(CLOCK_ADDRESS))); + tempBuffer = bcdToDec(_i2c.read(uint8_t(CLOCK_ADDRESS))); + + h12 = tempBuffer & 0x40; + if (h12) { + PM = tempBuffer & 0x20; + hour = bcdToDec(tempBuffer & 0x1F); + } else { + hour = bcdToDec(tempBuffer & 0x3F); + } + DoW = bcdToDec(_i2c.read(uint8_t(CLOCK_ADDRESS))); + date = bcdToDec(_i2c.read(uint8_t(CLOCK_ADDRESS))); + month = bcdToDec(_i2c.read(uint8_t(CLOCK_ADDRESS)) & 0x7F); + year = bcdToDec(_i2c.read(uint8_t(CLOCK_ADDRESS))); +} + +int8_t DS3231::getSecond(void) { + char data; + char cmd; + cmd = 0x00; + _i2c.write(ADDR, &cmd, 1 ); + _i2c.read( ADDR, &data, 1 ); + return(bcdToDec(data)); +} + +uint8_t DS3231::getMinute(void) { + char data; + char cmd; + cmd = 0x01; + _i2c.write(ADDR, &cmd, 1 ); + _i2c.read( ADDR, &data, 1 ); + return(bcdToDec(data)); +} + +uint8_t DS3231::getHour(bool& h12, bool& PM) { + char temp_buffer; + char hour; + char cmd; + cmd = 0x02; + _i2c.write(ADDR, &cmd, 1 ); + _i2c.read( ADDR, &temp_buffer, 1 ); + + h12 = temp_buffer & 0x40; + if (h12) { + PM = temp_buffer & 0x20; + hour = bcdToDec(temp_buffer & 0x1F); + } else { + hour = bcdToDec(temp_buffer & 0x3F); + } + return hour; +} + +uint8_t DS3231::getDoW(void) { + char data; + char cmd; + cmd = 0x03; + _i2c.write(ADDR, &cmd, 1 ); + _i2c.read( ADDR, &data, 1 ); + return(bcdToDec(data)); +} + +uint8_t DS3231::getDate(void) { + char data; + char cmd; + cmd = 0x04; + _i2c.write(ADDR, &cmd, 1 ); + _i2c.read( ADDR, &data, 1 ); + return(bcdToDec(data)); +} + +uint8_t DS3231::getMonth(bool& Century) { + char temp_buffer; + char cmd; + cmd = 0x05; + _i2c.write(ADDR, &cmd, 1 ); + _i2c.read( ADDR, &temp_buffer, 1 ); + + Century = temp_buffer & 0x80; + return (bcdToDec(temp_buffer & 0x7F)) ; +} + +uint8_t DS3231::getYear(void) { + char data; + char cmd; + cmd = 0x06; + _i2c.write(ADDR, &cmd, 1 ); + _i2c.read( ADDR, &data, 1 ); + return(bcdToDec(data)); +} + +void DS3231::setSecond(uint8_t Second) { + // Sets the seconds + // This function also resets the Oscillator Stop Flag, which is set + // whenever power is interrupted. + char cmd[2]; + cmd[0] = 0x00; + cmd[1] = decToBcd(Second); + _i2c.write(ADDR, cmd, 2); + + // Clear OSF flag + uint8_t temp_buffer = readControlByte(1); + writeControlByte((temp_buffer & 0x7F), 1); +} + +void DS3231::setMinute(uint8_t Minute) { + // Sets the minutes + char cmd[2]; + cmd[0] = 0x01; + cmd[1] = decToBcd(Minute); + _i2c.write(ADDR, cmd, 2); +} + +void DS3231::setHour(uint8_t Hour) { + // Sets the hour, without changing 12/24h mode. + // The hour must be in 24h format. + + bool h12; + + // Start by figuring out what the 12/24 mode is + char data; + char cmd; + cmd = 0x02; + _i2c.write(ADDR, &cmd, 1 ); + _i2c.read( ADDR, &data, 1 ); + + h12 = (data & 0x40); + // if h12 is true, it's 12h mode; false is 24h. + + if (h12) { + // 12 hour + if (Hour > 12) { + Hour = decToBcd(Hour-12) | 0x60; + } else { + Hour = decToBcd(Hour) & 0xDF; + } + } else { + // 24 hour + Hour = decToBcd(Hour) & 0xBF; + } + char cmdHour[2]; + cmdHour[0] = 0x02; + cmdHour[1] = Hour; + _i2c.write(ADDR, cmdHour, 2); +} + +void DS3231::setDoW(uint8_t DoW) { + // Sets the Day of Week + char cmd[2]; + cmd[0] = 0x03; + cmd[1] = decToBcd(DoW); + _i2c.write(ADDR, cmd, 2 ); +} + +void DS3231::setDate(uint8_t Date) { + // Sets the Date + char cmd[2]; + cmd[0] = 0x04; + cmd[1] = decToBcd(Date); + _i2c.write(ADDR, cmd, 2 ); +} + +void DS3231::setMonth(uint8_t Month) { + // Sets the month + char cmd[2]; + cmd[0] = 0x05; + cmd[1] = decToBcd(Month); + _i2c.write(ADDR, cmd, 2 ); +} + +void DS3231::setYear(uint8_t Year) { + // Sets the year + char cmd[2]; + cmd[0] = 0x06; + cmd[1] = decToBcd(Year); + _i2c.write(ADDR, cmd, 2 ); +} + +void DS3231::setClockMode(bool h12) { + // sets the mode to 12-hour (true) or 24-hour (false). + // One thing that bothers me about how I've written this is that + // if the read and right happen at the right hourly millisecnd, + // the clock will be set back an hour. Not sure how to do it better, + // though, and as long as one doesn't set the mode frequently it's + // a very minimal risk. + // It's zero risk if you call this BEFORE setting the hour, since + // the setHour() function doesn't change this mode. + + char temp_buffer; + char cmd; + cmd = 0x02; + + // Start by reading byte 0x02. + //I2C.beginTransmission(CLOCK_ADDRESS); + //I2C.write(uint8_t(0x02)); + //I2C.endTransmission(); + _i2c.write(ADDR, &cmd, 1 ); + _i2c.read( ADDR, &temp_buffer, 1 ); + + // Set the flag to the requested value: + if (h12) { + temp_buffer = temp_buffer | 0x40; + } else { + temp_buffer = temp_buffer & 0xBF; + } + + // Write the byte + _i2c.write(ADDR, &cmd, 1 ); + _i2c.write(temp_buffer); +} + +float DS3231::getTemperature(void) { + // Checks the internal thermometer on the DS3231 and returns the + // temperature as a floating-point value. + char tempMSB; + char tempLSB; + char cmd; + cmd = 0x11; + _i2c.write(ADDR, &cmd, 1 ); + _i2c.read( ADDR, &tempMSB, 1); // Here's the MSB + _i2c.read( ADDR, &tempLSB, 1); // Here's the LSB + return float(tempMSB) + 0.25*(tempLSB>>6); +} + +void DS3231::getA1Time(uint8_t& A1Day, uint8_t& A1Hour, uint8_t& A1Minute, uint8_t& A1Second, uint8_t& AlarmBits, bool& A1Dy, bool& A1h12, bool& A1PM) { + uint8_t temp_buffer; + //I2C.beginTransmission(CLOCK_ADDRESS); + //I2C.write(uint8_t(0x07)); + //I2C.endTransmission(); + _i2c.start(); + _i2c.write(uint8_t(CLOCK_ADDRESS)); + _i2c.write(uint8_t(0x07)); + _i2c.stop(); + + //I2C.requestFrom(CLOCK_ADDRESS, 4); + + temp_buffer = _i2c.read(uint8_t(CLOCK_ADDRESS)); // Get A1M1 and A1 Seconds + A1Second = bcdToDec(temp_buffer & 0x7F); + // put A1M1 bit in position 0 of DS3231_AlarmBits. + AlarmBits = AlarmBits | (temp_buffer & 0x80)>>7; + + temp_buffer = _i2c.read(uint8_t(CLOCK_ADDRESS)); // Get A1M2 and A1 minutes + A1Minute = bcdToDec(temp_buffer & 0x7F); + // put A1M2 bit in position 1 of DS3231_AlarmBits. + AlarmBits = AlarmBits | (temp_buffer & 0x80)>>6; + + temp_buffer = _i2c.read(uint8_t(CLOCK_ADDRESS)); // Get A1M3 and A1 Hour + // put A1M3 bit in position 2 of DS3231_AlarmBits. + AlarmBits = AlarmBits | (temp_buffer & 0x80)>>5; + // determine A1 12/24 mode + A1h12 = temp_buffer & 0x40; + if (A1h12) { + A1PM = temp_buffer & 0x20; // determine am/pm + A1Hour = bcdToDec(temp_buffer & 0x1F); // 12-hour + } else { + A1Hour = bcdToDec(temp_buffer & 0x3F); // 24-hour + } + + temp_buffer = _i2c.read(uint8_t(CLOCK_ADDRESS)); // Get A1M4 and A1 Day/Date + // put A1M3 bit in position 3 of DS3231_AlarmBits. + AlarmBits = AlarmBits | (temp_buffer & 0x80)>>4; + // determine A1 day or date flag + A1Dy = (temp_buffer & 0x40)>>6; + if (A1Dy) { + // alarm is by day of week, not date. + A1Day = bcdToDec(temp_buffer & 0x0F); + } else { + // alarm is by date, not day of week. + A1Day = bcdToDec(temp_buffer & 0x3F); + } +} + +void DS3231::getA2Time(uint8_t& A2Day, uint8_t& A2Hour, uint8_t& A2Minute, uint8_t& AlarmBits, bool& A2Dy, bool& A2h12, bool& A2PM) { + uint8_t temp_buffer; + //I2C.beginTransmission(CLOCK_ADDRESS); + //I2C.write(uint8_t(0x0b)); + //I2C.endTransmission(); + _i2c.start(); + _i2c.write(uint8_t(CLOCK_ADDRESS)); + _i2c.write(uint8_t(0x0b)); + _i2c.stop(); + + //I2C.requestFrom(CLOCK_ADDRESS, 3); + temp_buffer = _i2c.read(uint8_t(CLOCK_ADDRESS)); // Get A2M2 and A2 Minutes + A2Minute = bcdToDec(temp_buffer & 0x7F); + // put A2M2 bit in position 4 of DS3231_AlarmBits. + AlarmBits = AlarmBits | (temp_buffer & 0x80)>>3; + + temp_buffer = _i2c.read(uint8_t(CLOCK_ADDRESS)); // Get A2M3 and A2 Hour + // put A2M3 bit in position 5 of DS3231_AlarmBits. + AlarmBits = AlarmBits | (temp_buffer & 0x80)>>2; + // determine A2 12/24 mode + A2h12 = temp_buffer & 0x40; + if (A2h12) { + A2PM = temp_buffer & 0x20; // determine am/pm + A2Hour = bcdToDec(temp_buffer & 0x1F); // 12-hour + } else { + A2Hour = bcdToDec(temp_buffer & 0x3F); // 24-hour + } + + temp_buffer = _i2c.read(uint8_t(CLOCK_ADDRESS)); // Get A2M4 and A1 Day/Date + // put A2M4 bit in position 6 of DS3231_AlarmBits. + AlarmBits = AlarmBits | (temp_buffer & 0x80)>>1; + // determine A2 day or date flag + A2Dy = (temp_buffer & 0x40)>>6; + if (A2Dy) { + // alarm is by day of week, not date. + A2Day = bcdToDec(temp_buffer & 0x0F); + } else { + // alarm is by date, not day of week. + A2Day = bcdToDec(temp_buffer & 0x3F); + } +} + +void DS3231::setA1Time(uint8_t A1Day, uint8_t A1Hour, uint8_t A1Minute, uint8_t A1Second, uint8_t AlarmBits, bool A1Dy, bool A1h12, bool A1PM) { + // Sets the alarm-1 date and time on the DS3231, using A1* information + uint8_t temp_buffer; + //I2C.beginTransmission(CLOCK_ADDRESS); + //I2C.write(uint8_t(0x07)); // A1 starts at 07h + _i2c.start(); + _i2c.write(uint8_t(CLOCK_ADDRESS)); + _i2c.write(uint8_t(0x07)); + + // Send A1 second and A1M1 + //I2C.write(decToBcd(A1Second) | ((AlarmBits & 0x01) << 7)); + _i2c.write(decToBcd(A1Second) | ((AlarmBits & 0x01) << 7)); + + // Send A1 Minute and A1M2 + //I2C.write(decToBcd(A1Minute) | ((AlarmBits & 0x02) << 6)); + _i2c.write(decToBcd(A1Minute) | ((AlarmBits & 0x02) << 6)); + + // Figure out A1 hour + if (A1h12) { + // Start by converting existing time to h12 if it was given in 24h. + if (A1Hour > 12) { + // well, then, this obviously isn't a h12 time, is it? + A1Hour = A1Hour - 12; + A1PM = true; + } + if (A1PM) { + // Afternoon + // Convert the hour to BCD and add appropriate flags. + temp_buffer = decToBcd(A1Hour) | 0x60; + } else { + // Morning + // Convert the hour to BCD and add appropriate flags. + temp_buffer = decToBcd(A1Hour) | 0x40; + } + } else { + // Now for 24h + temp_buffer = decToBcd(A1Hour); + } + temp_buffer = temp_buffer | ((AlarmBits & 0x04)<<5); + // A1 hour is figured out, send it + //I2C.write(temp_buffer); + _i2c.write(temp_buffer); + + // Figure out A1 day/date and A1M4 + temp_buffer = ((AlarmBits & 0x08)<<4) | decToBcd(A1Day); + if (A1Dy) { + // Set A1 Day/Date flag (Otherwise it's zero) + temp_buffer = temp_buffer | 0x40; + } + //I2C.write(temp_buffer); + _i2c.write(temp_buffer); + + // All done! + //I2C.endTransmission(); + _i2c.stop(); +} + +void DS3231::setA2Time(uint8_t A2Day, uint8_t A2Hour, uint8_t A2Minute, uint8_t AlarmBits, bool A2Dy, bool A2h12, bool A2PM) { + // Sets the alarm-2 date and time on the DS3231, using A2* information + uint8_t temp_buffer; + //I2C.beginTransmission(CLOCK_ADDRESS); + //I2C.write(uint8_t(0x0b)); // A1 starts at 0bh + _i2c.start(); + _i2c.write(uint8_t(CLOCK_ADDRESS)); + _i2c.write(uint8_t(0x0b)); + + // Send A2 Minute and A2M2 + //I2C.write(decToBcd(A2Minute) | ((AlarmBits & 0x10) << 3)); + _i2c.write(decToBcd(A2Minute) | ((AlarmBits & 0x10) << 3)); + + // Figure out A2 hour + if (A2h12) { + // Start by converting existing time to h12 if it was given in 24h. + if (A2Hour > 12) { + // well, then, this obviously isn't a h12 time, is it? + A2Hour = A2Hour - 12; + A2PM = true; + } + if (A2PM) { + // Afternoon + // Convert the hour to BCD and add appropriate flags. + temp_buffer = decToBcd(A2Hour) | 0x60; + } else { + // Morning + // Convert the hour to BCD and add appropriate flags. + temp_buffer = decToBcd(A2Hour) | 0x40; + } + } else { + // Now for 24h + temp_buffer = decToBcd(A2Hour); + } + // add in A2M3 bit + temp_buffer = temp_buffer | ((AlarmBits & 0x20)<<2); + // A2 hour is figured out, send it + //I2C.write(temp_buffer); + _i2c.write(temp_buffer); + + // Figure out A2 day/date and A2M4 + temp_buffer = ((AlarmBits & 0x40)<<1) | decToBcd(A2Day); + if (A2Dy) { + // Set A2 Day/Date flag (Otherwise it's zero) + temp_buffer = temp_buffer | 0x40; + } + //I2C.write(temp_buffer); + _i2c.write(temp_buffer); + + // All done! + //I2C.endTransmission(); + _i2c.stop(); +} + +void DS3231::turnOnAlarm(uint8_t Alarm) { + // turns on alarm number "Alarm". Defaults to 2 if Alarm is not 1. + uint8_t temp_buffer = readControlByte(0); + // modify control byte + if (Alarm == 1) { + temp_buffer = temp_buffer | 0x05; + } else { + temp_buffer = temp_buffer | 0x06; + } + writeControlByte(temp_buffer, 0); +} + +void DS3231::turnOffAlarm(uint8_t Alarm) { + // turns off alarm number "Alarm". Defaults to 2 if Alarm is not 1. + // Leaves interrupt pin alone. + uint8_t temp_buffer = readControlByte(0); + // modify control byte + if (Alarm == 1) { + temp_buffer = temp_buffer & 0xFE; + } else { + temp_buffer = temp_buffer & 0xFD; + } + writeControlByte(temp_buffer, 0); +} + +bool DS3231::checkAlarmEnabled(uint8_t Alarm) { + // Checks whether the given alarm is enabled. + uint8_t result = 0x0; + uint8_t temp_buffer = readControlByte(0); + if (Alarm == 1) { + result = temp_buffer & 0x01; + } else { + result = temp_buffer & 0x02; + } + return result; +} + +bool DS3231::checkIfAlarm(uint8_t Alarm) { + // Checks whether alarm 1 or alarm 2 flag is on, returns T/F accordingly. + // Turns flag off, also. + // defaults to checking alarm 2, unless Alarm == 1. + uint8_t result; + uint8_t temp_buffer = readControlByte(1); + if (Alarm == 1) { + // Did alarm 1 go off? + result = temp_buffer & 0x01; + // clear flag + temp_buffer = temp_buffer & 0xFE; + } else { + // Did alarm 2 go off? + result = temp_buffer & 0x02; + // clear flag + temp_buffer = temp_buffer & 0xFD; + } + writeControlByte(temp_buffer, 1); + return result; +} + +void DS3231::enableOscillator(bool TF, bool battery, uint8_t frequency) { + // turns oscillator on or off. True is on, false is off. + // if battery is true, turns on even for battery-only operation, + // otherwise turns off if Vcc is off. + // frequency must be 0, 1, 2, or 3. + // 0 = 1 Hz + // 1 = 1.024 kHz + // 2 = 4.096 kHz + // 3 = 8.192 kHz (Default if frequency byte is out of range) + if (frequency > 3) frequency = 3; + // read control byte in, but zero out current state of RS2 and RS1. + uint8_t temp_buffer = readControlByte(0) & 0xE7; + if (battery) { + // turn on BBSQW flag + temp_buffer = temp_buffer | 0x40; + } else { + // turn off BBSQW flag + temp_buffer = temp_buffer & 0xBF; + } + if (TF) { + // set ~EOSC to 0 and INTCN to zero. + temp_buffer = temp_buffer & 0x7B; + } else { + // set ~EOSC to 1, leave INTCN as is. + temp_buffer = temp_buffer | 0x80; + } + // shift frequency into bits 3 and 4 and set. + frequency = frequency << 3; + temp_buffer = temp_buffer | frequency; + // And write the control bits + writeControlByte(temp_buffer, 0); +} + +void DS3231::enable32kHz(bool TF) { + // turn 32kHz pin on or off + uint8_t temp_buffer = readControlByte(1); + if (TF) { + // turn on 32kHz pin + temp_buffer = temp_buffer | 0x08; + } else { + // turn off 32kHz pin + temp_buffer = temp_buffer & 0xF7; + } + writeControlByte(temp_buffer, 1); +} + +bool DS3231::oscillatorCheck() { + // Returns false if the oscillator has been off for some reason. + // If this is the case, the time is probably not correct. + uint8_t temp_buffer = readControlByte(1); + bool result = true; + if (temp_buffer & 0x80) { + // Oscillator Stop Flag (OSF) is set, so return false. + result = false; + } + return result; +} + +/***************************************** + Private Functions + *****************************************/ +uint8_t DS3231::decToBcd(uint8_t val) { +// Convert normal decimal numbers to binary coded decimal + return ( (val/10*16) + (val%10) ); +} + +uint8_t DS3231::bcdToDec(uint8_t val) { +// Convert binary coded decimal to normal decimal numbers + return ( (val/16*10) + (val%16) ); +} + +uint8_t DS3231::readControlByte(bool which) { + // Read selected control byte + // first byte (0) is 0x0e, second (1) is 0x0f + //I2C.beginTransmission(CLOCK_ADDRESS); + _i2c.start(); + _i2c.write(uint8_t(CLOCK_ADDRESS)); + + if (which) { + // second control byte + //I2C.write(uint8_t(0x0f)); + _i2c.write(uint8_t(0x0f)); + + } else { + // first control byte + //I2C.write(uint8_t(0x0e)); + _i2c.write(uint8_t(0x0e)); + + } + //I2C.endTransmission(); + _i2c.stop(); + + //I2C.requestFrom(CLOCK_ADDRESS, 1); + return _i2c.read(uint8_t(CLOCK_ADDRESS)); +} + +void DS3231::writeControlByte(uint8_t control, bool which) { + // Write the selected control byte. + // which=false -> 0x0e, true->0x0f. + //I2C.beginTransmission(CLOCK_ADDRESS); + _i2c.start(); + _i2c.write(uint8_t(CLOCK_ADDRESS)); + + if (which) { + //I2C.write(uint8_t(0x0f)); + _i2c.write(uint8_t(0x0f)); + + } else { + //I2C.write(uint8_t(0x0e)); + _i2c.write(uint8_t(0x0e)); + + } + //I2C.write(control); + //I2C.endTransmission(); + _i2c.write(control); + _i2c.stop(); +} +