Mirror with some correction
Dependencies: mbed FastIO FastPWM USBDevice
VCNL4010/VCNL4010.cpp@111:42dc75fbe623, 2021-02-22 (annotated)
- Committer:
- mjr
- Date:
- Mon Feb 22 06:57:59 2021 +0000
- Revision:
- 111:42dc75fbe623
- Child:
- 113:7330439f2ffc
Add initial support for VCNL4010 IR distance sensor - experimental and untested
Who changed what in which revision?
User | Revision | Line number | New 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 | 111:42dc75fbe623 | 7 | VCNL4010::VCNL4010(PinName sda, PinName scl, bool internalPullups) |
mjr | 111:42dc75fbe623 | 8 | : i2c(sda, scl, internalPullups) |
mjr | 111:42dc75fbe623 | 9 | { |
mjr | 111:42dc75fbe623 | 10 | } |
mjr | 111:42dc75fbe623 | 11 | |
mjr | 111:42dc75fbe623 | 12 | void VCNL4010::init() |
mjr | 111:42dc75fbe623 | 13 | { |
mjr | 111:42dc75fbe623 | 14 | printf("VCNL4010 initializing\r\n"); |
mjr | 111:42dc75fbe623 | 15 | |
mjr | 111:42dc75fbe623 | 16 | // reset the I2C bus |
mjr | 111:42dc75fbe623 | 17 | i2c.reset(); |
mjr | 111:42dc75fbe623 | 18 | |
mjr | 111:42dc75fbe623 | 19 | // Set the proximity sampling rate to the fastest available rate of |
mjr | 111:42dc75fbe623 | 20 | // 250 samples/second (4ms/sample). This isn't really fast enough for |
mjr | 111:42dc75fbe623 | 21 | // good plunger motion tracking - a minimum sampling frequency of 400/s |
mjr | 111:42dc75fbe623 | 22 | // is needed to avoid aliasing during the bounce-back phase of release |
mjr | 111:42dc75fbe623 | 23 | // motions - but it's as fast as this device can go. |
mjr | 111:42dc75fbe623 | 24 | writeReg(0x82, 0x07); |
mjr | 111:42dc75fbe623 | 25 | |
mjr | 111:42dc75fbe623 | 26 | // Set the current for the IR LED (the light source for proximity |
mjr | 111:42dc75fbe623 | 27 | // measurements). From the data sheet, it appears that higher current |
mjr | 111:42dc75fbe623 | 28 | // settings yield slightly more linear response curves, but with |
mjr | 111:42dc75fbe623 | 29 | // diminishing returns above 100mA. Assuming that the installation |
mjr | 111:42dc75fbe623 | 30 | // will be powering the sensor from the KL25Z 3.3V regulator, we'd |
mjr | 111:42dc75fbe623 | 31 | // like to keep the current as small as possible, though, to avoid |
mjr | 111:42dc75fbe623 | 32 | // putting too much load on the regulator (since it has to provide |
mjr | 111:42dc75fbe623 | 33 | // power to teh KL25Z itself as well). I'm going to try 50mA as a |
mjr | 111:42dc75fbe623 | 34 | // compromise. It might be worth experimenting with different values |
mjr | 111:42dc75fbe623 | 35 | // to see if they make a different to signal quality. |
mjr | 111:42dc75fbe623 | 36 | // |
mjr | 111:42dc75fbe623 | 37 | // The LED current in milliamps is 10mA times the numeric value we |
mjr | 111:42dc75fbe623 | 38 | // set in the register, up to a maximum of 20 for 200mA. |
mjr | 111:42dc75fbe623 | 39 | writeReg(0x83, 5); |
mjr | 111:42dc75fbe623 | 40 | |
mjr | 111:42dc75fbe623 | 41 | // disable self-timed measurements - we'll start measurements on demand |
mjr | 111:42dc75fbe623 | 42 | writeReg(0x80, 0x00); |
mjr | 111:42dc75fbe623 | 43 | |
mjr | 111:42dc75fbe623 | 44 | // start the sample timer, which we use to gather timing statistics |
mjr | 111:42dc75fbe623 | 45 | sampleTimer.start(); |
mjr | 111:42dc75fbe623 | 46 | |
mjr | 111:42dc75fbe623 | 47 | printf("VCNL4010 initialization done\r\n"); |
mjr | 111:42dc75fbe623 | 48 | } |
mjr | 111:42dc75fbe623 | 49 | |
mjr | 111:42dc75fbe623 | 50 | // Start a proximity measurement |
mjr | 111:42dc75fbe623 | 51 | void VCNL4010::startProxReading() |
mjr | 111:42dc75fbe623 | 52 | { |
mjr | 111:42dc75fbe623 | 53 | // set the prox_od (initiate proximity on demand) bit (0x08) in |
mjr | 111:42dc75fbe623 | 54 | // the command register, if it's not already set |
mjr | 111:42dc75fbe623 | 55 | uint8_t b = readReg(0x80); |
mjr | 111:42dc75fbe623 | 56 | if ((b & 0x08) == 0) |
mjr | 111:42dc75fbe623 | 57 | { |
mjr | 111:42dc75fbe623 | 58 | tSampleStart = sampleTimer.read_us(); |
mjr | 111:42dc75fbe623 | 59 | writeReg(0x80, b | 0x08); |
mjr | 111:42dc75fbe623 | 60 | } |
mjr | 111:42dc75fbe623 | 61 | } |
mjr | 111:42dc75fbe623 | 62 | |
mjr | 111:42dc75fbe623 | 63 | bool VCNL4010::proxReady() |
mjr | 111:42dc75fbe623 | 64 | { |
mjr | 111:42dc75fbe623 | 65 | // read the command register to get the status bits |
mjr | 111:42dc75fbe623 | 66 | uint8_t b = readReg(0x80); |
mjr | 111:42dc75fbe623 | 67 | |
mjr | 111:42dc75fbe623 | 68 | // if the prox_data_rdy bit (0x20) is set, a reading is ready |
mjr | 111:42dc75fbe623 | 69 | if ((b & 0x20) != 0) |
mjr | 111:42dc75fbe623 | 70 | return true; |
mjr | 111:42dc75fbe623 | 71 | |
mjr | 111:42dc75fbe623 | 72 | // Not ready. Since the caller is polling, they must expect a reading |
mjr | 111:42dc75fbe623 | 73 | // to be in progress; if not, start one now. A reading in progress is |
mjr | 111:42dc75fbe623 | 74 | // indicated and initiated by the prox_od bit |
mjr | 111:42dc75fbe623 | 75 | if ((b & 0x08) == 0) |
mjr | 111:42dc75fbe623 | 76 | { |
mjr | 111:42dc75fbe623 | 77 | tSampleStart = sampleTimer.read_us(); |
mjr | 111:42dc75fbe623 | 78 | writeReg(0x80, b | 0x08); |
mjr | 111:42dc75fbe623 | 79 | } |
mjr | 111:42dc75fbe623 | 80 | |
mjr | 111:42dc75fbe623 | 81 | // a reading is available if the prox_data_rdy (0x08) is set |
mjr | 111:42dc75fbe623 | 82 | return (b & 0x20) != 0; |
mjr | 111:42dc75fbe623 | 83 | } |
mjr | 111:42dc75fbe623 | 84 | |
mjr | 111:42dc75fbe623 | 85 | int VCNL4010::getProx(uint8_t &distance, uint32_t &tMid, uint32_t &dt, uint32_t timeout_us) |
mjr | 111:42dc75fbe623 | 86 | { |
mjr | 111:42dc75fbe623 | 87 | // wait for the sample |
mjr | 111:42dc75fbe623 | 88 | Timer t; |
mjr | 111:42dc75fbe623 | 89 | t.start(); |
mjr | 111:42dc75fbe623 | 90 | for (;;) |
mjr | 111:42dc75fbe623 | 91 | { |
mjr | 111:42dc75fbe623 | 92 | // check for a sample |
mjr | 111:42dc75fbe623 | 93 | if (proxReady()) |
mjr | 111:42dc75fbe623 | 94 | break; |
mjr | 111:42dc75fbe623 | 95 | |
mjr | 111:42dc75fbe623 | 96 | // if we've exceeded the timeout, return failure |
mjr | 111:42dc75fbe623 | 97 | if (t.read_us() > timeout_us) |
mjr | 111:42dc75fbe623 | 98 | return -1; |
mjr | 111:42dc75fbe623 | 99 | } |
mjr | 111:42dc75fbe623 | 100 | |
mjr | 111:42dc75fbe623 | 101 | // figure the time since we initiated the reading |
mjr | 111:42dc75fbe623 | 102 | dt = sampleTimer.read_us() - tSampleStart; |
mjr | 111:42dc75fbe623 | 103 | |
mjr | 111:42dc75fbe623 | 104 | // figure the midpoint time |
mjr | 111:42dc75fbe623 | 105 | tMid = tSampleStart + dt/2; |
mjr | 111:42dc75fbe623 | 106 | |
mjr | 111:42dc75fbe623 | 107 | // read the result from the sensor, as a 16-bit proximity count value |
mjr | 111:42dc75fbe623 | 108 | int N = (readReg(0x87) << 8) | readReg(0x88); |
mjr | 111:42dc75fbe623 | 109 | |
mjr | 111:42dc75fbe623 | 110 | // start a new reading, so that the sensor is collecting the next |
mjr | 111:42dc75fbe623 | 111 | // reading concurrently with the time-consuming floating-point math |
mjr | 111:42dc75fbe623 | 112 | // we're about to do |
mjr | 111:42dc75fbe623 | 113 | startProxReading(); |
mjr | 111:42dc75fbe623 | 114 | |
mjr | 111:42dc75fbe623 | 115 | // Figure the distance in abstract units. The raw count data from the |
mjr | 111:42dc75fbe623 | 116 | // sensor is proportional to the intensity of the reflected light from |
mjr | 111:42dc75fbe623 | 117 | // the target, which is proportional to the inverse of the square of |
mjr | 111:42dc75fbe623 | 118 | // the distance. So the distance is proportional to the inverse of |
mjr | 111:42dc75fbe623 | 119 | // the square root of the count. The proportionality factor is chosen |
mjr | 111:42dc75fbe623 | 120 | // to normalize the result to a range of 0..65535. |
mjr | 111:42dc75fbe623 | 121 | distance = static_cast<int>(146540.0f / sqrtf(static_cast<float>(N))); |
mjr | 111:42dc75fbe623 | 122 | |
mjr | 111:42dc75fbe623 | 123 | // success |
mjr | 111:42dc75fbe623 | 124 | return 0; |
mjr | 111:42dc75fbe623 | 125 | } |
mjr | 111:42dc75fbe623 | 126 | |
mjr | 111:42dc75fbe623 | 127 | uint8_t VCNL4010::readReg(uint8_t registerAddr) |
mjr | 111:42dc75fbe623 | 128 | { |
mjr | 111:42dc75fbe623 | 129 | // write the request |
mjr | 111:42dc75fbe623 | 130 | uint8_t data_write[1] = { registerAddr }; |
mjr | 111:42dc75fbe623 | 131 | if (i2c.write(I2C_ADDR, data_write, 1, false)) |
mjr | 111:42dc75fbe623 | 132 | return 0x00; |
mjr | 111:42dc75fbe623 | 133 | |
mjr | 111:42dc75fbe623 | 134 | // read the result |
mjr | 111:42dc75fbe623 | 135 | uint8_t data_read[1]; |
mjr | 111:42dc75fbe623 | 136 | if (i2c.read(I2C_ADDR, data_read, 1)) |
mjr | 111:42dc75fbe623 | 137 | return 0x00; |
mjr | 111:42dc75fbe623 | 138 | |
mjr | 111:42dc75fbe623 | 139 | // return the result |
mjr | 111:42dc75fbe623 | 140 | return data_read[0]; |
mjr | 111:42dc75fbe623 | 141 | } |
mjr | 111:42dc75fbe623 | 142 | |
mjr | 111:42dc75fbe623 | 143 | void VCNL4010::writeReg(uint8_t registerAddr, uint8_t data) |
mjr | 111:42dc75fbe623 | 144 | { |
mjr | 111:42dc75fbe623 | 145 | // set up the write: register number, data byte |
mjr | 111:42dc75fbe623 | 146 | uint8_t data_write[2] = { registerAddr, data }; |
mjr | 111:42dc75fbe623 | 147 | i2c.write(I2C_ADDR, data_write, 2); |
mjr | 111:42dc75fbe623 | 148 | } |