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: mbed FastIO FastPWM USBDevice
Plunger/tsl14xxSensor.h@86:e30a1f60f783, 2017-04-21 (annotated)
- Committer:
- mjr
- Date:
- Fri Apr 21 18:50:37 2017 +0000
- Revision:
- 86:e30a1f60f783
- Parent:
- 82:4f6209cb5c33
- Child:
- 87:8d35c74403af
Capture a bunch of alternative bar code decoder tests, mostly unsuccessful
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
mjr | 82:4f6209cb5c33 | 1 | // Base class for TSL14xx-based plunger sensors. |
mjr | 82:4f6209cb5c33 | 2 | // |
mjr | 82:4f6209cb5c33 | 3 | // This provides a common base class for plunger sensors based on |
mjr | 82:4f6209cb5c33 | 4 | // AMS/TAOS TSL14xx sensors (TSL1410R, TSL1412S, TSL1401CL). The sensors |
mjr | 82:4f6209cb5c33 | 5 | // in this series all work the same way, differing mostly in the number |
mjr | 82:4f6209cb5c33 | 6 | // of pixels. However, we have two fundamentally different ways of using |
mjr | 82:4f6209cb5c33 | 7 | // these image sensors to detect position: sensing the position of the |
mjr | 82:4f6209cb5c33 | 8 | // shadow cast by the plunger on the sensor, and optically reading a bar |
mjr | 82:4f6209cb5c33 | 9 | // code telling us the location of the sensor along a scale. This class |
mjr | 82:4f6209cb5c33 | 10 | // provides the low-level pixel-sensor interface; subclasses provide the |
mjr | 82:4f6209cb5c33 | 11 | // image analysis that figures the position from the captured image. |
mjr | 82:4f6209cb5c33 | 12 | |
mjr | 82:4f6209cb5c33 | 13 | |
mjr | 82:4f6209cb5c33 | 14 | #ifndef _TSL14XXSENSOR_H_ |
mjr | 82:4f6209cb5c33 | 15 | #define _TSL14XXSENSOR_H_ |
mjr | 82:4f6209cb5c33 | 16 | |
mjr | 82:4f6209cb5c33 | 17 | #include "plunger.h" |
mjr | 82:4f6209cb5c33 | 18 | #include "TSL14xx.h" |
mjr | 82:4f6209cb5c33 | 19 | |
mjr | 82:4f6209cb5c33 | 20 | class PlungerSensorTSL14xx: public PlungerSensor |
mjr | 82:4f6209cb5c33 | 21 | { |
mjr | 82:4f6209cb5c33 | 22 | public: |
mjr | 86:e30a1f60f783 | 23 | PlungerSensorTSL14xx(int nativePix, int nativeScale, |
mjr | 86:e30a1f60f783 | 24 | PinName si, PinName clock, PinName ao) |
mjr | 86:e30a1f60f783 | 25 | : PlungerSensor(nativeScale), |
mjr | 86:e30a1f60f783 | 26 | sensor(nativePix, si, clock, ao) |
mjr | 82:4f6209cb5c33 | 27 | { |
mjr | 86:e30a1f60f783 | 28 | // remember the native pixel size |
mjr | 82:4f6209cb5c33 | 29 | native_npix = nativePix; |
mjr | 82:4f6209cb5c33 | 30 | |
mjr | 82:4f6209cb5c33 | 31 | // start with no additional integration time for automatic |
mjr | 82:4f6209cb5c33 | 32 | // exposure control |
mjr | 82:4f6209cb5c33 | 33 | axcTime = 0; |
mjr | 82:4f6209cb5c33 | 34 | } |
mjr | 82:4f6209cb5c33 | 35 | |
mjr | 82:4f6209cb5c33 | 36 | // is the sensor ready? |
mjr | 82:4f6209cb5c33 | 37 | virtual bool ready() { return sensor.ready(); } |
mjr | 82:4f6209cb5c33 | 38 | |
mjr | 82:4f6209cb5c33 | 39 | virtual void init() |
mjr | 82:4f6209cb5c33 | 40 | { |
mjr | 82:4f6209cb5c33 | 41 | sensor.clear(); |
mjr | 82:4f6209cb5c33 | 42 | } |
mjr | 82:4f6209cb5c33 | 43 | |
mjr | 82:4f6209cb5c33 | 44 | // Send a status report to the joystick interface. |
mjr | 82:4f6209cb5c33 | 45 | // See plunger.h for details on the arguments. |
mjr | 82:4f6209cb5c33 | 46 | virtual void sendStatusReport(USBJoystick &js, uint8_t flags, uint8_t extraTime) |
mjr | 82:4f6209cb5c33 | 47 | { |
mjr | 86:e30a1f60f783 | 48 | // The sensor's internal buffering scheme makes it a little tricky |
mjr | 86:e30a1f60f783 | 49 | // to get the requested timing, and our own double-buffering adds a |
mjr | 86:e30a1f60f783 | 50 | // little complexity as well. To get the exact timing requested, we |
mjr | 86:e30a1f60f783 | 51 | // have to work with the buffering pipeline like so: |
mjr | 82:4f6209cb5c33 | 52 | // |
mjr | 86:e30a1f60f783 | 53 | // 1. Call startCapture(). This waits for any in-progress pixel |
mjr | 86:e30a1f60f783 | 54 | // transfer from the sensor to finish, then executes a HOLD/SI pulse |
mjr | 86:e30a1f60f783 | 55 | // on the sensor. The HOLD/SI takes a snapshot of the current live |
mjr | 86:e30a1f60f783 | 56 | // photo receptors to the sensor shift register. These pixels have |
mjr | 86:e30a1f60f783 | 57 | // been integrating starting from before we were called; call this |
mjr | 86:e30a1f60f783 | 58 | // integration period A. So the shift register contains period A. |
mjr | 86:e30a1f60f783 | 59 | // The HOLD/SI then grounds the photo receptors, clearing their |
mjr | 86:e30a1f60f783 | 60 | // charge, thus starting a new integration period B. After sending |
mjr | 86:e30a1f60f783 | 61 | // the HOLD/SI pulse, startCapture() begins a DMA transfer of the |
mjr | 86:e30a1f60f783 | 62 | // shift register pixels (period A) to one of our two buffers (call |
mjr | 86:e30a1f60f783 | 63 | // it the EVEN buffer). |
mjr | 82:4f6209cb5c33 | 64 | // |
mjr | 86:e30a1f60f783 | 65 | // 2. Wait for the current transfer (period A to the EVEN buffer) |
mjr | 86:e30a1f60f783 | 66 | // to finish. The minimum integration time is the time of one |
mjr | 86:e30a1f60f783 | 67 | // transfer cycle, so this brings us to the minimum time for |
mjr | 86:e30a1f60f783 | 68 | // period B. |
mjr | 86:e30a1f60f783 | 69 | // |
mjr | 86:e30a1f60f783 | 70 | // 3. Now pause for the reqeusted extra delay time. Period B is |
mjr | 86:e30a1f60f783 | 71 | // still running at this point (it keeps going until we start a |
mjr | 86:e30a1f60f783 | 72 | // new capture), so this pause adds the requested extra time to |
mjr | 86:e30a1f60f783 | 73 | // period B's total integration time. This brings period B to |
mjr | 86:e30a1f60f783 | 74 | // exactly the requested total time. |
mjr | 82:4f6209cb5c33 | 75 | // |
mjr | 86:e30a1f60f783 | 76 | // 4. Call startCapture() to end period B, move period B's pixels |
mjr | 86:e30a1f60f783 | 77 | // to the sensor's shift register, and begin period C. This |
mjr | 86:e30a1f60f783 | 78 | // switches DMA buffers, so the EVEN buffer (with period A) is now |
mjr | 86:e30a1f60f783 | 79 | // available, and the ODD buffer becomes the DMA target for the |
mjr | 86:e30a1f60f783 | 80 | // period B pixels. |
mjr | 82:4f6209cb5c33 | 81 | // |
mjr | 86:e30a1f60f783 | 82 | // 5. Wait for the period B pixels to become available, via |
mjr | 86:e30a1f60f783 | 83 | // waitPix(). This waits for the DMA transfer to complete and |
mjr | 86:e30a1f60f783 | 84 | // hands us the latest (ODD) transfer buffer. |
mjr | 86:e30a1f60f783 | 85 | // |
mjr | 86:e30a1f60f783 | 86 | sensor.startCapture(axcTime); // begin transfer of pixels from incoming period A, begin integration period B |
mjr | 82:4f6209cb5c33 | 87 | sensor.wait(); // wait for scan of A to complete, as minimum integration B time |
mjr | 82:4f6209cb5c33 | 88 | wait_us(long(extraTime) * 100); // add extraTime (0.1ms == 100us increments) to integration B time |
mjr | 86:e30a1f60f783 | 89 | sensor.startCapture(axcTime); // begin transfer of pixels from integration period B, begin period C; period A pixels now available |
mjr | 82:4f6209cb5c33 | 90 | |
mjr | 86:e30a1f60f783 | 91 | // wait for the DMA transfer of period B to finish, and get the |
mjr | 86:e30a1f60f783 | 92 | // period B pixels |
mjr | 82:4f6209cb5c33 | 93 | uint8_t *pix; |
mjr | 82:4f6209cb5c33 | 94 | uint32_t t; |
mjr | 86:e30a1f60f783 | 95 | sensor.waitPix(pix, t); |
mjr | 82:4f6209cb5c33 | 96 | |
mjr | 82:4f6209cb5c33 | 97 | // start a timer to measure the processing time |
mjr | 82:4f6209cb5c33 | 98 | Timer pt; |
mjr | 82:4f6209cb5c33 | 99 | pt.start(); |
mjr | 82:4f6209cb5c33 | 100 | |
mjr | 82:4f6209cb5c33 | 101 | // process the pixels and read the position |
mjr | 86:e30a1f60f783 | 102 | int pos, rawPos; |
mjr | 82:4f6209cb5c33 | 103 | int n = native_npix; |
mjr | 86:e30a1f60f783 | 104 | if (process(pix, n, rawPos)) |
mjr | 86:e30a1f60f783 | 105 | { |
mjr | 86:e30a1f60f783 | 106 | // success - apply the jitter filter |
mjr | 86:e30a1f60f783 | 107 | pos = jitterFilter(rawPos); |
mjr | 86:e30a1f60f783 | 108 | } |
mjr | 86:e30a1f60f783 | 109 | else |
mjr | 86:e30a1f60f783 | 110 | { |
mjr | 86:e30a1f60f783 | 111 | // report 0xFFFF to indicate that the position wasn't read |
mjr | 82:4f6209cb5c33 | 112 | pos = 0xFFFF; |
mjr | 86:e30a1f60f783 | 113 | rawPos = 0xFFFF; |
mjr | 86:e30a1f60f783 | 114 | } |
mjr | 82:4f6209cb5c33 | 115 | |
mjr | 82:4f6209cb5c33 | 116 | // note the processing time |
mjr | 82:4f6209cb5c33 | 117 | uint32_t processTime = pt.read_us(); |
mjr | 82:4f6209cb5c33 | 118 | |
mjr | 86:e30a1f60f783 | 119 | // If a low-res scan is desired, reduce to a subset of pixels. Ignore |
mjr | 86:e30a1f60f783 | 120 | // this for smaller sensors (below 512 pixels) |
mjr | 86:e30a1f60f783 | 121 | if ((flags & 0x01) && n >= 512) |
mjr | 82:4f6209cb5c33 | 122 | { |
mjr | 82:4f6209cb5c33 | 123 | // figure how many sensor pixels we combine into each low-res pixel |
mjr | 82:4f6209cb5c33 | 124 | const int group = 8; |
mjr | 82:4f6209cb5c33 | 125 | int lowResPix = n / group; |
mjr | 82:4f6209cb5c33 | 126 | |
mjr | 82:4f6209cb5c33 | 127 | // combine the pixels |
mjr | 82:4f6209cb5c33 | 128 | int src, dst; |
mjr | 82:4f6209cb5c33 | 129 | for (src = dst = 0 ; dst < lowResPix ; ++dst) |
mjr | 82:4f6209cb5c33 | 130 | { |
mjr | 82:4f6209cb5c33 | 131 | // average this block of pixels |
mjr | 82:4f6209cb5c33 | 132 | int a = 0; |
mjr | 82:4f6209cb5c33 | 133 | for (int j = 0 ; j < group ; ++j) |
mjr | 82:4f6209cb5c33 | 134 | a += pix[src++]; |
mjr | 82:4f6209cb5c33 | 135 | |
mjr | 82:4f6209cb5c33 | 136 | // we have the sum, so get the average |
mjr | 82:4f6209cb5c33 | 137 | a /= group; |
mjr | 82:4f6209cb5c33 | 138 | |
mjr | 82:4f6209cb5c33 | 139 | // store the down-res'd pixel in the array |
mjr | 82:4f6209cb5c33 | 140 | pix[dst] = uint8_t(a); |
mjr | 82:4f6209cb5c33 | 141 | } |
mjr | 82:4f6209cb5c33 | 142 | |
mjr | 82:4f6209cb5c33 | 143 | // update the pixel count to the reduced array size |
mjr | 82:4f6209cb5c33 | 144 | n = lowResPix; |
mjr | 82:4f6209cb5c33 | 145 | } |
mjr | 82:4f6209cb5c33 | 146 | |
mjr | 86:e30a1f60f783 | 147 | // figure the report flags |
mjr | 86:e30a1f60f783 | 148 | int jsflags = 0; |
mjr | 86:e30a1f60f783 | 149 | |
mjr | 86:e30a1f60f783 | 150 | // add flags for the detected orientation: 0x01 for normal orientation, |
mjr | 86:e30a1f60f783 | 151 | // 0x02 for reversed orientation; no flags if orientation is unknown |
mjr | 86:e30a1f60f783 | 152 | int dir = getOrientation(); |
mjr | 86:e30a1f60f783 | 153 | if (dir == 1) |
mjr | 86:e30a1f60f783 | 154 | jsflags |= 0x01; |
mjr | 86:e30a1f60f783 | 155 | else if (dir == -1) |
mjr | 86:e30a1f60f783 | 156 | jsflags |= 0x02; |
mjr | 86:e30a1f60f783 | 157 | |
mjr | 86:e30a1f60f783 | 158 | // send the sensor status report headers |
mjr | 86:e30a1f60f783 | 159 | js.sendPlungerStatus(n, pos, jsflags, sensor.getAvgScanTime(), processTime); |
mjr | 86:e30a1f60f783 | 160 | js.sendPlungerStatus2(nativeScale, jfLo, jfHi, rawPos, axcTime); |
mjr | 82:4f6209cb5c33 | 161 | |
mjr | 82:4f6209cb5c33 | 162 | // If we're not in calibration mode, send the pixels |
mjr | 82:4f6209cb5c33 | 163 | extern bool plungerCalMode; |
mjr | 82:4f6209cb5c33 | 164 | if (!plungerCalMode) |
mjr | 82:4f6209cb5c33 | 165 | { |
mjr | 82:4f6209cb5c33 | 166 | // send the pixels in report-sized chunks until we get them all |
mjr | 82:4f6209cb5c33 | 167 | int idx = 0; |
mjr | 82:4f6209cb5c33 | 168 | while (idx < n) |
mjr | 82:4f6209cb5c33 | 169 | js.sendPlungerPix(idx, n, pix); |
mjr | 82:4f6209cb5c33 | 170 | } |
mjr | 82:4f6209cb5c33 | 171 | |
mjr | 82:4f6209cb5c33 | 172 | // It takes us a while to send all of the pixels, since we have |
mjr | 82:4f6209cb5c33 | 173 | // to break them up into many USB reports. This delay means that |
mjr | 82:4f6209cb5c33 | 174 | // the sensor has been sitting there integrating for much longer |
mjr | 82:4f6209cb5c33 | 175 | // than usual, so the next frame read will be overexposed. To |
mjr | 82:4f6209cb5c33 | 176 | // mitigate this, make sure we don't have a capture running, |
mjr | 82:4f6209cb5c33 | 177 | // then clear the sensor and start a new capture. |
mjr | 82:4f6209cb5c33 | 178 | sensor.wait(); |
mjr | 82:4f6209cb5c33 | 179 | sensor.clear(); |
mjr | 82:4f6209cb5c33 | 180 | sensor.startCapture(axcTime); |
mjr | 82:4f6209cb5c33 | 181 | } |
mjr | 82:4f6209cb5c33 | 182 | |
mjr | 82:4f6209cb5c33 | 183 | // get the average sensor scan time |
mjr | 82:4f6209cb5c33 | 184 | virtual uint32_t getAvgScanTime() { return sensor.getAvgScanTime(); } |
mjr | 82:4f6209cb5c33 | 185 | |
mjr | 82:4f6209cb5c33 | 186 | protected: |
mjr | 82:4f6209cb5c33 | 187 | // Analyze the image and find the plunger position. If successful, |
mjr | 82:4f6209cb5c33 | 188 | // fills in 'pixpos' with the plunger position using the 0..65535 |
mjr | 82:4f6209cb5c33 | 189 | // scale and returns true. If no position can be detected from the |
mjr | 82:4f6209cb5c33 | 190 | // image data, returns false. |
mjr | 82:4f6209cb5c33 | 191 | virtual bool process(const uint8_t *pix, int npix, int &pixpos) = 0; |
mjr | 82:4f6209cb5c33 | 192 | |
mjr | 82:4f6209cb5c33 | 193 | // Get the currently detected sensor orientation, if applicable. |
mjr | 82:4f6209cb5c33 | 194 | // Returns 1 for standard orientation, -1 for reversed orientation, |
mjr | 82:4f6209cb5c33 | 195 | // or 0 for orientation unknown or not applicable. Edge sensors can |
mjr | 82:4f6209cb5c33 | 196 | // automatically detect orientation by observing which side of the |
mjr | 82:4f6209cb5c33 | 197 | // image is in shadow. Bar code sensors generally can't detect |
mjr | 82:4f6209cb5c33 | 198 | // orientation. |
mjr | 82:4f6209cb5c33 | 199 | virtual int getOrientation() const { return 0; } |
mjr | 82:4f6209cb5c33 | 200 | |
mjr | 82:4f6209cb5c33 | 201 | // the low-level interface to the TSL14xx sensor |
mjr | 82:4f6209cb5c33 | 202 | TSL14xx sensor; |
mjr | 82:4f6209cb5c33 | 203 | |
mjr | 82:4f6209cb5c33 | 204 | // number of pixels |
mjr | 82:4f6209cb5c33 | 205 | int native_npix; |
mjr | 82:4f6209cb5c33 | 206 | |
mjr | 82:4f6209cb5c33 | 207 | // Automatic exposure control time, in microseconds. This is an amount |
mjr | 82:4f6209cb5c33 | 208 | // of time we add to each integration cycle to compensate for low light |
mjr | 82:4f6209cb5c33 | 209 | // levels. By default, this is always zero; the base class doesn't have |
mjr | 82:4f6209cb5c33 | 210 | // any logic for determining proper exposure, because that's a function |
mjr | 82:4f6209cb5c33 | 211 | // of the type of image we're looking for. Subclasses can add logic in |
mjr | 82:4f6209cb5c33 | 212 | // the process() function to check exposure level and adjust this value |
mjr | 82:4f6209cb5c33 | 213 | // if the image looks over- or under-exposed. |
mjr | 82:4f6209cb5c33 | 214 | uint32_t axcTime; |
mjr | 82:4f6209cb5c33 | 215 | }; |
mjr | 82:4f6209cb5c33 | 216 | |
mjr | 86:e30a1f60f783 | 217 | // --------------------------------------------------------------------- |
mjr | 86:e30a1f60f783 | 218 | // |
mjr | 86:e30a1f60f783 | 219 | // Subclass for the large sensors, such as TSL1410R (1280 pixels) |
mjr | 86:e30a1f60f783 | 220 | // and TSL1412S (1536 pixels). |
mjr | 86:e30a1f60f783 | 221 | // |
mjr | 86:e30a1f60f783 | 222 | // For the large sensors, pixel transfers take a long time: about |
mjr | 86:e30a1f60f783 | 223 | // 2.5ms on the 1410R and 1412S. This is much longer than our main |
mjr | 86:e30a1f60f783 | 224 | // loop time, so we don't want to block other work to do a transfer. |
mjr | 86:e30a1f60f783 | 225 | // Instead, we want to do our transfers asynchronously, so that the |
mjr | 86:e30a1f60f783 | 226 | // main loop can keep going while a transfer proceeds. This is |
mjr | 86:e30a1f60f783 | 227 | // possible via our DMA double buffering. |
mjr | 86:e30a1f60f783 | 228 | // |
mjr | 86:e30a1f60f783 | 229 | // This scheme gives us three images in our pipeline at any given time: |
mjr | 86:e30a1f60f783 | 230 | // |
mjr | 86:e30a1f60f783 | 231 | // - a live image integrating light on the photo receptors on the sensor |
mjr | 86:e30a1f60f783 | 232 | // - the prior image being held in the sensor's shift register and being |
mjr | 86:e30a1f60f783 | 233 | // transferred via DMA into one of our buffers |
mjr | 86:e30a1f60f783 | 234 | // - the image before that in our other buffer |
mjr | 86:e30a1f60f783 | 235 | // |
mjr | 86:e30a1f60f783 | 236 | // Integration of a live image starts when we begin the transfer of the |
mjr | 86:e30a1f60f783 | 237 | // prior image. Integration ends when we start the next transfer after |
mjr | 86:e30a1f60f783 | 238 | // that. So the total integration time, which is to say the exposure |
mjr | 86:e30a1f60f783 | 239 | // time, is the time between consecutive transfer starts. It's important |
mjr | 86:e30a1f60f783 | 240 | // for this time to be consistent from image to image, because that |
mjr | 86:e30a1f60f783 | 241 | // determines the exposure level. We use polling from the main loop |
mjr | 86:e30a1f60f783 | 242 | // to initiate new transfers, so the main loop is responsible for |
mjr | 86:e30a1f60f783 | 243 | // polling frequently during the 2.5ms transfer period. It would be |
mjr | 86:e30a1f60f783 | 244 | // more consistent if we did this in an interrupt handler instead, |
mjr | 86:e30a1f60f783 | 245 | // but that would complicate things considerably since our image |
mjr | 86:e30a1f60f783 | 246 | // analysis is too time-consuming to do in interrupt context. |
mjr | 86:e30a1f60f783 | 247 | // |
mjr | 86:e30a1f60f783 | 248 | class PlungerSensorTSL14xxLarge: public PlungerSensorTSL14xx |
mjr | 86:e30a1f60f783 | 249 | { |
mjr | 86:e30a1f60f783 | 250 | public: |
mjr | 86:e30a1f60f783 | 251 | PlungerSensorTSL14xxLarge(int nativePix, int nativeScale, |
mjr | 86:e30a1f60f783 | 252 | PinName si, PinName clock, PinName ao) |
mjr | 86:e30a1f60f783 | 253 | : PlungerSensorTSL14xx(nativePix, nativeScale, si, clock, ao) |
mjr | 86:e30a1f60f783 | 254 | { |
mjr | 86:e30a1f60f783 | 255 | } |
mjr | 86:e30a1f60f783 | 256 | |
mjr | 86:e30a1f60f783 | 257 | // read the plunger position |
mjr | 86:e30a1f60f783 | 258 | virtual bool readRaw(PlungerReading &r) |
mjr | 86:e30a1f60f783 | 259 | { |
mjr | 86:e30a1f60f783 | 260 | // start reading the next pixel array (this waits for any DMA |
mjr | 86:e30a1f60f783 | 261 | // transfer in progress to finish, ensuring a stable pixel buffer) |
mjr | 86:e30a1f60f783 | 262 | sensor.startCapture(axcTime); |
mjr | 86:e30a1f60f783 | 263 | |
mjr | 86:e30a1f60f783 | 264 | // get the image array from the last capture |
mjr | 86:e30a1f60f783 | 265 | uint8_t *pix; |
mjr | 86:e30a1f60f783 | 266 | uint32_t tpix; |
mjr | 86:e30a1f60f783 | 267 | sensor.getPix(pix, tpix); |
mjr | 86:e30a1f60f783 | 268 | |
mjr | 86:e30a1f60f783 | 269 | // process the pixels |
mjr | 86:e30a1f60f783 | 270 | int pixpos; |
mjr | 86:e30a1f60f783 | 271 | if (process(pix, native_npix, pixpos)) |
mjr | 86:e30a1f60f783 | 272 | { |
mjr | 86:e30a1f60f783 | 273 | r.pos = pixpos; |
mjr | 86:e30a1f60f783 | 274 | r.t = tpix; |
mjr | 86:e30a1f60f783 | 275 | |
mjr | 86:e30a1f60f783 | 276 | // success |
mjr | 86:e30a1f60f783 | 277 | return true; |
mjr | 86:e30a1f60f783 | 278 | } |
mjr | 86:e30a1f60f783 | 279 | else |
mjr | 86:e30a1f60f783 | 280 | { |
mjr | 86:e30a1f60f783 | 281 | // no position found |
mjr | 86:e30a1f60f783 | 282 | return false; |
mjr | 86:e30a1f60f783 | 283 | } |
mjr | 86:e30a1f60f783 | 284 | } |
mjr | 86:e30a1f60f783 | 285 | }; |
mjr | 86:e30a1f60f783 | 286 | |
mjr | 86:e30a1f60f783 | 287 | // --------------------------------------------------------------------- |
mjr | 86:e30a1f60f783 | 288 | // |
mjr | 86:e30a1f60f783 | 289 | // Subclass for the small sensors, such as TSL1401CL (128 pixels). |
mjr | 86:e30a1f60f783 | 290 | // |
mjr | 86:e30a1f60f783 | 291 | // For the small sensors, we can't use the asynchronous transfer |
mjr | 86:e30a1f60f783 | 292 | // scheme we use for the large sensors, because the transfer times |
mjr | 86:e30a1f60f783 | 293 | // are so short that the main loop can't poll frequently enough to |
mjr | 86:e30a1f60f783 | 294 | // maintain consistent exposure times. With the short transfer |
mjr | 86:e30a1f60f783 | 295 | // times, though, we don't need to do them asynchronously. |
mjr | 86:e30a1f60f783 | 296 | // |
mjr | 86:e30a1f60f783 | 297 | // Instead, each time we want to read the sensor, we do the whole |
mjr | 86:e30a1f60f783 | 298 | // integration and transfer synchronously, so that we can precisly |
mjr | 86:e30a1f60f783 | 299 | // control the total time. This requires two transfers. First, |
mjr | 86:e30a1f60f783 | 300 | // we start a transfer in order to set the exact starting time of |
mjr | 86:e30a1f60f783 | 301 | // an integration period: call it period A. We wait for the |
mjr | 86:e30a1f60f783 | 302 | // transfer to complete, which fills a buffer with the prior |
mjr | 86:e30a1f60f783 | 303 | // integration period's pixels. We don't want those pixels, |
mjr | 86:e30a1f60f783 | 304 | // because they started before we got here and thus we can't |
mjr | 86:e30a1f60f783 | 305 | // control how long they were integrating. So we discard that |
mjr | 86:e30a1f60f783 | 306 | // buffer and start a new transfer. This starts period B while |
mjr | 86:e30a1f60f783 | 307 | // transferring period A's pixels into a DMA buffer. We want |
mjr | 86:e30a1f60f783 | 308 | // those period A pixels, so we wait for this transfer to finish. |
mjr | 86:e30a1f60f783 | 309 | // |
mjr | 86:e30a1f60f783 | 310 | class PlungerSensorTSL14xxSmall: public PlungerSensorTSL14xx |
mjr | 86:e30a1f60f783 | 311 | { |
mjr | 86:e30a1f60f783 | 312 | public: |
mjr | 86:e30a1f60f783 | 313 | PlungerSensorTSL14xxSmall(int nativePix, int nativeScale, |
mjr | 86:e30a1f60f783 | 314 | PinName si, PinName clock, PinName ao) |
mjr | 86:e30a1f60f783 | 315 | : PlungerSensorTSL14xx(nativePix, nativeScale, si, clock, ao) |
mjr | 86:e30a1f60f783 | 316 | { |
mjr | 86:e30a1f60f783 | 317 | } |
mjr | 86:e30a1f60f783 | 318 | |
mjr | 86:e30a1f60f783 | 319 | // read the plunger position |
mjr | 86:e30a1f60f783 | 320 | virtual bool readRaw(PlungerReading &r) |
mjr | 86:e30a1f60f783 | 321 | { |
mjr | 86:e30a1f60f783 | 322 | // Clear the sensor. This sends a HOLD/SI pulse to the sensor, |
mjr | 86:e30a1f60f783 | 323 | // which ends the current integration period, starts a new one |
mjr | 86:e30a1f60f783 | 324 | // (call the new one period A) right now, and clocks out all |
mjr | 86:e30a1f60f783 | 325 | // of the pixels from the old cycle. We want to discard these |
mjr | 86:e30a1f60f783 | 326 | // pixels because they've been integrating from some time in |
mjr | 86:e30a1f60f783 | 327 | // the past, so we can't control the exact timing of that cycle. |
mjr | 86:e30a1f60f783 | 328 | // Clearing the sensor clocks the pixels out without waiting to |
mjr | 86:e30a1f60f783 | 329 | // read them on DMA, so it's much faster than a regular transfer |
mjr | 86:e30a1f60f783 | 330 | // and thus gives us the shortest possible base integration time |
mjr | 86:e30a1f60f783 | 331 | // for period A. |
mjr | 86:e30a1f60f783 | 332 | sensor.clear(); |
mjr | 86:e30a1f60f783 | 333 | |
mjr | 86:e30a1f60f783 | 334 | // Start a real transfer. This ends integration period A (the |
mjr | 86:e30a1f60f783 | 335 | // one we just started), starts a new integration period B, and |
mjr | 86:e30a1f60f783 | 336 | // begins transferring period A's pixels to memory via DMA. We |
mjr | 86:e30a1f60f783 | 337 | // use the auto-exposure time to get the optimal exposure. |
mjr | 86:e30a1f60f783 | 338 | sensor.startCapture(axcTime); |
mjr | 86:e30a1f60f783 | 339 | |
mjr | 86:e30a1f60f783 | 340 | // wait for the period A pixel transfer to finish, and grab |
mjr | 86:e30a1f60f783 | 341 | // its pixels |
mjr | 86:e30a1f60f783 | 342 | uint8_t *pix; |
mjr | 86:e30a1f60f783 | 343 | uint32_t tpix; |
mjr | 86:e30a1f60f783 | 344 | sensor.waitPix(pix, tpix); |
mjr | 86:e30a1f60f783 | 345 | |
mjr | 86:e30a1f60f783 | 346 | // process the pixels |
mjr | 86:e30a1f60f783 | 347 | int pixpos; |
mjr | 86:e30a1f60f783 | 348 | if (process(pix, native_npix, pixpos)) |
mjr | 86:e30a1f60f783 | 349 | { |
mjr | 86:e30a1f60f783 | 350 | r.pos = pixpos; |
mjr | 86:e30a1f60f783 | 351 | r.t = tpix; |
mjr | 86:e30a1f60f783 | 352 | |
mjr | 86:e30a1f60f783 | 353 | // success |
mjr | 86:e30a1f60f783 | 354 | return true; |
mjr | 86:e30a1f60f783 | 355 | } |
mjr | 86:e30a1f60f783 | 356 | else |
mjr | 86:e30a1f60f783 | 357 | { |
mjr | 86:e30a1f60f783 | 358 | // no position found |
mjr | 86:e30a1f60f783 | 359 | return false; |
mjr | 86:e30a1f60f783 | 360 | } |
mjr | 86:e30a1f60f783 | 361 | } |
mjr | 86:e30a1f60f783 | 362 | }; |
mjr | 86:e30a1f60f783 | 363 | |
mjr | 86:e30a1f60f783 | 364 | |
mjr | 82:4f6209cb5c33 | 365 | #endif |