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

     1  package i2c
     2  
     3  import (
     4  	"fmt"
     5  )
     6  
     7  // pca953xDefaultAddress is set to variant PCA9533/2
     8  const (
     9  	pca953xDebug          = false
    10  	pca953xDefaultAddress = 0x63
    11  )
    12  
    13  type pca953xRegister uint8
    14  
    15  // PCA953xGPIOMode is used to set the mode while write GPIO
    16  type PCA953xGPIOMode uint8
    17  
    18  const (
    19  	pca953xRegInp  pca953xRegister = 0x00 // input register
    20  	pca953xRegPsc0 pca953xRegister = 0x01 // r,   frequency prescaler 0
    21  	pca953xRegPwm0 pca953xRegister = 0x02 // r/w, PWM register 0
    22  	pca953xRegPsc1 pca953xRegister = 0x03 // r/w, frequency prescaler 1
    23  	pca953xRegPwm1 pca953xRegister = 0x04 // r/w, PWM register 1
    24  	pca953xRegLs0  pca953xRegister = 0x05 // r/w, LED selector 0
    25  	pca953xRegLs1  pca953xRegister = 0x06 // r/w, LED selector 1 (only in PCA9531, PCA9532)
    26  
    27  	pca953xAiMask = 0x10 // autoincrement bit
    28  
    29  	// PCA953xModeHighImpedance set the GPIO to high (LED off)
    30  	PCA953xModeHighImpedance PCA953xGPIOMode = 0x00
    31  	// PCA953xModeLowImpedance set the GPIO to low (LED on)
    32  	PCA953xModeLowImpedance PCA953xGPIOMode = 0x01
    33  	// PCA953xModePwm0 set the GPIO to PWM (PWM0 & PSC0)
    34  	PCA953xModePwm0 PCA953xGPIOMode = 0x02
    35  	// PCA953xModePwm1 set the GPIO to PWM (PWM1 & PSC1)
    36  	PCA953xModePwm1 PCA953xGPIOMode = 0x03
    37  )
    38  
    39  var errToSmallPeriod = fmt.Errorf("Given Period to small, must be at least 1/152s (~6.58ms) or 152Hz")
    40  var errToBigPeriod = fmt.Errorf("Given Period to high, must be max. 256/152s (~1.68s) or 152/256Hz (~0.6Hz)")
    41  var errToSmallDutyCycle = fmt.Errorf("Given Duty Cycle to small, must be at least 0%%")
    42  var errToBigDutyCycle = fmt.Errorf("Given Duty Cycle to high, must be max. 100%%")
    43  
    44  // PCA953xDriver is a Gobot Driver for LED Dimmer PCA9530 (2-bit), PCA9533 (4-bit), PCA9531 (8-bit), PCA9532 (16-bit)
    45  // Although this is designed for LED's it can be used as a GPIO (read, write, pwm).
    46  // The names of the public functions reflect this.
    47  //
    48  // please refer to data sheet: https://www.nxp.com/docs/en/data-sheet/PCA9533.pdf
    49  //
    50  // Address range:
    51  // * PCA9530   0x60-0x61 (96-97 dec)
    52  // * PCA9531   0x60-0x67 (96-103 dec)
    53  // * PCA9532   0x60-0x67 (96-103 dec)
    54  // * PCA9533/1 0x62      (98 dec)
    55  // * PCA9533/2 0x63      (99 dec)
    56  //
    57  // each new command must start by setting the register and the AI flag
    58  // 0 0 0 AI | 0 R2 R1 R0
    59  // AI=1 means auto incrementing for R0-R2, which enable reading/writing all registers sequentially
    60  // when AI=1 and reading, then R!=0
    61  // this means: do not start with reading input register, writing input register is recognized but has no effect
    62  // => when AI=1 in general start with R>0
    63  type PCA953xDriver struct {
    64  	*Driver
    65  }
    66  
    67  // NewPCA953xDriver creates a new driver with specified i2c interface
    68  // Params:
    69  //
    70  //	conn Connector - the Adaptor to use with this Driver
    71  //
    72  // Optional params:
    73  //
    74  //	i2c.WithBus(int):	bus to use with this driver
    75  //	i2c.WithAddress(int):	address to use with this driver
    76  func NewPCA953xDriver(c Connector, options ...func(Config)) *PCA953xDriver {
    77  	d := &PCA953xDriver{
    78  		Driver: NewDriver(c, "PCA953x", pca953xDefaultAddress),
    79  	}
    80  
    81  	for _, option := range options {
    82  		option(d)
    83  	}
    84  
    85  	// TODO: API commands
    86  	return d
    87  }
    88  
    89  // SetLED sets the mode (LED off, on, PWM0, PWM1) for the LED output (index 0-7)
    90  func (d *PCA953xDriver) SetLED(idx uint8, mode PCA953xGPIOMode) error {
    91  	d.mutex.Lock()
    92  	defer d.mutex.Unlock()
    93  
    94  	return d.writeLED(idx, mode)
    95  }
    96  
    97  // WriteGPIO writes a value to a gpio output (index 0-7)
    98  func (d *PCA953xDriver) WriteGPIO(idx uint8, val uint8) error {
    99  	d.mutex.Lock()
   100  	defer d.mutex.Unlock()
   101  
   102  	mode := PCA953xModeLowImpedance
   103  	if val > 0 {
   104  		mode = PCA953xModeHighImpedance
   105  	}
   106  
   107  	return d.writeLED(idx, mode)
   108  }
   109  
   110  // ReadGPIO reads a gpio input (index 0-7) to a value
   111  func (d *PCA953xDriver) ReadGPIO(idx uint8) (uint8, error) {
   112  	d.mutex.Lock()
   113  	defer d.mutex.Unlock()
   114  
   115  	// read input register
   116  	val, err := d.readRegister(pca953xRegInp)
   117  	// create return bit
   118  	if err != nil {
   119  		return val, err
   120  	}
   121  	val = 1 << uint8(idx) & val
   122  	if val > 1 {
   123  		val = 1
   124  	}
   125  	return val, nil
   126  }
   127  
   128  // WritePeriod set the content of the frequency prescaler of the given index (0,1) with the given value in seconds
   129  func (d *PCA953xDriver) WritePeriod(idx uint8, valSec float32) error {
   130  	d.mutex.Lock()
   131  	defer d.mutex.Unlock()
   132  
   133  	// period is valid in range ~6.58ms..1.68s
   134  	val, err := pca953xCalcPsc(valSec)
   135  	if err != nil && pca953xDebug {
   136  		fmt.Println(err, "value shrinked!")
   137  	}
   138  	var regPsc pca953xRegister = pca953xRegPsc0
   139  	if idx > 0 {
   140  		regPsc = pca953xRegPsc1
   141  	}
   142  	return d.writeRegister(regPsc, val)
   143  }
   144  
   145  // ReadPeriod reads the frequency prescaler in seconds of the given index (0,1)
   146  func (d *PCA953xDriver) ReadPeriod(idx uint8) (float32, error) {
   147  	d.mutex.Lock()
   148  	defer d.mutex.Unlock()
   149  
   150  	regPsc := pca953xRegPsc0
   151  	if idx > 0 {
   152  		regPsc = pca953xRegPsc1
   153  	}
   154  	psc, err := d.readRegister(regPsc)
   155  	if err != nil {
   156  		return -1, err
   157  	}
   158  	return pca953xCalcPeriod(psc), nil
   159  }
   160  
   161  // WriteFrequency set the content of the frequency prescaler of the given index (0,1) with the given value in Hz
   162  func (d *PCA953xDriver) WriteFrequency(idx uint8, valHz float32) error {
   163  	d.mutex.Lock()
   164  	defer d.mutex.Unlock()
   165  
   166  	// frequency is valid in range ~0.6..152Hz
   167  	val, err := pca953xCalcPsc(1 / valHz)
   168  	if err != nil && pca953xDebug {
   169  		fmt.Println(err, "value shrinked!")
   170  	}
   171  	regPsc := pca953xRegPsc0
   172  	if idx > 0 {
   173  		regPsc = pca953xRegPsc1
   174  	}
   175  	return d.writeRegister(regPsc, val)
   176  }
   177  
   178  // ReadFrequency read the frequency prescaler in Hz of the given index (0,1)
   179  func (d *PCA953xDriver) ReadFrequency(idx uint8) (float32, error) {
   180  	d.mutex.Lock()
   181  	defer d.mutex.Unlock()
   182  
   183  	regPsc := pca953xRegPsc0
   184  	if idx > 0 {
   185  		regPsc = pca953xRegPsc1
   186  	}
   187  	psc, err := d.readRegister(regPsc)
   188  	if err != nil {
   189  		return -1, err
   190  	}
   191  	// valHz = 1/valSec
   192  	return 1 / pca953xCalcPeriod(psc), nil
   193  }
   194  
   195  // WriteDutyCyclePercent set the PWM duty cycle of the given index (0,1) with the given value in percent
   196  func (d *PCA953xDriver) WriteDutyCyclePercent(idx uint8, valPercent float32) error {
   197  	d.mutex.Lock()
   198  	defer d.mutex.Unlock()
   199  
   200  	val, err := pca953xCalcPwm(valPercent)
   201  	if err != nil && pca953xDebug {
   202  		fmt.Println(err, "value shrinked!")
   203  	}
   204  	regPwm := pca953xRegPwm0
   205  	if idx > 0 {
   206  		regPwm = pca953xRegPwm1
   207  	}
   208  	return d.writeRegister(regPwm, val)
   209  }
   210  
   211  // ReadDutyCyclePercent get the PWM duty cycle in percent of the given index (0,1)
   212  func (d *PCA953xDriver) ReadDutyCyclePercent(idx uint8) (float32, error) {
   213  	d.mutex.Lock()
   214  	defer d.mutex.Unlock()
   215  
   216  	regPwm := pca953xRegPwm0
   217  	if idx > 0 {
   218  		regPwm = pca953xRegPwm1
   219  	}
   220  	pwm, err := d.readRegister(regPwm)
   221  	if err != nil {
   222  		return -1, err
   223  	}
   224  	// PWM=0..255
   225  	return pca953xCalcDutyCyclePercent(pwm), nil
   226  }
   227  
   228  func (d *PCA953xDriver) writeLED(idx uint8, mode PCA953xGPIOMode) error {
   229  	// prepare
   230  	regLs := pca953xRegLs0
   231  	if idx > 3 {
   232  		regLs = pca953xRegLs1
   233  		idx = idx - 4
   234  	}
   235  	regLsShift := idx * 2
   236  	// read old value
   237  	regLsVal, err := d.readRegister(regLs)
   238  	if err != nil {
   239  		return err
   240  	}
   241  	// reset 2 bits at LED position
   242  	regLsVal &= ^uint8(0x03 << regLsShift)
   243  	// set 2 bits according to mode at LED position
   244  	regLsVal |= uint8(mode) << regLsShift
   245  	// write new value
   246  	return d.writeRegister(regLs, regLsVal)
   247  }
   248  
   249  func (d *PCA953xDriver) writeRegister(regAddress pca953xRegister, val uint8) error {
   250  	// ensure AI bit is not set
   251  	regAddress = regAddress &^ pca953xAiMask
   252  	// write content of requested register
   253  	return d.connection.WriteByteData(uint8(regAddress), val)
   254  }
   255  
   256  func (d *PCA953xDriver) readRegister(regAddress pca953xRegister) (uint8, error) {
   257  	// ensure AI bit is not set
   258  	regAddress = regAddress &^ pca953xAiMask
   259  	return d.connection.ReadByteData(uint8(regAddress))
   260  }
   261  
   262  func pca953xCalcPsc(valSec float32) (uint8, error) {
   263  	// valSec = (PSC+1)/152; (PSC=0..255)
   264  	psc := 152*valSec - 1
   265  	if psc < 0 {
   266  		return 0, errToSmallPeriod
   267  	}
   268  	if psc > 255 {
   269  		return 255, errToBigPeriod
   270  	}
   271  	// add 0.5 for better rounding experience
   272  	return uint8(psc + 0.5), nil
   273  }
   274  
   275  func pca953xCalcPeriod(psc uint8) float32 {
   276  	return (float32(psc) + 1) / 152
   277  }
   278  
   279  func pca953xCalcPwm(valPercent float32) (uint8, error) {
   280  	// valPercent = PWM/256*(256/255*100); (PWM=0..255)
   281  	pwm := 255 * valPercent / 100
   282  	if pwm < 0 {
   283  		return 0, errToSmallDutyCycle
   284  	}
   285  	if pwm > 255 {
   286  		return 255, errToBigDutyCycle
   287  	}
   288  	// add 0.5 for better rounding experience
   289  	return uint8(pwm + 0.5), nil
   290  }
   291  
   292  func pca953xCalcDutyCyclePercent(pwm uint8) float32 {
   293  	return 100 * float32(pwm) / 255
   294  }