|
| 1 | +using System; |
| 2 | +using System.Collections.Generic; |
| 3 | +using Raspberry.IO.InterIntegratedCircuit; |
| 4 | +using System.Collections; |
| 5 | + |
| 6 | +namespace Raspberry.IO.Components.Clocks.Ds1307 |
| 7 | +{ |
| 8 | + /// <summary> |
| 9 | + /// Provides functionality for the DS1307 Real-Time Clock. |
| 10 | + /// </summary> |
| 11 | + /// <remarks> |
| 12 | + /// The Clock Module needs to be modified to work with the Raspberry Pi |
| 13 | + /// according to this article: http://electronics.stackexchange.com/questions/98361/how-to-modify-ds1307-rtc-to-use-3-3v-for-raspberry-pi |
| 14 | + /// The Datasheet can be found here: http://www.alldatasheet.com/datasheet-pdf/pdf/58481/DALLAS/DS1307.html (the Memory Map |
| 15 | + /// is on Page 5. Note that the values in RAM are stored in Nibbles). |
| 16 | + /// </remarks> |
| 17 | + public class Ds1307Connection |
| 18 | + { |
| 19 | + public I2cDeviceConnection Connection { get; set; } |
| 20 | + |
| 21 | + /// <summary> |
| 22 | + /// Creates a new instance of the class using the provided I2C Connection. |
| 23 | + /// </summary> |
| 24 | + /// <param name="connection">I2C Connection to the Clock.</param> |
| 25 | + public Ds1307Connection(I2cDeviceConnection connection) |
| 26 | + { |
| 27 | + Connection = connection; |
| 28 | + } |
| 29 | + |
| 30 | + /// <summary> |
| 31 | + /// Reads the Seconds-byte (first byte in the RAM) from the Clock and returns it. |
| 32 | + /// </summary> |
| 33 | + /// <returns>The Seconds-Byte including the CH-Flag in bit 7.</returns> |
| 34 | + private byte ReadSeconds() |
| 35 | + { |
| 36 | + Connection.Write(0x00); |
| 37 | + return Connection.ReadByte(); |
| 38 | + } |
| 39 | + |
| 40 | + /// <summary> |
| 41 | + /// Reads 7 bytes from the Clock and returns them. |
| 42 | + /// </summary> |
| 43 | + /// <returns>7 Bytes from the Clock.</returns> |
| 44 | + private byte[] ReadAll() |
| 45 | + { |
| 46 | + Connection.Write(0x00); |
| 47 | + return Connection.Read(7); |
| 48 | + } |
| 49 | + |
| 50 | + /// <summary> |
| 51 | + /// Reads the Date and Time from the Ds1307 and returns it. |
| 52 | + /// </summary> |
| 53 | + /// <returns>Date.</returns> |
| 54 | + public DateTime GetDate() |
| 55 | + { |
| 56 | + return GetDate(ReadAll()); |
| 57 | + } |
| 58 | + |
| 59 | + /// <summary> |
| 60 | + /// Converts the provided bytes to a DateTime. |
| 61 | + /// </summary> |
| 62 | + /// <param name="input">Bytes that should be converted.</param> |
| 63 | + /// <returns>DateTime resulting from the bytes.</returns> |
| 64 | + private DateTime GetDate(byte[] input) |
| 65 | + { |
| 66 | + /* Byte 1: CH-Flag + Seconds (00-59) |
| 67 | + * Byte 2: Minutes (00-59) |
| 68 | + * Byte 3: 12/24-Flag, AM/PM, Hours (01-12 or 00-23) |
| 69 | + * Byte 4: Day of week (1-7) |
| 70 | + * Byte 5: Day (01-31) |
| 71 | + * Byte 6: Month (01-12) |
| 72 | + * Byte 7: Year (00-99) |
| 73 | + * Byte 8: Control Register (for enabling/disabling Sqare Wave) |
| 74 | + */ |
| 75 | + |
| 76 | + int seconds = input[0]; |
| 77 | + if (!IsRtcEnabled(input[0])) seconds = seconds - 128; // Remove "CH"-bit from the seconds if present |
| 78 | + |
| 79 | + seconds = NibbleToInt((byte)seconds); |
| 80 | + |
| 81 | + int minutes = NibbleToInt(input[1]); |
| 82 | + int hours = NibbleToInt(input[2]); |
| 83 | + |
| 84 | + if ((hours & 64) == 64) |
| 85 | + { |
| 86 | + throw new NotImplementedException("AM/PM Time is currently not supported."); |
| 87 | + // 12 h Format |
| 88 | + //if ((hours & 32) == 32) |
| 89 | + //{ |
| 90 | + // //PM |
| 91 | + //} |
| 92 | + //else |
| 93 | + //{ |
| 94 | + // //AM |
| 95 | + //} |
| 96 | + } |
| 97 | + |
| 98 | + int dayOfWeek = NibbleToInt(input[3]); |
| 99 | + |
| 100 | + int day = NibbleToInt(input[4]); |
| 101 | + int month = NibbleToInt(input[5]); |
| 102 | + int year = NibbleToInt(input[6]); |
| 103 | + |
| 104 | + if (year == 0) return new DateTime(); |
| 105 | + |
| 106 | + return new DateTime(year + 2000, month, day, hours, minutes, seconds); |
| 107 | + } |
| 108 | + |
| 109 | + /// <summary> |
| 110 | + /// Writes the provided Date and Time to the Ds1307. |
| 111 | + /// </summary> |
| 112 | + /// <param name="date">The Date that should be set.</param> |
| 113 | + public void SetDate(DateTime date) |
| 114 | + { |
| 115 | + List<byte> toWrite = new List<byte>(); |
| 116 | + |
| 117 | + toWrite.Add(0x00); |
| 118 | + toWrite.Add(SetEnabledDisableRtc(IntToNibble(date.Second), !IsRtcEnabled())); |
| 119 | + toWrite.Add(IntToNibble(date.Minute)); |
| 120 | + toWrite.Add(IntToNibble(date.Hour)); |
| 121 | + toWrite.Add(Convert.ToByte(date.DayOfWeek)); |
| 122 | + toWrite.Add(IntToNibble(date.Day)); |
| 123 | + toWrite.Add(IntToNibble(date.Month)); |
| 124 | + toWrite.Add(IntToNibble(Convert.ToInt16(date.ToString("yy"), 10))); |
| 125 | + |
| 126 | + Connection.Write(toWrite.ToArray()); |
| 127 | + } |
| 128 | + |
| 129 | + /// <summary> |
| 130 | + /// Enables the Clock. |
| 131 | + /// </summary> |
| 132 | + public void EnableRtc() |
| 133 | + { |
| 134 | + // CH=1: Disabled, CH=0: Enabled |
| 135 | + byte seconds = ReadSeconds(); |
| 136 | + if (IsRtcEnabled(seconds)) return; |
| 137 | + |
| 138 | + Connection.Write(0x00, SetEnabledDisableRtc(seconds, false)); |
| 139 | + } |
| 140 | + |
| 141 | + /// <summary> |
| 142 | + /// Disables the Clock. When the Clock is diabled, it not ticking. |
| 143 | + /// </summary> |
| 144 | + public void DisableRtc() |
| 145 | + { |
| 146 | + byte seconds = ReadSeconds(); |
| 147 | + if (!IsRtcEnabled(seconds)) return; |
| 148 | + |
| 149 | + Connection.Write(0x00, SetEnabledDisableRtc(seconds, true)); |
| 150 | + } |
| 151 | + |
| 152 | + /// <summary> |
| 153 | + /// Disables or enables the Clock. |
| 154 | + /// </summary> |
| 155 | + /// <param name="seconds">The byte that contains the seconds and the CH-Flag.</param> |
| 156 | + /// <param name="disable">true will disable the Clock, false will enable it.</param> |
| 157 | + /// <returns></returns> |
| 158 | + /// <remarks>The disable/enabled-Flag is stored in bit 7 within the seconds-byte.</remarks> |
| 159 | + private byte SetEnabledDisableRtc(byte seconds, bool disable) |
| 160 | + { |
| 161 | + BitArray bits = new BitArray(new byte[] { seconds }); |
| 162 | + bits.Set(7, disable); |
| 163 | + |
| 164 | + byte[] result = new byte[1]; |
| 165 | + bits.CopyTo(result, 0); |
| 166 | + |
| 167 | + return result[0]; |
| 168 | + } |
| 169 | + |
| 170 | + /// <summary> |
| 171 | + /// Returns true, if the Clock is enabled, otherwise false is returned. |
| 172 | + /// </summary> |
| 173 | + /// <returns>true: Clock is enabled, false: Clock is diabled.</returns> |
| 174 | + public bool IsRtcEnabled() |
| 175 | + { |
| 176 | + return IsRtcEnabled(ReadSeconds()); |
| 177 | + } |
| 178 | + |
| 179 | + /// <summary> |
| 180 | + /// Returns true, if the Clock is enabled, otherwise false. |
| 181 | + /// </summary> |
| 182 | + /// <param name="seconds">The byte, that contains the seconds and the CH-Flag.</param> |
| 183 | + /// <returns>true: Clock is enabled, false: Clock is diabled.</returns> |
| 184 | + private bool IsRtcEnabled(byte seconds) |
| 185 | + { |
| 186 | + return !((seconds & 128) == 128); |
| 187 | + } |
| 188 | + |
| 189 | + /// <summary> |
| 190 | + /// Resets the Clock to the Factory Defaults. |
| 191 | + /// </summary> |
| 192 | + public void ResetToFactoryDefaults() |
| 193 | + { |
| 194 | + // The Factory Default is: 80,00,00,01,01,01,00,B3 |
| 195 | + Connection.Write(0x00, 0x80, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00); |
| 196 | + } |
| 197 | + |
| 198 | + /// <summary> |
| 199 | + /// Writes the current System Date to the Clock. |
| 200 | + /// </summary> |
| 201 | + public void SystemTimeToRtc() |
| 202 | + { |
| 203 | + SetDate(DateTime.Now); |
| 204 | + } |
| 205 | + |
| 206 | + /// <summary> |
| 207 | + /// Converts the specified two-nibble-byte to an integer. |
| 208 | + /// </summary> |
| 209 | + /// <param name="nibble">Nibble that should be converted.</param> |
| 210 | + /// <returns>Integer representation of the nibble.</returns> |
| 211 | + private static int NibbleToInt(byte nibble) |
| 212 | + { |
| 213 | + int result = 0; |
| 214 | + result *= 100; |
| 215 | + result += (10 * (nibble >> 4)); |
| 216 | + result += nibble & 0xf; |
| 217 | + return result; |
| 218 | + } |
| 219 | + |
| 220 | + /// <summary> |
| 221 | + /// Converts the specified integer to a two-nibble-byte. |
| 222 | + /// </summary> |
| 223 | + /// <param name="number">The integer that should be converted. Maximum is 99.</param> |
| 224 | + /// <returns>A byte with two nibbles that contains the integer.</returns> |
| 225 | + private static byte IntToNibble(int number) |
| 226 | + { |
| 227 | + int bcd = 0; |
| 228 | + for (int digit = 0; digit < 4; ++digit) |
| 229 | + { |
| 230 | + int nibble = number % 10; |
| 231 | + bcd |= nibble << (digit * 4); |
| 232 | + number /= 10; |
| 233 | + } |
| 234 | + return (byte)(bcd & 0xff); |
| 235 | + } |
| 236 | + } |
| 237 | +} |
0 commit comments