Realtime clock library for DS1307 and DS3231m

Dependents:   vfd_modular_clock_mbed

ds3231m.cpp

Committer:
perjg
Date:
2015-08-10
Revision:
2:6119507e6713
Parent:
1:3fe5649f1e02

File content as of revision 2:6119507e6713:

/*
 * RTC library - mbed
 * (C) 2011-15 Akafugu Corporation
 *
 * This program is free software; you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 *
 */

#include "ds3231m.h"

#define DS3231M_SLAVE_ADDR 0xD0

DS3231M::DS3231M(I2C& i2c)
 : RTC()
 , m_i2c(i2c)
{
    m_i2c.frequency(100000);    
}

void DS3231M::begin()
{
}

time_t DS3231M::time()
{
    return m_time;
}

struct tm* DS3231M::getTime()
{
    char rtc[7];
    
    rtc[0] = 0; // second register, 0
    int w = m_i2c.write(DS3231M_SLAVE_ADDR, rtc, 1);
    int r = m_i2c.read(DS3231M_SLAVE_ADDR, rtc, 7);
    
    // Clear clock halt bit from read data
    //rtc[0] &= ~(_BV(CH_BIT));
    
    m_tm.tm_sec  = bcd2dec(rtc[0]);
    m_tm.tm_min  = bcd2dec(rtc[1]);
    m_tm.tm_hour = bcd2dec(rtc[2]);
    m_tm.tm_wday = bcd2dec(rtc[3])-1;
    m_tm.tm_mday = bcd2dec(rtc[4]);
    m_tm.tm_mon  = bcd2dec(rtc[5])-1; // tm_mon is 0-11
    m_tm.tm_year = bcd2dec(rtc[6]);
    
    return &m_tm;    
}

void DS3231M::getTime(uint8_t* hour, uint8_t* min, uint8_t* sec)
{
    char rtc[3];
    
    rtc[0] = 0; // second register, 0
    int w = m_i2c.write(DS3231M_SLAVE_ADDR, rtc, 1);
    int r = m_i2c.read(DS3231M_SLAVE_ADDR, rtc, 3);
    
    // Clear clock halt bit from read data
    //rtc[0] &= ~(_BV(CH_BIT));
    
    if (sec)  *sec =  bcd2dec(rtc[0]);
    if (min)  *min =  bcd2dec(rtc[1]);
    if (hour) *hour = bcd2dec(rtc[2]);
}

void DS3231M::setTime(time_t t)
{
    struct tm * timeinfo;
    timeinfo = localtime (&t);
    setTime(timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec);
}

void DS3231M::setTime(uint8_t hour, uint8_t min, uint8_t sec)
{
    write_byte(dec2bcd(sec), 0);
    write_byte(dec2bcd(min), 1);
    write_byte(dec2bcd(hour), 2);
}

void DS3231M::setTime(struct tm* tm)
{
    write_byte(dec2bcd(tm->tm_sec),  0);
    write_byte(dec2bcd(tm->tm_min),  1);
    write_byte(dec2bcd(tm->tm_hour), 2);
    write_byte(dec2bcd(tm->tm_wday+1),  3);
    write_byte(dec2bcd(tm->tm_mday),  4);
    write_byte(dec2bcd(tm->tm_mon+1), 5);
    write_byte(dec2bcd(tm->tm_year),  6);
}

void DS3231M::setAlarm(time_t t)
{
    struct tm * timeinfo;
    timeinfo = localtime (&t);
    setAlarm(timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec);
}

void DS3231M::setAlarm(uint8_t hour, uint8_t min, uint8_t sec)
{
    /*
     *  07h: A1M1:0  Alarm 1 seconds
     *  08h: A1M2:0  Alarm 1 minutes
     *  09h: A1M3:0  Alarm 1 hour (bit6 is am/pm flag in 12h mode)
     *  0ah: A1M4:1  Alarm 1 day/date (bit6: 1 for day, 0 for date)
     *  Sets alarm to fire when hour, minute and second matches
     */
    write_byte(dec2bcd(sec), 0x07); // second
    write_byte(dec2bcd(min), 0x08); // minute
    write_byte(dec2bcd(hour), 0x09); // hour
    write_byte(0x81, 0x0a); // day (upper bit must be set)
    
    // clear alarm flag
    uint8_t val = read_byte(0x0f);
    write_byte(val & ~0x01, 0x0f);
}


