1////////////////////////////////////////////////////////////////////////////////
2// //
3// This software is supplied under the terms of a license agreement or //
4// nondisclosure agreement with Mitov Software and may not be copied //
5// or disclosed except in accordance with the terms of that agreement. //
6// Copyright(c) 2002-2016 Mitov Software. All Rights Reserved. //
7// //
8////////////////////////////////////////////////////////////////////////////////
9
10// Derived from https://github.com/msparks/arduino-ds1302
11
12#ifndef _MITOV_RTC_DS1302_h
13#define _MITOV_RTC_DS1302_h
14
15#include <Mitov.h>
16#include <Mitov_Basic_RTC.h>
17
18namespace Mitov
19{
20//---------------------------------------------------------------------------
21 template<int T_DATA_PIN, int T_CLOCK_PIN, int T_ENABLE_PIN> class RTC_DS1302 : public BasicHaltRTC
22 {
23 typedef BasicHaltRTC inherited;
24
25 enum Register
26 {
27 kSecondReg = 0,
28 kMinuteReg = 1,
29 kHourReg = 2,
30 kDateReg = 3,
31 kMonthReg = 4,
32 kDayReg = 5,
33 kYearReg = 6,
34 kWriteProtectReg = 7,
35
36 // The RAM register space follows the clock register space.
37 kRamAddress0 = 32
38 };
39
40 enum Command
41 {
42 kClockBurstRead = 0xBF,
43 kClockBurstWrite = 0xBE,
44 kRamBurstRead = 0xFF,
45 kRamBurstWrite = 0xFE
46 };
47
48 class SPISession
49 {
50 public:
51 SPISession()
52 {
53 digitalWrite(T_CLOCK_PIN, LOW);
54 digitalWrite( T_ENABLE_PIN, HIGH );
55 delayMicroseconds(4); // tCC
56 }
57
58 ~SPISession()
59 {
60 digitalWrite( T_ENABLE_PIN, LOW );
61 delayMicroseconds(4); // tCWH
62 }
63 };
64
65 public:
66 bool WriteProtect = false;
67
68 protected:
69 Mitov::TDateTime FLastDateTime;
70
71 public:
72 void SetWriteProtect( bool AValue )
73 {
74 if( WriteProtect == AValue )
75 return;
76
77 WriteProtect = AValue;
78 UpdateWriteProtect();
79 }
80
81 protected:
82 virtual void SystemInit()
83 {
84 pinMode( T_ENABLE_PIN, OUTPUT );
85 pinMode( T_CLOCK_PIN, OUTPUT );
86// pinMode( 10, OUTPUT );
87
88// Serial.println( "INIT" );
89 UpdateHalt();
90// Serial.println( "INIT1" );
91
92 inherited::SystemInit();
93 }
94
95 protected:
96 void writeOut(const uint8_t value)
97 {
98 pinMode( T_DATA_PIN, OUTPUT);
99 // This assumes that shiftOut is 'slow' enough for the DS1302 to read the
100 // bits. The datasheet specifies that SCLK must be in its high and low states
101 // for at least 0.25us at 5V or 1us at 2V. Experimentally, a 16MHz Arduino
102 // seems to spend ~4us high and ~12us low when shifting.
103 shiftOut( T_DATA_PIN, T_CLOCK_PIN, LSBFIRST, value);
104 }
105
106 uint8_t readIn()
107 {
108 uint8_t input_value = 0;
109 uint8_t bit = 0;
110 pinMode( T_DATA_PIN, INPUT);
111
112 // Bits from the DS1302 are output on the falling edge of the clock
113 // cycle. This method is called after a previous call to writeOut() or
114 // readIn(), which will have already set the clock low.
115 for (int i = 0; i < 8; ++i)
116 {
117 bit = digitalRead( T_DATA_PIN );
118 input_value |= (bit << i); // Bits are read LSB first.
119
120 // See the note in writeOut() about timing. digitalWrite() is slow enough to
121 // not require extra delays for tCH and tCL.
122 digitalWrite( T_CLOCK_PIN, HIGH);
123 digitalWrite( T_CLOCK_PIN, LOW);
124 }
125
126 return input_value;
127 }
128
129 virtual void UpdateHalt()
130 {
131 uint8_t sec = readRegister( kSecondReg );
132
133// Serial.println( sec );
134
135 sec &= ~(1 << 7);
136 sec |= ( ( Halt & 1 ) << 7 );
137 writeRegister( kSecondReg, sec );
138 }
139
140 void UpdateWriteProtect()
141 {
142 writeRegister(kWriteProtectReg, ( ( WriteProtect & 1 ) << 7));
143 }
144
145 uint8_t readRegister(const uint8_t reg)
146 {
147 const SPISession s;
148
149 const uint8_t cmd_byte = (0x81 | (reg << 1));
150 writeOut(cmd_byte);
151 return readIn();
152 }
153
154 void writeRegister( const uint8_t reg, const uint8_t value )
155 {
156 const SPISession s;
157
158 const uint8_t cmd_byte = (0x80 | (reg << 1));
159 writeOut(cmd_byte);
160 writeOut(value);
161 }
162
163 uint8_t hourFromRegisterValue(const uint8_t value)
164 {
165 uint8_t adj;
166 if (value & 128) // 12-hour mode
167 adj = 12 * ((value & 32) >> 5);
168
169 else // 24-hour mode
170 adj = 10 * ((value & (32 + 16)) >> 4);
171
172 return (value & 15) + adj;
173 }
174
175 virtual void ReadTime()
176 {
177 if( ! OutputPin.IsConnected() )
178 return;
179
180 const SPISession s;
181
182 writeOut(kClockBurstRead);
183 uint16_t ASecond = FromBcdToDec(readIn() & 0x7F);
184 uint16_t AMinute = FromBcdToDec(readIn());
185 uint16_t AHour = hourFromRegisterValue(readIn());
186 uint16_t ADate = FromBcdToDec(readIn());
187 uint16_t AMonth = FromBcdToDec(readIn());
188 uint16_t ADay = FromBcdToDec(readIn());
189 uint16_t AYear = 2000 + FromBcdToDec(readIn());
190
191 Mitov::TDateTime ADateTime;
192
193 if( ADateTime.TryEncodeDateTime( AYear, AMonth, ADate, AHour, AMinute, ASecond, 0 ))
194 {
195 if( FLastDateTime != ADateTime )
196 {
197 FLastDateTime = ADateTime;
198 OutputPin.Notify( &ADateTime );
199 }
200 }
201
202 }
203
204 protected:
205 virtual void DoSetReceive( void *_Data )
206 {
207 if( WriteProtect )
208 return;
209
210 Mitov::TDateTime &ADateTime = *(Mitov::TDateTime *)_Data;
211
212 uint16_t AYear;
213 uint16_t AMonth;
214 uint16_t ADay;
215 uint16_t AWeekDay;
216 uint16_t AHour;
217 uint16_t AMinute;
218 uint16_t ASecond;
219 uint16_t AMilliSecond;
220 ADateTime.DecodeDateTime( AYear, AMonth, ADay, AWeekDay, AHour, AMinute, ASecond, AMilliSecond );
221
222 const SPISession s;
223
224 writeOut(kClockBurstWrite);
225 writeOut( ( ( Halt & 1 ) << 7 ) | FromDecToBcd( ASecond ));
226 writeOut(FromDecToBcd( AMinute ));
227 writeOut(FromDecToBcd( AHour ));
228 writeOut(FromDecToBcd( ADay ));
229 writeOut(FromDecToBcd( AMonth ));
230 writeOut(FromDecToBcd( AWeekDay ));
231 writeOut(FromDecToBcd( AYear - 2000));
232 // All clock registers *and* the WP register have to be written for the time
233 // to be set.
234 writeOut(0); // Write protection register.
235 }
236
237 };
238}
239
240#endif