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 }