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/tcd1103Sensor.h@104:6e06e0f4b476, 2019-12-27 (annotated)
- Committer:
- mjr
- Date:
- Fri Dec 27 20:14:23 2019 +0000
- Revision:
- 104:6e06e0f4b476
- Parent:
- 101:755f44622abc
- Child:
- 106:e9e3b46132c1
AEAT-6012, TCD1103 updates
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
mjr | 100:1ff35c07217c | 1 | // Toshiba TCD1103 linear image sensors |
mjr | 100:1ff35c07217c | 2 | // |
mjr | 100:1ff35c07217c | 3 | // This sensor is similar to the original TSL1410R in both its electronic |
mjr | 100:1ff35c07217c | 4 | // interface and the theory of operation. The details of the electronics |
mjr | 100:1ff35c07217c | 5 | // are different enough that we can't reuse the same code at the hardware |
mjr | 100:1ff35c07217c | 6 | // interface level, but the principle of operation is similar: the sensor |
mjr | 100:1ff35c07217c | 7 | // provides a serial interface to a file of pixels transferred as analog |
mjr | 100:1ff35c07217c | 8 | // voltage levels representing the charge collected. |
mjr | 100:1ff35c07217c | 9 | // |
mjr | 100:1ff35c07217c | 10 | // As with the TSL1410R, we position the sensor so that the pixel row is |
mjr | 104:6e06e0f4b476 | 11 | // aligned with the plunger axis, and we detect the plunger position by |
mjr | 104:6e06e0f4b476 | 12 | // looking for a dark/light edge at the end of the plunger. However, |
mjr | 104:6e06e0f4b476 | 13 | // the optics for this sensor are very different because of the sensor's |
mjr | 104:6e06e0f4b476 | 14 | // size. The TSL1410R is by some magical coincidence the same size as |
mjr | 104:6e06e0f4b476 | 15 | // the plunger travel range, so we set that sensor up so that the plunger |
mjr | 104:6e06e0f4b476 | 16 | // is backlit with respect to the sensor, and simply casts a shadow on |
mjr | 104:6e06e0f4b476 | 17 | // the sensor. The TCD1103, in contrast, has a pixel array that's only |
mjr | 104:6e06e0f4b476 | 18 | // 8mm long, so we can't use the direct shadow approach. Instead, we |
mjr | 104:6e06e0f4b476 | 19 | // have to use a lens to focus an image of the plunger on the sensor. |
mjr | 104:6e06e0f4b476 | 20 | // With a focused image, we can front-light the plunger and take a picture |
mjr | 104:6e06e0f4b476 | 21 | // of the plunger itself rather than of an occluded back-light. |
mjr | 100:1ff35c07217c | 22 | // |
mjr | 104:6e06e0f4b476 | 23 | // Even though we use "edge sensing", this class isn't based on the |
mjr | 104:6e06e0f4b476 | 24 | // PlungerSensorEdgePos class. Our sensing algorithm is a little different, |
mjr | 104:6e06e0f4b476 | 25 | // and much simpler, because we're working with a proper image of the |
mjr | 104:6e06e0f4b476 | 26 | // plunger, rather than an image of its shadow. The shadow tends to be |
mjr | 104:6e06e0f4b476 | 27 | // rather fuzzy, and the TSL14xx sensors were pretty noisy, so we had to |
mjr | 104:6e06e0f4b476 | 28 | // work fairly hard to distinguish an edge in the image from a noise spike. |
mjr | 104:6e06e0f4b476 | 29 | // This sensor has very low noise, and the focused image produces a sharp |
mjr | 104:6e06e0f4b476 | 30 | // edge, so we can use a more straightforward algorithm that just looks |
mjr | 104:6e06e0f4b476 | 31 | // for the first bright spot. |
mjr | 104:6e06e0f4b476 | 32 | // |
mjr | 104:6e06e0f4b476 | 33 | // The TCD1103 uses a negative image: brighter pixels are represented by |
mjr | 104:6e06e0f4b476 | 34 | // lower numbers. The electronics of the sensor are such that the dynamic |
mjr | 104:6e06e0f4b476 | 35 | // range for the pixel analag voltage signal (which is what our pixel |
mjr | 104:6e06e0f4b476 | 36 | // elements represent) is only about 1V, or about 30% of the 3.3V range of |
mjr | 104:6e06e0f4b476 | 37 | // the ADC. Dark pixels read at about 2V (about 167 after 8-bit ADC |
mjr | 104:6e06e0f4b476 | 38 | // quantization), and saturated pixels read at 1V (78 on the ADC). So our |
mjr | 104:6e06e0f4b476 | 39 | // effective dynamic range after quantization is about 100 steps. That |
mjr | 104:6e06e0f4b476 | 40 | // would be pretty terrible if the goal were to take pictures for an art |
mjr | 104:6e06e0f4b476 | 41 | // gallery, and there are things we could do in the electronic interface |
mjr | 104:6e06e0f4b476 | 42 | // to improve it; in particular, we could use an op-amp to expand the |
mjr | 104:6e06e0f4b476 | 43 | // voltage range on the ADC input and remove the DC offset, so that the |
mjr | 104:6e06e0f4b476 | 44 | // signal going into the ADC covers the ADC's full 0V - 3.3V range. But |
mjr | 104:6e06e0f4b476 | 45 | // there's need for that for our purposes here; 100 steps is perfectly |
mjr | 104:6e06e0f4b476 | 46 | // adequate for our edge sensing, especially given the sensor's low |
mjr | 104:6e06e0f4b476 | 47 | // noise level. |
mjr | 100:1ff35c07217c | 48 | |
mjr | 104:6e06e0f4b476 | 49 | |
mjr | 104:6e06e0f4b476 | 50 | #include "plunger.h" |
mjr | 100:1ff35c07217c | 51 | #include "TCD1103.h" |
mjr | 100:1ff35c07217c | 52 | |
mjr | 100:1ff35c07217c | 53 | template <bool invertedLogicGates> |
mjr | 100:1ff35c07217c | 54 | class PlungerSensorImageInterfaceTCD1103: public PlungerSensorImageInterface |
mjr | 100:1ff35c07217c | 55 | { |
mjr | 100:1ff35c07217c | 56 | public: |
mjr | 100:1ff35c07217c | 57 | PlungerSensorImageInterfaceTCD1103(PinName fm, PinName os, PinName icg, PinName sh) |
mjr | 104:6e06e0f4b476 | 58 | : PlungerSensorImageInterface(1500), sensor(fm, os, icg, sh) |
mjr | 100:1ff35c07217c | 59 | { |
mjr | 100:1ff35c07217c | 60 | } |
mjr | 100:1ff35c07217c | 61 | |
mjr | 100:1ff35c07217c | 62 | // is the sensor ready? |
mjr | 100:1ff35c07217c | 63 | virtual bool ready() { return sensor.ready(); } |
mjr | 100:1ff35c07217c | 64 | |
mjr | 101:755f44622abc | 65 | virtual void init() { } |
mjr | 100:1ff35c07217c | 66 | |
mjr | 100:1ff35c07217c | 67 | // get the average sensor scan time |
mjr | 100:1ff35c07217c | 68 | virtual uint32_t getAvgScanTime() { return sensor.getAvgScanTime(); } |
mjr | 100:1ff35c07217c | 69 | |
mjr | 101:755f44622abc | 70 | virtual void readPix(uint8_t* &pix, uint32_t &t) |
mjr | 100:1ff35c07217c | 71 | { |
mjr | 100:1ff35c07217c | 72 | // get the image array from the last capture |
mjr | 104:6e06e0f4b476 | 73 | sensor.getPix(pix, t); |
mjr | 100:1ff35c07217c | 74 | } |
mjr | 100:1ff35c07217c | 75 | |
mjr | 101:755f44622abc | 76 | virtual void releasePix() { sensor.releasePix(); } |
mjr | 101:755f44622abc | 77 | |
mjr | 101:755f44622abc | 78 | virtual void setMinIntTime(uint32_t us) { sensor.setMinIntTime(us); } |
mjr | 100:1ff35c07217c | 79 | |
mjr | 100:1ff35c07217c | 80 | // the low-level interface to the TSL14xx sensor |
mjr | 100:1ff35c07217c | 81 | TCD1103<invertedLogicGates> sensor; |
mjr | 100:1ff35c07217c | 82 | }; |
mjr | 100:1ff35c07217c | 83 | |
mjr | 100:1ff35c07217c | 84 | template<bool invertedLogicGates> |
mjr | 104:6e06e0f4b476 | 85 | class PlungerSensorTCD1103: public PlungerSensorImage<int> |
mjr | 100:1ff35c07217c | 86 | { |
mjr | 100:1ff35c07217c | 87 | public: |
mjr | 100:1ff35c07217c | 88 | PlungerSensorTCD1103(PinName fm, PinName os, PinName icg, PinName sh) |
mjr | 104:6e06e0f4b476 | 89 | : PlungerSensorImage(sensor, 1500, 1499, true), sensor(fm, os, icg, sh) |
mjr | 100:1ff35c07217c | 90 | { |
mjr | 100:1ff35c07217c | 91 | } |
mjr | 100:1ff35c07217c | 92 | |
mjr | 100:1ff35c07217c | 93 | protected: |
mjr | 104:6e06e0f4b476 | 94 | // Process an image. This seeks the first dark-to-light edge in the image. |
mjr | 104:6e06e0f4b476 | 95 | // We assume that the background (open space behind the plunger) has a |
mjr | 104:6e06e0f4b476 | 96 | // dark (minimally reflective) backdrop, and that the tip of the plunger |
mjr | 104:6e06e0f4b476 | 97 | // has a bright white strip right at the end. So the end of the plunger |
mjr | 104:6e06e0f4b476 | 98 | // should be easily identifiable in the image as the first bright edge |
mjr | 104:6e06e0f4b476 | 99 | // we see starting at the "far" end. |
mjr | 104:6e06e0f4b476 | 100 | virtual bool process(const uint8_t *pix, int n, int &pos, int& /*processResult*/) |
mjr | 104:6e06e0f4b476 | 101 | { |
mjr | 104:6e06e0f4b476 | 102 | // Scan the pixel array to determine the actual dynamic range |
mjr | 104:6e06e0f4b476 | 103 | // of this image. That will let us determine what consistutes |
mjr | 104:6e06e0f4b476 | 104 | // "bright" when we're looking for the bright spot. |
mjr | 104:6e06e0f4b476 | 105 | uint8_t pixMin = 255, pixMax = 0; |
mjr | 104:6e06e0f4b476 | 106 | const uint8_t *p = pix; |
mjr | 104:6e06e0f4b476 | 107 | for (int i = n; i != 0; --i) |
mjr | 104:6e06e0f4b476 | 108 | { |
mjr | 104:6e06e0f4b476 | 109 | uint8_t c = *p++; |
mjr | 104:6e06e0f4b476 | 110 | if (c < pixMin) pixMin = c; |
mjr | 104:6e06e0f4b476 | 111 | if (c > pixMax) pixMax = c; |
mjr | 104:6e06e0f4b476 | 112 | } |
mjr | 104:6e06e0f4b476 | 113 | |
mjr | 104:6e06e0f4b476 | 114 | // Figure the threshold brightness for the bright spot as halfway |
mjr | 104:6e06e0f4b476 | 115 | // between the min and max. |
mjr | 104:6e06e0f4b476 | 116 | uint8_t threshold = (pixMin + pixMax)/2; |
mjr | 104:6e06e0f4b476 | 117 | |
mjr | 104:6e06e0f4b476 | 118 | // Scan for the first bright-enough pixel. Remember that we're |
mjr | 104:6e06e0f4b476 | 119 | // working with a negative image, so "brighter" is "less than". |
mjr | 104:6e06e0f4b476 | 120 | p = pix; |
mjr | 104:6e06e0f4b476 | 121 | for (int i = n; i != 0; --i, ++p) |
mjr | 104:6e06e0f4b476 | 122 | { |
mjr | 104:6e06e0f4b476 | 123 | if (*p < threshold) |
mjr | 104:6e06e0f4b476 | 124 | { |
mjr | 104:6e06e0f4b476 | 125 | // got it - report this position |
mjr | 104:6e06e0f4b476 | 126 | pos = p - pix; |
mjr | 104:6e06e0f4b476 | 127 | return true; |
mjr | 104:6e06e0f4b476 | 128 | } |
mjr | 104:6e06e0f4b476 | 129 | } |
mjr | 104:6e06e0f4b476 | 130 | |
mjr | 104:6e06e0f4b476 | 131 | // no edge found - report failure |
mjr | 104:6e06e0f4b476 | 132 | return false; |
mjr | 104:6e06e0f4b476 | 133 | } |
mjr | 104:6e06e0f4b476 | 134 | |
mjr | 104:6e06e0f4b476 | 135 | // Use a fixed orientation for this sensor. The shadow-edge sensors |
mjr | 104:6e06e0f4b476 | 136 | // try to infer the direction by checking which end of the image is |
mjr | 104:6e06e0f4b476 | 137 | // brighter, which works well for the shadow sensors because the back |
mjr | 104:6e06e0f4b476 | 138 | // end of the image will always be in shadow. But for this sensor, |
mjr | 104:6e06e0f4b476 | 139 | // we're taking an image of the plunger (not its shadow), and the |
mjr | 104:6e06e0f4b476 | 140 | // back end of the plunger is the part with the spring, which has a |
mjr | 104:6e06e0f4b476 | 141 | // fuzzy and complex reflectivity pattern because of the spring. |
mjr | 104:6e06e0f4b476 | 142 | // So for this sensor, it's better to insist that the user sets it |
mjr | 104:6e06e0f4b476 | 143 | // up in a canonical orientation. That's a reasaonble expectation |
mjr | 104:6e06e0f4b476 | 144 | // for this sensor anyway, because the physical installation won't |
mjr | 104:6e06e0f4b476 | 145 | // be as ad hoc as the TSL1410R setup, which only required that you |
mjr | 104:6e06e0f4b476 | 146 | // mounted the sensor itself. In this case, you have to build a |
mjr | 104:6e06e0f4b476 | 147 | // circuit board and mount a lens on it, so it's reasonable to |
mjr | 104:6e06e0f4b476 | 148 | // expect that everyone will be using the mounting apparatus plans |
mjr | 104:6e06e0f4b476 | 149 | // that we'll detail in the build guide. In any case, we'll just |
mjr | 104:6e06e0f4b476 | 150 | // make it clear in the instructions that you have to mount the |
mjr | 104:6e06e0f4b476 | 151 | // sensor in a certain orientation. |
mjr | 104:6e06e0f4b476 | 152 | virtual int getOrientation() const { return 1; } |
mjr | 104:6e06e0f4b476 | 153 | |
mjr | 104:6e06e0f4b476 | 154 | // the hardware sensor interface |
mjr | 100:1ff35c07217c | 155 | PlungerSensorImageInterfaceTCD1103<invertedLogicGates> sensor; |
mjr | 100:1ff35c07217c | 156 | }; |