Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
330 changes: 330 additions & 0 deletions ds3231/ds3231.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package ds3231 // import "tinygo.org/x/drivers/ds3231"

import (
"errors"
"time"

"tinygo.org/x/drivers"
Expand Down Expand Up @@ -158,6 +159,327 @@ func (d *Device) ReadTemperature() (int32, error) {
return milliCelsius(data[0], data[1]), nil
}

// GetSqwPinMode returns the current square wave output frequency
func (d *Device) GetSqwPinMode() SqwPinMode {
data := []uint8{0}
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CONTROL, data)
if err != nil {
return SQW_OFF
}

data[0] &= 0x1C // turn off INTCON
if data[0]&0x04 != 0 {
return SQW_OFF
}

return SqwPinMode(data[0])
}

// SetSqwPinMode sets the square wave output mode to the given frequency
func (d *Device) SetSqwPinMode(mode SqwPinMode) error {
data := []uint8{0}
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CONTROL, data)
if err != nil {
return err
}

data[0] &^= 0x04 // turn off INTCON
data[0] &^= 0x18 // set freq bits to 0

data[0] |= uint8(mode)

err = legacy.WriteRegister(d.bus, uint8(d.Address), REG_CONTROL, data)
if err != nil {
return err
}

return nil
}

// SetAlarm1 sets alarm1 to the given time and mode
func (d *Device) SetAlarm1(dt time.Time, mode Alarm1Mode) error {
dataCtrl := []uint8{0}
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CONTROL, dataCtrl)
if err != nil {
return err
}
if dataCtrl[0]&(1<<INTCN) == 0x00 {
return errors.New("INTCN has to be disabled")
}

A1M1 := uint8((mode & 0x01) << 7)
A1M2 := uint8((mode & 0x02) << 6)
A1M3 := uint8((mode & 0x04) << 5)
A1M4 := uint8((mode & 0x08) << 4)
DY_DT := uint8((mode & 0x10) << 2)

day := dt.Day()
if DY_DT > 0 {
day = dowToDS3231(int(dt.Weekday()))
}

data := make([]uint8, 4)
data[0] = uint8ToBCD(uint8(dt.Second())) | A1M1
data[1] = uint8ToBCD(uint8(dt.Minute())) | A1M2
data[2] = uint8ToBCD(uint8(dt.Hour())) | A1M3
data[3] = uint8ToBCD(uint8(day)) | A1M4 | DY_DT

err = legacy.WriteRegister(d.bus, uint8(d.Address), REG_ALARMONE, data)
if err != nil {
return err
}
dataCtrl[0] |= AlarmFlag_Alarm1
err = legacy.WriteRegister(d.bus, uint8(d.Address), REG_CONTROL, dataCtrl)
if err != nil {
return err
}

return nil
}

// ReadAlarm1 returns the alarm1 time
func (d *Device) ReadAlarm1() (dt time.Time, err error) {
data := make([]uint8, 5)
err = legacy.ReadRegister(d.bus, uint8(d.Address), REG_ALARMONE, data)
if err != nil {
return
}
second := bcdToInt(data[0] & 0x7F)
minute := bcdToInt(data[1] & 0x7F)
hour := hoursBCDToInt(data[2] & 0x3F)

isDayOfWeek := (data[3] & 0x40) >> 6
var day int
if isDayOfWeek > 0 {
day = bcdToInt(data[3] & 0x0F)
} else {
day = bcdToInt(data[3] & 0x3F)
}

dt = time.Date(2000, 5, day, hour, minute, second, 0, time.UTC)
return
}

// SetAlarm2 sets alarm2 to the given time and mode
func (d *Device) SetAlarm2(dt time.Time, mode Alarm2Mode) error {
dataCtrl := []uint8{0}
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CONTROL, dataCtrl)
if err != nil {
return err
}
if dataCtrl[0]&(1<<INTCN) == 0x00 {
return errors.New("INTCN has to be disabled")
}

A2M2 := uint8((mode & 0x01) << 7)
A2M3 := uint8((mode & 0x02) << 6)
A2M4 := uint8((mode & 0x04) << 5)
DY_DT := uint8((mode & 0x08) << 3)

