tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/pca9685/pca9685.go (about) 1 package pca9685 2 3 import ( 4 "encoding/binary" 5 "time" 6 7 "tinygo.org/x/drivers" 8 ) 9 10 const ( 11 // Internal oscillator frequency. 12 oscclock = 25000000 13 // Max value PWM value (on or off) can take. 14 maxtop = 1<<12 - 1 15 milliseconds = 1_000_000 // units in nanoseconds 16 ) 17 18 // Dev is a handle to the PCA9685 device given an address 19 // (usually 0x47) and an i2c bus. 20 type Dev struct { 21 addr uint8 22 bus drivers.I2C 23 buf [4]byte 24 } 25 26 type PWMConfig struct { 27 Period uint64 28 } 29 30 // New creates a new instance of a PCA9685 device. It performs 31 // no IO on the i2c bus. 32 func New(bus drivers.I2C, addr uint8) Dev { 33 return Dev{ 34 bus: bus, 35 addr: addr, 36 } 37 } 38 39 // Configure enables autoincrement, sets all PWM signals to logic low (Ground) 40 // and finally sets the Period. 41 func (d Dev) Configure(cfg PWMConfig) error { 42 err := d.SetAI(true) 43 if err != nil { 44 return err 45 } 46 d.SetAll(0) 47 d.SetDrive(true) 48 return d.SetPeriod(cfg.Period) 49 } 50 51 // SetPeriod updates the period of this PWM integrated circuit in nanoseconds. 52 // To set a particular frequency, use the following formula: 53 // 54 // period = 1e9 / frequency 55 // 56 // In the equation above frequency is in Hertz. 57 // 58 // If you use a period of 0, a period that works well for LEDs will be picked. 59 // 60 // PCA9685 accepts frequencies inbetween [40..1000 Hz], 61 // or expressed as a period [1..25ms]. 62 func (d Dev) SetPeriod(period uint64) error { 63 const div = maxtop + 1 64 if period == 0 { 65 period = 1 * milliseconds 66 } 67 if period > 25*milliseconds || period < 1*milliseconds { 68 return ErrBadPeriod 69 } 70 // Correct for overshoot in provided frequency: https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library/issues/11 71 // Note: 0.96 was empirically determined to be closer. Should follow up to understand what is happening here. 72 freq := 96 * 1e9 / (100 * period) 73 prescale := byte(oscclock/(div*freq) - 1) 74 err := d.Sleep(true) // Enable sleep to write to PRESCALE register 75 if err != nil { 76 return err 77 } 78 d.buf[0] = prescale 79 err = d.writeReg(PRESCALE, d.buf[:1]) 80 if err != nil { 81 return err 82 } 83 return d.Sleep(false) 84 } 85 86 // Top returns max value PWM can take. 87 func (d Dev) Top() uint32 { 88 return maxtop 89 } 90 91 // Set sets the `on` value of a PWM channel in the range [0..15]. 92 // Max value `on` can take is 4095. 93 // Example: 94 // 95 // d.Set(1, d.Top()/4) 96 // 97 // sets the dutycycle of second (LED1) channel to 25%. 98 func (d Dev) Set(channel uint8, on uint32) { 99 if on > maxtop { 100 panic("pca9685: value must be in range 0..4095") 101 } 102 d.SetPhased(channel, on, 0) 103 } 104 105 // SetAll sets all PWM signals to a ON value. Equivalent of calling 106 // 107 // Dev.Set(pca9685.ALLLED, value) 108 func (d Dev) SetAll(on uint32) { 109 d.Set(ALLLED, on) 110 } 111 112 // IsConnected returns error if read fails or if 113 // driver suspects device is not connected. 114 func (d Dev) IsConnected() error { 115 // Set data to the NOT of default MODE1 contents. 116 // If read is succesful then data will be modified 117 const notdefaultMODE1 = ^defaultMODE1Value 118 d.buf[0] = notdefaultMODE1 119 120 err := d.readReg(MODE1, d.buf[:1]) 121 if err != nil { 122 return err 123 } else if d.buf[0] == notdefaultMODE1 { 124 return ErrInvalidMode1 125 } 126 return nil 127 } 128 129 // SetAI enables or disables autoincrement feature on device. Useful for 130 // writing to many consecutive registers in one shot. 131 func (d Dev) SetAI(ai bool) error { 132 err := d.readReg(MODE1, d.buf[:1]) 133 if err != nil { 134 return err 135 } 136 if ai { 137 d.buf[0] |= AI 138 } else { 139 d.buf[0] &^= AI 140 } 141 err = d.writeReg(MODE1, d.buf[:1]) 142 return err 143 } 144 145 // SetDrive configures PWM output connection in MODE2 register. 146 // 147 // false: The 16 LEDn outputs are configured with an open-drain structure. 148 // true: The 16 LEDn outputs are configured with a totem pole structure. 149 func (d Dev) SetDrive(outdrv bool) error { 150 err := d.readReg(MODE2, d.buf[:1]) 151 if err != nil { 152 return err 153 } 154 if outdrv { 155 d.buf[0] |= OUTDRV 156 } else { 157 d.buf[0] &^= OUTDRV 158 } 159 return d.writeReg(MODE2, d.buf[:1]) 160 } 161 162 // Sleep sets/unsets SLEEP bit in MODE1. 163 // 164 // if sleepEnabled 165 // Stops PWM. Allows writing to PRE_SCALE register. 166 // else 167 // wakes PCA9685. Resumes PWM. 168 func (d Dev) Sleep(sleepEnabled bool) error { 169 err := d.readReg(MODE1, d.buf[:1]) 170 if err != nil { 171 return err 172 } 173 if sleepEnabled { 174 d.buf[0] |= SLEEP 175 return d.writeReg(MODE1, d.buf[:1]) 176 } 177 d.buf[0] &^= SLEEP 178 err = d.writeReg(MODE1, d.buf[:1]) 179 // It takes 500μs max for the oscillator to be up and running once SLEEP bit 180 // has been set to logic 0. Timings on LEDn outputs are not guaranteed if PWM 181 // control registers are accessed within the 500μs window. 182 // There is no start-up delay required when using the EXTCLK pin as the PWM clock. 183 time.Sleep(1000 * time.Microsecond) // Requested by datasheet. 184 return err 185 } 186 187 // SetInverting inverts ALL PCA9685 PWMs. The channel argument merely implements PWM interface. 188 // 189 // Without inverting, a 25% duty cycle would mean the output is high for 25% of 190 // the time and low for the rest. Inverting flips the output as if a NOT gate 191 // was placed at the output, meaning that the output would be 25% low and 75% 192 // high with a duty cycle of 25%. 193 func (d Dev) SetInverting(_ uint8, inverting bool) error { 194 err := d.readReg(MODE2, d.buf[:1]) 195 if err != nil { 196 return err 197 } 198 if inverting { 199 d.buf[0] |= INVRT 200 } else { 201 d.buf[0] &^= INVRT 202 } 203 return d.writeReg(MODE2, d.buf[:1]) 204 } 205 206 // SetPhased sets PWM on and off mark. 207 // The ON time, which is programmable, will be the time the LED output 208 // will be asserted and the OFF time, which is also programmable, will be 209 // the time when the LED output will be negated. 210 // In this way, the phase shift becomes completely programmable. 211 // The resolution for the phase shift is 1⁄4096 of the target frequency. 212 func (d Dev) SetPhased(channel uint8, on, off uint32) { 213 binary.LittleEndian.PutUint16(d.buf[:2], uint16(on)&maxtop) 214 binary.LittleEndian.PutUint16(d.buf[2:4], uint16(off)&maxtop) 215 onLReg, _, _, _ := LED(channel) 216 d.writeReg(onLReg, d.buf[:4]) 217 }