gobot.io/x/gobot/v2@v2.1.0/drivers/i2c/pcf8583_driver.go (about) 1 package i2c 2 3 import ( 4 "fmt" 5 "log" 6 "time" 7 ) 8 9 const ( 10 pcf8583Debug = false 11 12 // PCF8583 supports addresses 0x50 and 0x51 13 // The default address applies when the address pin is grounded. 14 pcf8583DefaultAddress = 0x50 15 16 // default is 0x10, when set to 0 also some free or unused RAM can be accessed 17 pcf8583RamOffset = 0x10 18 ) 19 20 // PCF8583Control is used to specify control and status register content 21 type PCF8583Control uint8 22 23 const ( 24 // registers are named according to the datasheet 25 pcf8583Reg_CTRL = iota // 0x00 26 pcf8583Reg_SUBSEC_D0D1 // 0x01 27 pcf8583Reg_SEC_D2D3 // 0x02 28 pcf8583Reg_MIN_D4D5 // 0x03 29 pcf8583Reg_HOUR // 0x04 30 pcf8583Reg_YEARDATE // 0x05 31 pcf8583Reg_WEEKDAYMONTH // 0x06 32 pcf8583Reg_TIMER // 0x07 33 pcf8583Reg_ALARMCTRL // 0x08, offset for all alarm registers 0x09 ... 0xF 34 35 pcf8583CtrlTimerFlag PCF8583Control = 0x01 // 50% duty factor, seconds flag if alarm enable bit is 0 36 pcf8583CtrlAlarmFlag PCF8583Control = 0x02 // 50% duty factor, minutes flag if alarm enable bit is 0 37 pcf8583CtrlAlarmEnable PCF8583Control = 0x04 // if enabled, memory 08h is alarm control register 38 pcf8583CtrlMask PCF8583Control = 0x08 // 0: read 05h, 06h unmasked, 1: read date and month count directly 39 PCF8583CtrlModeClock50 PCF8583Control = 0x10 // clock mode with 50 Hz 40 PCF8583CtrlModeCounter PCF8583Control = 0x20 // event counter mode 41 PCF8583CtrlModeTest PCF8583Control = 0x30 // test mode 42 pcf8583CtrlHoldLastCount PCF8583Control = 0x40 // 0: count, 1: store and hold count in capture latches 43 pcf8583CtrlStopCounting PCF8583Control = 0x80 // 0: count, 1: stop counting, reset divider 44 ) 45 46 // PCF8583Driver is a Gobot Driver for the PCF8583 clock and calendar chip & 240 x 8-bit bit RAM with 1 address program pin. 47 // please refer to data sheet: https://www.nxp.com/docs/en/data-sheet/PCF8583.pdf 48 // 49 // 0 1 0 1 0 0 0 A0|rd 50 // Lowest bit (rd) is mapped to switch between write(0)/read(1), it is not part of the "real" address. 51 // 52 // PCF8583 is mainly compatible to PCF8593, so this driver should also work for PCF8593 except RAM calls 53 // 54 // This driver was tested with Tinkerboard. 55 type PCF8583Driver struct { 56 *Driver 57 mode PCF8583Control // clock 32.768kHz (default), clock 50Hz, event counter 58 yearOffset int 59 ramOffset byte 60 } 61 62 // NewPCF8583Driver creates a new driver with specified i2c interface 63 // Params: 64 // c Connector - the Adaptor to use with this Driver 65 // 66 // Optional params: 67 // i2c.WithBus(int): bus to use with this driver 68 // i2c.WithAddress(int): address to use with this driver 69 // i2c.WithPCF8583Mode(PCF8583Control): mode of this driver 70 // 71 func NewPCF8583Driver(c Connector, options ...func(Config)) *PCF8583Driver { 72 d := &PCF8583Driver{ 73 Driver: NewDriver(c, "PCF8583", pcf8583DefaultAddress), 74 ramOffset: pcf8583RamOffset, 75 } 76 d.afterStart = d.initialize 77 78 for _, option := range options { 79 option(d) 80 } 81 82 // API commands 83 d.AddCommand("WriteTime", func(params map[string]interface{}) interface{} { 84 val := params["val"].(time.Time) 85 err := d.WriteTime(val) 86 return map[string]interface{}{"err": err} 87 }) 88 89 d.AddCommand("ReadTime", func(params map[string]interface{}) interface{} { 90 val, err := d.ReadTime() 91 return map[string]interface{}{"val": val, "err": err} 92 }) 93 94 d.AddCommand("WriteCounter", func(params map[string]interface{}) interface{} { 95 val := params["val"].(int32) 96 err := d.WriteCounter(val) 97 return map[string]interface{}{"err": err} 98 }) 99 100 d.AddCommand("ReadCounter", func(params map[string]interface{}) interface{} { 101 val, err := d.ReadCounter() 102 return map[string]interface{}{"val": val, "err": err} 103 }) 104 105 d.AddCommand("WriteRAM", func(params map[string]interface{}) interface{} { 106 address := params["address"].(uint8) 107 val := params["val"].(uint8) 108 err := d.WriteRAM(address, val) 109 return map[string]interface{}{"err": err} 110 }) 111 112 d.AddCommand("ReadRAM", func(params map[string]interface{}) interface{} { 113 address := params["address"].(uint8) 114 val, err := d.ReadRAM(address) 115 return map[string]interface{}{"val": val, "err": err} 116 }) 117 return d 118 } 119 120 // WithPCF8583Mode is used to change the mode between 32.678kHz clock, 50Hz clock, event counter 121 // Valid settings are of type "PCF8583Control" 122 func WithPCF8583Mode(mode PCF8583Control) func(Config) { 123 return func(c Config) { 124 d, ok := c.(*PCF8583Driver) 125 if ok { 126 if !mode.isClockMode() && !mode.isCounterMode() { 127 panic(fmt.Sprintf("%s: mode 0x%02x is not supported", d.name, mode)) 128 } 129 d.mode = mode 130 } else if pcf8583Debug { 131 log.Printf("trying to set mode for non-PCF8583Driver %v", c) 132 } 133 } 134 } 135 136 // WriteTime setup the clock registers with the given time 137 func (d *PCF8583Driver) WriteTime(val time.Time) error { 138 d.mutex.Lock() 139 defer d.mutex.Unlock() 140 141 // according to chapter 7.11 of the product data sheet, the stop counting flag of the control/status register 142 // must be set before, so we read the control byte before and only set/reset the stop 143 ctrlRegVal, err := d.connection.ReadByteData(uint8(pcf8583Reg_CTRL)) 144 if err != nil { 145 return err 146 } 147 if !PCF8583Control(ctrlRegVal).isClockMode() { 148 return fmt.Errorf("%s: can't write time because the device is in wrong mode 0x%02x", d.name, ctrlRegVal) 149 } 150 year, month, day := val.Date() 151 err = d.connection.WriteBlockData(uint8(pcf8583Reg_CTRL), 152 []byte{ctrlRegVal | uint8(pcf8583CtrlStopCounting), 153 pcf8583encodeBcd(uint8(val.Nanosecond() / 1000000 / 10)), // sub seconds in 1/10th seconds 154 pcf8583encodeBcd(uint8(val.Second())), 155 pcf8583encodeBcd(uint8(val.Minute())), 156 pcf8583encodeBcd(uint8(val.Hour())), 157 pcf8583encodeBcd(uint8(day)), // year, date (we keep the year counter zero and set the offset) 158 uint8(val.Weekday())<<5 | pcf8583encodeBcd(uint8(month)), // month, weekday (not BCD): Sunday = 0, Monday = 1 ... 159 }) 160 if err != nil { 161 return err 162 } 163 d.yearOffset = year 164 return d.run(ctrlRegVal) 165 } 166 167 // ReadTime reads the clock and returns the value 168 func (d *PCF8583Driver) ReadTime() (val time.Time, err error) { 169 d.mutex.Lock() 170 defer d.mutex.Unlock() 171 172 // according to chapter 7.1 of the product data sheet, the setting of "hold last count" flag 173 // is not needed when reading with auto increment 174 ctrlRegVal, err := d.connection.ReadByteData(uint8(pcf8583Reg_CTRL)) 175 if err != nil { 176 return 177 } 178 if !PCF8583Control(ctrlRegVal).isClockMode() { 179 return val, fmt.Errorf("%s: can't read time because the device is in wrong mode 0x%02x", d.name, ctrlRegVal) 180 } 181 // auto increment feature is used 182 clockDataSize := 6 183 data := make([]byte, clockDataSize) 184 read, err := d.connection.Read(data) 185 if err != nil { 186 return 187 } 188 if read != clockDataSize { 189 return val, fmt.Errorf("%s: %d bytes read, but %d expected", d.name, read, clockDataSize) 190 } 191 nanos := int(pcf8583decodeBcd(data[0])) * 1000000 * 10 // sub seconds in 1/10th seconds 192 seconds := int(pcf8583decodeBcd(data[1])) 193 minutes := int(pcf8583decodeBcd(data[2])) 194 hours := int(pcf8583decodeBcd(data[3])) 195 // year, date (the device can only count 4 years) 196 year := int(data[4]>>6) + d.yearOffset // use the first two bits, no BCD 197 date := int(pcf8583decodeBcd(data[4] & 0x3F)) // remove the year-bits for date 198 // weekday (not used here), month 199 month := time.Month(pcf8583decodeBcd(data[5] & 0x1F)) // remove the weekday-bits 200 return time.Date(year, month, date, hours, minutes, seconds, nanos, time.UTC), nil 201 } 202 203 // WriteCounter writes the counter registers 204 func (d *PCF8583Driver) WriteCounter(val int32) error { 205 d.mutex.Lock() 206 defer d.mutex.Unlock() 207 208 // we don't care of negative values here 209 // according to chapter 7.11 of the product data sheet, the stop counting flag of the control/status register 210 // must be set before, so we read the control byte before and only set/reset the stop 211 ctrlRegVal, err := d.connection.ReadByteData(uint8(pcf8583Reg_CTRL)) 212 if err != nil { 213 return err 214 } 215 if !PCF8583Control(ctrlRegVal).isCounterMode() { 216 return fmt.Errorf("%s: can't write counter because the device is in wrong mode 0x%02x", d.name, ctrlRegVal) 217 } 218 err = d.connection.WriteBlockData(uint8(pcf8583Reg_CTRL), 219 []byte{ctrlRegVal | uint8(pcf8583CtrlStopCounting), // stop 220 pcf8583encodeBcd(uint8(val % 100)), // 2 lowest digits 221 pcf8583encodeBcd(uint8((val / 100) % 100)), // 2 middle digits 222 pcf8583encodeBcd(uint8((val / 10000) % 100)), // 2 highest digits 223 }) 224 if err != nil { 225 return err 226 } 227 return d.run(ctrlRegVal) 228 } 229 230 // ReadCounter reads the counter registers 231 func (d *PCF8583Driver) ReadCounter() (val int32, err error) { 232 d.mutex.Lock() 233 defer d.mutex.Unlock() 234 235 // according to chapter 7.1 of the product data sheet, the setting of "hold last count" flag 236 // is not needed when reading with auto increment 237 ctrlRegVal, err := d.connection.ReadByteData(uint8(pcf8583Reg_CTRL)) 238 if err != nil { 239 return 240 } 241 if !PCF8583Control(ctrlRegVal).isCounterMode() { 242 return val, fmt.Errorf("%s: can't read counter because the device is in wrong mode 0x%02x", d.name, ctrlRegVal) 243 } 244 // auto increment feature is used 245 counterDataSize := 3 246 data := make([]byte, counterDataSize) 247 read, err := d.connection.Read(data) 248 if err != nil { 249 return 250 } 251 if read != counterDataSize { 252 return val, fmt.Errorf("%s: %d bytes read, but %d expected", d.name, read, counterDataSize) 253 } 254 return int32(pcf8583decodeBcd(data[0])) + 255 int32(pcf8583decodeBcd(data[1]))*100 + 256 int32(pcf8583decodeBcd(data[2]))*10000, nil 257 } 258 259 // WriteRAM writes a value to a given address in memory (0x00-0xFF) 260 func (d *PCF8583Driver) WriteRAM(address uint8, val uint8) error { 261 d.mutex.Lock() 262 defer d.mutex.Unlock() 263 264 realAddress := uint16(address) + uint16(d.ramOffset) 265 if realAddress > 0xFF { 266 return fmt.Errorf("%s: RAM address overflow %d", d.name, realAddress) 267 } 268 return d.connection.WriteByteData(uint8(realAddress), val) 269 } 270 271 // ReadRAM reads a value from a given address (0x00-0xFF) 272 func (d *PCF8583Driver) ReadRAM(address uint8) (val uint8, err error) { 273 d.mutex.Lock() 274 defer d.mutex.Unlock() 275 276 realAddress := uint16(address) + uint16(d.ramOffset) 277 if realAddress > 0xFF { 278 return val, fmt.Errorf("%s: RAM address overflow %d", d.name, realAddress) 279 } 280 return d.connection.ReadByteData(uint8(realAddress)) 281 } 282 283 func (d *PCF8583Driver) run(ctrlRegVal uint8) error { 284 ctrlRegVal = ctrlRegVal & ^uint8(pcf8583CtrlStopCounting) // reset stop bit 285 return d.connection.WriteByteData(uint8(pcf8583Reg_CTRL), ctrlRegVal) 286 } 287 288 func (d *PCF8583Driver) initialize() error { 289 // switch to configured mode 290 ctrlRegVal, err := d.connection.ReadByteData(uint8(pcf8583Reg_CTRL)) 291 if err != nil { 292 return err 293 } 294 if d.mode.isModeDiffer(PCF8583Control(ctrlRegVal)) { 295 ctrlRegVal = ctrlRegVal&^uint8(PCF8583CtrlModeTest) | uint8(d.mode) 296 if err = d.connection.WriteByteData(uint8(pcf8583Reg_CTRL), ctrlRegVal); err != nil { 297 return err 298 } 299 if pcf8583Debug { 300 if PCF8583Control(ctrlRegVal).isCounterMode() { 301 log.Printf("%s switched to counter mode 0x%02x", d.name, ctrlRegVal) 302 } else { 303 log.Printf("%s switched to clock mode 0x%02x", d.name, ctrlRegVal) 304 } 305 } 306 } 307 return nil 308 } 309 310 func (c PCF8583Control) isClockMode() bool { 311 return uint8(c)&uint8(PCF8583CtrlModeCounter) == 0 312 } 313 314 func (c PCF8583Control) isCounterMode() bool { 315 counterModeSet := (uint8(c) & uint8(PCF8583CtrlModeCounter)) != 0 316 clockMode50Set := (uint8(c) & uint8(PCF8583CtrlModeClock50)) != 0 317 return counterModeSet && !clockMode50Set 318 } 319 320 func (c PCF8583Control) isModeDiffer(mode PCF8583Control) bool { 321 return uint8(c)&uint8(PCF8583CtrlModeTest) != uint8(mode)&uint8(PCF8583CtrlModeTest) 322 } 323 324 func pcf8583encodeBcd(val byte) byte { 325 // decimal 12 => 0x12 326 if val > 99 { 327 val = 99 328 if pcf8583Debug { 329 log.Printf("PCF8583 BCD value (%d) exceeds limit of 99, now limited.", val) 330 } 331 } 332 hi, lo := byte(val/10), byte(val%10) 333 return hi<<4 | lo 334 } 335 336 func pcf8583decodeBcd(bcd byte) byte { 337 // 0x12 => decimal 12 338 hi, lo := byte(bcd>>4), byte(bcd&0x0f) 339 if hi > 9 { 340 hi = 9 341 if pcf8583Debug { 342 log.Printf("PCF8583 BCD value (%02x) exceeds limit 0x99 on most significant digit, now limited", bcd) 343 } 344 } 345 if lo > 9 { 346 lo = 9 347 if pcf8583Debug { 348 log.Printf("PCF8583 BCD value (%02x) exceeds limit 0x99 on least significant digit, now limited", bcd) 349 } 350 } 351 return 10*hi + lo 352 }