tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/vl6180x/vl6180x.go (about) 1 // Package vl6180x provides a driver for the VL6180X time-of-flight distance sensor 2 // 3 // Datasheet: 4 // https://www.st.com/resource/en/datasheet/vl6180x.pdf 5 // This driver was based on the library https://github.com/adafruit/Adafruit_VL6180X 6 // and document 'AN4545 VL6180X basic ranging application note': 7 // https://www.st.com/resource/en/application_note/an4545-vl6180x-basic-ranging-application-note-stmicroelectronics.pdf 8 package vl6180x // import "tinygo.org/x/drivers/vl6180x" 9 10 import ( 11 "time" 12 13 "tinygo.org/x/drivers" 14 ) 15 16 type VL6180XError uint8 17 18 // Device wraps an I2C connection to a VL6180X device. 19 type Device struct { 20 bus drivers.I2C 21 Address uint16 22 timeout uint32 23 } 24 25 // New creates a new VL6180X connection. The I2C bus must already be 26 // configured. 27 // 28 // This function only creates the Device object, it does not touch the device. 29 func New(bus drivers.I2C) Device { 30 return Device{ 31 bus: bus, 32 Address: Address, 33 timeout: 500, 34 } 35 } 36 37 // Connected returns whether a VL6180X has been found. 38 // It does a "who am I" request and checks the response. 39 func (d *Device) Connected() bool { 40 return d.readReg(WHO_AM_I) == CHIP_ID 41 } 42 43 // Configure sets up the device for communication 44 func (d *Device) Configure(use2v8Mode bool) bool { 45 if !d.Connected() { 46 return false 47 } 48 49 if (d.readReg(SYSTEM_FRESH_OUT_OF_RESET) & 0x01) == 0x01 { 50 51 // mandatory settings from page 24 of AN4545 52 d.writeReg(0x0207, 0x01) 53 d.writeReg(0x0208, 0x01) 54 d.writeReg(0x0096, 0x00) 55 d.writeReg(0x0097, 0xfd) 56 d.writeReg(0x00e3, 0x00) 57 d.writeReg(0x00e4, 0x04) 58 d.writeReg(0x00e5, 0x02) 59 d.writeReg(0x00e6, 0x01) 60 d.writeReg(0x00e7, 0x03) 61 d.writeReg(0x00f5, 0x02) 62 d.writeReg(0x00d9, 0x05) 63 d.writeReg(0x00db, 0xce) 64 d.writeReg(0x00dc, 0x03) 65 d.writeReg(0x00dd, 0xf8) 66 d.writeReg(0x009f, 0x00) 67 d.writeReg(0x00a3, 0x3c) 68 d.writeReg(0x00b7, 0x00) 69 d.writeReg(0x00bb, 0x3c) 70 d.writeReg(0x00b2, 0x09) 71 d.writeReg(0x00ca, 0x09) 72 d.writeReg(0x0198, 0x01) 73 d.writeReg(0x01b0, 0x17) 74 d.writeReg(0x01ad, 0x00) 75 d.writeReg(0x00ff, 0x05) 76 d.writeReg(0x0100, 0x05) 77 d.writeReg(0x0199, 0x05) 78 d.writeReg(0x01a6, 0x1b) 79 d.writeReg(0x01ac, 0x3e) 80 d.writeReg(0x01a7, 0x1f) 81 d.writeReg(0x0030, 0x00) 82 83 // recommended settings 84 d.writeReg(0x0011, 0x10) // enables polling when measurement completes 85 d.writeReg(0x010a, 0x30) // sets averaging sample period 86 d.writeReg(0x003f, 0x46) // sets light and dark gain 87 d.writeReg(0x0031, 0xFF) // sets the # of range measurements for auto calibration 88 d.writeReg(0x0041, 0x63) // sets ALS integration time to 100ms 89 d.writeReg(0x002e, 0x01) // performs a single temperature calibration 90 91 // optional settings 92 d.writeReg(RANGING_INTERMEASUREMENT_PERIOD, 0x09) // sets ranging inter-measurement period to 100ms 93 d.writeReg(ALS_INTERMEASUREMENT_PERIOD, 0x31) // sets default ALS inter-measurement period to 500ms 94 d.writeReg(SYSTEM_INTERRUPT_CONFIG, 0x24) // configures interrupt 95 96 d.writeReg(SYSTEM_FRESH_OUT_OF_RESET, 0x00) 97 time.Sleep(100 * time.Microsecond) 98 } 99 100 return true 101 } 102 103 // Read returns the proximity of the sensor in mm 104 func (d *Device) Read() uint16 { 105 start := time.Now() 106 107 for d.dataReady() { 108 elapsed := time.Since(start) 109 if d.timeout > 0 && uint32(elapsed.Seconds()*1000) > d.timeout { 110 return 0 111 } 112 } 113 114 d.writeReg(SYSRANGE_START, 0x01) 115 for (d.readReg(RESULT_INTERRUPT_STATUS_GPIO) & 0x04) == 0 { 116 } 117 118 return uint16(d.readRangeResult()) 119 } 120 121 // dataReady returns true when the data is ready to be read 122 func (d *Device) dataReady() bool { 123 return (d.readReg(RESULT_RANGE_STATUS) & 0x01) == 0 124 } 125 126 // startRange starts the readings 127 func (d *Device) startRange() { 128 for d.dataReady() { 129 } 130 d.writeReg(SYSRANGE_START, 0x01) 131 } 132 133 // IsRangeComplete return true when the reading is complete 134 func (d *Device) IsRangeComplete() bool { 135 if (d.readReg(RESULT_INTERRUPT_STATUS_GPIO) & 0x04) != 0 { 136 return true 137 } 138 return false 139 } 140 141 // readRangeResults returns the sensor value from the register 142 func (d *Device) readRangeResult() uint8 { 143 value := d.readReg(RESULT_RANGE_VAL) 144 145 d.writeReg(SYSTEM_INTERRUPT_CLEAR, 0x07) 146 return value 147 } 148 149 // StartRangeContinuous starts the continuous reading mode 150 func (d *Device) StartRangeContinuous(periodInMs uint16) { 151 var periodReg uint8 152 if periodInMs > 10 { 153 if periodInMs < 2550 { 154 periodReg = uint8(periodInMs/10) - 1 155 } else { 156 periodReg = 254 157 } 158 } 159 d.writeReg(RANGING_INTERMEASUREMENT_PERIOD, periodReg) 160 d.writeReg(SYSRANGE_START, 0x03) 161 } 162 163 // StopRangeContinuous stops the continuous reading mode 164 func (d *Device) StopRangeContinuous() { 165 d.writeReg(SYSRANGE_START, 0x01) 166 } 167 168 // ReadStatus returns the current status of the sensor 169 func (d *Device) ReadStatus() uint8 { 170 return d.readReg(RESULT_RANGE_STATUS) >> 4 171 } 172 173 // ReadLux returns the lux of the sensor 174 func (d *Device) ReadLux(gain uint8) (lux uint32) { 175 reg := d.readReg(SYSTEM_INTERRUPT_CONFIG) 176 reg &= ^uint8(0x38) 177 reg |= 0x4 << 3 178 d.writeReg(SYSTEM_INTERRUPT_CONFIG, reg) 179 180 d.writeReg(SYSALS_INTEGRATION_PERIOD_HI, 0) 181 d.writeReg(SYSALS_INTEGRATION_PERIOD_HI, 100) 182 183 if gain > ALS_GAIN_40 { 184 gain = ALS_GAIN_40 185 } 186 d.writeReg(SYSALS_ANALOGUE_GAIN, 0x40|gain) 187 188 d.writeReg(SYSALS_START, 0x1) 189 for 4 != ((d.readReg(RESULT_INTERRUPT_STATUS_GPIO) >> 3) & 0x7) { 190 } 191 192 lux = uint32(d.readReg16Bit(RESULT_ALS_VAL)) * 320 193 d.writeReg(SYSTEM_INTERRUPT_CLEAR, 0x07) 194 195 switch gain { 196 case ALS_GAIN_1: 197 break 198 case ALS_GAIN_1_25: 199 lux = (lux * 100) / 125 200 break 201 case ALS_GAIN_1_67: 202 lux = (lux * 100) / 167 203 break 204 case ALS_GAIN_2_5: 205 lux = (lux * 10) / 25 206 break 207 case ALS_GAIN_5: 208 lux /= 5 209 break 210 case ALS_GAIN_10: 211 lux /= 10 212 break 213 case ALS_GAIN_20: 214 lux /= 20 215 break 216 case ALS_GAIN_40: 217 lux /= 40 218 break 219 } 220 221 return lux 222 } 223 224 // SetOffset sets the offset 225 func (d *Device) SetOffset(offset uint8) { 226 d.writeReg(SYSRANGE_PART_TO_PART_RANGE_OFFSET, offset) 227 } 228 229 // SetAddress sets the I2C address which this device listens to. 230 func (d *Device) SetAddress(address uint8) { 231 d.writeReg(I2C_SLAVE_DEVICE_ADDRESS, address) 232 d.Address = uint16(address) 233 } 234 235 // GetAddress returns the I2C address which this device listens to. 236 func (d *Device) GetAddress() uint8 { 237 return uint8(d.Address) 238 } 239 240 // writeReg sends a single byte to the specified register address 241 func (d *Device) writeReg(reg uint16, value uint8) { 242 msb := byte((reg >> 8) & 0xFF) 243 lsb := byte(reg & 0xFF) 244 d.bus.Tx(d.Address, []byte{msb, lsb, value}, nil) 245 } 246 247 // readReg reads a single byte from the specified address 248 func (d *Device) readReg(reg uint16) uint8 { 249 data := []byte{0} 250 msb := byte((reg >> 8) & 0xFF) 251 lsb := byte(reg & 0xFF) 252 d.bus.Tx(d.Address, []byte{msb, lsb}, data) 253 return data[0] 254 } 255 256 // readReg16Bit reads two bytes from the specified address 257 // and returns it as a uint16 258 func (d *Device) readReg16Bit(reg uint16) uint16 { 259 data := []byte{0, 0} 260 msb := byte((reg >> 8) & 0xFF) 261 lsb := byte(reg & 0xFF) 262 d.bus.Tx(d.Address, []byte{msb, lsb}, data) 263 return readUint(data[0], data[1]) 264 } 265 266 // readUint converts two bytes to uint16 267 func readUint(msb byte, lsb byte) uint16 { 268 return (uint16(msb) << 8) | uint16(lsb) 269 }