A feature complete driver for the ISL1208 real time clock from Intersil.

Dependents:   ISL1208_HelloWorld

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ISL1208.cpp Source File

ISL1208.cpp

00001 /* ISL1208 Driver Library
00002  * Copyright (c) 2013 Neil Thiessen
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 #include "ISL1208.h"
00018 
00019 const int ISL1208::m_ADDR = (0x6F << 1);
00020 
00021 ISL1208::ISL1208(PinName sda, PinName scl, int hz) : m_I2C(sda, scl)
00022 {
00023     //Set the I2C bus frequency
00024     m_I2C.frequency(hz);
00025 }
00026 
00027 bool ISL1208::open()
00028 {
00029     //Probe for the ISL1208 using a Zero Length Transfer
00030     if (!m_I2C.write(m_ADDR, NULL, 0)) {
00031         //Read the current status register
00032         char sr = read8(REG_CTL_SR);
00033 
00034         //Disable auto reset for BAT and ALM bits
00035         sr &= ~(1 << 7);
00036 
00037         //Write the new status register
00038         write8(REG_CTL_SR, sr);
00039 
00040         //Return success
00041         return true;
00042     } else {
00043         //Return failure
00044         return false;
00045     }
00046 }
00047 
00048 time_t ISL1208::time()
00049 {
00050     //Setup a tm structure based on the RTC
00051     struct tm timeinfo;
00052     timeinfo.tm_sec = bcd2bin(read8(REG_RTC_SC));
00053     timeinfo.tm_min = bcd2bin(read8(REG_RTC_MN));
00054 
00055     //Make sure we get the proper hour regardless of the mode
00056     char hours = read8(REG_RTC_HR);
00057     if (hours & (1 << 7)) {
00058         //RTC is in 24-hour mode
00059         timeinfo.tm_hour = bcd2bin(hours & 0x3F);
00060     } else {
00061         //RTC is in 12-hour mode
00062         timeinfo.tm_hour = bcd2bin(hours & 0x1F);
00063 
00064         //Check for the PM flag
00065         if (hours & (1 << 5))
00066             timeinfo.tm_hour += 12;
00067     }
00068 
00069     //Continue reading the registers
00070     timeinfo.tm_mday = bcd2bin(read8(REG_RTC_DT));
00071     timeinfo.tm_mon = bcd2bin(read8(REG_RTC_MO)) - 1;
00072     timeinfo.tm_year = bcd2bin(read8(REG_RTC_YR)) + 100;
00073     timeinfo.tm_wday = bcd2bin(read8(REG_RTC_DW));
00074 
00075     //Return as a timestamp
00076     return mktime(&timeinfo);
00077 }
00078 
00079 void ISL1208::time(time_t t)
00080 {
00081     //Convert the time to a tm
00082     struct tm *timeinfo = localtime(&t);
00083 
00084     /* The clock has an 8 bit wide bcd-coded register (they never learn)
00085      * for the year. tm_year is an offset from 1900 and we are interested
00086      * in the 2000-2099 range, so any value less than 100 is invalid.
00087      */
00088     if (timeinfo->tm_year < 100)
00089         return;
00090 
00091     //Read the old SR register value
00092     char sr = read8(REG_CTL_SR);
00093 
00094     //Enable RTC writing
00095     write8(REG_CTL_SR, sr | (1 << 4));
00096 
00097     //Write the current time
00098     write8(REG_RTC_SC, bin2bcd(timeinfo->tm_sec));
00099     write8(REG_RTC_MN, bin2bcd(timeinfo->tm_min));
00100     write8(REG_RTC_HR, bin2bcd(timeinfo->tm_hour) | (1 << 7));    //24-hour mode
00101     write8(REG_RTC_DT, bin2bcd(timeinfo->tm_mday));
00102     write8(REG_RTC_MO, bin2bcd(timeinfo->tm_mon + 1));
00103     write8(REG_RTC_YR, bin2bcd(timeinfo->tm_year - 100));
00104     write8(REG_RTC_DW, bin2bcd(timeinfo->tm_wday & 7));
00105 
00106     //Disable RTC writing
00107     write8(REG_CTL_SR, sr);
00108 }
00109 
00110 bool ISL1208::powerFailed()
00111 {
00112     //Read the 8-bit register value
00113     char value = read8(REG_CTL_SR);
00114 
00115     //Return the status of the RTCF bit
00116     if (value & (1 << 0))
00117         return true;
00118     else
00119         return false;
00120 }
00121 
00122 bool ISL1208::batteryFlag()
00123 {
00124     //Read the 8-bit register value
00125     char value = read8(REG_CTL_SR);
00126 
00127     //Return the status of the BAT bit
00128     if (value & (1 << 1))
00129         return true;
00130     else
00131         return false;
00132 }
00133 
00134 void ISL1208::clearBatteryFlag()
00135 {
00136     //Read the current 8-bit register value
00137     char value = read8(REG_CTL_SR);
00138 
00139     //Clear the BAT bit
00140     value &= ~(1 << 1);
00141 
00142     //Write the value back out
00143     write8(REG_CTL_SR, value);
00144 }
00145 
00146 bool ISL1208::alarmFlag()
00147 {
00148     //Read the 8-bit register value
00149     char value = read8(REG_CTL_SR);
00150 
00151     //Return the status of the ALM bit
00152     if (value & (1 << 2))
00153         return true;
00154     else
00155         return false;
00156 }
00157 
00158 void ISL1208::clearAlarmFlag()
00159 {
00160     //Read the current 8-bit register value
00161     char value = read8(REG_CTL_SR);
00162 
00163     //Clear the ALM bit
00164     value &= ~(1 << 2);
00165 
00166     //Write the value back out
00167     write8(REG_CTL_SR, value);
00168 }
00169 
00170 ISL1208::OscillatorMode ISL1208::oscillatorMode()
00171 {
00172     //Read the 8-bit register value
00173     char value = read8(REG_CTL_SR);
00174 
00175     //Return the status of the XTOSCB bit
00176     if (value & (1 << 6))
00177         return OSCILLATOR_EXTERNAL;
00178     else
00179         return OSCILLATOR_CRYSTAL;
00180 }
00181 
00182 void ISL1208::oscillatorMode(OscillatorMode mode)
00183 {
00184     //Read the current 8-bit register value
00185     char value = read8(REG_CTL_SR);
00186 
00187     //Set or clear the XTOSCB bit
00188     if (mode == OSCILLATOR_EXTERNAL)
00189         value |= (1 << 6);
00190     else
00191         value &= ~(1 << 6);
00192 
00193     //Write the value back out
00194     write8(REG_CTL_SR, value);
00195 }
00196 
00197 ISL1208::OutputFrequency ISL1208::foutFrequency()
00198 {
00199     //Read the 8-bit register value
00200     char value = read8(REG_CTL_INT);
00201 
00202     //Return the lower nibble
00203     return (OutputFrequency)(value & 0x0F);
00204 }
00205 
00206 void ISL1208::foutFrequency(OutputFrequency freq)
00207 {
00208     //Read the current 8-bit register value
00209     char value = read8(REG_CTL_INT);
00210 
00211     //Clear the old frequency bits
00212     value &= 0xF0;
00213 
00214     //Set the new frequency bits
00215     value |= freq;
00216 
00217     //Write the value back out
00218     write8(REG_CTL_INT, value);
00219 }
00220 
00221 bool ISL1208::outputOnBattery()
00222 {
00223     //Read the 8-bit register value
00224     char value = read8(REG_CTL_INT);
00225 
00226     //Return the status of the FOBATB bit
00227     if (value & (1 << 4))
00228         return false;
00229     else
00230         return true;
00231 }
00232 
00233 void ISL1208::outputOnBattery(bool output)
00234 {
00235     //Read the current 8-bit register value
00236     char value = read8(REG_CTL_INT);
00237 
00238     //Set or clear the FOBATB bit
00239     if (output)
00240         value &= ~(1 << 4);
00241     else
00242         value |= (1 << 4);
00243 
00244     //Write the value back out
00245     write8(REG_CTL_INT, value);
00246 }
00247 
00248 ISL1208::PowerMode ISL1208::powerMode()
00249 {
00250     //Read the 8-bit register value
00251     char value = read8(REG_CTL_INT);
00252 
00253     //Return the status of the LPMODE bit
00254     if (value & (1 << 5))
00255         return POWER_LPMODE;
00256     else
00257         return POWER_NORMAL;
00258 }
00259 
00260 void ISL1208::powerMode(PowerMode mode)
00261 {
00262     //Read the current 8-bit register value
00263     char value = read8(REG_CTL_INT);
00264 
00265     //Set or clear the LPMODE bit
00266     if (mode == POWER_LPMODE)
00267         value |= (1 << 5);
00268     else
00269         value &= ~(1 << 5);
00270 
00271     //Write the value back out
00272     write8(REG_CTL_INT, value);
00273 }
00274 
00275 ISL1208::AlarmMode ISL1208::alarmMode()
00276 {
00277     //Read the 8-bit register value
00278     char value = read8(REG_CTL_INT);
00279 
00280     //Return the status of the ALME and IM bits
00281     if (value & (1 << 6)) {
00282         if (value & (1 << 7))
00283             return ALARM_INTERRUPT;
00284         else
00285             return ALARM_SINGLE;
00286     } else
00287         return ALARM_DISABLED;
00288 }
00289 
00290 void ISL1208::alarmMode(AlarmMode mode)
00291 {
00292     //Read the current 8-bit register value
00293     char value = read8(REG_CTL_INT);
00294 
00295     //Set or clear the ALME and IM bit
00296     if (mode != ALARM_DISABLED) {
00297         value |= (1 << 6);
00298         if (mode == ALARM_INTERRUPT)
00299             value |= (1 << 7);
00300         else
00301             value &= ~(1 << 7);
00302     } else
00303         value &= ~(1 << 6);
00304 
00305     //Write the value back out
00306     write8(REG_CTL_INT, value);
00307 }
00308 
00309 float ISL1208::analogTrim()
00310 {
00311     //Read the 8-bit register value
00312     char value = read8(REG_CTL_ATR);
00313 
00314     //Mask off the top 2 bits
00315     value &= 0x3F;
00316 
00317     //Invert bit 5
00318     value ^= 1 << 5;
00319 
00320     //Add an offset of 4.5pF (unit[atr] = 0.25pF)
00321     value += 2 * 9;
00322 
00323     //Return the analog trim in pF
00324     return value * 0.25;
00325 }
00326 
00327 void ISL1208::analogTrim(float trim)
00328 {
00329     //Range limit trim
00330     if (trim < 4.5)
00331         trim = 4.5;
00332     else if (trim > 20.25)
00333         trim = 20.25;
00334 
00335     //Convert the analog trim value to a 6-bit integer
00336     char value = (char)(trim / 0.25);
00337 
00338     //Remove the offset of 4.5pF (unit[atr] = 0.25pF)
00339     value -= 2 * 9;
00340 
00341     //Invert bit 5
00342     value ^= 1 << 5;
00343 
00344     //Read the current 8-bit register value
00345     char reg = read8(REG_CTL_ATR);
00346 
00347     //Clear the old ATR bits
00348     reg &= 0xC0;
00349 
00350     //Add the new ATR bits
00351     reg |= value;
00352 
00353     //Write the value back out
00354     write8(REG_CTL_ATR, reg);
00355 }
00356 
00357 ISL1208::BatteryModeATR ISL1208::batteryModeATR()
00358 {
00359     //Read the 8-bit register value
00360     char value = read8(REG_CTL_ATR);
00361 
00362     //Shift out the ATR bits
00363     value >>= 6;
00364 
00365     //Return the value as a BatteryModeATR enum
00366     return (BatteryModeATR)value;
00367 }
00368 
00369 void ISL1208::batteryModeATR(BatteryModeATR atr)
00370 {
00371     //Read the current 8-bit register value
00372     char value = read8(REG_CTL_ATR);
00373 
00374     //Clear the old battery mode ATR bits
00375     value &= 0x3F;
00376 
00377     //Add the new battery mode ATR bits
00378     value |= (atr << 6);
00379 
00380     //Write the value back out
00381     write8(REG_CTL_ATR, value);
00382 }
00383 
00384 ISL1208::DigitalTrim ISL1208::digitalTrim()
00385 {
00386     //Read the 8-bit register value
00387     char value = read8(REG_CTL_DTR);
00388 
00389     //Mask off the reserved bit
00390     value &= ~(1 << 7);
00391 
00392     //Return the value as a DigitalTrim enum
00393     return (DigitalTrim)value;
00394 }
00395 
00396 void ISL1208::digitalTrim(DigitalTrim dtr)
00397 {
00398     //Read the current 8-bit register value (to preserve the reserved bit)
00399     char value = read8(REG_CTL_DTR);
00400 
00401     //Clear the old DTR bits
00402     value &= 0xF8;
00403 
00404     //Add the new DTR bits
00405     value |= dtr;
00406 
00407     //Write the value back out
00408     write8(REG_CTL_DTR, value);
00409 }
00410 
00411 time_t ISL1208::alarmTime()
00412 {
00413     //Setup a tm structure based on the RTC
00414     struct tm timeinfo;
00415 
00416     //MSB of each alarm register is an enable bit
00417     timeinfo.tm_sec = bcd2bin(read8(REG_ALM_SCA) & 0x7F);
00418     timeinfo.tm_min = bcd2bin(read8(REG_ALM_MNA) & 0x7F);
00419     timeinfo.tm_hour = bcd2bin(read8(REG_ALM_HRA) & 0x3F);
00420     timeinfo.tm_mday = bcd2bin(read8(REG_ALM_DTA) & 0x3F);
00421     timeinfo.tm_mon = bcd2bin(read8(REG_ALM_MOA) & 0x1F) - 1;
00422     timeinfo.tm_wday = bcd2bin(read8(REG_ALM_DWA) & 0x03);
00423 
00424     //The alarm doesn't store the year, so get it from the RTC section
00425     timeinfo.tm_year = bcd2bin(read8(REG_RTC_YR)) + 100;
00426 
00427     //Return as a timestamp
00428     return mktime(&timeinfo);
00429 }
00430 
00431 void ISL1208::alarmTime(time_t t, bool sc, bool mn, bool hr, bool dt, bool mo, bool dw)
00432 {
00433     //Convert the time to a tm
00434     struct tm *timeinfo = localtime(&t);
00435 
00436     //Write the new alarm time components (if enabled)
00437     if (sc)
00438         write8(REG_ALM_SCA, bin2bcd(timeinfo->tm_sec) | 0x80);
00439     else
00440         write8(REG_ALM_SCA, 0x0);
00441     if (mn)
00442         write8(REG_ALM_MNA, bin2bcd(timeinfo->tm_min) | 0x80);
00443     else
00444         write8(REG_ALM_MNA, 0x0);
00445     if (hr)
00446         write8(REG_ALM_HRA, bin2bcd(timeinfo->tm_hour) | 0x80);
00447     else
00448         write8(REG_ALM_HRA, 0x0);
00449     if (hr)
00450         write8(REG_ALM_DTA, bin2bcd(timeinfo->tm_mday) | 0x80);
00451     else
00452         write8(REG_ALM_DTA, 0x0);
00453     if (mo)
00454         write8(REG_ALM_MOA, bin2bcd(timeinfo->tm_mon + 1) | 0x80);
00455     else
00456         write8(REG_ALM_MOA, 0x0);
00457     if (dw)
00458         write8(REG_ALM_DWA, bin2bcd(timeinfo->tm_wday & 7) | 0x80);
00459     else
00460         write8(REG_ALM_DWA, 0x0);
00461 }
00462 
00463 unsigned short ISL1208::sram()
00464 {
00465     //Return the complete contents of the SRAM
00466     return read16(REG_USR_USR1);
00467 }
00468 
00469 void ISL1208::sram(unsigned short data)
00470 {
00471     //Write the complete contents of the SRAM
00472     write16(REG_USR_USR1, data);
00473 }
00474 
00475 char ISL1208::read8(char reg)
00476 {
00477     //Select the register
00478     m_I2C.write(m_ADDR, &reg, 1, true);
00479 
00480     //Read the 8-bit register
00481     m_I2C.read(m_ADDR, &reg, 1);
00482 
00483     //Return the byte
00484     return reg;
00485 }
00486 
00487 void ISL1208::write8(char reg, char data)
00488 {
00489     //Create a temporary buffer
00490     char buff[2];
00491 
00492     //Load the register address and 8-bit data
00493     buff[0] = reg;
00494     buff[1] = data;
00495 
00496     //Write the data
00497     m_I2C.write(m_ADDR, buff, 2);
00498 }
00499 
00500 unsigned short ISL1208::read16(char reg)
00501 {
00502     //Create a temporary buffer
00503     char buff[2];
00504 
00505     //Select the register
00506     m_I2C.write(m_ADDR, &reg, 1, true);
00507 
00508     //Read the 16-bit register
00509     m_I2C.read(m_ADDR, buff, 2);
00510 
00511     //Return the combined 16-bit value
00512     return (buff[0] << 8) | buff[1];
00513 }
00514 
00515 void ISL1208::write16(char reg, unsigned short data)
00516 {
00517     //Create a temporary buffer
00518     char buff[3];
00519 
00520     //Load the register address and 16-bit data
00521     buff[0] = reg;
00522     buff[1] = data >> 8;
00523     buff[2] = data;
00524 
00525     //Write the data
00526     m_I2C.write(m_ADDR, buff, 3);
00527 }
00528 
00529 unsigned int ISL1208::bcd2bin(unsigned char val)
00530 {
00531     return (val & 0x0F) + (val >> 4) * 10;
00532 }
00533 
00534 char ISL1208::bin2bcd(unsigned int val)
00535 {
00536     return ((val / 10) << 4) + val % 10;
00537 }