1 /* 2 * Author: Jon Trulson <jtrulson (at) ics.com> 3 * Copyright (c) 2014 Intel Corporation. 4 * 5 * Adapted from Seeed Studio library: 6 * https://github.com/Seeed-Studio/RTC_DS1307 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining 9 * a copy of this software and associated documentation files (the 10 * "Software"), to deal in the Software without restriction, including 11 * without limitation the rights to use, copy, modify, merge, publish, 12 * distribute, sublicense, and/or sell copies of the Software, and to 13 * permit persons to whom the Software is furnished to do so, subject to 14 * the following conditions: 15 * 16 * The above copyright notice and this permission notice shall be 17 * included in all copies or substantial portions of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 */ 27 28 #include <iostream> 29 #include <string> 30 #include <stdexcept> 31 32 #include "ds1307.h" 33 34 using namespace upm; 35 using namespace std; 36 37 38 DS1307::DS1307(int bus) : m_i2c(bus) 39 { 40 // setup our i2c link 41 mraa::Result ret = m_i2c.address(DS1307_I2C_ADDR); 42 if (ret != mraa::SUCCESS){ 43 throw std::invalid_argument(std::string(__FUNCTION__) + 44 ": i2c.address() failed"); 45 return; 46 } 47 } 48 49 mraa::Result DS1307::writeBytes(uint8_t reg, uint8_t *buffer, int len) 50 { 51 if (!len || !buffer) 52 return mraa::ERROR_INVALID_PARAMETER; 53 54 // create a buffer 1 byte larger than the supplied buffer, 55 // store the register in the first byte 56 uint8_t buf2[len + 1]; 57 58 buf2[0] = reg; 59 60 // copy in the buffer after the reg byte 61 for (int i=1; i<(len + 1); i++) 62 buf2[i] = buffer[i-1]; 63 64 mraa::Result ret = m_i2c.address(DS1307_I2C_ADDR); 65 if (ret != mraa::SUCCESS){ 66 throw std::invalid_argument(std::string(__FUNCTION__) + 67 ": i2c.address() failed"); 68 return ret; 69 } 70 71 return m_i2c.write(buf2, len + 1); 72 } 73 74 int DS1307::readBytes(uint8_t reg, uint8_t *buffer, int len) 75 { 76 if (!len || !buffer) 77 return 0; 78 79 mraa::Result ret = m_i2c.address(DS1307_I2C_ADDR); 80 if (ret != mraa::SUCCESS){ 81 throw std::invalid_argument(std::string(__FUNCTION__) + 82 ": i2c.address() failed"); 83 return 0; 84 } 85 m_i2c.writeByte(reg); 86 87 return m_i2c.read(buffer, len); 88 } 89 90 bool DS1307::loadTime() 91 { 92 // read the first 7 registers 93 uint8_t buffer[7]; 94 int bytesRead = readBytes(0, buffer, 7); 95 96 if (bytesRead != 7) 97 { 98 // problem 99 throw std::runtime_error(std::string(__FUNCTION__) + 100 ": failed to read expected 7 bytes from device"); 101 return false; 102 } 103 104 // We need to mask some control bits off of some of these values 105 // and convert the result to decimal from BCD. We also need to account 106 // for format (AM/PM or 24hr), and if AM/PM, whether PM should be set. 107 108 // first bit here is the oscillator enable/disable bit 109 seconds = bcdToDec(buffer[0] & 0x7f); 110 minutes = bcdToDec(buffer[1]); 111 112 // check AM/PM or 24hr mode 113 if (buffer[2] & 0x40) 114 { 115 // We are in AM/PM mode 116 hours = bcdToDec(buffer[2] & 0x1f); 117 amPmMode = true; 118 pm = (buffer[2] & 0x20) ? true : false; 119 } 120 else 121 { 122 // 24hr mode 123 hours = bcdToDec(buffer[2] & 0x3f); 124 amPmMode = false; 125 pm = false; 126 } 127 128 dayOfWeek = bcdToDec(buffer[3]); 129 dayOfMonth = bcdToDec(buffer[4]); 130 month = bcdToDec(buffer[5]); 131 year = bcdToDec(buffer[6]); 132 133 return true; 134 } 135 136 bool DS1307::setTime() 137 { 138 uint8_t buffer[7]; 139 140 // seconds 141 // we need to read in seconds first to preserve the osc enable bit 142 uint8_t tmpbuf; 143 144 readBytes(0, &tmpbuf, 1); 145 buffer[0] = decToBcd(seconds) | (tmpbuf & 0x80); 146 147 // minutes 148 buffer[1] = decToBcd(minutes); 149 150 // hours 151 if (amPmMode) 152 { 153 buffer[2] = decToBcd(hours) | 0x40; 154 if (pm) 155 buffer[2] |= 0x20; 156 } 157 else 158 buffer[2] = decToBcd(hours); 159 160 // day of week 161 buffer[3] = decToBcd(dayOfWeek); 162 163 // day of month 164 buffer[4] = decToBcd(dayOfMonth); 165 166 // month 167 buffer[5] = decToBcd(month); 168 169 // year 170 buffer[6] = decToBcd(year); 171 172 return writeBytes(0, buffer, 7); 173 } 174 175 mraa::Result DS1307::enableClock() 176 { 177 // the oscillator enable bit is the high bit of reg 0 178 // so read it, clear it, and write it back. 179 180 uint8_t buf; 181 readBytes(0, &buf, 1); 182 183 buf &= ~0x80; 184 185 return writeBytes(0, &buf, 1); 186 } 187 188 mraa::Result DS1307::disableClock() 189 { 190 // the oscillator enable bit is the high bit of reg 0 191 // so read it, set it, and write it back. 192 193 uint8_t buf; 194 readBytes(0, &buf, 1); 195 196 buf |= 0x80; 197 198 return writeBytes(0, &buf, 1); 199 } 200 201 202 // Convert decimal to BCD 203 uint8_t DS1307::decToBcd(unsigned int val) 204 { 205 return ( (val/10*16) + (val%10) ); 206 } 207 208 // Convert BCD to decimal 209 unsigned int DS1307::bcdToDec(uint8_t val) 210 { 211 return ( (val/16*10) + (val%16) ); 212 } 213 214