mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 10:38: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=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=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=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/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/clkout/
|
||||||
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/time/
|
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/time/
|
||||||
|
|||||||
Reference in New Issue
Block a user