Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: FastAnalogIn FastIO FastPWM SimpleDMA USBDevice mbed
Fork of Pinscape_Controller by
ccdSensor.h@25:e22b88bd783a, 2015-09-01 (annotated)
- Committer:
- mjr
- Date:
- Tue Sep 01 04:27:15 2015 +0000
- Revision:
- 25:e22b88bd783a
- Parent:
- 18:5e890ebd0023
Centralized the CCD pixel count setting to a single config.h option; added an option to config.h to select the board's mounting orientation for the accelerometer
Who changed what in which revision?
| User | Revision | Line number | New contents of line | 
|---|---|---|---|
| mjr | 17:ab3cec0c8bf4 | 1 | // CCD plunger sensor | 
| mjr | 17:ab3cec0c8bf4 | 2 | // | 
| mjr | 17:ab3cec0c8bf4 | 3 | // This file implements our generic plunger sensor interface for the | 
| mjr | 17:ab3cec0c8bf4 | 4 | // TAOS TSL1410R CCD array sensor. | 
| mjr | 17:ab3cec0c8bf4 | 5 | |
| mjr | 17:ab3cec0c8bf4 | 6 | |
| mjr | 17:ab3cec0c8bf4 | 7 | |
| mjr | 25:e22b88bd783a | 8 | // Number of pixels we read from the CCD on each frame. Use the | 
| mjr | 25:e22b88bd783a | 9 | // sample size from config.h. | 
| mjr | 25:e22b88bd783a | 10 | const int npix = CCD_NPIXELS_SAMPLED; | 
| mjr | 17:ab3cec0c8bf4 | 11 | |
| mjr | 25:e22b88bd783a | 12 | // PlungerSensor interface implementation for the CCD | 
| mjr | 17:ab3cec0c8bf4 | 13 | class PlungerSensor | 
| mjr | 17:ab3cec0c8bf4 | 14 | { | 
| mjr | 17:ab3cec0c8bf4 | 15 | public: | 
| mjr | 17:ab3cec0c8bf4 | 16 | PlungerSensor() : ccd(CCD_SO_PIN) | 
| mjr | 17:ab3cec0c8bf4 | 17 | { | 
| mjr | 17:ab3cec0c8bf4 | 18 | } | 
| mjr | 17:ab3cec0c8bf4 | 19 | |
| mjr | 17:ab3cec0c8bf4 | 20 | // initialize | 
| mjr | 17:ab3cec0c8bf4 | 21 | void init() | 
| mjr | 17:ab3cec0c8bf4 | 22 | { | 
| mjr | 17:ab3cec0c8bf4 | 23 | // flush any random power-on values from the CCD's integration | 
| mjr | 17:ab3cec0c8bf4 | 24 | // capacitors, and start the first integration cycle | 
| mjr | 17:ab3cec0c8bf4 | 25 | ccd.clear(); | 
| mjr | 17:ab3cec0c8bf4 | 26 | } | 
| mjr | 17:ab3cec0c8bf4 | 27 | |
| mjr | 17:ab3cec0c8bf4 | 28 | // Perform a low-res scan of the sensor. | 
| mjr | 17:ab3cec0c8bf4 | 29 | int lowResScan() | 
| mjr | 17:ab3cec0c8bf4 | 30 | { | 
| mjr | 17:ab3cec0c8bf4 | 31 | |
| mjr | 17:ab3cec0c8bf4 | 32 | // read the pixels at low resolution | 
| mjr | 17:ab3cec0c8bf4 | 33 | const int nlpix = 32; | 
| mjr | 17:ab3cec0c8bf4 | 34 | uint16_t pix[nlpix]; | 
| mjr | 17:ab3cec0c8bf4 | 35 | ccd.read(pix, nlpix); | 
| mjr | 17:ab3cec0c8bf4 | 36 | |
| mjr | 17:ab3cec0c8bf4 | 37 | // determine which end is brighter | 
| mjr | 17:ab3cec0c8bf4 | 38 | uint16_t p1 = pix[0]; | 
| mjr | 17:ab3cec0c8bf4 | 39 | uint16_t p2 = pix[nlpix-1]; | 
| mjr | 17:ab3cec0c8bf4 | 40 | int si = 1, di = 1; | 
| mjr | 17:ab3cec0c8bf4 | 41 | if (p1 < p2) | 
| mjr | 17:ab3cec0c8bf4 | 42 | si = nlpix, di = -1; | 
| mjr | 17:ab3cec0c8bf4 | 43 | |
| mjr | 17:ab3cec0c8bf4 | 44 | // figure the shadow edge threshold - just use the midpoint | 
| mjr | 17:ab3cec0c8bf4 | 45 | // of the levels at the bright and dark ends | 
| mjr | 17:ab3cec0c8bf4 | 46 | uint16_t shadow = uint16_t((long(p1) + long(p2))/2); | 
| mjr | 17:ab3cec0c8bf4 | 47 | |
| mjr | 17:ab3cec0c8bf4 | 48 | // find the current tip position | 
| mjr | 17:ab3cec0c8bf4 | 49 | for (int n = 0 ; n < nlpix ; ++n, si += di) | 
| mjr | 17:ab3cec0c8bf4 | 50 | { | 
| mjr | 17:ab3cec0c8bf4 | 51 | // check to see if we found the shadow | 
| mjr | 17:ab3cec0c8bf4 | 52 | if (pix[si] <= shadow) | 
| mjr | 17:ab3cec0c8bf4 | 53 | { | 
| mjr | 17:ab3cec0c8bf4 | 54 | // got it - normalize it to normal 'npix' resolution and | 
| mjr | 17:ab3cec0c8bf4 | 55 | // return the result | 
| mjr | 17:ab3cec0c8bf4 | 56 | return n*npix/nlpix; | 
| mjr | 17:ab3cec0c8bf4 | 57 | } | 
| mjr | 17:ab3cec0c8bf4 | 58 | } | 
| mjr | 17:ab3cec0c8bf4 | 59 | |
| mjr | 17:ab3cec0c8bf4 | 60 | // didn't find a shadow - assume the whole array is in shadow (so | 
| mjr | 17:ab3cec0c8bf4 | 61 | // the edge is at the zero pixel point) | 
| mjr | 17:ab3cec0c8bf4 | 62 | return 0; | 
| mjr | 17:ab3cec0c8bf4 | 63 | } | 
| mjr | 17:ab3cec0c8bf4 | 64 | |
| mjr | 17:ab3cec0c8bf4 | 65 | // Perform a high-res scan of the sensor. | 
| mjr | 17:ab3cec0c8bf4 | 66 | bool highResScan(int &pos) | 
| mjr | 17:ab3cec0c8bf4 | 67 | { | 
| mjr | 17:ab3cec0c8bf4 | 68 | // read the array | 
| mjr | 18:5e890ebd0023 | 69 | ccd.read(pix, npix); | 
| mjr | 17:ab3cec0c8bf4 | 70 | |
| mjr | 18:5e890ebd0023 | 71 | // get the brightness at each end of the sensor | 
| mjr | 18:5e890ebd0023 | 72 | long b1 = pix[0]; | 
| mjr | 18:5e890ebd0023 | 73 | long b2 = pix[npix-1]; | 
| mjr | 17:ab3cec0c8bf4 | 74 | |
| mjr | 17:ab3cec0c8bf4 | 75 | // Work from the bright end to the dark end. VP interprets the | 
| mjr | 17:ab3cec0c8bf4 | 76 | // Z axis value as the amount the plunger is pulled: zero is the | 
| mjr | 17:ab3cec0c8bf4 | 77 | // rest position, and the axis maximum is fully pulled. So we | 
| mjr | 17:ab3cec0c8bf4 | 78 | // essentially want to report how much of the sensor is lit, | 
| mjr | 17:ab3cec0c8bf4 | 79 | // since this increases as the plunger is pulled back. | 
| mjr | 18:5e890ebd0023 | 80 | int si = 0, di = 1; | 
| mjr | 18:5e890ebd0023 | 81 | long hi = b1; | 
| mjr | 18:5e890ebd0023 | 82 | if (b1 < b2) | 
| mjr | 18:5e890ebd0023 | 83 | si = npix - 1, di = -1, hi = b2; | 
| mjr | 17:ab3cec0c8bf4 | 84 | |
| mjr | 17:ab3cec0c8bf4 | 85 | // Figure the shadow threshold. In practice, the portion of the | 
| mjr | 17:ab3cec0c8bf4 | 86 | // sensor that's not in shadow has all pixels consistently near | 
| mjr | 17:ab3cec0c8bf4 | 87 | // saturation; the first drop in brightness is pretty reliably the | 
| mjr | 17:ab3cec0c8bf4 | 88 | // start of the shadow. So set the threshold level to be closer | 
| mjr | 17:ab3cec0c8bf4 | 89 | // to the bright end's brightness level, so that we detect the leading | 
| mjr | 17:ab3cec0c8bf4 | 90 | // edge if the shadow isn't perfectly sharp. Use the point 1/3 of | 
| mjr | 17:ab3cec0c8bf4 | 91 | // the way down from the high top the low side, so: | 
| mjr | 17:ab3cec0c8bf4 | 92 | // | 
| mjr | 17:ab3cec0c8bf4 | 93 | // threshold = lo + (hi - lo)*2/3 | 
| mjr | 17:ab3cec0c8bf4 | 94 | // = lo + hi*2/3 - lo*2/3 | 
| mjr | 17:ab3cec0c8bf4 | 95 | // = lo - lo*2/3 + hi*2/3 | 
| mjr | 17:ab3cec0c8bf4 | 96 | // = lo*1/3 + hi*2/3 | 
| mjr | 17:ab3cec0c8bf4 | 97 | // = (lo + hi*2)/3 | 
| mjr | 17:ab3cec0c8bf4 | 98 | // | 
| mjr | 18:5e890ebd0023 | 99 | // Now, 'lo' is always one of b1 or b2, and 'hi' is the other | 
| mjr | 18:5e890ebd0023 | 100 | // one, so we can rewrite this as: | 
| mjr | 18:5e890ebd0023 | 101 | long midpt = (b1 + b2 + hi)/3; | 
| mjr | 17:ab3cec0c8bf4 | 102 | |
| mjr | 17:ab3cec0c8bf4 | 103 | // If we have enough contrast, proceed with the scan. | 
| mjr | 17:ab3cec0c8bf4 | 104 | // | 
| mjr | 17:ab3cec0c8bf4 | 105 | // If the bright end and dark end don't differ by enough, skip this | 
| mjr | 17:ab3cec0c8bf4 | 106 | // reading entirely. Either we have an overexposed or underexposed frame, | 
| mjr | 17:ab3cec0c8bf4 | 107 | // or the sensor is misaligned and is either fully in or out of shadow | 
| mjr | 17:ab3cec0c8bf4 | 108 | // (it's supposed to be mounted such that the edge of the shadow always | 
| mjr | 17:ab3cec0c8bf4 | 109 | // falls within the sensor, for any possible plunger position). | 
| mjr | 18:5e890ebd0023 | 110 | if (labs(b1 - b2) > 0x1000) | 
| mjr | 17:ab3cec0c8bf4 | 111 | { | 
| mjr | 17:ab3cec0c8bf4 | 112 | uint16_t *pixp = pix + si; | 
| mjr | 18:5e890ebd0023 | 113 | for (int n = 0 ; n < npix ; ++n, pixp += di) | 
| mjr | 17:ab3cec0c8bf4 | 114 | { | 
| mjr | 17:ab3cec0c8bf4 | 115 | // if we've crossed the midpoint, report this position | 
| mjr | 18:5e890ebd0023 | 116 | if (long(*pixp) < midpt) | 
| mjr | 17:ab3cec0c8bf4 | 117 | { | 
| mjr | 17:ab3cec0c8bf4 | 118 | // note the new position | 
| mjr | 17:ab3cec0c8bf4 | 119 | pos = n; | 
| mjr | 17:ab3cec0c8bf4 | 120 | return true; | 
| mjr | 17:ab3cec0c8bf4 | 121 | } | 
| mjr | 17:ab3cec0c8bf4 | 122 | } | 
| mjr | 17:ab3cec0c8bf4 | 123 | } | 
| mjr | 17:ab3cec0c8bf4 | 124 | |
| mjr | 17:ab3cec0c8bf4 | 125 | // we didn't find a shadow - return no reading | 
| mjr | 17:ab3cec0c8bf4 | 126 | return false; | 
| mjr | 17:ab3cec0c8bf4 | 127 | } | 
| mjr | 17:ab3cec0c8bf4 | 128 | |
| mjr | 17:ab3cec0c8bf4 | 129 | // send an exposure report to the joystick interface | 
| mjr | 17:ab3cec0c8bf4 | 130 | void sendExposureReport(USBJoystick &js) | 
| mjr | 17:ab3cec0c8bf4 | 131 | { | 
| mjr | 17:ab3cec0c8bf4 | 132 | // send reports for all pixels | 
| mjr | 17:ab3cec0c8bf4 | 133 | int idx = 0; | 
| mjr | 17:ab3cec0c8bf4 | 134 | while (idx < npix) | 
| mjr | 18:5e890ebd0023 | 135 | { | 
| mjr | 17:ab3cec0c8bf4 | 136 | js.updateExposure(idx, npix, pix); | 
| mjr | 18:5e890ebd0023 | 137 | wait_ms(1); | 
| mjr | 18:5e890ebd0023 | 138 | } | 
| mjr | 17:ab3cec0c8bf4 | 139 | |
| mjr | 17:ab3cec0c8bf4 | 140 | // The pixel dump requires many USB reports, since each report | 
| mjr | 17:ab3cec0c8bf4 | 141 | // can only send a few pixel values. An integration cycle has | 
| mjr | 17:ab3cec0c8bf4 | 142 | // been running all this time, since each read starts a new | 
| mjr | 17:ab3cec0c8bf4 | 143 | // cycle. Our timing is longer than usual on this round, so | 
| mjr | 17:ab3cec0c8bf4 | 144 | // the integration won't be comparable to a normal cycle. Throw | 
| mjr | 17:ab3cec0c8bf4 | 145 | // this one away by doing a read now, and throwing it away - that | 
| mjr | 17:ab3cec0c8bf4 | 146 | // will get the timing of the *next* cycle roughly back to normal. | 
| mjr | 17:ab3cec0c8bf4 | 147 | ccd.read(pix, npix); | 
| mjr | 17:ab3cec0c8bf4 | 148 | } | 
| mjr | 17:ab3cec0c8bf4 | 149 | |
| mjr | 17:ab3cec0c8bf4 | 150 | private: | 
| mjr | 17:ab3cec0c8bf4 | 151 | // pixel buffer | 
| mjr | 17:ab3cec0c8bf4 | 152 | uint16_t pix[npix]; | 
| mjr | 17:ab3cec0c8bf4 | 153 | |
| mjr | 17:ab3cec0c8bf4 | 154 | // the low-level interface to the CCD hardware | 
| mjr | 17:ab3cec0c8bf4 | 155 | TSL1410R<CCD_SI_PIN, CCD_CLOCK_PIN> ccd; | 
| mjr | 17:ab3cec0c8bf4 | 156 | }; | 
