tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/ds3231/ds3231.go (about) 1 // Package ds3231 provides a driver for the DS3231 RTC 2 // 3 // Datasheet: 4 // https://datasheets.maximintegrated.com/en/ds/DS3231.pdf 5 package ds3231 // import "tinygo.org/x/drivers/ds3231" 6 7 import ( 8 "time" 9 10 "tinygo.org/x/drivers" 11 "tinygo.org/x/drivers/internal/legacy" 12 ) 13 14 type Mode uint8 15 16 // Device wraps an I2C connection to a DS3231 device. 17 type Device struct { 18 bus drivers.I2C 19 Address uint16 20 } 21 22 // New creates a new DS3231 connection. The I2C bus must already be 23 // configured. 24 // 25 // This function only creates the Device object, it does not touch the device. 26 func New(bus drivers.I2C) Device { 27 return Device{ 28 bus: bus, 29 Address: Address, 30 } 31 } 32 33 // Configure sets up the device for communication 34 func (d *Device) Configure() bool { 35 return true 36 } 37 38 // IsTimeValid return true/false is the time in the device is valid 39 func (d *Device) IsTimeValid() bool { 40 data := []byte{0} 41 err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_STATUS, data) 42 if err != nil { 43 return false 44 } 45 return (data[0] & (1 << OSF)) == 0x00 46 } 47 48 // IsRunning returns if the oscillator is running 49 func (d *Device) IsRunning() bool { 50 data := []uint8{0} 51 err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CONTROL, data) 52 if err != nil { 53 return false 54 } 55 return (data[0] & (1 << EOSC)) == 0x00 56 } 57 58 // SetRunning starts the internal oscillator 59 func (d *Device) SetRunning(isRunning bool) error { 60 data := []uint8{0} 61 err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CONTROL, data) 62 if err != nil { 63 return err 64 } 65 if isRunning { 66 data[0] &^= uint8(1 << EOSC) 67 } else { 68 data[0] |= 1 << EOSC 69 } 70 err = legacy.WriteRegister(d.bus, uint8(d.Address), REG_CONTROL, data) 71 if err != nil { 72 return err 73 } 74 return nil 75 } 76 77 // SetTime sets the date and time in the DS3231. The DS3231 hardware supports 78 // only a 2-digit year field, so the current year will be stored as an offset 79 // from the year 2000, which supports the year 2000 until 2100. 80 // 81 // The DS3231 also supports a one-bit 'century' flag which is set by the chip 82 // when the year field rolls over from 99 to 00. The current code interprets 83 // this flag to be the year 2100, which appears to extend the range of years 84 // until the year 2200. However the DS3231 does not incorporate the 'century' 85 // flag in its leap year calculation, so it will incorrectly identify the year 86 // 2100 as a leap year, causing it to increment from 2100-02-28 to 2100-02-29 87 // instead of 2100-03-01. 88 func (d *Device) SetTime(dt time.Time) error { 89 data := []byte{0} 90 err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_STATUS, data) 91 if err != nil { 92 return err 93 } 94 data[0] &^= 1 << OSF 95 err = legacy.WriteRegister(d.bus, uint8(d.Address), REG_STATUS, data) 96 if err != nil { 97 return err 98 } 99 100 data = make([]uint8, 7) 101 data[0] = uint8ToBCD(uint8(dt.Second())) 102 data[1] = uint8ToBCD(uint8(dt.Minute())) 103 data[2] = uint8ToBCD(uint8(dt.Hour())) 104 105 year := uint8(dt.Year() - 2000) 106 // This code interprets the centuryFlag to be the year 2100. Warning: The 107 // DS3231 does not incorporate the centuryFlag in its leap year calculation. 108 // It will increment from 2100-02-28 to 2100-02-29, which is incorrect because 109 // the year 2100 is not a leap year in the Gregorian calendar. 110 centuryFlag := uint8(0) 111 if year >= 100 { 112 year -= 100 113 centuryFlag = 1 << 7 114 } 115 116 data[3] = uint8ToBCD(uint8(dt.Weekday())) 117 data[4] = uint8ToBCD(uint8(dt.Day())) 118 data[5] = uint8ToBCD(uint8(dt.Month()) | centuryFlag) 119 data[6] = uint8ToBCD(year) 120 121 err = legacy.WriteRegister(d.bus, uint8(d.Address), REG_TIMEDATE, data) 122 if err != nil { 123 return err 124 } 125 126 return nil 127 } 128 129 // ReadTime returns the date and time 130 func (d *Device) ReadTime() (dt time.Time, err error) { 131 data := make([]uint8, 7) 132 err = legacy.ReadRegister(d.bus, uint8(d.Address), REG_TIMEDATE, data) 133 if err != nil { 134 return 135 } 136 second := bcdToInt(data[0] & 0x7F) 137 minute := bcdToInt(data[1]) 138 hour := hoursBCDToInt(data[2]) 139 day := bcdToInt(data[4]) 140 monthRaw := data[5] 141 year := bcdToInt(data[6]) + 2000 142 if monthRaw&(1<<7) != 0x00 { 143 year += 100 144 } 145 month := time.Month(bcdToInt(monthRaw & 0x7F)) 146 147 dt = time.Date(year, month, day, hour, minute, second, 0, time.UTC) 148 return 149 } 150 151 // ReadTemperature returns the temperature in millicelsius (mC) 152 func (d *Device) ReadTemperature() (int32, error) { 153 data := make([]uint8, 2) 154 err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_TEMP, data) 155 if err != nil { 156 return 0, err 157 } 158 return milliCelsius(data[0], data[1]), nil 159 } 160 161 // milliCelsius converts the raw temperature bytes (msb and lsb) from the DS3231 162 // into a 32-bit signed integer in units of milli Celsius (1/1000 deg C). 163 // 164 // According to the DS3231 datasheet: "Temperature is represented as a 10-bit 165 // code with a resolution of 0.25 deg C and is accessible at location 11h and 166 // 12h. The temperature is encoded in two's complement format. The upper 8 bits, 167 // the integer portion, are at location 11h and the lower 2 bits, the fractional 168 // portion, are in the upper nibble at location 12h." 169 // 170 // In other words, the msb and lsb bytes should be treated as a signed 16-bit 171 // integer in units of (1/256 deg C). It is possible to convert this into a 172 // 16-bit signed integer in units of centi Celsius (1/100 deg C) with no loss of 173 // precision or dynamic range. But for backwards compatibility, let's instead 174 // convert this into a 32-bit signed integer in units of milli Celsius. 175 func milliCelsius(msb uint8, lsb uint8) int32 { 176 t256 := int16(uint16(msb)<<8 | uint16(lsb)) 177 t1000 := int32(t256) / 64 * 250 178 return t1000 179 } 180 181 // uint8ToBCD converts a byte to BCD for the DS3231 182 func uint8ToBCD(value uint8) uint8 { 183 return value + 6*(value/10) 184 } 185 186 // bcdToInt converts BCD from the DS3231 to int 187 func bcdToInt(value uint8) int { 188 return int(value - 6*(value>>4)) 189 } 190 191 // hoursBCDToInt converts the BCD hours to int 192 func hoursBCDToInt(value uint8) (hour int) { 193 if value&0x40 != 0x00 { 194 hour = bcdToInt(value & 0x1F) 195 if (value & 0x20) != 0x00 { 196 hour += 12 197 } 198 } else { 199 hour = bcdToInt(value) 200 } 201 return 202 }