├── examples └── read_write_time │ └── read_write_time.pde ├── PCF8583.h └── PCF8583.cpp /examples/read_write_time/read_write_time.pde: -------------------------------------------------------------------------------- 1 | #include // necessary, or the application won't build properly 2 | #include 3 | #include 4 | /***************************************************************************** 5 | * read/write serial interface to PCF8583 RTC via I2C interface 6 | * 7 | * Arduino analog input 5 - I2C SCL (PCF8583 pin 6) 8 | * Arduino analog input 4 - I2C SDA (PCF8583 pin 5) 9 | * 10 | * You can set the type by sending it YYMMddhhmmss; 11 | * the semicolon on the end tells it you're done... 12 | * 13 | ******************************************************************************/ 14 | 15 | int correct_address = 0; 16 | PCF8583 p (0xA0); 17 | void setup(void){ 18 | Serial.begin(9600); 19 | Serial.print("booting..."); 20 | Serial.println(" done"); 21 | 22 | } 23 | 24 | 25 | 26 | void loop(void){ 27 | if(Serial.available() > 0){ 28 | p.year= (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)) + 2000; 29 | p.month = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); 30 | p.day = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); 31 | p.hour = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); 32 | p.minute = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); 33 | p.second = (byte) ((Serial.read() - 48) * 10 + (Serial.read() - 48)); // Use of (byte) type casting and ascii math to achieve result. 34 | 35 | if(Serial.read() == ';'){ 36 | Serial.println("setting date"); 37 | p.set_time(); 38 | } 39 | } 40 | 41 | 42 | p.get_time(); 43 | char time[50]; 44 | sprintf(time, "%02d/%02d/%02d %02d:%02d:%02d", 45 | p.year, p.month, p.day, p.hour, p.minute, p.second); 46 | Serial.println(time); 47 | 48 | delay(3000); 49 | } 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /PCF8583.h: -------------------------------------------------------------------------------- 1 | /* 2 | Implements a simple interface to the time function of the PCF8583 RTC chip 3 | 4 | Works around the device's limited year storage by keeping the year in the 5 | first two bytes of user accessible storage 6 | 7 | Assumes device is attached in the standard location - Analog pins 4 and 5 8 | Device address is the 8 bit address (as in the device datasheet - normally A0) 9 | 10 | Copyright (c) 2009, Erik DeBill 11 | 12 | 13 | This library is free software; you can redistribute it and/or 14 | modify it under the terms of the GNU Lesser General Public 15 | License as published by the Free Software Foundation; either 16 | version 2.1 of the License, or (at your option) any later version. 17 | 18 | This library is distributed in the hope that it will be useful, 19 | but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 21 | Lesser General Public License for more details. 22 | 23 | You should have received a copy of the GNU Lesser General Public 24 | License along with this library; if not, write to the Free Software 25 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 26 | */ 27 | 28 | 29 | /* 30 | 31 | Usage: 32 | PCF8583 pcf(0xA0); 33 | pcf.get_time(); 34 | 35 | Serial.print("year: "); 36 | Serial.println(pcf.year); 37 | 38 | 39 | pcf.hour = 14; 40 | pcf.minute = 30 41 | pcf.second = 0 42 | pcf.year = 2009 43 | pcf.month = 9 44 | pcf.day = 12 45 | pcf.set_time(); 46 | 47 | 48 | */ 49 | 50 | #ifndef PCF8583_H 51 | #define PCF8583_H 52 | 53 | #include 54 | #include <../Wire/Wire.h> 55 | 56 | class PCF8583 { 57 | int address; 58 | int dow; 59 | public: 60 | int second; 61 | int minute; 62 | int hour; 63 | int day; 64 | int month; 65 | int year; 66 | int year_base; 67 | 68 | int alarm_milisec; 69 | int alarm_second; 70 | int alarm_minute; 71 | int alarm_hour; 72 | int alarm_day; 73 | 74 | PCF8583(int device_address); 75 | void init (); 76 | 77 | void get_time(); 78 | void set_time(); 79 | void get_alarm(); 80 | int get_day_of_week() const { 81 | return dow; 82 | } 83 | 84 | void set_daily_alarm(); 85 | int bcd_to_byte(byte bcd); 86 | byte int_to_bcd(int in); 87 | }; 88 | 89 | 90 | #endif //PCF8583_H 91 | -------------------------------------------------------------------------------- /PCF8583.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Implements a simple interface to the time function of the PCF8583 RTC chip 3 | 4 | Works around the device's limited year storage by keeping the year in the 5 | first two bytes of user accessible storage 6 | 7 | Assumes device is attached in the standard location - Analog pins 4 and 5 8 | Device address is the 8 bit address (as in the device datasheet - normally A0) 9 | 10 | Copyright (c) 2009, Erik DeBill 11 | 12 | 13 | This library is free software; you can redistribute it and/or 14 | modify it under the terms of the GNU Lesser General Public 15 | License as published by the Free Software Foundation; either 16 | version 2.1 of the License, or (at your option) any later version. 17 | 18 | This library is distributed in the hope that it will be useful, 19 | but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 21 | Lesser General Public License for more details. 22 | 23 | You should have received a copy of the GNU Lesser General Public 24 | License along with this library; if not, write to the Free Software 25 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 26 | */ 27 | 28 | 29 | #include 30 | #include 31 | #include "PCF8583.h" 32 | 33 | namespace { 34 | bool IsLeapYear(int year) { 35 | return !(year % 400) || ((year % 100) && !(year % 4)); 36 | } 37 | 38 | byte DayOfWeek(const PCF8583 &now) { 39 | static char PROGMEM MonthTable[24] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5, -1, 2, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5}; 40 | byte y = now.year % 100, c = 6 - 2 * ((now.year / 100) % 4); 41 | return (now.day + pgm_read_byte_near(MonthTable + IsLeapYear(now.year) * 12 + now.month - 1) + y + (y / 4) + c) % 7; 42 | } 43 | } 44 | 45 | // provide device address as a full 8 bit address (like the datasheet) 46 | PCF8583::PCF8583(int device_address) { 47 | address = device_address >> 1; // convert to 7 bit so Wire doesn't choke 48 | Wire.begin(); 49 | } 50 | 51 | // initialization 52 | void PCF8583::init() 53 | { 54 | 55 | Wire.beginTransmission(address); 56 | Wire.write(0x00); 57 | Wire.write(0x04); // Set alarm on int\ will turn to vcc 58 | Wire.endTransmission(); 59 | 60 | } 61 | 62 | void PCF8583::get_time(){ 63 | 64 | Wire.beginTransmission(address); 65 | Wire.write(0xC0); // stop counting, don't mask 66 | Wire.endTransmission(); 67 | 68 | Wire.beginTransmission(address); 69 | Wire.write(0x02); 70 | Wire.endTransmission(); 71 | Wire.requestFrom(address, 5); 72 | 73 | second = bcd_to_byte(Wire.read()); 74 | minute = bcd_to_byte(Wire.read()); 75 | hour = bcd_to_byte(Wire.read()); 76 | byte incoming = Wire.read(); // year/date counter 77 | day = bcd_to_byte(incoming & 0x3f); 78 | year = (int)((incoming >> 6) & 0x03); // it will only hold 4 years... 79 | incoming = Wire.read(); 80 | month = bcd_to_byte(incoming & 0x1f); 81 | dow = incoming >> 5; 82 | 83 | // but that's not all - we need to find out what the base year is 84 | // so we can add the 2 bits we got above and find the real year 85 | Wire.beginTransmission(address); 86 | Wire.write(0x10); 87 | Wire.endTransmission(); 88 | Wire.requestFrom(address, 2); 89 | year_base = 0; 90 | year_base = Wire.read(); 91 | year_base = year_base << 8; 92 | year_base = year_base | Wire.read(); 93 | year = year + year_base; 94 | } 95 | 96 | 97 | void PCF8583::set_time() 98 | { 99 | 100 | if (!IsLeapYear(year) && 2 == month && 29 == day) { 101 | month = 3; 102 | day = 1; 103 | } 104 | 105 | // Attempt to find the previous leap year 106 | year_base = year - year % 4; 107 | if (!IsLeapYear(year_base)) { 108 | // Not a leap year (new century), make sure the calendar won't use a 29 days February. 109 | year_base = year - 1; 110 | } 111 | 112 | dow = DayOfWeek(*this); 113 | 114 | Wire.beginTransmission(address); 115 | Wire.write(0xC0); // stop counting, don't mask 116 | Wire.endTransmission(); 117 | 118 | Wire.beginTransmission(address); 119 | Wire.write(0x02); 120 | Wire.write(int_to_bcd(second)); 121 | Wire.write(int_to_bcd(minute)); 122 | Wire.write(int_to_bcd(hour)); 123 | Wire.write(((byte)(year - year_base) << 6) | int_to_bcd(day)); 124 | Wire.write((dow << 5) | (int_to_bcd(month) & 0x1f)); 125 | Wire.endTransmission(); 126 | 127 | Wire.beginTransmission(address); 128 | Wire.write(0x10); 129 | Wire.write(year_base >> 8); 130 | Wire.write(year_base & 0x00ff); 131 | Wire.endTransmission(); 132 | 133 | init(); // re set the control/status register to 0x04 134 | 135 | } 136 | 137 | //Get the alarm at 0x09 adress 138 | void PCF8583::get_alarm() 139 | { 140 | Wire.beginTransmission(address); 141 | Wire.write(0x0A); // Set the register pointer to (0x0A) 142 | Wire.endTransmission(); 143 | 144 | Wire.requestFrom(address, 4); // Read 4 values 145 | 146 | alarm_second = bcd_to_byte(Wire.read()); 147 | alarm_minute = bcd_to_byte(Wire.read()); 148 | alarm_hour = bcd_to_byte(Wire.read()); 149 | 150 | Wire.beginTransmission(address); 151 | Wire.write(0x0E); 152 | Wire.endTransmission(); 153 | 154 | Wire.requestFrom(address, 1); // Read weekday value 155 | 156 | alarm_day = bcd_to_byte(Wire.read()); 157 | } 158 | 159 | //Set a daily alarm 160 | void PCF8583::set_daily_alarm() 161 | { 162 | Wire.beginTransmission(address); 163 | Wire.write(0x08); 164 | Wire.write(0x90); // daily alarm set 165 | Wire.endTransmission(); 166 | 167 | Wire.beginTransmission(address); 168 | Wire.write(0x09); // Set the register pointer to (0x09) 169 | Wire.write(0x00); // Set 00 at milisec 170 | Wire.write(int_to_bcd(alarm_second)); 171 | Wire.write(int_to_bcd(alarm_minute)); 172 | Wire.write(int_to_bcd(alarm_hour)); 173 | Wire.write(0x00); // Set 00 at day 174 | Wire.endTransmission(); 175 | } 176 | 177 | int PCF8583::bcd_to_byte(byte bcd){ 178 | return ((bcd >> 4) * 10) + (bcd & 0x0f); 179 | } 180 | 181 | byte PCF8583::int_to_bcd(int in){ 182 | return ((in / 10) << 4) + (in % 10); 183 | } 184 | 185 | --------------------------------------------------------------------------------