day := dt.Day()
if DY_DT > 0 {
day = dowToDS3231(int(dt.Weekday()))
}

data := make([]uint8, 4)
data[0] = uint8ToBCD(uint8(dt.Minute())) | A2M2
data[1] = uint8ToBCD(uint8(dt.Hour())) | A2M3
data[2] = uint8ToBCD(uint8(day)) | A2M4 | DY_DT

err = legacy.WriteRegister(d.bus, uint8(d.Address), REG_ALARMTWO, data)
if err != nil {
return err
}
dataCtrl[0] |= AlarmFlag_Alarm2
err = legacy.WriteRegister(d.bus, uint8(d.Address), REG_CONTROL, dataCtrl)
if err != nil {
return err
}

return nil
}

// ReadAlarm2 returns the alarm2 time
func (d *Device) ReadAlarm2() (dt time.Time, err error) {
data := make([]uint8, 5)
err = legacy.ReadRegister(d.bus, uint8(d.Address), REG_ALARMTWO, data)
if err != nil {
return
}
minute := bcdToInt(data[0] & 0x7F)
hour := hoursBCDToInt(data[1] & 0x3F)

isDayOfWeek := (data[2] & 0x40) >> 6
var day int
if isDayOfWeek > 0 {
day = bcdToInt(data[2] & 0x0F)
} else {
day = bcdToInt(data[2] & 0x3F)
}

dt = time.Date(2000, 5, day, hour, minute, 0, 0, time.UTC)
return
}

// IsEnabledAlarm1 returns true when alarm1 is enabled
func (d *Device) IsEnabledAlarm1() bool {
return d.isEnabledAlarm(1)
}

// DisableAlarm1 disables alarm1
func (d *Device) DisableAlarm1() error {
return d.disableAlarm(1)
}

// EnableAlarm1 enables alarm1
func (d *Device) EnableAlarm1() error {
return d.enableAlarm(1)
}

// IsEnabledAlarm2 returns true when alarm2 is enabled
func (d *Device) IsEnabledAlarm2() bool {
return d.isEnabledAlarm(2)
}

// DisableAlarm2 disables alarm2
func (d *Device) DisableAlarm2() error {
return d.disableAlarm(2)
}

// EnableAlarm2 enables alarm2
func (d *Device) EnableAlarm2() error {
return d.enableAlarm(2)
}

// ClearAlarm1 clears status of alarm1
func (d *Device) ClearAlarm1() error {
return d.clearAlarm(1)
}

// ClearAlarm2 clears status of alarm2
func (d *Device) ClearAlarm2() error {
return d.clearAlarm(2)
}

// IsAlarm1Fired returns true if alarm1 is firing
func (d *Device) IsAlarm1Fired() bool {
return d.isAlarmFired(1)
}

// IsAlarm2Fired returns true if alarm2 is firing
func (d *Device) IsAlarm2Fired() bool {
return d.isAlarmFired(2)
}

// Enable32K enables the 32KHz output
func (d *Device) Enable32K() error {
data := []byte{0}
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_STATUS, data)
if err != nil {
return err
}
data[0] |= 1 << EN32KHZ
err = legacy.WriteRegister(d.bus, uint8(d.Address), REG_STATUS, data)
if err != nil {
return err
}
return nil
}

// Disable32K disables the 32KHz output
func (d *Device) Disable32K() error {
data := []byte{0}
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_STATUS, data)
if err != nil {
return err
}
data[0] &^= 1 << EN32KHZ
err = legacy.WriteRegister(d.bus, uint8(d.Address), REG_STATUS, data)
if err != nil {
return err
}
return nil
}

// IsEnabled32K get status of 32KHz output
func (d *Device) IsEnabled32K() bool {
data := []byte{0}
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_STATUS, data)
if err != nil {
return false
}
return (data[0] & (1 << EN32KHZ)) != 0x00
}

// disableAlarm disable alarm
func (d *Device) disableAlarm(alarm_num uint8) error {
data := []byte{0}
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CONTROL, data)
if err != nil {
return err
}
data[0] &^= (1 << (alarm_num - 1))
err = legacy.WriteRegister(d.bus, uint8(d.Address), REG_CONTROL, data)
if err != nil {
return err
}
return nil
}

