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.
Dependencies: PCF8583_rtc mbed
PCF8583_rtc.cpp
- Committer:
- dennyem
- Date:
- 2014-02-13
- Revision:
- 1:a8b9fb95696b
- Parent:
- 0:f09cf90def53
File content as of revision 1:a8b9fb95696b:
/*********************************************************************************
* An mbed class to control the PCF8583 Real time Clock/Calender
* Copyright (c) 2014 Dennis (Denny) Smith - dennyem
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* 13.01.14 initial design for PIC devices using PICC compiler
* 21.01.14 ported to mBed LPC1768 in 'C'
* 09.02.14 converted to C++ and ported to mBed LPC812
*
* TODO
* WriteNVram function is still to be tested
* ReadNVram function is still to be tested
* FormatDateTime needs am/pm, 12/24 hour parsing
* Alarm functions not yet implemented
*/
#include <mbed.h>
#include <PCF8583_rtc.h>
//-----------------------------------------------------------------------------
// constructor -- accepts an I2c object to use for connection with the rtc
PCF8583rtc::PCF8583rtc(I2C *i2c, char I2cAddress)
{
_i2c = i2c;
_I2cAddress = I2cAddress;
ShortDateFormat = "d,m,yy";
LongDateFormat = "dddd dd mmmm yyyy";
ShortTimeFormat = "d:m:yy";
LongTimeFormat = "dd:nn:yyyy";
DateSeparator = '/';
TimeSeparator = ':';
ShortDayNames[0] = "Mon";
ShortDayNames[1] = "Tue";
ShortDayNames[2] = "Wed";
ShortDayNames[3] = "Thu";
ShortDayNames[4] = "Fri";
ShortDayNames[5] = "Sat";
ShortDayNames[6] = "Sun";
LongDayNames[0] = "Monday";
LongDayNames[1] = "Tuesday";
LongDayNames[2] = "Wednesday";
LongDayNames[3] = "Thursday";
LongDayNames[4] = "Friday";
LongDayNames[5] = "Saturday";
LongDayNames[6] = "Sunday";
ShortMonthNames[0] = "Jan";
ShortMonthNames[1] = "Feb";
ShortMonthNames[2] = "Mar";
ShortMonthNames[3] = "Apr";
ShortMonthNames[4] = "May";
ShortMonthNames[5] = "Jun";
ShortMonthNames[6] = "Jul";
ShortMonthNames[7] = "Aug";
ShortMonthNames[8] = "Sep";
ShortMonthNames[9] = "Oct";
ShortMonthNames[10] = "Nov";
ShortMonthNames[11] = "Dec";
LongMonthNames[0] = "January";
LongMonthNames[1] = "February";
LongMonthNames[2] = "March";
LongMonthNames[3] = "April";
LongMonthNames[4] = "May";
LongMonthNames[5] = "June";
LongMonthNames[6] = "July";
LongMonthNames[7] = "August";
LongMonthNames[8] = "September";
LongMonthNames[9] = "October";
LongMonthNames[10] = "November";
LongMonthNames[11] = "December";
};
void PCF8583rtc::write(const char address, struct DateTime_t dti)
{
char tmp[8];
pauseCounting(); //Must stop counting before initialising Date/time
tmp[0] = address; // Address is 1 for Time or 10 for Alarm
//Values must be in BCD form
tmp[1] = dti.time.hundreds; // Hundredths of a second
tmp[2] = dti.time.seconds; // Seconds
tmp[3] = dti.time.minutes; // Minutes
tmp[4] = dti.time.hours; // Hours
tmp[5] = dti.date.day & 0x3F; // Always set the 3 year bits to 0
if(address == TIME)
tmp[6] = (((dti.date.weekday & 7) << 5 ) | dti.date.month); // Weekday/month
else
tmp[6] = dti.date.month & 0x1f; // No Weekday for alarm
_i2c->write(_I2cAddress, tmp, 7); // Address PCF8583, see PCF8583 datasheet
if(address == TIME) {
writeByte(CENTURY_REG, dti.date.century); // Store the full 4 digit year in NV Ram
writeByte(YEAR_REG, dti.date.year);
};
enableCounting();
};
//--------------------- Reads time and date information from RTC (PCF8583)
struct DateTime_t PCF8583rtc::read(const char address)
{
char tmp[8];
char year_bits = 0; // To test for year change
tmp[0] = address;
_i2c->write(_I2cAddress, tmp, 1); // Address PCF8583, see PCF8583 datasheet
_i2c->read(_I2cAddress | 1, tmp, 6); // Address PCF8583 for reading R/W=1
dt.time.hundreds = tmp[0];
dt.time.seconds = tmp[1];
dt.time.minutes = tmp[2];
dt.time.hours = tmp[3] & 0x3F;
dt.time.fmt_hours = tmp[3] & 0x80; // 12/24 hour format
dt.time.am_pm_flag = tmp[3] & 0x40; // Am/Pm flag
dt.date.day = tmp[4] & 0x3F; // Day of the Month
dt.date.month = tmp[5] & 0x1F;
if(address == TIME)
year_bits = (tmp[4] & 0xC0) >> 6;
else
dt.date.year = 0; // No year for alarm
if(address == TIME)
dt.date.weekday = tmp[5] >> 5;
else
dt.date.weekday = 0; // No weekday for alarm
if(address == TIME) {
tmp[0] = readByte(CENTURY_REG);
dt.date.century = ((tmp[0] & 0xF0) >> 4) * 10 + (tmp[0] & 0x0F);
tmp[0] = readByte(YEAR_REG);
dt.date.year = ((tmp[0] & 0xF0) >> 4) * 10 + (tmp[0] & 0x0F);
if(year_bits > 0) { // Midnight on new years eve?
dt.date.year += 1; // Increment the year
writeByte(YEAR_REG, dt.date.year); // Save the new year value to NV Ram
writeByte(5, dt.date.day & 0x3F); // Clear the year bits but preserve the date
}
}
return dt;
};
void PCF8583rtc::FormatDateTime(char *dest, char *f)
{
int i;
if(f != 0 && *f != 0) { //If the format param is empty then do a default 'c'
while(*f != 0) { //expect null terminated string (we hope)
switch(*f) {
case 'c':
break;
case 'd':
if(*(f+1) != 'd') { //'d' - Day with no leading zero
dest += Bcd2Char(dest, dt.date.day, false);
} else {
f++;
if(*(f+1) != 'd') //'dd' - Day with leading zero
dest += Bcd2Char(dest, dt.date.day, true);
else {
f++;
if(*(f+1) != 'd') { //'ddd' - Short day name
i = 0;
while(ShortDayNames[dt.date.weekday][i] != 0)
*dest++ = ShortDayNames[dt.date.weekday][i++];
} else {
f++;
i = 0;
while(LongDayNames[dt.date.weekday][i] != 0)
*dest++ = LongDayNames[dt.date.weekday][i++];
}
}
}
break;
case 'm':
if(*(f+1) != 'm') { //'m' - Month with no leading zero
dest += Bcd2Char(dest, dt.date.month, false);
} else {
f++;
if(*(f+1) != 'm') //'mm' - Month with leading zero
dest += Bcd2Char(dest, dt.date.month, true);
else {
f++;
if(*(f+1) != 'm') { //'mmm' - Short month name
i = 0;
while(ShortMonthNames[dt.date.month - 1][i] != 0)
*dest++ = ShortMonthNames[dt.date.month - 1][i++];
} else {
f++;
i = 0;
while(LongMonthNames[dt.date.month - 1][i] != 0)
*dest++ = LongMonthNames[dt.date.month - 1][i++];
}
}
}
break;
case 'y':
if(*(f+1) == 'y') {
f++; //We have at least a 'yy'
if(*(f+1) == 'y' && *(f+2) == 'y') { //'yyyy' - 4 digit year
dest += Bcd2Char(dest, dt.date.century, true);
f += 2;
}
dest += Bcd2Char(dest, dt.date.year, true);
}
break;
case 'h':
if(*(f+1) != 'h') { //'h' - Hour with no leading zero
dest += Bcd2Char(dest, dt.time.hours, false);
} else {
f++;
dest += Bcd2Char(dest, dt.time.hours, true);
}
break;
case 'n':
if(*(f+1) != 'n') { //'m' - Minutes with no leading zero
dest += Bcd2Char(dest, dt.time.minutes, false);
} else {
f++;
dest += Bcd2Char(dest, dt.time.minutes, true);
}
break;
case 's':
if(*(f+1) != 's') { //'s' - Seconds with no leading zero
dest += Bcd2Char(dest, dt.time.seconds, false);
} else {
f++;
dest += Bcd2Char(dest, dt.time.seconds, true);
}
break;
case 'z':
if(*(f+1) != 'z') { //'z' - Hundredths with no leading zero
dest += Bcd2Char(dest, dt.time.hundreds, false);
} else {
f++;
dest += Bcd2Char(dest, dt.time.hundreds, true);
}
break;
case '/':
*dest++ = DateSeparator;
break;
case ':':
*dest++ = TimeSeparator;
break;
case 39 :
while(*++f != 0 && *f != 39) *dest++ = *f;
break; //Ignore the first '
default:
*dest++ = *f;
break; //Anything we don't recognise, return it
}
f++;
}
}
*dest = 0; //Null terminate
};
bool PCF8583rtc::WriteNVram(char address, char *value, char num)
{
if((address < USER_REG) || (num == 0)) // dont allow overwriting first 2 user bytes
return false;
_i2c->write(_I2cAddress, &address, 1); // set the rom address
return _i2c->write(_I2cAddress, value, num); // write the data
};
bool PCF8583rtc::ReadNVram(char address, char * dest, char num)
{
if((address < USER_REG) || (num == 0)) // dont allow overwriting first 2 user bytes
return false;
_i2c->write(_I2cAddress, &address, 1); // set the rom address
_i2c->read(_I2cAddress | 1, dest, num); // read the data
return true;
};
/*****************************************************************************/
/************************** Private Functions ********************************/
/*****************************************************************************/
char PCF8583rtc::Bcd2Char(char *d, char val, char WantLeadZero)
{
char n = 0;
if(WantLeadZero == true || (val / 10) != 0) {
*d++ = (val / 10) + 48;
n++;
}
*d = (val % 10) + 48;
return(n + 1);
}
//----------------------------------------------
// This function converts an 8 bit binary value to a 1 byte BCD value.
// The input range must be from 0 to 99.
char PCF8583rtc::bin2bcd(char value)
{
int tmp = 0;
while(1) {
// Get the tens digit by doing multiple subtraction
// of 10 from the binary value.
if(value >= 10) {
value -= 10;
tmp += 0x10;
} else { // Get the ones digit by adding the remainder.
tmp += value;
break;
}
}
return tmp;
}
void PCF8583rtc::writeByte(char address, char d)
{
char buf[2];
buf[0] = address;
buf[1] = d;
_i2c->write(_I2cAddress, buf, 2); // Address PCF8583, see PCF8583 datasheet
}
void PCF8583rtc::configureControlReg(char control)
{
writeByte(0, control);
}
void PCF8583rtc::configureAlarmReg(char alarm)
{
writeByte(0x08, alarm);
}
char PCF8583rtc::readByte(char address)
{
char buf[2];
buf[0] = address;
_i2c->write(_I2cAddress, buf, 1);
_i2c->read(_I2cAddress | 1, buf, 1);
return buf[0];
}
void PCF8583rtc::pauseCounting()
{
char tmp;
tmp = readByte(0x00);
tmp = tmp | 0x80;
writeByte(0x00, tmp);
}
void PCF8583rtc::enableCounting()
{
char tmp;
tmp = readByte(0x00);
tmp = tmp ^ 0x80;
writeByte(0x00, tmp);
}