libraries / Mitov / Mitov_RTC_DS1302.hon commit Added link to project report (97a3ba0)
   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