mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 02:28:41 +00:00
pcf8523: RTC driver
This commit is contained in:
committed by
deadprogram
parent
5888bb2ded
commit
cfa50fd3c2
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
"tinygo.org/x/drivers/pcf8523"
|
||||
)
|
||||
|
||||
func main() {
|
||||
machine.I2C0.Configure(machine.I2CConfig{})
|
||||
dev := pcf8523.New(machine.I2C0)
|
||||
|
||||
// make sure the battery takes over if power is lost
|
||||
err := dev.SetPowerManagement(pcf8523.PowerManagement_SwitchOver_ModeStandard)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// set RTC once, i.e. from `date -u +"%Y-%m-%dT%H:%M:%SZ"`
|
||||
now, _ := time.Parse(time.RFC3339, "2023-09-18T20:31:38Z")
|
||||
err = dev.SetTime(now)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for {
|
||||
ts, err := dev.ReadTime()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
println("tick-tock, it's: " + ts.String())
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
// Package pcf8523 implements a driver for the PCF8523 CMOS Real-Time Clock (RTC)
|
||||
//
|
||||
// Datasheet: https://www.nxp.com/docs/en/data-sheet/PCF8523.pdf
|
||||
package pcf8523
|
||||
|
||||
import (
|
||||
"time"
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
Address uint8
|
||||
}
|
||||
|
||||
func New(i2c drivers.I2C) Device {
|
||||
return Device{
|
||||
bus: i2c,
|
||||
Address: DefaultAddress,
|
||||
}
|
||||
}
|
||||
|
||||
// Reset resets the device according to the datasheet section 8.3
|
||||
// This does not wipe the time registers, but resets control registers.
|
||||
func (d *Device) Reset() (err error) {
|
||||
return d.bus.Tx(uint16(d.Address), []byte{rControl1, 0x58}, nil)
|
||||
}
|
||||
|
||||
// SetPowerManagement configures how the device makes use of the backup battery, see
|
||||
// datasheet section 8.5
|
||||
func (d *Device) SetPowerManagement(b PowerManagement) error {
|
||||
return d.setRegister(rControl3, byte(b)<<5, 0xE0)
|
||||
}
|
||||
|
||||
func (d *Device) setRegister(reg uint8, value, mask uint8) error {
|
||||
var buf [1]byte
|
||||
err := d.bus.Tx(uint16(d.Address), []byte{reg}, buf[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf[0] = (value & mask) | (buf[0] & (^mask))
|
||||
return d.bus.Tx(uint16(d.Address), []byte{reg, buf[0]}, nil)
|
||||
}
|
||||
|
||||
// SetTime sets the time and date
|
||||
func (d *Device) SetTime(t time.Time) error {
|
||||
buf := []byte{
|
||||
rSeconds,
|
||||
bin2bcd(t.Second()),
|
||||
bin2bcd(t.Minute()),
|
||||
bin2bcd(t.Hour()),
|
||||
bin2bcd(t.Day()),
|
||||
bin2bcd(int(t.Weekday())),
|
||||
bin2bcd(int(t.Month())),
|
||||
bin2bcd(t.Year() - 2000),
|
||||
}
|
||||
|
||||
return d.bus.Tx(uint16(d.Address), buf, nil)
|
||||
}
|
||||
|
||||
// ReadTime returns the date and time
|
||||
func (d *Device) ReadTime() (time.Time, error) {
|
||||
buf := make([]byte, 9)
|
||||
err := d.bus.Tx(uint16(d.Address), []byte{rSeconds}, buf)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
|
||||
seconds := bcd2bin(buf[0] & 0x7F)
|
||||
minute := bcd2bin(buf[1] & 0x7F)
|
||||
hour := bcd2bin(buf[2] & 0x3F)
|
||||
day := bcd2bin(buf[3] & 0x3F)
|
||||
//skipping weekday buf[4]
|
||||
month := time.Month(bcd2bin(buf[5] & 0x1F))
|
||||
year := int(bcd2bin(buf[6])) + 2000
|
||||
|
||||
t := time.Date(year, month, day, hour, minute, seconds, 0, time.UTC)
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// bin2bcd converts binary to BCD
|
||||
func bin2bcd(dec int) uint8 {
|
||||
return uint8(dec + 6*(dec/10))
|
||||
}
|
||||
|
||||
// bcd2bin converts BCD to binary
|
||||
func bcd2bin(bcd uint8) int {
|
||||
return int(bcd - 6*(bcd>>4))
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package pcf8523
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
"time"
|
||||
"tinygo.org/x/drivers/tester"
|
||||
)
|
||||
|
||||
func TestDecToBcd_RoundTrip(t *testing.T) {
|
||||
|
||||
for i := 0; i < 60; i++ {
|
||||
a := bcd2bin(bin2bcd(i))
|
||||
if a != i {
|
||||
t.Logf("not equal: %d != %d", a, i)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDevice_Reset(t *testing.T) {
|
||||
bus := tester.NewI2CBus(t)
|
||||
fake := bus.NewDevice(DefaultAddress)
|
||||
|
||||
dev := New(bus)
|
||||
|
||||
err := dev.Reset()
|
||||
assertNoError(t, err)
|
||||
|
||||
assertEquals(t, fake.Registers[rControl1], 0x58)
|
||||
}
|
||||
|
||||
func TestDevice_SetPowerManagement(t *testing.T) {
|
||||
bus := tester.NewI2CBus(t)
|
||||
fake := bus.NewDevice(DefaultAddress)
|
||||
|
||||
dev := New(bus)
|
||||
|
||||
err := dev.SetPowerManagement(PowerManagement_SwitchOver_ModeStandard)
|
||||
assertNoError(t, err)
|
||||
|
||||
assertEquals(t, fake.Registers[rControl3], 0b100<<5)
|
||||
}
|
||||
|
||||
func TestDevice_SetTime(t *testing.T) {
|
||||
bus := tester.NewI2CBus(t)
|
||||
fake := bus.NewDevice(DefaultAddress)
|
||||
|
||||
dev := New(bus)
|
||||
|
||||
pointInTime, _ := time.Parse(time.RFC3339, "2023-09-12T22:35:50Z")
|
||||
err := dev.SetTime(pointInTime)
|
||||
assertNoError(t, err)
|
||||
|
||||
actual := hex.EncodeToString(fake.Registers[rSeconds : rSeconds+7])
|
||||
expected := "50352212020923"
|
||||
assertEquals(t, actual, expected)
|
||||
}
|
||||
|
||||
func TestDevice_ReadTime(t *testing.T) {
|
||||
bus := tester.NewI2CBus(t)
|
||||
fake := bus.NewDevice(DefaultAddress)
|
||||
|
||||
expectedPointInTime := time.Date(2023, 9, 12, 17, 55, 42, 0, time.UTC)
|
||||
fake.Registers[rSeconds] = 0x42
|
||||
fake.Registers[rMinutes] = 0x55
|
||||
fake.Registers[rHours] = 0x17
|
||||
fake.Registers[rDays] = 0x12
|
||||
fake.Registers[rMonths] = 0x9
|
||||
fake.Registers[rYears] = 0x23
|
||||
|
||||
dev := New(bus)
|
||||
|
||||
//when
|
||||
actualPointInTime, err := dev.ReadTime()
|
||||
|
||||
//then
|
||||
assertNoError(t, err)
|
||||
assertEquals(t, actualPointInTime, expectedPointInTime)
|
||||
}
|
||||
|
||||
func assertNoError(t testing.TB, e error) {
|
||||
if e != nil {
|
||||
t.Fatalf("unexpected error: %v", e)
|
||||
}
|
||||
}
|
||||
func assertEquals[T comparable](t testing.TB, a, b T) {
|
||||
if a != b {
|
||||
t.Fatalf("%v != %v", a, b)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package pcf8523
|
||||
|
||||
const DefaultAddress = 0x68
|
||||
|
||||
// datasheet 8.5 Power management functions, table 11
|
||||
type PowerManagement byte
|
||||
|
||||
const (
|
||||
PowerManagement_SwitchOver_ModeStandard_LowDetection PowerManagement = 0b000
|
||||
PowerManagement_SwitchOver_ModeDirect_LowDetection PowerManagement = 0b001
|
||||
PowerManagement_VddOnly_LowDetection PowerManagement = 0b010
|
||||
PowerManagement_SwitchOver_ModeStandard PowerManagement = 0b100
|
||||
PowerManagement_SwitchOver_ModeDirect PowerManagement = 0b101
|
||||
PowerManagement_VddOnly PowerManagement = 0b101
|
||||
)
|
||||
|
||||
// constants for all internal registers
|
||||
const (
|
||||
rControl1 = 0x00 // Control_1
|
||||
rControl2 = 0x01 // Control_2
|
||||
rControl3 = 0x02 // Control_3
|
||||
rSeconds = 0x03 // Seconds
|
||||
rMinutes = 0x04 // Minutes
|
||||
rHours = 0x05 // Hours
|
||||
rDays = 0x06 // Days
|
||||
rWeekdays = 0x07 // Weekdays
|
||||
rMonths = 0x08 // Months
|
||||
rYears = 0x09 // Years
|
||||
rMinuteAlarm = 0x0A // Minute_alarm
|
||||
rHourAlarm = 0x0B // Hour_alarm
|
||||
rDayAlarm = 0x0C // Day_alarm
|
||||
rWeekdayAlarm = 0x0D // Weekday_alarm
|
||||
rOffset = 0x0E // Offset
|
||||
rTimerClkoutControl = 0x0F // Tmr_CLKOUT_ctrl
|
||||
rTimerAFrequencyControl = 0x10 // Tmr_A_freq_ctrl
|
||||
rTimerARegister = 0x11 // Tmr_A_reg
|
||||
rTimerBFrequencyControl = 0x12 // Tmr_B_freq_ctrl
|
||||
rTimerBRegister = 0x13 // Tmr_B_reg
|
||||
)
|
||||
@@ -103,6 +103,7 @@ tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examp
|
||||
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/max72xx/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/dht/main.go
|
||||
# tinygo build -size short -o ./build/test.hex -target=arduino ./examples/keypad4x4/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=feather-rp2040 ./examples/pcf8523/
|
||||
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/alarm/
|
||||
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/clkout/
|
||||
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/time/
|
||||
|
||||
Reference in New Issue
Block a user