Mirror with some correction

Dependencies:   mbed FastIO FastPWM USBDevice

Committer:
arnoz
Date:
Fri Oct 01 08:19:46 2021 +0000
Revision:
116:7a67265d7c19
Parent:
113:7330439f2ffc
- Correct information regarding your last merge

Who changed what in which revision?

UserRevisionLine numberNew contents of line
mjr 111:42dc75fbe623 1 // VCNL4010 IR proximity sensor
mjr 111:42dc75fbe623 2
mjr 111:42dc75fbe623 3 #include "mbed.h"
mjr 111:42dc75fbe623 4 #include "math.h"
mjr 111:42dc75fbe623 5 #include "VCNL4010.h"
mjr 111:42dc75fbe623 6
mjr 113:7330439f2ffc 7
mjr 113:7330439f2ffc 8 VCNL4010::VCNL4010(PinName sda, PinName scl, bool internalPullups, int iredCurrent)
mjr 111:42dc75fbe623 9 : i2c(sda, scl, internalPullups)
mjr 111:42dc75fbe623 10 {
mjr 113:7330439f2ffc 11 // Calculate the scaling factor with a minimum proximitiy count of 5.
mjr 113:7330439f2ffc 12 // In actual practice, the minimum will usually be a lot higher, but
mjr 113:7330439f2ffc 13 // this is a safe default that gives us valid distance calculations
mjr 113:7330439f2ffc 14 // across almost the whole possible range of count values. (Why not
mjr 113:7330439f2ffc 15 // zero? Because of the inverse relationship between distance and
mjr 113:7330439f2ffc 16 // brightness == proximity count. 1/0 isn't meaningful, so we have
mjr 113:7330439f2ffc 17 // to use a non-zero minimum in the scaling calculation. 5 is so
mjr 113:7330439f2ffc 18 // low that it'll probably never actually happen in real readings,
mjr 113:7330439f2ffc 19 // but still gives us a reasonable scaled range.)
mjr 113:7330439f2ffc 20 calibrating = false;
mjr 113:7330439f2ffc 21 minProxCount = 100;
mjr 113:7330439f2ffc 22 maxProxCount = 65535;
mjr 113:7330439f2ffc 23 parkProxCount = 20000;
mjr 113:7330439f2ffc 24 dcOffset = 0;
mjr 113:7330439f2ffc 25 lastProxCount = 0;
mjr 113:7330439f2ffc 26 calcScalingFactor();
mjr 113:7330439f2ffc 27
mjr 113:7330439f2ffc 28 // remember the desired IRED current setting
mjr 113:7330439f2ffc 29 this->iredCurrent = iredCurrent;
mjr 111:42dc75fbe623 30 }
mjr 111:42dc75fbe623 31
mjr 113:7330439f2ffc 32 // Initialize the sensor device
mjr 111:42dc75fbe623 33 void VCNL4010::init()
mjr 111:42dc75fbe623 34 {
mjr 113:7330439f2ffc 35 // debugging instrumentation
mjr 111:42dc75fbe623 36 printf("VCNL4010 initializing\r\n");
mjr 111:42dc75fbe623 37
mjr 111:42dc75fbe623 38 // reset the I2C bus
mjr 111:42dc75fbe623 39 i2c.reset();
mjr 111:42dc75fbe623 40
mjr 111:42dc75fbe623 41 // Set the proximity sampling rate to the fastest available rate of
mjr 113:7330439f2ffc 42 // 250 samples/second (4ms/sample). This isn't quite fast enough for
mjr 113:7330439f2ffc 43 // perfect plunger motion tracking - a minimum sampling frequency of
mjr 113:7330439f2ffc 44 // 400/s is needed to avoid aliasing during the bounce-back phase of
mjr 113:7330439f2ffc 45 // release motions. But the plunger-independent part of the code
mjr 113:7330439f2ffc 46 // does some data processing to tolerate aliasing for even slower
mjr 113:7330439f2ffc 47 // sensors than this one, so this isn't a showstopper. Apart from
mjr 113:7330439f2ffc 48 // the potential for aliasing during fast motion, 250/s is plenty
mjr 113:7330439f2ffc 49 // fast enough for responsive input and smooth animation.
mjr 111:42dc75fbe623 50 writeReg(0x82, 0x07);
mjr 111:42dc75fbe623 51
mjr 111:42dc75fbe623 52 // Set the current for the IR LED (the light source for proximity
mjr 113:7330439f2ffc 53 // measurements). This is in units of 10mA, up to 200mA. If the
mjr 113:7330439f2ffc 54 // parameter is zero in the configuration, apply a default. Make
mjr 113:7330439f2ffc 55 // sure it's in range (1..20).
mjr 111:42dc75fbe623 56 //
mjr 113:7330439f2ffc 57 // Note that the nominal current level isn't the same as the actual
mjr 113:7330439f2ffc 58 // current load on the sensor's power supply. The nominal current
mjr 113:7330439f2ffc 59 // set here is the instantaneous current the chip uses to generate
mjr 113:7330439f2ffc 60 // IR pulses. The pulses have a low duty cycle, so the continuous
mjr 113:7330439f2ffc 61 // current drawn on the chip's power inputs is much lower. The
mjr 113:7330439f2ffc 62 // data sheet says that the total continuous power supply current
mjr 113:7330439f2ffc 63 // drawn with the most power-hungry settings (IRED maxed out at
mjr 113:7330439f2ffc 64 // 200mA, sampling frequency maxed at 250 Hz) is only 4mA. So
mjr 113:7330439f2ffc 65 // there's no need to worry about blowing a fuse on the USB port
mjr 113:7330439f2ffc 66 // or frying the KL25Z 3.3V regulator - the chip draws negligible
mjr 113:7330439f2ffc 67 // power in those terms, even at the maximum IRED setting.
mjr 113:7330439f2ffc 68 uint8_t cur = static_cast<uint8_t>(iredCurrent);
mjr 113:7330439f2ffc 69 cur = (cur == 0 ? 10 : cur < 1 ? 1 : cur > 20 ? 20 : cur);
mjr 113:7330439f2ffc 70 writeReg(0x83, cur);
mjr 111:42dc75fbe623 71
mjr 111:42dc75fbe623 72 // disable self-timed measurements - we'll start measurements on demand
mjr 111:42dc75fbe623 73 writeReg(0x80, 0x00);
mjr 111:42dc75fbe623 74
mjr 111:42dc75fbe623 75 // start the sample timer, which we use to gather timing statistics
mjr 111:42dc75fbe623 76 sampleTimer.start();
mjr 111:42dc75fbe623 77
mjr 113:7330439f2ffc 78 // debugging instrumentation
mjr 111:42dc75fbe623 79 printf("VCNL4010 initialization done\r\n");
mjr 111:42dc75fbe623 80 }
mjr 111:42dc75fbe623 81
mjr 113:7330439f2ffc 82 // Start a proximity measurement. This initiates a proximity reading
mjr 113:7330439f2ffc 83 // in the chip, and returns immediately, allowing the KL25Z to tend to
mjr 113:7330439f2ffc 84 // other tasks while waiting for the reading to complete. proxReady()
mjr 113:7330439f2ffc 85 // can be used to poll for completion.
mjr 111:42dc75fbe623 86 void VCNL4010::startProxReading()
mjr 111:42dc75fbe623 87 {
mjr 111:42dc75fbe623 88 // set the prox_od (initiate proximity on demand) bit (0x08) in
mjr 111:42dc75fbe623 89 // the command register, if it's not already set
mjr 111:42dc75fbe623 90 uint8_t b = readReg(0x80);
mjr 111:42dc75fbe623 91 if ((b & 0x08) == 0)
mjr 111:42dc75fbe623 92 {
mjr 111:42dc75fbe623 93 tSampleStart = sampleTimer.read_us();
mjr 111:42dc75fbe623 94 writeReg(0x80, b | 0x08);
mjr 111:42dc75fbe623 95 }
mjr 111:42dc75fbe623 96 }
mjr 111:42dc75fbe623 97
mjr 113:7330439f2ffc 98 // Check if a proximity sample is ready. Implicitly starts a new reading
mjr 113:7330439f2ffc 99 // if one isn't already either completed or in progress. Returns true if
mjr 113:7330439f2ffc 100 // a reading is ready, false if not.
mjr 111:42dc75fbe623 101 bool VCNL4010::proxReady()
mjr 111:42dc75fbe623 102 {
mjr 111:42dc75fbe623 103 // read the command register to get the status bits
mjr 111:42dc75fbe623 104 uint8_t b = readReg(0x80);
mjr 111:42dc75fbe623 105
mjr 111:42dc75fbe623 106 // if the prox_data_rdy bit (0x20) is set, a reading is ready
mjr 111:42dc75fbe623 107 if ((b & 0x20) != 0)
mjr 111:42dc75fbe623 108 return true;
mjr 111:42dc75fbe623 109
mjr 111:42dc75fbe623 110 // Not ready. Since the caller is polling, they must expect a reading
mjr 111:42dc75fbe623 111 // to be in progress; if not, start one now. A reading in progress is
mjr 111:42dc75fbe623 112 // indicated and initiated by the prox_od bit
mjr 111:42dc75fbe623 113 if ((b & 0x08) == 0)
mjr 111:42dc75fbe623 114 {
mjr 111:42dc75fbe623 115 tSampleStart = sampleTimer.read_us();
mjr 111:42dc75fbe623 116 writeReg(0x80, b | 0x08);
mjr 111:42dc75fbe623 117 }
mjr 111:42dc75fbe623 118
mjr 111:42dc75fbe623 119 // a reading is available if the prox_data_rdy (0x08) is set
mjr 111:42dc75fbe623 120 return (b & 0x20) != 0;
mjr 111:42dc75fbe623 121 }
mjr 111:42dc75fbe623 122
mjr 113:7330439f2ffc 123 // Read the current proximity reading. If a reading isn't ready,
mjr 113:7330439f2ffc 124 // we'll block until one is, up to the specified timeout interval.
mjr 113:7330439f2ffc 125 // Returns zero if a reading was successfully retrieved, or a
mjr 113:7330439f2ffc 126 // non-zero error code if a timeout or error occurs.
mjr 113:7330439f2ffc 127 //
mjr 113:7330439f2ffc 128 // Note that the returned proximity count value is the raw reading
mjr 113:7330439f2ffc 129 // from the sensor, which indicates the intensity of the reflected
mjr 113:7330439f2ffc 130 // light detected on the sensor, on an abstract scale from 0 to
mjr 113:7330439f2ffc 131 // 65535. The proximity count is inversely related to the distance
mjr 113:7330439f2ffc 132 // to the target, but the relationship also depends upon many other
mjr 113:7330439f2ffc 133 // factors, such as the size and reflectivity of the target, ambient
mjr 113:7330439f2ffc 134 // light, and internal reflections within the sensor itself and
mjr 113:7330439f2ffc 135 // within the overall apparatus.
mjr 113:7330439f2ffc 136 int VCNL4010::getProx(int &proxCount,
mjr 113:7330439f2ffc 137 uint32_t &tMid, uint32_t &dt, uint32_t timeout_us)
mjr 111:42dc75fbe623 138 {
mjr 113:7330439f2ffc 139 // If the chip isn't responding, try resetting it. I2C will
mjr 113:7330439f2ffc 140 // generally report 0xFF on all byte reads when a device isn't
mjr 113:7330439f2ffc 141 // responding to commands, since the pull-up resistors on SDA
mjr 113:7330439f2ffc 142 // will make all data bits look like '1' on read. It's
mjr 113:7330439f2ffc 143 // conceivable that a device could lock up while holding SDA
mjr 113:7330439f2ffc 144 // low, too, so a value of 0x00 could also be reported. So to
mjr 113:7330439f2ffc 145 // sense if the device is answering, we should try reading a
mjr 113:7330439f2ffc 146 // register that, when things are working properly, should
mjr 113:7330439f2ffc 147 // always hold a value that's not either 0x00 or 0xFF. For
mjr 113:7330439f2ffc 148 // the VCNL4010, we can read the product ID register, which
mjr 113:7330439f2ffc 149 // should report ID value 0x21 per the data sheet. The low
mjr 113:7330439f2ffc 150 // nybble is a product revision number, so we shouldn't
mjr 113:7330439f2ffc 151 // insist on the value 0x21 - it could be 0x22 or 0x23, etc,
mjr 113:7330439f2ffc 152 // in future revisions of this chip. But in any case, the
mjr 113:7330439f2ffc 153 // register should definitely not be 0x00 or 0xFF, so it's
mjr 113:7330439f2ffc 154 // a good solid test.
mjr 113:7330439f2ffc 155 uint8_t prodId = readReg(0x81);
mjr 113:7330439f2ffc 156 if (prodId == 0x00 || prodId == 0xFF)
mjr 113:7330439f2ffc 157 {
mjr 113:7330439f2ffc 158 // try resetting the chip
mjr 113:7330439f2ffc 159 init();
mjr 113:7330439f2ffc 160
mjr 113:7330439f2ffc 161 // check if that cleared the problem; if not, give up and
mjr 113:7330439f2ffc 162 // return an error
mjr 113:7330439f2ffc 163 prodId = readReg(0x81);
mjr 113:7330439f2ffc 164 if (prodId == 0x00 || prodId == 0xFF)
mjr 113:7330439f2ffc 165 return 1;
mjr 113:7330439f2ffc 166 }
mjr 113:7330439f2ffc 167
mjr 111:42dc75fbe623 168 // wait for the sample
mjr 111:42dc75fbe623 169 Timer t;
mjr 111:42dc75fbe623 170 t.start();
mjr 111:42dc75fbe623 171 for (;;)
mjr 111:42dc75fbe623 172 {
mjr 111:42dc75fbe623 173 // check for a sample
mjr 111:42dc75fbe623 174 if (proxReady())
mjr 111:42dc75fbe623 175 break;
mjr 111:42dc75fbe623 176
mjr 111:42dc75fbe623 177 // if we've exceeded the timeout, return failure
mjr 111:42dc75fbe623 178 if (t.read_us() > timeout_us)
mjr 111:42dc75fbe623 179 return -1;
mjr 111:42dc75fbe623 180 }
mjr 111:42dc75fbe623 181
mjr 111:42dc75fbe623 182 // figure the time since we initiated the reading
mjr 111:42dc75fbe623 183 dt = sampleTimer.read_us() - tSampleStart;
mjr 111:42dc75fbe623 184
mjr 111:42dc75fbe623 185 // figure the midpoint time
mjr 111:42dc75fbe623 186 tMid = tSampleStart + dt/2;
mjr 111:42dc75fbe623 187
mjr 111:42dc75fbe623 188 // read the result from the sensor, as a 16-bit proximity count value
mjr 113:7330439f2ffc 189 int N = (static_cast<int>(readReg(0x87)) << 8) | readReg(0x88);
mjr 113:7330439f2ffc 190
mjr 113:7330439f2ffc 191 // remember the last raw reading
mjr 113:7330439f2ffc 192 lastProxCount = N;
mjr 111:42dc75fbe623 193
mjr 111:42dc75fbe623 194 // start a new reading, so that the sensor is collecting the next
mjr 111:42dc75fbe623 195 // reading concurrently with the time-consuming floating-point math
mjr 111:42dc75fbe623 196 // we're about to do
mjr 111:42dc75fbe623 197 startProxReading();
mjr 113:7330439f2ffc 198
mjr 113:7330439f2ffc 199 // if calibration is in progress, note the new min/max proximity
mjr 113:7330439f2ffc 200 // count readings, if applicable
mjr 113:7330439f2ffc 201 if (calibrating)
mjr 113:7330439f2ffc 202 {
mjr 113:7330439f2ffc 203 if (N < minProxCount)
mjr 113:7330439f2ffc 204 minProxCount = N;
mjr 113:7330439f2ffc 205 if (N > maxProxCount)
mjr 113:7330439f2ffc 206 maxProxCount = N;
mjr 113:7330439f2ffc 207 }
mjr 113:7330439f2ffc 208
mjr 113:7330439f2ffc 209 // report the raw count back to the caller
mjr 113:7330439f2ffc 210 proxCount = N;
mjr 113:7330439f2ffc 211
mjr 111:42dc75fbe623 212 // success
mjr 111:42dc75fbe623 213 return 0;
mjr 111:42dc75fbe623 214 }
mjr 111:42dc75fbe623 215
mjr 113:7330439f2ffc 216 // Restore the saved calibration data from the configuration
mjr 113:7330439f2ffc 217 void VCNL4010::restoreCalibration(Config &config)
mjr 113:7330439f2ffc 218 {
mjr 113:7330439f2ffc 219 // remember the calibrated minimum proximity count
mjr 113:7330439f2ffc 220 this->minProxCount = config.plunger.cal.raw0;
mjr 113:7330439f2ffc 221 this->maxProxCount = config.plunger.cal.raw1;
mjr 113:7330439f2ffc 222 this->parkProxCount = config.plunger.cal.raw2;
mjr 113:7330439f2ffc 223
mjr 113:7330439f2ffc 224 // figure the scaling factor for distance calculations
mjr 113:7330439f2ffc 225 calcScalingFactor();
mjr 113:7330439f2ffc 226 }
mjr 113:7330439f2ffc 227
mjr 113:7330439f2ffc 228 // Begin calibration
mjr 113:7330439f2ffc 229 void VCNL4010::beginCalibration()
mjr 113:7330439f2ffc 230 {
mjr 113:7330439f2ffc 231 // reset the min/max proximity count to the last reading
mjr 113:7330439f2ffc 232 calibrating = true;
mjr 113:7330439f2ffc 233 minProxCount = lastProxCount;
mjr 113:7330439f2ffc 234 maxProxCount = lastProxCount;
mjr 113:7330439f2ffc 235 parkProxCount = lastProxCount;
mjr 113:7330439f2ffc 236 }
mjr 113:7330439f2ffc 237
mjr 113:7330439f2ffc 238 // End calibration
mjr 113:7330439f2ffc 239 void VCNL4010::endCalibration(Config &config)
mjr 113:7330439f2ffc 240 {
mjr 113:7330439f2ffc 241 // save the proximity count range data from the calibration in the
mjr 113:7330439f2ffc 242 // caller's configuration, so that we can restore the scaling
mjr 113:7330439f2ffc 243 // factor calculation on the next boot
mjr 113:7330439f2ffc 244 config.plunger.cal.raw0 = minProxCount;
mjr 113:7330439f2ffc 245 config.plunger.cal.raw1 = maxProxCount;
mjr 113:7330439f2ffc 246 config.plunger.cal.raw2 = parkProxCount;
mjr 113:7330439f2ffc 247
mjr 113:7330439f2ffc 248 // calculate the new scaling factor for conversions to distance
mjr 113:7330439f2ffc 249 calcScalingFactor();
mjr 113:7330439f2ffc 250
mjr 113:7330439f2ffc 251 // Set the new calibration range in distance units. The range
mjr 113:7330439f2ffc 252 // in distance units is fixed, since we choose the scaling factor
mjr 113:7330439f2ffc 253 // specifically to cover the fixed range.
mjr 113:7330439f2ffc 254 config.plunger.cal.zero = 10922;
mjr 113:7330439f2ffc 255 config.plunger.cal.min = 0;
mjr 113:7330439f2ffc 256 config.plunger.cal.max = 65535;
mjr 113:7330439f2ffc 257
mjr 113:7330439f2ffc 258 // we're no longer calibrating
mjr 113:7330439f2ffc 259 calibrating = false;
mjr 113:7330439f2ffc 260 }
mjr 113:7330439f2ffc 261
mjr 113:7330439f2ffc 262 // Power law function for the relationship between sensor count
mjr 113:7330439f2ffc 263 // readings and distance. For our distance calculations, we use
mjr 113:7330439f2ffc 264 // this relationship:
mjr 113:7330439f2ffc 265 //
mjr 113:7330439f2ffc 266 // distance = <scaling factor> * 1/power(count - <DC offset>) + <scaling offset>
mjr 113:7330439f2ffc 267 //
mjr 113:7330439f2ffc 268 // where all of the constants in <angle brackets> are determined
mjr 113:7330439f2ffc 269 // through calibration.
mjr 113:7330439f2ffc 270 //
mjr 113:7330439f2ffc 271 // We use the square root of the count as our power law relation.
mjr 113:7330439f2ffc 272 // This was determined empirically (based on observation). This is
mjr 113:7330439f2ffc 273 // also the power law we'd expect from a naive application of physics,
mjr 113:7330439f2ffc 274 // on the principle that the observed brightness of a point light
mjr 113:7330439f2ffc 275 // source varies inversely with the square of the distance.
mjr 113:7330439f2ffc 276 //
mjr 113:7330439f2ffc 277 // The VCNL4010 data sheet doesn't specify a formulaic relationship,
mjr 113:7330439f2ffc 278 // which isn't surprising given that the relationship is undoubtedly
mjr 113:7330439f2ffc 279 // much more complex than just a power law equation, and also because
mjr 113:7330439f2ffc 280 // Vishay doesn't market this chip as a distance sensor in the first
mjr 113:7330439f2ffc 281 // place. It's a *proximity* sensor, which means it's only meant to
mjr 113:7330439f2ffc 282 // answer a yes/no question, "is an object within range?", and not
mjr 113:7330439f2ffc 283 // the quantitative question "how far?". So there's no reason for
mjr 113:7330439f2ffc 284 // Vishay to specify a precise relationship between distance and
mjr 113:7330439f2ffc 285 // brightness; all we have to know is that there's some kind of
mjr 113:7330439f2ffc 286 // inverse relationship, since beyond that, everything's just
mjr 113:7330439f2ffc 287 // relative. The data sheet does at least offer a (low-res) graph
mjr 113:7330439f2ffc 288 // of the distance-vs-proximity-count relationship under one set of
mjr 113:7330439f2ffc 289 // test conditions, and interestingly, that graph suggests a rather
mjr 113:7330439f2ffc 290 // different power law, more like ~1/distance^3.1. The graph also
mjr 113:7330439f2ffc 291 // makes it clear that the response isn't uniform - it doesn't
mjr 113:7330439f2ffc 292 // follow *any* power law exactly, but is something more complex
mjr 113:7330439f2ffc 293 // than that. This is another non-surprise, given that environmental
mjr 113:7330439f2ffc 294 // factors will inevitably confound the readings to some degree.
mjr 113:7330439f2ffc 295 //
mjr 113:7330439f2ffc 296 // At any rate, in the data I've gathered, it seems that a simple 1/R^2
mjr 113:7330439f2ffc 297 // power law is pretty close to reality, so I'm using that. (Brightness
mjr 113:7330439f2ffc 298 // varies with 1/R^2, so distance varies with 1/sqrt(brightness).) If
mjr 113:7330439f2ffc 299 // this turns out to produce noticeably non-linear results in other
mjr 113:7330439f2ffc 300 // people's installations, we might have to revisit this with something
mjr 113:7330439f2ffc 301 // more customized to the local setup. For example, we could gather
mjr 113:7330439f2ffc 302 // calibration data points across the whole plunger travel range and
mjr 113:7330439f2ffc 303 // then do a best-fit calculation to determine the best exponent
mjr 113:7330439f2ffc 304 // (which would still assume that there's *some* 1/R^x relationship
mjr 113:7330439f2ffc 305 // for some exponent x, but it wouldn't assume it's necessarily R^2.)
mjr 113:7330439f2ffc 306 //
mjr 113:7330439f2ffc 307 static inline float power(int x)
mjr 113:7330439f2ffc 308 {
mjr 113:7330439f2ffc 309 return sqrtf(static_cast<float>(x));
mjr 113:7330439f2ffc 310 }
mjr 113:7330439f2ffc 311
mjr 113:7330439f2ffc 312 // convert from a raw sensor count value to distance units, using our
mjr 113:7330439f2ffc 313 // current calibration data
mjr 113:7330439f2ffc 314 int VCNL4010::countToDistance(int count)
mjr 113:7330439f2ffc 315 {
mjr 113:7330439f2ffc 316 // remove the DC offset from teh signal
mjr 113:7330439f2ffc 317 count -= dcOffset;
mjr 113:7330439f2ffc 318
mjr 113:7330439f2ffc 319 // if the adjusted count (excess of DC offset) is zero or negative,
mjr 113:7330439f2ffc 320 // peg it to the minimum end = maximum retraction point
mjr 113:7330439f2ffc 321 if (count <= 0)
mjr 113:7330439f2ffc 322 return 65535;
mjr 113:7330439f2ffc 323
mjr 113:7330439f2ffc 324 // figure the distance based on our inverse power curve
mjr 113:7330439f2ffc 325 float d = scalingFactor/power(count) + scalingOffset;
mjr 113:7330439f2ffc 326
mjr 113:7330439f2ffc 327 // constrain it to the valid range and convert to int for return
mjr 113:7330439f2ffc 328 return d < 0.0f ? 0 : d > 65535.0f ? 65535 : static_cast<int>(d);
mjr 113:7330439f2ffc 329 }
mjr 113:7330439f2ffc 330
mjr 113:7330439f2ffc 331 // Calculate the scaling factors for our power-law formula for
mjr 113:7330439f2ffc 332 // converting proximity count (brightness) readings to distances.
mjr 113:7330439f2ffc 333 // We call this upon completing a new calibration pass, and during
mjr 113:7330439f2ffc 334 // initialization, when loading saved calibration data.
mjr 113:7330439f2ffc 335 void VCNL4010::calcScalingFactor()
mjr 113:7330439f2ffc 336 {
mjr 113:7330439f2ffc 337 // Don't let the minimum go below 100. The inverse relationship makes
mjr 113:7330439f2ffc 338 // the calculation meaningless at zero and unstable at very small
mjr 113:7330439f2ffc 339 // count values, so we need a reasonable floor to keep things in a
mjr 113:7330439f2ffc 340 // usable range. In practice, the minimum observed value will usually
mjr 113:7330439f2ffc 341 // be quite a lot higher (2000 to 20000 in my testing), which the
mjr 113:7330439f2ffc 342 // Vishay application note attributes to stray reflections from the
mjr 113:7330439f2ffc 343 // chip's mounting apparatus, ambient light, and noise within the
mjr 113:7330439f2ffc 344 // detector itself. But just in case, set a floor that will ensure
mjr 113:7330439f2ffc 345 // reasonable calculations.
mjr 113:7330439f2ffc 346 if (minProxCount < 100)
mjr 113:7330439f2ffc 347 minProxCount = 100;
mjr 113:7330439f2ffc 348
mjr 113:7330439f2ffc 349 // Set a ceiling of 65535, since the sensor can't go higher
mjr 113:7330439f2ffc 350 if (maxProxCount > 65535)
mjr 113:7330439f2ffc 351 maxProxCount = 65535;
mjr 113:7330439f2ffc 352
mjr 113:7330439f2ffc 353 // Figure the scaling factor and offset over the range from the park
mjr 113:7330439f2ffc 354 // position to the maximum retracted position, which corresponds to
mjr 113:7330439f2ffc 355 // the minimum count (lowest intensity reflection) we've observed.
mjr 113:7330439f2ffc 356 //
mjr 113:7330439f2ffc 357 // Do all calculations with the counts *after* subtracting out the
mjr 113:7330439f2ffc 358 // signal's DC offset, which is the brightness level registered on the
mjr 113:7330439f2ffc 359 // sensor when there's no reflective target in range. We can't directly
mjr 113:7330439f2ffc 360 // measure the DC offset in a plunger setup, since that would require
mjr 113:7330439f2ffc 361 // removing the plunger entirely, but we can guess that the minimum
mjr 113:7330439f2ffc 362 // reading observed during calibration is approximately equal to the
mjr 113:7330439f2ffc 363 // DC offset. The minimum brightness occurs when the plunger is at the
mjr 113:7330439f2ffc 364 // most distance point in its travel range from the sensor, which is
mjr 113:7330439f2ffc 365 // when it's pulled all the way back. The plunger travel distance is
mjr 113:7330439f2ffc 366 // just about at the limit of the VCNL4010's sensitivity, so the inverse
mjr 113:7330439f2ffc 367 // curve should be very nearly flat at this point, thus this is a very
mjr 113:7330439f2ffc 368 // close approximation of the true DC offset.
mjr 113:7330439f2ffc 369 const int dcOffsetDelta = 50;
mjr 113:7330439f2ffc 370 dcOffset = minProxCount > dcOffsetDelta ? minProxCount - dcOffsetDelta : 0;
mjr 113:7330439f2ffc 371 int park = parkProxCount - dcOffset;
mjr 113:7330439f2ffc 372 float parkInv = 1.0f/power(park);
mjr 113:7330439f2ffc 373 scalingFactor = 54612.5f / (1.0f/power(minProxCount - dcOffset) - parkInv);
mjr 113:7330439f2ffc 374 scalingOffset = 10922.5f - (scalingFactor * parkInv);
mjr 113:7330439f2ffc 375 }
mjr 113:7330439f2ffc 376
mjr 113:7330439f2ffc 377 // Read an I2C register on the device
mjr 111:42dc75fbe623 378 uint8_t VCNL4010::readReg(uint8_t registerAddr)
mjr 111:42dc75fbe623 379 {
mjr 111:42dc75fbe623 380 // write the request
mjr 111:42dc75fbe623 381 uint8_t data_write[1] = { registerAddr };
mjr 111:42dc75fbe623 382 if (i2c.write(I2C_ADDR, data_write, 1, false))
mjr 111:42dc75fbe623 383 return 0x00;
mjr 111:42dc75fbe623 384
mjr 111:42dc75fbe623 385 // read the result
mjr 111:42dc75fbe623 386 uint8_t data_read[1];
mjr 111:42dc75fbe623 387 if (i2c.read(I2C_ADDR, data_read, 1))
mjr 111:42dc75fbe623 388 return 0x00;
mjr 111:42dc75fbe623 389
mjr 111:42dc75fbe623 390 // return the result
mjr 111:42dc75fbe623 391 return data_read[0];
mjr 111:42dc75fbe623 392 }
mjr 113:7330439f2ffc 393
mjr 113:7330439f2ffc 394 // Write to an I2C register on the device
mjr 111:42dc75fbe623 395 void VCNL4010::writeReg(uint8_t registerAddr, uint8_t data)
mjr 111:42dc75fbe623 396 {
mjr 111:42dc75fbe623 397 // set up the write: register number, data byte
mjr 111:42dc75fbe623 398 uint8_t data_write[2] = { registerAddr, data };
mjr 111:42dc75fbe623 399 i2c.write(I2C_ADDR, data_write, 2);
mjr 111:42dc75fbe623 400 }