gobot.io/x/gobot/v2@v2.1.0/drivers/i2c/pca9501_driver.go (about)

     1  package i2c
     2  
     3  // PCA9501 supports addresses from 0x00 to 0x7F
     4  // 0x00 - 0x3F: GPIO, 0x40 - 0x7F: EEPROM
     5  //
     6  // 0 EE A5 A4 A3 A2 A1 A0|rd
     7  // Lowest bit (rd) is mapped to switch between write(0)/read(1), it is not part of the "real" address.
     8  // Highest bit (EE) is mapped to switch between GPIO(0)/EEPROM(1).
     9  //
    10  // The EEPROM address will be generated from GPIO address in this driver.
    11  const pca9501DefaultAddress = 0x3F // this applies, if all 6 address pins left open (have pull up resistors)
    12  
    13  // PCA9501Driver is a Gobot Driver for the PCA9501 8-bit GPIO & 2-kbit EEPROM with 6 address program pins.
    14  // 2-kbit EEPROM has 256 byte, means addresses between 0x00-0xFF
    15  //
    16  // please refer to data sheet: https://www.nxp.com/docs/en/data-sheet/PCA9501.pdf
    17  //
    18  // PCA9501 is the replacement for PCF8574, so this driver should also work for PCF8574 except EEPROM calls
    19  //
    20  type PCA9501Driver struct {
    21  	connectionMem Connection
    22  	*Driver
    23  }
    24  
    25  // NewPCA9501Driver creates a new driver with specified i2c interface
    26  // Params:
    27  //		a Connector - the Adaptor to use with this Driver
    28  //
    29  // Optional params:
    30  //		i2c.WithBus(int):	bus to use with this driver
    31  //		i2c.WithAddress(int):	address to use with this driver
    32  //
    33  func NewPCA9501Driver(a Connector, options ...func(Config)) *PCA9501Driver {
    34  	p := &PCA9501Driver{
    35  		Driver: NewDriver(a, "PCA9501", pca9501DefaultAddress, options...),
    36  	}
    37  	p.afterStart = p.initialize
    38  
    39  	// API commands
    40  	p.AddCommand("WriteGPIO", func(params map[string]interface{}) interface{} {
    41  		pin := params["pin"].(uint8)
    42  		val := params["val"].(uint8)
    43  		err := p.WriteGPIO(pin, val)
    44  		return map[string]interface{}{"err": err}
    45  	})
    46  
    47  	p.AddCommand("ReadGPIO", func(params map[string]interface{}) interface{} {
    48  		pin := params["pin"].(uint8)
    49  		val, err := p.ReadGPIO(pin)
    50  		return map[string]interface{}{"val": val, "err": err}
    51  	})
    52  
    53  	p.AddCommand("WriteEEPROM", func(params map[string]interface{}) interface{} {
    54  		address := params["address"].(uint8)
    55  		val := params["val"].(uint8)
    56  		err := p.WriteEEPROM(address, val)
    57  		return map[string]interface{}{"err": err}
    58  	})
    59  
    60  	p.AddCommand("ReadEEPROM", func(params map[string]interface{}) interface{} {
    61  		address := params["address"].(uint8)
    62  		val, err := p.ReadEEPROM(address)
    63  		return map[string]interface{}{"val": val, "err": err}
    64  	})
    65  	return p
    66  }
    67  
    68  // WriteGPIO writes a value to a gpio pin (0-7)
    69  func (p *PCA9501Driver) WriteGPIO(pin uint8, val uint8) error {
    70  	p.mutex.Lock()
    71  	defer p.mutex.Unlock()
    72  
    73  	// read current value of CTRL register, 0 is output, 1 is no output
    74  	iodir, err := p.connection.ReadByte()
    75  	if err != nil {
    76  		return err
    77  	}
    78  	// set pin as output by clearing bit
    79  	iodirVal := clearBit(iodir, uint8(pin))
    80  	// write CTRL register
    81  	err = p.connection.WriteByte(uint8(iodirVal))
    82  	if err != nil {
    83  		return err
    84  	}
    85  	// read current value of port
    86  	cVal, err := p.connection.ReadByte()
    87  	if err != nil {
    88  		return err
    89  	}
    90  	// set or reset the bit in value
    91  	var nVal uint8
    92  	if val == 0 {
    93  		nVal = clearBit(cVal, uint8(pin))
    94  	} else {
    95  		nVal = setBit(cVal, uint8(pin))
    96  	}
    97  	// write new value to port
    98  	err = p.connection.WriteByte(uint8(nVal))
    99  	if err != nil {
   100  		return err
   101  	}
   102  	return nil
   103  }
   104  
   105  // ReadGPIO reads a value from a given gpio pin (0-7)
   106  func (p *PCA9501Driver) ReadGPIO(pin uint8) (uint8, error) {
   107  	p.mutex.Lock()
   108  	defer p.mutex.Unlock()
   109  
   110  	// read current value of CTRL register, 0 is no input, 1 is an input
   111  	iodir, err := p.connection.ReadByte()
   112  	if err != nil {
   113  		return 0, err
   114  	}
   115  	// set pin as input by setting bit
   116  	iodirVal := setBit(iodir, uint8(pin))
   117  	// write CTRL register
   118  	err = p.connection.WriteByte(uint8(iodirVal))
   119  	if err != nil {
   120  		return 0, err
   121  	}
   122  	// read port and create return bit
   123  	val, err := p.connection.ReadByte()
   124  	if err != nil {
   125  		return val, err
   126  	}
   127  	val = 1 << uint8(pin) & val
   128  	if val > 1 {
   129  		val = 1
   130  	}
   131  	return val, nil
   132  }
   133  
   134  // ReadEEPROM reads a value from a given address (0x00-0xFF)
   135  // Note: only this sequence for memory read is supported: "STARTW-DATA1-STARTR-DATA2-STOP"
   136  // DATA1: EEPROM address, DATA2: read value
   137  func (p *PCA9501Driver) ReadEEPROM(address uint8) (uint8, error) {
   138  	p.mutex.Lock()
   139  	defer p.mutex.Unlock()
   140  
   141  	return p.connectionMem.ReadByteData(address)
   142  }
   143  
   144  // WriteEEPROM writes a value to a given address in memory (0x00-0xFF)
   145  func (p *PCA9501Driver) WriteEEPROM(address uint8, val uint8) error {
   146  	p.mutex.Lock()
   147  	defer p.mutex.Unlock()
   148  
   149  	return p.connectionMem.WriteByteData(address, val)
   150  }
   151  
   152  func (p *PCA9501Driver) initialize() (err error) {
   153  	// initialize the EEPROM connection
   154  	bus := p.GetBusOrDefault(p.connector.DefaultI2cBus())
   155  	addressMem := p.GetAddressOrDefault(pca9501DefaultAddress) | 0x40
   156  	p.connectionMem, err = p.connector.GetI2cConnection(addressMem, bus)
   157  	return
   158  }