gobot.io/x/gobot@v1.16.0/drivers/i2c/pcf8591_driver.go (about) 1 package i2c 2 3 import ( 4 "fmt" 5 "log" 6 "strings" 7 "sync" 8 "time" 9 10 "gobot.io/x/gobot" 11 ) 12 13 // PCF8591 supports addresses from 0x48 to 0x4F 14 // The default address applies when all address pins connected to ground. 15 const pcf8591DefaultAddress = 0x48 16 17 const ( 18 pcf8591Debug = false 19 ) 20 21 type pcf8591Mode uint8 22 type PCF8591Channel uint8 23 24 const ( 25 pcf8591_CHAN0 PCF8591Channel = 0x00 26 pcf8591_CHAN1 PCF8591Channel = 0x01 27 pcf8591_CHAN2 PCF8591Channel = 0x02 28 pcf8591_CHAN3 PCF8591Channel = 0x03 29 ) 30 31 const pcf8591_AION = 0x04 // auto increment, only relevant for ADC 32 33 const ( 34 pcf8591_ALLSINGLE pcf8591Mode = 0x00 35 pcf8591_THREEDIFF pcf8591Mode = 0x10 36 pcf8591_MIXED pcf8591Mode = 0x20 37 pcf8591_TWODIFF pcf8591Mode = 0x30 38 pcf8591_ANAON pcf8591Mode = 0x40 39 ) 40 41 const pcf8591_ADMASK = 0x33 // channels and mode 42 43 type pcf8591ModeChan struct { 44 mode pcf8591Mode 45 channel PCF8591Channel 46 } 47 48 // modeMap is to define the relation between a given description and the mode and channel 49 // beside the long form there are some short forms available without risk of confusion 50 // 51 // pure single mode 52 // "s.0"..."s.3": read single value of input n => channel n 53 // pure differential mode 54 // "d.0-1": differential value between input 0 and 1 => channel 0 55 // "d.2-3": differential value between input 2 and 3 => channel 1 56 // mixed mode 57 // "m.0": single value of input 0 => channel 0 58 // "m.1": single value of input 1 => channel 1 59 // "m.2-3": differential value between input 2 and 3 => channel 2 60 // three differential inputs, related to input 3 61 // "t.0-3": differential value between input 0 and 3 => channel 0 62 // "t.1-3": differential value between input 1 and 3 => channel 1 63 // "t.2-3": differential value between input 1 and 3 => channel 2 64 var pcf8591ModeMap = map[string]pcf8591ModeChan{ 65 "s.0": {pcf8591_ALLSINGLE, pcf8591_CHAN0}, 66 "0": {pcf8591_ALLSINGLE, pcf8591_CHAN0}, 67 "s.1": {pcf8591_ALLSINGLE, pcf8591_CHAN1}, 68 "1": {pcf8591_ALLSINGLE, pcf8591_CHAN1}, 69 "s.2": {pcf8591_ALLSINGLE, pcf8591_CHAN2}, 70 "2": {pcf8591_ALLSINGLE, pcf8591_CHAN2}, 71 "s.3": {pcf8591_ALLSINGLE, pcf8591_CHAN3}, 72 "3": {pcf8591_ALLSINGLE, pcf8591_CHAN3}, 73 "d.0-1": {pcf8591_TWODIFF, pcf8591_CHAN0}, 74 "0-1": {pcf8591_TWODIFF, pcf8591_CHAN0}, 75 "d.2-3": {pcf8591_TWODIFF, pcf8591_CHAN1}, 76 "m.0": {pcf8591_MIXED, pcf8591_CHAN0}, 77 "m.1": {pcf8591_MIXED, pcf8591_CHAN1}, 78 "m.2-3": {pcf8591_MIXED, pcf8591_CHAN2}, 79 "t.0-3": {pcf8591_THREEDIFF, pcf8591_CHAN0}, 80 "0-3": {pcf8591_THREEDIFF, pcf8591_CHAN0}, 81 "t.1-3": {pcf8591_THREEDIFF, pcf8591_CHAN1}, 82 "1-3": {pcf8591_THREEDIFF, pcf8591_CHAN1}, 83 "t.2-3": {pcf8591_THREEDIFF, pcf8591_CHAN2}, 84 } 85 86 // PCF8591Driver is a Gobot Driver for the PCF8591 8-bit 4xA/D & 1xD/A converter with i2c (100 kHz) and 3 address pins. 87 // The analog inputs can be used as differential inputs in different ways. 88 // 89 // All values are linear scaled to 3.3V by default. This can be changed, see example "tinkerboard_pcf8591.go". 90 // 91 // Address specification: 92 // 1 0 0 1 A2 A1 A0|rd 93 // Lowest bit (rd) is mapped to switch between write(0)/read(1), it is not part of the "real" address. 94 // 95 // Example: A1,A2=1, others are 0 96 // Address mask => 1001110|1 => real 7-bit address mask 0100 1110 = 0x4E 97 // 98 // For example, here is the Adafruit board that uses this chip: 99 // https://www.adafruit.com/product/4648 100 // 101 // This driver was tested with Tinkerboard and the YL-40 driver. 102 // 103 type PCF8591Driver struct { 104 name string 105 connector Connector 106 connection Connection 107 Config 108 gobot.Commander 109 lastCtrlByte byte 110 lastAnaOut byte 111 additionalReadWrite uint8 112 additionalRead uint8 113 forceRefresh bool 114 LastRead [][]byte // for debugging purposes 115 mutex *sync.Mutex // mutex needed to ensure write-read sequence of AnalogRead() is not interrupted 116 } 117 118 // NewPCF8591Driver creates a new driver with specified i2c interface 119 // Params: 120 // conn Connector - the Adaptor to use with this Driver 121 // 122 // Optional params: 123 // i2c.WithBus(int): bus to use with this driver 124 // i2c.WithAddress(int): address to use with this driver 125 // i2c.WithPCF8591With400kbitStabilization(uint8, uint8): stabilize read in 400 kbit mode 126 // 127 func NewPCF8591Driver(a Connector, options ...func(Config)) *PCF8591Driver { 128 p := &PCF8591Driver{ 129 name: gobot.DefaultName("PCF8591"), 130 connector: a, 131 Config: NewConfig(), 132 Commander: gobot.NewCommander(), 133 mutex: &sync.Mutex{}, 134 } 135 136 for _, option := range options { 137 option(p) 138 } 139 140 return p 141 } 142 143 // WithPCF8591With400kbitStabilisation option sets the PCF8591 additionalReadWrite and additionalRead value 144 func WithPCF8591With400kbitStabilization(additionalReadWrite, additionalRead int) func(Config) { 145 return func(c Config) { 146 p, ok := c.(*PCF8591Driver) 147 if ok { 148 if additionalReadWrite < 0 { 149 additionalReadWrite = 1 // works in most cases 150 } 151 if additionalRead < 0 { 152 additionalRead = 2 // works in most cases 153 } 154 p.additionalReadWrite = uint8(additionalReadWrite) 155 p.additionalRead = uint8(additionalRead) 156 if pcf8591Debug { 157 log.Printf("400 kbit stabilization for PCF8591Driver set rw: %d, r: %d", p.additionalReadWrite, p.additionalRead) 158 } 159 } else if pcf8591Debug { 160 log.Printf("trying to set 400 kbit stabilization for non-PCF8591Driver %v", c) 161 } 162 } 163 } 164 165 // WithPCF8591ForceWrite option modifies the PCF8591Driver forceRefresh option 166 // Setting to true (1) will force refresh operation to register, although there is no change. 167 // Normally this is not needed, so default is off (0). 168 // When there is something flaky, there is a small chance to stabilize by setting this flag to true. 169 // However, setting this flag to true slows down each IO operation up to 100%. 170 func WithPCF8591ForceRefresh(val uint8) func(Config) { 171 return func(c Config) { 172 d, ok := c.(*PCF8591Driver) 173 if ok { 174 d.forceRefresh = val > 0 175 } else if pcf8591Debug { 176 log.Printf("Trying to set forceRefresh for non-PCF8591Driver %v", c) 177 } 178 } 179 } 180 181 // Name returns the Name for the Driver 182 func (p *PCF8591Driver) Name() string { return p.name } 183 184 // SetName sets the Name for the Driver 185 func (p *PCF8591Driver) SetName(n string) { p.name = n } 186 187 // Connection returns the connection for the Driver 188 func (p *PCF8591Driver) Connection() gobot.Connection { return p.connector.(gobot.Connection) } 189 190 // Start initializes the PCF8591 191 func (p *PCF8591Driver) Start() (err error) { 192 bus := p.GetBusOrDefault(p.connector.GetDefaultBus()) 193 address := p.GetAddressOrDefault(pcf8591DefaultAddress) 194 195 p.connection, err = p.connector.GetConnection(address, bus) 196 if err != nil { 197 return err 198 } 199 200 if err := p.AnalogOutputState(false); err != nil { 201 return err 202 } 203 return 204 } 205 206 // Halt stops the device 207 func (p *PCF8591Driver) Halt() (err error) { 208 return p.AnalogOutputState(false) 209 } 210 211 // AnalogRead returns value from analog reading of given input description 212 // 213 // Vlsb = (Vref-Vagnd)/256, value = (Van+ - Van-)/Vlsb, Van-=Vagnd for single mode 214 // 215 // The first read contains the last converted value (usually the last read). 216 // After the channel was switched this means the value of the previous read channel. 217 // After power on, the first byte read will be 80h, because the read is one cycle behind. 218 // 219 // Important note for 440 kbit mode: 220 // With a bus speed of 100 kBit/sec, the ADC conversion has ~80 us + ACK (time to transfer the previous value). 221 // This time is the limit for A-D conversion (datasheet 90 us). 222 // An i2c bus extender (LTC4311) don't fix it (it seems rather the opposite). 223 // 224 // This leads to following behavior: 225 // * the control byte is not written correctly 226 // * the transition process takes an additional cycle, very often 227 // * some circuits takes one cycle longer transition time in addition 228 // * reading more than one byte by Read([]byte), e.g. to calculate an average, is not sufficient, 229 // because some missing integration steps in each conversion (each byte value is a little bit lower than expected) 230 // 231 // So, for default, we drop the first three bytes to get the right value. 232 func (p *PCF8591Driver) AnalogRead(description string) (value int, err error) { 233 p.mutex.Lock() 234 defer p.mutex.Unlock() 235 236 mc, err := PCF8591ParseModeChan(description) 237 if err != nil { 238 return 0, err 239 } 240 241 // reset channel and mode 242 ctrlByte := p.lastCtrlByte & ^uint8(pcf8591_ADMASK) 243 // set to current channel and mode, AI must be off, because we need reading twice 244 ctrlByte = ctrlByte | uint8(mc.mode) | uint8(mc.channel) & ^uint8(pcf8591_AION) 245 246 var uval byte 247 p.LastRead = make([][]byte, p.additionalReadWrite+1) 248 // repeated write and read cycle to stabilize value in 400 kbit mode 249 for writeReadCycle := uint8(1); writeReadCycle <= p.additionalReadWrite+1; writeReadCycle++ { 250 if err = p.writeCtrlByte(ctrlByte, p.forceRefresh || writeReadCycle > 1); err != nil { 251 return 0, err 252 } 253 254 // initiate read but skip some bytes 255 if err := p.readBuf(writeReadCycle, 1+p.additionalRead); err != nil { 256 return 0, err 257 } 258 259 // additional relax time 260 time.Sleep(1 * time.Millisecond) 261 262 // real used read 263 if uval, err = p.connection.ReadByte(); err != nil { 264 return 0, err 265 } 266 267 if pcf8591Debug { 268 p.LastRead[writeReadCycle-1] = append(p.LastRead[writeReadCycle-1], uval) 269 } 270 } 271 272 // prepare return value 273 value = int(uval) 274 if mc.pcf8591IsDiff() { 275 if uval > 127 { 276 // first bit is set, means negative 277 value = int(uval) - 256 278 } 279 } 280 281 return value, err 282 } 283 284 // AnalogWrite writes the given value to the analog output (DAC) 285 // Vlsb = (Vref-Vagnd)/256, Vaout = Vagnd+Vlsb*value 286 // implements the aio.AnalogWriter interface, pin is unused here 287 func (p *PCF8591Driver) AnalogWrite(pin string, value int) (err error) { 288 p.mutex.Lock() 289 defer p.mutex.Unlock() 290 291 byteVal := byte(value) 292 293 if p.lastAnaOut == byteVal { 294 if pcf8591Debug { 295 log.Printf("write skipped because value unchanged: 0x%X\n", byteVal) 296 } 297 return nil 298 } 299 300 ctrlByte := p.lastCtrlByte | byte(pcf8591_ANAON) 301 err = p.connection.WriteByteData(ctrlByte, byteVal) 302 if err != nil { 303 return err 304 } 305 306 p.lastCtrlByte = ctrlByte 307 p.lastAnaOut = byteVal 308 return nil 309 } 310 311 // AnalogOutputState enables or disables the analog output 312 // Please note that in case of using the internal oscillator 313 // and the auto increment mode the output should not switched off. 314 // Otherwise conversion errors could occur. 315 func (p *PCF8591Driver) AnalogOutputState(state bool) (err error) { 316 p.mutex.Lock() 317 defer p.mutex.Unlock() 318 319 var ctrlByte uint8 320 if state { 321 ctrlByte = p.lastCtrlByte | byte(pcf8591_ANAON) 322 } else { 323 ctrlByte = p.lastCtrlByte & ^uint8(pcf8591_ANAON) 324 } 325 326 if err = p.writeCtrlByte(ctrlByte, p.forceRefresh); err != nil { 327 return err 328 } 329 return nil 330 } 331 332 // PCF8591ParseModeChan is used to get a working combination between mode (single, mixed, 2 differential, 3 differential) 333 // and the related channel to read from, parsed from the given description string. 334 func PCF8591ParseModeChan(description string) (*pcf8591ModeChan, error) { 335 mc, ok := pcf8591ModeMap[description] 336 if !ok { 337 descriptions := []string{} 338 for k := range pcf8591ModeMap { 339 descriptions = append(descriptions, k) 340 } 341 ds := strings.Join(descriptions, ", ") 342 return nil, fmt.Errorf("Unknown description '%s' for read analog value, accepted values: %s", description, ds) 343 } 344 345 return &mc, nil 346 } 347 348 func (p *PCF8591Driver) writeCtrlByte(ctrlByte uint8, forceRefresh bool) error { 349 if p.lastCtrlByte != ctrlByte || forceRefresh { 350 if err := p.connection.WriteByte(ctrlByte); err != nil { 351 return err 352 } 353 p.lastCtrlByte = ctrlByte 354 } else { 355 if pcf8591Debug { 356 log.Printf("write skipped because control byte unchanged: 0x%X\n", ctrlByte) 357 } 358 } 359 return nil 360 } 361 362 func (p *PCF8591Driver) readBuf(nr uint8, cntBytes uint8) error { 363 buf := make([]byte, cntBytes) 364 cntRead, err := p.connection.Read(buf) 365 if err != nil { 366 return err 367 } 368 if cntRead != len(buf) { 369 return fmt.Errorf("Not enough bytes (%d of %d) read", cntRead, len(buf)) 370 } 371 if pcf8591Debug { 372 p.LastRead[nr-1] = buf 373 } 374 return nil 375 } 376 377 func (mc pcf8591ModeChan) pcf8591IsDiff() bool { 378 switch mc.mode { 379 case pcf8591_TWODIFF: 380 return true 381 case pcf8591_THREEDIFF: 382 return true 383 case pcf8591_MIXED: 384 return mc.channel == pcf8591_CHAN2 385 default: 386 return false 387 } 388 }