1 /*
2 * Author: Jon Trulson <jtrulson@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
DS1307(int bus)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
writeBytes(uint8_t reg,uint8_t * buffer,int len)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
readBytes(uint8_t reg,uint8_t * buffer,int len)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
loadTime()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
setTime()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
enableClock()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
disableClock()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
decToBcd(unsigned int val)203 uint8_t DS1307::decToBcd(unsigned int val)
204 {
205 return ( (val/10*16) + (val%10) );
206 }
207
208 // Convert BCD to decimal
bcdToDec(uint8_t val)209 unsigned int DS1307::bcdToDec(uint8_t val)
210 {
211 return ( (val/16*10) + (val%16) );
212 }
213
214