An I/O controller for virtual pinball machines: accelerometer nudge sensing, analog plunger input, button input encoding, LedWiz compatible output controls, and more.
Dependencies: mbed FastIO FastPWM USBDevice
Fork of Pinscape_Controller by
VCNL4010.h
00001 // Vishay VCNL4010 Proximity Sensor interface 00002 // 00003 // OVERVIEW 00004 // 00005 // The Vishay VCNL4010 is an IR proximity sensor chip with an I2C interface 00006 // and an effective operating range of about 2mm to 100mm. The VirtuaPin 00007 // plunger kit v3 is based on this sensor, so it's in fairly widespread 00008 // use among pin cab builders. 00009 // 00010 // Like all proximity sensors, this chip is designed for sensing *proxmity*, 00011 // not distance. Proximity sensing is answering the yes/no question "is 00012 // there an object within my detection range?". However, many types of 00013 // proximity sensors, including this one, don't answer the proximity 00014 // question directly, but rather report an analog quantity that correlates 00015 // with distance to a detected object. For a proximity reading, we'd 00016 // compare that analog quantitiy to a threshold level to determine whether 00017 // or not an object is in range. But we can "abuse" the analog reading by 00018 // interpreting it as a continuous value instead of merely being on one side 00019 // or the other of a cutoff point. Since the analog value varies (in some 00020 // way) with the distance to the detected object, we can re-interpret the 00021 // reported analog quantity as a continuous distance value, as long as we 00022 // know the mathematical relationship between the distance to the target 00023 // and the sensor's reported reading. 00024 // 00025 // In the case of IR proximity sensors like this one, the analog quantity 00026 // that the sensor reports is the intensity of light reflected from the 00027 // target object. This type of sensor projects an IR light source in the 00028 // direction of the target, and measures the intensity of light reflected 00029 // from the target. At the basic physics level, the apparent brightness 00030 // of a point light source varies with the inverse of the square of the 00031 // distance between source and observer. Our setup isn't quite as simple 00032 // as that idealized model: we have a reflector instead of a point source, 00033 // so there are other factors that could vary by distance, especially the 00034 // cross-section of the target (the portion of the target within the spread 00035 // angle of the source light). These other factors might not even have 00036 // simple polynomial relationships to distance. Even so, the general idea 00037 // that the reflected brightness varies inversely with the distance should 00038 // hold, at least within a limited distance range. Assuming we can hold 00039 // all of the other quantities constant (brightness of the light source, 00040 // reflectivity of the target, etc), then, the reflected brightness should 00041 // serve as a proxy for the distance. It's obviously not possible to 00042 // compute an absolute distance (in millimeters from the sensor, say) from 00043 // the brightness reading alone, since that depends upon knowing the actual 00044 // values of all of the other quantities that assuming are held constant. 00045 // But we don't have to know those variables individually; we can roll them 00046 // into a proportionality constant that we can compute via calibration, by 00047 // taking brightness readings at known distances and then solving for the 00048 // constant. 00049 // 00050 // The VCNL4010 data sheet doesn't provide any specifications of how the 00051 // brightness reading relates to distance - it can't, for all of the reasons 00052 // mentioned above. But it at least provides a rough plot of readings taken 00053 // for a particular test configuration. That plot suggests that the power 00054 // law observed in the test configuration is roughly 00055 // 00056 // Brightness ~ 1/Distance^3.2 00057 // 00058 // over most of the range from 10mm to 100mm. In my own testing, the best 00059 // fit was more like 1/r^2. I suspect that the power law depends quite a 00060 // lot on the size and shape of the reflector. Vishay's test setup uses a 00061 // 3cm x 3cm square reflector, whereas my plunger test rig has about a 2.5cm 00062 // circular reflector, which is about as big as you can make the reflector 00063 // for a pin cab plunger without conflicting with the flipper switches. I 00064 // don't know if the difference in observed power law is due to the 00065 // reflector geometry or other factors. We might need to revisit the 00066 // formula I used for the distance conversion as we gain experience from 00067 // different users setting up the sensor. A possible future enhancement 00068 // would be to do a more detailed calibration as follows: 00069 // 00070 // - Ask the user to pull back the plunger slowly at a very steady rate, 00071 // maybe 3-5 seconds per pull 00072 // 00073 // - Collect frequent readings throughout this period, say every 50ms 00074 // (so around 60-100 readings per pull) 00075 // 00076 // - Do a best-fit calculation on the data to solve for the exponent X 00077 // and proportionality constant C in (Brightness = C/Distance^X), 00078 // assuming that the distances are uniformly distributed over the 00079 // pull range (because the user was pulling at constant speed). 00080 // 00081 // - Save the exponent as config.plunger.cal.raw1 (perhaps as a 4.4 bit 00082 // fixed-point value, such that X = raw1/16.0f) 00083 // 00084 // Alternatively, we could let the user provide the power law exponent 00085 // manually, as a configuration parameter, and add a Config Tool command 00086 // to collect the same calibration data described above and do the best-fit 00087 // analysis. It might be preferable to do it that way - the user could 00088 // experiment with different values manually to find one that provides the 00089 // best subjective feel, and they could use the analysis tool to suggest 00090 // the best value based on data collection. The reason I like the manual 00091 // approach is that the actual distance/brightness relationship isn't as 00092 // uniform as a simple power law, so even the best-fit power law will be 00093 // imperfect. What looks best subjectively might not match the mathematical 00094 // best fit, because divergence from the fit might be more noticeable to 00095 // the eye in some regions than in others. A manual fit would allow the 00096 // user to tweak it until it looked best in the whatever region they find 00097 // most noticeable. 00098 // 00099 // 00100 // SENSOR INTIALIZATION 00101 // 00102 // Initializing the VCNL4010 from the software side is just a matter of 00103 // programming the registers that control sample rate and sample collection 00104 // policy. From experience with other plunger sensors, we know that good 00105 // plunger motion tracking without aliasing requires samples at very short 00106 // intervals - ideally 2.5ms or less The VCNL4010's fastest sampling rate 00107 // for proximity is 250 samples/second, or 4ms intervals, so it's not quite 00108 // as fast as we'd like. But it's still usable. In addition, we'll use the 00109 // "on demand" mode to collect readings (rather than its interrupt mode), 00110 // since the upper software layers poll the sensor by design. 00111 // 00112 // 00113 // I2C INFORMATION 00114 // 00115 // This chip has an I2C interface with an immutable I2C address of 0010 011x. 00116 // In 8-bit address terms, this is 0x26 write, 0x27 read; or, if you prefer 00117 // the 7-bit notation, it's address 0x13. 00118 // 00119 00120 #ifndef _VCNL4010_H_ 00121 #define _VCNL4010_H_ 00122 00123 #include "mbed.h" 00124 #include "BitBangI2C.h" 00125 #include "config.h" 00126 00127 class VCNL4010 00128 { 00129 public: 00130 // Set up the interface with the given I2C pins. 00131 // 00132 // If 'internalPullups' is true, we'll set the I2C SDA/SCL pins to 00133 // enable the internal pullup resistors. Set this to false if you're 00134 // using your own external pullup resistors on the lines. External 00135 // pullups are better if you're attaching more than one device to the 00136 // same physical I2C bus; the internal pullups are fine if there's only 00137 // one I2C device (in this case the VCNL4010) connected to these pins. 00138 VCNL4010(PinName sda, PinName scl, bool internalPullups, int iredCurrent); 00139 00140 // initialize the chip 00141 void init(); 00142 00143 // Start a distance reading, returning immediately without waiting 00144 // for the reading to finish. The caller can poll for the finished 00145 // reading via proxReady(). 00146 void startProxReading(); 00147 00148 // Is a proximity reading ready? 00149 bool proxReady(); 00150 00151 // Read the proximity value. Note that this returns the "brightness" 00152 // value from the sensor, not a distance reading. This must be converted 00153 // into a distance reading via countToDistance(). 00154 int getProx(int &proxCount, uint32_t &tMid, uint32_t &dt, uint32_t timeout_us); 00155 00156 // convert from raw sensor count values to distance units 00157 int countToDistance(int count); 00158 00159 // This chip has a fixed I2C address of 0x26 write, 0x27 read 00160 static const uint8_t I2C_ADDR = 0x26; 00161 00162 // Restore the saved calibration data from the configuration 00163 virtual void restoreCalibration(Config &config); 00164 00165 // Begin calibration 00166 virtual void beginCalibration(); 00167 00168 // End calibration 00169 virtual void endCalibration(Config &config); 00170 00171 protected: 00172 // I2C read/write 00173 uint8_t readReg(uint8_t regAddr); 00174 void writeReg(uint8_t regAddr, uint8_t data); 00175 00176 // I2C interface to device 00177 BitBangI2C i2c; 00178 00179 // IR LED current setting (from configuration) 00180 int iredCurrent; 00181 00182 // sample timer 00183 Timer sampleTimer; 00184 00185 // time (from Timer t) of start of last range sample 00186 uint32_t tSampleStart; 00187 00188 // last raw proximity reading 00189 uint16_t lastProxCount; 00190 00191 // flag: calibration is in progress 00192 bool calibrating; 00193 00194 // minimum and maximum observed proximity counts during calibration 00195 uint16_t minProxCount; 00196 uint16_t maxProxCount; 00197 00198 // proximity count observed at "park" position during calibration 00199 uint16_t parkProxCount; 00200 00201 // Calculate the scaling factor for count -> distance conversions. 00202 // This uses the data collected during calibration to figure the 00203 // conversion factors. 00204 void calcScalingFactor(); 00205 00206 // DC Offset for converting from count to distance. Per the Vishay 00207 // application notes, the sensor brightness signal contains a fixed 00208 // component that comes from a combination of physical factors such 00209 // as internal reflections, ambient light, ADC artifacts, and sensor 00210 // noise. This must be subtracted from the reported proximity count 00211 // to get a measure of the actual reflected brightness level. The 00212 // DC offset is a function of the overall setup, so it has to be 00213 // determined through calibration. 00214 int dcOffset; 00215 00216 // Scaling factor and offset for converting from count to distance. 00217 // We calculate these based on the counts collected at known points 00218 // during calibration. 00219 float scalingFactor; 00220 float scalingOffset; 00221 }; 00222 00223 #endif // _VCNL4010_H_
Generated on Wed Jul 13 2022 03:30:11 by 1.7.2