Mike R / Mbed 2 deprecated Pinscape_Controller

Dependencies:   USBDevice mbed FastAnalogIn FastIO FastPWM SimpleDMA

Committer:
mjr
Date:
Sat Feb 06 20:21:48 2016 +0000
Revision:
43:7a6364d82a41
Parent:
40:cc0d9814522b
Child:
44:b5ac89b9cd5d
Before floating point plunger ranging

Who changed what in which revision?

UserRevisionLine numberNew contents of line
mjr 17:ab3cec0c8bf4 1 // CCD plunger sensor
mjr 17:ab3cec0c8bf4 2 //
mjr 35:e959ffba78fd 3 // This class implements our generic plunger sensor interface for the
mjr 35:e959ffba78fd 4 // TAOS TSL1410R and TSL1412R linear sensor arrays. Physically, these
mjr 35:e959ffba78fd 5 // sensors are installed with their image window running parallel to
mjr 35:e959ffba78fd 6 // the plunger rod, spanning the travel range of the plunger tip.
mjr 35:e959ffba78fd 7 // A light source is positioned on the opposite side of the rod, so
mjr 35:e959ffba78fd 8 // that the rod casts a shadow on the sensor. We sense the position
mjr 35:e959ffba78fd 9 // by looking for the edge of the shadow.
mjr 35:e959ffba78fd 10 //
mjr 35:e959ffba78fd 11 // These sensors can take an image quickly, but it takes a significant
mjr 35:e959ffba78fd 12 // amount of time to transfer the image data from the sensor to the
mjr 35:e959ffba78fd 13 // microcontroller, since each pixel's analog voltage level must be
mjr 35:e959ffba78fd 14 // sampled serially. It takes about 20us to sample a pixel accurately.
mjr 35:e959ffba78fd 15 // The TSL1410R has 1280 pixels, and the 1412R has 1536. Sampling
mjr 35:e959ffba78fd 16 // every pixel would thus take about 25ms or 30ms respectively.
mjr 35:e959ffba78fd 17 // This is too slow for a responsive feel in the UI, and much too
mjr 35:e959ffba78fd 18 // slow to track the plunger release motion in real time. To improve
mjr 35:e959ffba78fd 19 // on the read speed, we only sample a subset of pixels for each
mjr 35:e959ffba78fd 20 // reading - for higher speed at the expense of spatial resolution.
mjr 35:e959ffba78fd 21 // The sensor's native resolution is much higher than we need, so
mjr 35:e959ffba78fd 22 // this is a perfectly equitable trade.
mjr 17:ab3cec0c8bf4 23
mjr 35:e959ffba78fd 24 #include "plunger.h"
mjr 17:ab3cec0c8bf4 25
mjr 17:ab3cec0c8bf4 26
mjr 25:e22b88bd783a 27 // PlungerSensor interface implementation for the CCD
mjr 35:e959ffba78fd 28 class PlungerSensorCCD: public PlungerSensor
mjr 17:ab3cec0c8bf4 29 {
mjr 17:ab3cec0c8bf4 30 public:
mjr 43:7a6364d82a41 31 PlungerSensorCCD(int nativePix, PinName si, PinName clock, PinName ao1, PinName ao2)
mjr 43:7a6364d82a41 32 : ccd(nativePix, si, clock, ao1, ao2)
mjr 17:ab3cec0c8bf4 33 {
mjr 17:ab3cec0c8bf4 34 }
mjr 17:ab3cec0c8bf4 35
mjr 17:ab3cec0c8bf4 36 // initialize
mjr 35:e959ffba78fd 37 virtual void init()
mjr 17:ab3cec0c8bf4 38 {
mjr 17:ab3cec0c8bf4 39 // flush any random power-on values from the CCD's integration
mjr 17:ab3cec0c8bf4 40 // capacitors, and start the first integration cycle
mjr 17:ab3cec0c8bf4 41 ccd.clear();
mjr 17:ab3cec0c8bf4 42 }
mjr 17:ab3cec0c8bf4 43
mjr 17:ab3cec0c8bf4 44 // Perform a low-res scan of the sensor.
mjr 35:e959ffba78fd 45 virtual bool lowResScan(int &pos)
mjr 17:ab3cec0c8bf4 46 {
mjr 17:ab3cec0c8bf4 47 // read the pixels at low resolution
mjr 17:ab3cec0c8bf4 48 uint16_t pix[nlpix];
mjr 17:ab3cec0c8bf4 49 ccd.read(pix, nlpix);
mjr 17:ab3cec0c8bf4 50
mjr 17:ab3cec0c8bf4 51 // determine which end is brighter
mjr 17:ab3cec0c8bf4 52 uint16_t p1 = pix[0];
mjr 17:ab3cec0c8bf4 53 uint16_t p2 = pix[nlpix-1];
mjr 40:cc0d9814522b 54 int si = 0, di = 1;
mjr 17:ab3cec0c8bf4 55 if (p1 < p2)
mjr 40:cc0d9814522b 56 si = nlpix - 1, di = -1;
mjr 17:ab3cec0c8bf4 57
mjr 17:ab3cec0c8bf4 58 // figure the shadow edge threshold - just use the midpoint
mjr 17:ab3cec0c8bf4 59 // of the levels at the bright and dark ends
mjr 17:ab3cec0c8bf4 60 uint16_t shadow = uint16_t((long(p1) + long(p2))/2);
mjr 17:ab3cec0c8bf4 61
mjr 17:ab3cec0c8bf4 62 // find the current tip position
mjr 17:ab3cec0c8bf4 63 for (int n = 0 ; n < nlpix ; ++n, si += di)
mjr 17:ab3cec0c8bf4 64 {
mjr 17:ab3cec0c8bf4 65 // check to see if we found the shadow
mjr 17:ab3cec0c8bf4 66 if (pix[si] <= shadow)
mjr 17:ab3cec0c8bf4 67 {
mjr 17:ab3cec0c8bf4 68 // got it - normalize it to normal 'npix' resolution and
mjr 17:ab3cec0c8bf4 69 // return the result
mjr 35:e959ffba78fd 70 pos = n*npix/nlpix;
mjr 35:e959ffba78fd 71 return true;
mjr 17:ab3cec0c8bf4 72 }
mjr 17:ab3cec0c8bf4 73 }
mjr 17:ab3cec0c8bf4 74
mjr 35:e959ffba78fd 75 // didn't find a shadow - return failure
mjr 35:e959ffba78fd 76 return false;
mjr 17:ab3cec0c8bf4 77 }
mjr 17:ab3cec0c8bf4 78
mjr 17:ab3cec0c8bf4 79 // Perform a high-res scan of the sensor.
mjr 35:e959ffba78fd 80 virtual bool highResScan(int &pos)
mjr 17:ab3cec0c8bf4 81 {
mjr 17:ab3cec0c8bf4 82 // read the array
mjr 18:5e890ebd0023 83 ccd.read(pix, npix);
mjr 17:ab3cec0c8bf4 84
mjr 18:5e890ebd0023 85 // get the brightness at each end of the sensor
mjr 18:5e890ebd0023 86 long b1 = pix[0];
mjr 18:5e890ebd0023 87 long b2 = pix[npix-1];
mjr 17:ab3cec0c8bf4 88
mjr 17:ab3cec0c8bf4 89 // Work from the bright end to the dark end. VP interprets the
mjr 17:ab3cec0c8bf4 90 // Z axis value as the amount the plunger is pulled: zero is the
mjr 17:ab3cec0c8bf4 91 // rest position, and the axis maximum is fully pulled. So we
mjr 17:ab3cec0c8bf4 92 // essentially want to report how much of the sensor is lit,
mjr 17:ab3cec0c8bf4 93 // since this increases as the plunger is pulled back.
mjr 18:5e890ebd0023 94 int si = 0, di = 1;
mjr 18:5e890ebd0023 95 long hi = b1;
mjr 18:5e890ebd0023 96 if (b1 < b2)
mjr 18:5e890ebd0023 97 si = npix - 1, di = -1, hi = b2;
mjr 17:ab3cec0c8bf4 98
mjr 17:ab3cec0c8bf4 99 // Figure the shadow threshold. In practice, the portion of the
mjr 17:ab3cec0c8bf4 100 // sensor that's not in shadow has all pixels consistently near
mjr 17:ab3cec0c8bf4 101 // saturation; the first drop in brightness is pretty reliably the
mjr 17:ab3cec0c8bf4 102 // start of the shadow. So set the threshold level to be closer
mjr 17:ab3cec0c8bf4 103 // to the bright end's brightness level, so that we detect the leading
mjr 17:ab3cec0c8bf4 104 // edge if the shadow isn't perfectly sharp. Use the point 1/3 of
mjr 17:ab3cec0c8bf4 105 // the way down from the high top the low side, so:
mjr 17:ab3cec0c8bf4 106 //
mjr 17:ab3cec0c8bf4 107 // threshold = lo + (hi - lo)*2/3
mjr 17:ab3cec0c8bf4 108 // = lo + hi*2/3 - lo*2/3
mjr 17:ab3cec0c8bf4 109 // = lo - lo*2/3 + hi*2/3
mjr 17:ab3cec0c8bf4 110 // = lo*1/3 + hi*2/3
mjr 17:ab3cec0c8bf4 111 // = (lo + hi*2)/3
mjr 17:ab3cec0c8bf4 112 //
mjr 18:5e890ebd0023 113 // Now, 'lo' is always one of b1 or b2, and 'hi' is the other
mjr 18:5e890ebd0023 114 // one, so we can rewrite this as:
mjr 18:5e890ebd0023 115 long midpt = (b1 + b2 + hi)/3;
mjr 17:ab3cec0c8bf4 116
mjr 17:ab3cec0c8bf4 117 // If we have enough contrast, proceed with the scan.
mjr 17:ab3cec0c8bf4 118 //
mjr 17:ab3cec0c8bf4 119 // If the bright end and dark end don't differ by enough, skip this
mjr 17:ab3cec0c8bf4 120 // reading entirely. Either we have an overexposed or underexposed frame,
mjr 17:ab3cec0c8bf4 121 // or the sensor is misaligned and is either fully in or out of shadow
mjr 17:ab3cec0c8bf4 122 // (it's supposed to be mounted such that the edge of the shadow always
mjr 17:ab3cec0c8bf4 123 // falls within the sensor, for any possible plunger position).
mjr 18:5e890ebd0023 124 if (labs(b1 - b2) > 0x1000)
mjr 17:ab3cec0c8bf4 125 {
mjr 17:ab3cec0c8bf4 126 uint16_t *pixp = pix + si;
mjr 18:5e890ebd0023 127 for (int n = 0 ; n < npix ; ++n, pixp += di)
mjr 17:ab3cec0c8bf4 128 {
mjr 17:ab3cec0c8bf4 129 // if we've crossed the midpoint, report this position
mjr 18:5e890ebd0023 130 if (long(*pixp) < midpt)
mjr 17:ab3cec0c8bf4 131 {
mjr 17:ab3cec0c8bf4 132 // note the new position
mjr 17:ab3cec0c8bf4 133 pos = n;
mjr 17:ab3cec0c8bf4 134 return true;
mjr 17:ab3cec0c8bf4 135 }
mjr 17:ab3cec0c8bf4 136 }
mjr 17:ab3cec0c8bf4 137 }
mjr 17:ab3cec0c8bf4 138
mjr 17:ab3cec0c8bf4 139 // we didn't find a shadow - return no reading
mjr 17:ab3cec0c8bf4 140 return false;
mjr 17:ab3cec0c8bf4 141 }
mjr 17:ab3cec0c8bf4 142
mjr 17:ab3cec0c8bf4 143 // send an exposure report to the joystick interface
mjr 35:e959ffba78fd 144 virtual void sendExposureReport(USBJoystick &js)
mjr 17:ab3cec0c8bf4 145 {
mjr 43:7a6364d82a41 146 // Read a fresh high-res scan, then do another right away. This
mjr 43:7a6364d82a41 147 // gives us the shortest possible exposure for the sample we report,
mjr 43:7a6364d82a41 148 // which helps ensure that the user inspecting the data sees something
mjr 43:7a6364d82a41 149 // close to what we see when we calculate the plunger position.
mjr 43:7a6364d82a41 150 ccd.read(pix, npix);
mjr 43:7a6364d82a41 151 ccd.read(pix, npix);
mjr 43:7a6364d82a41 152
mjr 17:ab3cec0c8bf4 153 // send reports for all pixels
mjr 17:ab3cec0c8bf4 154 int idx = 0;
mjr 17:ab3cec0c8bf4 155 while (idx < npix)
mjr 17:ab3cec0c8bf4 156 js.updateExposure(idx, npix, pix);
mjr 17:ab3cec0c8bf4 157
mjr 17:ab3cec0c8bf4 158 // The pixel dump requires many USB reports, since each report
mjr 17:ab3cec0c8bf4 159 // can only send a few pixel values. An integration cycle has
mjr 17:ab3cec0c8bf4 160 // been running all this time, since each read starts a new
mjr 17:ab3cec0c8bf4 161 // cycle. Our timing is longer than usual on this round, so
mjr 17:ab3cec0c8bf4 162 // the integration won't be comparable to a normal cycle. Throw
mjr 17:ab3cec0c8bf4 163 // this one away by doing a read now, and throwing it away - that
mjr 17:ab3cec0c8bf4 164 // will get the timing of the *next* cycle roughly back to normal.
mjr 17:ab3cec0c8bf4 165 ccd.read(pix, npix);
mjr 17:ab3cec0c8bf4 166 }
mjr 17:ab3cec0c8bf4 167
mjr 35:e959ffba78fd 168 protected:
mjr 43:7a6364d82a41 169 // pixel buffer - concrete subclasses must set to a buffer of the
mjr 43:7a6364d82a41 170 // appropriate size
mjr 35:e959ffba78fd 171 uint16_t *pix;
mjr 17:ab3cec0c8bf4 172
mjr 43:7a6364d82a41 173 // number of pixels in low-res scan - concrete subclasses must set
mjr 43:7a6364d82a41 174 // this to a value that evenly divides the native sensor size
mjr 43:7a6364d82a41 175 int nlpix;
mjr 43:7a6364d82a41 176
mjr 17:ab3cec0c8bf4 177 // the low-level interface to the CCD hardware
mjr 43:7a6364d82a41 178 public://$$$
mjr 35:e959ffba78fd 179 TSL1410R ccd;
mjr 17:ab3cec0c8bf4 180 };
mjr 35:e959ffba78fd 181
mjr 35:e959ffba78fd 182
mjr 35:e959ffba78fd 183 // TSL1410R sensor
mjr 35:e959ffba78fd 184 class PlungerSensorTSL1410R: public PlungerSensorCCD
mjr 35:e959ffba78fd 185 {
mjr 35:e959ffba78fd 186 public:
mjr 35:e959ffba78fd 187 PlungerSensorTSL1410R(PinName si, PinName clock, PinName ao1, PinName ao2)
mjr 35:e959ffba78fd 188 : PlungerSensorCCD(1280, si, clock, ao1, ao2)
mjr 35:e959ffba78fd 189 {
mjr 35:e959ffba78fd 190 // This sensor is 1x1280 pixels at 400dpi. Sample every 8th
mjr 35:e959ffba78fd 191 // pixel -> 160 pixels at 50dpi == 0.5mm spatial resolution.
mjr 43:7a6364d82a41 192 npix = 320;
mjr 43:7a6364d82a41 193
mjr 43:7a6364d82a41 194 // for the low-res scan, sample every 40th pixel -> 32 pixels
mjr 43:7a6364d82a41 195 // at 10dpi == 2.54mm spatial resolution.
mjr 43:7a6364d82a41 196 nlpix = 32;
mjr 43:7a6364d82a41 197
mjr 43:7a6364d82a41 198 // set the pixel buffer
mjr 35:e959ffba78fd 199 pix = pixbuf;
mjr 35:e959ffba78fd 200 }
mjr 35:e959ffba78fd 201
mjr 43:7a6364d82a41 202 uint16_t pixbuf[320];
mjr 35:e959ffba78fd 203 };
mjr 35:e959ffba78fd 204
mjr 35:e959ffba78fd 205 // TSL1412R
mjr 35:e959ffba78fd 206 class PlungerSensorTSL1412R: public PlungerSensorCCD
mjr 35:e959ffba78fd 207 {
mjr 35:e959ffba78fd 208 public:
mjr 35:e959ffba78fd 209 PlungerSensorTSL1412R(PinName si, PinName clock, PinName ao1, PinName ao2)
mjr 35:e959ffba78fd 210 : PlungerSensorCCD(1536, si, clock, ao1, ao2)
mjr 35:e959ffba78fd 211 {
mjr 35:e959ffba78fd 212 // This sensor is 1x1536 pixels at 400dpi. Sample every 8th
mjr 35:e959ffba78fd 213 // pixel -> 192 pixels at 50dpi == 0.5mm spatial resolution.
mjr 35:e959ffba78fd 214 npix = 192;
mjr 43:7a6364d82a41 215
mjr 43:7a6364d82a41 216 // for the low-res scan, sample every 48 pixels -> 32 pixels
mjr 43:7a6364d82a41 217 // at 8.34dpi = 3.05mm spatial resolution
mjr 43:7a6364d82a41 218 nlpix = 32;
mjr 43:7a6364d82a41 219
mjr 43:7a6364d82a41 220 // set the pixel buffer
mjr 35:e959ffba78fd 221 pix = pixbuf;
mjr 35:e959ffba78fd 222 }
mjr 35:e959ffba78fd 223
mjr 35:e959ffba78fd 224 uint16_t pixbuf[192];
mjr 35:e959ffba78fd 225 };
mjr 35:e959ffba78fd 226