Library for Maxim DS3232M super-accurate, I2C based Real Time Clock chip with 234 bytes of user RAM. Library includes user RAM read/write operations along with CRC routines for accessing the user RAM area.

Dependents:   ds3232m_HelloWorld

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ds3232m.cpp Source File

ds3232m.cpp

00001 #include "mbed.h"
00002 #include "ds3232m.h"
00003 
00004 #define RTCI2CADDRESS           0xd0        //i2c address of the DS3232M
00005 #define DS3232SECONDS           0x00        //seconds register
00006 #define DS3232DAYOFWEEK         0x03        //day of the week register
00007 #define DS3232ALM1BOTTOM        0x07        //alarm 1 registers (not used)
00008 #define DS3232ALM2BOTTOM        0x0b        //alarm 2 registers (not used)
00009 #define DS3232CTLREG            0x0e        //control register
00010 #define DS3232STATREG           0x0f        //status register
00011 #define DS3232TEMPERATURE       0x11        //temperature registers
00012 #define DS3232TESTREG           0x13        //test register (do not use)
00013 #define DS3232RAMBOTTOM         0x14        //where user RAM starts
00014 #define DS3232RAMCRC16          0xfc        //crc16 registers (top 2 bytes of user RAM)
00015 #define DS3232RAMTOP            0xfe        //where user RAM ends. Note: bug in F746 only allows for a I2C xfer of 255 max!!!!
00016 
00017 //control register
00018 #define DS3232_EOSCN_BIT        0x80        //turn on/off oscillator in batt backup mode, 0 = on
00019 #define DS3232_BBSQW_BIT        0x40        //battery backed 1Hz enable, 1 = enable
00020 #define DS3232_CONV_BIT         0x20        //start temperature conversion, 1 = start
00021 #define DS3232_INTCN_BIT        0x04        //interrupt control, 1 = interrupt mode, 0 = 1Hz mode
00022 
00023 //status register
00024 #define DS3232_OSF_BIT          0x80        //oscillator stopped, 1 = stopped
00025 #define DS3232_BB32KHZ_BIT      0x40        //battery backed 32KHz enable, 1 = enable(writable bit)
00026 #define DS3232_EN32KHZ_BIT      0x08        //32KHz enable, 1 = enable(writable bit)
00027 #define DS3232_BUSY_BIT         0x04        //busy doing a temperature A:D conversion, 1 = busy
00028 
00029 char RtcCtlReg = 0;                         //mirror of control register
00030 char RtcStatReg = 0;                        //mirror of status register
00031 
00032 //char RTCbuffer[256];                        //buffer may contain up to 256 bytes, depending on command used
00033 
00034 //--------------------------------------------------------------------------------------------------------------------------------------//
00035 // constructor with fixed frequency 
00036 
00037 ds3232m::ds3232m(PinName sda, PinName scl) {
00038     // Create a new I2C object
00039     _i2c_ = new I2C(sda, scl);
00040     // Set the frequency to standard 400kHz
00041     _i2c_->frequency(400000);
00042 }
00043 
00044 //--------------------------------------------------------------------------------------------------------------------------------------//
00045 // constructor with I2C frequency selection
00046 
00047 ds3232m::ds3232m(PinName sda, PinName scl, int i2cFrequency) {
00048     _i2c_ = new I2C(sda, scl);
00049     _i2c_->frequency(i2cFrequency);
00050 }
00051 
00052 //--------------------------------------------------------------------------------------------------------------------------------------//
00053 // deconstructor
00054 
00055 ds3232m::~ds3232m() {
00056 }
00057 
00058 //--------------------------------------------------------------------------------------------------------------------------------------//
00059 // set time up into the DS3232M
00060 
00061 void ds3232m::setTime(Time_rtc& dsSTR) {
00062     dsSTR.RTCbuffer [0] = DS3232SECONDS;
00063     dsSTR.RTCbuffer [1] = DecToBCD(dsSTR.sec );
00064     dsSTR.RTCbuffer [2] = DecToBCD(dsSTR.min );
00065     dsSTR.RTCbuffer [3] = DecToBCD(dsSTR.hour );
00066     dsSTR.RTCbuffer [4] = dsSTR.wday ;
00067     dsSTR.RTCbuffer [5] = DecToBCD(dsSTR.date );
00068     dsSTR.RTCbuffer [6] = DecToBCD(dsSTR.mon );
00069     dsSTR.RTCbuffer [7] = DecToBCD(dsSTR.year  - 2000);
00070     
00071     _i2c_->write(RTCI2CADDRESS, dsSTR.RTCbuffer , 8, false);
00072 }
00073 
00074 //--------------------------------------------------------------------------------------------------------------------------------------//
00075 // get time from the DS3232M and stick it into the mbed's RTC
00076 
00077 void ds3232m::getTime(Time_rtc& dsSTR) {
00078     dsSTR.RTCbuffer [0] = DS3232SECONDS;
00079     _i2c_->write(RTCI2CADDRESS, dsSTR.RTCbuffer , 1, true);
00080     _i2c_->read(RTCI2CADDRESS, dsSTR.RTCbuffer , 7, false);
00081     
00082     dsSTR.sec  = BCDToDec(dsSTR.RTCbuffer [0]);
00083     dsSTR.min  = BCDToDec(dsSTR.RTCbuffer [1]);
00084     dsSTR.hour  = ds3232m::BCDToDec(dsSTR.RTCbuffer [2]);
00085     dsSTR.wday  = dsSTR.RTCbuffer [3]; 
00086     dsSTR.date  = ds3232m::BCDToDec(dsSTR.RTCbuffer [4]);
00087     dsSTR.mon  = ds3232m::BCDToDec(dsSTR.RTCbuffer [5]);
00088     dsSTR.year  = ds3232m::BCDToDec(dsSTR.RTCbuffer [6]) + 2000;
00089 }
00090 
00091 //--------------------------------------------------------------------------------------------------------------------------------------//
00092 // retrieve the control and status registers
00093 
00094 void ds3232m::getControlStatusRegs(Time_rtc& dsSTR) {
00095     dsSTR.RTCbuffer[0] = DS3232CTLREG;
00096     _i2c_->write(RTCI2CADDRESS, dsSTR.RTCbuffer, 1, true);
00097     _i2c_->read(RTCI2CADDRESS, dsSTR.RTCbuffer, 2, false);
00098     RtcCtlReg = dsSTR.RTCbuffer[0];
00099     RtcStatReg = dsSTR.RTCbuffer[1];
00100 }
00101     
00102 //--------------------------------------------------------------------------------------------------------------------------------------//
00103 // enables/disables the main oscillator during battery backup mode. true = enable during battery backup mode
00104 
00105 void ds3232m::enableBattClock(Time_rtc& dsSTR, bool batt) {
00106     getControlStatusRegs(dsSTR);
00107     if(batt == true) {
00108         RtcCtlReg &= ~DS3232_EOSCN_BIT;
00109     } else {
00110         RtcCtlReg |= DS3232_EOSCN_BIT;
00111     }
00112 
00113     dsSTR.RTCbuffer [0] = DS3232CTLREG;
00114     dsSTR.RTCbuffer [1] = RtcCtlReg;
00115     _i2c_->write(RTCI2CADDRESS, dsSTR.RTCbuffer , 2, false);
00116 }
00117 
00118 //--------------------------------------------------------------------------------------------------------------------------------------//
00119 // check to see if DS3232M's temperature conversion cycle is bust or not
00120 // only 1 per second max
00121 
00122 bool ds3232m::checkTempBusy(Time_rtc& dsSTR) {
00123     getControlStatusRegs(dsSTR);
00124     if(RtcCtlReg & DS3232_CONV_BIT) return true;     //is already busy
00125     if(RtcStatReg & DS3232_BUSY_BIT) return true;    //is already busy
00126     return false;
00127 }
00128 
00129 //--------------------------------------------------------------------------------------------------------------------------------------//
00130 // start a temperature conversion cycle
00131 // only 1 per second max
00132 
00133 bool ds3232m::startTempCycle(Time_rtc& dsSTR) {
00134     getControlStatusRegs(dsSTR);
00135     if((checkTempBusy(dsSTR)) == true) return false;     //is already busy
00136     RtcCtlReg |= DS3232_CONV_BIT;
00137 
00138     dsSTR.RTCbuffer [0] = DS3232CTLREG;
00139     dsSTR.RTCbuffer [1] = RtcCtlReg;
00140     _i2c_->write(RTCI2CADDRESS, dsSTR.RTCbuffer , 2, false);
00141     return true;
00142 }
00143 
00144 //--------------------------------------------------------------------------------------------------------------------------------------//
00145 // get temperature
00146 // if returned value = 255.0, then temperature conversion was still busy
00147 
00148 float ds3232m::getTemperature(Time_rtc& dsSTR) {
00149     getControlStatusRegs(dsSTR);
00150     if((checkTempBusy(dsSTR)) == false) return(255.0);     //is already busy
00151 
00152     dsSTR.RTCbuffer [0] = DS3232TEMPERATURE;
00153     _i2c_->write(RTCI2CADDRESS, dsSTR.RTCbuffer , 1, true);
00154     _i2c_->read(RTCI2CADDRESS, dsSTR.RTCbuffer , 2, false);
00155     int8_t temp3232a = dsSTR.RTCbuffer [0];
00156     uint8_t temp3232b = dsSTR.RTCbuffer [1];
00157 
00158     float temp3232 = (float)temp3232a;
00159     //for positive temp
00160     if((temp3232b == 0x40) && (temp3232a >= 0)) temp3232 += 0.25;
00161     if (temp3232b == 0x80) temp3232 += 0.5;
00162     if((temp3232b == 0xc0) && (temp3232a >= 0)) temp3232 += 0.75;
00163     //for negative temp
00164     if((temp3232b == 0x40) && (temp3232a < 0)) temp3232 += 0.75;
00165     if((temp3232b == 0xc0) && (temp3232a < 0)) temp3232 += 0.25;
00166     return (temp3232);
00167 }
00168 
00169 //--------------------------------------------------------------------------------------------------------------------------------------//
00170 // get seconds register
00171 
00172 uint8_t ds3232m::getSeconds(Time_rtc& dsSTR) {
00173     dsSTR.RTCbuffer [0] = DS3232SECONDS;
00174     _i2c_->write(RTCI2CADDRESS, dsSTR.RTCbuffer , 1, true);
00175     _i2c_->read(RTCI2CADDRESS, dsSTR.RTCbuffer , 1, false);
00176     int seconds3232 = dsSTR.RTCbuffer [0];
00177     int xbcd = BCDToDec(seconds3232);
00178     return(xbcd);
00179 }
00180 
00181 //--------------------------------------------------------------------------------------------------------------------------------------//
00182 // get day of the week register (1 = Monday... 7 = Sunday)
00183 
00184 uint8_t ds3232m::getDayOfWeek(Time_rtc& dsSTR) {
00185     dsSTR.RTCbuffer [0] = DS3232DAYOFWEEK;
00186     _i2c_->write(RTCI2CADDRESS, dsSTR.RTCbuffer , 1, true);
00187     _i2c_->read(RTCI2CADDRESS, dsSTR.RTCbuffer , 1, false);
00188     int dow3232 = dsSTR.RTCbuffer [0];
00189     return(BCDToDec(dow3232));
00190 }
00191 
00192 //--------------------------------------------------------------------------------------------------------------------------------------//
00193 // set day of the week register (1 = Monday... 7 = Sunday)
00194 
00195 void ds3232m::putDayOfWeek(Time_rtc& dsSTR, uint8_t dow3232) {
00196     dow3232 = dow3232 & 7;
00197     dsSTR.RTCbuffer [0] = DS3232DAYOFWEEK;
00198     dsSTR.RTCbuffer [1] = DecToBCD(dow3232);
00199     _i2c_->write(RTCI2CADDRESS, dsSTR.RTCbuffer , 2, false);
00200 }
00201 
00202 //--------------------------------------------------------------------------------------------------------------------------------------//
00203 // clears all user memory inside the DS3232M.  Top 2 locations - reserved for CRC, also set to 00 since CRC value is 0x00 0x00
00204 
00205 void ds3232m::clearRAM(Time_rtc& dsSTR) {
00206     for(int i = DS3232SECONDS; i <= DS3232RAMTOP; i++) dsSTR.RTCbuffer [i] = 0;
00207     dsSTR.RTCbuffer [0] = DS3232RAMBOTTOM;
00208     _i2c_->write(RTCI2CADDRESS, dsSTR.RTCbuffer , (DS3232RAMTOP - DS3232RAMBOTTOM + 2), false);
00209 }
00210 
00211 //--------------------------------------------------------------------------------------------------------------------------------------//
00212 // retrieves user data from DS3232M RAM
00213 
00214 uint8_t ds3232m::getUserRAM(char *buffer, Time_rtc& dsSTR, int offset, int length) {
00215     if((offset + length) > DS3232RAMCRC16) return(DS3232_OVERFLOWERROR);
00216     if(offset < DS3232RAMBOTTOM) return(DS3232_OFFSETERROR);
00217     if(length == 0) return(DS3232_LENZEROERROR);
00218     dsSTR.RTCbuffer [0] = offset;
00219     _i2c_->write(RTCI2CADDRESS, dsSTR.RTCbuffer , 1, true);
00220     _i2c_->read(RTCI2CADDRESS, buffer, length, false);
00221     if(LoadRTCRam(dsSTR) == false) return(DS3232_CRCERROR);
00222     return(DS3232_NOERROR);
00223 }
00224 
00225 //--------------------------------------------------------------------------------------------------------------------------------------//
00226 // puts user data into DS3232M RAM. CRC16 value added to the top 2 RAM location
00227 
00228 uint8_t ds3232m::putUserRAM(char *buffer, Time_rtc& dsSTR, int offset, int length) {
00229     if((offset + length) > DS3232RAMCRC16) return(DS3232_OVERFLOWERROR);
00230     if(offset < DS3232RAMBOTTOM) return(DS3232_OFFSETERROR);
00231     if(length == 0) return(DS3232_LENZEROERROR);
00232     for(int i = 0; i < 32; i++) dsSTR.RTCbuffer [i] = NULL;
00233     dsSTR.RTCbuffer [0] = offset;
00234     dsSTR.RTCbuffer [1] = NULL;
00235     strcat(dsSTR.RTCbuffer , buffer);
00236     _i2c_->write(RTCI2CADDRESS, dsSTR.RTCbuffer , length + 1, false);
00237     addCRC16(dsSTR);
00238     return(DS3232_NOERROR);
00239 }
00240 
00241 //--------------------------------------------------------------------------------------------------------------------------------------//
00242 // enable/disable 32KHz output with run on battery mode option
00243 
00244 void ds3232m::set32KhzOutput(Time_rtc& dsSTR, bool ena, bool batt) {
00245     getControlStatusRegs(dsSTR);
00246     if(ena == true) {
00247         RtcStatReg |= DS3232_EN32KHZ_BIT;
00248         if(batt == true) {
00249             RtcStatReg |= DS3232_BB32KHZ_BIT;
00250         } else {
00251             RtcStatReg &= ~DS3232_BB32KHZ_BIT;
00252         }
00253     } else {
00254         RtcStatReg &= ~DS3232_EN32KHZ_BIT;
00255         RtcStatReg &= ~DS3232_BB32KHZ_BIT;
00256     }
00257 
00258     dsSTR.RTCbuffer [0] = DS3232STATREG;
00259     dsSTR.RTCbuffer [1] = RtcStatReg;
00260     _i2c_->write(RTCI2CADDRESS, dsSTR.RTCbuffer , 2, false);
00261 }
00262 
00263 //--------------------------------------------------------------------------------------------------------------------------------------//
00264 // enable/disable 1Hz output with run on battery mode option
00265 
00266 void ds3232m::set1hzOutput(Time_rtc& dsSTR, bool ena, bool batt) {
00267     getControlStatusRegs(dsSTR);
00268     if(ena == true) {
00269         RtcCtlReg &= ~DS3232_INTCN_BIT;
00270         if(batt == true) {
00271             RtcCtlReg |= DS3232_BBSQW_BIT;
00272         } else {
00273             RtcCtlReg &= ~DS3232_BBSQW_BIT;
00274         }
00275     } else {
00276         RtcCtlReg |= DS3232_INTCN_BIT;
00277         RtcCtlReg &= ~DS3232_BBSQW_BIT;
00278     }
00279 
00280     dsSTR.RTCbuffer [0] = DS3232CTLREG;
00281     dsSTR.RTCbuffer [1] = RtcCtlReg;
00282     _i2c_->write(RTCI2CADDRESS, dsSTR.RTCbuffer , 2, false);
00283 }
00284 
00285 //--------------------------------------------------------------------------------------------------------------------------------------//
00286 // CRC table and routine taken from Emilie Laverge's CRC16 library
00287 //--------------------------------------------------------------------------------------------------------------------------------------//
00288 // lookup table for calculating crc16
00289 
00290 uint16_t crc16table[256] = { 
00291         0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011,
00292         0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022,
00293         0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072,
00294         0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041,
00295         0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2,
00296         0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1,
00297         0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1,
00298         0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082,
00299         0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192,
00300         0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1,
00301         0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1,
00302         0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2,
00303         0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151,
00304         0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162,
00305         0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132,
00306         0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, 
00307         0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312,
00308         0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321,
00309         0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371,
00310         0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342,
00311         0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1,
00312         0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2,
00313         0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2,
00314         0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381,
00315         0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291,
00316         0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2,
00317         0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2,
00318         0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1,
00319         0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252,
00320         0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261,
00321         0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231,
00322         0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202
00323 };
00324 
00325 //--------------------------------------------------------------------------------------------------------------------------------------//
00326 // calculate a crc value for the DS3232M user ram area.  CRC value calculated from address 0x14 - 0xfd
00327 
00328 uint16_t ds3232m::calculateCRC16(char input[], Time_rtc& dsSTR, int offset, int length) {
00329     uint16_t CRC16s = 0;
00330     input+= offset;
00331     for(int i = offset; i < (length + offset); i++) {
00332         uint16_t tableValue = crc16table[((CRC16s >> 8) ^ *(char *)input++) & 0x00ff];
00333         CRC16s = (CRC16s << 8) ^ tableValue;
00334     }
00335     dsSTR.c_crc  = CRC16s;
00336     return CRC16s;
00337 }
00338 
00339 //--------------------------------------------------------------------------------------------------------------------------------------//
00340 // This function gets the entire contents of the RTC. Returns flase if a CRC error occured
00341 
00342 bool ds3232m::LoadRTCRam(Time_rtc& dsSTR) {
00343     dsSTR.RTCbuffer [0] = DS3232SECONDS;
00344     _i2c_->write(RTCI2CADDRESS, dsSTR.RTCbuffer , 1, true);
00345     _i2c_->read(RTCI2CADDRESS, dsSTR.RTCbuffer , DS3232RAMTOP, false); //BUG in F746 only allows for xfer of 255 bytes max!!!
00346 
00347     dsSTR.s_crc  = (dsSTR.RTCbuffer [DS3232RAMCRC16] << 8) | dsSTR.RTCbuffer [DS3232RAMCRC16 + 1];
00348     calculateCRC16(dsSTR.RTCbuffer , dsSTR, DS3232RAMBOTTOM, (DS3232RAMCRC16 - DS3232RAMBOTTOM));
00349     
00350     ////reload the time registers
00351     //dsSTR.RTCbuffer[0] = DS3232SECONDS;
00352     //_i2c_->write(RTCI2CADDRESS, dsSTR.RTCbuffer, 1, true);
00353     //_i2c_->read(RTCI2CADDRESS, dsSTR.RTCbuffer, 7, false);
00354 
00355     if(dsSTR.c_crc  != dsSTR.s_crc ) return false;
00356     return true;
00357 }
00358 
00359 //--------------------------------------------------------------------------------------------------------------------------------------//
00360 // This function calculates and saves CRC data to the end of RTC's RAM.  
00361 // CRC calculated address range is 0x14 - 0xfd. CRC data is placed in 0xfe and 0xff
00362 
00363 void ds3232m::addCRC16(Time_rtc& dsSTR) {
00364     LoadRTCRam(dsSTR);
00365     dsSTR.c_crc = calculateCRC16(dsSTR.RTCbuffer, dsSTR, DS3232RAMBOTTOM, (DS3232RAMCRC16 - DS3232RAMBOTTOM)); //0x14 is offset from 0 in buffer, 230 is length //
00366     dsSTR.RTCbuffer[0] = DS3232RAMCRC16;
00367     dsSTR.RTCbuffer[1] = dsSTR.c_crc >> 8;
00368     dsSTR.RTCbuffer[2] = dsSTR.c_crc & 0xff;
00369     _i2c_->write(RTCI2CADDRESS, dsSTR.RTCbuffer, 3, false);
00370 }
00371