pcf8563: add support for pcf8563 real time clock

This commit is contained in:
sago35
2021-05-09 13:44:05 +09:00
committed by Ron Evans
parent 91dadd5535
commit 761bcfc4db
8 changed files with 453 additions and 2 deletions
+10 -1
View File
@@ -177,11 +177,20 @@ endif
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino ./examples/keypad4x4/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/alarm/
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/clkout/
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/time/
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/timer/
@md5sum ./build/test.hex
DRIVERS = $(wildcard */)
NOTESTS = build examples flash semihosting pcd8544 shiftregister st7789 microphone mcp3008 gps microbitmatrix \
hcsr04 ssd1331 ws2812 thermistor apa102 easystepper ssd1351 ili9341 wifinina shifter hub75 \
hd44780 buzzer ssd1306 espat l9110x st7735 bmi160 l293x dht keypad4x4 max72xx p1am tone tm1637
hd44780 buzzer ssd1306 espat l9110x st7735 bmi160 l293x dht keypad4x4 max72xx p1am tone tm1637 \
pcf8563
TESTS = $(filter-out $(addsuffix /%,$(NOTESTS)),$(DRIVERS))
unit-test:
+2 -1
View File
@@ -52,7 +52,7 @@ func main() {
## Currently supported devices
The following 58 devices are supported.
The following 60 devices are supported.
| Device Name | Interface Type |
|----------|-------------|
@@ -95,6 +95,7 @@ The following 58 devices are supported.
| [MPU6050 accelerometer/gyroscope](https://store.invensense.com/datasheets/invensense/MPU-6050_DataSheet_V3%204.pdf) | I2C |
| [P1AM-100 Base Controller](https://facts-engineering.github.io/modules/P1AM-100/P1AM-100.html) | SPI |
| [PCD8544 display](http://eia.udg.edu/~forest/PCD8544_1.pdf) | SPI |
| [PCF8563 real time clock](https://www.nxp.com/docs/en/data-sheet/PCF8563.pdf) | I2C |
| [Resistive Touchscreen (4-wire)](http://ww1.microchip.com/downloads/en/Appnotes/doc8091.pdf) | GPIO |
| [Semihosting](https://wiki.segger.com/Semihosting) | Debug |
| [Shift register (PISO)](https://en.wikipedia.org/wiki/Shift_register#Parallel-in_serial-out_\(PISO\)) | GPIO |
+45
View File
@@ -0,0 +1,45 @@
package main
import (
"fmt"
"machine"
"time"
"tinygo.org/x/drivers/pcf8563"
)
var (
i2c = machine.I2C0
rtc = pcf8563.New(i2c)
)
func main() {
i2c.Configure(machine.I2CConfig{Frequency: machine.TWI_FREQ_400KHZ})
rtc.Reset()
time.Sleep(3 * time.Second)
rtc.SetTime(time.Date(2006, 1, 2, 15, 4, 50, 0, time.UTC))
rtc.SetAlarm(time.Date(2006, 1, 2, 15, 5, 0, 0, time.UTC))
rtc.EnableAlarmInterrupt()
prev := -1
for {
for {
t, _ := rtc.ReadTime()
if prev != t.Second() {
fmt.Printf("%s\r\n", t.String())
prev = t.Second()
if rtc.AlarmTriggered() {
fmt.Printf("alarm triggered\r\n")
rtc.ClearAlarm()
}
break
}
time.Sleep(time.Millisecond * 100)
}
}
}
+31
View File
@@ -0,0 +1,31 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/pcf8563"
)
var (
i2c = machine.I2C0
rtc = pcf8563.New(i2c)
)
func main() {
i2c.Configure(machine.I2CConfig{Frequency: machine.TWI_FREQ_400KHZ})
rtc.Reset()
for {
rtc.SetOscillatorFrequency(pcf8563.RTC_COT_1HZ)
time.Sleep(3 * time.Second)
rtc.SetOscillatorFrequency(pcf8563.RTC_COT_32HZ)
time.Sleep(3 * time.Second)
rtc.SetOscillatorFrequency(pcf8563.RTC_COT_1KHZ)
time.Sleep(3 * time.Second)
rtc.SetOscillatorFrequency(pcf8563.RTC_COT_32KHZ)
time.Sleep(3 * time.Second)
rtc.SetOscillatorFrequency(pcf8563.RTC_COT_DISABLE)
time.Sleep(3 * time.Second)
}
}
+36
View File
@@ -0,0 +1,36 @@
package main
import (
"fmt"
"machine"
"time"
"tinygo.org/x/drivers/pcf8563"
)
var (
i2c = machine.I2C0
rtc = pcf8563.New(i2c)
)
func main() {
i2c.Configure(machine.I2CConfig{Frequency: machine.TWI_FREQ_400KHZ})
rtc.Reset()
rtc.SetTime(time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC))
prev := -1
for {
for {
t, _ := rtc.ReadTime()
if prev != t.Second() {
fmt.Printf("%s\r\n", t.String())
prev = t.Second()
break
}
time.Sleep(time.Millisecond * 100)
}
}
}
+46
View File
@@ -0,0 +1,46 @@
package main
import (
"fmt"
"machine"
"time"
"tinygo.org/x/drivers/pcf8563"
)
var (
i2c = machine.I2C0
rtc = pcf8563.New(i2c)
)
func main() {
i2c.Configure(machine.I2CConfig{Frequency: machine.TWI_FREQ_400KHZ})
rtc.Reset()
time.Sleep(3 * time.Second)
rtc.SetTime(time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC))
rtc.SetTimer(15 * time.Second)
rtc.EnableTimerInterrupt()
prev := -1
for {
for {
t, _ := rtc.ReadTime()
if prev != t.Second() {
fmt.Printf("%s\r\n", t.String())
prev = t.Second()
if rtc.TimerTriggered() {
fmt.Printf("timer triggered\r\n")
rtc.ClearTimer()
rtc.SetTimer(10 * time.Second)
}
break
}
time.Sleep(time.Millisecond * 100)
}
}
}
+252
View File
@@ -0,0 +1,252 @@
// Package pcf8563 implements a driver for the PCF8563 CMOS Real-Time Clock (RTC)
//
// Datasheet: https://www.nxp.com/docs/en/data-sheet/PCF8563.pdf
//
package pcf8563
import (
"time"
"tinygo.org/x/drivers"
)
// Device wraps an I2C connection to a PCF8563 device.
type Device struct {
bus drivers.I2C
Address uint16
}
// New creates a new PCF8563 connection. I2C bus must be already configured.
func New(i2c drivers.I2C) Device {
return Device{
bus: i2c,
Address: PCF8563_ADDR,
}
}
// Reset resets the `control and status registers`. When this method is
// called, it writes `0x00` to the `control and status registers`. This will
// cause `Alarm` and `Timer` to become Inactive. Please refer to the datasheet
// for details.
func (d *Device) Reset() (err error) {
return d.bus.Tx(d.Address, []byte{0x00, 0x00, 0x00}, nil)
}
// SetTime sets the time and date
func (d *Device) SetTime(t time.Time) error {
var buf [9]byte
buf[0] = 0x02
buf[1] = decToBcd(t.Second())
buf[2] = decToBcd(t.Minute())
buf[3] = decToBcd(t.Hour())
buf[4] = decToBcd(t.Day())
buf[5] = decToBcd(int(t.Weekday() + 1))
buf[6] = decToBcd(int(t.Month()))
buf[7] = decToBcd(t.Year() - 2000)
err := d.bus.Tx(d.Address, buf[:], nil)
return err
}
// ReadTime returns the date and time
func (d *Device) ReadTime() (time.Time, error) {
var buf [9]byte
err := d.bus.Tx(d.Address, []byte{0x00}, buf[:])
if err != nil {
return time.Time{}, err
}
seconds := bcdToDec(buf[2] & 0x7F)
minute := bcdToDec(buf[3] % 0x7F)
hour := bcdToDec(buf[4] & 0x3F)
day := bcdToDec(buf[5] & 0x3F)
month := time.Month(bcdToDec(buf[7] & 0x0F))
year := int(bcdToDec(buf[8])) + 2000
t := time.Date(year, month, day, hour, minute, seconds, 0, time.UTC)
return t, nil
}
// SetAlarm sets the alarm
func (d *Device) SetAlarm(t time.Time) error {
var buf [5]byte
buf[0] = 0x09
buf[1] = RTC_ALARM_ENABLE | decToBcd(t.Minute())
buf[2] = RTC_ALARM_ENABLE | decToBcd(t.Hour())
buf[3] = RTC_ALARM_ENABLE | decToBcd(t.Day())
buf[4] = RTC_ALARM_DISABLE
err := d.bus.Tx(d.Address, buf[:], nil)
if err != nil {
return err
}
// enable alarm
buf[0] = 0x01
err = d.bus.Tx(d.Address, buf[:1], buf[1:])
if err != nil {
return err
}
buf[1] |= RTC_CTRL_AF
err = d.bus.Tx(d.Address, buf[:2], nil)
return err
}
// ClearAlarm disables alarm.
func (d *Device) ClearAlarm() error {
var buf [2]byte
buf[0] = 0x01
err := d.bus.Tx(d.Address, buf[:1], buf[1:])
if err != nil {
return err
}
buf[1] &= ^uint8(RTC_CTRL_AF)
err = d.bus.Tx(d.Address, buf[:], nil)
return err
}
// EnableAlarmInterrupt enables alarm interrupt. When triggered, INT pin (3)
// goes low.
func (d *Device) EnableAlarmInterrupt() error {
var buf [2]byte
buf[0] = 0x01
err := d.bus.Tx(d.Address, buf[:1], buf[1:])
if err != nil {
return err
}
buf[1] |= RTC_CTRL_AIE
err = d.bus.Tx(d.Address, buf[:], nil)
return err
}
// DisableAlarmInterrupt disable alarm interrupt.
func (d *Device) DisableAlarmInterrupt() error {
var buf [2]byte
buf[0] = 0x01
err := d.bus.Tx(d.Address, buf[:1], buf[1:])
if err != nil {
return err
}
buf[1] &= ^uint8(RTC_CTRL_AIE)
err = d.bus.Tx(d.Address, buf[:], nil)
return err
}
// AlarmTriggered returns whether or not an Alarm has been triggered.
func (d *Device) AlarmTriggered() bool {
var buf [1]byte
buf[0] = 0x01
err := d.bus.Tx(d.Address, buf[:], buf[:])
if err != nil {
return false
}
return (buf[0] & RTC_CTRL_AF) != 0
}
// SetTimer sets timer. The available durations are 1 to 127 seconds. If any
// other value is specified, it will be truncated.
func (d *Device) SetTimer(dur time.Duration) error {
var buf [3]byte
sec := dur / time.Second
if sec > 127 {
sec = 127
}
// Treat as sec timer.
buf[0] = 0x0E
buf[1] = RTC_TIMER_1S
buf[2] = byte(sec)
err := d.bus.Tx(d.Address, buf[:], nil)
if err != nil {
return err
}
// enable alarm
buf[0] = 0x01
err = d.bus.Tx(d.Address, buf[:1], buf[1:])
if err != nil {
return err
}
buf[1] |= RTC_CTRL_TF
err = d.bus.Tx(d.Address, buf[:2], nil)
return err
}
// ClearTimer disables timer.
func (d *Device) ClearTimer() error {
var buf [2]byte
buf[0] = 0x01
err := d.bus.Tx(d.Address, buf[:1], buf[1:])
if err != nil {
return err
}
buf[1] &= ^uint8(RTC_CTRL_TF)
err = d.bus.Tx(d.Address, buf[:], nil)
return err
}
// EnableTimerInterrupt enables timer interrupt. When triggered, INT pin (3)
// goes low.
func (d *Device) EnableTimerInterrupt() error {
var buf [2]byte
buf[0] = 0x01
err := d.bus.Tx(d.Address, buf[:1], buf[1:])
if err != nil {
return err
}
buf[1] |= RTC_CTRL_TIE
err = d.bus.Tx(d.Address, buf[:], nil)
return err
}
// DisableTimerInterrupt disable timer interrupt.
func (d *Device) DisableTimerInterrupt() error {
var buf [2]byte
buf[0] = 0x01
err := d.bus.Tx(d.Address, buf[:1], buf[1:])
if err != nil {
return err
}
buf[1] &= ^uint8(RTC_CTRL_TIE)
err = d.bus.Tx(d.Address, buf[:], nil)
return err
}
// TimerTriggered returns whether or not an Alarm has been triggered.
func (d *Device) TimerTriggered() bool {
var buf [1]byte
buf[0] = 0x01
err := d.bus.Tx(d.Address, buf[:], buf[:])
if err != nil {
return false
}
return (buf[0] & RTC_CTRL_TF) != 0
}
// SetOscillatorFrequency sets output oscillator frequency
// Available modes: RTC_COT_DISABLE, RTC_COT_32KHZ, RTC_COT_1KHZ,
// RTC_COT_32Hz, RTC_COT_1HZ.
func (d *Device) SetOscillatorFrequency(sqw uint8) error {
var buf [2]byte
buf[0] = 0x0D
buf[1] = sqw
return d.bus.Tx(d.Address, buf[:], nil)
}
// decToBcd converts int to BCD
func decToBcd(dec int) uint8 {
return uint8(dec + 6*(dec/10))
}
// bcdToDec converts BCD to int
func bcdToDec(bcd uint8) int {
return int(bcd - 6*(bcd>>4))
}
+31
View File
@@ -0,0 +1,31 @@
package pcf8563
// Registers
const (
PCF8563_ADDR = 0x51 // R:A3 W:A2
I2C_SPEED_STANDARD = 100000
I2C_SPEED_DOUBLE = 200000
RTC_CTRL_STOP = 0x20
RTC_CTRL_TITP = 0x10
RTC_CTRL_AF = 0x08
RTC_CTRL_TF = 0x04
RTC_CTRL_AIE = 0x02
RTC_CTRL_TIE = 0x01
RTC_COT_DISABLE = 0x00
RTC_COT_32KHZ = 0x80
RTC_COT_1KHZ = 0x81
RTC_COT_32HZ = 0x82
RTC_COT_1HZ = 0x83
RTC_TIMER_DISABLE = 0x00
RTC_TIMER_4KHZ = 0x80
RTC_TIMER_64HZ = 0x81
RTC_TIMER_1S = 0x82
RTC_TIMER_60S = 0x83
RTC_ALARM_DISABLE = 0x80
RTC_ALARM_ENABLE = 0x00
)