mirror of
https://github.com/hybridgroup/gobot.git
synced 2025-04-24 13:48:49 +08:00
i2c PCF8583 clock and counter driver introduced
This commit is contained in:
parent
d827c913c5
commit
9d1fd13a2f
@ -288,6 +288,7 @@ drivers provided using the `gobot/drivers/i2c` package:
|
||||
- MPU6050 Accelerometer/Gyroscope
|
||||
- PCA9501 8-bit I/O port with interrupt, 2-kbit EEPROM
|
||||
- PCA9685 16-channel 12-bit PWM/Servo Driver
|
||||
- PCF8583 clock and calendar or event counter, 240 x 8-bit RAM
|
||||
- PCF8591 8-bit 4xA/D & 1xD/A converter
|
||||
- SHT2x Temperature/Humidity
|
||||
- SHT3x-D Temperature/Humidity
|
||||
|
@ -40,6 +40,7 @@ Gobot has a extensible system for connecting to hardware devices. The following
|
||||
- MPU6050 Accelerometer/Gyroscope
|
||||
- PCA9501 8-bit I/O port with interrupt, 2-kbit EEPROM
|
||||
- PCA9685 16-channel 12-bit PWM/Servo Driver
|
||||
- PCF8583 clock and calendar or event counter, 240 x 8-bit RAM
|
||||
- PCF8591 8-bit 4xA/D & 1xD/A converter
|
||||
- SHT2x Temperature/Humidity
|
||||
- SHT3x-D Temperature/Humidity
|
||||
|
360
drivers/i2c/pcf8583_driver.go
Normal file
360
drivers/i2c/pcf8583_driver.go
Normal file
@ -0,0 +1,360 @@
|
||||
package i2c
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
pcf8583Debug = true
|
||||
|
||||
// PCF8583 supports addresses 0x50 and 0x51
|
||||
// The default address applies when the address pin is grounded.
|
||||
pcf8583DefaultAddress = 0x50
|
||||
|
||||
// default is 0x10, when set to 0 also some free or unused RAM can be accessed
|
||||
pcf8583RamOffset = 0x10
|
||||
)
|
||||
|
||||
// PCF8583Control is used to specify control and status register content
|
||||
type PCF8583Control uint8
|
||||
|
||||
const (
|
||||
// registers are named according to the datasheet
|
||||
pcf8583Reg_CTRL = iota // 0x00
|
||||
pcf8583Reg_SUBSEC_D0D1 // 0x01
|
||||
pcf8583Reg_SEC_D2D3 // 0x02
|
||||
pcf8583Reg_MIN_D4D5 // 0x03
|
||||
pcf8583Reg_HOUR // 0x04
|
||||
pcf8583Reg_YEARDATE // 0x05
|
||||
pcf8583Reg_WEEKDAYMONTH // 0x06
|
||||
pcf8583Reg_TIMER // 0x07
|
||||
pcf8583Reg_ALARMCTRL // 0x08, offset for all alarm registers 0x09 ... 0xF
|
||||
|
||||
pcf8583CtrlTimerFlag PCF8583Control = 0x01 // 50% duty factor, seconds flag if alarm enable bit is 0
|
||||
pcf8583CtrlAlarmFlag PCF8583Control = 0x02 // 50% duty factor, minutes flag if alarm enable bit is 0
|
||||
pcf8583CtrlAlarmEnable PCF8583Control = 0x04 // if enabled, memory 08h is alarm control register
|
||||
pcf8583CtrlMask PCF8583Control = 0x08 // 0: read 05h, 06h unmasked, 1: read date and month count directly
|
||||
PCF8583CtrlModeClock50 PCF8583Control = 0x10 // clock mode with 50 Hz
|
||||
PCF8583CtrlModeCounter PCF8583Control = 0x20 // event counter mode
|
||||
PCF8583CtrlModeTest PCF8583Control = 0x30 // test mode
|
||||
pcf8583CtrlHoldLastCount PCF8583Control = 0x40 // 0: count, 1: store and hold count in capture latches
|
||||
pcf8583CtrlStopCounting PCF8583Control = 0x80 // 0: count, 1: stop counting, reset divider
|
||||
)
|
||||
|
||||
// PCF8583Driver is a Gobot Driver for the PCF8583 clock and calendar chip & 240 x 8-bit bit RAM with 1 address program pin.
|
||||
// please refer to data sheet: https://www.nxp.com/docs/en/data-sheet/PCF8583.pdf
|
||||
//
|
||||
// 0 1 0 1 0 0 0 A0|rd
|
||||
// Lowest bit (rd) is mapped to switch between write(0)/read(1), it is not part of the "real" address.
|
||||
//
|
||||
// PCF8583 is mainly compatible to PCF8593, so this driver should also work for PCF8593 except RAM calls
|
||||
//
|
||||
// This driver was tested with Tinkerboard.
|
||||
type PCF8583Driver struct {
|
||||
*Driver
|
||||
mode PCF8583Control // clock 32.768kHz (default), clock 50Hz, event counter
|
||||
yearOffset int
|
||||
ramOffset byte
|
||||
}
|
||||
|
||||
// NewPCF8583Driver creates a new driver with specified i2c interface
|
||||
// Params:
|
||||
// c Connector - the Adaptor to use with this Driver
|
||||
//
|
||||
// Optional params:
|
||||
// i2c.WithBus(int): bus to use with this driver
|
||||
// i2c.WithAddress(int): address to use with this driver
|
||||
// i2c.WithPCF8583Mode(PCF8583Control): mode of this driver
|
||||
//
|
||||
func NewPCF8583Driver(c Connector, options ...func(Config)) *PCF8583Driver {
|
||||
d := &PCF8583Driver{
|
||||
Driver: NewDriver(c, "PCF8583", pcf8583DefaultAddress),
|
||||
ramOffset: pcf8583RamOffset,
|
||||
}
|
||||
d.afterStart = d.initialize
|
||||
|
||||
for _, option := range options {
|
||||
option(d)
|
||||
}
|
||||
|
||||
// API commands
|
||||
d.AddCommand("WriteTime", func(params map[string]interface{}) interface{} {
|
||||
val := params["val"].(time.Time)
|
||||
err := d.WriteTime(val)
|
||||
return map[string]interface{}{"err": err}
|
||||
})
|
||||
|
||||
d.AddCommand("ReadTime", func(params map[string]interface{}) interface{} {
|
||||
val, err := d.ReadTime()
|
||||
return map[string]interface{}{"val": val, "err": err}
|
||||
})
|
||||
|
||||
d.AddCommand("WriteCounter", func(params map[string]interface{}) interface{} {
|
||||
val := params["val"].(int32)
|
||||
err := d.WriteCounter(val)
|
||||
return map[string]interface{}{"err": err}
|
||||
})
|
||||
|
||||
d.AddCommand("ReadCounter", func(params map[string]interface{}) interface{} {
|
||||
val, err := d.ReadCounter()
|
||||
return map[string]interface{}{"val": val, "err": err}
|
||||
})
|
||||
|
||||
d.AddCommand("WriteRAM", func(params map[string]interface{}) interface{} {
|
||||
address := params["address"].(uint8)
|
||||
val := params["val"].(uint8)
|
||||
err := d.WriteRAM(address, val)
|
||||
return map[string]interface{}{"err": err}
|
||||
})
|
||||
|
||||
d.AddCommand("ReadRAM", func(params map[string]interface{}) interface{} {
|
||||
address := params["address"].(uint8)
|
||||
val, err := d.ReadRAM(address)
|
||||
return map[string]interface{}{"val": val, "err": err}
|
||||
})
|
||||
return d
|
||||
}
|
||||
|
||||
// WithPCF8583Mode is used to change the mode between 32.678kHz clock, 50Hz clock, event counter
|
||||
// Valid settings are of type "PCF8583Control"
|
||||
func WithPCF8583Mode(mode PCF8583Control) func(Config) {
|
||||
return func(c Config) {
|
||||
d, ok := c.(*PCF8583Driver)
|
||||
if ok {
|
||||
if !mode.isClockMode() && !mode.isCounterMode() {
|
||||
panic(fmt.Sprintf("%s: mode 0x%02x is not supported", d.name, mode))
|
||||
}
|
||||
d.mode = mode
|
||||
} else if pcf8583Debug {
|
||||
log.Printf("trying to set mode for non-PCF8583Driver %v", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WriteTime setup the clock registers with the given time
|
||||
func (d *PCF8583Driver) WriteTime(val time.Time) error {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
// according to chapter 7.11 of the product data sheet, the stop counting flag of the control/status register
|
||||
// must be set before, so we read the control byte before and only set/reset the stop
|
||||
ctrlRegVal, err := d.connection.ReadByteData(uint8(pcf8583Reg_CTRL))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !PCF8583Control(ctrlRegVal).isClockMode() {
|
||||
return fmt.Errorf("%s: can't write time because the device is in wrong mode 0x%02x", d.name, ctrlRegVal)
|
||||
}
|
||||
// auto increment feature is used
|
||||
year, month, day := val.Date()
|
||||
written, err := d.connection.Write([]byte{
|
||||
uint8(pcf8583Reg_CTRL), ctrlRegVal | uint8(pcf8583CtrlStopCounting),
|
||||
pcf8583encodeBcd(uint8(val.Nanosecond() / 1000000 / 10)), // sub seconds in 1/10th seconds
|
||||
pcf8583encodeBcd(uint8(val.Second())),
|
||||
pcf8583encodeBcd(uint8(val.Minute())),
|
||||
pcf8583encodeBcd(uint8(val.Hour())),
|
||||
pcf8583encodeBcd(uint8(day)), // year, date (we keep the year counter zero and set the offset)
|
||||
uint8(val.Weekday())<<5 | pcf8583encodeBcd(uint8(month)), // month, weekday (not BCD): Sunday = 0, Monday = 1 ...
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if written != 8 {
|
||||
return fmt.Errorf("%s: %d bytes written, but %d expected", d.name, written, 8)
|
||||
}
|
||||
d.yearOffset = year
|
||||
return d.run(ctrlRegVal)
|
||||
}
|
||||
|
||||
// ReadTime reads the clock and returns the value
|
||||
func (d *PCF8583Driver) ReadTime() (val time.Time, err error) {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
// according to chapter 7.1 of the product data sheet, the setting of "hold last count" flag
|
||||
// is not needed when reading with auto increment
|
||||
ctrlRegVal, err := d.connection.ReadByteData(uint8(pcf8583Reg_CTRL))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !PCF8583Control(ctrlRegVal).isClockMode() {
|
||||
return val, fmt.Errorf("%s: can't read time because the device is in wrong mode 0x%02x", d.name, ctrlRegVal)
|
||||
}
|
||||
// auto increment feature is used
|
||||
clockDataSize := 6
|
||||
data := make([]byte, clockDataSize)
|
||||
read, err := d.connection.Read(data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if read != clockDataSize {
|
||||
return val, fmt.Errorf("%s: %d bytes read, but %d expected", d.name, read, clockDataSize)
|
||||
}
|
||||
nanos := int(pcf8583decodeBcd(data[0])) * 1000000 * 10 // sub seconds in 1/10th seconds
|
||||
seconds := int(pcf8583decodeBcd(data[1]))
|
||||
minutes := int(pcf8583decodeBcd(data[2]))
|
||||
hours := int(pcf8583decodeBcd(data[3]))
|
||||
// year, date (the device can only count 4 years)
|
||||
year := int(data[4]>>6) + d.yearOffset // use the first two bits, no BCD
|
||||
date := int(pcf8583decodeBcd(data[4] & 0x3F)) // remove the year-bits for date
|
||||
// weekday (not used here), month
|
||||
month := time.Month(pcf8583decodeBcd(data[5] & 0x1F)) // remove the weekday-bits
|
||||
return time.Date(year, month, date, hours, minutes, seconds, nanos, time.UTC), nil
|
||||
}
|
||||
|
||||
// WriteCounter writes the counter registers
|
||||
func (d *PCF8583Driver) WriteCounter(val int32) error {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
// we don't care of negative values here
|
||||
// according to chapter 7.11 of the product data sheet, the stop counting flag of the control/status register
|
||||
// must be set before, so we read the control byte before and only set/reset the stop
|
||||
ctrlRegVal, err := d.connection.ReadByteData(uint8(pcf8583Reg_CTRL))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !PCF8583Control(ctrlRegVal).isCounterMode() {
|
||||
return fmt.Errorf("%s: can't write counter because the device is in wrong mode 0x%02x", d.name, ctrlRegVal)
|
||||
}
|
||||
// auto increment feature is used, PCF8583 not working with WriteBlockData
|
||||
written, err := d.connection.Write([]byte{
|
||||
uint8(pcf8583Reg_CTRL), ctrlRegVal | uint8(pcf8583CtrlStopCounting), // stop
|
||||
pcf8583encodeBcd(uint8(val % 100)), // 2 lowest digits
|
||||
pcf8583encodeBcd(uint8((val / 100) % 100)), // 2 middle digits
|
||||
pcf8583encodeBcd(uint8((val / 10000) % 100)), // 2 highest digits
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if written != 5 {
|
||||
return fmt.Errorf("%s: %d bytes written, but %d expected", d.name, written, 5)
|
||||
}
|
||||
return d.run(ctrlRegVal)
|
||||
}
|
||||
|
||||
// ReadCounter reads the counter registers
|
||||
func (d *PCF8583Driver) ReadCounter() (val int32, err error) {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
// according to chapter 7.1 of the product data sheet, the setting of "hold last count" flag
|
||||
// is not needed when reading with auto increment
|
||||
ctrlRegVal, err := d.connection.ReadByteData(uint8(pcf8583Reg_CTRL))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !PCF8583Control(ctrlRegVal).isCounterMode() {
|
||||
return val, fmt.Errorf("%s: can't read counter because the device is in wrong mode 0x%02x", d.name, ctrlRegVal)
|
||||
}
|
||||
// auto increment feature is used
|
||||
counterDataSize := 3
|
||||
data := make([]byte, counterDataSize)
|
||||
read, err := d.connection.Read(data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if read != counterDataSize {
|
||||
return val, fmt.Errorf("%s: %d bytes read, but %d expected", d.name, read, counterDataSize)
|
||||
}
|
||||
return int32(pcf8583decodeBcd(data[0])) +
|
||||
int32(pcf8583decodeBcd(data[1]))*100 +
|
||||
int32(pcf8583decodeBcd(data[2]))*10000, nil
|
||||
}
|
||||
|
||||
// WriteRAM writes a value to a given address in memory (0x00-0xFF)
|
||||
func (d *PCF8583Driver) WriteRAM(address uint8, val uint8) error {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
realAddress := uint16(address) + uint16(d.ramOffset)
|
||||
if realAddress > 0xFF {
|
||||
return fmt.Errorf("%s: RAM address overflow %d", d.name, realAddress)
|
||||
}
|
||||
return d.connection.WriteByteData(uint8(realAddress), val)
|
||||
}
|
||||
|
||||
// ReadRAM reads a value from a given address (0x00-0xFF)
|
||||
func (d *PCF8583Driver) ReadRAM(address uint8) (val uint8, err error) {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
realAddress := uint16(address) + uint16(d.ramOffset)
|
||||
if realAddress > 0xFF {
|
||||
return val, fmt.Errorf("%s: RAM address overflow %d", d.name, realAddress)
|
||||
}
|
||||
return d.connection.ReadByteData(uint8(realAddress))
|
||||
}
|
||||
|
||||
func (d *PCF8583Driver) run(ctrlRegVal uint8) error {
|
||||
ctrlRegVal = ctrlRegVal & ^uint8(pcf8583CtrlStopCounting) // reset stop bit
|
||||
return d.connection.WriteByteData(uint8(pcf8583Reg_CTRL), ctrlRegVal)
|
||||
}
|
||||
|
||||
func (d *PCF8583Driver) initialize() error {
|
||||
// switch to configured mode
|
||||
ctrlRegVal, err := d.connection.ReadByteData(uint8(pcf8583Reg_CTRL))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.mode.isModeDiffer(PCF8583Control(ctrlRegVal)) {
|
||||
ctrlRegVal = ctrlRegVal&^uint8(PCF8583CtrlModeTest) | uint8(d.mode)
|
||||
if err = d.connection.WriteByteData(uint8(pcf8583Reg_CTRL), ctrlRegVal); err != nil {
|
||||
return err
|
||||
}
|
||||
if pcf8583Debug {
|
||||
if PCF8583Control(ctrlRegVal).isCounterMode() {
|
||||
log.Printf("%s switched to counter mode 0x%02x", d.name, ctrlRegVal)
|
||||
} else {
|
||||
log.Printf("%s switched to clock mode 0x%02x", d.name, ctrlRegVal)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c PCF8583Control) isClockMode() bool {
|
||||
return uint8(c)&uint8(PCF8583CtrlModeCounter) == 0
|
||||
}
|
||||
|
||||
func (c PCF8583Control) isCounterMode() bool {
|
||||
counterModeSet := (uint8(c) & uint8(PCF8583CtrlModeCounter)) != 0
|
||||
clockMode50Set := (uint8(c) & uint8(PCF8583CtrlModeClock50)) != 0
|
||||
return counterModeSet && !clockMode50Set
|
||||
}
|
||||
|
||||
func (c PCF8583Control) isModeDiffer(mode PCF8583Control) bool {
|
||||
return uint8(c)&uint8(PCF8583CtrlModeTest) != uint8(mode)&uint8(PCF8583CtrlModeTest)
|
||||
}
|
||||
|
||||
func pcf8583encodeBcd(val byte) byte {
|
||||
// decimal 12 => 0x12
|
||||
if val > 99 {
|
||||
val = 99
|
||||
if pcf8583Debug {
|
||||
log.Printf("PCF8583 BCD value (%d) exceeds limit of 99, now limited.", val)
|
||||
}
|
||||
}
|
||||
hi, lo := byte(val/10), byte(val%10)
|
||||
return hi<<4 | lo
|
||||
}
|
||||
|
||||
func pcf8583decodeBcd(bcd byte) byte {
|
||||
// 0x12 => decimal 12
|
||||
hi, lo := byte(bcd>>4), byte(bcd&0x0f)
|
||||
if hi > 9 {
|
||||
hi = 9
|
||||
if pcf8583Debug {
|
||||
log.Printf("PCF8583 BCD value (%02x) exceeds limit 0x99 on most significant digit, now limited", bcd)
|
||||
}
|
||||
}
|
||||
if lo > 9 {
|
||||
lo = 9
|
||||
if pcf8583Debug {
|
||||
log.Printf("PCF8583 BCD value (%02x) exceeds limit 0x99 on least significant digit, now limited", bcd)
|
||||
}
|
||||
}
|
||||
return 10*hi + lo
|
||||
}
|
612
drivers/i2c/pcf8583_driver_test.go
Normal file
612
drivers/i2c/pcf8583_driver_test.go
Normal file
@ -0,0 +1,612 @@
|
||||
package i2c
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gobot.io/x/gobot"
|
||||
"gobot.io/x/gobot/gobottest"
|
||||
)
|
||||
|
||||
// this ensures that the implementation is based on i2c.Driver, which implements the gobot.Driver
|
||||
// and tests all implementations, so no further tests needed here for gobot.Driver interface
|
||||
var _ gobot.Driver = (*PCF8583Driver)(nil)
|
||||
|
||||
func initTestPCF8583WithStubbedAdaptor() (*PCF8583Driver, *i2cTestAdaptor) {
|
||||
a := newI2cTestAdaptor()
|
||||
d := NewPCF8583Driver(a)
|
||||
d.Start()
|
||||
return d, a
|
||||
}
|
||||
|
||||
func TestNewPCF8583Driver(t *testing.T) {
|
||||
var di interface{} = NewPCF8583Driver(newI2cTestAdaptor())
|
||||
d, ok := di.(*PCF8583Driver)
|
||||
if !ok {
|
||||
t.Errorf("NewPCF8583Driver() should have returned a *PCF8583Driver")
|
||||
}
|
||||
gobottest.Refute(t, d.Driver, nil)
|
||||
gobottest.Assert(t, strings.HasPrefix(d.name, "PCF8583"), true)
|
||||
gobottest.Assert(t, d.mode, PCF8583Control(0x00))
|
||||
gobottest.Assert(t, d.yearOffset, 0)
|
||||
gobottest.Assert(t, d.ramOffset, uint8(0x10))
|
||||
}
|
||||
|
||||
func TestPCF8583Options(t *testing.T) {
|
||||
// This is a general test, that options are applied in constructor by using the common WithBus() option and
|
||||
// least one of this driver. Further tests for options can also be done by call of "WithOption(val)(d)".
|
||||
d := NewPCF8583Driver(newI2cTestAdaptor(), WithBus(2), WithPCF8583Mode(PCF8583CtrlModeClock50))
|
||||
gobottest.Assert(t, d.GetBusOrDefault(1), 2)
|
||||
gobottest.Assert(t, d.mode, PCF8583CtrlModeClock50)
|
||||
}
|
||||
|
||||
func TestPCF8583CommandsWriteTime(t *testing.T) {
|
||||
// arrange
|
||||
d, a := initTestPCF8583WithStubbedAdaptor()
|
||||
readCtrlState := uint8(0x10) // clock 50Hz
|
||||
// arrange writes
|
||||
a.i2cWriteImpl = func(b []byte) (int, error) {
|
||||
return len(b), nil
|
||||
}
|
||||
// arrange reads
|
||||
numCallsRead := 0
|
||||
a.i2cReadImpl = func(b []byte) (int, error) {
|
||||
numCallsRead++
|
||||
b[len(b)-1] = readCtrlState
|
||||
return len(b), nil
|
||||
}
|
||||
// act
|
||||
result := d.Command("WriteTime")(map[string]interface{}{"val": time.Now()})
|
||||
// assert
|
||||
gobottest.Assert(t, result.(map[string]interface{})["err"], nil)
|
||||
}
|
||||
|
||||
func TestPCF8583CommandsReadTime(t *testing.T) {
|
||||
// arrange
|
||||
d, a := initTestPCF8583WithStubbedAdaptor()
|
||||
d.yearOffset = 2019
|
||||
milliSec := 550 * time.Millisecond // 0.55 sec = 550 ms
|
||||
want := time.Date(2021, time.December, 24, 18, 00, 00, int(milliSec), time.UTC)
|
||||
reg0Val := uint8(0x00) // clock mode 32.768 kHz
|
||||
reg1Val := uint8(0x55) // BCD: 1/10 and 1/100 sec (55)
|
||||
reg2Val := uint8(0x00) // BCD: 10 and 1 sec (00)
|
||||
reg3Val := uint8(0x00) // BCD: 10 and 1 min (00)
|
||||
reg4Val := uint8(0x18) // BCD: 10 and 1 hour (18)
|
||||
reg5Val := uint8(0xA4) // year (2) and BCD: date (24)
|
||||
reg6Val := uint8(0xB2) // weekday 5, bit 5 and bit 7 (0xA0) and BCD: month (0x12)
|
||||
returnRead := [2][]uint8{
|
||||
{reg0Val},
|
||||
{reg1Val, reg2Val, reg3Val, reg4Val, reg5Val, reg6Val},
|
||||
}
|
||||
// arrange reads
|
||||
// arrange reads
|
||||
numCallsRead := 0
|
||||
a.i2cReadImpl = func(b []byte) (int, error) {
|
||||
numCallsRead++
|
||||
rr := returnRead[numCallsRead-1]
|
||||
for i := 0; i < len(b); i++ {
|
||||
b[i] = rr[i]
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
// act
|
||||
result := d.Command("ReadTime")(map[string]interface{}{})
|
||||
// assert
|
||||
gobottest.Assert(t, result.(map[string]interface{})["err"], nil)
|
||||
gobottest.Assert(t, result.(map[string]interface{})["val"], want)
|
||||
}
|
||||
|
||||
func TestPCF8583CommandsWriteCounter(t *testing.T) {
|
||||
// arrange
|
||||
d, a := initTestPCF8583WithStubbedAdaptor()
|
||||
readCtrlState := uint8(0x20) // counter
|
||||
// arrange writes
|
||||
a.i2cWriteImpl = func(b []byte) (int, error) {
|
||||
return len(b), nil
|
||||
}
|
||||
// arrange reads
|
||||
numCallsRead := 0
|
||||
a.i2cReadImpl = func(b []byte) (int, error) {
|
||||
numCallsRead++
|
||||
b[len(b)-1] = readCtrlState
|
||||
return len(b), nil
|
||||
}
|
||||
// act
|
||||
result := d.Command("WriteCounter")(map[string]interface{}{"val": int32(123456)})
|
||||
// assert
|
||||
gobottest.Assert(t, result.(map[string]interface{})["err"], nil)
|
||||
}
|
||||
|
||||
func TestPCF8583CommandsReadCounter(t *testing.T) {
|
||||
// arrange
|
||||
d, a := initTestPCF8583WithStubbedAdaptor()
|
||||
want := int32(123456)
|
||||
reg0Val := uint8(0x20) // counter mode
|
||||
reg1Val := uint8(0x56) // BCD: 56
|
||||
reg2Val := uint8(0x34) // BCD: 34
|
||||
reg3Val := uint8(0x12) // BCD: 12
|
||||
returnRead := [2][]uint8{
|
||||
{reg0Val},
|
||||
{reg1Val, reg2Val, reg3Val},
|
||||
}
|
||||
// arrange reads
|
||||
// arrange reads
|
||||
numCallsRead := 0
|
||||
a.i2cReadImpl = func(b []byte) (int, error) {
|
||||
numCallsRead++
|
||||
rr := returnRead[numCallsRead-1]
|
||||
for i := 0; i < len(b); i++ {
|
||||
b[i] = rr[i]
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
// act
|
||||
result := d.Command("ReadCounter")(map[string]interface{}{})
|
||||
// assert
|
||||
gobottest.Assert(t, result.(map[string]interface{})["err"], nil)
|
||||
gobottest.Assert(t, result.(map[string]interface{})["val"], want)
|
||||
}
|
||||
|
||||
func TestPCF8583CommandsWriteRAM(t *testing.T) {
|
||||
// arrange
|
||||
d, _ := initTestPCF8583WithStubbedAdaptor()
|
||||
var addressValue = map[string]interface{}{
|
||||
"address": uint8(0x12),
|
||||
"val": uint8(0x45),
|
||||
}
|
||||
// act
|
||||
result := d.Command("WriteRAM")(addressValue)
|
||||
// assert
|
||||
gobottest.Assert(t, result.(map[string]interface{})["err"], nil)
|
||||
}
|
||||
|
||||
func TestPCF8583CommandsReadRAM(t *testing.T) {
|
||||
// arrange
|
||||
d, _ := initTestPCF8583WithStubbedAdaptor()
|
||||
var address = map[string]interface{}{
|
||||
"address": uint8(0x34),
|
||||
}
|
||||
// act
|
||||
result := d.Command("ReadRAM")(address)
|
||||
// assert
|
||||
gobottest.Assert(t, result.(map[string]interface{})["err"], nil)
|
||||
gobottest.Assert(t, result.(map[string]interface{})["val"], uint8(0))
|
||||
}
|
||||
|
||||
func TestPCF8583WriteTime(t *testing.T) {
|
||||
// sequence to write the time:
|
||||
// * read control register for get current state and ensure an clock mode is set
|
||||
// * write the control register (stop counting)
|
||||
// * create the values for date registers (default is 24h mode)
|
||||
// * write the clock and calendar registers with auto increment
|
||||
// * write the control register (start counting)
|
||||
// arrange
|
||||
d, a := initTestPCF8583WithStubbedAdaptor()
|
||||
a.written = []byte{} // reset writes of Start() and former test
|
||||
readCtrlState := uint8(0x07) // 32.768kHz clock mode
|
||||
milliSec := 210 * time.Millisecond // 0.21 sec = 210 ms
|
||||
initDate := time.Date(2022, time.December, 16, 15, 14, 13, int(milliSec), time.UTC)
|
||||
wantCtrlStop := uint8(0x87) // stop counting bit is set
|
||||
wantReg1Val := uint8(0x21) // BCD: 1/10 and 1/100 sec (21)
|
||||
wantReg2Val := uint8(0x13) // BCD: 10 and 1 sec (13)
|
||||
wantReg3Val := uint8(0x14) // BCD: 10 and 1 min (14)
|
||||
wantReg4Val := uint8(0x15) // BCD: 10 and 1 hour (15)
|
||||
wantReg5Val := uint8(0x16) // year (0) and BCD: date (16)
|
||||
wantReg6Val := uint8(0xB2) // weekday 5, bit 5 and bit 7 (0xA0) and BCD: month (0x12)
|
||||
wantCrtlStart := uint8(0x07) // stop counting bit is reset
|
||||
// arrange writes
|
||||
a.i2cWriteImpl = func(b []byte) (int, error) {
|
||||
return len(b), nil
|
||||
}
|
||||
// arrange reads
|
||||
numCallsRead := 0
|
||||
a.i2cReadImpl = func(b []byte) (int, error) {
|
||||
numCallsRead++
|
||||
b[len(b)-1] = readCtrlState
|
||||
return len(b), nil
|
||||
}
|
||||
// act
|
||||
err := d.WriteTime(initDate)
|
||||
// assert
|
||||
gobottest.Assert(t, err, nil)
|
||||
gobottest.Assert(t, d.yearOffset, initDate.Year())
|
||||
gobottest.Assert(t, numCallsRead, 1)
|
||||
gobottest.Assert(t, len(a.written), 11)
|
||||
gobottest.Assert(t, a.written[0], uint8(pcf8583Reg_CTRL))
|
||||
gobottest.Assert(t, a.written[1], uint8(pcf8583Reg_CTRL))
|
||||
gobottest.Assert(t, a.written[2], wantCtrlStop)
|
||||
gobottest.Assert(t, a.written[3], wantReg1Val)
|
||||
gobottest.Assert(t, a.written[4], wantReg2Val)
|
||||
gobottest.Assert(t, a.written[5], wantReg3Val)
|
||||
gobottest.Assert(t, a.written[6], wantReg4Val)
|
||||
gobottest.Assert(t, a.written[7], wantReg5Val)
|
||||
gobottest.Assert(t, a.written[8], wantReg6Val)
|
||||
gobottest.Assert(t, a.written[9], uint8(pcf8583Reg_CTRL))
|
||||
gobottest.Assert(t, a.written[10], wantCrtlStart)
|
||||
}
|
||||
|
||||
func TestPCF8583WriteTimeNoTimeModeFails(t *testing.T) {
|
||||
// arrange
|
||||
d, a := initTestPCF8583WithStubbedAdaptor()
|
||||
a.written = []byte{} // reset writes of Start() and former test
|
||||
readCtrlState := uint8(0x30) // test mode
|
||||
// arrange writes
|
||||
a.i2cWriteImpl = func(b []byte) (int, error) {
|
||||
return len(b), nil
|
||||
}
|
||||
// arrange reads
|
||||
numCallsRead := 0
|
||||
a.i2cReadImpl = func(b []byte) (int, error) {
|
||||
numCallsRead++
|
||||
b[len(b)-1] = readCtrlState
|
||||
return len(b), nil
|
||||
}
|
||||
// act
|
||||
err := d.WriteTime(time.Now())
|
||||
// assert
|
||||
gobottest.Refute(t, err, nil)
|
||||
gobottest.Assert(t, strings.Contains(err.Error(), "wrong mode 0x30"), true)
|
||||
gobottest.Assert(t, len(a.written), 1)
|
||||
gobottest.Assert(t, a.written[0], uint8(pcf8583Reg_CTRL))
|
||||
gobottest.Assert(t, numCallsRead, 1)
|
||||
}
|
||||
|
||||
func TestPCF8583ReadTime(t *testing.T) {
|
||||
// sequence to read the time:
|
||||
// * read the control register to determine mask flag and ensure an clock mode is set
|
||||
// * read the clock and calendar registers with auto increment
|
||||
// * create the value out of registers content
|
||||
// arrange
|
||||
d, a := initTestPCF8583WithStubbedAdaptor()
|
||||
a.written = []byte{} // reset writes of Start() and former test
|
||||
d.yearOffset = 2020
|
||||
milliSec := 210 * time.Millisecond // 0.21 sec = 210 ms
|
||||
want := time.Date(2022, time.December, 16, 15, 14, 13, int(milliSec), time.UTC)
|
||||
reg0Val := uint8(0x10) // clock mode 50Hz
|
||||
reg1Val := uint8(0x21) // BCD: 1/10 and 1/100 sec (21)
|
||||
reg2Val := uint8(0x13) // BCD: 10 and 1 sec (13)
|
||||
reg3Val := uint8(0x14) // BCD: 10 and 1 min (14)
|
||||
reg4Val := uint8(0x15) // BCD: 10 and 1 hour (15)
|
||||
reg5Val := uint8(0x96) // year (2) and BCD: date (16)
|
||||
reg6Val := uint8(0xB2) // weekday 5, bit 5 and bit 7 (0xA0) and BCD: month (0x12)
|
||||
returnRead := [2][]uint8{
|
||||
{reg0Val},
|
||||
{reg1Val, reg2Val, reg3Val, reg4Val, reg5Val, reg6Val},
|
||||
}
|
||||
// arrange writes
|
||||
a.i2cWriteImpl = func(b []byte) (int, error) {
|
||||
return len(b), nil
|
||||
}
|
||||
// arrange reads
|
||||
numCallsRead := 0
|
||||
a.i2cReadImpl = func(b []byte) (int, error) {
|
||||
numCallsRead++
|
||||
rr := returnRead[numCallsRead-1]
|
||||
for i := 0; i < len(b); i++ {
|
||||
b[i] = rr[i]
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
// act
|
||||
got, err := d.ReadTime()
|
||||
// assert
|
||||
gobottest.Assert(t, err, nil)
|
||||
gobottest.Assert(t, len(a.written), 1)
|
||||
gobottest.Assert(t, a.written[0], uint8(pcf8583Reg_CTRL))
|
||||
gobottest.Assert(t, numCallsRead, 2)
|
||||
gobottest.Assert(t, got, want)
|
||||
}
|
||||
|
||||
func TestPCF8583ReadTimeNoTimeModeFails(t *testing.T) {
|
||||
// arrange
|
||||
d, a := initTestPCF8583WithStubbedAdaptor()
|
||||
a.written = []byte{} // reset writes of Start() and former test
|
||||
readCtrlState := uint8(0x20) // counter mode
|
||||
// arrange writes
|
||||
a.i2cWriteImpl = func(b []byte) (int, error) {
|
||||
return len(b), nil
|
||||
}
|
||||
// arrange reads
|
||||
numCallsRead := 0
|
||||
a.i2cReadImpl = func(b []byte) (int, error) {
|
||||
numCallsRead++
|
||||
b[len(b)-1] = readCtrlState
|
||||
return len(b), nil
|
||||
}
|
||||
// act
|
||||
got, err := d.ReadTime()
|
||||
// assert
|
||||
gobottest.Refute(t, err, nil)
|
||||
gobottest.Assert(t, strings.Contains(err.Error(), "wrong mode 0x20"), true)
|
||||
gobottest.Assert(t, got, time.Time{})
|
||||
gobottest.Assert(t, len(a.written), 1)
|
||||
gobottest.Assert(t, a.written[0], uint8(pcf8583Reg_CTRL))
|
||||
gobottest.Assert(t, numCallsRead, 1)
|
||||
}
|
||||
|
||||
func TestPCF8583WriteCounter(t *testing.T) {
|
||||
// sequence to write the counter:
|
||||
// * read control register for get current state and ensure the event counter mode is set
|
||||
// * write the control register (stop counting)
|
||||
// * create the values for counter registers
|
||||
// * write the counter registers
|
||||
// * write the control register (start counting)
|
||||
// arrange
|
||||
d, a := initTestPCF8583WithStubbedAdaptor()
|
||||
a.written = []byte{} // reset writes of Start() and former test
|
||||
readCtrlState := uint8(0x27) // counter mode
|
||||
initCount := int32(654321) // 6 digits used of 10 possible with int32
|
||||
wantCtrlStop := uint8(0xA7) // stop counting bit is set
|
||||
wantReg1Val := uint8(0x21) // BCD: 21
|
||||
wantReg2Val := uint8(0x43) // BCD: 43
|
||||
wantReg3Val := uint8(0x65) // BCD: 65
|
||||
wantCtrlStart := uint8(0x27) // counter mode
|
||||
// arrange writes
|
||||
a.i2cWriteImpl = func(b []byte) (int, error) {
|
||||
return len(b), nil
|
||||
}
|
||||
// arrange reads
|
||||
numCallsRead := 0
|
||||
a.i2cReadImpl = func(b []byte) (int, error) {
|
||||
numCallsRead++
|
||||
b[len(b)-1] = readCtrlState
|
||||
return len(b), nil
|
||||
}
|
||||
// act
|
||||
err := d.WriteCounter(initCount)
|
||||
// assert
|
||||
gobottest.Assert(t, err, nil)
|
||||
gobottest.Assert(t, numCallsRead, 1)
|
||||
gobottest.Assert(t, len(a.written), 8)
|
||||
gobottest.Assert(t, a.written[0], uint8(pcf8583Reg_CTRL))
|
||||
gobottest.Assert(t, a.written[1], uint8(pcf8583Reg_CTRL))
|
||||
gobottest.Assert(t, a.written[2], wantCtrlStop)
|
||||
gobottest.Assert(t, a.written[3], wantReg1Val)
|
||||
gobottest.Assert(t, a.written[4], wantReg2Val)
|
||||
gobottest.Assert(t, a.written[5], wantReg3Val)
|
||||
gobottest.Assert(t, a.written[6], uint8(pcf8583Reg_CTRL))
|
||||
gobottest.Assert(t, a.written[7], wantCtrlStart)
|
||||
}
|
||||
|
||||
func TestPCF8583WriteCounterNoCounterModeFails(t *testing.T) {
|
||||
// arrange
|
||||
d, a := initTestPCF8583WithStubbedAdaptor()
|
||||
a.written = []byte{} // reset writes of Start() and former test
|
||||
readCtrlState := uint8(0x10) // 50Hz mode
|
||||
// arrange writes
|
||||
a.i2cWriteImpl = func(b []byte) (int, error) {
|
||||
return len(b), nil
|
||||
}
|
||||
// arrange reads
|
||||
numCallsRead := 0
|
||||
a.i2cReadImpl = func(b []byte) (int, error) {
|
||||
numCallsRead++
|
||||
b[len(b)-1] = readCtrlState
|
||||
return len(b), nil
|
||||
}
|
||||
// act
|
||||
err := d.WriteCounter(123)
|
||||
// assert
|
||||
gobottest.Refute(t, err, nil)
|
||||
gobottest.Assert(t, strings.Contains(err.Error(), "wrong mode 0x10"), true)
|
||||
gobottest.Assert(t, len(a.written), 1)
|
||||
gobottest.Assert(t, a.written[0], uint8(pcf8583Reg_CTRL))
|
||||
gobottest.Assert(t, numCallsRead, 1)
|
||||
}
|
||||
|
||||
func TestPCF8583ReadCounter(t *testing.T) {
|
||||
// sequence to read the counter:
|
||||
// * read the control register to ensure the event counter mode is set
|
||||
// * read the counter registers
|
||||
// * create the value out of registers content
|
||||
// arrange
|
||||
d, a := initTestPCF8583WithStubbedAdaptor()
|
||||
a.written = []byte{} // reset writes of Start() and former test
|
||||
want := int32(654321)
|
||||
reg0Val := uint8(0x20) // counter mode
|
||||
reg1Val := uint8(0x21) // BCD: 21
|
||||
reg2Val := uint8(0x43) // BCD: 43
|
||||
reg3Val := uint8(0x65) // BCD: 65
|
||||
returnRead := [2][]uint8{
|
||||
{reg0Val},
|
||||
{reg1Val, reg2Val, reg3Val},
|
||||
}
|
||||
// arrange writes
|
||||
a.i2cWriteImpl = func(b []byte) (int, error) {
|
||||
return len(b), nil
|
||||
}
|
||||
// arrange reads
|
||||
numCallsRead := 0
|
||||
a.i2cReadImpl = func(b []byte) (int, error) {
|
||||
numCallsRead++
|
||||
rr := returnRead[numCallsRead-1]
|
||||
for i := 0; i < len(b); i++ {
|
||||
b[i] = rr[i]
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
// act
|
||||
got, err := d.ReadCounter()
|
||||
// assert
|
||||
gobottest.Assert(t, err, nil)
|
||||
gobottest.Assert(t, len(a.written), 1)
|
||||
gobottest.Assert(t, a.written[0], uint8(pcf8583Reg_CTRL))
|
||||
gobottest.Assert(t, numCallsRead, 2)
|
||||
gobottest.Assert(t, got, want)
|
||||
}
|
||||
|
||||
func TestPCF8583ReadCounterNoCounterModeFails(t *testing.T) {
|
||||
// arrange
|
||||
d, a := initTestPCF8583WithStubbedAdaptor()
|
||||
a.written = []byte{} // reset writes of Start() and former test
|
||||
readCtrlState := uint8(0x30) // test mode
|
||||
// arrange writes
|
||||
a.i2cWriteImpl = func(b []byte) (int, error) {
|
||||
return len(b), nil
|
||||
}
|
||||
// arrange reads
|
||||
numCallsRead := 0
|
||||
a.i2cReadImpl = func(b []byte) (int, error) {
|
||||
numCallsRead++
|
||||
b[len(b)-1] = readCtrlState
|
||||
return len(b), nil
|
||||
}
|
||||
// act
|
||||
got, err := d.ReadCounter()
|
||||
// assert
|
||||
gobottest.Refute(t, err, nil)
|
||||
gobottest.Assert(t, strings.Contains(err.Error(), "wrong mode 0x30"), true)
|
||||
gobottest.Assert(t, got, int32(0))
|
||||
gobottest.Assert(t, len(a.written), 1)
|
||||
gobottest.Assert(t, a.written[0], uint8(pcf8583Reg_CTRL))
|
||||
gobottest.Assert(t, numCallsRead, 1)
|
||||
}
|
||||
|
||||
func TestPCF8583WriteRam(t *testing.T) {
|
||||
// sequence to write the RAM:
|
||||
// * calculate the RAM address and check for valid range
|
||||
// * write the given value to the given RAM address
|
||||
// arrange
|
||||
d, a := initTestPCF8583WithStubbedAdaptor()
|
||||
a.written = []byte{} // reset writes of Start() and former test
|
||||
wantRamAddress := uint8(0xFF)
|
||||
wantRamValue := uint8(0xEF)
|
||||
// arrange writes
|
||||
a.i2cWriteImpl = func(b []byte) (int, error) {
|
||||
return len(b), nil
|
||||
}
|
||||
// act
|
||||
err := d.WriteRAM(wantRamAddress-pcf8583RamOffset, wantRamValue)
|
||||
// assert
|
||||
gobottest.Assert(t, err, nil)
|
||||
gobottest.Assert(t, len(a.written), 2)
|
||||
gobottest.Assert(t, a.written[0], wantRamAddress)
|
||||
gobottest.Assert(t, a.written[1], wantRamValue)
|
||||
}
|
||||
|
||||
func TestPCF8583WriteRamAddressOverflowFails(t *testing.T) {
|
||||
// arrange
|
||||
d, a := initTestPCF8583WithStubbedAdaptor()
|
||||
a.written = []byte{} // reset writes of Start() and former test
|
||||
// act
|
||||
err := d.WriteRAM(uint8(0xF0), 15)
|
||||
// assert
|
||||
gobottest.Refute(t, err, nil)
|
||||
gobottest.Assert(t, strings.Contains(err.Error(), "overflow 256"), true)
|
||||
gobottest.Assert(t, len(a.written), 0)
|
||||
}
|
||||
|
||||
func TestPCF8583ReadRam(t *testing.T) {
|
||||
// sequence to read the RAM:
|
||||
// * calculate the RAM address and check for valid range
|
||||
// * read the value from the given RAM address
|
||||
// arrange
|
||||
d, a := initTestPCF8583WithStubbedAdaptor()
|
||||
a.written = []byte{} // reset writes of Start() and former test
|
||||
wantRamAddress := uint8(pcf8583RamOffset)
|
||||
want := uint8(0xAB)
|
||||
// arrange writes
|
||||
a.i2cWriteImpl = func(b []byte) (int, error) {
|
||||
return len(b), nil
|
||||
}
|
||||
// arrange reads
|
||||
numCallsRead := 0
|
||||
a.i2cReadImpl = func(b []byte) (int, error) {
|
||||
numCallsRead++
|
||||
b[len(b)-1] = want
|
||||
return len(b), nil
|
||||
}
|
||||
// act
|
||||
got, err := d.ReadRAM(wantRamAddress - pcf8583RamOffset)
|
||||
// assert
|
||||
gobottest.Assert(t, err, nil)
|
||||
gobottest.Assert(t, got, want)
|
||||
gobottest.Assert(t, len(a.written), 1)
|
||||
gobottest.Assert(t, a.written[0], wantRamAddress)
|
||||
gobottest.Assert(t, numCallsRead, 1)
|
||||
}
|
||||
|
||||
func TestPCF8583ReadRamAddressOverflowFails(t *testing.T) {
|
||||
// arrange
|
||||
d, a := initTestPCF8583WithStubbedAdaptor()
|
||||
a.written = []byte{} // reset writes of Start() and former test
|
||||
// arrange writes
|
||||
a.i2cWriteImpl = func(b []byte) (int, error) {
|
||||
return len(b), nil
|
||||
}
|
||||
// arrange reads
|
||||
numCallsRead := 0
|
||||
a.i2cReadImpl = func(b []byte) (int, error) {
|
||||
numCallsRead++
|
||||
return len(b), nil
|
||||
}
|
||||
// act
|
||||
got, err := d.ReadRAM(uint8(0xF0))
|
||||
// assert
|
||||
gobottest.Refute(t, err, nil)
|
||||
gobottest.Assert(t, strings.Contains(err.Error(), "overflow 256"), true)
|
||||
gobottest.Assert(t, got, uint8(0))
|
||||
gobottest.Assert(t, len(a.written), 0)
|
||||
gobottest.Assert(t, numCallsRead, 0)
|
||||
}
|
||||
|
||||
func TestPCF8583_initializeNoModeSwitch(t *testing.T) {
|
||||
// arrange
|
||||
a := newI2cTestAdaptor()
|
||||
d := NewPCF8583Driver(a)
|
||||
a.written = []byte{} // reset writes of former tests
|
||||
readCtrlState := uint8(0x01) // 32.768kHz clock mode
|
||||
// arrange writes
|
||||
a.i2cWriteImpl = func(b []byte) (int, error) {
|
||||
return len(b), nil
|
||||
}
|
||||
// arrange reads
|
||||
numCallsRead := 0
|
||||
a.i2cReadImpl = func(b []byte) (int, error) {
|
||||
numCallsRead++
|
||||
b[len(b)-1] = readCtrlState
|
||||
return len(b), nil
|
||||
}
|
||||
// act, assert - initialize() must be called on Start()
|
||||
err := d.Start()
|
||||
// assert
|
||||
gobottest.Assert(t, err, nil)
|
||||
gobottest.Assert(t, numCallsRead, 1)
|
||||
gobottest.Assert(t, len(a.written), 1)
|
||||
gobottest.Assert(t, a.written[0], uint8(pcf8583Reg_CTRL))
|
||||
}
|
||||
|
||||
func TestPCF8583_initializeWithModeSwitch(t *testing.T) {
|
||||
// sequence to change mode:
|
||||
// * read control register for get current state
|
||||
// * reset old mode bits and set new mode bit
|
||||
// * write the control register
|
||||
// arrange
|
||||
a := newI2cTestAdaptor()
|
||||
d := NewPCF8583Driver(a)
|
||||
d.mode = PCF8583CtrlModeCounter
|
||||
a.written = []byte{} // reset writes of former tests
|
||||
readCtrlState := uint8(0x02) // 32.768kHz clock mode
|
||||
wantReg0Val := uint8(0x22) // event counter mode
|
||||
// arrange writes
|
||||
a.i2cWriteImpl = func(b []byte) (int, error) {
|
||||
return len(b), nil
|
||||
}
|
||||
// arrange reads
|
||||
numCallsRead := 0
|
||||
a.i2cReadImpl = func(b []byte) (int, error) {
|
||||
numCallsRead++
|
||||
b[len(b)-1] = readCtrlState
|
||||
return len(b), nil
|
||||
}
|
||||
// act, assert - initialize() must be called on Start()
|
||||
err := d.Start()
|
||||
// assert
|
||||
gobottest.Assert(t, err, nil)
|
||||
gobottest.Assert(t, numCallsRead, 1)
|
||||
gobottest.Assert(t, len(a.written), 3)
|
||||
gobottest.Assert(t, a.written[0], uint8(pcf8583Reg_CTRL))
|
||||
gobottest.Assert(t, a.written[1], uint8(pcf8583Reg_CTRL))
|
||||
gobottest.Assert(t, a.written[2], uint8(wantReg0Val))
|
||||
}
|
61
examples/tinkerboard_pcf8583_clock.go
Normal file
61
examples/tinkerboard_pcf8583_clock.go
Normal file
@ -0,0 +1,61 @@
|
||||
// +build example
|
||||
//
|
||||
// Do not build by default.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"gobot.io/x/gobot"
|
||||
"gobot.io/x/gobot/drivers/i2c"
|
||||
"gobot.io/x/gobot/platforms/tinkerboard"
|
||||
)
|
||||
|
||||
// Wiring
|
||||
// PWR Tinkerboard: 1 (+3.3V, VCC), 6, 9, 14, 20 (GND)
|
||||
// I2C1 Tinkerboard: 3 (SDA), 5 (SCL)
|
||||
// PCF8583 DIP package: 1 (OSCI, 50Hz), 2 (OSCO, nc), 3 (A0 - GND), 4 (VSS, +3.3V), 5 (SDA), 6 (SCL), 7 (/INT, nc), 8 (VDD, GND)
|
||||
func main() {
|
||||
board := tinkerboard.NewAdaptor()
|
||||
pcf := i2c.NewPCF8583Driver(board, i2c.WithBus(1), i2c.WithPCF8583Mode(i2c.PCF8583CtrlModeClock50))
|
||||
|
||||
work := func() {
|
||||
|
||||
currentTime := time.Now()
|
||||
log.Println(currentTime)
|
||||
|
||||
if err := pcf.WriteTime(currentTime); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
gobot.Every(10*time.Second, func() {
|
||||
if val, err := pcf.ReadTime(); err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
log.Printf("read Time: %v", val)
|
||||
}
|
||||
|
||||
ramVal, err := pcf.ReadRAM(uint8(0))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
log.Printf("read RAM: %v", ramVal)
|
||||
ramVal++
|
||||
}
|
||||
if err := pcf.WriteRAM(uint8(0), ramVal); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
robot := gobot.NewRobot("pcfBot",
|
||||
[]gobot.Connection{board},
|
||||
[]gobot.Device{pcf},
|
||||
work,
|
||||
)
|
||||
|
||||
robot.Start()
|
||||
}
|
61
examples/tinkerboard_pcf8583_counter.go
Normal file
61
examples/tinkerboard_pcf8583_counter.go
Normal file
@ -0,0 +1,61 @@
|
||||
// +build example
|
||||
//
|
||||
// Do not build by default.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"gobot.io/x/gobot"
|
||||
"gobot.io/x/gobot/drivers/i2c"
|
||||
"gobot.io/x/gobot/platforms/tinkerboard"
|
||||
)
|
||||
|
||||
// Wiring
|
||||
// PWR Tinkerboard: 1 (+3.3V, VCC), 6, 9, 14, 20 (GND)
|
||||
// I2C1 Tinkerboard: 3 (SDA), 5 (SCL)
|
||||
// PCF8583 DIP package: 1 (OSCI, event), 2 (OSCO, nc), 3 (A0 - GND), 4 (VSS, +3.3V), 5 (SDA), 6 (SCL), 7 (/INT, nc), 8 (VDD, GND)
|
||||
// Note: event can be created by e.g. an debounced button
|
||||
func main() {
|
||||
board := tinkerboard.NewAdaptor()
|
||||
pcf := i2c.NewPCF8583Driver(board, i2c.WithBus(1), i2c.WithPCF8583Mode(i2c.PCF8583CtrlModeCounter))
|
||||
|
||||
work := func() {
|
||||
lastCnt := int32(1234)
|
||||
|
||||
if err := pcf.WriteCounter(lastCnt); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
gobot.Every(1000*time.Millisecond, func() {
|
||||
if val, err := pcf.ReadCounter(); err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
log.Printf("read Counter: %d, diff: %d", val, val-lastCnt)
|
||||
lastCnt = val
|
||||
}
|
||||
|
||||
ramVal, err := pcf.ReadRAM(uint8(0))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
log.Printf("read RAM: %d", ramVal)
|
||||
ramVal++
|
||||
}
|
||||
if err := pcf.WriteRAM(uint8(0), ramVal); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
robot := gobot.NewRobot("pcfBot",
|
||||
[]gobot.Connection{board},
|
||||
[]gobot.Device{pcf},
|
||||
work,
|
||||
)
|
||||
|
||||
robot.Start()
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user