// enableAlarm enable alarm
func (d *Device) enableAlarm(alarm_num uint8) error {
data := []byte{0}
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CONTROL, data)
if err != nil {
return err
}
data[0] |= (1 << (alarm_num - 1))
err = legacy.WriteRegister(d.bus, uint8(d.Address), REG_CONTROL, data)
if err != nil {
return err
}
return nil
}

// isEnabledAlarm check if alarm is enabled for interrupt
func (d *Device) isEnabledAlarm(alarm_num uint8) bool {
data := []byte{0}
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CONTROL, data)
if err != nil {
return false
}
return (data[0] & (1 << (alarm_num - 1))) != 0x00
}

// clearAlarm clear status of alarm
func (d *Device) clearAlarm(alarm_num uint8) error {
data := []byte{0}
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_STATUS, data)
if err != nil {
return err
}
data[0] &^= (1 << (alarm_num - 1))
err = legacy.WriteRegister(d.bus, uint8(d.Address), REG_STATUS, data)
if err != nil {
return err
}
return nil
}

// IsAlarmFired get status of alarm
func (d *Device) isAlarmFired(alarm_num uint8) bool {
dataCtrl := []byte{0}
if err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CONTROL, dataCtrl); err != nil {
return false
}
dataCtrl[0] &^= (1 << (alarm_num - 1))
data := []byte{0}
if err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_STATUS, data); err != nil {
return false
}
return (data[0] & (1 << (alarm_num - 1))) != 0x00
}

// milliCelsius converts the raw temperature bytes (msb and lsb) from the DS3231
// into a 32-bit signed integer in units of milli Celsius (1/1000 deg C).
//
Expand Down Expand Up @@ -200,3 +522,11 @@ func hoursBCDToInt(value uint8) (hour int) {
}
return
}

// dowToDS3231 converts the day of the week to internal DS3231 format
func dowToDS3231(d int) int {
if d == 0 {
return 7
}
return d
}
47 changes: 47 additions & 0 deletions ds3231/registers.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,50 @@ const (
AlarmTwo Mode = 4
ModeAlarmBoth Mode = 5
)

// SQW Pin Modes
type SqwPinMode uint8

const (
SQW_OFF SqwPinMode = 0x1C
SQW_1HZ SqwPinMode = 0x00
SQW_1KHZ SqwPinMode = 0x08
SQW_4KHZ SqwPinMode = 0x10
SQW_8KHZ SqwPinMode = 0x18
)

// Alarm1 Modes define which parts of the set alarm time has to match the current timestamp of the clock device for alarm1 to fire
type Alarm1Mode uint8

const (
// Alarm1 fires every second
A1_PER_SECOND Alarm1Mode = 0x0F
// Alarm1 fires when the seconds match
A1_SECOND Alarm1Mode = 0x0E
// Alarm1 fires when both seconds and minutes match
A1_MINUTE Alarm1Mode = 0x0C
// Alarm1 fires when seconds, minutes and hours match
A1_HOUR Alarm1Mode = 0x08
// Alarm1 fires when seconds, minutes, hours and the day of the month match
A1_DATE Alarm1Mode = 0x00
// Alarm1 fires when seconds, minutes, hours and the day of the week match
A1_DAY Alarm1Mode = 0x10
)

// Alarm2 Modes define which parts of the set alarm time has to match the current timestamp of the clock device for alarm2 to fire.
//
// Alarm2 only supports matching down to the minute unlike alarm1 which supports matching down to the second.
type Alarm2Mode uint8

const (
// Alarm2 fires every minute
A2_PER_MINUTE Alarm2Mode = 0x07
// Alarm2 fires when the minutes match
A2_MINUTE Alarm2Mode = 0x06
// Alarm2 fires when both minutes and hours match
A2_HOUR Alarm2Mode = 0x04
// Alarm2 fires when minutes, hours and the day of the month match
A2_DATE Alarm2Mode = 0x00
// Alarm2 fires when minutes, hours and the day of the week match
A2_DAY Alarm2Mode = 0x08
)
Loading