struct tm* DS3231M::getAlarm(void)
{
    struct tm * timeinfo = getTime();
    uint8_t hour, min, sec;
    
    getAlarm(&hour, &min, &sec);
    timeinfo->tm_hour = hour;
    timeinfo->tm_min = min;
    timeinfo->tm_sec = sec;
    return timeinfo;
}

void DS3231M::getAlarm(uint8_t* hour, uint8_t* min, uint8_t* sec)
{
    *sec  = bcd2dec(read_byte(0x07) & ~0x80);
    *min  = bcd2dec(read_byte(0x08) & ~0x80);
    *hour = bcd2dec(read_byte(0x09) & ~0x80);
}

bool DS3231M::checkAlarm(void)
{
    // Alarm 1 flag (A1F) in bit 0
    uint8_t val = read_byte(0x0f);

    // clear flag when set
    if (val & 1)
        write_byte(val & ~0x01, 0x0f);
        
    return val & 1 ? 1 : 0;
}

void DS3231M::SQWEnable(bool enable)
{
    uint8_t control = read_byte(0x0E);  // read control register
    if (enable) {
        control |=  0x40; // set BBSQW to 1
        control &= ~0x04; // set INTCN to 0
    }
    else {
        control &= ~0x40; // set BBSQW to 0
    }
    // write control back
    write_byte(control, 0x0E);
}

void DS3231M::SQWSetFreq(enum RTC_SQW_FREQ freq)
{
    uint8_t control = read_byte(0x0E);  // read control register
    control &= ~0x18; // Set to 0
    control |= (freq << 4); // Set freq bitmask

    // write control back
    write_byte(control, 0x0E);
}

void DS3231M::Osc32kHzEnable(bool enable)
{
    uint8_t status = read_byte(0x0F);  // read status

    if (enable)
        status |= 0x08; // set to 1
    else
        status &= ~0x08; // Set to 0

    // write status back
    write_byte(status, 0x0F);
}

void DS3231M::getTemp(int8_t* i, uint8_t* f)
{
    char data[2]; // msb, lsb
    
    *i = 0;
    *f = 0;
    
    data[0] = 0x11;
    int w = m_i2c.write(DS3231M_SLAVE_ADDR, data, 1);
    int r = m_i2c.read(DS3231M_SLAVE_ADDR, data, 2);
    
    // integer part in entire byte
    *i = (uint8_t)data[0];
    // fractional part in top two bits (increments of 0.25)
    *f = ((uint8_t)data[1] >> 6) * 25;
    
    // float value can be read like so:
    // float temp = ((((short)data[0] << 8) | (short)data[1]) >> 6) / 4.0f;
}

void DS3231M::forceTempConversion(uint8_t block)
{
    // read control register (0x0E)
    uint8_t control = read_byte(0x0E);  // read control register
    control |= 0x20; // Set CONV bit

    // write new control register value
    write_byte(control, 0x0E);

    if (!block) return;
    
    uint8_t data;
    // Temp conversion is ready when control register becomes 0
    do {
        // Block until CONV is 0
        data = read_byte(0x0E);
    } while ((data & 0x20) != 0);
}


uint8_t DS3231M::read_byte(uint8_t offset)
{
    char buf[1];
    buf[0] = offset;
    
    int w = m_i2c.write(DS3231M_SLAVE_ADDR, buf, 1);
    int r = m_i2c.read(DS3231M_SLAVE_ADDR,  buf, 1);    
    //error = ((w!=0) || (r!=0));
    
    return buf[0];
}

void DS3231M::write_byte(uint8_t b, uint8_t offset)
{
    char buf[2];
    buf[0] = offset;
    buf[1] = b;
    
    int w = m_i2c.write(DS3231M_SLAVE_ADDR, buf, 2); 
    //error=(w!=0);   
}

void DS3231M::write_addr(uint8_t addr)
{
    /*
    Wire.beginTransmission(RTC_ADDR);
    Wire.write(addr);
    Wire.endTransmission();
    */
}