Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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 }
Generated on Mon Jul 18 2022 17:25:31 by
1.7.2