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