Mirror with some correction

Dependencies:   mbed FastIO FastPWM USBDevice

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

Who changed what in which revision?

UserRevisionLine numberNew contents of line
mjr 111:42dc75fbe623 1 // Vishay VCNL4010 Proximity Sensor interface
mjr 111:42dc75fbe623 2 //
mjr 111:42dc75fbe623 3 // OVERVIEW
mjr 111:42dc75fbe623 4 //
mjr 111:42dc75fbe623 5 // The Vishay VCNL4010 is an IR proximity sensor chip with an I2C interface
mjr 111:42dc75fbe623 6 // and an effective operating range of about 2mm to 100mm. The VirtuaPin
mjr 113:7330439f2ffc 7 // plunger kit v3 is based on this sensor, so it's in fairly widespread
mjr 113:7330439f2ffc 8 // use among pin cab builders.
mjr 111:42dc75fbe623 9 //
mjr 113:7330439f2ffc 10 // Like all proximity sensors, this chip is designed for sensing *proxmity*,
mjr 113:7330439f2ffc 11 // not distance. Proximity sensing is answering the yes/no question "is
mjr 113:7330439f2ffc 12 // there an object within my detection range?". However, many types of
mjr 113:7330439f2ffc 13 // proximity sensors, including this one, don't answer the proximity
mjr 113:7330439f2ffc 14 // question directly, but rather report an analog quantity that correlates
mjr 113:7330439f2ffc 15 // with distance to a detected object. For a proximity reading, we'd
mjr 113:7330439f2ffc 16 // compare that analog quantitiy to a threshold level to determine whether
mjr 113:7330439f2ffc 17 // or not an object is in range. But we can "abuse" the analog reading by
mjr 113:7330439f2ffc 18 // interpreting it as a continuous value instead of merely being on one side
mjr 113:7330439f2ffc 19 // or the other of a cutoff point. Since the analog value varies (in some
mjr 113:7330439f2ffc 20 // way) with the distance to the detected object, we can re-interpret the
mjr 113:7330439f2ffc 21 // reported analog quantity as a continuous distance value, as long as we
mjr 113:7330439f2ffc 22 // know the mathematical relationship between the distance to the target
mjr 113:7330439f2ffc 23 // and the sensor's reported reading.
mjr 111:42dc75fbe623 24 //
mjr 113:7330439f2ffc 25 // In the case of IR proximity sensors like this one, the analog quantity
mjr 113:7330439f2ffc 26 // that the sensor reports is the intensity of light reflected from the
mjr 113:7330439f2ffc 27 // target object. This type of sensor projects an IR light source in the
mjr 113:7330439f2ffc 28 // direction of the target, and measures the intensity of light reflected
mjr 114:c2410d2cfaf1 29 // from the target. At the basic physics level, the apparent brightness
mjr 114:c2410d2cfaf1 30 // of a point light source varies with the inverse of the square of the
mjr 114:c2410d2cfaf1 31 // distance between source and observer. Our setup isn't quite as simple
mjr 114:c2410d2cfaf1 32 // as that idealized model: we have a reflector instead of a point source,
mjr 114:c2410d2cfaf1 33 // so there are other factors that could vary by distance, especially the
mjr 114:c2410d2cfaf1 34 // cross-section of the target (the portion of the target within the spread
mjr 114:c2410d2cfaf1 35 // angle of the source light). These other factors might not even have
mjr 114:c2410d2cfaf1 36 // simple polynomial relationships to distance. Even so, the general idea
mjr 114:c2410d2cfaf1 37 // that the reflected brightness varies inversely with the distance should
mjr 114:c2410d2cfaf1 38 // hold, at least within a limited distance range. Assuming we can hold
mjr 114:c2410d2cfaf1 39 // all of the other quantities constant (brightness of the light source,
mjr 114:c2410d2cfaf1 40 // reflectivity of the target, etc), then, the reflected brightness should
mjr 114:c2410d2cfaf1 41 // serve as a proxy for the distance. It's obviously not possible to
mjr 113:7330439f2ffc 42 // compute an absolute distance (in millimeters from the sensor, say) from
mjr 114:c2410d2cfaf1 43 // the brightness reading alone, since that depends upon knowing the actual
mjr 114:c2410d2cfaf1 44 // values of all of the other quantities that assuming are held constant.
mjr 114:c2410d2cfaf1 45 // But we don't have to know those variables individually; we can roll them
mjr 114:c2410d2cfaf1 46 // into a proportionality constant that we can compute via calibration, by
mjr 114:c2410d2cfaf1 47 // taking brightness readings at known distances and then solving for the
mjr 114:c2410d2cfaf1 48 // constant.
mjr 114:c2410d2cfaf1 49 //
mjr 114:c2410d2cfaf1 50 // The VCNL4010 data sheet doesn't provide any specifications of how the
mjr 114:c2410d2cfaf1 51 // brightness reading relates to distance - it can't, for all of the reasons
mjr 114:c2410d2cfaf1 52 // mentioned above. But it at least provides a rough plot of readings taken
mjr 114:c2410d2cfaf1 53 // for a particular test configuration. That plot suggests that the power
mjr 114:c2410d2cfaf1 54 // law observed in the test configuration is roughly
mjr 114:c2410d2cfaf1 55 //
mjr 114:c2410d2cfaf1 56 // Brightness ~ 1/Distance^3.2
mjr 114:c2410d2cfaf1 57 //
mjr 114:c2410d2cfaf1 58 // over most of the range from 10mm to 100mm. In my own testing, the best
mjr 114:c2410d2cfaf1 59 // fit was more like 1/r^2. I suspect that the power law depends quite a
mjr 114:c2410d2cfaf1 60 // lot on the size and shape of the reflector. Vishay's test setup uses a
mjr 114:c2410d2cfaf1 61 // 3cm x 3cm square reflector, whereas my plunger test rig has about a 2.5cm
mjr 114:c2410d2cfaf1 62 // circular reflector, which is about as big as you can make the reflector
mjr 114:c2410d2cfaf1 63 // for a pin cab plunger without conflicting with the flipper switches. I
mjr 114:c2410d2cfaf1 64 // don't know if the difference in observed power law is due to the
mjr 114:c2410d2cfaf1 65 // reflector geometry or other factors. We might need to revisit the
mjr 114:c2410d2cfaf1 66 // formula I used for the distance conversion as we gain experience from
mjr 114:c2410d2cfaf1 67 // different users setting up the sensor. A possible future enhancement
mjr 114:c2410d2cfaf1 68 // would be to do a more detailed calibration as follows:
mjr 114:c2410d2cfaf1 69 //
mjr 114:c2410d2cfaf1 70 // - Ask the user to pull back the plunger slowly at a very steady rate,
mjr 114:c2410d2cfaf1 71 // maybe 3-5 seconds per pull
mjr 114:c2410d2cfaf1 72 //
mjr 114:c2410d2cfaf1 73 // - Collect frequent readings throughout this period, say every 50ms
mjr 114:c2410d2cfaf1 74 // (so around 60-100 readings per pull)
mjr 114:c2410d2cfaf1 75 //
mjr 114:c2410d2cfaf1 76 // - Do a best-fit calculation on the data to solve for the exponent X
mjr 114:c2410d2cfaf1 77 // and proportionality constant C in (Brightness = C/Distance^X),
mjr 114:c2410d2cfaf1 78 // assuming that the distances are uniformly distributed over the
mjr 114:c2410d2cfaf1 79 // pull range (because the user was pulling at constant speed).
mjr 114:c2410d2cfaf1 80 //
mjr 114:c2410d2cfaf1 81 // - Save the exponent as config.plunger.cal.raw1 (perhaps as a 4.4 bit
mjr 114:c2410d2cfaf1 82 // fixed-point value, such that X = raw1/16.0f)
mjr 114:c2410d2cfaf1 83 //
mjr 114:c2410d2cfaf1 84 // Alternatively, we could let the user provide the power law exponent
mjr 114:c2410d2cfaf1 85 // manually, as a configuration parameter, and add a Config Tool command
mjr 114:c2410d2cfaf1 86 // to collect the same calibration data described above and do the best-fit
mjr 114:c2410d2cfaf1 87 // analysis. It might be preferable to do it that way - the user could
mjr 114:c2410d2cfaf1 88 // experiment with different values manually to find one that provides the
mjr 114:c2410d2cfaf1 89 // best subjective feel, and they could use the analysis tool to suggest
mjr 114:c2410d2cfaf1 90 // the best value based on data collection. The reason I like the manual
mjr 114:c2410d2cfaf1 91 // approach is that the actual distance/brightness relationship isn't as
mjr 114:c2410d2cfaf1 92 // uniform as a simple power law, so even the best-fit power law will be
mjr 114:c2410d2cfaf1 93 // imperfect. What looks best subjectively might not match the mathematical
mjr 114:c2410d2cfaf1 94 // best fit, because divergence from the fit might be more noticeable to
mjr 114:c2410d2cfaf1 95 // the eye in some regions than in others. A manual fit would allow the
mjr 114:c2410d2cfaf1 96 // user to tweak it until it looked best in the whatever region they find
mjr 114:c2410d2cfaf1 97 // most noticeable.
mjr 111:42dc75fbe623 98 //
mjr 111:42dc75fbe623 99 //
mjr 111:42dc75fbe623 100 // SENSOR INTIALIZATION
mjr 111:42dc75fbe623 101 //
mjr 111:42dc75fbe623 102 // Initializing the VCNL4010 from the software side is just a matter of
mjr 111:42dc75fbe623 103 // programming the registers that control sample rate and sample collection
mjr 111:42dc75fbe623 104 // policy. From experience with other plunger sensors, we know that good
mjr 113:7330439f2ffc 105 // plunger motion tracking without aliasing requires samples at very short
mjr 113:7330439f2ffc 106 // intervals - ideally 2.5ms or less The VCNL4010's fastest sampling rate
mjr 113:7330439f2ffc 107 // for proximity is 250 samples/second, or 4ms intervals, so it's not quite
mjr 113:7330439f2ffc 108 // as fast as we'd like. But it's still usable. In addition, we'll use the
mjr 113:7330439f2ffc 109 // "on demand" mode to collect readings (rather than its interrupt mode),
mjr 113:7330439f2ffc 110 // since the upper software layers poll the sensor by design.
mjr 111:42dc75fbe623 111 //
mjr 111:42dc75fbe623 112 //
mjr 111:42dc75fbe623 113 // I2C INFORMATION
mjr 111:42dc75fbe623 114 //
mjr 113:7330439f2ffc 115 // This chip has an I2C interface with an immutable I2C address of 0010 011x.
mjr 113:7330439f2ffc 116 // In 8-bit address terms, this is 0x26 write, 0x27 read; or, if you prefer
mjr 113:7330439f2ffc 117 // the 7-bit notation, it's address 0x13.
mjr 111:42dc75fbe623 118 //
mjr 111:42dc75fbe623 119
mjr 111:42dc75fbe623 120 #ifndef _VCNL4010_H_
mjr 111:42dc75fbe623 121 #define _VCNL4010_H_
mjr 111:42dc75fbe623 122
mjr 111:42dc75fbe623 123 #include "mbed.h"
mjr 111:42dc75fbe623 124 #include "BitBangI2C.h"
mjr 113:7330439f2ffc 125 #include "config.h"
mjr 111:42dc75fbe623 126
mjr 111:42dc75fbe623 127 class VCNL4010
mjr 111:42dc75fbe623 128 {
mjr 111:42dc75fbe623 129 public:
mjr 111:42dc75fbe623 130 // Set up the interface with the given I2C pins.
mjr 111:42dc75fbe623 131 //
mjr 111:42dc75fbe623 132 // If 'internalPullups' is true, we'll set the I2C SDA/SCL pins to
mjr 111:42dc75fbe623 133 // enable the internal pullup resistors. Set this to false if you're
mjr 111:42dc75fbe623 134 // using your own external pullup resistors on the lines. External
mjr 111:42dc75fbe623 135 // pullups are better if you're attaching more than one device to the
mjr 111:42dc75fbe623 136 // same physical I2C bus; the internal pullups are fine if there's only
mjr 111:42dc75fbe623 137 // one I2C device (in this case the VCNL4010) connected to these pins.
mjr 113:7330439f2ffc 138 VCNL4010(PinName sda, PinName scl, bool internalPullups, int iredCurrent);
mjr 111:42dc75fbe623 139
mjr 111:42dc75fbe623 140 // initialize the chip
mjr 111:42dc75fbe623 141 void init();
mjr 111:42dc75fbe623 142
mjr 111:42dc75fbe623 143 // Start a distance reading, returning immediately without waiting
mjr 111:42dc75fbe623 144 // for the reading to finish. The caller can poll for the finished
mjr 113:7330439f2ffc 145 // reading via proxReady().
mjr 111:42dc75fbe623 146 void startProxReading();
mjr 111:42dc75fbe623 147
mjr 111:42dc75fbe623 148 // Is a proximity reading ready?
mjr 111:42dc75fbe623 149 bool proxReady();
mjr 113:7330439f2ffc 150
mjr 113:7330439f2ffc 151 // Read the proximity value. Note that this returns the "brightness"
mjr 113:7330439f2ffc 152 // value from the sensor, not a distance reading. This must be converted
mjr 113:7330439f2ffc 153 // into a distance reading via countToDistance().
mjr 113:7330439f2ffc 154 int getProx(int &proxCount, uint32_t &tMid, uint32_t &dt, uint32_t timeout_us);
mjr 111:42dc75fbe623 155
mjr 113:7330439f2ffc 156 // convert from raw sensor count values to distance units
mjr 113:7330439f2ffc 157 int countToDistance(int count);
mjr 113:7330439f2ffc 158
mjr 111:42dc75fbe623 159 // This chip has a fixed I2C address of 0x26 write, 0x27 read
mjr 111:42dc75fbe623 160 static const uint8_t I2C_ADDR = 0x26;
mjr 113:7330439f2ffc 161
mjr 113:7330439f2ffc 162 // Restore the saved calibration data from the configuration
mjr 113:7330439f2ffc 163 virtual void restoreCalibration(Config &config);
mjr 113:7330439f2ffc 164
mjr 113:7330439f2ffc 165 // Begin calibration
mjr 113:7330439f2ffc 166 virtual void beginCalibration();
mjr 111:42dc75fbe623 167
mjr 113:7330439f2ffc 168 // End calibration
mjr 113:7330439f2ffc 169 virtual void endCalibration(Config &config);
mjr 113:7330439f2ffc 170
mjr 111:42dc75fbe623 171 protected:
mjr 111:42dc75fbe623 172 // I2C read/write
mjr 111:42dc75fbe623 173 uint8_t readReg(uint8_t regAddr);
mjr 111:42dc75fbe623 174 void writeReg(uint8_t regAddr, uint8_t data);
mjr 111:42dc75fbe623 175
mjr 111:42dc75fbe623 176 // I2C interface to device
mjr 111:42dc75fbe623 177 BitBangI2C i2c;
mjr 111:42dc75fbe623 178
mjr 113:7330439f2ffc 179 // IR LED current setting (from configuration)
mjr 113:7330439f2ffc 180 int iredCurrent;
mjr 113:7330439f2ffc 181
mjr 111:42dc75fbe623 182 // sample timer
mjr 111:42dc75fbe623 183 Timer sampleTimer;
mjr 111:42dc75fbe623 184
mjr 111:42dc75fbe623 185 // time (from Timer t) of start of last range sample
mjr 111:42dc75fbe623 186 uint32_t tSampleStart;
mjr 113:7330439f2ffc 187
mjr 113:7330439f2ffc 188 // last raw proximity reading
mjr 113:7330439f2ffc 189 uint16_t lastProxCount;
mjr 113:7330439f2ffc 190
mjr 113:7330439f2ffc 191 // flag: calibration is in progress
mjr 113:7330439f2ffc 192 bool calibrating;
mjr 113:7330439f2ffc 193
mjr 113:7330439f2ffc 194 // minimum and maximum observed proximity counts during calibration
mjr 113:7330439f2ffc 195 uint16_t minProxCount;
mjr 113:7330439f2ffc 196 uint16_t maxProxCount;
mjr 113:7330439f2ffc 197
mjr 113:7330439f2ffc 198 // proximity count observed at "park" position during calibration
mjr 113:7330439f2ffc 199 uint16_t parkProxCount;
mjr 113:7330439f2ffc 200
mjr 113:7330439f2ffc 201 // Calculate the scaling factor for count -> distance conversions.
mjr 113:7330439f2ffc 202 // This uses the data collected during calibration to figure the
mjr 113:7330439f2ffc 203 // conversion factors.
mjr 113:7330439f2ffc 204 void calcScalingFactor();
mjr 113:7330439f2ffc 205
mjr 113:7330439f2ffc 206 // DC Offset for converting from count to distance. Per the Vishay
mjr 113:7330439f2ffc 207 // application notes, the sensor brightness signal contains a fixed
mjr 113:7330439f2ffc 208 // component that comes from a combination of physical factors such
mjr 113:7330439f2ffc 209 // as internal reflections, ambient light, ADC artifacts, and sensor
mjr 113:7330439f2ffc 210 // noise. This must be subtracted from the reported proximity count
mjr 113:7330439f2ffc 211 // to get a measure of the actual reflected brightness level. The
mjr 113:7330439f2ffc 212 // DC offset is a function of the overall setup, so it has to be
mjr 113:7330439f2ffc 213 // determined through calibration.
mjr 113:7330439f2ffc 214 int dcOffset;
mjr 113:7330439f2ffc 215
mjr 113:7330439f2ffc 216 // Scaling factor and offset for converting from count to distance.
mjr 113:7330439f2ffc 217 // We calculate these based on the counts collected at known points
mjr 113:7330439f2ffc 218 // during calibration.
mjr 113:7330439f2ffc 219 float scalingFactor;
mjr 113:7330439f2ffc 220 float scalingOffset;
mjr 111:42dc75fbe623 221 };
mjr 111:42dc75fbe623 222
mjr 111:42dc75fbe623 223 #endif // _VCNL4010_H_