Dennis Smith / PCF8583_rtc

Dependents:   NixieClock800Max

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers PCF8583_rtc.cpp Source File

PCF8583_rtc.cpp

00001 /*
00002  ********************************************************************************
00003  * An mbed class to control the PCF8583 Real time Clock/Calender
00004  * Copyright (c) 2014 Dennis (Denny) Smith - dennyem
00005  *
00006  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00007  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00008  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00009  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00010  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00011  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00012  * THE SOFTWARE.
00013  *
00014  * 13.01.14  DWS  initial design for PIC devices using PICC compiler
00015  * 21.01.14  DWS  ported to mBed LPC1768 in 'C'
00016  * 09.02.14  DWS  converted to C++ and ported to mBed LPC812
00017  *
00018  * TODO
00019  *       FormatDateTime needs am/pm, 12/24 hour parsing
00020  *       Alarm functions not yet implemented
00021 */
00022 
00023 #include <mbed.h>
00024 #include <PCF8583_rtc.h>
00025 
00026 //-----------------------------------------------------------------------------
00027 // constructor -- accepts an I2c object to use for connection with the rtc
00028 PCF8583rtc::PCF8583rtc(I2C *i2c, char I2cAddress)
00029 {
00030    _i2c = i2c;
00031    _I2cAddress = I2cAddress;
00032    
00033   ShortDateFormat = "d,m,yy";
00034   LongDateFormat  = "dddd dd mmmm yyyy";
00035   ShortTimeFormat = "d:m:yy";
00036   LongTimeFormat  = "dd:nn:yyyy";
00037 
00038   DateSeparator   = '/';
00039   TimeSeparator   = ':';
00040 
00041   ShortDayNames[0] = "Mon";
00042   ShortDayNames[1] = "Tue";
00043   ShortDayNames[2] = "Wed";
00044   ShortDayNames[3] = "Thu";
00045   ShortDayNames[4] = "Fri";
00046   ShortDayNames[5] = "Sat";
00047   ShortDayNames[6] = "Sun";
00048 
00049   LongDayNames[0]  = "Monday";
00050   LongDayNames[1]  = "Tuesday";
00051   LongDayNames[2]  = "Wednesday";
00052   LongDayNames[3]  = "Thursday";
00053   LongDayNames[4]  = "Friday";
00054   LongDayNames[5]  = "Saturday";
00055   LongDayNames[6]  = "Sunday";
00056 
00057   ShortMonthNames[0]  = "Jan";
00058   ShortMonthNames[1]  = "Feb";
00059   ShortMonthNames[2]  = "Mar";
00060   ShortMonthNames[3]  = "Apr";
00061   ShortMonthNames[4]  = "May";
00062   ShortMonthNames[5]  = "Jun";
00063   ShortMonthNames[6]  = "Jul";
00064   ShortMonthNames[7]  = "Aug";
00065   ShortMonthNames[8]  = "Sep";
00066   ShortMonthNames[9]  = "Oct";
00067   ShortMonthNames[10] = "Nov";
00068   ShortMonthNames[11] = "Dec";
00069 
00070   LongMonthNames[0]   = "January";
00071   LongMonthNames[1]   = "February";
00072   LongMonthNames[2]   = "March";
00073   LongMonthNames[3]   = "April";
00074   LongMonthNames[4]   = "May";
00075   LongMonthNames[5]   = "June";
00076   LongMonthNames[6]   = "July";
00077   LongMonthNames[7]   = "August";
00078   LongMonthNames[8]   = "September";
00079   LongMonthNames[9]   = "October";
00080   LongMonthNames[10]  = "November";
00081   LongMonthNames[11]  = "December";
00082 };
00083 
00084 void PCF8583rtc::write(const char address, struct DateTime_t dti)
00085 {
00086     char tmp[8];
00087 
00088     pauseCounting();    //Must stop counting before initialising Date/time
00089 
00090     tmp[0] = address;                                               // Address is 1 for Time or 10 for Alarm
00091     
00092     //Values must be in BCD form
00093     tmp[1] = dti.time.hundreds;                                     // Hundredths of a second
00094     tmp[2] = dti.time.seconds;                                      // Seconds
00095     tmp[3] = dti.time.minutes;                                      // Minutes
00096     tmp[4] = dti.time.hours;                                        // Hours
00097     tmp[5] = dti.date.day & 0x3F;                                   // Always set the 3 year bits to 0
00098 
00099     if(address == TIME)
00100         tmp[6] = (((dti.date.weekday & 7) << 5 ) | dti.date.month); // Weekday/month
00101     else
00102         tmp[6] = dti.date.month & 0x1f;                             // No Weekday for alarm
00103 
00104     _i2c->write(_I2cAddress, tmp, 7);                              // Address PCF8583, see PCF8583 datasheet
00105     _i2c->stop();
00106 
00107     if(address == TIME) {
00108         writeByte(CENTURY_REG, dti.date.century);                   // Store the full 4 digit year in NV Ram
00109         writeByte(YEAR_REG, dti.date.year);
00110     };
00111 
00112     enableCounting();
00113 };
00114 
00115 //--------------------- Reads time and date information from RTC (PCF8583)
00116 struct DateTime_t PCF8583rtc::read(const char address)
00117 {
00118     char tmp[8];
00119     char year_bits = 0;                   // To test for year change
00120 
00121     tmp[0] = address;
00122     _i2c->write(_I2cAddress, tmp, 1);     // Address PCF8583, see PCF8583 datasheet
00123     _i2c->read(_I2cAddress | 1, tmp, 6);  // Address PCF8583 for reading R/W=1
00124     _i2c->stop();
00125     
00126     dt.time.hundreds   = tmp[0];
00127     dt.time.seconds    = tmp[1];
00128     dt.time.minutes    = tmp[2];
00129     dt.time.hours      = tmp[3] & 0x3F;
00130     dt.time.fmt_hours  = tmp[3] & 0x80;   // 12/24 hour format
00131     dt.time.am_pm_flag = tmp[3] & 0x40;   // Am/Pm flag
00132     dt.date.day        = tmp[4] & 0x3F;   // Day of the Month
00133     dt.date.month      = tmp[5] & 0x1F;
00134 
00135     if(address == TIME)
00136         year_bits = (tmp[4] & 0xC0) >> 6;
00137     else
00138         dt.date.year = 0;                                              // No year for alarm
00139 
00140 
00141     if(address == TIME)
00142         dt.date.weekday =  tmp[5] >> 5;
00143     else
00144         dt.date.weekday =  0;                                          // No weekday for alarm
00145 
00146     if(address == TIME) {
00147         tmp[0] = readByte(CENTURY_REG);
00148         dt.date.century  = ((tmp[0] & 0xF0) >> 4) * 10 + (tmp[0] & 0x0F);
00149         tmp[0] = readByte(YEAR_REG);
00150         dt.date.year  = ((tmp[0] & 0xF0)  >> 4) * 10 + (tmp[0] & 0x0F);
00151 
00152         if(year_bits > 0) {                                            // Midnight on new years eve?
00153             dt.date.year += 1;                                         // Increment the year
00154             writeByte(YEAR_REG, dt.date.year);                         // Save the new year value to NV Ram
00155             writeByte(5, dt.date.day & 0x3F);                          // Clear the year bits but preserve the date
00156         }
00157     }
00158    
00159     return dt;
00160 };
00161 
00162 void PCF8583rtc::FormatDateTime(char *dest, char *f)
00163 {
00164     int i;
00165     
00166     if(f != 0 && *f != 0) {   //If the format param is empty then do a default 'c'
00167         while(*f != 0) {        //expect null terminated string (we hope)
00168             switch(*f) {
00169                 case 'c':
00170                     break;
00171                 case 'd':
00172                     if(*(f+1) != 'd') {            //'d' - Day with no leading zero
00173                         dest += Bcd2Char(dest, dt.date.day, false);
00174                     } else {
00175                         f++;
00176                         if(*(f+1) != 'd')            //'dd' - Day with leading zero
00177                             dest += Bcd2Char(dest, dt.date.day, true);
00178                         else {
00179                             f++;
00180                             if(*(f+1) != 'd') {        //'ddd' - Short day name
00181                                 i = 0;
00182                                 while(ShortDayNames[dt.date.weekday][i] != 0)
00183                                     *dest++ = ShortDayNames[dt.date.weekday][i++];
00184                             } else {
00185                                 f++;
00186                                 i = 0;
00187                                 while(LongDayNames[dt.date.weekday][i] != 0)
00188                                     *dest++ = LongDayNames[dt.date.weekday][i++];
00189                             }
00190                         }
00191                     }
00192                     break;
00193                 case 'm':
00194                     if(*(f+1) != 'm') {            //'m' - Month with no leading zero
00195                         dest += Bcd2Char(dest, dt.date.month, false);
00196                     } else {
00197                         f++;
00198                         if(*(f+1) != 'm')            //'mm' - Month with leading zero
00199                             dest += Bcd2Char(dest, dt.date.month, true);
00200                         else {
00201                             f++;
00202                             if(*(f+1) != 'm') {        //'mmm' - Short month name
00203                                 i = 0;
00204                                 while(ShortMonthNames[dt.date.month - 1][i] != 0)
00205                                     *dest++ = ShortMonthNames[dt.date.month - 1][i++];
00206                             } else {
00207                                 f++;
00208                                 i = 0;
00209                                 while(LongMonthNames[dt.date.month - 1][i] != 0)
00210                                     *dest++ = LongMonthNames[dt.date.month - 1][i++];
00211                             }
00212                         }
00213                     }
00214                     break;
00215                 case 'y':
00216                     if(*(f+1) == 'y') {
00217                         f++;                                 //We have at least a 'yy'
00218                         if(*(f+1) == 'y' && *(f+2) == 'y') { //'yyyy' - 4 digit year
00219                             dest += Bcd2Char(dest, dt.date.century, true);
00220                             f += 2;
00221                         }
00222                         dest += Bcd2Char(dest, dt.date.year, true);
00223                     }
00224                     break;
00225                 case 'h':
00226                     if(*(f+1) != 'h') {            //'h' - Hour with no leading zero
00227                         dest += Bcd2Char(dest, dt.time.hours, false);
00228                     } else {
00229                         f++;
00230                         dest += Bcd2Char(dest, dt.time.hours, true);
00231                     }
00232                     break;
00233                 case 'n':
00234                     if(*(f+1) != 'n') {            //'m' - Minutes with no leading zero
00235                         dest += Bcd2Char(dest, dt.time.minutes, false);
00236                     } else {
00237                         f++;
00238                         dest += Bcd2Char(dest, dt.time.minutes, true);
00239                     }
00240                     break;
00241                 case 's':
00242                     if(*(f+1) != 's') {            //'s' - Seconds with no leading zero
00243                         dest += Bcd2Char(dest, dt.time.seconds, false);
00244                     } else {
00245                         f++;
00246                         dest += Bcd2Char(dest, dt.time.seconds, true);
00247                     }
00248                     break;
00249                 case 'z':
00250                     if(*(f+1) != 'z') {            //'z' - Hundredths with no leading zero
00251                         dest += Bcd2Char(dest, dt.time.hundreds, false);
00252                     } else {
00253                         f++;
00254                         dest += Bcd2Char(dest, dt.time.hundreds, true);
00255                     }
00256                     break;
00257                 case '/':
00258                     *dest++ = DateSeparator;
00259                     break;
00260                 case ':':
00261                     *dest++ = TimeSeparator;
00262                     break;
00263                 case 39 :
00264                     while(*++f != 0 && *f != 39) *dest++ = *f;
00265                     break;  //Ignore the first '
00266                 default:
00267                     *dest++ = *f;
00268                     break;            //Anything we don't recognise, return it
00269             }
00270             f++;
00271         }
00272     }
00273     *dest = 0;    //Null terminate
00274 };
00275 
00276 bool PCF8583rtc::WriteNVram(char address, char *value, char num)
00277 {
00278     char buf[252];
00279     
00280     buf[0] = address;
00281     memcpy(&buf[1], value, num);
00282     
00283     if((address < USER_REG) || (num == 0))   // dont allow overwriting first 2 bytes
00284         return false;
00285     
00286     _i2c->write(_I2cAddress, buf, num + 1);  // write the data
00287     _i2c->stop();
00288 
00289     return true;
00290 };
00291 
00292 bool PCF8583rtc::ReadNVram(char address, char * dest, char num)
00293 {
00294     char    buf[2];
00295     
00296     if((address < USER_REG) || (num == 0))     // dont allow overwriting first 2 user bytes
00297         return false;
00298       
00299     buf[0] = address;
00300     _i2c->write(_I2cAddress, buf, 1);          // set the rom address
00301     _i2c->read(_I2cAddress | 1, dest, num);    // read the data
00302     _i2c->stop();
00303     
00304     return true;
00305 };
00306 
00307 /*****************************************************************************/
00308 /************************** Private Functions ********************************/
00309 /*****************************************************************************/
00310 
00311 char PCF8583rtc::Bcd2Char(char *d, char val, char WantLeadZero)
00312 {
00313     char  n = 0;
00314 
00315     if(WantLeadZero == true || (val / 10) != 0) {
00316         *d++ = (val / 10) + 48;
00317         n++;
00318     }
00319     *d = (val % 10) + 48;
00320     return(n + 1);
00321 }
00322 
00323 //----------------------------------------------
00324 // This function converts an 8 bit binary value to a 1 byte BCD value.
00325 // The input range must be from 0 to 99.
00326 char PCF8583rtc::bin2bcd(char value)
00327 {
00328     int tmp = 0;
00329 
00330     while(1) {
00331         // Get the tens digit by doing multiple subtraction
00332         // of 10 from the binary value.
00333         if(value >= 10) {
00334             value -= 10;
00335             tmp += 0x10;
00336         } else {  // Get the ones digit by adding the remainder.
00337             tmp += value;
00338             break;
00339         }
00340     }
00341     return tmp;
00342 }
00343 
00344 void PCF8583rtc::configureControlReg(char control)
00345 {
00346     writeByte(0, control);
00347 }
00348 
00349 void PCF8583rtc::configureAlarmReg(char alarm)
00350 {
00351     writeByte(0x08, alarm);
00352 }
00353 
00354 void PCF8583rtc::writeByte(char address, char d)
00355 {
00356     char buf[2];
00357 
00358     buf[0] = address;
00359     buf[1] = d;
00360     _i2c->write(_I2cAddress, buf, 2);
00361     _i2c->stop();
00362 }
00363 
00364 char PCF8583rtc::readByte(char address)
00365 {
00366     char buf[2];
00367 
00368     buf[0] = address;
00369     _i2c->write(_I2cAddress, buf, 1);
00370     _i2c->read(_I2cAddress | 1, buf, 1);
00371     _i2c->stop();
00372 
00373     return buf[0];
00374 }
00375 
00376 void PCF8583rtc::pauseCounting()
00377 {
00378     char tmp;
00379     tmp = readByte(0);
00380     tmp = tmp | 0x80;
00381     writeByte(0, tmp);
00382 }
00383 
00384 void PCF8583rtc::enableCounting()
00385 {
00386     char tmp;
00387     tmp = readByte(0);
00388     tmp = tmp ^ 0x80;
00389     writeByte(0, tmp);
00390 }